written by Robert O'Callahan (roc)
This is my attempt to explain what the key GFX abstractions mean. I'm also writing down how I think they need to be fixed.
Now that we are writing new C++ wrapper APIs in the form of Thebes, these problems will quietly go away.
nsIRenderingContext is an abstraction of a "native rendering context". This means it abstracts (for example) a Windows HDC and a GDK GdkGC. It represents a channel to which rendering commands (e.g., DrawRect) can be issued. It encapsulates a "current rendering state" (e.g., current transform, current colour).
- The current transform is not applied to text rendering operations. This leads hacks being necessary when trying to scale a document.
- Clip regions are taken "raw", i.e. not transformed, but clip rects are. This is evil.
- We need a way to say "begin rendering"/"done rendering"
- Needs deCOMtamination
- Instead of using XPCOM factories we really just want to allocate these via a method call on nsIWidget or whatever
- Need to audit methods to see which ones are actually used; many aren't.
- May need to revisit rendering primitives: use float coords? Use nsFloatPoint/nsFloatRect (nsFloatRect doesn't exist yet)? Provide retained mode as well as immediate mode (or both merged in some funky way)?
- Remove drawing surfaces (Lock/Unlock, explicit backbuffer management); see below
nsIDeviceContext is an abstraction of a "native device context". Windows and GDK don't have an explicit native device context; in Windows you just use an HDC, and in GDK you use the GdkWindow. It represents a device, such as a screen or printer, which can be queried for its characteristics (e.g., bit depth) and can be manipulated in device-specific ways (e.g., start new page).
- Needs deCOMtamination
- Need to audit methods to see which ones are actually used; some aren't.
- The canonical pixel scale stuff must go, along with "zoom" and "text zoom".
- The printing methods should probably be on a separate interface.
For historical reasons (I think), both of these types are used to access the same underlying object. This type represents something which can be rendered into. The most important case is when an nsDrawingSurface represents a region of off-screen memory which is being rendered into. However, when rendering into a widget there is also an associated nsDrawingSurface selected into the context; however, it can't be Lock()ed to get the raw pixels. There is no way to get from an nsDrawingSurface for a widget to the original nsIWidget.
When an nsIRenderingContext is created it has a default nsDrawingSurface already selected into it; the type of surface depends on how the rendering context was created. nsIRenderingContext::SelectOffScreenSurface can select an alternative surface to be rendered into (this was used for double-buffering, for example); it can also be used to switch back to the original surface. Thus every rendering context keeps an "original" and "current" drawing surface pointer. This particular method is not used outside of obsolete SVG code, however.
We can also allocate a new rendering context for a specific drawing surface (e.g., an existing chunk of offscreen graphics memory).
For some reason that predates my work on this code, all the useful methods of nsIDrawingSurface are replicated in nsIRenderingContext (operating on the current surface), and clients use only those methods and not nsIDrawingSurface itself.
nsIRenderingContext::Get/Release/Destroy/UseBackbuffer are alternate methods for managing a drawing surface for the context, that can take advantage of platform specific caching strategies.
ns(I)DrawingSurface should not be exposed to the client. It's useless for rendering into on-screen widgets. Then there are only two places where Gecko needs to manipulate offscreen pixel data: 'opacity' (and potentially other filter effects), and double-buffering. Each platform's GFX implementation should instead implement 'opacity' (and potentially other filters) and double-buffering APIs natively, possibly using shared code such as nsBlender when native support is not available. Native support for these features is increasingly available and when it's available, there are overwhelming performance reasons to use it.