diff options
Diffstat (limited to 'lib/gcstar/GCItemsLists/GCImageListComponents.pm')
-rw-r--r-- | lib/gcstar/GCItemsLists/GCImageListComponents.pm | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/lib/gcstar/GCItemsLists/GCImageListComponents.pm b/lib/gcstar/GCItemsLists/GCImageListComponents.pm new file mode 100644 index 0000000..aa2bf2b --- /dev/null +++ b/lib/gcstar/GCItemsLists/GCImageListComponents.pm @@ -0,0 +1,848 @@ +package GCImageListComponents; + +################################################### +# +# Copyright 2005-2011 Christian Jodar +# +# This file is part of GCstar. +# +# GCstar is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# GCstar is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCstar; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +# +################################################### + +use strict; + +{ + package GCImageListItem; + + use GCUtils; + use GCStyle; + use base "Gtk2::EventBox"; + use File::Temp qw/ tempfile /; + + @GCImageListItem::ISA = ('Gtk2::EventBox'); + + sub new + { + my ($proto, $container, $info) = @_; + my $class = ref($proto) || $proto; + my $self = $class->SUPER::new; + bless ($self, $class); + + # Some information that we'll need later + $self->{info} = $info; + $self->{container} = $container; + $self->{style} = $container->{style}; + $self->{tooltips} = $container->{tooltips}; + $self->{file} = $container->{parent}->{options}->file; + $self->{collectionDir} = $container->{collectionDir}; + $self->{model} = $container->{parent}->{model}; + $self->{imageCache} = $container->{imageCache}; + $self->{dataManager} = $container->{parent}->{items}; + + $self->can_focus(1); + my $image = new Gtk2::Image; + $self->add($image); + $self->refreshInfo($info); + $self->set_size_request($container->{style}->{vboxWidth}, $container->{style}->{vboxHeight}); + $self->show_all; + + return $self; + } + + sub setInfo + { + my ($self, $info) = @_; + + $self->{info} = $info; + } + + sub refreshInfo + { + my ($self, $info, $cacheRefresh) = @_; + + $self->setInfo($info); + + $self->refreshPopup; + + delete $self->{zoomedPixbufCache}; + + { + my $pixbuf = $self->createPixbuf($info, $cacheRefresh); + if (! $self->{style}->{withImage}) + { + $self->modify_bg('normal', $self->{style}->{inactiveBg}); + } + $self->{previousPixbuf} = $pixbuf->copy; + $self->child->set_from_pixbuf($pixbuf); + } + if ($self->{selected}) + { + $self->{selected} = 0; + $self->highlight; + $self->{selected} = 1; + } + } + + sub refreshPopup + { + my $self = shift; + # Old versions of Gtk2 don't support set_tooltip_markup + eval { + $self->set_tooltip_markup($self->{dataManager}->getSummary($self->{info}->{idx})); + }; + if ($@) + { + print "$@\n"; + # So we do it the old way for them + $self->{tooltips}->set_tip($self, $self->{info}->{title}, ''); + } + } + + sub savePicture + { + my $self = shift; + $self->{previousPixbuf} = $self->child->get_pixbuf->copy + if $self->child; + } + + sub restorePicture + { + my $self = shift; + $self->child->set_from_pixbuf($self->{previousPixbuf}) + if $self->{previousPixbuf} && $self->child; + } + + sub startZoomAnimation + { + my $self = shift; + $self->{currentZoom} = 1.01; + my $pixbuf = $self->createPixbuf($self->{info}, 0, 1.01); + $self->child->set_from_pixbuf($pixbuf); + $self->{zoomTimeout} = Glib::Timeout->add(20 , sub { + my $widget = shift; + $widget->{currentZoom} += 0.02; + if ($widget->{currentZoom} > 1.06) + { + $widget->{zoomTimeout} = undef; + return 0; + } + my $pixbuf = $widget->createPixbuf($self->{info}, 0, $widget->{currentZoom}); + $widget->child->set_from_pixbuf($pixbuf) + if $widget->child; + return 1; + }, $self); + } + + sub stopZoomAnimation + { + my $self = shift; + Glib::Source->remove($self->{zoomTimeout}) + if $self->{zoomTimeout}; + } + + # This method sets all the event callbacks + sub prepareHandlers + { + my ($self, $idx, $info) = @_; + $self->{idx} = $idx; + $self->{info} = $info; + + $self->signal_handler_disconnect($self->{mouseHandler}) + if $self->{mouseHandler}; + $self->{mouseHandler} = $self->signal_connect('button_press_event' => sub { + my ($widget, $event) = @_; + + if (($event->type ne '2button-press') && !(($event->button eq 3) && ($widget->{selected}))) + { + my $state = $event->get_state; + my $keepPrevious = 0; + if ($state =~ /control-mask/) + { + $widget->{container}->select($widget->{idx}, 0, 1); + } + elsif ($state =~ /shift-mask/) + { + $widget->{container}->restorePrevious; + $widget->{container}->selectMany($widget->{idx}); + } + else + { + $widget->{container}->select($widget->{idx}); + } + $widget->{container}->setPreviousSelectedDisplayed($widget->{idx}); + + #$self->{parent}->display($widget->{idx}) unless $event->type eq '2button-press'; + $widget->{container}->displayDetails(0, keys %{$widget->{container}->{selectedIndexes}}); + } + + $widget->{container}->displayDetails(1, $widget->{idx}) if $event->type eq '2button-press'; + $widget->{container}->showPopupMenu($event->button, $event->time) if ($event->button eq 3); + $widget->grab_focus; + }); + + if ($self->{style}->{withAnimation}) + { + $self->signal_handler_disconnect($self->{enterHandler}) + if $self->{enterHandler}; + $self->{enterHandler} = $self->signal_connect('enter_notify_event' => sub { + my ($widget, $event) = @_; + if (!$widget->{selected}) + { + $widget->startZoomAnimation; + } + }); + + $self->signal_handler_disconnect($self->{leaveHandler}) + if $self->{leaveHandler}; + $self->{leaveHandler} = $self->signal_connect('leave_notify_event' => sub { + my ($widget, $event) = @_; + if (!$widget->{selected}) + { + $widget->stopZoomAnimation; + $widget->restorePicture; + } + }); + } + + + $self->signal_handler_disconnect($self->{keyHandler}) + if $self->{keyHandler}; + + $self->{keyHandler} = $self->signal_connect('key-press-event' => sub { + my ($widget, $event) = @_; + my $displayed = $self->{container}->convertIdxToDisplayed($widget->{idx}); + my $key = Gtk2::Gdk->keyval_name($event->keyval); + if ($key eq 'Delete') + { + $widget->{container}->{parent}->deleteCurrentItem; + return 1; + } + if (($key eq 'Return') || ($key eq 'space')) + { + $widget->{container}->displayDetails(1, $widget->{idx}); + return 1; + } + my $unicode = Gtk2::Gdk->keyval_to_unicode($event->keyval); + if ($unicode) + { + $self->{container}->showSearch(pack('U',$unicode)); + } + else + { + my $columns = $widget->{container}->getColumnsNumber; + + ($key eq 'Right') ? $displayed++ : + ($key eq 'Left') ? $displayed-- : + ($key eq 'Down') ? $displayed += $columns : + ($key eq 'Up') ? $displayed -= $columns : + ($key eq 'Page_Down') ? $displayed += ($widget->{style}->{pageCount} * $columns): + ($key eq 'Page_Up') ? $displayed -= ($widget->{style}->{pageCount} * $columns): + ($key eq 'Home') ? $displayed = 0 : + ($key eq 'End') ? $displayed = $widget->{container}->getNbItems - 1 : + return 1; + + return 1 if ($displayed < 0) || ($displayed >= $widget->{container}->getNbItems); + my $column = $displayed % $columns; + my $valueIdx = $widget->{container}->convertDisplayedToIdx($displayed); +# my $keepPrevious = 0; + my $state = $event->get_state; + if ($state =~ /control-mask/) + { + $widget->{container}->select($valueIdx, 0, 1); + $widget->{container}->unsetPreviousSelectedDisplayed; + } + elsif ($state =~ /shift-mask/) + { + $widget->{container}->setPreviousSelectedDisplayed($widget->{idx}); + $widget->{container}->restorePrevious; + $widget->{container}->selectMany($valueIdx); + } + else + { + $widget->{container}->select($valueIdx); + $widget->{container}->unsetPreviousSelectedDisplayed; + } + $widget->{container}->displayDetails(0, $valueIdx); + $widget->{container}->grab_focus; + $widget->{container}->showCurrent unless (($key eq 'Left') && ($column != ($columns - 1))) + || (($key eq 'Right') && ($column != 0)); + } + return 1; + + }); + + } + + sub highlight + { + my ($self, $keepPrevious) = @_; + return if $self->{selected}; + $self->{selected} = 1; + if (! $self->{style}->{withImage}) + { + $self->modify_bg('normal', $self->{style}->{activeBg}); + } +# $self->savePicture +# unless $keepPrevious; + + my $pixbuf = $self->createPixbuf($self->{info}, 0, 1.1); + + $pixbuf->saturate_and_pixelate($pixbuf, 1.5, 0); + $pixbuf = $pixbuf->composite_color_simple ($pixbuf->get_width, $pixbuf->get_height, 'nearest',220, 128, $self->{style}->{activeBgValue}, $self->{style}->{activeBgValue}); + $self->child->set_from_pixbuf($pixbuf); + } + + sub unhighlight + { + my ($self) = @_; + + $self->modify_bg('normal', $self->{style}->{inactiveBg}) + if (! $self->{style}->{withImage}); + $self->restorePicture; + $self->{selected} = 0; + } + + sub createPixbuf + { + my ($self, $info, $cacheRefresh, $zoom) = @_; + + my $displayedImage = $info->{picture}; + my $pixbuf = undef; + + my $borrower = $info->{borrower}; + my $favourite = $info->{favourite}; + + # Item has a picture assigned + if ($cacheRefresh) + { + $self->{imageCache}->forceCacheUpdateForNextUse; + } + + if ($zoom) + { + if (! exists $self->{zoomedPixbufCache}->{$zoom}) + { + $self->{zoomedPixbufCache}->{$zoom} = $self->{imageCache}->getPixbuf($info, $zoom); + } + $pixbuf = $self->{zoomedPixbufCache}->{$zoom}; + } + else + { + $pixbuf = $self->{imageCache}->getPixbuf($info, $zoom); + } + + my $width; + my $height; + my $boxWidth = $self->{style}->{imgWidth}; + my $boxHeight = $self->{style}->{imgHeight}; + + my $overlay; + my $imgWidth; + my $imgHeight; + my $targetOverlayHeight; + my $targetOverlayWidth; + my $pixbufTempHeight; + my $pixbufTempWidth; + my $alpha = 1; + if ($self->{style}->{useOverlays}) + { + # Need to call this to get the overlay padding + ($imgWidth, $imgHeight, $overlay) = $self->{imageCache}->getDestinationImgSize($pixbuf->get_width, + $pixbuf->get_height); + } + $width = $pixbuf->get_width; + $height = $pixbuf->get_height; + + # Do the composition + + if ($self->{style}->{useOverlays}) + { + if ($self->{style}->{withImage}) + { + # Using background, so center accordingly + my $offsetX = (($self->{style}->{offsetX} / 2) * $self->{style}->{factor}) + (($boxWidth - ($width + $overlay->{paddingLeft} + $overlay->{paddingRight})) / 2); + my $offsetY = 15 * $self->{style}->{factor} + ($boxHeight - ($height + $overlay->{paddingTop} + $overlay->{paddingBottom})); + + # Make an empty pixbuf to work within + my $tempPixbuf =Gtk2::Gdk::Pixbuf->new('rgb', 1, 8, + $self->{style}->{backgroundPixbuf}->get_width, + $self->{style}->{backgroundPixbuf}->get_height); + $tempPixbuf->fill(0x00000000); + + # Place cover in pixbuf + $pixbuf->composite($tempPixbuf, + $offsetX + $overlay->{paddingLeft}, $offsetY + $overlay->{paddingTop}, + $width , $height, + $offsetX + $overlay->{paddingLeft}, $offsetY + $overlay->{paddingTop}, + 1, 1, + 'nearest', 255); + $pixbuf = $tempPixbuf; + + # Composite overlay picture + $self->{style}->{overlayPixbuf}->composite($pixbuf, + $offsetX, $offsetY, + $width + $overlay->{paddingLeft} + $overlay->{paddingRight}, + $height + $overlay->{paddingTop} + $overlay->{paddingBottom}, + $offsetX, $offsetY, + ($width + $overlay->{paddingLeft} + $overlay->{paddingRight}) / $self->{style}->{overlayPixbuf}->get_width, + ($height + $overlay->{paddingTop} + $overlay->{paddingBottom}) / $self->{style}->{overlayPixbuf}->get_height, + 'nearest', 255); + + # Overlay borrower image if required + if ($borrower && ($borrower ne 'none')) + { + # De-saturate borrowed items + $pixbuf->saturate_and_pixelate($pixbuf, .1, 0); + $self->{style}->{lendPixbuf}->composite($pixbuf, + $pixbuf->get_width - $self->{style}->{lendPixbuf}->get_width - $offsetX, + $offsetY + $height + $overlay->{paddingTop} + $overlay->{paddingBottom} - $self->{style}->{lendPixbuf}->get_height, + $self->{style}->{lendPixbuf}->get_width, $self->{style}->{lendPixbuf}->get_height, + $pixbuf->get_width - $self->{style}->{lendPixbuf}->get_width - $offsetX, + $offsetY + $height + $overlay->{paddingTop} + $overlay->{paddingBottom} - $self->{style}->{lendPixbuf}->get_height, + 1, 1, + 'nearest', 255); + } + + # Overlay favourite image if required + if ($favourite) + { + $self->{style}->{favPixbuf}->composite($pixbuf, + $pixbuf->get_width - $self->{style}->{favPixbuf}->get_width - $offsetX, + $offsetY, + $self->{style}->{favPixbuf}->get_width, $self->{style}->{favPixbuf}->get_height, + $pixbuf->get_width - $self->{style}->{favPixbuf}->get_width - $offsetX, + $offsetY, + 1, 1, + 'nearest', 255); + } + + # Create and apply reflection if required + if ($self->{style}->{withReflect}) + { + my $reflect; + $reflect = $pixbuf->flip(0); + $reflect->composite( + $pixbuf, + 0, 2 * ($offsetY + $height + $overlay->{paddingTop} + $overlay->{paddingBottom}) - $pixbuf->get_height, + $pixbuf->get_width, + 2 * ($pixbuf->get_height - $height - $offsetY - $overlay->{paddingTop} - $overlay->{paddingBottom}) - (10 * $self->{style}->{factor}), + 0, 2 * ($offsetY + $height + $overlay->{paddingTop} + $overlay->{paddingBottom}) - $pixbuf->get_height, + 1, 1, + 'nearest', 100 + ); + + # Apply foreground fading + $self->{style}->{foregroundPixbuf}->composite( + $pixbuf, + 0, 0, + $pixbuf->get_width, $pixbuf->get_height, + 0, 0, + 1, 1, + 'nearest', 255 + ); + } + + # Heft created pixbuf onto background + my $bgPixbuf = $self->{style}->{backgroundPixbuf}->copy; + $pixbuf->composite($bgPixbuf, + 0,0, + $pixbuf->get_width , $pixbuf->get_height, + 0,0, + 1, 1, + 'nearest', 255); + $pixbuf = $bgPixbuf; + + } + else + { + # Not using background, so we need to make an empty pixbuf which is right size for overlay first + my $tempPixbuf =Gtk2::Gdk::Pixbuf->new('rgb', 1, 8, + $width + $overlay->{paddingLeft} + $overlay->{paddingRight}, + $height + $overlay->{paddingTop} + $overlay->{paddingBottom}); + $tempPixbuf->fill(0x00000000); + + # Now, place list image inside empty pixbuf + $pixbuf->composite($tempPixbuf, + $overlay->{paddingLeft}, $overlay->{paddingTop}, + $width , $height, + $overlay->{paddingLeft}, $overlay->{paddingTop}, + 1, 1, + 'nearest', 255 * $alpha); + $pixbuf = $tempPixbuf; + + # Place overlay on top of pixbuf + $self->{style}->{overlayPixbuf}->composite($pixbuf, + 0, 0, + $width + $overlay->{paddingLeft} + $overlay->{paddingRight}, + $height + $overlay->{paddingTop} + $overlay->{paddingBottom}, + 0, 0, + ($width + $overlay->{paddingLeft} + $overlay->{paddingRight}) / $self->{style}->{overlayPixbuf}->get_width, + ($height + $overlay->{paddingTop} + $overlay->{paddingBottom}) / $self->{style}->{overlayPixbuf}->get_height, + 'nearest', 255 * $alpha); + + # Overlay borrower image if required + if ($borrower && ($borrower ne 'none')) + { + # De-saturate borrowed items + $pixbuf->saturate_and_pixelate($pixbuf, .1, 0); + + $self->{style}->{lendPixbuf}->composite($pixbuf, + $pixbuf->get_width - $self->{style}->{lendPixbuf}->get_width, + $pixbuf->get_height - $self->{style}->{lendPixbuf}->get_height, + $self->{style}->{lendPixbuf}->get_width, $self->{style}->{lendPixbuf}->get_height, + $pixbuf->get_width - $self->{style}->{lendPixbuf}->get_width, + $pixbuf->get_height - $self->{style}->{lendPixbuf}->get_height, + 1, 1, + 'nearest', 255); + } + + # Overlay favourite image if required + if ($favourite) + { + $self->{style}->{favPixbuf}->composite($pixbuf, + $pixbuf->get_width - $self->{style}->{favPixbuf}->get_width, + 0, + $self->{style}->{favPixbuf}->get_width, $self->{style}->{favPixbuf}->get_height, + $pixbuf->get_width - $self->{style}->{favPixbuf}->get_width, + 0, + 1, 1, + 'nearest', 255); + } + + } + } + else + { + # No overlays, nice and simple + + # Overlay borrower image if required + if ($borrower && ($borrower ne 'none')) + { + # De-saturate borrowed items + $pixbuf->saturate_and_pixelate($pixbuf, .1, 0); + $self->{style}->{lendPixbuf}->composite($pixbuf, + $width - $self->{style}->{lendPixbuf}->get_width - $self->{style}->{factor}, + $height - $self->{style}->{lendPixbuf}->get_height - $self->{style}->{factor}, + $self->{style}->{lendPixbuf}->get_width, $self->{style}->{lendPixbuf}->get_height, + $width - $self->{style}->{lendPixbuf}->get_width - $self->{style}->{factor}, + $height - $self->{style}->{lendPixbuf}->get_height - $self->{style}->{factor}, + 1, 1, + 'nearest', 255); + } + + # Overlay favourite image if required + if ($favourite) + { + $self->{style}->{favPixbuf}->composite($pixbuf, + $width - $self->{style}->{favPixbuf}->get_width - $self->{style}->{factor}, + $self->{style}->{factor}, + $self->{style}->{favPixbuf}->get_width, $self->{style}->{favPixbuf}->get_height, + $width - $self->{style}->{favPixbuf}->get_width - $self->{style}->{factor}, + $self->{style}->{factor}, + 1, 1, + 'nearest', 255); + } + + my $reflect; + $reflect = $pixbuf->flip(0) + if $self->{style}->{withReflect}; + + my $offsetX = (($self->{style}->{offsetX} / 2) * $self->{style}->{factor}) + (($boxWidth - $width) / 2); + my $offsetY = 15 * $self->{style}->{factor} + ($boxHeight - $height); + if ($self->{style}->{withImage}) + { + my $bgPixbuf = $self->{style}->{backgroundPixbuf}->copy; + $pixbuf->composite($bgPixbuf, + $offsetX, $offsetY, + $width, $height, + $offsetX, $offsetY, + 1, 1, + 'nearest', 255); + $pixbuf = $bgPixbuf; + } + + if ($self->{style}->{withReflect}) + { + $reflect->composite( + $pixbuf, + $offsetX, $height + $offsetY, + $width, $pixbuf->get_height - $height - $offsetY - (10 * $self->{style}->{factor}), + $offsetX, $height + $offsetY, + 1, 1, + 'nearest', 100 + ); + + # Apply foreground fading + $self->{style}->{foregroundPixbuf}->composite( + $pixbuf, + 0, 0, + $pixbuf->get_width, $pixbuf->get_height, + 0, 0, + 1, 1, + 'nearest', 255 + ); + } + } + return $pixbuf; + } + + + +} + +{ + package GCImageCache; + + use File::Path; + use File::Copy; + use List::Util qw/min/; + + sub new + { + my ($proto, $imagesDir, $imageSize, $style, $defaultImage) = @_; + my $class = ref($proto) || $proto; + my $self = { + imagesDir => $imagesDir, + imageSize => $imageSize, + style => $style, + cacheDir => $imagesDir.'/.cache/', + oldCacheDir => $imagesDir, + defaultImage => $defaultImage, + forceUpdate => 0, + }; + # Make sure destination directory exists + if ( ! -d $self->{cacheDir}) + { + mkpath $self->{cacheDir}; + } + bless ($self, $class); + + $self->clearOldCache; + + return $self; + } + + # This method removes images cached by previous versions + sub clearOldCache + { + my $self = shift; + my $trashDir = $self->{imagesDir}.'.trash'; + mkpath $trashDir; + foreach (glob $self->{oldCacheDir}.'/*') + { + if (/\.cache\.[0-4](\.|$)/) + { + move $_, $trashDir; + } + } + } + + sub forceCacheUpdateForNextUse + { + my ($self) = @_; + $self->{forceUpdate} = 1; + } + + sub getPixbuf + { + my ($self, $info, $zoom) = @_; + my $fileName; + my $pixbuf = undef; + if (!$zoom) + { + $fileName = $self->getCachedFileName($info); + if ($self->{forceUpdate} || (! -e $fileName)) + { + $self->createImageCache($info); + } + $self->{forceUpdate} = 0; + eval { + $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($fileName); + }; + } + else + { + # When a zoom is requested, we have to generate the picture + $fileName = $self->getCachedFileName($info); + # Get picture size from cached file to avoid re-computing everything + my ($picFormat, $picWidth, $picHeight) = Gtk2::Gdk::Pixbuf->get_file_info($fileName); + # Then open the original file + my $origFileName = $info->{picture}; + if (! -f $origFileName) + { + $origFileName = $self->{defaultImage}; + } + if (!$self->{style}->{useOverlays}) + { + $zoom -= 0.01; + } + + eval { + $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($origFileName); + my $newWidth = int($picWidth * $zoom); + my $newHeight = int($picHeight * $zoom); + $pixbuf = GCUtils::scaleMaxPixbuf($pixbuf, $newWidth, $newHeight, 1, 0); + }; + } + + return $pixbuf; + } + + sub getCachedFileName + { + my ($self, $info, $size) = @_; + + my $gcsautoid = $info->{autoid}; + my $title = $info->{title}; + + $title =~ s/[^a-zA-Z0-9]*//g; + my $cacheFilename = $self->{cacheDir}; + if ($info->{picture}) + { + $cacheFilename .= $gcsautoid + ."." + .$title; + } + else + { + $cacheFilename .= 'GCSDefaultImage'; + } + $cacheFilename .= (defined $size ? $size : $self->{imageSize}); + $cacheFilename .= ".overlay" + if $self->{style}->{useOverlays}; + + return $cacheFilename; + } + + # Resizes artwork to required sizes and saves copies of the images, for fast loading + sub createImageCache + { + my ($self, $info) = @_; + + my $srcImage = $info->{picture}; + if (! -f $srcImage) + { + $srcImage = $self->{defaultImage}; + $info->{picture} = ""; + } + + # Load in the original source image + my $origPixbuf = Gtk2::Gdk::Pixbuf->new_from_file($srcImage); + + my $gcsautoid = $info->{autoid}; + my $title = $info->{title}; + $title =~ s/[^a-zA-Z0-9]*//g; + # Get original picture format + my ($picFormat, $picWidth, $picHeight) = Gtk2::Gdk::Pixbuf->get_file_info($srcImage); + + # Loop through possible sizes + for (my $size = 0; $size < 5; $size++) { + my $imgWidth; + my $imgHeight; + my $overlay; + + my $cacheFilename = $self->getCachedFileName($info, $size); + + # Get size for cached image + ($imgWidth, $imgHeight, $overlay) = $self->getDestinationImgSize($picWidth, + $picHeight, + $size); + + # Scale pixbuf and save + my $scaledPixbuf = GCUtils::scaleMaxPixbuf($origPixbuf, $imgWidth, $imgHeight, 0, 0); + if ($picFormat->{name} eq 'jpeg') + { + $scaledPixbuf->save ($cacheFilename, 'jpeg', quality => '99'); + } + else + { + $scaledPixbuf->save ($cacheFilename, 'png'); + } + } + } + + # Calculates height and width of list image + sub getDestinationImgSize + { + my ($self, $origWidth, $origHeight, $size) = @_; + + $size = $self->{imageSize} + if (!defined $size); + + my $imgWidth; + my $imgHeight; + my $overlay; + + # No overlays + $imgWidth = $self->{style}->{imgWidth} / $self->{style}->{factor}; + $imgHeight = $self->{style}->{imgHeight} / $self->{style}->{factor}; + + if ($self->{style}->{useOverlays}) + { + # Overlays + + # Calculate size of list image with proportional size of overlay padding added + my $pixbufTempHeight = (($self->{style}->{overlayPaddingTop} + $self->{style}->{overlayPaddingBottom})/$self->{style}->{overlayPixbuf}->get_height + 1) * $origHeight; + my $pixbufTempWidth = (($self->{style}->{overlayPaddingLeft} + $self->{style}->{overlayPaddingRight})/$self->{style}->{overlayPixbuf}->get_width + 1) * $origWidth; + + # Find out target size of overlay, keeping the same ratio as the size calculated above (ie, list image + relative padding) + my $ratio = $pixbufTempHeight / $pixbufTempWidth; + my $targetOverlayHeight; + my $targetOverlayWidth; + if (($pixbufTempWidth > $imgWidth) || ($pixbufTempHeight > $imgHeight)) + { + if (($pixbufTempWidth * $imgHeight/$pixbufTempHeight) < $imgHeight ) + { + $targetOverlayHeight = $imgHeight; + $targetOverlayWidth = int($imgHeight / $ratio); + } + else + { + $targetOverlayHeight = int( $imgWidth * $ratio); + $targetOverlayWidth = $imgWidth; + } + } + else + { + # Special case when image is small enough and doesn't need to be resized + $targetOverlayHeight = $pixbufTempHeight; + $targetOverlayWidth = $pixbufTempWidth; + } + + # Calculate final offset amounts for target size of overlay + $overlay->{paddingLeft} = int($self->{style}->{overlayPaddingLeft} * $targetOverlayWidth / $self->{style}->{overlayPixbuf}->get_width); + $overlay->{paddingRight} = int($self->{style}->{overlayPaddingRight} * $targetOverlayWidth / $self->{style}->{overlayPixbuf}->get_width); + $overlay->{paddingTop} = int($self->{style}->{overlayPaddingTop} * $targetOverlayHeight / $self->{style}->{overlayPixbuf}->get_height); + $overlay->{paddingBottom} = int($self->{style}->{overlayPaddingBottom} * $targetOverlayHeight / $self->{style}->{overlayPixbuf}->get_height); + + $imgWidth = $imgWidth - $overlay->{paddingLeft} - $overlay->{paddingRight}; + $imgHeight = $imgHeight - $overlay->{paddingTop} - $overlay->{paddingBottom}; + } + + my $factor = ($size == 0) ? 0.5 + : ($size == 1) ? 0.8 + : ($size == 3) ? 1.5 + : ($size == 4) ? 2 + : 1; + $imgWidth *= $factor; + $imgHeight *= $factor; + + return ($imgWidth, $imgHeight, $overlay); + } + +} + +1; |