From 15f0bd24617959a0623ad91816465f67f2261e0a Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Wed, 2 Aug 2023 00:54:34 +0900 Subject: [PATCH] dwrite: Move background-color and color-emoji options to effect object ... and simplify background rendering Part-of: --- .../sys/dwrite/gstdwrite-effect.cpp | 25 +- .../sys/dwrite/gstdwrite-effect.h | 9 +- .../sys/dwrite/gstdwrite-renderer.cpp | 230 +++++------------- .../sys/dwrite/gstdwrite-renderer.h | 4 - .../sys/dwrite/gstdwritebaseoverlay.cpp | 22 +- .../sys/dwrite/gstdwriteoverlayobject.cpp | 20 +- .../sys/dwrite/gstdwriteoverlayobject.h | 3 - 7 files changed, 103 insertions(+), 210 deletions(-) diff --git a/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.cpp b/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.cpp index fbc12fd703..ff88ab1b62 100644 --- a/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.cpp +++ b/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.cpp @@ -90,9 +90,11 @@ IGstDWriteTextEffect::Clone (IGstDWriteTextEffect ** effect) if (!copy) return E_OUTOFMEMORY; - for (UINT i = 0; i <= GST_DWRITE_BRUSH_SHADOW; i++) + for (UINT i = 0; i < GST_DWRITE_BRUSH_LAST; i++) copy->brush_[i] = this->brush_[i]; + copy->enable_color_font_ = enable_color_font_; + *effect = copy; return S_OK; @@ -127,9 +129,28 @@ IGstDWriteTextEffect::SetBrushColor (GST_DWRITE_BRUSH_TARGET target, return S_OK; } +STDMETHODIMP +IGstDWriteTextEffect::SetEnableColorFont (BOOL enable) +{ + enable_color_font_ = enable; + + return S_OK; +} + +STDMETHODIMP +IGstDWriteTextEffect::GetEnableColorFont (BOOL * enable) +{ + if (!enable) + return E_INVALIDARG; + + *enable = enable_color_font_ ; + + return S_OK; +} + IGstDWriteTextEffect::IGstDWriteTextEffect (void) { - for (UINT32 i = 0; i < GST_DWRITE_BRUSH_SHADOW; i++) + for (UINT32 i = 0; i < GST_DWRITE_BRUSH_LAST; i++) brush_[i] = D2D1::ColorF (D2D1::ColorF::Black); /* Disable custom shadow effects by default */ diff --git a/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.h b/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.h index cff4831461..65fbd9230b 100644 --- a/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.h +++ b/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-effect.h @@ -29,6 +29,8 @@ enum GST_DWRITE_BRUSH_TARGET GST_DWRITE_BRUSH_UNDERLINE, GST_DWRITE_BRUSH_STRIKETHROUGH, GST_DWRITE_BRUSH_SHADOW, + GST_DWRITE_BRUSH_BACKGROUND, + GST_DWRITE_BRUSH_LAST }; DEFINE_GUID (IID_IGstDWriteTextEffect, 0x23c579ae, 0x2e18, @@ -53,11 +55,16 @@ public: STDMETHODIMP SetBrushColor (GST_DWRITE_BRUSH_TARGET target, const D2D1_COLOR_F * color); + STDMETHODIMP SetEnableColorFont (BOOL enable); + + STDMETHODIMP GetEnableColorFont (BOOL * enable); + private: IGstDWriteTextEffect (void); virtual ~IGstDWriteTextEffect (void); private: ULONG ref_count_ = 1; - D2D1_COLOR_F brush_[GST_DWRITE_BRUSH_SHADOW + 1]; + D2D1_COLOR_F brush_[GST_DWRITE_BRUSH_LAST]; + BOOL enable_color_font_ = FALSE; }; diff --git a/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.cpp b/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.cpp index 9c99f273b3..545ce027eb 100644 --- a/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.cpp +++ b/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.cpp @@ -31,89 +31,21 @@ GST_DEBUG_CATEGORY_EXTERN (gst_dwrite_debug); /* *INDENT-OFF* */ using namespace Microsoft::WRL; +enum class RenderPath +{ + BACKGROUND, + TEXT, +}; + struct RenderContext { - gboolean collect_geometry; - std::vector> backgrounds; - D2D1_RECT_F background_padding = D2D1::RectF (); + RenderPath render_path; ID2D1Factory *factory; ID2D1RenderTarget *target; RECT client_rect; - D2D1_SIZE_F scale; - gboolean enable_color_font; }; /* *INDENT-ON* */ -static HRESULT -CombineTwoGeometries (ID2D1Factory * factory, ID2D1Geometry * a, - ID2D1Geometry * b, ID2D1Geometry ** result) -{ - HRESULT hr; - ComPtr < ID2D1GeometrySink > sink; - ComPtr < ID2D1PathGeometry > geometry; - - hr = factory->CreatePathGeometry (&geometry); - if (FAILED (hr)) { - GST_WARNING ("Couldn't create path geometry, 0x%x", (guint) hr); - return hr; - } - - hr = geometry->Open (&sink); - if (FAILED (hr)) { - GST_WARNING ("Couldn't open path geometry, 0x%x", (guint) hr); - return hr; - } - - hr = a->CombineWithGeometry (b, - D2D1_COMBINE_MODE_UNION, nullptr, sink.Get ()); - if (FAILED (hr)) { - GST_WARNING ("Couldn't combine geometry, 0x%x", (guint) hr); - return hr; - } - - hr = sink->Close (); - if (FAILED (hr)) { - GST_WARNING ("Couldn't close sink, 0x%x", (guint) hr); - return hr; - } - - *result = geometry.Detach (); - - return S_OK; -} - -static void -CombineGeometries (ID2D1Factory * factory, - std::vector < ComPtr < ID2D1Geometry >> &geometries, - ID2D1Geometry ** result) -{ - ComPtr < ID2D1Geometry > combined; - HRESULT hr; - - if (geometries.empty ()) - return; - - /* *INDENT-OFF* */ - for (const auto & it: geometries) { - if (!combined) { - combined = it; - } else { - ComPtr tmp; - hr = CombineTwoGeometries (factory, it.Get (), combined.Get (), &tmp); - if (FAILED (hr)) - return; - - combined = tmp; - } - } - /* *INDENT-ON* */ - - if (!combined) - return; - - *result = combined.Detach (); -} - STDMETHODIMP IGstDWriteTextRenderer::CreateInstance (IDWriteFactory * factory, IGstDWriteTextRenderer ** renderer) @@ -227,6 +159,7 @@ STDMETHODIMP HRESULT hr; RECT client_rect; D2D1_COLOR_F fg_color = D2D1::ColorF (D2D1::ColorF::Black); + BOOL enable_color_font = FALSE; g_assert (context != nullptr); @@ -235,6 +168,43 @@ STDMETHODIMP target = render_ctx->target; factory = render_ctx->factory; + if (client_effect) + client_effect->QueryInterface (IID_IGstDWriteTextEffect, &effect); + + if (render_ctx->render_path == RenderPath::BACKGROUND) { + D2D1_COLOR_F color; + BOOL enabled; + DWRITE_FONT_METRICS font_metrics; + FLOAT run_width = 0; + FLOAT adjust, ascent, descent; + D2D1_RECT_F bg_rect; + ComPtr < ID2D1SolidColorBrush > bg_brush; + + if (!effect) + return S_OK; + + effect->GetBrushColor (GST_DWRITE_BRUSH_BACKGROUND, &color, &enabled); + if (!enabled) + return S_OK; + + for (UINT32 i = 0; i < glyph_run->glyphCount; i++) + run_width += glyph_run->glyphAdvances[i]; + + glyph_run->fontFace->GetMetrics (&font_metrics); + adjust = glyph_run->fontEmSize / font_metrics.designUnitsPerEm; + ascent = adjust * font_metrics.ascent; + descent = adjust * font_metrics.descent; + + bg_rect = D2D1::RectF (origin_x, origin_y - ascent, origin_x + run_width, + origin_y + descent); + + target->CreateSolidColorBrush (color, &bg_brush); + target->FillRectangle (bg_rect, bg_brush.Get ()); + target->DrawRectangle (bg_rect, bg_brush.Get ()); + + return S_OK; + } + hr = factory->CreatePathGeometry (&geometry); if (FAILED (hr)) return hr; @@ -254,60 +224,11 @@ STDMETHODIMP sink->Close (); hr = factory->CreateTransformedGeometry (geometry.Get (), - D2D1::Matrix3x2F::Translation (origin_x, origin_y) * - D2D1::Matrix3x2F::Scale (render_ctx->scale), &transformed); + D2D1::Matrix3x2F::Translation (origin_x, origin_y), &transformed); if (FAILED (hr)) return hr; - /* Create new path geometry from the bound rect. - * Note that rect geometry cannot be used since the combined background - * geometry might not be represented as a single rectangle */ - if (render_ctx->collect_geometry) { - D2D1_RECT_F bounds; - ComPtr < ID2D1RectangleGeometry > rect_geometry; - ComPtr < ID2D1PathGeometry > path_geometry; - ComPtr < ID2D1GeometrySink > path_sink; - - hr = transformed->GetBounds (nullptr, &bounds); - if (FAILED (hr)) - return hr; - - bounds.left += render_ctx->background_padding.left; - bounds.right += render_ctx->background_padding.right; - bounds.top += render_ctx->background_padding.top; - bounds.bottom += render_ctx->background_padding.bottom; - - bounds.left = MAX (bounds.left, (FLOAT) client_rect.left); - bounds.right = MIN (bounds.right, (FLOAT) client_rect.right); - bounds.top = MAX (bounds.top, (FLOAT) client_rect.top); - bounds.bottom = MIN (bounds.bottom, (FLOAT) client_rect.bottom); - - hr = factory->CreateRectangleGeometry (bounds, &rect_geometry); - if (FAILED (hr)) - return hr; - - hr = factory->CreatePathGeometry (&path_geometry); - if (FAILED (hr)) - return hr; - - hr = path_geometry->Open (&path_sink); - if (FAILED (hr)) - return hr; - - hr = rect_geometry->Outline (nullptr, path_sink.Get ()); - if (FAILED (hr)) - return hr; - - path_sink->Close (); - render_ctx->backgrounds.push_back (path_geometry); - - return S_OK; - } - - if (client_effect) - client_effect->QueryInterface (IID_IGstDWriteTextEffect, &effect); - if (effect) { D2D1_COLOR_F color; BOOL enabled; @@ -325,13 +246,15 @@ STDMETHODIMP effect->GetBrushColor (GST_DWRITE_BRUSH_SHADOW, &color, &enabled); if (enabled) target->CreateSolidColorBrush (color, &shadow_brush); + + effect->GetEnableColorFont (&enable_color_font); } else { target->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::Black), &brush); outline_brush = brush; } #ifdef HAVE_DWRITE_COLOR_FONT - if (render_ctx->enable_color_font) { + if (enable_color_font) { const DWRITE_GLYPH_IMAGE_FORMATS supported_formats = DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE | DWRITE_GLYPH_IMAGE_FORMATS_CFF | @@ -422,10 +345,10 @@ STDMETHODIMP #endif /* HAVE_DWRITE_COLOR_FONT */ if (shadow_brush) { - /* TODO: do we want to make this shadow configurable ? */ + FLOAT adjust = glyph_run->fontEmSize * 0.06; hr = factory->CreateTransformedGeometry (geometry.Get (), - D2D1::Matrix3x2F::Translation (origin_x + 1.5, origin_y + 1.5) * - D2D1::Matrix3x2F::Scale (render_ctx->scale), &shadow_transformed); + D2D1::Matrix3x2F::Translation (origin_x + adjust, origin_y + adjust), + &shadow_transformed); if (FAILED (hr)) return hr; @@ -459,7 +382,7 @@ STDMETHODIMP g_assert (context != nullptr); render_ctx = (RenderContext *) context; - if (render_ctx->collect_geometry) + if (render_ctx->render_path == RenderPath::BACKGROUND) return S_OK; target = render_ctx->target; @@ -474,8 +397,7 @@ STDMETHODIMP } hr = factory->CreateTransformedGeometry (geometry.Get (), - D2D1::Matrix3x2F::Translation (origin_x, origin_y) * - D2D1::Matrix3x2F::Scale (render_ctx->scale), &transformed); + D2D1::Matrix3x2F::Translation (origin_x, origin_y), &transformed); if (FAILED (hr)) { GST_WARNING ("Couldn't create transformed geometry, 0x%x", (guint) hr); return hr; @@ -518,7 +440,7 @@ STDMETHODIMP g_assert (context != nullptr); render_ctx = (RenderContext *) context; - if (render_ctx->collect_geometry) + if (render_ctx->render_path == RenderPath::BACKGROUND) return S_OK; target = render_ctx->target; @@ -534,8 +456,7 @@ STDMETHODIMP } hr = factory->CreateTransformedGeometry (geometry.Get (), - D2D1::Matrix3x2F::Translation (origin_x, origin_y) * - D2D1::Matrix3x2F::Scale (render_ctx->scale), &transformed); + D2D1::Matrix3x2F::Translation (origin_x, origin_y), &transformed); if (FAILED (hr)) { GST_WARNING ("Couldn't create transformed geometry, 0x%x", (guint) hr); @@ -582,11 +503,8 @@ IGstDWriteTextRenderer::~IGstDWriteTextRenderer (void) STDMETHODIMP IGstDWriteTextRenderer::Draw (const D2D1_POINT_2F & origin, - const D2D1_SIZE_F & scale, const RECT & client_rect, - const D2D1_COLOR_F & background_color, - const D2D1_RECT_F & background_padding, - gboolean enable_color_font, - IDWriteTextLayout * layout, ID2D1RenderTarget * target) + const RECT & client_rect, IDWriteTextLayout * layout, + ID2D1RenderTarget * target) { HRESULT hr; RenderContext context; @@ -596,40 +514,14 @@ STDMETHODIMP g_return_val_if_fail (target != nullptr, E_INVALIDARG); target->GetFactory (&d2d_factory); + context.render_path = RenderPath::BACKGROUND; context.client_rect = client_rect; context.target = target; context.factory = d2d_factory.Get (); - context.scale = scale; - context.enable_color_font = enable_color_font; - if (IGstDWriteTextEffect::IsEnabledColor (background_color)) { - ComPtr < ID2D1Geometry > geometry; - - context.collect_geometry = TRUE; - context.background_padding = background_padding; - - hr = layout->Draw (&context, this, origin.x, origin.y); - if (FAILED (hr)) { - GST_WARNING ("Couldn't draw layout for collecting geometry, 0x%x", - (guint) hr); - return hr; - } - - CombineGeometries (d2d_factory.Get (), context.backgrounds, &geometry); - if (geometry) { - ComPtr < ID2D1SolidColorBrush > brush; - hr = target->CreateSolidColorBrush (background_color, &brush); - if (FAILED (hr)) { - GST_WARNING ("Couldn't create solid brush, 0x%x", (guint) hr); - return hr; - } - - target->FillGeometry (geometry.Get (), brush.Get ()); - } - } - - context.collect_geometry = FALSE; + hr = layout->Draw (&context, this, origin.x, origin.y); + context.render_path = RenderPath::TEXT; hr = layout->Draw (&context, this, origin.x, origin.y); if (FAILED (hr)) { diff --git a/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.h b/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.h index caaa58e38c..a36587a228 100644 --- a/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.h +++ b/subprojects/gst-plugins-bad/sys/dwrite/gstdwrite-renderer.h @@ -74,11 +74,7 @@ public: /* Entry point for drawing text */ STDMETHODIMP Draw (const D2D1_POINT_2F & origin, - const D2D1_SIZE_F & scale, const RECT & client_rect, - const D2D1_COLOR_F & background_color, - const D2D1_RECT_F & background_padding, - gboolean enable_color_font, IDWriteTextLayout * layout, ID2D1RenderTarget * target); diff --git a/subprojects/gst-plugins-bad/sys/dwrite/gstdwritebaseoverlay.cpp b/subprojects/gst-plugins-bad/sys/dwrite/gstdwritebaseoverlay.cpp index 8e9335c4b6..441650e6a6 100644 --- a/subprojects/gst-plugins-bad/sys/dwrite/gstdwritebaseoverlay.cpp +++ b/subprojects/gst-plugins-bad/sys/dwrite/gstdwritebaseoverlay.cpp @@ -115,7 +115,6 @@ struct _GstDWriteBaseOverlayPrivate D2D_POINT_2F layout_origin; D2D_POINT_2F layout_size; - D2D1_RECT_F background_padding; std::wstring prev_text; std::wstring cur_text; @@ -771,7 +770,6 @@ gst_dwrite_base_overlay_update_text_format (GstDWriteBaseOverlay * self) { GstDWriteBaseOverlayPrivate *priv = self->priv; FLOAT font_size; - FLOAT background_padding; HRESULT hr; if (priv->text_format) @@ -785,12 +783,6 @@ gst_dwrite_base_overlay_update_text_format (GstDWriteBaseOverlay * self) font_size = priv->font_size; } - background_padding = (font_size / priv->font_size) * 5.0f; - priv->background_padding.left = priv->background_padding.top = - -background_padding; - priv->background_padding.right = priv->background_padding.bottom = - background_padding; - std::wstring wfont_family = gst_dwrite_string_to_wstring (priv->font_family); hr = priv->dwrite_factory->CreateTextFormat (wfont_family.c_str (), nullptr, @@ -863,6 +855,11 @@ gst_dwrite_base_overlay_create_layout (GstDWriteBaseOverlay * self, color = unpack_argb (priv->shadow_color); effect->SetBrushColor (GST_DWRITE_BRUSH_SHADOW, &color); + color = unpack_argb (priv->background_color); + effect->SetBrushColor (GST_DWRITE_BRUSH_BACKGROUND, &color); + + effect->SetEnableColorFont (priv->color_font); + hr = priv->layout->SetDrawingEffect (effect.Get (), range); if (FAILED (hr)) { GST_ERROR_OBJECT (self, "Couldn't set drawing effect"); @@ -881,8 +878,6 @@ gst_dwrite_base_overlay_transform (GstBaseTransform * trans, GstBuffer * inbuf, GstDWriteBaseOverlayPrivate *priv = self->priv; GstDWriteBaseOverlayClass *klass = GST_DWRITE_BASE_OVERLAY_GET_CLASS (self); std::lock_guard < std::mutex > lk (priv->prop_lock); - D2D1_COLOR_F bg_color; - D2D1_RECT_F bg_padding = D2D1::RectF (); if (!priv->overlay) { GST_ERROR_OBJECT (self, "Overlay object is not configured"); @@ -897,13 +892,8 @@ gst_dwrite_base_overlay_transform (GstBaseTransform * trans, GstBuffer * inbuf, return GST_FLOW_ERROR; } - bg_color = unpack_argb (priv->background_color); - if (priv->background_color) - bg_padding = priv->background_padding; - if (!gst_dwrite_overlay_object_draw (priv->overlay, outbuf, - priv->layout.Get (), bg_color, bg_padding, priv->color_font, - priv->layout_origin.x, priv->layout_origin.y)) { + priv->layout.Get (), priv->layout_origin.x, priv->layout_origin.y)) { GST_ERROR_OBJECT (self, "Draw failed"); return GST_FLOW_ERROR; } diff --git a/subprojects/gst-plugins-bad/sys/dwrite/gstdwriteoverlayobject.cpp b/subprojects/gst-plugins-bad/sys/dwrite/gstdwriteoverlayobject.cpp index 72153cd25b..832abc073e 100644 --- a/subprojects/gst-plugins-bad/sys/dwrite/gstdwriteoverlayobject.cpp +++ b/subprojects/gst-plugins-bad/sys/dwrite/gstdwriteoverlayobject.cpp @@ -1055,14 +1055,12 @@ gst_dwrite_overlay_object_get_target_from_bitmap (GstDWriteOverlayObject * self, static gboolean gst_dwrite_overlay_object_draw_layout (GstDWriteOverlayObject * self, - IDWriteTextLayout * layout, D2D1_COLOR_F background_color, - D2D1_RECT_F background_padding, gboolean enable_color_font, gint x, gint y) + IDWriteTextLayout * layout, gint x, gint y) { GstDWriteOverlayObjectPrivate *priv = self->priv; gint width, height; GstMemory *mem; ComPtr < ID2D1RenderTarget > target; - D2D1_POINT_2F origin; GstMapInfo info; if (priv->layout_buf) { @@ -1136,14 +1134,10 @@ gst_dwrite_overlay_object_draw_layout (GstDWriteOverlayObject * self, } } - origin.x = 0; - origin.y = 0; - target->BeginDraw (); target->Clear (D2D1::ColorF (D2D1::ColorF::Black, 0.0)); - priv->renderer->Draw (D2D1::Point2F (), D2D1::SizeF (1.0, 1.0), - D2D1::Rect (0, 0, width, height), background_color, - background_padding, enable_color_font, layout, target.Get ()); + priv->renderer->Draw (D2D1::Point2F (), + D2D1::Rect (0, 0, width, height), layout, target.Get ()); target->EndDraw (); if (!priv->use_bitmap) { @@ -1279,17 +1273,13 @@ error: gboolean gst_dwrite_overlay_object_draw (GstDWriteOverlayObject * object, - GstBuffer * buffer, IDWriteTextLayout * layout, - D2D1_COLOR_F background_color, D2D1_RECT_F background_padding, - gboolean enable_color_font, gint x, gint y) + GstBuffer * buffer, IDWriteTextLayout * layout, gint x, gint y) { GstDWriteOverlayObjectPrivate *priv = object->priv; gboolean ret = FALSE; - if (!gst_dwrite_overlay_object_draw_layout (object, layout, background_color, - background_padding, enable_color_font, x, y)) { + if (!gst_dwrite_overlay_object_draw_layout (object, layout, x, y)) return FALSE; - } switch (priv->blend_mode) { case GstDWriteBlendMode::ATTACH_TEXTURE: diff --git a/subprojects/gst-plugins-bad/sys/dwrite/gstdwriteoverlayobject.h b/subprojects/gst-plugins-bad/sys/dwrite/gstdwriteoverlayobject.h index b943e77e18..cc0078e166 100644 --- a/subprojects/gst-plugins-bad/sys/dwrite/gstdwriteoverlayobject.h +++ b/subprojects/gst-plugins-bad/sys/dwrite/gstdwriteoverlayobject.h @@ -103,9 +103,6 @@ GstFlowReturn gst_dwrite_overlay_object_prepare_output (GstDWriteOverlayObject * gboolean gst_dwrite_overlay_object_draw (GstDWriteOverlayObject * object, GstBuffer * buffer, IDWriteTextLayout * layout, - D2D1_COLOR_F background_color, - D2D1_RECT_F background_padding, - gboolean enable_color_font, gint x, gint y);