Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-96223

Painting glitches under fractional DPR scaling: collection task



    • Type: Epic
    • Status: Open
    • Priority: P2: Important
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: None
    • Labels:
    • Epic Name:
      Painting glitches under fractional DPR scaling


      When painting code originally implemented for fixed (DPR=1) rendering is run with a fractional DPR, there will sometimes appear off-by-one glitches due to rounding.

      To illustrate the basic issue, consider a horizontal line from x1=1 to x2=5, thus having length 4. Scaled by a factor of 1.33, this becomes x1=1.33 and x2=6.65, which rounds to a line with x1=1 and x2=7, having length 6. However if one scales the original length, 4*1.33=5.32, which rounds to 5, not 6. So, depending on whether the painted end result depends on the line's length or its endpoints, the scaled x2 should be either 6 or 7.  Add a dimension, and this is the basic issue of scaling rectangles and then converting them to integer coordinates. The latter happens either when the painting code code converts to QRect or during aliased painting.

      It is recognized that no general perfect solution exist, since the paint engine cannot know the intended end result . The goal of this task is then just to improve the default behavior, its consistency and predictability.

      Key code points

      The key points where most of the relevant scaling and rounding happen are

      • QTransform::mapRect(...)
      • QTransform::map(const QRegion&)
      • QRectF::toRect()
      • QRasterPaintEngine::toNormalizedFillRect(...)

      In addition, the QHighDpi namespace uses its own scaling functions, e.g.

      • QHighDpi::scale(const QRect&)
      • QHighDpi::scale(const QRegion&)

      The scaling/rounding of rectangles is not currently consistent between all these functions.

      Clipping and QRegion

      Clipping areas are stored in a QRegion, which is based on QRects, i.e. integer coordinates. In the typical case, it contains only a single rectangle, but in case there are more, the basic assumption of QRegion is that an area can be expressed as edge-to-edge rectangles. As illustrated above, that assumption needs special care fractional scaling.

      There may also be an issue here in that clip rects are scaled and rounded when they are set, and the original rect lost. So rounding errors may accumulate if further scaling is applied in later painting steps.

      Anti-aliasing and coordinate system shift

      Glitches due to rounding effects can sometimes be avoided by using anti-aliased painting. Then, instead of snapping to integer coordinates/whole pixel and filling them with 100% color, the paint engine will fill partially covered pixels with semi-transparent color. 

       A caveat when working with legacy code is to note that the coordinate system was shifted by a half unit (pixel) as of Qt 5.0. Earlier, (0.0, 0.0) designated the center point of the top left pixel. With Qt5, (0.0, 0.0) designates the top left corner of that pixel, while (0.5, 0.5) designates its center point. The result is that by default, an anti-aliased 1-pixel-wide line,  drawn with integer coordinates will now span two pixels in width, filling each of them 50%. By shifting the coordinates by 0.5, e.g. by applying a translation transform, one gets the same result as earlier Qt versions.

      Although this shift is normally inconsequential under integer DPR and aliased painting, it can become significant with fractional DPR or anti-aliased painting.



          Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.



              Unassigned Unassigned
              vgt Eirik Aavitsland
              0 Vote for this issue
              3 Start watching this issue



                  Gerrit Reviews

                  There are no open Gerrit changes