videoscale: Add support for adding black borders to keep the DAR if necessary

Fixes bug #617506.
This commit is contained in:
Sebastian Dröge 2010-07-17 20:24:18 +02:00
parent 619e5b6e44
commit 52e711b11d
7 changed files with 804 additions and 198 deletions

View file

@ -7,7 +7,8 @@ libgstvideoscale_la_SOURCES = \
gstvideoscale.c \
vs_image.c \
vs_scanline.c \
vs_4tap.c
vs_4tap.c \
vs_fill_borders.c
nodist_libgstvideoscale_la_SOURCES = $(ORC_NODIST_SOURCES)
@ -22,6 +23,6 @@ noinst_HEADERS = \
gstvideoscale.h \
vs_image.h \
vs_scanline.h \
vs_4tap.h
vs_4tap.h \
vs_fill_borders.h

View file

@ -48,6 +48,26 @@
* Last reviewed on 2006-03-02 (0.10.4)
*/
/*
* Formulas for PAR, DAR, width and height relations:
*
* dar_n w par_n
* ----- = - * -----
* dar_d h par_d
*
* par_n h dar_n
* ----- = - * -----
* par_d w dar_d
*
* dar_n par_d
* w = h * ----- * -----
* dar_d par_n
*
* dar_d par_n
* h = w * ----- * -----
* dar_n par_d
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@ -61,17 +81,19 @@
#include "gstvideoscale.h"
#include "vs_image.h"
#include "vs_4tap.h"
#include "vs_fill_borders.h"
/* debug variable definition */
GST_DEBUG_CATEGORY (video_scale_debug);
#define DEFAULT_PROP_METHOD GST_VIDEO_SCALE_BILINEAR
#define DEFAULT_PROP_METHOD GST_VIDEO_SCALE_BILINEAR
#define DEFAULT_PROP_ADD_BORDERS FALSE
enum
{
PROP_0,
PROP_METHOD
PROP_METHOD,
PROP_ADD_BORDERS
/* FILL ME */
};
@ -220,6 +242,12 @@ gst_video_scale_class_init (GstVideoScaleClass * klass)
GST_TYPE_VIDEO_SCALE_METHOD, DEFAULT_PROP_METHOD,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_ADD_BORDERS,
g_param_spec_boolean ("borders", "Add Borders",
"Add black borders if necessary to keep the display aspect ratio",
DEFAULT_PROP_ADD_BORDERS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
trans_class->transform_caps =
GST_DEBUG_FUNCPTR (gst_video_scale_transform_caps);
trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_video_scale_set_caps);
@ -235,6 +263,7 @@ gst_video_scale_init (GstVideoScale * videoscale, GstVideoScaleClass * klass)
{
videoscale->tmp_buf = NULL;
videoscale->method = DEFAULT_PROP_METHOD;
videoscale->add_borders = DEFAULT_PROP_ADD_BORDERS;
}
static void
@ -258,6 +287,12 @@ gst_video_scale_set_property (GObject * object, guint prop_id,
vscale->method = g_value_get_enum (value);
GST_OBJECT_UNLOCK (vscale);
break;
case PROP_ADD_BORDERS:
GST_OBJECT_LOCK (vscale);
vscale->add_borders = g_value_get_boolean (value);
GST_OBJECT_UNLOCK (vscale);
gst_base_transform_reconfigure (GST_BASE_TRANSFORM_CAST (vscale));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -276,6 +311,11 @@ gst_video_scale_get_property (GObject * object, guint prop_id, GValue * value,
g_value_set_enum (value, vscale->method);
GST_OBJECT_UNLOCK (vscale);
break;
case PROP_ADD_BORDERS:
GST_OBJECT_LOCK (vscale);
g_value_set_boolean (value, vscale->add_borders);
GST_OBJECT_UNLOCK (vscale);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -313,17 +353,6 @@ gst_video_scale_transform_caps (GstBaseTransform * trans,
return ret;
}
static void
gst_video_scale_setup_vs_image (VSImage * image, GstVideoFormat format,
gint component, gint width, gint height)
{
image->width =
gst_video_format_get_component_width (format, component, width);
image->height =
gst_video_format_get_component_height (format, component, height);
image->stride = gst_video_format_get_row_stride (format, component, width);
}
static gboolean
gst_video_scale_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
{
@ -341,21 +370,11 @@ gst_video_scale_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
if (!ret)
goto done;
videoscale->src_size = gst_video_format_get_size (videoscale->format,
videoscale->from_width, videoscale->from_height);
videoscale->dest_size = gst_video_format_get_size (videoscale->format,
videoscale->to_width, videoscale->to_height);
gst_video_scale_setup_vs_image (&videoscale->src, videoscale->format, 0,
videoscale->from_width, videoscale->from_height);
gst_video_scale_setup_vs_image (&videoscale->dest, videoscale->format, 0,
videoscale->to_width, videoscale->to_height);
if (videoscale->tmp_buf)
g_free (videoscale->tmp_buf);
videoscale->tmp_buf = g_malloc (videoscale->dest.stride * 4);
if (!gst_video_parse_caps_pixel_aspect_ratio (in, &from_par_n, &from_par_d))
from_par_n = from_par_d = 1;
if (!gst_video_parse_caps_pixel_aspect_ratio (out, &to_par_n, &to_par_d))
@ -367,24 +386,53 @@ gst_video_scale_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
from_dar_n = from_dar_d = -1;
}
if (!gst_util_fraction_multiply (videoscale->to_width, videoscale->to_height,
to_par_n, to_par_d, &to_dar_n, &to_dar_d)) {
if (!gst_util_fraction_multiply (videoscale->to_width,
videoscale->to_height, to_par_n, to_par_d, &to_dar_n, &to_dar_d)) {
to_dar_n = to_dar_d = -1;
}
if (to_dar_n != from_dar_n || to_dar_d != from_dar_d)
GST_WARNING_OBJECT (videoscale, "Can't keep DAR!");
videoscale->borders_w = videoscale->borders_h = 0;
if (to_dar_n != from_dar_n || to_dar_d != from_dar_d) {
if (videoscale->add_borders) {
gint n, d, to_h, to_w;
if (from_dar_n != -1 && from_dar_d != -1
&& gst_util_fraction_multiply (from_dar_n, from_dar_d, to_par_n,
to_par_d, &n, &d)) {
to_h = gst_util_uint64_scale_int (videoscale->to_width, d, n);
if (to_h <= videoscale->to_height) {
videoscale->borders_h = videoscale->to_height - to_h;
videoscale->borders_w = 0;
} else {
to_w = gst_util_uint64_scale_int (videoscale->to_height, n, d);
g_assert (to_w <= videoscale->to_width);
videoscale->borders_h = 0;
videoscale->borders_w = videoscale->to_width - to_w;
}
} else {
GST_WARNING_OBJECT (videoscale, "Can't calculate borders");
}
} else {
GST_WARNING_OBJECT (videoscale, "Can't keep DAR!");
}
}
if (videoscale->tmp_buf)
g_free (videoscale->tmp_buf);
videoscale->tmp_buf =
g_malloc (gst_video_format_get_row_stride (videoscale->format, 0,
videoscale->to_width) * 4);
gst_base_transform_set_passthrough (trans,
(videoscale->from_width == videoscale->to_width
&& videoscale->from_height == videoscale->to_height));
GST_DEBUG_OBJECT (videoscale, "from=%dx%d (par=%d/%d dar=%d/%d), size %d "
"-> to=%dx%d (par=%d/%d dar=%d/%d), size %d",
"-> to=%dx%d (par=%d/%d dar=%d/%d borders=%d:%d), size %d",
videoscale->from_width, videoscale->from_height, from_par_n, from_par_d,
from_dar_n, from_dar_d, videoscale->src_size, videoscale->to_width,
videoscale->to_height, to_par_n, to_par_d, to_dar_n, to_dar_d,
videoscale->dest_size);
videoscale->borders_w, videoscale->borders_h, videoscale->dest_size);
done:
return ret;
@ -814,33 +862,113 @@ done:
g_value_unset (&tpar);
}
static gboolean
gst_video_scale_prepare_image (gint format, GstBuffer * buf,
VSImage * img, VSImage * img_u, VSImage * img_v)
static void
gst_video_scale_setup_vs_image (VSImage * image, GstVideoFormat format,
gint component, gint width, gint height, gint b_w, gint b_h, uint8_t * data)
{
gboolean res = TRUE;
image->real_width =
gst_video_format_get_component_width (format, component, width);
image->real_height =
gst_video_format_get_component_height (format, component, height);
image->width =
gst_video_format_get_component_width (format, component, MAX (1,
width - b_w));
image->height =
gst_video_format_get_component_height (format, component, MAX (1,
height - b_h));
image->stride = gst_video_format_get_row_stride (format, component, width);
image->border_top = (image->real_height - image->height) / 2;
image->border_bottom = image->real_height - image->height - image->border_top;
if (format == GST_VIDEO_FORMAT_YUY2 || format == GST_VIDEO_FORMAT_YVYU
|| format == GST_VIDEO_FORMAT_UYVY) {
g_assert (component == 0);
image->border_left = (image->real_width - image->width) / 2;
if (image->border_left % 2 == 1)
image->border_left--;
image->border_right = image->real_width - image->width - image->border_left;
} else {
image->border_left = (image->real_width - image->width) / 2;
image->border_right = image->real_width - image->width - image->border_left;
}
if (format == GST_VIDEO_FORMAT_I420
|| format == GST_VIDEO_FORMAT_YV12
|| format == GST_VIDEO_FORMAT_Y444
|| format == GST_VIDEO_FORMAT_Y42B || format == GST_VIDEO_FORMAT_Y41B) {
image->real_pixels = data + gst_video_format_get_component_offset (format,
component, width, height);
} else {
g_assert (component == 0);
image->real_pixels = data;
}
image->pixels =
image->real_pixels + image->border_top * image->stride +
image->border_left * gst_video_format_get_pixel_stride (format,
component);
}
static const guint8 *
_get_black_for_format (GstVideoFormat format)
{
static const guint8 black[][4] = {
{255, 0, 0, 0}, /* 0 = ARGB, ABGR, xRGB, xBGR */
{0, 0, 0, 255}, /* 1 = RGBA, BGRA, RGBx, BGRx */
{255, 16, 128, 128}, /* 2 = AYUV */
{0, 0, 0, 0}, /* 3 = RGB and BGR */
{16, 128, 128, 0}, /* 4 = v301 */
{16, 128, 16, 128}, /* 5 = YUY2, YUYV */
{128, 16, 128, 16}, /* 6 = UYVY */
{16, 0, 0, 0}, /* 7 = Y */
{0, 0, 0, 0} /* 8 = RGB565, RGB666 */
};
switch (format) {
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_xBGR:
return black[0];
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_BGRA:
case GST_VIDEO_FORMAT_RGBx:
case GST_VIDEO_FORMAT_BGRx:
return black[1];
case GST_VIDEO_FORMAT_AYUV:
return black[2];
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
return black[3];
case GST_VIDEO_FORMAT_v308:
return black[4];
case GST_VIDEO_FORMAT_YUY2:
case GST_VIDEO_FORMAT_YVYU:
return black[5];
case GST_VIDEO_FORMAT_UYVY:
return black[6];
case GST_VIDEO_FORMAT_Y800:
case GST_VIDEO_FORMAT_GRAY8:
return black[7];
case GST_VIDEO_FORMAT_GRAY16_LE:
case GST_VIDEO_FORMAT_GRAY16_BE:
case GST_VIDEO_FORMAT_Y16:
return NULL; /* Handled by the caller */
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
case GST_VIDEO_FORMAT_Y444:
case GST_VIDEO_FORMAT_Y42B:
case GST_VIDEO_FORMAT_Y41B:
gst_video_scale_setup_vs_image (img_u, format, 1, img->width,
img->height);
gst_video_scale_setup_vs_image (img_v, format, 2, img->width,
img->height);
img_u->pixels =
GST_BUFFER_DATA (buf) + gst_video_format_get_component_offset (format,
1, img->width, img->height);
img_v->pixels =
GST_BUFFER_DATA (buf) + gst_video_format_get_component_offset (format,
2, img->width, img->height);
break;
return black[4]; /* Y, U, V, 0 */
case GST_VIDEO_FORMAT_RGB16:
case GST_VIDEO_FORMAT_RGB15:
return black[8];
default:
break;
return NULL;
}
return res;
}
static GstFlowReturn
@ -849,195 +977,230 @@ gst_video_scale_transform (GstBaseTransform * trans, GstBuffer * in,
{
GstVideoScale *videoscale = GST_VIDEO_SCALE (trans);
GstFlowReturn ret = GST_FLOW_OK;
VSImage dest = videoscale->dest;
VSImage src = videoscale->src;
VSImage dest = { NULL, };
VSImage src = { NULL, };
VSImage dest_u = { NULL, };
VSImage dest_v = { NULL, };
VSImage src_u = { NULL, };
VSImage src_v = { NULL, };
gint method;
const guint8 *black = _get_black_for_format (videoscale->format);
gboolean add_borders;
GST_OBJECT_LOCK (videoscale);
method = videoscale->method;
add_borders = videoscale->add_borders;
GST_OBJECT_UNLOCK (videoscale);
src.pixels = GST_BUFFER_DATA (in);
dest.pixels = GST_BUFFER_DATA (out);
gst_video_scale_setup_vs_image (&src, videoscale->format, 0,
videoscale->from_width, videoscale->from_height, 0, 0,
GST_BUFFER_DATA (in));
gst_video_scale_setup_vs_image (&dest, videoscale->format, 0,
videoscale->to_width, videoscale->to_height, videoscale->borders_w,
videoscale->borders_h, GST_BUFFER_DATA (out));
if (src.height < 4 && method == GST_VIDEO_SCALE_4TAP)
method = GST_VIDEO_SCALE_BILINEAR;
if (videoscale->format == GST_VIDEO_FORMAT_I420
|| videoscale->format == GST_VIDEO_FORMAT_YV12
|| videoscale->format == GST_VIDEO_FORMAT_Y444
|| videoscale->format == GST_VIDEO_FORMAT_Y42B
|| videoscale->format == GST_VIDEO_FORMAT_Y41B) {
gst_video_scale_setup_vs_image (&src_u, videoscale->format, 1,
videoscale->from_width, videoscale->from_height, 0, 0,
GST_BUFFER_DATA (in));
gst_video_scale_setup_vs_image (&src_v, videoscale->format, 2,
videoscale->from_width, videoscale->from_height, 0, 0,
GST_BUFFER_DATA (in));
gst_video_scale_setup_vs_image (&dest_u, videoscale->format, 1,
videoscale->to_width, videoscale->to_height, videoscale->borders_w,
videoscale->borders_h, GST_BUFFER_DATA (out));
gst_video_scale_setup_vs_image (&dest_v, videoscale->format, 2,
videoscale->to_width, videoscale->to_height, videoscale->borders_w,
videoscale->borders_h, GST_BUFFER_DATA (out));
}
gst_video_scale_prepare_image (videoscale->format, in, &videoscale->src,
&src_u, &src_v);
gst_video_scale_prepare_image (videoscale->format, out, &videoscale->dest,
&dest_u, &dest_v);
switch (method) {
case GST_VIDEO_SCALE_NEAREST:
GST_LOG_OBJECT (videoscale, "doing nearest scaling");
switch (videoscale->format) {
case GST_VIDEO_FORMAT_RGBx:
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_BGRx:
case GST_VIDEO_FORMAT_xBGR:
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_BGRA:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_AYUV:
switch (videoscale->format) {
case GST_VIDEO_FORMAT_RGBx:
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_BGRx:
case GST_VIDEO_FORMAT_xBGR:
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_BGRA:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_AYUV:
if (add_borders)
vs_fill_borders_RGBA (&dest, black);
switch (method) {
case GST_VIDEO_SCALE_NEAREST:
vs_image_scale_nearest_RGBA (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
case GST_VIDEO_FORMAT_v308:
case GST_VIDEO_SCALE_BILINEAR:
vs_image_scale_linear_RGBA (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_SCALE_4TAP:
vs_image_scale_4tap_RGBA (&dest, &src, videoscale->tmp_buf);
break;
default:
goto unknown_mode;
}
break;
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
case GST_VIDEO_FORMAT_v308:
if (add_borders)
vs_fill_borders_RGB (&dest, black);
switch (method) {
case GST_VIDEO_SCALE_NEAREST:
vs_image_scale_nearest_RGB (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_YUY2:
case GST_VIDEO_FORMAT_YVYU:
case GST_VIDEO_SCALE_BILINEAR:
vs_image_scale_linear_RGB (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_SCALE_4TAP:
vs_image_scale_4tap_RGB (&dest, &src, videoscale->tmp_buf);
break;
default:
goto unknown_mode;
}
break;
case GST_VIDEO_FORMAT_YUY2:
case GST_VIDEO_FORMAT_YVYU:
if (add_borders)
vs_fill_borders_YUYV (&dest, black);
switch (method) {
case GST_VIDEO_SCALE_NEAREST:
vs_image_scale_nearest_YUYV (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_UYVY:
case GST_VIDEO_SCALE_BILINEAR:
vs_image_scale_linear_YUYV (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_SCALE_4TAP:
vs_image_scale_4tap_YUYV (&dest, &src, videoscale->tmp_buf);
break;
default:
goto unknown_mode;
}
break;
case GST_VIDEO_FORMAT_UYVY:
if (add_borders)
vs_fill_borders_UYVY (&dest, black);
switch (method) {
case GST_VIDEO_SCALE_NEAREST:
vs_image_scale_nearest_UYVY (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_Y800:
case GST_VIDEO_FORMAT_GRAY8:
case GST_VIDEO_SCALE_BILINEAR:
vs_image_scale_linear_UYVY (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_SCALE_4TAP:
vs_image_scale_4tap_UYVY (&dest, &src, videoscale->tmp_buf);
break;
default:
goto unknown_mode;
}
break;
case GST_VIDEO_FORMAT_Y800:
case GST_VIDEO_FORMAT_GRAY8:
if (add_borders)
vs_fill_borders_Y (&dest, black);
switch (method) {
case GST_VIDEO_SCALE_NEAREST:
vs_image_scale_nearest_Y (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_GRAY16_LE:
case GST_VIDEO_FORMAT_GRAY16_BE:
case GST_VIDEO_FORMAT_Y16:
case GST_VIDEO_SCALE_BILINEAR:
vs_image_scale_linear_Y (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_SCALE_4TAP:
vs_image_scale_4tap_Y (&dest, &src, videoscale->tmp_buf);
break;
default:
goto unknown_mode;
}
break;
case GST_VIDEO_FORMAT_GRAY16_LE:
case GST_VIDEO_FORMAT_GRAY16_BE:
case GST_VIDEO_FORMAT_Y16:
if (add_borders)
vs_fill_borders_Y16 (&dest, 0);
switch (method) {
case GST_VIDEO_SCALE_NEAREST:
vs_image_scale_nearest_Y16 (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
case GST_VIDEO_FORMAT_Y444:
case GST_VIDEO_FORMAT_Y42B:
case GST_VIDEO_FORMAT_Y41B:
case GST_VIDEO_SCALE_BILINEAR:
vs_image_scale_linear_Y16 (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_SCALE_4TAP:
vs_image_scale_4tap_Y16 (&dest, &src, videoscale->tmp_buf);
break;
default:
goto unknown_mode;
}
break;
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
case GST_VIDEO_FORMAT_Y444:
case GST_VIDEO_FORMAT_Y42B:
case GST_VIDEO_FORMAT_Y41B:
if (add_borders) {
vs_fill_borders_Y (&dest, black);
vs_fill_borders_Y (&dest_u, black + 1);
vs_fill_borders_Y (&dest_v, black + 2);
}
switch (method) {
case GST_VIDEO_SCALE_NEAREST:
vs_image_scale_nearest_Y (&dest, &src, videoscale->tmp_buf);
vs_image_scale_nearest_Y (&dest_u, &src_u, videoscale->tmp_buf);
vs_image_scale_nearest_Y (&dest_v, &src_v, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_RGB16:
vs_image_scale_nearest_RGB565 (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_RGB15:
vs_image_scale_nearest_RGB555 (&dest, &src, videoscale->tmp_buf);
break;
default:
goto unsupported;
}
break;
case GST_VIDEO_SCALE_BILINEAR:
GST_LOG_OBJECT (videoscale, "doing bilinear scaling");
switch (videoscale->format) {
case GST_VIDEO_FORMAT_RGBx:
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_BGRx:
case GST_VIDEO_FORMAT_xBGR:
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_BGRA:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_AYUV:
vs_image_scale_linear_RGBA (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
case GST_VIDEO_FORMAT_v308:
vs_image_scale_linear_RGB (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_YUY2:
case GST_VIDEO_FORMAT_YVYU:
vs_image_scale_linear_YUYV (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_UYVY:
vs_image_scale_linear_UYVY (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_Y800:
case GST_VIDEO_FORMAT_GRAY8:
vs_image_scale_linear_Y (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_GRAY16_LE:
case GST_VIDEO_FORMAT_GRAY16_BE:
case GST_VIDEO_FORMAT_Y16:
vs_image_scale_linear_Y16 (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
case GST_VIDEO_FORMAT_Y444:
case GST_VIDEO_FORMAT_Y42B:
case GST_VIDEO_FORMAT_Y41B:
case GST_VIDEO_SCALE_BILINEAR:
vs_image_scale_linear_Y (&dest, &src, videoscale->tmp_buf);
vs_image_scale_linear_Y (&dest_u, &src_u, videoscale->tmp_buf);
vs_image_scale_linear_Y (&dest_v, &src_v, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_RGB16:
vs_image_scale_linear_RGB565 (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_RGB15:
vs_image_scale_linear_RGB555 (&dest, &src, videoscale->tmp_buf);
break;
default:
goto unsupported;
}
break;
case GST_VIDEO_SCALE_4TAP:
GST_LOG_OBJECT (videoscale, "doing 4tap scaling");
switch (videoscale->format) {
case GST_VIDEO_FORMAT_RGBx:
case GST_VIDEO_FORMAT_xRGB:
case GST_VIDEO_FORMAT_BGRx:
case GST_VIDEO_FORMAT_xBGR:
case GST_VIDEO_FORMAT_RGBA:
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_BGRA:
case GST_VIDEO_FORMAT_ABGR:
case GST_VIDEO_FORMAT_AYUV:
vs_image_scale_4tap_RGBA (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_RGB:
case GST_VIDEO_FORMAT_BGR:
case GST_VIDEO_FORMAT_v308:
vs_image_scale_4tap_RGB (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_YUY2:
case GST_VIDEO_FORMAT_YVYU:
vs_image_scale_4tap_YUYV (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_UYVY:
vs_image_scale_4tap_UYVY (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_Y800:
case GST_VIDEO_FORMAT_GRAY8:
vs_image_scale_4tap_Y (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_GRAY16_LE:
case GST_VIDEO_FORMAT_GRAY16_BE:
case GST_VIDEO_FORMAT_Y16:
vs_image_scale_4tap_Y16 (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_YV12:
case GST_VIDEO_FORMAT_Y444:
case GST_VIDEO_FORMAT_Y42B:
case GST_VIDEO_FORMAT_Y41B:
case GST_VIDEO_SCALE_4TAP:
vs_image_scale_4tap_Y (&dest, &src, videoscale->tmp_buf);
vs_image_scale_4tap_Y (&dest_u, &src_u, videoscale->tmp_buf);
vs_image_scale_4tap_Y (&dest_v, &src_v, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_RGB16:
default:
goto unknown_mode;
}
break;
case GST_VIDEO_FORMAT_RGB16:
if (add_borders)
vs_fill_borders_RGB565 (&dest, black);
switch (method) {
case GST_VIDEO_SCALE_NEAREST:
vs_image_scale_nearest_RGB565 (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_SCALE_BILINEAR:
vs_image_scale_linear_RGB565 (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_SCALE_4TAP:
vs_image_scale_4tap_RGB565 (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_FORMAT_RGB15:
default:
goto unknown_mode;
}
break;
case GST_VIDEO_FORMAT_RGB15:
if (add_borders)
vs_fill_borders_RGB555 (&dest, black);
switch (method) {
case GST_VIDEO_SCALE_NEAREST:
vs_image_scale_nearest_RGB555 (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_SCALE_BILINEAR:
vs_image_scale_linear_RGB555 (&dest, &src, videoscale->tmp_buf);
break;
case GST_VIDEO_SCALE_4TAP:
vs_image_scale_4tap_RGB555 (&dest, &src, videoscale->tmp_buf);
break;
default:
goto unsupported;
goto unknown_mode;
}
break;
default:
goto unknown_mode;
goto unsupported;
}
GST_LOG_OBJECT (videoscale, "pushing buffer of %d bytes",

View file

@ -68,6 +68,7 @@ struct _GstVideoScale {
GstVideoFilter element;
GstVideoScaleMethod method;
gboolean add_borders;
/* negotiated stuff */
GstVideoFormat format;
@ -78,9 +79,9 @@ struct _GstVideoScale {
guint src_size;
guint dest_size;
VSImage src;
VSImage dest;
gint borders_h;
gint borders_w;
/*< private >*/
guint8 *tmp_buf;
};

View file

@ -30,3 +30,15 @@ addl t1, t1, t2
shrul t1, t1, 16
convlw d1, t1
.function orc_splat_u16
.dest 2 d1
.param 2 p1
copyw d1, p1
.function orc_splat_u32
.dest 4 d1
.param 4 p1
copyl d1, p1

View file

@ -0,0 +1,381 @@
/*
* Image Scaling Functions
* Copyright (c) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gst.h>
#include <string.h>
#include "vs_fill_borders.h"
#include "gstvideoscaleorc.h"
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define READ_UINT32(ptr) GST_READ_UINT32_LE(ptr)
#define READ_UINT16(ptr) GST_READ_UINT16_LE(ptr)
#else
#define READ_UINT32(ptr) GST_READ_UINT32_BE(ptr)
#define READ_UINT16(ptr) GST_READ_UINT16_BE(ptr)
#endif
void
vs_fill_borders_RGBA (const VSImage * dest, const uint8_t * val)
{
int i;
int top = dest->border_top, bottom = dest->border_bottom;
int left = dest->border_left, right = dest->border_right;
int width = dest->width;
int height = dest->height;
int real_width = dest->real_width;
int stride = dest->stride;
int tmp, tmp2;
uint8_t *data;
uint32_t v = READ_UINT32 (val);
data = dest->real_pixels;
for (i = 0; i < top; i++) {
orc_splat_u32 ((uint32_t *) data, v, real_width);
data += stride;
}
if (left || right) {
tmp = height;
tmp2 = (left + width) * 4;
for (i = 0; i < tmp; i++) {
orc_splat_u32 ((uint32_t *) data, v, left);
orc_splat_u32 ((uint32_t *) (data + tmp2), v, right);
data += stride;
}
} else {
data += stride * height;
}
for (i = 0; i < bottom; i++) {
orc_splat_u32 ((uint32_t *) data, v, real_width);
data += stride;
}
}
static void
_memset_u24 (uint8_t * data, uint8_t val1, uint8_t val2, uint8_t val3,
unsigned int n)
{
unsigned int i;
for (i = 0; i < n; i++) {
data[0] = val1;
data[1] = val2;
data[2] = val3;
data += 3;
}
}
void
vs_fill_borders_RGB (const VSImage * dest, const uint8_t * val)
{
int i;
int top = dest->border_top, bottom = dest->border_bottom;
int left = dest->border_left, right = dest->border_right;
int width = dest->width;
int height = dest->height;
int real_width = dest->real_width;
int stride = dest->stride;
int tmp, tmp2;
uint8_t *data;
data = dest->real_pixels;
for (i = 0; i < top; i++) {
_memset_u24 (data, val[0], val[1], val[2], real_width);
data += stride;
}
if (left || right) {
tmp = height;
tmp2 = (left + width) * 3;
for (i = 0; i < tmp; i++) {
_memset_u24 (data, val[0], val[1], val[2], left);
_memset_u24 (data + tmp2, val[0], val[1], val[2], right);
data += stride;
}
} else {
data += stride * height;
}
for (i = 0; i < bottom; i++) {
_memset_u24 (data, val[0], val[1], val[2], real_width);
data += stride;
}
}
void
vs_fill_borders_YUYV (const VSImage * dest, const uint8_t * val)
{
int i, j;
int top = dest->border_top, bottom = dest->border_bottom;
int left = dest->border_left, right = dest->border_right;
int width = dest->width;
int height = dest->height;
int real_width = dest->real_width;
int stride = dest->stride;
int tmp, tmp2;
uint8_t *data;
data = dest->real_pixels;
for (i = 0; i < top; i++) {
for (j = 0; j < real_width; j++) {
data[2 * j] = val[0];
data[2 * j + 1] = (j % 2 == 0) ? val[1] : val[3];
}
data += stride;
}
if (left || right) {
tmp = height;
tmp2 = (left + width) * 2;
for (i = 0; i < tmp; i++) {
for (j = 0; j < left; j++) {
data[2 * j] = val[0];
data[2 * j + 1] = (j % 2 == 0) ? val[1] : val[3];
}
for (j = 0; j < right; j++) {
data[tmp2 + 2 * j] = val[0];
data[tmp2 + 2 * j + 1] = (j % 2 == 0) ? val[1] : val[3];
}
data += stride;
}
} else {
data += stride * height;
}
for (i = 0; i < bottom; i++) {
for (j = 0; j < real_width; j++) {
data[2 * j] = val[0];
data[2 * j + 1] = (j % 2 == 0) ? val[1] : val[3];
}
data += stride;
}
}
void
vs_fill_borders_UYVY (const VSImage * dest, const uint8_t * val)
{
int i, j;
int top = dest->border_top, bottom = dest->border_bottom;
int left = dest->border_left, right = dest->border_right;
int width = dest->width;
int height = dest->height;
int real_width = dest->real_width;
int stride = dest->stride;
int tmp, tmp2;
uint8_t *data;
data = dest->real_pixels;
for (i = 0; i < top; i++) {
for (j = 0; j < real_width; j++) {
data[2 * j] = (j % 2 == 0) ? val[0] : val[2];
data[2 * j + 1] = val[1];
}
data += stride;
}
if (left || right) {
tmp = height;
tmp2 = (left + width) * 2;
for (i = 0; i < tmp; i++) {
for (j = 0; j < left; j++) {
data[2 * j] = (j % 2 == 0) ? val[0] : val[2];
data[2 * j + 1] = val[1];
}
for (j = 0; j < right; j++) {
data[tmp2 + 2 * j] = (j % 2 == 0) ? val[0] : val[2];
data[tmp2 + 2 * j + 1] = val[1];
}
data += stride;
}
} else {
data += stride * height;
}
for (i = 0; i < bottom; i++) {
for (j = 0; j < real_width; j++) {
data[2 * j] = (j % 2 == 0) ? val[0] : val[2];
data[2 * j + 1] = val[1];
}
data += stride;
}
}
void
vs_fill_borders_Y (const VSImage * dest, const uint8_t * val)
{
int i;
int top = dest->border_top, bottom = dest->border_bottom;
int left = dest->border_left, right = dest->border_right;
int width = dest->width;
int height = dest->height;
int real_width = dest->real_width;
int stride = dest->stride;
int tmp, tmp2;
uint8_t *data;
data = dest->real_pixels;
for (i = 0; i < top; i++) {
memset (data, *val, real_width);
data += stride;
}
if (left || right) {
tmp = height;
tmp2 = left + width;
for (i = 0; i < tmp; i++) {
memset (data, *val, left);
memset (data + tmp2, *val, right);
data += stride;
}
} else {
data += stride * height;
}
for (i = 0; i < bottom; i++) {
memset (data, *val, real_width);
data += stride;
}
}
void
vs_fill_borders_Y16 (const VSImage * dest, const uint16_t val)
{
int i;
int top = dest->border_top, bottom = dest->border_bottom;
int left = dest->border_left, right = dest->border_right;
int width = dest->width;
int height = dest->height;
int real_width = dest->real_width;
int stride = dest->stride;
int tmp, tmp2;
uint8_t *data;
data = dest->real_pixels;
for (i = 0; i < top; i++) {
orc_splat_u16 ((uint16_t *) data, val, real_width);
data += stride;
}
if (left || right) {
tmp = height;
tmp2 = (left + width) * 2;
for (i = 0; i < tmp; i++) {
orc_splat_u16 ((uint16_t *) data, val, left);
orc_splat_u16 ((uint16_t *) (data + tmp2), val, right);
data += stride;
}
} else {
data += stride * height;
}
for (i = 0; i < bottom; i++) {
orc_splat_u16 ((uint16_t *) data, val, real_width);
data += stride;
}
}
void
vs_fill_borders_RGB565 (const VSImage * dest, const uint8_t * val)
{
int i;
int top = dest->border_top, bottom = dest->border_bottom;
int left = dest->border_left, right = dest->border_right;
int width = dest->width;
int height = dest->height;
int real_width = dest->real_width;
int stride = dest->stride;
int tmp, tmp2;
uint8_t *data;
uint16_t v = READ_UINT16 (val);
data = dest->real_pixels;
for (i = 0; i < top; i++) {
orc_splat_u16 ((uint16_t *) data, v, real_width);
data += stride;
}
if (left || right) {
tmp = height;
tmp2 = (left + width) * 2;
for (i = 0; i < tmp; i++) {
orc_splat_u16 ((uint16_t *) data, v, left);
orc_splat_u16 ((uint16_t *) (data + tmp2), v, right);
data += stride;
}
} else {
data += stride * height;
}
for (i = 0; i < bottom; i++) {
orc_splat_u16 ((uint16_t *) data, v, real_width);
data += stride;
}
}
void
vs_fill_borders_RGB555 (const VSImage * dest, const uint8_t * val)
{
int i;
int top = dest->border_top, bottom = dest->border_bottom;
int left = dest->border_left, right = dest->border_right;
int width = dest->width;
int height = dest->height;
int real_width = dest->real_width;
int stride = dest->stride;
int tmp, tmp2;
uint8_t *data;
uint16_t v = READ_UINT16 (val);
data = dest->real_pixels;
for (i = 0; i < top; i++) {
orc_splat_u16 ((uint16_t *) data, v, real_width);
data += stride;
}
if (left || right) {
tmp = height;
tmp2 = (left + width) * 2;
for (i = 0; i < tmp; i++) {
orc_splat_u16 ((uint16_t *) data, v, left);
orc_splat_u16 ((uint16_t *) (data + tmp2), v, right);
data += stride;
}
} else {
data += stride * height;
}
for (i = 0; i < bottom; i++) {
orc_splat_u16 ((uint16_t *) data, v, real_width);
data += stride;
}
}

View file

@ -0,0 +1,43 @@
/*
* Image Scaling Functions
* Copyright (c) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __VS_FILL_BORDERS_H__
#define __VS_FILL_BORDERS_H__
#include <_stdint.h>
#include "vs_image.h"
void vs_fill_borders_RGBA (const VSImage *dest, const uint8_t *val);
void vs_fill_borders_RGB (const VSImage *dest, const uint8_t *val);
void vs_fill_borders_YUYV (const VSImage *dest, const uint8_t *val);
void vs_fill_borders_UYVY (const VSImage *dest, const uint8_t *val);
void vs_fill_borders_Y (const VSImage *dest, const uint8_t *val);
void vs_fill_borders_Y16 (const VSImage *dest, const uint16_t val);
void vs_fill_borders_RGB565 (const VSImage *dest, const uint8_t *val);
void vs_fill_borders_RGB555 (const VSImage *dest, const uint8_t *val);
#endif /* __VS_FILL_BORDERS_H__ */

View file

@ -33,6 +33,11 @@
typedef struct _VSImage VSImage;
struct _VSImage {
uint8_t *real_pixels;
int real_width;
int real_height;
int border_left, border_right;
int border_top, border_bottom;
uint8_t *pixels;
int width;
int height;