Re: Canvas shortcomings

Lauris Kaplinski <lauris ximian com> writes:

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

This is a fantasic summary, Lauris.  Thanks for taking the time to put
it together!

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

Agreed.  However, I don't think it matters that the root item's
transformation is directly settable.  I want the transformation of the
root item to be the one that transforms things to window coordinates.
I want to kill pixels_per_unit with extreme prejudice :)

> 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)

Item coordinates are trivial; they are just the composited
transformations of all the items from the root.

With this scheme, we can get rid of "world coordinates", which are a
side effect of the current pixels_per_unit mess.  If you make the
transformation of the root item be the one that converts from
root-item coordinates into window coordinates, the problem is solved
and we get rid of the so-called world coordinates.

(And we get a consistent model like SVG...)

> 1. Item classes
> I do not know average canvas usage. AFAIK all applications needing
> extensibe canvas use, are implementing set of customized items, 
> 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. 

I agree that the stock canvas items are almost useless.  I would like
to have something like

	Item - Abstract class, the most basic one.

	Group : Item - Abstract class, defines the API for an item
	that contains other items.

	Stack : Group - Like the current GnomeCanvasGroup, implements
	Z-ordering for items.

	BPath : Item - PS-like path, which is the most general shape.
	You can then have convenience functions like
	make_bpath_from_rectangle (x1, y1, x2, y2) or make_ellipse (x, y, a, b).

	Pixbuf : Item - A basic item for images.

	Text : Item - Joe has been writing a canvas item for the text
	buffer thing from GTK+ 2.0.

The idea of having an abstract Group class is to have something as
general as GtkContainer.  Then we provide a convenient Stack
implementation of Group, which would be equivalent to the current

In Evolution we have a few interesting classes like CanvasVBox; it
takes items that can compute their sizes and lays them out like a VBox
would.  You would implement such things by deriving from Group, not
Stack, and make your life easier.

Of course we can get rid of historical crap like the "x" and "y"
arguments for canvas groups.
> 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.

As you mentioned below in your message, the ::update() method should
get passed a structure with the clip path, global opacity, etc.  We
can extend that structure instead of passing many arguments.

> 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

Yes, this would be nice.  Perhaps we should make the ::event() method be

	typedef enum {
	} EventPropagation;

	EventPropagation ::event (item, event);

Or something like that?
> b) Simple convenience - root item should pick items on empty space

Yes.  I don't know what I was smoking when I did not do this.

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

UTAs are actually really nice if you use them properly.  However, the
canvas as it is right now uses them in about the worst way possible :)

For proper use of UTAs, you can of course look at EOG.  I am very
happy with what you can do with microtiles.

> 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)

Yes.  Libart should have a function that takes an existing microtile
array and puts the coverage of a SVP in there; you should not have to
merge UTAs at all.

All of the functions inside the canvas that deal with UTAs can use the
code from EOG, which does the right thing.  It has all the smarts to
deal with scrolling and such.

Finally, we can do many things to avoid tearing when repainting.  It
is easy to experiment with this to see what leads to the best results,
so I would not take a decision on it right now.

> 6. Canvas ::print and other extensions.

I don't think we want to be able to plug renderers into the canvas.
People who are really into overabstraction and stuff can
query_interface for a Print interface on their canvas items (assuming
you use GTK+ interfaces).

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

Hmmm, this is pretty interesting.

For the Reflow mechanism in the ECanvas (Evolution's extensions to the
GNOME canvas) we had to pull some gross hacks like using an idle
handler with higher priority like the one the canvas uses.  This is
required because reflow-enabled groups would change the layout of
their children in their ::update() method but the update cycle is not

I guess re-entrancy of the update cycle is completely different from a
multi-step render, though.

Other things I wanted to have:

1. Infinite items.  Items would have an IS_INFINITE flag.  Groups
   would keep a number_of_infinite_children field.  When redrawing,
   you would ignore the bounding box of groups or items that have this
   flag set, and always call their ::redraw() methods.  This would
   allow you to implement "background grid"-type items very easily.

2. Plenty of virtualization for the base Group item.  This is required
   for cleanliness and it would make it much easier to proxy the
   canvas over Bonobo without the fantastically broken hacks that we
   have right now.

A derivation of (2) is that we could have groups whose children do not
quite exist but are generated on the fly.  My canonical application
for this is a music score editor; you could avoid having thousands of
items and just generate them on the fly when you need them, because
you already know their types and positions.

A model/view canvas is a completely different can of worms and I am
still not convinced that we want to do that.  I guess we can still
make a point that the canvas is not your data model and beat people on
the head if they don't agree :)


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