gstreamer/subprojects/gst-plugins-bad/sys/dwrite/gstdwriterender_bitmap.cpp
Seungha Yang 15434ce51d dwrite: D3D12 integration
Adding d3d12 backend text renderer/blender by using d3d11on12 interop.
And subclassing renderer object per backend (i.e., d3d11, d3d12, and bitmap)

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6620>
2024-04-14 09:55:41 +00:00

269 lines
7.9 KiB
C++

/* GStreamer
* Copyright (C) 2024 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 "gstdwriterender_bitmap.h"
#include "gstdwritebitmappool.h"
#include "gstdwrite-renderer.h"
#include <wrl.h>
GST_DEBUG_CATEGORY_EXTERN (dwrite_overlay_object_debug);
#define GST_CAT_DEFAULT dwrite_overlay_object_debug
/* *INDENT-OFF* */
using namespace Microsoft::WRL;
struct GstDWriteBitmapRenderPrivate
{
~GstDWriteBitmapRenderPrivate ()
{
renderer = nullptr;
dwrite_factory = nullptr;
d2d_factory = nullptr;
if (layout_pool)
gst_buffer_pool_set_active (layout_pool, FALSE);
gst_clear_object (&layout_pool);
}
ComPtr<ID2D1Factory> d2d_factory;
ComPtr<IDWriteFactory> dwrite_factory;
ComPtr<IGstDWriteTextRenderer> renderer;
GstBufferPool *layout_pool = nullptr;
GstVideoInfo layout_info;
GstVideoInfo info;
};
/* *INDENT-ON* */
struct _GstDWriteBitmapRender
{
GstDWriteRender parent;
GstDWriteBitmapRenderPrivate *priv;
};
static void gst_dwrite_bitmap_render_finalize (GObject * object);
static GstBuffer *gst_dwrite_bitmap_render_draw_layout (GstDWriteRender *
render, IDWriteTextLayout * layout, gint x, gint y);
static gboolean gst_dwrite_bitmap_render_blend (GstDWriteRender * render,
GstBuffer * layout_buf, gint x, gint y, GstBuffer * output);
static gboolean gst_dwrite_bitmap_render_update_device (GstDWriteRender *
render, GstBuffer * buffer);
static gboolean
gst_dwrite_bitmap_render_handle_allocation_query (GstDWriteRender * render,
GstElement * elem, GstQuery * query);
static gboolean gst_dwrite_bitmap_render_can_inplace (GstDWriteRender * render,
GstBuffer * buffer);
#define gst_dwrite_bitmap_render_parent_class parent_class
G_DEFINE_FINAL_TYPE (GstDWriteBitmapRender, gst_dwrite_bitmap_render,
GST_TYPE_DWRITE_RENDER);
static void
gst_dwrite_bitmap_render_class_init (GstDWriteBitmapRenderClass * klass)
{
auto object_class = G_OBJECT_CLASS (klass);
auto render_class = GST_DWRITE_RENDER_CLASS (klass);
object_class->finalize = gst_dwrite_bitmap_render_finalize;
render_class->draw_layout = gst_dwrite_bitmap_render_draw_layout;
render_class->blend = gst_dwrite_bitmap_render_blend;
render_class->update_device = gst_dwrite_bitmap_render_update_device;
render_class->handle_allocation_query =
gst_dwrite_bitmap_render_handle_allocation_query;
render_class->can_inplace = gst_dwrite_bitmap_render_can_inplace;
}
static void
gst_dwrite_bitmap_render_init (GstDWriteBitmapRender * self)
{
self->priv = new GstDWriteBitmapRenderPrivate ();
}
static void
gst_dwrite_bitmap_render_finalize (GObject * object)
{
auto self = GST_DWRITE_BITMAP_RENDER (object);
delete self->priv;
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstBufferPool *
gst_dwrite_bitmap_render_create_pool (GstDWriteBitmapRender * self,
const GstVideoInfo * info)
{
auto caps = gst_video_info_to_caps (info);
if (!caps) {
GST_ERROR_OBJECT (self, "Invalid info");
return nullptr;
}
auto pool = gst_dwrite_bitmap_pool_new ();
auto config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps, info->size, 0, 0);
gst_caps_unref (caps);
if (!gst_buffer_pool_set_config (pool, config)) {
GST_ERROR_OBJECT (self, "Couldn't set pool config");
gst_object_unref (pool);
return nullptr;
}
if (!gst_buffer_pool_set_active (pool, TRUE)) {
GST_ERROR_OBJECT (self, "Couldn't activate pool");
gst_object_unref (pool);
return nullptr;
}
return pool;
}
static GstBuffer *
gst_dwrite_bitmap_render_draw_layout (GstDWriteRender * render,
IDWriteTextLayout * layout, gint x, gint y)
{
auto self = GST_DWRITE_BITMAP_RENDER (render);
auto priv = self->priv;
auto width = (gint) layout->GetMaxWidth ();
auto height = (gint) layout->GetMaxHeight ();
if (priv->layout_pool && (priv->layout_info.width != width ||
priv->layout_info.height != height)) {
gst_buffer_pool_set_active (priv->layout_pool, FALSE);
gst_clear_object (&priv->layout_pool);
}
if (!priv->layout_pool) {
gst_video_info_set_format (&priv->layout_info, GST_VIDEO_FORMAT_BGRA,
width, height);
priv->layout_pool = gst_dwrite_bitmap_render_create_pool (self,
&priv->layout_info);
if (!priv->layout_pool) {
GST_ERROR_OBJECT (self, "Couldn't create pool");
return nullptr;
}
}
GstBuffer *layout_buf = nullptr;
gst_buffer_pool_acquire_buffer (priv->layout_pool, &layout_buf, nullptr);
if (!layout_buf) {
GST_ERROR_OBJECT (self, "Couldn't acquire buffer");
return nullptr;
}
ComPtr < IDXGISurface > surface;
auto bmem = (GstDWriteBitmapMemory *) gst_buffer_peek_memory (layout_buf, 0);
static const D2D1_RENDER_TARGET_PROPERTIES props = {
D2D1_RENDER_TARGET_TYPE_DEFAULT, DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_PREMULTIPLIED, 0, 0, D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT
};
ComPtr < ID2D1RenderTarget > target;
auto hr = priv->d2d_factory->CreateWicBitmapRenderTarget (bmem->bitmap, props,
&target);
if (FAILED (hr)) {
GST_ERROR_OBJECT (self, "Couldn't create d2d render target");
gst_buffer_unref (layout_buf);
return nullptr;
}
target->BeginDraw ();
target->Clear (D2D1::ColorF (D2D1::ColorF::Black, 0.0));
priv->renderer->Draw (D2D1::Point2F (),
D2D1::Rect (0, 0, width, height), layout, target.Get ());
target->EndDraw ();
target = nullptr;
return layout_buf;
}
static gboolean
gst_dwrite_bitmap_render_blend (GstDWriteRender * render,
GstBuffer * layout_buf, gint x, gint y, GstBuffer * output)
{
auto self = GST_DWRITE_BITMAP_RENDER (render);
auto priv = self->priv;
GstVideoFrame dst_frame, src_frame;
gboolean ret;
if (!gst_video_frame_map (&dst_frame, &priv->info, output, GST_MAP_WRITE)) {
GST_ERROR_OBJECT (self, "Couldn't map output buffer");
return FALSE;
}
if (!gst_video_frame_map (&src_frame, &priv->layout_info, layout_buf,
GST_MAP_READ)) {
gst_video_frame_unmap (&dst_frame);
GST_ERROR_OBJECT (self, "Couldn't map layout buffer");
return FALSE;
}
src_frame.info.flags = (GstVideoFlags)
(src_frame.info.flags | GST_VIDEO_FLAG_PREMULTIPLIED_ALPHA);
ret = gst_video_blend (&dst_frame, &src_frame, x, y, 1.0);
gst_video_frame_unmap (&src_frame);
gst_video_frame_unmap (&dst_frame);
return ret;
}
static gboolean
gst_dwrite_bitmap_render_update_device (GstDWriteRender * render,
GstBuffer * buffer)
{
return FALSE;
}
static gboolean
gst_dwrite_bitmap_render_handle_allocation_query (GstDWriteRender * render,
GstElement * elem, GstQuery * query)
{
return TRUE;
}
static gboolean
gst_dwrite_bitmap_render_can_inplace (GstDWriteRender * render,
GstBuffer * buffer)
{
return TRUE;
}
GstDWriteRender *
gst_dwrite_bitmap_render_new (const GstVideoInfo * info,
ID2D1Factory * d2d_factory, IDWriteFactory * dwrite_factory)
{
auto self = (GstDWriteBitmapRender *)
g_object_new (GST_TYPE_DWRITE_BITMAP_RENDER, nullptr);
gst_object_ref_sink (self);
auto priv = self->priv;
priv->info = *info;
priv->d2d_factory = d2d_factory;
priv->dwrite_factory = dwrite_factory;
IGstDWriteTextRenderer::CreateInstance (dwrite_factory, &priv->renderer);
return GST_DWRITE_RENDER (self);
}