diff --git a/data/widgets/gif_options.xml b/data/widgets/gif_options.xml
new file mode 100644
index 000000000..3c901c05e
--- /dev/null
+++ b/data/widgets/gif_options.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/app.cpp b/src/app/app.cpp
index 148dda847..b3c5f33f8 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -214,7 +214,7 @@ int App::run()
end = m_files.end();
it != end; ++it) {
// Load the sprite
- Document* document = load_document(it->c_str());
+ Document* document = load_document(context, it->c_str());
if (!document) {
if (!isGui())
console.printf("Error loading file \"%s\"\n", it->c_str());
diff --git a/src/app/commands/cmd_open_file.cpp b/src/app/commands/cmd_open_file.cpp
index 4859d6205..c1bc5e140 100644
--- a/src/app/commands/cmd_open_file.cpp
+++ b/src/app/commands/cmd_open_file.cpp
@@ -126,7 +126,7 @@ void OpenFileCommand::onExecute(Context* context)
}
if (!m_filename.empty()) {
- base::UniquePtr fop(fop_to_load_document(m_filename.c_str(), FILE_LOAD_SEQUENCE_ASK));
+ base::UniquePtr fop(fop_to_load_document(context, m_filename.c_str(), FILE_LOAD_SEQUENCE_ASK));
bool unrecent = false;
if (fop) {
diff --git a/src/app/commands/cmd_palette_editor.cpp b/src/app/commands/cmd_palette_editor.cpp
index 4fcfabfa9..ce382de0a 100644
--- a/src/app/commands/cmd_palette_editor.cpp
+++ b/src/app/commands/cmd_palette_editor.cpp
@@ -587,7 +587,8 @@ void PaletteEntryEditor::onQuantizeClick(Event& ev)
return;
}
- palette = quantization::create_palette_from_rgb(sprite, reader.frame());
+ palette = quantization::create_palette_from_rgb(
+ sprite, reader.frame(), NULL);
}
setNewPalette(palette, "Quantize Palette");
diff --git a/src/app/commands/cmd_save_file.cpp b/src/app/commands/cmd_save_file.cpp
index 1545f229d..9b40b5189 100644
--- a/src/app/commands/cmd_save_file.cpp
+++ b/src/app/commands/cmd_save_file.cpp
@@ -81,9 +81,9 @@ private:
FileOp* m_fop;
};
-static void save_document_in_background(Document* document, bool mark_as_saved)
+static void save_document_in_background(Context* context, Document* document, bool mark_as_saved)
{
- base::UniquePtr fop(fop_to_save_document(document));
+ base::UniquePtr fop(fop_to_save_document(context, document));
if (!fop)
return;
@@ -192,7 +192,7 @@ protected:
m_selectedFilename = filename;
// Save the document
- save_document_in_background(documentWriter, markAsSaved);
+ save_document_in_background(writer.context(), documentWriter, markAsSaved);
if (documentWriter->isModified())
documentWriter->setFilename(oldFilename);
@@ -251,7 +251,7 @@ void SaveFileCommand::onExecute(Context* context)
if (!confirmReadonly(documentWriter->getFilename()))
return;
- save_document_in_background(documentWriter, true);
+ save_document_in_background(context, documentWriter, true);
update_screen_for_document(documentWriter);
}
// If the document isn't associated to a file, we must to show the
diff --git a/src/app/context.cpp b/src/app/context.cpp
index 9aeba559e..7b839dd25 100644
--- a/src/app/context.cpp
+++ b/src/app/context.cpp
@@ -32,6 +32,11 @@
namespace app {
+Context::Context()
+ : m_settings(NULL)
+{
+}
+
Context::Context(ISettings* settings)
: m_settings(settings)
{
@@ -159,4 +164,9 @@ void Context::onRemoveDocument(Document* document)
m_observers.notifyRemoveDocument(this, document);
}
+void Context::onGetActiveLocation(DocumentLocation* location) const
+{
+ // Without active location
+}
+
} // namespace app
diff --git a/src/app/context.h b/src/app/context.h
index 1de054295..a1dde48b1 100644
--- a/src/app/context.h
+++ b/src/app/context.h
@@ -44,6 +44,9 @@ namespace app {
class Context {
public:
+ Context();
+ // The "settings" are deleted automatically in the ~Context destructor
+ Context(ISettings* settings);
virtual ~Context();
virtual bool isUiAvailable() const { return false; }
@@ -76,19 +79,11 @@ namespace app {
void removeObserver(ContextObserver* observer);
protected:
-
- // The "settings" are deleted automatically in the ~Context destructor
- Context(ISettings* settings);
-
virtual void onAddDocument(Document* document);
virtual void onRemoveDocument(Document* document);
- virtual void onGetActiveLocation(DocumentLocation* location) const = 0;
+ virtual void onGetActiveLocation(DocumentLocation* location) const;
private:
-
- // Without default constructor
- Context();
-
// List of all documents.
Documents m_documents;
diff --git a/src/app/document.h b/src/app/document.h
index 149e0cc42..8ac8e326e 100644
--- a/src/app/document.h
+++ b/src/app/document.h
@@ -140,6 +140,7 @@ namespace app {
// Loaded options from file
void setFormatOptions(const SharedPtr& format_options);
+ SharedPtr getFormatOptions() { return m_format_options; }
//////////////////////////////////////////////////////////////////////
// Boundaries
diff --git a/src/app/document_api.cpp b/src/app/document_api.cpp
index 3c8141208..6f62a9e73 100644
--- a/src/app/document_api.cpp
+++ b/src/app/document_api.cpp
@@ -187,7 +187,7 @@ void DocumentApi::setPixelFormat(Sprite* sprite, PixelFormat newFormat, Ditherin
}
new_image = quantization::convert_pixel_format
- (old_image, newFormat, dithering_method, rgbmap,
+ (old_image, NULL, newFormat, dithering_method, rgbmap,
sprite->getPalette(frame),
is_image_from_background);
diff --git a/src/app/document_exporter.cpp b/src/app/document_exporter.cpp
index 1e468d3e3..1b2ab67af 100644
--- a/src/app/document_exporter.cpp
+++ b/src/app/document_exporter.cpp
@@ -25,6 +25,7 @@
#include "app/document.h"
#include "app/document_api.h"
#include "app/file/file.h"
+#include "app/ui_context.h"
#include "base/compiler_specific.h"
#include "base/path.h"
#include "base/unique_ptr.h"
@@ -178,7 +179,7 @@ void DocumentExporter::exportSheet()
// Save the image files.
if (!m_textureFilename.empty()) {
textureDocument->setFilename(m_textureFilename.c_str());
- save_document(textureDocument.get());
+ save_document(UIContext::instance(), textureDocument.get());
}
}
diff --git a/src/app/file/file.cpp b/src/app/file/file.cpp
index ac5978395..85b40350e 100644
--- a/src/app/file/file.cpp
+++ b/src/app/file/file.cpp
@@ -22,8 +22,8 @@
#include "app/file/file.h"
-#include "app/app.h"
#include "app/console.h"
+#include "app/context.h"
#include "app/document.h"
#include "app/file/file_format.h"
#include "app/file/file_formats_manager.h"
@@ -48,7 +48,7 @@ namespace app {
using namespace base;
-static FileOp* fop_new(FileOpType type);
+static FileOp* fop_new(FileOpType type, Context* context);
static void fop_prepare_for_sequence(FileOp* fop);
static FileFormat* get_fileformat(const char* extension);
@@ -90,12 +90,12 @@ void get_writable_extensions(char* buf, int size)
}
}
-Document* load_document(const char* filename)
+Document* load_document(Context* context, const char* filename)
{
Document* document;
/* TODO add a option to configure what to do with the sequence */
- FileOp *fop = fop_to_load_document(filename, FILE_LOAD_SEQUENCE_NONE);
+ FileOp *fop = fop_to_load_document(context, filename, FILE_LOAD_SEQUENCE_NONE);
if (!fop)
return NULL;
@@ -116,10 +116,10 @@ Document* load_document(const char* filename)
return document;
}
-int save_document(Document* document)
+int save_document(Context* context, Document* document)
{
int ret;
- FileOp* fop = fop_to_save_document(document);
+ FileOp* fop = fop_to_save_document(context, document);
if (!fop)
return -1;
@@ -138,11 +138,11 @@ int save_document(Document* document)
return ret;
}
-FileOp* fop_to_load_document(const char* filename, int flags)
+FileOp* fop_to_load_document(Context* context, const char* filename, int flags)
{
FileOp *fop;
- fop = fop_new(FileOpLoad);
+ fop = fop_new(FileOpLoad, context);
if (!fop)
return NULL;
@@ -199,8 +199,7 @@ FileOp* fop_to_load_document(const char* filename, int flags)
}
/* TODO add a better dialog to edit file-names */
- if ((flags & FILE_LOAD_SEQUENCE_ASK) &&
- App::instance()->isGui()) {
+ if ((flags & FILE_LOAD_SEQUENCE_ASK) && context->isUiAvailable()) {
/* really want load all files? */
if ((fop->seq.filename_list.size() > 1) &&
(ui::Alert::show("Notice"
@@ -231,12 +230,12 @@ done:;
return fop;
}
-FileOp* fop_to_save_document(Document* document)
+FileOp* fop_to_save_document(Context* context, Document* document)
{
FileOp *fop;
bool fatal;
- fop = fop_new(FileOpSave);
+ fop = fop_new(FileOpSave, context);
if (!fop)
return NULL;
@@ -323,7 +322,7 @@ FileOp* fop_to_save_document(Document* document)
// Show the confirmation alert
if (!warnings.empty()) {
// Interative
- if (App::instance()->isGui()) {
+ if (context->isUiAvailable()) {
int ret;
if (fatal)
@@ -663,8 +662,9 @@ void fop_post_load(FileOp* fop)
fop->document->getSprite()->getPalettes().size() <= 1 &&
fop->document->getSprite()->getPalette(FrameNumber(0))->isBlack()) {
SharedPtr palette
- (quantization::create_palette_from_rgb(fop->document->getSprite(),
- FrameNumber(0)));
+ (quantization::create_palette_from_rgb(
+ fop->document->getSprite(),
+ FrameNumber(0), NULL));
fop->document->getSprite()->resetPalettes();
fop->document->getSprite()->setPalette(palette, false);
@@ -802,13 +802,14 @@ bool fop_is_stop(FileOp *fop)
return stop;
}
-static FileOp* fop_new(FileOpType type)
+static FileOp* fop_new(FileOpType type, Context* context)
{
FileOp* fop = new FileOp;
fop->type = type;
fop->format = NULL;
fop->format_data = NULL;
+ fop->context = context;
fop->document = NULL;
fop->mutex = new base::mutex();
diff --git a/src/app/file/file.h b/src/app/file/file.h
index 463776467..d29d2b71f 100644
--- a/src/app/file/file.h
+++ b/src/app/file/file.h
@@ -46,6 +46,7 @@ namespace raster {
}
namespace app {
+ class Context;
class Document;
class FileFormat;
class FormatOptions;
@@ -70,6 +71,7 @@ namespace app {
FileOpType type; // Operation type: 0=load, 1=save.
FileFormat* format;
void* format_data; // Custom data for the FileFormat::onLoad/onSave operations.
+ Context* context;
Document* document; // Loaded document, or document to be saved.
std::string filename; // File-name to load/save.
@@ -119,13 +121,13 @@ namespace app {
// High-level routines to load/save documents.
- Document* load_document(const char* filename);
- int save_document(Document* document);
+ Document* load_document(Context* context, const char* filename);
+ int save_document(Context* context, Document* document);
// Low-level routines to load/save documents.
- FileOp* fop_to_load_document(const char* filename, int flags);
- FileOp* fop_to_save_document(Document* document);
+ FileOp* fop_to_load_document(Context* context, const char* filename, int flags);
+ FileOp* fop_to_save_document(Context* context, Document* document);
void fop_operate(FileOp* fop, IFileOpProgress* progress);
void fop_done(FileOp* fop);
void fop_stop(FileOp* fop);
diff --git a/src/app/file/file_tests.cpp b/src/app/file/file_tests.cpp
index 8aa0769de..3362c5242 100644
--- a/src/app/file/file_tests.cpp
+++ b/src/app/file/file_tests.cpp
@@ -19,6 +19,7 @@
#include "tests/test.h"
#include "app/app.h"
+#include "app/context.h"
#include "app/document.h"
#include "app/file/file.h"
#include "app/file/file_formats_manager.h"
@@ -37,6 +38,7 @@ TEST(File, SeveralSizes)
// Register all possible image formats.
FileFormatsManager::instance().registerAllFormats();
std::vector fn(256);
+ app::Context context;
for (int w=10; w<=10+503*2; w+=503) {
for (int h=10; h<=10+503*2; h+=503) {
@@ -61,11 +63,11 @@ TEST(File, SeveralSizes)
}
}
- save_document(doc);
+ save_document(&context, doc);
}
{
- base::UniquePtr doc(load_document(&fn[0]));
+ base::UniquePtr doc(load_document(&context, &fn[0]));
ASSERT_EQ(w, doc->getSprite()->getWidth());
ASSERT_EQ(h, doc->getSprite()->getHeight());
diff --git a/src/app/file/gif_format.cpp b/src/app/file/gif_format.cpp
index 606273941..e603d3cd0 100644
--- a/src/app/file/gif_format.cpp
+++ b/src/app/file/gif_format.cpp
@@ -1,5 +1,5 @@
/* Aseprite
- * Copyright (C) 2001-2013 David Capello
+ * Copyright (C) 2001-2014 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
@@ -20,16 +20,23 @@
#include "config.h"
#endif
+#include "app/console.h"
+#include "app/context.h"
#include "app/document.h"
#include "app/file/file.h"
#include "app/file/file_format.h"
#include "app/file/format_options.h"
+#include "app/file/gif_options.h"
+#include "app/ini_file.h"
#include "app/modules/gui.h"
#include "app/util/autocrop.h"
#include "base/file_handle.h"
#include "base/unique_ptr.h"
#include "raster/raster.h"
#include "ui/alert.h"
+#include "ui/button.h"
+
+#include "generated_gif_options.h"
#include
@@ -72,6 +79,7 @@ struct GifData
};
class GifFormat : public FileFormat {
+
const char* onGetName() const { return "gif"; }
const char* onGetExtensions() const { return "gif"; }
int onGetFlags() const {
@@ -84,7 +92,8 @@ class GifFormat : public FileFormat {
FILE_SUPPORT_GRAYA |
FILE_SUPPORT_INDEXED |
FILE_SUPPORT_FRAMES |
- FILE_SUPPORT_PALETTES;
+ FILE_SUPPORT_PALETTES |
+ FILE_SUPPORT_GET_FORMAT_OPTIONS;
}
bool onLoad(FileOp* fop);
@@ -93,6 +102,7 @@ class GifFormat : public FileFormat {
#ifdef ENABLE_SAVE
bool onSave(FileOp* fop) OVERRIDE;
#endif
+ SharedPtr onGetFormatOptions(FileOp* fop) OVERRIDE;
};
FileFormat* CreateGifFormat()
@@ -157,7 +167,8 @@ bool GifFormat::onLoad(FileOp* fop)
ColorMapObject* colormap = gif_file->SColorMap;
for (int i=0; iColorCount; ++i) {
current_palette->setEntry(i,
- rgba(colormap->Colors[i].Red,
+ rgba(
+ colormap->Colors[i].Red,
colormap->Colors[i].Green,
colormap->Colors[i].Blue, 255));
}
@@ -208,9 +219,11 @@ bool GifFormat::onLoad(FileOp* fop)
if (gif_file->Image.ColorMap) {
ColorMapObject* colormap = gif_file->Image.ColorMap;
for (int i=0; iColorCount; ++i) {
- current_palette->setEntry(i, rgba(colormap->Colors[i].Red,
- colormap->Colors[i].Green,
- colormap->Colors[i].Blue, 255));
+ current_palette->setEntry(i,
+ rgba(
+ colormap->Colors[i].Red,
+ colormap->Colors[i].Green,
+ colormap->Colors[i].Blue, 255));
}
}
@@ -249,7 +262,7 @@ bool GifFormat::onLoad(FileOp* fop)
data->frames[frame_num].disposal_method = disposal_method;
data->frames[frame_num].mask_index = transparent_index;
- PRINTF("Frame[%d] transparent index = %d\n", (int)frame_num, transparent_index);
+ // PRINTF("Frame[%d] transparent index = %d\n", (int)frame_num, transparent_index);
++frame_num;
@@ -272,8 +285,8 @@ bool GifFormat::onLoad(FileOp* fop)
transparent_index = (extension[1] & 1) ? extension[4]: -1;
frame_delay = (extension[3] << 8) | extension[2];
- TRACE("Disposal method: %d\nTransparent index: %d\nFrame delay: %d\n",
- disposal_method, transparent_index, frame_delay);
+ // PRINTF("Disposal method: %d\nTransparent index: %d\nFrame delay: %d\n",
+ // disposal_method, transparent_index, frame_delay);
}
}
@@ -355,7 +368,7 @@ bool GifFormat::onPostLoad(FileOp* fop)
ui::Alert::show("GIF Conversion"
"<document->getFilename().c_str());
@@ -367,6 +380,7 @@ bool GifFormat::onPostLoad(FileOp* fop)
}
// Create the sprite with the GIF dimension
+ // TODO instead of 256 use the number of colors from the document
UniquePtr sprite(new Sprite(pixelFormat, data->sprite_w, data->sprite_h, 256));
// Create the main layer
@@ -524,26 +538,58 @@ bool GifFormat::onSave(FileOp* fop)
if (!gif_file)
throw Exception("Error creating GIF file.\n");
+ SharedPtr gif_options = fop->seq.format_options;
Sprite* sprite = fop->document->getSprite();
int sprite_w = sprite->getWidth();
int sprite_h = sprite->getHeight();
PixelFormat sprite_format = sprite->getPixelFormat();
- bool interlace = false;
+ bool interlaced = gif_options->interlaced();
int loop = 0;
+ bool has_background = (sprite->getBackgroundLayer() ? true: false);
int background_color = (sprite_format == IMAGE_INDEXED ? sprite->getTransparentColor(): 0);
- int transparent_index = (sprite->getBackgroundLayer() ? -1: sprite->getTransparentColor());
+ int transparent_index = (has_background ? -1: sprite->getTransparentColor());
- Palette* current_palette = sprite->getPalette(FrameNumber(0));
- Palette* previous_palette = current_palette;
- ColorMapObject* color_map = GifMakeMapObject(current_palette->size(), NULL);
- for (int i = 0; i < current_palette->size(); ++i) {
- color_map->Colors[i].Red = rgba_getr(current_palette->getEntry(i));
- color_map->Colors[i].Green = rgba_getg(current_palette->getEntry(i));
- color_map->Colors[i].Blue = rgba_getb(current_palette->getEntry(i));
+ Palette current_palette = *sprite->getPalette(FrameNumber(0));
+ Palette previous_palette(current_palette);
+ RgbMap rgbmap;
+
+ // The color map must be a power of two.
+ int color_map_size = current_palette.size();
+ for (int i = 30; i >= 0; --i) {
+ if (color_map_size & (1 << i)) {
+ color_map_size = (1 << (i + (color_map_size & (1 << (i - 1)) ? 1: 0)));
+ break;
+ }
}
+ ASSERT(color_map_size > 0 && color_map_size <= 256);
- if (EGifPutScreenDesc(gif_file, sprite_w, sprite_h,
- color_map->BitsPerPixel,
+ ColorMapObject* color_map = NULL;
+ int bpp;
+
+ // We use a global color map only if this is a transparent GIF
+ if (!has_background) {
+ color_map = GifMakeMapObject(color_map_size, NULL);
+ if (color_map == NULL)
+ throw std::bad_alloc();
+
+ int i;
+ for (i = 0; i < current_palette.size(); ++i) {
+ color_map->Colors[i].Red = rgba_getr(current_palette.getEntry(i));
+ color_map->Colors[i].Green = rgba_getg(current_palette.getEntry(i));
+ color_map->Colors[i].Blue = rgba_getb(current_palette.getEntry(i));
+ }
+ for (; i < color_map_size; ++i) {
+ color_map->Colors[i].Red = 0;
+ color_map->Colors[i].Green = 0;
+ color_map->Colors[i].Blue = 0;
+ }
+
+ bpp = color_map->BitsPerPixel;
+ }
+ else
+ bpp = 8;
+
+ if (EGifPutScreenDesc(gif_file, sprite_w, sprite_h, bpp,
background_color, color_map) == GIF_ERROR)
throw Exception("Error writing GIF header.\n");
@@ -562,44 +608,40 @@ bool GifFormat::onSave(FileOp* fop)
clear_image(current_image, background_color);
clear_image(previous_image, background_color);
- for (FrameNumber frame_num(0); frame_numgetTotalFrames(); ++frame_num) {
- current_palette = sprite->getPalette(frame_num);
+ ColorMapObject* image_color_map = NULL;
+ for (FrameNumber frame_num(0); frame_numgetTotalFrames(); ++frame_num) {
// If the sprite is RGB or Grayscale, we must to convert it to Indexed on the fly.
if (sprite_format != IMAGE_INDEXED) {
- clear_image(buffer_image, 0);
+ clear_image(buffer_image, background_color);
layer_render(sprite->getFolder(), buffer_image, 0, 0, frame_num);
- switch (sprite_format) {
-
- // Convert the RGB image to Indexed
- case IMAGE_RGB:
- for (int y = 0; y < sprite_h; ++y)
- for (int x = 0; x < sprite_w; ++x) {
- uint32_t pixel_value = get_pixel_fast(buffer_image, x, y);
- put_pixel_fast(current_image, x, y,
- (rgba_geta(pixel_value) >= 128) ?
- current_palette->findBestfit(rgba_getr(pixel_value),
- rgba_getg(pixel_value),
- rgba_getb(pixel_value)):
- transparent_index);
- }
+ switch (gif_options->quantize()) {
+ case GifOptions::NoQuantize:
+ sprite->getPalette(frame_num)->copyColorsTo(¤t_palette);
break;
+ case GifOptions::QuantizeEach:
+ case GifOptions::QuantizeAll:
+ {
+ current_palette.makeBlack();
- // Convert the Grayscale image to Indexed
- case IMAGE_GRAYSCALE:
- for (int y = 0; y < sprite_h; ++y)
- for (int x = 0; x < sprite_w; ++x) {
- uint16_t pixel_value = get_pixel_fast(buffer_image, x, y);
- put_pixel_fast(current_image, x, y,
- (graya_geta(pixel_value) >= 128) ?
- current_palette->findBestfit(graya_getv(pixel_value),
- graya_getv(pixel_value),
- graya_getv(pixel_value)):
- transparent_index);
- }
+ std::vector imgarray(1);
+ imgarray[0] = buffer_image;
+ raster::quantization::create_palette_from_images(imgarray, ¤t_palette, has_background);
+ }
break;
}
+
+ rgbmap.regenerate(¤t_palette, transparent_index);
+
+ quantization::convert_pixel_format(
+ buffer_image,
+ current_image,
+ IMAGE_INDEXED,
+ gif_options->dithering(),
+ &rgbmap,
+ ¤t_palette,
+ has_background);
}
// If the sprite is Indexed, we can render directly into "current_image".
else {
@@ -679,26 +721,32 @@ bool GifFormat::onSave(FileOp* fop)
}
// Image color map
- ColorMapObject* image_color_map = NULL;
- if (current_palette != previous_palette) {
- image_color_map = GifMakeMapObject(current_palette->size(), NULL);
- for (int i = 0; i < current_palette->size(); ++i) {
- image_color_map->Colors[i].Red = rgba_getr(current_palette->getEntry(i));
- image_color_map->Colors[i].Green = rgba_getg(current_palette->getEntry(i));
- image_color_map->Colors[i].Blue = rgba_getb(current_palette->getEntry(i));
+ if ((!color_map && frame_num == 0) ||
+ (current_palette.countDiff(&previous_palette, NULL, NULL) > 0)) {
+ if (!image_color_map) {
+ image_color_map = GifMakeMapObject(current_palette.size(), NULL);
+ if (image_color_map == NULL)
+ throw std::bad_alloc();
}
- previous_palette = current_palette;
+
+ for (int i = 0; i < current_palette.size(); ++i) {
+ image_color_map->Colors[i].Red = rgba_getr(current_palette.getEntry(i));
+ image_color_map->Colors[i].Green = rgba_getg(current_palette.getEntry(i));
+ image_color_map->Colors[i].Blue = rgba_getb(current_palette.getEntry(i));
+ }
+
+ current_palette.copyColorsTo(&previous_palette);
}
// Write the image record.
if (EGifPutImageDesc(gif_file,
frame_x, frame_y,
- frame_w, frame_h, interlace ? 1: 0,
+ frame_w, frame_h, interlaced ? 1: 0,
image_color_map) == GIF_ERROR)
throw Exception("Error writing GIF frame %d.\n", (int)frame_num);
// Write the image data (pixels).
- if (interlace) {
+ if (interlaced) {
// Need to perform 4 passes on the images.
for (int i=0; i<4; ++i)
for (int y = interlaced_offset[i]; y < frame_h; y += interlaced_jumps[i]) {
@@ -711,7 +759,7 @@ bool GifFormat::onSave(FileOp* fop)
}
else {
// Write all image scanlines (not interlaced in this case).
- for (int y = 0; y < frame_h; ++y) {
+ for (int y=0; ygetPixelAddress(frame_x, frame_y + y);
@@ -727,4 +775,69 @@ bool GifFormat::onSave(FileOp* fop)
}
#endif
+SharedPtr GifFormat::onGetFormatOptions(FileOp* fop)
+{
+ SharedPtr gif_options;
+ if (fop->document->getFormatOptions() != NULL)
+ gif_options = SharedPtr(fop->document->getFormatOptions());
+
+ if (!gif_options)
+ gif_options.reset(new GifOptions);
+
+ // Non-interactive mode
+ if (!fop->context->isUiAvailable())
+ return gif_options;
+
+ try {
+ // Configuration parameters
+ gif_options->setQuantize((GifOptions::Quantize)get_config_int("GIF", "Quantize", (int)gif_options->quantize()));
+ gif_options->setInterlaced(get_config_bool("GIF", "Interlaced", gif_options->interlaced()));
+ gif_options->setDithering((raster::DitheringMethod)get_config_int("GIF", "Dither", (int)gif_options->dithering()));
+
+ // Load the window to ask to the user the GIF options he wants.
+
+ app::gen::GifOptions win;
+ win.rgbOptions()->setVisible(fop->document->getSprite()->getPixelFormat() != IMAGE_INDEXED);
+
+ switch (gif_options->quantize()) {
+ case GifOptions::NoQuantize: win.noQuantize()->setSelected(true); break;
+ case GifOptions::QuantizeEach: win.quantizeEach()->setSelected(true); break;
+ case GifOptions::QuantizeAll: win.quantizeAll()->setSelected(true); break;
+ }
+ win.interlaced()->setSelected(gif_options->interlaced());
+
+ win.dither()->setEnabled(true);
+ win.dither()->setSelected(gif_options->dithering() == raster::DITHERING_ORDERED);
+
+ win.openWindowInForeground();
+
+ if (win.getKiller() == win.ok()) {
+ if (win.quantizeAll()->isSelected())
+ gif_options->setQuantize(GifOptions::QuantizeAll);
+ else if (win.quantizeEach()->isSelected())
+ gif_options->setQuantize(GifOptions::QuantizeEach);
+ else if (win.noQuantize()->isSelected())
+ gif_options->setQuantize(GifOptions::NoQuantize);
+
+ gif_options->setInterlaced(win.interlaced()->isSelected());
+ gif_options->setDithering(win.dither()->isSelected() ?
+ raster::DITHERING_ORDERED:
+ raster::DITHERING_NONE);
+
+ set_config_int("GIF", "Quantize", gif_options->quantize());
+ set_config_bool("GIF", "Interlaced", gif_options->interlaced());
+ set_config_int("GIF", "Dither", gif_options->dithering());
+ }
+ else {
+ gif_options.reset(NULL);
+ }
+
+ return gif_options;
+ }
+ catch (std::exception& e) {
+ Console::showException(e);
+ return SharedPtr(0);
+ }
+}
+
} // namespace app
diff --git a/src/app/file/gif_options.h b/src/app/file/gif_options.h
new file mode 100644
index 000000000..ba723934b
--- /dev/null
+++ b/src/app/file/gif_options.h
@@ -0,0 +1,58 @@
+/* Aseprite
+ * Copyright (C) 2001-2014 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
+ */
+
+#ifndef APP_FILE_GIF_OPTIONS_H_INCLUDED
+#define APP_FILE_GIF_OPTIONS_H_INCLUDED
+#pragma once
+
+#include "app/file/format_options.h"
+#include "raster/dithering_method.h"
+
+namespace app {
+
+ // Data for GIF files
+ class GifOptions : public FormatOptions {
+ public:
+ enum Quantize { NoQuantize, QuantizeEach, QuantizeAll };
+
+ GifOptions(
+ Quantize quantize = QuantizeEach,
+ bool interlaced = false,
+ DitheringMethod dithering = raster::DITHERING_NONE)
+ : m_quantize(quantize)
+ , m_interlaced(interlaced)
+ , m_dithering(dithering) {
+ }
+
+ Quantize quantize() const { return m_quantize; }
+ bool interlaced() const { return m_interlaced; }
+ raster::DitheringMethod dithering() const { return m_dithering; }
+
+ void setQuantize(const Quantize quantize) { m_quantize = quantize; }
+ void setInterlaced(bool interlaced) { m_interlaced = interlaced; }
+ void setDithering(const raster::DitheringMethod dithering) { m_dithering = dithering; }
+
+ private:
+ Quantize m_quantize;
+ bool m_interlaced;
+ raster::DitheringMethod m_dithering;
+ };
+
+} // namespace app
+
+#endif
diff --git a/src/app/file/gif_tests.cpp b/src/app/file/gif_tests.cpp
new file mode 100644
index 000000000..5277e636b
--- /dev/null
+++ b/src/app/file/gif_tests.cpp
@@ -0,0 +1,312 @@
+/* Aseprite
+ * Copyright (C) 2014 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 "tests/test.h"
+
+#include "app/context.h"
+#include "app/document.h"
+#include "app/file/file.h"
+#include "app/file/file_formats_manager.h"
+#include "app/file/gif_options.h"
+#include "raster/raster.h"
+#include "she/scoped_handle.h"
+#include "she/system.h"
+
+using namespace app;
+
+typedef base::UniquePtr DocumentPtr;
+
+class GifFormat : public ::testing::Test {
+public:
+ GifFormat() : m_system(she::create_system()) {
+ FileFormatsManager::instance().registerAllFormats();
+ }
+
+protected:
+ app::Context m_context;
+ she::ScopedHandle m_system;
+};
+
+TEST_F(GifFormat, Dimensions)
+{
+ const char* fn = "test.gif";
+
+ {
+ DocumentPtr doc(Document::createBasicDocument(IMAGE_INDEXED, 31, 29, 14));
+ Sprite* sprite = doc->getSprite();
+ doc->setFilename(fn);
+ sprite->setTransparentColor(3);
+
+ LayerImage* layer = dynamic_cast(sprite->getFolder()->getFirstLayer());
+ ASSERT_NE((LayerImage*)NULL, layer);
+
+ Image* image = sprite->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage());
+ clear_image(image, doc->getSprite()->getTransparentColor());
+
+ save_document(&m_context, doc);
+ }
+
+ {
+ DocumentPtr doc(load_document(&m_context, fn));
+ Sprite* sprite = doc->getSprite();
+
+ EXPECT_EQ(31, sprite->getWidth());
+ EXPECT_EQ(29, sprite->getHeight());
+ EXPECT_EQ(3, sprite->getTransparentColor());
+ // TODO instead of 256, this should be 16 as Gif files contains
+ // palettes that are power of two.
+ EXPECT_EQ(256, sprite->getPalette(FrameNumber(0))->size());
+ }
+}
+
+TEST_F(GifFormat, OpaqueIndexed)
+{
+ const char* fn = "test.gif";
+
+ {
+ DocumentPtr doc(Document::createBasicDocument(IMAGE_INDEXED, 2, 2, 4));
+ Sprite* sprite = doc->getSprite();
+ doc->setFilename(fn);
+
+ Palette* pal = sprite->getPalette(FrameNumber(0));
+ pal->setEntry(0, rgb(255, 255, 255));
+ pal->setEntry(1, rgb(255, 13, 254));
+ pal->setEntry(2, rgb(129, 255, 32));
+ pal->setEntry(3, rgb(0, 0, 255));
+
+ LayerImage* layer = dynamic_cast(sprite->getFolder()->getFirstLayer());
+ layer->setBackground(true);
+ ASSERT_NE((LayerImage*)NULL, layer);
+
+ Image* image = sprite->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage());
+ image->putPixel(0, 0, 0);
+ image->putPixel(0, 1, 1);
+ image->putPixel(1, 0, 2);
+ image->putPixel(1, 1, 3);
+
+ save_document(&m_context, doc);
+ }
+
+ {
+ DocumentPtr doc(load_document(&m_context, fn));
+ Sprite* sprite = doc->getSprite();
+
+ LayerImage* layer = dynamic_cast(sprite->getFolder()->getFirstLayer());
+ ASSERT_NE((LayerImage*)NULL, layer);
+ EXPECT_TRUE(layer->isBackground());
+
+ Palette* pal = sprite->getPalette(FrameNumber(0));
+ EXPECT_EQ(rgb(255, 255, 255), pal->getEntry(0));
+ EXPECT_EQ(rgb(255, 13, 254), pal->getEntry(1));
+ EXPECT_EQ(rgb(129, 255, 32), pal->getEntry(2));
+ EXPECT_EQ(rgb(0, 0, 255), pal->getEntry(3));
+
+ Image* image = sprite->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage());
+ EXPECT_EQ(0, sprite->getTransparentColor());
+ EXPECT_EQ(0, image->getPixel(0, 0));
+ EXPECT_EQ(1, image->getPixel(0, 1));
+ EXPECT_EQ(2, image->getPixel(1, 0));
+ EXPECT_EQ(3, image->getPixel(1, 1));
+ }
+}
+
+TEST_F(GifFormat, TransparentIndexed)
+{
+ const char* fn = "test.gif";
+
+ {
+ DocumentPtr doc(Document::createBasicDocument(IMAGE_INDEXED, 2, 2, 4));
+ Sprite* sprite = doc->getSprite();
+ doc->setFilename(fn);
+
+ Palette* pal = sprite->getPalette(FrameNumber(0));
+ pal->setEntry(0, rgb(255, 255, 255));
+ pal->setEntry(1, rgb(255, 13, 254));
+ pal->setEntry(2, rgb(129, 255, 32));
+ pal->setEntry(3, rgb(0, 0, 255));
+
+ LayerImage* layer = dynamic_cast(sprite->getFolder()->getFirstLayer());
+ ASSERT_NE((LayerImage*)NULL, layer);
+
+ Image* image = sprite->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage());
+ image->putPixel(0, 0, 0);
+ image->putPixel(0, 1, 1);
+ image->putPixel(1, 0, 2);
+ image->putPixel(1, 1, 3);
+
+ save_document(&m_context, doc);
+ }
+
+ {
+ DocumentPtr doc(load_document(&m_context, fn));
+ Sprite* sprite = doc->getSprite();
+
+ LayerImage* layer = dynamic_cast(sprite->getFolder()->getFirstLayer());
+ ASSERT_NE((LayerImage*)NULL, layer);
+ EXPECT_FALSE(layer->isBackground());
+
+ Palette* pal = sprite->getPalette(FrameNumber(0));
+ EXPECT_EQ(rgb(255, 255, 255), pal->getEntry(0));
+ EXPECT_EQ(rgb(255, 13, 254), pal->getEntry(1));
+ EXPECT_EQ(rgb(129, 255, 32), pal->getEntry(2));
+ EXPECT_EQ(rgb(0, 0, 255), pal->getEntry(3));
+
+ Image* image = sprite->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage());
+ EXPECT_EQ(0, sprite->getTransparentColor());
+ EXPECT_EQ(0, image->getPixel(0, 0));
+ EXPECT_EQ(1, image->getPixel(0, 1));
+ EXPECT_EQ(2, image->getPixel(1, 0));
+ EXPECT_EQ(3, image->getPixel(1, 1));
+ }
+}
+
+TEST_F(GifFormat, TransparentRgbQuantization)
+{
+ const char* fn = "test.gif";
+
+ {
+ DocumentPtr doc(Document::createBasicDocument(IMAGE_RGB, 2, 2, 256));
+ Sprite* sprite = doc->getSprite();
+ doc->setFilename(fn);
+
+ LayerImage* layer = dynamic_cast(sprite->getFolder()->getFirstLayer());
+ ASSERT_NE((LayerImage*)NULL, layer);
+
+ Image* image = sprite->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage());
+ image->putPixel(0, 0, rgba(0, 0, 0, 0));
+ image->putPixel(0, 1, rgb(255, 0, 0));
+ image->putPixel(1, 0, rgb(0, 255, 0));
+ image->putPixel(1, 1, rgb(0, 0, 255));
+
+ save_document(&m_context, doc);
+ }
+
+ {
+ DocumentPtr doc(load_document(&m_context, fn));
+ Sprite* sprite = doc->getSprite();
+
+ LayerImage* layer = dynamic_cast(sprite->getFolder()->getFirstLayer());
+ ASSERT_NE((LayerImage*)NULL, layer);
+
+ Palette* pal = sprite->getPalette(FrameNumber(0));
+ Image* image = sprite->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage());
+ EXPECT_EQ(0, sprite->getTransparentColor());
+ EXPECT_EQ(0, image->getPixel(0, 0));
+ EXPECT_EQ(rgb(255, 0, 0), pal->getEntry(image->getPixel(0, 1)));
+ EXPECT_EQ(rgb(0, 255, 0), pal->getEntry(image->getPixel(1, 0)));
+ EXPECT_EQ(rgb(0, 0, 255), pal->getEntry(image->getPixel(1, 1)));
+ }
+}
+
+TEST_F(GifFormat, OpaqueRgbQuantization)
+{
+ const char* fn = "test.gif";
+
+ {
+ DocumentPtr doc(Document::createBasicDocument(IMAGE_RGB, 2, 2, 256));
+ Sprite* sprite = doc->getSprite();
+ doc->setFilename(fn);
+
+ LayerImage* layer = dynamic_cast(sprite->getFolder()->getFirstLayer());
+ layer->setBackground(true);
+ ASSERT_NE((LayerImage*)NULL, layer);
+ EXPECT_NE((LayerImage*)NULL, sprite->getBackgroundLayer());
+
+ Image* image = sprite->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage());
+ image->putPixel(0, 0, rgb(0, 0, 0));
+ image->putPixel(0, 1, rgb(255, 0, 0));
+ image->putPixel(1, 0, rgb(0, 255, 0));
+ image->putPixel(1, 1, rgb(0, 0, 255));
+
+ save_document(&m_context, doc);
+ }
+
+ {
+ DocumentPtr doc(load_document(&m_context, fn));
+ Sprite* sprite = doc->getSprite();
+
+ LayerImage* layer = dynamic_cast(sprite->getFolder()->getFirstLayer());
+ ASSERT_NE((LayerImage*)NULL, layer);
+ EXPECT_TRUE(layer->isBackground());
+ EXPECT_EQ(layer, sprite->getBackgroundLayer());
+
+ Palette* pal = sprite->getPalette(FrameNumber(0));
+ Image* image = sprite->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage());
+ EXPECT_EQ(0, sprite->getTransparentColor());
+ EXPECT_EQ(rgb(0, 0, 0), pal->getEntry(image->getPixel(0, 0)));
+ EXPECT_EQ(rgb(255, 0, 0), pal->getEntry(image->getPixel(0, 1)));
+ EXPECT_EQ(rgb(0, 255, 0), pal->getEntry(image->getPixel(1, 0)));
+ EXPECT_EQ(rgb(0, 0, 255), pal->getEntry(image->getPixel(1, 1)));
+ }
+}
+
+TEST_F(GifFormat, OpaqueRgbQuantizationTwoLayers)
+{
+ const char* fn = "test.gif";
+
+ {
+ DocumentPtr doc(Document::createBasicDocument(IMAGE_RGB, 2, 2, 256));
+ Sprite* sprite = doc->getSprite();
+ doc->setFilename(fn);
+
+ LayerImage* layer1 = dynamic_cast(sprite->getFolder()->getFirstLayer());
+ layer1->setBackground(true);
+
+ LayerImage* layer2 = new LayerImage(sprite);
+ sprite->getFolder()->addLayer(layer2);
+
+ Image* image1 = sprite->getStock()->getImage(layer1->getCel(FrameNumber(0))->getImage());
+ Image* image2 = Image::create(IMAGE_RGB, 2, 2);
+ int image2Idx = sprite->getStock()->addImage(image2);
+ Cel* cel2 = new Cel(FrameNumber(0), image2Idx);
+ layer2->addCel(cel2);
+
+ image1->clear(rgba(255, 255, 255, 255));
+ image2->putPixel(0, 0, rgba(255, 0, 0, 255));
+ image2->putPixel(1, 1, rgba(196, 0, 0, 255));
+
+ Palette* pal = sprite->getPalette(FrameNumber(0));
+ pal->setEntry(0, rgba(255, 255, 255, 255));
+ pal->setEntry(1, rgba(255, 0, 0, 255));
+
+ // Do not modify palettes
+ doc->setFormatOptions(SharedPtr(new GifOptions(GifOptions::NoQuantize)));
+ save_document(&m_context, doc);
+ }
+
+ {
+ DocumentPtr doc(load_document(&m_context, fn));
+ Sprite* sprite = doc->getSprite();
+
+ LayerImage* layer = dynamic_cast(sprite->getFolder()->getFirstLayer());
+ ASSERT_NE((LayerImage*)NULL, layer);
+ ASSERT_TRUE(layer->isBackground());
+
+ Palette* pal = sprite->getPalette(FrameNumber(0));
+ Image* image = sprite->getStock()->getImage(layer->getCel(FrameNumber(0))->getImage());
+ EXPECT_EQ(0, sprite->getTransparentColor());
+
+ EXPECT_EQ(1, image->getPixel(0, 0));
+ EXPECT_EQ(0, image->getPixel(0, 1));
+ EXPECT_EQ(0, image->getPixel(1, 0));
+ EXPECT_EQ(1, image->getPixel(1, 1));
+
+ EXPECT_EQ(rgba(255, 255, 255, 255), pal->getEntry(0));
+ EXPECT_EQ(rgba(255, 0, 0, 255), pal->getEntry(1));
+ }
+}
diff --git a/src/app/file/jpeg_format.cpp b/src/app/file/jpeg_format.cpp
index e4e39a8a8..aaf57001d 100644
--- a/src/app/file/jpeg_format.cpp
+++ b/src/app/file/jpeg_format.cpp
@@ -22,6 +22,8 @@
#include "app/app.h"
#include "app/console.h"
+#include "app/context.h"
+#include "app/document.h"
#include "app/file/file.h"
#include "app/file/file_format.h"
#include "app/file/format_options.h"
@@ -358,15 +360,21 @@ bool JpegFormat::onSave(FileOp* fop)
// Shows the JPEG configuration dialog.
SharedPtr JpegFormat::onGetFormatOptions(FileOp* fop)
{
- SharedPtr jpeg_options(new JpegOptions());
+ SharedPtr jpeg_options;
+ if (fop->document->getFormatOptions() != NULL)
+ jpeg_options = SharedPtr(fop->document->getFormatOptions());
+
+ if (!jpeg_options)
+ jpeg_options.reset(new JpegOptions);
+
+ // Non-interactive mode
+ if (!fop->context->isUiAvailable())
+ return jpeg_options;
+
try {
// Configuration parameters
jpeg_options->quality = get_config_float("JPEG", "Quality", 1.0f);
- // Interactive mode
- if (!App::instance()->isGui())
- return jpeg_options;
-
// Load the window to ask to the user the JPEG options he wants.
UniquePtr window(app::load_widget("jpeg_options.xml", "jpeg_options"));
ui::Slider* slider_quality = app::find_widget(window, "quality");
diff --git a/src/app/thumbnail_generator.cpp b/src/app/thumbnail_generator.cpp
index 1f156a185..30aa190d1 100644
--- a/src/app/thumbnail_generator.cpp
+++ b/src/app/thumbnail_generator.cpp
@@ -23,6 +23,7 @@
#include "app/thumbnail_generator.h"
#include "app/app.h"
+#include "app/context.h"
#include "app/document.h"
#include "app/file/file.h"
#include "app/file_system.h"
@@ -190,9 +191,12 @@ void ThumbnailGenerator::addWorkerToGenerateThumbnail(IFileItem* fileitem)
getWorkerStatus(fileitem, progress) != WithoutWorker)
return;
- FileOp* fop = fop_to_load_document(fileitem->getFileName().c_str(),
- FILE_LOAD_SEQUENCE_NONE |
- FILE_LOAD_ONE_FRAME);
+ Context tmpContext;
+ FileOp* fop = fop_to_load_document(&tmpContext,
+ fileitem->getFileName().c_str(),
+ FILE_LOAD_SEQUENCE_NONE |
+ FILE_LOAD_ONE_FRAME);
+
if (!fop)
return;
diff --git a/src/app/util/clipboard.cpp b/src/app/util/clipboard.cpp
index 13231a09d..64cc1bb46 100644
--- a/src/app/util/clipboard.cpp
+++ b/src/app/util/clipboard.cpp
@@ -204,7 +204,7 @@ void clipboard::paste()
RgbMap* dst_rgbmap = dst_sprite->getRgbMap(editor->getFrame());
src_image = quantization::convert_pixel_format(
- clipboard_image, dst_sprite->getPixelFormat(),
+ clipboard_image, NULL, dst_sprite->getPixelFormat(),
DITHERING_NONE, dst_rgbmap, clipboard_palette,
false);
}
diff --git a/src/raster/color.h b/src/raster/color.h
index 085581136..0200827c5 100644
--- a/src/raster/color.h
+++ b/src/raster/color.h
@@ -63,6 +63,10 @@ namespace raster {
(a << rgba_a_shift));
}
+ inline uint32_t rgb(uint8_t r, uint8_t g, uint8_t b) {
+ return rgba(0, 0, 0, 255);
+ }
+
//////////////////////////////////////////////////////////////////////
// Grayscale
@@ -85,6 +89,10 @@ namespace raster {
(a << graya_a_shift));
}
+ inline uint16_t gray(uint8_t v) {
+ return graya(v, 255);
+ }
+
} // namespace raster
#endif
diff --git a/src/raster/palette.cpp b/src/raster/palette.cpp
index 633dd81d9..9320fc69e 100644
--- a/src/raster/palette.cpp
+++ b/src/raster/palette.cpp
@@ -46,9 +46,9 @@ Palette::Palette(FrameNumber frame, int ncolors)
m_frame = frame;
m_colors.resize(ncolors);
- m_modifications = 0;
- std::fill(m_colors.begin(), m_colors.end(), rgba(0, 0, 0, 255));
+ makeBlack();
+ m_modifications = 0;
}
Palette::Palette(const Palette& palette)
diff --git a/src/raster/quantization.cpp b/src/raster/quantization.cpp
index 592d3b91f..e28304feb 100644
--- a/src/raster/quantization.cpp
+++ b/src/raster/quantization.cpp
@@ -45,17 +45,22 @@ namespace quantization {
using namespace gfx;
// Converts a RGB image to indexed with ordered dithering method.
-static Image* ordered_dithering(const Image* src_image,
- int offsetx, int offsety,
- const RgbMap* rgbmap,
- const Palette* palette);
+static Image* ordered_dithering(
+ const Image* src_image,
+ Image* dst_image,
+ int offsetx, int offsety,
+ const RgbMap* rgbmap,
+ const Palette* palette);
-static void create_palette_from_bitmaps(const std::vector& images, Palette* palette, bool has_background_layer);
-
-Palette* create_palette_from_rgb(const Sprite* sprite, FrameNumber frameNumber)
+Palette* create_palette_from_rgb(
+ const Sprite* sprite,
+ FrameNumber frameNumber,
+ Palette* palette)
{
+ if (!palette)
+ palette = new Palette(FrameNumber(0), 256);
+
bool has_background_layer = (sprite->getBackgroundLayer() != NULL);
- Palette* palette = new Palette(FrameNumber(0), 256);
Image* flat_image;
ImagesCollector images(sprite->getFolder(), // All layers
@@ -78,27 +83,31 @@ Palette* create_palette_from_rgb(const Sprite* sprite, FrameNumber frameNumber)
image_array[c++] = flat_image; // The 'flat_image'
// Generate an optimized palette for all images
- create_palette_from_bitmaps(image_array, palette, has_background_layer);
+ create_palette_from_images(image_array, palette, has_background_layer);
delete flat_image;
return palette;
}
-Image* convert_pixel_format(const Image* image,
- PixelFormat pixelFormat,
- DitheringMethod ditheringMethod,
- const RgbMap* rgbmap,
- const Palette* palette,
- bool is_background_layer)
+Image* convert_pixel_format(
+ const Image* image,
+ Image* new_image,
+ PixelFormat pixelFormat,
+ DitheringMethod ditheringMethod,
+ const RgbMap* rgbmap,
+ const Palette* palette,
+ bool is_background)
{
+ if (!new_image)
+ new_image = Image::create(pixelFormat, image->getWidth(), image->getHeight());
+
// RGB -> Indexed with ordered dithering
if (image->getPixelFormat() == IMAGE_RGB &&
pixelFormat == IMAGE_INDEXED &&
ditheringMethod == DITHERING_ORDERED) {
- return ordered_dithering(image, 0, 0, rgbmap, palette);
+ return ordered_dithering(image, new_image, 0, 0, rgbmap, palette);
}
- Image* new_image = Image::create(pixelFormat, image->getWidth(), image->getHeight());
color_t c;
int r, g, b;
@@ -223,7 +232,7 @@ Image* convert_pixel_format(const Image* image,
ASSERT(dst_it != dst_end);
c = *src_it;
- if (!is_background_layer && c == image->getMaskColor())
+ if (!is_background && c == image->getMaskColor())
*dst_it = 0;
else
*dst_it = rgba(rgba_getr(palette->getEntry(c)),
@@ -243,7 +252,7 @@ Image* convert_pixel_format(const Image* image,
ASSERT(dst_it != dst_end);
c = *src_it;
- if (!is_background_layer && c == image->getMaskColor())
+ if (!is_background && c == image->getMaskColor())
*dst_it = 0;
else {
r = rgba_getr(palette->getEntry(c));
@@ -268,7 +277,7 @@ Image* convert_pixel_format(const Image* image,
ASSERT(dst_it != dst_end);
c = *src_it;
- if (!is_background_layer && c == image->getMaskColor())
+ if (!is_background && c == image->getMaskColor())
*dst_it = dstMaskColor;
else {
r = rgba_getr(palette->getEntry(c));
@@ -321,13 +330,14 @@ static int pattern[8][8] = {
4 * ((g1)-(g2)) * ((g1)-(g2)) + \
2 * ((b1)-(b2)) * ((b1)-(b2)))
-static Image* ordered_dithering(const Image* src_image,
- int offsetx, int offsety,
- const RgbMap* rgbmap,
- const Palette* palette)
+static Image* ordered_dithering(
+ const Image* src_image,
+ Image* dst_image,
+ int offsetx, int offsety,
+ const RgbMap* rgbmap,
+ const Palette* palette)
{
int oppr, oppg, oppb, oppnrcm;
- Image *dst_image;
int dither_const;
int nr, ng, nb;
int r, g, b, a;
@@ -335,10 +345,6 @@ static Image* ordered_dithering(const Image* src_image,
int x, y;
color_t c;
- dst_image = Image::create(IMAGE_INDEXED, src_image->getWidth(), src_image->getHeight());
- if (!dst_image)
- return NULL;
-
const LockImageBits src_bits(src_image);
LockImageBits dst_bits(dst_image);
LockImageBits::const_iterator src_it = src_bits.begin();
@@ -405,7 +411,7 @@ static Image* ordered_dithering(const Image* src_image,
// Creation of optimized palette for RGB images
// by David Capello
-static void create_palette_from_bitmaps(const std::vector& images, Palette* palette, bool has_background_layer)
+void create_palette_from_images(const std::vector& images, Palette* palette, bool has_background_layer)
{
quantization::ColorHistogram<5, 6, 5> histogram;
uint32_t color;
@@ -418,16 +424,45 @@ static void create_palette_from_bitmaps(const std::vector& images, Palet
for (int i=0; i<(int)images.size(); ++i) {
const Image* image = images[i];
- const LockImageBits bits(image);
- LockImageBits::const_iterator it = bits.begin(), end = bits.end();
- for (; it != end; ++it) {
- color = *it;
+ switch (image->getPixelFormat()) {
+
+ case IMAGE_RGB:
+ {
+ const LockImageBits bits(image);
+ LockImageBits::const_iterator it = bits.begin(), end = bits.end();
+
+ for (; it != end; ++it) {
+ color = *it;
+
+ if (rgba_geta(color) > 0) {
+ color |= rgba(0, 0, 0, 255);
+ histogram.addSamples(color, 1);
+ }
+ }
+ }
+ break;
+
+ case IMAGE_GRAYSCALE:
+ {
+ const LockImageBits bits(image);
+ LockImageBits::const_iterator it = bits.begin(), end = bits.end();
+
+ for (; it != end; ++it) {
+ color = *it;
+
+ if (graya_geta(color) > 0) {
+ color = graya_getv(color);
+ histogram.addSamples(rgba(color, color, color, 255), 1);
+ }
+ }
+ }
+ break;
+
+ case IMAGE_INDEXED:
+ ASSERT(false);
+ break;
- if (rgba_geta(color) > 0) {
- color |= rgba(0, 0, 0, 255);
- histogram.addSamples(color, 1);
- }
}
}
diff --git a/src/raster/quantization.h b/src/raster/quantization.h
index cb28e7bce..0a75055c9 100644
--- a/src/raster/quantization.h
+++ b/src/raster/quantization.h
@@ -24,6 +24,8 @@
#include "raster/frame_number.h"
#include "raster/pixel_format.h"
+#include
+
namespace raster {
class Image;
@@ -34,17 +36,27 @@ namespace raster {
namespace quantization {
+ void create_palette_from_images(
+ const std::vector& images,
+ Palette* palette,
+ bool has_background_layer);
+
// Creates a new palette suitable to quantize the given RGB sprite to Indexed color.
- Palette* create_palette_from_rgb(const Sprite* sprite, FrameNumber frameNumber);
+ Palette* create_palette_from_rgb(
+ const Sprite* sprite,
+ FrameNumber frameNumber,
+ Palette* newPalette); // Can be NULL to create a new palette
// Changes the image pixel format. The dithering method is used only
// when you want to convert from RGB to Indexed.
- Image* convert_pixel_format(const Image* image,
- PixelFormat pixelFormat,
- DitheringMethod ditheringMethod,
- const RgbMap* rgbmap,
- const Palette* palette,
- bool is_background_layer);
+ Image* convert_pixel_format(
+ const Image* src,
+ Image* dst, // Can be NULL to create a new image
+ PixelFormat pixelFormat,
+ DitheringMethod ditheringMethod,
+ const RgbMap* rgbmap,
+ const Palette* palette,
+ bool is_background);
} // namespace quantization
} // namespace raster
diff --git a/src/raster/rgbmap.cpp b/src/raster/rgbmap.cpp
index 0fb6d3cff..25ffa8426 100644
--- a/src/raster/rgbmap.cpp
+++ b/src/raster/rgbmap.cpp
@@ -22,85 +22,52 @@
#include "raster/rgbmap.h"
-#include "raster/conversion_alleg.h"
+#include "raster/color_scales.h"
#include "raster/palette.h"
-#include
-
namespace raster {
-class RgbMapImpl {
-public:
- RgbMapImpl() {
- m_allegMap = new RGB_MAP;
- m_palette = NULL;
- m_modifications = 0;
- }
-
- ~RgbMapImpl() {
- delete m_allegMap;
- }
-
- bool match(const Palette* palette) const {
- return (m_palette == palette &&
- m_modifications == palette->getModifications());
- }
-
- void regenerate(const Palette* palette) {
- m_palette = palette;
- m_modifications = palette->getModifications();
-
- PALETTE allegPal;
- convert_palette_to_allegro(palette, allegPal);
- create_rgb_table(m_allegMap, allegPal, NULL);
-
- for (int r=0; r<32; ++r)
- for (int g=0; g<32; ++g)
- for (int b=0; b<32; ++b) {
- if (m_allegMap->data[r][g][b] >= palette->size())
- m_allegMap->data[r][g][b] = palette->findBestfit(_rgb_scale_5[r],
- _rgb_scale_5[g],
- _rgb_scale_5[b]);
- }
- }
-
- int mapColor(int r, int g, int b) const {
- ASSERT(r >= 0 && r < 256);
- ASSERT(g >= 0 && g < 256);
- ASSERT(b >= 0 && b < 256);
- return m_allegMap->data[r>>3][g>>3][b>>3];
- }
-
-private:
- RGB_MAP* m_allegMap;
- const Palette* m_palette;
- int m_modifications;
-};
+#define MAPSIZE 32*32*32
RgbMap::RgbMap()
: Object(OBJECT_RGBMAP)
+ , m_map(MAPSIZE)
+ , m_palette(NULL)
+ , m_modifications(0)
{
- m_impl = new RgbMapImpl;
-}
-
-RgbMap::~RgbMap()
-{
- delete m_impl;
}
bool RgbMap::match(const Palette* palette) const
{
- return m_impl->match(palette);
+ return (m_palette == palette &&
+ m_modifications == palette->getModifications());
}
-void RgbMap::regenerate(const Palette* palette)
+void RgbMap::regenerate(const Palette* palette, int mask_index)
{
- m_impl->regenerate(palette);
+ m_palette = palette;
+ m_modifications = palette->getModifications();
+
+ int i = 0;
+ for (int r=0; r<32; ++r) {
+ for (int g=0; g<32; ++g) {
+ for (int b=0; b<32; ++b) {
+ m_map[i++] =
+ palette->findBestfit(
+ scale_5bits_to_8bits(r),
+ scale_5bits_to_8bits(g),
+ scale_5bits_to_8bits(b), mask_index);
+ }
+ }
+ }
}
int RgbMap::mapColor(int r, int g, int b) const
{
- return m_impl->mapColor(r, g, b);
+ ASSERT(r >= 0 && r < 256);
+ ASSERT(g >= 0 && g < 256);
+ ASSERT(b >= 0 && b < 256);
+ return m_map[((r>>3) << 10) + ((g>>3) << 5) + (b>>3)];
}
} // namespace raster
diff --git a/src/raster/rgbmap.h b/src/raster/rgbmap.h
index 4a92c0331..273f561f6 100644
--- a/src/raster/rgbmap.h
+++ b/src/raster/rgbmap.h
@@ -23,6 +23,8 @@
#include "base/disable_copying.h"
#include "raster/object.h"
+#include
+
namespace raster {
class Palette;
@@ -30,15 +32,16 @@ namespace raster {
class RgbMap : public Object {
public:
RgbMap();
- virtual ~RgbMap();
bool match(const Palette* palette) const;
- void regenerate(const Palette* palette);
+ void regenerate(const Palette* palette, int mask_index);
int mapColor(int r, int g, int b) const;
private:
- class RgbMapImpl* m_impl;
+ std::vector m_map;
+ const Palette* m_palette;
+ int m_modifications;
DISABLE_COPYING(RgbMap);
};
diff --git a/src/raster/sprite.cpp b/src/raster/sprite.cpp
index bd832c7d3..8a5b9aeca 100644
--- a/src/raster/sprite.cpp
+++ b/src/raster/sprite.cpp
@@ -278,13 +278,16 @@ void Sprite::deletePalette(Palette* pal)
RgbMap* Sprite::getRgbMap(FrameNumber frame)
{
+ int mask_color = (getBackgroundLayer() ? -1: getTransparentColor());
+
if (m_rgbMap == NULL) {
m_rgbMap = new RgbMap();
- m_rgbMap->regenerate(getPalette(frame));
+ m_rgbMap->regenerate(getPalette(frame), mask_color);
}
else if (!m_rgbMap->match(getPalette(frame))) {
- m_rgbMap->regenerate(getPalette(frame));
+ m_rgbMap->regenerate(getPalette(frame), mask_color);
}
+
return m_rgbMap;
}