Gecko:2DGraphicsThoughts

From MozillaWiki
Revision as of 10:18, 23 November 2010 by Roc (talk | contribs) (→‎API)
Jump to navigation Jump to search

Gecko Needs

Semantics

The basics of cairo's semantics seem to be OK --- fill path/stroke path/showglyphs, with source patterns, destination surfaces, operators, etc.

Some semantic improvements could be made:

  • Replace REPEAT/PAD/NONE extend modes with source clipping in tile space (NONE not needed)
  • Mask-based complex clipping is really slow and therefore pointless
  • Allowing changes to the CTM during path emission is useless for us

API

Gecko doesn't make much use of the statefulness of contexts. Every time we draw a display item, we reset most of the state. The exceptions are clipping and transforms. (But we often do need to be able to reset clipping to a given clipping state.)

Canvas is different, of course.

Some state is not reset but assumed to be constant between operations: OPERATOR_OVER, default antialiasing mode, etc.

We can do without pushgroup and make do with temporary surfaces quite easily.

We probably don't need the context/surface distinction. We don't draw into a surface with multiple contexts at the same time.

We don't really care about API convenience in Gecko. Unlike a regular application, we don't draw specific content, we just provide abstractions. So for example we only paint gradients in a couple of places, we only paint text in a few places, etc. Therefore we don't need to worry about passing lots of parameters to functions.

We don't care about API stability.

Performance

We need a thin wrapper around native backends, some of which use a lot of state in their contexts (Quartz), some of which don't (D2D).

cairo wraps a stateful context around mostly-stateless surface backends. This imposes unnecessary overhead on us (e.g. routing through the gstate) especially where the stateless surface backend maps back to a stateful platform abstraction (e.g. D2D clipping, Quartz everything).

For example, every time layout paints a display item, we want to explicitly set the clip region for the display item. Because cairo_t is stateful, we have to restore back to a known point, save, and set the clip (layout identifies when the clip region doesn't need to be changed to avoid this on every display item paint). But underneath cairo is creating clipper objects representing the "current clip" and passing them to a stateless surface backend. In turn, the backends for Quartz and D2D are managing clip state on the native contexts.

Proposal

API

Stateful abstractions are a pain because we reset them a lot anyway and mapping one stateful abstraction onto another is tricky where they don't quite match up (e.g. semantics of changing the transform while emitting a path).

So, go stateless ... almost. As noted above, we almost always need to account for a "current transform" and "current clip", so leave those in the context, otherwise we'd just have to pass them around everywhere, which would be annoying. Make everything else a parameter to the drawing calls.

It's already easy to manipulate transforms since we have SetMatrix as well as GetMatrix. Do the same for clipping; expose clip objects, GetClip/SetClip, and operations on clip objects. Allow the clip objects to have backend-private implementation.

No gstates or save/restore should be required, except of course we can have helper autos to save and restore matrix and/or clip.

Pack current operator, antialiasing mode, snapping mode, and sundry flags into a single flags word passed to each draw call.

Stateless means we use explicit path objects (backend-dependent implementation).

Provide explicit API for fillRect and other canvas operations so we don't have to waste time in backends identifying which fast path to use having gone through a generic path operation.

Implementation

For optimal efficiency with stateful backends we need to figure out what state needs to be updated at each drawing call and only update that state. For example, with Quartz, if the operator, antialiasing mode, clip and source pattern are the same as the previous drawing call, we shouldn't do Quartz calls to set them. Packing flags and modes into a single flags word will help quick identification of changes. We'll want to be able to identify reused clips and source patterns efficiently without making those objects too heavyweight. This may require some ingenuity.