Re: Decreasing seams in scaled windows
Neil <[email protected]> Thu, 21 May 2026 23:03:16 -0700 (PDT)
| Newsgroups | gmane.comp.lib.scintilla.devel |
|---|---|
| Message-ID | <[email protected]> |
------=_Part_27316_912696815.1779429796946 Content-Type: multipart/alternative; boundary="----=_Part_27317_1524870078.1779429796946" ------=_Part_27317_1524870078.1779429796946 Content-Type: text/plain; charset="UTF-8" Implemented scaling for Qt with the attached patch. It works, like John suggested, drawing into a full size bitmap then copying that to the window. An image showing both the new implementation (left) and the old (right) at 150% scaling can be seen at https://www.scintilla.org/QtScale.png . Just setting the painter scale to 1/scalingFactor should have worked but still produces seams. It's possible that scaling is being performed twice here. This does some extra work since the entire Scintilla window is redrawn for every change so can be slow although it is mostly adequate. There is some commented-out code in Window::InvalidateRectangle that tried to redraw just the rectangle needed but, while this almost worked, there were occasional single pixel line debris when selecting with the mouse. It is likely that scaling will need to be applied in more places. The code builds and works with Qt 6.10.2 on both Linux (Ubuntu 26.04 Wayland) and Windows 10. It will likely need #if to avoid new APIs for projects still with Qt 5.x. There is an updated version of Haven that can test this at https://www.scintilla.org/HavenScale.zip . It uses a dark background as that makes seams more prominent when the system is in standard light 'daytime' mode. Since this will need Qt version checks and may decrease performance, it is likely this will be delivered behind a preprocessor switch so downstream projects can opt into the change. Neil -- You received this message because you are subscribed to the Google Groups "scintilla-interest" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion visit https://groups.google.com/d/msgid/scintilla-interest/52afcff1-cb9b-4691-a0b5-58cb0e1eaf1dn%40googlegroups.com. ------=_Part_27317_1524870078.1779429796946 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Implemented scaling for Qt with the attached patch. It works, like John sug= gested, drawing into a full size bitmap then copying that to the window.<di= v><br /></div><div>An image showing both the new implementation (left) and = the old (right) at 150% scaling can be seen at=C2=A0https://www.scintilla.o= rg/QtScale.png .</div><div><br /></div><div>Just setting the painter scale = to 1/scalingFactor should have worked but still produces seams. It's possib= le that scaling is being performed twice here.</div><div><br /></div><div>T= his does some extra work since the entire Scintilla window is redrawn for e= very change so can be slow although it is mostly adequate. There is some co= mmented-out code in=C2=A0Window::InvalidateRectangle that tried to redraw j= ust the rectangle needed but, while this almost worked, there were occasion= al single pixel line debris when selecting with the mouse. It is likely tha= t scaling will need to be applied in more places.</div><div><br /></div><di= v>The code builds and works with Qt 6.10.2 on both Linux (Ubuntu 26.04 Wayl= and) and Windows 10. It will likely need #if to avoid new APIs for projects= still with Qt 5.x.</div><div><br /></div><div>There is an updated version = of Haven that can test this at=C2=A0https://www.scintilla.org/HavenScale.zi= p . It uses a dark background as that makes seams more prominent when the s= ystem is in standard light 'daytime' mode.</div><div><br /></div><div>Since= this will need Qt version checks and may decrease performance, it is likel= y this will be delivered behind a preprocessor switch so downstream project= s can opt into the change.</div><div><br /></div><div>Neil</div><div><br />= </div> <p></p> -- <br /> You received this message because you are subscribed to the Google Groups &= quot;scintilla-interest" group.<br /> To unsubscribe from this group and stop receiving emails from it, send an e= mail to <a href=3D"mailto:[email protected]">= [email protected]</a>.<br /> To view this discussion visit <a href=3D"https://groups.google.com/d/msgid/= scintilla-interest/52afcff1-cb9b-4691-a0b5-58cb0e1eaf1dn%40googlegroups.com= ?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.com/d/msgid/= scintilla-interest/52afcff1-cb9b-4691-a0b5-58cb0e1eaf1dn%40googlegroups.com= </a>.<br /> ------=_Part_27317_1524870078.1779429796946-- ------=_Part_27316_912696815.1779429796946 Content-Type: text/plain; charset=US-ASCII; name=QtScale5.patch Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename=QtScale5.patch X-Attachment-Id: c409e3e1-ac77-4b01-b515-321a47caab47 Content-ID: <c409e3e1-ac77-4b01-b515-321a47caab47> diff -r 72f3cb86adae qt/ScintillaEditBase/PlatQt.cpp --- a/qt/ScintillaEditBase/PlatQt.cpp Thu May 14 10:55:45 2026 +1000 +++ b/qt/ScintillaEditBase/PlatQt.cpp Fri May 22 13:08:50 2026 +1000 @@ -228,6 +228,11 @@ return std::make_unique<SurfaceImpl>(width, height, mode); } +std::unique_ptr<Surface> SurfaceImpl_AllocatePixMap(int width, int height, SurfaceMode mode) +{ + return std::make_unique<SurfaceImpl>(width, height, mode); +} + void SurfaceImpl::SetMode(SurfaceMode mode_) { mode = mode_; @@ -308,7 +313,7 @@ int SurfaceImpl::DeviceHeightFont(int points) { - return points; + return points * device->devicePixelRatioF(); } void SurfaceImpl::LineDraw(Point start, Point end, Stroke stroke) @@ -795,6 +800,9 @@ painter->setRenderHint(QPainter::TextAntialiasing, true); painter->setRenderHint(QPainter::Antialiasing, true); + + const qreal scale = device->devicePixelRatioF(); + painter->scale(1.0 / scale, 1.0 / scale); } return painter; @@ -883,7 +891,9 @@ PRectangle Window::GetClientPosition() const { // The client position is the window position - return GetPosition(); + const qreal scale = wid ? window(wid)->devicePixelRatioF() : 1.0; + const PRectangle rc = GetPosition(); + return rc * scale; } void Window::Show(bool show) @@ -900,8 +910,15 @@ void Window::InvalidateRectangle(PRectangle rc) { - if (wid) - window(wid)->update(QRectFromPRect(rc)); + if (wid) { + //const qreal scale = window(wid)->devicePixelRatioF(); + // Avoids aliasing leaving a partial bottom pixel + //rc = rc / scale; + //rc.top -= 20; + //rc.bottom += 2; + //window(wid)->update(QRectFromPRect(rc)); + window(wid)->update(); + } } void Window::SetCursor(Cursor curs) diff -r 72f3cb86adae qt/ScintillaEditBase/PlatQt.h --- a/qt/ScintillaEditBase/PlatQt.h Thu May 14 10:55:45 2026 +1000 +++ b/qt/ScintillaEditBase/PlatQt.h Fri May 22 13:08:50 2026 +1000 @@ -164,6 +164,8 @@ QPainter *GetPainter(); }; +std::unique_ptr<Surface> SurfaceImpl_AllocatePixMap(int width, int height, SurfaceMode mode); + } #endif diff -r 72f3cb86adae qt/ScintillaEditBase/ScintillaEditBase.cpp --- a/qt/ScintillaEditBase/ScintillaEditBase.cpp Thu May 14 10:55:45 2026 +1000 +++ b/qt/ScintillaEditBase/ScintillaEditBase.cpp Fri May 22 13:08:50 2026 +1000 @@ -127,6 +127,10 @@ } else if (event->type() == QEvent::Hide) { setMouseTracking(false); result = QAbstractScrollArea::event(event); + } else if (event->type() == QEvent::DevicePixelRatioChange) { + // Reset cached data + sqt->InvalidateStyleRedraw(); + result = QAbstractScrollArea::event(event); } else { result = QAbstractScrollArea::event(event); } @@ -318,7 +322,7 @@ void ScintillaEditBase::mousePressEvent(QMouseEvent *event) { - const Point pos = PointFromQPoint(event->pos()); + const Point pos = PointFromQPoint(event->pos()) * devicePixelRatioF(); emit buttonPressed(event); @@ -347,7 +351,8 @@ void ScintillaEditBase::mouseReleaseEvent(QMouseEvent *event) { - const QPoint point = event->pos(); + const QPoint point = event->pos() * devicePixelRatioF(); + if (event->button() == Qt::LeftButton) sqt->ButtonUpWithModifiers(PointFromQPoint(point), TimeOfEvent(time), ModifiersOfKeyboard()); @@ -367,7 +372,7 @@ void ScintillaEditBase::mouseMoveEvent(QMouseEvent *event) { - const Point pos = PointFromQPoint(event->pos()); + const Point pos = PointFromQPoint(event->pos()) * devicePixelRatioF(); const bool shift = QApplication::keyboardModifiers() & Qt::ShiftModifier; const bool ctrl = QApplication::keyboardModifiers() & Qt::ControlModifier; diff -r 72f3cb86adae qt/ScintillaEditBase/ScintillaQt.cpp --- a/qt/ScintillaEditBase/ScintillaQt.cpp Thu May 14 10:55:45 2026 +1000 +++ b/qt/ScintillaEditBase/ScintillaQt.cpp Fri May 22 13:08:50 2026 +1000 @@ -801,13 +801,22 @@ void ScintillaQt::PartialPaint(const PRectangle &rect) { - rcPaint = rect; + QWidget *widget = static_cast<QWidget *>(wMain.GetID()); + const qreal scale = widget->devicePixelRatioF(); + + rcPaint = rect * scale; paintState = PaintState::painting; PRectangle rcClient = GetClientRectangle(); paintingAllText = rcPaint.Contains(rcClient); + // Create Bitmap of exactly window size then blit after Paint call + const int w = static_cast<int>(rcClient.Width() * scale); + const int h = static_cast<int>(rcClient.Height() * scale); + std::unique_ptr<Surface> surfPix = SurfaceImpl_AllocatePixMap(w, h, CurrentSurfaceMode()); + PRectangle rcScaled = PRectangle::FromInts(0,0,w,h); + Paint(surfPix.get(), rcScaled); AutoSurface surfacePaint(this); - Paint(surfacePaint, rcPaint); + surfacePaint->Copy(rcScaled, Point(0,0), *surfPix); surfacePaint->Release(); if (paintState == PaintState::abandoned) { diff -r 72f3cb86adae src/Geometry.h --- a/src/Geometry.h Thu May 14 10:55:45 2026 +1000 +++ b/src/Geometry.h Fri May 22 13:08:50 2026 +1000 @@ -45,6 +45,10 @@ return Point(x - other.x, y - other.y); } + constexpr Point operator*(XYPOSITION multiplier) const noexcept { + return Point(x * multiplier, y * multiplier); + } + // Other automatically defined methods (assignment, copy constructor, destructor) are fine }; @@ -101,6 +105,15 @@ return (rc.left == left) && (rc.right == right) && (rc.top == top) && (rc.bottom == bottom); } + + constexpr PRectangle operator*(XYPOSITION multiplier) const noexcept { + return PRectangle(left * multiplier, top * multiplier, right * multiplier, bottom * multiplier); + } + + constexpr PRectangle operator/(XYPOSITION divisor) const noexcept { + return PRectangle(left / divisor, top / divisor, right / divisor, bottom / divisor); + } + constexpr bool Contains(Point pt) const noexcept { return (pt.x >= left) && (pt.x <= right) && (pt.y >= top) && (pt.y <= bottom); ------=_Part_27316_912696815.1779429796946--