diff options
author | Neil <nyamatongwe@gmail.com> | 2021-03-20 17:18:10 +1100 |
---|---|---|
committer | Neil <nyamatongwe@gmail.com> | 2021-03-20 17:18:10 +1100 |
commit | 1f630daf4b61ea12bc2ec2d688dba2586c172611 (patch) | |
tree | 9f76118ba231a193b7077f029bcef7f43421454a /src | |
parent | 434ea41aa400557964d73867008cfa8b1f64d8c4 (diff) | |
download | scintilla-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.cxx | 195 |
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); } } |