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

Hello,

I've been dubbed with a task of emulating the output of a Charge-Coupled Device (CCD) camera. The idea is that the CCD chip at hand (Texas Instruments TC255 for those who are interested) outputs 8bit grayscale values for each pixels, one row at a time. This chip has a resolution of 324x243 pixels.

My first task was to actually generate a "random" image and display it for approval. This image could then be saved for the next step in our process. But here's my dillema. Since my data is just a large array of pixel values, I am displaying the data by drawing a rectangle on the canvas to represent each pixel, and for those who are counting, that is 78,732 pixels. My 700MHz Pentium comes to a screaching halt when the program rendering (or for that matter, refreshing) takes place. So, I am wondering if there is a better/faster way of displaying thousands of pixels. Here's my code

use strict; use Tk; my ( $mw, $canvas, $color, $radius, $colorLabel, $radiusLabel, @data, ); my $TCWidth = 324; my $TCHeight = 243; my $size = 1; $mw = MainWindow->new(-title=>"TC255 Emulator", -width=>$TCWidth*$size+100, -height=>$TCHeight*$size); newCanvas(); @data = getData(); drawData(\@data); $mw->Label(-borderwidth=>'1', -text=>"Radius:", -width=>'7', -font=>"Arial 8 bold")->place(-x=>$TCWidth*$size+10, -y=>'17'); $radiusLabel = $mw->Label(-borderwidth=>'1', -text=>$radius, -width=>'4', -anchor=>"w", -font=>"Arial 8")->place(-x=>$TCWidth*$size+55, -y=>'17'); $mw->Label(-borderwidth=>'1', -text=>"Color:", -width=>'7', -anchor=>"e", -font=>"Arial 8 bold")->place(-x=>$TCWidth*$size+10, -y=>'37'); $colorLabel = $mw->Label(-borderwidth=>'1', -text=>$color, -anchor=>"w", -width=>'4', -font=>"Arial 8")->place(-x=>$TCWidth*$size+55, -y=>'37'); $mw->Button(-text=>"New", -width=>5, -font=>"Times 8 bold", -command=>sub{$canvas->destroy(); newCanvas(); @data = getData(); drawData(\@data); $radiusLabel->configure(-text=>$radius); $colorLabel->configure(-text=>$color); })->place(-x=>$TCWidth*$size+28,-y=>67); $mw->Button(-text=>"Save", -width=>6, -font=>"Times 8 bold", -command=>sub{saveData(\@data); })->place(-x=>$TCWidth*$size+25,-y=>100); $mw->Button(-text=>"Open", -width=>6, -font=>"Times 8 bold", -command=>sub{$canvas->destroy(); newCanvas(); @data = openData(); drawData(\@data); })->place(-x=>$TCWidth*$size+25,-y=>133); $mw->Button(-text=>"Exit", -width=>6, -font=>"Times 8 bold", -command=>sub{exit;})->place(-x=>$TCWidth*$size+25,-y=>$TCHeig +ht-50); MainLoop; sub saveData{ my @d = @{$_[0]}; my $file = $mw->getSaveFile(-defaultextension => ".img", -filetypes => [['TC255 Image', '.img' ], ['All Files', '*', ], ], -initialdir => ".", -initialfile => "default.img", -title => "Save Image", ); if (!defined($file)){return;} open(DAT,">$file") || die "Can't open file: $!\n"; for (my $y=0; $y<$TCHeight; $y++){ for (my $x=0; $x<$TCWidth; $x++){ print DAT "$d[$y][$x],"; } print DAT "\n"; } close(DAT); } sub openData{ my @d; my $file = $mw->getOpenFile(-defaultextension => ".img", -filetypes => [['TC255 Image', '.img' ], ['All Files', '*', ], ], -initialdir => ".", -initialfile => "default.img", -title => "Open Image", ); if (!defined($file)){return;} open(DAT,"<$file") || die "Can't open file: $!\n"; my $y = 0; while ($_ = <DAT>){ chomp; my @q = split(/,/,$_); for (my $x=0; $x<$TCWidth; $x++){ $d[$y][$x] = $q[$x]; } $y++; } close(DAT); return @d; } sub newCanvas{ $canvas = $mw->Canvas(-width=>$TCWidth*$size, -height=>$TCHeight*$size, -relief=>"sunken", -borderwidth=>1)->place(-x=>0,-y=>0); } sub drawData{ my @d = @{$_[0]}; for (my $y=0; $y<$TCHeight; $y++){ for (my $x=0; $x<$TCWidth; $x++){ my $c = sprintf("#%02x%02x%02x",$d[$y][$x],$d[$y][$x],$d[$y][$x] +); $canvas->createRectangle($x*$size,$y*$size,($x+1)*$size,($y+1)*$ +size,-fill=>$c,-outline=>$c); } } } sub getData{ my @d; #intensity is a 8 bit value corresponding to grayscale colors # 0 = black # 255 = white $color = int rand(255); $radius = int rand(200); for (my $y=0; $y<$TCHeight; $y++){ for (my $x=0; $x<$TCWidth; $x++){ if((($x-$TCWidth/2)**2 + ($y-$TCHeight/2)**2) < ($radius**2)){ # +a circle $d[$y][$x] = $color; } else { $d[$y][$x] = 255; #white background } } } return @d; }

Thank you.

Replies are listed 'Best First'.
Re: Many Rectangles in Tk
by dreadpiratepeter (Priest) on Mar 11, 2002 at 17:38 UTC
    I had the same problem while drawing the map-overview in my roguelike game. What I did was make a large blank image, the size of the map, and a another image with 1-by-1 boxes containing all the possible values for color.
    Then I used the copy facility of the photo widget to copy the right color into the right spot on the map image. So in your algorithm just replace the createRectangle call with a call to Photo->copy. It was a lot faster and less memory intensize than the rectangle method.
    I could dig up the code if needed.

    -pete
    "I am Jack's utter lack of disbelief"
      Thank you very much. This sounds much like what I need. The only question is, is it absolutely necessary to have a 1x1 image for each possible color beforehand? The program is not very scaleable this way. Can Tk::Images or Tk::Photos be created on the fly by specifying the colors of the pixels (or pixel as the case may be)?
        I found the answer to this to be "yes". Tk:Photo allows me to put data in one pixel at a time. It is significantly faster, but no as fast as I thought it should be. Regardless, this is not a critical part of my emulation, so this ought to do.