mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-05 08:48:00 +02:00
Merge branch 'd2d-no-copy' into 'master'
Draft: Implement zero-copy rendering path for Direct2D See merge request cairo/cairo!607
This commit is contained in:
commit
9d5d038674
5 changed files with 3000 additions and 17 deletions
|
|
@ -181,6 +181,7 @@ cairo_feature_sources = {
|
|||
],
|
||||
'cairo-dwrite-font': [
|
||||
'win32/cairo-dwrite-font.cpp',
|
||||
'win32/wicbitmapfordata.cpp',
|
||||
],
|
||||
'cairo-script': [
|
||||
'cairo-script-surface.c',
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
#include "cairo-truetype-subset-private.h"
|
||||
#include "cairo-scaled-font-subsets-private.h"
|
||||
#include "cairo-dwrite.h"
|
||||
#include "wicbitmapfordata.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <float.h>
|
||||
|
|
@ -1201,18 +1202,30 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s
|
|||
if (FAILED(hr))
|
||||
return _cairo_dwrite_error (hr, "TranslateColorGlyphRun failed");
|
||||
|
||||
/* We have a color glyph(s). Use Direct2D to render it to a bitmap */
|
||||
// We have a color glyph(s). Use Direct2D to render it to a bitmap
|
||||
|
||||
// Technically we don't need to initialize WIC, because we use our
|
||||
// own IWICBitmap inmplementation. However it's unclear whether
|
||||
// Direct2D uses other parts of WIC internally, and may assume that
|
||||
// COM is initialized. Technically, it shouldn't assume anything
|
||||
// about COM, since WIC can be used in a COM-free way via proxy
|
||||
// functions, but better be safe.
|
||||
if (!WICImagingFactory::Instance() || !D2DFactory::Instance())
|
||||
return _cairo_dwrite_error (hr, "Instance failed");
|
||||
|
||||
RefPtr<IWICBitmap> bitmap;
|
||||
hr = WICImagingFactory::Instance()->CreateBitmap ((UINT)width,
|
||||
(UINT)height,
|
||||
GUID_WICPixelFormat32bppPBGRA,
|
||||
WICBitmapCacheOnLoad,
|
||||
&bitmap);
|
||||
if (FAILED(hr))
|
||||
return _cairo_dwrite_error (hr, "CreateBitmap failed");
|
||||
cairo_surface_t *image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
|
||||
unsigned char *data = cairo_image_surface_get_data (image);
|
||||
int stride = cairo_image_surface_get_stride (image);
|
||||
|
||||
WICBitmapForData bitmap(data,
|
||||
static_cast<UINT> (width),
|
||||
static_cast<UINT> (height),
|
||||
static_cast<UINT> (stride),
|
||||
4,
|
||||
GUID_WICPixelFormat32bppPBGRA,
|
||||
nullptr);
|
||||
//if (bitmap.in_error ())
|
||||
// return (cairo_int_status_t)_cairo_error (CAIRO_STATUS_DWRITE_ERROR);
|
||||
|
||||
D2D1_RENDER_TARGET_PROPERTIES properties = D2D1::RenderTargetProperties(
|
||||
D2D1_RENDER_TARGET_TYPE_DEFAULT,
|
||||
|
|
@ -1225,7 +1238,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s
|
|||
D2D1_FEATURE_LEVEL_DEFAULT);
|
||||
|
||||
RefPtr<ID2D1RenderTarget> rt;
|
||||
hr = D2DFactory::Instance()->CreateWicBitmapRenderTarget (bitmap, properties, &rt);
|
||||
hr = D2DFactory::Instance()->CreateWicBitmapRenderTarget (&bitmap, properties, &rt);
|
||||
if (FAILED(hr))
|
||||
return _cairo_dwrite_error (hr, "CreateWicBitmapRenderTarget failed");
|
||||
|
||||
|
|
@ -1339,13 +1352,8 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s
|
|||
if (FAILED(hr))
|
||||
return _cairo_dwrite_error (hr, "EndDraw failed");
|
||||
|
||||
cairo_surface_t *image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
|
||||
int stride = cairo_image_surface_get_stride (image);
|
||||
WICRect rect = { 0, 0, width, height };
|
||||
bitmap->CopyPixels(&rect,
|
||||
stride,
|
||||
height * stride,
|
||||
cairo_image_surface_get_data (image));
|
||||
//dc4->Flush ();
|
||||
|
||||
cairo_surface_mark_dirty (image);
|
||||
cairo_surface_set_device_offset (image, -x1, -y1);
|
||||
_cairo_scaled_glyph_set_color_surface (scaled_glyph,
|
||||
|
|
|
|||
2062
src/win32/optional.hpp
Normal file
2062
src/win32/optional.hpp
Normal file
File diff suppressed because it is too large
Load diff
597
src/win32/wicbitmapfordata.cpp
Normal file
597
src/win32/wicbitmapfordata.cpp
Normal file
|
|
@ -0,0 +1,597 @@
|
|||
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
|
||||
/* Cairo - a vector graphics library with display and print output
|
||||
*
|
||||
* Copyright © 2025 Luca Bacci
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it either under the terms of the GNU Lesser General Public
|
||||
* License version 2.1 as published by the Free Software Foundation
|
||||
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
||||
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
||||
* notice, a recipient may use your version of this file under either
|
||||
* the MPL or the LGPL.
|
||||
*
|
||||
* You should have received a copy of the LGPL along with this library
|
||||
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
||||
* Foundation < licensing@fsf.org >. You should have received a copy of
|
||||
* the MPL along with this library in the file COPYING-MPL-1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.1 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* https://www.mozilla.org/MPL/
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
||||
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
||||
* the specific language governing rights and limitations.
|
||||
*
|
||||
* The Original Code is the cairo graphics library.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Luca Bacci <luca.bacci@outlook.com>
|
||||
*/
|
||||
|
||||
#include "cairoint.h"
|
||||
|
||||
#include "wicbitmapfordata.hpp"
|
||||
|
||||
#include <windows.h>
|
||||
#include <wincodec.h>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <cassert>
|
||||
#include <intrin.h>
|
||||
#include <new>
|
||||
|
||||
#if _WIN32_WINNT < _WIN32_WINNT_WIN8
|
||||
# define CHECK_FAST_FAIL IsProcessorFeaturePresent (PF_FASTFAIL_AVAILABLE)
|
||||
#else
|
||||
# define CHECK_FAST_FAIL 1
|
||||
#endif
|
||||
|
||||
#ifndef FAST_FAIL_INVALID_REFERENCE_COUNT
|
||||
#define FAST_FAIL_INVALID_REFERENCE_COUNT 14
|
||||
#endif
|
||||
|
||||
constexpr double DEFAULT_DPI = 96.0;
|
||||
|
||||
static void
|
||||
print_unhandled_iid (const char *context,
|
||||
REFIID riid);
|
||||
|
||||
thread_local ULONG
|
||||
WICBitmapForData::s_thread_instances_count;
|
||||
|
||||
thread_local ULONG
|
||||
WICBitmapLockForData::s_thread_instances_count;
|
||||
|
||||
// WICBitmapForData
|
||||
|
||||
WICBitmapForData::WICBitmapForData (unsigned char * const data,
|
||||
UINT width,
|
||||
UINT height,
|
||||
UINT stride,
|
||||
UINT pixel_size,
|
||||
WICPixelFormatGUID wic_pixel_format,
|
||||
deleter_t deleter) noexcept
|
||||
: m_data (data)
|
||||
, m_width (width)
|
||||
, m_height (height)
|
||||
, m_stride (stride)
|
||||
, m_pixel_size (pixel_size)
|
||||
, m_wic_pixel_format (wic_pixel_format)
|
||||
, m_dpi_x (DEFAULT_DPI)
|
||||
, m_dpi_y (DEFAULT_DPI)
|
||||
, m_write_lock ()
|
||||
, m_read_locks_count (0)
|
||||
, m_reference_count (deleter ? 1 : 0)
|
||||
, m_deleter (deleter)
|
||||
, m_thread_id (GetCurrentThreadId ())
|
||||
{
|
||||
assert (m_data != nullptr);
|
||||
|
||||
assert (m_width != 0);
|
||||
assert (m_height != 0);
|
||||
assert (m_pixel_size != 0);
|
||||
|
||||
assert (m_stride / m_pixel_size >= m_width);
|
||||
assert (UINT_MAX / stride >= m_height);
|
||||
|
||||
// Technically, should be m_stride * (m_height - 1) + m_row_size
|
||||
assert (UINTPTR_MAX - (std::uintptr_t)m_data >= m_stride * m_height);
|
||||
|
||||
s_thread_instances_count++;
|
||||
}
|
||||
|
||||
WICBitmapForData::~WICBitmapForData () noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
assert (!have_locks ());
|
||||
assert (m_reference_count == 0);
|
||||
|
||||
s_thread_instances_count--;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapForData::QueryInterface (REFIID riid,
|
||||
void **ppvObject) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
if (riid == IID_IUnknown ||
|
||||
riid == IID_IWICBitmapSource ||
|
||||
riid == IID_IWICBitmap)
|
||||
{
|
||||
AddRef();
|
||||
*ppvObject = this;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
print_unhandled_iid ("WICBitmapForData", riid);
|
||||
*ppvObject = nullptr;
|
||||
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG)
|
||||
WICBitmapForData::AddRef () noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
if (m_reference_count >= s_reference_count_limit) {
|
||||
if (CHECK_FAST_FAIL) {
|
||||
__fastfail (FAST_FAIL_INVALID_REFERENCE_COUNT);
|
||||
}
|
||||
std::abort ();
|
||||
}
|
||||
|
||||
m_reference_count++;
|
||||
|
||||
return m_reference_count;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG)
|
||||
WICBitmapForData::Release () noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
if (m_reference_count == 0 || m_reference_count > s_reference_count_limit) {
|
||||
if (CHECK_FAST_FAIL) {
|
||||
__fastfail (FAST_FAIL_INVALID_REFERENCE_COUNT);
|
||||
}
|
||||
std::abort ();
|
||||
}
|
||||
|
||||
m_reference_count--;
|
||||
|
||||
assert (m_reference_count >= m_read_locks_count);
|
||||
|
||||
if (m_reference_count == 0) {
|
||||
if (m_deleter) {
|
||||
m_deleter (this);
|
||||
}
|
||||
|
||||
// Note: the object has been destroyed, do not access any
|
||||
// member method, call any member function, or use this.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_reference_count;
|
||||
}
|
||||
|
||||
// IWICBitmapSource
|
||||
IFACEMETHODIMP
|
||||
WICBitmapForData::CopyPalette (IWICPalette *pIPalette) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
return WINCODEC_ERR_PALETTEUNAVAILABLE;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapForData::CopyPixels (const WICRect *prc,
|
||||
UINT cbStride,
|
||||
UINT cbBufferSize,
|
||||
BYTE *pbBuffer) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
if (have_write_locks ()) {
|
||||
return WINCODEC_ERR_ALREADYLOCKED;
|
||||
}
|
||||
|
||||
UINT x = 0;
|
||||
UINT y = 0;
|
||||
UINT width = m_width;
|
||||
UINT height = m_height;
|
||||
|
||||
if (prc) {
|
||||
if (!check_sub_rectangle (*prc))
|
||||
return E_INVALIDARG;
|
||||
|
||||
x = static_cast<UINT> (prc->X);
|
||||
y = static_cast<UINT> (prc->Y);
|
||||
width = static_cast<UINT> (prc->Width);
|
||||
height = static_cast<UINT> (prc->Height);
|
||||
}
|
||||
|
||||
const unsigned char *src_iter = get_address_for_point (x, y);
|
||||
unsigned char *dst_iter = pbBuffer;
|
||||
const UINT row_size = m_width * m_pixel_size;
|
||||
|
||||
for (UINT i = 0; i < height; i++) {
|
||||
std::memcpy (dst_iter, src_iter, row_size);
|
||||
|
||||
src_iter += m_stride;
|
||||
dst_iter += cbStride;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapForData::GetPixelFormat (WICPixelFormatGUID *pPixelFormat) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
*pPixelFormat = m_wic_pixel_format;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapForData::GetResolution (double *pDpiX,
|
||||
double *pDpiY) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
*pDpiX = m_dpi_x;
|
||||
*pDpiY = m_dpi_y;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapForData::GetSize (UINT *puiWidth,
|
||||
UINT *puiHeight) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
*puiWidth = m_width;
|
||||
*puiHeight = m_height;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapForData::Lock (const WICRect *prcLock,
|
||||
DWORD flags,
|
||||
IWICBitmapLock **ppILock) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
*ppILock = nullptr;
|
||||
|
||||
UINT x = 0;
|
||||
UINT y = 0;
|
||||
UINT width = m_width;
|
||||
UINT height = m_height;
|
||||
|
||||
if (prcLock) {
|
||||
if (!check_sub_rectangle (*prcLock))
|
||||
return E_INVALIDARG;
|
||||
|
||||
x = static_cast<UINT> (prcLock->X);
|
||||
y = static_cast<UINT> (prcLock->Y);
|
||||
width = static_cast<UINT> (prcLock->Width);
|
||||
height = static_cast<UINT> (prcLock->Height);
|
||||
}
|
||||
|
||||
const bool is_write_lock = (flags & WICBitmapLockWrite) != 0;
|
||||
|
||||
if (is_write_lock) {
|
||||
if (have_locks ()) {
|
||||
return WINCODEC_ERR_ALREADYLOCKED;
|
||||
}
|
||||
|
||||
AddRef ();
|
||||
|
||||
const auto& deleter = [](WICBitmapLockForData *lock) noexcept {
|
||||
WICBitmapForData& bitmap = lock->m_parent_bitmap;
|
||||
|
||||
assert (bitmap.m_write_lock.has_value ());
|
||||
bitmap.m_write_lock.reset ();
|
||||
|
||||
bitmap.Release ();
|
||||
};
|
||||
|
||||
// There can only be one write lock, and we made it a member of this class
|
||||
m_write_lock.emplace(*this, x, y, width, height, deleter);
|
||||
|
||||
*ppILock = &m_write_lock.value ();
|
||||
}
|
||||
else {
|
||||
if (have_write_locks ()) {
|
||||
return WINCODEC_ERR_ALREADYLOCKED;
|
||||
}
|
||||
|
||||
if (m_read_locks_count >= s_reference_count_limit - 1) {
|
||||
if (CHECK_FAST_FAIL) {
|
||||
__fastfail (FAST_FAIL_INVALID_REFERENCE_COUNT);
|
||||
}
|
||||
std::abort ();
|
||||
}
|
||||
|
||||
AddRef ();
|
||||
m_read_locks_count++;
|
||||
|
||||
const auto& deleter = [](WICBitmapLockForData *lock) noexcept {
|
||||
WICBitmapForData& bitmap = lock->m_parent_bitmap;
|
||||
|
||||
delete lock;
|
||||
lock = nullptr;
|
||||
|
||||
if (bitmap.m_read_locks_count == 0) {
|
||||
if (CHECK_FAST_FAIL) {
|
||||
__fastfail (FAST_FAIL_INVALID_REFERENCE_COUNT);
|
||||
}
|
||||
std::abort ();
|
||||
}
|
||||
|
||||
bitmap.m_read_locks_count--;
|
||||
bitmap.Release ();
|
||||
};
|
||||
|
||||
// Read locks are allocated on the heap
|
||||
*ppILock = new (std::nothrow) WICBitmapLockForData (*this, x, y, width, height, deleter);
|
||||
if (*ppILock == nullptr) {
|
||||
Release ();
|
||||
m_read_locks_count--;
|
||||
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapForData::SetPalette (IWICPalette *pIPalette) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
return WINCODEC_ERR_PALETTEUNAVAILABLE;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapForData::SetResolution (double dpiX,
|
||||
double dpiY) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
m_dpi_x = dpiX;
|
||||
m_dpi_y = dpiY;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
WICBitmapForData::get_address_for_point (UINT x, UINT y) const noexcept
|
||||
{
|
||||
static_assert (sizeof (*m_data) == 1, "");
|
||||
|
||||
return m_data + (y * m_stride) + (m_pixel_size * x);
|
||||
}
|
||||
|
||||
bool
|
||||
WICBitmapForData::check_sub_rectangle (const WICRect& rectangle) const noexcept
|
||||
{
|
||||
return rectangle.X >= 0 &&
|
||||
rectangle.Y >= 0 &&
|
||||
rectangle.Width > 0 &&
|
||||
rectangle.Height > 0 &&
|
||||
(UINT)rectangle.X < m_width &&
|
||||
(UINT)rectangle.Y < m_height &&
|
||||
m_width - (UINT)rectangle.X <= (UINT)rectangle.Width &&
|
||||
m_height - (UINT)rectangle.Y <= (UINT)rectangle.Height;
|
||||
}
|
||||
|
||||
bool
|
||||
WICBitmapForData::have_write_locks () const noexcept
|
||||
{
|
||||
return m_write_lock.has_value ();
|
||||
}
|
||||
|
||||
bool
|
||||
WICBitmapForData::have_read_locks () const noexcept
|
||||
{
|
||||
return m_read_locks_count != 0;
|
||||
}
|
||||
|
||||
bool
|
||||
WICBitmapForData::have_locks () const noexcept
|
||||
{
|
||||
return have_read_locks () || have_write_locks ();
|
||||
}
|
||||
|
||||
void
|
||||
WICBitmapForData::method_check () const noexcept
|
||||
{
|
||||
assert (m_thread_id == GetCurrentThreadId ());
|
||||
}
|
||||
|
||||
// WICBitmapLockForData
|
||||
|
||||
WICBitmapLockForData::WICBitmapLockForData (WICBitmapForData& parent_bitmap,
|
||||
UINT x,
|
||||
UINT y,
|
||||
UINT width,
|
||||
UINT height,
|
||||
deleter_t deleter) noexcept
|
||||
: m_parent_bitmap (parent_bitmap)
|
||||
, m_x (x)
|
||||
, m_y (y)
|
||||
, m_width (width)
|
||||
, m_height (height)
|
||||
, m_reference_count (1)
|
||||
, m_deleter (deleter)
|
||||
, m_thread_id (GetCurrentThreadId ())
|
||||
{
|
||||
assert (m_x < m_parent_bitmap.m_width);
|
||||
assert (m_y < m_parent_bitmap.m_height);
|
||||
|
||||
assert (m_width <= m_parent_bitmap.m_width);
|
||||
assert (m_height <= m_parent_bitmap.m_height);
|
||||
|
||||
assert (m_parent_bitmap.m_width - m_width >= m_x);
|
||||
assert (m_parent_bitmap.m_height - m_height >= m_y);
|
||||
|
||||
s_thread_instances_count++;
|
||||
}
|
||||
|
||||
WICBitmapLockForData::~WICBitmapLockForData () noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
assert (m_reference_count == 0);
|
||||
|
||||
s_thread_instances_count--;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapLockForData::QueryInterface (REFIID riid,
|
||||
void **ppvObject) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
if (riid == IID_IUnknown ||
|
||||
riid == IID_IWICBitmapLock)
|
||||
{
|
||||
AddRef();
|
||||
*ppvObject = this;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
print_unhandled_iid ("WICBitmapLockForData", riid);
|
||||
*ppvObject = nullptr;
|
||||
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG)
|
||||
WICBitmapLockForData::AddRef () noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
if (m_reference_count >= s_reference_count_limit) {
|
||||
if (CHECK_FAST_FAIL) {
|
||||
__fastfail (FAST_FAIL_INVALID_REFERENCE_COUNT);
|
||||
}
|
||||
std::abort ();
|
||||
}
|
||||
|
||||
m_reference_count++;
|
||||
|
||||
return m_reference_count;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG)
|
||||
WICBitmapLockForData::Release () noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
if (m_reference_count == 0) {
|
||||
if (CHECK_FAST_FAIL) {
|
||||
__fastfail (FAST_FAIL_INVALID_REFERENCE_COUNT);
|
||||
}
|
||||
std::abort ();
|
||||
}
|
||||
|
||||
m_reference_count--;
|
||||
|
||||
if (m_reference_count == 0) {
|
||||
if (m_deleter) {
|
||||
m_deleter (this);
|
||||
}
|
||||
|
||||
// Note: the object has been destroyed, do not access any
|
||||
// member method, call any member function, or use this.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return m_reference_count;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapLockForData::GetDataPointer (UINT *pcbBufferSize,
|
||||
WICInProcPointer *ppbData) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
const UINT stride = m_parent_bitmap.m_stride;
|
||||
const UINT pixel_size = m_parent_bitmap.m_pixel_size;
|
||||
|
||||
*pcbBufferSize = stride * (m_height - 1) + (pixel_size * m_width);
|
||||
*ppbData = m_parent_bitmap.get_address_for_point (m_x, m_y);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapLockForData::GetPixelFormat (WICPixelFormatGUID *pPixelFormat) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
*pPixelFormat = m_parent_bitmap.m_wic_pixel_format;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapLockForData::GetSize (UINT *puiWidth,
|
||||
UINT *puiHeight) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
*puiWidth = m_width;
|
||||
*puiHeight = m_height;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP
|
||||
WICBitmapLockForData::GetStride (UINT *pcbStride) noexcept
|
||||
{
|
||||
method_check ();
|
||||
|
||||
*pcbStride = m_parent_bitmap.m_stride;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void
|
||||
WICBitmapLockForData::method_check () const noexcept
|
||||
{
|
||||
assert (m_thread_id == GetCurrentThreadId ());
|
||||
}
|
||||
|
||||
// Utils
|
||||
|
||||
static void
|
||||
print_unhandled_iid (const char *context,
|
||||
REFIID riid)
|
||||
{
|
||||
static_assert (alignof (GUID) % sizeof (unsigned short) == 0, "");
|
||||
static_assert (offsetof (GUID, Data4) % sizeof (unsigned short) == 0, "");
|
||||
unsigned short *data4 = (unsigned short *)riid.Data4;
|
||||
|
||||
std::fprintf (stderr,
|
||||
"%s: %s {%08lX-%04hX-%04hX-%04hX-%04hX%04hX%04hX}\n",
|
||||
context, "Could not handle QueryInterface for IID",
|
||||
riid.Data1, riid.Data2, riid.Data3,
|
||||
data4[0], data4[1], data4[2], data4[3]);
|
||||
}
|
||||
315
src/win32/wicbitmapfordata.hpp
Normal file
315
src/win32/wicbitmapfordata.hpp
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
|
||||
/* Cairo - a vector graphics library with display and print output
|
||||
*
|
||||
* Copyright © 2025 Luca Bacci
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it either under the terms of the GNU Lesser General Public
|
||||
* License version 2.1 as published by the Free Software Foundation
|
||||
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
||||
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
||||
* notice, a recipient may use your version of this file under either
|
||||
* the MPL or the LGPL.
|
||||
*
|
||||
* You should have received a copy of the LGPL along with this library
|
||||
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
||||
* Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA.
|
||||
* You should have received a copy of the MPL along with this library
|
||||
* in the file COPYING-MPL-1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.1 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* https://www.mozilla.org/MPL/
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
||||
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
||||
* the specific language governing rights and limitations.
|
||||
*
|
||||
* The Original Code is the cairo graphics library.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Luca Bacci <luca.bacci@outlook.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "cairoint.h"
|
||||
|
||||
#include <limits>
|
||||
#include <windows.h>
|
||||
#include <wincodec.h>
|
||||
|
||||
#include "optional.hpp"
|
||||
|
||||
/* WICBitmapForData (bitmap), WICBitmapLockForData (lock).
|
||||
*
|
||||
* WICBitmapForData is an implementation of IWICBitmap that wraps an
|
||||
* existing memory buffer. This is needed to make Direct2D render
|
||||
* directly on in-memory graphics data that is created externally,
|
||||
* provided that pixel formats match. Note that WIC supports many
|
||||
* pixel formats natively [1], but Direct2D WIC render targets support
|
||||
* only a few selected formats [2].
|
||||
*
|
||||
* Constructor:
|
||||
*
|
||||
* WICBitmapForData (unsigned char * const data,
|
||||
* UINT width,
|
||||
* UINT height,
|
||||
* UINT stride,
|
||||
* UINT pixel_size,
|
||||
* WICPixelFormatGUID wic_pixel_format,
|
||||
* deleter_t deleter);
|
||||
*
|
||||
* @data: pointer to the memory buffer containing graphics data
|
||||
*
|
||||
* @width: the image width (cannot be zero)
|
||||
*
|
||||
* @height: the image height (cannot be zero)
|
||||
*
|
||||
* @stride: the size (in bytes) to move from one row to the next
|
||||
*
|
||||
* @pixel_size: pizel size (in bytes). As this argument is a byte size,
|
||||
* WICBitmapForData cannot support packed pixel formats
|
||||
* (e.g A1). Supporting such formats would complicate the
|
||||
* implementation, and is not needed for Direct2D anyway.
|
||||
*
|
||||
* @wic_pixel_format: the WICPixelFormatGUID of the image data
|
||||
*
|
||||
* @deleter: a deleter callback with signature:
|
||||
*
|
||||
* void (*)(WICBitmapForData*) noexcept
|
||||
*
|
||||
* It will be called once the reference count of the object
|
||||
* drops to zero. If lifetime should be managed externally,
|
||||
* pass nullptr.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
* The pixel size could be derived automatically from the pixel format
|
||||
* GUID, however that's a quite elaborated process, which involve
|
||||
* potential runtime activation of COM objects due to the extensible
|
||||
* nature of WIC. That can be slow to do repeatedly, and renders the
|
||||
* constructor failable, adding complexity.
|
||||
*
|
||||
* WICBitmapForData can be allocated by whatever mean, thanks to the
|
||||
* deleter construct-time argument. This was added because most COM
|
||||
* objects trigger their destruction internally from the Release()
|
||||
* method, however, the object doesn't know how the user allocated it.
|
||||
* The user passes a deleter callback which is responsible for deleting
|
||||
* the instance.
|
||||
*
|
||||
* In some cases, lifetime (deletion) of the object is outside the control
|
||||
* of the object user; for example, when the object is allocated on the
|
||||
* stack or within another structure that it's not owned. In such cases,
|
||||
* a nullptr deleter can ber specified. Internal reference counting is
|
||||
* kept for debugging purposes, but won't trigger destruction. The user
|
||||
* need not drop its own initial reference count.
|
||||
*
|
||||
* WICBitmapForData only supports pixel formats where pixels are byte-aligned.
|
||||
* That is, bits-per-pixel must be a multiple of eight. See the @pixel_size
|
||||
* argument description.
|
||||
*
|
||||
* WICBitmapForData is meant for use from a single thread only.
|
||||
*
|
||||
* References:
|
||||
*
|
||||
* [1] - https://learn.microsoft.com/en-us/windows/win32/wic/-wic-codec-native-pixel-formats
|
||||
* [2] - https://learn.microsoft.com/en-us/windows/win32/direct2d/supported-pixel-formats-and-alpha-modes#supported-formats-for-wic-bitmap-render-target
|
||||
*/
|
||||
|
||||
class WICBitmapForData;
|
||||
class WICBitmapLockForData;
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// mingw-w64 headers don't have WICInProcPointer
|
||||
typedef BYTE* WICInProcPointer;
|
||||
#endif
|
||||
|
||||
// We define the lock class first, as we want to make the
|
||||
// lock class (write lock) a member of the bitmap class.
|
||||
|
||||
// https://stackoverflow.com/questions/27489558/how-do-i-create-an-alias-for-a-noexcept-function-pointer
|
||||
void lock_deleter_prototype (WICBitmapLockForData*) noexcept;
|
||||
|
||||
class WICBitmapLockForData final
|
||||
: public IWICBitmapLock
|
||||
{
|
||||
public:
|
||||
// Cannot use noexcept in using declarations, so we use
|
||||
// a function declaration and decltype as a workaround.
|
||||
using deleter_t = decltype (&lock_deleter_prototype);
|
||||
// TODO: Use a functor / callable object
|
||||
|
||||
// Constructor
|
||||
explicit WICBitmapLockForData (WICBitmapForData& parent_bitmap,
|
||||
UINT x,
|
||||
UINT y,
|
||||
UINT width,
|
||||
UINT height,
|
||||
deleter_t deleter) noexcept;
|
||||
|
||||
public:
|
||||
// Disable copy
|
||||
WICBitmapLockForData (const WICBitmapLockForData&) = delete;
|
||||
WICBitmapLockForData& operator= (const WICBitmapLockForData&) = delete;
|
||||
|
||||
// Destructor
|
||||
~WICBitmapLockForData () noexcept;
|
||||
|
||||
// IUnknown
|
||||
IFACEMETHOD (QueryInterface) (REFIID riid,
|
||||
void **ppvObject) noexcept override;
|
||||
|
||||
IFACEMETHOD_(ULONG, AddRef) () noexcept override;
|
||||
|
||||
IFACEMETHOD_(ULONG, Release) () noexcept override;
|
||||
|
||||
// IWICBitmapLock
|
||||
IFACEMETHOD (GetDataPointer) (UINT *pcbBufferSize,
|
||||
WICInProcPointer *ppbData) noexcept override;
|
||||
|
||||
IFACEMETHOD (GetPixelFormat) (WICPixelFormatGUID *pPixelFormat) noexcept override;
|
||||
|
||||
IFACEMETHOD (GetSize) (UINT *puiWidth,
|
||||
UINT *puiHeight) noexcept override;
|
||||
|
||||
IFACEMETHOD (GetStride) (UINT *pcbStride) noexcept override;
|
||||
|
||||
private:
|
||||
void method_check () const noexcept;
|
||||
|
||||
private:
|
||||
WICBitmapForData& m_parent_bitmap;
|
||||
|
||||
const UINT m_x;
|
||||
const UINT m_y;
|
||||
const UINT m_width;
|
||||
const UINT m_height;
|
||||
|
||||
ULONG m_reference_count;
|
||||
|
||||
const deleter_t m_deleter;
|
||||
|
||||
const DWORD m_thread_id;
|
||||
|
||||
friend class WICBitmapForData;
|
||||
|
||||
private:
|
||||
static constexpr ULONG s_reference_count_limit {
|
||||
std::numeric_limits<ULONG>::max()
|
||||
};
|
||||
|
||||
static thread_local ULONG s_thread_instances_count;
|
||||
};
|
||||
|
||||
void bitmap_deleter_prototype (WICBitmapForData*) noexcept;
|
||||
|
||||
class WICBitmapForData final
|
||||
: public IWICBitmap
|
||||
{
|
||||
public:
|
||||
// Disable copy
|
||||
WICBitmapForData (const WICBitmapForData&) = delete;
|
||||
WICBitmapForData& operator= (const WICBitmapForData&) = delete;
|
||||
|
||||
// Cannot use noexcept in using declarations, so we use
|
||||
// a function declaration and decltype as a workaround.
|
||||
using deleter_t = decltype (&bitmap_deleter_prototype);
|
||||
// TODO: Use a functor / callable object
|
||||
|
||||
// Constructor
|
||||
explicit WICBitmapForData (unsigned char * const data,
|
||||
UINT width,
|
||||
UINT height,
|
||||
UINT stride,
|
||||
UINT pixel_size,
|
||||
WICPixelFormatGUID wic_pixel_format,
|
||||
deleter_t deleter) noexcept;
|
||||
|
||||
// Destructor
|
||||
~WICBitmapForData () noexcept;
|
||||
|
||||
// IUnknown
|
||||
IFACEMETHOD (QueryInterface) (REFIID riid,
|
||||
void **ppvObject) noexcept override;
|
||||
|
||||
IFACEMETHOD_(ULONG, AddRef) () noexcept override;
|
||||
|
||||
IFACEMETHOD_(ULONG, Release) () noexcept override;
|
||||
|
||||
// IWICBitmapSource
|
||||
IFACEMETHOD (CopyPalette) (IWICPalette *pIPalette) noexcept override;
|
||||
|
||||
IFACEMETHOD (CopyPixels) (const WICRect *prc,
|
||||
UINT cbStride,
|
||||
UINT cbBufferSize,
|
||||
BYTE *pbBuffer) noexcept override;
|
||||
|
||||
IFACEMETHOD (GetPixelFormat) (WICPixelFormatGUID *pPixelFormat) noexcept override;
|
||||
|
||||
IFACEMETHOD (GetResolution) (double *pDpiX,
|
||||
double *pDpiY) noexcept override;
|
||||
|
||||
IFACEMETHOD (GetSize) (UINT *puiWidth,
|
||||
UINT *puiHeight) noexcept override;
|
||||
|
||||
// IWICBitmap
|
||||
IFACEMETHOD (Lock) (const WICRect *prcLock,
|
||||
DWORD flags,
|
||||
IWICBitmapLock **ppILock) noexcept override;
|
||||
|
||||
IFACEMETHOD (SetPalette) (IWICPalette *pIPalette) noexcept override;
|
||||
|
||||
IFACEMETHOD (SetResolution) (double dpiX,
|
||||
double dpiY) noexcept override;
|
||||
|
||||
private:
|
||||
unsigned char * get_address_for_point (UINT x,
|
||||
UINT y) const noexcept;
|
||||
|
||||
bool check_sub_rectangle (const WICRect& rectangle) const noexcept;
|
||||
|
||||
bool have_write_locks () const noexcept;
|
||||
bool have_read_locks () const noexcept;
|
||||
bool have_locks () const noexcept;
|
||||
|
||||
// Check for public methods
|
||||
void method_check () const noexcept;
|
||||
|
||||
private:
|
||||
unsigned char * const m_data;
|
||||
const UINT m_width;
|
||||
const UINT m_height;
|
||||
const UINT m_stride;
|
||||
const UINT m_pixel_size;
|
||||
const WICPixelFormatGUID m_wic_pixel_format;
|
||||
|
||||
double m_dpi_x;
|
||||
double m_dpi_y;
|
||||
|
||||
// There can only be one write lock at a time. Make it
|
||||
// a member so that we avoid an allocation on the heap.
|
||||
tl::optional<WICBitmapLockForData> m_write_lock;
|
||||
|
||||
// Keeps track of how many read locks are outstanding.
|
||||
ULONG m_read_locks_count;
|
||||
|
||||
ULONG m_reference_count;
|
||||
|
||||
const deleter_t m_deleter;
|
||||
|
||||
// The thread ID at construction time. This class is
|
||||
// meant to be used by one thread only, so we check
|
||||
// the thread ID in all public methods.
|
||||
const DWORD m_thread_id;
|
||||
|
||||
friend class WICBitmapLockForData;
|
||||
|
||||
private:
|
||||
static constexpr ULONG s_reference_count_limit {
|
||||
std::numeric_limits<ULONG>::max()
|
||||
};
|
||||
|
||||
static thread_local ULONG s_thread_instances_count;
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue