diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5ec69b050..d0b0147df 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -65,6 +65,7 @@ endif(COMPILER_MSVC)
######################################################################
# Directories
+set(GIFLIB_DIR ${CMAKE_SOURCE_DIR}/third_party/giflib)
set(LIBFREETYPE_DIR ${CMAKE_SOURCE_DIR}/third_party/freetype)
set(LIBJPEG_DIR ${CMAKE_SOURCE_DIR}/third_party/jpeg)
set(LIBPNG_DIR ${CMAKE_SOURCE_DIR}/third_party/libpng)
diff --git a/README.html b/README.html
index 8852d6f5e..3b7004152 100644
--- a/README.html
+++ b/README.html
@@ -274,7 +274,8 @@ data/widgets/*.xml XML files with dialogs
Elias Pschernig
For his excelent bresenham ellipse algorithm and to report some
- nasty keyboard bugs. And his patch to load/save GIF files.
+ nasty keyboard bugs. And his patch to load/save GIF files (used
+ in old versions of ASE).
Elver Loho
@@ -289,6 +290,10 @@ data/widgets/*.xml XML files with dialogs
For his code to quantize RGB images with ordered dither method.
+
Gershon Elber and Eric Raymond
+
+ And everyone who contributed to giflib.
+
Javier Gonzalez
For his AllegroFont wrapper.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 682314926..b0fc4c2ca 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -9,7 +9,7 @@ if(MSVC)
endif(MSVC)
# Third-party libraries
-set(libs3rdparty freetype libart_lgpl loadpng tinyxml)
+set(libs3rdparty freetype libart_lgpl loadpng tinyxml giflib)
if(USE_SHARED_JPEGLIB)
find_package(JPEG)
@@ -48,6 +48,7 @@ set(all_libs aseprite-library gui-lib gfx-lib base-lib ${libs3rdparty} allegro $
# Directories where .h files can be found
include_directories(
. .. ../third_party
+ ${GIFLIB_DIR}/lib
${LIBFREETYPE_DIR}/include
${LIBJPEG_DIR}
${LIBPNG_DIR}
@@ -180,8 +181,6 @@ add_library(aseprite-library
file/file_formats_manager.cpp
file/fli/fli.cpp
file/fli_format.cpp
- file/gif/format.cpp
- file/gif/lzw.cpp
file/gif_format.cpp
file/ico_format.cpp
file/jpeg_format.cpp
diff --git a/src/file/gif/format.cpp b/src/file/gif/format.cpp
deleted file mode 100644
index 3923ccc74..000000000
--- a/src/file/gif/format.cpp
+++ /dev/null
@@ -1,419 +0,0 @@
-#include "config.h"
-
-#include
-#include
-#include
-
-#include
-
-#include "file/gif/format.h"
-#include "file/gif/lzw.h"
-#include "file/file.h"
-
-GIF_ANIMATION* gif_create_animation(int frames_count)
-{
- GIF_ANIMATION* gif = reinterpret_cast(calloc(1, sizeof *gif));
- /* Create frames. */
- gif->frames_count = frames_count;
- gif->frames = reinterpret_cast(calloc(gif->frames_count, sizeof *gif->frames));
- return gif;
-}
-
-/* Destroy a complete gif, including all frames. */
-void
-gif_destroy_animation (GIF_ANIMATION *gif)
-{
- int i;
-
- for (i = 0; i < gif->frames_count; i++)
- {
- GIF_FRAME *frame = gif->frames + i;
-
- if (frame->bitmap_8_bit)
- free (frame->bitmap_8_bit);
- }
- free (gif->frames);
- free (gif);
-}
-
-static void
-write_palette(FILE* file, GIF_PALETTE* palette, int bits)
-{
- int i;
- for (i = 0; i < (1 << bits); i++)
- {
- fputc (palette->colors[i].r, file);
- fputc (palette->colors[i].g, file);
- fputc (palette->colors[i].b, file);
- }
-}
-
-static void
-read_palette (FILE * file, GIF_PALETTE *palette)
-{
- int i;
-
- for (i = 0; i < palette->colors_count; i++)
- {
- palette->colors[i].r = fgetc (file);
- palette->colors[i].g = fgetc (file);
- palette->colors[i].b = fgetc (file);
- }
-}
-
-
-
-static int lzw_read_pixel (int pos, unsigned char *data)
-{
- unsigned char *bitmap = data;
- ASSERT(pos >= 0);
- return bitmap[pos];
-}
-
-
-
-static void lzw_write_pixel (int pos, int c, unsigned char *data)
-{
- unsigned char *bitmap = data;
- ASSERT(pos >= 0);
- ASSERT(c >= 0 && c <= 255);
- bitmap[pos] = c;
-}
-
-
-
-/*
- * Compresses all the frames in the animation and writes to a gif file.
- * Nothing else like extensions or comments is written.
- *
- * Returns 0 on success.
- *
- * Note: All bitmaps must have a color depth of 8.
- */
-int
-gif_save_animation (const char *filename, GIF_ANIMATION *gif,
- void (*progress) (void *, float),
- void *dp)
-{
- int frame;
- int i, j;
- FILE *file;
-
- file = fopen(filename, "wb");
- if (!file)
- return -1;
-
- fwrite ("GIF89a", 1, 6, file);
- fputw (gif->width, file);
- fputw (gif->height, file);
- /* 7 global palette
- * 456 color richness
- * 3 sorted
- * 012 palette bits
- */
- for (i = 1, j = 0; i < gif->palette.colors_count; i *= 2, j++);
- fputc ((j ? 128 : 0) + 64 + 32 + 16 + (j ? j - 1 : 0), file);
- fputc (gif->background_index, file);
- fputc (0, file); /* No aspect ratio. */
-
- if (j)
- write_palette (file, &gif->palette, j);
-
- if (gif->loop != -1)
- /* Loop count extension. */
- {
- fputc (0x21, file); /* Extension Introducer. */
- fputc (0xff, file); /* Application Extension. */
- fputc (11, file); /* Size. */
- fwrite ("NETSCAPE2.0", 1, 11, file);
- fputc (3, file); /* Size. */
- fputc (1, file);
- fputw (gif->loop, file);
- fputc (0, file);
- }
-
- progress(dp, 0.0f);
- for (frame = 0; frame < gif->frames_count; frame++)
- {
- int w = gif->frames[frame].w;
- int h = gif->frames[frame].h;
-
- fputc (0x21, file); /* Extension Introducer. */
- fputc (0xf9, file); /* Graphic Control Extension. */
- fputc (4, file); /* Size. */
- /* Disposal method, and enable transparency. */
- i = gif->frames[frame].disposal_method << 2;
- if (gif->frames[frame].transparent_index != -1)
- i |= 1;
- fputc (i, file);
- fputw (gif->frames[frame].duration, file); /* In 1/100th seconds. */
- if (gif->frames[frame].transparent_index != -1)
- fputc (gif->frames[frame].transparent_index, file); /* Transparent color index. */
- else
- fputc (0, file);
- fputc (0x00, file); /* Terminator. */
-
- fputc (0x2c, file); /* Image Descriptor. */
- fputw (gif->frames[frame].xoff, file);
- fputw (gif->frames[frame].yoff, file);
- fputw (w, file);
- fputw (h, file);
-
- /* 7: local palette
- * 6: interlaced
- * 5: sorted
- * 012: palette bits
- */
-
- for (i = 1, j = 0; i < gif->frames[frame].palette.colors_count; i *= 2, j++);
- fputc ((j ? 128 : 0) + (j ? j - 1 : 0), file);
-
- if (j)
- write_palette (file, &gif->frames[frame].palette, j);
-
- LZW_encode (file, lzw_read_pixel, w * h,
- gif->frames[frame].bitmap_8_bit);
-
- fputc (0x00, file); /* Terminator. */
-
- progress(dp, (float)frame / (float)gif->frames_count);
- }
- progress(dp, 1.0f);
-
- fputc (0x3b, file); /* Trailer. */
-
- fclose (file);
- return 0;
-}
-
-static void
-deinterlace(unsigned char* bmp, int w, int h)
-{
- unsigned char* temp = (unsigned char*)malloc(w * h);
- int y, i = 0;
- for (y = 0; y < h; y += 8) {
- memcpy(temp, bmp + i++ * w, w);
- }
- for (y = 4; y < h; y += 8) {
- memcpy(temp, bmp + i++ * w, w);
- }
- for (y = 2; y < h; y += 4) {
- memcpy(temp, bmp + i++ * w, w);
- }
- for (y = 1; y < h; y += 2) {
- memcpy(temp, bmp + i++ * w, w);
- }
- memcpy(bmp, temp, w * h);
- free(temp);
-}
-
-static GIF_ANIMATION*
-load_object(FILE* file, long size, void(*progress)(void*, float), void* dp)
-{
- int version;
- unsigned char* bmp = NULL;
- int i, j;
- GIF_ANIMATION* gif = reinterpret_cast(calloc(1, sizeof *gif));
- GIF_FRAME frame;
- int have_global_palette = 0;
-
- gif->frames_count = 0;
-
- /* is it really a GIF? */
- if (fgetc (file) != 'G')
- goto error;
- if (fgetc (file) != 'I')
- goto error;
- if (fgetc (file) != 'F')
- goto error;
- if (fgetc (file) != '8')
- goto error;
- /* '7' or '9', for 87a or 89a. */
- version = fgetc (file);
- if (version != '7' && version != '9')
- goto error;
- if (fgetc (file) != 'a')
- goto error;
-
- gif->width = fgetw (file);
- gif->height = fgetw (file);
- i = fgetc (file);
- /* Global color table? */
- if (i & 128)
- gif->palette.colors_count = 1 << ((i & 7) + 1);
- else
- gif->palette.colors_count = 0;
- /* Background color is only valid with a global palette. */
- gif->background_index = fgetc (file);
-
- /* Skip aspect ratio. */
- fseek (file, 1, SEEK_CUR);
-
- if (gif->palette.colors_count)
- {
- read_palette (file, &gif->palette);
- have_global_palette = 1;
- }
-
- progress(dp, 0.0f);
- do
- {
- i = fgetc (file);
- progress(dp, (float)i / (float)size);
-
- switch (i)
- {
- case 0x2c: /* Image Descriptor */
- {
- int w, h;
- int interlaced = 0;
-
- frame.xoff = fgetw (file);
- frame.yoff = fgetw (file);
- w = fgetw (file);
- h = fgetw (file);
- if (w < 1 || h < 1)
- goto error;
- bmp = reinterpret_cast(calloc(w, h));
- if (!bmp)
- goto error;
- frame.w = w;
- frame.h = h;
- i = fgetc (file);
-
- /* Local palette. */
- if (i & 128)
- {
- frame.palette.colors_count = 1 << ((i & 7) + 1);
- read_palette (file, &frame.palette);
- }
- else
- {
- frame.palette.colors_count = 0;
- }
-
- if (i & 64)
- interlaced = 1;
-
- if (ferror (file) ||
- LZW_decode (file, lzw_write_pixel, bmp))
- goto error;
-
- if (interlaced)
- deinterlace (bmp, w, h);
-
- frame.bitmap_8_bit = bmp;
- bmp = NULL;
-
- gif->frames_count++;
- gif->frames = reinterpret_cast
- (realloc(gif->frames,
- gif->frames_count * sizeof *gif->frames));
- gif->frames[gif->frames_count - 1] = frame;
- break;
- }
- case 0x21: /* Extension Introducer. */
- j = fgetc (file); /* Extension Type. */
- i = fgetc (file); /* Size. */
- if (j == 0xf9) /* Graphic Control Extension. */
- {
- /* size must be 4 */
- if (i != 4)
- goto error;
- i = fgetc (file);
- frame.disposal_method = (i >> 2) & 7;
- frame.duration = fgetw (file);
- if (i & 1) /* Transparency? */
- {
- frame.transparent_index = fgetc (file);
- }
- else
- {
- fseek (file, 1, SEEK_CUR);
- frame.transparent_index = -1;
- }
- i = fgetc (file); /* Size. */
- }
- /* Application Extension. */
- else if (j == 0xff)
- {
- if (i == 11)
- {
- char name[12];
- fread (name, 1, 11, file);
- i = fgetc (file); /* Size. */
- name[11] = '\0';
- if (!strcmp (name, "NETSCAPE2.0"))
- {
- if (i == 3)
- {
- j = fgetc (file);
- gif->loop = fgetw (file);
- if (j != 1)
- gif->loop = 0;
- i = fgetc (file); /* Size. */
- }
- }
- }
- }
-
- /* Possibly more blocks until terminator block (0). */
- while (i)
- {
- fseek (file, i, SEEK_CUR);
- i = fgetc (file);
- }
- break;
- case 0x3b:
- /* GIF Trailer. */
- fclose (file);
- progress(dp, 1.0f);
- return gif;
- }
- }
- while (true);
- error:
- if (file)
- fclose (file);
- if (gif)
- gif_destroy_animation (gif);
- if (bmp)
- free (bmp);
- return NULL;
-}
-
-/*
- * Allocates and reads a GIF_ANIMATION structure, filling in all the
- * frames found in the file. On error, nothing is allocated, and NULL is
- * returned. No extensions or comments are read in. If the gif contains
- * a transparency index, and it is no 0, it is swapped with 0 - so index
- * 0 will be the transparent color. There is no way to know when a file
- * contained no transparency originally. Frame duration is specified in
- * 1/100th seconds.
- *
- * All bitmaps will have a color depth of 8.
- */
-GIF_ANIMATION*
-gif_load_animation(const char* filename, void (*progress)(void*, float), void* dp)
-{
- FILE* file;
- GIF_ANIMATION* gif = NULL;
-#if (MAKE_VERSION(4, 2, 1) >= MAKE_VERSION(ALLEGRO_VERSION, \
- ALLEGRO_SUB_VERSION, \
- ALLEGRO_WIP_VERSION))
- int size = file_size(filename);
-#else
- int size = file_size_ex(filename);
-#endif
-
- file = fopen(filename, "rb");
- if (file)
- gif = load_object(file, size, progress, dp);
- return gif;
-}
-
-/* static int */
-/* get_rgbcolor (RGB *rgb) */
-/* { */
-/* return makecol (rgb->r, rgb->g, rgb->b); */
-/* } */
diff --git a/src/file/gif/format.h b/src/file/gif/format.h
deleted file mode 100644
index 00e5a331a..000000000
--- a/src/file/gif/format.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef FILE_GIF_FORMAT_H_INCLUDED
-#define FILE_GIF_FORMAT_H_INCLUDED
-
-#include
-
-typedef struct GIF_ANIMATION GIF_ANIMATION;
-typedef struct GIF_FRAME GIF_FRAME;
-typedef struct GIF_PALETTE GIF_PALETTE;
-
-struct GIF_PALETTE
-{
- int colors_count;
- RGB colors[256];
-};
-
-struct GIF_ANIMATION
-{
- int width, height;
- int frames_count;
- int background_index;
- int loop; /* -1 = no, 0 = forever, 1..65535 = that many times */
- GIF_PALETTE palette;
- GIF_FRAME *frames;
-};
-
-struct GIF_FRAME
-{
- int w, h;
- unsigned char *bitmap_8_bit;
- GIF_PALETTE palette;
- int xoff, yoff;
- int duration; /* in 1/100th seconds */
- int disposal_method; /* 0 = don't care, 1 = keep, 2 = background, 3 = previous */
- int transparent_index;
-};
-
-GIF_ANIMATION *gif_create_animation(int frames_count);
-void gif_destroy_animation(GIF_ANIMATION *gif);
-int gif_save_animation(const char *filename, GIF_ANIMATION *gif,
- void (*progress) (void *, float),
- void *dp);
-GIF_ANIMATION *gif_load_animation(const char *filename,
- void (*progress) (void *, float),
- void *dp);
-
-#endif
-
diff --git a/src/file/gif/lzw.cpp b/src/file/gif/lzw.cpp
deleted file mode 100644
index c2616bceb..000000000
--- a/src/file/gif/lzw.cpp
+++ /dev/null
@@ -1,293 +0,0 @@
-#include "file/gif/lzw.h"
-
-static int
-read_code (FILE * file, unsigned char *buf, int *bit_pos, int bit_size)
-{
- int i;
- int code = 0;
- int pos = 1;
-
- for (i = 0; i < bit_size; i++)
- {
- int byte_pos = (*bit_pos >> 3) & 255;
-
- if (byte_pos == 0)
- {
- int data_len = fgetc (file);
-
- if (data_len == 0)
- {
- //printf ("Fatal. Errorneous GIF stream.\n");
- //abort ();
- return -1;
- }
- fread (buf + 256 - data_len, 1, data_len, file);
- byte_pos = 256 - data_len;
- *bit_pos = byte_pos << 3;
- }
- if (buf[byte_pos] & (1 << (*bit_pos & 7)))
- code += pos;
- pos += pos;
- (*bit_pos)++;
- }
- return code;
-}
-
-static void
-write_code (FILE * file, unsigned char *buf, int *bit_pos, int bit_size, int code)
-{
- int i;
- int pos = 1;
-
- for (i = 0; i < bit_size; i++)
- {
- int byte_pos = *bit_pos >> 3;
-
- if (code & pos)
- buf[byte_pos] |= (1 << (*bit_pos & 7));
- else
- buf[byte_pos] &= ~(1 << (*bit_pos & 7));
- (*bit_pos)++;
- if (*bit_pos == 2040)
- {
- fputc (byte_pos + 1, file);
- fwrite (buf, 1, byte_pos + 1, file);
- *bit_pos = 0;
- }
- pos += pos;
- }
-}
-
-int
-LZW_decode (FILE * file,
- void (*write_pixel)(int pos, int code, unsigned char *data),
- unsigned char *data)
-{
- unsigned char buf[256];
- int orig_bit_size;
- int bit_size;
- int bit_pos;
- int clear_marker;
- int end_marker;
- struct
- {
- int prefix;
- int c;
- int len;
- }
- codes[4096]; /* Maximum bit size is 12. */
- int n;
- int i, prev, code, c;
- int out_pos = 0;
-
- orig_bit_size = fgetc (file);
- n = 2 + (1 << orig_bit_size);
-
- for (i = 0; i < n; i++)
- {
- codes[i].c = i;
- codes[i].len = 0;
- }
-
- clear_marker = n - 2;
- end_marker = n - 1;
-
- bit_size = orig_bit_size + 1;
-
- bit_pos = 0;
-
- /* Expect to read clear code as first code here. */
- prev = read_code (file, buf, &bit_pos, bit_size);
- if (prev == -1 || ferror (file))
- return -1;
- do
- {
- code = read_code (file, buf, &bit_pos, bit_size);
- if (code == -1 || ferror (file))
- return -1;
- if (code == clear_marker)
- {
- bit_size = orig_bit_size;
- n = 1 << bit_size;
- n += 2;
- bit_size++;
- prev = code;
- continue;
- }
-
- if (code == end_marker)
- break;
-
- /* Known code: ok. Else: must be doubled char. */
- if (code < n)
- c = code;
- else
- c = prev;
-
- /* Output the code. */
- out_pos += codes[c].len;
-
- i = 0;
- do
- {
- if (out_pos - i < 0)
- return -1;
-
- write_pixel (out_pos - i, codes[c].c, data);
- if (codes[c].len)
- c = codes[c].prefix;
- else
- break;
- i++;
- }
- while (1);
-
- out_pos++;
-
- /* Unknown code -> must be double char. */
- if (code >= n)
- {
- write_pixel (out_pos, codes[c].c, data);
- out_pos++;
- }
-
- /* Except after clear marker, build new code. */
- if (prev != clear_marker)
- {
- codes[n].prefix = prev;
- codes[n].len = codes[prev].len + 1;
- codes[n].c = codes[c].c;
- n++;
- }
-
- /* Out of bits? Increase. */
- if (n == (1 << bit_size))
- {
- if (bit_size < 12)
- bit_size++;
- }
-
- prev = code;
- }
- while (1);
- return 0;
-}
-
-static int
-get_minimum_bitsize (int (*read_pixel)(int pos, unsigned char *data),
- int size, unsigned char *data)
-{
- int i, max = 0, b = 2;
- for (i = 0; i < size; i++)
- {
- int c = read_pixel (i, data);
- if (c > max)
- max = c;
- }
- while ((1 << b) <= max)
- {
- b++;
- }
- return b;
-}
-
-void
-LZW_encode (FILE * file, int (*read_pixel)(int pos, unsigned char *data),
- int size, unsigned char *data)
-{
- unsigned char buf[256];
- int orig_bit_size;
- int bit_size;
- int bit_pos;
- int clear_marker;
- int end_marker;
- struct
- {
- int prefix;
- int c;
- int len;
- }
- codes[4096]; /* Maximum bit size is 12. */
- int code, prev;
- int in_pos;
- int n, i;
-
- orig_bit_size = get_minimum_bitsize (read_pixel, size, data);
-
- n = 2 + (1 << orig_bit_size);
-
- for (i = 0; i < n; i++)
- {
- codes[i].c = i;
- codes[i].len = 0;
- }
-
- clear_marker = n - 2;
- end_marker = n - 1;
-
- fputc (orig_bit_size, file);
-
- bit_size = orig_bit_size + 1;
-
- bit_pos = 0;
-
- /* Play fair and put a clear marker at the start. */
- write_code (file, buf, &bit_pos, bit_size, clear_marker);
-
- prev = read_pixel (0, data);
-
- for (in_pos = 1; in_pos < size; in_pos++)
- {
- code = read_pixel (in_pos, data);
-
- if (prev != clear_marker)
- {
- /* Search for the code. */
- for (i = end_marker + 1; i < n; i++)
- {
- if (codes[i].prefix == prev && codes[i].c == code)
- {
- code = i;
- break;
- }
- }
-
- /* If not found, add it, and write previous code. */
- if (i == n)
- {
- codes[n].prefix = prev;
- codes[n].len = codes[prev].len + 1;
- codes[n].c = code;
- n++;
-
- write_code (file, buf, &bit_pos, bit_size, prev);
- }
- }
-
- /* Out of bits? Increase. */
- if (n == 1 + (1 << bit_size))
- {
- if (bit_size < 12)
- bit_size++;
- }
-
- /* Too big table? Clear and start over. */
- if (n == 4096)
- {
- write_code (file, buf, &bit_pos, bit_size, clear_marker);
- bit_size = orig_bit_size + 1;
- n = end_marker + 1;
- }
-
- prev = code;
- }
- write_code (file, buf, &bit_pos, bit_size, prev);
- write_code (file, buf, &bit_pos, bit_size, end_marker);
- if (bit_pos)
- {
- int byte_pos = (bit_pos + 7) / 8;
-
- fputc (byte_pos, file);
- fwrite (buf, 1, byte_pos, file);
- }
-}
diff --git a/src/file/gif/lzw.h b/src/file/gif/lzw.h
deleted file mode 100644
index 183613ffd..000000000
--- a/src/file/gif/lzw.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef FILE_GIF_LZW_H_INCLUDED
-#define FILE_GIF_LZW_H_INCLUDED
-
-#include
-
-int LZW_decode (FILE *file, void (*write_pixel)(int pos, int code, unsigned char *data), unsigned char *data);
-void LZW_encode (FILE *file, int (*read_pixel)(int pos, unsigned char *data), int size, unsigned char *data);
-
-#endif
diff --git a/src/file/gif_format.cpp b/src/file/gif_format.cpp
index c5b431904..af1aa7ca6 100644
--- a/src/file/gif_format.cpp
+++ b/src/file/gif_format.cpp
@@ -14,30 +14,26 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * gif.c - written by Elias Pschernig.
*/
-/* You can uncomment this if do you want to load the internal GIF
- structure to watch if the low-level writer/reader are working good */
-/* #define LOAD_GIF_STRUCTURE */
-
#include "config.h"
-#include
-#include
-
-#include "gui/jbase.h"
-
+#include "base/shared_ptr.h"
#include "file/file.h"
#include "file/file_format.h"
-#include "file/gif/format.h"
-#include "modules/palettes.h"
#include "raster/raster.h"
#include "util/autocrop.h"
+#include "gui/jinete.h"
+#include "modules/gui.h"
-#include
-#include
+#include
+
+enum DisposalMethod {
+ DISPOSAL_METHOD_NONE,
+ DISPOSAL_METHOD_DO_NOT_DISPOSE,
+ DISPOSAL_METHOD_RESTORE_BGCOLOR,
+ DISPOSAL_METHOD_RESTORE_PREVIOUS,
+};
class GifFormat : public FileFormat
{
@@ -47,6 +43,10 @@ class GifFormat : public FileFormat
return
FILE_SUPPORT_LOAD |
FILE_SUPPORT_SAVE |
+ FILE_SUPPORT_RGB |
+ FILE_SUPPORT_RGBA |
+ FILE_SUPPORT_GRAY |
+ FILE_SUPPORT_GRAYA |
FILE_SUPPORT_INDEXED |
FILE_SUPPORT_FRAMES |
FILE_SUPPORT_PALETTES;
@@ -61,392 +61,445 @@ FileFormat* CreateGifFormat()
return new GifFormat;
}
-static void render_gif_frame(GIF_FRAME *frame, Image *image,
- int x, int y, int w, int h)
+class DGifDeleter
{
- int u, v, c;
-
- for (v = 0; v < h; v++) {
- for (u = 0; u < w; u++) {
- c = frame->bitmap_8_bit[v * w + u];
- /* TODO fix this!!! */
- /* if (c == frame->transparent_index) */
- /* c = 0; */
- if (c != frame->transparent_index)
- image_putpixel(image, x+u, y+v, c);
- }
+public:
+ static void destroy(GifFileType* gif_file)
+ {
+ DGifCloseFile(gif_file);
}
-}
+};
+
+class EGifDeleter
+{
+public:
+ static void destroy(GifFileType* gif_file)
+ {
+ EGifCloseFile(gif_file);
+ }
+};
+
+static int interlaced_offset[] = { 0, 4, 2, 1 };
+static int interlaced_jumps[] = { 8, 8, 4, 2 };
bool GifFormat::onLoad(FileOp* fop)
{
- GIF_ANIMATION *gif = NULL;
- Sprite *sprite = NULL;
- LayerImage *layer = NULL;
- Cel *cel = NULL;
- Image *image = NULL;
- Image *current_image_old = NULL;
- Image *current_image = NULL;
- Palette *opal = NULL;
- Palette *npal = NULL;
- bool ret = false;
- int i, c;
-
- gif = gif_load_animation(fop->filename.c_str(),
- reinterpret_cast(fop_progress), fop);
- if (!gif) {
- fop_error(fop, "Error loading GIF file.\n");
- goto error;
+ SharedPtr gif_file(DGifOpenFileName(fop->filename.c_str()));
+ if (!gif_file) {
+ fop_error(fop, "Error loading GIF header.\n");
+ return false;
}
- current_image = image_new(IMAGE_INDEXED, gif->width, gif->height);
- current_image_old = image_new(IMAGE_INDEXED, gif->width, gif->height);
- opal = new Palette(0, 256);
- npal = new Palette(0, 256);
- if (!current_image || !current_image_old || !opal || !npal) {
- fop_error(fop, "Error creating temporary image.\n");
- goto error;
- }
+ int sprite_w = gif_file->SWidth;
+ int sprite_h = gif_file->SHeight;
- sprite = new Sprite(IMAGE_INDEXED, gif->width, gif->height, 256);
- if (!sprite) {
- fop_error(fop, "Error creating sprite.\n");
- goto error;
- }
+ // The previous image is used to support the special disposal method
+ // of GIF frames DISPOSAL_METHOD_RESTORE_PREVIOUS (number 3 in
+ // Graphics Extension)
+ SharedPtr current_image(image_new(IMAGE_RGB, sprite_w, sprite_h));
+ SharedPtr previous_image(image_new(IMAGE_RGB, sprite_w, sprite_h));
+ SharedPtr current_palette(new Palette(0, 256));
+ SharedPtr previous_palette(new Palette(0, 256));
- sprite->setTotalFrames(gif->frames_count);
+ Sprite* sprite = NULL;
+ try {
+ // Create the sprite with the GIF dimension
+ sprite = new Sprite(IMAGE_RGB, sprite_w, sprite_h, 256);
- layer = new LayerImage(sprite);
- if (!layer) {
- fop_error(fop, "Error creating main layer.\n");
- goto error;
- }
+ // Create the main layer
+ LayerImage* layer = new LayerImage(sprite);
+ sprite->getFolder()->add_layer(layer);
- sprite->getFolder()->add_layer(layer);
+ // If the GIF image has a global palette, it has a valid
+ // background color (so the GIF is not transparent).
+ int bgcolor_index;
+ if (gif_file->SColorMap != NULL) {
+ bgcolor_index = gif_file->SBackGroundColor;
- // If the GIF image has a global palette (colors_count > 0), it has
- // a valid background_index color.
- if (gif->palette.colors_count > 0)
- layer->configureAsBackground();
- else
- gif->background_index = 0;
-
- image_clear(current_image, gif->background_index);
- image_clear(current_image_old, gif->background_index);
-
- for (i = 0; i < gif->frames_count; i++) {
- GIF_PALETTE *pal = &gif->frames[i].palette;
-
- /* Use global palette if no local. */
- if (pal->colors_count == 0)
- pal = &gif->palette;
-
- /* 1/100th seconds to milliseconds */
- sprite->setFrameDuration(i, gif->frames[i].duration*10);
-
- /* make the palette */
- for (c=0; ccolors_count; c++) {
- npal->setEntry(c, _rgba(pal->colors[c].r,
- pal->colors[c].g,
- pal->colors[c].b, 255));
- }
- if (i == 0)
- for (; c<256; c++)
- npal->setEntry(c, _rgba(0, 0, 0, 255));
- else
- for (; c<256; c++) {
- npal->setEntry(c, opal->getEntry(c));
+ // Setup the first palette using the global color map.
+ ColorMapObject* colormap = gif_file->SColorMap;
+ for (int i=0; iColorCount; ++i) {
+ current_palette->setEntry(i, _rgba(colormap->Colors[i].Red,
+ colormap->Colors[i].Green,
+ colormap->Colors[i].Blue, 255));
}
-
- /* first frame or palette changes */
- if (i == 0 || opal->countDiff(npal, NULL, NULL)) {
- npal->setFrame(i);
- sprite->setPalette(npal, true);
+ }
+ else {
+ bgcolor_index = 0;
}
- /* copy new palette to old palette */
- npal->copyColorsTo(opal);
+ // Clear both images with the transparent color (alpha = 0).
+ image_clear(current_image, _rgba(0, 0, 0, 0));
+ image_clear(previous_image, _rgba(0, 0, 0, 0));
- cel = cel_new(i, 0);
- image = image_new(IMAGE_INDEXED,
-#ifdef LOAD_GIF_STRUCTURE
- gif->frames[i].w,
- gif->frames[i].h
-#else
- sprite->getWidth(), sprite->getHeight()
-#endif
- );
- if (!cel || !image) {
- if (cel) cel_free(cel);
- if (image) image_free(image);
- fop_error(fop, "Error creating cel %d.\n", i);
- break;
- }
+ // Scan the content of the GIF file (read record by record)
+ GifRecordType record_type;
+ int frame_num = 0;
+ DisposalMethod disposal_method = DISPOSAL_METHOD_NONE;
+ int transparent_index = -1;
+ int frame_delay = -1;
+ do {
+ if (DGifGetRecordType(gif_file, &record_type) == GIF_ERROR)
+ throw ase_exception("Invalid GIF record in file.\n");
- cel_set_position(cel,
-#ifdef LOAD_GIF_STRUCTURE
- gif->frames[i].xoff,
- gif->frames[i].yoff
-#else
- 0, 0
-#endif
- );
+ switch (record_type) {
- image_copy(current_image_old, current_image, 0, 0);
- render_gif_frame(gif->frames+i, current_image,
- gif->frames[i].xoff,
- gif->frames[i].yoff,
- gif->frames[i].w,
- gif->frames[i].h);
+ case IMAGE_DESC_RECORD_TYPE: {
+ if (DGifGetImageDesc(gif_file) == GIF_ERROR)
+ throw ase_exception("Invalid GIF image descriptor.\n");
- image_copy(image, current_image,
-#ifdef LOAD_GIF_STRUCTURE
- -gif->frames[i].xoff, -gif->frames[i].yoff
-#else
- 0, 0
-#endif
- );
- cel->image = sprite->getStock()->addImage(image);
- layer->addCel(cel);
+ // These are the bounds of the image to read.
+ int frame_x = gif_file->Image.Left;
+ int frame_y = gif_file->Image.Top;
+ int frame_w = gif_file->Image.Width;
+ int frame_h = gif_file->Image.Height;
-#ifdef LOAD_GIF_STRUCTURE
- /* when load the GIF structure, the disposal method is ever
- clear the old image */
- image_rectfill(current_image,
- gif->frames[i].xoff,
- gif->frames[i].yoff,
- gif->frames[i].xoff+gif->frames[i].w-1,
- gif->frames[i].yoff+gif->frames[i].h-1,
- gif->background_index);
-#else
- /* disposal method */
- switch (gif->frames[i].disposal_method) {
+ if (frame_x < 0 || frame_y < 0 ||
+ frame_x + frame_w > sprite_w ||
+ frame_y + frame_h > sprite_h)
+ throw ase_exception("Image %d is out of sprite bounds.\n", frame_num);
- case 0: /* no disposal specified */
- case 1: /* do not dispose */
- /* do nothing */
- break;
+ // Add a new frame in the sprite.
+ sprite->setTotalFrames(frame_num+1);
- case 2: /* restore to background color */
- case 3: /* restore to previous */
- image_rectfill(current_image,
- gif->frames[i].xoff,
- gif->frames[i].yoff,
- gif->frames[i].xoff+gif->frames[i].w-1,
- gif->frames[i].yoff+gif->frames[i].h-1,
- gif->background_index);
+ // Set frame delay (1/100th seconds to milliseconds)
+ if (frame_delay >= 0)
+ sprite->setFrameDuration(frame_num, frame_delay*10);
- /* draw old frame */
- if (gif->frames[i].disposal_method == 3 && i > 0) {
- Image *tmp = image_crop(current_image_old,
- gif->frames[i].xoff,
- gif->frames[i].yoff,
- gif->frames[i].w,
- gif->frames[i].h, 0);
- if (tmp) {
- image_copy(current_image, tmp,
- gif->frames[i].xoff,
- gif->frames[i].yoff);
- image_free(tmp);
+ // Update palette for this frame (the first frame always need a palette).
+ if (gif_file->Image.ColorMap) {
+ ColorMapObject* colormap = gif_file->Image.ColorMap;
+ for (int i=0; iColorCount; ++i) {
+ current_palette->setEntry(i, _rgba(colormap->Colors[i].Red,
+ colormap->Colors[i].Green,
+ colormap->Colors[i].Blue, 255));
+ }
}
+
+ if (frame_num == 0 || previous_palette->countDiff(current_palette, NULL, NULL)) {
+ current_palette->setFrame(frame_num);
+ sprite->setPalette(current_palette, true);
+
+ current_palette->copyColorsTo(previous_palette);
+ }
+
+ // Create a temporary image to load frame pixels.
+ SharedPtr frame_image(image_new(IMAGE_INDEXED, frame_w, frame_h));
+ IndexedTraits::address_t addr;
+
+ if (gif_file->Image.Interlace) {
+ // Need to perform 4 passes on the images.
+ for (int i=0; i<4; ++i)
+ for (int y = interlaced_offset[i]; y < frame_h; y += interlaced_jumps[i]) {
+ addr = image_address_fast(frame_image, 0, y);
+ if (DGifGetLine(gif_file, addr, frame_w) == GIF_ERROR)
+ throw ase_exception("Invalid interlaced image data.");
+ }
+ }
+ else {
+ for (int y = 0; y < frame_h; ++y) {
+ addr = image_address_fast(frame_image, 0, y);
+ if (DGifGetLine(gif_file, addr, frame_w) == GIF_ERROR)
+ throw ase_exception("Invalid image data (%d).\n", GifLastError());
+ }
+ }
+
+ // Convert the indexed image to RGB
+ for (int y = 0; y < frame_h; ++y)
+ for (int x = 0; x < frame_w; ++x) {
+ int pixel_index = image_getpixel_fast(frame_image, x, y);
+ if (pixel_index != transparent_index)
+ image_putpixel_fast(current_image,
+ frame_x + x,
+ frame_y + y,
+ current_palette->getEntry(pixel_index));
+ }
+
+ // Create a new Cel and a image with the whole content of "current_image"
+ Cel* cel = cel_new(frame_num, 0);
+ try {
+ Image* cel_image = image_new_copy(current_image);
+ try {
+ // Add the image in the sprite's stock and update the cel's
+ // reference to the new stock's image.
+ cel->image = sprite->getStock()->addImage(cel_image);
+ }
+ catch (...) {
+ delete cel_image;
+ throw;
+ }
+
+ layer->addCel(cel);
+ }
+ catch (...) {
+ delete cel;
+ throw;
+ }
+
+ // The current_image was already copied to represent the
+ // current frame (frame_num), so now we have to clear the
+ // area occupied by frame_image using the desired disposal
+ // method.
+ switch (disposal_method) {
+
+ case DISPOSAL_METHOD_NONE:
+ case DISPOSAL_METHOD_DO_NOT_DISPOSE:
+ // Do nothing
+ break;
+
+ case DISPOSAL_METHOD_RESTORE_BGCOLOR:
+ image_rectfill(current_image,
+ frame_x, frame_y,
+ frame_x+frame_w-1,
+ frame_y+frame_h-1,
+ _rgba(0, 0, 0, 0));
+ break;
+
+ case DISPOSAL_METHOD_RESTORE_PREVIOUS:
+ image_copy(current_image, previous_image, 0, 0);
+ break;
+ }
+
+ // Update previous_image with current_image only if the
+ // disposal method is not "restore previous" (which means
+ // that we have already updated current_image from
+ // previous_image).
+ if (disposal_method != DISPOSAL_METHOD_RESTORE_PREVIOUS)
+ image_copy(previous_image, current_image, 0, 0);
+
+ ++frame_num;
+
+ disposal_method = DISPOSAL_METHOD_NONE;
+ transparent_index = -1;
+ frame_delay = -1;
+ break;
}
- break;
- }
-#endif
+
+ case EXTENSION_RECORD_TYPE: {
+ GifByteType* extension;
+ int ext_code;
+
+ if (DGifGetExtension(gif_file, &ext_code, &extension) == GIF_ERROR)
+ throw ase_exception("Invalid GIF extension record.\n");
+
+ if (ext_code == GRAPHICS_EXT_FUNC_CODE) {
+ if (extension[0] >= 4) {
+ disposal_method = (DisposalMethod)((extension[1] >> 2) & 7);
+ transparent_index = (extension[1] & 1) ? extension[4]: -1;
+ frame_delay = (extension[3] << 8) | extension[2];
+
+ TRACE("Disposal method: %d\nTransparent index: %d\nFrame delay: %d\n",
+ disposal_method, transparent_index, frame_delay);
+ }
+ }
+
+ while (extension != NULL) {
+ if (DGifGetExtensionNext(gif_file, &extension) == GIF_ERROR)
+ throw ase_exception("Invalid GIF extension record.\n");
+ }
+ break;
+ }
+
+ case TERMINATE_RECORD_TYPE:
+ break;
+
+ default:
+ break;
+ }
+ } while (record_type != TERMINATE_RECORD_TYPE);
+ }
+ catch (...) {
+ delete sprite;
+ throw;
}
fop->sprite = sprite;
sprite = NULL;
- ret = true;
-
-error:;
- if (gif) gif_destroy_animation(gif);
- if (current_image) image_free(current_image);
- if (current_image_old) image_free(current_image_old);
- if (npal) delete npal;
- if (opal) delete opal;
- delete sprite;
- return ret;
+ return true;
}
-/* TODO: find the colors that are used and resort the palette */
-static int max_used_index(Image *image)
-{
- unsigned char *address = image->dat;
- int size = image->w*image->h;
- register int i, c, max = 0;
- for (i=0; i gif_file(EGifOpenFileName(fop->filename.c_str(), 0));
+ if (!gif_file)
+ throw ase_exception("Error creating GIF file.\n");
+
Sprite *sprite = fop->sprite;
- GIF_ANIMATION *gif;
- int x1, y1, x2, y2;
+ int sprite_w = sprite->getWidth();
+ int sprite_h = sprite->getHeight();
+ int sprite_imgtype = sprite->getImgType();
+ int background_color = 0;
+ bool interlace = false;
+ int loop = 0;
+ int transparent_index = (sprite->getBackgroundLayer() == NULL) ? 0: -1;
+
+ Palette* current_palette = sprite->getPalette(0);
+ Palette* previous_palette = current_palette;
+ ColorMapObject* color_map = MakeMapObject(current_palette->size(), NULL);
+ for (int i = 0; i < current_palette->size(); ++i) {
+ color_map->Colors[i].Red = _rgba_getr(current_palette->getEntry(i));
+ color_map->Colors[i].Green = _rgba_getg(current_palette->getEntry(i));
+ color_map->Colors[i].Blue = _rgba_getb(current_palette->getEntry(i));
+ }
+
+ if (EGifPutScreenDesc(gif_file, sprite_w, sprite_h,
+ color_map->BitsPerPixel,
+ background_color, color_map) == GIF_ERROR)
+ throw ase_exception("Error writing GIF header.\n");
+
+ SharedPtr buffer_image;
+ SharedPtr current_image(image_new(IMAGE_INDEXED, sprite_w, sprite_h));
+ SharedPtr previous_image(image_new(IMAGE_INDEXED, sprite_w, sprite_h));
+ int frame_x, frame_y, frame_w, frame_h;
int u1, v1, u2, v2;
int i1, j1, i2, j2;
- Image *bmp, *old;
- Palette *opal, *npal;
- int c, i, x, y;
- int w, h;
- int ret;
- bmp = image_new(IMAGE_INDEXED, sprite->getWidth(), sprite->getHeight());
- old = image_new(IMAGE_INDEXED, sprite->getWidth(), sprite->getHeight());
- if (!bmp || !old) {
- if (bmp) image_free(bmp);
- if (old) image_free(old);
- fop_error(fop, "Not enough memory for temporary bitmaps.\n");
- return false;
- }
+ // If the sprite is not Indexed type, we will need a temporary
+ // buffer to render the full RGB or Grayscale sprite.
+ if (sprite_imgtype != IMAGE_INDEXED)
+ buffer_image.reset(image_new(sprite_imgtype, sprite_w, sprite_h));
- gif = gif_create_animation(sprite->getTotalFrames());
- if (!gif) {
- image_free(bmp);
- image_free(old);
- fop_error(fop, "Error creating GIF structure.\n");
- return false;
- }
+ image_clear(current_image, background_color);
+ image_clear(previous_image, background_color);
- gif->width = sprite->getWidth();
- gif->height = sprite->getHeight();
- gif->background_index = 0;
+ for (int frame_num=0; frame_numgetTotalFrames(); ++frame_num) {
+ current_palette = sprite->getPalette(frame_num);
- /* defaults:
- - disposal method: restore to background
- - transparent pixel: 0 */
- for (i = 0; i < gif->frames_count; i++) {
- gif->frames[i].disposal_method = 2;
- gif->frames[i].transparent_index = 0;
- }
+ // If the sprite is RGB or Grayscale, we must to convert it to Indexed on the fly.
+ if (sprite_imgtype != IMAGE_INDEXED) {
+ image_clear(buffer_image, 0);
+ layer_render(sprite->getFolder(), buffer_image, 0, 0, frame_num);
- /* avoid compilation warnings */
- x1 = y1 = x2 = y2 = 0;
+ switch (sprite_imgtype) {
- opal = NULL;
- for (i=0; igetTotalFrames(); ++i) {
- /* frame palette */
- npal = sprite->getPalette(i);
+ // Convert the RGB image to Indexed
+ case IMAGE_RGB:
+ for (int y = 0; y < sprite_h; ++y)
+ for (int x = 0; x < sprite_w; ++x) {
+ ase_uint32 pixel_value = image_getpixel_fast(buffer_image, x, y);
+ image_putpixel_fast(current_image, x, y,
+ (_rgba_geta(pixel_value) >= 128) ?
+ current_palette->findBestfit(_rgba_getr(pixel_value),
+ _rgba_getg(pixel_value),
+ _rgba_getb(pixel_value)):
+ transparent_index);
+ }
+ break;
- /* render the frame in the bitmap */
- image_clear(bmp, 0);
- layer_render(sprite->getFolder(), bmp, 0, 0, i);
-
- /* first frame */
- if (i == 0) {
- /* TODO: don't use 256 colors, but only as much as needed. */
- gif->palette.colors_count = max_used_index(bmp)+1;
- for (c=0; cpalette.colors_count; ++c) {
- gif->palette.colors[c].r = _rgba_getr(npal->getEntry(c));
- gif->palette.colors[c].g = _rgba_getg(npal->getEntry(c));
- gif->palette.colors[c].b = _rgba_getb(npal->getEntry(c));
+ // Convert the Grayscale image to Indexed
+ case IMAGE_GRAYSCALE:
+ for (int y = 0; y < sprite_h; ++y)
+ for (int x = 0; x < sprite_w; ++x) {
+ ase_uint16 pixel_value = image_getpixel_fast(buffer_image, x, y);
+ image_putpixel_fast(current_image, x, y,
+ (_graya_geta(pixel_value) >= 128) ?
+ current_palette->findBestfit(_graya_getv(pixel_value),
+ _graya_getv(pixel_value),
+ _graya_getv(pixel_value)):
+ transparent_index);
+ }
+ break;
}
-
- /* render all */
- x1 = y1 = 0;
- x2 = bmp->w-1;
- y2 = bmp->h-1;
}
- /* other frames */
+ // If the sprite is Indexed, we can render directly into "current_image".
else {
-#if 0 /* TODO remove this (doesn't work right for sprites with trans-color */
- /* get the rectangle where start differences with the previous frame */
- if (!get_shrink_rect2(&u1, &v1, &u2, &v2, bmp, old)) {
- /* there aren't differences with the previous image? */
- /* TODO: in this case, we should remove this frame, and
- expand the previous frame to the time that this frame is using */
- x1 = gif->frames[i-1].xoff;
- y1 = gif->frames[i-1].yoff;
- x2 = gif->frames[i-1].xoff+gif->frames[i-1].w-1;
- y2 = gif->frames[i-1].yoff+gif->frames[i-1].h-1;
- /* change previous frame disposal method: left in place */
- gif->frames[i-1].disposal_method = 1;
- }
- /* check the minimal area with the background color */
- else if (get_shrink_rect(&x1, &y1, &x2, &y2, bmp,
- gif->background_index)) {
- int a1 = (x2-x1+1) * (y2-y1+1);
- int a2 = (u2-u1+1) * (v2-v1+1);
- /* differences with previous is better that differences with
- background color? */
- if (a2 < a1) {
- x1 = u1;
- y1 = v1;
- x2 = u2;
- y2 = v2;
- /* change previous frame disposal method: left in place */
- gif->frames[i-1].disposal_method = 1;
- /* change this frame to non-transparent */
- gif->frames[i].transparent_index = -1;
- }
- }
- /* the new image is different of the old one, but is all cleared
- with the background color */
- else {
- x1 = u1;
- y1 = v1;
- x2 = u2;
- y2 = v2;
- /* change this frame to non-transparent */
- gif->frames[i].transparent_index = -1;
- }
-#else
- /* get the rectangle where start differences with the previous frame */
- if (get_shrink_rect2(&u1, &v1, &u2, &v2, bmp, old)) {
- /* check the minimal area with the background color */
- if (get_shrink_rect(&i1, &j1, &i2, &j2, bmp, gif->background_index)) {
- x1 = MIN(u1, i1);
- y1 = MIN(v1, j1);
- x2 = MAX(u2, i2);
- y2 = MAX(v2, j2);
- }
- }
-#endif
+ layer_render(sprite->getFolder(), current_image, 0, 0, frame_num);
+ }
- /* palette changes */
- if (opal != npal) {
- gif->frames[i].palette.colors_count = max_used_index(bmp)+1;
- for (c=0; cframes[i].palette.colors_count; ++c) {
- gif->frames[i].palette.colors[c].r = _rgba_getr(npal->getEntry(c));
- gif->frames[i].palette.colors[c].g = _rgba_getg(npal->getEntry(c));
- gif->frames[i].palette.colors[c].b = _rgba_getb(npal->getEntry(c));
+ if (frame_num == 0) {
+ frame_x = 0;
+ frame_y = 0;
+ frame_w = sprite->getWidth();
+ frame_h = sprite->getHeight();
+ }
+ else {
+ // Get the rectangle where start differences with the previous frame.
+ if (get_shrink_rect2(&u1, &v1, &u2, &v2, current_image, previous_image)) {
+ // Check the minimal area with the background color.
+ if (get_shrink_rect(&i1, &j1, &i2, &j2, current_image, background_color)) {
+ frame_x = MIN(u1, i1);
+ frame_y = MIN(v1, j1);
+ frame_w = MAX(u2, i2) - MIN(u1, i1) + 1;
+ frame_h = MAX(v2, j2) - MIN(v1, j1) + 1;
}
}
}
- w = x2-x1+1;
- h = y2-y1+1;
+ // Specify loop extension.
+ if (frame_num == 0 && loop >= 0) {
+ unsigned char extension_bytes[11];
- gif->frames[i].w = w;
- gif->frames[i].h = h;
- gif->frames[i].bitmap_8_bit = (unsigned char*)calloc(w, h);
- gif->frames[i].xoff = x1;
- gif->frames[i].yoff = y1;
+ memcpy(extension_bytes, "NETSCAPE2.0", 11);
+ if (EGifPutExtensionFirst(gif_file, APPLICATION_EXT_FUNC_CODE, 11, extension_bytes) == GIF_ERROR)
+ throw ase_exception("Error writing GIF graphics extension record for frame %d.\n", frame_num);
- /* milliseconds to 1/100th seconds */
- gif->frames[i].duration = sprite->getFrameDuration(i)/10;
+ extension_bytes[0] = 1;
+ extension_bytes[1] = (loop & 0xff);
+ extension_bytes[2] = (loop >> 8) & 0xff;
+ if (EGifPutExtensionNext(gif_file, APPLICATION_EXT_FUNC_CODE, 3, extension_bytes) == GIF_ERROR)
+ throw ase_exception("Error writing GIF graphics extension record for frame %d.\n", frame_num);
- /* image data */
- for (y = 0; y < h; y++) {
- for (x = 0; x < w; x++) {
- gif->frames[i].bitmap_8_bit[x + y * w] =
- image_getpixel(bmp, x1+x, y1+y);
+ if (EGifPutExtensionLast(gif_file, APPLICATION_EXT_FUNC_CODE, 0, NULL) == GIF_ERROR)
+ throw ase_exception("Error writing GIF graphics extension record for frame %d.\n", frame_num);
+ }
+
+ // Write graphics extension record (to save the duration of the
+ // frame and maybe the transparency index).
+ {
+ unsigned char extension_bytes[5];
+ int disposal_method = (sprite->getBackgroundLayer() == NULL) ? 2: 1;
+ int frame_delay = sprite->getFrameDuration(frame_num) / 10;
+
+ extension_bytes[0] = (((disposal_method & 7) << 2) |
+ (transparent_index >= 0 ? 1: 0));
+ extension_bytes[1] = (frame_delay & 0xff);
+ extension_bytes[2] = (frame_delay >> 8) & 0xff;
+ extension_bytes[3] = transparent_index;
+
+ if (EGifPutExtension(gif_file, GRAPHICS_EXT_FUNC_CODE, 4, extension_bytes) == GIF_ERROR)
+ throw ase_exception("Error writing GIF graphics extension record for frame %d.\n", frame_num);
+ }
+
+ // Image color map
+ ColorMapObject* image_color_map = NULL;
+ if (current_palette != previous_palette) {
+ image_color_map = MakeMapObject(current_palette->size(), NULL);
+ for (int i = 0; i < current_palette->size(); ++i) {
+ image_color_map->Colors[i].Red = _rgba_getr(current_palette->getEntry(i));
+ image_color_map->Colors[i].Green = _rgba_getg(current_palette->getEntry(i));
+ image_color_map->Colors[i].Blue = _rgba_getb(current_palette->getEntry(i));
+ }
+ previous_palette = current_palette;
+ }
+
+ // Write the image record.
+ if (EGifPutImageDesc(gif_file,
+ frame_x, frame_y,
+ frame_w, frame_h, interlace ? 1: 0,
+ image_color_map) == GIF_ERROR)
+ throw ase_exception("Error writing GIF frame %d.\n", frame_num);
+
+ // Write the image data (pixels).
+ if (interlace) {
+ // Need to perform 4 passes on the images.
+ for (int i=0; i<4; ++i)
+ for (int y = interlaced_offset[i]; y < frame_h; y += interlaced_jumps[i]) {
+ IndexedTraits::address_t addr = image_address_fast(current_image, frame_x, frame_y + y);
+ if (EGifPutLine(gif_file, addr, frame_w) == GIF_ERROR)
+ throw ase_exception("Error writing GIF image scanlines for frame %d.\n", frame_num);
+ }
+ }
+ else {
+ // Write all image scanlines (not interlaced in this case).
+ for (int y = 0; y < frame_h; ++y) {
+ IndexedTraits::address_t addr = image_address_fast(current_image, frame_x, frame_y + y);
+ if (EGifPutLine(gif_file, addr, frame_w) == GIF_ERROR)
+ throw ase_exception("Error writing GIF image scanlines for frame %d.\n", frame_num);
}
}
- /* update the old image and color-map to the new ones to compare later */
- image_copy(old, bmp, 0, 0);
- opal = npal;
+ image_copy(previous_image, current_image, 0, 0);
}
- ret = gif_save_animation(fop->filename.c_str(), gif,
- reinterpret_cast(fop_progress), fop);
- gif_destroy_animation(gif);
- return ret == 0 ? true: false;
+ return true;
}
diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt
index 4a9ba5fa2..319c935cf 100644
--- a/third_party/CMakeLists.txt
+++ b/third_party/CMakeLists.txt
@@ -21,6 +21,7 @@ if(NOT USE_SHARED_LIBPNG)
add_subdirectory(libpng)
endif()
+add_subdirectory(giflib)
add_subdirectory(gtest)
add_subdirectory(freetype)
add_subdirectory(libart_lgpl)