From c32551db64a2dc3cf76ba9bd1731a00c81b72006 Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 25 Jun 2011 17:12:08 -0300 Subject: [PATCH] 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(). --- src/commands/cmd_open_file.cpp | 3 + src/document.cpp | 6 + src/document.h | 2 + src/file/file.cpp | 72 ++++--- src/file/file.h | 4 + src/file/file_format.cpp | 10 + src/file/file_format.h | 9 + src/file/gif_format.cpp | 367 ++++++++++++++++++++++++--------- src/raster/layer.cpp | 2 + src/util/render.cpp | 6 +- src/widgets/fileview.cpp | 76 ++++--- 11 files changed, 402 insertions(+), 155 deletions(-) diff --git a/src/commands/cmd_open_file.cpp b/src/commands/cmd_open_file.cpp index 796ad5201..4901fdfbb 100644 --- a/src/commands/cmd_open_file.cpp +++ b/src/commands/cmd_open_file.cpp @@ -180,6 +180,9 @@ void OpenFileCommand::onExecute(Context* context) fop_stop(data->fop); thread.join(); + // Post-load processing, it is called from the GUI because may require user intervention. + fop_post_load(fop); + // Show any error if (fop->has_error()) console.printf(fop->error.c_str()); diff --git a/src/document.cpp b/src/document.cpp index 22913bc10..bf008e74e 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -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 } +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 { return m_filename.c_str(); diff --git a/src/document.h b/src/document.h index 48ea6db92..c5dc6eb41 100644 --- a/src/document.h +++ b/src/document.h @@ -84,6 +84,8 @@ public: Sprite* getSprite() { return m_sprite; } undo::UndoHistory* getUndoHistory() { return m_undoHistory; } + void addSprite(Sprite* sprite); + ////////////////////////////////////////////////////////////////////// // File related properties diff --git a/src/file/file.cpp b/src/file/file.cpp index 094255625..4584aabdf 100644 --- a/src/file/file.cpp +++ b/src/file/file.cpp @@ -94,6 +94,8 @@ Document* load_document(const char* filename) fop_operate(fop); fop_done(fop); + fop_post_load(fop); + if (fop->has_error()) { Console console; 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->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(quantization::create_palette_from_rgb(fop->document->getSprite())); - - fop->document->getSprite()->resetPalettes(); - fop->document->getSprite()->setPalette(palette, false); - } - - fop->document->markAsSaved(); - } } // Save ////////////////////////////////////////////////////////////////////// else if (fop->type == FileOpSave && @@ -640,6 +616,8 @@ void fop_stop(FileOp *fop) void fop_free(FileOp *fop) { + fop->format->destroyData(fop); + if (fop->seq.palette != NULL) delete fop->seq.palette; @@ -649,6 +627,47 @@ void fop_free(FileOp *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(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& format_options) { ASSERT(fop->seq.format_options == NULL); @@ -782,6 +801,7 @@ static FileOp* fop_new(FileOpType type) fop->type = type; fop->format = NULL; + fop->format_data = NULL; fop->document = NULL; fop->mutex = new Mutex(); diff --git a/src/file/file.h b/src/file/file.h index e1cf88188..e9184c2ba 100644 --- a/src/file/file.h +++ b/src/file/file.h @@ -50,6 +50,7 @@ struct FileOp { FileOpType type; // Operation type: 0=load, 1=save. FileFormat* format; + void* format_data; // Custom data for the FileFormat::onLoad/onSave operations. Document* document; // Loaded document, or document to be saved. std::string filename; // File-name to load/save. @@ -108,6 +109,9 @@ void fop_done(FileOp* fop); void fop_stop(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& format_options); 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); diff --git a/src/file/file_format.cpp b/src/file/file_format.cpp index 29be55b9d..de49c99b0 100644 --- a/src/file/file_format.cpp +++ b/src/file/file_format.cpp @@ -52,3 +52,13 @@ bool FileFormat::save(FileOp* fop) ASSERT(support(FILE_SUPPORT_SAVE)); return onSave(fop); } + +bool FileFormat::postLoad(FileOp* fop) +{ + return onPostLoad(fop); +} + +void FileFormat::destroyData(FileOp* fop) +{ + onDestroyData(fop); +} diff --git a/src/file/file_format.h b/src/file/file_format.h index 97d37ad13..0997943a4 100644 --- a/src/file/file_format.h +++ b/src/file/file_format.h @@ -54,6 +54,13 @@ public: bool load(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 // only if flags() returns FILE_SUPPORT_GET_FORMAT_OPTIONS. SharedPtr getFormatOptions(FileOp* fop) { @@ -71,7 +78,9 @@ protected: virtual int onGetFlags() const = 0; virtual bool onLoad(FileOp* fop) = 0; + virtual bool onPostLoad(FileOp* fop) { return true; } virtual bool onSave(FileOp* fop) = 0; + virtual void onDestroyData(FileOp* fop) { } virtual SharedPtr onGetFormatOptions(FileOp* fop) { return SharedPtr(0); diff --git a/src/file/gif_format.cpp b/src/file/gif_format.cpp index f918b9472..aa1213ccb 100644 --- a/src/file/gif_format.cpp +++ b/src/file/gif_format.cpp @@ -37,6 +37,34 @@ enum DisposalMethod { 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 GifFrames; + +struct GifData +{ + int sprite_w; + int sprite_h; + int bgcolor_index; + GifFrames frames; +}; + class GifFormat : public FileFormat { const char* onGetName() const { return "gif"; } @@ -55,6 +83,8 @@ class GifFormat : public FileFormat } bool onLoad(FileOp* fop); + bool onPostLoad(FileOp* fop) OVERRIDE; + void onDestroyData(FileOp* fop) OVERRIDE; bool onSave(FileOp* fop); }; @@ -75,29 +105,19 @@ bool GifFormat::onLoad(FileOp* fop) return false; } - int sprite_w = gif_file->SWidth; - int sprite_h = gif_file->SHeight; + GifData* data = new GifData; + fop->format_data = reinterpret_cast(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 current_image(image_new(IMAGE_RGB, sprite_w, sprite_h)); - UniquePtr previous_image(image_new(IMAGE_RGB, sprite_w, sprite_h)); UniquePtr current_palette(new Palette(0, 256)); UniquePtr previous_palette(new Palette(0, 256)); - // Create the sprite with the GIF dimension - UniquePtr 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 // background color (so the GIF is not transparent). - int bgcolor_index; 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. ColorMapObject* colormap = gif_file->SColorMap; @@ -108,16 +128,12 @@ bool GifFormat::onLoad(FileOp* fop) } } 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) GifRecordType record_type; - int frame_num = 0; + size_t frame_num = 0; DisposalMethod disposal_method = DISPOSAL_METHOD_NONE; int transparent_index = -1; int frame_delay = -1; @@ -138,16 +154,20 @@ bool GifFormat::onLoad(FileOp* fop) int frame_h = gif_file->Image.Height; if (frame_x < 0 || frame_y < 0 || - frame_x + frame_w > sprite_w || - frame_y + frame_h > sprite_h) + frame_x + frame_w > data->sprite_w || + frame_y + frame_h > data->sprite_h) throw base::Exception("Image %d is out of sprite bounds.\n", frame_num); - // Add a new frame in the sprite. - sprite->setTotalFrames(frame_num+1); + // Add a new frames. + 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) 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). 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)) { 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); } @@ -187,68 +209,12 @@ bool GifFormat::onLoad(FileOp* fop) } } - // 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)); - } + // Detach the pointer of the frame-image and put it in the list of frames. + data->frames[frame_num].image = frame_image.release(); + data->frames[frame_num].disposal_method = disposal_method; + data->frames[frame_num].mask_index = transparent_index; - // 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 (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); + PRINTF("Frame[%d] transparent index = %d\n", frame_num, transparent_index); ++frame_num; @@ -298,12 +264,222 @@ bool GifFormat::onLoad(FileOp* fop) break; } 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(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; yimage->h; ++y) { + for (int x=0; ximage->w; ++x) { + int pixel_index = image_getpixel_fast(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" + "<document->getFilename()); + + if (result == 1) + imgtype = IMAGE_RGB; + else if (result != 2) + return false; + } + + // Create the sprite with the GIF dimension + UniquePtr 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 current_image(image_new(imgtype, data->sprite_w, data->sprite_h)); + UniquePtr 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(frame_it->image, x, y); + if (pixel_index != frame_it->mask_index) + image_putpixel_fast(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(frame_it->image, x, y); + if (pixel_index != frame_it->mask_index) + image_putpixel_fast(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 return true; } +void GifFormat::onDestroyData(FileOp* fop) +{ + GifData* data = reinterpret_cast(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) { UniquePtr gif_file(EGifOpenFileName(fop->filename.c_str(), 0), @@ -315,10 +491,10 @@ bool GifFormat::onSave(FileOp* fop) 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; + 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* 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". else { - image_clear(current_image, 0); + image_clear(current_image, background_color); 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). { 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; 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; + extension_bytes[3] = (transparent_index >= 0 ? transparent_index: 0); 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); diff --git a/src/raster/layer.cpp b/src/raster/layer.cpp index b996a975e..d1feffbb3 100644 --- a/src/raster/layer.cpp +++ b/src/raster/layer.cpp @@ -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()); ASSERT(src_image != NULL); + src_image->mask_color = layer->getSprite()->getTransparentColor(); + image_merge(image, src_image, cel->getX() + x, cel->getY() + y, diff --git a/src/util/render.cpp b/src/util/render.cpp index f72a8aa32..f86736a34 100644 --- a/src/util/render.cpp +++ b/src/util/render.cpp @@ -392,7 +392,7 @@ Image* RenderEngine::renderSprite(const Document* document, case IMAGE_INDEXED: zoomed_func = merge_zoomed_image; if (!need_checked_bg) - bg_color = sprite->getPalette(frame)->getEntry(0); + bg_color = sprite->getPalette(frame)->getEntry(sprite->getTransparentColor()); break; default: @@ -571,7 +571,7 @@ void RenderEngine::renderLayer(const Document* document, const Cel* cel = static_cast(layer)->getCel(frame); if (cel != NULL) { - const Image* src_image; + Image* src_image; /* is the 'rastering_image' setted to be used with this layer? */ if ((frame == sprite->getCurrentFrame()) && @@ -593,6 +593,8 @@ void RenderEngine::renderLayer(const Document* document, output_opacity = MID(0, cel->getOpacity(), 255); output_opacity = INT_MULT(output_opacity, global_opacity, t); + src_image->mask_color = sprite->getTransparentColor(); + (*zoomed_func)(image, src_image, sprite->getPalette(frame), (cel->getX() << zoom) - source_x, (cel->getY() << zoom) - source_y, diff --git a/src/widgets/fileview.cpp b/src/widgets/fileview.cpp index 4fe3e2406..4b4393201 100644 --- a/src/widgets/fileview.cpp +++ b/src/widgets/fileview.cpp @@ -755,41 +755,14 @@ static void fileview_stop_threads(FileView* fileview) static void openfile_bg(ThumbnailData* data) { FileOp* fop = (FileOp*)data->fop; - int thumb_w, thumb_h; - Image* image; - // Load the file. - fop_operate(fop); - - Sprite* sprite = (fop->document && fop->document->getSprite()) ? fop->document->getSprite(): - NULL; - if (!fop_is_stop(fop) && sprite) { - // The palette to convert the Image to a BITMAP - data->palette = new Palette(*sprite->getPalette(0)); - - // Render the 'sprite' in one plain 'image' - image = image_new(sprite->getImgType(), sprite->getWidth(), sprite->getHeight()); - image_clear(image, 0); - sprite->render(image, 0, 0); - - // Calculate the thumbnail size - thumb_w = MAX_THUMBNAIL_SIZE * image->w / MAX(image->w, image->h); - thumb_h = MAX_THUMBNAIL_SIZE * image->h / MAX(image->w, image->h); - if (MAX(thumb_w, thumb_h) > MAX(image->w, image->h)) { - thumb_w = image->w; - thumb_h = image->h; - } - thumb_w = MID(1, thumb_w, MAX_THUMBNAIL_SIZE); - thumb_h = MID(1, thumb_h, MAX_THUMBNAIL_SIZE); - - // Stretch the 'image' - data->thumbnail = image_new(image->imgtype, thumb_w, thumb_h); - image_clear(data->thumbnail, 0); - image_scale(data->thumbnail, image, 0, 0, thumb_w, thumb_h); - image_free(image); + try { + fop_operate(fop); + } + catch (const std::exception& e) { + fop_error(fop, "Error loading file:\n%s", e.what()); } - delete fop->document; fop_done(fop); } @@ -806,6 +779,45 @@ static void monitor_thumbnail_generation(void *_data) /* 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; + Image* image; + + Sprite* sprite = (fop->document && fop->document->getSprite()) ? fop->document->getSprite(): + NULL; + if (!fop_is_stop(fop) && sprite) { + // The palette to convert the Image to a BITMAP + data->palette = new Palette(*sprite->getPalette(0)); + + // Render the 'sprite' in one plain 'image' + image = image_new(sprite->getImgType(), sprite->getWidth(), sprite->getHeight()); + image_clear(image, 0); + sprite->render(image, 0, 0); + + // Calculate the thumbnail size + thumb_w = MAX_THUMBNAIL_SIZE * image->w / MAX(image->w, image->h); + thumb_h = MAX_THUMBNAIL_SIZE * image->h / MAX(image->w, image->h); + if (MAX(thumb_w, thumb_h) > MAX(image->w, image->h)) { + thumb_w = image->w; + thumb_h = image->h; + } + thumb_w = MID(1, thumb_w, MAX_THUMBNAIL_SIZE); + thumb_h = MID(1, thumb_h, MAX_THUMBNAIL_SIZE); + + // Stretch the 'image' + data->thumbnail = image_new(image->imgtype, thumb_w, thumb_h); + image_clear(data->thumbnail, 0); + image_scale(data->thumbnail, image, 0, 0, thumb_w, thumb_h); + image_free(image); + } + + delete fop->document; + } + /* set the thumbnail of the file-item */ if (data->thumbnail) { BITMAP *bmp = create_bitmap_ex(16,