def _get_pixbuf128(path): try: if "gif" not in path[-3:].lower(): return gtk.gdk.pixbuf_new_from_file_at_size(path, 128, 128) else: thumb = gtk.gdk.PixbufAnimation(path).get_static_image() width = thumb.get_width() height = thumb.get_height() if width > height: return thumb.scale_simple(128, int(max(height * 128 / width, 1)), gtk.gdk.INTERP_TILES) else: return thumb.scale_simple(int(max(width * 128 / height, 1)), 128, gtk.gdk.INTERP_TILES) except Exception: pass # Try imaging try: im = Image.open(path) width = im.size[0] height = im.size[1] if width > height: im.resize(128, int(max(height * 128 / width, 1))) else: im.resize(int(max(width * 128 / height, 1)), 128) return pil_to_pixbuf(im) except: return None
def _get_pixbuf(self, index): """Return the pixbuf indexed by <index> from cache. Pixbufs not found in cache are fetched from disk first. """ if index not in self._raw_pixbufs: self._wait_on_page(index + 1) pxb_err = False try: """ Check for gif in the name of the file. If it is a gif, and the user wishes GIFs to be animated, load it as a PixbufAnimation and make sure that it actually is animated. If it isn't animated, load a pixbuf instead. """ if not (prefs['animate gifs'] or prefs['animate']) \ or "gif" not in self._image_files[index][-3:].lower(): self._raw_pixbufs[index] = gtk.gdk.pixbuf_new_from_file(self._image_files[index]) else: self._raw_pixbufs[index] = gtk.gdk.PixbufAnimation(self._image_files[index]) if self._raw_pixbufs[index].is_static_image(): self._raw_pixbufs[index] = self._raw_pixbufs[index].get_static_image() except Exception: pxb_err = True if pxb_err: try: im = image.Image.open(self._image_files[index]) self._raw_pixbufs[index] = image.pil_to_pixbuf(im) except Exception: self._raw_pixbufs[index] = self._get_missing_image() return self._raw_pixbufs[index]
def _get_pixbuf(self, index): """Return the pixbuf indexed by <index> from cache. Pixbufs not found in cache are fetched from disk first. """ if index not in self._raw_pixbufs: self._wait_on_page(index + 1) pxb_err = False try: """ Check for gif in the name of the file. If it is a gif, and the user wishes GIFs to be animated, load it as a PixbufAnimation and make sure that it actually is animated. If it isn't animated, load a pixbuf instead. """ if not (prefs['animate gifs'] or prefs['animate']) \ or "gif" not in self._image_files[index][-3:].lower(): self._raw_pixbufs[index] = gtk.gdk.pixbuf_new_from_file(self._image_files[index]) else: self._raw_pixbufs[index] = gtk.gdk.PixbufAnimation(self._image_files[index]) if self._raw_pixbufs[index].is_static_image(): self._raw_pixbufs[index] = self._raw_pixbufs[index].get_static_image() except Exception: pxb_err = True if pxb_err: try: im = image.Image.open(self._image_files[index]) self._raw_pixbufs[index] = image.pil_to_pixbuf(im) except Exception: self._raw_pixbufs[index] = self._get_missing_image() return self._raw_pixbufs[index]
def draw_histogram(pixbuf, height=170, fill=170, text=True): """Draw a histogram from <pixbuf> and return it as another pixbuf. The returned prixbuf will be 262x<height> px. The value of <fill> determines the colour intensity of the filled graphs, valid values are between 0 and 255. If <text> is True a label with the maximum pixel value will be added to one corner. """ im = Image.new('RGB', (258, height - 4), (30, 30, 30)) hist_data = image.pixbuf_to_pil(pixbuf).histogram() maximum = max(hist_data[:768] + [1]) y_scale = float(height - 6) / maximum r = [int(hist_data[n] * y_scale) for n in range(256)] g = [int(hist_data[n] * y_scale) for n in range(256, 512)] b = [int(hist_data[n] * y_scale) for n in range(512, 768)] im_data = im.getdata() # Draw the filling colours for x in range(256): for y in range(1, max(r[x], g[x], b[x]) + 1): r_px = y <= r[x] and fill or 0 g_px = y <= g[x] and fill or 0 b_px = y <= b[x] and fill or 0 im_data.putpixel((x + 1, height - 5 - y), (r_px, g_px, b_px)) # Draw the outlines for x in range(1, 256): for y in range(r[x - 1] + 1, r[x] + 1) + [r[x]] * (r[x] != 0): r_px, g_px, b_px = im_data.getpixel((x + 1, height - 5 - y)) im_data.putpixel((x + 1, height - 5 - y), (255, g_px, b_px)) for y in range(r[x] + 1, r[x - 1] + 1): r_px, g_px, b_px = im_data.getpixel((x, height - 5 - y)) im_data.putpixel((x, height - 5 - y), (255, g_px, b_px)) for y in range(g[x - 1] + 1, g[x] + 1) + [g[x]] * (g[x] != 0): r_px, g_px, b_px = im_data.getpixel((x + 1, height - 5 - y)) im_data.putpixel((x + 1, height - 5 - y), (r_px, 255, b_px)) for y in range(g[x] + 1, g[x - 1] + 1): r_px, g_px, b_px = im_data.getpixel((x, height - 5 - y)) im_data.putpixel((x, height - 5 - y), (r_px, 255, b_px)) for y in range(b[x - 1] + 1, b[x] + 1) + [b[x]] * (b[x] != 0): r_px, g_px, b_px = im_data.getpixel((x + 1, height - 5 - y)) im_data.putpixel((x + 1, height - 5 - y), (r_px, g_px, 255)) for y in range(b[x] + 1, b[x - 1] + 1): r_px, g_px, b_px = im_data.getpixel((x, height - 5 - y)) im_data.putpixel((x, height - 5 - y), (r_px, g_px, 255)) if text: maxstr = 'max: ' + str(maximum) draw = ImageDraw.Draw(im) draw.rectangle((0, 0, len(maxstr) * 6 + 2, 10), fill=(30, 30, 30)) draw.text((2, 0), maxstr, fill=(255, 255, 255)) im = ImageOps.expand(im, 1, (80, 80, 80)) im = ImageOps.expand(im, 1, (0, 0, 0)) return image.pil_to_pixbuf(im)
def _drag_begin(self, iconview, context): """Create a cursor image for drag-n-drop from the library. This method relies on implementation details regarding PIL's drawing functions and default font to produce good looking results. If those are changed in a future release of PIL, this method might produce bad looking output (e.g. non-centered text). It's also used with connect_after() to overwrite the cursor automatically created when using enable_model_drag_source(), so in essence it's a hack, but at least it works. """ icon_path = iconview.get_cursor()[0] num_books = len(iconview.get_selected_items()) book = self.get_book_at_path(icon_path) cover = self._library.backend.get_book_cover(book) if cover is None: cover = self._library.render_icon(gtk.STOCK_MISSING_IMAGE, gtk.ICON_SIZE_DIALOG) cover = cover.scale_simple(max(0, cover.get_width() // 2), max(0, cover.get_height() // 2), gtk.gdk.INTERP_TILES) cover = image.add_border(cover, 1, 0xFFFFFFFF) cover = image.add_border(cover, 1) if num_books > 1: cover_width = cover.get_width() cover_height = cover.get_height() pointer = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, max(30, cover_width + 15), max(30, cover_height + 10)) pointer.fill(0x00000000) cover.composite(pointer, 0, 0, cover_width, cover_height, 0, 0, 1, 1, gtk.gdk.INTERP_TILES, 255) im = Image.new('RGBA', (30, 30), 0x00000000) draw = ImageDraw.Draw(im) draw.polygon( (8, 0, 20, 0, 28, 8, 28, 20, 20, 28, 8, 28, 0, 20, 0, 8), fill=(0, 0, 0), outline=(0, 0, 0)) draw.polygon( (8, 1, 20, 1, 27, 8, 27, 20, 20, 27, 8, 27, 1, 20, 1, 8), fill=(128, 0, 0), outline=(255, 255, 255)) text = str(num_books) draw.text((15 - (6 * len(text) // 2), 9), text, fill=(255, 255, 255)) circle = image.pil_to_pixbuf(im) circle.composite(pointer, max(0, cover_width - 15), max(0, cover_height - 20), 30, 30, max(0, cover_width - 15), max(0, cover_height - 20), 1, 1, gtk.gdk.INTERP_TILES, 255) else: pointer = cover context.set_icon_pixbuf(pointer, -5, -5)
def _add_page_number(pixbuf, page): """Add page number <page> in a black rectangle in the top left corner of <pixbuf>. This is highly dependent on the dimensions of the built-in font in PIL (bad). If the PIL font was changed, this function would likely produce badly positioned numbers on the pixbuf. """ text = str(page) width = min(6 * len(text) + 2, pixbuf.get_width()) height = min(10, pixbuf.get_height()) im = Image.new('RGB', (width, height), (0, 0, 0)) draw = ImageDraw.Draw(im) draw.text((1, -1), text, fill=(255, 255, 255)) num_pixbuf = image.pil_to_pixbuf(im) num_pixbuf.copy_area(0, 0, width, height, pixbuf, 0, 0)
def _add_page_number(pixbuf, page): """Add page number <page> in a black rectangle in the top left corner of <pixbuf>. This is highly dependent on the dimensions of the built-in font in PIL (bad). If the PIL font was changed, this function would likely produce badly positioned numbers on the pixbuf. """ text = str(page) width = min(6 * len(text) + 2, pixbuf.get_width()) height = min(10, pixbuf.get_height()) im = Image.new('RGB', (width, height), (0, 0, 0)) draw = ImageDraw.Draw(im) draw.text((1, -1), text, fill=(255, 255, 255)) num_pixbuf = image.pil_to_pixbuf(im) num_pixbuf.copy_area(0, 0, width, height, pixbuf, 0, 0)
def _drag_begin(self, iconview, context): """Create a cursor image for drag-n-drop from the library. This method relies on implementation details regarding PIL's drawing functions and default font to produce good looking results. If those are changed in a future release of PIL, this method might produce bad looking output (e.g. non-centered text). It's also used with connect_after() to overwrite the cursor automatically created when using enable_model_drag_source(), so in essence it's a hack, but at least it works. """ icon_path = iconview.get_cursor()[0] num_books = len(iconview.get_selected_items()) book = self.get_book_at_path(icon_path) cover = self._library.backend.get_book_cover(book) if cover is None: cover = self._library.render_icon(gtk.STOCK_MISSING_IMAGE, gtk.ICON_SIZE_DIALOG) cover = cover.scale_simple(max(0, cover.get_width() // 2), max(0, cover.get_height() // 2), gtk.gdk.INTERP_TILES) cover = image.add_border(cover, 1, 0xFFFFFFFF) cover = image.add_border(cover, 1) if num_books > 1: cover_width = cover.get_width() cover_height = cover.get_height() pointer = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, max(30, cover_width + 15), max(30, cover_height + 10)) pointer.fill(0x00000000) cover.composite(pointer, 0, 0, cover_width, cover_height, 0, 0, 1, 1, gtk.gdk.INTERP_TILES, 255) im = Image.new('RGBA', (30, 30), 0x00000000) draw = ImageDraw.Draw(im) draw.polygon( (8, 0, 20, 0, 28, 8, 28, 20, 20, 28, 8, 28, 0, 20, 0, 8), fill=(0, 0, 0), outline=(0, 0, 0)) draw.polygon( (8, 1, 20, 1, 27, 8, 27, 20, 20, 27, 8, 27, 1, 20, 1, 8), fill=(128, 0, 0), outline=(255, 255, 255)) text = str(num_books) draw.text((15 - (6 * len(text) // 2), 9), text, fill=(255, 255, 255)) circle = image.pil_to_pixbuf(im) circle.composite(pointer, max(0, cover_width - 15), max(0, cover_height - 20), 30, 30, max(0, cover_width - 15), max(0, cover_height - 20), 1, 1, gtk.gdk.INTERP_TILES, 255) else: pointer = cover context.set_icon_pixbuf(pointer, -5, -5)