From 6d6a6bc3d8e73e27002fb4d31ff182190b1d7310 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Sun, 21 May 2017 12:34:08 +0200 Subject: [PATCH] textoverlay: optionally scale text to ensure proper display text aspect ratio ... by prescaling with an inverse aspect scaling as applied by video scaling --- ext/pango/gstbasetextoverlay.c | 150 +++++++++++++++++++++++++++++---- ext/pango/gstbasetextoverlay.h | 20 +++++ 2 files changed, 154 insertions(+), 16 deletions(-) diff --git a/ext/pango/gstbasetextoverlay.c b/ext/pango/gstbasetextoverlay.c index 44f5f529e3..58d458167b 100644 --- a/ext/pango/gstbasetextoverlay.c +++ b/ext/pango/gstbasetextoverlay.c @@ -63,6 +63,9 @@ GST_DEBUG_CATEGORY (pango_debug); #define DEFAULT_PROP_WAIT_TEXT TRUE #define DEFAULT_PROP_AUTO_ADJUST_SIZE TRUE #define DEFAULT_PROP_VERTICAL_RENDER FALSE +#define DEFAULT_PROP_SCALE_MODE GST_BASE_TEXT_OVERLAY_SCALE_MODE_NONE +#define DEFAULT_PROP_SCALE_PAR_N 1 +#define DEFAULT_PROP_SCALE_PAR_D 1 #define DEFAULT_PROP_DRAW_SHADOW TRUE #define DEFAULT_PROP_DRAW_OUTLINE TRUE #define DEFAULT_PROP_COLOR 0xffffffff @@ -99,6 +102,8 @@ enum PROP_WAIT_TEXT, PROP_AUTO_ADJUST_SIZE, PROP_VERTICAL_RENDER, + PROP_SCALE_MODE, + PROP_SCALE_PAR, PROP_COLOR, PROP_DRAW_SHADOW, PROP_DRAW_OUTLINE, @@ -223,6 +228,27 @@ gst_base_text_overlay_line_align_get_type (void) return base_text_overlay_line_align_type; } +#define GST_TYPE_BASE_TEXT_OVERLAY_SCALE_MODE (gst_base_text_overlay_scale_mode_get_type()) +static GType +gst_base_text_overlay_scale_mode_get_type (void) +{ + static GType base_text_overlay_scale_mode_type = 0; + static const GEnumValue base_text_overlay_scale_mode[] = { + {GST_BASE_TEXT_OVERLAY_SCALE_MODE_NONE, "none", "none"}, + {GST_BASE_TEXT_OVERLAY_SCALE_MODE_PAR, "par", "par"}, + {GST_BASE_TEXT_OVERLAY_SCALE_MODE_DISPLAY, "display", "display"}, + {GST_BASE_TEXT_OVERLAY_SCALE_MODE_USER, "user", "user"}, + {0, NULL, NULL} + }; + + if (!base_text_overlay_scale_mode_type) { + base_text_overlay_scale_mode_type = + g_enum_register_static ("GstBaseTextOverlayScaleMode", + base_text_overlay_scale_mode); + } + return base_text_overlay_scale_mode_type; +} + #define GST_BASE_TEXT_OVERLAY_GET_LOCK(ov) (&GST_BASE_TEXT_OVERLAY (ov)->lock) #define GST_BASE_TEXT_OVERLAY_GET_COND(ov) (&GST_BASE_TEXT_OVERLAY (ov)->cond) #define GST_BASE_TEXT_OVERLAY_LOCK(ov) (g_mutex_lock (GST_BASE_TEXT_OVERLAY_GET_LOCK (ov))) @@ -600,6 +626,33 @@ gst_base_text_overlay_class_init (GstBaseTextOverlayClass * klass) g_param_spec_boolean ("vertical-render", "vertical render", "Vertical Render.", DEFAULT_PROP_VERTICAL_RENDER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstBaseTextOverlay:scale-mode: + * + * Scale text to compensate for and avoid distortion by subsequent video scaling + * + * Since: 1.14 + */ + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SCALE_MODE, + g_param_spec_enum ("scale-mode", "scale mode", + "Scale text to compensate for and avoid distortion by subsequent video scaling.", + GST_TYPE_BASE_TEXT_OVERLAY_SCALE_MODE, DEFAULT_PROP_SCALE_MODE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * GstBaseTextOverlay:scale-pixel-aspect-ratio: + * + * Video scaling pixel-aspect-ratio to compensate for in user scale-mode. + * + * Since: 1.14 + */ + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SCALE_PAR, + gst_param_spec_fraction ("scale-pixel-aspect-ratio", + "scale pixel aspect ratio", + "Pixel aspect ratio of video scale to compensate for in user scale-mode", + 1, 100, 100, 1, DEFAULT_PROP_SCALE_PAR_N, DEFAULT_PROP_SCALE_PAR_D, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static void @@ -717,6 +770,9 @@ gst_base_text_overlay_init (GstBaseTextOverlay * overlay, overlay->need_render = TRUE; overlay->text_image = NULL; overlay->use_vertical_render = DEFAULT_PROP_VERTICAL_RENDER; + overlay->scale_mode = DEFAULT_PROP_SCALE_MODE; + overlay->scale_par_n = DEFAULT_PROP_SCALE_PAR_N; + overlay->scale_par_d = DEFAULT_PROP_SCALE_PAR_D; overlay->line_align = DEFAULT_PROP_LINE_ALIGNMENT; pango_layout_set_alignment (overlay->layout, @@ -1099,6 +1155,13 @@ gst_base_text_overlay_set_property (GObject * object, guint prop_id, g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock); } break; + case PROP_SCALE_MODE: + overlay->scale_mode = g_value_get_enum (value); + break; + case PROP_SCALE_PAR: + overlay->scale_par_n = gst_value_get_fraction_numerator (value); + overlay->scale_par_d = gst_value_get_fraction_denominator (value); + break; case PROP_SHADING_VALUE: overlay->shading_value = g_value_get_uint (value); break; @@ -1179,6 +1242,13 @@ gst_base_text_overlay_get_property (GObject * object, guint prop_id, case PROP_VERTICAL_RENDER: g_value_set_boolean (value, overlay->use_vertical_render); break; + case PROP_SCALE_MODE: + g_value_set_enum (value, overlay->scale_mode); + break; + case PROP_SCALE_PAR: + gst_value_set_fraction (value, overlay->scale_par_n, + overlay->scale_par_d); + break; case PROP_COLOR: g_value_set_uint (value, overlay->color); break; @@ -1677,7 +1747,7 @@ gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay, gint unscaled_width, unscaled_height; gint width, height; gboolean full_width = FALSE; - double scalef = 1.0; + double scalef_x = 1.0, scalef_y = 1.0; double a, r, g, b; gdouble shadow_offset = 0.0; gdouble outline_offset = 0.0; @@ -1689,7 +1759,44 @@ gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay, if (overlay->auto_adjust_size) { /* 640 pixel is default */ - scalef = (double) (overlay->width) / DEFAULT_SCALE_BASIS; + scalef_x = scalef_y = (double) (overlay->width) / DEFAULT_SCALE_BASIS; + } + + if (overlay->scale_mode != GST_BASE_TEXT_OVERLAY_SCALE_MODE_NONE) { + gint par_n = 1, par_d = 1; + + switch (overlay->scale_mode) { + case GST_BASE_TEXT_OVERLAY_SCALE_MODE_PAR: + par_n = overlay->info.par_n; + par_d = overlay->info.par_d; + break; + case GST_BASE_TEXT_OVERLAY_SCALE_MODE_DISPLAY: + /* (width * par_n) / (height * par_d) = (display_w / display_h) */ + gst_util_fraction_multiply (overlay->window_width, + overlay->window_height, overlay->height, overlay->width, + &par_n, &par_d); + break; + case GST_BASE_TEXT_OVERLAY_SCALE_MODE_USER: + par_n = overlay->scale_par_n; + par_d = overlay->scale_par_d; + break; + default: + break; + } + /* sanitize */ + if (!par_n || !par_d) + par_n = par_d = 1; + /* compensate later scaling as would be done for a par_n / par_d p-a-r; + * apply all scaling to y so as to allow for predictable text width + * layout independent of the presentation aspect scaling */ + if (overlay->use_vertical_render) { + scalef_y *= ((gdouble) par_d) / ((gdouble) par_n); + } else { + scalef_y *= ((gdouble) par_n) / ((gdouble) par_d); + } + GST_DEBUG_OBJECT (overlay, + "compensate scaling mode %d par %d/%d, scale %f, %f", + overlay->scale_mode, par_n, par_d, scalef_x, scalef_y); } if (overlay->draw_shadow) @@ -1716,7 +1823,7 @@ gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay, pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect); unscaled_width = ink_rect.width + shadow_offset + outline_offset; - width = ceil (unscaled_width * scalef); + width = ceil (unscaled_width * scalef_x); /* * subtitle image width can be larger then overlay width @@ -1733,26 +1840,26 @@ gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay, } if (full_width) { - unscaled_width = width / scalef; + unscaled_width = width / scalef_x; gst_base_text_overlay_set_wrap_mode (overlay, unscaled_width - shadow_offset - outline_offset); pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect); unscaled_width = ink_rect.width + shadow_offset + outline_offset; - width = ceil (unscaled_width * scalef); + width = ceil (unscaled_width * scalef_x); } unscaled_height = ink_rect.height + shadow_offset + outline_offset; - height = ceil (unscaled_height * scalef); + height = ceil (unscaled_height * scalef_y); if (overlay->use_vertical_render) { if (height + xpad > overlay->width) { height = overlay->width - xpad; - unscaled_height = width / scalef; + unscaled_height = width / scalef_y; } } else if (height + ypad > overlay->height) { height = overlay->height - ypad; - unscaled_height = height / scalef; + unscaled_height = height / scalef_y; } GST_DEBUG_OBJECT (overlay, "Rendering with ink rect (%d, %d) %dx%d and " @@ -1766,20 +1873,20 @@ gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay, /* Save and scale the rectangles so get_pos() can place the text */ overlay->ink_rect.x = - ceil ((ink_rect.x - ceil (outline_offset / 2.0l)) * scalef); + ceil ((ink_rect.x - ceil (outline_offset / 2.0l)) * scalef_x); overlay->ink_rect.y = - ceil ((ink_rect.y - ceil (outline_offset / 2.0l)) * scalef); + ceil ((ink_rect.y - ceil (outline_offset / 2.0l)) * scalef_y); overlay->ink_rect.width = width; overlay->ink_rect.height = height; overlay->logical_rect.x = - ceil ((logical_rect.x - ceil (outline_offset / 2.0l)) * scalef); + ceil ((logical_rect.x - ceil (outline_offset / 2.0l)) * scalef_x); overlay->logical_rect.y = - ceil ((logical_rect.y - ceil (outline_offset / 2.0l)) * scalef); + ceil ((logical_rect.y - ceil (outline_offset / 2.0l)) * scalef_y); overlay->logical_rect.width = - ceil ((logical_rect.width + shadow_offset + outline_offset) * scalef); + ceil ((logical_rect.width + shadow_offset + outline_offset) * scalef_x); overlay->logical_rect.height = - ceil ((logical_rect.height + shadow_offset + outline_offset) * scalef); + ceil ((logical_rect.height + shadow_offset + outline_offset) * scalef_y); /* flip the rectangle if doing vertical render */ if (overlay->use_vertical_render) { @@ -1803,7 +1910,8 @@ gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay, /* scale to reported window size */ width = ceil (width * overlay->render_scale); height = ceil (height * overlay->render_scale); - scalef *= overlay->render_scale; + scalef_x *= overlay->render_scale; + scalef_y *= overlay->render_scale; if (width <= 0 || height <= 0) { g_mutex_unlock (GST_BASE_TEXT_OVERLAY_GET_CLASS (overlay)->pango_lock); @@ -1821,7 +1929,17 @@ gst_base_text_overlay_render_pangocairo (GstBaseTextOverlay * overlay, /* Prepare the transformation matrix. Note that the transformation happens * in reverse order. So for horizontal text, we will translate and then * scale. This is important to understand which scale shall be used. */ - cairo_matrix_init_scale (&cairo_matrix, scalef, scalef); + /* So, as this init'ed scale happens last, when the rectangle has already + * been rotated, the scaling applied to text height (up to now), + * has to be applied along the x-axis */ + if (overlay->use_vertical_render) { + double tmp; + + tmp = scalef_x; + scalef_x = scalef_y; + scalef_y = tmp; + } + cairo_matrix_init_scale (&cairo_matrix, scalef_x, scalef_y); if (overlay->use_vertical_render) { gint tmp; diff --git a/ext/pango/gstbasetextoverlay.h b/ext/pango/gstbasetextoverlay.h index a078fb7c4c..4f009942b5 100644 --- a/ext/pango/gstbasetextoverlay.h +++ b/ext/pango/gstbasetextoverlay.h @@ -115,6 +115,23 @@ typedef enum { GST_BASE_TEXT_OVERLAY_LINE_ALIGN_RIGHT = PANGO_ALIGN_RIGHT } GstBaseTextOverlayLineAlign; +/** + * GstBaseTextOverlayScaleMode: + * @GST_BASE_TEXT_OVERLAY_SCALE_MODE_NONE: no compensation + * @GST_BASE_TEXT_OVERLAY_SCALE_MODE_PAR: compensate pixel-aspect-ratio scaling + * @GST_BASE_TEXT_OVERLAY_SCALE_MODE_DISPLAY: compensate for scaling to display (as determined by overlay allocation meta) + * @GST_BASE_TEXT_OVERLAY_SCALE_MODE_USER: compensate scaling set by #GstBaseTextOverlay:scale-pixel-aspect-ratio property + * + * Scale text to compensate for and avoid aspect distortion by subsequent + * scaling of video + */ +typedef enum { + GST_BASE_TEXT_OVERLAY_SCALE_MODE_NONE, + GST_BASE_TEXT_OVERLAY_SCALE_MODE_PAR, + GST_BASE_TEXT_OVERLAY_SCALE_MODE_DISPLAY, + GST_BASE_TEXT_OVERLAY_SCALE_MODE_USER +} GstBaseTextOverlayScaleMode; + /** * GstBaseTextOverlay: * @@ -170,6 +187,9 @@ struct _GstBaseTextOverlay { GstBaseTextOverlayHAlign halign; GstBaseTextOverlayWrapMode wrap_mode; GstBaseTextOverlayLineAlign line_align; + GstBaseTextOverlayScaleMode scale_mode; + gint scale_par_n; + gint scale_par_d; /* text pad format */ gboolean have_pango_markup;