2023-05-27 16:59:23 +00:00
|
|
|
/* GStreamer
|
|
|
|
* Copyright (C) 2023 Seungha Yang <seungha@centricular.com>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* 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., 51 Franklin St, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "gstdwrite-renderer.h"
|
|
|
|
#include <wrl.h>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
GST_DEBUG_CATEGORY_EXTERN (gst_dwrite_debug);
|
|
|
|
#define GST_CAT_DEFAULT gst_dwrite_debug
|
|
|
|
|
|
|
|
/* *INDENT-OFF* */
|
|
|
|
using namespace Microsoft::WRL;
|
|
|
|
|
2023-08-01 15:54:34 +00:00
|
|
|
enum class RenderPath
|
|
|
|
{
|
|
|
|
BACKGROUND,
|
|
|
|
TEXT,
|
|
|
|
};
|
|
|
|
|
2023-05-27 16:59:23 +00:00
|
|
|
struct RenderContext
|
|
|
|
{
|
2023-08-01 15:54:34 +00:00
|
|
|
RenderPath render_path;
|
2023-05-27 16:59:23 +00:00
|
|
|
ID2D1Factory *factory;
|
|
|
|
ID2D1RenderTarget *target;
|
|
|
|
RECT client_rect;
|
2023-08-13 12:58:39 +00:00
|
|
|
std::vector<DWRITE_LINE_METRICS> line_metrics;
|
|
|
|
UINT line_index = 0;
|
|
|
|
UINT char_index = 0;
|
|
|
|
ComPtr<ID2D1Geometry> bg_rect;
|
|
|
|
D2D1_COLOR_F bg_color = D2D1::ColorF (D2D1::ColorF::Black, 0.0);
|
2023-05-27 16:59:23 +00:00
|
|
|
};
|
|
|
|
/* *INDENT-ON* */
|
|
|
|
|
2023-08-13 12:58:39 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-05-27 16:59:23 +00:00
|
|
|
STDMETHODIMP
|
|
|
|
IGstDWriteTextRenderer::CreateInstance (IDWriteFactory * factory,
|
|
|
|
IGstDWriteTextRenderer ** renderer)
|
|
|
|
{
|
|
|
|
IGstDWriteTextRenderer *self = new IGstDWriteTextRenderer ();
|
|
|
|
|
|
|
|
if (!self)
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
|
|
|
self->factory_ = factory;
|
|
|
|
factory->AddRef ();
|
|
|
|
*renderer = self;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* IUnknown */
|
|
|
|
STDMETHODIMP_ (ULONG)
|
|
|
|
IGstDWriteTextRenderer::AddRef (void)
|
|
|
|
{
|
|
|
|
return InterlockedIncrement (&ref_count_);
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP_ (ULONG)
|
|
|
|
IGstDWriteTextRenderer::Release (void)
|
|
|
|
{
|
|
|
|
ULONG ref_count;
|
|
|
|
|
|
|
|
ref_count = InterlockedDecrement (&ref_count_);
|
|
|
|
|
|
|
|
if (ref_count == 0)
|
|
|
|
delete this;
|
|
|
|
|
|
|
|
return ref_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
IGstDWriteTextRenderer::QueryInterface (REFIID riid, void **object)
|
|
|
|
{
|
|
|
|
if (riid == __uuidof (IUnknown)) {
|
|
|
|
*object = static_cast < IUnknown * >
|
|
|
|
(static_cast < IGstDWriteTextRenderer * >(this));
|
|
|
|
} else if (riid == __uuidof (IDWritePixelSnapping)) {
|
|
|
|
*object = static_cast < IDWritePixelSnapping * >
|
|
|
|
(static_cast < IGstDWriteTextRenderer * >(this));
|
|
|
|
} else if (riid == IID_IGstDWriteTextRenderer) {
|
|
|
|
*object = static_cast < IDWriteTextRenderer * >
|
|
|
|
(static_cast < IGstDWriteTextRenderer * >(this));
|
|
|
|
} else {
|
|
|
|
*object = nullptr;
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
AddRef ();
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* IDWritePixelSnapping */
|
|
|
|
STDMETHODIMP
|
|
|
|
IGstDWriteTextRenderer::IsPixelSnappingDisabled (void *context,
|
|
|
|
BOOL * is_disabled)
|
|
|
|
{
|
|
|
|
*is_disabled = FALSE;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
IGstDWriteTextRenderer::GetCurrentTransform (void *context,
|
|
|
|
DWRITE_MATRIX * transform)
|
|
|
|
{
|
|
|
|
RenderContext *render_ctx;
|
|
|
|
|
|
|
|
g_assert (context != nullptr);
|
|
|
|
|
|
|
|
render_ctx = (RenderContext *) context;
|
|
|
|
render_ctx->target->GetTransform (reinterpret_cast <
|
|
|
|
D2D1_MATRIX_3X2_F * >(transform));
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
IGstDWriteTextRenderer::GetPixelsPerDip (void *context,
|
|
|
|
FLOAT * pixels_per_dip)
|
|
|
|
{
|
|
|
|
*pixels_per_dip = 1.0f;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* IDWriteTextRenderer */
|
|
|
|
STDMETHODIMP
|
|
|
|
IGstDWriteTextRenderer::DrawGlyphRun (void *context, FLOAT origin_x,
|
|
|
|
FLOAT origin_y, DWRITE_MEASURING_MODE mode,
|
|
|
|
DWRITE_GLYPH_RUN const *glyph_run,
|
|
|
|
DWRITE_GLYPH_RUN_DESCRIPTION const *glyph_run_desc,
|
|
|
|
IUnknown * client_effect)
|
|
|
|
{
|
|
|
|
ComPtr < ID2D1PathGeometry > geometry;
|
|
|
|
ComPtr < ID2D1GeometrySink > sink;
|
|
|
|
ComPtr < ID2D1TransformedGeometry > transformed;
|
|
|
|
ComPtr < ID2D1TransformedGeometry > shadow_transformed;
|
|
|
|
ComPtr < IGstDWriteTextEffect > effect;
|
|
|
|
ComPtr < ID2D1SolidColorBrush > brush;
|
|
|
|
ComPtr < ID2D1SolidColorBrush > outline_brush;
|
|
|
|
ComPtr < ID2D1SolidColorBrush > shadow_brush;
|
|
|
|
RenderContext *render_ctx;
|
|
|
|
ID2D1RenderTarget *target;
|
|
|
|
ID2D1Factory *factory;
|
|
|
|
HRESULT hr;
|
|
|
|
RECT client_rect;
|
|
|
|
D2D1_COLOR_F fg_color = D2D1::ColorF (D2D1::ColorF::Black);
|
2023-08-01 15:54:34 +00:00
|
|
|
BOOL enable_color_font = FALSE;
|
2023-05-27 16:59:23 +00:00
|
|
|
|
|
|
|
g_assert (context != nullptr);
|
|
|
|
|
|
|
|
render_ctx = (RenderContext *) context;
|
|
|
|
client_rect = render_ctx->client_rect;
|
|
|
|
target = render_ctx->target;
|
|
|
|
factory = render_ctx->factory;
|
|
|
|
|
2023-08-01 15:54:34 +00:00
|
|
|
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;
|
2023-08-13 12:58:39 +00:00
|
|
|
DWRITE_LINE_METRICS line_metrics =
|
|
|
|
render_ctx->line_metrics.at (render_ctx->line_index);
|
|
|
|
|
|
|
|
if (effect &&
|
|
|
|
render_ctx->char_index + line_metrics.trailingWhitespaceLength <
|
|
|
|
line_metrics.length) {
|
|
|
|
effect->GetBrushColor (GST_DWRITE_BRUSH_BACKGROUND, &color, &enabled);
|
|
|
|
if (enabled) {
|
|
|
|
ComPtr < ID2D1RectangleGeometry > rect_geometry;
|
|
|
|
ComPtr < ID2D1PathGeometry > path_geometry;
|
|
|
|
ComPtr < ID2D1GeometrySink > path_sink;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
hr = factory->CreateRectangleGeometry (bg_rect, &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 ();
|
|
|
|
|
|
|
|
if (render_ctx->bg_rect) {
|
|
|
|
if (render_ctx->bg_color.r == color.r &&
|
|
|
|
render_ctx->bg_color.g == color.g &&
|
|
|
|
render_ctx->bg_color.b == color.b &&
|
|
|
|
render_ctx->bg_color.a == color.a) {
|
|
|
|
ComPtr < ID2D1Geometry > combined;
|
|
|
|
hr = CombineTwoGeometries (factory,
|
|
|
|
render_ctx->bg_rect.Get (), path_geometry.Get (), &combined);
|
|
|
|
if (FAILED (hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
render_ctx->bg_rect = combined;
|
|
|
|
} else {
|
|
|
|
target->CreateSolidColorBrush (render_ctx->bg_color, &bg_brush);
|
|
|
|
target->FillGeometry (render_ctx->bg_rect.Get (), bg_brush.Get ());
|
|
|
|
render_ctx->bg_rect = nullptr;
|
|
|
|
}
|
|
|
|
}
|
2023-08-01 15:54:34 +00:00
|
|
|
|
2023-08-13 12:58:39 +00:00
|
|
|
if (!render_ctx->bg_rect) {
|
|
|
|
render_ctx->bg_rect = path_geometry;
|
|
|
|
render_ctx->bg_color = color;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-08-01 15:54:34 +00:00
|
|
|
|
2023-08-13 12:58:39 +00:00
|
|
|
render_ctx->char_index += glyph_run_desc->stringLength;
|
|
|
|
if (render_ctx->char_index >= line_metrics.length) {
|
|
|
|
render_ctx->line_index++;
|
|
|
|
render_ctx->char_index = 0;
|
|
|
|
}
|
2023-08-01 15:54:34 +00:00
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2023-05-27 16:59:23 +00:00
|
|
|
hr = factory->CreatePathGeometry (&geometry);
|
|
|
|
if (FAILED (hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
hr = geometry->Open (&sink);
|
|
|
|
if (FAILED (hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
hr = glyph_run->fontFace->GetGlyphRunOutline (glyph_run->fontEmSize,
|
|
|
|
glyph_run->glyphIndices, glyph_run->glyphAdvances,
|
|
|
|
glyph_run->glyphOffsets, glyph_run->glyphCount, glyph_run->isSideways,
|
|
|
|
glyph_run->bidiLevel % 2, sink.Get ());
|
|
|
|
|
|
|
|
if (FAILED (hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
sink->Close ();
|
|
|
|
|
|
|
|
hr = factory->CreateTransformedGeometry (geometry.Get (),
|
2023-08-01 15:54:34 +00:00
|
|
|
D2D1::Matrix3x2F::Translation (origin_x, origin_y), &transformed);
|
2023-05-27 16:59:23 +00:00
|
|
|
|
|
|
|
if (FAILED (hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
if (effect) {
|
|
|
|
D2D1_COLOR_F color;
|
|
|
|
BOOL enabled;
|
|
|
|
|
2023-07-25 10:57:27 +00:00
|
|
|
effect->GetBrushColor (GST_DWRITE_BRUSH_FORGROUND, &color, &enabled);
|
2023-05-27 16:59:23 +00:00
|
|
|
if (enabled) {
|
|
|
|
target->CreateSolidColorBrush (color, &brush);
|
|
|
|
fg_color = color;
|
|
|
|
}
|
|
|
|
|
2023-07-25 10:57:27 +00:00
|
|
|
effect->GetBrushColor (GST_DWRITE_BRUSH_OUTLINE, &color, &enabled);
|
2023-05-27 16:59:23 +00:00
|
|
|
if (enabled)
|
|
|
|
target->CreateSolidColorBrush (color, &outline_brush);
|
|
|
|
|
|
|
|
effect->GetBrushColor (GST_DWRITE_BRUSH_SHADOW, &color, &enabled);
|
|
|
|
if (enabled)
|
|
|
|
target->CreateSolidColorBrush (color, &shadow_brush);
|
2023-08-01 15:54:34 +00:00
|
|
|
|
|
|
|
effect->GetEnableColorFont (&enable_color_font);
|
2023-05-27 16:59:23 +00:00
|
|
|
} else {
|
|
|
|
target->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::Black), &brush);
|
|
|
|
outline_brush = brush;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_DWRITE_COLOR_FONT
|
2023-08-01 15:54:34 +00:00
|
|
|
if (enable_color_font) {
|
2023-05-27 16:59:23 +00:00
|
|
|
const DWRITE_GLYPH_IMAGE_FORMATS supported_formats =
|
|
|
|
DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE |
|
|
|
|
DWRITE_GLYPH_IMAGE_FORMATS_CFF |
|
|
|
|
DWRITE_GLYPH_IMAGE_FORMATS_COLR |
|
|
|
|
DWRITE_GLYPH_IMAGE_FORMATS_SVG |
|
|
|
|
DWRITE_GLYPH_IMAGE_FORMATS_PNG |
|
|
|
|
DWRITE_GLYPH_IMAGE_FORMATS_JPEG |
|
|
|
|
DWRITE_GLYPH_IMAGE_FORMATS_TIFF |
|
|
|
|
DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8;
|
|
|
|
|
|
|
|
ComPtr < IDWriteColorGlyphRunEnumerator1 > glyph_run_enum;
|
|
|
|
ComPtr < IDWriteFactory4 > factory4;
|
|
|
|
ComPtr < ID2D1DeviceContext4 > ctx4;
|
|
|
|
hr = factory_->QueryInterface (IID_PPV_ARGS (&factory4));
|
|
|
|
if (SUCCEEDED (hr))
|
|
|
|
hr = target->QueryInterface (IID_PPV_ARGS (&ctx4));
|
|
|
|
|
|
|
|
if (SUCCEEDED (hr)) {
|
|
|
|
hr = factory4->TranslateColorGlyphRun (D2D1::Point2 (origin_x, origin_y),
|
|
|
|
glyph_run, glyph_run_desc, supported_formats,
|
|
|
|
DWRITE_MEASURING_MODE_NATURAL, nullptr, 0, &glyph_run_enum);
|
|
|
|
if (hr != DWRITE_E_NOCOLOR && SUCCEEDED (hr)) {
|
|
|
|
ComPtr < ID2D1SolidColorBrush > tmp_brush;
|
|
|
|
BOOL has_run = FALSE;
|
|
|
|
do {
|
|
|
|
DWRITE_COLOR_GLYPH_RUN1 const *color_run;
|
|
|
|
hr = glyph_run_enum->MoveNext (&has_run);
|
|
|
|
|
|
|
|
if (FAILED (hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
if (!has_run)
|
|
|
|
return S_OK;
|
|
|
|
|
|
|
|
hr = glyph_run_enum->GetCurrentRun (&color_run);
|
|
|
|
if (FAILED (hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
const auto cur_origin = D2D1::Point2F (color_run->baselineOriginX,
|
|
|
|
color_run->baselineOriginY);
|
|
|
|
switch (color_run->glyphImageFormat) {
|
|
|
|
case DWRITE_GLYPH_IMAGE_FORMATS_PNG:
|
|
|
|
case DWRITE_GLYPH_IMAGE_FORMATS_JPEG:
|
|
|
|
case DWRITE_GLYPH_IMAGE_FORMATS_TIFF:
|
|
|
|
case DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8:
|
|
|
|
ctx4->DrawColorBitmapGlyphRun (color_run->glyphImageFormat,
|
|
|
|
cur_origin, &color_run->glyphRun,
|
|
|
|
DWRITE_MEASURING_MODE_NATURAL);
|
|
|
|
break;
|
|
|
|
case DWRITE_GLYPH_IMAGE_FORMATS_SVG:
|
|
|
|
{
|
|
|
|
ComPtr < ID2D1SolidColorBrush > svg_brush;
|
|
|
|
if (brush)
|
|
|
|
svg_brush = brush;
|
|
|
|
else if (outline_brush)
|
|
|
|
svg_brush = outline_brush;
|
|
|
|
|
|
|
|
ctx4->DrawSvgGlyphRun (cur_origin, &color_run->glyphRun,
|
|
|
|
svg_brush.Get ());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE:
|
|
|
|
case DWRITE_GLYPH_IMAGE_FORMATS_CFF:
|
|
|
|
case DWRITE_GLYPH_IMAGE_FORMATS_COLR:
|
|
|
|
default:
|
|
|
|
if (!tmp_brush) {
|
|
|
|
hr = target->CreateSolidColorBrush (fg_color, &tmp_brush);
|
|
|
|
if (FAILED (hr))
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (color_run->paletteIndex == 0xffff)
|
|
|
|
tmp_brush->SetColor (fg_color);
|
|
|
|
else
|
|
|
|
tmp_brush->SetColor (color_run->runColor);
|
|
|
|
|
|
|
|
target->DrawGlyphRun (cur_origin, &color_run->glyphRun,
|
|
|
|
tmp_brush.Get ());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (has_run && SUCCEEDED (hr));
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* HAVE_DWRITE_COLOR_FONT */
|
|
|
|
|
|
|
|
if (shadow_brush) {
|
2023-08-01 15:54:34 +00:00
|
|
|
FLOAT adjust = glyph_run->fontEmSize * 0.06;
|
2023-05-27 16:59:23 +00:00
|
|
|
hr = factory->CreateTransformedGeometry (geometry.Get (),
|
2023-08-01 15:54:34 +00:00
|
|
|
D2D1::Matrix3x2F::Translation (origin_x + adjust, origin_y + adjust),
|
|
|
|
&shadow_transformed);
|
2023-05-27 16:59:23 +00:00
|
|
|
|
|
|
|
if (FAILED (hr))
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shadow_brush)
|
|
|
|
target->FillGeometry (shadow_transformed.Get (), shadow_brush.Get ());
|
|
|
|
|
|
|
|
if (outline_brush)
|
|
|
|
target->DrawGeometry (transformed.Get (), outline_brush.Get ());
|
|
|
|
|
|
|
|
if (brush)
|
|
|
|
target->FillGeometry (transformed.Get (), brush.Get ());
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
IGstDWriteTextRenderer::DrawUnderline (void *context, FLOAT origin_x,
|
|
|
|
FLOAT origin_y, DWRITE_UNDERLINE const *underline, IUnknown * client_effect)
|
|
|
|
{
|
|
|
|
ComPtr < ID2D1RectangleGeometry > geometry;
|
|
|
|
ComPtr < ID2D1TransformedGeometry > transformed;
|
|
|
|
ComPtr < IGstDWriteTextEffect > effect;
|
|
|
|
ComPtr < ID2D1SolidColorBrush > brush;
|
|
|
|
RenderContext *render_ctx;
|
|
|
|
ID2D1RenderTarget *target;
|
|
|
|
ID2D1Factory *factory;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
g_assert (context != nullptr);
|
|
|
|
|
|
|
|
render_ctx = (RenderContext *) context;
|
2023-08-01 15:54:34 +00:00
|
|
|
if (render_ctx->render_path == RenderPath::BACKGROUND)
|
2023-05-27 16:59:23 +00:00
|
|
|
return S_OK;
|
|
|
|
|
|
|
|
target = render_ctx->target;
|
|
|
|
factory = render_ctx->factory;
|
|
|
|
|
|
|
|
hr = factory->CreateRectangleGeometry (D2D1::RectF (0, underline->offset,
|
|
|
|
underline->width, underline->offset + underline->thickness),
|
|
|
|
&geometry);
|
|
|
|
if (FAILED (hr)) {
|
|
|
|
GST_WARNING ("Couldn't create geometry, 0x%x", (guint) hr);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = factory->CreateTransformedGeometry (geometry.Get (),
|
2023-08-01 15:54:34 +00:00
|
|
|
D2D1::Matrix3x2F::Translation (origin_x, origin_y), &transformed);
|
2023-05-27 16:59:23 +00:00
|
|
|
if (FAILED (hr)) {
|
|
|
|
GST_WARNING ("Couldn't create transformed geometry, 0x%x", (guint) hr);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (client_effect)
|
|
|
|
client_effect->QueryInterface (IID_IGstDWriteTextEffect, &effect);
|
|
|
|
|
|
|
|
if (effect) {
|
|
|
|
D2D1_COLOR_F color;
|
|
|
|
BOOL enabled;
|
|
|
|
|
|
|
|
effect->GetBrushColor (GST_DWRITE_BRUSH_UNDERLINE, &color, &enabled);
|
|
|
|
if (enabled)
|
|
|
|
target->CreateSolidColorBrush (color, &brush);
|
|
|
|
} else {
|
|
|
|
target->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::Black), &brush);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (brush)
|
|
|
|
target->FillGeometry (transformed.Get (), brush.Get ());
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
IGstDWriteTextRenderer::DrawStrikethrough (void *context, FLOAT origin_x,
|
|
|
|
FLOAT origin_y, DWRITE_STRIKETHROUGH const *strikethrough,
|
|
|
|
IUnknown * client_effect)
|
|
|
|
{
|
|
|
|
ComPtr < ID2D1RectangleGeometry > geometry;
|
|
|
|
ComPtr < ID2D1TransformedGeometry > transformed;
|
|
|
|
ComPtr < IGstDWriteTextEffect > effect;
|
|
|
|
ComPtr < ID2D1SolidColorBrush > brush;
|
|
|
|
RenderContext *render_ctx;
|
|
|
|
ID2D1RenderTarget *target;
|
|
|
|
ID2D1Factory *factory;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
g_assert (context != nullptr);
|
|
|
|
|
|
|
|
render_ctx = (RenderContext *) context;
|
2023-08-01 15:54:34 +00:00
|
|
|
if (render_ctx->render_path == RenderPath::BACKGROUND)
|
2023-05-27 16:59:23 +00:00
|
|
|
return S_OK;
|
|
|
|
|
|
|
|
target = render_ctx->target;
|
|
|
|
factory = render_ctx->factory;
|
|
|
|
|
|
|
|
hr = factory->CreateRectangleGeometry (D2D1::RectF (0,
|
|
|
|
strikethrough->offset, strikethrough->width,
|
|
|
|
strikethrough->offset + strikethrough->thickness), &geometry);
|
|
|
|
|
|
|
|
if (FAILED (hr)) {
|
|
|
|
GST_WARNING ("Couldn't create geometry, 0x%x", (guint) hr);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
hr = factory->CreateTransformedGeometry (geometry.Get (),
|
2023-08-01 15:54:34 +00:00
|
|
|
D2D1::Matrix3x2F::Translation (origin_x, origin_y), &transformed);
|
2023-05-27 16:59:23 +00:00
|
|
|
|
|
|
|
if (FAILED (hr)) {
|
|
|
|
GST_WARNING ("Couldn't create transformed geometry, 0x%x", (guint) hr);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (client_effect)
|
|
|
|
client_effect->QueryInterface (IID_IGstDWriteTextEffect, &effect);
|
|
|
|
|
|
|
|
if (effect) {
|
|
|
|
D2D1_COLOR_F color;
|
|
|
|
BOOL enabled;
|
|
|
|
|
|
|
|
effect->GetBrushColor (GST_DWRITE_BRUSH_STRIKETHROUGH, &color, &enabled);
|
|
|
|
if (enabled)
|
|
|
|
target->CreateSolidColorBrush (color, &brush);
|
|
|
|
} else {
|
|
|
|
target->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF::Black), &brush);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (brush)
|
|
|
|
target->FillGeometry (transformed.Get (), brush.Get ());
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
IGstDWriteTextRenderer::DrawInlineObject (void *context, FLOAT origin_x,
|
|
|
|
FLOAT origin_y, IDWriteInlineObject * inline_object, BOOL is_sideways,
|
|
|
|
BOOL is_right_to_left, IUnknown * client_effect)
|
|
|
|
{
|
|
|
|
GST_WARNING ("Not implemented");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
IGstDWriteTextRenderer::IGstDWriteTextRenderer (void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
IGstDWriteTextRenderer::~IGstDWriteTextRenderer (void)
|
|
|
|
{
|
|
|
|
factory_->Release ();
|
|
|
|
}
|
|
|
|
|
|
|
|
STDMETHODIMP
|
|
|
|
IGstDWriteTextRenderer::Draw (const D2D1_POINT_2F & origin,
|
2023-08-01 15:54:34 +00:00
|
|
|
const RECT & client_rect, IDWriteTextLayout * layout,
|
|
|
|
ID2D1RenderTarget * target)
|
2023-05-27 16:59:23 +00:00
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
RenderContext context;
|
|
|
|
ComPtr < ID2D1Factory > d2d_factory;
|
2023-08-13 12:58:39 +00:00
|
|
|
UINT32 num_lines = 0;
|
2023-05-27 16:59:23 +00:00
|
|
|
|
|
|
|
g_return_val_if_fail (layout != nullptr, E_INVALIDARG);
|
|
|
|
g_return_val_if_fail (target != nullptr, E_INVALIDARG);
|
|
|
|
|
2023-08-13 12:58:39 +00:00
|
|
|
hr = layout->GetLineMetrics (nullptr, 0, &num_lines);
|
|
|
|
if (hr != E_NOT_SUFFICIENT_BUFFER)
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
context.line_metrics.resize (num_lines);
|
|
|
|
hr = layout->GetLineMetrics (&context.line_metrics[0],
|
|
|
|
context.line_metrics.size (), &num_lines);
|
|
|
|
if (FAILED (hr))
|
|
|
|
return hr;
|
|
|
|
|
2023-05-27 16:59:23 +00:00
|
|
|
target->GetFactory (&d2d_factory);
|
2023-08-01 15:54:34 +00:00
|
|
|
context.render_path = RenderPath::BACKGROUND;
|
2023-05-27 16:59:23 +00:00
|
|
|
context.client_rect = client_rect;
|
|
|
|
context.target = target;
|
|
|
|
context.factory = d2d_factory.Get ();
|
|
|
|
|
2023-08-01 15:54:34 +00:00
|
|
|
hr = layout->Draw (&context, this, origin.x, origin.y);
|
2023-05-27 16:59:23 +00:00
|
|
|
|
2023-08-13 12:58:39 +00:00
|
|
|
if (FAILED (hr)) {
|
|
|
|
GST_WARNING ("Background Draw failed with 0x%x", (guint) hr);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (context.bg_rect) {
|
|
|
|
ComPtr < ID2D1SolidColorBrush > bg_brush;
|
|
|
|
target->CreateSolidColorBrush (context.bg_color, &bg_brush);
|
|
|
|
target->FillGeometry (context.bg_rect.Get (), bg_brush.Get ());
|
|
|
|
context.bg_rect = nullptr;
|
|
|
|
}
|
|
|
|
|
2023-08-01 15:54:34 +00:00
|
|
|
context.render_path = RenderPath::TEXT;
|
2023-05-27 16:59:23 +00:00
|
|
|
hr = layout->Draw (&context, this, origin.x, origin.y);
|
|
|
|
|
|
|
|
if (FAILED (hr)) {
|
|
|
|
GST_WARNING ("Draw failed with 0x%x", (guint) hr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|