Hi. I’ve added a few features to gdiplus for my own use. For now, this is enough for my current tasks. Maybe it’ll help someone else.
Add to GdiPlusGraphics.h
Status DrawDashLine(const Pen* pen, REAL x1, REAL y1, REAL x2, REAL y2, REAL dashLen, REAL gapLen)
{
return updateStatus(DLLExports::GdipDrawDashLine(
nativeGraphics, pen ? pen->nativePen : NULL,
x1, y1, x2, y2, dashLen, gapLen));
}
Status DrawDashLine(const Pen* pen, INT x1, INT y1, INT x2, INT y2, INT dashLen, INT gapLen)
{
return updateStatus(DLLExports::GdipDrawDashLineI(
nativeGraphics, pen ? pen->nativePen : NULL,
x1, y1, x2, y2, dashLen, gapLen));
}
Status DrawDashLine(const Pen* pen, const PointF& pt1, const PointF& pt2, REAL dashLen, REAL gapLen)
{
return updateStatus(DLLExports::GdipDrawDashLine(
nativeGraphics, pen ? pen->nativePen : NULL,
pt1.X, pt1.Y, pt2.X, pt2.Y, dashLen, gapLen));
}
Status DrawDashLine(const Pen* pen, const Point& pt1, const Point& pt2, INT dashLen, INT gapLen)
{
return updateStatus(DLLExports::GdipDrawDashLineI(
nativeGraphics, pen ? pen->nativePen : NULL,
pt1.X, pt1.Y, pt2.X, pt2.Y, dashLen, gapLen));
}
Status DrawDashArc(const Pen* pen, REAL x, REAL y, REAL width, REAL height,
REAL startAngle, REAL sweepAngle, REAL dashAngle, REAL gapAngle)
{
return updateStatus(DLLExports::GdipDrawDashArc(
nativeGraphics, pen ? pen->nativePen : NULL,
x, y, width, height, startAngle, sweepAngle, dashAngle, gapAngle));
}
Status DrawDashArc(const Pen* pen, INT x, INT y, INT width, INT height,
REAL startAngle, REAL sweepAngle, REAL dashAngle, REAL gapAngle)
{
return updateStatus(DLLExports::GdipDrawDashArcI(
nativeGraphics, pen ? pen->nativePen : NULL,
x, y, width, height, startAngle, sweepAngle, dashAngle, gapAngle));
}
Status DrawDashArc(const Pen* pen, const RectF& rect,
REAL startAngle, REAL sweepAngle, REAL dashAngle, REAL gapAngle)
{
return updateStatus(DLLExports::GdipDrawDashArc(
nativeGraphics, pen ? pen->nativePen : NULL,
rect.X, rect.Y, rect.Width, rect.Height,
startAngle, sweepAngle, dashAngle, gapAngle));
}
Status DrawDashArc(const Pen* pen, const Rect& rect,
REAL startAngle, REAL sweepAngle, REAL dashAngle, REAL gapAngle)
{
return updateStatus(DLLExports::GdipDrawDashArcI(
nativeGraphics, pen ? pen->nativePen : NULL,
rect.X, rect.Y, rect.Width, rect.Height,
startAngle, sweepAngle, dashAngle, gapAngle));
}
Status DrawDashBezier(const Pen* pen,
REAL x1, REAL y1, REAL x2, REAL y2,
REAL x3, REAL y3, REAL x4, REAL y4, REAL dashLen, REAL gapLen)
{
return updateStatus(DLLExports::GdipDrawDashBezier(
nativeGraphics, pen ? pen->nativePen : NULL,
x1, y1, x2, y2, x3, y3, x4, y4, dashLen, gapLen));
}
Status DrawDashBezier(const Pen* pen,
INT x1, INT y1, INT x2, INT y2,
INT x3, INT y3, INT x4, INT y4, INT dashLen, INT gapLen)
{
return updateStatus(DLLExports::GdipDrawDashBezierI(
nativeGraphics, pen ? pen->nativePen : NULL,
x1, y1, x2, y2, x3, y3, x4, y4, dashLen, gapLen));
}
Status DrawDashBezier(const Pen* pen,
const PointF& pt1, const PointF& pt2,
const PointF& pt3, const PointF& pt4, REAL dashLen, REAL gapLen)
{
return updateStatus(DLLExports::GdipDrawDashBezier(
nativeGraphics, pen ? pen->nativePen : NULL,
pt1.X, pt1.Y, pt2.X, pt2.Y,
pt3.X, pt3.Y, pt4.X, pt4.Y, dashLen, gapLen));
}
Status DrawDashBezier(const Pen* pen, const Point& pt1, const Point& pt2, const Point& pt3, const Point& pt4, INT dashLen, INT gapLen)
{
return updateStatus(DLLExports::GdipDrawDashBezierI(
nativeGraphics, pen ? pen->nativePen : NULL,
pt1.X, pt1.Y, pt2.X, pt2.Y,
pt3.X, pt3.Y, pt4.X, pt4.Y, dashLen, gapLen));
}
To graphics.cpp
GpStatus WINGDIPAPI GdipDrawDashLine(GpGraphics* graphics, GpPen* pen, REAL x1, REAL y1, REAL x2, REAL y2, REAL dashLen, REAL gapLen)
{
convertPen(graphics, pen);
float dx = x2 - x1;
float dy = y2 - y1;
float len = sqrtf(dx * dx + dy * dy);
dx /= len;
dy /= len;
for(float dist = 0; dist < len; dist += (dashLen + gapLen)) {
float d = (dist + dashLen > len) ? (len - dist) : dashLen;
nvgBeginPath(graphics->ctx);
nvgMoveTo(graphics->ctx, x1 + dx * dist, y1 + dy * dist);
nvgLineTo(graphics->ctx, x1 + dx * (dist + d), y1 + dy * (dist + d));
nvgStroke(graphics->ctx);
}
return GpStatus::Ok;
}
GpStatus WINGDIPAPI GdipDrawDashLineI(GpGraphics* graphics, GpPen* pen, INT x1, INT y1, INT x2, INT y2, INT dashLen, INT gapLen)
{
return GdipDrawDashLine(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2, (REAL)dashLen, (REAL)gapLen);
}
GpStatus WINGDIPAPI GdipDrawDashArc(GpGraphics* graphics, GpPen* pen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle, REAL dashAngle, REAL gapAngle)
{
#ifdef GDIFLAT_SAFE
if(!graphics || !graphics->ctx)
return GpStatus::InvalidParameter;
#endif
convertPen(graphics, pen);
for(float a = nvgDegToRad(startAngle); a < nvgDegToRad(sweepAngle); a += (dashAngle + gapAngle)) {
float segmentEnd = a + dashAngle;
if(segmentEnd > sweepAngle) {
segmentEnd = sweepAngle;
}
nvgBeginPath(graphics->ctx);
nvgEllipticalArc(graphics->ctx, x + width * 0.5f, y + height * 0.5f, width * 0.5f, height * 0.5f, a, segmentEnd, (sweepAngle < 0 ? NVG_CCW : NVG_CW));
nvgStroke(graphics->ctx);
}
return GpStatus::Ok;
}
GpStatus WINGDIPAPI GdipDrawDashArcI(GpGraphics* graphics, GpPen* pen, INT x, INT y, INT width, INT height, REAL startAngle, REAL sweepAngle, REAL dashAngle, REAL gapAngle)
{
return GdipDrawDashArc(graphics, pen, (REAL)x, (REAL)y, (REAL)width, (REAL)height, startAngle, sweepAngle, dashAngle, gapAngle);
}
GpStatus WINGDIPAPI GdipDrawDashBezier(GpGraphics* graphics, GpPen* pen, REAL x1, REAL y1, REAL x2, REAL y2, REAL x3, REAL y3,
REAL x4, REAL y4, REAL dashLen, REAL gapLen)
{
#ifdef GDIFLAT_SAFE
if(!graphics || !graphics->ctx)
return GpStatus::InvalidParameter;
#endif
convertPen(graphics, pen);
float lastX = x1, lastY = y1;
float currentDist = 0;
bool drawing = true;
auto getBezierPt = [](float t,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3,
float x4,
float y4) -> std::tuple<float, float> {
float c = 1.0f - t;
float c2 = c * c;
float c3 = c2 * c;
float t2 = t * t;
float t3 = t2 * t;
float outX = c3 * x1 + 3 * c2 * t * x2 + 3 * c * t2 * x3 + t3 * x4;
float outY = c3 * y1 + 3 * c2 * t * y2 + 3 * c * t2 * y3 + t3 * y4;
return {outX, outY};
};
nvgBeginPath(graphics->ctx);
nvgMoveTo(graphics->ctx, x1, y1);
for(float t = 0.01f; t <= 1.01f; t += 0.01f) {
auto [px, py] = getBezierPt(fminf(t, 1.0f), x1, y1, x2, y2, x3, y3, x4, y4);
float dist = sqrtf((px - lastX) * (px - lastX) + (py - lastY) * (py - lastY));
currentDist += dist;
if(drawing && currentDist >= dashLen) {
nvgLineTo(graphics->ctx, px, py);
drawing = false;
currentDist = 0;
} else if(!drawing && currentDist >= gapLen) {
nvgMoveTo(graphics->ctx, px, py);
drawing = true;
currentDist = 0;
} else if(drawing) {
nvgLineTo(graphics->ctx, px, py);
}
lastX = px;
lastY = py;
}
nvgStroke(graphics->ctx);
return GpStatus::Ok;
}
GpStatus WINGDIPAPI GdipDrawDashBezierI(GpGraphics* graphics, GpPen* pen, INT x1, INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4, INT dashLen, INT gapLen)
{
return GdipDrawDashBezier(graphics, pen, (REAL)x1, (REAL)y1, (REAL)x2, (REAL)y2, (REAL)x3, (REAL)y3, (REAL)x4, (REAL)y4, (REAL)dashLen, (REAL)gapLen);
}
To graphics.h
GpStatus WINGDIPAPI GdipDrawDashLine(GpGraphics* graphics, GpPen* pen, REAL x1, REAL y1, REAL x2, REAL y2, REAL dashLen, REAL gapLen);
GpStatus WINGDIPAPI GdipDrawDashLineI(GpGraphics* graphics, GpPen* pen, INT x1, INT y1, INT x2, INT y2, INT dashLen, INT gapLen);
GpStatus WINGDIPAPI GdipDrawDashArc(GpGraphics* graphics, GpPen* pen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle, REAL dashAngle, REAL gapAngle);
GpStatus WINGDIPAPI GdipDrawDashArcI(GpGraphics* graphics, GpPen* pen, INT x, INT y, INT width, INT height, REAL startAngle, REAL sweepAngle, REAL dashAngle, REAL gapAngle);
GpStatus WINGDIPAPI GdipDrawDashBezier(GpGraphics* graphics, GpPen* pen, REAL x1, REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4, REAL dashLen, REAL gapLen);
GpStatus WINGDIPAPI GdipDrawDashBezierI(GpGraphics* graphics, GpPen* pen, INT x1, INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4, INT dashLen, INT gapLen);
And samples
mGdi->Clear(Color::Black);
mGdi->DrawDashLine(&PenRed, 0, 0, 1000, 1000, 20, 10);
mGdi->DrawDashLine(&PenGreen, 500, 0, 500, 1000, 20, 20);
mGdi->DrawDashLine(&PenBlue, 1000, 0, 0, 1000, 30, 10);
mGdi->DrawDashLine(&PenWhite, 0, 500, 1000, 500, 10, 40);
mGdi->DrawDashArc(&PenPurple, 200, 200, 600, 600, 0, 180, 0.1, 0.1);
mGdi->DrawDashBezier(&PenYellow, 0, 800, 250, 200, 500, 800, 1000, 200, 20, 10);