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);
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());

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
}
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();

View File

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

View File

@ -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> 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> 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)
{
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();

View File

@ -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<FormatOptions>& 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);

View File

@ -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);
}

View File

@ -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<FormatOptions> 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<FormatOptions> onGetFormatOptions(FileOp* fop) {
return SharedPtr<FormatOptions>(0);

View File

@ -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<GifFrame> 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<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> 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
// 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<IndexedTraits>(frame_image, x, y);
if (pixel_index != transparent_index)
image_putpixel_fast<RgbTraits>(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<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
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)
{
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_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);

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());
ASSERT(src_image != NULL);
src_image->mask_color = layer->getSprite()->getTransparentColor();
image_merge(image, src_image,
cel->getX() + x,
cel->getY() + y,

View File

@ -392,7 +392,7 @@ Image* RenderEngine::renderSprite(const Document* document,
case IMAGE_INDEXED:
zoomed_func = merge_zoomed_image<RgbTraits, IndexedTraits>;
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<const LayerImage*>(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,

View File

@ -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,