docs: remove old 0.10 Since markers
[platform/upstream/gstreamer.git] / ext / pango / gstbasetextoverlay.c
index 92076bb..be1cd6e 100644 (file)
@@ -18,8 +18,8 @@
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 /**
@@ -76,6 +76,7 @@
 #endif
 
 #include <gst/video/video.h>
+#include <gst/video/gstvideometa.h>
 
 #include "gstbasetextoverlay.h"
 #include "gsttextoverlay.h"
@@ -98,8 +99,6 @@ GST_DEBUG_CATEGORY (pango_debug);
 #define DEFAULT_PROP_SHADING   FALSE
 #define DEFAULT_PROP_VALIGNMENT        GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE
 #define DEFAULT_PROP_HALIGNMENT        GST_BASE_TEXT_OVERLAY_HALIGN_CENTER
-#define DEFAULT_PROP_VALIGN    "baseline"
-#define DEFAULT_PROP_HALIGN    "center"
 #define DEFAULT_PROP_XPAD      25
 #define DEFAULT_PROP_YPAD      25
 #define DEFAULT_PROP_DELTAX    0
@@ -122,55 +121,11 @@ GST_DEBUG_CATEGORY (pango_debug);
 #define MINIMUM_OUTLINE_OFFSET 1.0
 #define DEFAULT_SCALE_BASIS    640
 
-#define COMP_Y(ret, r, g, b) \
-{ \
-   ret = (int) (((19595 * r) >> 16) + ((38470 * g) >> 16) + ((7471 * b) >> 16)); \
-   ret = CLAMP (ret, 0, 255); \
-}
-
-#define COMP_U(ret, r, g, b) \
-{ \
-   ret = (int) (-((11059 * r) >> 16) - ((21709 * g) >> 16) + ((32768 * b) >> 16) + 128); \
-   ret = CLAMP (ret, 0, 255); \
-}
-
-#define COMP_V(ret, r, g, b) \
-{ \
-   ret = (int) (((32768 * r) >> 16) - ((27439 * g) >> 16) - ((5329 * b) >> 16) + 128); \
-   ret = CLAMP (ret, 0, 255); \
-}
-
-#define BLEND(ret, alpha, v0, v1) \
-{ \
-       ret = (v0 * alpha + v1 * (255 - alpha)) / 255; \
-}
-
-#define OVER(ret, alphaA, Ca, alphaB, Cb, alphaNew)    \
-{ \
-    gint _tmp; \
-    _tmp = (Ca * alphaA + Cb * alphaB * (255 - alphaA) / 255) / alphaNew; \
-    ret = CLAMP (_tmp, 0, 255); \
-}
-
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-# define CAIRO_ARGB_A 3
-# define CAIRO_ARGB_R 2
-# define CAIRO_ARGB_G 1
-# define CAIRO_ARGB_B 0
-#else
-# define CAIRO_ARGB_A 0
-# define CAIRO_ARGB_R 1
-# define CAIRO_ARGB_G 2
-# define CAIRO_ARGB_B 3
-#endif
-
 enum
 {
   PROP_0,
   PROP_TEXT,
   PROP_SHADING,
-  PROP_VALIGN,                  /* deprecated */
-  PROP_HALIGN,                  /* deprecated */
   PROP_HALIGNMENT,
   PROP_VALIGNMENT,
   PROP_XPAD,
@@ -192,9 +147,7 @@ enum
   PROP_LAST
 };
 
-#define VIDEO_FORMATS "{ BGRx, RGBx, xRGB, xBGR, RGBA, BGRA, ARGB, ABGR, RGB, BGR, \
-    I420, YV12, AYUV, YUY2, UYVY, v308, v210, v216, Y41B, Y42B, Y444, \
-    Y800, Y16, NV12, NV21, UYVP, A420, YUV9, IYU1 }"
+#define VIDEO_FORMATS GST_VIDEO_OVERLAY_COMPOSITION_BLEND_FORMATS
 
 static GstStaticPadTemplate src_template_factory =
 GST_STATIC_PAD_TEMPLATE ("src",
@@ -335,8 +288,9 @@ static gboolean gst_base_text_overlay_text_event (GstPad * pad,
 static GstFlowReturn gst_base_text_overlay_text_chain (GstPad * pad,
     GstObject * parent, GstBuffer * buffer);
 static GstPadLinkReturn gst_base_text_overlay_text_pad_link (GstPad * pad,
-    GstPad * peer);
-static void gst_base_text_overlay_text_pad_unlink (GstPad * pad);
+    GstObject * parent, GstPad * peer);
+static void gst_base_text_overlay_text_pad_unlink (GstPad * pad,
+    GstObject * parent);
 static void gst_base_text_overlay_pop_text (GstBaseTextOverlay * overlay);
 static void gst_base_text_overlay_update_render_mode (GstBaseTextOverlay *
     overlay);
@@ -445,14 +399,6 @@ gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
       g_param_spec_enum ("halignment", "horizontal alignment",
           "Horizontal alignment of the text", GST_TYPE_BASE_TEXT_OVERLAY_HALIGN,
           DEFAULT_PROP_HALIGNMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VALIGN,
-      g_param_spec_string ("valign", "vertical alignment",
-          "Vertical alignment of the text (deprecated; use valignment)",
-          DEFAULT_PROP_VALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
-  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HALIGN,
-      g_param_spec_string ("halign", "horizontal alignment",
-          "Horizontal alignment of the text (deprecated; use halignment)",
-          DEFAULT_PROP_HALIGN, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPAD,
       g_param_spec_int ("xpad", "horizontal paddding",
           "Horizontal paddding when using left/right alignment", 0, G_MAXINT,
@@ -471,24 +417,20 @@ gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
           "Shift Y position up or down. Unit is pixels.", G_MININT, G_MAXINT,
           DEFAULT_PROP_DELTAY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   /**
-   * GstBaseTextOverlay:xpos
+   * GstBaseTextOverlay:xpos:
    *
    * Horizontal position of the rendered text when using positioned alignment.
-   *
-   * Since: 0.10.31
-   **/
+   */
   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_XPOS,
       g_param_spec_double ("xpos", "horizontal position",
           "Horizontal position when using position alignment", 0, 1.0,
           DEFAULT_PROP_XPOS,
           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
   /**
-   * GstBaseTextOverlay:ypos
+   * GstBaseTextOverlay:ypos:
    *
    * Vertical position of the rendered text when using positioned alignment.
-   *
-   * Since: 0.10.31
-   **/
+   */
   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_YPOS,
       g_param_spec_double ("ypos", "vertical position",
           "Vertical position when using position alignment", 0, 1.0,
@@ -506,24 +448,20 @@ gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
           "for syntax.", DEFAULT_PROP_FONT_DESC,
           G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
   /**
-   * GstBaseTextOverlay:color
+   * GstBaseTextOverlay:color:
    *
    * Color of the rendered text.
-   *
-   * Since: 0.10.31
-   **/
+   */
   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COLOR,
       g_param_spec_uint ("color", "Color",
           "Color to use for text (big-endian ARGB).", 0, G_MAXUINT32,
           DEFAULT_PROP_COLOR,
           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
   /**
-   * GstTextOverlay:outline-color
+   * GstTextOverlay:outline-color:
    *
    * Color of the outline of the rendered text.
-   *
-   * Since: 0.10.35
-   **/
+   */
   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_OUTLINE_COLOR,
       g_param_spec_uint ("outline-color", "Text Outline Color",
           "Color to use for outline the text (big-endian ARGB).", 0,
@@ -531,25 +469,21 @@ gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
 
   /**
-   * GstBaseTextOverlay:line-alignment
+   * GstBaseTextOverlay:line-alignment:
    *
    * Alignment of text lines relative to each other (for multi-line text)
-   *
-   * Since: 0.10.15
-   **/
+   */
   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LINE_ALIGNMENT,
       g_param_spec_enum ("line-alignment", "line alignment",
           "Alignment of text lines relative to each other.",
           GST_TYPE_BASE_TEXT_OVERLAY_LINE_ALIGN, DEFAULT_PROP_LINE_ALIGNMENT,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   /**
-   * GstBaseTextOverlay:silent
+   * GstBaseTextOverlay:silent:
    *
    * If set, no text is rendered. Useful to switch off text rendering
    * temporarily without removing the textoverlay element from the pipeline.
-   *
-   * Since: 0.10.15
-   **/
+   */
   /* FIXME 0.11: rename to "visible" or "text-visible" or "render-text" */
   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT,
       g_param_spec_boolean ("silent", "silent",
@@ -557,14 +491,12 @@ gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass)
           DEFAULT_PROP_SILENT,
           G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
   /**
-   * GstBaseTextOverlay:wait-text
+   * GstBaseTextOverlay:wait-text:
    *
    * If set, the video will block until a subtitle is received on the text pad.
    * If video and subtitles are sent in sync, like from the same demuxer, this
    * property should be set.
-   *
-   * Since: 0.10.20
-   **/
+   */
   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WAIT_TEXT,
       g_param_spec_boolean ("wait-text", "Wait Text",
           "Whether to wait for subtitles",
@@ -632,6 +564,7 @@ gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
       GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_chain));
   gst_pad_set_query_function (overlay->video_sinkpad,
       GST_DEBUG_FUNCPTR (gst_base_text_overlay_video_query));
+  GST_PAD_SET_PROXY_ALLOCATION (overlay->video_sinkpad);
   gst_element_add_pad (GST_ELEMENT (overlay), overlay->video_sinkpad);
 
   template =
@@ -640,7 +573,6 @@ gst_base_text_overlay_init (GstBaseTextOverlay * overlay,
   if (template) {
     /* text sink */
     overlay->text_sinkpad = gst_pad_new_from_template (template, "text_sink");
-    gst_object_unref (template);
 
     gst_pad_set_event_function (overlay->text_sinkpad,
         GST_DEBUG_FUNCPTR (gst_base_text_overlay_text_event));
@@ -756,10 +688,11 @@ static gboolean
 gst_base_text_overlay_setcaps_txt (GstBaseTextOverlay * overlay, GstCaps * caps)
 {
   GstStructure *structure;
+  const gchar *format;
 
   structure = gst_caps_get_structure (caps, 0);
-  overlay->have_pango_markup =
-      gst_structure_has_name (structure, "text/x-pango-markup");
+  format = gst_structure_get_string (structure, "format");
+  overlay->have_pango_markup = (strcmp (format, "pango-markup") == 0);
 
   return TRUE;
 }
@@ -789,8 +722,8 @@ gst_base_text_overlay_negotiate (GstBaseTextOverlay * overlay)
     GST_DEBUG_OBJECT (overlay, "ALLOCATION query failed");
   }
 
-  if (gst_query_has_allocation_meta (query,
-          GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE))
+  if (gst_query_find_allocation_meta (query,
+          GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL))
     attach = TRUE;
 
   overlay->attach_compo_to_buffer = attach;
@@ -877,34 +810,6 @@ gst_base_text_overlay_set_property (GObject * object, guint prop_id,
     case PROP_YPOS:
       overlay->ypos = g_value_get_double (value);
       break;
-    case PROP_HALIGN:{
-      const gchar *s = g_value_get_string (value);
-
-      if (s && g_ascii_strcasecmp (s, "left") == 0)
-        overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_LEFT;
-      else if (s && g_ascii_strcasecmp (s, "center") == 0)
-        overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_CENTER;
-      else if (s && g_ascii_strcasecmp (s, "right") == 0)
-        overlay->halign = GST_BASE_TEXT_OVERLAY_HALIGN_RIGHT;
-      else
-        g_warning ("Invalid value '%s' for textoverlay property 'halign'",
-            GST_STR_NULL (s));
-      break;
-    }
-    case PROP_VALIGN:{
-      const gchar *s = g_value_get_string (value);
-
-      if (s && g_ascii_strcasecmp (s, "baseline") == 0)
-        overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BASELINE;
-      else if (s && g_ascii_strcasecmp (s, "bottom") == 0)
-        overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_BOTTOM;
-      else if (s && g_ascii_strcasecmp (s, "top") == 0)
-        overlay->valign = GST_BASE_TEXT_OVERLAY_VALIGN_TOP;
-      else
-        g_warning ("Invalid value '%s' for textoverlay property 'valign'",
-            GST_STR_NULL (s));
-      break;
-    }
     case PROP_VALIGNMENT:
       overlay->valign = g_value_get_enum (value);
       break;
@@ -1069,7 +974,7 @@ gst_base_text_overlay_src_query (GstPad * pad, GstObject * parent,
       break;
     }
     default:
-      ret = gst_pad_peer_query (overlay->video_sinkpad, query);
+      ret = gst_pad_query_default (pad, parent, query);
       break;
   }
 
@@ -1272,8 +1177,10 @@ gst_base_text_overlay_set_composition (GstBaseTextOverlay * overlay)
   gst_base_text_overlay_get_pos (overlay, &xpos, &ypos);
 
   if (overlay->text_image) {
-    rectangle = gst_video_overlay_rectangle_new_argb (overlay->text_image,
-        overlay->image_width, overlay->image_height, 4 * overlay->image_width,
+    gst_buffer_add_video_meta (overlay->text_image, GST_VIDEO_FRAME_FLAG_NONE,
+        GST_VIDEO_OVERLAY_COMPOSITION_FORMAT_RGB,
+        overlay->image_width, overlay->image_height);
+    rectangle = gst_video_overlay_rectangle_new_raw (overlay->text_image,
         xpos, ypos, overlay->image_width, overlay->image_height,
         GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA);
 
@@ -1288,6 +1195,16 @@ gst_base_text_overlay_set_composition (GstBaseTextOverlay * overlay)
   }
 }
 
+static gboolean
+gst_text_overlay_filter_foreground_attr (PangoAttribute * attr, gpointer data)
+{
+  if (attr->klass->type == PANGO_ATTR_FOREGROUND) {
+    return FALSE;
+  } else {
+    return TRUE;
+  }
+}
+
 static void
 gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
     const gchar * string, gint textlen)
@@ -1402,11 +1319,30 @@ gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
    */
 
   /* draw shadow text */
-  cairo_save (cr);
-  cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
-  cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
-  pango_cairo_show_layout (cr, overlay->layout);
-  cairo_restore (cr);
+  {
+    PangoAttrList *origin_attr, *filtered_attr, *temp_attr;
+
+    /* Store a ref on the original attributes for later restoration */
+    origin_attr =
+        pango_attr_list_ref (pango_layout_get_attributes (overlay->layout));
+    /* Take a copy of the original attributes, because pango_attr_list_filter
+     * modifies the passed list */
+    temp_attr = pango_attr_list_copy (origin_attr);
+    filtered_attr =
+        pango_attr_list_filter (temp_attr,
+        gst_text_overlay_filter_foreground_attr, NULL);
+    pango_attr_list_unref (temp_attr);
+
+    cairo_save (cr);
+    cairo_translate (cr, overlay->shadow_offset, overlay->shadow_offset);
+    cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
+    pango_layout_set_attributes (overlay->layout, filtered_attr);
+    pango_cairo_show_layout (cr, overlay->layout);
+    pango_layout_set_attributes (overlay->layout, origin_attr);
+    pango_attr_list_unref (filtered_attr);
+    pango_attr_list_unref (origin_attr);
+    cairo_restore (cr);
+  }
 
   a = (overlay->outline_color >> 24) & 0xff;
   r = (overlay->outline_color >> 16) & 0xff;
@@ -1443,9 +1379,6 @@ gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay,
   gst_base_text_overlay_set_composition (overlay);
 }
 
-#define BOX_XPAD         6
-#define BOX_YPAD         6
-
 static inline void
 gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
     GstVideoFrame * dest, gint x0, gint x1, gint y0, gint y1)
@@ -1456,12 +1389,6 @@ gst_base_text_overlay_shade_planar_Y (GstBaseTextOverlay * overlay,
   dest_stride = dest->info.stride[0];
   dest_ptr = dest->data[0];
 
-  x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
-  x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
-
-  y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
-  y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
-
   for (i = y0; i < y1; ++i) {
     for (j = x0; j < x1; ++j) {
       gint y = dest_ptr[(i * dest_stride) + j] + overlay->shading_value;
@@ -1483,12 +1410,6 @@ gst_base_text_overlay_shade_packed_Y (GstBaseTextOverlay * overlay,
   dest_ptr = GST_VIDEO_FRAME_COMP_DATA (dest, 0);
   pixel_stride = GST_VIDEO_FRAME_COMP_PSTRIDE (dest, 0);
 
-  x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
-  x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
-
-  y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
-  y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
-
   if (x0 != 0)
     x0 = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (dest->info.finfo, 0, x0);
   if (x1 != 0)
@@ -1524,12 +1445,6 @@ gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay,
 
   dest_ptr = dest->data[0];
 
-  x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
-  x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
-
-  y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
-  y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
-
   for (i = y0; i < y1; i++) {
     for (j = x0; j < x1; j++) {
       gint y, y_pos, k;
@@ -1543,6 +1458,60 @@ gst_base_text_overlay_shade_xRGB (GstBaseTextOverlay * overlay,
   }
 }
 
+/* FIXME: orcify */
+static void
+gst_base_text_overlay_shade_rgb24 (GstBaseTextOverlay * overlay,
+    GstVideoFrame * frame, gint x0, gint x1, gint y0, gint y1)
+{
+  const int pstride = 3;
+  gint y, x, stride, shading_val, tmp;
+  guint8 *p;
+
+  shading_val = overlay->shading_value;
+  stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
+
+  for (y = y0; y < y1; ++y) {
+    p = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
+    p += (y * stride) + (x0 * pstride);
+    for (x = x0; x < x1; ++x) {
+      tmp = *p + shading_val;
+      *p++ = CLAMP (tmp, 0, 255);
+      tmp = *p + shading_val;
+      *p++ = CLAMP (tmp, 0, 255);
+      tmp = *p + shading_val;
+      *p++ = CLAMP (tmp, 0, 255);
+    }
+  }
+}
+
+static void
+gst_base_text_overlay_shade_IYU1 (GstBaseTextOverlay * overlay,
+    GstVideoFrame * frame, gint x0, gint x1, gint y0, gint y1)
+{
+  gint y, x, stride, shading_val, tmp;
+  guint8 *p;
+
+  shading_val = overlay->shading_value;
+  stride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
+
+  /* IYU1: packed 4:1:1 YUV (Cb-Y0-Y1-Cr-Y2-Y3 ...) */
+  for (y = y0; y < y1; ++y) {
+    p = GST_VIDEO_FRAME_PLANE_DATA (frame, 0);
+    /* move to Y0 or Y1 (we pretend the chroma is the last of the 3 bytes) */
+    /* FIXME: we're not pixel-exact here if x0 is an odd number, but it's
+     * unlikely anyone will notice.. */
+    p += (y * stride) + ((x0 / 2) * 3) + 1;
+    for (x = x0; x < x1; x += 2) {
+      tmp = *p + shading_val;
+      *p++ = CLAMP (tmp, 0, 255);
+      tmp = *p + shading_val;
+      *p++ = CLAMP (tmp, 0, 255);
+      /* skip chroma */
+      p++;
+    }
+  }
+}
+
 #define ARGB_SHADE_FUNCTION(name, OFFSET)      \
 static inline void \
 gst_base_text_overlay_shade_##name (GstBaseTextOverlay * overlay, GstVideoFrame * dest, \
@@ -1553,12 +1522,6 @@ gint x0, gint x1, gint y0, gint y1) \
   \
   dest_ptr = dest->data[0];\
   \
-  x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);\
-  x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);\
-  \
-  y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);\
-  y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);\
-  \
   for (i = y0; i < y1; i++) {\
     for (j = x0; j < x1; j++) {\
       gint y, y_pos, k;\
@@ -1609,11 +1572,82 @@ gst_base_text_overlay_render_text (GstBaseTextOverlay * overlay,
   overlay->need_render = FALSE;
 }
 
+/* FIXME: should probably be relative to width/height (adjusted for PAR) */
+#define BOX_XPAD  6
+#define BOX_YPAD  6
+
+static void
+gst_base_text_overlay_shade_background (GstBaseTextOverlay * overlay,
+    GstVideoFrame * frame, gint x0, gint x1, gint y0, gint y1)
+{
+  x0 = CLAMP (x0 - BOX_XPAD, 0, overlay->width);
+  x1 = CLAMP (x1 + BOX_XPAD, 0, overlay->width);
+
+  y0 = CLAMP (y0 - BOX_YPAD, 0, overlay->height);
+  y1 = CLAMP (y1 + BOX_YPAD, 0, overlay->height);
+
+  switch (overlay->format) {
+    case GST_VIDEO_FORMAT_I420:
+    case GST_VIDEO_FORMAT_YV12:
+    case GST_VIDEO_FORMAT_NV12:
+    case GST_VIDEO_FORMAT_NV21:
+    case GST_VIDEO_FORMAT_Y41B:
+    case GST_VIDEO_FORMAT_Y42B:
+    case GST_VIDEO_FORMAT_Y444:
+    case GST_VIDEO_FORMAT_YUV9:
+    case GST_VIDEO_FORMAT_YVU9:
+    case GST_VIDEO_FORMAT_GRAY8:
+    case GST_VIDEO_FORMAT_A420:
+      gst_base_text_overlay_shade_planar_Y (overlay, frame, x0, x1, y0, y1);
+      break;
+    case GST_VIDEO_FORMAT_AYUV:
+    case GST_VIDEO_FORMAT_UYVY:
+    case GST_VIDEO_FORMAT_YUY2:
+    case GST_VIDEO_FORMAT_v308:
+      gst_base_text_overlay_shade_packed_Y (overlay, frame, x0, x1, y0, y1);
+      break;
+    case GST_VIDEO_FORMAT_xRGB:
+      gst_base_text_overlay_shade_xRGB (overlay, frame, x0, x1, y0, y1);
+      break;
+    case GST_VIDEO_FORMAT_xBGR:
+      gst_base_text_overlay_shade_xBGR (overlay, frame, x0, x1, y0, y1);
+      break;
+    case GST_VIDEO_FORMAT_BGRx:
+      gst_base_text_overlay_shade_BGRx (overlay, frame, x0, x1, y0, y1);
+      break;
+    case GST_VIDEO_FORMAT_RGBx:
+      gst_base_text_overlay_shade_RGBx (overlay, frame, x0, x1, y0, y1);
+      break;
+    case GST_VIDEO_FORMAT_ARGB:
+      gst_base_text_overlay_shade_ARGB (overlay, frame, x0, x1, y0, y1);
+      break;
+    case GST_VIDEO_FORMAT_ABGR:
+      gst_base_text_overlay_shade_ABGR (overlay, frame, x0, x1, y0, y1);
+      break;
+    case GST_VIDEO_FORMAT_RGBA:
+      gst_base_text_overlay_shade_RGBA (overlay, frame, x0, x1, y0, y1);
+      break;
+    case GST_VIDEO_FORMAT_BGRA:
+      gst_base_text_overlay_shade_BGRA (overlay, frame, x0, x1, y0, y1);
+      break;
+    case GST_VIDEO_FORMAT_BGR:
+    case GST_VIDEO_FORMAT_RGB:
+      gst_base_text_overlay_shade_rgb24 (overlay, frame, x0, x1, y0, y1);
+      break;
+    case GST_VIDEO_FORMAT_IYU1:
+      gst_base_text_overlay_shade_IYU1 (overlay, frame, x0, x1, y0, y1);
+      break;
+    default:
+      GST_FIXME_OBJECT (overlay, "implement background shading for format %s",
+          gst_video_format_to_string (GST_VIDEO_FRAME_FORMAT (frame)));
+      break;
+  }
+}
+
 static GstFlowReturn
 gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
     GstBuffer * video_frame)
 {
-  gint xpos, ypos;
   GstVideoFrame frame;
 
   if (overlay->composition == NULL)
@@ -1636,67 +1670,14 @@ gst_base_text_overlay_push_frame (GstBaseTextOverlay * overlay,
           GST_MAP_READWRITE))
     goto invalid_frame;
 
-  gst_base_text_overlay_get_pos (overlay, &xpos, &ypos);
-
   /* shaded background box */
   if (overlay->want_shading) {
-    switch (overlay->format) {
-      case GST_VIDEO_FORMAT_I420:
-      case GST_VIDEO_FORMAT_NV12:
-      case GST_VIDEO_FORMAT_NV21:
-        gst_base_text_overlay_shade_planar_Y (overlay, &frame,
-            xpos, xpos + overlay->image_width,
-            ypos, ypos + overlay->image_height);
-        break;
-      case GST_VIDEO_FORMAT_AYUV:
-      case GST_VIDEO_FORMAT_UYVY:
-        gst_base_text_overlay_shade_packed_Y (overlay, &frame,
-            xpos, xpos + overlay->image_width,
-            ypos, ypos + overlay->image_height);
-        break;
-      case GST_VIDEO_FORMAT_xRGB:
-        gst_base_text_overlay_shade_xRGB (overlay, &frame,
-            xpos, xpos + overlay->image_width,
-            ypos, ypos + overlay->image_height);
-        break;
-      case GST_VIDEO_FORMAT_xBGR:
-        gst_base_text_overlay_shade_xBGR (overlay, &frame,
-            xpos, xpos + overlay->image_width,
-            ypos, ypos + overlay->image_height);
-        break;
-      case GST_VIDEO_FORMAT_BGRx:
-        gst_base_text_overlay_shade_BGRx (overlay, &frame,
-            xpos, xpos + overlay->image_width,
-            ypos, ypos + overlay->image_height);
-        break;
-      case GST_VIDEO_FORMAT_RGBx:
-        gst_base_text_overlay_shade_RGBx (overlay, &frame,
-            xpos, xpos + overlay->image_width,
-            ypos, ypos + overlay->image_height);
-        break;
-      case GST_VIDEO_FORMAT_ARGB:
-        gst_base_text_overlay_shade_ARGB (overlay, &frame,
-            xpos, xpos + overlay->image_width,
-            ypos, ypos + overlay->image_height);
-        break;
-      case GST_VIDEO_FORMAT_ABGR:
-        gst_base_text_overlay_shade_ABGR (overlay, &frame,
-            xpos, xpos + overlay->image_width,
-            ypos, ypos + overlay->image_height);
-        break;
-      case GST_VIDEO_FORMAT_RGBA:
-        gst_base_text_overlay_shade_RGBA (overlay, &frame,
-            xpos, xpos + overlay->image_width,
-            ypos, ypos + overlay->image_height);
-        break;
-      case GST_VIDEO_FORMAT_BGRA:
-        gst_base_text_overlay_shade_BGRA (overlay, &frame,
-            xpos, xpos + overlay->image_width,
-            ypos, ypos + overlay->image_height);
-        break;
-      default:
-        g_assert_not_reached ();
-    }
+    gint xpos, ypos;
+
+    gst_base_text_overlay_get_pos (overlay, &xpos, &ypos);
+
+    gst_base_text_overlay_shade_background (overlay, &frame,
+        xpos, xpos + overlay->image_width, ypos, ypos + overlay->image_height);
   }
 
   gst_video_overlay_composition_blend (overlay->composition, &frame);
@@ -1717,11 +1698,12 @@ invalid_frame:
 }
 
 static GstPadLinkReturn
-gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
+gst_base_text_overlay_text_pad_link (GstPad * pad, GstObject * parent,
+    GstPad * peer)
 {
   GstBaseTextOverlay *overlay;
 
-  overlay = GST_BASE_TEXT_OVERLAY (gst_pad_get_parent (pad));
+  overlay = GST_BASE_TEXT_OVERLAY (parent);
   if (G_UNLIKELY (!overlay))
     return GST_PAD_LINK_REFUSED;
 
@@ -1729,18 +1711,16 @@ gst_base_text_overlay_text_pad_link (GstPad * pad, GstPad * peer)
 
   overlay->text_linked = TRUE;
 
-  gst_object_unref (overlay);
-
   return GST_PAD_LINK_OK;
 }
 
 static void
-gst_base_text_overlay_text_pad_unlink (GstPad * pad)
+gst_base_text_overlay_text_pad_unlink (GstPad * pad, GstObject * parent)
 {
   GstBaseTextOverlay *overlay;
 
   /* don't use gst_pad_get_parent() here, will deadlock */
-  overlay = GST_BASE_TEXT_OVERLAY (GST_PAD_PARENT (pad));
+  overlay = GST_BASE_TEXT_OVERLAY (parent);
 
   GST_DEBUG_OBJECT (overlay, "Text pad unlinked");
 
@@ -1799,6 +1779,24 @@ gst_base_text_overlay_text_event (GstPad * pad, GstObject * parent,
       GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
       break;
     }
+    case GST_EVENT_GAP:
+    {
+      GstClockTime start, duration;
+
+      gst_event_parse_gap (event, &start, &duration);
+      if (GST_CLOCK_TIME_IS_VALID (duration))
+        start += duration;
+      /* we do not expect another buffer until after gap,
+       * so that is our position now */
+      overlay->text_segment.position = start;
+
+      /* wake up the video chain, it might be waiting for a text buffer or
+       * a text segment update */
+      GST_BASE_TEXT_OVERLAY_LOCK (overlay);
+      GST_BASE_TEXT_OVERLAY_BROADCAST (overlay);
+      GST_BASE_TEXT_OVERLAY_UNLOCK (overlay);
+      break;
+    }
     case GST_EVENT_FLUSH_STOP:
       GST_BASE_TEXT_OVERLAY_LOCK (overlay);
       GST_INFO_OBJECT (overlay, "text flush stop");
@@ -2229,40 +2227,45 @@ wait_for_text_buf:
         in_text = (gchar *) map.data;
         in_size = map.size;
 
-        /* g_markup_escape_text() absolutely requires valid UTF8 input, it
-         * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
-         * here on purpose, this is something that needs fixing upstream */
-        if (!g_utf8_validate (in_text, in_size, NULL)) {
-          const gchar *end = NULL;
-
-          GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
-          in_text = g_strndup (in_text, in_size);
-          while (!g_utf8_validate (in_text, in_size, &end) && end)
-            *((gchar *) end) = '*';
-        }
-
-        /* Get the string */
-        if (overlay->have_pango_markup) {
-          text = g_strndup (in_text, in_size);
-        } else {
-          text = g_markup_escape_text (in_text, in_size);
-        }
+        if (in_size > 0) {
+          /* g_markup_escape_text() absolutely requires valid UTF8 input, it
+           * might crash otherwise. We don't fall back on GST_SUBTITLE_ENCODING
+           * here on purpose, this is something that needs fixing upstream */
+          if (!g_utf8_validate (in_text, in_size, NULL)) {
+            const gchar *end = NULL;
+
+            GST_WARNING_OBJECT (overlay, "received invalid UTF-8");
+            in_text = g_strndup (in_text, in_size);
+            while (!g_utf8_validate (in_text, in_size, &end) && end)
+              *((gchar *) end) = '*';
+          }
 
-        if (text != NULL && *text != '\0') {
-          gint text_len = strlen (text);
+          /* Get the string */
+          if (overlay->have_pango_markup) {
+            text = g_strndup (in_text, in_size);
+          } else {
+            text = g_markup_escape_text (in_text, in_size);
+          }
 
-          while (text_len > 0 && (text[text_len - 1] == '\n' ||
-                  text[text_len - 1] == '\r')) {
-            --text_len;
+          if (text != NULL && *text != '\0') {
+            gint text_len = strlen (text);
+
+            while (text_len > 0 && (text[text_len - 1] == '\n' ||
+                    text[text_len - 1] == '\r')) {
+              --text_len;
+            }
+            GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
+            gst_base_text_overlay_render_text (overlay, text, text_len);
+          } else {
+            GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
+            gst_base_text_overlay_render_text (overlay, " ", 1);
           }
-          GST_DEBUG_OBJECT (overlay, "Rendering text '%*s'", text_len, text);
-          gst_base_text_overlay_render_text (overlay, text, text_len);
+          if (in_text != (gchar *) map.data)
+            g_free (in_text);
         } else {
           GST_DEBUG_OBJECT (overlay, "No text to render (empty buffer)");
           gst_base_text_overlay_render_text (overlay, " ", 1);
         }
-        if (in_text != (gchar *) map.data)
-          g_free (in_text);
 
         gst_buffer_unmap (overlay->text_buffer, &map);