Re: Rgba to pixbuf



On Mon, 14 Feb 2011 20:40:21 +0100
Mario Kemper <mario kemper googlemail com> wrote:

Hi all,

how do I convert rgba information into a pixbuf? I have an array with
rgba values (0-255) for each pixel. How do I need to pack those
information in order to use Gtk2::Gdk::Pixbuf->new_from_data?
Thanks.
Mario

Hi Mario,
Here are some old notes pertinent to your question.
I'm not keeping to up to date right now, but I hope
these old posts help. :-)
Here are a few old code gems from muppet and other experts, which may help.
I don't think much about this stuff now. Hope it helps :-) 

#################### note 1 #####################
On Feb 25, 2007, at 2:52 PM, Scott White wrote:

I am writing an application that reads medical imaging files (CT,  
MRI, etc in DICOM format) and simply displays them. However, I am  
having trouble using Gtk2::Gdk::Pixmap. I must use a pixmap since  
DICOM images are usually 16-bit, and almost always grayscale.
[...]
Unfortunately, I cannot make use of the much simpler pixbuf, since  
it only supports 8-bit pixels.

Stop right there.  GdkPixmap's option for 16 on the bits-per-pixel  
has nothing to do with what you're doing here.

GdkPixmap is a wrapper for XPixmap, which is a server-side resource.   
The pixel data is at the server's resolution and bitdepth.  When you  
think of a 16-bit pixmap, you are usually talking about packed 16-bit  
RGB data, or possibly 16-bit indexed color.  This is very, very  
different from a 16-bit grayscale image.

A 16-bit grayscale image contains grayscale data with 64k possible  
shades -- 256 times as much information as 8-bit data.  You monitor  
can't show you this much detail, and your video card deals only with  
8 bits per channel (8 x 3 = 24, sometimes with an unused padding byte  
for 32), so in order to display this image, you *must* mangle it to 8- 
bits-per-channel somehow.  The art it is how you choose to compress  
the dynamic range to make it visible.

The very simplest thing you can do is just show the most significant  
byte of each pixel.  That is:

        foreach pixel
                output = input >> 8

or

        $data = pack "C*", map { $_ >> 8 } unpack "S*", $raw;

(To display this in a native GdkPixbuf, you'd have to explode that to  
24-bit by replicating each pixel three times, which rather sucks,  
memory-wise.  The method draw_gray_image() on Gtk2::Gdk::Drawable  
would allow you not to have to expand your memory usage like this,  
but would require you to write your own image display widget.)

If the image uses its full dynamic range, you'll be able to see  
*something*.

However, it's often the case that 16-bit images don't use the full  
spectrum.  If you get a histogram, you may see hotspots in different  
parts of the spectrum.  You might also be losing the detail that you  
really need to see.  So, to be really fancy, you'd use different  
algorithms to map the 16-bit data to 8-bit.  That's where the fun  
comes in... gamma curves, sliders for the top and bottom of the  
selected portion of the dynamic range, etc, etc.


Here's a quick, very crappy example of a few different ways you can  
mangle the data.  Anything truly good will require more finesse than  
i was willing to put into 130 lines of code.  :-)


#!/usr/bin/perl -w
use strict;
use Gtk2 -init;
use Glib qw(:constants);

# input file is raw 16-bit grayscale image data at 512x512, from
# http://zentara.net/perlplay/DICOM/CT-MONO2-16-brain.raw
my $file = "CT-MONO2-16-brain.raw";
my $bits = 16;
my $width = 512;
my $height = 512;
open IN, $file or die "$file: $!\n";
my $expect_size = $width * $height * ($bits/8);
my $data;
my $n_read = sysread IN, $data, $expect_size;
die "File is the wrong size -- got $n_read, expected $expect_size\n"
        unless $n_read == $expect_size;


my $window = Gtk2::Window->new;
$window->signal_connect (destroy => sub { Gtk2->main_quit });

my $hbox = Gtk2::HBox->new;
$window->add ($hbox);

my $image = Gtk2::Image->new;
$image->set_size_request ($width, $height);
$hbox->add ($image);

my $vbox = Gtk2::VBox->new;
$hbox->add ($vbox);

foreach (
    { label => "out = low byte", func => \&low_byte },
    { label => "out = in >>  1", func => sub { shift_by (1) } },
    { label => "out = in >>  2", func => sub { shift_by (2) } },
    { label => "out = in >>  3", func => sub { shift_by (3) } },
    { label => "out = in >>  4", func => sub { shift_by (4) } },
    { label => "out = in >>  5", func => sub { shift_by (5) } },
    { label => "out = in >>  6", func => sub { shift_by (6) } },
    { label => "out = in >>  7", func => sub { shift_by (7) } },
    { label => "out = in >>  8", func => sub { shift_by (8) } },
    { label => "top 12 bits", func => sub { stretch ((1<<4)-1, (1<<16)-1) } },
    { label => "bottom 12 bits", func => sub { stretch (0, (1<<12)-1) } },
    { label => "bottom 10 bits", func => sub { stretch (0, (1<<10)-1) } },
    { label => "bottom 6 bits", func => sub { stretch (0, (1<<6)-1) } },
    { label => "sqrt curve", func => sub { curve () } },
) {
        my $button = Gtk2::Button->new ($_->{label});
        $button->signal_connect (clicked => $_->{func});
        $vbox->pack_start ($button, FALSE, FALSE, 0);
}

$window->show_all;

Gtk2->main;

sub set_from_rgb {
    my $rgb = shift;
    my $pixbuf = Gtk2::Gdk::Pixbuf->new_from_data ($rgb, 'rgb', FALSE, 8,
                                                   $width, $height,
                                                   $width * 3);
    $image->set_from_pixbuf ($pixbuf);
}


#
# Dead simple -- just mask off bits 9-16 and show only the low byte.
# This will result in interesting wrapping effects for any pixel >255.
#
sub low_byte {
    my $rgb = pack "C*",
                  map { ($_ & 0x00ff) x 3 }
                      unpack "S*", $data;

    set_from_rgb ($rgb);
}

#
# Like low_byte(), but shift right by n first.
#
sub shift_by {
    my ($n) = @_;
    my $rgb = pack "C*",
                  map { (($_ >> $n) & 0x00ff) x 3 }
                      unpack "S*", $data;

    set_from_rgb ($rgb);
}

#
# Attempt to do a linear scaling of the input pixels from the range
# [$min, $max] to [0,255].  This is implemented in a very slow manner...
#
sub stretch {
    my ($min, $max) = @_;

    sub scale_one {
        my ($min, $max, $this) = @_;
        my $val = int (($this - $min) / ($max - $min) * 255);
        $val = 255 if $val > 255;
        $val = 0 if $val < 0;
        return 0x00ff & $val;
    }

    my $rgb = pack "C*",
                  map { (scale_one ($min, $max, $_)) x 3 }
                      unpack "S*", $data;

    set_from_rgb ($rgb);
}

#
# Apply a square root transformation to each pixel.
# The result is like looking at the high byte, but with more detail
# in the dark areas.
#
sub curve {

    my $rgb = pack "C*",
                  map { (int (255 * sqrt ($_ / 65535.0))) x 3 }
                      unpack "S*", $data;

    my $pixbuf = Gtk2::Gdk::Pixbuf->new_from_data ($rgb, 'rgb', FALSE, 8,
                                                   $width, $height,
                                                   $width * 3);
    $image->set_from_pixbuf ($pixbuf);
}

##################### end note 1 #################################

###################### start note 2 ##################################

#!/usr/bin/perl
use warnings;
use strict;
use Glib qw(FALSE TRUE);
use Gtk2 -init;


# method1 by muppet ( no alpha ) 
# Width and height are in pixels.
# Red, green, and blue are assumed to be [0, 255].
sub create_solid_color_pixbuf {
  my ($width, $height, $red, $green, $blue) = @_;
  my $data = pack "C*", ($red, $green, $blue) x ($width * $height);
  return Gtk2::Gdk::Pixbuf->new_from_data ($data, 'rgb', 
           FALSE, 8, $width, $height,  $width * 3)
}

# method1a by muppet (improved to handle alpha see note at bottom) 
# Width and height are in pixels.
# Red, green, and blue are assumed to be [0, 255].
sub create_solid_color_pixbuf_with_alpha {
     my ($width, $height, $red, $green, $blue, $alpha) = @_;
     my $data = pack "C*", ($red, $green, $blue, $alpha) x ($width * $height);

return Gtk2::Gdk::Pixbuf->new_from_data ($data, 'rgb', TRUE, 8, 
                                         $width, $height, $width * 4);
}

# method1b by muppet (  alpha with gradient ) 
sub create_solid_color_pixbuf_with_alpha_gradient {
     my ($width, $height, $red, $green, $blue) = @_;
     my $data = pack "C*",
         map { ($red, $green, $blue, 
         ($_ * 255.0 / ($height - 1))) x $width } 0 .. ($height-1);

  return Gtk2::Gdk::Pixbuf->new_from_data ($data, 'rgb', TRUE, 8,
                                     $width, $height, $width * 4);
}

# method3 by Emmanuel Bassi
#even this might be seen as "complicated", when there's a nice method for
#Gtk2::Gdk::Pixbuf like fill():
sub create_solid_color_pixbuf1 {
    my ($width, $height, $red, $green, $blue,$alpha) = @_;
   # $red, $green, $blue and $alpha preconditions hold as above
  my $packed = $alpha
             | $blue  << 8
             | $green << 16
             | $red   << 24;
  my $pixbuf = Gtk2::Gdk::Pixbuf->new('rgb', TRUE, 8, $width, $height);
  $pixbuf->fill($packed);
  return $pixbuf;
}

my $window = Gtk2::Window->new;


my $pixbuf1 = create_solid_color_pixbuf(100,100,128,0,128);
my $image1 = Gtk2::Image->new_from_pixbuf ($pixbuf1);

# with alpha
my $pixbuf1a = create_solid_color_pixbuf_with_alpha(100,100,128,0,128,32);
my $image1a = Gtk2::Image->new_from_pixbuf ($pixbuf1a);

# with alpha gradient
my $pixbuf1b = create_solid_color_pixbuf_with_alpha_gradient(100,100,128,0,128,32);
my $image1b = Gtk2::Image->new_from_pixbuf ($pixbuf1b);


# with alpha layer (probably best approach)
my $pixbuf2 = create_solid_color_pixbuf1(100,100,0,128,128,32);
my $image2 = Gtk2::Image->new_from_pixbuf($pixbuf2);

my $hbox = Gtk2::HBox->new;
$hbox->pack_start ($image1, FALSE, FALSE, 6);
$hbox->pack_start ($image1a, FALSE, FALSE, 6);
$hbox->pack_start ($image1b, FALSE, FALSE, 6);
$hbox->pack_start ($image2, FALSE, FALSE, 6);

$window->add ($hbox);
$window->set_title ("Color Pixbufs");
$window->show_all;

$window->signal_connect (delete_event => sub {Gtk2->main_quit;});

Gtk2->main;

__END__
On Feb 17, 2007, at 10:30 AM, zentara wrote:

Emmanuel's seems more useful since it takes $alpha values too.
(but maybe muppet can improve his to take alpha :-)  )

Here you go.  Not very different.  Note that the row stride is now  
$width * 4 instead of $width * 3.

sub create_solid_color_pixbuf_with_alpha {
     my ($width, $height, $red, $green, $blue, $alpha) = @_;
     my $data = pack "C*", ($red, $green, $blue, $alpha) x ($width *  
$height);
     return Gtk2::Gdk::Pixbuf->new_from_data ($data, 'rgb', TRUE, 8,
                                              $width, $height, $width  
* 4);
}

However, now that Emmanuel has gently reminded me of the existence of  
fill(), i'd probably use that, because all of the work happens under  
the hood.

Still, now that you know how to use pack() with the repeat operator  
to fill with a color, it's just a short conceptual jump to  
manipulating your image data in perl.  For example:


sub create_solid_color_pixbuf_with_alpha_gradient {
     my ($width, $height, $red, $green, $blue) = @_;

     my $data = pack "C*",
         map { ($red, $green, $blue, ($_ * 255.0 / ($height - 1))) x  
$width }
             0 .. ($height-1);

     return Gtk2::Gdk::Pixbuf->new_from_data ($data, 'rgb', TRUE, 8,
                                             $width, $height, $width * 4);
}

Now I feel like i deserve an award for egregious disregard for memory  
efficiency.  :-)




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