Fix issue #9: GIF files are loaded as Indexed images now.

+ Added support for non-zero transparent index.
+ Added fop_post_load() to do post-load processing which need user-interaction.
+ Added FileFormat::onPostLoad/onDestroyData members.
+ Added Document::addSprite().
This commit is contained in:
David Capello 2011-06-25 17:12:08 -03:00
parent ff481003c8
commit c32551db64
11 changed files with 402 additions and 155 deletions

View File

@ -180,6 +180,9 @@ void OpenFileCommand::onExecute(Context* context)
fop_stop(data->fop); fop_stop(data->fop);
thread.join(); thread.join();
// Post-load processing, it is called from the GUI because may require user intervention.
fop_post_load(fop);
// Show any error // Show any error
if (fop->has_error()) if (fop->has_error())
console.printf(fop->error.c_str()); console.printf(fop->error.c_str());

View File

@ -126,6 +126,12 @@ Document* Document::createBasicDocument(int imgtype, int width, int height, int
return document.release(); // Release the document (it does not throw) returning the raw pointer return document.release(); // Release the document (it does not throw) returning the raw pointer
} }
void Document::addSprite(Sprite* sprite)
{
ASSERT(m_sprite == NULL); // TODO add support for more sprites in the future (e.g. for .ico files)
m_sprite.reset(sprite);
}
const char* Document::getFilename() const const char* Document::getFilename() const
{ {
return m_filename.c_str(); return m_filename.c_str();

View File

@ -84,6 +84,8 @@ public:
Sprite* getSprite() { return m_sprite; } Sprite* getSprite() { return m_sprite; }
undo::UndoHistory* getUndoHistory() { return m_undoHistory; } undo::UndoHistory* getUndoHistory() { return m_undoHistory; }
void addSprite(Sprite* sprite);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// File related properties // File related properties

View File

@ -94,6 +94,8 @@ Document* load_document(const char* filename)
fop_operate(fop); fop_operate(fop);
fop_done(fop); fop_done(fop);
fop_post_load(fop);
if (fop->has_error()) { if (fop->has_error()) {
Console console; Console console;
console.printf(fop->error.c_str()); console.printf(fop->error.c_str());
@ -528,32 +530,6 @@ void fop_operate(FileOp *fop)
fop_error(fop, "Error loading sprite from file \"%s\"\n", fop_error(fop, "Error loading sprite from file \"%s\"\n",
fop->filename.c_str()); fop->filename.c_str());
} }
if (fop->document != NULL && fop->document->getSprite() != NULL) {
// Select the last layer
if (fop->document->getSprite()->getFolder()->get_layers_count() > 0) {
LayerIterator last_layer = --fop->document->getSprite()->getFolder()->get_layer_end();
fop->document->getSprite()->setCurrentLayer(*last_layer);
}
// Set the filename.
if (fop->is_sequence())
fop->document->setFilename(fop->seq.filename_list.begin()->c_str());
else
fop->document->setFilename(fop->filename.c_str());
// Creates a suitable palette for RGB images
if (fop->document->getSprite()->getImgType() == IMAGE_RGB &&
fop->document->getSprite()->getPalettes().size() <= 1 &&
fop->document->getSprite()->getPalette(0)->isBlack()) {
SharedPtr<Palette> palette(quantization::create_palette_from_rgb(fop->document->getSprite()));
fop->document->getSprite()->resetPalettes();
fop->document->getSprite()->setPalette(palette, false);
}
fop->document->markAsSaved();
}
} }
// Save ////////////////////////////////////////////////////////////////////// // Save //////////////////////////////////////////////////////////////////////
else if (fop->type == FileOpSave && else if (fop->type == FileOpSave &&
@ -640,6 +616,8 @@ void fop_stop(FileOp *fop)
void fop_free(FileOp *fop) void fop_free(FileOp *fop)
{ {
fop->format->destroyData(fop);
if (fop->seq.palette != NULL) if (fop->seq.palette != NULL)
delete fop->seq.palette; delete fop->seq.palette;
@ -649,6 +627,47 @@ void fop_free(FileOp *fop)
delete fop; delete fop;
} }
void fop_post_load(FileOp* fop)
{
if (fop->document == NULL)
return;
// Set the filename.
if (fop->is_sequence())
fop->document->setFilename(fop->seq.filename_list.begin()->c_str());
else
fop->document->setFilename(fop->filename.c_str());
bool result = fop->format->postLoad(fop);
if (!result) {
// Destroy the document
delete fop->document;
fop->document = NULL;
return;
}
if (fop->document->getSprite() != NULL) {
// Select the last layer
if (fop->document->getSprite()->getFolder()->get_layers_count() > 0) {
LayerIterator last_layer = --fop->document->getSprite()->getFolder()->get_layer_end();
fop->document->getSprite()->setCurrentLayer(*last_layer);
}
// Creates a suitable palette for RGB images
if (fop->document->getSprite()->getImgType() == IMAGE_RGB &&
fop->document->getSprite()->getPalettes().size() <= 1 &&
fop->document->getSprite()->getPalette(0)->isBlack()) {
SharedPtr<Palette> palette(quantization::create_palette_from_rgb(fop->document->getSprite()));
fop->document->getSprite()->resetPalettes();
fop->document->getSprite()->setPalette(palette, false);
}
}
fop->document->markAsSaved();
}
void fop_sequence_set_format_options(FileOp* fop, const SharedPtr<FormatOptions>& format_options) void fop_sequence_set_format_options(FileOp* fop, const SharedPtr<FormatOptions>& format_options)
{ {
ASSERT(fop->seq.format_options == NULL); ASSERT(fop->seq.format_options == NULL);
@ -782,6 +801,7 @@ static FileOp* fop_new(FileOpType type)
fop->type = type; fop->type = type;
fop->format = NULL; fop->format = NULL;
fop->format_data = NULL;
fop->document = NULL; fop->document = NULL;
fop->mutex = new Mutex(); fop->mutex = new Mutex();

View File

@ -50,6 +50,7 @@ struct FileOp
{ {
FileOpType type; // Operation type: 0=load, 1=save. FileOpType type; // Operation type: 0=load, 1=save.
FileFormat* format; FileFormat* format;
void* format_data; // Custom data for the FileFormat::onLoad/onSave operations.
Document* document; // Loaded document, or document to be saved. Document* document; // Loaded document, or document to be saved.
std::string filename; // File-name to load/save. std::string filename; // File-name to load/save.
@ -108,6 +109,9 @@ void fop_done(FileOp* fop);
void fop_stop(FileOp* fop); void fop_stop(FileOp* fop);
void fop_free(FileOp* fop); void fop_free(FileOp* fop);
// Does extra post-load processing which may require user intervention.
void fop_post_load(FileOp* fop);
void fop_sequence_set_format_options(FileOp* fop, const SharedPtr<FormatOptions>& format_options); void fop_sequence_set_format_options(FileOp* fop, const SharedPtr<FormatOptions>& format_options);
void fop_sequence_set_color(FileOp* fop, int index, int r, int g, int b); void fop_sequence_set_color(FileOp* fop, int index, int r, int g, int b);
void fop_sequence_get_color(FileOp* fop, int index, int *r, int *g, int *b); void fop_sequence_get_color(FileOp* fop, int index, int *r, int *g, int *b);

View File

@ -52,3 +52,13 @@ bool FileFormat::save(FileOp* fop)
ASSERT(support(FILE_SUPPORT_SAVE)); ASSERT(support(FILE_SUPPORT_SAVE));
return onSave(fop); return onSave(fop);
} }
bool FileFormat::postLoad(FileOp* fop)
{
return onPostLoad(fop);
}
void FileFormat::destroyData(FileOp* fop)
{
onDestroyData(fop);
}

View File

@ -54,6 +54,13 @@ public:
bool load(FileOp* fop); bool load(FileOp* fop);
bool save(FileOp* fop); bool save(FileOp* fop);
// Does post-load operation which require user intervention.
// Returns false cancelled the operation.
bool postLoad(FileOp* fop);
// Destroys the custom data stored in "fop->format_data" field.
void destroyData(FileOp* fop);
// Returns extra options for this format. It can return != NULL // Returns extra options for this format. It can return != NULL
// only if flags() returns FILE_SUPPORT_GET_FORMAT_OPTIONS. // only if flags() returns FILE_SUPPORT_GET_FORMAT_OPTIONS.
SharedPtr<FormatOptions> getFormatOptions(FileOp* fop) { SharedPtr<FormatOptions> getFormatOptions(FileOp* fop) {
@ -71,7 +78,9 @@ protected:
virtual int onGetFlags() const = 0; virtual int onGetFlags() const = 0;
virtual bool onLoad(FileOp* fop) = 0; virtual bool onLoad(FileOp* fop) = 0;
virtual bool onPostLoad(FileOp* fop) { return true; }
virtual bool onSave(FileOp* fop) = 0; virtual bool onSave(FileOp* fop) = 0;
virtual void onDestroyData(FileOp* fop) { }
virtual SharedPtr<FormatOptions> onGetFormatOptions(FileOp* fop) { virtual SharedPtr<FormatOptions> onGetFormatOptions(FileOp* fop) {
return SharedPtr<FormatOptions>(0); return SharedPtr<FormatOptions>(0);

View File

@ -37,6 +37,34 @@ enum DisposalMethod {
DISPOSAL_METHOD_RESTORE_PREVIOUS, DISPOSAL_METHOD_RESTORE_PREVIOUS,
}; };
struct GifFrame
{
int x, y;
int duration;
int mask_index;
Image* image;
Palette* palette;
DisposalMethod disposal_method;
GifFrame()
: x(0), y(0)
, mask_index(-1)
, image(0)
, palette(0)
, disposal_method(DISPOSAL_METHOD_NONE) {
}
};
typedef std::vector<GifFrame> GifFrames;
struct GifData
{
int sprite_w;
int sprite_h;
int bgcolor_index;
GifFrames frames;
};
class GifFormat : public FileFormat class GifFormat : public FileFormat
{ {
const char* onGetName() const { return "gif"; } const char* onGetName() const { return "gif"; }
@ -55,6 +83,8 @@ class GifFormat : public FileFormat
} }
bool onLoad(FileOp* fop); bool onLoad(FileOp* fop);
bool onPostLoad(FileOp* fop) OVERRIDE;
void onDestroyData(FileOp* fop) OVERRIDE;
bool onSave(FileOp* fop); bool onSave(FileOp* fop);
}; };
@ -75,29 +105,19 @@ bool GifFormat::onLoad(FileOp* fop)
return false; return false;
} }
int sprite_w = gif_file->SWidth; GifData* data = new GifData;
int sprite_h = gif_file->SHeight; fop->format_data = reinterpret_cast<void*>(data);
data->sprite_w = gif_file->SWidth;
data->sprite_h = gif_file->SHeight;
// The previous image is used to support the special disposal method
// of GIF frames DISPOSAL_METHOD_RESTORE_PREVIOUS (number 3 in
// Graphics Extension)
UniquePtr<Image> current_image(image_new(IMAGE_RGB, sprite_w, sprite_h));
UniquePtr<Image> previous_image(image_new(IMAGE_RGB, sprite_w, sprite_h));
UniquePtr<Palette> current_palette(new Palette(0, 256)); UniquePtr<Palette> current_palette(new Palette(0, 256));
UniquePtr<Palette> previous_palette(new Palette(0, 256)); UniquePtr<Palette> previous_palette(new Palette(0, 256));
// Create the sprite with the GIF dimension
UniquePtr<Sprite> sprite(new Sprite(IMAGE_RGB, sprite_w, sprite_h, 256));
// Create the main layer
LayerImage* layer = new LayerImage(sprite);
sprite->getFolder()->add_layer(layer);
// If the GIF image has a global palette, it has a valid // If the GIF image has a global palette, it has a valid
// background color (so the GIF is not transparent). // background color (so the GIF is not transparent).
int bgcolor_index;
if (gif_file->SColorMap != NULL) { if (gif_file->SColorMap != NULL) {
bgcolor_index = gif_file->SBackGroundColor; data->bgcolor_index = gif_file->SBackGroundColor;
// Setup the first palette using the global color map. // Setup the first palette using the global color map.
ColorMapObject* colormap = gif_file->SColorMap; ColorMapObject* colormap = gif_file->SColorMap;
@ -108,16 +128,12 @@ bool GifFormat::onLoad(FileOp* fop)
} }
} }
else { else {
bgcolor_index = 0; data->bgcolor_index = -1;
} }
// 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));
// Scan the content of the GIF file (read record by record) // Scan the content of the GIF file (read record by record)
GifRecordType record_type; GifRecordType record_type;
int frame_num = 0; size_t frame_num = 0;
DisposalMethod disposal_method = DISPOSAL_METHOD_NONE; DisposalMethod disposal_method = DISPOSAL_METHOD_NONE;
int transparent_index = -1; int transparent_index = -1;
int frame_delay = -1; int frame_delay = -1;
@ -138,16 +154,20 @@ bool GifFormat::onLoad(FileOp* fop)
int frame_h = gif_file->Image.Height; int frame_h = gif_file->Image.Height;
if (frame_x < 0 || frame_y < 0 || if (frame_x < 0 || frame_y < 0 ||
frame_x + frame_w > sprite_w || frame_x + frame_w > data->sprite_w ||
frame_y + frame_h > sprite_h) frame_y + frame_h > data->sprite_h)
throw base::Exception("Image %d is out of sprite bounds.\n", frame_num); throw base::Exception("Image %d is out of sprite bounds.\n", frame_num);
// Add a new frame in the sprite. // Add a new frames.
sprite->setTotalFrames(frame_num+1); if (frame_num >= data->frames.size())
data->frames.resize(frame_num+1);
data->frames[frame_num].x = frame_x;
data->frames[frame_num].y = frame_y;
// Set frame delay (1/100th seconds to milliseconds) // Set frame delay (1/100th seconds to milliseconds)
if (frame_delay >= 0) if (frame_delay >= 0)
sprite->setFrameDuration(frame_num, frame_delay*10); data->frames[frame_num].duration = frame_delay*10;
// Update palette for this frame (the first frame always need a palette). // Update palette for this frame (the first frame always need a palette).
if (gif_file->Image.ColorMap) { if (gif_file->Image.ColorMap) {
@ -161,7 +181,9 @@ bool GifFormat::onLoad(FileOp* fop)
if (frame_num == 0 || previous_palette->countDiff(current_palette, NULL, NULL)) { if (frame_num == 0 || previous_palette->countDiff(current_palette, NULL, NULL)) {
current_palette->setFrame(frame_num); current_palette->setFrame(frame_num);
sprite->setPalette(current_palette, true);
data->frames[frame_num].palette = new Palette(*current_palette);
data->frames[frame_num].palette->setFrame(frame_num);
current_palette->copyColorsTo(previous_palette); current_palette->copyColorsTo(previous_palette);
} }
@ -187,68 +209,12 @@ bool GifFormat::onLoad(FileOp* fop)
} }
} }
// Convert the indexed image to RGB // Detach the pointer of the frame-image and put it in the list of frames.
for (int y = 0; y < frame_h; ++y) data->frames[frame_num].image = frame_image.release();
for (int x = 0; x < frame_w; ++x) { data->frames[frame_num].disposal_method = disposal_method;
int pixel_index = image_getpixel_fast<IndexedTraits>(frame_image, x, y); data->frames[frame_num].mask_index = transparent_index;
if (pixel_index != transparent_index)
image_putpixel_fast<RgbTraits>(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" PRINTF("Frame[%d] transparent index = %d\n", frame_num, transparent_index);
Cel* cel = new Cel(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->setImage(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; ++frame_num;
@ -298,12 +264,222 @@ bool GifFormat::onLoad(FileOp* fop)
break; break;
} while (record_type != TERMINATE_RECORD_TYPE); } while (record_type != TERMINATE_RECORD_TYPE);
fop->document = new Document(sprite); fop->document = new Document(NULL);
return true;
}
bool GifFormat::onPostLoad(FileOp* fop)
{
GifData* data = reinterpret_cast<GifData*>(fop->format_data);
if (!data)
return true;
int imgtype = IMAGE_INDEXED;
bool askForConversion = false;
if (!fop->oneframe) {
int global_mask_index = -1;
for (GifFrames::iterator
frame_it=data->frames.begin(),
frame_end=data->frames.end(); frame_it != frame_end; ++frame_it) {
// Convert the indexed image to RGB
for (int y=0; y<frame_it->image->h; ++y) {
for (int x=0; x<frame_it->image->w; ++x) {
int pixel_index = image_getpixel_fast<IndexedTraits>(frame_it->image, x, y);
if (pixel_index >= 0 && pixel_index < 256) {
// This pixel matches the frame's transparent color
if (pixel_index == frame_it->mask_index) {
// If we haven't set a background color yet, this is our new background color.
if (global_mask_index < 0) {
global_mask_index = pixel_index;
}
}
else {
// Drawing the mask color
if (global_mask_index == pixel_index) {
askForConversion = true;
goto done;
}
}
}
}
}
}
// New background color
data->bgcolor_index = global_mask_index;
done:;
}
if (askForConversion) {
int result =
Alert::show("GIF Conversion"
"<<The selected file: %s"
"<<is a transparent GIF image which uses multiple background colors."
"<<ASEPRITE cannot handle this kind of GIF correctly in Indexed format."
"<<What would you like to do?"
"||Convert to &RGBA||Keep &Indexed||&Cancel",
fop->document->getFilename());
if (result == 1)
imgtype = IMAGE_RGB;
else if (result != 2)
return false;
}
// Create the sprite with the GIF dimension
UniquePtr<Sprite> sprite(new Sprite(imgtype, data->sprite_w, data->sprite_h, 256));
// Create the main layer
LayerImage* layer = new LayerImage(sprite);
sprite->getFolder()->add_layer(layer);
if (imgtype == IMAGE_INDEXED) {
if (data->bgcolor_index >= 0)
sprite->setTransparentColor(data->bgcolor_index);
else
layer->configureAsBackground();
}
// The previous image is used to support the special disposal method
// of GIF frames DISPOSAL_METHOD_RESTORE_PREVIOUS (number 3 in
// Graphics Extension)
UniquePtr<Image> current_image(image_new(imgtype, data->sprite_w, data->sprite_h));
UniquePtr<Image> previous_image(image_new(imgtype, data->sprite_w, data->sprite_h));
// Clear both images with the transparent color (alpha = 0).
uint32_t bgcolor = (imgtype == IMAGE_RGB ? _rgba(0, 0, 0, 0):
(data->bgcolor_index >= 0 ? data->bgcolor_index: 0));
image_clear(current_image, bgcolor);
image_clear(previous_image, bgcolor);
// Add all frames in the sprite.
sprite->setTotalFrames(data->frames.size());
Palette* current_palette = NULL;
size_t frame_num = 0;
for (GifFrames::iterator
frame_it=data->frames.begin(),
frame_end=data->frames.end(); frame_it != frame_end; ++frame_it, ++frame_num) {
// Set frame duration
sprite->setFrameDuration(frame_num, frame_it->duration);
// Set frame palette
if (frame_it->palette) {
sprite->setPalette(frame_it->palette, true);
current_palette = frame_it->palette;
}
switch (imgtype) {
case IMAGE_INDEXED:
for (int y = 0; y < frame_it->image->h; ++y)
for (int x = 0; x < frame_it->image->w; ++x) {
int pixel_index = image_getpixel_fast<IndexedTraits>(frame_it->image, x, y);
if (pixel_index != frame_it->mask_index)
image_putpixel_fast<IndexedTraits>(current_image,
frame_it->x + x,
frame_it->y + y,
pixel_index);
}
break;
case IMAGE_RGB:
// Convert the indexed image to RGB
for (int y = 0; y < frame_it->image->h; ++y)
for (int x = 0; x < frame_it->image->w; ++x) {
int pixel_index = image_getpixel_fast<IndexedTraits>(frame_it->image, x, y);
if (pixel_index != frame_it->mask_index)
image_putpixel_fast<RgbTraits>(current_image,
frame_it->x + x,
frame_it->y + y,
current_palette->getEntry(pixel_index));
}
break;
}
// Create a new Cel and a image with the whole content of "current_image"
Cel* cel = new Cel(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->setImage(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 (frame_it->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_it->x,
frame_it->y,
frame_it->x+frame_it->image->w-1,
frame_it->y+frame_it->image->h-1,
bgcolor);
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 (frame_it->disposal_method != DISPOSAL_METHOD_RESTORE_PREVIOUS)
image_copy(previous_image, current_image, 0, 0);
}
fop->document->addSprite(sprite);
sprite.release(); // Now the sprite is owned by fop->document sprite.release(); // Now the sprite is owned by fop->document
return true; return true;
} }
void GifFormat::onDestroyData(FileOp* fop)
{
GifData* data = reinterpret_cast<GifData*>(fop->format_data);
if (data) {
GifFrames::iterator frame_it = data->frames.begin();
GifFrames::iterator frame_end = data->frames.end();
for (; frame_it != frame_end; ++frame_it) {
delete frame_it->image;
delete frame_it->palette;
}
delete data;
}
}
bool GifFormat::onSave(FileOp* fop) bool GifFormat::onSave(FileOp* fop)
{ {
UniquePtr<GifFileType, int(*)(GifFileType*)> gif_file(EGifOpenFileName(fop->filename.c_str(), 0), UniquePtr<GifFileType, int(*)(GifFileType*)> gif_file(EGifOpenFileName(fop->filename.c_str(), 0),
@ -315,10 +491,10 @@ bool GifFormat::onSave(FileOp* fop)
int sprite_w = sprite->getWidth(); int sprite_w = sprite->getWidth();
int sprite_h = sprite->getHeight(); int sprite_h = sprite->getHeight();
int sprite_imgtype = sprite->getImgType(); int sprite_imgtype = sprite->getImgType();
int background_color = 0;
bool interlace = false; bool interlace = false;
int loop = 0; int loop = 0;
int transparent_index = (sprite->getBackgroundLayer() == NULL) ? 0: -1; int background_color = (sprite_imgtype == IMAGE_INDEXED ? sprite->getTransparentColor(): 0);
int transparent_index = (sprite->getBackgroundLayer() ? -1: sprite->getTransparentColor());
Palette* current_palette = sprite->getPalette(0); Palette* current_palette = sprite->getPalette(0);
Palette* previous_palette = current_palette; Palette* previous_palette = current_palette;
@ -390,7 +566,7 @@ bool GifFormat::onSave(FileOp* fop)
} }
// If the sprite is Indexed, we can render directly into "current_image". // If the sprite is Indexed, we can render directly into "current_image".
else { else {
image_clear(current_image, 0); image_clear(current_image, background_color);
layer_render(sprite->getFolder(), current_image, 0, 0, frame_num); layer_render(sprite->getFolder(), current_image, 0, 0, frame_num);
} }
@ -435,14 +611,15 @@ bool GifFormat::onSave(FileOp* fop)
// frame and maybe the transparency index). // frame and maybe the transparency index).
{ {
unsigned char extension_bytes[5]; unsigned char extension_bytes[5];
int disposal_method = (sprite->getBackgroundLayer() == NULL) ? 2: 1; int disposal_method = (sprite->getBackgroundLayer() ? DISPOSAL_METHOD_DO_NOT_DISPOSE:
DISPOSAL_METHOD_RESTORE_BGCOLOR);
int frame_delay = sprite->getFrameDuration(frame_num) / 10; int frame_delay = sprite->getFrameDuration(frame_num) / 10;
extension_bytes[0] = (((disposal_method & 7) << 2) | extension_bytes[0] = (((disposal_method & 7) << 2) |
(transparent_index >= 0 ? 1: 0)); (transparent_index >= 0 ? 1: 0));
extension_bytes[1] = (frame_delay & 0xff); extension_bytes[1] = (frame_delay & 0xff);
extension_bytes[2] = (frame_delay >> 8) & 0xff; extension_bytes[2] = (frame_delay >> 8) & 0xff;
extension_bytes[3] = transparent_index; extension_bytes[3] = (transparent_index >= 0 ? transparent_index: 0);
if (EGifPutExtension(gif_file, GRAPHICS_EXT_FUNC_CODE, 4, extension_bytes) == GIF_ERROR) if (EGifPutExtension(gif_file, GRAPHICS_EXT_FUNC_CODE, 4, extension_bytes) == GIF_ERROR)
throw base::Exception("Error writing GIF graphics extension record for frame %d.\n", frame_num); throw base::Exception("Error writing GIF graphics extension record for frame %d.\n", frame_num);

View File

@ -364,6 +364,8 @@ void layer_render(const Layer* layer, Image* image, int x, int y, int frame)
src_image = layer->getSprite()->getStock()->getImage(cel->getImage()); src_image = layer->getSprite()->getStock()->getImage(cel->getImage());
ASSERT(src_image != NULL); ASSERT(src_image != NULL);
src_image->mask_color = layer->getSprite()->getTransparentColor();
image_merge(image, src_image, image_merge(image, src_image,
cel->getX() + x, cel->getX() + x,
cel->getY() + y, cel->getY() + y,

View File

@ -392,7 +392,7 @@ Image* RenderEngine::renderSprite(const Document* document,
case IMAGE_INDEXED: case IMAGE_INDEXED:
zoomed_func = merge_zoomed_image<RgbTraits, IndexedTraits>; zoomed_func = merge_zoomed_image<RgbTraits, IndexedTraits>;
if (!need_checked_bg) if (!need_checked_bg)
bg_color = sprite->getPalette(frame)->getEntry(0); bg_color = sprite->getPalette(frame)->getEntry(sprite->getTransparentColor());
break; break;
default: default:
@ -571,7 +571,7 @@ void RenderEngine::renderLayer(const Document* document,
const Cel* cel = static_cast<const LayerImage*>(layer)->getCel(frame); const Cel* cel = static_cast<const LayerImage*>(layer)->getCel(frame);
if (cel != NULL) { if (cel != NULL) {
const Image* src_image; Image* src_image;
/* is the 'rastering_image' setted to be used with this layer? */ /* is the 'rastering_image' setted to be used with this layer? */
if ((frame == sprite->getCurrentFrame()) && if ((frame == sprite->getCurrentFrame()) &&
@ -593,6 +593,8 @@ void RenderEngine::renderLayer(const Document* document,
output_opacity = MID(0, cel->getOpacity(), 255); output_opacity = MID(0, cel->getOpacity(), 255);
output_opacity = INT_MULT(output_opacity, global_opacity, t); output_opacity = INT_MULT(output_opacity, global_opacity, t);
src_image->mask_color = sprite->getTransparentColor();
(*zoomed_func)(image, src_image, sprite->getPalette(frame), (*zoomed_func)(image, src_image, sprite->getPalette(frame),
(cel->getX() << zoom) - source_x, (cel->getX() << zoom) - source_x,
(cel->getY() << zoom) - source_y, (cel->getY() << zoom) - source_y,

View File

@ -755,12 +755,38 @@ static void fileview_stop_threads(FileView* fileview)
static void openfile_bg(ThumbnailData* data) static void openfile_bg(ThumbnailData* data)
{ {
FileOp* fop = (FileOp*)data->fop; FileOp* fop = (FileOp*)data->fop;
try {
fop_operate(fop);
}
catch (const std::exception& e) {
fop_error(fop, "Error loading file:\n%s", e.what());
}
fop_done(fop);
}
/**
* Called by the GUI-monitor (a timer in the gui module that is called
* every 100 milliseconds).
*
* [main thread]
*/
static void monitor_thumbnail_generation(void *_data)
{
ThumbnailData* data = (ThumbnailData*)_data;
FileOp* fop = data->fop;
/* is done? ...ok, now the thumbnail is in the main thread only... */
if (fop_is_done(fop)) {
// Post load
fop_post_load(fop);
// Convert the loaded document into the Allegro bitmap "data->thumbnail".
{
int thumb_w, thumb_h; int thumb_w, thumb_h;
Image* image; Image* image;
// Load the file.
fop_operate(fop);
Sprite* sprite = (fop->document && fop->document->getSprite()) ? fop->document->getSprite(): Sprite* sprite = (fop->document && fop->document->getSprite()) ? fop->document->getSprite():
NULL; NULL;
if (!fop_is_stop(fop) && sprite) { if (!fop_is_stop(fop) && sprite) {
@ -790,22 +816,8 @@ static void openfile_bg(ThumbnailData* data)
} }
delete fop->document; delete fop->document;
fop_done(fop); }
}
/**
* Called by the GUI-monitor (a timer in the gui module that is called
* every 100 milliseconds).
*
* [main thread]
*/
static void monitor_thumbnail_generation(void *_data)
{
ThumbnailData* data = (ThumbnailData*)_data;
FileOp* fop = data->fop;
/* is done? ...ok, now the thumbnail is in the main thread only... */
if (fop_is_done(fop)) {
/* set the thumbnail of the file-item */ /* set the thumbnail of the file-item */
if (data->thumbnail) { if (data->thumbnail) {
BITMAP *bmp = create_bitmap_ex(16, BITMAP *bmp = create_bitmap_ex(16,