diff options
-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) |