Graphics-DZI

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

	- 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

TODO  view on Meta::CPAN


- 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 )