/* ASE - Allegro Sprite Editor * Copyright (C) 2001-2011 David Capello * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include "file/file.h" #include "file/file_format.h" #include "file/fli/fli.h" #include "modules/palettes.h" #include "raster/raster.h" static int get_time_precision(Sprite *sprite); class FliFormat : public FileFormat { const char* onGetName() const { return "flc"; } const char* onGetExtensions() const { return "flc,fli"; } int onGetFlags() const { return FILE_SUPPORT_LOAD | FILE_SUPPORT_SAVE | FILE_SUPPORT_INDEXED | FILE_SUPPORT_FRAMES | FILE_SUPPORT_PALETTES; } bool onLoad(FileOp* fop); bool onSave(FileOp* fop); }; FileFormat* CreateFliFormat() { return new FliFormat; } 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; Image *bmp, *old, *image; Sprite *sprite; LayerImage *layer; Palette *pal; int c, w, h; int frpos_in; int frpos_out; int index = 0; Cel *cel; FILE *f; /* open the file to read in binary mode */ f = fopen(fop->filename.c_str(), "rb"); if (!f) return false; fli_read_header(f, &fli_header); fseek(f, 128, SEEK_SET); if (fli_header.magic == NO_HEADER) { fop_error(fop, "The file doesn't have a FLIC header\n"); fclose(f); return false; } /* size by frame */ w = fli_header.width; h = fli_header.height; /* create the bitmaps */ bmp = image_new(IMAGE_INDEXED, w, h); old = image_new(IMAGE_INDEXED, w, h); pal = new Palette(0, 256); if (!bmp || !old || !pal) { fop_error(fop, "Not enough memory.\n"); if (bmp) image_free(bmp); if (old) image_free(old); if (pal) delete pal; fclose(f); return false; } // Create the image sprite = new Sprite(IMAGE_INDEXED, w, h, 256); layer = new LayerImage(sprite); sprite->getFolder()->add_layer(layer); layer->configureAsBackground(); // Set frames and speed sprite->setTotalFrames(fli_header.frames); sprite->setDurationForAllFrames(fli_header.speed); /* write frame by frame */ for (frpos_in=frpos_out=0; frpos_ingetTotalFrames(); frpos_in++) { /* read the frame */ fli_read_frame(f, &fli_header, (unsigned char *)old->dat, omap, (unsigned char *)bmp->dat, cmap); /* first frame, or the frames changes, or the palette changes */ if ((frpos_in == 0) || (image_count_diff(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++; /* add the new frame */ image = image_new_copy(bmp); if (!image) { fop_error(fop, "Not enough memory\n"); break; } index = sprite->getStock()->addImage(image); if (index < 0) { image_free(image); fop_error(fop, "Not enough memory\n"); break; } cel = cel_new(frpos_out, index); if (!cel) { fop_error(fop, "Not enough memory\n"); break; } layer->addCel(cel); /* first frame or the palette changes */ if ((frpos_in == 0) || (memcmp(omap, cmap, 768) != 0)) SETPAL(); } #ifdef USE_LINK /* the palette changes */ else if (memcmp(omap, cmap, 768) != 0) { frpos_out++; SETPAL(); /* add link */ cel = cel_new(frpos_out, index); if (!cel) { fop_error(fop, _("Not enough memory\n")); break; } layer_add_cel(layer, cel); } #endif // The palette and the image don't change: add duration to the last added frame else { sprite->setFrameDuration(frpos_out, sprite->getFrameDuration(frpos_out)+fli_header.speed); } /* update the old image and color-map to the new ones to compare later */ image_copy(old, bmp, 0, 0); memcpy(omap, cmap, 768); /* update progress */ fop_progress(fop, (float)(frpos_in+1) / (float)(sprite->getTotalFrames())); if (fop_is_stop(fop)) break; /* just one frame? */ if (fop->oneframe) break; } // Update number of frames sprite->setTotalFrames(frpos_out+1); // Close the file fclose(f); // Destroy the bitmaps image_free(bmp); image_free(old); delete pal; fop->sprite = sprite; return true; } bool FliFormat::onSave(FileOp* fop) { Sprite *sprite = fop->sprite; unsigned char cmap[768]; unsigned char omap[768]; s_fli_header fli_header; int c, frpos, times; Image *bmp, *old; Palette *pal; FILE *f; /* prepare fli header */ fli_header.filesize = 0; fli_header.frames = 0; fli_header.width = sprite->getWidth(); fli_header.height = sprite->getHeight(); 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 */ f = fopen(fop->filename.c_str(), "wb"); if (!f) return false; fseek(f, 128, SEEK_SET); /* create the bitmaps */ bmp = image_new(IMAGE_INDEXED, sprite->getWidth(), sprite->getHeight()); old = image_new(IMAGE_INDEXED, sprite->getWidth(), sprite->getHeight()); if ((!bmp) || (!old)) { fop_error(fop, "Not enough memory for temporary bitmaps.\n"); if (bmp) image_free(bmp); if (old) image_free(old); fclose(f); return false; } /* write frame by frame */ for (frpos=0; frposgetTotalFrames(); frpos++) { /* get color map */ pal = sprite->getPalette(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)); } /* render the frame in the bitmap */ image_clear(bmp, 0); layer_render(sprite->getFolder(), bmp, 0, 0, frpos); /* how many times this frame should be written to get the same time that it has in the sprite */ times = sprite->getFrameDuration(frpos) / fli_header.speed; for (c=0; cdat, cmap, W_ALL); else fli_write_frame(f, &fli_header, (unsigned char *)old->dat, omap, (unsigned char *)bmp->dat, cmap, W_ALL); /* update the old image and color-map to the new ones to compare later */ image_copy(old, bmp, 0, 0); memcpy(omap, cmap, 768); } /* update progress */ fop_progress(fop, (float)(frpos+1) / (float)(sprite->getTotalFrames())); } /* write the header and close the file */ fli_write_header(f, &fli_header); fclose(f); /* destroy the bitmaps */ image_free(bmp); image_free(old); return true; } static int get_time_precision(Sprite *sprite) { int precision = 1000; int c, len; for (c = 0; c < sprite->getTotalFrames() && precision > 1; c++) { len = sprite->getFrameDuration(c); while (len / precision == 0) precision /= 10; } return precision; }