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

Clipping glitches when painting with fractional scaling

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P2: Important
    • 6.3.0
    • 6.x
    • GUI: Painting
    • None

    Description

      Clipping is inherently a discrete (aliased) operation. So fractional clip coordinates - e.g. the result of fractional scaling - must be rounded to integers. There are three problems in the current code causing glitches:

      1. The various clip commands - setClipRect(QRect), setClipRect(QRectF), setClipRegion(QRect) etc. - do not use consistent rounding. Some use QRectF::toRect() others QRectF::toAlignedRect().
      2. Clip rects are a way of subdividing an area into discrete subareas. So when two cliprects are given as edge-to-edge, they should keep that property also under scaling. There should be neither gaps or overlaps between the scaled versions of such cliprects. The right edge of the left rectangle should always map to the pixel next to the left edge of the right rectangle. But neither of the two roundings above guarantee that property.
      3. A second-order issue is that QTransform(QRectF) can hit a numerical accuracy limit around the .5 point. Since QRectF internally stores (x,y,w,h), not (x1,y1,x2,y2), the mapping of the left/top edges is a slighly different operation than the mapping of the right/bottom edges. So when both should be X.5 (to be rounded up), it can happen that one of them is slightly less, so being rounded down. The result is a gap.

      Issue 2 above is particularly critical for QRegions with more than one rect, since QRegion is implemented as a collection of edge-to-edge rects. Ref. QTBUG-95957.

      The attached clipdemo app demonstrates the various issues. The area is divided into 4 edge-to-edge cliprects, which can be scaled by resizing the window. Gaps then appear in lighter color, overlaps in darker.

       About the toAlignedRect() rounding: This appeared relatively recently, as a result of the discussion in QTBUG-83229. toAlignedRect() gives a surrounding QRect, i.e. it always rounds "outwards". However, the argument for that seems to rest on a strange premise: namely that any pixel partially covered by the floating-point cliprect should be included in the actual cliprect. I.e. so that even just a 1% covered pixel should be included. That seems like unexpected behavior. Instead, the natural breaking point is at 50% coverage, like fillRect() does. The remaining question is then, what ahppens at exactly 50% coverage? (something which often happens, as it corresponds to an X.5 corner coordinate). Again the suggested answer is to do the same as fillRect(): we use Qt's default policy of snapping rightwards and downwards. So 50% pixels along top and left edges should be excluded, while those on the right and bottom edges should be included.

      The obvious way to achieve the edge-to-edge property (#2 above) is to use a rounding that always rounds the corner coordinates, and ignores the height/width. (That corresponds to item 2 in the history of QRectF::toRect() here: QTBUG-96223). It is also what toNormalizedFillRect() does, which is what QPainter::fillRect() uses. However, to also solve issue #3 above, not only the rounding, but even the QTransform mapping should be done on the corner coordinates, not rect size.

       

      Attachments

        1. clipdemo.zip
          2 kB
        2. ClipRect-Qt4_8.png
          ClipRect-Qt4_8.png
          102 kB
        3. stroking-vs-clipping.png
          stroking-vs-clipping.png
          19 kB

        Issue Links

          For Gerrit Dashboard: QTBUG-100329
          # Subject Branch Project Status CR V

          Activity

            People

              vgt Eirik Aavitsland
              vgt Eirik Aavitsland
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes