This is an automated email from the git hooks/post-receive script. thansen pushed a commit to branch master in repository aseprite.
commit 3d700ab94c1f261a17c554c8dc3b59d8474d9f1a Author: David Capello <[email protected]> Date: Thu Sep 10 16:10:31 2015 -0300 New FLI/FLC encoder/decoder (fix #7) --- .gitmodules | 3 + README.md | 1 - src/CMakeLists.txt | 1 + src/README.md | 1 + src/app/CMakeLists.txt | 2 +- src/app/file/fli/README | 35 --- src/app/file/fli/fli.cpp | 725 -------------------------------------------- src/app/file/fli/fli.h | 103 ------- src/app/file/fli_format.cpp | 278 ++++++++--------- src/flic | 1 + 10 files changed, 131 insertions(+), 1019 deletions(-) diff --git a/.gitmodules b/.gitmodules index 83da9fb..bcc6806 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "third_party/libwebp"] path = third_party/libwebp url = https://chromium.googlesource.com/webm/libwebp +[submodule "src/flic"] + path = src/flic + url = https://github.com/aseprite/flic.git diff --git a/README.md b/README.md index 81076bd..6e42528 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,6 @@ of the following projects created by third-parties: * [Google Test](http://code.google.com/p/googletest/) - [gtest license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/gtest-LICENSE.txt) * [XFree86](http://www.x.org/) - [XFree86 license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/XFree86-LICENSE.txt) * [curl](http://curl.haxx.se/) - [curl license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/curl-LICENSE.txt) -* [gfli](https://github.com/aseprite/aseprite/blob/master/src/app/file/fli/README) - [GPL license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/GPL.txt) * [giflib](http://sourceforge.net/projects/giflib/) - [giflib license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/giflib-LICENSE.txt) * [libjpeg](http://www.ijg.org/) - [libjpeg license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/libjpeg-LICENSE.txt) * [libpng](http://www.libpng.org/pub/png/) - [libpng license](https://github.com/aseprite/aseprite/tree/master/docs/licenses/libpng-LICENSE.txt) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4f7bd7..dd1d794 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -91,6 +91,7 @@ add_subdirectory(base) include_directories(${BASE_INCLUDE_DIR}) add_subdirectory(cfg) +add_subdirectory(flic) add_subdirectory(css) add_subdirectory(doc) add_subdirectory(render) diff --git a/src/README.md b/src/README.md index 18b4a57..9c13f29 100644 --- a/src/README.md +++ b/src/README.md @@ -16,6 +16,7 @@ because they don't depend on any other component. * [allegro](allegro/): Modified version of [Allegro](http://alleg.sourceforge.net/) library, used for keyboard/mouse input, and drawing 2D graphics on screen. * [base](base/): Core/basic stuff, multithreading, utf8, sha1, file system, memory, etc. * [css](css/): Pseudo-style sheet library. + * [flic](flic/): Library to load/save FLI/FLC files * [gfx](gfx/): Abstract graphics structures like point, size, rectangle, region, color, etc. * [scripting](scripting/): JavaScript engine ([V8](https://code.google.com/p/v8/)). * [undo](undo/): Generic library to manage a history of undoable commands. diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 3d1ab30..51617f5 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -280,7 +280,6 @@ add_library(app-lib file/file.cpp file/file_format.cpp file/file_formats_manager.cpp - file/fli/fli.cpp file/palette_file.cpp file/split_filename.cpp ${file_formats} @@ -411,6 +410,7 @@ target_link_libraries(app-lib doc-lib filters-lib fixmath-lib + flic-lib gfx-lib net-lib render-lib diff --git a/src/app/file/fli/README b/src/app/file/fli/README deleted file mode 100644 index 655cfd8..0000000 --- a/src/app/file/fli/README +++ /dev/null @@ -1,35 +0,0 @@ -GFLI ----- - -This is the second version of my FLI plugin for "The GIMP". It now adds -saving, and the fli load/save functions are separated from the GIMP -interface, to allow them to be reused for other projects. - -The saving supports currently only BRUN and LC chunks. LC2 chunks may -be added in the future. You should make a backup as an animated GIF if -possible, because saving is not tested very much. - -gfli.c: Gimp wrapper for fli.c -fli.c: functions to load/save FLI movies - -Please write me about your experiences with this plug-in: -<[email protected]> - -This is another idea I had recently: -The FLI format allows to add chunks with new data to a frame, that are -skipped by readers that don't understand them. -They will require a special reader. This is easy to write, because all the -fli handling is in "fli.c", and can be reused for other programs. -- MIDI events: Background musik ! (I'd need to recycle some code from -"playmidi" and "timidity") -- Text events (subtitles) -- CD-Audio synchronisation -- Trigger playback of external PCM files (digitized speech) - -Known limitations: -- The FLI format allows to change the palette from frame to frame. This does -not work in Gimp, because Gimp allows only one palette per image. I'd have -to translate the image to True-Color while loading. -- Animations consume a lot of memory and swapping will slow the playback -down. - Jens Ch. Restemeier diff --git a/src/app/file/fli/fli.cpp b/src/app/file/fli/fli.cpp deleted file mode 100644 index aec4402..0000000 --- a/src/app/file/fli/fli.cpp +++ /dev/null @@ -1,725 +0,0 @@ -/* - * Written 1998 Jens Ch. Restemeier <[email protected]> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -/* Modified by David Capello to use with Aseprite (2001-2012). */ - -#include <cstdio> -#include <cstdlib> -#include <cstring> - -#include "fli.h" - -using namespace std; - -/* - * To avoid endian-problems I wrote these functions: - */ -static unsigned char fli_read_char(FILE *f) -{ - unsigned char b; - fread(&b,1,1,f); - return b; -} - -static unsigned short fli_read_short(FILE *f) -{ - unsigned char b[2]; - fread(&b,1,2,f); - return (unsigned short)(b[1]<<8) | b[0]; -} - -static unsigned long fli_read_long(FILE *f) -{ - unsigned char b[4]; - fread(&b,1,4,f); - return (unsigned long)(b[3]<<24) | (b[2]<<16) | (b[1]<<8) | b[0]; -} - -static void fli_write_char(FILE *f, unsigned char b) -{ - fwrite(&b,1,1,f); -} - -static void fli_write_short(FILE *f, unsigned short w) -{ - unsigned char b[2]; - b[0]=w&255; - b[1]=(w>>8)&255; - fwrite(&b,1,2,f); -} - -static void fli_write_long(FILE *f, unsigned long l) -{ - unsigned char b[4]; - b[0]=l&255; - b[1]=(l>>8)&255; - b[2]=(l>>16)&255; - b[3]=(l>>24)&255; - fwrite(&b,1,4,f); -} - -void fli_read_header(FILE *f, s_fli_header *fli_header) -{ - fli_header->filesize=fli_read_long(f); /* 0 */ - fli_header->magic=fli_read_short(f); /* 4 */ - fli_header->frames=fli_read_short(f); /* 6 */ - fli_header->width=fli_read_short(f); /* 8 */ - fli_header->height=fli_read_short(f); /* 10 */ - fli_header->depth=fli_read_short(f); /* 12 */ - fli_header->flags=fli_read_short(f); /* 14 */ - if (fli_header->magic == HEADER_FLI) { - /* FLI saves speed in 1/70s */ - fli_header->speed=fli_read_short(f)*14; /* 16 */ - } else { - if (fli_header->magic == HEADER_FLC) { - /* FLC saves speed in 1/1000s */ - fli_header->speed=fli_read_long(f); /* 16 */ - } else { - fprintf(stderr, "error: magic number is wrong !\n"); - fli_header->magic = NO_HEADER; - } - } - - if (fli_header->width == 0) - fli_header->width = 320; - - if (fli_header->height == 0) - fli_header->height = 200; -} - -void fli_write_header(FILE *f, s_fli_header *fli_header) -{ - fli_header->filesize=ftell(f); - fseek(f, 0, SEEK_SET); - fli_write_long(f, fli_header->filesize); /* 0 */ - fli_write_short(f, fli_header->magic); /* 4 */ - fli_write_short(f, fli_header->frames); /* 6 */ - fli_write_short(f, fli_header->width); /* 8 */ - fli_write_short(f, fli_header->height); /* 10 */ - fli_write_short(f, fli_header->depth); /* 12 */ - fli_write_short(f, fli_header->flags); /* 14 */ - if (fli_header->magic == HEADER_FLI) { - /* FLI saves speed in 1/70s */ - fli_write_short(f, (unsigned short)fli_header->speed / 14); /* 16 */ - } else { - if (fli_header->magic == HEADER_FLC) { - /* FLC saves speed in 1/1000s */ - fli_write_long(f, fli_header->speed); /* 16 */ - fseek(f, 80, SEEK_SET); - fli_write_long(f, fli_header->oframe1); /* 80 */ - fli_write_long(f, fli_header->oframe2); /* 84 */ - } else { - fprintf(stderr, "error: magic number in header is wrong !\n"); - } - } -} - -void fli_read_frame(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *old_cmap, unsigned char *framebuf, unsigned char *cmap) -{ - s_fli_frame fli_frame; - unsigned long framepos; - int c; - framepos=ftell(f); - - fli_frame.size=fli_read_long(f); - fli_frame.magic=fli_read_short(f); - fli_frame.chunks=fli_read_short(f); - - if (fli_frame.magic == FRAME) { - fseek(f, framepos+16, SEEK_SET); - for (c=0;c<fli_frame.chunks;c++) { - s_fli_chunk chunk; - unsigned long chunkpos; - chunkpos = ftell(f); - chunk.size=fli_read_long(f); - chunk.magic=fli_read_short(f); - switch (chunk.magic) { - case FLI_COLOR: fli_read_color(f, fli_header, old_cmap, cmap); break; - case FLI_COLOR_2: fli_read_color_2(f, fli_header, old_cmap, cmap); break; - case FLI_BLACK: fli_read_black(f, fli_header, framebuf); break; - case FLI_BRUN: fli_read_brun(f, fli_header, framebuf); break; - case FLI_COPY: fli_read_copy(f, fli_header, framebuf); break; - case FLI_LC: fli_read_lc(f, fli_header, old_framebuf, framebuf); break; - case FLI_LC_2: fli_read_lc_2(f, fli_header, old_framebuf, framebuf); break; - case FLI_MINI: /* unused, skip */ break; - default: /* unknown, skip */ break; - } - if (chunk.size & 1) chunk.size++; - fseek(f,chunkpos+chunk.size,SEEK_SET); - } - } /* else: unknown, skip */ - fseek(f, framepos+fli_frame.size, SEEK_SET); -} - -void fli_write_frame(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *old_cmap, unsigned char *framebuf, unsigned char *cmap, unsigned short codec_mask) -{ - s_fli_frame fli_frame; - unsigned long framepos, frameend; - framepos=ftell(f); - fseek(f, framepos+16, SEEK_SET); - - switch (fli_header->frames) { - case 0: fli_header->oframe1=framepos; break; - case 1: fli_header->oframe2=framepos; break; - } - - fli_frame.size=0; - fli_frame.magic=FRAME; - fli_frame.chunks=0; - - /* - * create color chunk - */ - if (fli_header->magic == HEADER_FLI) { - if (fli_write_color(f, fli_header, old_cmap, cmap)) fli_frame.chunks++; - } else { - if (fli_header->magic == HEADER_FLC) { - if (fli_write_color_2(f, fli_header, old_cmap, cmap)) fli_frame.chunks++; - } else { - fprintf(stderr, "error: magic number in header is wrong !\n"); - } - } - -#if 0 - if (codec_mask & W_COLOR) { - if (fli_write_color(f, fli_header, old_cmap, cmap)) fli_frame.chunks++; - } - if (codec_mask & W_COLOR_2) { - if (fli_write_color_2(f, fli_header, old_cmap, cmap)) fli_frame.chunks++; - } -#endif - /* create bitmap chunk */ - if (old_framebuf==NULL) { - fli_write_brun(f, fli_header, framebuf); - } else { - fli_write_lc(f, fli_header, old_framebuf, framebuf); - } - fli_frame.chunks++; - - frameend=ftell(f); - fli_frame.size=frameend-framepos; - fseek(f, framepos, SEEK_SET); - fli_write_long(f, fli_frame.size); - fli_write_short(f, fli_frame.magic); - fli_write_short(f, fli_frame.chunks); - fseek(f, frameend, SEEK_SET); - fli_header->frames++; -} - -/* - * palette chunks from the classical Autodesk Animator. - */ -void fli_read_color(FILE *f, s_fli_header *fli_header, unsigned char *old_cmap, unsigned char *cmap) -{ - unsigned short num_packets, cnt_packets, col_pos; - col_pos=0; - num_packets=fli_read_short(f); - for (cnt_packets=num_packets; cnt_packets>0; cnt_packets--) { - unsigned short skip_col, num_col, col_cnt; - skip_col=fli_read_char(f); - num_col=fli_read_char(f); - if (num_col==0) { - for (col_pos=0; col_pos<768; col_pos++) { - cmap[col_pos]=fli_read_char(f)<<2; - } - return; - } - for (col_cnt=skip_col; (col_cnt>0) && (col_pos<768); col_cnt--) { - cmap[col_pos]=old_cmap[col_pos];col_pos++; - cmap[col_pos]=old_cmap[col_pos];col_pos++; - cmap[col_pos]=old_cmap[col_pos];col_pos++; - } - for (col_cnt=num_col; (col_cnt>0) && (col_pos<768); col_cnt--) { - cmap[col_pos++]=fli_read_char(f)<<2; - cmap[col_pos++]=fli_read_char(f)<<2; - cmap[col_pos++]=fli_read_char(f)<<2; - } - } -} - -int fli_write_color(FILE *f, s_fli_header *fli_header, unsigned char *old_cmap, unsigned char *cmap) -{ - unsigned long chunkpos; - unsigned short num_packets; - s_fli_chunk chunk; - chunkpos=ftell(f); - fseek(f, chunkpos+8, SEEK_SET); - num_packets=0; - if (old_cmap==NULL) { - unsigned short col_pos; - num_packets=1; - fli_write_char(f, 0); /* skip no color */ - fli_write_char(f, 0); /* 256 color */ - for (col_pos=0; col_pos<768; col_pos++) { - fli_write_char(f, cmap[col_pos]>>2); - } - } else { - unsigned short cnt_skip, cnt_col, col_pos, col_start; - col_pos=0; - do { - cnt_skip=0; - while ((col_pos<256) && (old_cmap[col_pos*3+0]==cmap[col_pos*3+0]) && (old_cmap[col_pos*3+1]==cmap[col_pos*3+1]) && (old_cmap[col_pos*3+2]==cmap[col_pos*3+2])) { - cnt_skip++; col_pos++; - } - col_start=col_pos*3; - cnt_col=0; - while ((col_pos<256) && !((old_cmap[col_pos*3+0]==cmap[col_pos*3+0]) && (old_cmap[col_pos*3+1]==cmap[col_pos*3+1]) && (old_cmap[col_pos*3+2]==cmap[col_pos*3+2]))) { - cnt_col++; col_pos++; - } - if (cnt_col>0) { - num_packets++; - fli_write_char(f, cnt_skip & 255); - fli_write_char(f, cnt_col & 255); - while (cnt_col>0) { - fli_write_char(f, cmap[col_start++]>>2); - fli_write_char(f, cmap[col_start++]>>2); - fli_write_char(f, cmap[col_start++]>>2); - cnt_col--; - } - } - } while (col_pos<256); - } - - if (num_packets>0) { - chunk.size=ftell(f)-chunkpos; - chunk.magic=FLI_COLOR; - - fseek(f, chunkpos, SEEK_SET); - fli_write_long(f, chunk.size); - fli_write_short(f, chunk.magic); - fli_write_short(f, num_packets); - - if (chunk.size & 1) chunk.size++; - fseek(f,chunkpos+chunk.size,SEEK_SET); - return 1; - } - fseek(f,chunkpos, SEEK_SET); - return 0; -} - -/* - * palette chunks from Autodesk Animator pro - */ -void fli_read_color_2(FILE *f, s_fli_header *fli_header, unsigned char *old_cmap, unsigned char *cmap) -{ - unsigned short num_packets, cnt_packets, col_pos; - num_packets=fli_read_short(f); - col_pos=0; - for (cnt_packets=num_packets; cnt_packets>0; cnt_packets--) { - unsigned short skip_col, num_col, col_cnt; - skip_col=fli_read_char(f); - num_col=fli_read_char(f); - if (num_col == 0) { - for (col_pos=0; col_pos<768; col_pos++) { - cmap[col_pos]=fli_read_char(f); - } - return; - } - for (col_cnt=skip_col; (col_cnt>0) && (col_pos<768); col_cnt--) { - cmap[col_pos]=old_cmap[col_pos];col_pos++; - cmap[col_pos]=old_cmap[col_pos];col_pos++; - cmap[col_pos]=old_cmap[col_pos];col_pos++; - } - for (col_cnt=num_col; (col_cnt>0) && (col_pos<768); col_cnt--) { - cmap[col_pos++]=fli_read_char(f); - cmap[col_pos++]=fli_read_char(f); - cmap[col_pos++]=fli_read_char(f); - } - } -} - -int fli_write_color_2(FILE *f, s_fli_header *fli_header, unsigned char *old_cmap, unsigned char *cmap) -{ - unsigned long chunkpos; - unsigned short num_packets; - s_fli_chunk chunk; - chunkpos=ftell(f); - fseek(f, chunkpos+8, SEEK_SET); - num_packets=0; - if (old_cmap==NULL) { - unsigned short col_pos; - num_packets=1; - fli_write_char(f, 0); /* skip no color */ - fli_write_char(f, 0); /* 256 color */ - for (col_pos=0; col_pos<768; col_pos++) { - fli_write_char(f, cmap[col_pos]); - } - } else { - unsigned short cnt_skip, cnt_col, col_pos, col_start; - col_pos=0; - do { - cnt_skip=0; - while ((col_pos<256) && (old_cmap[col_pos*3+0]==cmap[col_pos*3+0]) && (old_cmap[col_pos*3+1]==cmap[col_pos*3+1]) && (old_cmap[col_pos*3+2]==cmap[col_pos*3+2])) { - cnt_skip++; col_pos++; - } - col_start=col_pos*3; - cnt_col=0; - while ((col_pos<256) && !((old_cmap[col_pos*3+0]==cmap[col_pos*3+0]) && (old_cmap[col_pos*3+1]==cmap[col_pos*3+1]) && (old_cmap[col_pos*3+2]==cmap[col_pos*3+2]))) { - cnt_col++; col_pos++; - } - if (cnt_col>0) { - num_packets++; - fli_write_char(f, (unsigned char)cnt_skip); - fli_write_char(f, (unsigned char)cnt_col); - while (cnt_col>0) { - fli_write_char(f, cmap[col_start++]); - fli_write_char(f, cmap[col_start++]); - fli_write_char(f, cmap[col_start++]); - cnt_col--; - } - } - } while (col_pos<256); - } - - if (num_packets>0) { - chunk.size=ftell(f)-chunkpos; - chunk.magic=FLI_COLOR_2; - - fseek(f, chunkpos, SEEK_SET); - fli_write_long(f, chunk.size); - fli_write_short(f, chunk.magic); - fli_write_short(f, num_packets); - - if (chunk.size & 1) chunk.size++; - fseek(f,chunkpos+chunk.size,SEEK_SET); - return 1; - } - fseek(f,chunkpos, SEEK_SET); - return 0; -} - -/* - * completely black frame - */ -void fli_read_black(FILE *f, s_fli_header *fli_header, unsigned char *framebuf) -{ - memset(framebuf, 0, fli_header->width * fli_header->height); -} - -void fli_write_black(FILE *f, s_fli_header *fli_header, unsigned char *framebuf) -{ - s_fli_chunk chunk; - - chunk.size=6; - chunk.magic=FLI_BLACK; - - fli_write_long(f, chunk.size); - fli_write_short(f, chunk.magic); -} - -/* - * Uncompressed frame - */ -void fli_read_copy(FILE *f, s_fli_header *fli_header, unsigned char *framebuf) -{ - fread(framebuf, fli_header->width, fli_header->height, f); -} - -void fli_write_copy(FILE *f, s_fli_header *fli_header, unsigned char *framebuf) -{ - - unsigned long chunkpos; - s_fli_chunk chunk; - chunkpos=ftell(f); - fseek(f, chunkpos+6, SEEK_SET); - fwrite(framebuf, fli_header->width, fli_header->height, f); - chunk.size=ftell(f)-chunkpos; - chunk.magic=FLI_COPY; - - fseek(f, chunkpos, SEEK_SET); - fli_write_long(f, chunk.size); - fli_write_short(f, chunk.magic); - - if (chunk.size & 1) chunk.size++; - fseek(f,chunkpos+chunk.size,SEEK_SET); -} - -/* - * This is a RLE algorithm, used for the first image of an animation - */ -void fli_read_brun(FILE *f, s_fli_header *fli_header, unsigned char *framebuf) -{ - unsigned short yc; - unsigned char *pos; - for (yc=0; yc < fli_header->height; yc++) { - unsigned short xc, pc, pcnt; - pc=fli_read_char(f); - xc=0; - pos=framebuf+(fli_header->width * yc); - for (pcnt=pc; pcnt>0; pcnt--) { - unsigned short ps; - ps=fli_read_char(f); - if (ps & 0x80) { - unsigned short len; - for (len=-(signed char)ps; len>0; len--) { - pos[xc++]=fli_read_char(f); - } - } else { - unsigned char val; - val=fli_read_char(f); - memset(&(pos[xc]), val, ps); - xc+=ps; - } - } - } -} - -void fli_write_brun(FILE *f, s_fli_header *fli_header, unsigned char *framebuf) -{ - unsigned long chunkpos; - s_fli_chunk chunk; - unsigned short yc; - unsigned char *linebuf; - - chunkpos=ftell(f); - fseek(f, chunkpos+6, SEEK_SET); - - for (yc=0; yc < fli_header->height; yc++) { - unsigned short xc, t1, pc, tc; - unsigned long linepos, lineend, bc; - linepos=ftell(f); bc=0; - fseek(f, 1, SEEK_CUR); - linebuf=framebuf + (yc*fli_header->width); - xc=0; tc=0; t1=0; - while (xc < fli_header->width) { - pc=1; - while ((pc<120) && ((xc+pc)<fli_header->width) && (linebuf[xc+pc] == linebuf[xc])) { - pc++; - } - if (pc>2) { - if (tc>0) { - bc++; - fli_write_char(f, (tc-1)^0xFF); - fwrite(linebuf+t1, 1, tc, f); - tc=0; - } - bc++; - fli_write_char(f, (unsigned char)pc); - fli_write_char(f, linebuf[xc]); - t1=xc+pc; - } else { - tc+=pc; - if (tc>120) { - bc++; - fli_write_char(f, (tc-1)^0xFF); - fwrite(linebuf+t1, 1, tc, f); - tc=0; - t1=xc+pc; - } - } - xc+=pc; - } - if (tc>0) { - bc++; - fli_write_char(f, (tc-1)^0xFF); - fwrite(linebuf+t1, 1, tc, f); - tc=0; - } - lineend=ftell(f); - fseek(f, linepos, SEEK_SET); - fli_write_char(f, (unsigned char)bc); - fseek(f, lineend, SEEK_SET); - } - - chunk.size=ftell(f)-chunkpos; - chunk.magic=FLI_BRUN; - - fseek(f, chunkpos, SEEK_SET); - fli_write_long(f, chunk.size); - fli_write_short(f, chunk.magic); - - if (chunk.size & 1) chunk.size++; - fseek(f,chunkpos+chunk.size,SEEK_SET); -} - -/* - * This is the delta-compression method from the classic Autodesk Animator. - * It's basically the RLE method from above, but it allows to skip unchanged - * lines at the beginning and end of an image, and unchanged pixels in a line - * This chunk is used in FLI files. - */ -void fli_read_lc(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *framebuf) -{ - unsigned short yc, firstline, numline; - unsigned char *pos; - memcpy(framebuf, old_framebuf, fli_header->width * fli_header->height); - firstline = fli_read_short(f); - numline = fli_read_short(f); - for (yc=0; yc < numline; yc++) { - unsigned short xc, pc, pcnt; - pc=fli_read_char(f); - xc=0; - pos=framebuf+(fli_header->width * (firstline+yc)); - for (pcnt=pc; pcnt>0; pcnt--) { - unsigned short ps,skip; - skip=fli_read_char(f); - ps=fli_read_char(f); - xc+=skip; - if (ps & 0x80) { - unsigned char val; - ps=-(signed char)ps; - val=fli_read_char(f); - memset(&(pos[xc]), val, ps); - xc+=ps; - } else { - fread(&(pos[xc]), ps, 1, f); - xc+=ps; - } - } - } -} - -void fli_write_lc(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *framebuf) -{ - unsigned long chunkpos; - s_fli_chunk chunk; - unsigned short yc, firstline, numline, lastline; - unsigned char *linebuf, *old_linebuf; - - chunkpos=ftell(f); - fseek(f, chunkpos+6, SEEK_SET); - - /* first check, how many lines are unchanged at the beginning */ - firstline=0; - while ((memcmp(old_framebuf+(firstline*fli_header->width), framebuf+(firstline*fli_header->width), fli_header->width)==0) && (firstline<fli_header->height)) firstline++; - - /* then check from the end, how many lines are unchanged */ - if (firstline<fli_header->height) { - lastline=fli_header->height-1; - while ((memcmp(old_framebuf+(lastline*fli_header->width), framebuf+(lastline*fli_header->width), fli_header->width)==0) && (lastline>firstline)) lastline--; - numline=(lastline-firstline)+1; - } else { - numline=0; - } - if (numline==0) firstline=0; - - fli_write_short(f, firstline); - fli_write_short(f, numline); - - for (yc=0; yc < numline; yc++) { - unsigned short xc, sc, cc, tc; - unsigned long linepos, lineend, bc; - linepos=ftell(f); bc=0; - fseek(f, 1, SEEK_CUR); - - linebuf=framebuf + ((firstline+yc)*fli_header->width); - old_linebuf=old_framebuf + ((firstline+yc)*fli_header->width); - xc=0; - while (xc < fli_header->width) { - sc=0; - while ((linebuf[xc]==old_linebuf[xc]) && (xc<fli_header->width) && (sc<255)) { - xc++; sc++; - } - fli_write_char(f, (unsigned char)sc); - cc=1; - while ((linebuf[xc]==linebuf[xc+cc]) && ((xc+cc)<fli_header->width) && (cc<120)) { - cc++; - } - if (cc>2) { - bc++; - fli_write_char(f, (cc-1)^0xFF); - fli_write_char(f, linebuf[xc]); - xc+=cc; - } else { - tc=0; - do { - sc=0; - while ((linebuf[tc+xc+sc]==old_linebuf[tc+xc+sc]) && ((tc+xc+sc)<fli_header->width) && (sc<5)) { - sc++; - } - cc=1; - while ((linebuf[tc+xc]==linebuf[tc+xc+cc]) && ((tc+xc+cc)<fli_header->width) && (cc<10)) { - cc++; - } - tc++; - } while ((tc<120) && (cc<9) && (sc<4) && ((xc+tc)<fli_header->width)); - bc++; - fli_write_char(f, (unsigned char)tc); - fwrite(linebuf+xc, tc, 1, f); - xc+=tc; - } - } - lineend=ftell(f); - fseek(f, linepos, SEEK_SET); - fli_write_char(f, (unsigned char)bc); - fseek(f, lineend, SEEK_SET); - } - - chunk.size=ftell(f)-chunkpos; - chunk.magic=FLI_LC; - - fseek(f, chunkpos, SEEK_SET); - fli_write_long(f, chunk.size); - fli_write_short(f, chunk.magic); - - if (chunk.size & 1) chunk.size++; - fseek(f,chunkpos+chunk.size,SEEK_SET); -} - - -/* - * This is an enhanced version of the old delta-compression used by - * the autodesk animator pro. It's word-oriented, and allows to skip - * larger parts of the image. This chunk is used in FLC files. - */ -void fli_read_lc_2(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *framebuf) -{ - unsigned short yc, lc, numline; - unsigned char *pos; - memcpy(framebuf, old_framebuf, fli_header->width * fli_header->height); - yc=0; - numline = fli_read_short(f); - for (lc=0; lc < numline; lc++) { - unsigned short xc, pc, pcnt, lpf, lpn; - pc=fli_read_short(f); - lpf=0; lpn=0; - while (pc & 0x8000) { - if (pc & 0x4000) { - yc+=-(signed short)pc; - } else { - lpf=1;lpn=pc&0xFF; - } - pc=fli_read_short(f); - } - xc=0; - pos=framebuf+(fli_header->width * yc); - for (pcnt=pc; pcnt>0; pcnt--) { - unsigned short ps,skip; - skip=fli_read_char(f); - ps=fli_read_char(f); - xc+=skip; - if (ps & 0x80) { - unsigned char v1,v2; - ps=-(signed char)ps; - v1=fli_read_char(f); - v2=fli_read_char(f); - while (ps>0) { - pos[xc++]=v1; - pos[xc++]=v2; - ps--; - } - } else { - fread(&(pos[xc]), ps, 2, f); - xc+=ps << 1; - } - } - if (lpf) pos[xc]=(unsigned char)lpn; - yc++; - } -} diff --git a/src/app/file/fli/fli.h b/src/app/file/fli/fli.h deleted file mode 100644 index b3ed3e4..0000000 --- a/src/app/file/fli/fli.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Written 1998 Jens Ch. Restemeier <[email protected]> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ -#ifndef APP_FILE_FLI_FLI_H_INCLUDED -#define APP_FILE_FLI_FLI_H_INCLUDED -#pragma once - -/** structures */ - -typedef struct _fli_header { - unsigned long filesize; - unsigned short magic; - unsigned short frames; - unsigned short width; - unsigned short height; - unsigned short depth; - unsigned short flags; - unsigned long speed; - unsigned long created; - unsigned long creator; - unsigned long updated; - unsigned short aspect_x, aspect_y; - unsigned long oframe1, oframe2; -} s_fli_header; - -typedef struct _fli_frame { - unsigned long size; - unsigned short magic; - unsigned short chunks; -} s_fli_frame; - -typedef struct _fli_chunk { - unsigned long size; - unsigned short magic; - unsigned char *data; -} s_fli_chunk; - -/** chunk magics */ -#define NO_HEADER 0 -#define HEADER_FLI 0xAF11 -#define HEADER_FLC 0xAF12 -#define FRAME 0xF1FA - -/** codec magics */ -#define FLI_COLOR 11 -#define FLI_BLACK 13 -#define FLI_BRUN 15 -#define FLI_COPY 16 -#define FLI_LC 12 -#define FLI_LC_2 7 -#define FLI_COLOR_2 4 -#define FLI_MINI 18 - -/** codec masks */ -#define W_COLOR 0x0001 -#define W_BLACK 0x0002 -#define W_BRUN 0x0004 -#define W_COPY 0x0008 -#define W_LC 0x0010 -#define W_LC_2 0x0020 -#define W_COLOR_2 0x0040 -#define W_MINI 0x0080 -#define W_ALL 0xFFFF - -/** functions */ -void fli_read_header(FILE *f, s_fli_header *fli_header); -void fli_read_frame(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *old_cmap, unsigned char *framebuf, unsigned char *cmap); - -void fli_read_color(FILE *f, s_fli_header *fli_header, unsigned char *old_cmap, unsigned char *cmap); -void fli_read_color_2(FILE *f, s_fli_header *fli_header, unsigned char *old_cmap, unsigned char *cmap); -void fli_read_black(FILE *f, s_fli_header *fli_header, unsigned char *framebuf); -void fli_read_brun(FILE *f, s_fli_header *fli_header, unsigned char *framebuf); -void fli_read_copy(FILE *f, s_fli_header *fli_header, unsigned char *framebuf); -void fli_read_lc(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *framebuf); -void fli_read_lc_2(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *framebuf); - -void fli_write_header(FILE *f, s_fli_header *fli_header); -void fli_write_frame(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *old_cmap, unsigned char *framebuf, unsigned char *cmap, unsigned short codec_mask); - -int fli_write_color(FILE *f, s_fli_header *fli_header, unsigned char *old_cmap, unsigned char *cmap); -int fli_write_color_2(FILE *f, s_fli_header *fli_header, unsigned char *old_cmap, unsigned char *cmap); -void fli_write_black(FILE *f, s_fli_header *fli_header, unsigned char *framebuf); -void fli_write_brun(FILE *f, s_fli_header *fli_header, unsigned char *framebuf); -void fli_write_copy(FILE *f, s_fli_header *fli_header, unsigned char *framebuf); -void fli_write_lc(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *framebuf); -void fli_write_lc_2(FILE *f, s_fli_header *fli_header, unsigned char *old_framebuf, unsigned char *framebuf); - -#endif diff --git a/src/app/file/fli_format.cpp b/src/app/file/fli_format.cpp index 618af25..919eb77 100644 --- a/src/app/file/fli_format.cpp +++ b/src/app/file/fli_format.cpp @@ -12,11 +12,11 @@ #include "app/document.h" #include "app/file/file.h" #include "app/file/file_format.h" -#include "app/file/fli/fli.h" #include "app/file/format_options.h" #include "app/modules/palettes.h" #include "base/file_handle.h" #include "doc/doc.h" +#include "flic/flic.h" #include "render/render.h" #include <cstdio> @@ -25,8 +25,6 @@ namespace app { using namespace base; -static int get_time_precision(Sprite *sprite); - class FliFormat : public FileFormat { const char* onGetName() const { return "flc"; } const char* onGetExtensions() const { return "flc,fli"; } @@ -52,223 +50,195 @@ FileFormat* CreateFliFormat() bool FliFormat::onLoad(FileOp* fop) { -#define SETPAL() \ - do { \ - for (c=0; c<256; c++) { \ - pal->setEntry(c, rgba(cmap[c*3], \ - cmap[c*3+1], \ - cmap[c*3+2], 255)); \ - } \ - pal->setFrame(frpos_out); \ - sprite->setPalette(pal, true); \ - } while (0) - - unsigned char cmap[768]; - unsigned char omap[768]; - s_fli_header fli_header; - int c, w, h; - frame_t frpos_in; - frame_t frpos_out; -#ifdef USE_LINK - int index = 0; // TODO this is used to create linked cels -#endif - // Open the file to read in binary mode FileHandle handle(open_file_with_exception(fop->filename, "rb")); FILE* f = handle.get(); + flic::StdioFileInterface finterface(f); + flic::Decoder decoder(&finterface); - fli_read_header(f, &fli_header); - fseek(f, 128, SEEK_SET); - - if (fli_header.magic == NO_HEADER) { + flic::Header header; + if (!decoder.readHeader(header)) { fop_error(fop, "The file doesn't have a FLIC header\n"); return false; } // Size by frame - w = fli_header.width; - h = fli_header.height; + int w = header.width; + int h = header.height; - // Create the bitmaps - base::UniquePtr<Image> bmp(Image::create(IMAGE_INDEXED, w, h)); - base::UniquePtr<Image> old(Image::create(IMAGE_INDEXED, w, h)); - base::UniquePtr<Palette> pal(new Palette(frame_t(0), 256)); + // Create a temporal bitmap + ImageRef bmp(Image::create(IMAGE_INDEXED, w, h)); + Palette pal(0, 1); + Cel* prevCel = nullptr; - // Create the image + // Create the sprite Sprite* sprite = new Sprite(IMAGE_INDEXED, w, h, 256); LayerImage* layer = new LayerImage(sprite); sprite->folder()->addLayer(layer); layer->configureAsBackground(); // Set frames and speed - sprite->setTotalFrames(frame_t(fli_header.frames)); - sprite->setDurationForAllFrames(fli_header.speed); - - // Write frame by frame - for (frpos_in = frpos_out = frame_t(0); - frpos_in < sprite->totalFrames(); - ++frpos_in) { + sprite->setTotalFrames(frame_t(header.frames)); + sprite->setDurationForAllFrames(header.speed); + + flic::Frame fliFrame; + flic::Colormap oldFliColormap; + fliFrame.pixels = bmp->getPixelAddress(0, 0); + fliFrame.rowstride = IndexedTraits::getRowStrideBytes(bmp->width()); + + frame_t frame_out = 0; + for (frame_t frame_in=0; + frame_in<sprite->totalFrames(); + ++frame_in) { // Read the frame - fli_read_frame(f, &fli_header, - (unsigned char *)old->getPixelAddress(0, 0), omap, - (unsigned char *)bmp->getPixelAddress(0, 0), cmap); - - // First frame, or the frames changes, or the palette changes - if ((frpos_in == 0) || - (count_diff_between_images(old, bmp)) -#ifndef USE_LINK /* TODO this should be configurable through a check-box */ - || (memcmp(omap, cmap, 768) != 0) -#endif - ) { - // The image changes? - if (frpos_in != 0) - ++frpos_out; + if (!decoder.readFrame(fliFrame)) { + fop_error(fop, "Error reading frame %d\n", frame_in); + continue; + } + + // Palette change + bool palChange = false; + if (frame_out == 0 || oldFliColormap != fliFrame.colormap) { + oldFliColormap = fliFrame.colormap; + + pal.resize(fliFrame.colormap.size()); + for (int c=0; c<int(fliFrame.colormap.size()); c++) { + pal.setEntry(c, rgba(fliFrame.colormap[c].r, + fliFrame.colormap[c].g, + fliFrame.colormap[c].b, 255)); + } + pal.setFrame(frame_out); + sprite->setPalette(&pal, true); + + palChange = true; + } + // First frame, or the frame changes + if (!prevCel || + (count_diff_between_images(prevCel->image(), bmp.get()))) { // Add the new frame - ImageRef image(Image::createCopy(bmp)); - Cel* cel = new Cel(frpos_out, image); + ImageRef image(Image::createCopy(bmp.get())); + Cel* cel = new Cel(frame_out, image); layer->addCel(cel); - // First frame or the palette changes - if ((frpos_in == 0) || (memcmp(omap, cmap, 768) != 0)) - SETPAL(); + prevCel = cel; + ++frame_out; } -#ifdef USE_LINK - // The palette changes - else if (memcmp(omap, cmap, 768) != 0) { - ++frpos_out; - SETPAL(); - - // Add link - Cel* cel = new Cel(frpos_out, index); - layer_add_cel(layer, cel); + else if (palChange) { + Cel* cel = Cel::createLink(prevCel); + cel->setFrame(frame_out); + layer->addCel(cel); + + ++frame_out; } -#endif // The palette and the image don't change: add duration to the last added frame else { - sprite->setFrameDuration(frpos_out, - sprite->frameDuration(frpos_out)+fli_header.speed); + sprite->setFrameDuration( + frame_out-1, sprite->frameDuration(frame_out-1) + header.speed); } - // Update the old image and color-map to the new ones to compare later - copy_image(old, bmp); - memcpy(omap, cmap, 768); + if (header.frames > 0) + fop_progress(fop, (float)(frame_in+1) / (float)(header.frames)); - // Update progress - fop_progress(fop, (float)(frpos_in+1) / (float)(sprite->totalFrames())); if (fop_is_stop(fop)) break; - // Just one frame? if (fop->oneframe) break; } - // Update number of frames - sprite->setTotalFrames(frpos_out+1); + if (frame_out > 0) + sprite->setTotalFrames(frame_out); fop->createDocument(sprite); return true; } #ifdef ENABLE_SAVE + +static int get_time_precision(Sprite *sprite) +{ + // Check if all frames have the same duration + bool constantFrameRate = true; + for (frame_t c(1); c < sprite->totalFrames(); ++c) { + if (sprite->frameDuration(c-1) != sprite->frameDuration(c)) { + constantFrameRate = false; + break; + } + } + if (constantFrameRate) + return sprite->frameDuration(0); + + int precision = 1000; + for (frame_t c(0); c < sprite->totalFrames() && precision > 1; ++c) { + int len = sprite->frameDuration(c); + while (len / precision == 0) + precision /= 10; + } + return precision; +} + bool FliFormat::onSave(FileOp* fop) { Sprite* sprite = fop->document->sprite(); - unsigned char cmap[768]; - unsigned char omap[768]; - s_fli_header fli_header; - int c, times; - Palette *pal; - - /* prepare fli header */ - fli_header.filesize = 0; - fli_header.frames = 0; - fli_header.width = sprite->width(); - fli_header.height = sprite->height(); - - if ((fli_header.width == 320) && (fli_header.height == 200)) - fli_header.magic = HEADER_FLI; - else - fli_header.magic = HEADER_FLC; - - fli_header.depth = 8; - fli_header.flags = 3; - fli_header.speed = get_time_precision(sprite); - fli_header.created = 0; - fli_header.updated = 0; - fli_header.aspect_x = 1; - fli_header.aspect_y = 1; - fli_header.oframe1 = fli_header.oframe2 = 0; - - /* open the file to write in binary mode */ + + // Open the file to write in binary mode FileHandle handle(open_file_with_exception(fop->filename, "wb")); FILE* f = handle.get(); + flic::StdioFileInterface finterface(f); + flic::Encoder encoder(&finterface); - fseek(f, 128, SEEK_SET); + flic::Header header; + header.frames = 0; + header.width = sprite->width(); + header.height = sprite->height(); + header.speed = get_time_precision(sprite); + encoder.writeHeader(header); // Create the bitmaps - base::UniquePtr<Image> bmp(Image::create(IMAGE_INDEXED, sprite->width(), sprite->height())); - base::UniquePtr<Image> old(Image::create(IMAGE_INDEXED, sprite->width(), sprite->height())); + ImageRef bmp(Image::create(IMAGE_INDEXED, sprite->width(), sprite->height())); render::Render render; // Write frame by frame - for (frame_t frpos(0); - frpos < sprite->totalFrames(); - ++frpos) { - // Get color map - pal = sprite->palette(frpos); - for (c=0; c<256; c++) { - cmap[3*c ] = rgba_getr(pal->getEntry(c)); - cmap[3*c+1] = rgba_getg(pal->getEntry(c)); - cmap[3*c+2] = rgba_getb(pal->getEntry(c)); + flic::Frame fliFrame; + fliFrame.pixels = bmp->getPixelAddress(0, 0); + fliFrame.rowstride = IndexedTraits::getRowStrideBytes(bmp->width()); + for (frame_t frame_it=0; + frame_it <= sprite->totalFrames(); + ++frame_it) { + frame_t frame = (frame_it % sprite->totalFrames()); + const Palette* pal = sprite->palette(frame); + int size = MIN(256, pal->size()); + + for (int c=0; c<size; c++) { + color_t color = pal->getEntry(c); + fliFrame.colormap[c].r = rgba_getr(color); + fliFrame.colormap[c].g = rgba_getg(color); + fliFrame.colormap[c].b = rgba_getb(color); } // Render the frame in the bitmap - render.renderSprite(bmp, sprite, frpos); + render.renderSprite(bmp.get(), sprite, frame); // How many times this frame should be written to get the same // time that it has in the sprite - times = sprite->frameDuration(frpos) / fli_header.speed; - - for (c=0; c<times; c++) { - // Write this frame - if (frpos == 0 && c == 0) - fli_write_frame(f, &fli_header, NULL, NULL, - (unsigned char *)bmp->getPixelAddress(0, 0), cmap, W_ALL); - else - fli_write_frame(f, &fli_header, - (unsigned char *)old->getPixelAddress(0, 0), omap, - (unsigned char *)bmp->getPixelAddress(0, 0), cmap, W_ALL); - - // Update the old image and color-map to the new ones to compare later - copy_image(old, bmp); - memcpy(omap, cmap, 768); + if (frame_it < sprite->totalFrames()) { + int times = sprite->frameDuration(frame) / header.speed; + times = MAX(1, times); + for (int c=0; c<times; c++) + encoder.writeFrame(fliFrame); + } + else { + encoder.writeRingFrame(fliFrame); } // Update progress - fop_progress(fop, (float)(frpos+1) / (float)(sprite->totalFrames())); + fop_progress(fop, (float)(frame_it+1) / (float)(sprite->totalFrames()+1)); } - // Write the header and close the file - fli_write_header(f, &fli_header); - return true; } -#endif -static int get_time_precision(Sprite *sprite) -{ - int precision = 1000; - - for (frame_t c(0); c < sprite->totalFrames() && precision > 1; ++c) { - int len = sprite->frameDuration(c); - - while (len / precision == 0) - precision /= 10; - } - - return precision; -} +#endif } // namespace app diff --git a/src/flic b/src/flic new file mode 160000 index 0000000..357b718 --- /dev/null +++ b/src/flic @@ -0,0 +1 @@ +Subproject commit 357b71838560fd5ff1043719b4ebe4f2a8d0e6e4 -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/aseprite.git _______________________________________________ Pkg-games-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-games-commits

