diff options
| author | Neil <nyamatongwe@gmail.com> | 2018-05-22 09:08:54 +1000 | 
|---|---|---|
| committer | Neil <nyamatongwe@gmail.com> | 2018-05-22 09:08:54 +1000 | 
| commit | 2e2e70c7ee89b353ad0e6ef409cb9621729d72f8 (patch) | |
| tree | d5899867d76d457bda2c48dd298b6f41a0217ad1 | |
| parent | cc5e63013d7aa447a74fafba0ee4dd560b6952c7 (diff) | |
| download | scintilla-mirror-2e2e70c7ee89b353ad0e6ef409cb9621729d72f8.tar.gz | |
Add GradientRectangle method to Surface to draw rectangles with vertical or
horizontal gradients.
| -rw-r--r-- | cocoa/PlatCocoa.h | 1 | ||||
| -rw-r--r-- | cocoa/PlatCocoa.mm | 44 | ||||
| -rw-r--r-- | gtk/PlatGTK.cxx | 27 | ||||
| -rw-r--r-- | include/Platform.h | 74 | ||||
| -rw-r--r-- | qt/ScintillaEditBase/PlatQt.cpp | 20 | ||||
| -rw-r--r-- | qt/ScintillaEditBase/PlatQt.h | 6 | ||||
| -rw-r--r-- | win32/PlatWin.cxx | 58 | 
7 files changed, 226 insertions, 4 deletions
diff --git a/cocoa/PlatCocoa.h b/cocoa/PlatCocoa.h index 6a9bada96..940de83d5 100644 --- a/cocoa/PlatCocoa.h +++ b/cocoa/PlatCocoa.h @@ -94,6 +94,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 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 Copy(PRectangle rc, Scintilla::Point from, Surface &surfaceSource) override; diff --git a/cocoa/PlatCocoa.mm b/cocoa/PlatCocoa.mm index 0e1e1544c..7d7eafb42 100644 --- a/cocoa/PlatCocoa.mm +++ b/cocoa/PlatCocoa.mm @@ -641,6 +641,50 @@ void Scintilla::SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, Colou  	}  } +void Scintilla::SurfaceImpl::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) { +	if (!gc) { +		return; +	} + +	CGPoint ptStart = CGPointMake(rc.left, rc.top); +	CGPoint ptEnd = CGPointMake(rc.left, rc.bottom); +	if (options == GradientOptions::leftToRight) { +		ptEnd = CGPointMake(rc.right, rc.top); +	} + +	std::vector<CGFloat> components; +	std::vector<CGFloat> locations; +	for (const ColourStop &stop : stops) { +		locations.push_back(stop.position); +		components.push_back(stop.colour.GetRedComponent()); +		components.push_back(stop.colour.GetGreenComponent()); +		components.push_back(stop.colour.GetBlueComponent()); +		components.push_back(stop.colour.GetAlphaComponent()); +	} + +	CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); +	if (!colorSpace) { +		return; +	} + +	CGGradientRef gradiantRef = CGGradientCreateWithColorComponents(colorSpace, +									components.data(), +									locations.data(), +									locations.size()); +	if (gradiantRef) { +		CGContextSaveGState(gc); +		CGRect rect = PRectangleToCGRect(rc); +		CGContextClipToRect(gc, rect); +		CGContextBeginPath(gc); +		CGContextAddRect(gc, rect); +		CGContextClosePath(gc); +		CGContextDrawLinearGradient(gc, gradiantRef, ptStart, ptEnd, 0); +		CGGradientRelease(gradiantRef); +		CGContextRestoreGState(gc); +	} +	CGColorSpaceRelease(colorSpace); +} +  static void ProviderReleaseData(void *, const void *data, size_t) {  	const unsigned char *pixels = static_cast<const unsigned char *>(data);  	delete []pixels; diff --git a/gtk/PlatGTK.cxx b/gtk/PlatGTK.cxx index 42db59903..0f5ea58d9 100644 --- a/gtk/PlatGTK.cxx +++ b/gtk/PlatGTK.cxx @@ -161,6 +161,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 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 Copy(PRectangle rc, Point from, Surface &surfaceSource) override; @@ -545,6 +546,32 @@ void SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fi  	}  } +void SurfaceImpl::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) { +	if (context) { +		cairo_pattern_t *pattern; +		switch (options) { +		case GradientOptions::leftToRight: +			pattern = cairo_pattern_create_linear(rc.left, rc.top, rc.right, rc.top); +			break; +		case GradientOptions::topToBottom: +		default: +			pattern = cairo_pattern_create_linear(rc.left, rc.top, rc.left, rc.bottom); +			break; +		} +		for (const ColourStop &stop : stops) { +			cairo_pattern_add_color_stop_rgba(pattern, stop.position, +				stop.colour.GetRedComponent(), +				stop.colour.GetGreenComponent(), +				stop.colour.GetBlueComponent(), +				stop.colour.GetAlphaComponent()); +		} +		cairo_rectangle(context, rc.left, rc.top, rc.Width(), rc.Height()); +		cairo_set_source(context, pattern); +		cairo_fill(context); +		cairo_pattern_destroy(pattern); +	} +} +  void SurfaceImpl::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) {  	PLATFORM_ASSERT(context);  	if (rc.Width() > width) diff --git a/include/Platform.h b/include/Platform.h index 0b730944f..e83b8e5e5 100644 --- a/include/Platform.h +++ b/include/Platform.h @@ -164,12 +164,13 @@ public:  };  /** - * Holds a desired RGB colour. + * Holds an RGB colour with 8 bits for each component.   */ +constexpr const float componentMaximum = 255.0f;  class ColourDesired {  	int co;  public: -	ColourDesired(int co_=0) noexcept : co(co_) { +	explicit ColourDesired(int co_=0) noexcept : co(co_) {  	}  	ColourDesired(unsigned int red, unsigned int green, unsigned int blue) noexcept : @@ -184,17 +185,80 @@ public:  		return co;  	} +	// Red, green and blue values as bytes 0..255  	unsigned char GetRed() const noexcept {  		return co & 0xff;  	} -  	unsigned char GetGreen() const noexcept {  		return (co >> 8) & 0xff;  	} -  	unsigned char GetBlue() const noexcept {  		return (co >> 16) & 0xff;  	} + +	// Red, green and blue values as float 0..1.0 +	float GetRedComponent() const noexcept { +		return GetRed() / componentMaximum; +	} +	float GetGreenComponent() const noexcept { +		return GetGreen() / componentMaximum; +	} +	float GetBlueComponent() const noexcept { +		return GetBlue() / componentMaximum; +	} +}; + +/** +* Holds an RGBA colour. +*/ +class ColourAlpha : public ColourDesired { +public: +	explicit ColourAlpha(int co_ = 0) noexcept : ColourDesired(co_) { +	} + +	ColourAlpha(unsigned int red, unsigned int green, unsigned int blue) noexcept : +		ColourDesired(red | (green << 8) | (blue << 16)) { +	} + +	ColourAlpha(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha) noexcept : +		ColourDesired(red | (green << 8) | (blue << 16) | (alpha << 24)) { +	} + +	ColourAlpha(ColourDesired cd, unsigned int alpha) noexcept : +		ColourDesired(cd.AsInteger() | (alpha << 24)) { +	} + +	ColourDesired GetColour() const noexcept { +		return ColourDesired(AsInteger() & 0xffffff); +	} + +	unsigned char GetAlpha() const noexcept { +		return (AsInteger() >> 24) & 0xff; +	} + +	float GetAlphaComponent() const noexcept { +		return GetAlpha() / componentMaximum; +	} + +	ColourAlpha MixedWith(ColourAlpha other) const noexcept { +		const unsigned int red = (GetRed() + other.GetRed()) / 2; +		const unsigned int green = (GetGreen() + other.GetGreen()) / 2; +		const unsigned int blue = (GetBlue() + other.GetBlue()) / 2; +		const unsigned int alpha = (GetAlpha() + other.GetAlpha()) / 2; +		return ColourAlpha(red, green, blue, alpha); +	} +}; + +/** +* Holds an element of a gradient with an RGBA colour and a relative position. +*/ +class ColourStop { +public: +	float position; +	ColourAlpha colour; +	ColourStop(float position_, ColourAlpha colour_) noexcept : +		position(position_), colour(colour_) { +	}  };  /** @@ -284,6 +348,8 @@ 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; +	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;  	virtual void Ellipse(PRectangle rc, ColourDesired fore, ColourDesired back)=0;  	virtual void Copy(PRectangle rc, Point from, Surface &surfaceSource)=0; diff --git a/qt/ScintillaEditBase/PlatQt.cpp b/qt/ScintillaEditBase/PlatQt.cpp index 0a4609acc..44082c77a 100644 --- a/qt/ScintillaEditBase/PlatQt.cpp +++ b/qt/ScintillaEditBase/PlatQt.cpp @@ -353,6 +353,26 @@ void SurfaceImpl::AlphaRectangle(PRectangle rc,  	GetPainter()->drawRoundedRect(rect, radius, radius);  } +void SurfaceImpl::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) { +	QRectF rect = QRectFFromPRect(rc); +	QLinearGradient linearGradient; +	switch (options) { +	case GradientOptions::leftToRight: +		linearGradient = QLinearGradient(rc.left, rc.top, rc.right, rc.top); +		break; +	case GradientOptions::topToBottom: +	default: +		linearGradient = QLinearGradient(rc.left, rc.top, rc.left, rc.bottom); +		break; +	} +	linearGradient.setSpread(QGradient::RepeatSpread); +	for (const ColourStop &stop : stops) { +		linearGradient.setColorAt(stop.position, QColorFromColourAlpha(stop.colour)); +	} +	QBrush brush = QBrush(linearGradient); +	GetPainter()->fillRect(rect, brush); +} +  static std::vector<unsigned char> ImageByteSwapped(int width, int height, const unsigned char *pixelsImage)  {  	// Input is RGBA, but Format_ARGB32 is BGRA, so swap the red bytes and blue bytes diff --git a/qt/ScintillaEditBase/PlatQt.h b/qt/ScintillaEditBase/PlatQt.h index 0cd62cb8b..6baa12877 100644 --- a/qt/ScintillaEditBase/PlatQt.h +++ b/qt/ScintillaEditBase/PlatQt.h @@ -31,6 +31,11 @@ inline QColor QColorFromCA(ColourDesired ca)  	return QColor(c & 0xff, (c >> 8) & 0xff, (c >> 16) & 0xff);  } +inline QColor QColorFromColourAlpha(ColourAlpha ca) +{ +	return QColor(ca.GetRed(), ca.GetGreen(), ca.GetBlue(), ca.GetAlpha()); +} +  inline QRect QRectFromPRect(PRectangle pr)  {  	return QRect(pr.left, pr.top, pr.Width(), pr.Height()); @@ -91,6 +96,7 @@ public:  		ColourDesired back) override;  	void AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill,  		int alphaFill, ColourDesired outline, int alphaOutline, int flags) 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, diff --git a/win32/PlatWin.cxx b/win32/PlatWin.cxx index 83bf54178..81e5b827b 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -561,6 +561,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 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 Copy(PRectangle rc, Point from, Surface &surfaceSource) override; @@ -853,6 +854,13 @@ void SurfaceGDI::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fil  	}  } +void SurfaceGDI::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions) { +	// Would be better to average start and end. +	const ColourAlpha colourAverage = stops[0].colour.MixedWith(stops[1].colour); +	AlphaRectangle(rc, 0, colourAverage.GetColour(), colourAverage.GetAlpha(), +		colourAverage.GetColour(), colourAverage.GetAlpha(), 0); +} +  void SurfaceGDI::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) {  	if (rc.Width() > 0) {  		HDC hMemDC = ::CreateCompatibleDC(hdc); @@ -1123,6 +1131,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 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 Copy(PRectangle rc, Point from, Surface &surfaceSource) override; @@ -1462,6 +1471,55 @@ void SurfaceD2D::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fil  	}  } +namespace { + +D2D_COLOR_F ColorFromColourAlpha(ColourAlpha colour) noexcept { +	D2D_COLOR_F col; +	col.r = colour.GetRedComponent(); +	col.g = colour.GetGreenComponent(); +	col.b = colour.GetBlueComponent(); +	col.a = colour.GetAlphaComponent(); +	return col; +} + +} + +void SurfaceD2D::GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, GradientOptions options) { +	if (pRenderTarget) { +		D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lgbp; +		lgbp.startPoint = D2D1::Point2F(rc.left, rc.top); +		switch (options) { +		case GradientOptions::leftToRight: +			lgbp.endPoint = D2D1::Point2F(rc.right, rc.top); +			break; +		case GradientOptions::topToBottom: +		default: +			lgbp.endPoint = D2D1::Point2F(rc.left, rc.bottom); +			break; +		} + +		std::vector<D2D1_GRADIENT_STOP> gradientStops; +		for (const ColourStop &stop : stops) { +			gradientStops.push_back({ stop.position, ColorFromColourAlpha(stop.colour) }); +		} +		ID2D1GradientStopCollection *pGradientStops = nullptr; +		HRESULT hr = pRenderTarget->CreateGradientStopCollection( +			gradientStops.data(), static_cast<UINT32>(gradientStops.size()), &pGradientStops); +		if (FAILED(hr)) { +			return; +		} +		ID2D1LinearGradientBrush *pBrushLinear = nullptr; +		hr = pRenderTarget->CreateLinearGradientBrush( +			lgbp, pGradientStops, &pBrushLinear); +		if (SUCCEEDED(hr)) { +			const D2D1_RECT_F rectangle = D2D1::RectF(round(rc.left), rc.top, round(rc.right), rc.bottom); +			pRenderTarget->FillRectangle(&rectangle, pBrushLinear); +			pBrushLinear->Release(); +		} +		pGradientStops->Release(); +	} +} +  void SurfaceD2D::DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) {  	if (pRenderTarget) {  		if (rc.Width() > width)  | 
