aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorNeil <nyamatongwe@gmail.com>2021-03-20 17:18:10 +1100
committerNeil <nyamatongwe@gmail.com>2021-03-20 17:18:10 +1100
commit1f630daf4b61ea12bc2ec2d688dba2586c172611 (patch)
tree9f76118ba231a193b7077f029bcef7f43421454a /src
parent434ea41aa400557964d73867008cfa8b1f64d8c4 (diff)
downloadscintilla-mirror-1f630daf4b61ea12bc2ec2d688dba2586c172611.tar.gz
Use new Surface APIs that can adapt to different stroke widths and use clipping
to allow more accurate less complex drawing code.
Diffstat (limited to 'src')
-rw-r--r--src/Indicator.cxx195
1 files changed, 102 insertions, 93 deletions
diff --git a/src/Indicator.cxx b/src/Indicator.cxx
index 9a182469d..93bf68ed0 100644
--- a/src/Indicator.cxx
+++ b/src/Indicator.cxx
@@ -20,18 +20,11 @@
#include "Platform.h"
#include "Scintilla.h"
-#include "IntegerRectangle.h"
#include "Indicator.h"
#include "XPM.h"
using namespace Scintilla;
-static PRectangle PixelGridAlign(const PRectangle &rc) noexcept {
- // Move left and right side to nearest pixel to avoid blurry visuals
- return PRectangle(std::round(rc.left), std::floor(rc.top),
- std::round(rc.right), std::floor(rc.bottom));
-}
-
void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, const PRectangle &rcCharacter, State state, int value) const {
StyleAndColour sacDraw = sacNormal;
if (Flags() & SC_INDICFLAG_VALUEFORE) {
@@ -40,32 +33,45 @@ void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &r
if (state == State::hover) {
sacDraw = sacHover;
}
- const IntegerRectangle irc(rc);
- surface->PenColour(sacDraw.fore);
- const int ymid = (irc.bottom + irc.top) / 2;
+
+ const int pixelDivisions = surface->PixelDivisions();
+
+ const XYPOSITION strokeWidth = 1.0f;
+ const XYPOSITION halfWidth = strokeWidth / 2.0f;
+
+ const PRectangle rcAligned(PixelAlignOutside(rc, pixelDivisions));
+ PRectangle rcFullHeightAligned = PixelAlignOutside(rcLine, pixelDivisions);
+ rcFullHeightAligned.left = rcAligned.left;
+ rcFullHeightAligned.right = rcAligned.right;
+
+ const XYPOSITION ymid = PixelAlign(rc.Centre().y, pixelDivisions);
+
+ // This is a reasonable clip for indicators beneath text like underlines
+ PRectangle rcClip = rcAligned;
+ rcClip.bottom = rcFullHeightAligned.bottom;
switch (sacDraw.style) {
case INDIC_SQUIGGLE: {
- const IntegerRectangle ircSquiggle(PixelGridAlign(rc));
- int x = ircSquiggle.left;
- const int xLast = ircSquiggle.right;
- int y = 0;
- surface->MoveTo(x, irc.top + y);
+ surface->SetClip(rcClip);
+ XYPOSITION x = rcAligned.left + halfWidth;
+ const XYPOSITION top = rcAligned.top + halfWidth;
+ const XYPOSITION xLast = rcAligned.right + halfWidth;
+ XYPOSITION y = 0;
+ std::vector<Point> pts;
+ const XYPOSITION pitch = 1 + strokeWidth;
+ pts.emplace_back(x, top + y);
while (x < xLast) {
- if ((x + 2) > xLast) {
- y = 1;
- x = xLast;
- } else {
- x += 2;
- y = 2 - y;
+ x += pitch;
+ y = pitch - y;
+ pts.emplace_back(x, top + y);
}
- surface->LineTo(x, irc.top + y);
- }
+ surface->PolyLine(pts.data(), std::size(pts), Stroke(sacDraw.fore, strokeWidth));
+ surface->PopClip();
}
break;
case INDIC_SQUIGGLEPIXMAP: {
- const PRectangle rcSquiggle = PixelGridAlign(rc);
+ const PRectangle rcSquiggle = PixelAlign(rc, 1);
const int width = std::min(4000, static_cast<int>(rcSquiggle.Width()));
RGBAImage image(width, 3, 1.0, nullptr);
@@ -87,57 +93,61 @@ void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &r
break;
case INDIC_SQUIGGLELOW: {
- surface->MoveTo(irc.left, irc.top);
- int x = irc.left + 3;
+ std::vector<Point> pts;
+ const XYPOSITION top = rcAligned.top + halfWidth;
int y = 0;
- while (x < rc.right) {
- surface->LineTo(x - 1, irc.top + y);
+ XYPOSITION x = std::round(rcAligned.left) + halfWidth;
+ pts.emplace_back(x, top + y);
+ const XYPOSITION pitch = 2 + strokeWidth;
+ x += pitch;
+ while (x < rcAligned.right) {
+ pts.emplace_back(x - 1, top + y);
y = 1 - y;
- surface->LineTo(x, irc.top + y);
- x += 3;
+ pts.emplace_back(x, top + y);
+ x += pitch;
}
- surface->LineTo(irc.right, irc.top + y); // Finish the line
+ pts.emplace_back(rcAligned.right, top + y);
+ surface->PolyLine(pts.data(), std::size(pts), Stroke(sacDraw.fore, strokeWidth));
}
break;
case INDIC_TT: {
- surface->MoveTo(irc.left, ymid);
- int x = irc.left + 5;
- while (x < rc.right) {
- surface->LineTo(x, ymid);
- surface->MoveTo(x-3, ymid);
- surface->LineTo(x-3, ymid+2);
+ surface->SetClip(rcClip);
+ const XYPOSITION yLine = ymid;
+ XYPOSITION x = rcAligned.left + 5.0f;
+ const XYPOSITION pitch = 4 + strokeWidth;
+ while (x < rc.right + pitch) {
+ const PRectangle line(x-pitch, yLine, x, yLine + strokeWidth);
+ surface->FillRectangle(line, sacDraw.fore);
+ const PRectangle tail(x - 2 - strokeWidth, yLine + strokeWidth, x - 2, yLine + strokeWidth * 2);
+ surface->FillRectangle(tail, sacDraw.fore);
x++;
- surface->MoveTo(x, ymid);
- x += 5;
- }
- surface->LineTo(irc.right, ymid); // Finish the line
- if (x - 3 <= rc.right) {
- surface->MoveTo(x-3, ymid);
- surface->LineTo(x-3, ymid+2);
+ x += pitch;
}
+ surface->PopClip();
}
break;
case INDIC_DIAGONAL: {
- int x = irc.left;
+ surface->SetClip(rcClip);
+ XYPOSITION x = rcAligned.left + halfWidth;
+ const XYPOSITION top = rcAligned.top + halfWidth;
+ const XYPOSITION pitch = 3 + strokeWidth;
while (x < rc.right) {
- surface->MoveTo(x, irc.top + 2);
- int endX = x+3;
- int endY = irc.top - 1;
- if (endX > rc.right) {
- endY += endX - irc.right;
- endX = irc.right;
- }
- surface->LineTo(endX, endY);
- x += 4;
+ const XYPOSITION endX = x+3;
+ const XYPOSITION endY = top - 1;
+ surface->LineDraw(Point(x, top + 2), Point(endX, endY), Stroke(sacDraw.fore, strokeWidth));
+ x += pitch;
}
+ surface->PopClip();
}
break;
case INDIC_STRIKE: {
- surface->MoveTo(irc.left, irc.top - 4);
- surface->LineTo(irc.right, irc.top - 4);
+ const XYPOSITION yStrike = std::round(rcLine.Centre().y);
+ const PRectangle rcStrike(
+ rcAligned.left, yStrike, rcAligned.right, yStrike + strokeWidth);
+ surface->FillRectangle(rcStrike, sacDraw.fore);
}
break;
@@ -147,33 +157,28 @@ void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &r
break;
case INDIC_BOX: {
- surface->MoveTo(irc.left, ymid + 1);
- surface->LineTo(irc.right, ymid + 1);
- const int lineTop = static_cast<int>(rcLine.top) + 1;
- surface->LineTo(irc.right, lineTop);
- surface->LineTo(irc.left, lineTop);
- surface->LineTo(irc.left, ymid + 1);
+ PRectangle rcBox = rcFullHeightAligned;
+ rcBox.top = rcBox.top + 1.0f;
+ rcBox.bottom = ymid + 1.0f;
+ surface->RectangleFrame(rcBox, Stroke(ColourAlpha(sacDraw.fore, outlineAlpha), strokeWidth));
}
break;
case INDIC_ROUNDBOX:
case INDIC_STRAIGHTBOX:
case INDIC_FULLBOX: {
- PRectangle rcBox = rcLine;
+ PRectangle rcBox = rcFullHeightAligned;
if (sacDraw.style != INDIC_FULLBOX)
- rcBox.top = rcLine.top + 1;
- rcBox.left = rc.left;
- rcBox.right = rc.right;
- surface->AlphaRectangle(rcBox, (sacDraw.style == INDIC_ROUNDBOX) ? 1 : 0,
- sacDraw.fore, fillAlpha, sacDraw.fore, outlineAlpha, 0);
+ rcBox.top = rcBox.top + 1;
+ surface->AlphaRectangle(rcBox, (sacDraw.style == INDIC_ROUNDBOX) ? 1.0f : 0.0f,
+ FillStroke(ColourAlpha(sacDraw.fore, fillAlpha), ColourAlpha(sacDraw.fore, outlineAlpha), strokeWidth));
}
break;
case INDIC_GRADIENT:
case INDIC_GRADIENTCENTRE: {
- PRectangle rcBox = rc;
- rcBox.top = rcLine.top + 1;
- rcBox.bottom = rcLine.bottom;
+ PRectangle rcBox = rcFullHeightAligned;
+ rcBox.top = rcBox.top + 1;
const Surface::GradientOptions options = Surface::GradientOptions::topToBottom;
const ColourAlpha start(sacDraw.fore, fillAlpha);
const ColourAlpha end(sacDraw.fore, 0);
@@ -194,21 +199,20 @@ void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &r
break;
case INDIC_DOTBOX: {
- PRectangle rcBox = PixelGridAlign(rc);
- rcBox.top = rcLine.top + 1;
- rcBox.bottom = rcLine.bottom;
- const IntegerRectangle ircBox(rcBox);
+ PRectangle rcBox = rcFullHeightAligned;
+ rcBox.top = rcBox.top + 1;
// Cap width at 4000 to avoid large allocations when mistakes made
- const int width = std::min(ircBox.Width(), 4000);
- RGBAImage image(width, ircBox.Height(), 1.0, nullptr);
+ const int width = std::min(static_cast<int>(rcBox.Width()), 4000);
+ const int height = static_cast<int>(rcBox.Height());
+ RGBAImage image(width, height, 1.0, nullptr);
// Draw horizontal lines top and bottom
for (int x=0; x<width; x++) {
- for (int y = 0; y<ircBox.Height(); y += ircBox.Height() - 1) {
+ for (int y = 0; y< height; y += height - 1) {
image.SetPixel(x, y, sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha);
}
}
// Draw vertical lines left and right
- for (int y = 1; y<ircBox.Height(); y++) {
+ for (int y = 1; y<height; y++) {
for (int x=0; x<width; x += width-1) {
image.SetPixel(x, y, sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha);
}
@@ -218,21 +222,25 @@ void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &r
break;
case INDIC_DASH: {
- int x = irc.left;
+ XYPOSITION x = std::floor(rc.left);
+ const XYPOSITION widthDash = 3 + std::round(strokeWidth);
while (x < rc.right) {
- surface->MoveTo(x, ymid);
- surface->LineTo(std::min(x + 4, irc.right), ymid);
- x += 7;
+ const PRectangle rcDash = PRectangle(x, ymid,
+ x + widthDash, ymid + std::round(strokeWidth));
+ surface->FillRectangle(rcDash, sacDraw.fore);
+ x += 3 + widthDash;
}
}
break;
case INDIC_DOTS: {
- int x = irc.left;
- while (x < irc.right) {
- const PRectangle rcDot = PRectangle::FromInts(x, ymid, x + 1, ymid + 1);
+ const XYPOSITION widthDot = std::round(strokeWidth);
+ XYPOSITION x = std::floor(rc.left);
+ while (x < rc.right) {
+ const PRectangle rcDot = PRectangle(x, ymid,
+ x + widthDot, ymid + widthDot);
surface->FillRectangle(rcDot, sacDraw.fore);
- x += 2;
+ x += widthDot * 2;
}
}
break;
@@ -254,21 +262,22 @@ void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &r
if (rcCharacter.Width() >= 0.1) {
const XYPOSITION pixelHeight = std::floor(rc.Height() - 1.0f); // 1 pixel onto next line if multiphase
const XYPOSITION x = (sacDraw.style == INDIC_POINT) ? (rcCharacter.left) : ((rcCharacter.right + rcCharacter.left) / 2);
- const XYPOSITION ix = std::round(x);
- const XYPOSITION iy = std::floor(rc.top + 1.0f);
- Point pts[] = {
+ // 0.5f is to hit midpoint of pixels:
+ const XYPOSITION ix = std::round(x) + 0.5f;
+ const XYPOSITION iy = std::floor(rc.top + 1.0f) + 0.5f;
+ const Point pts[] = {
Point(ix - pixelHeight, iy + pixelHeight), // Left
Point(ix + pixelHeight, iy + pixelHeight), // Right
Point(ix, iy) // Top
};
- surface->Polygon(pts, std::size(pts), sacDraw.fore, sacDraw.fore);
+ surface->Polygon(pts, std::size(pts), FillStroke(sacDraw.fore));
}
break;
default:
// Either INDIC_PLAIN or unknown
- surface->MoveTo(irc.left, ymid);
- surface->LineTo(irc.right, ymid);
+ surface->FillRectangle(PRectangle(rcAligned.left, ymid,
+ rcAligned.right, ymid + std::round(strokeWidth)), sacDraw.fore);
}
}