Graphics-DZI
view release on metacpan or search on metacpan
- dix: doc, added hint Re: PNG
- fix: doc: default for tilesize is 256
- add: canvas can now be a stack of images
- add: dzi parameter for constructor specifies the tile positions
- add: Files version now supports 'generate' method
0.04 Thu Mar 18 20:35:25 CET 2010
- fixed the problem of script not being installed automatically
0.03 Thu Mar 18 19:20:25 CET 2010
- added sparse image support via overlays
- added sparse document images
- added Graphics::DZI: overlays option
- changed invocation line in deepzoom
- changed defaults for tilesizes to 256
0.02 Sun Mar 14 18:13:34 CET 2010
- first release
0.01 Tue Mar 9 17:44:54 CET 2010
- experimental version
- DZI: stretch parameter != 1? then do a more intelligent crop:
1) crop larger than tile necessary
2) resize this (to avoid artefacts)
3) crop again, at tile size
- add Graphics::DZI::cpio (if there were a streaming solution)
- add Graphics::DZI::tar (if there were a *working* streaming solution
- add Graphics::DZI::DAV
- tests must be improved over time
- add --overlay option to deepzoom
lib/Graphics/DZI.pm view on Meta::CPAN
Specifies how much the image is stretched in the process.
=item C<overlap> (integer, default: 4)
Specifies how much the individual tiles overlap.
=item C<tilesize> (integer, default: 128)
Specifies the quadratic size of each tile.
=item C<overlays> (list reference, default: [])
An array of L<Graphics::DZI::Overlay> objects which describe how further images are supposed to be
composed onto the canvas image.
=back
=cut
has 'image' => (isa => 'Image::Magick', is => 'rw', required => 1);
has 'scale' => (isa => 'Int', is => 'ro', default => 1);
has 'overlap' => (isa => 'Int', is => 'ro', default => 4);
has 'tilesize' => (isa => 'Int', is => 'ro', default => 256);
has 'format' => (isa => 'Str' , is => 'ro', default => 'png');
has 'overlays' => (isa => 'ArrayRef', is => 'rw', default => sub { [] });
=head2 Methods
=over
=item B<crop>
I<$tile> = I<$dzi>->crop (I<$scale>, I<$x>, I<$y>, I<$dx>, I<$dy>)
Given the dimensions of a tile and a current (not the original)
lib/Graphics/DZI.pm view on Meta::CPAN
return $tile;
}
=item B<dimensions>
(I<$W>, I<$H>) = I<$dzi>->dimensions ('total')
(I<$W>, I<$H>) = I<$dzi>->dimensions ('canvas')
This method computes how large (in pixels) the overall image will be. If C<canvas> is passed in,
then any overlays are ignored. Otherwise their size (with their squeeze factors) are used to blow up
the canvas, so that the overlays fit onto the canvas.
=cut
sub dimensions {
my $self = shift;
my $what = shift || 'total';
my ($W, $H);
if ($what eq 'total') {
use List::Util qw(max);
my $max_squeeze = max map { $_->squeeze } @{ $self->overlays };
$self->{scale} = defined $max_squeeze ? $max_squeeze : 1;
($W, $H) = map { $_ * $self->{scale} } $self->image->GetAttributes ('width', 'height');
} else {
($W, $H) = $self->image->GetAttributes ('width', 'height');
}
use POSIX;
my $level = POSIX::ceil (log ($W > $H ? $W : $H) / log (2));
$log->debug (" dimensions: $W, $H --> levels: $level");
return ($W, $H, $level);
}
lib/Graphics/DZI.pm view on Meta::CPAN
my ($x, $col) = (0, 0);
while ($x < $width) {
my ($y, $row) = (0, 0);
my $tile_dx = $x == 0 ? $border_tilesize : $overlap_tilesize;
while ($y < $height) {
my $tile_dy = $y == 0 ? $border_tilesize : $overlap_tilesize;
my @tiles = grep { defined $_ } # only where there was some intersection
map {
$_->crop ($x, $y, $tile_dx, $tile_dy); # and for each overlay crop it onto a tile
} @{ $self->overlays }; # look at all overlays
if (@tiles) { # if there is at least one overlay tile
my $tile = $self->crop ($scale, $x, $y, $tile_dx, $tile_dy); # do a crop in the canvas and try to get a tile
map {
$tile->Composite (image => $_, x => 0, 'y' => 0, compose => 'Over')
} @tiles;
$self->manifest ($tile, $level, $row, $col); # we flush it
} elsif ($level <= $CANVAS_LEVEL) { # only if we are in the same granularity of the canvas
my $tile = $self->crop ($scale, $x, $y, $tile_dx, $tile_dy); # do a crop there and try to get a tile
#warn "tile "; $tile->Display();
$self->manifest ($tile, $level, $row, $col); # we flush it
lib/Graphics/DZI.pm view on Meta::CPAN
$y += ($tile_dy - 2 * $self->{overlap}); # progress y forward
$row++; # also the row count
}
$x += ($tile_dx - 2 * $self->{overlap}); # progress x forward
$col++; # the col count
}
#-- resizing canvas
($width, $height) = map { POSIX::ceil ($_ / 2) } ($width, $height);
if (@{ $self->overlays }) { # do we have overlays from which the scale came?
$scale /= 2; # the overall magnification is to be reduced
foreach my $o (@{ $self->overlays }) { # also resize all overlays
$o->halfsize;
}
} else {
# keep scale == 1
$self->{image}->Resize (width => $width, height => $height); # resize the canvas for next iteration
}
$self->pop; # for multi-level images
}
}
lib/Graphics/DZI/Document.pm view on Meta::CPAN
=head1 NAME
Graphics::DZI::Document - DeepZoom Image Pyramid, Sparse Document Images
=head1 SYNOPSIS
# prepare a bunch of Image::Magick objects
@pages = ......;
# create the overlay itself
use Graphics::DZI::Document;
my $o = new Graphics::DZI::Document (pages => \@pages,
x => 80000, 'y' => 40000,
pack => 'linear',
squeeze => 256);
# use the Graphics::DZI::Files and add this as overlay
=head1 DESCRIPTION
This subclass of L<Graphics::DZI::Overlay> handles documents as overlays for extremely sparse
DeepZoom images. Documents here are also images, but not a single one, but one for each document
page.
What is also different from a normal overlay image is that document overlays will show a different
number of images, depending on the zoom level. First, when the canvas is the dominant feature, only
a small first page is show. Whenever that first page is fairly readable, the first 4 pages are shown
in the slot. Then the next 9 or 16, depending on whether the growth is C<linear> or C<exponential>.
=cut
use Moose;
extends 'Graphics::DZI::Overlay';
=head1 INTERFACE
lib/Graphics/DZI/Document.pm view on Meta::CPAN
# $huge->Display();
return $huge;
}
=head2 Methods
=over
=item B<halfsize>
This will be called by the overall DZI algorithm whenever this overlay is to be size-reduced by 2.
=cut
sub halfsize {
my $self = shift;
my ($w, $h) = $self->image->GetAttributes ('width', 'height'); # current dimensions
if ($self->{ sqrt } > 1) {
use feature "switch";
given ($self->{pack}) {
lib/Graphics/DZI/Overlay.pm view on Meta::CPAN
BEGIN {
$log = Log::Log4perl->get_logger ();
}
=head1 NAME
Graphics::DZI::Overlay - DeepZoom Image Pyramid, Sparse Images
=head1 SYNOPSIS
# build some overlays first
use Graphics::DZI::Overlay;
my $o1 = new Graphics::DZI::Overlay (image => ..., # what is the image?
x => 1000, y=>1000, # where on the canvas?
squeeze => 64); # how much smaller than the canvas?
my $o2 = new Graphics::DZI::Overlay (image => ..., # what is the image?
x => 2000, y=>2000, # where on the canvas?
squeeze => 32); # how much smaller than the canvas?
# then add the overlay over the canvas
use Graphics::DZI::Files;
my $dzi = new Graphics::DZI::Files (image => $image,
overlap => 4,
tilesize => 512,
format => 'png',
overlays => [ $o1, $o2],
path => $path . 'xxx_files/',
prefix => 'xxx',
);
# normal DZI generation
write_file ($path . 'xxx.xml', $dzi->descriptor);
$dzi->iterate ();
=head1 DESCRIPTION
This package can hold one overlay image, together with a coordinate and a factor how much this
images is smaller than the canvas onto which the image is to be put.
=head1 INTERFACE
=head2 Constructor
It expects the following fields:
=over
lib/Graphics/DZI/Overlay.pm view on Meta::CPAN
has 'x' => (isa => 'Int', is => 'rw');
has 'y' => (isa => 'Int', is => 'rw');
has 'squeeze' => (isa => 'Num', is => 'rw');
=head2 Methods
=over
=item B<halfsize>
Makes the overlay smaller by 2. This will be called by the DZI algorithm.
=cut
sub halfsize {
my $self = shift;
my ($w, $h) = $self->image->GetAttributes ('width', 'height'); # current dimensions
$self->image->Resize (width => int($w/2), height => int($h/2)); # half size
$self->{x} /= 2; # dont forget x, y
$self->{y} /= 2;
}
=item B<crop>
Gets a tile off the overlay.
=cut
sub crop {
my $self = shift;
my ($tx, $ty, $tdx, $tdy) = @_;
my ($w, $h) = $self->{image}->GetAttributes ('width', 'height');
$self->{dx} = $w;
$self->{dy} = $h;
# warn "before intersection tile $tile"; $tile->Display() if $tile;
if (my $r = _intersection ($tx, $ty, $tx+$tdx, $ty+$tdy, # tile and overlay intersect?
$self->{x}, $self->{y}, $self->{x} + $self->{dx}, $self->{y} +$self->{dy})) {
# warn " intersection!";
my ($ox, $oy, $dx, $dy) = (
$r->[0] - $self->{x}, # x relative to overlay
$r->[1] - $self->{y}, # y relative to overlay
$r->[2] - $r->[0], # width of the intersection
$r->[3] - $r->[1], # height
);
my $oc = $self->{image}->clone;
# warn "overlay clone "; $oc->Display();
$oc->Crop (geometry => "${dx}x${dy}+${ox}+${oy}");
# warn "cropped oc"; $oc->Display();
# unless ($tile) { # this just makes sure that we are composing onto SOMETHING
## warn "XXXXXXXXX generating substitute tile";
my $tile = Image::Magick->new ("${tdx}x${tdy}"); # create an empty one
$tile->Read ('xc:yellow'); # paint it white (otherwise composite would not work?)
$tile->Transparent (color => 'yellow');
# warn "substitute tile "; $tile->Display();
# }
# warn "before overlay tile "; $tile->Display();
$tile->Composite (image => $oc,
x => $r->[0] - $tx, # intersection left/top relative to tile
'y' => $r->[1] - $ty,
compose => 'Over',
);
# warn "after overlay tile "; $tile->Display();
return $tile;
}
return undef;
}
sub _intersection {
my ($ax, $ay, $axx, $ayy,
$bx, $by, $bxx, $byy) = @_;
if (_intersects ($ax, $ay, $axx, $ayy,
( run in 0.900 second using v1.01-cache-2.11-cpan-49f99fa48dc )