diff options
author | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2010-07-17 20:24:18 +0200 |
---|---|---|
committer | Sebastian Dröge <sebastian.droege@collabora.co.uk> | 2010-07-18 15:44:14 +0200 |
commit | 52e711b11d5aaca5e52390259b9c76fa7b816932 (patch) | |
tree | e09e1e140a96a3577024fc81bebbd456370fef95 | |
parent | 619e5b6e44469edbb345c7fd6386fce9982bab04 (diff) |
videoscale: Add support for adding black borders to keep the DAR if necessary
Fixes bug #617506.
-rw-r--r-- | gst/videoscale/Makefile.am | 7 | ||||
-rw-r--r-- | gst/videoscale/gstvideoscale.c | 533 | ||||
-rw-r--r-- | gst/videoscale/gstvideoscale.h | 7 | ||||
-rw-r--r-- | gst/videoscale/gstvideoscaleorc.orc | 12 | ||||
-rw-r--r-- | gst/videoscale/vs_fill_borders.c | 381 | ||||
-rw-r--r-- | gst/videoscale/vs_fill_borders.h | 43 | ||||
-rw-r--r-- | gst/videoscale/vs_image.h | 5 |
7 files changed, 797 insertions, 191 deletions
diff --git a/gst/videoscale/Makefile.am b/gst/videoscale/Makefile.am index 4299a6ff7..941eac0e2 100644 --- a/gst/videoscale/Makefile.am +++ b/gst/videoscale/Makefile.am @@ -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 diff --git a/gst/videoscale/gstvideoscale.c b/gst/videoscale/gstvideoscale.c index d7656a5b1..0a7971fd9 100644 --- a/gst/videoscale/gstvideoscale.c +++ b/gst/videoscale/gstvideoscale.c @@ -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); - - if (src.height < 4 && method == GST_VIDEO_SCALE_4TAP) - method = GST_VIDEO_SCALE_BILINEAR; - - 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: + 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 (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)); + } + + 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: - vs_image_scale_nearest_RGB (&dest, &src, videoscale->tmp_buf); + case GST_VIDEO_SCALE_BILINEAR: + vs_image_scale_linear_RGBA (&dest, &src, videoscale->tmp_buf); break; - case GST_VIDEO_FORMAT_YUY2: - case GST_VIDEO_FORMAT_YVYU: - vs_image_scale_nearest_YUYV (&dest, &src, videoscale->tmp_buf); + case GST_VIDEO_SCALE_4TAP: + vs_image_scale_4tap_RGBA (&dest, &src, videoscale->tmp_buf); break; - case GST_VIDEO_FORMAT_UYVY: - vs_image_scale_nearest_UYVY (&dest, &src, videoscale->tmp_buf); + 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_Y800: - case GST_VIDEO_FORMAT_GRAY8: - vs_image_scale_nearest_Y (&dest, &src, videoscale->tmp_buf); + case GST_VIDEO_SCALE_BILINEAR: + vs_image_scale_linear_RGB (&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_nearest_Y16 (&dest, &src, videoscale->tmp_buf); + case GST_VIDEO_SCALE_4TAP: + vs_image_scale_4tap_RGB (&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: - 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); + 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_RGB16: - vs_image_scale_nearest_RGB565 (&dest, &src, videoscale->tmp_buf); + case GST_VIDEO_SCALE_BILINEAR: + vs_image_scale_linear_YUYV (&dest, &src, videoscale->tmp_buf); break; - case GST_VIDEO_FORMAT_RGB15: - vs_image_scale_nearest_RGB555 (&dest, &src, videoscale->tmp_buf); + case GST_VIDEO_SCALE_4TAP: + vs_image_scale_4tap_YUYV (&dest, &src, videoscale->tmp_buf); break; default: - goto unsupported; + goto unknown_mode; } 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); + 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_UYVY: + case GST_VIDEO_SCALE_BILINEAR: 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); + case GST_VIDEO_SCALE_4TAP: + vs_image_scale_4tap_UYVY (&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); + 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_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); + case GST_VIDEO_SCALE_4TAP: + vs_image_scale_4tap_Y (&dest, &src, videoscale->tmp_buf); break; default: - goto unsupported; + goto unknown_mode; } 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); + 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_YUY2: - case GST_VIDEO_FORMAT_YVYU: - vs_image_scale_4tap_YUYV (&dest, &src, videoscale->tmp_buf); + case GST_VIDEO_SCALE_BILINEAR: + vs_image_scale_linear_Y16 (&dest, &src, videoscale->tmp_buf); break; - case GST_VIDEO_FORMAT_UYVY: - vs_image_scale_4tap_UYVY (&dest, &src, videoscale->tmp_buf); + case GST_VIDEO_SCALE_4TAP: + vs_image_scale_4tap_Y16 (&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); + 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_GRAY16_LE: - case GST_VIDEO_FORMAT_GRAY16_BE: - case GST_VIDEO_FORMAT_Y16: - vs_image_scale_4tap_Y16 (&dest, &src, videoscale->tmp_buf); + 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_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", diff --git a/gst/videoscale/gstvideoscale.h b/gst/videoscale/gstvideoscale.h index acebcfb1d..a09d76942 100644 --- a/gst/videoscale/gstvideoscale.h +++ b/gst/videoscale/gstvideoscale.h @@ -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; }; diff --git a/gst/videoscale/gstvideoscaleorc.orc b/gst/videoscale/gstvideoscaleorc.orc index 58ac1a81b..45434fcd8 100644 --- a/gst/videoscale/gstvideoscaleorc.orc +++ b/gst/videoscale/gstvideoscaleorc.orc @@ -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 + diff --git a/gst/videoscale/vs_fill_borders.c b/gst/videoscale/vs_fill_borders.c new file mode 100644 index 000000000..b796031dc --- /dev/null +++ b/gst/videoscale/vs_fill_borders.c @@ -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; + } +} diff --git a/gst/videoscale/vs_fill_borders.h b/gst/videoscale/vs_fill_borders.h new file mode 100644 index 000000000..84d0be32c --- /dev/null +++ b/gst/videoscale/vs_fill_borders.h @@ -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__ */ diff --git a/gst/videoscale/vs_image.h b/gst/videoscale/vs_image.h index 89a2e3d78..23f12805c 100644 --- a/gst/videoscale/vs_image.h +++ b/gst/videoscale/vs_image.h @@ -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; |