diff options
author | mitchell <unknown> | 2018-05-25 17:44:28 -0400 |
---|---|---|
committer | mitchell <unknown> | 2018-05-25 17:44:28 -0400 |
commit | fba451a1d76be9b6945c9d5d7e219f7d7d1a4260 (patch) | |
tree | 0e1aa6f197c34e6c1a9782e264359c220ed014a7 | |
parent | 9f00df4e57e917035d2f939ce70ccf2ee83a9725 (diff) | |
download | scintilla-mirror-fba451a1d76be9b6945c9d5d7e219f7d7d1a4260.tar.gz |
Backport: Add GradientRectangle method to Surface to draw rectangles with vertical or horizontal gradients.
Backport of changeset 6965:90c71d69e3b6.
-rw-r--r-- | cocoa/PlatCocoa.h | 1 | ||||
-rw-r--r-- | cocoa/PlatCocoa.mm | 44 | ||||
-rw-r--r-- | curses/ScintillaCurses.cxx | 3 | ||||
-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 |
8 files changed, 229 insertions, 4 deletions
diff --git a/cocoa/PlatCocoa.h b/cocoa/PlatCocoa.h index 00b4ce2ae..c9b3a8355 100644 --- a/cocoa/PlatCocoa.h +++ b/cocoa/PlatCocoa.h @@ -96,6 +96,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 b517a7e6a..65481c252 100644 --- a/cocoa/PlatCocoa.mm +++ b/cocoa/PlatCocoa.mm @@ -690,6 +690,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/curses/ScintillaCurses.cxx b/curses/ScintillaCurses.cxx index 1a04912a1..f5c55ed21 100644 --- a/curses/ScintillaCurses.cxx +++ b/curses/ScintillaCurses.cxx @@ -343,6 +343,9 @@ public: mvwchgat(win, y, x, 1, attrs, term_color_pair(fore, fill), NULL); } } + /** Drawing gradients is not implemented. */ + void GradientRectangle(PRectangle rc, const std::vector<ColourStop> &stops, + GradientOptions options) {} /** Drawing images is not implemented. */ void DrawRGBAImage(PRectangle rc, int width, int height, const unsigned char *pixelsImage) {} diff --git a/gtk/PlatGTK.cxx b/gtk/PlatGTK.cxx index 539402b01..26e1b5f58 100644 --- a/gtk/PlatGTK.cxx +++ b/gtk/PlatGTK.cxx @@ -160,6 +160,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; @@ -544,6 +545,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 0cbca2f40..7b2fe2119 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 26890e4b7..1f4c3fb05 100644 --- a/qt/ScintillaEditBase/PlatQt.cpp +++ b/qt/ScintillaEditBase/PlatQt.cpp @@ -349,6 +349,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 ed51cf598..345538bb0 100644 --- a/qt/ScintillaEditBase/PlatQt.h +++ b/qt/ScintillaEditBase/PlatQt.h @@ -32,6 +32,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()); @@ -92,6 +97,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 7f9f06ef6..54ef2c295 100644 --- a/win32/PlatWin.cxx +++ b/win32/PlatWin.cxx @@ -560,6 +560,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; @@ -852,6 +853,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); @@ -1121,6 +1129,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; @@ -1460,6 +1469,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) |