diff options
-rw-r--r-- | cocoa/PlatCocoa.h | 2 | ||||
-rw-r--r-- | cocoa/PlatCocoa.mm | 60 | ||||
-rwxr-xr-x | gtk/PlatGTK.cxx | 26 | ||||
-rw-r--r-- | qt/ScintillaEditBase/PlatQt.cpp | 32 | ||||
-rw-r--r-- | qt/ScintillaEditBase/PlatQt.h | 1 | ||||
-rw-r--r-- | src/Platform.h | 1 | ||||
-rw-r--r-- | win32/PlatWin.cxx | 87 |
7 files changed, 209 insertions, 0 deletions
diff --git a/cocoa/PlatCocoa.h b/cocoa/PlatCocoa.h index 98a9b11ed..f50c0e7f7 100644 --- a/cocoa/PlatCocoa.h +++ b/cocoa/PlatCocoa.h @@ -62,6 +62,7 @@ private: void FillColour(const ColourDesired &back); void FillColour(ColourAlpha fill); + void PenColourAlpha(ColourAlpha fore); // 24-bit RGB+A bitmap data constants static const int BITS_PER_COMPONENT = 8; @@ -101,6 +102,7 @@ public: void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) 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; diff --git a/cocoa/PlatCocoa.mm b/cocoa/PlatCocoa.mm index a4f01c5c6..714e4f484 100644 --- a/cocoa/PlatCocoa.mm +++ b/cocoa/PlatCocoa.mm @@ -502,6 +502,17 @@ void SurfaceImpl::FillColour(const ColourDesired &back) { //-------------------------------------------------------------------------------------------------- +void SurfaceImpl::PenColourAlpha(ColourAlpha fore) { + // Set the Stroke color to match + CGContextSetRGBStrokeColor(gc, + fore.GetRedComponent(), + fore.GetGreenComponent(), + fore.GetBlueComponent(), + fore.GetAlphaComponent()); +} + +//-------------------------------------------------------------------------------------------------- + CGImageRef SurfaceImpl::CreateImage() { // For now, assume that CreateImage can only be called on PixMap surfaces. if (!bitmapData) @@ -895,6 +906,55 @@ void Scintilla::SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, Colou } } +void Scintilla::SurfaceImpl::AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) { + if (gc) { + const XYPOSITION halfStroke = fillStroke.stroke.width / 2.0f; + // Set the Fill color to match + FillColour(fillStroke.fill.colour); + PenColourAlpha(fillStroke.stroke.colour); + PRectangle rcFill = rc; + if (cornerSize == 0) { + // A simple rectangle, no rounded corners + if (fillStroke.fill.colour == fillStroke.stroke.colour) { + // Optimization for simple case + CGRect rect = PRectangleToCGRect(rcFill); + CGContextFillRect(gc, rect); + } else { + rcFill.left += fillStroke.stroke.width; + rcFill.top += fillStroke.stroke.width; + rcFill.right -= fillStroke.stroke.width; + rcFill.bottom -= fillStroke.stroke.width; + CGRect rect = PRectangleToCGRect(rcFill); + CGContextFillRect(gc, rect); + CGContextAddRect(gc, CGRectMake(rc.left + halfStroke, rc.top + halfStroke, + rc.Width() - fillStroke.stroke.width, rc.Height() - fillStroke.stroke.width)); + CGContextStrokeRectWithWidth(gc, + CGRectMake(rc.left + halfStroke, rc.top + halfStroke, + rc.Width() - fillStroke.stroke.width, rc.Height() - fillStroke.stroke.width), + fillStroke.stroke.width); + } + } else { + // Approximate rounded corners with 45 degree chamfers. + // Drawing real circular arcs often leaves some over- or under-drawn pixels. + if (fillStroke.fill.colour == fillStroke.stroke.colour) { + // Specializing this case avoids a few stray light/dark pixels in corners. + rcFill.left -= halfStroke; + rcFill.top -= halfStroke; + rcFill.right += halfStroke; + rcFill.bottom += halfStroke; + DrawChamferedRectangle(gc, rcFill, cornerSize, kCGPathFill); + } else { + rcFill.left += halfStroke; + rcFill.top += halfStroke; + rcFill.right -= halfStroke; + rcFill.bottom -= halfStroke; + DrawChamferedRectangle(gc, rcFill, cornerSize-fillStroke.stroke.width, kCGPathFill); + DrawChamferedRectangle(gc, rc, cornerSize, kCGPathStroke); + } + } + } +} + void Scintilla::SurfaceImpl::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) { if (!gc) { return; diff --git a/gtk/PlatGTK.cxx b/gtk/PlatGTK.cxx index 3debf8fc8..b1079a31a 100755 --- a/gtk/PlatGTK.cxx +++ b/gtk/PlatGTK.cxx @@ -169,6 +169,7 @@ public: void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) 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; @@ -592,6 +593,31 @@ void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fi } } +void SurfaceImpl::AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) { + if (context && rc.Width() > 0) { + const float halfStroke = fillStroke.stroke.width / 2.0f; + const float doubleStroke = fillStroke.stroke.width * 2.0f; + PenColourAlpha(fillStroke.fill.colour); + if (cornerSize > 0) + PathRoundRectangle(context, rc.left + fillStroke.stroke.width, rc.top + fillStroke.stroke.width, + rc.Width() - doubleStroke, rc.Height() - doubleStroke, cornerSize); + else + cairo_rectangle(context, rc.left + fillStroke.stroke.width, rc.top + fillStroke.stroke.width, + rc.Width() - doubleStroke, rc.Height() - doubleStroke); + cairo_fill(context); + + PenColourAlpha(fillStroke.stroke.colour); + if (cornerSize > 0) + PathRoundRectangle(context, rc.left + halfStroke, rc.top + halfStroke, + rc.Width() - fillStroke.stroke.width, rc.Height() - fillStroke.stroke.width, cornerSize); + else + cairo_rectangle(context, rc.left + halfStroke, rc.top + halfStroke, + rc.Width() - fillStroke.stroke.width, rc.Height() - fillStroke.stroke.width); + cairo_set_line_width(context, fillStroke.stroke.width); + cairo_stroke(context); + } +} + void SurfaceImpl::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) { if (context) { cairo_pattern_t *pattern; diff --git a/qt/ScintillaEditBase/PlatQt.cpp b/qt/ScintillaEditBase/PlatQt.cpp index 37fc409f7..a8553e658 100644 --- a/qt/ScintillaEditBase/PlatQt.cpp +++ b/qt/ScintillaEditBase/PlatQt.cpp @@ -390,6 +390,38 @@ void SurfaceImpl::AlphaRectangle(PRectangle rc, } } +void SurfaceImpl::AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) +{ + QColor qFill = QColorFromColourAlpha(fillStroke.fill.colour); + QBrush brushFill(qFill); + GetPainter()->setBrush(brushFill); + if (fillStroke.fill.colour == fillStroke.stroke.colour) { + painter->setPen(Qt::NoPen); + QRectF rect = QRectFFromPRect(rc); + if (cornerSize > 0.0f) { + // A radius of 1 shows no curve so add 1 + qreal radius = cornerSize+1; + GetPainter()->drawRoundedRect(rect, radius, radius); + } else { + GetPainter()->fillRect(rect, brushFill); + } + } else { + QColor qOutline = QColorFromColourAlpha(fillStroke.stroke.colour); + QPen penOutline(qOutline); + penOutline.setWidthF(fillStroke.stroke.width); + GetPainter()->setPen(penOutline); + + QRectF rect = QRectFFromPRect(rc.Inset(fillStroke.stroke.width / 2)); + if (cornerSize > 0.0f) { + // A radius of 1 shows no curve so add 1 + qreal radius = cornerSize+1; + GetPainter()->drawRoundedRect(rect, radius, radius); + } else { + GetPainter()->drawRect(rect); + } + } +} + void SurfaceImpl::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) { QRectF rect = QRectFFromPRect(rc); QLinearGradient linearGradient; diff --git a/qt/ScintillaEditBase/PlatQt.h b/qt/ScintillaEditBase/PlatQt.h index 173bd53d5..257dbda56 100644 --- a/qt/ScintillaEditBase/PlatQt.h +++ b/qt/ScintillaEditBase/PlatQt.h @@ -109,6 +109,7 @@ public: ColourDesired back) 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; diff --git a/src/Platform.h b/src/Platform.h index abad8abfa..1b9f2a6d1 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -196,6 +196,7 @@ public: virtual void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back)=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; enum class GradientOptions { leftToRight, topToBottom }; 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; diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index 3fae8e90f..ae819bb69 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -494,6 +494,7 @@ public: void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) 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; @@ -908,6 +909,55 @@ void SurfaceGDI::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fil } } +void SurfaceGDI::AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) { + // TODO: Implement strokeWidth + const RECT rcw = RectFromPRectangle(rc); + const SIZE size = SizeOfRect(rcw); + + if (size.cx > 0) { + + DIBSection section(hdc, size); + + if (section) { + + // Ensure not distorted too much by corners when small + const LONG corner = std::min(static_cast<LONG>(cornerSize), (std::min(size.cx, size.cy) / 2) - 2); + + constexpr DWORD valEmpty = dwordFromBGRA(0,0,0,0); + const DWORD valFill = dwordMultiplied(fillStroke.fill.colour, fillStroke.fill.colour.GetAlpha()); + const DWORD valOutline = dwordMultiplied(fillStroke.stroke.colour, fillStroke.stroke.colour.GetAlpha()); + + // Draw a framed rectangle + for (int y=0; y<size.cy; y++) { + for (int x=0; x<size.cx; x++) { + if ((x==0) || (x==size.cx-1) || (y == 0) || (y == size.cy -1)) { + section.SetPixel(x, y, valOutline); + } else { + section.SetPixel(x, y, valFill); + } + } + } + + // Make the corners transparent + for (LONG c=0; c<corner; c++) { + for (LONG x=0; x<c+1; x++) { + section.SetSymmetric(x, c - x, valEmpty); + } + } + + // Draw the corner frame pieces + for (LONG x=1; x<corner; x++) { + section.SetSymmetric(x, corner - x, valOutline); + } + + AlphaBlend(hdc, rcw.left, rcw.top, size.cx, size.cy, section.DC(), 0, 0, size.cx, size.cy, mergeAlpha); + } + } else { + BrushColour(fillStroke.stroke.colour); + FrameRect(hdc, &rcw, brush); + } +} + void SurfaceGDI::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) { const RECT rcw = RectFromPRectangle(rc); @@ -1242,6 +1292,14 @@ constexpr D2D_COLOR_F ColorFromColourAlpha(ColourAlpha colour) noexcept { }; } +constexpr D2D1_RECT_F RectangleInset(D2D1_RECT_F rect, FLOAT inset) noexcept { + return D2D1_RECT_F{ + rect.left + inset, + rect.top + inset, + rect.right - inset, + rect.bottom - inset }; +} + } class BlobInline; @@ -1305,6 +1363,7 @@ public: void RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesired back) 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; @@ -1673,6 +1732,34 @@ void SurfaceD2D::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fil } } +void SurfaceD2D::AlphaRectangle(PRectangle rc, XYPOSITION cornerSize, FillStroke fillStroke) { + 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); + if (pRenderTarget) { + if (cornerSize == 0) { + // When corner size is zero, draw square rectangle to prevent blurry pixels at corners + D2DPenColourAlpha(fillStroke.fill.colour); + pRenderTarget->FillRectangle(rectFill, pBrush); + + D2DPenColourAlpha(fillStroke.stroke.colour); + pRenderTarget->DrawRectangle(rectOutline, pBrush, fillStroke.stroke.width); + } else { + const float cornerSizeF = static_cast<float>(cornerSize); + D2D1_ROUNDED_RECT roundedRectFill = { + rectFill, cornerSizeF - 1.0f, cornerSizeF - 1.0f }; + D2DPenColourAlpha(fillStroke.fill.colour); + pRenderTarget->FillRoundedRectangle(roundedRectFill, pBrush); + + D2D1_ROUNDED_RECT roundedRect = { + rectOutline, cornerSizeF, cornerSizeF}; + D2DPenColourAlpha(fillStroke.stroke.colour); + pRenderTarget->DrawRoundedRectangle(roundedRect, pBrush, fillStroke.stroke.width); + } + } +} + void SurfaceD2D::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) { if (pRenderTarget) { D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lgbp { |