mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Use the new convert_PIL_image_to_pixmap function.
I can't use ReadOnlyFileBuffer because PIL requires that the buffer have the lstrip() function. I did eliminate a copy operation when writing the image to the thumbnail cache.
This commit is contained in:
parent
7ab5fedd1a
commit
5ba71155ac
@ -34,6 +34,7 @@ from calibre.gui2.library.caches import CoverCache, ThumbnailCache
|
|||||||
from calibre.gui2.pin_columns import PinContainer
|
from calibre.gui2.pin_columns import PinContainer
|
||||||
from calibre.utils import join_with_timeout
|
from calibre.utils import join_with_timeout
|
||||||
from calibre.utils.config import prefs, tweaks
|
from calibre.utils.config import prefs, tweaks
|
||||||
|
from calibre.utils.img import convert_PIL_image_to_pixmap
|
||||||
from polyglot.builtins import itervalues
|
from polyglot.builtins import itervalues
|
||||||
from polyglot.queue import LifoQueue
|
from polyglot.queue import LifoQueue
|
||||||
|
|
||||||
@ -959,7 +960,8 @@ class GridView(QListView):
|
|||||||
db = self.dbref()
|
db = self.dbref()
|
||||||
if db is None:
|
if db is None:
|
||||||
return
|
return
|
||||||
cdata, timestamp = self.thumbnail_cache[book_id] # None, None if not cached.
|
tc = self.thumbnail_cache
|
||||||
|
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)
|
||||||
@ -992,7 +994,7 @@ class GridView(QListView):
|
|||||||
# source code don't have this problem because the cache UUID is
|
# source code don't have this problem because the cache UUID is
|
||||||
# set when the database changes instead of when the cache thread
|
# set when the database changes instead of when the cache thread
|
||||||
# is created.
|
# is created.
|
||||||
self.thumbnail_cache.invalidate((book_id,))
|
tc.invalidate((book_id,))
|
||||||
cache_valid = None
|
cache_valid = None
|
||||||
cdata = None
|
cdata = None
|
||||||
if has_cover and cdata is None:
|
if has_cover and cdata is None:
|
||||||
@ -1003,27 +1005,20 @@ class GridView(QListView):
|
|||||||
def make_thumbnail(self, cover_tuple):
|
def make_thumbnail(self, cover_tuple):
|
||||||
# Render the cover image data to the thumbnail size and correct format.
|
# Render the cover image data to the thumbnail size and correct format.
|
||||||
# Rendering isn't needed if the cover came from the cache and the cache
|
# Rendering isn't needed if the cover came from the cache and the cache
|
||||||
# is valid. Put a newly rendered image into the cache. Returns a byte
|
# is valid. Put a newly rendered image into the cache. Returns a
|
||||||
# string for the thumbnail image that can be rendered in the GUI. This
|
# QPixmap. This method is called on the cover thread.
|
||||||
# method is called on the cover thread.
|
|
||||||
|
|
||||||
def pil_image_to_data():
|
|
||||||
# Convert the image to a byte string. We don't use the global
|
|
||||||
# image_to_data() because it uses Qt types that might not be thread
|
|
||||||
# safe.
|
|
||||||
bio = BytesIO()
|
|
||||||
cdata.save(bio, format=CACHE_FORMAT)
|
|
||||||
return bio.getvalue()
|
|
||||||
|
|
||||||
cdata = cover_tuple.cdata
|
cdata = cover_tuple.cdata
|
||||||
book_id = cover_tuple.book_id
|
book_id = cover_tuple.book_id
|
||||||
|
tc = self.thumbnail_cache
|
||||||
|
thumb = None
|
||||||
if cover_tuple.has_cover:
|
if cover_tuple.has_cover:
|
||||||
# The book has a cover. Render the cover data as needed to get the
|
# The book has a cover. Render the cover data as needed to get the
|
||||||
# thumbnail that will be cached.
|
# thumbnail that will be cached.
|
||||||
if cdata.getbbox() is None and cover_tuple.cache_valid:
|
if cdata.getbbox() is None and cover_tuple.cache_valid:
|
||||||
# Something wrong with the cover data in the cache. Remove it
|
# Something wrong with the cover data in the cache. Remove it
|
||||||
# from the cache and render it again.
|
# from the cache and render it again.
|
||||||
self.thumbnail_cache.invalidate((book_id,))
|
tc.invalidate((book_id,))
|
||||||
self.render_queue.put(book_id)
|
self.render_queue.put(book_id)
|
||||||
return None
|
return None
|
||||||
if not cover_tuple.cache_valid:
|
if not cover_tuple.cache_valid:
|
||||||
@ -1047,43 +1042,36 @@ class GridView(QListView):
|
|||||||
cdata.thumbnail((int(nwidth), int(nheight)))
|
cdata.thumbnail((int(nwidth), int(nheight)))
|
||||||
# Put the new thumbnail into the cache.
|
# Put the new thumbnail into the cache.
|
||||||
try:
|
try:
|
||||||
cdata = pil_image_to_data()
|
with BytesIO() as buf:
|
||||||
self.thumbnail_cache.insert(book_id, cover_tuple.timestamp, cdata)
|
cdata.save(buf, format=CACHE_FORMAT)
|
||||||
|
# use getbuffer() instead of getvalue() to avoid a copy
|
||||||
|
tc.insert(book_id, cover_tuple.timestamp, buf.getbuffer())
|
||||||
|
thumb = convert_PIL_image_to_pixmap(cdata)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.thumbnail_cache.invalidate((book_id,))
|
tc.invalidate((book_id,))
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
else:
|
else:
|
||||||
# The cover data isn't valid. Remove it from the cache
|
# The cover data isn't valid. Remove it from the cache
|
||||||
self.thumbnail_cache.invalidate((book_id,))
|
tc.invalidate((book_id,))
|
||||||
else:
|
else:
|
||||||
# The data from the cover cache is valid. Get its byte string to
|
# The data from the cover cache is valid. the QPixmap to pass to
|
||||||
# pass to the GUI
|
# the GUI
|
||||||
cdata = pil_image_to_data()
|
thumb = convert_PIL_image_to_pixmap(cdata)
|
||||||
elif cover_tuple.cache_valid is not None:
|
elif cover_tuple.cache_valid is not None:
|
||||||
# Cover was removed, but it exists in cache. Remove it from the cache
|
# Cover was removed, but it exists in cache. Remove it from the cache
|
||||||
self.thumbnail_cache.invalidate((book_id,))
|
tc.invalidate((book_id,))
|
||||||
cdata = None
|
# Return the thumbnail, which is either None or a QPixmap. This can
|
||||||
# Return the thumbnail, which is either None or an image byte string.
|
# result in putting None into the cache so re-rendering doesn't try
|
||||||
# This can result in putting None into the cache so re-rendering doesn't
|
# again.
|
||||||
# try again.
|
return thumb
|
||||||
return cdata
|
|
||||||
|
|
||||||
def re_render(self, book_id, thumb):
|
def re_render(self, book_id, thumb):
|
||||||
# This is called on the GUI thread when a cover thumbnail is not in the
|
# This is called on the GUI thread when a cover thumbnail is not in the
|
||||||
# CoverCache. The parameter "thumb" is a byte string representation of
|
# CoverCache. The parameter "thumb" is None if there is no cover or a
|
||||||
# the correctly-sized thumbnail.
|
# QPixmap of the correctly scaled cover
|
||||||
self.delegate.cover_cache.clear_staging()
|
self.delegate.cover_cache.clear_staging()
|
||||||
if thumb is None:
|
self.delegate.cover_cache.set(book_id, thumb)
|
||||||
self.delegate.cover_cache.set(book_id, None)
|
|
||||||
else:
|
|
||||||
# There seems to be deadlock or memory corruption problems when
|
|
||||||
# using loadFromData in a non-GUI thread. Avoid these by doing the
|
|
||||||
# conversion here instead of in the cover thread.
|
|
||||||
p = QPixmap()
|
|
||||||
p.setDevicePixelRatio(self.device_pixel_ratio)
|
|
||||||
p.loadFromData(thumb, CACHE_FORMAT)
|
|
||||||
self.delegate.cover_cache.set(book_id, p)
|
|
||||||
m = self.model()
|
m = self.model()
|
||||||
try:
|
try:
|
||||||
index = m.db.row(book_id)
|
index = m.db.row(book_id)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user