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.|| |
| 27 | ||InlineBoxes||float||''unchanged''|| |
| 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 [https://bugs.webkit.org/show_bug.cgi?id=23170 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.|| |
| 40 | ||SVG||float||''unchanged''|| |
| 41 | [[BR]] |
| 42 | |
| 44 | = Converting Subpixels to Pixels = |
| 45 | |
| 46 | When 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. |
| 47 | |
| 48 | The 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. |
| 49 | |
| 50 | [[Image(/raw-attachment/wiki/LayoutUnit/WebKitlayouttypes.png)]] |
| 51 | |
| 52 | The 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. |
| 53 | |
| 54 | == enclosingIntRect == |
| 55 | |
| 56 | When computing the enclosing rectangle the left and top edges are floored and the right and bottom ones ceiled. |
| 57 | |
| 58 | The 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]] |
| 65 | |
| 66 | == pixelSnappedIntRect == |
| 67 | |
| 68 | When snapping the left and top edge is simply rounded to the nearest device pixel. |
| 69 | The 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. |
| 70 | |
| 71 | The 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]] |
| 78 | |
| 79 | We 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. |
| 80 | [[BR]][[BR]] |
| 81 | |
| 82 | = Determining the right unit and conversion = |
| 83 | |
| 84 | When attempting to determine the proper unit and conversion for a new variable, here are some common types to help you decide: |
| 85 | |
| 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.|| |
| 90 | [[BR]] |
| 91 | |
| 92 | = Notes = |