Difference between revisions of "Canvas"

From Apache OpenOffice Wiki
Jump to: navigation, search
Line 56: Line 56:
 
*** '''vcl/''' The VCL OutputDevice-based implementation
 
*** '''vcl/''' The VCL OutputDevice-based implementation
 
** '''workben/''' a little demo app exercising the Canvas
 
** '''workben/''' a little demo app exercising the Canvas
 +
  
 
==Client code==
 
==Client code==
Line 62: Line 63:
  
 
For fast development turnarounds, it's recommended to use (and extend, if necessary) the canvasdemo executable in the canvas/workben directory.
 
For fast development turnarounds, it's recommended to use (and extend, if necessary) the canvasdemo executable in the canvas/workben directory.
 +
 +
 +
==Concepts==
 +
 +
A lot of the concepts founding the Canvas design are outlined in the UNO IDL documentation - please start [http://api.openoffice.org/docs/common/ref/com/sun/star/rendering/XCanvas.html  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 extend,
 +
    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).
 +
 +
    Furtheron, 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).
 +
 +
    Things 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.

Revision as of 23:16, 3 March 2008

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 ubiquituous 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.

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 (located here). 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.


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 extend,
   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).
   Furtheron, 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).
   Things 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.
Personal tools