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 { | 
