mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-10 03:44:16 +00:00
2429 lines
57 KiB
C++
2429 lines
57 KiB
C++
/* ASE - Allegro Sprite Editor
|
|
* Copyright (C) 2001-2010 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 "config.h"
|
|
|
|
#include <vector>
|
|
#include <list>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <allegro/config.h>
|
|
|
|
#include "raster/cel.h"
|
|
#include "raster/dirty.h"
|
|
#include "raster/image.h"
|
|
#include "raster/layer.h"
|
|
#include "raster/mask.h"
|
|
#include "raster/palette.h"
|
|
#include "raster/sprite.h"
|
|
#include "raster/stock.h"
|
|
#include "raster/undo.h"
|
|
|
|
/* undo state */
|
|
enum {
|
|
DO_UNDO,
|
|
DO_REDO,
|
|
};
|
|
|
|
// undo types
|
|
enum {
|
|
// group
|
|
UNDO_TYPE_OPEN,
|
|
UNDO_TYPE_CLOSE,
|
|
|
|
// data management
|
|
UNDO_TYPE_DATA,
|
|
|
|
// image management
|
|
UNDO_TYPE_IMAGE,
|
|
UNDO_TYPE_FLIP,
|
|
UNDO_TYPE_DIRTY,
|
|
|
|
// stock management
|
|
UNDO_TYPE_ADD_IMAGE,
|
|
UNDO_TYPE_REMOVE_IMAGE,
|
|
UNDO_TYPE_REPLACE_IMAGE,
|
|
|
|
// cel management
|
|
UNDO_TYPE_ADD_CEL,
|
|
UNDO_TYPE_REMOVE_CEL,
|
|
|
|
// layer management
|
|
UNDO_TYPE_SET_LAYER_NAME,
|
|
UNDO_TYPE_ADD_LAYER,
|
|
UNDO_TYPE_REMOVE_LAYER,
|
|
UNDO_TYPE_MOVE_LAYER,
|
|
UNDO_TYPE_SET_LAYER,
|
|
|
|
// palette management
|
|
UNDO_TYPE_ADD_PALETTE,
|
|
UNDO_TYPE_REMOVE_PALETTE,
|
|
UNDO_TYPE_REMAP_PALETTE,
|
|
|
|
/* misc */
|
|
UNDO_TYPE_SET_MASK,
|
|
UNDO_TYPE_SET_IMGTYPE,
|
|
UNDO_TYPE_SET_SIZE,
|
|
UNDO_TYPE_SET_FRAME,
|
|
UNDO_TYPE_SET_FRAMES,
|
|
UNDO_TYPE_SET_FRLEN,
|
|
};
|
|
|
|
struct UndoChunkData;
|
|
struct UndoChunkImage;
|
|
struct UndoChunkFlip;
|
|
struct UndoChunkDirty;
|
|
struct UndoChunkAddImage;
|
|
struct UndoChunkRemoveImage;
|
|
struct UndoChunkReplaceImage;
|
|
struct UndoChunkAddCel;
|
|
struct UndoChunkRemoveCel;
|
|
struct UndoChunkSetLayerName;
|
|
struct UndoChunkAddLayer;
|
|
struct UndoChunkRemoveLayer;
|
|
struct UndoChunkMoveLayer;
|
|
struct UndoChunkSetLayer;
|
|
struct UndoChunkAddPalette;
|
|
struct UndoChunkRemovePalette;
|
|
struct UndoChunkSetMask;
|
|
struct UndoChunkSetImgType;
|
|
struct UndoChunkSetSize;
|
|
struct UndoChunkSetFrame;
|
|
struct UndoChunkSetFrames;
|
|
struct UndoChunkSetFrlen;
|
|
struct UndoChunkRemapPalette;
|
|
|
|
struct UndoChunk
|
|
{
|
|
int type;
|
|
int size;
|
|
const char *label;
|
|
};
|
|
|
|
static UndoChunk* undo_chunk_new(UndoStream* stream, int type, int size);
|
|
static void undo_chunk_free(UndoChunk* chunk);
|
|
|
|
typedef std::list<UndoChunk*> ChunksList;
|
|
|
|
#include "undo_stream.h"
|
|
|
|
struct UndoAction
|
|
{
|
|
const char *name;
|
|
void (*invert)(UndoStream* stream, UndoChunk* chunk);
|
|
};
|
|
|
|
static int count_undo_groups(UndoStream* undo_stream);
|
|
static bool out_of_group(UndoStream* undo_stream);
|
|
|
|
/* Undo actions */
|
|
|
|
static void chunk_open_new(UndoStream* stream);
|
|
static void chunk_open_invert(UndoStream* stream, UndoChunk* chunk);
|
|
|
|
static void chunk_close_new(UndoStream* stream);
|
|
static void chunk_close_invert(UndoStream* stream, UndoChunk* chunk);
|
|
|
|
static void chunk_data_new(UndoStream* stream, GfxObj *gfxobj, void *data, int size);
|
|
static void chunk_data_invert(UndoStream* stream, UndoChunkData *chunk);
|
|
|
|
static void chunk_image_new(UndoStream* stream, Image* image, int x, int y, int w, int h);
|
|
static void chunk_image_invert(UndoStream* stream, UndoChunkImage* chunk);
|
|
|
|
static void chunk_flip_new(UndoStream* stream, Image* image, int x1, int y1, int x2, int y2, bool horz);
|
|
static void chunk_flip_invert(UndoStream* stream, UndoChunkFlip *chunk);
|
|
|
|
static void chunk_dirty_new(UndoStream* stream, Dirty *dirty);
|
|
static void chunk_dirty_invert(UndoStream* stream, UndoChunkDirty *chunk);
|
|
|
|
static void chunk_add_image_new(UndoStream* stream, Stock *stock, int image_index);
|
|
static void chunk_add_image_invert(UndoStream* stream, UndoChunkAddImage* chunk);
|
|
|
|
static void chunk_remove_image_new(UndoStream* stream, Stock *stock, int image_index);
|
|
static void chunk_remove_image_invert(UndoStream* stream, UndoChunkRemoveImage* chunk);
|
|
|
|
static void chunk_replace_image_new(UndoStream* stream, Stock *stock, int image_index);
|
|
static void chunk_replace_image_invert(UndoStream* stream, UndoChunkReplaceImage* chunk);
|
|
|
|
static void chunk_add_cel_new(UndoStream* stream, Layer* layer, Cel* cel);
|
|
static void chunk_add_cel_invert(UndoStream* stream, UndoChunkAddCel* chunk);
|
|
|
|
static void chunk_remove_cel_new(UndoStream* stream, Layer* layer, Cel* cel);
|
|
static void chunk_remove_cel_invert(UndoStream* stream, UndoChunkRemoveCel* chunk);
|
|
|
|
static void chunk_set_layer_name_new(UndoStream* stream, Layer *layer);
|
|
static void chunk_set_layer_name_invert(UndoStream* stream, UndoChunkSetLayerName* chunk);
|
|
|
|
static void chunk_add_layer_new(UndoStream* stream, Layer* set, Layer* layer);
|
|
static void chunk_add_layer_invert(UndoStream* stream, UndoChunkAddLayer* chunk);
|
|
|
|
static void chunk_remove_layer_new(UndoStream* stream, Layer* layer);
|
|
static void chunk_remove_layer_invert(UndoStream* stream, UndoChunkRemoveLayer* chunk);
|
|
|
|
static void chunk_move_layer_new(UndoStream* stream, Layer* layer);
|
|
static void chunk_move_layer_invert(UndoStream* stream, UndoChunkMoveLayer* chunk);
|
|
|
|
static void chunk_set_layer_new(UndoStream* stream, Sprite *sprite);
|
|
static void chunk_set_layer_invert(UndoStream* stream, UndoChunkSetLayer* chunk);
|
|
|
|
static void chunk_add_palette_new(UndoStream* stream, Sprite *sprite, Palette* palette);
|
|
static void chunk_add_palette_invert(UndoStream* stream, UndoChunkAddPalette *chunk);
|
|
|
|
static void chunk_remove_palette_new(UndoStream* stream, Sprite *sprite, Palette* palette);
|
|
static void chunk_remove_palette_invert(UndoStream* stream, UndoChunkRemovePalette *chunk);
|
|
|
|
static void chunk_remap_palette_new(UndoStream* stream, Sprite* sprite, int frame_from, int frame_to, const std::vector<int>& mapping);
|
|
static void chunk_remap_palette_invert(UndoStream* stream, UndoChunkRemapPalette *chunk);
|
|
|
|
static void chunk_set_mask_new(UndoStream* stream, Sprite *sprite);
|
|
static void chunk_set_mask_invert(UndoStream* stream, UndoChunkSetMask* chunk);
|
|
|
|
static void chunk_set_imgtype_new(UndoStream* stream, Sprite *sprite);
|
|
static void chunk_set_imgtype_invert(UndoStream* stream, UndoChunkSetImgType *chunk);
|
|
|
|
static void chunk_set_size_new(UndoStream* stream, Sprite *sprite);
|
|
static void chunk_set_size_invert(UndoStream* stream, UndoChunkSetSize *chunk);
|
|
|
|
static void chunk_set_frame_new(UndoStream* stream, Sprite *sprite);
|
|
static void chunk_set_frame_invert(UndoStream* stream, UndoChunkSetFrame *chunk);
|
|
|
|
static void chunk_set_frames_new(UndoStream* stream, Sprite *sprite);
|
|
static void chunk_set_frames_invert(UndoStream* stream, UndoChunkSetFrames *chunk);
|
|
|
|
static void chunk_set_frlen_new(UndoStream* stream, Sprite *sprite, int frame);
|
|
static void chunk_set_frlen_invert(UndoStream* stream, UndoChunkSetFrlen *chunk);
|
|
|
|
#define DECL_UNDO_ACTION(name) \
|
|
{ #name, (void (*)(UndoStream* , UndoChunk*))chunk_##name##_invert }
|
|
|
|
/* warning: in the same order as in UNDO_TYPEs */
|
|
static UndoAction undo_actions[] = {
|
|
DECL_UNDO_ACTION(open),
|
|
DECL_UNDO_ACTION(close),
|
|
DECL_UNDO_ACTION(data),
|
|
DECL_UNDO_ACTION(image),
|
|
DECL_UNDO_ACTION(flip),
|
|
DECL_UNDO_ACTION(dirty),
|
|
DECL_UNDO_ACTION(add_image),
|
|
DECL_UNDO_ACTION(remove_image),
|
|
DECL_UNDO_ACTION(replace_image),
|
|
DECL_UNDO_ACTION(add_cel),
|
|
DECL_UNDO_ACTION(remove_cel),
|
|
DECL_UNDO_ACTION(set_layer_name),
|
|
DECL_UNDO_ACTION(add_layer),
|
|
DECL_UNDO_ACTION(remove_layer),
|
|
DECL_UNDO_ACTION(move_layer),
|
|
DECL_UNDO_ACTION(set_layer),
|
|
DECL_UNDO_ACTION(add_palette),
|
|
DECL_UNDO_ACTION(remove_palette),
|
|
DECL_UNDO_ACTION(remap_palette),
|
|
DECL_UNDO_ACTION(set_mask),
|
|
DECL_UNDO_ACTION(set_imgtype),
|
|
DECL_UNDO_ACTION(set_size),
|
|
DECL_UNDO_ACTION(set_frame),
|
|
DECL_UNDO_ACTION(set_frames),
|
|
DECL_UNDO_ACTION(set_frlen),
|
|
};
|
|
|
|
/* Raw data */
|
|
|
|
static Dirty *read_raw_dirty(ase_uint8* raw_data);
|
|
static ase_uint8* write_raw_dirty(ase_uint8* raw_data, Dirty *dirty);
|
|
static int get_raw_dirty_size(Dirty *dirty);
|
|
|
|
static Image* read_raw_image(ase_uint8* raw_data);
|
|
static ase_uint8* write_raw_image(ase_uint8* raw_data, Image* image);
|
|
static int get_raw_image_size(Image* image);
|
|
|
|
static Cel* read_raw_cel(ase_uint8* raw_data);
|
|
static ase_uint8* write_raw_cel(ase_uint8* raw_data, Cel* cel);
|
|
static int get_raw_cel_size(Cel* cel);
|
|
|
|
static Layer* read_raw_layer(ase_uint8* raw_data);
|
|
static ase_uint8* write_raw_layer(ase_uint8* raw_data, Layer* layer);
|
|
static int get_raw_layer_size(Layer* layer);
|
|
|
|
static Palette* read_raw_palette(ase_uint8* raw_data);
|
|
static ase_uint8* write_raw_palette(ase_uint8* raw_data, Palette* palette);
|
|
static int get_raw_palette_size(Palette* palette);
|
|
|
|
static Mask* read_raw_mask(ase_uint8* raw_data);
|
|
static ase_uint8* write_raw_mask(ase_uint8* raw_data, Mask* mask);
|
|
static int get_raw_mask_size(Mask* mask);
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
Undo::Undo(Sprite* sprite)
|
|
: GfxObj(GFXOBJ_UNDO)
|
|
{
|
|
m_sprite = sprite;
|
|
m_diffCount = 0;
|
|
m_diffSaved = 0;
|
|
m_enabled = true;
|
|
m_label = NULL;
|
|
|
|
m_undoStream = new UndoStream(this);
|
|
try {
|
|
m_redoStream = new UndoStream(this);
|
|
}
|
|
catch (...) {
|
|
delete m_undoStream;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
Undo::~Undo()
|
|
{
|
|
delete m_undoStream;
|
|
delete m_redoStream;
|
|
}
|
|
|
|
bool Undo::isEnabled() const
|
|
{
|
|
return m_enabled ? true: false;
|
|
}
|
|
|
|
void Undo::setEnabled(bool state)
|
|
{
|
|
m_enabled = state;
|
|
}
|
|
|
|
bool Undo::canUndo() const
|
|
{
|
|
return !m_undoStream->empty();
|
|
}
|
|
|
|
bool Undo::canRedo() const
|
|
{
|
|
return !m_redoStream->empty();
|
|
}
|
|
|
|
void Undo::doUndo()
|
|
{
|
|
runUndo(DO_UNDO);
|
|
}
|
|
|
|
void Undo::doRedo()
|
|
{
|
|
runUndo(DO_REDO);
|
|
}
|
|
|
|
void Undo::clearRedo()
|
|
{
|
|
if (!m_redoStream->empty())
|
|
m_redoStream->clear();
|
|
}
|
|
|
|
const char* Undo::getLabel()
|
|
{
|
|
return m_label;
|
|
}
|
|
|
|
void Undo::setLabel(const char* label)
|
|
{
|
|
m_label = label;
|
|
}
|
|
|
|
const char* Undo::getNextUndoLabel() const
|
|
{
|
|
UndoChunk* chunk;
|
|
|
|
ASSERT(canUndo());
|
|
|
|
chunk = *m_undoStream->begin();
|
|
return chunk->label;
|
|
}
|
|
|
|
const char* Undo::getNextRedoLabel() const
|
|
{
|
|
UndoChunk* chunk;
|
|
|
|
ASSERT(canRedo());
|
|
|
|
chunk = *m_redoStream->begin();
|
|
return chunk->label;
|
|
}
|
|
|
|
bool Undo::isSavedState() const
|
|
{
|
|
return (m_diffCount == m_diffSaved);
|
|
}
|
|
|
|
void Undo::markSavedState()
|
|
{
|
|
m_diffSaved = m_diffCount;
|
|
}
|
|
|
|
void Undo::runUndo(int state)
|
|
{
|
|
UndoStream* undo_stream = ((state == DO_UNDO)? m_undoStream:
|
|
m_redoStream);
|
|
UndoStream* redo_stream = ((state == DO_REDO)? m_undoStream:
|
|
m_redoStream);
|
|
UndoChunk* chunk;
|
|
int level = 0;
|
|
|
|
do {
|
|
chunk = undo_stream->popChunk(false); // read from head
|
|
if (!chunk)
|
|
break;
|
|
|
|
setLabel(chunk->label);
|
|
(undo_actions[chunk->type].invert)(redo_stream, chunk);
|
|
|
|
if (chunk->type == UNDO_TYPE_OPEN)
|
|
level++;
|
|
else if (chunk->type == UNDO_TYPE_CLOSE)
|
|
level--;
|
|
|
|
undo_chunk_free(chunk);
|
|
|
|
if (state == DO_UNDO)
|
|
m_diffCount--;
|
|
else if (state == DO_REDO)
|
|
m_diffCount++;
|
|
} while (level);
|
|
}
|
|
|
|
void Undo::discardTail()
|
|
{
|
|
UndoStream* undo_stream = m_undoStream;
|
|
UndoChunk* chunk;
|
|
int level = 0;
|
|
|
|
do {
|
|
chunk = undo_stream->popChunk(true); // read from tail
|
|
if (!chunk)
|
|
break;
|
|
|
|
if (chunk->type == UNDO_TYPE_OPEN)
|
|
level++;
|
|
else if (chunk->type == UNDO_TYPE_CLOSE)
|
|
level--;
|
|
|
|
undo_chunk_free(chunk);
|
|
} while (level);
|
|
}
|
|
|
|
static int count_undo_groups(UndoStream* undo_stream)
|
|
{
|
|
UndoChunk* chunk;
|
|
int groups = 0;
|
|
int level;
|
|
|
|
ChunksList::iterator it = undo_stream->begin();
|
|
while (it != undo_stream->end()) {
|
|
level = 0;
|
|
|
|
do {
|
|
chunk = *it;
|
|
++it;
|
|
|
|
if (chunk->type == UNDO_TYPE_OPEN)
|
|
level++;
|
|
else if (chunk->type == UNDO_TYPE_CLOSE)
|
|
level--;
|
|
} while (level && (it != undo_stream->end()));
|
|
|
|
if (level == 0)
|
|
groups++;
|
|
}
|
|
|
|
return groups;
|
|
}
|
|
|
|
static bool out_of_group(UndoStream* undo_stream)
|
|
{
|
|
UndoChunk* chunk;
|
|
int level = 0;
|
|
|
|
ChunksList::iterator it = undo_stream->begin();
|
|
while (it != undo_stream->end()) {
|
|
level = 0;
|
|
|
|
do {
|
|
chunk = *it;
|
|
++it;
|
|
|
|
if (chunk->type == UNDO_TYPE_OPEN)
|
|
level++;
|
|
else if (chunk->type == UNDO_TYPE_CLOSE)
|
|
level--;
|
|
} while (level && (it != undo_stream->end()));
|
|
}
|
|
|
|
return level == 0;
|
|
}
|
|
|
|
// Called every time a new undo is added.
|
|
void Undo::updateUndo()
|
|
{
|
|
// TODO replace this with the following implementation:
|
|
// * Add the undo limit to Undo class as a normal member (non-static).
|
|
// * Add a new AseSprite (wrapper of a generic Sprite).
|
|
// * Add ASE delegates to listen changes in ASE configuration
|
|
// * AseSprite should implement a delegate to listen changes to the undo limit,
|
|
// * When a change is produced, AseSprite updates the wrappedSprite->getUndo()->setUndoLimit().
|
|
int undo_size_limit = get_config_int("Options", "UndoSizeLimit", 8)*1024*1024;
|
|
|
|
// More differences.
|
|
m_diffCount++;
|
|
|
|
// Reset the "redo" stream.
|
|
clearRedo();
|
|
|
|
if (out_of_group(m_undoStream)) {
|
|
int groups = count_undo_groups(m_undoStream);
|
|
|
|
// "undo" is too big?
|
|
while (groups > 1 && m_undoStream->getMemSize() > undo_size_limit) {
|
|
discardTail();
|
|
groups--;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"open"
|
|
|
|
no data
|
|
|
|
***********************************************************************/
|
|
|
|
void Undo::undo_open()
|
|
{
|
|
chunk_open_new(m_undoStream);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_open_new(UndoStream* stream)
|
|
{
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_OPEN,
|
|
sizeof(UndoChunk));
|
|
}
|
|
|
|
static void chunk_open_invert(UndoStream* stream, UndoChunk* chunk)
|
|
{
|
|
chunk_close_new(stream);
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"close"
|
|
|
|
no data
|
|
|
|
***********************************************************************/
|
|
|
|
void Undo::undo_close()
|
|
{
|
|
chunk_close_new(m_undoStream);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_close_new(UndoStream* stream)
|
|
{
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_CLOSE,
|
|
sizeof(UndoChunk));
|
|
}
|
|
|
|
static void chunk_close_invert(UndoStream* stream, UndoChunk* chunk)
|
|
{
|
|
chunk_open_new(stream);
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"data"
|
|
|
|
DWORD object ID
|
|
DWORD data address offset
|
|
DWORD data size
|
|
BYTE[] data bytes
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkData
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 gfxobj_id;
|
|
ase_uint32 dataoffset;
|
|
ase_uint32 datasize;
|
|
ase_uint8 data[0];
|
|
};
|
|
|
|
void Undo::undo_data(GfxObj *gfxobj, void *data, int size)
|
|
{
|
|
chunk_data_new(m_undoStream, gfxobj, data, size);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_data_new(UndoStream* stream, GfxObj *gfxobj, void *data, int size)
|
|
{
|
|
UndoChunkData *chunk;
|
|
ase_uint32 offset = (unsigned int)(((ase_uint8* )data) -
|
|
((ase_uint8* )gfxobj));
|
|
|
|
ASSERT(size >= 1);
|
|
|
|
chunk = (UndoChunkData *)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_DATA,
|
|
sizeof(UndoChunkData)+size);
|
|
|
|
chunk->gfxobj_id = gfxobj->getId();
|
|
chunk->dataoffset = offset;
|
|
chunk->datasize = size;
|
|
|
|
memcpy(chunk->data, data, size);
|
|
}
|
|
|
|
static void chunk_data_invert(UndoStream* stream, UndoChunkData *chunk)
|
|
{
|
|
unsigned int id = chunk->gfxobj_id;
|
|
unsigned int offset = chunk->dataoffset;
|
|
unsigned int size = chunk->datasize;
|
|
GfxObj *gfxobj = GfxObj::find(id);
|
|
|
|
if (gfxobj) {
|
|
void *data = (void *)(((ase_uint8* )gfxobj) + offset);
|
|
|
|
/* save the current data */
|
|
chunk_data_new(stream, gfxobj, data, size);
|
|
|
|
/* copy back the old data */
|
|
memcpy(data, chunk->data, size);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"image"
|
|
|
|
DWORD image ID
|
|
BYTE image type
|
|
WORD[4] x, y, w, h
|
|
for each row ("h" times)
|
|
for each pixel ("w" times)
|
|
BYTE[4] for RGB images, or
|
|
BYTE[2] for Grayscale images, or
|
|
BYTE for Indexed images
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkImage
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 image_id;
|
|
ase_uint8 imgtype;
|
|
ase_uint16 x, y, w, h;
|
|
ase_uint8 data[0];
|
|
};
|
|
|
|
void Undo::undo_image(Image* image, int x, int y, int w, int h)
|
|
{
|
|
chunk_image_new(m_undoStream, image, x, y, w, h);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_image_new(UndoStream* stream, Image* image, int x, int y, int w, int h)
|
|
{
|
|
UndoChunkImage* chunk;
|
|
ase_uint8* ptr;
|
|
int v, size;
|
|
|
|
ASSERT(w >= 1 && h >= 1);
|
|
ASSERT(x >= 0 && y >= 0 && x+w <= image->w && y+h <= image->h);
|
|
|
|
size = image_line_size(image, w);
|
|
|
|
chunk = (UndoChunkImage* )
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_IMAGE,
|
|
sizeof(UndoChunkImage) + size*h);
|
|
|
|
chunk->image_id = image->getId();
|
|
chunk->imgtype = image->imgtype;
|
|
chunk->x = x;
|
|
chunk->y = y;
|
|
chunk->w = w;
|
|
chunk->h = h;
|
|
|
|
ptr = chunk->data;
|
|
for (v=0; v<h; ++v) {
|
|
memcpy(ptr, image_address(image, x, y+v), size);
|
|
ptr += size;
|
|
}
|
|
}
|
|
|
|
static void chunk_image_invert(UndoStream* stream, UndoChunkImage* chunk)
|
|
{
|
|
unsigned int id = chunk->image_id;
|
|
int imgtype = chunk->imgtype;
|
|
Image* image = (Image*)GfxObj::find(id);
|
|
|
|
if ((image) && (image->getType() == GFXOBJ_IMAGE) &&
|
|
(image->imgtype == imgtype)) {
|
|
int x, y, w, h;
|
|
ase_uint8* ptr;
|
|
int v, size;
|
|
|
|
x = chunk->x;
|
|
y = chunk->y;
|
|
w = chunk->w;
|
|
h = chunk->h;
|
|
|
|
/* backup the current image portion */
|
|
chunk_image_new(stream, image, x, y, w, h);
|
|
|
|
/* restore the old image portion */
|
|
size = image_line_size(image, chunk->w);
|
|
ptr = chunk->data;
|
|
|
|
for (v=0; v<h; ++v) {
|
|
memcpy(image_address(image, x, y+v), ptr, size);
|
|
ptr += size;
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"flip"
|
|
|
|
DWORD image ID
|
|
BYTE image type
|
|
WORD[4] x1, y1, x2, y2
|
|
BYTE 1=horizontal 0=vertical
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkFlip
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 image_id;
|
|
ase_uint8 imgtype;
|
|
ase_uint16 x1, y1, x2, y2;
|
|
ase_uint8 horz;
|
|
};
|
|
|
|
void Undo::undo_flip(Image* image, int x1, int y1, int x2, int y2, bool horz)
|
|
{
|
|
chunk_flip_new(m_undoStream, image, x1, y1, x2, y2, horz);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_flip_new(UndoStream* stream, Image* image, int x1, int y1, int x2, int y2, bool horz)
|
|
{
|
|
UndoChunkFlip *chunk = (UndoChunkFlip *)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_FLIP,
|
|
sizeof(UndoChunkFlip));
|
|
|
|
chunk->image_id = image->getId();
|
|
chunk->imgtype = image->imgtype;
|
|
chunk->x1 = x1;
|
|
chunk->y1 = y1;
|
|
chunk->x2 = x2;
|
|
chunk->y2 = y2;
|
|
chunk->horz = horz ? 1: 0;
|
|
}
|
|
|
|
static void chunk_flip_invert(UndoStream* stream, UndoChunkFlip* chunk)
|
|
{
|
|
Image* image = (Image*)GfxObj::find(chunk->image_id);
|
|
|
|
if ((image) &&
|
|
(image->getType() == GFXOBJ_IMAGE) &&
|
|
(image->imgtype == chunk->imgtype)) {
|
|
int x1 = chunk->x1;
|
|
int y1 = chunk->y1;
|
|
int x2 = chunk->x2;
|
|
int y2 = chunk->y2;
|
|
bool horz = (chunk->horz != 0);
|
|
|
|
chunk_flip_new(stream, image, x1, y1, x2, y2, horz);
|
|
|
|
Image* area = image_crop(image, x1, y1, x2-x1+1, y2-y1+1, 0);
|
|
int x, y;
|
|
|
|
for (y=0; y<(y2-y1+1); y++)
|
|
for (x=0; x<(x2-x1+1); x++)
|
|
image_putpixel(image,
|
|
horz ? x2-x: x1+x,
|
|
!horz? y2-y: y1+y,
|
|
image_getpixel(area, x, y));
|
|
|
|
image_free(area);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"dirty"
|
|
|
|
DIRTY_DATA see read/write_raw_dirty
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkDirty
|
|
{
|
|
UndoChunk head;
|
|
ase_uint8 data[0];
|
|
};
|
|
|
|
void Undo::undo_dirty(Dirty *dirty)
|
|
{
|
|
chunk_dirty_new(m_undoStream, dirty);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_dirty_new(UndoStream* stream, Dirty *dirty)
|
|
{
|
|
UndoChunkDirty *chunk = (UndoChunkDirty *)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_DIRTY,
|
|
sizeof(UndoChunkDirty)+get_raw_dirty_size(dirty));
|
|
|
|
write_raw_dirty(chunk->data, dirty);
|
|
}
|
|
|
|
static void chunk_dirty_invert(UndoStream* stream, UndoChunkDirty *chunk)
|
|
{
|
|
Dirty *dirty = read_raw_dirty(chunk->data);
|
|
|
|
if (dirty != NULL) {
|
|
dirty_swap(dirty);
|
|
chunk_dirty_new(stream, dirty);
|
|
dirty_free(dirty);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"add_image"
|
|
|
|
DWORD stock ID
|
|
DWORD index of the image in the stock
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkAddImage
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 stock_id;
|
|
ase_uint32 image_index;
|
|
};
|
|
|
|
void Undo::undo_add_image(Stock *stock, int image_index)
|
|
{
|
|
chunk_add_image_new(m_undoStream, stock, image_index);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_add_image_new(UndoStream* stream, Stock *stock, int image_index)
|
|
{
|
|
UndoChunkAddImage* chunk = (UndoChunkAddImage* )
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_ADD_IMAGE,
|
|
sizeof(UndoChunkAddImage));
|
|
|
|
chunk->stock_id = stock->getId();
|
|
chunk->image_index = image_index;
|
|
}
|
|
|
|
static void chunk_add_image_invert(UndoStream* stream, UndoChunkAddImage* chunk)
|
|
{
|
|
unsigned int stock_id = chunk->stock_id;
|
|
unsigned int image_index = chunk->image_index;
|
|
Stock *stock = (Stock *)GfxObj::find(stock_id);
|
|
|
|
if (stock) {
|
|
Image* image = stock->getImage(image_index);
|
|
if (image != NULL) {
|
|
chunk_remove_image_new(stream, stock, image_index);
|
|
stock->removeImage(image);
|
|
image_free(image);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"remove_image"
|
|
|
|
DWORD stock ID
|
|
DWORD index of the image in the stock
|
|
IMAGE_DATA see read/write_raw_image
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkRemoveImage
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 stock_id;
|
|
ase_uint32 image_index;
|
|
ase_uint8 data[0];
|
|
};
|
|
|
|
void Undo::undo_remove_image(Stock *stock, int image_index)
|
|
{
|
|
chunk_remove_image_new(m_undoStream, stock, image_index);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_remove_image_new(UndoStream* stream, Stock *stock, int image_index)
|
|
{
|
|
Image* image = stock->getImage(image_index);
|
|
UndoChunkRemoveImage* chunk = (UndoChunkRemoveImage*)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_REMOVE_IMAGE,
|
|
sizeof(UndoChunkRemoveImage)+get_raw_image_size(image));
|
|
|
|
chunk->stock_id = stock->getId();
|
|
chunk->image_index = image_index;
|
|
|
|
write_raw_image(chunk->data, image);
|
|
}
|
|
|
|
static void chunk_remove_image_invert(UndoStream* stream, UndoChunkRemoveImage* chunk)
|
|
{
|
|
unsigned int stock_id = chunk->stock_id;
|
|
unsigned int image_index = chunk->image_index;
|
|
Stock *stock = (Stock *)GfxObj::find(stock_id);
|
|
|
|
if (stock) {
|
|
Image* image = read_raw_image(chunk->data);
|
|
|
|
/* ASSERT(image != NULL); */
|
|
|
|
stock->replaceImage(image_index, image);
|
|
chunk_add_image_new(stream, stock, image_index);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"replace_image"
|
|
|
|
DWORD stock ID
|
|
DWORD index of the image in the stock
|
|
IMAGE_DATA see read/write_raw_image
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkReplaceImage
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 stock_id;
|
|
ase_uint32 image_index;
|
|
ase_uint8 data[0];
|
|
};
|
|
|
|
void Undo::undo_replace_image(Stock *stock, int image_index)
|
|
{
|
|
chunk_replace_image_new(m_undoStream, stock, image_index);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_replace_image_new(UndoStream* stream, Stock *stock, int image_index)
|
|
{
|
|
Image* image = stock->getImage(image_index);
|
|
UndoChunkReplaceImage* chunk = (UndoChunkReplaceImage* )
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_REPLACE_IMAGE,
|
|
sizeof(UndoChunkReplaceImage)+get_raw_image_size(image));
|
|
|
|
chunk->stock_id = stock->getId();
|
|
chunk->image_index = image_index;
|
|
|
|
write_raw_image(chunk->data, image);
|
|
}
|
|
|
|
static void chunk_replace_image_invert(UndoStream* stream, UndoChunkReplaceImage* chunk)
|
|
{
|
|
unsigned long stock_id = chunk->stock_id;
|
|
unsigned long image_index = chunk->image_index;
|
|
Stock* stock = (Stock*)GfxObj::find(stock_id);
|
|
|
|
if (stock) {
|
|
// read the image to be restored from the chunk
|
|
Image* image = read_raw_image(chunk->data);
|
|
|
|
// save the current image in the (redo) stream
|
|
chunk_replace_image_new(stream, stock, image_index);
|
|
Image* old_image = stock->getImage(image_index);
|
|
|
|
// replace the image in the stock
|
|
stock->replaceImage(image_index, image);
|
|
|
|
// destroy the old image
|
|
image_free(old_image);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"add_cel"
|
|
|
|
DWORD layer ID
|
|
DWORD cel ID
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkAddCel
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 layer_id;
|
|
ase_uint32 cel_id;
|
|
};
|
|
|
|
void Undo::undo_add_cel(Layer* layer, Cel* cel)
|
|
{
|
|
chunk_add_cel_new(m_undoStream, layer, cel);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_add_cel_new(UndoStream* stream, Layer* layer, Cel* cel)
|
|
{
|
|
UndoChunkAddCel* chunk = (UndoChunkAddCel* )
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_ADD_CEL,
|
|
sizeof(UndoChunkAddCel));
|
|
|
|
chunk->layer_id = layer->getId();
|
|
chunk->cel_id = cel->getId();
|
|
}
|
|
|
|
static void chunk_add_cel_invert(UndoStream* stream, UndoChunkAddCel* chunk)
|
|
{
|
|
LayerImage* layer = (LayerImage*)GfxObj::find(chunk->layer_id);
|
|
Cel* cel = (Cel*)GfxObj::find(chunk->cel_id);
|
|
|
|
if (layer && cel) {
|
|
chunk_remove_cel_new(stream, layer, cel);
|
|
|
|
layer->removeCel(cel);
|
|
cel_free(cel);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"remove_cel"
|
|
|
|
DWORD layer ID
|
|
CEL_DATA see read/write_raw_cel
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkRemoveCel
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 layer_id;
|
|
ase_uint8 data[0];
|
|
};
|
|
|
|
void Undo::undo_remove_cel(Layer* layer, Cel* cel)
|
|
{
|
|
chunk_remove_cel_new(m_undoStream, layer, cel);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_remove_cel_new(UndoStream* stream, Layer* layer, Cel* cel)
|
|
{
|
|
UndoChunkRemoveCel* chunk = (UndoChunkRemoveCel*)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_REMOVE_CEL,
|
|
sizeof(UndoChunkRemoveCel)+get_raw_cel_size(cel));
|
|
|
|
chunk->layer_id = layer->getId();
|
|
write_raw_cel(chunk->data, cel);
|
|
}
|
|
|
|
static void chunk_remove_cel_invert(UndoStream* stream, UndoChunkRemoveCel* chunk)
|
|
{
|
|
unsigned int layer_id = chunk->layer_id;
|
|
LayerImage* layer = (LayerImage*)GfxObj::find(layer_id);
|
|
|
|
if (layer) {
|
|
Cel* cel = read_raw_cel(chunk->data);
|
|
|
|
/* ASSERT(cel != NULL); */
|
|
|
|
chunk_add_cel_new(stream, layer, cel);
|
|
layer->addCel(cel);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"set_layer_name"
|
|
|
|
DWORD layer ID
|
|
DWORD name length
|
|
BYTES[length] name text
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkSetLayerName
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 layer_id;
|
|
ase_uint16 name_length;
|
|
ase_uint8 name_text[0];
|
|
};
|
|
|
|
void Undo::undo_set_layer_name(Layer* layer)
|
|
{
|
|
chunk_set_layer_name_new(m_undoStream, layer);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_set_layer_name_new(UndoStream* stream, Layer *layer)
|
|
{
|
|
std::string layer_name = layer->getName();
|
|
|
|
UndoChunkSetLayerName* chunk = (UndoChunkSetLayerName*)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_SET_LAYER_NAME,
|
|
sizeof(UndoChunkSetLayerName) + layer_name.size());
|
|
|
|
chunk->layer_id = layer->getId();
|
|
chunk->name_length = layer_name.size();
|
|
|
|
for (int c=0; c<chunk->name_length; c++)
|
|
chunk->name_text[c] = layer_name[c];
|
|
}
|
|
|
|
static void chunk_set_layer_name_invert(UndoStream* stream, UndoChunkSetLayerName* chunk)
|
|
{
|
|
Layer* layer = (Layer*)GfxObj::find(chunk->layer_id);
|
|
|
|
if (layer) {
|
|
chunk_set_layer_name_new(stream, layer);
|
|
|
|
std::string layer_name;
|
|
layer_name.reserve(chunk->name_length);
|
|
|
|
for (int c=0; c<chunk->name_length; c++)
|
|
layer_name.push_back(chunk->name_text[c]);
|
|
|
|
layer->setName(layer_name.c_str());
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"add_layer"
|
|
|
|
DWORD parent layer set ID
|
|
DWORD layer ID
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkAddLayer
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 folder_id;
|
|
ase_uint32 layer_id;
|
|
};
|
|
|
|
void Undo::undo_add_layer(Layer* folder, Layer* layer)
|
|
{
|
|
chunk_add_layer_new(m_undoStream, folder, layer);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_add_layer_new(UndoStream* stream, Layer* folder, Layer* layer)
|
|
{
|
|
UndoChunkAddLayer* chunk = (UndoChunkAddLayer* )
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_ADD_LAYER,
|
|
sizeof(UndoChunkAddLayer));
|
|
|
|
chunk->folder_id = folder->getId();
|
|
chunk->layer_id = layer->getId();
|
|
}
|
|
|
|
static void chunk_add_layer_invert(UndoStream* stream, UndoChunkAddLayer* chunk)
|
|
{
|
|
LayerFolder* folder = (LayerFolder*)GfxObj::find(chunk->folder_id);
|
|
Layer* layer = (Layer*)GfxObj::find(chunk->layer_id);
|
|
|
|
if (folder && layer) {
|
|
chunk_remove_layer_new(stream, layer);
|
|
|
|
folder->remove_layer(layer);
|
|
delete layer;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"remove_layer"
|
|
|
|
DWORD parent layer folder ID
|
|
DWORD after layer ID
|
|
LAYER_DATA see read/write_raw_layer
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkRemoveLayer
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 folder_id;
|
|
ase_uint32 after_id;
|
|
ase_uint8 data[0];
|
|
};
|
|
|
|
void Undo::undo_remove_layer(Layer* layer)
|
|
{
|
|
chunk_remove_layer_new(m_undoStream, layer);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_remove_layer_new(UndoStream* stream, Layer* layer)
|
|
{
|
|
UndoChunkRemoveLayer* chunk = (UndoChunkRemoveLayer*)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_REMOVE_LAYER,
|
|
sizeof(UndoChunkRemoveLayer)+get_raw_layer_size(layer));
|
|
LayerFolder* folder = layer->get_parent();
|
|
Layer* after = layer->get_prev();
|
|
|
|
chunk->folder_id = folder->getId();
|
|
chunk->after_id = (after != NULL ? after->getId(): 0);
|
|
|
|
write_raw_layer(chunk->data, layer);
|
|
}
|
|
|
|
static void chunk_remove_layer_invert(UndoStream* stream, UndoChunkRemoveLayer* chunk)
|
|
{
|
|
LayerFolder* folder = (LayerFolder*)GfxObj::find(chunk->folder_id);
|
|
Layer* after = (Layer*)GfxObj::find(chunk->after_id);
|
|
|
|
if (folder) {
|
|
Layer* layer = read_raw_layer(chunk->data);
|
|
|
|
/* ASSERT(layer != NULL); */
|
|
|
|
chunk_add_layer_new(stream, folder, layer);
|
|
folder->add_layer(layer);
|
|
folder->move_layer(layer, after);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"move_layer"
|
|
|
|
DWORD parent layer folder ID
|
|
DWORD layer ID
|
|
DWORD after layer ID
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkMoveLayer
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 folder_id;
|
|
ase_uint32 layer_id;
|
|
ase_uint32 after_id;
|
|
};
|
|
|
|
void Undo::undo_move_layer(Layer* layer)
|
|
{
|
|
chunk_move_layer_new(m_undoStream, layer);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_move_layer_new(UndoStream* stream, Layer* layer)
|
|
{
|
|
UndoChunkMoveLayer* chunk = (UndoChunkMoveLayer* )
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_MOVE_LAYER,
|
|
sizeof(UndoChunkMoveLayer));
|
|
LayerFolder* folder = layer->get_parent();
|
|
Layer* after = layer->get_prev();
|
|
|
|
chunk->folder_id = folder->getId();
|
|
chunk->layer_id = layer->getId();
|
|
chunk->after_id = (after ? after->getId(): 0);
|
|
}
|
|
|
|
static void chunk_move_layer_invert(UndoStream* stream, UndoChunkMoveLayer* chunk)
|
|
{
|
|
LayerFolder* folder = (LayerFolder*)GfxObj::find(chunk->folder_id);
|
|
Layer* layer = (Layer*)GfxObj::find(chunk->layer_id);
|
|
Layer* after = (Layer*)GfxObj::find(chunk->after_id);
|
|
|
|
if (folder == NULL || layer == NULL)
|
|
throw UndoException("chunk_move_layer_invert");
|
|
|
|
chunk_move_layer_new(stream, layer);
|
|
folder->move_layer(layer, after);
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"set_layer"
|
|
|
|
DWORD sprite ID
|
|
DWORD layer ID
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkSetLayer
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 sprite_id;
|
|
ase_uint32 layer_id;
|
|
};
|
|
|
|
void Undo::undo_set_layer(Sprite *sprite)
|
|
{
|
|
chunk_set_layer_new(m_undoStream, sprite);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_set_layer_new(UndoStream* stream, Sprite *sprite)
|
|
{
|
|
UndoChunkSetLayer* chunk = (UndoChunkSetLayer*)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_SET_LAYER,
|
|
sizeof(UndoChunkSetLayer));
|
|
|
|
chunk->sprite_id = sprite->getId();
|
|
chunk->layer_id = sprite->getCurrentLayer() ? sprite->getCurrentLayer()->getId(): 0;
|
|
}
|
|
|
|
static void chunk_set_layer_invert(UndoStream* stream, UndoChunkSetLayer* chunk)
|
|
{
|
|
Sprite *sprite = (Sprite *)GfxObj::find(chunk->sprite_id);
|
|
Layer* layer = (Layer* )GfxObj::find(chunk->layer_id);
|
|
|
|
if (sprite == NULL)
|
|
throw UndoException("chunk_set_layer_invert");
|
|
|
|
chunk_set_layer_new(stream, sprite);
|
|
|
|
sprite->setCurrentLayer(layer);
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"add_palette"
|
|
|
|
DWORD sprite ID
|
|
DWORD palette ID
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkAddPalette
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 sprite_id;
|
|
ase_uint32 palette_id;
|
|
};
|
|
|
|
void Undo::undo_add_palette(Sprite *sprite, Palette* palette)
|
|
{
|
|
chunk_add_palette_new(m_undoStream, sprite, palette);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_add_palette_new(UndoStream* stream, Sprite *sprite, Palette* palette)
|
|
{
|
|
UndoChunkAddPalette* chunk = (UndoChunkAddPalette*)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_ADD_PALETTE,
|
|
sizeof(UndoChunkAddPalette));
|
|
|
|
chunk->sprite_id = sprite->getId();
|
|
chunk->palette_id = palette->getId();
|
|
}
|
|
|
|
static void chunk_add_palette_invert(UndoStream* stream, UndoChunkAddPalette *chunk)
|
|
{
|
|
Sprite* sprite = (Sprite*)GfxObj::find(chunk->sprite_id);
|
|
Palette* palette = (Palette*)GfxObj::find(chunk->palette_id);
|
|
|
|
if (sprite == NULL || palette == NULL)
|
|
throw UndoException("chunk_add_palette_invert");
|
|
|
|
chunk_remove_palette_new(stream, sprite, palette);
|
|
sprite->deletePalette(palette);
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"remove_palette"
|
|
|
|
DWORD sprite ID
|
|
PALETTE_DATA see read/write_raw_palette
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkRemovePalette
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 sprite_id;
|
|
ase_uint8 data[0];
|
|
};
|
|
|
|
void Undo::undo_remove_palette(Sprite *sprite, Palette* palette)
|
|
{
|
|
chunk_remove_palette_new(m_undoStream, sprite, palette);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_remove_palette_new(UndoStream* stream, Sprite *sprite, Palette* palette)
|
|
{
|
|
UndoChunkRemovePalette* chunk = (UndoChunkRemovePalette*)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_REMOVE_PALETTE,
|
|
sizeof(UndoChunkRemovePalette)+get_raw_palette_size(palette));
|
|
|
|
chunk->sprite_id = sprite->getId();
|
|
write_raw_palette(chunk->data, palette);
|
|
}
|
|
|
|
static void chunk_remove_palette_invert(UndoStream* stream, UndoChunkRemovePalette *chunk)
|
|
{
|
|
Sprite *sprite = (Sprite *)GfxObj::find(chunk->sprite_id);
|
|
if (sprite == NULL)
|
|
throw UndoException("chunk_remove_palette_invert");
|
|
|
|
Palette* palette = read_raw_palette(chunk->data);
|
|
|
|
chunk_add_palette_new(stream, sprite, palette);
|
|
sprite->setPalette(palette, true);
|
|
|
|
delete palette;
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"remap_palette"
|
|
|
|
DWORD sprite ID
|
|
DWORD first frame in range
|
|
DWORD last frame in range
|
|
BYTE[256] mapping table
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkRemapPalette
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 sprite_id;
|
|
ase_uint32 frame_from;
|
|
ase_uint32 frame_to;
|
|
ase_uint8 mapping[256];
|
|
};
|
|
|
|
void Undo::undo_remap_palette(Sprite* sprite, int frame_from, int frame_to, const std::vector<int>& mapping)
|
|
{
|
|
chunk_remap_palette_new(m_undoStream, sprite, frame_from, frame_to, mapping);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_remap_palette_new(UndoStream* stream, Sprite *sprite, int frame_from, int frame_to, const std::vector<int>& mapping)
|
|
{
|
|
UndoChunkRemapPalette* chunk = (UndoChunkRemapPalette*)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_REMAP_PALETTE,
|
|
sizeof(UndoChunkRemapPalette));
|
|
|
|
chunk->sprite_id = sprite->getId();
|
|
chunk->frame_from = frame_from;
|
|
chunk->frame_to = frame_to;
|
|
|
|
ASSERT(mapping.size() == 256 && "Mapping tables must have 256 entries");
|
|
|
|
for (size_t c=0; c<256; c++)
|
|
chunk->mapping[c] = mapping[c];
|
|
}
|
|
|
|
static void chunk_remap_palette_invert(UndoStream* stream, UndoChunkRemapPalette* chunk)
|
|
{
|
|
Sprite *sprite = (Sprite *)GfxObj::find(chunk->sprite_id);
|
|
if (sprite == NULL)
|
|
throw UndoException("chunk_remap_palette_invert");
|
|
|
|
// Inverse mapping
|
|
std::vector<int> inverse_mapping(256);
|
|
for (size_t c=0; c<256; ++c)
|
|
inverse_mapping[chunk->mapping[c]] = c;
|
|
|
|
chunk_remap_palette_new(stream, sprite, chunk->frame_from, chunk->frame_to, inverse_mapping);
|
|
|
|
// Remap in inverse order
|
|
sprite->remapImages(chunk->frame_from, chunk->frame_to, inverse_mapping);
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"set_mask"
|
|
|
|
DWORD sprite ID
|
|
MASK_DATA see read/write_raw_mask
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkSetMask
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 sprite_id;
|
|
ase_uint8 data[0];
|
|
};
|
|
|
|
void Undo::undo_set_mask(Sprite *sprite)
|
|
{
|
|
chunk_set_mask_new(m_undoStream, sprite);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_set_mask_new(UndoStream* stream, Sprite *sprite)
|
|
{
|
|
UndoChunkSetMask* chunk = (UndoChunkSetMask* )
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_SET_MASK,
|
|
sizeof(UndoChunkSetMask)+get_raw_mask_size(sprite->getMask()));
|
|
|
|
chunk->sprite_id = sprite->getId();
|
|
write_raw_mask(chunk->data, sprite->getMask());
|
|
}
|
|
|
|
static void chunk_set_mask_invert(UndoStream* stream, UndoChunkSetMask* chunk)
|
|
{
|
|
Sprite *sprite = (Sprite *)GfxObj::find(chunk->sprite_id);
|
|
|
|
if (sprite) {
|
|
Mask* mask = read_raw_mask(chunk->data);
|
|
|
|
chunk_set_mask_new(stream, sprite);
|
|
mask_copy(sprite->getMask(), mask);
|
|
|
|
mask_free(mask);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"set_imgtype"
|
|
|
|
DWORD sprite ID
|
|
DWORD imgtype
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkSetImgType
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 sprite_id;
|
|
ase_uint32 imgtype;
|
|
};
|
|
|
|
void Undo::undo_set_imgtype(Sprite* sprite)
|
|
{
|
|
chunk_set_imgtype_new(m_undoStream, sprite);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_set_imgtype_new(UndoStream* stream, Sprite* sprite)
|
|
{
|
|
UndoChunkSetImgType* chunk = (UndoChunkSetImgType*)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_SET_IMGTYPE,
|
|
sizeof(UndoChunkSetImgType));
|
|
|
|
chunk->sprite_id = sprite->getId();
|
|
chunk->imgtype = sprite->getImgType();
|
|
}
|
|
|
|
static void chunk_set_imgtype_invert(UndoStream* stream, UndoChunkSetImgType *chunk)
|
|
{
|
|
Sprite *sprite = (Sprite *)GfxObj::find(chunk->sprite_id);
|
|
|
|
if (sprite) {
|
|
chunk_set_imgtype_new(stream, sprite);
|
|
sprite->setImgType(chunk->imgtype);
|
|
|
|
// Regenerate extras
|
|
sprite->destroyExtraCel();
|
|
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"set_size"
|
|
|
|
DWORD sprite ID
|
|
DWORD width
|
|
DWORD height
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkSetSize
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 sprite_id;
|
|
ase_uint32 width;
|
|
ase_uint32 height;
|
|
};
|
|
|
|
void Undo::undo_set_size(Sprite* sprite)
|
|
{
|
|
chunk_set_size_new(m_undoStream, sprite);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_set_size_new(UndoStream* stream, Sprite* sprite)
|
|
{
|
|
UndoChunkSetSize* chunk = (UndoChunkSetSize*)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_SET_SIZE,
|
|
sizeof(UndoChunkSetSize));
|
|
|
|
chunk->sprite_id = sprite->getId();
|
|
chunk->width = sprite->getWidth();
|
|
chunk->height = sprite->getHeight();
|
|
}
|
|
|
|
static void chunk_set_size_invert(UndoStream* stream, UndoChunkSetSize *chunk)
|
|
{
|
|
Sprite *sprite = (Sprite *)GfxObj::find(chunk->sprite_id);
|
|
|
|
if (sprite) {
|
|
chunk_set_size_new(stream, sprite);
|
|
sprite->setSize(chunk->width, chunk->height);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"set_frame"
|
|
|
|
DWORD sprite ID
|
|
DWORD frame
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkSetFrame
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 sprite_id;
|
|
ase_uint32 frame;
|
|
};
|
|
|
|
void Undo::undo_set_frame(Sprite* sprite)
|
|
{
|
|
chunk_set_frame_new(m_undoStream, sprite);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_set_frame_new(UndoStream* stream, Sprite* sprite)
|
|
{
|
|
UndoChunkSetFrame* chunk = (UndoChunkSetFrame*)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_SET_FRAME,
|
|
sizeof(UndoChunkSetFrame));
|
|
|
|
chunk->sprite_id = sprite->getId();
|
|
chunk->frame = sprite->getCurrentFrame();
|
|
}
|
|
|
|
static void chunk_set_frame_invert(UndoStream* stream, UndoChunkSetFrame *chunk)
|
|
{
|
|
Sprite *sprite = (Sprite *)GfxObj::find(chunk->sprite_id);
|
|
|
|
if (sprite) {
|
|
chunk_set_frame_new(stream, sprite);
|
|
sprite->setCurrentFrame(chunk->frame);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"set_frames"
|
|
|
|
DWORD sprite ID
|
|
DWORD frames
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkSetFrames
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 sprite_id;
|
|
ase_uint32 frames;
|
|
};
|
|
|
|
void Undo::undo_set_frames(Sprite *sprite)
|
|
{
|
|
chunk_set_frames_new(m_undoStream, sprite);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_set_frames_new(UndoStream* stream, Sprite *sprite)
|
|
{
|
|
UndoChunkSetFrames *chunk = (UndoChunkSetFrames *)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_SET_FRAMES,
|
|
sizeof(UndoChunkSetFrames));
|
|
|
|
chunk->sprite_id = sprite->getId();
|
|
chunk->frames = sprite->getTotalFrames();
|
|
}
|
|
|
|
static void chunk_set_frames_invert(UndoStream* stream, UndoChunkSetFrames *chunk)
|
|
{
|
|
Sprite *sprite = (Sprite *)GfxObj::find(chunk->sprite_id);
|
|
|
|
if (sprite) {
|
|
chunk_set_frames_new(stream, sprite);
|
|
sprite->setTotalFrames(chunk->frames);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
"set_frlen"
|
|
|
|
DWORD sprite ID
|
|
DWORD frame
|
|
DWORD duration
|
|
|
|
***********************************************************************/
|
|
|
|
struct UndoChunkSetFrlen
|
|
{
|
|
UndoChunk head;
|
|
ase_uint32 sprite_id;
|
|
ase_uint32 frame;
|
|
ase_uint32 duration;
|
|
};
|
|
|
|
void Undo::undo_set_frlen(Sprite *sprite, int frame)
|
|
{
|
|
chunk_set_frlen_new(m_undoStream, sprite, frame);
|
|
updateUndo();
|
|
}
|
|
|
|
static void chunk_set_frlen_new(UndoStream* stream, Sprite *sprite, int frame)
|
|
{
|
|
ASSERT(frame >= 0 && frame < sprite->getTotalFrames());
|
|
|
|
UndoChunkSetFrlen *chunk = (UndoChunkSetFrlen *)
|
|
undo_chunk_new(stream,
|
|
UNDO_TYPE_SET_FRLEN,
|
|
sizeof(UndoChunkSetFrlen));
|
|
|
|
chunk->sprite_id = sprite->getId();
|
|
chunk->frame = frame;
|
|
chunk->duration = sprite->getFrameDuration(frame);
|
|
}
|
|
|
|
static void chunk_set_frlen_invert(UndoStream* stream, UndoChunkSetFrlen *chunk)
|
|
{
|
|
Sprite *sprite = (Sprite*)GfxObj::find(chunk->sprite_id);
|
|
|
|
if (sprite != NULL) {
|
|
chunk_set_frlen_new(stream, sprite, chunk->frame);
|
|
sprite->setFrameDuration(chunk->frame, chunk->duration);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Helper routines for UndoChunk
|
|
|
|
***********************************************************************/
|
|
|
|
static UndoChunk* undo_chunk_new(UndoStream* stream, int type, int size)
|
|
{
|
|
UndoChunk* chunk;
|
|
|
|
ASSERT(size >= (int)sizeof(UndoChunk));
|
|
|
|
chunk = (UndoChunk* )jmalloc0(size);
|
|
if (!chunk)
|
|
return NULL;
|
|
|
|
chunk->type = type;
|
|
chunk->size = size;
|
|
chunk->label = stream->getUndo()->getLabel() ?
|
|
stream->getUndo()->getLabel():
|
|
undo_actions[chunk->type].name;
|
|
|
|
stream->pushChunk(chunk);
|
|
return chunk;
|
|
}
|
|
|
|
static void undo_chunk_free(UndoChunk* chunk)
|
|
{
|
|
jfree(chunk);
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Raw data
|
|
|
|
***********************************************************************/
|
|
|
|
#define read_raw_uint32(dst) \
|
|
{ \
|
|
memcpy(&dword, raw_data, 4); \
|
|
dst = dword; \
|
|
raw_data += 4; \
|
|
}
|
|
|
|
#define read_raw_uint16(dst) \
|
|
{ \
|
|
memcpy(&word, raw_data, 2); \
|
|
dst = word; \
|
|
raw_data += 2; \
|
|
}
|
|
|
|
#define read_raw_int16(dst) \
|
|
{ \
|
|
memcpy(&word, raw_data, 2); \
|
|
dst = (int16_t)word; \
|
|
raw_data += 2; \
|
|
}
|
|
|
|
#define read_raw_uint8(dst) \
|
|
{ \
|
|
dst = *raw_data; \
|
|
++raw_data; \
|
|
}
|
|
|
|
#define read_raw_data(dst, size) \
|
|
{ \
|
|
memcpy(dst, raw_data, size); \
|
|
raw_data += size; \
|
|
}
|
|
|
|
#define write_raw_uint32(src) \
|
|
{ \
|
|
dword = src; \
|
|
memcpy(raw_data, &dword, 4); \
|
|
raw_data += 4; \
|
|
}
|
|
|
|
#define write_raw_uint16(src) \
|
|
{ \
|
|
word = src; \
|
|
memcpy(raw_data, &word, 2); \
|
|
raw_data += 2; \
|
|
}
|
|
|
|
#define write_raw_int16(src) \
|
|
{ \
|
|
word = (int16_t)src; \
|
|
memcpy(raw_data, &word, 2); \
|
|
raw_data += 2; \
|
|
}
|
|
|
|
#define write_raw_uint8(src) \
|
|
{ \
|
|
*raw_data = src; \
|
|
++raw_data; \
|
|
}
|
|
|
|
#define write_raw_data(src, size) \
|
|
{ \
|
|
memcpy(raw_data, src, size); \
|
|
raw_data += size; \
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Raw dirty data
|
|
|
|
DWORD image ID
|
|
BYTE image type
|
|
WORD[4] x1, y1, x2, y2
|
|
WORD rows
|
|
for each row
|
|
WORD[2] y, columns
|
|
for each column
|
|
WORD[2] x, w
|
|
for each pixel ("w" times)
|
|
BYTE[4] for RGB images, or
|
|
BYTE[2] for Grayscale images, or
|
|
BYTE for Indexed images
|
|
|
|
***********************************************************************/
|
|
|
|
static Dirty *read_raw_dirty(ase_uint8* raw_data)
|
|
{
|
|
ase_uint32 dword;
|
|
ase_uint16 word;
|
|
int x1, y1, x2, y2, size;
|
|
unsigned int image_id;
|
|
int u, v, x, y;
|
|
int imgtype;
|
|
Image* image;
|
|
Dirty *dirty = NULL;
|
|
|
|
read_raw_uint32(image_id);
|
|
read_raw_uint8(imgtype);
|
|
|
|
image = (Image*)GfxObj::find(image_id);
|
|
|
|
if ((image) &&
|
|
(image->getType() == GFXOBJ_IMAGE) &&
|
|
(image->imgtype == imgtype)) {
|
|
|
|
read_raw_uint16(x1);
|
|
read_raw_uint16(y1);
|
|
read_raw_uint16(x2);
|
|
read_raw_uint16(y2);
|
|
|
|
dirty = dirty_new(image, x1, y1, x2, y2, false);
|
|
read_raw_uint16(dirty->rows);
|
|
|
|
if (dirty->rows > 0) {
|
|
dirty->row = (DirtyRow*)jmalloc(sizeof(DirtyRow) * dirty->rows);
|
|
|
|
for (v=0; v<dirty->rows; v++) {
|
|
read_raw_uint16(dirty->row[v].y);
|
|
read_raw_uint16(dirty->row[v].cols);
|
|
|
|
y = dirty->row[v].y;
|
|
|
|
dirty->row[v].col = (DirtyCol*)jmalloc(sizeof(DirtyCol) * dirty->row[v].cols);
|
|
for (u=0; u<dirty->row[v].cols; u++) {
|
|
read_raw_uint16(dirty->row[v].col[u].x);
|
|
read_raw_uint16(dirty->row[v].col[u].w);
|
|
|
|
x = dirty->row[v].col[u].x;
|
|
|
|
size = image_line_size(dirty->image, dirty->row[v].col[u].w);
|
|
|
|
dirty->row[v].col[u].flags = DIRTY_VALID_COLUMN;
|
|
dirty->row[v].col[u].data = jmalloc(size);
|
|
dirty->row[v].col[u].ptr = image_address(dirty->image, x, y);
|
|
|
|
read_raw_data(dirty->row[v].col[u].data, size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return dirty;
|
|
}
|
|
|
|
static ase_uint8* write_raw_dirty(ase_uint8* raw_data, Dirty *dirty)
|
|
{
|
|
ase_uint32 dword;
|
|
ase_uint16 word;
|
|
int u, v, size;
|
|
|
|
write_raw_uint32(dirty->image->getId());
|
|
write_raw_uint8(dirty->image->imgtype);
|
|
write_raw_uint16(dirty->x1);
|
|
write_raw_uint16(dirty->y1);
|
|
write_raw_uint16(dirty->x2);
|
|
write_raw_uint16(dirty->y2);
|
|
write_raw_uint16(dirty->rows);
|
|
|
|
for (v=0; v<dirty->rows; v++) {
|
|
write_raw_uint16(dirty->row[v].y);
|
|
write_raw_uint16(dirty->row[v].cols);
|
|
|
|
for (u=0; u<dirty->row[v].cols; u++) {
|
|
write_raw_uint16(dirty->row[v].col[u].x);
|
|
write_raw_uint16(dirty->row[v].col[u].w);
|
|
|
|
size = image_line_size(dirty->image, dirty->row[v].col[u].w);
|
|
write_raw_data(dirty->row[v].col[u].data, size);
|
|
}
|
|
}
|
|
|
|
return raw_data;
|
|
}
|
|
|
|
static int get_raw_dirty_size(Dirty *dirty)
|
|
{
|
|
int u, v, size = 4+1+2*4+2; /* DWORD+BYTE+WORD[4]+WORD */
|
|
|
|
for (v=0; v<dirty->rows; v++) {
|
|
size += 4; /* y, cols (WORD[2]) */
|
|
for (u=0; u<dirty->row[v].cols; u++) {
|
|
size += 4; /* x, w (WORD[2]) */
|
|
size += image_line_size(dirty->image, dirty->row[v].col[u].w);
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Raw image data
|
|
|
|
DWORD image ID
|
|
BYTE image type
|
|
WORD[2] w, h
|
|
for each line ("h" times)
|
|
for each pixel ("w" times)
|
|
BYTE[4] for RGB images, or
|
|
BYTE[2] for Grayscale images, or
|
|
BYTE for Indexed images
|
|
|
|
***********************************************************************/
|
|
|
|
static Image* read_raw_image(ase_uint8* raw_data)
|
|
{
|
|
ase_uint32 dword;
|
|
ase_uint16 word;
|
|
GfxObjId image_id;
|
|
int imgtype;
|
|
int width;
|
|
int height;
|
|
Image* image;
|
|
int c, size;
|
|
|
|
read_raw_uint32(image_id); /* ID */
|
|
if (!image_id)
|
|
return NULL;
|
|
|
|
read_raw_uint8(imgtype); /* imgtype */
|
|
read_raw_uint16(width); /* width */
|
|
read_raw_uint16(height); /* height */
|
|
|
|
image = image_new(imgtype, width, height);
|
|
size = image_line_size(image, image->w);
|
|
|
|
for (c=0; c<image->h; c++)
|
|
read_raw_data(image->line[c], size);
|
|
|
|
image->_setGfxObjId(image_id);
|
|
return image;
|
|
}
|
|
|
|
static ase_uint8* write_raw_image(ase_uint8* raw_data, Image* image)
|
|
{
|
|
ase_uint32 dword;
|
|
ase_uint16 word;
|
|
int c, size;
|
|
|
|
write_raw_uint32(image->getId()); /* ID */
|
|
write_raw_uint8(image->imgtype); /* imgtype */
|
|
write_raw_uint16(image->w); /* width */
|
|
write_raw_uint16(image->h); /* height */
|
|
|
|
size = image_line_size(image, image->w);
|
|
for (c=0; c<image->h; c++)
|
|
write_raw_data(image->line[c], size);
|
|
|
|
return raw_data;
|
|
}
|
|
|
|
static int get_raw_image_size(Image* image)
|
|
{
|
|
ASSERT(image != NULL);
|
|
return 4+1+2+2+image_line_size(image, image->w) * image->h;
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Raw cel data
|
|
|
|
DWORD cel ID
|
|
WORD frame
|
|
WORD image index
|
|
WORD[2] x, y
|
|
WORD opacity
|
|
|
|
***********************************************************************/
|
|
|
|
static Cel* read_raw_cel(ase_uint8* raw_data)
|
|
{
|
|
ase_uint32 dword;
|
|
ase_uint16 word;
|
|
int frame, image, x, y, opacity;
|
|
GfxObjId cel_id;
|
|
Cel* cel;
|
|
|
|
read_raw_uint32(cel_id);
|
|
read_raw_uint16(frame);
|
|
read_raw_uint16(image);
|
|
read_raw_int16(x);
|
|
read_raw_int16(y);
|
|
read_raw_uint16(opacity);
|
|
|
|
cel = cel_new(frame, image);
|
|
cel_set_position(cel, x, y);
|
|
cel_set_opacity(cel, opacity);
|
|
|
|
cel->_setGfxObjId(cel_id);
|
|
return cel;
|
|
}
|
|
|
|
static ase_uint8* write_raw_cel(ase_uint8* raw_data, Cel* cel)
|
|
{
|
|
ase_uint32 dword;
|
|
ase_uint16 word;
|
|
|
|
write_raw_uint32(cel->getId());
|
|
write_raw_uint16(cel->frame);
|
|
write_raw_uint16(cel->image);
|
|
write_raw_int16(cel->x);
|
|
write_raw_int16(cel->y);
|
|
write_raw_uint16(cel->opacity);
|
|
|
|
return raw_data;
|
|
}
|
|
|
|
static int get_raw_cel_size(Cel* cel)
|
|
{
|
|
return 4+2*5;
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Raw layer data
|
|
|
|
***********************************************************************/
|
|
|
|
static Layer* read_raw_layer(ase_uint8* raw_data)
|
|
{
|
|
ase_uint32 dword;
|
|
ase_uint16 word;
|
|
GfxObjId layer_id, sprite_id;
|
|
std::vector<char> name(1);
|
|
int name_length, flags, layer_type;
|
|
Layer* layer = NULL;
|
|
Sprite *sprite;
|
|
|
|
read_raw_uint32(layer_id); /* ID */
|
|
|
|
read_raw_uint16(name_length); /* name length */
|
|
name.resize(name_length+1);
|
|
if (name_length > 0) {
|
|
read_raw_data(&name[0], name_length); /* name */
|
|
name[name_length] = 0;
|
|
}
|
|
else
|
|
name[0] = 0;
|
|
|
|
read_raw_uint8(flags); /* flags */
|
|
read_raw_uint16(layer_type); /* type */
|
|
read_raw_uint32(sprite_id); /* sprite */
|
|
|
|
sprite = (Sprite*)GfxObj::find(sprite_id);
|
|
|
|
switch (layer_type) {
|
|
|
|
case GFXOBJ_LAYER_IMAGE: {
|
|
int c, blend_mode, cels;
|
|
|
|
read_raw_uint8(blend_mode); /* blend mode */
|
|
read_raw_uint16(cels); /* cels */
|
|
|
|
/* create layer */
|
|
layer = new LayerImage(sprite);
|
|
|
|
/* set blend mode */
|
|
static_cast<LayerImage*>(layer)->set_blend_mode(blend_mode);
|
|
|
|
/* read cels */
|
|
for (c=0; c<cels; c++) {
|
|
Cel* cel;
|
|
ase_uint8 has_image;
|
|
|
|
/* read the cel */
|
|
cel = read_raw_cel(raw_data);
|
|
raw_data += get_raw_cel_size(cel);
|
|
|
|
/* add the cel in the layer */
|
|
static_cast<LayerImage*>(layer)->addCel(cel);
|
|
|
|
/* read the image */
|
|
read_raw_uint8(has_image);
|
|
if (has_image != 0) {
|
|
Image* image = read_raw_image(raw_data);
|
|
raw_data += get_raw_image_size(image);
|
|
|
|
layer->getSprite()->getStock()->replaceImage(cel->image, image);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GFXOBJ_LAYER_FOLDER: {
|
|
int c, layers;
|
|
|
|
/* create the layer set */
|
|
layer = new LayerFolder(sprite);
|
|
|
|
/* read how many sub-layers */
|
|
read_raw_uint16(layers);
|
|
|
|
for (c=0; c<layers; c++) {
|
|
Layer* child = read_raw_layer(raw_data);
|
|
if (child) {
|
|
static_cast<LayerFolder*>(layer)->add_layer(child);
|
|
raw_data += get_raw_layer_size(child);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (layer != NULL) {
|
|
layer->setName(&name[0]);
|
|
*layer->flags_addr() = flags;
|
|
|
|
layer->_setGfxObjId(layer_id);
|
|
}
|
|
|
|
return layer;
|
|
}
|
|
|
|
static ase_uint8* write_raw_layer(ase_uint8* raw_data, Layer* layer)
|
|
{
|
|
ase_uint32 dword;
|
|
ase_uint16 word;
|
|
std::string name = layer->getName();
|
|
|
|
write_raw_uint32(layer->getId()); /* ID */
|
|
|
|
write_raw_uint16(name.size()); /* name length */
|
|
if (!name.empty())
|
|
write_raw_data(name.c_str(), name.size()); /* name */
|
|
|
|
write_raw_uint8(*layer->flags_addr()); /* flags */
|
|
write_raw_uint16(layer->getType()); /* type */
|
|
write_raw_uint32(layer->getSprite()->getId()); /* sprite */
|
|
|
|
switch (layer->getType()) {
|
|
|
|
case GFXOBJ_LAYER_IMAGE: {
|
|
// blend mode
|
|
write_raw_uint8(static_cast<LayerImage*>(layer)->get_blend_mode());
|
|
|
|
// cels
|
|
write_raw_uint16(static_cast<LayerImage*>(layer)->getCelsCount());
|
|
|
|
CelIterator it = static_cast<LayerImage*>(layer)->getCelBegin();
|
|
CelIterator end = static_cast<LayerImage*>(layer)->getCelEnd();
|
|
|
|
for (; it != end; ++it) {
|
|
Cel* cel = *it;
|
|
raw_data = write_raw_cel(raw_data, cel);
|
|
|
|
Image* image = layer->getSprite()->getStock()->getImage(cel->image);
|
|
ASSERT(image != NULL);
|
|
|
|
write_raw_uint8(1);
|
|
raw_data = write_raw_image(raw_data, image);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GFXOBJ_LAYER_FOLDER: {
|
|
LayerIterator it = static_cast<LayerFolder*>(layer)->get_layer_begin();
|
|
LayerIterator end = static_cast<LayerFolder*>(layer)->get_layer_end();
|
|
|
|
// how many sub-layers
|
|
write_raw_uint16(static_cast<LayerFolder*>(layer)->get_layers_count());
|
|
|
|
for (; it != end; ++it)
|
|
raw_data = write_raw_layer(raw_data, *it);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return raw_data;
|
|
}
|
|
|
|
static int get_raw_layer_size(Layer* layer)
|
|
{
|
|
int size = 4+2+layer->getName().size()+1+2+4;
|
|
|
|
switch (layer->getType()) {
|
|
|
|
case GFXOBJ_LAYER_IMAGE: {
|
|
size += 1; // blend mode
|
|
size += 2; // num of cels
|
|
|
|
CelIterator it = static_cast<LayerImage*>(layer)->getCelBegin();
|
|
CelIterator end = static_cast<LayerImage*>(layer)->getCelEnd();
|
|
|
|
for (; it != end; ++it) {
|
|
Cel* cel = *it;
|
|
size += get_raw_cel_size(cel);
|
|
size++; // has image?
|
|
|
|
Image* image = layer->getSprite()->getStock()->getImage(cel->image);
|
|
size += get_raw_image_size(image);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GFXOBJ_LAYER_FOLDER: {
|
|
size += 2; // how many sub-layers
|
|
|
|
LayerIterator it = static_cast<LayerFolder*>(layer)->get_layer_begin();
|
|
LayerIterator end = static_cast<LayerFolder*>(layer)->get_layer_end();
|
|
|
|
for (; it != end; ++it)
|
|
size += get_raw_layer_size(*it);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Raw palette data
|
|
|
|
WORD frame
|
|
WORD ncolors
|
|
for each color ("ncolors" times)
|
|
DWORD _rgba color
|
|
|
|
***********************************************************************/
|
|
|
|
static Palette* read_raw_palette(ase_uint8* raw_data)
|
|
{
|
|
ase_uint32 dword;
|
|
ase_uint16 word;
|
|
ase_uint32 color;
|
|
int frame, ncolors;
|
|
Palette* palette;
|
|
|
|
read_raw_uint16(frame); /* frame */
|
|
read_raw_uint16(ncolors); /* ncolors */
|
|
|
|
palette = new Palette(frame, ncolors);
|
|
if (!palette)
|
|
return NULL;
|
|
|
|
for (int c=0; c<ncolors; c++) {
|
|
read_raw_uint32(color);
|
|
palette->setEntry(c, color);
|
|
}
|
|
|
|
return palette;
|
|
}
|
|
|
|
static ase_uint8* write_raw_palette(ase_uint8* raw_data, Palette* palette)
|
|
{
|
|
ase_uint32 dword;
|
|
ase_uint16 word;
|
|
ase_uint32 color;
|
|
|
|
write_raw_uint16(palette->getFrame()); // frame
|
|
write_raw_uint16(palette->size()); // number of colors
|
|
|
|
for (int c=0; c<palette->size(); c++) {
|
|
color = palette->getEntry(c);
|
|
write_raw_uint32(color);
|
|
}
|
|
|
|
return raw_data;
|
|
}
|
|
|
|
static int get_raw_palette_size(Palette* palette)
|
|
{
|
|
// 2 WORD + 4 BYTES*ncolors
|
|
return 2*2 + 4*palette->size();
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
Raw mask data
|
|
|
|
WORD[4] x, y, w, h
|
|
for each line ("h" times)
|
|
for each packet ("((w+7)/8)" times)
|
|
BYTE 8 pixels of the mask
|
|
|
|
***********************************************************************/
|
|
|
|
static Mask* read_raw_mask(ase_uint8* raw_data)
|
|
{
|
|
ase_uint16 word;
|
|
int x, y, w, h;
|
|
int c, size;
|
|
Mask* mask;
|
|
|
|
read_raw_uint16(x); /* xpos */
|
|
read_raw_uint16(y); /* ypos */
|
|
read_raw_uint16(w); /* width */
|
|
read_raw_uint16(h); /* height */
|
|
|
|
mask = mask_new();
|
|
if (!mask)
|
|
return NULL;
|
|
|
|
if (w > 0 && h > 0) {
|
|
size = (w+7)/8;
|
|
|
|
mask->add(x, y, w, h);
|
|
for (c=0; c<mask->h; c++)
|
|
read_raw_data(mask->bitmap->line[c], size);
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
static ase_uint8* write_raw_mask(ase_uint8* raw_data, Mask* mask)
|
|
{
|
|
ase_uint16 word;
|
|
int c, size = (mask->w+7)/8;
|
|
|
|
write_raw_uint16(mask->x); /* xpos */
|
|
write_raw_uint16(mask->y); /* ypos */
|
|
write_raw_uint16(mask->bitmap ? mask->w: 0); /* width */
|
|
write_raw_uint16(mask->bitmap ? mask->h: 0); /* height */
|
|
|
|
if (mask->bitmap)
|
|
for (c=0; c<mask->h; c++)
|
|
write_raw_data(mask->bitmap->line[c], size);
|
|
|
|
return raw_data;
|
|
}
|
|
|
|
static int get_raw_mask_size(Mask* mask)
|
|
{
|
|
int size = (mask->w+7)/8;
|
|
|
|
return 2*4 + (mask->bitmap ? mask->h*size: 0);
|
|
}
|