Re: Handling Gnome::Canvas data




On Jun 23, 2006, at 12:14 PM, Chris Bartak wrote:

I've been trying to render a Gnome::Canvas to a pixbuf, without any luck. I read that in Perl you must use the pack() function to pack the pixel data from the Gdk::Window into a scalar. Unfortunately, I'm not sure how to get the pixel data, or how to pack it properly. Could anyone provide a quick demo of how this is done?

There are a few different issues with what you're asking, so forgive me for answering the pieces and not the whole.

The pixel data of a Gtk2::Gdk::Pixbuf is, indeed, packed into a scalar. It's either 24-bit RGB or 32-bit RGBA, depending on whether the pixbuf has an alpha channel. The math to walk around in the pixels is quite simple, provided you remember to use the rowstride. I've included a simple bit expensive example of walking through pixel data below; there's another example included with Gtk2 in examples/ color_snooper.pl[1], which uses substr() along with unpack() to use less memory when attempting to deal with packed pixel data.

You can get a pixbuf from any drawable with Gtk2::Gdk::Pixbuf::new_from_drawable(). This basically gets the contents of the drawable as a GdkImage, then converts the GdkImage to a GdkPixbuf for you -- that is, it's rather like a screenshot. Beware that it will just get the contents of the window as they are, so if the window is obscured or not completely drawn, you'll get what you see. The example code below shows how to use new_from_drawable (), as well as how to deal with no-window widgets.

So, as you might guess, about the only way i know of to "render" a Gnome2::Canvas to a pixbuf is to use the screen grab method i just described, with all the potential pitfalls. There may be some other trick with canvas, but i don't know of it. Good luck!


[1] http://gtk2-perl.cvs.sourceforge.net/gtk2-perl/gtk2-perl-xs/Gtk2/ examples/color_snooper.pl?revision=1.1&view=markup

-=-=-=-=-=-

#!/usr/bin/perl -w

use strict;
use Gtk2 -init;

my $window = Gtk2::Window->new;
my $label = Gtk2::Label->new ("hi there");
my $button = Gtk2::Button->new ("grab it");
my $vbox = Gtk2::VBox->new;
$window->add ($vbox);
$vbox->add ($label);
$vbox->add ($button);
$window->show_all;
$window->signal_connect (destroy => sub { Gtk2->main_quit });
$button->signal_connect (clicked => sub {
        my $w2 = Gtk2::Window->new;
        $w2->add (Gtk2::Image->new_from_pixbuf (widget_to_pixbuf ($label)));
        $w2->show_all;
        my $w3 = Gtk2::Window->new;
        $w3->add (Gtk2::Image->new_from_pixbuf (widget_to_pixbuf ($button)));
        $w3->show_all;
        my $w4 = Gtk2::Window->new;
$w4->add (Gtk2::Image->new_from_pixbuf (swizzle (widget_to_pixbuf ($window))));
        $w4->show_all;
});
Gtk2->main;


sub widget_to_pixbuf {
        my $widget = shift;
        # The widget's allocation holds the coordinates of the widget's a
        # rectangle in the gdk window on which it is drawn.
        my $allocation = $widget->allocation;
        # Of course, the widget itself may not have a gdk window.  We'll
        # walk backwards up the widget tree until we find a widget with a
        # gdk window, and grab from that one.
        $widget = $widget->get_parent while $widget->no_window;
        # Now we get all the pixels of the widget as a pixbuf.
        return Gtk2::Gdk::Pixbuf->get_from_drawable
                                ($widget->window,
                                 $widget->get_colormap,
                                 $allocation->x,
                                 $allocation->y,
                                 0, 0,
                                 $allocation->width,
                                 $allocation->height);
}

sub swizzle {
        my $pixbuf = shift;
        # Get all the pixels of the image as a packed array of bytes.
        my $pixels = $pixbuf->get_pixels;
        # We'll be really wasteful and unpack the whole thing into a
        # large array.
        my @big = unpack 'C*', $pixels;
        # The image data is either 24-bit RGB or 32-bit RGBA.  We'll decide
        # whether the column offset (a.k.a. number of bytes per pixel) is
        # 3 for RGB or 4 for RGBA based on the pixbuf's has-alpha value.
        my $coloff = $pixbuf->get_has_alpha ? 4 : 3;
        # Now we'll do something silly -- zero out the green plane, and put
        # a ramp pattern into the red plane, while leaving the blue (and
        # alpha, if applicable) planes untouched.
        for (my $row = 0 ; $row < $pixbuf->get_height ; $row++) {
                # The length of a row and the number of bytes from one row
                # to the next are not necessarily the same.  The rowstride
                # tells you exactly how many bytes to go forward to get from
                # here to the same column in the next row.  We'll use that
                # instead of width * coloff to ensure that we don't have
                # runout problems.
                my $this_row_offset = $row * $pixbuf->get_rowstride;
                for (my $col = 0 ; $col < $pixbuf->get_width ; $col++) {
                        my $this_pixel_index =
                                $this_row_offset + $col * $coloff;
                        $big[$this_pixel_index+0] =
                                ($col / $pixbuf->get_width) * 255;
                        $big[$this_pixel_index+1] = 0;
                }
        }
        # Now, pack those pixels back together...
        $pixels = pack 'C*', @big;
        # and wrap a new pixbuf around them.
        return Gtk2::Gdk::Pixbuf->new_from_data ($pixels, 'rgb',
                                                 $pixbuf->get_has_alpha,
                                                 8,
                                                 $pixbuf->get_width,
                                                 $pixbuf->get_height,
                                                 $pixbuf->get_rowstride);
}



--
Walk softly, and carry a BFG-9000.
  -- unknown




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