File Coverage

blib/lib/Image/TextMode/Canvas.pm
Criterion Covered Total %
statement 57 75 76.0
branch 18 26 69.2
condition 1 3 33.3
subroutine 12 13 92.3
pod 10 10 100.0
total 98 127 77.1


line stmt bran cond sub pod time code
1             package Image::TextMode::Canvas;
2              
3 29     29   16929 use Moo;
  29         11950  
  29         182  
4 29     29   9569 use Types::Standard qw( Int ArrayRef );
  29         51599  
  29         237  
5 29     29   26841 use Image::TextMode::Pixel;
  29         74  
  29         23378  
6              
7             =head1 NAME
8              
9             Image::TextMode::Canvas - A canvas of text mode pixels
10              
11             =head1 DESCRIPTION
12              
13             This module represents the graphical portion of an image, i.e. a grid
14             of pixels.
15              
16             =head1 ACCESSORS
17              
18             =over 4
19              
20             =item * width - the width of the canvas
21              
22             =item * height - the height of the canvas
23              
24             =item * pixeldata - an arrayref of arrayrefs of pixel data
25              
26             =back
27              
28             =cut
29              
30             has 'width' => ( is => 'rw', lazy => 1, isa => Int, default => 0 );
31              
32             has 'height' => ( is => 'rw', lazy => 1, isa => Int, default => 0 );
33              
34             has 'pixeldata' => ( is => 'rw', lazy => 1, isa => ArrayRef, default => sub { [] } );
35              
36             =head1 METHODS
37              
38             =head2 new( %args )
39              
40             Creates a new canvas.
41              
42             =head2 getpixel( $x, $y )
43              
44             Get raw pixel data at C<$x>, C<$y>.
45              
46             =cut
47              
48             sub getpixel {
49 255     255 1 1836 my ( $self, $x, $y ) = @_;
50 255 100       4981 return unless exists $self->pixeldata->[ $y ]; # avoid autovivification
51 254         5857 return $self->pixeldata->[ $y ]->[ $x ];
52             }
53              
54             =head2 getpixel_obj( $x, $y, \%options )
55              
56             Create a pixel object data at C<$x>, C<$y>. Available options include:
57              
58             =over 4
59              
60             =item * blink_mode - enabed or disable blink mode for the pixel object
61              
62             =back
63              
64             =cut
65              
66             sub getpixel_obj {
67 2     2 1 4471 my ( $self, $x, $y, $options ) = @_;
68 2         6 my $pixel = $self->getpixel( $x, $y );
69 2 100       17 return unless $pixel;
70 1         7 return Image::TextMode::Pixel->new( %$pixel, $options );
71             }
72              
73             =head2 putpixel( \%pixel, $x, $y )
74              
75             Store pixel data at C<$x>, C<$y>.
76              
77             =cut
78              
79             sub putpixel {
80 1062     1062 1 19257 my ( $self, $pixel, $x, $y ) = @_;
81 1062         19697 $self->pixeldata->[ $y ]->[ $x ] = $pixel;
82              
83 1062         6734 my ( $w, $h ) = ( $x + 1, $y + 1 );
84 1062 100       18187 $self->height( $h ) if $self->height < $h;
85 1062 100       26083 $self->width( $w ) if $self->width < $w;
86             }
87              
88             =head2 dimensions( )
89              
90             returns a list of the width and height of the image.
91              
92             =cut
93              
94             sub dimensions {
95 6     6 1 1334 my $self = shift;
96 6         144 return $self->width, $self->height;
97             }
98              
99             =head2 clear_screen( )
100              
101             Clears the canvas pixel data.
102              
103             =cut
104              
105             sub clear_screen {
106 5     5 1 127 my $self = shift;
107 5         86 $self->width( 0 );
108 5         182 $self->height( 0 );
109 5         169 $self->pixeldata( [] );
110             }
111              
112             =head2 clear_line( $y, [ \@range ] )
113              
114             Clears the data at line C<$y>. Specify a range to clear only a portion of
115             line C<$y>.
116              
117             =cut
118              
119             sub clear_line {
120 6     6 1 202 my $self = shift;
121 6         5 my $y = shift;
122 6         8 my $range = shift;
123              
124 6 50       87 return unless defined $self->pixeldata->[ $y ];
125              
126 6 100       47 if ( !$range ) {
127 2         31 $self->pixeldata->[ $y ] = [];
128             }
129             else {
130 4 100       11 $range->[ 1 ] = @{ $self->pixeldata->[ $y ] } - 1 if $range->[ 1 ] == -1;
  2         29  
131             $self->pixeldata->[ $y ]->[ $_ ] = undef
132 4         74 for $range->[ 0 ] .. $range->[ 1 ];
133             }
134             }
135              
136             =head2 delete_line( $y )
137              
138             Removes the line from the canvas, moving all subsquent lines up.
139              
140             =cut
141              
142             sub delete_line {
143 2     2 1 87 my $self = shift;
144 2         2 my $y = shift;
145              
146 2 100       29 return unless exists $self->pixeldata->[ $y ];
147              
148 1         7 delete @{ $self->pixeldata }[ $y ];
  1         22  
149 1         20 $self->height( $self->height - 1 );
150             }
151              
152             =head2 as_ascii( )
153              
154             Returns only the character data stored in the canvas.
155              
156             =cut
157              
158             sub as_ascii {
159 1     1 1 2 my ( $self ) = @_;
160              
161 1         18 my $output = '';
162 1         2 for my $row ( @{ $self->pixeldata } ) {
  1         23  
163 1         7 for my $col ( @$row ) {
164             $output .= defined $col
165 1 50 33     9 && defined $col->{ char } ? $col->{ char } : ' ';
166             }
167 1         2 $output .= "\n";
168             }
169              
170 1         4 return $output;
171             }
172              
173             =head2 max_x( $line )
174              
175             Finds the last defined pixel on a given line. Useful for optimizing writes
176             in formats where width matters. Returns undef for a missing line.
177              
178             =cut
179              
180             sub max_x {
181 3     3 1 5 my ( $self, $y ) = @_;
182 3         54 my $line = $self->pixeldata->[ $y ];
183              
184 3 50       25 return unless $line;
185              
186 3         5 my $x;
187 3         8 for ( 0 .. @$line - 1 ) {
188 12 50       25 $x = $_ if defined $line->[ $_ ];
189             }
190              
191 3         8 return $x;
192             }
193              
194             =head2 ansiscale( $factor )
195              
196             Perform nearest neighbor scaling in text mode. Returns a new textmode
197             image.
198              
199             # scale down to 1/4 the original size
200             my $scaled = $image->ansiscale( 0.25 );
201              
202             =cut
203              
204             sub ansiscale {
205 0     0 1   my ( $self, $factor ) = @_;
206              
207 0           my $new = ( ref $self )->new;
208 0           my $width = $self->width * $factor;
209 0           my $height = $self->height * $factor;
210              
211 0 0         $width = int( $width + 1 ) if int( $width ) != $width;
212 0 0         $height = int( $height + 1 ) if int( $height ) != $height;
213              
214 0           my $oldpixels = $self->pixeldata;
215 0           my $newpixels = [];
216              
217 0           my $inv_ratio = ( 1 / $factor );
218              
219 0           for my $y ( 0 .. $height - 1 ) {
220 0           for my $x ( 0 .. $width - 1 ) {
221 0           my $px = int( $x * $inv_ratio );
222 0           my $py = int( $y * $inv_ratio );
223              
224 0           $newpixels->[ $y ]->[ $x ] = $oldpixels->[ $py ]->[ $px ];
225             }
226             }
227              
228 0           $new->width( $width );
229 0           $new->height( $height );
230 0           $new->pixeldata( $newpixels );
231 0           return $new;
232             }
233              
234             =head1 AUTHOR
235              
236             Brian Cassidy Ebricas@cpan.orgE
237              
238             =head1 COPYRIGHT AND LICENSE
239              
240             Copyright 2008-2015 by Brian Cassidy
241              
242             This library is free software; you can redistribute it and/or modify
243             it under the same terms as Perl itself.
244              
245             =cut
246              
247             1;