aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--cocoa/PlatCocoa.h1
-rw-r--r--cocoa/PlatCocoa.mm44
-rw-r--r--curses/ScintillaCurses.cxx3
-rw-r--r--gtk/PlatGTK.cxx27
-rw-r--r--include/Platform.h74
-rw-r--r--qt/ScintillaEditBase/PlatQt.cpp20
-rw-r--r--qt/ScintillaEditBase/PlatQt.h6
-rw-r--r--win32/PlatWin.cxx58
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)