Example #1
0
    def get_palette_loop_timer(self, animation, direction, palettes):
        pose_list = self.get_pose_list(animation, direction)
        returnvalue = 1  # default
        for pose_number in range(len(pose_list)):
            tile_list = pose_list[pose_number]["tiles"][::-1]
            tile_list += self.get_supplemental_tiles(animation, direction,
                                                     pose_number, palettes, 0)
            for tile_info in tile_list:
                new_palette = None
                for possible_palette_info_location in [
                        pose_list[pose_number], tile_info
                ]:
                    if "palette" in possible_palette_info_location:
                        palette_info_location = possible_palette_info_location
                        new_palette = palette_info_location["palette"]
                if new_palette:
                    palettes.append(new_palette)

                this_palette_duration = max(
                    1, self.get_palette_duration(palettes))
                returnvalue = common.lcm(returnvalue, this_palette_duration)
        return returnvalue
    def export_animation_as_gif(self, filename, zoom=1, speed=1):
        #TODO: factor out common code with the collage function
        GIF_MAX_FRAMERATE = 100.0  #GIF format in theory supports 100 FPS, but some programs display at 50 FPS
        ACTUAL_FRAMERATE = 60.0

        image_list = []

        current_animation, displayed_direction, _, palette_info, _, pose_list = self.get_image_arguments_from_frame_number(
            self.frame_getter())

        if current_animation:
            for pose_number in range(len(pose_list)):
                image_list.append(
                    self.sprite.get_image(current_animation,
                                          displayed_direction, pose_number,
                                          palette_info, 0))

            #TODO: Factor this and the corresponding code in layoutlib.py out to common.py
            x_min = min([origin[0] for image, origin in image_list])
            x_max = max(
                [image.size[0] + origin[0] for image, origin in image_list])
            y_min = min([origin[1] for image, origin in image_list])
            y_max = max(
                [image.size[1] + origin[1] for image, origin in image_list])

            gif_x_size = x_max - x_min
            gif_y_size = y_max - y_min

            palette_duration = self.sprite.get_palette_loop_timer(
                current_animation, displayed_direction, palette_info)
            animation_duration = sum([pose["frames"] for pose in pose_list])
            full_animation_duration = common.lcm(palette_duration,
                                                 animation_duration)

            frames = []
            durations = []

            for frame_number in range(full_animation_duration):
                _, _, _, palette_info, _, _ = self.get_image_arguments_from_frame_number(
                    frame_number)
                pose_number = self.get_pose_number_from_frames(frame_number)
                image, origin = self.sprite.get_image(current_animation,
                                                      displayed_direction,
                                                      pose_number,
                                                      palette_info,
                                                      frame_number)

                this_frame = Image.new("RGBA", (gif_x_size, gif_y_size))
                this_frame.paste(image, (origin[0] - x_min, origin[1] - y_min),
                                 image)

                #PIL makes transparency so difficult...
                alpha = this_frame.split()[3]
                #reserve color number 255
                this_frame = this_frame.convert('P',
                                                palette=Image.ADAPTIVE,
                                                colors=255)

                mask = Image.eval(alpha, lambda a: 255 if a == 0 else 0)
                #apply color number 255
                this_frame.paste(255, mask)

                new_size = tuple(int(dim * zoom) for dim in this_frame.size)
                this_frame = this_frame.resize(new_size,
                                               resample=Image.NEAREST)

                if frames and common.equal(this_frame, frames[-1]):
                    durations[-1] += 1
                else:
                    frames.append(this_frame)
                    durations.append(1)

            gif_durations = [
                1000.0 *  #millisecond conversion
                max(1.0 / GIF_MAX_FRAMERATE,
                    round(duration / (speed * ACTUAL_FRAMERATE), 2))
                for duration in durations
            ]

            if len(frames) > 1:
                frames[0].save(filename,
                               format='GIF',
                               append_images=frames[1:],
                               save_all=True,
                               transparency=255,
                               disposal=2,
                               duration=gif_durations,
                               loop=0,
                               optimize=False)
                return True
            elif len(frames) == 1:
                frames[0].save(filename)
            else:
                return False
        else:
            return False