summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Dröge <sebastian.droege@collabora.co.uk>2010-07-17 20:24:18 +0200
committerSebastian Dröge <sebastian.droege@collabora.co.uk>2010-07-18 15:44:14 +0200
commit52e711b11d5aaca5e52390259b9c76fa7b816932 (patch)
treee09e1e140a96a3577024fc81bebbd456370fef95
parent619e5b6e44469edbb345c7fd6386fce9982bab04 (diff)
videoscale: Add support for adding black borders to keep the DAR if necessary
Fixes bug #617506.
-rw-r--r--gst/videoscale/Makefile.am7
-rw-r--r--gst/videoscale/gstvideoscale.c533
-rw-r--r--gst/videoscale/gstvideoscale.h7
-rw-r--r--gst/videoscale/gstvideoscaleorc.orc12
-rw-r--r--gst/videoscale/vs_fill_borders.c381
-rw-r--r--gst/videoscale/vs_fill_borders.h43
-rw-r--r--gst/videoscale/vs_image.h5
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;