Gecko:Image Snapping and Rendering

Revision as of 03:56, 4 October 2008 by Roc (talk | contribs) (→‎Algorithm)

Preamble: General Rendering Principles (dbaron)

  1. Paints with different dirty rectangles draw the same values for the device pixels in the intersection of the dirty rects. Otherwise, visual glitches are inevitable.
  2. All edges (e.g., of background color, background image, border, foreground image, etc.) at the same subpixel location must be snapped (or not snapped) to the same place. This includes multiple edges of the same element, edges of ancestor/descendant elements, and edges of elements without an ancestor/descendant relationship.
  3. Any two edges separated by a width that maps to an exact number of device pixels must snap to locations separated by the same amount (and direction).

Image Snapping And Rendering Requirements

Input:

  • Image (actual size in CSS pixels)
  • Image initial rectangle (appunits)
  • Logical fill rectangle (appunits)
  • Ratio of appunits to device pixels
  • Actual dirty rectangle (device units)

For CSS background drawing, the "image initial rectangle" is determined by background-size and the "anchor point" for the image (which is determined by background-position and background-attachment). The "logical fill rectangle" is the intersection of background-clip and the area covered by the image (determined by taking the image initial rectangle and tiling it as per background-repeat).

Requirements:

  1. The basic plan is that the image is scaled to fill the initial rectangle, which is tiled over the plane and clipped to the logical fill rectangle. Pixels outside the dirty rectangle need not be drawn. We should approximate that as closely as possible given the limitations of device pixels.
  2. Paints with different dirty rectangles draw the same values for the pixels they draw in common. (RATIONALE: Principle #1.)
  3. The device pixels that are filled should be the logical fill rectangle rounded to device pixel edges, preserving device pixel centers. (RATIONALE: That's how we "pixel snap" solid rect fills and image drawing should be consistent with solid fills in the pixels that are touched. Satisfies Principles #2 and #3.)
  4. Every image pixel sampled in actual rendering would also be sampled by an ideal rendering to an infinite-resolution device. (RATIONALE: Web authors should not be faced with fringes contributed by pixels they did not intend to be sampled.)
  5. If the initial rectangle size in device pixels equals the image size in CSS pixels, then the filled area is rendered as a simple copy of the ideal rendering translated by less than or equal to half a pixel in the horizontal and vertical directions (except where the ideal rendering has only partial pixels, in which case the rendering is not constrained by this requirement). (RATIONALE: If the scale factor in the author's design is exactly the inverse of the scale required by the device, we must avoid scaling and subpixel translation or there will be unnecessary image blurring and performance penalty; in fact, all we're allowed to do is translate the rendering by a minimal amount to pixel-align the image.)

Algorithm

An algorithm that satisfied these requirements:

  • Compute source image pixel rectangle by mapping the logical fill rectangle back to image pixels and rounding out to the nearest image pixel. This will be passed down to gfx to ensure requirement 4 holds --- gfx will limit sampling to this area using EXTEND_PAD or equivalent.
  • Compute device pixel fill rectangle by snapping the logical fill rectangle to device pixels, preserving device pixel centers, thus ensuring requirement 3.
  • Compute device pixel draw rectangle by intersecting the device pixel fill rectangle with the dirty rectangle scaled to device pixels and rounded out. This is the area to cairo_fill.
  • Compute the transformation from image space to device space: compute the device pixel initial rectangle by snapping the initial rectangle to device pixels, preserving device pixel centers. Then the transformation from image space to device space is the transformation that maps the image bounds to the device pixel initial rectangle.

Requirements 3 and 4 are satisfied by construction. Proof that requirement 5 is always satisfied: Assume the initial rectangle size in device pixels equals the image size in CSS pixels. Then the initial rectangle size is integral numbers of device pixels, so the device pixel initial rectangle has the same size. So the scale factors of the transform are 1. Now, the translation from image space to device context space in the ideal rendering is just the top-left of the initial rectangle in device pixels. The translation in our rendering is the top-left of the device pixel initial rectangle, which is the top-left of the initial rectangle snapped to the nearest pixel edges --- a translation of at most half a pixel in each direction. QED.