[SOLVED] Another cairo problem: transferring data between gtk drawing area and backup cairo surface
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Another cairo problem: transferring data between gtk drawing area and backup cairo surface
So I have managed to draw all the musical glyphs that I need (except for crotchet rest, which still looks all wrong). Now I have a more fundamental problem.
The way pmwScribe works is that you pin glyphs on a musical stave which is actually a gtk drawing area. This has a backing pixbuf on which the actual drawing is done by the appropriate glyph subroutine. The rectangular area that is to contain the glyph is then refreshed from the pixbuf so that the glyphs become visible to the user. At the same time, the corresponding pmw code is written into the .pmw text file that will generate the score when processed by Philp's Music Writer.
In the old (GTK2) version of this program, the backup was a GdkPixbuf. With GTK3, you have to do it with a cairo surface. I can create such a surface with the properties of the widget's window by using gdk_window_create_similar_surface(). And I can create a cairo context for this surface and write my glyphs using it (I don't actually know if all that works but the individual bits of drawing code do work and the whole thing compiles).
But no amount of poring over cairo manuals has yet explained to me how to do the transfers from that surface to the window. It surely has to involve some kind of expose event/draw signal handler containing a cairo_paint() operation, but I can't find simple code for painting from an offscreen cairo surface onto a window.
To copy from a surface to a device, use cairo_set_source_surface () to select the surface into the device, and then call cairo_paint ().
If you want to read from the surface, create an image surface rather than a similar surface as the device. Cairo supports many types of surfaces, most of which are write-only. The image surface can be both read and written.
Ed
Thanks. Presumably I'll need a separate context for the transfer from the one I've been using for drawing on the pixbuf. Can I get the surface (now the source) from the old context (which I assume has an internal pointer to it)? I have that context permanently saved so that it is accessible from any function.
The best approach is to create the image surface and then create a context (cairo_t) from the surface to draw on. The image surface is kept. Contexts are created and destroyed as needed.
Does the program really need an intermediate (GdkPixbuf in GTK2 and cairo_image_surface in GTK3)? The reason I ask is that GTK3 automatically double-buffers drawing.
Ed
Does the program really need an intermediate (GdkPixbuf in GTK2 and cairo_image_surface in GTK3)? The reason I ask is that GTK3 automatically double-buffers drawing.
Ed
Ah! That's something I never considered. As I said before, this program was originally written using gtk2 with gdk2 graphics. I found, in some online source or other, a way of writing to a gtk_drawing_area using a GdkPixbuf as an intermediary surface and I used that. The program I was copying from didn't write to the widget directly so I assumed that was how it had to be done.
So what you are saying, if I understand you, is that gtk3 can do it all in a much simpler and more logical way. Just use the gdk window itself as a cairo surface like here and write the stave and the glyphs onto it and they'll be buffered automatically. And will this automatically handle expose events too so that I don't have to write a handler for them? If so, it's a great improvement in the API. I can understand how that works whereas before I was copying someone else's work blindly.
Thank you Tekk. That will take a bit of digesting. Actually I don't really draw on this widget with my mouse. I just select notes and rests from a musical toolbox and click on the stave where I want them to go. All the actual drawing is done by background functions. There's also a backspace/edit function incorporated in case of need, but the whole thing is really just a visual guide, so I don't have to memorise all the pmw code. The real output is the pmw text file which is being written in the background, which I can then convert into sheet music. I've been using this for years now to write out my cello parts.
I have a suggestion. I will spend the next few days seeing if I can apply your advice and Ed's to the program and get a fully compilable version. If so, we go on to debugging! If not, I'll come back for more help.
hazel - You need to write a draw () handler. GTK3 passes the device context on which to draw. teckk's example shows how to do this. It is using a similar surface rather than an image surface which is okay for a program that does not need to access pixel data.
Ed
Ok. I've rejigged everything so that all primary drawing is now on the drawing area itself and not the pixbuf. I found some code on Stackoverflow for getting a cairo context for the widget (you get a GdkContext first and then convert it), and all my glyph writing functions now use this context. I don't remember any more where I borrowed the original code from, but I suspect now that their use of an offscreen pixbuf as the primary drawing surface was a concession to the flicker effect that apparently occurred in gtk2 when you wrote directly to the screen.
Now about handlers: in the original gtk2 version I had handlers for a configure event and an expose event. The handler for the configure event created the backup surface if it did not already exist, and the one for the expose event copied from the backup to the exposed area of the window. There was always an expose event after each configure event and then there were additional ones due to the movement of other windows.
In the new version, the configure event can still work in the same way: the backup surface is created only if it does not already exist. This is different from the way Tekk does it in his example because his is a scribble program, whereas my stave and the notes drawn on it are serious work that has to be preserved for the duration of the program (or until the user calls for a new stave) and not casually overwritten. Also I have protected the stave area from changing shape even when the window is maximised so a one-off configuration should be OK (and hasn't caused any problems to date).
I notice that Tekk's little program does not contain an expose event handler but I think I shall need one, again because the stave data in my program are essential and must be redrawn from backup as and when required. I think this will require turning the pixbuf surface into a temporary source for the window's stored cairo context, somehow clipping the area I want to refresh and then calling cairo paint for that area.
And I shall need to copy newly drawn glyphs onto the pixbuf for safe storage as they are written. Is that what the draw signal handler does?
gdk_cairo_set_source_window and gdk_cairo_set_source_pixbuf should handle the copying between the two images: the window becomes the source during the configure event (using the pixbuf's cairo context) and the pixbuf during the expose event (using the window's cairo context).
The GTK3 draw handler replaces the expose handler in GTK2. The draw handler should do the actual drawing to the screen. The configure handler works in the same way in both GTK2 and GTK3.
Reading pixels from the framebuffer is too slow for general use. This functionality is intended for screen capture programs.
I know you don't want to hear suggestions for major changes, but IMO, the program should maintain a data structure of the musical score. All editing, drawing, saving, and printing are done from the data structure. Very little code should deal with drawing, and none should deal with pixels (pixels don't exist if the surface is a SVG or Postscript file). Written this way, the same code can draw to both raster and vector devices.
Ed
I think there's a slight misunderstanding here. pmwScribe is not a complete music-writing program. Such a thing would be far beyond my capacity. As its name suggests, pmwScribe is just a convenient adjunct to the wonderful pmw (Phillip's Music Writer) program, a quick-and-dirty way of creating pmw text files.
Think of pmw as a language. You write files in it, using your favourite text editor, and then the pmw program translates those files into printable music scores. The problem is that, as an occasional user, I could never remember the codes! For me, a stave interface is friendlier than an editor for this sort of thing. The big, well-known music-writing programs do of course have a stave writing interface but pmw doesn't. It's just a coding system plus a rather unusual postscript font and a translation program. So I decided to make things easier for myself by creating a visual aid in the form of an onscreen stave, where I could pin notes and the program would turn them into pmw code in the background. In addition it would be fun to write and I'd learn a bit more about programmig.
That was a few years ago now. Like all my graphical programs, pmwScribe used gtk2 and it still works very well. But last year I decided to rewrite all my graphical programs in gtk3. Think of it as a translation project. pmwScribe is the last one I need to do. I don't want to do more code rearrangement than is necessary. Remember I'm nearly eighty!
OK, so the screenwriting part of the program has four tasks:
1) Create a backup surface (done; the original configuration event handler does that);
2) Do the visual backup each time a new glyph is written onscreen (still to do and I'm not quite sure how);
3) Handle expose events (effectively done; I know now that I just need to convert the old expose event handler into a draw handler);
4) write the corresponding pmw code into the file (done because this part of the program doesn't need any re-coding).
PS: The reason why stage 2 is a problem for me is that there is nothing corresponding to it in the gtk2 version, where the glyphs were written directly to the backing pixbuf. Then they could be transferred to the screen on a one-by-one basis by using gtk_widget_queue_draw_area. Now I need to perform that particular operation the other way up.
Last edited by hazel; 04-01-2024 at 02:39 AM.
Reason: Added postscript
Thanks for the explanation! Your program is a simple editor.
I found that drag-drop had to be implemented differently on GTK3 compared to GTK2 because the Cairo->GDK->X11 pipeline operates in the forward direction only.
The draw handler should first draw the existing notes and then draw the dragged note on top. Every time the dragged note changes position, the entire window is redrawn. Incremental drawing is no longer advantageous since modern hardware can do a complete redraw at the hardware's frame rate.
The dragged note is dropped by drawing it on the surface containing the existing notes. Called in this way, Cairo writes to the framebuffer and never reads from it. I think you will find that the new code is much cleaner.
No, you've lost me again! Here's how it works at present:
You select a glyph type (say a crotchet) and click on the stave where you want it to go. Two button-press handlers are activated: the first causes the appropriate drawing function to draw the glyph on whatever surface you are using for that purpose, and the second writes the corresponding pmw code to the pmw code file. The visible notes aren't actually dragged over from the toolbox. I know there are interfaces that work like that but I have/had no idea how to do such a thing, so I have drawing routines that create the glyphs in situ. That means of course that we don't have to worry about
Quote:
the dragged note changing position.
Nothing ever changes position except during an edit operation.
Under the old gtk2 regime, the drawing surface was the backup pixbuf, so all data transfers were from the pixbuf to the drawing_area window. There was no traffic in the opposite direction. Individual notes were transferred to the window by exactly the same handler as was used for expose events; in this case it was invoked by a gtk_widget_queue_draw call at the end of the drawing operation.
Under the new regime, the drawing surface is the window itself, which I really like because it is far more logical than the old way. But it means that we now need an extra handler to copy individual notes to the backup pixbuf as they are written. Logically this means a third button-press handler.
Thanks for the explanation! Your program is a simple editor.
I found that drag-drop had to be implemented differently on GTK3 compared to GTK2 because the Cairo->GDK->X11 pipeline operates in the forward direction only.
The draw handler should first draw the existing notes and then draw the dragged note on top. Every time the dragged note changes position, the entire window is redrawn. Incremental drawing is no longer advantageous since modern hardware can do a complete redraw at the hardware's frame rate.
The dragged note is dropped by drawing it on the surface containing the existing notes. Called in this way, Cairo writes to the framebuffer and never reads from it. I think you will find that the new code is much cleaner.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.