An image viewer on the Gnome::Canvas in perl
- From: dov imagic weizmann ac il
- To: gtk-list redhat com
- Subject: An image viewer on the Gnome::Canvas in perl
- Date: Sat, 22 Jan 2000 23:21:55 IST
Hello,
I wrote the following image viewer with the perl Gnome::Canvas module.
In terms of functionality everything except scrolling works quite well.
The main work of this pseudo widget is done in the sub view_changed()
that does the following series of calls:
$sim = $img->crop_and_clone($x0,$y0,$cw,$ch);
$sim2 = $sim->clone_scaled_image($sw, $sh);
$canv_img->get("image")->destroy_image;
$canv_img->set("image",$sim2);
Where:
$img is the original imlib image
$x0, $y0 Are the coordinates in the orig image beeing zoomed in.
$cw, $ch Are the width and the height of the area beeing zoomed in.
$sw, $sh are the scaled width and height.
Thus there is a lot of work being done for every scrolling and scaling
callback action. If the image had a parameter "-crop" indicating the
rectangle of the original image currently being viewed, then there
could be more intelligent scrolling being done inside the canvas image
widget code, but as it is now, I don't think I can make it any faster.
But I would be happy if you could prove me wrong.
I would also be very thankful if someone could make this pseudo widget
into a real widget, or at least point me to documentation of how to
do this in perl.
TIA,
Dov Grobgeld
#!/usr/local/bin/perl -w
######################################################################
# An example pseudo widget that is zoomable with callbacks similar
# to gimp.
#
# This code is released under the GPL.
# Dov Grobgeld 2000-01-23
######################################################################
use Gnome;
use strict;
######################################################################
# The zoom canvas code. This should be changed into a real widget.
######################################################################
sub new_zoom_canvas {
my ($img) = shift;
my $this = {};
$this->{current_scale} = 0;
$this->{x0} = 0;
$this->{y0} = 0;
$this->{do_scroll} = 0;
$this->{img} = $img;
$this->{canvas_width} = $img->rgb_width;
$this->{canvas_height} = $img->rgb_height;
my $canvas = Gnome::Canvas->new();
$this->{root} = $canvas->root();
$this->{canvas} = $canvas;
my $canv_img = Gnome::CanvasItem->new($this->{root},
"Gnome::CanvasImage",
image => $img->crop_and_clone_image(0,0,$img->rgb_width, $img->rgb_height),
x => 0,
y => 0,
width => $img->rgb_width,
height => $img->rgb_height,
anchor => "nw",
);
$canvas->set_usize($this->{canvas_width},$this->{canvas_height});
$canvas->set_scroll_region (0, 0, $this->{canvas_width},
$this->{canvas_height});
$canvas->signal_connect("key_press_event" =>
[\&key_press_handler, $this]);
$canvas->signal_connect("button_press_event" =>
[\&button_press_handler, $this]);
$canvas->signal_connect("button_release_event" =>
[\&button_release_handler, $this]);
$canvas->signal_connect("motion_notify_event" =>
[\&motion_handler, $this]);
$this->{canv_img} = $canv_img;
$this->{last_x} = 0;
$this->{last_y} = 0;
$this->{do_scroll} = 0;
view_changed($this, 1,0,0);
return $canvas;
}
sub key_press_handler {
my($this, $zc, $ev) = @_;
my $keych = pack("C*", $ev->{keyval});
if ($keych =~ /^[1]$/) { zoom_reset($zc); }
elsif ($keych =~ /^[>=]$/) { zoom_in($zc, $zc->{last_x}, $zc->{last_y}); }
elsif ($keych =~ /^[<-]$/) { zoom_out($zc, $zc->{last_x},$zc->{last_y}); }
}
sub button_press_handler {
my($this, $zc, $ev) = @_;
my($x,$y) = ($ev->{x}, $ev->{y});
my($button) = $ev->{button};
if ($button == 1) {
zoom_in($zc, $x,$y);
}
elsif ($button == 2) {
$zc->{do_scroll} = 1;
$zc->{last_x} = $x;
$zc->{last_y} = $y;
}
elsif ($button == 3) {
zoom_out($zc, $x,$y);
}
}
sub button_release_handler {
my($this, $zc, $ev) = @_;
my($button) = $ev->{button};
if ($button == 2) {
$zc->{do_scroll} = 0;
}
}
sub motion_handler {
my($this, $zc, $ev) = @_;
my($x,$y) = ($ev->{x}, $ev->{y});
my $current_scale = $zc->{current_scale};
my $current_x0 = $zc->{current_x0};
my $current_y0 = $zc->{current_y0};
my $last_x = $zc->{last_x};
my $last_y = $zc->{last_y};
if ($zc->{do_scroll}) {
my($dx, $dy) = (($last_x-$x) / $current_scale,
($last_y-$y) / $current_scale);
view_changed($zc, $current_scale, $current_x0 + $dx, $current_y0 + $dy);
}
$zc->{last_x} = $x;
$zc->{last_y} = $y;
}
######################################################################
# Called to do the repainting according to the scale and the
# new origin.
######################################################################
sub view_changed {
my($zc, $scale, $x0, $y0) = @_;
my $img = $zc->{img};
my $canv_img = $zc->{canv_img};
my $canvas_width = $zc->{canvas_width};
my $canvas_height = $zc->{canvas_height};
my $current_x0 = $zc->{current_x0};
my $current_y0 = $zc->{current_y0};
my $current_scale = $zc->{current_scale};
my ($width, $height);
if ($scale>128) {
return 0;
}
# Crop the request
$x0 = 0 if ($x0 < 0);
$y0 = 0 if ($y0 < 0);
if ($zc->{img}) {
my $img_width = $img->rgb_width;
my $img_height = $img->rgb_height;
if ($img_width*$scale > $canvas_width) {
$width = $canvas_width;
}
else {
$width = $img_width * $scale;
}
if ($img_height*$scale > $canvas_height) {
$height = $canvas_height;
}
else {
$height = $img->rgb_height * $scale;
}
if ($x0 + $width/$scale > $img->rgb_width) {
$x0 = int($img->rgb_width - $width/$scale);
}
if ($y0 + $height/$scale > $img->rgb_height) {
$y0 = int($img->rgb_height - $height/$scale);
}
}
$x0 = int($x0);
$y0 = int($y0);
if ($current_scale != $scale || $x0 != $current_x0 || $y0 != $current_y0) {
if ($img) {
my $cut_width = int(1.0*$width/$scale+0.9999);
my $cut_height = int(1.0*$height/$scale+0.9999);
my ($scale_width, $scale_height);
if ($cut_width > $img->rgb_width) {
$cut_width = $img->rgb_width;
}
if ($cut_height > $img->rgb_height) {
$cut_height = $img->rgb_height;
}
$scale_width = $cut_width * $scale;
$scale_height = $cut_height * $scale;
# Cut out a small rectangle
my $sim = $img->crop_and_clone_image($x0,$y0,$cut_width, $cut_height);
# Scale it to the current scale
my $sim2 = $sim->clone_scaled_image($scale_width, $scale_height);
$canv_img->get("image")->destroy_image; # Critical in order not to get a memory leek!
$canv_img->set("image",$sim2);
$canv_img->set("width",$sim2->rgb_width);
$canv_img->set("height",$sim2->rgb_height);
# cleanup;
$sim->destroy_image;
}
}
$zc->{current_scale} = $scale;
$zc->{current_x0} = $x0;
$zc->{current_y0} = $y0;
}
sub zoom_reset {
my($zc) = shift;
view_changed($zc, 1,0,0);
}
sub zoom_out
{
my ($zc, $x, $y) = @_;
my $current_scale = $zc->{current_scale}; # shortcut
my $new_scale=$current_scale /2;
my ($x0, $y0);
if ($new_scale < 1.0/16) {
$new_scale = 1.0/16;
}
$x0 = $zc->{current_x0} - $zc->{canvas_width}/$current_scale/2;
if ($x0<0) {
$x0 = 0;
}
$y0 = $zc->{current_y0} - $zc->{canvas_height}/$current_scale/2;
if ($y0<0) {
$y0 = 0;
}
view_changed($zc, $new_scale, $x0, $y0);
}
sub zoom_in {
my($zc, $x, $y) = @_;
my ($x0, $y0);
my $scale = $zc->{current_scale}; # shortcut
$x0 = $zc->{current_x0} + $x/$scale - $zc->{canvas_width}/4/$scale;
$y0 = $zc->{current_y0} + $y/$scale - $zc->{canvas_height}/4/$scale;
view_changed($zc, $scale * 2, $x0, $y0);
}
######################################################################
# main
######################################################################
init Gnome "img on canvas";
init Gtk::Gdk::ImlibImage;
my $img = Gtk::Gdk::ImlibImage->load_image(shift||"toroid.png") || die;
# Create main window
my $mw = Gtk::Widget->new("Gtk::Window",
-type => -toplevel,
-title => "Test of ZoomCanvas",
-signal::destroy => sub {exit}
);
# Create a zoom canvas
my $canvas = new_zoom_canvas($img);
$mw->add($canvas);
show $canvas;
$canvas->grab_focus();
$mw->show;
main Gtk;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]