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-&#62;window, NULL, FALSE);  /* Refresh double buffer, then copy the "dirtied" area to   * the on-screen GdkWindow   */  gdk_window_copy_area(widget-&#62;window,    widget-&#62;style-&#62;fg_gc[GTK_STATE_NORMAL],    event-&#62;area.x, event-&#62;area.y,    dbuf_pixmap,    event-&#62;area.x, event-&#62;area.y,    event-&#62;area.width, event-&#62;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, &#9645;);}static void nudge_loader(  ){  guint bitesize = 256;  guint writesize = bitesize;  if (curpos &#62;= filesize)    return;  /* Trim writesize if it extends past the end of the file */  if (curpos + bitesize &#62;= filesize)    writesize = filesize - curpos;  /* Send next chunk of image */  if (!gdk_pixbuf_loader_write(loader, &#38;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 &#62;= 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, &#38;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-&#62;type)  {  case GDK_BUTTON_PRESS:    /* Handle mouse button press */    /* For clicks on the feather icon */    if (bevent-&#62;x &#62;= 20 &#38;&#38; bevent-&#62;x &#60;= 68 &#38;&#38;      bevent-&#62;y &#62;= 130 &#38;&#38; bevent-&#62;y &#60;= 178)    {      GdkRectangle rect = { 20, 130, 48, 48 };      opacity += 63;      if (opacity &#62; 255)        opacity = 0;      render_feathers(  );      gtk_widget_draw(widget, &#38;rect);    }    /* For clicks on the globe loader icon */    if (bevent-&#62;x &#62;= 100 &#38;&#38; bevent-&#62;x &#60;= 148 &#38;&#38;      bevent-&#62;y &#62;= 140 &#38;&#38; bevent-&#62;y &#60;= 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-&#62;window is created. Inherits color map and visual   * from app.   */  dbuf_pixmap = gdk_pixmap_new(app-&#62;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-&#62;style-&#62;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-&#62;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 + -
显示快捷键?