[Webkit-unassigned] [Bug 85532] [meta] Enable sub-pixel layout on all platforms

bugzilla-daemon at webkit.org bugzilla-daemon at webkit.org
Tue Jun 26 07:48:03 PDT 2012


https://bugs.webkit.org/show_bug.cgi?id=85532





--- Comment #6 from Nikolas Zimmermann <zimmermann at kde.org>  2012-06-26 07:48:01 PST ---
(In reply to comment #3)
> I'd love an update on plans for enabling it in the KDE project ;)
I'm not working on KDE stuff since half a decade btw, historical reasons for my bugzilla account :-)

Anyhow, I've experimented lots with SUBPIXEL_LAYOUT turned on, on Mac. You've done a great job, it's working as-expected for most parts.

I've ran into an issue with transforming an absolute positioned <div>, which is aligned on sub-pixel boundaries, like this:

<html>
<body>
<div style="-moz-transform: scale(100); -webkit-transform: scale(100); background-color: green; position: absolute; left: -0.5px; top: -0.5px; width: 1px; height: 1px;"></div>
</body>
</html>

If you run this in trunk, you won't see anything, unless you zoom in or out at least once.
I've nailed down the problem, but I'm yet unsure on how to fix it properly, so I'd like to share it with you to get your insight:
void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    LayoutPoint adjustedPaintOffset = paintOffset + location();

    PaintPhase phase = paintInfo.phase;

    // Check if we need to do anything at all.
    // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView
    // paints the root's background.
    if (!isRoot()) {
        LayoutRect overflowBox = visualOverflowRect();
        flipForWritingMode(overflowBox);
        overflowBox.inflate(maximalOutlineSize(paintInfo.phase));
        overflowBox.moveBy(adjustedPaintOffset);
        if (!overflowBox.intersects(paintInfo.rect))
            return;
    ....

Break on RenderBlock::paint(), at some point:
Breakpoint 1, WebCore::RenderBlock::paint (this=0x1091271e8, paintInfo=@0x7fff5fbf97a8, paintOffset=@0x7fff5fbf98d0) at /Users/nzimmermann/Coding/WebKit/Source/WebCore/rendering/RenderBlock.cpp:2626
2626        LayoutPoint adjustedPaintOffset = paintOffset + location();
(gdb) p showRenderTreeForThis()
RenderView 0x109124108                     #document    0x10882a600
  RenderBlock 0x109125b78                  HTML    0x109120380
    RenderBody 0x1091266d8                 BODY    0x109125ca0
*     RenderBlock (positioned) 0x1091271e8    DIV    0x109126400 STYLE=-moz-transform: scale(100); -webkit-transform: scale(100); background-color: green; position: absolute; left: -0.5px; top: -0.5px; width: 1px; height: 1px;

is reached.

2638            if (!overflowBox.intersects(paintInfo.rect))
(gdb) p overflowBox
$39 = {
  m_location = {
    m_x = {
      m_value = 0
    }, 
    m_y = {
      m_value = 0
    }
  }, 
  m_size = {
    m_width = {
      m_value = 60
    }, 
    m_height = {
      m_value = 60
    }
  }
}

p paintInfo.rect
$40 = {
  m_location = {
    m_x = 1, 
    m_y = 1
  }, 
  m_size = {
    m_width = 8, 
    m_height = 6
  }
}

0x000000010269d7af in WebCore::RenderBlock::paint (this=0x1091271e8, paintInfo=@0x7fff5fbf97a8, paintOffset=@0x7fff5fbf98d0) at /Users/nzimmermann/Coding/WebKit/Source/WebCore/rendering/RenderBlock.cpp:2638
2638            if (!overflowBox.intersects(paintInfo.rect))
Value returned is $41 = false

And here's the problem. The paintInfo.rect has a location of 1x1, and a size of 8x6.
So let's examine where the incorrect paintInfo.rect is coming from:

(gdb) bt
#0  0x000000010269d7af in WebCore::RenderBlock::paint (this=0x1091271e8, paintInfo=@0x7fff5fbf97a8, paintOffset=@0x7fff5fbf98d0) at /Users/nzimmermann/Coding/WebKit/Source/WebCore/rendering/RenderBlock.cpp:2638
#1  0x000000010279d729 in WebCore::RenderLayer::paintLayerContents (this=0x1091272b8, rootLayer=0x1091272b8, context=0x7fff5fbfb4c0, parentPaintDirtyRect=@0x7fff5fbf9b10, paintBehavior=0, paintingRoot=0x0, region=0x0, overlapTestRequests=0x7fff5fbfb1c0, paintFlags=480) at /Users/nzimmermann/Coding/WebKit/Source/WebCore/rendering/RenderLayer.cpp:3149

Looking at frame #1:
#1  0x000000010279d729 in WebCore::RenderLayer::paintLayerContents (this=0x1091272b8, rootLayer=0x1091272b8, context=0x7fff5fbfb4c0, parentPaintDirtyRect=@0x7fff5fbf9b10, paintBehavior=0, paintingRoot=0x0, region=0x0, overlapTestRequests=0x7fff5fbfb1c0, paintFlags=480) at /Users/nzimmermann/Coding/WebKit/Source/WebCore/rendering/RenderLayer.cpp:3149
3147                // Paint the background.
3148                PaintInfo paintInfo(context, pixelSnappedIntRect(damageRect.rect()), PaintPhaseBlockBackground, false, paintingRootForRenderer, region, 0);
3149                renderer()->paint(paintInfo, paintOffset);

(gdb) p damageRect.rect()
$51 = (const LayoutRect &) @0x7fff5fbf9908: {
  m_location = {
    m_x = {
      m_value = 30
    }, 
    m_y = {
      m_value = 30
    }
  }, 
  m_size = {
    m_width = {
      m_value = 480
    }, 
    m_height = {
      m_value = 360
    }
  }
}

The pixelSnappedIntRect() produces the x/y=1, width=8, height=6 IntRect.

Some more background info:
The RenderLayer associated with the <div> has a transform, scale(100).
#3  0x000000010279c370 in WebCore::RenderLayer::paintLayer (this=0x1091272b8, rootLayer=0x1091244c8, context=0x7fff5fbfb4c0, paintDirtyRect=@0x7fff5fbfa2d0, paintBehavior=0, paintingRoot=0x0, region=0x0, overlapTestRequests=0x7fff5fbfb1c0, paintFlags=480) at /Users/nzimmermann/Coding/WebKit/Source/WebCore/rendering/RenderLayer.cpp:3006

3001            {
3002                GraphicsContextStateSaver stateSaver(*context);
3003                context->concatCTM(transform.toAffineTransform());
3004    
3005                // Now do a paint with the root layer shifted to be us.
3006                paintLayerContentsAndReflection(this, context, transform.inverse().mapRect(paintDirtyRect), paintBehavior, paintingRoot, region, overlapTestRequests, paintFlags);
3007            }        
3008    
3009            // Restore the clip.
3010            if (parent())

(gdb) p transform
$56 = {
  m_matrix = {{100, 0, 0, 0}, {0, 100, 0, 0}, {0, 0, 1, 0}, {-50, -50, 0, 1}}
}

(gdb) p paintDirtyRect
$58 = (const LayoutRect &) @0x7fff5fbfa2d0: {
  m_location = {
    m_x = {
      m_value = 0
    }, 
    m_y = {
      m_value = 0
    }
  }, 
  m_size = {
    m_width = {
      m_value = 48000
    }, 
    m_height = {
      m_value = 36000
    }
  }
}

The mapping of the paintDirtyRect through the inverse of the transform, produces x/y=30 (aka. 0.5).
Now that we know the root of the problem, if you change left/top to 0.4999px, it'll show up as expected, as the paintInfo.rect is now at 0x0, as 0.4999px is pixel-snapped to 0, instead of 1.

I'm unsure on how to fix this properly: the intersection between the pixel-snapped paint info rect and the overflow box is lossy, as the overflow box is sub-pixel aware.

(gdb) p toRenderBox(renderer())->m_frameRect
$61 = {
  m_location = {
    m_x = {
      m_value = -30
    }, 
    m_y = {
      m_value = -30
    }
  }, 
  m_size = {
    m_width = {
      m_value = 60
    }, 
    m_height = {
      m_value = 60
    }
  }
}

As no overflow is involved in my example, visualOverflowRect() == borderBoxRect(), aka. LayoutRect(0, 0, m_frameRect->width(), m_frameRect->height()) (in this example).

Sorry for the long post, but I thought I'd give as much information as possible, so we can discuss this easier. Levi, any idea?

-- 
Configure bugmail: https://bugs.webkit.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug.



More information about the webkit-unassigned mailing list