| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Imager::Bing::MapLayer::Image; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 5 |  |  | 5 |  | 2111 | use v5.10.1; | 
|  | 5 |  |  |  |  | 16 |  | 
|  | 5 |  |  |  |  | 239 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 5 |  |  | 5 |  | 30 | use Moose; | 
|  | 5 |  |  |  |  | 10 |  | 
|  | 5 |  |  |  |  | 40 |  | 
| 6 | 5 |  |  | 5 |  | 61414 | use MooseX::StrictConstructor; | 
|  | 5 |  |  |  |  | 14 |  | 
|  | 5 |  |  |  |  | 48 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 5 |  |  | 5 |  | 18124 | use Moose::Util::TypeConstraints; | 
|  | 5 |  |  |  |  | 14 |  | 
|  | 5 |  |  |  |  | 99 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 | 5 |  |  | 5 |  | 12744 | use Class::MOP::Method; | 
|  | 5 |  |  |  |  | 12 |  | 
|  | 5 |  |  |  |  | 168 |  | 
| 11 | 5 |  |  | 5 |  | 30 | use Const::Fast; | 
|  | 5 |  |  |  |  | 10 |  | 
|  | 5 |  |  |  |  | 53 |  | 
| 12 | 5 |  |  | 5 |  | 448 | use Imager; | 
|  | 5 |  |  |  |  | 12 |  | 
|  | 5 |  |  |  |  | 42 |  | 
| 13 | 5 |  |  | 5 |  | 245 | use Imager::Color; | 
|  | 5 |  |  |  |  | 9 |  | 
|  | 5 |  |  |  |  | 127 |  | 
| 14 | 5 |  |  | 5 |  | 3465 | use Imager::Fill; | 
|  | 5 |  |  |  |  | 4495 |  | 
|  | 5 |  |  |  |  | 134 |  | 
| 15 | 5 |  |  | 5 |  | 4924 | use Imager::Fountain; | 
|  | 5 |  |  |  |  | 15569 |  | 
|  | 5 |  |  |  |  | 198 |  | 
| 16 | 5 |  |  | 5 |  | 43 | use List::Util 1.30 qw/ min pairmap /; | 
|  | 5 |  |  |  |  | 265 |  | 
|  | 5 |  |  |  |  | 424 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 | 5 |  |  | 5 |  | 36 | use namespace::autoclean; | 
|  | 5 |  |  |  |  | 10 |  | 
|  | 5 |  |  |  |  | 93 |  | 
| 19 |  |  |  |  |  |  |  | 
| 20 | 5 |  |  | 5 |  | 538 | use version 0.77; our $VERSION = version->declare('v0.1.8'); | 
|  | 5 |  |  |  |  | 112 |  | 
|  | 5 |  |  |  |  | 362 |  | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | =head1 NAME | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | Imager::Bing::MapLayer::Image - a wrapper for L<Imager> objects | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | my $image = Imager::Bing::MapLayer::Image->new( | 
| 29 |  |  |  |  |  |  | pixel_origin => [ $left, $top ], | 
| 30 |  |  |  |  |  |  | width        => 1 + $right - $left, | 
| 31 |  |  |  |  |  |  | height       => 1 + $bottom - $top, | 
| 32 |  |  |  |  |  |  | ); | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | This module is for internal use by L<Imager::Bing::MapLayer>. | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | =begin :internal | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | This is a base class for images that acts as a wrapper around | 
| 41 |  |  |  |  |  |  | L<Imager> but automatically translates coordinates from the pixel | 
| 42 |  |  |  |  |  |  | origin. | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | This is mainly used for rendering a large polyline so that sections of | 
| 45 |  |  |  |  |  |  | it can be composed onto tiles. | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | =head1 ATTRIBUTES | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | =head2 C<pixel_origin> | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | The coordinates of the top-left point on the image. | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | =cut | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | has 'pixel_origin' => ( | 
| 56 |  |  |  |  |  |  | is  => 'ro', | 
| 57 |  |  |  |  |  |  | isa => 'ArrayRef', | 
| 58 |  |  |  |  |  |  | ); | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | =head2 C<width> | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | The width of the image. | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | =cut | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | has 'width' => ( | 
| 67 |  |  |  |  |  |  | is       => 'ro', | 
| 68 |  |  |  |  |  |  | isa      => subtype( as 'Int', where { $_ >= 1 }, ), | 
| 69 |  |  |  |  |  |  | required => 1, | 
| 70 |  |  |  |  |  |  | ); | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | =head2 C<height> | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | The height of the image. | 
| 75 |  |  |  |  |  |  |  | 
| 76 |  |  |  |  |  |  | =cut | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | has 'height' => ( | 
| 79 |  |  |  |  |  |  | is       => 'ro', | 
| 80 |  |  |  |  |  |  | isa      => subtype( as 'Int', where { $_ >= 1 }, ), | 
| 81 |  |  |  |  |  |  | required => 1, | 
| 82 |  |  |  |  |  |  | ); | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | =head2 C<left> | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | The left-most point of the C<x> axis of the image.  This corresponds to | 
| 87 |  |  |  |  |  |  | the C<x> coordinate of the C</pixel_origin>. | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | =cut | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | has 'left' => ( | 
| 92 |  |  |  |  |  |  | is      => 'ro', | 
| 93 |  |  |  |  |  |  | isa     => 'Int', | 
| 94 |  |  |  |  |  |  | default => sub { | 
| 95 |  |  |  |  |  |  | my ($self) = @_; | 
| 96 |  |  |  |  |  |  | my $origin = $self->pixel_origin; | 
| 97 |  |  |  |  |  |  | return $origin->[0]; | 
| 98 |  |  |  |  |  |  | }, | 
| 99 |  |  |  |  |  |  | lazy     => 1, | 
| 100 |  |  |  |  |  |  | init_arg => undef, | 
| 101 |  |  |  |  |  |  | ); | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | =head2 C<top> | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | The top-most point of the C<y> axis on the image.  This corresponds to | 
| 106 |  |  |  |  |  |  | the C<y> coordinate of the C</pixel_origin>. | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | =cut | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | has 'top' => ( | 
| 111 |  |  |  |  |  |  | is      => 'ro', | 
| 112 |  |  |  |  |  |  | isa     => 'Int', | 
| 113 |  |  |  |  |  |  | default => sub { | 
| 114 |  |  |  |  |  |  | my ($self) = @_; | 
| 115 |  |  |  |  |  |  | my $origin = $self->pixel_origin; | 
| 116 |  |  |  |  |  |  | return $origin->[1]; | 
| 117 |  |  |  |  |  |  | }, | 
| 118 |  |  |  |  |  |  | lazy     => 1, | 
| 119 |  |  |  |  |  |  | init_arg => undef, | 
| 120 |  |  |  |  |  |  | ); | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | =head2 C<right> | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | The rightmost point on the C<x> axis. | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | =cut | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | has 'right' => ( | 
| 129 |  |  |  |  |  |  | is      => 'ro', | 
| 130 |  |  |  |  |  |  | isa     => 'Int', | 
| 131 |  |  |  |  |  |  | default => sub { | 
| 132 |  |  |  |  |  |  | my ($self) = @_; | 
| 133 |  |  |  |  |  |  | return $self->left + $self->width - 1; | 
| 134 |  |  |  |  |  |  | }, | 
| 135 |  |  |  |  |  |  | lazy     => 1, | 
| 136 |  |  |  |  |  |  | init_arg => undef, | 
| 137 |  |  |  |  |  |  | ); | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | =head2 C<bottom> | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | The bottom-most point of the C<y> axis. | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | =cut | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | has 'bottom' => ( | 
| 146 |  |  |  |  |  |  | is      => 'ro', | 
| 147 |  |  |  |  |  |  | isa     => 'Int', | 
| 148 |  |  |  |  |  |  | default => sub { | 
| 149 |  |  |  |  |  |  | my ($self) = @_; | 
| 150 |  |  |  |  |  |  | return $self->top + $self->height - 1; | 
| 151 |  |  |  |  |  |  | }, | 
| 152 |  |  |  |  |  |  | lazy     => 1, | 
| 153 |  |  |  |  |  |  | init_arg => undef, | 
| 154 |  |  |  |  |  |  | ); | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | =head2 C<image> | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | The L<Imager> object. | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | =cut | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | has 'image' => ( | 
| 163 |  |  |  |  |  |  | is      => 'ro', | 
| 164 |  |  |  |  |  |  | isa     => 'Imager', | 
| 165 |  |  |  |  |  |  | lazy    => 1, | 
| 166 |  |  |  |  |  |  | default => sub { | 
| 167 |  |  |  |  |  |  | my ($self) = @_; | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | my $image = Imager->new( | 
| 170 |  |  |  |  |  |  | xsize    => $self->width, | 
| 171 |  |  |  |  |  |  | ysize    => $self->height, | 
| 172 |  |  |  |  |  |  | channels => 4, | 
| 173 |  |  |  |  |  |  | ); | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | # We draw a transparent white box on the image so as to fix | 
| 176 |  |  |  |  |  |  | # any issues with colour composition. | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | $image->box( | 
| 179 |  |  |  |  |  |  | color => Imager::Color->new( 255, 255, 255, 0 ), | 
| 180 |  |  |  |  |  |  | box => [ 0, 0, $self->width - 1, $self->height - 1 ], | 
| 181 |  |  |  |  |  |  | ); | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | return $image; | 
| 184 |  |  |  |  |  |  | }, | 
| 185 |  |  |  |  |  |  | init_arg => undef, | 
| 186 |  |  |  |  |  |  | handles  => [qw/ errstr getwidth getheight /], | 
| 187 |  |  |  |  |  |  | ); | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =head1 METHODS | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | =head2 C<errstr> | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | The L<Imager> error string. | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | =cut | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | sub _translate_x { | 
| 198 | 93 |  |  | 93 |  | 153 | my ( $self, $x ) = @_; | 
| 199 |  |  |  |  |  |  |  | 
| 200 | 93 |  |  |  |  | 4841 | my $left = $self->left; | 
| 201 |  |  |  |  |  |  |  | 
| 202 | 93 | 100 |  |  |  | 236 | if ( ref $x ) { | 
| 203 | 4 |  |  |  |  | 10 | return [ map { $_ - $left } @{$x} ]; | 
|  | 10 |  |  |  |  | 35 |  | 
|  | 4 |  |  |  |  | 11 |  | 
| 204 |  |  |  |  |  |  | } else { | 
| 205 | 89 |  |  |  |  | 383 | return $x - $left; | 
| 206 |  |  |  |  |  |  | } | 
| 207 |  |  |  |  |  |  | } | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | sub _translate_y { | 
| 210 | 93 |  |  | 93 |  | 149 | my ( $self, $y ) = @_; | 
| 211 |  |  |  |  |  |  |  | 
| 212 | 93 |  |  |  |  | 4342 | my $top = $self->top; | 
| 213 |  |  |  |  |  |  |  | 
| 214 | 93 | 100 |  |  |  | 247 | if ( ref $y ) { | 
| 215 | 4 |  |  |  |  | 280 | return [ map { $_ - $top } @{$y} ]; | 
|  | 10 |  |  |  |  | 37 |  | 
|  | 4 |  |  |  |  | 13 |  | 
| 216 |  |  |  |  |  |  | } else { | 
| 217 | 89 |  |  |  |  | 352 | return $y - $top; | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  | } | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | sub _translate_points { | 
| 222 | 2 |  |  | 2 |  | 6 | my ( $self, $points ) = @_; | 
| 223 |  |  |  |  |  |  | return [ | 
| 224 | 8 |  |  |  |  | 32 | map { | 
| 225 | 2 |  |  |  |  | 7 | [ $self->_translate_x( $_->[0] ), $self->_translate_y( $_->[1] ) ] | 
| 226 | 2 |  |  |  |  | 7 | } @{$points} | 
| 227 |  |  |  |  |  |  | ]; | 
| 228 |  |  |  |  |  |  | } | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | sub _translate_coords { | 
| 231 | 2 |  |  | 2 |  | 7 | my ( $self, $points ) = @_; | 
| 232 | 5 |  |  | 5 |  | 5300 | no warnings 'once'; | 
|  | 5 |  |  |  |  | 10 |  | 
|  | 5 |  |  |  |  | 8098 |  | 
| 233 | 4 |  |  | 4 |  | 16 | return [ pairmap { ( $self->_translate_x($a), $self->_translate_y($b) ) } | 
| 234 | 2 |  |  |  |  | 12 | @{$points} ]; | 
|  | 2 |  |  |  |  | 41 |  | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | const my %ARG_TO_METHOD => ( | 
| 238 |  |  |  |  |  |  | points => '_translate_points', | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | box => '_translate_coords', | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | x   => '_translate_x', | 
| 243 |  |  |  |  |  |  | 'y' => '_translate_y', | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | x1 => '_translate_x', | 
| 246 |  |  |  |  |  |  | y1 => '_translate_y', | 
| 247 |  |  |  |  |  |  | x2 => '_translate_x', | 
| 248 |  |  |  |  |  |  | y2 => '_translate_y', | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | xmin => '_translate_x', | 
| 251 |  |  |  |  |  |  | ymin => '_translate_y', | 
| 252 |  |  |  |  |  |  | xmax => '_translate_x', | 
| 253 |  |  |  |  |  |  | ymax => '_translate_y', | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | left => '_translate_x', | 
| 256 |  |  |  |  |  |  | top  => '_translate_y', | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | right  => '_translate_x', | 
| 259 |  |  |  |  |  |  | bottom => '_translate_y', | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | ); | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | sub _translate_point_arguments { | 
| 264 | 83 |  |  | 83 |  | 269 | my ( $self, %args ) = @_; | 
| 265 |  |  |  |  |  |  |  | 
| 266 | 83 |  |  |  |  | 114 | my %i_args; | 
| 267 |  |  |  |  |  |  |  | 
| 268 | 83 |  |  |  |  | 506 | foreach my $arg ( keys %ARG_TO_METHOD ) { | 
| 269 |  |  |  |  |  |  |  | 
| 270 | 1328 | 50 |  |  |  | 6087 | if ( my $method = $self->can( $ARG_TO_METHOD{$arg} ) ) { | 
| 271 |  |  |  |  |  |  |  | 
| 272 | 1328 | 100 |  |  |  | 4044 | $i_args{$arg} = $self->$method( $args{$arg} ) | 
| 273 |  |  |  |  |  |  | if ( exists $args{$arg} ); | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | } | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | } | 
| 278 |  |  |  |  |  |  |  | 
| 279 | 83 |  |  |  |  | 577 | return %i_args; | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | =head2 C<_make_imager_wrapper_method> | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | Rather than have a lot of cut-and-paste code for wrappers to L<Imager> | 
| 285 |  |  |  |  |  |  | methods, we have a L<Moose> method for creating new methods. | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | These methods translate the C<points>, C<x> and C<y> arguments for the | 
| 288 |  |  |  |  |  |  | level into coordinates on the tile, and then run the corresponding | 
| 289 |  |  |  |  |  |  | L<Imager> methods on the tile. | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | =cut | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | sub _make_imager_wrapper_method { | 
| 294 | 75 |  |  | 75 |  | 122 | my ( $class, $opts ) = @_; | 
| 295 |  |  |  |  |  |  |  | 
| 296 | 75 |  | 100 |  |  | 296 | $opts->{args} //= []; | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | $class->meta->add_method( | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | $opts->{name} => sub { | 
| 301 |  |  |  |  |  |  |  | 
| 302 | 83 |  |  | 83 |  | 36921 | my ( $self, %args ) = @_; | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
|  |  |  |  | 83 |  |  |  | 
| 303 |  |  |  |  |  |  |  | 
| 304 | 83 |  |  |  |  | 504 | my %imager_args = $self->_translate_point_arguments(%args); | 
| 305 |  |  |  |  |  |  |  | 
| 306 | 83 |  |  |  |  | 173 | foreach my $arg ( @{ $opts->{args} } ) { | 
|  | 83 |  |  |  |  | 326 |  | 
| 307 | 256 | 100 |  |  |  | 743 | $imager_args{$arg} = $args{$arg} if ( exists $args{$arg} ); | 
| 308 |  |  |  |  |  |  | } | 
| 309 |  |  |  |  |  |  |  | 
| 310 | 83 |  |  |  |  | 906 | my $method = Imager->can( $opts->{name} ); | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | return wantarray | 
| 313 | 83 | 100 |  |  |  | 6362 | ? ( $self->image->$method(%imager_args) ) | 
| 314 |  |  |  |  |  |  | : $self->image->$method(%imager_args); | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | }, | 
| 317 | 75 |  |  |  |  | 235 | ); | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | } | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | # TODO test copy, crop, paste and compose etc. | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | =head2 C<copy> | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | =cut | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( { name => 'copy', } ); | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | =head2 C<crop> | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | =cut | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 334 |  |  |  |  |  |  | {   name => 'crop', | 
| 335 |  |  |  |  |  |  | args => [qw/ width height /], | 
| 336 |  |  |  |  |  |  | } | 
| 337 |  |  |  |  |  |  | ); | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | =head2 C<paste> | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | =cut | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 344 |  |  |  |  |  |  | {   name => 'paste', | 
| 345 |  |  |  |  |  |  | args => [ | 
| 346 |  |  |  |  |  |  | qw/ width height src img combine src_minx src_miny src_maxx src_maxy / | 
| 347 |  |  |  |  |  |  | ], | 
| 348 |  |  |  |  |  |  | } | 
| 349 |  |  |  |  |  |  | ); | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | =head2 C<compose> | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | =cut | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 356 |  |  |  |  |  |  | {   name => 'compose', | 
| 357 |  |  |  |  |  |  | args => [ | 
| 358 |  |  |  |  |  |  | qw/ width height src combine opacity mask src_minx src_miny src_maxx src_maxy / | 
| 359 |  |  |  |  |  |  | ], | 
| 360 |  |  |  |  |  |  | } | 
| 361 |  |  |  |  |  |  | ); | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | =head2 C<getpixel> | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | This method used mainly for testing, and may not be usable from the | 
| 366 |  |  |  |  |  |  | L<Imager::Bing::MapLayer::Level> and | 
| 367 |  |  |  |  |  |  | L<Imager::Bing::MapLayer> objects that this tile belongs to. | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | =cut | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( { name => 'getpixel', } ); | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | =head2 C<setpixel> | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | =cut | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 378 |  |  |  |  |  |  | {   name => 'setpixel', | 
| 379 |  |  |  |  |  |  | args => [qw/ color /], | 
| 380 |  |  |  |  |  |  | } | 
| 381 |  |  |  |  |  |  | ); | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | =head2 C<line> | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | =cut | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 388 |  |  |  |  |  |  | {   name => 'line', | 
| 389 |  |  |  |  |  |  | args => [qw/ color endp aa antialias /], | 
| 390 |  |  |  |  |  |  | } | 
| 391 |  |  |  |  |  |  | ); | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | =head2 C<box> | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | =cut | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 398 |  |  |  |  |  |  | {   name => 'box', | 
| 399 |  |  |  |  |  |  | args => [qw/ color filled fill /], | 
| 400 |  |  |  |  |  |  | } | 
| 401 |  |  |  |  |  |  | ); | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | =head2 C<polyline> | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | =cut | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 408 |  |  |  |  |  |  | {   name => 'polyline', | 
| 409 |  |  |  |  |  |  | args => [qw/ color aa antialias /], | 
| 410 |  |  |  |  |  |  | } | 
| 411 |  |  |  |  |  |  | ); | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | =head2 C<polygon> | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | =cut | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 418 |  |  |  |  |  |  | {   name => 'polygon', | 
| 419 |  |  |  |  |  |  | args => [qw/ color fill /], | 
| 420 |  |  |  |  |  |  | } | 
| 421 |  |  |  |  |  |  | ); | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | =head2 C<arc> | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | =cut | 
| 426 |  |  |  |  |  |  |  | 
| 427 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 428 |  |  |  |  |  |  | {   name => 'arc', | 
| 429 |  |  |  |  |  |  | args => [qw/ r d1 d2 color fill aa filled /], | 
| 430 |  |  |  |  |  |  | } | 
| 431 |  |  |  |  |  |  | ); | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | =head2 C<circle> | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  | =cut | 
| 436 |  |  |  |  |  |  |  | 
| 437 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 438 |  |  |  |  |  |  | {   name => 'circle', | 
| 439 |  |  |  |  |  |  | args => [qw/ r color fill aa filled /], | 
| 440 |  |  |  |  |  |  | } | 
| 441 |  |  |  |  |  |  | ); | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | =head2 C<flood_fill> | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | =cut | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 448 |  |  |  |  |  |  | {   name => 'flood_fill', | 
| 449 |  |  |  |  |  |  | args => [qw/ color border fill /], | 
| 450 |  |  |  |  |  |  | } | 
| 451 |  |  |  |  |  |  | ); | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | =head2 C<string> | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | =cut | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 458 |  |  |  |  |  |  | {   name => 'string', | 
| 459 |  |  |  |  |  |  | args => [ | 
| 460 |  |  |  |  |  |  | qw/ string font aa align channel color size sizew utf8 vlayout text / | 
| 461 |  |  |  |  |  |  | ], | 
| 462 |  |  |  |  |  |  | } | 
| 463 |  |  |  |  |  |  | ); | 
| 464 |  |  |  |  |  |  |  | 
| 465 |  |  |  |  |  |  | =head2 C<align_string> | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | =cut | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | __PACKAGE__->_make_imager_wrapper_method( | 
| 470 |  |  |  |  |  |  | {   name => 'align_string', | 
| 471 |  |  |  |  |  |  | args => [ | 
| 472 |  |  |  |  |  |  | qw/ string font aa valign halign channel color size sizew utf8 vlayout text / | 
| 473 |  |  |  |  |  |  | ], | 
| 474 |  |  |  |  |  |  | } | 
| 475 |  |  |  |  |  |  | ); | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | =head2 C<radial_circle> | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | Draw a fuzzy, "radial" greyscale circle: used for plotting points in a | 
| 480 |  |  |  |  |  |  | heatmap.  When all radial circles have been plotted, the L</colourise> | 
| 481 |  |  |  |  |  |  | method should be run. | 
| 482 |  |  |  |  |  |  |  | 
| 483 |  |  |  |  |  |  | =cut | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  | sub radial_circle { | 
| 486 | 0 |  |  | 0 | 1 |  | my ( $self, %args ) = @_; | 
| 487 |  |  |  |  |  |  |  | 
| 488 | 0 |  |  |  |  |  | my $center_x = $args{x}; | 
| 489 | 0 |  |  |  |  |  | my $center_y = $args{y}; | 
| 490 | 0 |  |  |  |  |  | my $radius   = $args{r}; | 
| 491 |  |  |  |  |  |  |  | 
| 492 | 0 |  |  |  |  |  | state $palette; | 
| 493 |  |  |  |  |  |  |  | 
| 494 | 0 | 0 |  |  |  |  | unless ($palette) { | 
| 495 |  |  |  |  |  |  |  | 
| 496 | 0 |  |  |  |  |  | my $shades = 20; | 
| 497 |  |  |  |  |  |  |  | 
| 498 | 0 |  |  |  |  |  | my ( @palette, @positions ); | 
| 499 |  |  |  |  |  |  |  | 
| 500 | 0 |  |  |  |  |  | foreach my $i ( 0 .. $shades ) { | 
| 501 | 0 | 0 |  |  |  |  | my $alpha = $i ? int( sqrt( ( $i / $shades ) ) * 96 ) : 0; | 
| 502 | 0 | 0 |  |  |  |  | my $val = $i ? int( ( 1 - $i / $shades ) * 128 ) + 128 : 255; | 
| 503 | 0 |  |  |  |  |  | unshift @palette, Imager::Color->new( ($val) x 3, $alpha, ); | 
| 504 | 0 |  |  |  |  |  | push @positions, ( $i / $shades ); | 
| 505 |  |  |  |  |  |  | } | 
| 506 |  |  |  |  |  |  |  | 
| 507 | 0 |  |  |  |  |  | $palette = Imager::Fountain->simple( | 
| 508 |  |  |  |  |  |  | positions => \@positions, | 
| 509 |  |  |  |  |  |  | colors    => \@palette, | 
| 510 |  |  |  |  |  |  | ); | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | } | 
| 513 |  |  |  |  |  |  |  | 
| 514 | 0 |  |  |  |  |  | my $fill = Imager::Fill->new( | 
| 515 |  |  |  |  |  |  | fountain     => 'radial', | 
| 516 |  |  |  |  |  |  | segments     => $palette, | 
| 517 |  |  |  |  |  |  | xa           => $radius, | 
| 518 |  |  |  |  |  |  | ya           => $radius, | 
| 519 |  |  |  |  |  |  | xb           => 0, | 
| 520 |  |  |  |  |  |  | yb           => $radius, | 
| 521 |  |  |  |  |  |  | super_sample => 'circle', | 
| 522 |  |  |  |  |  |  | ); | 
| 523 |  |  |  |  |  |  |  | 
| 524 | 0 | 0 |  |  |  |  | if ( my $diam = ( $radius + $radius ) ) { | 
| 525 |  |  |  |  |  |  |  | 
| 526 | 0 |  |  |  |  |  | my $circle = Imager->new( | 
| 527 |  |  |  |  |  |  | xsize    => $diam, | 
| 528 |  |  |  |  |  |  | ysize    => $diam, | 
| 529 |  |  |  |  |  |  | channels => 4 | 
| 530 |  |  |  |  |  |  | ); | 
| 531 |  |  |  |  |  |  |  | 
| 532 | 0 |  |  |  |  |  | $circle->circle( | 
| 533 |  |  |  |  |  |  | r      => $radius, | 
| 534 |  |  |  |  |  |  | x      => $radius, | 
| 535 |  |  |  |  |  |  | 'y'    => $radius, | 
| 536 |  |  |  |  |  |  | aa     => 1, | 
| 537 |  |  |  |  |  |  | filled => 1, | 
| 538 |  |  |  |  |  |  | fill   => $fill, | 
| 539 |  |  |  |  |  |  | ); | 
| 540 |  |  |  |  |  |  |  | 
| 541 | 0 |  |  |  |  |  | $self->compose( | 
| 542 |  |  |  |  |  |  | src     => $circle, | 
| 543 |  |  |  |  |  |  | tx      => $center_x - $radius, | 
| 544 |  |  |  |  |  |  | ty      => $center_y - $radius, | 
| 545 |  |  |  |  |  |  | combine => 'normal',              # TODO change this? | 
| 546 |  |  |  |  |  |  | ); | 
| 547 |  |  |  |  |  |  | } | 
| 548 |  |  |  |  |  |  | } | 
| 549 |  |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | # TODO/FIXME - generic method with callbacks to apply a function to a tile? | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | =head2 C<colourise> | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | =head2 C<colorize> | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | $tile->colourise(); | 
| 557 |  |  |  |  |  |  |  | 
| 558 |  |  |  |  |  |  | The method colourises greyscale tiles. | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | It is intended to be run for all tiles on a map when the rendering is | 
| 561 |  |  |  |  |  |  | completed. | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  | Note that the the color of a pixel is determined by the opacity of the | 
| 564 |  |  |  |  |  |  | the pixel, and not the gray level. | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | =cut | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | sub colourise { | 
| 569 | 0 |  |  | 0 | 1 |  | my ( $self, %args ) = @_; | 
| 570 |  |  |  |  |  |  |  | 
| 571 | 0 |  |  |  |  |  | state $colorize = {}; | 
| 572 |  |  |  |  |  |  |  | 
| 573 | 0 |  |  |  |  |  | my $img = $self->image; | 
| 574 |  |  |  |  |  |  |  | 
| 575 | 0 |  |  |  |  |  | foreach my $y ( 0 .. $img->getheight - 1 ) { | 
| 576 |  |  |  |  |  |  |  | 
| 577 | 0 |  |  |  |  |  | my @colors = $img->getscanline( 'y' => $y ); | 
| 578 | 0 |  |  |  |  |  | for ( my $i = 0; $i < @colors; $i++ ) { | 
| 579 |  |  |  |  |  |  |  | 
| 580 | 0 |  |  |  |  |  | my $a = ( $colors[$i]->rgba )[-1]; | 
| 581 |  |  |  |  |  |  |  | 
| 582 | 0 |  | 0 |  |  |  | $colorize->{$a} //= Imager::Color->new( | 
| 583 |  |  |  |  |  |  | hue => int( ( ( 255 - $a ) / 255 ) * 240 ), | 
| 584 |  |  |  |  |  |  | saturation => 1.0, | 
| 585 |  |  |  |  |  |  | value      => 1.0, | 
| 586 |  |  |  |  |  |  | alpha      => min( $a, 128 ), | 
| 587 |  |  |  |  |  |  | ); | 
| 588 |  |  |  |  |  |  |  | 
| 589 | 0 |  |  |  |  |  | $colors[$i] = $colorize->{$a}; | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | } | 
| 592 |  |  |  |  |  |  |  | 
| 593 | 0 |  |  |  |  |  | $img->setscanline( 'y' => $y, pixels => \@colors ); | 
| 594 |  |  |  |  |  |  |  | 
| 595 |  |  |  |  |  |  | } | 
| 596 |  |  |  |  |  |  |  | 
| 597 | 0 |  |  |  |  |  | return 1; | 
| 598 |  |  |  |  |  |  | } | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | sub colorize { | 
| 601 | 0 |  |  | 0 | 1 |  | my ( $self, %args ) = @_; | 
| 602 | 0 |  |  |  |  |  | $self->colourise(%args); | 
| 603 |  |  |  |  |  |  | } | 
| 604 |  |  |  |  |  |  |  | 
| 605 |  |  |  |  |  |  | =end :internal | 
| 606 |  |  |  |  |  |  |  | 
| 607 |  |  |  |  |  |  | =cut | 
| 608 |  |  |  |  |  |  |  | 
| 609 | 5 |  |  | 5 |  | 36 | use namespace::autoclean; | 
|  | 5 |  |  |  |  | 13 |  | 
|  | 5 |  |  |  |  | 31 |  | 
| 610 |  |  |  |  |  |  |  | 
| 611 |  |  |  |  |  |  | 1; |