Пример #1
0
    def _draw_image(self, at_bottom, scroll):
        self._display_active_widgets()

        if not self.filehandler.file_loaded:
            self._waiting_for_redraw = False
            return False

        self.is_virtual_double_page = self.imagehandler.get_virtual_double_page()

        if self.imagehandler.page_is_available():
            distribution_axis = constants.DISTRIBUTION_AXIS
            alignment_axis = constants.ALIGNMENT_AXIS
            n = 2 if self.displayed_double() else 1 # XXX limited to at most 2 pages
            pixbufs = list(self.imagehandler.get_pixbufs(n))
            rotations = [self._get_pixbuf_rotation(x, True) for x in pixbufs]
            sizes = map(lambda y: tuple(reversed(y[1])) \
                if rotations[y[0]] in (90, 270) else y[1], # 2D only
                enumerate([(x.get_width(), x.get_height()) for x in pixbufs]))

            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 * (n - 1)
                if dasize <= 0:
                    dasize = 1
                zoom_dummy_size[distribution_axis] = dasize
                scaled_sizes = self.zoom.get_zoomed_size(sizes, zoom_dummy_size,
                    distribution_axis)
                self.layout = scrolling.FiniteLayout(scaled_sizes, viewport_size,
                    scrolling.MANGA_ORIENTATION if self.is_manga_mode
                    else scrolling.WESTERN_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(n):
                pixbufs[i] = image_tools.fit_pixbuf_to_rectangle(
                    pixbufs[i], scaled_sizes[i], rotations[i])

            for i in range(n):
                if prefs['horizontal flip']: # 2D only
                    pixbufs[i] = pixbufs[i].flip(horizontal=True)
                if prefs['vertical flip']: # 2D only
                    pixbufs[i] = pixbufs[i].flip(horizontal=False)
                pixbufs[i] = self.enhancer.enhance(pixbufs[i])

            for i in range(n):
                self.images[i].set_from_pixbuf(pixbufs[i])

            scales = tuple(map(lambda x, y: math.sqrt(tools.div(
                tools.volume(x), tools.volume(y))), scaled_sizes, sizes))

            resolutions = tuple(map(lambda x, y: x + (y,), sizes, scales))
            if self.is_manga_mode:
                resolutions = tuple(reversed(resolutions))
            self.statusbar.set_resolution(resolutions)

            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(n)
            if smartbg:
                self.set_bg_colour(bg_colour)
            if smartthumbbg:
                self.thumbnailsidebar.change_thumbnail_background_color(bg_colour)

            #self._image_box.window.freeze_updates() # XXX replacement necessary?
            self._main_layout.set_size(*union_scaled_size)
            content_boxes = self.layout.get_content_boxes()
            for i in range(n):
                self._main_layout.move(self.images[i],
                    *content_boxes[i].get_position())

            for i in range(n):
                self.images[i].show()
            for i in range(n, len(self.images)):
                self.images[i].hide()

            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() # XXX replacement necessary?
        else:
            # 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._update_page_information()
        self._waiting_for_redraw = False

        return False
Пример #2
0
    def _scale_distributed(sizes, axis, max_size, allow_upscaling,
                           do_not_transform):
        ''' Calculates scales for a list of boxes that are distributed along a
        given axis (without any gaps). If the resulting scales are applied to
        their respective boxes, their new total size along axis will be as close
        as possible to max_size. The current implementation ensures that equal
        box sizes are mapped to equal scales.
        @param sizes: A list of box sizes.
        @param axis: The axis along which those boxes are distributed.
        @param max_size: The maximum size the scaled boxes may have along axis.
        @param allow_upscaling: True if upscaling is allowed, False otherwise.
        @param do_not_transform: True if the resulting scale must be 1, False
        otherwise.
        @return: A list of scales where the i-th scale belongs to the i-th box
        size. If sizes is empty, the empty list is returned. If there are more
        boxes than max_size, an approximation is returned where all resulting
        scales will shrink their respective boxes to 1 along axis. In this case,
        the scaled total size might be greater than max_size. '''
        n = len(sizes)
        # trivial cases first
        if n == 0:
            return []
        if n >= max_size:
            # In this case, only one solution or only an approximation is available.
            # if n > max_size, the result won't fit into max_size.
            return map(lambda x: tools.div(1, x[axis]),
                       sizes)  # FIXME ignores do_not_transform
        total_axis_size = sum(map(lambda x: x[axis], sizes))
        if (total_axis_size <= max_size) and not allow_upscaling:
            # identity
            return [IDENTITY_ZOOM] * n

        # non-trival case
        scale = tools.div(
            max_size, total_axis_size
        )  # FIXME initial guess should take unscalable images into account
        scaling_data = [None] * n
        total_axis_size = 0
        # This loop collects some data we need for the actual computations later.
        for i in range(n):
            this_size = sizes[i]
            # Shortcut: If the size cannot be changed, accept the original size.
            if do_not_transform[i]:
                total_axis_size += this_size[axis]
                scaling_data[i] = [
                    IDENTITY_ZOOM, IDENTITY_ZOOM, False, IDENTITY_ZOOM, 0.0
                ]
                continue
            # Initial guess: The current scale works for all tuples.
            ideal = tools.scale(this_size, scale)
            ideal_vol = tools.volume(ideal)
            # Let's use a dummy to compute the actual (rounded) size along axis
            # so we can rescale the rounded tuple with a better local_scale
            # later. This rescaling is necessary to ensure that the sizes in ALL
            # dimensions are monotonically scaled (with respect to local_scale).
            # A nice side effect of this is that it keeps the aspect ratio better.
            dummy_approx = _round_nonempty((ideal[axis], ))[0]
            local_scale = tools.div(dummy_approx, this_size[axis])
            total_axis_size += dummy_approx
            can_be_downscaled = dummy_approx > 1
            if can_be_downscaled:
                forced_size = dummy_approx - 1
                forced_scale = tools.div(forced_size, this_size[axis])
                forced_approx = _scale_image_size(this_size, forced_scale)
                forced_vol_err = tools.relerr(tools.volume(forced_approx),
                                              ideal_vol)
            else:
                forced_scale = None
                forced_vol_err = None
            scaling_data[i] = [
                local_scale, ideal, can_be_downscaled, forced_scale,
                forced_vol_err
            ]
        # Now we need to find at most total_axis_size - max_size occasions to
        # scale down some tuples so the whole thing would fit into max_size. If
        # we are lucky, there will be no gaps at the end (or at least fewer gaps
        # than we would have if we always rounded down).
        dirty = True  # This flag prevents infinite loops if nothing can be made any smaller.
        while dirty and (total_axis_size > max_size):
            # This algorithm needs O(n*n) time. Let's hope that n is small enough.
            dirty = False
            current_index = 0
            current_min = None
            for i in range(n):
                d = scaling_data[i]
                if not d[2]:
                    # Ignore elements that cannot be made any smaller.
                    continue
                if (current_min is None) or (d[4] < current_min[4]):
                    # We are searching for the tuple where downscaling results
                    # in the smallest relative volume error (compared to the
                    # respective ideal volume).
                    current_min = d
                    current_index = i
            for i in range(current_index, n):
                # We must scale down ALL equal tuples. Otherwise, images that
                # are of equal size might appear to be of different size
                # afterwards. The downside of this approach is that it might
                # introduce more gaps than necessary.
                d = scaling_data[i]
                if (not d[2]) or (d[1] != current_min[1]):
                    continue
                d[0] = d[3]
                d[2] = False  # only once per tuple
                total_axis_size -= 1
                dirty = True
        else:
            # If we are here and total_axis_size < max_size, we could try to
            # upscale some tuples similarily to the other loop (i.e. smallest
            # relative volume error first, equal boxes in conjunction with each
            # other). However, this is not as useful as the other loop, slightly
            # more complicated and it won't do anything if all tuples are equal.
            pass
        return map(lambda d: d[0], scaling_data)
Пример #3
0
    def _scale_distributed(sizes, axis, max_size, allow_upscaling,
        do_not_transform):
        """ Calculates scales for a list of boxes that are distributed along a
        given axis (without any gaps). If the resulting scales are applied to
        their respective boxes, their new total size along axis will be as close
        as possible to max_size. The current implementation ensures that equal
        box sizes are mapped to equal scales.
        @param sizes: A list of box sizes.
        @param axis: The axis along which those boxes are distributed.
        @param max_size: The maximum size the scaled boxes may have along axis.
        @param allow_upscaling: True if upscaling is allowed, False otherwise.
        @param do_not_transform: True if the resulting scale must be 1, False
        otherwise.
        @return: A list of scales where the i-th scale belongs to the i-th box
        size. If sizes is empty, the empty list is returned. If there are more
        boxes than max_size, an approximation is returned where all resulting
        scales will shrink their respective boxes to 1 along axis. In this case,
        the scaled total size might be greater than max_size. """
        n = len(sizes)
        # trivial cases first
        if n == 0:
            return []
        if n >= max_size:
            # In this case, only one solution or only an approximation is available.
            # if n > max_size, the result won't fit into max_size.
            return map(lambda x: tools.div(1, x[axis]), sizes) # FIXME ignores do_not_transform
        total_axis_size = sum(map(lambda x: x[axis], sizes))
        if (total_axis_size <= max_size) and not allow_upscaling:
            # identity
            return [IDENTITY_ZOOM] * n

        # non-trival case
        scale = tools.div(max_size, total_axis_size) # FIXME initial guess should take unscalable images into account
        scaling_data = [None] * n
        total_axis_size = 0
        # This loop collects some data we need for the actual computations later.
        for i in range(n):
            this_size = sizes[i]
            # Shortcut: If the size cannot be changed, accept the original size.
            if do_not_transform[i]:
                total_axis_size += this_size[axis]
                scaling_data[i] = [IDENTITY_ZOOM, IDENTITY_ZOOM, False,
                    IDENTITY_ZOOM, 0.0]
                continue
            # Initial guess: The current scale works for all tuples.
            ideal = tools.scale(this_size, scale)
            ideal_vol = tools.volume(ideal)
            # Let's use a dummy to compute the actual (rounded) size along axis
            # so we can rescale the rounded tuple with a better local_scale
            # later. This rescaling is necessary to ensure that the sizes in ALL
            # dimensions are monotonically scaled (with respect to local_scale).
            # A nice side effect of this is that it keeps the aspect ratio better.
            dummy_approx = _round_nonempty((ideal[axis],))[0]
            local_scale = tools.div(dummy_approx, this_size[axis])
            total_axis_size += dummy_approx
            can_be_downscaled = dummy_approx > 1
            if can_be_downscaled:
                forced_size = dummy_approx - 1
                forced_scale = tools.div(forced_size, this_size[axis])
                forced_approx = _scale_image_size(this_size, forced_scale)
                forced_vol_err = tools.relerr(tools.volume(forced_approx), ideal_vol)
            else:
                forced_scale = None
                forced_vol_err = None
            scaling_data[i] = [local_scale, ideal, can_be_downscaled,
                forced_scale, forced_vol_err]
        # Now we need to find at most total_axis_size - max_size occasions to
        # scale down some tuples so the whole thing would fit into max_size. If
        # we are lucky, there will be no gaps at the end (or at least fewer gaps
        # than we would have if we always rounded down).
        dirty=True # This flag prevents infinite loops if nothing can be made any smaller.
        while dirty and (total_axis_size > max_size):
            # This algorithm needs O(n*n) time. Let's hope that n is small enough.
            dirty=False
            current_index = 0
            current_min = None
            for i in range(n):
                d = scaling_data[i]
                if not d[2]:
                    # Ignore elements that cannot be made any smaller.
                    continue
                if (current_min is None) or (d[4] < current_min[4]):
                    # We are searching for the tuple where downscaling results
                    # in the smallest relative volume error (compared to the
                    # respective ideal volume).
                    current_min = d
                    current_index = i
            for i in range(current_index, n):
                # We must scale down ALL equal tuples. Otherwise, images that
                # are of equal size might appear to be of different size
                # afterwards. The downside of this approach is that it might
                # introduce more gaps than necessary.
                d = scaling_data[i]
                if (not d[2]) or (d[1] != current_min[1]):
                    continue
                d[0] = d[3]
                d[2] = False # only once per tuple
                total_axis_size -= 1
                dirty=True
        else:
            # If we are here and total_axis_size < max_size, we could try to
            # upscale some tuples similarly to the other loop (i.e. smallest
            # relative volume error first, equal boxes in conjunction with each
            # other). However, this is not as useful as the other loop, slightly
            # more complicated and it won't do anything if all tuples are equal.
            pass
        return map(lambda d: d[0], scaling_data)
Пример #4
0
    def _draw_image(self, at_bottom, scroll):
        self._display_active_widgets()

        if not self.filehandler.file_loaded:
            self._waiting_for_redraw = False
            return False

        self.is_virtual_double_page = self.imagehandler.get_virtual_double_page(
        )

        if self.imagehandler.page_is_available():
            distribution_axis = constants.DISTRIBUTION_AXIS
            alignment_axis = constants.ALIGNMENT_AXIS
            n = 2 if self.displayed_double(
            ) else 1  # XXX limited to at most 2 pages
            pixbufs = list(self.imagehandler.get_pixbufs(n))
            rotations = [self._get_pixbuf_rotation(x, True) for x in pixbufs]
            sizes = map(lambda y: tuple(reversed(y[1])) \
                if rotations[y[0]] in (90, 270) else y[1], # 2D only
                enumerate([(x.get_width(), x.get_height()) for x in pixbufs]))

            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 * (n - 1)
                if dasize <= 0:
                    dasize = 1
                zoom_dummy_size[distribution_axis] = dasize
                scaled_sizes = self.zoom.get_zoomed_size(
                    sizes, zoom_dummy_size, distribution_axis)
                self.layout = layout.FiniteLayout(
                    scaled_sizes, viewport_size, constants.MANGA_ORIENTATION
                    if self.is_manga_mode else constants.WESTERN_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(n):
                pixbufs[i] = image_tools.fit_pixbuf_to_rectangle(
                    pixbufs[i], scaled_sizes[i], rotations[i])

            for i in range(n):
                if prefs['horizontal flip']:  # 2D only
                    pixbufs[i] = pixbufs[i].flip(horizontal=True)
                if prefs['vertical flip']:  # 2D only
                    pixbufs[i] = pixbufs[i].flip(horizontal=False)
                pixbufs[i] = self.enhancer.enhance(pixbufs[i])

            for i in range(n):
                image_tools.set_from_pixbuf(self.images[i], pixbufs[i])

            scales = tuple(
                map(
                    lambda x, y: math.sqrt(
                        tools.div(tools.volume(x), tools.volume(y))),
                    scaled_sizes, sizes))

            resolutions = tuple(map(lambda x, y: x + (y, ), sizes, scales))
            if self.is_manga_mode:
                resolutions = tuple(reversed(resolutions))
            self.statusbar.set_resolution(resolutions)

            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(n)
            if smartbg:
                self.set_bg_colour(bg_colour)
            if smartthumbbg:
                self.thumbnailsidebar.change_thumbnail_background_color(
                    bg_colour)

            #self._image_box.window.freeze_updates() # XXX replacement necessary?
            self._main_layout.set_size(*union_scaled_size)
            content_boxes = self.layout.get_content_boxes()
            for i in range(n):
                self._main_layout.move(self.images[i],
                                       *content_boxes[i].get_position())

            for i in range(n):
                self.images[i].show()
            for i in range(n, len(self.images)):
                self.images[i].hide()

            if scroll:
                if at_bottom:
                    self.scroll_to_predefined((constants.SCROLL_TO_END, ) * 2,
                                              constants.LAST_INDEX)
                else:
                    self.scroll_to_predefined(
                        (constants.SCROLL_TO_START, ) * 2,
                        constants.FIRST_INDEX)

            #self._image_box.window.thaw_updates() # XXX replacement necessary?
        else:
            # 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._update_page_information()
        self._waiting_for_redraw = False

        return False
Пример #5
0
    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))
            do_not_transform = [
                image_tools.is_animation(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):
                    if do_not_transform[i]:
                        continue
                    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 = 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):
                if do_not_transform[i]:
                    continue
                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 do_not_transform[i]:
                    continue
                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
Пример #6
0
    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