Canvas shortcomings


As I promised, here are my thoughts about current canvas infrastructure.

1. Coordinate hell
The pixels_per_unit and coordinate systems are mess from prehistoric 
times, when there were no per-item transformations. The clean structure
should be something like:
a) Every item has 'transform' attribute, that is either NULL
(==identity), os 2x6 matrix. No separate translation case.
b) Root item has transformation as well. By convenience it should
   not be directly settable (add a child group, if you need something
   more complex), but ..._pixels_per_unit could modify it, so we
   retain compatibility, while cleaning up structure.
c) There should be 3 coordinate systems:
   - world coordinates
   - window coordinates
   - user coordinates (item coordinates)
   with convenience methods (using window ones is tricky at moment)

1. Item classes
I do not know average canvas usage. AFAIK all applications needing
extensibe canvas use, are implementing set of customized items, so
stock ones are mainly unused - and I cannot imagine, in which case
these should be used at all. You cannot build good-looking display
easily from blocks like polygons, ellipses and lines. Compare canvas
to Gtk+, that has relatively constrained usage (user inputs), but
is using complex styling, theming etc. enginery.
Well, nevertheless we could create certain set of stock items, and
hope that people will find some uses to these.

a) Group item
It is unfortunate, because many implementors want to derive their
own item trees. Because they want to support grouping as well,
everything has to be subclass of base canvas group.
One good approach would be to make standard grouping procedures
::add_child ::remove_child ::set_order class methods. So implementors
can create custom item class, and implement grouping methods only for
their derived grouping class.
So gnome_canvas_item_new should not take GnomeCanvasGroup as parent,
but GnomeCanvasItem - and check, that given item implements grouping
class methods.
Also one should get rid of "x" and "y" attributes - we can
use convenience frontend gnome_canvas_item_translate, that modifies
affine transormation instead.

b) shape item
It is bloat to create endless classes of shape items (polygins, 
polylines, ellipses, rects...). I would suggest taking oo approach,
and implement single GnomeCanvasShape object, that draws bezier path.
Everything else are simple subclasses, generating bpath from
A bit more complex approach would be to create class hiearchy:
Path -> Shape -> rects,ellipses,...
So path could be reused for clip path specifying.

c) Image item
This is conceptually simple. I would not use "x" & "y" here too.

d) Text item
Well, that breaks everything. There is no simple way to have single
text item for both antialiased and Gdk canvas, unless:
- one uses XRender (but it breaks compatibility for many systems)
- one uses custom font specifying code (like SVG style that specifies
  font by name) and hopes that both X and gnome-print have similar fonts
- one uses überhack to read glyph images back from X server via
I am not sure. I would break aa/Gdk canvas compatibility here.

e) Widgets
Little to say here

f) Clipgroup
Clipping is present in current canvas infrastructure, but the single 
implemention known to me is in GnomePrintPreview. It is simply group,
that has bpath argument, and sets clip_path for its children update.

g) Magic items
It is relatively easy to implement translator objects - that allow
using aa items on Gdk canvas and vice versa. It would be cool to
implement these as stock items, because it is too much to hope
that all bonobo canvas objects plan to implement both rendering
One suboptimal but working sample is in sodipodi bonobo subdir.

h) Nifty stuff
Things like non-antialiased lines, handles with selectable shape and
extended set of events (grab, drag...), point collections etc.
If done well, these would be possibly more useful than basic
objects (rects, ellipses...).

3. Events
Real canvas uses have to extend event system. To adress more needs, it
would be helpful, if:

a) You can pass events through - i.e. make they to propagate not only
   towards root of item tree, but also to underlying item, if topmost
   one is not interested

b) Simple convenience - root item should pick items on empty space

4. Rendering bottlenecks
Current UTA system is evil for 2 reasons:

a) libart bugs
The biggest showstopper was art_uta_from_svp, which called 
art_vpath_from_svp (slow method), and the latter generated UTA
covering full object bbox.
Instead, if there would be art_svp_uta_merge, that would render
SVP directly to existing canvas coverage UTA, it could be very fast.
Also gnome_canvas_request_redraw_uta should merge UTA directly,
instead of doing union (involving allocing and freeing)

b) UTA scrolling problem.
Method sequence:
Without intermediate idle loop run possibly dirtyfies most of canvas
area immediately, because of merging offseted UTA-s (or dirtyfies
a much bigger area than original at least).
It could be solved by using offseted UTA. I.e. the one always aligned
to UTA grid in world coordinates, so scrolling canvas would either
modify UTA offset, or possibly scroll uta by grid step (which
preserves coverage).
There have been talks about abandoning UTAs completely - I do not
think this is justified if abovementioned problems could be solved.
Still it may be studied, whether 32x32 is optimal unit size.
Also more intelligent UTA->rect generator is needed. I'd like one,
that has user-definable coverage percentage. I.e. it tries to break
redraw area into rectangles, that are as big as possible, with
coverage at least n%

5. Graphic mode extensions
It would be convenient, if ::update would take some structure as
argument, instead of clip_path and affine, so implementation could
extend it according to needs.
Some examples:
For Gdk canvas it would make sense to have mask bitmap instead of
SVP clip path in context.
Many inplementation would love to use global opacity setting, inherited
along item tree.
It would make sense to implement extensible PaintServer class(es) for
things like bitmap and fractal fills, that could be reused by
gnome-print (and other interested parties). Read my gnome-print
extension proposal for more.

6. Canvas ::print and other extensions.
This is EVIL. Canvas is view, not model, and ignoring that drives people
inot bad programming practices :(
The problem:
Canvas is the only place in Gnome implementing reasonable, 
object-oriented 2D graph tree. So people find it, and think, that
they should subclass it for their program model. I have done that
myself, so I should know ;-)
Implement separate extensible 2D graph tree, that has methods like
::view (generates tree of canvas items) and ::print, so people could 
subclass that instead. It would make much more sense - plus cool things
like thumbnail generation etc. could be added much more easily. 

7. Some future ideas
It would be extra-cool to have multi-step interruptible render. I have
outlined required design for sodipodi experimental renderer (but there
is no usable code yet).
Basically I've extended ::update to have level parameter and requested
bbox. If item is already updated to given level, or is completely 
outside of bbox, it's ::update is not called.
Take the test case of some complex image, that has both low and high
quality modes and area of interest (cursor area for example):

a) update level A (bbox) is called for all items. As a result, items
   calculate their bounding boxes. If given item has to update itself to
   higher level for that, it sets level to that, so for next ::update
   with higher level, it can be skipped.
b) update level B (low-quality coverage) is called for items in
   area of interest. As a result, coverage is calculated, and redraw
c) render is called for coverage
d) Idle run to update widgetry
e) update level C is called for full canvas
f) if some items are outside of area of interest, these update
themselves, and request redraw
g) redraw
h) idle
i) update level D (hi quality coverage) is called on area of interest
j) render
k) idle
l) update level D for full canvas
m) render
It will also help, if both update and render would be interruptible in
case there are events pending.
Levels are implementation-specific, there can be more, or several ones
may be merged into one (if there is no different rendering modes).

Well, these are my ideas/suggestions. Feel free to ignore these.

Best wishes,
lauris Kaplinski

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]