def get_virtual_double_page(self, page=None): """Return True if the current state warrants use of virtual double page mode (i.e. if double page mode is on, the corresponding preference is set, and one of the two images that should normally be displayed has a width that exceeds its height), or if currently on the first page. """ if page == None: page = self.get_current_page() if (page == 1 and prefs['virtual double page for fitting images'] & constants.SHOW_DOUBLE_AS_ONE_TITLE and self._window.filehandler.archive_type is not None): return True if (not prefs['default double page'] or not prefs['virtual double page for fitting images'] & constants.SHOW_DOUBLE_AS_ONE_WIDE or page == self.get_number_of_pages()): return False for page in (page, page + 1): if not self.page_is_available(page): return False pixbuf = self._get_pixbuf(page - 1) width, height = pixbuf.get_width(), pixbuf.get_height() if prefs['auto rotate from exif']: rotation = image_tools.get_implied_rotation(pixbuf) assert rotation in (0, 90, 180, 270) if rotation in (90, 270): width, height = height, width if width > height: return True return False
def _get_pixbuf_rotation(self, pixbuf, no_autorotation=False): """ Determines if a pixbuf must be rotated before being displayed. Returns the degree of rotation (0, 90, 180, 270). """ width, height = pixbuf.get_width(), pixbuf.get_height() rotation = prefs['rotation'] if prefs['auto rotate from exif']: rotation += image_tools.get_implied_rotation(pixbuf) rotation = rotation % 360 if (height > width and not no_autorotation and prefs['auto rotate depending on size'] in (constants.AUTOROTATE_HEIGHT_90, constants.AUTOROTATE_HEIGHT_270)): if prefs['auto rotate depending on size'] == constants.AUTOROTATE_HEIGHT_90: rotation = 90 else: rotation = 270 elif (width > height and not no_autorotation and prefs['auto rotate depending on size'] in (constants.AUTOROTATE_WIDTH_90, constants.AUTOROTATE_WIDTH_270)): if prefs['auto rotate depending on size'] == constants.AUTOROTATE_WIDTH_90: rotation = 90 else: rotation = 270 return rotation
def _get_pixbuf_rotation(self, pixbuf, no_autorotation=False): """ Determines if a pixbuf must be rotated before being displayed. Returns the degree of rotation (0, 90, 180, 270). """ width, height = pixbuf.get_width(), pixbuf.get_height() rotation = prefs['rotation'] if prefs['auto rotate from exif']: rotation += image_tools.get_implied_rotation(pixbuf) rotation = rotation % 360 if (height > width and not no_autorotation and prefs['auto rotate depending on size'] in (constants.AUTOROTATE_HEIGHT_90, constants.AUTOROTATE_HEIGHT_270)): if prefs[ 'auto rotate depending on size'] == constants.AUTOROTATE_HEIGHT_90: rotation = 90 else: rotation = 270 elif (width > height and not no_autorotation and prefs['auto rotate depending on size'] in (constants.AUTOROTATE_WIDTH_90, constants.AUTOROTATE_WIDTH_270)): if prefs[ 'auto rotate depending on size'] == constants.AUTOROTATE_WIDTH_90: rotation = 90 else: rotation = 270 return rotation
def test_get_implied_rotation(self): for name in ( # JPEG. 'landscape-exif-270-rotation.jpg', 'landscape-no-exif.jpg', 'portrait-exif-180-rotation.jpg', 'portrait-no-exif.jpg', # PNG. 'landscape-exif-270-rotation.png', 'landscape-no-exif.png', 'portrait-exif-180-rotation.png', 'portrait-no-exif.png', ): image = get_test_image(name) pixbuf = image_tools.load_pixbuf(get_image_path(name)) rotation = image_tools.get_implied_rotation(pixbuf) self.assertEqual(rotation, image.rotation, msg='get_implied_rotation(%s) failed: %u instead of %u' % (image, rotation, image.rotation))
def _add_subpixbuf(self, canvas, x, y, image_size, 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 = image_tools.static_image(source_pixbuf) rotation = prefs['rotation'] if prefs['auto rotate from exif']: rotation += image_tools.get_implied_rotation(source_pixbuf) rotation = rotation % 360 if rotation in [90, 270]: scale = float(source_pixbuf.get_height()) / image_size[0] else: scale = float(source_pixbuf.get_width()) / image_size[0] x *= scale y *= scale source_mag = prefs['lens magnification'] / scale width = height = prefs['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 prefs['horizontal flip']: if rotation in (90, 270): y = source_pixbuf.get_height() - y else: x = source_pixbuf.get_width() - x if prefs['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())), prefs['scaling quality']) if rotation == 90: subpixbuf = subpixbuf.rotate_simple(Gdk.PIXBUF_ROTATE_CLOCKWISE) elif rotation == 180: subpixbuf = subpixbuf.rotate_simple(Gdk.PIXBUF_ROTATE_UPSIDEDOWN) elif rotation == 270: subpixbuf = subpixbuf.rotate_simple( Gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE) if prefs['horizontal flip']: subpixbuf = subpixbuf.flip(horizontal=True) if prefs['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 prefs['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, y, image_size, 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 rotation = prefs['rotation'] if prefs['auto rotate from exif']: rotation += image_tools.get_implied_rotation(source_pixbuf) rotation = rotation % 360 if rotation in [90, 270]: scale = float(source_pixbuf.get_height()) / image_size[0] else: scale = float(source_pixbuf.get_width()) / image_size[0] x *= scale y *= scale source_mag = prefs['lens magnification'] / scale width = height = prefs['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 prefs['horizontal flip']: if rotation in (90, 270): y = source_pixbuf.get_height() - y else: x = source_pixbuf.get_width() - x if prefs['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.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())), prefs['scaling quality']) if rotation == 90: subpixbuf = subpixbuf.rotate_simple( gtk.gdk.PIXBUF_ROTATE_CLOCKWISE) elif rotation == 180: subpixbuf = subpixbuf.rotate_simple( gtk.gdk.PIXBUF_ROTATE_UPSIDEDOWN) elif rotation == 270: subpixbuf = subpixbuf.rotate_simple( gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE) if prefs['horizontal flip']: subpixbuf = subpixbuf.flip(horizontal=True) if prefs['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 prefs['checkered bg for transparent images']: subpixbuf = subpixbuf.composite_color_simple(subpixbuf.get_width(), subpixbuf.get_height(), gtk.gdk.INTERP_NEAREST, 255, 8, 0x777777, 0x999999) subpixbuf.copy_area(0, 0, subpixbuf.get_width(), subpixbuf.get_height(), canvas, dest_x, dest_y)
def _draw_image(self, at_bottom, scroll): self._display_active_widgets() while gtk.events_pending(): gtk.main_iteration(False) if not self.filehandler.file_loaded: self._waiting_for_redraw = False return False area_width, area_height = self.get_visible_area_size() if prefs['zoom mode'] == constants.ZOOM_MODE_HEIGHT: scaled_width = -1 else: scaled_width = area_width if prefs['zoom mode'] == constants.ZOOM_MODE_WIDTH: scaled_height = -1 else: scaled_height = area_height scale_up = prefs['stretch'] self.is_virtual_double_page = \ self.imagehandler.get_virtual_double_page() skip_pixbuf = not self.imagehandler.page_is_available() if self.displayed_double() and not skip_pixbuf: left_pixbuf, right_pixbuf = self.imagehandler.get_pixbufs() if self.is_manga_mode: right_pixbuf, left_pixbuf = left_pixbuf, right_pixbuf left_unscaled_x = left_pixbuf.get_width() left_unscaled_y = left_pixbuf.get_height() right_unscaled_x = right_pixbuf.get_width() right_unscaled_y = right_pixbuf.get_height() left_rotation = prefs['rotation'] right_rotation = prefs['rotation'] if prefs['auto rotate from exif']: left_rotation += image_tools.get_implied_rotation(left_pixbuf) left_rotation = left_rotation % 360 right_rotation += image_tools.get_implied_rotation(right_pixbuf) right_rotation = right_rotation % 360 if prefs['zoom mode'] == constants.ZOOM_MODE_MANUAL: if left_rotation in (90, 270): total_width = left_unscaled_y total_height = left_unscaled_x else: total_width = left_unscaled_x total_height = left_unscaled_y if right_rotation in (90, 270): total_width += right_unscaled_y total_height += right_unscaled_x else: total_width += right_unscaled_x total_height += right_unscaled_y total_width += 2 # For the 2 px gap between images. scaled_width = int(self._manual_zoom * total_width / 100) scaled_height = int(self._manual_zoom * total_height / 100) scale_up = True left_pixbuf, right_pixbuf = image_tools.fit_2_in_rectangle( left_pixbuf, right_pixbuf, scaled_width, scaled_height, scale_up=scale_up, rotation1=left_rotation, rotation2=right_rotation) if prefs['horizontal flip']: left_pixbuf = left_pixbuf.flip(horizontal=True) right_pixbuf = right_pixbuf.flip(horizontal=True) if prefs['vertical flip']: left_pixbuf = left_pixbuf.flip(horizontal=False) right_pixbuf = right_pixbuf.flip(horizontal=False) left_pixbuf = self.enhancer.enhance(left_pixbuf) right_pixbuf = self.enhancer.enhance(right_pixbuf) self.left_image.set_from_pixbuf(left_pixbuf) self.right_image.set_from_pixbuf(right_pixbuf) x_padding = (area_width - left_pixbuf.get_width() - right_pixbuf.get_width()) / 2 y_padding = (area_height - max(left_pixbuf.get_height(), right_pixbuf.get_height())) / 2 if left_rotation in (90, 270): left_scale_percent = (100.0 * left_pixbuf.get_width() / left_unscaled_y) else: left_scale_percent = (100.0 * left_pixbuf.get_width() / left_unscaled_x) if right_rotation in (90, 270): right_scale_percent = (100.0 * right_pixbuf.get_width() / right_unscaled_y) else: right_scale_percent = (100.0 * right_pixbuf.get_width() / right_unscaled_x) self.statusbar.set_resolution( (left_unscaled_x, left_unscaled_y, left_scale_percent), (right_unscaled_x, right_unscaled_y, right_scale_percent)) elif not skip_pixbuf: pixbuf = self.imagehandler.get_pixbufs(single=True)[ 0 ] unscaled_x = pixbuf.get_width() unscaled_y = pixbuf.get_height() rotation = prefs['rotation'] if prefs['auto rotate from exif']: rotation += image_tools.get_implied_rotation(pixbuf) rotation = rotation % 360 if prefs['zoom mode'] == constants.ZOOM_MODE_MANUAL: # If 'Scale small images' is true, scale up the image's base size scale_x = max(scale_up and scaled_width or unscaled_x, unscaled_x) scale_y = max(scale_up and scaled_height or unscaled_y, unscaled_y) scaled_width = int(self._manual_zoom * scale_x / 100) scaled_height = int(self._manual_zoom * scale_y / 100) if rotation in (90, 270): scaled_width, scaled_height = scaled_height, scaled_width scale_up = True pixbuf = image_tools.fit_in_rectangle(pixbuf, scaled_width, scaled_height, scale_up=scale_up, rotation=rotation) if prefs['horizontal flip']: pixbuf = pixbuf.flip(horizontal=True) if prefs['vertical flip']: pixbuf = pixbuf.flip(horizontal=False) pixbuf = self.enhancer.enhance(pixbuf) self.left_image.set_from_pixbuf(pixbuf) self.right_image.clear() x_padding = (area_width - pixbuf.get_width()) / 2 y_padding = (area_height - pixbuf.get_height()) / 2 if rotation in (90, 270): scale_percent = 100.0 * pixbuf.get_width() / unscaled_y else: scale_percent = 100.0 * pixbuf.get_width() / unscaled_x self.statusbar.set_resolution((unscaled_x, unscaled_y, scale_percent)) if prefs['smart bg'] and not skip_pixbuf: bg_colour = self.imagehandler.get_pixbuf_auto_background( not self.displayed_double()) self.set_bg_colour(bg_colour) if prefs['smart thumb bg'] and prefs['show thumbnails']: self.thumbnailsidebar.change_thumbnail_background_color(bg_colour) elif prefs['smart thumb bg'] and prefs['show thumbnails'] and not skip_pixbuf: bg_colour = image_tools.get_most_common_edge_colour( self.left_image.get_pixbuf()) self.thumbnailsidebar.change_thumbnail_background_color(bg_colour) if not skip_pixbuf: self._image_box.window.freeze_updates() self._main_layout.move(self._image_box, max(0, x_padding), max(0, y_padding)) self.left_image.show() if self.displayed_double(): self.right_image.show() else: self.right_image.hide() self._main_layout.set_size(*self._image_box.size_request()) if scroll: if at_bottom: self.scroll_to_fixed(horiz='endsecond', vert='bottom') else: self.scroll_to_fixed(horiz='startfirst', vert='top') self._image_box.window.thaw_updates() else: # If the pixbuf for the current page(s) isn't available, # hide both images to clear any old pixbufs. self.left_image.hide() self.right_image.hide() self._update_page_information() self._waiting_for_redraw = False while gtk.events_pending(): gtk.main_iteration(False) return False
def _draw_image(self, scroll_to): self._update_toggles_visibility() self.osd.clear() if not self.filehandler.file_loaded: self._clear_main_area() self._waiting_for_redraw = False return False if self.imagehandler.page_is_available(): distribution_axis = constants.DISTRIBUTION_AXIS alignment_axis = constants.ALIGNMENT_AXIS pixbuf_count = 2 if self.displayed_double( ) else 1 # XXX limited to at most 2 pages pixbuf_list = list(self.imagehandler.get_pixbufs(pixbuf_count)) do_not_transform = [ image_tools.disable_transform(x) for x in pixbuf_list ] size_list = [[pixbuf.get_width(), pixbuf.get_height()] for pixbuf in pixbuf_list] if self.is_manga_mode: orientation = constants.MANGA_ORIENTATION else: orientation = constants.WESTERN_ORIENTATION # Rotation handling: # - apply Exif rotation on individual images # - apply automatic rotation (size based) on whole page # - apply manual rotation on whole page if prefs['auto rotate from exif']: rotation_list = [ image_tools.get_implied_rotation(pixbuf) for pixbuf in pixbuf_list ] else: rotation_list = [0] * len(pixbuf_list) virtual_size = [0, 0] for i in range(pixbuf_count): if rotation_list[i] in (90, 270): size_list[i].reverse() size = size_list[i] virtual_size[distribution_axis] += size[distribution_axis] virtual_size[alignment_axis] = max( virtual_size[alignment_axis], size[alignment_axis]) rotation = self._get_size_rotation(*virtual_size) rotation = (rotation + prefs['rotation']) % 360 if rotation in (90, 270): distribution_axis, alignment_axis = alignment_axis, distribution_axis orientation = list(orientation) orientation.reverse() for i in range(pixbuf_count): size_list[i].reverse() if rotation in (180, 270): orientation = tools.vector_opposite(orientation) for i in range(pixbuf_count): rotation_list[i] = (rotation_list[i] + rotation) % 360 if prefs['vertical flip'] and rotation in (90, 270): orientation = tools.vector_opposite(orientation) if prefs['horizontal flip'] and rotation in (0, 180): orientation = tools.vector_opposite(orientation) viewport_size = () # dummy expand_area = False scrollbar_requests = [False] * len(self._scroll) # Visible area size is recomputed depending on scrollbar visibility while True: self._show_scrollbars(scrollbar_requests) new_viewport_size = self.get_visible_area_size() if new_viewport_size == viewport_size: break viewport_size = new_viewport_size zoom_dummy_size = list(viewport_size) dasize = zoom_dummy_size[distribution_axis] - \ self._spacing * (pixbuf_count - 1) if dasize <= 0: dasize = 1 zoom_dummy_size[distribution_axis] = dasize scaled_sizes = self.zoom.get_zoomed_size( size_list, zoom_dummy_size, distribution_axis, do_not_transform) self.layout = layout.FiniteLayout(scaled_sizes, viewport_size, orientation, self._spacing, expand_area, distribution_axis, alignment_axis) union_scaled_size = self.layout.get_union_box().get_size() scrollbar_requests = [(old or new) for old, new in zip( scrollbar_requests, tools.smaller(viewport_size, union_scaled_size))] if len(tuple(filter( None, scrollbar_requests))) > 1 and not expand_area: expand_area = True viewport_size = () # start anew for i in range(pixbuf_count): pixbuf_list[i] = image_tools.fit_pixbuf_to_rectangle( pixbuf_list[i], scaled_sizes[i], rotation_list[i]) for i in range(pixbuf_count): pixbuf_list[i] = image_tools.trans_pixbuf( pixbuf_list[i], flip=prefs['vertical flip'], flop=prefs['horizontal flip']) pixbuf_list[i] = self.enhancer.enhance(pixbuf_list[i]) for i in range(pixbuf_count): image_tools.set_from_pixbuf(self.images[i], pixbuf_list[i]) resolutions = [ (*size, scaled_size[0] / size[0]) for scaled_size, size in zip(scaled_sizes, size_list) ] if self.is_manga_mode: resolutions.reverse() self.statusbar.set_resolution(resolutions) self.statusbar.update() smartbg = prefs['smart bg'] smartthumbbg = prefs['show thumbnails'] and prefs['smart thumb bg'] if smartbg or smartthumbbg: bg_color = self.imagehandler.get_pixbuf_auto_background( pixbuf_count) if smartbg: self.set_bg_color(bg_color) if smartthumbbg: self.thumbnailsidebar.change_thumbnail_background_color( bg_color) self._main_layout.get_bin_window().freeze_updates() self._main_layout.set_size(*union_scaled_size) content_boxes = self.layout.get_content_boxes() for i in range(pixbuf_count): self._main_layout.move(self.images[i], *content_boxes[i].get_position()) for i in range(pixbuf_count): self.images[i].show() for i in range(pixbuf_count, len(self.images)): self.images[i].hide() # Reset orientation so scrolling behaviour is sane. if self.is_manga_mode: self.layout.set_orientation(constants.MANGA_ORIENTATION) else: self.layout.set_orientation(constants.WESTERN_ORIENTATION) if scroll_to is not None: destination = (scroll_to, ) * 2 if constants.SCROLL_TO_START == scroll_to: index = constants.FIRST_INDEX elif constants.SCROLL_TO_END == scroll_to: index = constants.LAST_INDEX else: index = None self.scroll_to_predefined(destination, index) self._main_layout.get_bin_window().thaw_updates() else: # Save scroll destination for when the page becomes available. self._last_scroll_destination = scroll_to # If the pixbuf for the current page(s) isn't available, # hide all images to clear any old pixbufs. # XXX How about calling self._clear_main_area? for i in range(len(self.images)): self.images[i].hide() self._show_scrollbars([False] * len(self._scroll)) self._waiting_for_redraw = False return False
def _add_subpixbuf(self, canvas, x, y, image_size, source_pixbuf, other_image_width=0, left=True): """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>. If <other_image_width> is given, it is the width of the "other" image in double page mode. The image we are getting the coordinates for is the left one unless <left> is False. """ # Prevent division by zero exceptions further down if not image_size[0]: return area_x, area_y = self._window.get_visible_area_size() if left: padding_x = max(0, (area_x - other_image_width - image_size[0]) // 2) else: padding_x = \ (max(0, (area_x - other_image_width - image_size[0]) // 2) + other_image_width + 2) padding_y = max(0, (area_y - image_size[1]) // 2) x -= padding_x y -= padding_y rotation = prefs['rotation'] if prefs['auto rotate from exif']: rotation += image_tools.get_implied_rotation(source_pixbuf) rotation = rotation % 360 if rotation in [90, 270]: scale = float(source_pixbuf.get_height()) / image_size[0] else: scale = float(source_pixbuf.get_width()) / image_size[0] x *= scale y *= scale source_mag = prefs['lens magnification'] / scale width = height = prefs['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 prefs['horizontal flip']: if rotation in (90, 270): y = source_pixbuf.get_height() - y else: x = source_pixbuf.get_width() - x if prefs['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.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())), prefs['scaling quality']) if rotation == 90: subpixbuf = subpixbuf.rotate_simple( gtk.gdk.PIXBUF_ROTATE_CLOCKWISE) elif rotation == 180: subpixbuf = subpixbuf.rotate_simple( gtk.gdk.PIXBUF_ROTATE_UPSIDEDOWN) elif rotation == 270: subpixbuf = subpixbuf.rotate_simple( gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE) if prefs['horizontal flip']: subpixbuf = subpixbuf.flip(horizontal=True) if prefs['vertical flip']: subpixbuf = subpixbuf.flip(horizontal=False) 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) subpixbuf.copy_area(0, 0, subpixbuf.get_width(), subpixbuf.get_height(), canvas, dest_x, dest_y)
def _add_subpixbuf(self, canvas, x, y, image_size, source_pixbuf, other_image_width=0, left=True): """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>. If <other_image_width> is given, it is the width of the "other" image in double page mode. The image we are getting the coordinates for is the left one unless <left> is False. """ # Prevent division by zero exceptions further down if not image_size[0]: return area_x, area_y = self._window.get_visible_area_size() if left: padding_x = max(0, (area_x - other_image_width - image_size[0]) // 2) else: padding_x = \ (max(0, (area_x - other_image_width - image_size[0]) // 2) + other_image_width + 2) padding_y = max(0, (area_y - image_size[1]) // 2) x -= padding_x y -= padding_y rotation = prefs['rotation'] if prefs['auto rotate from exif']: rotation += image_tools.get_implied_rotation(source_pixbuf) rotation = rotation % 360 if rotation in [90, 270]: scale = float(source_pixbuf.get_height()) / image_size[0] else: scale = float(source_pixbuf.get_width()) / image_size[0] x *= scale y *= scale source_mag = prefs['lens magnification'] / scale width = height = prefs['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 prefs['horizontal flip']: if rotation in (90, 270): y = source_pixbuf.get_height() - y else: x = source_pixbuf.get_width() - x if prefs['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.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())), gtk.gdk.INTERP_TILES) if rotation == 90: subpixbuf = subpixbuf.rotate_simple( gtk.gdk.PIXBUF_ROTATE_CLOCKWISE) elif rotation == 180: subpixbuf = subpixbuf.rotate_simple( gtk.gdk.PIXBUF_ROTATE_UPSIDEDOWN) elif rotation == 270: subpixbuf = subpixbuf.rotate_simple( gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE) if prefs['horizontal flip']: subpixbuf = subpixbuf.flip(horizontal=True) if prefs['vertical flip']: subpixbuf = subpixbuf.flip(horizontal=False) 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) subpixbuf.copy_area(0, 0, subpixbuf.get_width(), subpixbuf.get_height(), canvas, dest_x, dest_y)
def _draw_image(self, scroll_to): self._update_toggles_visibility() if not self.filehandler.file_loaded: self._clear_main_area() self._waiting_for_redraw = False return False if self.imagehandler.page_is_available(): distribution_axis = constants.DISTRIBUTION_AXIS alignment_axis = constants.ALIGNMENT_AXIS pixbuf_count = 2 if self.displayed_double() else 1 # XXX limited to at most 2 pages pixbuf_list = list(self.imagehandler.get_pixbufs(pixbuf_count)) size_list = [[pixbuf.get_width(), pixbuf.get_height()] for pixbuf in pixbuf_list] if self.is_manga_mode: orientation = constants.MANGA_ORIENTATION else: orientation = constants.WESTERN_ORIENTATION # Rotation handling: # - apply Exif rotation on individual images # - apply automatic rotation (size based) on whole page # - apply manual rotation on whole page if prefs['auto rotate from exif']: rotation_list = [image_tools.get_implied_rotation(pixbuf) for pixbuf in pixbuf_list] else: rotation_list = [0] * len(pixbuf_list) virtual_size = [0, 0] for i in range(pixbuf_count): if rotation_list[i] in (90, 270): size_list[i].reverse() size = size_list[i] virtual_size[distribution_axis] += size[distribution_axis] virtual_size[alignment_axis] = max(virtual_size[alignment_axis], size[alignment_axis]) rotation = self._get_size_rotation(*virtual_size) rotation = (rotation + prefs['rotation']) % 360 if rotation in (90, 270): distribution_axis, alignment_axis = alignment_axis, distribution_axis orientation = list(orientation) orientation.reverse() for i in range(pixbuf_count): size_list[i].reverse() if rotation in (180, 270): orientation = tools.vector_opposite(orientation) for i in range(pixbuf_count): rotation_list[i] = (rotation_list[i] + rotation) % 360 if prefs['vertical flip'] and rotation in (90, 270): orientation = tools.vector_opposite(orientation) if prefs['horizontal flip'] and rotation in (0, 180): orientation = tools.vector_opposite(orientation) viewport_size = () # dummy expand_area = False scrollbar_requests = [False] * len(self._scroll) # Visible area size is recomputed depending on scrollbar visibility while True: self._show_scrollbars(scrollbar_requests) new_viewport_size = self.get_visible_area_size() if new_viewport_size == viewport_size: break viewport_size = new_viewport_size zoom_dummy_size = list(viewport_size) dasize = zoom_dummy_size[distribution_axis] - \ self._spacing * (pixbuf_count - 1) if dasize <= 0: dasize = 1 zoom_dummy_size[distribution_axis] = dasize scaled_sizes = self.zoom.get_zoomed_size(size_list, zoom_dummy_size, distribution_axis) self.layout = layout.FiniteLayout(scaled_sizes, viewport_size, orientation, self._spacing, expand_area, distribution_axis, alignment_axis) union_scaled_size = self.layout.get_union_box().get_size() scrollbar_requests = map(operator.or_, scrollbar_requests, tools.smaller(viewport_size, union_scaled_size)) if len(filter(None, scrollbar_requests)) > 1 and not expand_area: expand_area = True viewport_size = () # start anew for i in range(pixbuf_count): pixbuf_list[i] = image_tools.fit_pixbuf_to_rectangle( pixbuf_list[i], scaled_sizes[i], rotation_list[i]) for i in range(pixbuf_count): if prefs['horizontal flip']: pixbuf_list[i] = pixbuf_list[i].flip(horizontal=True) if prefs['vertical flip']: pixbuf_list[i] = pixbuf_list[i].flip(horizontal=False) pixbuf_list[i] = self.enhancer.enhance(pixbuf_list[i]) for i in range(pixbuf_count): image_tools.set_from_pixbuf(self.images[i], pixbuf_list[i]) scales = tuple(map(lambda x, y: math.sqrt(tools.div( tools.volume(x), tools.volume(y))), scaled_sizes, size_list)) resolutions = tuple(map(lambda x, y: x + [y,], size_list, scales)) if self.is_manga_mode: resolutions = tuple(reversed(resolutions)) self.statusbar.set_resolution(resolutions) self.statusbar.update() smartbg = prefs['smart bg'] smartthumbbg = prefs['smart thumb bg'] and prefs['show thumbnails'] if smartbg or smartthumbbg: bg_colour = self.imagehandler.get_pixbuf_auto_background(pixbuf_count) if smartbg: self.set_bg_colour(bg_colour) if smartthumbbg: self.thumbnailsidebar.change_thumbnail_background_color(bg_colour) self._main_layout.window.freeze_updates() self._main_layout.set_size(*union_scaled_size) content_boxes = self.layout.get_content_boxes() for i in range(pixbuf_count): self._main_layout.move(self.images[i], *content_boxes[i].get_position()) for i in range(pixbuf_count): self.images[i].show() for i in range(pixbuf_count, len(self.images)): self.images[i].hide() # Reset orientation so scrolling behaviour is sane. if self.is_manga_mode: self.layout.set_orientation(constants.MANGA_ORIENTATION) else: self.layout.set_orientation(constants.WESTERN_ORIENTATION) if scroll_to is not None: destination = (scroll_to,) * 2 if constants.SCROLL_TO_START == scroll_to: index = constants.FIRST_INDEX elif constants.SCROLL_TO_END == scroll_to: index = constants.LAST_INDEX else: index = None self.scroll_to_predefined(destination, index) self._main_layout.window.thaw_updates() else: # Save scroll destination for when the page becomes available. self._last_scroll_destination = scroll_to # If the pixbuf for the current page(s) isn't available, # hide all images to clear any old pixbufs. # XXX How about calling self._clear_main_area? for i in range(len(self.images)): self.images[i].hide() self._show_scrollbars([False] * len(self._scroll)) self._waiting_for_redraw = False return False
def _draw_image(self, at_bottom, scroll): self._display_active_widgets() while gtk.events_pending(): gtk.main_iteration(False) if not self.filehandler.file_loaded: self._waiting_for_redraw = False return False area_width, area_height = self.get_visible_area_size() if prefs['zoom mode'] == constants.ZOOM_MODE_HEIGHT: scaled_width = -1 else: scaled_width = area_width if prefs['zoom mode'] == constants.ZOOM_MODE_WIDTH: scaled_height = -1 else: scaled_height = area_height scale_up = prefs['stretch'] self.is_virtual_double_page = \ self.imagehandler.get_virtual_double_page() skip_pixbuf = not self.imagehandler.page_is_available() if self.displayed_double() and not skip_pixbuf: left_pixbuf, right_pixbuf = self.imagehandler.get_pixbufs() if self.is_manga_mode: right_pixbuf, left_pixbuf = left_pixbuf, right_pixbuf left_unscaled_x = left_pixbuf.get_width() left_unscaled_y = left_pixbuf.get_height() right_unscaled_x = right_pixbuf.get_width() right_unscaled_y = right_pixbuf.get_height() left_rotation = prefs['rotation'] right_rotation = prefs['rotation'] if prefs['auto rotate from exif']: left_rotation += image_tools.get_implied_rotation(left_pixbuf) left_rotation = left_rotation % 360 right_rotation += image_tools.get_implied_rotation( right_pixbuf) right_rotation = right_rotation % 360 if prefs['zoom mode'] == constants.ZOOM_MODE_MANUAL: if left_rotation in (90, 270): total_width = left_unscaled_y total_height = left_unscaled_x else: total_width = left_unscaled_x total_height = left_unscaled_y if right_rotation in (90, 270): total_width += right_unscaled_y total_height += right_unscaled_x else: total_width += right_unscaled_x total_height += right_unscaled_y total_width += 2 # For the 2 px gap between images. scaled_width = int(self._manual_zoom * total_width / 100) scaled_height = int(self._manual_zoom * total_height / 100) scale_up = True left_pixbuf, right_pixbuf = image_tools.fit_2_in_rectangle( left_pixbuf, right_pixbuf, scaled_width, scaled_height, scale_up=scale_up, rotation1=left_rotation, rotation2=right_rotation) if prefs['horizontal flip']: left_pixbuf = left_pixbuf.flip(horizontal=True) right_pixbuf = right_pixbuf.flip(horizontal=True) if prefs['vertical flip']: left_pixbuf = left_pixbuf.flip(horizontal=False) right_pixbuf = right_pixbuf.flip(horizontal=False) left_pixbuf = self.enhancer.enhance(left_pixbuf) right_pixbuf = self.enhancer.enhance(right_pixbuf) self.left_image.set_from_pixbuf(left_pixbuf) self.right_image.set_from_pixbuf(right_pixbuf) x_padding = (area_width - left_pixbuf.get_width() - right_pixbuf.get_width()) / 2 y_padding = (area_height - max(left_pixbuf.get_height(), right_pixbuf.get_height())) / 2 if left_rotation in (90, 270): left_scale_percent = (100.0 * left_pixbuf.get_width() / left_unscaled_y) else: left_scale_percent = (100.0 * left_pixbuf.get_width() / left_unscaled_x) if right_rotation in (90, 270): right_scale_percent = (100.0 * right_pixbuf.get_width() / right_unscaled_y) else: right_scale_percent = (100.0 * right_pixbuf.get_width() / right_unscaled_x) self.statusbar.set_resolution( (left_unscaled_x, left_unscaled_y, left_scale_percent), (right_unscaled_x, right_unscaled_y, right_scale_percent)) elif not skip_pixbuf: pixbuf = self.imagehandler.get_pixbufs(single=True)[0] unscaled_x = pixbuf.get_width() unscaled_y = pixbuf.get_height() rotation = prefs['rotation'] if prefs['auto rotate from exif']: rotation += image_tools.get_implied_rotation(pixbuf) rotation = rotation % 360 if prefs['zoom mode'] == constants.ZOOM_MODE_MANUAL: # If 'Scale small images' is true, scale up the image's base size scale_x = max(scale_up and scaled_width or unscaled_x, unscaled_x) scale_y = max(scale_up and scaled_height or unscaled_y, unscaled_y) scaled_width = int(self._manual_zoom * scale_x / 100) scaled_height = int(self._manual_zoom * scale_y / 100) if rotation in (90, 270): scaled_width, scaled_height = scaled_height, scaled_width scale_up = True pixbuf = image_tools.fit_in_rectangle(pixbuf, scaled_width, scaled_height, scale_up=scale_up, rotation=rotation) if prefs['horizontal flip']: pixbuf = pixbuf.flip(horizontal=True) if prefs['vertical flip']: pixbuf = pixbuf.flip(horizontal=False) pixbuf = self.enhancer.enhance(pixbuf) self.left_image.set_from_pixbuf(pixbuf) self.right_image.clear() x_padding = (area_width - pixbuf.get_width()) / 2 y_padding = (area_height - pixbuf.get_height()) / 2 if rotation in (90, 270): scale_percent = 100.0 * pixbuf.get_width() / unscaled_y else: scale_percent = 100.0 * pixbuf.get_width() / unscaled_x self.statusbar.set_resolution( (unscaled_x, unscaled_y, scale_percent)) if prefs['smart bg'] and not skip_pixbuf: bg_colour = self.imagehandler.get_pixbuf_auto_background( not self.displayed_double()) self.set_bg_colour(bg_colour) if prefs['smart thumb bg'] and prefs['show thumbnails']: self.thumbnailsidebar.change_thumbnail_background_color( bg_colour) elif prefs['smart thumb bg'] and prefs[ 'show thumbnails'] and not skip_pixbuf: bg_colour = image_tools.get_most_common_edge_colour( self.left_image.get_pixbuf()) self.thumbnailsidebar.change_thumbnail_background_color(bg_colour) if not skip_pixbuf: self._image_box.window.freeze_updates() self._main_layout.move(self._image_box, max(0, x_padding), max(0, y_padding)) self.left_image.show() if self.displayed_double(): self.right_image.show() else: self.right_image.hide() self._main_layout.set_size(*self._image_box.size_request()) if scroll: if at_bottom: self.scroll_to_fixed(horiz='endsecond', vert='bottom') else: self.scroll_to_fixed(horiz='startfirst', vert='top') self._image_box.window.thaw_updates() else: # If the pixbuf for the current page(s) isn't available, # hide both images to clear any old pixbufs. self.left_image.hide() self.right_image.hide() self._update_page_information() self._waiting_for_redraw = False while gtk.events_pending(): gtk.main_iteration(False) return False