aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNeil <nyamatongwe@gmail.com>2021-03-25 10:07:29 +1100
committerNeil <nyamatongwe@gmail.com>2021-03-25 10:07:29 +1100
commit6ca286d41a1ca8e6d948d131fdaaf8b8199c905e (patch)
tree417cd9fa371dac8432198aed2005a96ae6f3a6c3
parent216fca2d218c3e0dcb5cdb4273cfb609da06cbd2 (diff)
downloadscintilla-mirror-6ca286d41a1ca8e6d948d131fdaaf8b8199c905e.tar.gz
Use FillStroke for parameters to Polygon, RectangleDraw, RoundedRectangle, and
Ellipse.
-rw-r--r--cocoa/PlatCocoa.h6
-rw-r--r--cocoa/PlatCocoa.mm140
-rwxr-xr-xgtk/PlatGTK.cxx59
-rw-r--r--qt/ScintillaEditBase/PlatQt.cpp47
-rw-r--r--qt/ScintillaEditBase/PlatQt.h11
-rw-r--r--src/Platform.h4
-rw-r--r--win32/PlatWin.cxx156
7 files changed, 417 insertions, 6 deletions
diff --git a/cocoa/PlatCocoa.h b/cocoa/PlatCocoa.h
index f50c0e7f7..4487d9945 100644
--- a/cocoa/PlatCocoa.h
+++ b/cocoa/PlatCocoa.h
@@ -64,6 +64,8 @@ private:
void PenColourAlpha(ColourAlpha fore);
+ void SetFillStroke(FillStroke fillStroke);
+
// 24-bit RGB+A bitmap data constants
static const int BITS_PER_COMPONENT = 8;
static const int BITS_PER_PIXEL = BITS_PER_COMPONENT * 4;
@@ -95,17 +97,21 @@ public:
void MoveTo(int x_, int y_) override;
void LineTo(int x_, int y_) override;
void Polygon(Scintilla::Point *pts, size_t npts, ColourDesired fore, ColourDesired back) override;
+ void Polygon(const Scintilla::Point *pts, size_t npts, FillStroke fillStroke) override;
void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void RectangleDraw(PRectangle rc, FillStroke fillStroke) override;
void FillRectangle(PRectangle rc, ColourDesired back) override;
void FillRectangle(PRectangle rc, Fill fill) override;
void FillRectangle(PRectangle rc, Surface &surfacePattern) override;
void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void RoundedRectangle(PRectangle rc, FillStroke fillStroke) override;
void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
ColourDesired outline, int alphaOutline, int flags) override;
void AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) override;
void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;
void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void Ellipse(PRectangle rc, FillStroke fillStroke) override;
void Copy(PRectangle rc, Scintilla::Point from, Surface &surfaceSource) override;
std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
diff --git a/cocoa/PlatCocoa.mm b/cocoa/PlatCocoa.mm
index 714e4f484..618d17de1 100644
--- a/cocoa/PlatCocoa.mm
+++ b/cocoa/PlatCocoa.mm
@@ -51,6 +51,15 @@ extern sptr_t scintilla_send_message(void *sci, unsigned int iMessage, uptr_t wP
//--------------------------------------------------------------------------------------------------
/**
+ * Converts a Point as used by Scintilla to a Quartz-style CGPoint.
+ */
+inline CGPoint CGPointFromPoint(Scintilla::Point pt) {
+ return CGPointMake(pt.x, pt.y);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
* Converts a PRectangle as used by Scintilla to standard Obj-C NSRect structure .
*/
NSRect PRectangleToNSRect(const PRectangle &rc) {
@@ -77,6 +86,18 @@ inline CGRect PRectangleToCGRect(PRectangle &rc) {
return CGRectMake(rc.left, rc.top, rc.Width(), rc.Height());
}
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Converts a PRectangle as used by Scintilla to a Quartz-style rectangle.
+ * Result is inset by strokeWidth / 2 so stroking does not go outside the rectangle.
+ */
+inline CGRect CGRectFromPRectangleInset(PRectangle rc, XYPOSITION strokeWidth) {
+ const XYPOSITION halfStroke = strokeWidth / 2.0f;
+ const CGRect rect = PRectangleToCGRect(rc);
+ return CGRectInset(rect, halfStroke, halfStroke);
+}
+
//----------------- FontQuartz ---------------------------------------------------------------------
class FontQuartz : public Font {
@@ -513,6 +534,14 @@ void SurfaceImpl::PenColourAlpha(ColourAlpha fore) {
//--------------------------------------------------------------------------------------------------
+void SurfaceImpl::SetFillStroke(FillStroke fillStroke) {
+ FillColour(fillStroke.fill.colour);
+ PenColourAlpha(fillStroke.stroke.colour);
+ CGContextSetLineWidth(gc, fillStroke.stroke.width);
+}
+
+//--------------------------------------------------------------------------------------------------
+
CGImageRef SurfaceImpl::CreateImage() {
// For now, assume that CreateImage can only be called on PixMap surfaces.
if (!bitmapData)
@@ -655,6 +684,27 @@ void SurfaceImpl::Polygon(Scintilla::Point *pts, size_t npts, ColourDesired fore
//--------------------------------------------------------------------------------------------------
+void SurfaceImpl::Polygon(const Scintilla::Point *pts, size_t npts, FillStroke fillStroke) {
+ std::vector<CGPoint> points;
+ std::transform(pts, pts + npts, std::back_inserter(points), CGPointFromPoint);
+
+ CGContextBeginPath(gc);
+
+ SetFillStroke(fillStroke);
+
+ // Draw the polygon
+ CGContextAddLines(gc, points.data(), npts);
+
+ // Explicitly close the path, so it is closed for stroking AND filling (implicit close = filling only)
+ CGContextClosePath(gc);
+ CGContextDrawPath(gc, kCGPathFillStroke);
+
+ // Restore as not all paths set
+ CGContextSetLineWidth(gc, 1.0f);
+}
+
+//--------------------------------------------------------------------------------------------------
+
void SurfaceImpl::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) {
if (gc) {
CGContextBeginPath(gc);
@@ -671,6 +721,22 @@ void SurfaceImpl::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired
//--------------------------------------------------------------------------------------------------
+void SurfaceImpl::RectangleDraw(PRectangle rc, FillStroke fillStroke) {
+ if (!gc)
+ return;
+ CGContextBeginPath(gc);
+ SetFillStroke(fillStroke);
+
+ CGContextAddRect(gc, CGRectFromPRectangleInset(rc, fillStroke.stroke.width));
+
+ CGContextDrawPath(gc, kCGPathFillStroke);
+
+ // Restore as not all paths set
+ CGContextSetLineWidth(gc, 1.0f);
+}
+
+//--------------------------------------------------------------------------------------------------
+
void SurfaceImpl::FillRectangle(PRectangle rc, ColourDesired back) {
if (gc) {
FillColour(back);
@@ -813,6 +879,70 @@ void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesi
CGContextDrawPath(gc, kCGPathFillStroke);
}
+void SurfaceImpl::RoundedRectangle(PRectangle rc, FillStroke fillStroke) {
+ // This is only called from the margin marker drawing code for SC_MARK_ROUNDRECT
+ // The Win32 version does
+ // ::RoundRect(hdc, rc.left + 1, rc.top, rc.right - 1, rc.bottom, 8, 8 );
+ // which is a rectangle with rounded corners each having a radius of 4 pixels.
+ // It would be almost as good just cutting off the corners with lines at
+ // 45 degrees as is done on GTK+.
+
+ // Create a rectangle with semicircles at the corners
+ const int MAX_RADIUS = 4;
+ const int radius = std::min(MAX_RADIUS, static_cast<int>(std::min(rc.Height()/2, rc.Width()/2)));
+
+ // Points go clockwise, starting from just below the top left
+ // Corners are kept together, so we can easily create arcs to connect them
+ CGPoint corners[4][3] = {
+ {
+ { rc.left, rc.top + radius },
+ { rc.left, rc.top },
+ { rc.left + radius, rc.top },
+ },
+ {
+ { rc.right - radius - 1, rc.top },
+ { rc.right - 1, rc.top },
+ { rc.right - 1, rc.top + radius },
+ },
+ {
+ { rc.right - 1, rc.bottom - radius - 1 },
+ { rc.right - 1, rc.bottom - 1 },
+ { rc.right - radius - 1, rc.bottom - 1 },
+ },
+ {
+ { rc.left + radius, rc.bottom - 1 },
+ { rc.left, rc.bottom - 1 },
+ { rc.left, rc.bottom - radius - 1 },
+ },
+ };
+
+ // Align the points in the middle of the pixels
+ for (int i = 0; i < 4; ++ i) {
+ for (int j = 0; j < 3; ++ j) {
+ corners[i][j].x += 0.5;
+ corners[i][j].y += 0.5;
+ }
+ }
+
+ SetFillStroke(fillStroke);
+
+ // Move to the last point to begin the path
+ CGContextBeginPath(gc);
+ CGContextMoveToPoint(gc, corners[3][2].x, corners[3][2].y);
+
+ for (int i = 0; i < 4; ++ i) {
+ CGContextAddLineToPoint(gc, corners[i][0].x, corners[i][0].y);
+ CGContextAddArcToPoint(gc, corners[i][1].x, corners[i][1].y, corners[i][2].x, corners[i][2].y, radius);
+ }
+
+ // Close the path to enclose it for stroking and for filling, then draw it
+ CGContextClosePath(gc);
+ CGContextDrawPath(gc, kCGPathFillStroke);
+
+ // Restore as not all paths set
+ CGContextSetLineWidth(gc, 1.0f);
+}
+
// DrawChamferedRectangle is a helper function for AlphaRectangle that either fills or strokes a
// rectangle with its corners chamfered at 45 degrees.
static void DrawChamferedRectangle(CGContextRef gc, PRectangle rc, int cornerSize, CGPathDrawingMode mode) {
@@ -1073,6 +1203,16 @@ void SurfaceImpl::Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back)
CGContextDrawPath(gc, kCGPathFillStroke);
}
+void SurfaceImpl::Ellipse(PRectangle rc, FillStroke fillStroke) {
+ const CGRect ellipseRect = CGRectFromPRectangleInset(rc, fillStroke.stroke.width / 2.0f);
+ SetFillStroke(fillStroke);
+ CGContextBeginPath(gc);
+ CGContextAddEllipseInRect(gc, ellipseRect);
+ CGContextDrawPath(gc, kCGPathFillStroke);
+ // Restore as not all paths set
+ CGContextSetLineWidth(gc, 1.0f);
+}
+
void SurfaceImpl::CopyImageRectangle(Surface &surfaceSource, PRectangle srcRect, PRectangle dstRect) {
SurfaceImpl &source = static_cast<SurfaceImpl &>(surfaceSource);
CGImageRef image = source.CreateImage();
diff --git a/gtk/PlatGTK.cxx b/gtk/PlatGTK.cxx
index b1079a31a..b71689007 100755
--- a/gtk/PlatGTK.cxx
+++ b/gtk/PlatGTK.cxx
@@ -162,17 +162,21 @@ public:
void MoveTo(int x_, int y_) override;
void LineTo(int x_, int y_) override;
void Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) override;
+ void Polygon(const Point *pts, size_t npts, FillStroke fillStroke) override;
void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void RectangleDraw(PRectangle rc, FillStroke fillStroke) override;
void FillRectangle(PRectangle rc, ColourDesired back) override;
void FillRectangle(PRectangle rc, Fill fill) override;
void FillRectangle(PRectangle rc, Surface &surfacePattern) override;
void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void RoundedRectangle(PRectangle rc, FillStroke fillStroke) override;
void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
ColourDesired outline, int alphaOutline, int flags) override;
void AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) override;
void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;
void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void Ellipse(PRectangle rc, FillStroke fillStroke) override;
void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
@@ -494,6 +498,20 @@ void SurfaceImpl::Polygon(Point *pts, size_t npts, ColourDesired fore,
cairo_stroke(context);
}
+void SurfaceImpl::Polygon(const Point *pts, size_t npts, FillStroke fillStroke) {
+ PLATFORM_ASSERT(context);
+ PenColourAlpha(fillStroke.fill.colour);
+ cairo_move_to(context, pts[0].x, pts[0].y);
+ for (size_t i = 1; i < npts; i++) {
+ cairo_line_to(context, pts[i].x, pts[i].y);
+ }
+ cairo_close_path(context);
+ cairo_fill_preserve(context);
+ PenColourAlpha(fillStroke.stroke.colour);
+ cairo_set_line_width(context, fillStroke.stroke.width);
+ cairo_stroke(context);
+}
+
void SurfaceImpl::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) {
if (context) {
cairo_rectangle(context, rc.left + 0.5, rc.top + 0.5,
@@ -505,6 +523,17 @@ void SurfaceImpl::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired
}
}
+void SurfaceImpl::RectangleDraw(PRectangle rc, FillStroke fillStroke) {
+ if (context) {
+ CairoRectangle(rc.Inset(fillStroke.stroke.width / 2));
+ PenColourAlpha(fillStroke.fill.colour);
+ cairo_fill_preserve(context);
+ PenColourAlpha(fillStroke.stroke.colour);
+ cairo_set_line_width(context, fillStroke.stroke.width);
+ cairo_stroke(context);
+ }
+}
+
void SurfaceImpl::FillRectangle(PRectangle rc, ColourDesired back) {
PenColour(back);
if (context && (rc.left < maxCoordinate)) { // Protect against out of range
@@ -553,6 +582,25 @@ void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesi
}
}
+void SurfaceImpl::RoundedRectangle(PRectangle rc, FillStroke fillStroke) {
+ if (((rc.right - rc.left) > 4) && ((rc.bottom - rc.top) > 4)) {
+ // Approximate a round rect with some cut off corners
+ Point pts[] = {
+ Point(rc.left + 2, rc.top),
+ Point(rc.right - 2, rc.top),
+ Point(rc.right, rc.top + 2),
+ Point(rc.right, rc.bottom - 2),
+ Point(rc.right - 2, rc.bottom),
+ Point(rc.left + 2, rc.bottom),
+ Point(rc.left, rc.bottom - 2),
+ Point(rc.left, rc.top + 2),
+ };
+ Polygon(pts, std::size(pts), fillStroke);
+ } else {
+ RectangleDraw(rc, fillStroke);
+ }
+}
+
static void PathRoundRectangle(cairo_t *context, double left, double top, double width, double height, double radius) noexcept {
constexpr double degrees = kPi / 180.0;
@@ -680,6 +728,17 @@ void SurfaceImpl::Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back)
cairo_stroke(context);
}
+void SurfaceImpl::Ellipse(PRectangle rc, FillStroke fillStroke) {
+ PLATFORM_ASSERT(context);
+ PenColourAlpha(fillStroke.fill.colour);
+ cairo_arc(context, (rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2,
+ (std::min(rc.Width(), rc.Height()) - fillStroke.stroke.width) / 2, 0, 2*kPi);
+ cairo_fill_preserve(context);
+ PenColourAlpha(fillStroke.stroke.colour);
+ cairo_set_line_width(context, fillStroke.stroke.width);
+ cairo_stroke(context);
+}
+
void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
SurfaceImpl &surfi = static_cast<SurfaceImpl &>(surfaceSource);
const bool canDraw = surfi.psurf != nullptr;
diff --git a/qt/ScintillaEditBase/PlatQt.cpp b/qt/ScintillaEditBase/PlatQt.cpp
index a8553e658..6c7f789ce 100644
--- a/qt/ScintillaEditBase/PlatQt.cpp
+++ b/qt/ScintillaEditBase/PlatQt.cpp
@@ -230,11 +230,24 @@ void SurfaceImpl::PenColour(ColourDesired fore)
GetPainter()->setPen(penOutline);
}
+void SurfaceImpl::PenColourWidth(ColourAlpha fore, XYPOSITION strokeWidth) {
+ QPen penOutline(QColorFromColourAlpha(fore));
+ penOutline.setCapStyle(Qt::FlatCap);
+ penOutline.setJoinStyle(Qt::MiterJoin);
+ penOutline.setWidthF(strokeWidth);
+ GetPainter()->setPen(penOutline);
+}
+
void SurfaceImpl::BrushColour(ColourDesired back)
{
GetPainter()->setBrush(QBrush(QColorFromCA(back)));
}
+void SurfaceImpl::BrushColour(ColourAlpha back)
+{
+ GetPainter()->setBrush(QBrush(QColorFromColourAlpha(back)));
+}
+
void SurfaceImpl::SetCodec(const Font *font)
{
const FontAndCharacterSet *pfacs = AsFontAndCharacterSet(font);
@@ -304,6 +317,17 @@ void SurfaceImpl::Polygon(Point *pts,
GetPainter()->drawPolygon(&qpts[0], static_cast<int>(npts));
}
+void SurfaceImpl::Polygon(const Point *pts, size_t npts, FillStroke fillStroke)
+{
+ PenColourWidth(fillStroke.stroke.colour, fillStroke.stroke.width);
+ BrushColour(fillStroke.fill.colour);
+
+ std::vector<QPointF> qpts;
+ std::transform(pts, pts + npts, std::back_inserter(qpts), QPointFFromPoint);
+
+ GetPainter()->drawPolygon(&qpts[0], static_cast<int>(npts));
+}
+
void SurfaceImpl::RectangleDraw(PRectangle rc,
ColourDesired fore,
ColourDesired back)
@@ -314,6 +338,14 @@ void SurfaceImpl::RectangleDraw(PRectangle rc,
GetPainter()->drawRect(rect);
}
+void SurfaceImpl::RectangleDraw(PRectangle rc, FillStroke fillStroke)
+{
+ PenColourWidth(fillStroke.stroke.colour, fillStroke.stroke.width);
+ BrushColour(fillStroke.fill.colour);
+ const QRectF rect = QRectFFromPRect(rc.Inset(fillStroke.stroke.width / 2));
+ GetPainter()->drawRect(rect);
+}
+
void SurfaceImpl::FillRectangle(PRectangle rc, ColourDesired back)
{
GetPainter()->fillRect(QRectFFromPRect(rc), QColorFromCA(back));
@@ -352,6 +384,13 @@ void SurfaceImpl::RoundedRectangle(PRectangle rc,
GetPainter()->drawRoundedRect(QRectFFromPRect(RectangleInset(rc, 0.5f)), 3.0f, 3.0f);
}
+void SurfaceImpl::RoundedRectangle(PRectangle rc, FillStroke fillStroke)
+{
+ PenColourWidth(fillStroke.stroke.colour, fillStroke.stroke.width);
+ BrushColour(fillStroke.fill.colour);
+ GetPainter()->drawRoundedRect(QRectFFromPRect(rc), 3.0f, 3.0f);
+}
+
void SurfaceImpl::AlphaRectangle(PRectangle rc,
int cornerSize,
ColourDesired fill,
@@ -469,6 +508,14 @@ void SurfaceImpl::Ellipse(PRectangle rc,
GetPainter()->drawEllipse(QRectFFromPRect(rc));
}
+void SurfaceImpl::Ellipse(PRectangle rc, FillStroke fillStroke)
+{
+ PenColourWidth(fillStroke.stroke.colour, fillStroke.stroke.width);
+ BrushColour(fillStroke.fill.colour);
+ const QRectF rect = QRectFFromPRect(rc.Inset(fillStroke.stroke.width / 2));
+ GetPainter()->drawEllipse(rect);
+}
+
void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource)
{
SurfaceImpl *source = dynamic_cast<SurfaceImpl *>(&surfaceSource);
diff --git a/qt/ScintillaEditBase/PlatQt.h b/qt/ScintillaEditBase/PlatQt.h
index 257dbda56..12f3e7726 100644
--- a/qt/ScintillaEditBase/PlatQt.h
+++ b/qt/ScintillaEditBase/PlatQt.h
@@ -62,6 +62,11 @@ inline Point PointFromQPoint(QPoint qp)
return Point(qp.x(), qp.y());
}
+inline QPointF QPointFFromPoint(Point qp)
+{
+ return QPointF(qp.x, qp.y);
+}
+
constexpr PRectangle RectangleInset(PRectangle rc, XYPOSITION delta) noexcept {
return PRectangle(rc.left + delta, rc.top + delta, rc.right - delta, rc.bottom - delta);
}
@@ -93,6 +98,7 @@ public:
int Supports(int feature) noexcept override;
bool Initialised() override;
void PenColour(ColourDesired fore) override;
+ void PenColourWidth(ColourAlpha fore, XYPOSITION strokeWidth);
int LogPixelsY() override;
int PixelDivisions() override;
int DeviceHeightFont(int points) override;
@@ -100,13 +106,16 @@ public:
void LineTo(int x_, int y_) override;
void Polygon(Point *pts, size_t npts, ColourDesired fore,
ColourDesired back) override;
+ void Polygon(const Point *pts, size_t npts, FillStroke fillStroke) override;
void RectangleDraw(PRectangle rc, ColourDesired fore,
ColourDesired back) override;
+ void RectangleDraw(PRectangle rc, FillStroke fillStroke) override;
void FillRectangle(PRectangle rc, ColourDesired back) override;
void FillRectangle(PRectangle rc, Fill fill) override;
void FillRectangle(PRectangle rc, Surface &surfacePattern) override;
void RoundedRectangle(PRectangle rc, ColourDesired fore,
ColourDesired back) override;
+ void RoundedRectangle(PRectangle rc, FillStroke fillStroke) override;
void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill,
int alphaFill, ColourDesired outline, int alphaOutline, int flags) override;
void AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) override;
@@ -115,6 +124,7 @@ public:
const unsigned char *pixelsImage) override;
void Ellipse(PRectangle rc, ColourDesired fore,
ColourDesired back) override;
+ void Ellipse(PRectangle rc, FillStroke fillStroke) override;
void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
@@ -155,6 +165,7 @@ public:
void SetBidiR2L(bool bidiR2L_) override;
void BrushColour(ColourDesired back);
+ void BrushColour(ColourAlpha back);
void SetCodec(const Font *font);
void SetFont(const Font *font);
diff --git a/src/Platform.h b/src/Platform.h
index 1b9f2a6d1..6848e4d4f 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -189,11 +189,14 @@ public:
virtual void MoveTo(int x_, int y_)=0;
virtual void LineTo(int x_, int y_)=0;
virtual void Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back)=0;
+ virtual void Polygon(const Point *pts, size_t npts, FillStroke fillStroke)=0;
virtual void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back)=0;
+ virtual void RectangleDraw(PRectangle rc, FillStroke fillStroke)=0;
virtual void FillRectangle(PRectangle rc, ColourDesired back)=0;
virtual void FillRectangle(PRectangle rc, Fill fill)=0;
virtual void FillRectangle(PRectangle rc, Surface &surfacePattern)=0;
virtual void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back)=0;
+ virtual void RoundedRectangle(PRectangle rc, FillStroke fillStroke)=0;
virtual void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
ColourDesired outline, int alphaOutline, int flags)=0;
virtual void AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke)=0;
@@ -201,6 +204,7 @@ public:
virtual void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options)=0;
virtual void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) = 0;
virtual void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back)=0;
+ virtual void Ellipse(PRectangle rc, FillStroke fillStroke)=0;
virtual void Copy(PRectangle rc, Point from, Surface &surfaceSource)=0;
virtual std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) = 0;
diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx
index ae819bb69..dacea8522 100644
--- a/win32/PlatWin.cxx
+++ b/win32/PlatWin.cxx
@@ -459,7 +459,9 @@ class SurfaceGDI : public Surface {
int codePage = 0;
- void BrushColour(ColourDesired back) noexcept;
+ void PenColour(ColourAlpha fore, XYPOSITION widthStroke) noexcept;
+
+ void BrushColour(ColourAlpha back) noexcept;
void SetFont(const Font *font_) noexcept;
void Clear() noexcept;
@@ -487,17 +489,21 @@ public:
void MoveTo(int x_, int y_) override;
void LineTo(int x_, int y_) override;
void Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) override;
+ void Polygon(const Point *pts, size_t npts, FillStroke fillStroke) override;
void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void RectangleDraw(PRectangle rc, FillStroke fillStroke) override;
void FillRectangle(PRectangle rc, ColourDesired back) override;
void FillRectangle(PRectangle rc, Fill fill) override;
void FillRectangle(PRectangle rc, Surface &surfacePattern) override;
void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void RoundedRectangle(PRectangle rc, FillStroke fillStroke) override;
void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
ColourDesired outline, int alphaOutline, int flags) override;
void AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) override;
void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;
void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void Ellipse(PRectangle rc, FillStroke fillStroke) override;
void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
@@ -625,14 +631,36 @@ void SurfaceGDI::PenColour(ColourDesired fore) {
penOld = SelectPen(hdc, pen);
}
-void SurfaceGDI::BrushColour(ColourDesired back) noexcept {
+void SurfaceGDI::PenColour(ColourAlpha fore, XYPOSITION widthStroke) noexcept {
+ if (pen) {
+ ::SelectObject(hdc, penOld);
+ ::DeleteObject(pen);
+ pen = {};
+ penOld = {};
+ }
+ const DWORD penWidth = std::lround(widthStroke);
+ const COLORREF penColour = fore.GetColour().AsInteger();
+ if (widthStroke > 1) {
+ const LOGBRUSH brushParameters{ BS_SOLID, penColour, 0 };
+ pen = ::ExtCreatePen(PS_GEOMETRIC | PS_ENDCAP_ROUND | PS_JOIN_MITER,
+ penWidth,
+ &brushParameters,
+ 0,
+ nullptr);
+ } else {
+ pen = ::CreatePen(PS_INSIDEFRAME, penWidth, penColour);
+ }
+ penOld = SelectPen(hdc, pen);
+}
+
+void SurfaceGDI::BrushColour(ColourAlpha back) noexcept {
if (brush) {
::SelectObject(hdc, brushOld);
::DeleteObject(brush);
brush = {};
brushOld = {};
}
- brush = ::CreateSolidBrush(back.AsInteger());
+ brush = ::CreateSolidBrush(back.GetColour().AsInteger());
brushOld = SelectBrush(hdc, brush);
}
@@ -675,6 +703,14 @@ void SurfaceGDI::Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesi
::Polygon(hdc, outline.data(), static_cast<int>(npts));
}
+void SurfaceGDI::Polygon(const Point *pts, size_t npts, FillStroke fillStroke) {
+ PenColour(fillStroke.stroke.colour.GetColour(), fillStroke.stroke.width);
+ BrushColour(fillStroke.fill.colour.GetColour());
+ std::vector<POINT> outline;
+ std::transform(pts, pts + npts, std::back_inserter(outline), POINTFromPoint);
+ ::Polygon(hdc, outline.data(), static_cast<int>(npts));
+}
+
void SurfaceGDI::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) {
PenColour(fore);
BrushColour(back);
@@ -682,6 +718,11 @@ void SurfaceGDI::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired
::Rectangle(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom);
}
+void SurfaceGDI::RectangleDraw(PRectangle rc, FillStroke fillStroke) {
+ FillRectangle(rc, fillStroke.stroke.colour);
+ FillRectangle(rc.Inset(fillStroke.stroke.width), fillStroke.fill.colour);
+}
+
void SurfaceGDI::FillRectangle(PRectangle rc, ColourDesired back) {
// Using ExtTextOut rather than a FillRect ensures that no dithering occurs.
// There is no need to allocate a brush either.
@@ -698,9 +739,7 @@ void SurfaceGDI::FillRectangle(PRectangle rc, Fill fill) {
::SetBkColor(hdc, fill.colour.GetColour().AsInteger());
::ExtTextOut(hdc, rcw.left, rcw.top, ETO_OPAQUE, &rcw, TEXT(""), 0, nullptr);
} else {
- const ColourDesired fillOpaque = fill.colour.GetColour();
- const int alpha = fill.colour.GetAlpha();
- AlphaRectangle(rc, 0, fillOpaque, alpha, fillOpaque, alpha, 0);
+ AlphaRectangle(rc, 0, FillStroke(fill.colour));
}
}
@@ -726,6 +765,16 @@ void SurfaceGDI::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesir
8, 8);
}
+void SurfaceGDI::RoundedRectangle(PRectangle rc, FillStroke fillStroke) {
+ PenColour(fillStroke.stroke.colour, fillStroke.stroke.width);
+ BrushColour(fillStroke.fill.colour);
+ const RECT rcw = RectFromPRectangle(rc);
+ ::RoundRect(hdc,
+ rcw.left + 1, rcw.top,
+ rcw.right - 1, rcw.bottom,
+ 8, 8);
+}
+
namespace {
constexpr DWORD dwordFromBGRA(byte b, byte g, byte r, byte a) noexcept {
@@ -1020,6 +1069,13 @@ void SurfaceGDI::Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back)
::Ellipse(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom);
}
+void SurfaceGDI::Ellipse(PRectangle rc, FillStroke fillStroke) {
+ PenColour(fillStroke.stroke.colour, fillStroke.stroke.width);
+ BrushColour(fillStroke.fill.colour);
+ const RECT rcw = RectFromPRectangle(rc);
+ ::Ellipse(hdc, rcw.left, rcw.top, rcw.right, rcw.bottom);
+}
+
void SurfaceGDI::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
::BitBlt(hdc,
static_cast<int>(rc.left), static_cast<int>(rc.top),
@@ -1355,18 +1411,23 @@ public:
int DeviceHeightFont(int points) override;
void MoveTo(int x_, int y_) override;
void LineTo(int x_, int y_) override;
+ ID2D1PathGeometry *Geometry(const Point *pts, size_t npts, D2D1_FIGURE_BEGIN figureBegin) noexcept;
void Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) override;
+ void Polygon(const Point *pts, size_t npts, FillStroke fillStroke) override;
void RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void RectangleDraw(PRectangle rc, FillStroke fillStroke) override;
void FillRectangle(PRectangle rc, ColourDesired back) override;
void FillRectangle(PRectangle rc, Fill fill) override;
void FillRectangle(PRectangle rc, Surface &surfacePattern) override;
void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void RoundedRectangle(PRectangle rc, FillStroke fillStroke) override;
void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
ColourDesired outline, int alphaOutline, int flags) override;
void AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) override;
void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) override;
void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) override;
void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back) override;
+ void Ellipse(PRectangle rc, FillStroke fillStroke) override;
void Copy(PRectangle rc, Point from, Surface &surfaceSource) override;
std::unique_ptr<IScreenLineLayout> Layout(const IScreenLine *screenLine) override;
@@ -1610,6 +1671,26 @@ void SurfaceD2D::LineTo(int x_, int y_) {
}
}
+ID2D1PathGeometry *SurfaceD2D::Geometry(const Point *pts, size_t npts, D2D1_FIGURE_BEGIN figureBegin) noexcept {
+ ID2D1PathGeometry *geometry = nullptr;
+ HRESULT hr = pD2DFactory->CreatePathGeometry(&geometry);
+ if (SUCCEEDED(hr) && geometry) {
+ ID2D1GeometrySink *sink = nullptr;
+ hr = geometry->Open(&sink);
+ if (SUCCEEDED(hr) && sink) {
+ sink->BeginFigure(D2D1::Point2F(pts[0].x, pts[0].y), figureBegin);
+ for (size_t i = 1; i < npts; i++) {
+ sink->AddLine(D2D1::Point2F(pts[i].x, pts[i].y));
+ }
+ sink->EndFigure((figureBegin == D2D1_FIGURE_BEGIN_FILLED) ?
+ D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN);
+ sink->Close();
+ ReleaseUnknown(sink);
+ }
+ }
+ return geometry;
+}
+
void SurfaceD2D::Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesired back) {
PLATFORM_ASSERT(pRenderTarget && (npts > 2));
if (pRenderTarget) {
@@ -1639,6 +1720,21 @@ void SurfaceD2D::Polygon(Point *pts, size_t npts, ColourDesired fore, ColourDesi
}
}
+void SurfaceD2D::Polygon(const Point *pts, size_t npts, FillStroke fillStroke) {
+ PLATFORM_ASSERT(pRenderTarget && (npts > 2));
+ if (pRenderTarget) {
+ ID2D1PathGeometry *geometry = Geometry(pts, npts, D2D1_FIGURE_BEGIN_FILLED);
+ PLATFORM_ASSERT(geometry);
+ if (geometry) {
+ D2DPenColourAlpha(fillStroke.fill.colour);
+ pRenderTarget->FillGeometry(geometry, pBrush);
+ D2DPenColourAlpha(fillStroke.stroke.colour);
+ pRenderTarget->DrawGeometry(geometry, pBrush, fillStroke.stroke.width);
+ ReleaseUnknown(geometry);
+ }
+ }
+}
+
void SurfaceD2D::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired back) {
if (pRenderTarget) {
const D2D1_RECT_F rectangle1 = D2D1::RectF(std::round(rc.left) + 0.5f, rc.top+0.5f, std::round(rc.right) - 0.5f, rc.bottom-0.5f);
@@ -1649,6 +1745,20 @@ void SurfaceD2D::RectangleDraw(PRectangle rc, ColourDesired fore, ColourDesired
}
}
+void SurfaceD2D::RectangleDraw(PRectangle rc, FillStroke fillStroke) {
+ if (!pRenderTarget)
+ return;
+ const D2D1_RECT_F rect = RectangleFromPRectangle(rc);
+ const D2D1_RECT_F rectFill = RectangleInset(rect, fillStroke.stroke.width);
+ const float halfStroke = fillStroke.stroke.width / 2.0f;
+ const D2D1_RECT_F rectOutline = RectangleInset(rect, halfStroke);
+
+ D2DPenColourAlpha(fillStroke.fill.colour);
+ pRenderTarget->FillRectangle(&rectFill, pBrush);
+ D2DPenColourAlpha(fillStroke.stroke.colour);
+ pRenderTarget->DrawRectangle(&rectOutline, pBrush, fillStroke.stroke.width);
+}
+
void SurfaceD2D::FillRectangle(PRectangle rc, ColourDesired back) {
if (pRenderTarget) {
D2DPenColour(back);
@@ -1703,6 +1813,22 @@ void SurfaceD2D::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesir
}
}
+void SurfaceD2D::RoundedRectangle(PRectangle rc, FillStroke fillStroke) {
+ if (pRenderTarget) {
+ D2D1_ROUNDED_RECT roundedRectFill = {
+ D2D1::RectF(rc.left+1.0f, rc.top+1.0f, rc.right-1.0f, rc.bottom-1.0f),
+ 4, 4};
+ D2DPenColourAlpha(fillStroke.fill.colour);
+ pRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush);
+
+ D2D1_ROUNDED_RECT roundedRect = {
+ D2D1::RectF(rc.left + 0.5f, rc.top+0.5f, rc.right - 0.5f, rc.bottom-0.5f),
+ 4, 4};
+ D2DPenColourAlpha(fillStroke.stroke.colour);
+ pRenderTarget->DrawRoundedRectangle(roundedRect, pBrush, fillStroke.stroke.width);
+ }
+}
+
void SurfaceD2D::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill,
ColourDesired outline, int alphaOutline, int /* flags*/ ) {
if (pRenderTarget) {
@@ -1837,6 +1963,24 @@ void SurfaceD2D::Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back)
}
}
+void SurfaceD2D::Ellipse(PRectangle rc, FillStroke fillStroke) {
+ if (!pRenderTarget)
+ return;
+ const D2D1_POINT_2F centre = D2D1::Point2F((rc.left + rc.right) / 2.0f, (rc.top + rc.bottom) / 2.0f);
+
+ const FLOAT radiusFill = rc.Width() / 2.0f - fillStroke.stroke.width;
+ const D2D1_ELLIPSE ellipseFill = { centre, radiusFill, radiusFill };
+
+ D2DPenColourAlpha(fillStroke.fill.colour);
+ pRenderTarget->FillEllipse(ellipseFill, pBrush);
+
+ const FLOAT radiusOutline = rc.Width() / 2.0f - fillStroke.stroke.width / 2.0f;
+ const D2D1_ELLIPSE ellipseOutline = { centre, radiusOutline, radiusOutline };
+
+ D2DPenColourAlpha(fillStroke.stroke.colour);
+ pRenderTarget->DrawEllipse(ellipseOutline, pBrush, fillStroke.stroke.width);
+}
+
void SurfaceD2D::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
SurfaceD2D &surfOther = dynamic_cast<SurfaceD2D &>(surfaceSource);
ID2D1Bitmap *pBitmap = nullptr;