def thumbnail(self, filepath, mt=False): ''' Returns a thumbnail pixbuf for <filepath>, transparently handling both normal image files and archives. If a thumbnail file already exists, it is re-used. Otherwise, a new thumbnail is created from <filepath>. Returns None if thumbnail creation failed, or if the thumbnail creation is run asynchrounosly. ''' # Update width and height from preferences if they haven't been set explicitly if self.default_sizes: self.width = prefs['thumbnail size'] self.height = prefs['thumbnail size'] if self._thumbnail_exists(filepath): thumbpath = self._path_to_thumbpath(filepath) pixbuf = image_tools.load_pixbuf(thumbpath) self.thumbnail_finished(filepath, pixbuf) return pixbuf else: if mt: thread = threading.Thread(target=self._create_thumbnail, args=(filepath, )) thread.name += '-thumbnailer' thread.daemon = True thread.start() return None else: return self._create_thumbnail(filepath)
def test_pixbuf_to_pil(self): for image in ( 'transparent.png', 'transparent-indexed.png', 'pattern-opaque-rgb.png', 'pattern-opaque-rgba.png', 'pattern-transparent-rgba.png', ): pixbuf = image_tools.load_pixbuf(get_image_path(image)) im = image_tools.pixbuf_to_pil(pixbuf) msg = ('pixbuf_to_pil("%s") failed; ' 'result %%(diff_type)s differs: %%(diff)s' % (image, )) self.assertImagesEqual(im, pixbuf, msg=msg)
def test_load_pixbuf_basic(self): for image in _TEST_IMAGES: image_path = get_image_path(image.name) if self.use_pil: # When using PIL, indexed formats will be # converted to RGBA by pixbuf_to_pil. expected_mode = pil_mode_to_gdk_mode(image.mode) else: expected_mode = 'RGBA' if image.has_alpha else 'RGB' im = Image.open(image_path).convert(expected_mode) pixbuf = image_tools.load_pixbuf(image_path) msg = ('load_pixbuf("%s") failed; ' 'result %%(diff_type)s differs: %%(diff)s' % (image.name, )) self.assertImagesEqual(pixbuf, im, msg=msg)
def test_fit_in_rectangle_opaque_no_resize(self): # Check opaque image is unchanged when not resizing. for image in ( 'pattern-opaque-rgb.png', 'pattern-opaque-rgba.png', ): input = image_tools.load_pixbuf(get_image_path(image)) width, height = input.get_width(), input.get_height() for scaling_quality in range(4): prefs['scaling quality'] = scaling_quality result = image_tools.fit_in_rectangle( input, width, height, scaling_quality=scaling_quality) msg = ('fit_in_rectangle("%s", scaling quality=%d) failed; ' 'result %%(diff_type)s differs: %%(diff)s' % (image, scaling_quality)) self.assertImagesEqual(result, input, msg=msg)
def test_load_pixbuf_modes(self): tmp_file = tempfile.NamedTemporaryFile(prefix=u'image.', suffix=u'.png', delete=False) tmp_file.close() base_im = Image.open(get_image_path('transparent.png')) for supported, expected_pixbuf_mode, mode in _IMAGE_MODES: if not supported: continue input_im = base_im.convert(mode) input_im.save(tmp_file.name) pixbuf = image_tools.load_pixbuf(tmp_file.name) expected_im = input_im.convert(expected_pixbuf_mode) msg = ('load_pixbuf("%s") failed; ' 'result %%(diff_type)s differs: %%(diff)s' % (mode, )) self.assertImagesEqual(pixbuf, expected_im, msg=msg)
def _cache_pixbuf(self, index, force=False): self._wait_on_page(index + 1) with self._cache_lock[index]: if index in self._raw_pixbufs: return with self._lock: if not force and index not in self._wanted_pixbufs: return log.debug('Caching page %u', index + 1) try: pixbuf = image_tools.load_pixbuf(self._image_files[index]) tools.garbage_collect() except Exception as e: log.error('Could not load pixbuf for page %u: %r', index + 1, e) pixbuf = image_tools.MISSING_IMAGE_ICON self._raw_pixbufs[index] = pixbuf
def test_pixbuf_to_pil(self): for image in ( 'transparent.png', 'transparent-indexed.png', 'pattern-opaque-rgb.png', 'pattern-opaque-rgba.png', 'pattern-transparent-rgba.png', ): pixbuf = image_tools.load_pixbuf(get_image_path(image)) im = image_tools.pixbuf_to_pil(pixbuf) msg = ( 'pixbuf_to_pil("%s") failed; ' 'result %%(diff_type)s differs: %%(diff)s' % (image,) ) self.assertImagesEqual(im, pixbuf, msg=msg)
def _get_pixbuf(self, index): """Return the pixbuf indexed by <index> from cache. Pixbufs not found in cache are fetched from disk first. """ pixbuf = constants.MISSING_IMAGE_ICON if index not in self._raw_pixbufs: self._wait_on_page(index + 1) try: pixbuf = image_tools.load_pixbuf(self._image_files[index]) self._raw_pixbufs[index] = pixbuf tools.garbage_collect() except Exception, e: self._raw_pixbufs[index] = constants.MISSING_IMAGE_ICON log.debug('Could not load pixbuf for page %d: %r', index, e)
def _get_pixbuf(self, index): """Return the pixbuf indexed by <index> from cache. Pixbufs not found in cache are fetched from disk first. """ pixbuf = image_tools.MISSING_IMAGE_ICON if index not in self._raw_pixbufs: self._wait_on_page(index + 1) try: pixbuf = image_tools.load_pixbuf(self._image_files[index]) self._raw_pixbufs[index] = pixbuf tools.garbage_collect() except Exception, e: self._raw_pixbufs[index] = image_tools.MISSING_IMAGE_ICON log.error('Could not load pixbuf for page %u: %r', index + 1, e)
def test_load_pixbuf_basic(self): for image in _TEST_IMAGES: image_path = get_image_path(image.name) if self.use_pil: # When using PIL, indexed formats will be # converted to RGBA by pixbuf_to_pil. expected_mode = pil_mode_to_gdk_mode(image.mode) else: expected_mode = 'RGBA' if image.has_alpha else 'RGB' im = Image.open(image_path).convert(expected_mode) pixbuf = image_tools.load_pixbuf(image_path) msg = ( 'load_pixbuf("%s") failed; ' 'result %%(diff_type)s differs: %%(diff)s' % (image.name,) ) self.assertImagesEqual(pixbuf, im, msg=msg)
def test_fit_in_rectangle_opaque_no_resize(self): # Check opaque image is unchanged when not resizing. for image in ( 'pattern-opaque-rgb.png', 'pattern-opaque-rgba.png', ): input = image_tools.load_pixbuf(get_image_path(image)) width, height = input.get_width(), input.get_height() for scaling_quality in range(4): prefs['scaling quality'] = scaling_quality result = image_tools.fit_in_rectangle(input, width, height, scaling_quality=scaling_quality) msg = ( 'fit_in_rectangle("%s", scaling quality=%d) failed; ' 'result %%(diff_type)s differs: %%(diff)s' % (image, scaling_quality) ) self.assertImagesEqual(result, input, msg=msg)
def test_load_pixbuf_modes(self): tmp_file = tempfile.NamedTemporaryFile(prefix=u'image.', suffix=u'.png', delete=False) tmp_file.close() base_im = Image.open(get_image_path('transparent.png')) for supported, expected_pixbuf_mode, mode in _IMAGE_MODES: if not supported: continue input_im = base_im.convert(mode) input_im.save(tmp_file.name) pixbuf = image_tools.load_pixbuf(tmp_file.name) expected_im = input_im.convert(expected_pixbuf_mode) msg = ( 'load_pixbuf("%s") failed; ' 'result %%(diff_type)s differs: %%(diff)s' % (mode,) ) self.assertImagesEqual(pixbuf, expected_im, msg=msg)
def test_get_implied_rotation(self): for name in ( # JPEG. 'landscape-exif-270-rotation.jpg', 'landscape-no-exif.jpg', 'portrait-exif-180-rotation.jpg', 'portrait-no-exif.jpg', # PNG. 'landscape-exif-270-rotation.png', 'landscape-no-exif.png', 'portrait-exif-180-rotation.png', 'portrait-no-exif.png', ): image = get_test_image(name) pixbuf = image_tools.load_pixbuf(get_image_path(name)) rotation = image_tools.get_implied_rotation(pixbuf) self.assertEqual(rotation, image.rotation, msg='get_implied_rotation(%s) failed: %u instead of %u' % (image, rotation, image.rotation))
def _get_pixbuf(self, index): """Return the pixbuf indexed by <index> from cache. Pixbufs not found in cache are fetched from disk first. """ pixbuf = constants.MISSING_IMAGE_ICON if index not in self._raw_pixbufs: self._wait_on_page(index + 1) try: pixbuf = image_tools.load_pixbuf(self._image_files[index]) self._raw_pixbufs[index] = pixbuf except Exception: self._raw_pixbufs[index] = constants.MISSING_IMAGE_ICON else: try: pixbuf = self._raw_pixbufs[index] except Exception: pass return pixbuf
class Thumbnailer(object): """ The Thumbnailer class is responsible for managing MComix internal thumbnail creation. Depending on its settings, it either stores thumbnails on disk and retrieves them later, or simply creates new thumbnails each time it is called. """ def __init__(self, dst_dir=constants.THUMBNAIL_PATH, store_on_disk=None, size=None, force_recreation=False, archive_support=False): """ <dst_dir> set the thumbnailer's storage directory. If <store_on_disk> on disk is True, it changes the thumbnailer's behaviour to store files on disk, or just create new thumbnails each time it was called when set to False. Defaults to the 'create thumbnails' preference if not set. The dimensions for the created thumbnails is set by <size>, a (width, height) tupple. Defaults to the 'thumbnail size' preference if not set. If <force_recreation> is True, thumbnails stored on disk will always be re-created instead of being re-used. If <archive_support> is True, support for archive thumbnail creation (based on cover detection) is enabled. Otherwise, only image files are supported. """ self.dst_dir = dst_dir if store_on_disk is None: self.store_on_disk = prefs['create thumbnails'] else: self.store_on_disk = store_on_disk if size is None: self.width = self.height = prefs['thumbnail size'] self.default_sizes = True else: self.width, self.height = size self.default_sizes = False self.force_recreation = force_recreation self.archive_support = archive_support def thumbnail(self, filepath, async=False): """ Returns a thumbnail pixbuf for <filepath>, transparently handling both normal image files and archives. If a thumbnail file already exists, it is re-used. Otherwise, a new thumbnail is created from <filepath>. Returns None if thumbnail creation failed, or if the thumbnail creation is run asynchrounosly. """ # Update width and height from preferences if they haven't been set explicitly if self.default_sizes: self.width = prefs['thumbnail size'] self.height = prefs['thumbnail size'] if self._thumbnail_exists(filepath): thumbpath = self._path_to_thumbpath(filepath) pixbuf = image_tools.load_pixbuf(thumbpath) self.thumbnail_finished(filepath, pixbuf) return pixbuf else: if async: thread = threading.Thread(target=self._create_thumbnail, args=(filepath, )) thread.name += '-thumbnailer' thread.setDaemon(True) thread.start() return None else: return self._create_thumbnail(filepath)
class Thumbnailer(object): """ The Thumbnailer class is responsible for managing MComix internal thumbnail creation. Depending on its settings, it either stores thumbnails on disk and retrieves them later, or simply creates new thumbnails each time it is called. """ def __init__(self, dst_dir=constants.THUMBNAIL_PATH): self.store_on_disk = prefs['create thumbnails'] self.dst_dir = dst_dir self.width = prefs['thumbnail size'] self.height = prefs['thumbnail size'] self.default_sizes = True self.force_recreation = False def set_size(self, width, height): """ Sets <weight> and <height> for created thumbnails. """ self.width = width self.height = height self.default_sizes = False def set_force_recreation(self, force_recreation): """ If <force_recreation> is True, thumbnails stored on disk will always be re-created instead of being re-used. """ self.force_recreation = force_recreation def set_store_on_disk(self, store_on_disk): """ Changes the thumbnailer's behaviour to store files on disk, or just create new thumbnails each time it was called. """ self.store_on_disk = store_on_disk def set_destination_dir(self, dst_dir): """ Changes the Thumbnailer's storage directory. """ self.dst_dir = dst_dir def thumbnail(self, filepath, async=False): """ Returns a thumbnail pixbuf for <filepath>, transparently handling both normal image files and archives. If a thumbnail file already exists, it is re-used. Otherwise, a new thumbnail is created from <filepath>. Returns None if thumbnail creation failed, or if the thumbnail creation is run asynchrounosly. """ # Update width and height from preferences if they haven't been set explicitly if self.default_sizes: self.width = prefs['thumbnail size'] self.height = prefs['thumbnail size'] thumbpath = self._path_to_thumbpath(filepath) if self._thumbnail_exists(filepath): pixbuf = image_tools.load_pixbuf(thumbpath) self.thumbnail_finished(filepath, pixbuf) return pixbuf else: if async: thread = threading.Thread(target=self._create_thumbnail, args=(filepath,)) thread.setDaemon(True) thread.start() return None else: return self._create_thumbnail(filepath)