graphics-gdk-pixbuf.html
来自「linux下gnome编程」· HTML 代码 · 共 1,729 行 · 第 1/4 页
HTML
1,729 行
width, height, GDK_INTERP_TILES, opacity, 16, 0xaaaaaa, 0x555555); /* Render checkerboard feather first, so the overlapping * normal feather (pixbuf1) will come out on top */ gdk_pixbuf_render_to_drawable_alpha(pixbuf2, dbuf_pixmap, 0, 0, 20, 130, width * 1, height * 1, GDK_PIXBUF_ALPHA_BILEVEL, 128, GDK_RGB_DITHER_NORMAL, 0, 0); gdk_pixbuf_render_to_drawable_alpha(pixbuf1, dbuf_pixmap, 0, 0, 0, 110, width, height, GDK_PIXBUF_ALPHA_BILEVEL, 128, GDK_RGB_DITHER_NORMAL, 0, 0); gdk_pixbuf_unref(pixbuf1); gdk_pixbuf_unref(pixbuf2); g_free(featherfile); }}void expose_event_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data){ /* Don't repaint entire window upon each exposure */ gdk_window_set_back_pixmap (widget->window, NULL, FALSE); /* Refresh double buffer, then copy the "dirtied" area to * the on-screen GdkWindow */ gdk_window_copy_area(widget->window, widget->style->fg_gc[GTK_STATE_NORMAL], event->area.x, event->area.y, dbuf_pixmap, event->area.x, event->area.y, event->area.width, event->area.height);}static void area_prepared_cb(GdkPixbufLoader *loader, gint x, gint y, gint width, gint height, gpointer data){ gchar *bkgd_file; GdkPixbuf *pixbuf; loader_pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); gdk_pixbuf_ref(loader_pixbuf); bkgd_file = gnome_pixmap_file ("gnome-error.png"); pixbuf = gdk_pixbuf_new_from_file(bkgd_file); g_free(bkgd_file); /* Copy placeholder image to loader_pixbuf */ gdk_pixbuf_copy_area(pixbuf, 0, 0, gdk_pixbuf_get_width(pixbuf), gdk_pixbuf_get_height(pixbuf), loader_pixbuf, 0, 0);}static void area_updated_cb(GdkPixbufLoader *loader, gpointer data){ gint height, width; GdkRectangle rect = { LOADER_X, LOADER_Y, 48, 48 }; g_message("Rendering Loader Pixbuf"); width = gdk_pixbuf_get_width(loader_pixbuf); height = gdk_pixbuf_get_height(loader_pixbuf); /* Copy latest version of progressive image to the double * buffer */ gdk_pixbuf_render_to_drawable_alpha(loader_pixbuf, dbuf_pixmap, 0, 0, LOADER_X, LOADER_Y, width, height, GDK_PIXBUF_ALPHA_BILEVEL, 128, GDK_RGB_DITHER_NORMAL, 0, 0); /* Trigger an expose_event to flush the changes to the * drawing area */ gtk_widget_draw(drawing_area, ▭);}static void nudge_loader( ){ guint bitesize = 256; guint writesize = bitesize; if (curpos >= filesize) return; /* Trim writesize if it extends past the end of the file */ if (curpos + bitesize >= filesize) writesize = filesize - curpos; /* Send next chunk of image */ if (!gdk_pixbuf_loader_write(loader, &filebuf[curpos], writesize)) g_warning("Loader write failed!!"); curpos += writesize; /* Clean up loader when we're finished writing the entire * file; loader_pixbuf is still around because we referenced it */ if (curpos >= filesize) gdk_pixbuf_loader_close(loader);}static void init_loader( ){ FILE *file; gchar *loadfile; loader = gdk_pixbuf_loader_new( ); gtk_signal_connect(GTK_OBJECT(loader), "area_prepared", GTK_SIGNAL_FUNC(area_prepared_cb), NULL); gtk_signal_connect(GTK_OBJECT(loader), "area_updated", GTK_SIGNAL_FUNC(area_updated_cb), NULL); /* Load file into memory for easier cycling */ loadfile = gnome_pixmap_file ("gnome-globe.png"); file = fopen (loadfile, "r"); if (file) { struct stat statbuf; if (stat(loadfile, &statbuf) == 0) { filesize = statbuf.st_size; g_message("Found file %s, size=%d", loadfile, filesize); filebuf = g_malloc(filesize); fread(filebuf, sizeof(char), filesize, file); } fclose(file); } else g_warning("Failed to find file %s", loadfile); /* Load the first small chunk, i.e., enough to trigger an * area_prepared event */ nudge_loader( ); g_free(loadfile);}gint event_cb(GtkWidget *widget, GdkEvent *event, gpointer data){ GdkEventButton *bevent = (GdkEventButton *)event; switch ((gint)event->type) { case GDK_BUTTON_PRESS: /* Handle mouse button press */ /* For clicks on the feather icon */ if (bevent->x >= 20 && bevent->x <= 68 && bevent->y >= 130 && bevent->y <= 178) { GdkRectangle rect = { 20, 130, 48, 48 }; opacity += 63; if (opacity > 255) opacity = 0; render_feathers( ); gtk_widget_draw(widget, &rect); } /* For clicks on the globe loader icon */ if (bevent->x >= 100 && bevent->x <= 148 && bevent->y >= 140 && bevent->y <= 188) { nudge_loader( ); } return TRUE; } /* Event not handled; try parent item */ return FALSE;}void quit_cb (GtkWidget *widget){ gdk_pixbuf_unref(loader_pixbuf); g_free(filebuf); gtk_main_quit ( );}int main (int argc, char **argv){ GtkWidget *app; GdkPixbuf *pixbuf; /* Includes a call to gdk_rgb_init( ) */ gnome_init (PACKAGE, VERSION, argc, argv); gtk_widget_push_visual(gdk_rgb_get_visual( )); gtk_widget_push_colormap(gdk_rgb_get_cmap( )); app = gnome_app_new(PACKAGE, "Sample GdkPixbuf App"); gtk_widget_pop_visual( ); gtk_widget_pop_colormap( ); /* Restrict window resizing to size of drawing buffer */ gtk_widget_set_usize(GTK_WIDGET(app), WIDTH, HEIGHT); gtk_window_set_policy(GTK_WINDOW(app), TRUE, FALSE, FALSE); /* Handle window manager closing */ gtk_signal_connect(GTK_OBJECT(app), "delete_event", GTK_SIGNAL_FUNC(quit_cb), NULL); /* Create drawing-area widget */ drawing_area = gtk_drawing_area_new ( ); gtk_widget_add_events(GTK_WIDGET(drawing_area), GDK_BUTTON_PRESS_MASK); gtk_signal_connect(GTK_OBJECT(drawing_area), "expose_event", GTK_SIGNAL_FUNC(expose_event_cb), NULL); gtk_signal_connect(GTK_OBJECT(drawing_area), "event", GTK_SIGNAL_FUNC(event_cb), NULL); gnome_app_set_contents(GNOME_APP(app), drawing_area); gtk_widget_show_all(app); /* Create double-buffered pixmap; must do it here, after * app->window is created. Inherits color map and visual * from app. */ dbuf_pixmap = gdk_pixmap_new(app->window, WIDTH, HEIGHT, -1); /* Create a GdkPixbuf out of our background gradient, then * copy it to our double-buffered pixmap */ init_drawing_buffer( ); pixbuf = gdk_pixbuf_new_from_data(drawbuf, GDK_COLORSPACE_RGB, FALSE, 8, WIDTH, HEIGHT, WIDTH * 3, NULL, NULL); gdk_pixbuf_render_to_drawable(pixbuf, dbuf_pixmap, app->style->fg_gc[GTK_STATE_NORMAL], 0, 0, 0, 0, WIDTH, HEIGHT, GDK_RGB_DITHER_NORMAL, 0, 0); init_loader( ); render_apples( ); render_feathers( ); gtk_main ( ); return 0;} </PRE></TD></TR></TABLE><P> This sample application works by creating a GdkPixmap resource to hold our double buffer and a GtkDrawingArea widget to display our graphical images. We use instances of GdkPixbuf to create and load smaller parts of the main image, which we then copy into our GdkPixmap with gdk_pixbuf_render_to_drawable_alpha( ). Each time we get an expose_event signal, we copy to the drawing-area widget only the portion that needs refreshing. When we change the double buffer, we must trigger an expose_event signal for that region with a call to gtk_widget_draw( ). </P><P> The action starts in main( ). We create a GnomeApp widget for our top-level window, pushing the GdkRGB visual and color map while we do so. This extra step ensures that we have a compatible color setup for gdk-pixbuf. Next we set the resizing policy so that the user can't resize the main window to larger than our drawing area. After that, we connect GTK+ signal handlers for the delete_event signal (to clean up on exit), the event signal (to handle mouse button presses), and the expose_event signal (to copy "dirty" areas from the double buffer to the drawing area). Finally, we create our GdkPixmap double buffer and load it with various icon images from the gnome-core package. </P><P> Our expose_event handler is quite simple. All it does is copy a rectangular area from the double buffer into the drawing area. It uses the coordinates passed in with event->area to avoid copying more than it has to, to restore the window after a resize or exposure. </P><P> After rendering the background gradient and the apple icons the first time, we don't have to worry about them again for the life of the application. Their images will continue to reside in the double buffer because we never do anything to overwrite them. The only areas we need to update after the initial rendering are the two areas we set up as clickable regions, in the event handler. To minimize the amount of extra work we do and increase the performance of our application, we set up a clipping region for these two areas by passing a GdkRectangle instance into gtk_widget_draw( ) when we update their images in the double buffer. </P><P> The way we handle mouse clicks is a little heavy-handed. We end up hardcoding the coordinates for the two clickable regions. If the user clicks inside the region we define for the feather icon, we increase the opacity and redraw the feather. If the mouse click lands on the globe icon, we update that icon through the progressive loader. We ignore mouse clicks everywhere else. This approach is fairly manageable for a couple of small icons that never move, but it isn't scalable at all. If we had movable objects, we would have to add a lot of extra code to keep track of the clickable "hot spots." This type of code can become complex very quickly, and it probably isn't something you'll want to attempt if you don't have to. The GNOME Canvas (see Chapter 11) addresses exactly these issues and abstracts away a great deal of the complexity of dealing with movable graphics. If you're doing anything more complex than displaying static images, you should consider using the Canvas instead of GtkDrawingArea. </P><P> The progressive loader updates the globe icon. Inside our init_loader( ) function, we set up the callbacks for the loader, load the entire gnome-globe.png file into filebuf, and then call nudge_loader( ) once to write the first bit of the file into the loader's pixel buffer. This action loads enough image data to determine the size and color depth of the image, which in turn triggers the area_prepared signal. In the area_prepared callback, we set up a tempo- rary placeholder icon, using the gnome-error.png graphic. We use the gdk_pixbuf_copy_area( ) function to copy the image from one pixel buffer to another. </P><P> Each time you click on this icon, the application calls nudge_loader( ) again, which writes out another few scan lines, slowly replacing the background gnome-error.png placeholder image with the foreground gnome-globe.png image. When the image is completely loaded, we call gdk_pixbuf_loader_close( ) to free up the loader. At this point, if we hadn't referenced loader_pixbuf in the area_prepared callback, that pixbuf would have been destroyed along with the loader. As it is, the loader_pixbuf will remain until we unreference it in the delete_event callback. </P><P> You may have noticed from the code or from the jagged edges in the screen shot that we are not achieving the best graphics quality possible. Even though the graphics are all four-channel RGBA images, fully capable of alpha blending, we fail to take advantage of the alpha channel because our double buffer is an X11 pixmap, not a GdkPixbuf instance. Because we render each image separately to the server-side pixmap, the alpha channel is reduced to a 1-bit mask for each copy operation. If we had used GdkPixbuf for the double buffer instead of GdkPixmap, we could have kept the full range of the alpha channel intact, leading to smoother edges on the apples and better blending with the background gradient. Consider this a challenge to modify the sample application to use full alpha blending. </P></DIV></DIV><DIVCLASS="NAVFOOTER"><HRALIGN="LEFT"WIDTH="100%"><TABLEWIDTH="100%"BORDER="0"CELLPADDING="0"CELLSPACING="0"><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top"><AHREF="graphics-libart.html">Prev</A></TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="index.html">Home</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top"><AHREF="gnome-canvas.html">Next</A></TD></TR><TR><TDWIDTH="33%"ALIGN="left"VALIGN="top">Libart</TD><TDWIDTH="34%"ALIGN="center"VALIGN="top"><AHREF="graphics.html">Up</A></TD><TDWIDTH="33%"ALIGN="right"VALIGN="top">The GNOME Canvas</TD></TR></TABLE></DIV></BODY></HTML>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?