Add support to save .ase files with more than 256 palette color entries

We've added a new chunk type in .ase files to save palettes with alpha
channel, color name, and palettes with more than 256 colors.

Related to #668, #467, and #286
This commit is contained in:
David Capello 2015-06-30 13:34:55 -03:00
parent a12816c04e
commit 7a63476a98
5 changed files with 111 additions and 32 deletions

View File

@ -108,7 +108,7 @@ Then each chunk format is:
5. Chunk types
========================================
Palette chunk (0x0004)
Old palette chunk (0x0004)
----------------------------------------
WORD Number of packets
@ -244,6 +244,24 @@ Frame Tags Chunk (0x2018)
STRING Tag name
Palette Chunk (0x2019)
----------------------------------------
DWORD New palette size (total number of entries)
DWORD First color index to change
DWORD Last color index to change
BYTE[8] For future (set to zero)
+ For each palette entry in [from,to] range (to-from+1 entries)
WORD Entry flags:
1 = Has name
BYTE Red (0-255)
BYTE Green (0-255)
BYTE Blue (0-255)
BYTE Alpha (0-255)
+ If has name bit in entry flags
STRING Color name
Notes
----------------------------------------

View File

@ -31,6 +31,7 @@
#define ASE_FILE_CHUNK_MASK 0x2016
#define ASE_FILE_CHUNK_PATH 0x2017
#define ASE_FILE_CHUNK_FRAME_TAGS 0x2018
#define ASE_FILE_CHUNK_PALETTE 0x2019
#define ASE_FILE_RAW_CEL 0
#define ASE_FILE_LINK_CEL 1
@ -97,9 +98,10 @@ static void ase_file_write_string(FILE* f, const std::string& string);
static void ase_file_write_start_chunk(FILE* f, ASE_FrameHeader* frame_header, int type, ASE_Chunk* chunk);
static void ase_file_write_close_chunk(FILE* f, ASE_Chunk* chunk);
static Palette* ase_file_read_color_chunk(FILE* f, Sprite* sprite, frame_t frame);
static Palette* ase_file_read_color2_chunk(FILE* f, Sprite* sprite, frame_t frame);
static void ase_file_write_color2_chunk(FILE* f, ASE_FrameHeader* frame_header, Palette* pal);
static Palette* ase_file_read_color_chunk(FILE* f, Palette* prevPal, frame_t frame);
static Palette* ase_file_read_color2_chunk(FILE* f, Palette* prevPal, frame_t frame);
static Palette* ase_file_read_palette_chunk(FILE* f, Palette* prevPal, frame_t frame);
static void ase_file_write_palette_chunk(FILE* f, ASE_FrameHeader* frame_header, Palette* pal, int from, int to);
static Layer* ase_file_read_layer_chunk(FILE* f, Sprite* sprite, Layer** previous_layer, int* current_level);
static void ase_file_write_layer_chunk(FILE* f, ASE_FrameHeader* frame_header, Layer* layer);
static Cel* ase_file_read_cel_chunk(FILE* f, Sprite* sprite, frame_t frame, PixelFormat pixelFormat, FileOp* fop, ASE_Header* header, size_t chunk_end);
@ -141,7 +143,8 @@ class AseFormat : public FileFormat {
FILE_SUPPORT_LAYERS |
FILE_SUPPORT_FRAMES |
FILE_SUPPORT_PALETTES |
FILE_SUPPORT_FRAME_TAGS;
FILE_SUPPORT_FRAME_TAGS |
FILE_SUPPORT_BIG_PALETTES;
}
bool onLoad(FileOp* fop) override;
@ -210,19 +213,24 @@ bool AseFormat::onLoad(FileOp* fop)
switch (chunk_type) {
/* only for 8 bpp images */
case ASE_FILE_CHUNK_FLI_COLOR:
case ASE_FILE_CHUNK_FLI_COLOR2: {
Palette* prev_pal = sprite->palette(frame);
Palette* pal =
chunk_type == ASE_FILE_CHUNK_FLI_COLOR ?
ase_file_read_color_chunk(f, sprite, frame):
ase_file_read_color2_chunk(f, sprite, frame);
Palette* prevPal = sprite->palette(frame);
UniquePtr<Palette> pal(chunk_type == ASE_FILE_CHUNK_FLI_COLOR ?
ase_file_read_color_chunk(f, prevPal, frame):
ase_file_read_color2_chunk(f, prevPal, frame));
if (prev_pal->countDiff(pal, NULL, NULL) > 0)
sprite->setPalette(pal, true);
if (prevPal->countDiff(pal.get(), NULL, NULL) > 0)
sprite->setPalette(pal.get(), true);
break;
}
delete pal;
case ASE_FILE_CHUNK_PALETTE: {
Palette* prevPal = sprite->palette(frame);
UniquePtr<Palette> pal(ase_file_read_palette_chunk(f, prevPal, frame));
if (prevPal->countDiff(pal.get(), NULL, NULL) > 0)
sprite->setPalette(pal.get(), true);
break;
}
@ -321,10 +329,13 @@ bool AseFormat::onSave(FileOp* fop)
frame_header.duration = sprite->frameDuration(frame);
// is the first frame or did the palette change?
Palette* pal = sprite->palette(frame);
int palFrom = 0, palTo = pal->size()-1;
if ((frame == 0 ||
sprite->palette(frame-1)->countDiff(sprite->palette(frame), NULL, NULL) > 0)) {
sprite->palette(frame-1)->countDiff(pal, &palFrom, &palTo) > 0)) {
// Write the color chunk
ase_file_write_color2_chunk(f, &frame_header, sprite->palette(frame));
ase_file_write_palette_chunk(f, &frame_header,
pal, palFrom, palTo);
}
// Write extra chunks in the first frame
@ -582,10 +593,10 @@ static void ase_file_write_close_chunk(FILE* f, ASE_Chunk* chunk)
fseek(f, chunk_end, SEEK_SET);
}
static Palette* ase_file_read_color_chunk(FILE* f, Sprite* sprite, frame_t frame)
static Palette* ase_file_read_color_chunk(FILE* f, Palette* prevPal, frame_t frame)
{
int i, c, r, g, b, packets, skip, size;
Palette* pal = new Palette(*sprite->palette(frame));
Palette* pal = new Palette(*prevPal);
pal->setFrame(frame);
packets = fgetw(f); // Number of packets
@ -610,10 +621,10 @@ static Palette* ase_file_read_color_chunk(FILE* f, Sprite* sprite, frame_t frame
return pal;
}
static Palette* ase_file_read_color2_chunk(FILE* f, Sprite* sprite, frame_t frame)
static Palette* ase_file_read_color2_chunk(FILE* f, Palette* prevPal, frame_t frame)
{
int i, c, r, g, b, packets, skip, size;
Palette* pal = new Palette(*sprite->palette(frame));
Palette* pal = new Palette(*prevPal);
pal->setFrame(frame);
packets = fgetw(f); // Number of packets
@ -636,23 +647,55 @@ static Palette* ase_file_read_color2_chunk(FILE* f, Sprite* sprite, frame_t fram
return pal;
}
/* writes the original color chunk in FLI files for the entire palette "pal" */
static void ase_file_write_color2_chunk(FILE* f, ASE_FrameHeader* frame_header, Palette* pal)
static Palette* ase_file_read_palette_chunk(FILE* f, Palette* prevPal, frame_t frame)
{
ChunkWriter chunk(f, frame_header, ASE_FILE_CHUNK_FLI_COLOR2);
int c, color;
Palette* pal = new Palette(*prevPal);
pal->setFrame(frame);
fputw(1, f); // number of packets
int newSize = fgetl(f);
int from = fgetl(f);
int to = fgetl(f);
ase_file_read_padding(f, 8);
// First packet
fputc(0, f); // skip 0 colors
fputc(pal->size() == 256 ? 0: pal->size(), f); // number of colors
for (c=0; c<pal->size(); c++) {
color = pal->getEntry(c);
if (newSize > 0)
pal->resize(newSize);
for (int c=from; c<=to; ++c) {
int flags = fgetw(f);
int r = fgetc(f);
int g = fgetc(f);
int b = fgetc(f);
int a = fgetc(f);
// TODO don't ignore alpha
pal->setEntry(c, rgba(r, g, b, 255));
// Skip name
if (flags & 1) {
std::string name = ase_file_read_string(f);
// Ignore color entry name
}
}
return pal;
}
static void ase_file_write_palette_chunk(FILE* f, ASE_FrameHeader* frame_header, Palette* pal, int from, int to)
{
ChunkWriter chunk(f, frame_header, ASE_FILE_CHUNK_PALETTE);
fputl(pal->size(), f);
fputl(from, f);
fputl(to, f);
ase_file_write_padding(f, 8);
for (int c=from; c<=to; ++c) {
color_t color = pal->getEntry(c);
fputw(0, f); // Entry flags (without name)
fputc(rgba_getr(color), f);
fputc(rgba_getg(color), f);
fputc(rgba_getb(color), f);
fputc(rgba_geta(color), f);
}
}

View File

@ -322,6 +322,16 @@ FileOp* fop_to_save_document(const Context* context, const Document* document,
}
}
// Big palettes
if (!fop->format->support(FILE_SUPPORT_BIG_PALETTES)) {
for (Palette* pal : fop->document->sprite()->getPalettes()) {
if (pal->size() > 256) {
warnings += "<<- Palettes with more than 256 colors";
break;
}
}
}
// Show the confirmation alert
if (!warnings.empty()) {
// Interative
@ -737,7 +747,13 @@ 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)
{
uint32_t c = fop->seq.palette->getEntry(index);
uint32_t c;
ASSERT(index >= 0);
if (index >= 0 && index < fop->seq.palette->size())
c = fop->seq.palette->getEntry(index);
else
c = rgba(0, 0, 0, 255); // Black color
*r = rgba_getr(c);
*g = rgba_getg(c);

View File

@ -26,6 +26,7 @@
#define FILE_SUPPORT_SEQUENCES 0x00000400
#define FILE_SUPPORT_GET_FORMAT_OPTIONS 0x00000800
#define FILE_SUPPORT_FRAME_TAGS 0x00001000
#define FILE_SUPPORT_BIG_PALETTES 0x00002000 // Palettes w/more than 256 colors
namespace app {

View File

@ -46,7 +46,8 @@ namespace doc {
// there are situations that are not handled quite well yet.
// E.g. A palette with lesser colors is loaded
//
//ASSERT(i >= 0 && i < size());
//ASSERT(i < size());
ASSERT(i >= 0);
if (i >= 0 && i < size())
return m_colors[i];
else