def draw_histogram(self, height: int = 170, fill: int = 170): """ Draw a histogram (RGB) from self.__pixbuf and return it as another pixbuf. :param height: height of the returned pixbuf :param fill: determines the color intensity of the filled graphs, valid values are between 0 and 255. :returns: modified pixbuf, the returned prixbuf will be 262x<height> px. """ self.__pixbuf = ImageTools.static_image(self.__pixbuf) im = Image.new('RGB', (258, height - 4), (30, 30, 30)) hist_data = ImageTools.pixbuf_to_pil(self.__pixbuf).histogram() maximum = max(hist_data[:768] + [1]) y_scale = (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 colors for x in range(256): for y in range(1, max(r[x], g[x], b[x]) + 1): r_px = fill if y <= r[x] else 0 g_px = fill if y <= g[x] else 0 b_px = fill if y <= b[x] else 0 im_data.putpixel((x + 1, height - 5 - y), (r_px, g_px, b_px)) # Draw the outlines for x in range(1, 256): self._im_putpixel(x=x, channel=r, height=height, im_data=im_data) self._im_putpixel(x=x, channel=g, height=height, im_data=im_data) self._im_putpixel(x=x, channel=b, height=height, im_data=im_data) if config['ENHANCE_EXTRA']: # if True a label with the maximum pixel value will be added to one corner maxstr = f'max pixel value: {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)) self.__hist_image.set_from_pixbuf(ImageTools.pil_to_pixbuf(im))
def _add_subpixbuf(self, canvas, x: int, y: int, image_size: tuple, source_pixbuf): """ Copy a subpixbuf from <source_pixbuf> to <canvas> as it should be in the lens if the coordinates <x>, <y> are the mouse pointer position on the main window layout area. The displayed image (scaled from the <source_pixbuf>) must have size <image_size> """ # Prevent division by zero exceptions further down if not image_size[0]: return # FIXME This merely prevents Errors being raised if source_pixbuf is an # animation. The result might be broken, though, since animation, # rotation etc. might not match or will be ignored: source_pixbuf = ImageTools.static_image(source_pixbuf) rotation = config['ROTATION'] if config['AUTO_ROTATE_FROM_EXIF']: rotation += ImageTools.get_implied_rotation(source_pixbuf) rotation %= 360 if rotation in (90, 270): scale = source_pixbuf.get_height() / image_size[0] else: scale = source_pixbuf.get_width() / image_size[0] x *= scale y *= scale source_mag = config['LENS_MAGNIFICATION'] / scale width = height = config['LENS_SIZE'] / source_mag paste_left = x > width / 2 paste_top = y > height / 2 dest_x = max(0, int(math.ceil((width / 2 - x) * source_mag))) dest_y = max(0, int(math.ceil((height / 2 - y) * source_mag))) if rotation == 90: x, y = y, source_pixbuf.get_height() - x elif rotation == 180: x = source_pixbuf.get_width() - x y = source_pixbuf.get_height() - y elif rotation == 270: x, y = source_pixbuf.get_width() - y, x if config['HORIZONTAL_FLIP']: if rotation in (90, 270): y = source_pixbuf.get_height() - y else: x = source_pixbuf.get_width() - x if config['VERTICAL_FLIP']: if rotation in (90, 270): x = source_pixbuf.get_width() - x else: y = source_pixbuf.get_height() - y src_x = x - width / 2 src_y = y - height / 2 if src_x < 0: width += src_x src_x = 0 if src_y < 0: height += src_y src_y = 0 width = max(0, min(source_pixbuf.get_width() - src_x, width)) height = max(0, min(source_pixbuf.get_height() - src_y, height)) if width < 1 or height < 1: return subpixbuf = source_pixbuf.new_subpixbuf(int(src_x), int(src_y), int(width), int(height)) subpixbuf = subpixbuf.scale_simple( int(math.ceil(source_mag * subpixbuf.get_width())), int(math.ceil(source_mag * subpixbuf.get_height())), config['SCALING_QUALITY']) if rotation == 0: subpixbuf = subpixbuf.rotate_simple(GdkPixbuf.PixbufRotation.NONE) elif rotation == 90: subpixbuf = subpixbuf.rotate_simple(GdkPixbuf.PixbufRotation.CLOCKWISE) elif rotation == 180: subpixbuf = subpixbuf.rotate_simple(GdkPixbuf.PixbufRotation.UPSIDEDOWN) elif rotation == 270: subpixbuf = subpixbuf.rotate_simple(GdkPixbuf.PixbufRotation.COUNTERCLOCKWISE) if config['HORIZONTAL_FLIP']: subpixbuf = subpixbuf.flip(horizontal=True) if config['VERTICAL_FLIP']: subpixbuf = subpixbuf.flip(horizontal=False) subpixbuf = self.__window.enhancer.enhance(subpixbuf) if paste_left: dest_x = 0 else: dest_x = min(canvas.get_width() - subpixbuf.get_width(), dest_x) if paste_top: dest_y = 0 else: dest_y = min(canvas.get_height() - subpixbuf.get_height(), dest_y) if subpixbuf.get_has_alpha() and config['CHECKERED_BG_FOR_TRANSPARENT_IMAGES']: subpixbuf = subpixbuf.composite_color_simple(subpixbuf.get_width(), subpixbuf.get_height(), GdkPixbuf.InterpType.NEAREST, 255, 8, 0x777777, 0x999999) subpixbuf.copy_area(0, 0, subpixbuf.get_width(), subpixbuf.get_height(), canvas, dest_x, dest_y)
def _add_subpixbuf(self, canvas, x: int, y: int, image_size: tuple, source_pixbuf): """ Copy a subpixbuf from <source_pixbuf> to <canvas> as it should be in the lens if the coordinates <x>, <y> are the mouse pointer position on the main window layout area. The displayed image (scaled from the <source_pixbuf>) must have size <image_size> """ # Prevent division by zero exceptions further down if not image_size[0]: return # FIXME This merely prevents Errors being raised if source_pixbuf is an # animation. The result might be broken, though, since animation, # rotation etc. might not match or will be ignored: source_pixbuf = ImageTools.static_image(source_pixbuf) rotation = config['ROTATION'] if config['AUTO_ROTATE_FROM_EXIF']: rotation += ImageTools.get_implied_rotation(source_pixbuf) rotation %= 360 if rotation in (90, 270): scale = source_pixbuf.get_height() / image_size[0] else: scale = source_pixbuf.get_width() / image_size[0] x *= scale y *= scale source_mag = config['LENS_MAGNIFICATION'] / scale width = height = config['LENS_SIZE'] / source_mag paste_left = x > width / 2 paste_top = y > height / 2 dest_x = max(0, int(math.ceil((width / 2 - x) * source_mag))) dest_y = max(0, int(math.ceil((height / 2 - y) * source_mag))) match rotation: case 90: x, y = y, source_pixbuf.get_height() - x case 180: x = source_pixbuf.get_width() - x y = source_pixbuf.get_height() - y case 270: x, y = source_pixbuf.get_width() - y, x src_x = x - width / 2 src_y = y - height / 2 if src_x < 0: width += src_x src_x = 0 if src_y < 0: height += src_y src_y = 0 width = max(0, min(source_pixbuf.get_width() - src_x, width)) height = max(0, min(source_pixbuf.get_height() - src_y, height)) if width < 1 or height < 1: return subpixbuf = source_pixbuf.new_subpixbuf(int(src_x), int(src_y), int(width), int(height)) subpixbuf = subpixbuf.scale_simple( int(math.ceil(source_mag * subpixbuf.get_width())), int(math.ceil(source_mag * subpixbuf.get_height())), config['GDK_SCALING_FILTER']) subpixbuf = ImageTools.rotate_pixbuf(subpixbuf, rotation) subpixbuf = ImageTools.enhance(subpixbuf) if paste_left: dest_x = 0 else: dest_x = min(canvas.get_width() - subpixbuf.get_width(), dest_x) if paste_top: dest_y = 0 else: dest_y = min(canvas.get_height() - subpixbuf.get_height(), dest_y) if subpixbuf.get_has_alpha(): subpixbuf = ImageTools.add_alpha_background(subpixbuf, subpixbuf.get_width(), subpixbuf.get_height()) subpixbuf.copy_area(0, 0, subpixbuf.get_width(), subpixbuf.get_height(), canvas, dest_x, dest_y)