Canvas

From Apache OpenOffice Wiki
Jump to: navigation, search

Introduction

Starting for OOo 2.0, the Canvas effort tries to modernize OOo's graphical output layer - its predecessor, VCL's OutputDevice, carries the burden of being designed as a shallow wrapper around Win16 GDI. Back in the day, everything was integer coordinates, bezier curves didn't exist, nor a notion of alpha.

The Canvas Architecture. Click on image for full view

In contrast, the Canvas offers a contemporary set of features useful for rendering application content, including ubiquitous alpha, floating point coordinates, texturing, and advanced text layouting facilities. Where VCL OutputDevice is a concrete class (without any virtual methods), the Canvas consists of a set of Uno interfaces grouped around the central com::sun::star::rendering::XCanvas interface.

Because Canvas is interface-based, it's easy to provide multiple implementations, even for the same platform, and switchable during runtime (VCL does that during compile-time, depending on the platform it is built for). To facilitate easier migration, one of the Canvas implementations is based on VCL. Thus, every platform that has VCL working automatically gets a working Canvas - important to reduce effort for new ports, e.g. the MacOS Aqua port. Later on, these platforms might consider providing a native XCanvas implementation.

Currently, there are seven Canvas implementations in varying levels of completeness:

  • Java-Graphics2D-based
  • VCL-based
  • cairo-based
    • X11 backend
    • Windows backend
    • Aqua backend
  • DirectX/GDI+-based

and one empty demo implementation (nullcanvas), that can serve as a template for new ports.

For migration purposes, the new Canvas and VCL's OutputDevice will be contained in OOo for quite some time, allowing a step-by-step adaptation of the applications. Therefore, the canvas and VCL must somehow interoperate, since new code, using the canvas, typically is embedded in a VCL-based environment (for example, the OOo windows are still provided by VCL. To render into such a window via the canvas, a canvas must be constructable from a VCL window). Thus, essentially two new methods are provided at VCL's window, namely Window::GetCanvas() and Window::GetFullScreenCanvas(). Apart from that small link, canvas and VCL are completely separated.

The canvas framework currently consists of the following CVS modules, gsl/canvas and gsl/cppcanvas. Additionally, a new generic graphics tooling is used (but not exclusively by the canvas, AW's recent drawing layer fixups also make use of it), which resides in graphics/basegfx /graphics/basegfx.

Module structure

  • canvas/
    • inc/
      • canvas/ Public canvas headers
        • base/ Base classes and template wrappers for XCanvas-implementors
        • rendering/ Interfaces for a software render suitable for XCanvas
    • prj/ usual build system boilerplate
    • source/
      • cairo/ The cairo-based canvas implementation
      • directx/ The DirectX/GDI+-based implementation
      • factory/ A Canvas factory, allowing smart fallbacks and user configuration (e.g. for the hw acceleration checkbox)
      • java/ A Java-Graphics2D-based implementation
      • null/ A dummy implementation that renders nothing, but contains all the plumbing to successfully fake a canvas for OOo
      • simplecanvas/ A wrapper around XCanvas, providing old-skool procedural rendering functions
      • tools/ Common tools shared among c++ canvas implementations
      • vcl/ The VCL OutputDevice-based implementation
    • workben/ a little demo app exercising the Canvas


Client code

Currently, the only production code using Canvas is the Impress Slideshow. For OOo 3.0, it's planned to also port Draw and Impress application to use it.

For fast development turnarounds, it's recommended to use (and extend, if necessary) the canvasdemo executable in the canvas/workben directory.

If you want to use the canvas in your code, and have access to VCL classes, it's easiest to grab your canvas instance by invoking OutputDevice::GetCanvas() or Window::GetSpriteCanvas() (if you need a nicely double-buffered, ready-for-animations instance). For a quick start on how to use the canvas, start perusing the XCanvas API documentation.

If you don't have access to VCL, things are a bit more complicated right now - you should use the com.sun.star.rendering.CanvasFactory service:

Reference<lang::XMultiServiceFactory> xCanvasFactory(xFactory->createInstance(
     OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.rendering.CanvasFactory"))), 
     UNO_QUERY );
Reference<rendering::XCanvas> xCanvas( 
     xCanvasFactory->createInstanceWithArguments(
         OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.rendering.Canvas")),
         aArg),
     UNO_QUERY);

That leaves the content of aArg - and herein lies the rub, as a certain amount of system-specific information has to be transported there. Generally, the canvas implementations expect a sequence of six Anys, with the following content:

  • ptr to creating VCL instance (Window or VirtualDevice) - this is only used from the vclcanvas implementation
  • a SystemEnvData struct as a streamed Any (or empty for VirtualDevice) - see sysdata.hxx for what your platform expects therein
  • current bounds of creating instance as an awt::Rect
  • bool, denoting always on top state for Window (always false for VirtualDevice)
  • awt::XWindow as the parent/owning Window (or empty for VirtualDevice)
  • a SystemGraphicsData struct as a streamed Any (or empty for Window) - see sysdata.hxx for what your platform expects therein

Concepts

A lot of the concepts founding the Canvas design are outlined in the UNO IDL documentation - please start here exploring the set of Canvas interfaces.

The central interface is css::rendering::XCanvas, this is place where most of the drawing methods are located. The XCanvas interface is free of client-modifiable state, i.e. it can be used safely and without external synchronization in a multi-threaded environment. On the other hand, this implies that for nearly every canvas operation, external state is required. This is provided by ViewState and RenderState structs in a unified fashion, supplemented by a few extra state parameters for some methods (e.g. for textured polygons or text rendering).

When used careless, this scheme can be inefficient to some extent, because internally, view, render and other states have to be combined before rendering. This is especially expensive for complex clip polygons, i.e. when both ViewState and RenderState have a complex clip polygon set, which have to be intersected before rendering. It is therefore recommended to combine ViewState and RenderState already at the client side, when objects are organized in a hierarchical way: the classic example are grouped draw shapes, whose parent group object imposes a common clipping and a common transformation on its siblings. The group object would therefore merge the ViewState and the RenderState it is called with into a new ViewState, and call its siblings with a RenderState containing only the local offset (and no extra clipping).

Further on, this stateless nature provides easy ways for caching. Every non-trivial operation on XCanvas can (but need not) return a cache object, which, when called to redraw, renders the primitive usually much more quickly than the original method. Note that such caching is a lot more complicated, should the actual rendering a method yields depend on internal state (which is deliberately not the case for XCanvas). Please note, though, that deciding whether to return an XCachedPrimitive is completely up to the implementation - don't rely on the methods returning something (this is because there might be cases when returning such a cache object will actually be a pessimization, since it involves memory allocation and comparisons).

Entities that need more than a small, fixed amount of data are encapsulated in own interfaces, e.g. polygons and bitmaps. As a client of the Canvas, you can, in principle, roll your own implementations of these interfaces, wrap it around your internal representation of polygons and bitmaps, and hand them over to the XCanvas to render them. It might just not be overly fast, because the XCanvas would need to convert for each render call. It is therefore recommended to create such objects via the XGraphicDevice factory (to be retrieved from every canvas object via the getDevice() call) - they will then be internally optimize to the underlying graphics subsystem.

Configuration

With the introduction of dxcanvas in OOo (was a StarOffice-only feature before), it became apparent that some sort of user-tweakable configuration was necessary - because with DirectX, this canvas implementation uses a part of the operating system that has some notoriety for bugs.

So, the DirectX canvas implementation can be disabled by black-listing specific card/driver combinations. If the canvas has issues on your specific setup, consider generating a blacklist entry and share that with the rest of the community.

Personal tools