Changes between Version 1 and Version 2 of LayoutUnit

Feb 10, 2012 3:39:56 PM (12 years ago)



  • LayoutUnit

    v1 v2  
     1= Background =
     3To better support zooming, both on desktop and mobile devices, we are currently working on adding subpixel layout support WebKit. To do this we are changing the rendering tree to use subpixel units, called LayoutUnit, instead of integers to represent locations and sizes.
     5This working is currently being undertaken by [ Levi Weintraub] and [ Emil A Eklund], please talk to us directly or ask on webkit-dev or #webkit if you have any questions and/or concerns.
     7= LayoutUnit & Subpixel Layout =
    19LayoutUnit^1^ is an abstraction used to represent the location or size of a render object in fractions of a logical pixel, it is used primarily for layout and hit testing. The current implementation represents values as multiples of 1/60th pixel^2^. This allows us to use integer math and avoids floating point imprecision.
    311Even though layout calculations are done using LayoutUnits the values are aligned to integer pixel values at paint time to line up with device pixels. While most modern graphics libraries support painting with subpixel precision, this results in unwanted anti-aliasing.
    412When aligning to device pixels the edges are aligned to the nearest pixel and then the size is adjusted accordingly. This ensures that the bottom/right edge and the total width/height is at most off-by-one.
    6 Certain cases require us to snap LayoutUnits to whole-pixel values, including:
    7  * scroll offsets - scrolling by subpixel values would cause off-by-one errors when pixel snapping, and would expose the embedding application to LayoutUnits.
    8  * border widths - to ensure that all borders are the same width as specified.
    9  * tables - to ensure even distribution of available space across columns.
    10  * size/position of widgets, plugins, video, and foreign objects - these are either rendered outside of WebCore, or pass through an intermediary embedding layer. We need to use integers so the contents are painted at whole-pixel values.
    11  * positioning of InlineBoxes in a RenderBlock - we lose the ability to pixel snap in sync with containing block because the line box tree paints with floats.
    12  * painting - to avoid unwanted anti-aliasing.
     15= Use cases =
     17||'''Use case'''||'''Type'''||'''Justification'''||
     18||location and size of render objects||subpixel||This is enables the goal of this patch: to accumulate subpixel position information through the render tree.||
     19||paddings||subpixel||''see above''||
     20||margins||subpixel||''see above''||
     21||borders||pixel||Using integers to ensure that all borders are the same width as specified.||
     22||scroll width/height||subpixel||Based on actual content size, which is stored with subpixel precision.||
     23||scroll offset||pixel, rounded||Scrolling by subpixel values would cause off-by-one errors when pixel snapping, and would expose the embedding application to LayoutUnits.||
     24||clip rects||subpixel||Clip rects are repositioned based on the parent containers location and size.||
     25||overflow rects||subpixel||Computed based on size and location of object, both of which are represented with subpixel precision.||
     26||painting||pixel, snapped||To avoid unwanted anti-aliasing.||
     28||Overflow rects for InlineBoxes||subpixel, enclosed||Smallest possible subpixel rectangle representation guaranteed to contain subtree.||
     29||InlineBoxes in a RenderBlock||pixel, snapped||The line box tree uses floats for layout and painting, we snap to pixels when positioning and sizing the line box tree to ensure that text isn’t drawn outside the pixel snapped bounds of its block.||
     30||SVG Boxes in a RenderBlock||subpixel, enclosed||Smallest possible subpixel rectangle representation guaranteed to contain subtree.||
     31||tables||pixel||Using integers to ensure even distribution of available space across columns and to match specification.||
     32||RenderLayers||subpixel||RenderLayers are positioned along with their renderer, and need to accumulate their offsets with subpixel precision.||
     33||borders||pixel||Using integers to ensure that all borders are the same width as specified.||
     34||floats||subpixel||These are always blocks and we'd otherwise be incapable of having 2 floats on the same line, each at 50% width on a line with an odd width.||
     35||widgets, plugins, video, and foreign objects||pixel, snapped||These are either rendered outside of WebCore, or pass through an intermediary embedding layer. We need to use integers so the contents are painted at whole-pixel values.||
     36||windows coordinates and sizes||pixel||These communicate with the embedding layer, and represent actual pixels.||
     37||hit testing||pixel, rounded||Hit testing is staying integer based for now. The eventual plan is to convert it to subpixel precision to fix long standing bugs such as [ 23170].||
     38||RenderTreeAsText||pixel, snapped||Pixel snapping subpixel values makes the text output match what is painted.||
     39||RenderTheme||pixel, snapped||Interfaces with the platform code.||
     44= Converting Subpixels to Pixels =
     46When converting a rectangle with subpixel precision to one with integer precision one of two methods is used. The first, seen in the enclosingIntRect function, returns the smallest possible rectangle that fully contains the original subpixel one. As such the resulting rectangle is guaranteed to be the same size or larger than the original. This method has been in use to convert rects in floating point in WebKit for some time.
     48The method used to align render objects to pixels is referred to as pixel snapping. Pixel snapping applies rounding to the logical top left point of the rectangle, then moves the resulting edges to the nearest pixel boundaries. This results in a rectangle aligned to pixel boundaries as close to the original as possible, but is not guaranteed to fully contain the original.
     52The figure above illustrates the differences between the two methods. The light gray squares represents pixels, the blue square represents the original subpixel rectangle while the black outline represents the resulting rectangle returned by the two functions.
     54== enclosingIntRect ==
     56When computing the enclosing rectangle the left and top edges are floored and the right and bottom ones ceiled.
     58The values are computed as follows:
     59  x: floor(x)[[BR]]
     60  y: floor(y)[[BR]]
     61  maxX: floor(x) + ceil(width)[[BR]]
     62  maxY: floor(y + ceil(height)[[BR]]
     63  width: ceil(width)[[BR]]
     64  height: ceil(height)[[BR]]
     66== pixelSnappedIntRect ==
     68When snapping the left and top edge is simply rounded to the nearest device pixel.
     69The right and bottom edges are computed by subtracting the rounded left/top edge from the precise location and size. This ensures that the edges all line up with device pixels and that the total size of an object, including borders, is at most one pixel off.
     71The values are computed as follows:
     72  x: round(x)[[BR]]
     73  y: round(y)[[BR]]
     74  maxX: round(x + width)[[BR]]
     75  maxY: round(y + height)[[BR]]
     76  width: round(x + width) - round(x)[[BR]]
     77  height: round(y + height) - round(y)[[BR]]
     79We use the term pixel snapped to indicate that the numbers are not merely rounded. Logical widths and heights, as well as right and bottom edges should never be rounded. When working with pairs of values instead of rects, the helper function snapSizeToPixel can be used to calculate these values. This also matches the naming used by the line box tree.
     82= Determining the right unit and conversion =
     84When attempting to determine the proper unit and conversion for a new variable, here are some common types to help you decide:
     86||A value passed in or out of the embedding layer.||Pixels: the embedding layer has no knowledge of WebKit’s subpixel units. This includes things in the render tree that are rendered outside of WebKit (like Widgets) or pass out of WebKit before being drawn (like SVG Foreign Objects). Values originating in subpixel units should be pixel snapped.||
     87||A value passed to the graphics context.||Float for text, otherwise pixels: LayoutUnits should always be pixel snapped before being drawn. This avoids painting off pixel boundaries, which causes the graphics system to use unwanted antialiasing.||
     88||A point (or offset) in the render tree.||Subpixel, rounded if needed: Points in the render tree are often converted between absolute and relative coordinates, and need to accumulate sub-pixels. Points are ultimately rounded when passed into the embedding layer or to the graphics context.||
     89||A size in the render tree.||Subpixel, snapped if needed: Sizes in the render tree are stored in subpixel units for precision, but should not be rounded. Rounding a size can result in objects bleeding over the boundaries of neighboring renderers when the neighbor’s positions are rounded. Instead, sizes should be snapped along with their corresponding position before being painted or passed to the embedding layer.||
     92= Notes =
    16941: LayoutUnit is currently a typedef that maps to int. Once the subpixel branch has been merged this will map to AppUnit which is the underlying fixed point implementation. The eventual plan is to rename AppUnit to LayoutUnit and remove the typedef.