From bbd345ce0b34fc255a8c09e30f8fe83b398ee3ec Mon Sep 17 00:00:00 2001 From: Stephan Jaekel Date: Mon, 13 Mar 2017 14:14:05 +0100 Subject: [PATCH] Refactor thumbnail tag, moved logic from tag to new ThumbnailSet class. Fixes #6. --- .../templatetags/ultimatethumb_tags.py | 35 ++----------- ultimatethumb/thumbnail.py | 51 ++++++++++++++++++- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/ultimatethumb/templatetags/ultimatethumb_tags.py b/ultimatethumb/templatetags/ultimatethumb_tags.py index 7d0ae22..aae6ca8 100644 --- a/ultimatethumb/templatetags/ultimatethumb_tags.py +++ b/ultimatethumb/templatetags/ultimatethumb_tags.py @@ -2,8 +2,8 @@ from django.template import Library -from ..thumbnail import Thumbnail -from ..utils import get_size_for_path, parse_sizes, parse_source +from ..thumbnail import ThumbnailSet +from ..utils import parse_source VALID_IMAGE_FILE_EXTENSIONS = ('jpg', 'jpeg', 'png', 'gif', 'ico') @@ -34,7 +34,7 @@ def ultimatethumb( context[as_var] = None return '' - thumbnail_options = {'upscale': upscale} + thumbnail_options = {'upscale': upscale, 'factor2x': retina} if crop is not None: thumbnail_options['crop'] = crop @@ -45,31 +45,4 @@ def ultimatethumb( if pngquant is not None: thumbnail_options['pngquant'] = pngquant - source_size = get_size_for_path(source) - - # If retina option is enabled, pretend that the source is half as large as - # it is. We do this to ensure that we have "retina" images which effectively - # are doubled in size. Doing this, we never have to upscale the image. - if retina: - source_size = (int(source_size[0] / 2), int(source_size[1] / 2)) - else: - thumbnail_options['factor2x'] = False - - thumbnails = [] - - oversize = False - for size in parse_sizes(sizes): - if '%' not in size[0] and not upscale: - if int(size[0]) > source_size[0] or int(size[1]) > source_size[1]: - size = [str(source_size[0]), str(source_size[1])] - oversize = True - - options = {'size': size} - options.update(thumbnail_options) - thumbnails.append(Thumbnail(source, options)) - - if oversize: - break - - context[as_var] = thumbnails - return '' + context[as_var] = ThumbnailSet(source, sizes, thumbnail_options).thumbnails diff --git a/ultimatethumb/thumbnail.py b/ultimatethumb/thumbnail.py index b2e09cd..48382e8 100644 --- a/ultimatethumb/thumbnail.py +++ b/ultimatethumb/thumbnail.py @@ -4,10 +4,12 @@ from barbeque.commands.imaging import GmConvertCommand from barbeque.files import MoveableNamedTemporaryFile from django.conf import settings +from django.utils.functional import cached_property from .commands import PngquantCommand from .storage import thumbnail_storage -from .utils import build_url, factor_size, get_size_for_path, get_thumb_data, get_thumb_name +from .utils import ( + build_url, factor_size, get_size_for_path, get_thumb_data, get_thumb_name, parse_sizes) CROP_GRAVITY = { @@ -37,6 +39,51 @@ Size = namedtuple('Size', ('width', 'height')) +class ThumbnailSet(object): + + def __init__(self, source, sizes, options): + self.source = source + self.sizes = sizes + self.options = options + + @cached_property + def thumbnails(self): + return self.get_thumbnails() + + def get_source_size(self): + # If retina option is enabled, pretend that the source is half as large as + # it is. We do this to ensure that we have "retina" images which effectively + # are doubled in size. Doing this, we never have to upscale the image. + source_size = get_size_for_path(self.source) + if self.options.get('factor2x', True): + source_size = int(source_size[0] / 2), int(source_size[1] / 2) + + return source_size + + def get_sizes(self): + return parse_sizes(self.sizes) + + def get_thumbnails(self): + thumbnails = [] + source_size = self.get_source_size() + + oversize = False + for size in self.get_sizes(): + if '%' not in size[0] and not self.options.get('upscale', False): + if int(size[0]) > source_size[0] or int(size[1]) > source_size[1]: + size = [str(source_size[0]), str(source_size[1])] + oversize = True + + options = {'size': size} + options.update(self.options) + thumbnails.append(Thumbnail(self.source, options)) + + if oversize: + break + + return thumbnails + + class Thumbnail(object): def __init__(self, source, opts): @@ -67,7 +114,7 @@ def from_name(cls, name): def get_name(self): return get_thumb_name(self.source, **self.options) - @property + @cached_property def size(self): return self.get_estimated_size()