File Coverage

blib/lib/Image/Base/Tk/Canvas.pm
Criterion Covered Total %
statement 15 112 13.3
branch 0 68 0.0
condition 0 38 0.0
subroutine 5 17 29.4
pod 9 10 90.0
total 29 245 11.8


line stmt bran cond sub pod time code
1             # Copyright 2010, 2011, 2012 Kevin Ryde
2              
3             # This file is part of Image-Base-Tk.
4             #
5             # Image-Base-Tk is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Image-Base-Tk is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Image-Base-Tk. If not, see .
17              
18              
19             # Tk::Canvas
20             # Tk::options configure(), cget()
21             #
22              
23             package Image::Base::Tk::Canvas;
24 1     1   757 use 5.004;
  1         3  
  1         44  
25 1     1   5 use strict;
  1         2  
  1         35  
26 1     1   6 use Carp;
  1         1  
  1         114  
27              
28 1     1   5 use vars '$VERSION', '@ISA';
  1         2  
  1         70  
29              
30 1     1   968 use Image::Base;
  1         2221  
  1         1861  
31             @ISA = ('Image::Base');
32              
33             $VERSION = 3;
34              
35             # uncomment this to run the ### lines
36             #use Devel::Comments '###';
37              
38             sub new {
39 0     0 1   my ($class, %params) = @_;
40             ### Image-Base-Tk new(): %params
41              
42             # $obj->new(...) means make a copy, with some extra settings
43 0 0         if (ref $class) {
44 0           croak "Cannot clone Image::Base::Tk::Canvas";
45              
46             # my $self = $class;
47             # $class = ref $class;
48             # if (! defined $params{'-tkcanvas'}) {
49             # $params{'-tkcanvas'} = $self->get('-tkcanvas')->copy;
50             # }
51             # # inherit everything else
52             # %params = (%$self, %params);
53             # ### copy params: \%params
54             }
55              
56 0 0         if (! defined $params{'-tkcanvas'}) {
57 0   0       my $for_widget = delete $params{'-for_widget'}
58             || croak 'Must have -for_widget to create new Tk::Canvas';
59 0 0         $params{'-tkcanvas'} = $for_widget->Canvas
    0          
60             ((exists $params{'-width'} ? (-width => $params{'-width'}) : ()),
61             (exists $params{'-height'} ? (-height => $params{'-height'}) : ()))->pack;
62             }
63 0           my $self = bless { -file_format => "eps" }, $class;
64 0           $self->set (%params);
65              
66 0 0         if (exists $params{'-file'}) {
67 0           $self->load;
68             }
69              
70             ### new made: $self
71 0           return $self;
72             }
73              
74              
75             my %attr_to_option = (-width => '-width',
76             -height => '-height',
77             );
78             sub _get {
79 0     0     my ($self, $key) = @_;
80             ### Image-Base-Tk-Canvas _get(): $key
81 0 0         if (my $option = $attr_to_option{$key}) {
82             ### $option
83 0           return $self->{'-tkcanvas'}->cget($option);
84             }
85 0           return $self->SUPER::_get ($key);
86             }
87              
88             sub set {
89 0     0 1   my ($self, %param) = @_;
90             ### Image-Base-Tk-Canvas set(): \%param
91              
92             # apply this first
93 0 0         if (my $tkcanvas = delete $param{'-tkcanvas'}) {
94 0           $self->{'-tkcanvas'} = $tkcanvas;
95             }
96              
97             {
98 0           my @configure;
  0            
99 0           foreach my $key (keys %param) {
100 0 0         if (my $option = $attr_to_option{$key}) {
101 0           push @configure, $option, delete $param{$key};
102             }
103             }
104             ### @configure
105 0 0         if (@configure) {
106 0           $self->{'-tkcanvas'}->configure (@configure);
107             }
108             }
109              
110 0           %$self = (%$self, %param);
111             }
112              
113             sub load {
114 0     0 1   my ($self, $filename) = @_;
115             ### Image-Base-Tk-Canvas load()
116 0           croak "Cannot load into canvas";
117             }
118              
119             my %format_to_module = (fig => 'Tk::CanvasFig');
120             my %format_to_method = (eps => 'postscript',
121             fig => 'fig');
122             sub _format_use {
123 0     0     my ($format) = @_;
124 0 0         if (! defined $format) { $format = "eps"; }
  0            
125 0           $format = lc($format);
126 0 0         if (my $module = $format_to_module{$format}) {
127 0 0         eval "require $module; 1" or die;
128             }
129 0           return $format;
130             }
131              
132             sub save {
133 0     0 1   my ($self, $filename) = @_;
134             ### Image-Base-Tk-Canvas save()
135 0 0         if (@_ == 2) {
136 0           $self->set('-file', $filename);
137             } else {
138 0           $filename = $self->get('-file');
139             }
140              
141 0           my $format = _format_use($self->get('-file_format'));
142 0           my $tkcanvas = $self->{'-tkcanvas'};
143 0 0         if ($format eq 'fig') {
144 0           $tkcanvas->fig (-file => $filename);
145             } else {
146 0           my $ret = $tkcanvas->postscript (-file => $filename);
147 0 0         if (defined $ret) {
148 0           croak $ret;
149             }
150             }
151             }
152              
153             # undocumented ...
154             sub save_string {
155 0     0 0   my ($self, $filename) = @_;
156             ### Image-Base-Tk-Canvas save()
157 0           return $self->{'-tkcanvas'}->postscript;
158             }
159              
160             #------------------------------------------------------------------------------
161              
162             my %anchor_is_xcentre = (n => 1, centre => 1, s => 1);
163             my %anchor_is_xright = (ne => 1, e => 1, se => 1);
164             my %anchor_is_ycentre = (w => 1, centre => 1, e => 1);
165             my %anchor_is_ybot = (sw => 1, s => 1, se => 1);
166              
167             sub xy {
168 0     0 1   my ($self, $x, $y, $colour) = @_;
169             ### Image-Base-Tk-Canvas xy(): "$x,$y"
170              
171 0           my $tkcanvas = $self->{'-tkcanvas'};
172 0 0         if (@_ > 3) {
173 0           $tkcanvas->createRectangle($x,$y, $x+1,$y+1,
174             -fill => $colour,
175             -width => 0); # outline width
176             } else {
177 0           my $item = ($tkcanvas->find('overlapping',$x,$y,$x,$y))[0];
178 0 0         if (! defined $item) {
179             ### no overlapping item, return background: $tkcanvas->cget('-background')
180 0           return $tkcanvas->cget('-background');
181             }
182 0           my $type = $tkcanvas->type($item);
183             ### $item
184             ### $type
185             # FIXME: look at -activefill etc according to state
186 0 0 0       if ($type eq 'rectangle' || $type eq 'oval' || $type eq 'polygon'
      0        
      0        
187             || $type eq 'arc') {
188             # FIXME: to do this properly would have to check if x,y is on the
189             # outline, according to the width, or the fill area
190 0   0       return (scalar($tkcanvas->itemcget($item,'-fill'))
191             || scalar($tkcanvas->itemcget($item,'-outline')));
192             }
193 0 0 0       if ($type eq 'line' || $type eq 'text') {
194 0           return scalar($tkcanvas->itemcget($item,'-fill'));
195             }
196 0 0         if ($type eq 'window') {
197 0           my ($wx,$wy) = $tkcanvas->coords($item);
198             ### $wx
199             ### $wy
200 0           my $widget = $tkcanvas->itemcget($item,'-window');
201 0   0       my $width = $tkcanvas->itemcget($item,'-width') || $widget->reqwidth;
202 0   0       my $height = $tkcanvas->itemcget($item,'-height') || $widget->reqheight;
203              
204             # change wx,wy to its top-left corner according to anchor
205 0           my $anchor = $tkcanvas->itemcget($item,'-anchor');
206 0 0         if ($anchor_is_xright{$anchor}) {
    0          
207 0           $wx -= $width-1;
208             } elsif ($anchor_is_xcentre{$anchor}) {
209 0           $wx -= int(($width-1)/2);
210             }
211 0 0         if ($anchor_is_ybot{$anchor}) {
    0          
212 0           $wy -= $height-1;
213             } elsif ($anchor_is_ycentre{$anchor}) {
214 0           $wy -= int(($height-1)/2);
215             }
216              
217             # change x,y to a position within the $widget
218 0           $x -= $wx;
219 0           $y -= $wy;
220              
221             ### $anchor
222             ### $wx
223             ### $wy
224             ### $width
225             ### $height
226             ### $x
227             ### $y
228             ### id: $widget->id
229 0 0 0       if ($x < 0 || $y < 0 || $x >= $width || $y >= $height) {
      0        
      0        
230             ### oops, why does overlapping give an out-of-range ? ...
231 0           return undef;
232             }
233 0           $widget->update;
234 0           require Tk::WinPhoto;
235 0           my $photo = $widget->Photo (-format => 'window',
236             -data => oct($widget->id));
237             # $photo->write ('/tmp/x.png', -format => 'xpm');
238             ### rgb: $photo->get($x,$y)
239 0           return sprintf ('#%02X%02X%02X', $photo->get ($x, $y)); # r,g,b
240             }
241             # if ($type eq 'image') {
242             # # copy Tk::Image to Tk::Photo to get its pixels, maybe ...
243             # }
244             # if ($type eq 'grid') {
245             # # but never occurs as an "overlapping", or something ...
246             # }
247             # if ($type eq 'bitmap') {
248             # # either its -background or -foreground ...
249             # }
250 0           return undef;
251             }
252             }
253              
254             # lower and right edges are excluded when filled, per X11 style
255             sub rectangle {
256 0     0 1   my ($self, $x1, $y1, $x2, $y2, $colour, $fill) = @_;
257             ### Image-Base-Tk-Canvas rectangle() ...
258              
259 0 0         $self->{'-tkcanvas'}->createRectangle($x1,$y1, $x2,$y2,
260             -outline => $colour,
261             ($fill ? (-fill => $colour) : ()));
262             }
263             sub ellipse {
264 0     0 1   my ($self, $x1, $y1, $x2, $y2, $colour, $fill) = @_;
265             ### Image-Base-Tk-Canvas ellipse() ...
266              
267             # seems that a 1xN or Nx1 pixel unfilled doesn't draw anything, so go filled
268 0   0       $fill ||= ($x1 == $x2 || $y1 == $y2);
      0        
269 0 0         $self->{'-tkcanvas'}->createOval($x1,$y1, $x2,$y2,
270             -outline => $colour,
271             ($fill ? (-fill => $colour) : ()));
272             }
273              
274             sub line {
275 0     0 1   my ($self, $x1, $y1, $x2, $y2, $colour) = @_;
276              
277             # must have 'projecting' to ensure the bottom right pixel drawn, per X style
278 0           $self->{'-tkcanvas'}->createLine($x1,$y1, $x2,$y2,
279             -fill => $colour,
280             -capstyle => 'projecting');
281             }
282              
283             sub diamond {
284 0     0 1   my ($self, $x1, $y1, $x2, $y2, $colour, $fill) = @_;
285             ### Image-Base-Tk-Canvas diamond()
286              
287 0           my $xh = ($x2 - $x1);
288 0           my $yh = ($y2 - $y1);
289 0           my $xeven = ($xh & 1);
290 0           my $yeven = ($yh & 1);
291 0           $xh = int($xh / 2);
292 0           $yh = int($yh / 2);
293             ### assert: $x1+$xh+$xeven == $x2-$xh
294             ### assert: $y1+$yh+$yeven == $y2-$yh
295              
296             # my $xh = ($x2 - $x1 + 1);
297             # my $yh = ($y2 - $y1 + 1);
298             # my $xeven = ! ($xh & 1);
299             # my $yeven = ! ($yh & 1);
300             # $xh = int($xh / 2);
301             # $yh = int($yh / 2);
302              
303 0 0         my $method = ($fill ? 'createPolygon' : 'createLine');
304 0 0         $self->{'-tkcanvas'}->$method ($x1+$xh,$y1, # top centre
    0          
    0          
    0          
    0          
    0          
305              
306             # left
307             $x1,$y1+$yh,
308             ($yeven ? ($x1,$y2-$yh) : ()),
309              
310             # bottom
311             $x1+$xh,$y2,
312             ($xeven ? ($x2-$xh,$y2) : ()),
313              
314             # right
315             ($yeven ? ($x2,$y2-$yh) : ()),
316             $x2,$y1+$yh,
317              
318             ($xeven ? ($x2-$xh,$y1) : ()),
319             ($fill ? () : ($x1+$xh,$y1)),
320              
321             -fill => $colour,
322             ($fill ? (-outline => $colour) : ()));
323             }
324              
325             1;
326             __END__