mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Some optimizations for previous PR
1) Set device pixel ratio on generated QPixmap 2) Avoid copying image data when loading cover from library 3) Convert thumbnail to RGBA in cover thread so as to do less work in GUI thread
This commit is contained in:
parent
dbc4860ac5
commit
f97147afec
@ -1708,7 +1708,7 @@ class DB:
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def cover_or_cache(self, path, timestamp):
|
def cover_or_cache(self, path, timestamp, as_what='bytes'):
|
||||||
path = os.path.abspath(os.path.join(self.library_path, path, COVER_FILE_NAME))
|
path = os.path.abspath(os.path.join(self.library_path, path, COVER_FILE_NAME))
|
||||||
try:
|
try:
|
||||||
stat = os.stat(path)
|
stat = os.stat(path)
|
||||||
@ -1723,7 +1723,13 @@ class DB:
|
|||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
f = open(path, 'rb')
|
f = open(path, 'rb')
|
||||||
with f:
|
with f:
|
||||||
return True, f.read(), stat.st_mtime
|
if as_what == 'pil_image':
|
||||||
|
from PIL import Image
|
||||||
|
data = Image.open(f)
|
||||||
|
data.load()
|
||||||
|
else:
|
||||||
|
data = f.read()
|
||||||
|
return True, data, stat.st_mtime
|
||||||
|
|
||||||
def compress_covers(self, path_map, jpeg_quality, progress_callback):
|
def compress_covers(self, path_map, jpeg_quality, progress_callback):
|
||||||
cpath_map = {}
|
cpath_map = {}
|
||||||
|
@ -1161,12 +1161,12 @@ class Cache:
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
@read_api
|
@read_api
|
||||||
def cover_or_cache(self, book_id, timestamp):
|
def cover_or_cache(self, book_id, timestamp, as_what='bytes'):
|
||||||
try:
|
try:
|
||||||
path = self._field_for('path', book_id).replace('/', os.sep)
|
path = self._field_for('path', book_id).replace('/', os.sep)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return False, None, None
|
return False, None, None
|
||||||
return self.backend.cover_or_cache(path, timestamp)
|
return self.backend.cover_or_cache(path, timestamp, as_what)
|
||||||
|
|
||||||
@read_api
|
@read_api
|
||||||
def cover_last_modified(self, book_id):
|
def cover_last_modified(self, book_id):
|
||||||
|
@ -777,10 +777,7 @@ class GridView(QListView):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def device_pixel_ratio(self):
|
def device_pixel_ratio(self):
|
||||||
try:
|
return self.devicePixelRatioF()
|
||||||
return self.devicePixelRatioF()
|
|
||||||
except AttributeError:
|
|
||||||
return self.devicePixelRatio()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def first_visible_row(self):
|
def first_visible_row(self):
|
||||||
@ -964,17 +961,16 @@ class GridView(QListView):
|
|||||||
cdata, timestamp = tc[book_id] # None, None if not cached.
|
cdata, timestamp = tc[book_id] # None, None if not cached.
|
||||||
if timestamp is None:
|
if timestamp is None:
|
||||||
# Cover not in cache. Try to read the cover from the library.
|
# Cover not in cache. Try to read the cover from the library.
|
||||||
has_cover, cdata, timestamp = db.new_api.cover_or_cache(book_id, 0)
|
has_cover, cdata, timestamp = db.new_api.cover_or_cache(book_id, 0, as_what='pil_image')
|
||||||
if has_cover:
|
if has_cover:
|
||||||
# There is a cover.jpg. Convert the byte string to an image.
|
# There is a cover.jpg. Convert the byte string to an image.
|
||||||
cache_valid = False
|
cache_valid = False
|
||||||
cdata = Image.open(BytesIO(cdata))
|
|
||||||
else:
|
else:
|
||||||
# No cover.jpg
|
# No cover.jpg
|
||||||
cache_valid = None
|
cache_valid = None
|
||||||
else:
|
else:
|
||||||
# A cover is in the cache. Check whether it is up to date.
|
# A cover is in the cache. Check whether it is up to date.
|
||||||
has_cover, tcdata, timestamp = db.new_api.cover_or_cache(book_id, timestamp)
|
has_cover, tcdata, timestamp = db.new_api.cover_or_cache(book_id, timestamp, as_what='pil_image')
|
||||||
if has_cover:
|
if has_cover:
|
||||||
if tcdata is None:
|
if tcdata is None:
|
||||||
# The cached cover is up-to-date.
|
# The cached cover is up-to-date.
|
||||||
@ -983,9 +979,6 @@ class GridView(QListView):
|
|||||||
else:
|
else:
|
||||||
# The cached cover is stale
|
# The cached cover is stale
|
||||||
cache_valid = False
|
cache_valid = False
|
||||||
# Convert the bytes from the cover.jpg. The image will be
|
|
||||||
# resized later.
|
|
||||||
cdata = Image.open(BytesIO(tcdata))
|
|
||||||
else:
|
else:
|
||||||
# We found a cached cover for a book without a cover. This can
|
# We found a cached cover for a book without a cover. This can
|
||||||
# happen in older version of calibre that can reuse book_ids
|
# happen in older version of calibre that can reuse book_ids
|
||||||
@ -1067,6 +1060,10 @@ class GridView(QListView):
|
|||||||
# Return the thumbnail, which is either None or a PIL Image. If not None
|
# Return the thumbnail, which is either None or a PIL Image. If not None
|
||||||
# the image will be converted to a QPixmap on the GUI thread. Putting
|
# the image will be converted to a QPixmap on the GUI thread. Putting
|
||||||
# None into the CoverCache ensures re-rendering won't try again.
|
# None into the CoverCache ensures re-rendering won't try again.
|
||||||
|
if getattr(thumb, 'mode', None) == 'RGB':
|
||||||
|
# Conversion to QPixmap needs RGBA data so do it here rather than
|
||||||
|
# in the GUI thread
|
||||||
|
thumb = thumb.convert('RGBA')
|
||||||
return thumb
|
return thumb
|
||||||
|
|
||||||
def re_render(self, book_id, thumb):
|
def re_render(self, book_id, thumb):
|
||||||
@ -1076,7 +1073,7 @@ class GridView(QListView):
|
|||||||
self.delegate.cover_cache.clear_staging()
|
self.delegate.cover_cache.clear_staging()
|
||||||
if thumb is not None:
|
if thumb is not None:
|
||||||
# Convert the image to a QPixmap
|
# Convert the image to a QPixmap
|
||||||
thumb = convert_PIL_image_to_pixmap(thumb)
|
thumb = convert_PIL_image_to_pixmap(thumb, self.device_pixel_ratio)
|
||||||
self.delegate.cover_cache.set(book_id, thumb)
|
self.delegate.cover_cache.set(book_id, thumb)
|
||||||
m = self.model()
|
m = self.model()
|
||||||
try:
|
try:
|
||||||
|
@ -688,10 +688,16 @@ def align8to32(bytes, width, mode):
|
|||||||
return b"".join(new_data)
|
return b"".join(new_data)
|
||||||
|
|
||||||
|
|
||||||
def convert_PIL_image_to_pixmap(im):
|
def convert_PIL_image_to_pixmap(im, device_pixel_ratio=1.0):
|
||||||
data = None
|
data = None
|
||||||
colortable = None
|
colortable = None
|
||||||
if im.mode == "1":
|
if im.mode == "RGBA":
|
||||||
|
fmt = QImage.Format.Format_RGBA8888
|
||||||
|
data = im.tobytes("raw", "RGBA")
|
||||||
|
elif im.mode == "RGB":
|
||||||
|
fmt = QImage.Format.Format_RGBX8888
|
||||||
|
data = im.convert("RGBA").tobytes("raw", "RGBA")
|
||||||
|
elif im.mode == "1":
|
||||||
fmt = QImage.Format.Format_Mono
|
fmt = QImage.Format.Format_Mono
|
||||||
elif im.mode == "L":
|
elif im.mode == "L":
|
||||||
fmt = QImage.Format.Format_Indexed8
|
fmt = QImage.Format.Format_Indexed8
|
||||||
@ -700,12 +706,6 @@ def convert_PIL_image_to_pixmap(im):
|
|||||||
fmt = QImage.Format.Format_Indexed8
|
fmt = QImage.Format.Format_Indexed8
|
||||||
palette = im.getpalette()
|
palette = im.getpalette()
|
||||||
colortable = [qRgba(*palette[i : i + 3], 255) & 0xFFFFFFFF for i in range(0, len(palette), 3)]
|
colortable = [qRgba(*palette[i : i + 3], 255) & 0xFFFFFFFF for i in range(0, len(palette), 3)]
|
||||||
elif im.mode == "RGB":
|
|
||||||
fmt = QImage.Format.Format_RGBX8888
|
|
||||||
data = im.convert("RGBA").tobytes("raw", "RGBA")
|
|
||||||
elif im.mode == "RGBA":
|
|
||||||
fmt = QImage.Format.Format_RGBA8888
|
|
||||||
data = im.tobytes("raw", "RGBA")
|
|
||||||
elif im.mode == "I;16":
|
elif im.mode == "I;16":
|
||||||
im = im.point(lambda i: i * 256)
|
im = im.point(lambda i: i * 256)
|
||||||
fmt = QImage.Format.Format_Grayscale16
|
fmt = QImage.Format.Format_Grayscale16
|
||||||
@ -715,6 +715,8 @@ def convert_PIL_image_to_pixmap(im):
|
|||||||
size = im.size
|
size = im.size
|
||||||
data = data or align8to32(im.tobytes(), size[0], im.mode)
|
data = data or align8to32(im.tobytes(), size[0], im.mode)
|
||||||
qimg = QImage(data, size[0], size[1], fmt)
|
qimg = QImage(data, size[0], size[1], fmt)
|
||||||
|
if device_pixel_ratio != 1.0:
|
||||||
|
qimg.setDevicePixelRatio(device_pixel_ratio)
|
||||||
if colortable:
|
if colortable:
|
||||||
qimg.setColorTable(colortable)
|
qimg.setColorTable(colortable)
|
||||||
return QPixmap.fromImage(qimg)
|
return QPixmap.fromImage(qimg)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user