From 7905e70cdfa3e545d7cf7e1386cccbc7b2b17a1f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 13 Nov 2015 21:51:32 +0530 Subject: [PATCH] Refactor scale_img into its own module --- src/calibre/srv/content.py | 28 ++------------------- src/calibre/utils/img.py | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 src/calibre/utils/img.py diff --git a/src/calibre/srv/content.py b/src/calibre/srv/content.py index beca1ea322..d442e4887c 100644 --- a/src/calibre/srv/content.py +++ b/src/calibre/srv/content.py @@ -11,8 +11,6 @@ from binascii import hexlify from io import BytesIO from threading import Lock -from PyQt5.Qt import QImage, QByteArray, QBuffer, Qt - from calibre import fit_image from calibre.constants import config_dir, iswindows from calibre.db.errors import NoSuchFormat @@ -25,6 +23,7 @@ from calibre.srv.routes import endpoint, json from calibre.srv.utils import http_date from calibre.utils.config_base import tweaks from calibre.utils.date import timestampfromdt +from calibre.utils.img import scale_image, image_from_data from calibre.utils.filenames import ascii_filename, atomic_rename from calibre.utils.shared_file import share_open @@ -94,28 +93,6 @@ def create_file_copy(ctx, rd, prefix, library_id, book_id, ext, mtime, copy_func rd.outheaders['Tempfile'] = hexlify(fname.encode('utf-8')) return rd.filesystem_file_with_custom_etag(ans, prefix, library_id, book_id, mtime, extra_etag_data) -def scale_image(data, width=60, height=80, compression_quality=75, as_png=False): - # We use Qt instead of ImageMagick here because ImageMagick seems to use - # some kind of memory pool, causing memory consumption in the server to - # sky rocket. Since we are only using QImage this method is thread safe, - # and does not require a QApplication - if isinstance(data, QImage): - img = data - else: - img = QImage() - if not img.loadFromData(data): - raise ValueError('Could not load image for thumbnail generation') - scaled, nwidth, nheight = fit_image(img.width(), img.height(), width, height) - if scaled: - img = img.scaled(nwidth, nheight, Qt.KeepAspectRatio, Qt.SmoothTransformation) - ba = QByteArray() - buf = QBuffer(ba) - buf.open(QBuffer.WriteOnly) - fmt = 'PNG' if as_png else 'JPEG' - if not img.save(buf, fmt, quality=compression_quality): - raise ValueError('Failed to export thumbnail image to: ' + fmt) - return ba.data() - def cover(ctx, rd, library_id, db, book_id, width=None, height=None): mtime = db.cover_last_modified(book_id) if mtime is None: @@ -231,9 +208,8 @@ def icon(ctx, rd, which): except EnvironmentError: raise HTTPNotFound() with src: - img = QImage() idata = src.read() - img.loadFromData(idata) + img = image_from_data(idata) scaled, width, height = fit_image(img.width(), img.height(), sz, sz) if scaled: idata = scale_image(img, width, height, as_png=True) diff --git a/src/calibre/utils/img.py b/src/calibre/utils/img.py new file mode 100644 index 0000000000..0177e0961a --- /dev/null +++ b/src/calibre/utils/img.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python2 +# vim:fileencoding=utf-8 +# License: GPLv3 Copyright: 2015, Kovid Goyal + +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +from PyQt5.Qt import QImage, QByteArray, QBuffer, Qt, QPainter + +from calibre import fit_image + +def image_from_data(data): + i = QImage() + if not i.loadFromData(data): + raise ValueError('Not a valid image') + return i + +def scale_image(data, width=60, height=80, compression_quality=70, as_png=False, preserve_aspect_ratio=True): + # We use Qt instead of ImageMagick here because ImageMagick seems to use + # some kind of memory pool, causing memory consumption to sky rocket. Since + # we are only using QImage this method is thread safe, and does not require + # a QApplication/GUI thread + if isinstance(data, QImage): + img = data + else: + img = QImage() + if not img.loadFromData(data): + raise ValueError('Could not load image for thumbnail generation') + if preserve_aspect_ratio: + scaled, nwidth, nheight = fit_image(img.width(), img.height(), width, height) + if scaled: + img = img.scaled(nwidth, nheight, Qt.KeepAspectRatio, Qt.SmoothTransformation) + else: + if img.width() != width or img.height() != height: + img = img.scaled(width, height, Qt.IgnoreAspectRatio, Qt.SmoothTransformation) + if not as_png and img.hasAlphaChannel(): + nimg = QImage(img.size(), QImage.Format_RGB32) + nimg.fill(Qt.white) + p = QPainter(nimg) + p.drawImage(0, 0, img) + p.end() + img = nimg + ba = QByteArray() + buf = QBuffer(ba) + buf.open(QBuffer.WriteOnly) + fmt = 'PNG' if as_png else 'JPEG' + if not img.save(buf, fmt, quality=compression_quality): + raise ValueError('Failed to export thumbnail image to: ' + fmt) + return ba.data() + +