alanonymous has asked for the wisdom of the Perl Monks concerning the following question:

Monks,

I'm done some text manipulation in perl, but I'm QUITE new to working with images and I've run into a bit of a problem. I'm just testing out some of the basic draw functions with the GD module that I'd like to use on a bigger more complex program. Start simple right? This is just supposed to draw an image and have the color go black to red on the x axis and black to green on the y axis.

The real problem I have is in the output of the picture (can be seen here: http://www.jasonmmoffett.com/alan_temporary/picture.png) Basically, about 1/3 of the way through scaling the x, the colors mess up. Is there a limit to the number of colors that can be defined with colorAllocate? Is there an easier way which you can define specific colors other than using a ton of colorAllocate calls? I dropped the actual number of colors used down because it was taking so long to run. Maybe a different drawing package all together would work better? Maybe I should try my hand in C? Really, I just need to be able to define colors based on equation output on a pixel by pixel basis. So I could have a script color any pixel in the image say 8-bits worth of color, or however many.

The code is below.

Thanks for your time!

-Alan

#!/usr/bin/perl #use strict; use GD; $width = 800; $height = 600; $outpizzle = new GD::Image($width,$height); #$outpizzle->interlaced('true'); #define a $%!@ ton of colors ... bad idea? for ($i=0; $i<10; $i++) { for ($j=0; $j<10; $j++) { for ($k=0; $k<10; $k++) { $colors[$i][$j][$k] = $outpizzle->colorAllocate($i*25,$j*2 +5,$k*25); } } } #draw some shizzle for ($i=0; $i<$width; $i++) { for ($j=0; $j<$height; $j++) { $outpizzle->setPixel($i,$j,$colors[int(10*($i/$width))][int(10 +*($j/$height))][0]); #print int(10*($i/$width))+"\n"; } } #output the picture open(PICTURE, ">picture.png") or die("uh oh spaghettio"); binmode PICTURE; print PICTURE $outpizzle->png; close PICTURE;

Replies are listed 'Best First'.
Re: Image Editing with GD
by BrowserUk (Patriarch) on Nov 10, 2006 at 12:13 UTC

    By default, for backwards compatibility with earlier versions of GD, images are created as 8-bit palletised. To get 24-bit color (truecolor), you need to indicate that you want this when you create the image, or afterwards using the $img->truecolor( 1 ); method.

    Change your image creation line to

    my $outpizzle = GD::Image->new( $width, $height, 1 );

    and observe the difference it makes to your output.

    Also, faking #use strict; serves no purpose whatsoever :) It took all of 10 seconds to do it properly.

    #!/usr/bin/perl use strict; use GD; my $width = 800; my $height = 600; my $outpizzle = new GD::Image( $width, $height, 1 ); #$outpizzle->interlaced('true'); #define a $%!@ ton of colors ... bad idea? my @colors; for ( my $i=0; $i<10; $i++) { for ( my $j=0; $j<10; $j++) { for ( my $k=0; $k<10; $k++) { $colors[$i][$j][$k] = $outpizzle->colorAllocate($i*25,$j*2 +5,$k*25); } } } #draw some shizzle for ( my $i=0; $i<$width; $i++) { for ( my $j=0; $j<$height; $j++) { $outpizzle->setPixel($i,$j,$colors[int(10*($i/$width))][int(10 +*($j/$height))][0]); #print int(10*($i/$width))+"\n"; } } #output the picture open(PICTURE, ">picture.png") or die("uh oh spaghettio"); binmode PICTURE; print PICTURE $outpizzle->png; close PICTURE; ## display result on win32 systems system 'picture.png';

    There are lots of other things that could be 'improved', but it's your code.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Thank you much for the help. I didn't realize it could have been as easy as adding a '1'.

      As far as the strict goes, it was just a reminder to do things correctly later :) I know I'm a bad programmer but I'm trying at least.

      As far as things that can be done differently go, is there an easier way to set a pixel to a specific color, without first creating that color with colorAllocate? I mean, it seems silly to me for it to not be easier. Using any more than 8 bits of color pixel by pixel seems like it would take forever to run.

      Thanks again for the help.

      -Alan

        When using truecolor images, I've found that I can skip the colorAllocate() step by specifying the packed rgb value inplace of the index. I use

        sub rgb{ unpack 'N', pack 'xCCC', @_ }

        for this, though the name is probably a misnomer.

        I'm not sure if this is documented anywhere, and cpan seems to be down at the moment, so I haven't been able to check. It seems to work okay with GD v2.30. It's also a lot faster and more convenient than using colorAllocate() to build an rbg->index table.

        This is a simpler, faster version of your original that uses the above:

        #!/usr/bin/perl -slw use strict; use GD; sub rgb{ unpack 'N', pack 'xCCC', @_ } my $width = 800; my $height = 600; my $img = new GD::Image( $width, $height, 1 ); for my $x ( 0 .. $width -1 ) { for my $y ( 0 .. $height -1 ) { $img->setPixel( $x, $y, rgb( $x * 255 / $width, $y * 255 / $height, 0 ) ); } } open my $fh, '>:raw', 'picture.png' or die "uh oh spaghettio : $!"; print $fh $img->png; close $fh; ## display result on win32 systems system 'picture.png';

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        Dunno that much about GD, but you can make your code more efficient like so:

        use GD; my $width = 800; my $height = 600; my $img = new GD::Image($width,$height,1); #draw some shizzle for (my $i=0; $i<=$width; $i++) { # calc $i colour only when $i changes my $icol = int(($i*255)/$width); for (my $j=0; $j<=$height; $j++) { # calc colorAllocate here and lose previous loop # also, no need to calc 3rd colour param $img->setPixel($i,$j,$img->colorAllocate($icol,int(($j*255)/$heigh +t),0)); } } #output the picture open(PICTURE, ">$test.png") or die("uh oh spaghettio"); binmode PICTURE; print PICTURE $img->png; close PICTURE;

        <update>wasn't black to red...</update>

        <update>...and we don't need @colors anymore...</update>

        Tom Melly, tom@tomandlu.co.uk