mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-14 12:26:36 +00:00
269 lines
7.9 KiB
C++
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);
|
||
|
}
|