1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Direct2D: Move D2DHelpers to Direct2DGraphicsContext.cpp

This commit is contained in:
reuk 2025-04-17 14:03:09 +01:00 committed by Oli
parent 2aed72d0d7
commit 137d9820b1
3 changed files with 232 additions and 264 deletions

View file

@ -35,6 +35,238 @@
namespace juce
{
//==============================================================================
struct D2DHelpers
{
static bool isTransformAxisAligned (const AffineTransform& transform)
{
return transform.mat01 == 0.0f && transform.mat10 == 0.0f;
}
static void pathToGeometrySink (const Path& path,
ID2D1GeometrySink* sink,
const AffineTransform& transform,
D2D1_FIGURE_BEGIN figureMode)
{
class ScopedFigure
{
public:
ScopedFigure (ID2D1GeometrySink* s, D2D1_POINT_2F pt, D2D1_FIGURE_BEGIN mode)
: sink (s)
{
sink->BeginFigure (pt, mode);
}
~ScopedFigure()
{
if (sink != nullptr)
sink->EndFigure (end);
}
void setClosed()
{
end = D2D1_FIGURE_END_CLOSED;
}
private:
ID2D1GeometrySink* sink = nullptr;
D2D1_FIGURE_END end = D2D1_FIGURE_END_OPEN;
JUCE_DECLARE_NON_COPYABLE (ScopedFigure)
JUCE_DECLARE_NON_MOVEABLE (ScopedFigure)
};
// Every call to BeginFigure must have a matching call to EndFigure. But - the Path does not necessarily
// have matching startNewSubPath and closePath markers.
D2D1_POINT_2F lastLocation{};
std::optional<ScopedFigure> figure;
Path::Iterator it (path);
const auto doTransform = [&transform] (float x, float y)
{
transform.transformPoint (x, y);
return D2D1_POINT_2F { x, y };
};
const auto updateFigure = [&] (float x, float y)
{
if (! figure.has_value())
figure.emplace (sink, lastLocation, figureMode);
lastLocation = doTransform (x, y);
};
while (it.next())
{
switch (it.elementType)
{
case Path::Iterator::lineTo:
updateFigure (it.x1, it.y1);
sink->AddLine (lastLocation);
break;
case Path::Iterator::quadraticTo:
updateFigure (it.x2, it.y2);
sink->AddQuadraticBezier ({ doTransform (it.x1, it.y1), lastLocation });
break;
case Path::Iterator::cubicTo:
updateFigure (it.x3, it.y3);
sink->AddBezier ({ doTransform (it.x1, it.y1), doTransform (it.x2, it.y2), lastLocation });
break;
case Path::Iterator::closePath:
if (figure.has_value())
figure->setClosed();
figure.reset();
break;
case Path::Iterator::startNewSubPath:
figure.reset();
lastLocation = doTransform (it.x1, it.y1);
figure.emplace (sink, lastLocation, figureMode);
break;
}
}
}
static D2D1_POINT_2F pointTransformed (Point<float> pt, const AffineTransform& transform)
{
transform.transformPoint (pt.x, pt.y);
return { (FLOAT) pt.x, (FLOAT) pt.y };
}
static void rectToGeometrySink (const Rectangle<float>& rect,
ID2D1GeometrySink* sink,
const AffineTransform& transform,
D2D1_FIGURE_BEGIN figureMode)
{
const auto a = pointTransformed (rect.getTopLeft(), transform);
const auto b = pointTransformed (rect.getTopRight(), transform);
const auto c = pointTransformed (rect.getBottomRight(), transform);
const auto d = pointTransformed (rect.getBottomLeft(), transform);
sink->BeginFigure (a, figureMode);
sink->AddLine (b);
sink->AddLine (c);
sink->AddLine (d);
sink->EndFigure (D2D1_FIGURE_END_CLOSED);
}
static ComSmartPtr<ID2D1Geometry> rectListToPathGeometry (ID2D1Factory* factory,
const RectangleList<float>& clipRegion,
const AffineTransform& transform,
D2D1_FILL_MODE fillMode,
D2D1_FIGURE_BEGIN figureMode,
[[maybe_unused]] Direct2DMetrics* metrics)
{
JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, createGeometryTime)
ScopedGeometryWithSink objects { factory, fillMode };
if (objects.sink == nullptr)
return {};
for (int i = clipRegion.getNumRectangles(); --i >= 0;)
rectToGeometrySink (clipRegion.getRectangle (i), objects.sink, transform, figureMode);
return objects.geometry;
}
static ComSmartPtr<ID2D1Geometry> pathToPathGeometry (ID2D1Factory* factory,
const Path& path,
const AffineTransform& transform,
D2D1_FIGURE_BEGIN figureMode,
[[maybe_unused]] Direct2DMetrics* metrics)
{
JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, createGeometryTime)
ScopedGeometryWithSink objects { factory, path.isUsingNonZeroWinding() ? D2D1_FILL_MODE_WINDING : D2D1_FILL_MODE_ALTERNATE };
if (objects.sink == nullptr)
return {};
pathToGeometrySink (path, objects.sink, transform, figureMode);
return objects.geometry;
}
static ComSmartPtr<ID2D1StrokeStyle1> pathStrokeTypeToStrokeStyle (ID2D1Factory1* factory,
const PathStrokeType& strokeType)
{
// JUCE JointStyle ID2D1StrokeStyle
// --------------- ----------------
// mitered D2D1_LINE_JOIN_MITER
// curved D2D1_LINE_JOIN_ROUND
// beveled D2D1_LINE_JOIN_BEVEL
//
// JUCE EndCapStyle ID2D1StrokeStyle
// ---------------- ----------------
// butt D2D1_CAP_STYLE_FLAT
// square D2D1_CAP_STYLE_SQUARE
// rounded D2D1_CAP_STYLE_ROUND
auto lineJoin = D2D1_LINE_JOIN_MITER;
switch (strokeType.getJointStyle())
{
case PathStrokeType::JointStyle::mitered:
// already set
break;
case PathStrokeType::JointStyle::curved:
lineJoin = D2D1_LINE_JOIN_ROUND;
break;
case PathStrokeType::JointStyle::beveled:
lineJoin = D2D1_LINE_JOIN_BEVEL;
break;
default:
// invalid EndCapStyle
jassertfalse;
break;
}
auto capStyle = D2D1_CAP_STYLE_FLAT;
switch (strokeType.getEndStyle())
{
case PathStrokeType::EndCapStyle::butt:
// already set
break;
case PathStrokeType::EndCapStyle::square:
capStyle = D2D1_CAP_STYLE_SQUARE;
break;
case PathStrokeType::EndCapStyle::rounded:
capStyle = D2D1_CAP_STYLE_ROUND;
break;
default:
// invalid EndCapStyle
jassertfalse;
break;
}
D2D1_STROKE_STYLE_PROPERTIES1 strokeStyleProperties
{
capStyle,
capStyle,
capStyle,
lineJoin,
strokeType.getStrokeThickness(),
D2D1_DASH_STYLE_SOLID,
0.0f,
D2D1_STROKE_TRANSFORM_TYPE_NORMAL
};
ComSmartPtr<ID2D1StrokeStyle1> strokeStyle;
factory->CreateStrokeStyle (strokeStyleProperties,
nullptr,
0,
strokeStyle.resetAndGetPointerAddress());
return strokeStyle;
}
};
//==============================================================================
Direct2DGraphicsContext::Direct2DGraphicsContext() = default;
Direct2DGraphicsContext::~Direct2DGraphicsContext() = default;

View file

@ -58,235 +58,6 @@ ScopedGeometryWithSink::~ScopedGeometryWithSink()
jassertquiet (SUCCEEDED (hr));
}
//==============================================================================
bool D2DHelpers::isTransformAxisAligned (const AffineTransform& transform)
{
return transform.mat01 == 0.0f && transform.mat10 == 0.0f;
}
void D2DHelpers::pathToGeometrySink (const Path& path,
ID2D1GeometrySink* sink,
const AffineTransform& transform,
D2D1_FIGURE_BEGIN figureMode)
{
class ScopedFigure
{
public:
ScopedFigure (ID2D1GeometrySink* s, D2D1_POINT_2F pt, D2D1_FIGURE_BEGIN mode)
: sink (s)
{
sink->BeginFigure (pt, mode);
}
~ScopedFigure()
{
if (sink != nullptr)
sink->EndFigure (end);
}
void setClosed()
{
end = D2D1_FIGURE_END_CLOSED;
}
private:
ID2D1GeometrySink* sink = nullptr;
D2D1_FIGURE_END end = D2D1_FIGURE_END_OPEN;
JUCE_DECLARE_NON_COPYABLE (ScopedFigure)
JUCE_DECLARE_NON_MOVEABLE (ScopedFigure)
};
// Every call to BeginFigure must have a matching call to EndFigure. But - the Path does not necessarily
// have matching startNewSubPath and closePath markers.
D2D1_POINT_2F lastLocation{};
std::optional<ScopedFigure> figure;
Path::Iterator it (path);
const auto doTransform = [&transform] (float x, float y)
{
transform.transformPoint (x, y);
return D2D1_POINT_2F { x, y };
};
const auto updateFigure = [&] (float x, float y)
{
if (! figure.has_value())
figure.emplace (sink, lastLocation, figureMode);
lastLocation = doTransform (x, y);
};
while (it.next())
{
switch (it.elementType)
{
case Path::Iterator::lineTo:
updateFigure (it.x1, it.y1);
sink->AddLine (lastLocation);
break;
case Path::Iterator::quadraticTo:
updateFigure (it.x2, it.y2);
sink->AddQuadraticBezier ({ doTransform (it.x1, it.y1), lastLocation });
break;
case Path::Iterator::cubicTo:
updateFigure (it.x3, it.y3);
sink->AddBezier ({ doTransform (it.x1, it.y1), doTransform (it.x2, it.y2), lastLocation });
break;
case Path::Iterator::closePath:
if (figure.has_value())
figure->setClosed();
figure.reset();
break;
case Path::Iterator::startNewSubPath:
figure.reset();
lastLocation = doTransform (it.x1, it.y1);
figure.emplace (sink, lastLocation, figureMode);
break;
}
}
}
D2D1_POINT_2F D2DHelpers::pointTransformed (Point<float> pt, const AffineTransform& transform)
{
transform.transformPoint (pt.x, pt.y);
return { (FLOAT) pt.x, (FLOAT) pt.y };
}
void D2DHelpers::rectToGeometrySink (const Rectangle<float>& rect,
ID2D1GeometrySink* sink,
const AffineTransform& transform,
D2D1_FIGURE_BEGIN figureMode)
{
const auto a = pointTransformed (rect.getTopLeft(), transform);
const auto b = pointTransformed (rect.getTopRight(), transform);
const auto c = pointTransformed (rect.getBottomRight(), transform);
const auto d = pointTransformed (rect.getBottomLeft(), transform);
sink->BeginFigure (a, figureMode);
sink->AddLine (b);
sink->AddLine (c);
sink->AddLine (d);
sink->EndFigure (D2D1_FIGURE_END_CLOSED);
}
ComSmartPtr<ID2D1Geometry> D2DHelpers::rectListToPathGeometry (ID2D1Factory* factory,
const RectangleList<float>& clipRegion,
const AffineTransform& transform,
D2D1_FILL_MODE fillMode,
D2D1_FIGURE_BEGIN figureMode,
[[maybe_unused]] Direct2DMetrics* metrics)
{
JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, createGeometryTime)
ScopedGeometryWithSink objects { factory, fillMode };
if (objects.sink == nullptr)
return {};
for (int i = clipRegion.getNumRectangles(); --i >= 0;)
rectToGeometrySink (clipRegion.getRectangle (i), objects.sink, transform, figureMode);
return objects.geometry;
}
ComSmartPtr<ID2D1Geometry> D2DHelpers::pathToPathGeometry (ID2D1Factory* factory,
const Path& path,
const AffineTransform& transform,
D2D1_FIGURE_BEGIN figureMode,
[[maybe_unused]] Direct2DMetrics* metrics)
{
JUCE_D2DMETRICS_SCOPED_ELAPSED_TIME (metrics, createGeometryTime)
ScopedGeometryWithSink objects { factory, path.isUsingNonZeroWinding() ? D2D1_FILL_MODE_WINDING : D2D1_FILL_MODE_ALTERNATE };
if (objects.sink == nullptr)
return {};
pathToGeometrySink (path, objects.sink, transform, figureMode);
return objects.geometry;
}
ComSmartPtr<ID2D1StrokeStyle1> D2DHelpers::pathStrokeTypeToStrokeStyle (ID2D1Factory1* factory,
const PathStrokeType& strokeType)
{
// JUCE JointStyle ID2D1StrokeStyle
// --------------- ----------------
// mitered D2D1_LINE_JOIN_MITER
// curved D2D1_LINE_JOIN_ROUND
// beveled D2D1_LINE_JOIN_BEVEL
//
// JUCE EndCapStyle ID2D1StrokeStyle
// ---------------- ----------------
// butt D2D1_CAP_STYLE_FLAT
// square D2D1_CAP_STYLE_SQUARE
// rounded D2D1_CAP_STYLE_ROUND
auto lineJoin = D2D1_LINE_JOIN_MITER;
switch (strokeType.getJointStyle())
{
case PathStrokeType::JointStyle::mitered:
// already set
break;
case PathStrokeType::JointStyle::curved:
lineJoin = D2D1_LINE_JOIN_ROUND;
break;
case PathStrokeType::JointStyle::beveled:
lineJoin = D2D1_LINE_JOIN_BEVEL;
break;
default:
// invalid EndCapStyle
jassertfalse;
break;
}
auto capStyle = D2D1_CAP_STYLE_FLAT;
switch (strokeType.getEndStyle())
{
case PathStrokeType::EndCapStyle::butt:
// already set
break;
case PathStrokeType::EndCapStyle::square:
capStyle = D2D1_CAP_STYLE_SQUARE;
break;
case PathStrokeType::EndCapStyle::rounded:
capStyle = D2D1_CAP_STYLE_ROUND;
break;
default:
// invalid EndCapStyle
jassertfalse;
break;
}
D2D1_STROKE_STYLE_PROPERTIES1 strokeStyleProperties
{
capStyle,
capStyle,
capStyle,
lineJoin,
strokeType.getStrokeThickness(),
D2D1_DASH_STYLE_SOLID,
0.0f,
D2D1_STROKE_TRANSFORM_TYPE_NORMAL
};
ComSmartPtr<ID2D1StrokeStyle1> strokeStyle;
factory->CreateStrokeStyle (strokeStyleProperties,
nullptr,
0,
strokeStyle.resetAndGetPointerAddress());
return strokeStyle;
}
//==============================================================================
void DirectWriteGlyphRun::replace (Span<const Point<float>> positions, float scale)
{

View file

@ -70,41 +70,6 @@ private:
std::unique_ptr<std::remove_pointer_t<HANDLE>, FunctionPointerDestructor<CloseHandle>> handle;
};
//==============================================================================
struct D2DHelpers
{
static bool isTransformAxisAligned (const AffineTransform& transform);
static void pathToGeometrySink (const Path& path,
ID2D1GeometrySink* sink,
const AffineTransform& transform,
D2D1_FIGURE_BEGIN figureMode);
static D2D1_POINT_2F pointTransformed (Point<float> pt, const AffineTransform& transform);
static void rectToGeometrySink (const Rectangle<float>& rect,
ID2D1GeometrySink* sink,
const AffineTransform& transform,
D2D1_FIGURE_BEGIN figureMode);
static ComSmartPtr<ID2D1Geometry> rectListToPathGeometry (ID2D1Factory* factory,
const RectangleList<float>& clipRegion,
const AffineTransform& transform,
D2D1_FILL_MODE fillMode,
D2D1_FIGURE_BEGIN figureMode,
[[maybe_unused]] Direct2DMetrics* metrics);
static ComSmartPtr<ID2D1Geometry> pathToPathGeometry (ID2D1Factory* factory,
const Path& path,
const AffineTransform& transform,
D2D1_FIGURE_BEGIN figureMode,
[[maybe_unused]] Direct2DMetrics* metrics);
static ComSmartPtr<ID2D1StrokeStyle1> pathStrokeTypeToStrokeStyle (ID2D1Factory1* factory,
const PathStrokeType& strokeType);
};
//==============================================================================
/** Heap storage for a DirectWrite glyph run */
class DirectWriteGlyphRun