File Coverage

blib/lib/Image/Base/PNGwriter.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             # Copyright 2010, 2011, 2012 Kevin Ryde
2              
3             # This file is part of Image-Base-PNGwriter.
4             #
5             # Image-Base-PNGwriter is free software; you can redistribute it and/or
6             # modify it under the terms of the GNU General Public License as published
7             # by the Free Software Foundation; either version 3, or (at your option) any
8             # later version.
9             #
10             # Image-Base-PNGwriter is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13             # Public License for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Image-Base-PNGwriter. If not, see .
17              
18              
19             # Crib notes:
20             # ->plot() and in turn everything using that clips to the image size
21             # automatically
22              
23             package Image::Base::PNGwriter;
24             # Image::Base is good for 5.004 or some such far back, though
25             # Image::PNGwriter 0.01 requires 5.8.5, so that's the actual minimum. It
26             # looks like Image::PNGwriter could probably go earlier, unless maybe it
27             # needs a new enough xsubpp for C++.
28 3     3   55552 use 5.006;
  3         9  
  3         417  
29 3     3   18 use strict;
  3         6  
  3         105  
30 3     3   15 use warnings;
  3         9  
  3         116  
31 3     3   15 use Carp;
  3         6  
  3         258  
32 3     3   5774 use Image::PNGwriter;
  0            
  0            
33              
34             our $VERSION = 8;
35              
36             # version 1.12 for ellipse() $fill
37             # version 1.16 for diamond()
38             use Image::Base 1.12;
39             our @ISA = ('Image::Base');
40              
41             # uncomment this to run the ### lines
42             #use Devel::Comments;
43              
44             # Cribs:
45             #
46             # /usr/include/pngwriter.h
47              
48             use constant _DEFAULT_PALETTE => { 'black' => [ 0,0,0 ],
49             'white' => [ 1,1,1 ] };
50              
51             sub new {
52             my ($class, %params) = @_;
53             ### Image-Base-PNGwriter new(): %params
54              
55             # $obj->new(...) means make a copy, with some extra settings
56             if (ref $class) {
57             # needs the pngwriter copy-constructor ...
58             die "Image cloning not yet implemented";
59             # my $self = $class;
60             # $class = ref $class;
61             # if (! defined $params{'-pngwriter'}) {
62             # $params{'-pngwriter'} = $self->get('-pngwriter')->clone;
63             # }
64             # # inherit everything else
65             # %params = (%$self, %params);
66             }
67              
68             # -palette not yet documented, maybe call it -cindex anyway
69             # FIXME: make a per-instance anon hash
70             my $self = bless { -palette => _DEFAULT_PALETTE,
71             -zlib_compression => -1,
72             }, $class;
73             if (! defined $params{'-pngwriter'}) {
74             my $width = delete $params{'-width'};
75             if (! defined $width) { $width = 1; }
76             my $height = delete $params{'-height'};
77             if (! defined $height) { $height = 1; }
78              
79             # can't pass undef to Image::PNGwriter->new
80             my $filename = $params{'-file'};
81             if (! defined $filename) { $filename = ''; }
82              
83             # the filename to new() supplied is not read, just recorded in the $pw
84             my $pw = $self->{'-pngwriter'}
85             = Image::PNGwriter->new ($width, $height,
86             0, # background
87             $filename);
88             }
89             my $filename = delete $params{'-file'};
90              
91             $self->set (%params);
92              
93             if (defined $filename) {
94             $self->load ($filename);
95             }
96             ### $self
97             return $self;
98             }
99              
100             my %attr_to_get_method = (-width => 'getwidth',
101             -height => 'getheight',
102             # these not documented yet ...
103             -bitdepth => 'getbitdepth',
104             -gamma => 'getgamma',
105             -colortype => 'getcolortype');
106             sub _get {
107             my ($self, $key) = @_;
108             ### Image-Base-PNGwriter _get(): $key
109             if (my $method = $attr_to_get_method{$key}) {
110             return $self->{'-pngwriter'}->$method;
111             }
112             ### field: $self->{$key}
113             return $self->SUPER::_get ($key);
114             }
115              
116             sub set {
117             my ($self, %params) = @_;
118              
119             if (exists $params{'-pngwriter'}) {
120             $self->{'-pngwriter'} = delete $params{'-pngwriter'};
121             delete $self->{'-file'};
122             delete $self->{'-zlib_compression'};
123             delete $self->{'-title'};
124             delete $self->{'-author'};
125             delete $self->{'-description'};
126             delete $self->{'-software'};
127             }
128              
129             if (exists $params{'-width'} || exists $params{'-height'}) {
130             my $width = (exists $params{'-width'}
131             ? delete $params{'-width'}
132             : $self->{'-pngwriter'}->getwidth);
133             my $height = (exists $params{'-height'}
134             ? delete $params{'-height'}
135             : $self->{'-pngwriter'}->getheight);
136             $self->{'-pngwriter'}->resize ($width, $height);
137             }
138              
139             # not documented, yet ...
140             if (exists $params{'-gamma'}) {
141             $self->{'-pngwriter'}->setgamma (delete $params{'-gamma'});
142             }
143              
144             %$self = (%$self, %params);
145              
146             if (exists $params{'-file'}) {
147             $self->{'-pngwriter'}->pngwriter_rename ($params{'-file'});
148             }
149             if (exists $params{'-zlib_compression'}) {
150             $self->{'-pngwriter'}->setcompressionlevel ($params{'-zlib_compression'});
151             }
152              
153             # not documented yet ...
154             if (exists $params{'-title'} || exists $params{'-author'} || exists $params{'-description'} || exists $params{'-software'}) {
155             $self->{'-pngwriter'}->settext
156             (map {defined $params{$_} ? $params{$_} : ''} '-title', '-author', '-description', '-software');
157             }
158             }
159              
160             #-------------------------------------------------------------------------------
161             # load/save
162              
163             sub load {
164             my ($self, $filename) = @_;
165             if (@_ == 1) {
166             $filename = $self->get('-file');
167             } else {
168             $self->set('-file', $filename);
169             }
170             $self->{'-pngwriter'}->readfromfile ($filename);
171             }
172             sub save {
173             my ($self, $filename) = @_;
174             if (@_ == 2) {
175             $self->set('-file', $filename);
176             }
177             $self->{'-pngwriter'}->write_png;
178             }
179              
180              
181             #-------------------------------------------------------------------------------
182             # drawing
183              
184             sub xy {
185             my ($self, $x, $y, $colour) = @_;
186             ### xy: $x, $y, $colour
187             my $pw = $self->{'-pngwriter'};
188             $x = int($x);
189             $y = int($y);
190             $x++;
191             $y = $pw->getheight - $y;
192             if (@_ == 4) {
193             ### plot: $x, $y, $self->colour_to_drgb($colour)
194             $pw->plot ($x, $y, $self->colour_to_drgb($colour));
195             } else {
196             ### dread: $x, $y, $pw->dread($x,$y,1), $pw->dread($x,$y,2), $pw->dread($x,$y,3)
197             return sprintf ('#%02X%02X%02X',
198             map {int (255 * $pw->dread($x,$y,$_) + 0.5)} 1,2,3);
199             }
200             }
201             sub line {
202             my ($self, $x1, $y1, $x2, $y2, $colour) = @_;
203             my $pw = $self->{'-pngwriter'};
204             my $height = $pw->getheight;
205             $pw->line ($x1+1, $height-$y1,
206             $x2+1, $height-$y2,
207             $self->colour_to_drgb($colour));
208             }
209             sub rectangle {
210             my ($self, $x1, $y1, $x2, $y2, $colour, $fill) = @_;
211             ### Image-Base-PNGwriter rectangle(): $x1, $y1, $x2, $y2, $colour, $fill
212              
213             my $pw = $self->{'-pngwriter'};
214             my $height = $pw->getheight;
215             my $method = ($fill ? 'filledsquare' : 'square');
216             $pw->$method ($x1+1, $height-$y1,
217             $x2+1, $height-$y2,
218             $self->colour_to_drgb($colour));
219             }
220              
221             # Only $pw->circle available, apparently. For radius 2 it draws something
222             # like
223             #
224             # O
225             # O O
226             # O . O
227             # O O
228             # O
229             #
230             # which is x2==x1+4 and y2==y1+4. The parameters to circle() are integers,
231             # so only odd number of pixels across like this can be done ($x2-$x1 an even
232             # number), others go to Image::Base.
233             #
234             sub ellipse {
235             my ($self, $x1, $y1, $x2, $y2, $colour, $fill) = @_;
236             ### ellipse(): $x1, $y1, $x2, $y2, $colour, $fill
237             my $xr = $x2 - $x1;
238             if (! ($xr & 1) && $xr == ($y2 - $y1)) {
239             my $pw = $self->{'-pngwriter'};
240             $xr /= 2;
241             ### $xr
242             ### x centre: $x1+$xr
243             ### y centre: $pw->getheight() - ($y1+$xr)
244             my $method = ($fill ? 'filledcircle' : 'circle');
245             $pw->$method ($x1+$xr+1, $pw->getheight() - ($y1+$xr), $xr,
246             $self->colour_to_drgb($colour));
247             } else {
248             ### plain Image-Base
249             shift->SUPER::ellipse(@_);
250             }
251             }
252              
253             sub diamond {
254             my ($self, $x1, $y1, $x2, $y2, $colour, $fill) = @_;
255             ### diamond(): "$x1,$y1, $x2,$y2, $colour, fill=".($fill||0)
256             my $w = $x2 - $x1;
257             my $h = $y2 - $y1;
258             my $pw = $self->{'-pngwriter'};
259              
260             if ($w && $h) {
261              
262             ### x centre: $x1+int(($w+1)/2)+1
263             ### y centre: $pw->getheight() - ($y1+int($h/2)+1)
264             ### $w
265             ### $h
266              
267             my $method = ($fill ? 'filleddiamond' : 'diamond');
268             $pw->$method ($x1+int(($w+1)/2)+1,
269             $pw->getheight() - ($y1+int($h/2)),
270             $w, $h,
271             $self->colour_to_drgb($colour));
272             } else {
273             # 1xN or Nx1 dubious in PNGwriter 0.5.3, use rectangle instead
274             shift->rectangle (@_);
275             }
276             }
277              
278             #------------------------------------------------------------------------------
279             # colours
280              
281             # not documented, yet ...
282             sub colour_to_drgb {
283             my ($self, $colour) = @_;
284             if (exists $self->{'-palette'}->{$colour}) {
285             $colour = $self->{'-palette'}->{$colour};
286             }
287             if (ref $colour) {
288             return @$colour;
289             }
290              
291             # 1 to 4 digit hex, equally spaced from 00 -> 0.0 through FF -> 1.0, or
292             # FFFF -> 1.0 etc.
293             # Crib: [:xdigit:] matches some wide chars, but hex() as of perl 5.12.4
294             # doesn't accept them, so only 0-9A-F
295             if ($colour =~ /^#(([0-9A-F]{3}){1,4})$/i) {
296             my $len = length($1)/3; # of each group, so 1,2,3 or 4
297             my $divisor = hex('F' x $len);
298             return (map {hex($_)/$divisor}
299             substr ($colour, 1, $len), # full size groups
300             substr ($colour, 1+$len, $len),
301             substr ($colour, -$len));
302             }
303              
304             croak "Unknown colour: $colour";
305             }
306              
307             1;
308             __END__