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)