Wrapping Cairo

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Wrapping Cairo

Joachim Geidel
Hello everybody,

I have done some experiments with a backwards compatibility layer for
CairoGraphics. The idea was to wrap an arbitrary VisualComponent in a
CairoWrapper, and use a CairoGraphicsContext for redirecting the
rendering messages to a CairoContext.

I have uploaded my code to the public repository. If you want to play
with it, have a look at:
  CairoGraphics-Wrappers
  CairoGraphics-Wrappers-Examples
For the examples, you will need Akamaru 5.1 and CairoGraphics 68.2. The
Akamaru.DemoUI will show an additional display mode based on CairoWrapper.

CairoWrapper may already work for many VisualComponents which contain
only graphics and single-line text labels. Give it a try, and tell me
what you find.

CairoGraphicsContext supports scaling and transparency. This might be an
opportunity to add these features to existing VisualComponents without
having to completely rewrite their rendering code for Cairo. However,
use it at your own risk - the package is experimental code and not
production ready.

This is still very experimental, and I am not yet convinced that it is
possible to wrap an arbitrary window's component. Many of the widgets
coming with VisualWorks bypass the invalidate/damage repair mechanism
for updating their contents, and it will be a lot of work to hook into
all of the relevant methods (mostly because there's a lot of these
methods, they are hard to find, and some of these methods are, well, not
very accessible for changes). Another issue is the output of formatted
text. It will be hard to convince ComposedText, ComposedTextView,
ParagraphEditor and their respective subclasses to use Cairo. It may be
possible to replace a ComposedTextView by a Cairo-aware (or Pango-aware)
component (similar to Ditto.Label) on-the-fly, but this has still to be
done, and it also needs a controller which enables text editing. I'm not
sure how much work this would be, but it can probably be done.
CairoWrapper does not seem to work with the
DoubleBufferingWindowDisplayPolicy so far.

If you want to contribute or fix bugs, please use version names like
"n.xx.m", where n is the number of the version on which your changes are
based, xx are your initials, and m is a sequential number. Please
describe your changes in the blessing comment of your version.

Have fun with Cairo,
Joachim Geidel

Reply | Threaded
Open this post in threaded view
|

Re: Wrapping Cairo

Andre Schnoor

Joachim Geidel wrote:
> Hello everybody,
>
> I have done some experiments with a backwards compatibility layer for
> CairoGraphics.
[snip]

> This is still very experimental, and I am not yet convinced that it is
> possible to wrap an arbitrary window's component. Many of the widgets
> coming with VisualWorks bypass the invalidate/damage repair mechanism
> for updating their contents, and it will be a lot of work to hook into
> all of the relevant methods (mostly because there's a lot of these
> methods, they are hard to find, and some of these methods are, well, not
> very accessible for changes).
Yep. I experienced similar barriers while implementing our fast graphics
API for MacOS X. The majority of widget code assumes it has direct and
immediate control over the display surface (i.e. instant output). This
control however, is not available (or at least extremely expensive) on
most platforms other than Windows.

> Another issue is the output of formatted
> text. It will be hard to convince ComposedText, ComposedTextView,
> ParagraphEditor and their respective subclasses to use Cairo. It may be
> possible to replace a ComposedTextView by a Cairo-aware (or Pango-aware)
> component (similar to Ditto.Label) on-the-fly, but this has still to be
> done, and it also needs a controller which enables text editing. I'm not
> sure how much work this would be, but it can probably be done.
>  

I doubt this would be possible without requiring a full rewrite of large
portions of existing code. Text rendering and measurement (i.e.
determining character postions) is tightly interwoven with the VM
(probably for historical reasons). ParagraphEditor and related classes
rely on that.

> CairoWrapper does not seem to work with the
> DoubleBufferingWindowDisplayPolicy so far.
>  

 From what I have learned about the graphics system so far, I would
assume this is because Smalltalk makes ad-hoc copies of a
GraphicsContext like mad. I had an extremely hard time tracking the
countless copies of a GC on MacOS X. This is probably similar with
Cairo. A more obvious reason may be that Cairo's drawing is not stored
on the buffer medium (Pixmap).

BTW: Isn't double buffering redundant with Cairo anyway?

> If you want to contribute or fix bugs, please use version names like
> "n.xx.m", where n is the number of the version on which your changes are
> based, xx are your initials, and m is a sequential number. Please
> describe your changes in the blessing comment of your version.
>
> Have fun with Cairo,
> Joachim Geidel
>
>
>  

Andre

Reply | Threaded
Open this post in threaded view
|

Re: Wrapping Cairo

Michael Lucas-Smith-2

> Yep. I experienced similar barriers while implementing our fast
> graphics API for MacOS X. The majority of widget code assumes it has
> direct and immediate control over the display surface (i.e. instant
> output). This control however, is not available (or at least extremely
> expensive) on most platforms other than Windows.
You can't do that in WPF either. So it really only works on DirectFB and
Win32.

>
>> Another issue is the output of formatted
>> text. It will be hard to convince ComposedText, ComposedTextView,
>> ParagraphEditor and their respective subclasses to use Cairo. It may be
>> possible to replace a ComposedTextView by a Cairo-aware (or Pango-aware)
>> component (similar to Ditto.Label) on-the-fly, but this has still to be
>> done, and it also needs a controller which enables text editing. I'm not
>> sure how much work this would be, but it can probably be done.
>>  
>
> I doubt this would be possible without requiring a full rewrite of
> large portions of existing code. Text rendering and measurement (i.e.
> determining character postions) is tightly interwoven with the VM
> (probably for historical reasons). ParagraphEditor and related classes
> rely on that.
>
>> CairoWrapper does not seem to work with the
>> DoubleBufferingWindowDisplayPolicy so far.
>>  
>
> From what I have learned about the graphics system so far, I would
> assume this is because Smalltalk makes ad-hoc copies of a
> GraphicsContext like mad. I had an extremely hard time tracking the
> countless copies of a GC on MacOS X. This is probably similar with
> Cairo. A more obvious reason may be that Cairo's drawing is not stored
> on the buffer medium (Pixmap).
>
> BTW: Isn't double buffering redundant with Cairo anyway?
Not quite. You do double buffering by doing a group push, pop, write
around the main drawing to get double-buffering.

Michael

Reply | Threaded
Open this post in threaded view
|

Re: Wrapping Cairo

Joachim Geidel
In reply to this post by Andre Schnoor
Andre Schnoor schrieb am 08.09.2007 22:58:

>> Another issue is the output of formatted text. It will be hard to
>> convince ComposedText, ComposedTextView, ParagraphEditor and their
>> respective subclasses to use Cairo. It may be possible to replace a
>> ComposedTextView by a Cairo-aware (or Pango-aware) component
>> (similar to Ditto.Label) on-the-fly, but this has still to be done,
>> and it also needs a controller which enables text editing. I'm not
>> sure how much work this would be, but it can probably be done.
>
> I doubt this would be possible without requiring a full rewrite of
> large portions of existing code. Text rendering and measurement (i.e.
>  determining character postions) is tightly interwoven with the VM
> (probably for historical reasons). ParagraphEditor and related
> classes rely on that.
That's why I thought about completely replacing ComposedTextViews
and instances of its subclasses by new components, and also replace
their controllers (instances of ParagraphEditor and its subclasses) by
new ones implemented from scratch. It's probably cheaper to go that way
than trying to hook into the existing classes. By "replacing
on-the-fly", I meant something like this (renderOn: is the replacement
for displayOn: in CairoGraphics-Wrappers and is used with instances of
CairoGraphicsContext only):

ComposedTextView>>renderOn: aCairoGraphicsContext
        "If the receiver is still part of a hierarchy of VisualParts,
        replace it by a new text view which uses Pango."
        newComponent := PangoTextView initializedFrom: self.
        self container ifNotNil:
                [:c | c component: newComponent. self release.].
        "Draw the contents of the text view. All subsequent rendering
        messages will be sent to newComponent."
        newComponent renderOn: aCairoGraphicsContext.

On the other hand, I am not sure if it's worth the effort in the
presence of Widgetry, and people experimenting with making Widgetry use
Cairo, IIRC. My initial motivation was to make existing custom graphics
components look better by wrapping them in a CairoWrapper. The typical
use would be to wrap one VisualComponent, not the complete contents of a
window. However, it was tempting to investigate how far one could go...
so I have attached a screenshot showing a ComposedTextView rendered with
Cairo, including text and scrollbars. Scrolling the text actually works!
:-) Even selecting the text is not too far from what it should be.

> From what I have learned about the graphics system so far, I would
> assume this is because Smalltalk makes ad-hoc copies of a
> GraphicsContext like mad. I had an extremely hard time tracking the
> countless copies of a GC on MacOS X. This is probably similar with
> Cairo.
Yes, copying the GraphicsContext was one of the first issues I had to
deal with. Setting the clipping rectangle of a copied GC and forwarding
this to the CairoContext would totally eclipse all output which was
generated later. I have tried to track down the methods where the
GraphicsContext is copied in the code of the VisualComponent hierarchy.
Instead of

        copyGC:= aGraphicsContext copy
        ...draw on copyGC...

my code uses

        aGraphicsContext saveWhile: [:copyGC |...draw on copyGC...].

which will copy the GraphicsContext, but continue to draw on the same
CairoContext. The drawing operations are bracketed by calls to
cairo_save and cairo_restore. cairo_restore rolls back any temporary
changes to graphics attributes like color, line width, scale, clipping
etc. I wrote the replacement methods rather mechanically, which means
that there are several components where the Cairo compatibility isn't
"finished" in any way, because I did not yet deal with the methods which
do not copy the GC.

> BTW: Isn't double buffering redundant with Cairo anyway?
As Michael has already remarked, no. There is an example of Cairo-based
double buffering in the examples package. Using a CairoGraphicsContext,
you can write this:

aGraphicsContext bufferOutputWhile:
        [:copyGc | self displaySomethingOn: copyGc]

where copyGc is a copy of aGraphicsContext. During the execution of the
Block, all Cairo output is redirected to a temporary surface using
Cairo's cairo_push_group, and copied back to the original surface at the
end using cairo_pop_group_to_source and cairo_paint.

Of course, if the graphical component which was the reason for using a
DoubleBufferingWindowDisplayPolicy is wrapped in a CairoWrapper and uses
bufferOutputWhile: in its renderOn: method, double buffering at the
displayPolicy level should be unnecessary.

Thanks for the feedback!
Joachim

Workspace.jpg (31K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: Wrapping Cairo

Samuel S. Shuster <sames@interaccess.com>
In reply to this post by Andre Schnoor
Andre,

>Yep. I experienced similar barriers while implementing our fast graphics
>API for MacOS X. The majority of widget code assumes it has direct and
>immediate control over the display surface (i.e. instant output). This
>control however, is not available (or at least extremely expensive) on
>most platforms other than Windows.

FWIW, this is a Wrapper behavior. In Widgetry with FlickerFree, there is no
place in the framework that does direct display.

                                And So It Goes
                                     Sames
______________________________________________________________________

Samuel S. Shuster [|]
VisualWorks Engineering, GUI Project
Smalltalk Enables Success -- What Are YOU Using?

Reply | Threaded
Open this post in threaded view
|

Re: Wrapping Cairo

Andre Schnoor

Samuel S. Shuster wrote:
Andre,

  
Yep. I experienced similar barriers while implementing our fast graphics 
API for MacOS X. The majority of widget code assumes it has direct and 
immediate control over the display surface (i.e. instant output). This 
control however, is not available (or at least extremely expensive) on 
most platforms other than Windows.
    

FWIW, this is a Wrapper behavior. In Widgetry with FlickerFree, there is no
place in the framework that does direct display.

  

Great. That's is a big achievement. Such a clean behavior will simplify the adoption of Widgetry on our lightning fast graphics API for MacOS X a lot! Does Widgetry also avoid making copies of GCs?

Andre

Reply | Threaded
Open this post in threaded view
|

Re: Wrapping Cairo

Samuel S. Shuster <sames@interaccess.com>
Andre,

>Does Widgetry also avoid making copies of GCs?

Almost all of the time. I think there are about 2 or 3 places it copies, but
they may not be needed.

                                And So It Goes
                                     Sames
______________________________________________________________________

Samuel S. Shuster [|]
VisualWorks Engineering, GUI Project
Smalltalk Enables Success -- What Are YOU Using?

Reply | Threaded
Open this post in threaded view
|

Re: Wrapping Cairo

Travis Griggs-3
In reply to this post by Samuel S. Shuster <sames@interaccess.com>
On Sep 9, 2007, at 11:00, Samuel S. Shuster wrote:

Andre,

Yep. I experienced similar barriers while implementing our fast graphics 
API for MacOS X. The majority of widget code assumes it has direct and 
immediate control over the display surface (i.e. instant output). This 
control however, is not available (or at least extremely expensive) on 
most platforms other than Windows.

FWIW, this is a Wrapper behavior. In Widgetry with FlickerFree, there is no
place in the framework that does direct display.

I think it's important to distinguish (the posters probably all get this distinction, but not sure it's obvious to the casual readers) between two separable issues.

One is the technique of doing deferred operations. You can do this a variety of ways. You can do it with a backing store at the OS level. You can use a backing store at the Smalltalk level as the FlickerFree stuff does, which is basically to use a Pixmap via a DoubleBufferingDamagePolicy (and then play by the rules of using only invalidation to update it). You can do as the new single threaded OSX VM does, which is to actually queue real live NSObjects representing the drawing commands until draw time. Or as Andre did with his work which is a combination of VM and VI work, done for the compositional graphics. For completeness sake, Cairographics provides a facility for doing fast buffering too (groupWhile:).

The other issue is what the effect of copying GC's is. Copying GC's makes some of the above techniques harder (not all of them), especially when you're trying to layer over them. I think it's wrong to view GC copying as completely bad. If it makes what you're trying to do (i.e. map Cairo cr's on top of them), then it's bad because of that. But writing display code, it seems perfectly reasonable to me to want some way of entering a section of drawing code where you can make changes to the GC that will not affect other sections of code which are rendering to the same eventual back end. I completely love the fact that Cairo cr's have a fast/efficient stack implementation where you can enter a "drawing transaction" where you make adjustments to the current context, that will be forgotten after the "transaction." I've used it prodigiously. Without such a stack model, copying the GC's would be the next best thing.

--
Travis Griggs
Objologist
"Every institution finally perishes by an excess of its own first principle." - Lord Acton