2025-11-28 00:35:46 +09:00

389 lines
11 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
//
// Contents: Adapter render target draws using D2D or DirectWrite.
// This demonstrates how to implement your own render target
// for layout drawing callbacks.
//
//----------------------------------------------------------------------------
#pragma once
struct ID2D1HwndRenderTarget;
struct ID2D1Bitmap;
class InlineImage;
class DrawingEffect;
typedef D2D1_RECT_F RectF;
////////////////////////////////////////////////////////////////////////////////
class RenderTarget;
// Intermediate render target for UI to draw to either a D2D or GDI surface.
class DECLSPEC_UUID("4327AC14-3172-4807-BF40-02C7475A2520") RenderTarget
: public ComBase<
QiListSelf<RenderTarget,
QiList<IDWriteTextRenderer>
> >
{
public:
virtual ~RenderTarget() {};
virtual void BeginDraw() = NULL;
virtual void EndDraw() = NULL;
virtual void Clear(UINT32 color) = NULL;
virtual void Resize(UINT width, UINT height) = NULL;
virtual void UpdateMonitor() = NULL;
virtual void SetTransform(DWRITE_MATRIX const& transform) = NULL;
virtual void GetTransform(DWRITE_MATRIX& transform) = NULL;
virtual void SetAntialiasing(bool isEnabled) = NULL;
virtual void DrawTextLayout(
IDWriteTextLayout* textLayout,
const RectF& rect
) = NULL;
// Draws a single image, from the given coordinates, to the given coordinates.
// If the height and width differ, they will be scaled, but mirroring must be
// done via a matrix transform.
virtual void DrawImage(
IWICBitmapSource* image,
const RectF& sourceRect, // where in source atlas texture
const RectF& destRect // where on display to draw it
) = NULL;
virtual void FillRectangle(
const RectF& destRect,
const DrawingEffect& drawingEffect
) = NULL;
protected:
// This context is not persisted, only existing on the stack as it
// is passed down through. This is mainly needed to handle cases
// where runs where no drawing effect set, like those of an inline
// object or trimming sign.
struct Context
{
Context(RenderTarget* initialTarget, IUnknown* initialDrawingEffect)
: target(initialTarget),
drawingEffect(initialDrawingEffect)
{ }
// short lived weak pointers
RenderTarget* target;
IUnknown* drawingEffect;
};
IUnknown* GetDrawingEffect(void* clientDrawingContext, IUnknown* drawingEffect)
{
// Callbacks use this to use a drawing effect from the client context
// if none was passed into the callback.
if (drawingEffect != NULL)
return drawingEffect;
return (reinterpret_cast<Context*>(clientDrawingContext))->drawingEffect;
}
};
////////////////////////////////////////////////////////////////////////////////
class RenderTargetD2D : public RenderTarget
{
public:
RenderTargetD2D(ID2D1Factory* d2dFactory, IDWriteFactory* dwriteFactory, HWND hwnd);
HRESULT static Create(ID2D1Factory* d2dFactory, IDWriteFactory* dwriteFactory, HWND hwnd, OUT RenderTarget** renderTarget);
virtual ~RenderTargetD2D();
virtual void BeginDraw();
virtual void EndDraw();
virtual void Clear(UINT32 color);
virtual void Resize(UINT width, UINT height);
virtual void UpdateMonitor();
virtual void SetTransform(DWRITE_MATRIX const& transform);
virtual void GetTransform(DWRITE_MATRIX& transform);
virtual void SetAntialiasing(bool isEnabled);
virtual void DrawTextLayout(
IDWriteTextLayout* textLayout,
const RectF& rect
);
virtual void DrawImage(
IWICBitmapSource* image,
const RectF& sourceRect, // where in source atlas texture
const RectF& destRect // where on display to draw it
);
void FillRectangle(
const RectF& destRect,
const DrawingEffect& drawingEffect
);
// IDWriteTextRenderer implementation
IFACEMETHOD(DrawGlyphRun)(
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode,
const DWRITE_GLYPH_RUN* glyphRun,
const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
IUnknown* clientDrawingEffect
);
IFACEMETHOD(DrawUnderline)(
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
const DWRITE_UNDERLINE* underline,
IUnknown* clientDrawingEffect
);
IFACEMETHOD(DrawStrikethrough)(
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
const DWRITE_STRIKETHROUGH* strikethrough,
IUnknown* clientDrawingEffect
);
IFACEMETHOD(DrawInlineObject)(
void* clientDrawingContext,
FLOAT originX,
FLOAT originY,
IDWriteInlineObject* inlineObject,
BOOL isSideways,
BOOL isRightToLeft,
IUnknown* clientDrawingEffect
);
IFACEMETHOD(IsPixelSnappingDisabled)(
void* clientDrawingContext,
OUT BOOL* isDisabled
);
IFACEMETHOD(GetCurrentTransform)(
void* clientDrawingContext,
OUT DWRITE_MATRIX* transform
);
IFACEMETHOD(GetPixelsPerDip)(
void* clientDrawingContext,
OUT FLOAT* pixelsPerDip
);
public:
// For cached images, to avoid needing to recreate the textures each draw call.
struct ImageCacheEntry
{
ImageCacheEntry(IWICBitmapSource* initialOriginal, ID2D1Bitmap* initialConverted)
: original(SafeAcquire(initialOriginal)),
converted(SafeAcquire(initialConverted))
{ }
ImageCacheEntry(const ImageCacheEntry& b)
{
original = SafeAcquire(b.original);
converted = SafeAcquire(b.converted);
}
ImageCacheEntry& operator=(const ImageCacheEntry& b)
{
if (this != &b)
{
// Define assignment operator in terms of destructor and
// placement new constructor, paying heed to self assignment.
this->~ImageCacheEntry();
new(this) ImageCacheEntry(b);
}
return *this;
}
~ImageCacheEntry()
{
SafeRelease(&original);
SafeRelease(&converted);
}
IWICBitmapSource* original;
ID2D1Bitmap* converted;
};
protected:
HRESULT CreateTarget();
ID2D1Bitmap* GetCachedImage(IWICBitmapSource* image);
ID2D1Brush* GetCachedBrush(const DrawingEffect* effect);
protected:
IDWriteFactory* dwriteFactory_;
ID2D1Factory* d2dFactory_;
ID2D1HwndRenderTarget* target_; // D2D render target
ID2D1SolidColorBrush* brush_; // reusable scratch brush for current color
std::vector<ImageCacheEntry> imageCache_;
HWND hwnd_;
HMONITOR hmonitor_;
};
////////////////////////////////////////////////////////////////////////////////
class RenderTargetDW : public RenderTarget
{
public:
RenderTargetDW(IDWriteFactory* dwriteFactory, HWND hwnd);
HRESULT static Create(IDWriteFactory* dwriteFactory, HWND hwnd, OUT RenderTarget** renderTarget);
virtual ~RenderTargetDW();
virtual void BeginDraw();
virtual void EndDraw();
virtual void Clear(UINT32 color);
virtual void Resize(UINT width, UINT height);
virtual void UpdateMonitor();
virtual void SetTransform(DWRITE_MATRIX const& transform);
virtual void GetTransform(DWRITE_MATRIX& transform);
virtual void SetAntialiasing(bool isEnabled) {}
virtual void DrawTextLayout(
IDWriteTextLayout* textLayout,
const RectF& rect
);
virtual void DrawImage(
IWICBitmapSource* image,
const RectF& sourceRect, // where in source atlas texture
const RectF& destRect // where on display to draw it
);
void FillRectangle(
const RectF& destRect,
const DrawingEffect& drawingEffect
);
// IDWriteTextRenderer implementation
IFACEMETHOD(DrawGlyphRun)(
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_MEASURING_MODE measuringMode,
const DWRITE_GLYPH_RUN* glyphRun,
const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription,
IUnknown* clientDrawingEffect
);
IFACEMETHOD(DrawUnderline)(
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
const DWRITE_UNDERLINE* underline,
IUnknown* clientDrawingEffect
);
IFACEMETHOD(DrawStrikethrough)(
void* clientDrawingContext,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
const DWRITE_STRIKETHROUGH* strikethrough,
IUnknown* clientDrawingEffect
);
IFACEMETHOD(DrawInlineObject)(
void* clientDrawingContext,
FLOAT originX,
FLOAT originY,
IDWriteInlineObject* inlineObject,
BOOL isSideways,
BOOL isRightToLeft,
IUnknown* clientDrawingEffect
);
IFACEMETHOD(IsPixelSnappingDisabled)(
void* clientDrawingContext,
OUT BOOL* isDisabled
);
IFACEMETHOD(GetCurrentTransform)(
void* clientDrawingContext,
OUT DWRITE_MATRIX* transform
);
IFACEMETHOD(GetPixelsPerDip)(
void* clientDrawingContext,
OUT FLOAT* pixelsPerDip
);
public:
// For cached images, to avoid creating device dependent bitmaps each time.
struct ImageCacheEntry
{
ImageCacheEntry(IWICBitmapSource* initialOriginal, HBITMAP initialConverted)
: original(SafeAcquire(initialOriginal)),
converted(initialConverted)
{ }
ImageCacheEntry(const ImageCacheEntry& b)
{
converted = b.converted;
original = SafeAcquire(b.original);
}
ImageCacheEntry& operator=(const ImageCacheEntry& b)
{
if (this != &b)
{
// Define assignment operator in terms of destructor and
// placement new constructor, paying heed to self assignment.
this->~ImageCacheEntry();
new(this) ImageCacheEntry(b);
}
return *this;
}
~ImageCacheEntry()
{
SafeRelease(&original);
}
IWICBitmapSource* original;
HBITMAP converted;
};
protected:
HRESULT Initialize();
HBITMAP GetCachedImage(IWICBitmapSource* image);
HRESULT DrawLine(
float baselineOriginX,
float baselineOriginY,
float width,
float offset,
float thickness,
IUnknown* clientDrawingEffect
);
protected:
IDWriteFactory* dwriteFactory_;
IDWriteGdiInterop* gdiInterop_;
IDWriteBitmapRenderTarget* target_;
IDWriteRenderingParams* renderingParams_;
std::vector<ImageCacheEntry> imageCache_;
HWND hwnd_;
HDC memoryHdc_;
HMONITOR hmonitor_;
};