Add support to recover tilemaps/tilesets from backup data

This commit is contained in:
David Capello 2020-02-21 17:17:39 -03:00
parent 3e0a3d6f61
commit ffcd4983c1
11 changed files with 241 additions and 91 deletions

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -61,8 +61,8 @@ void AddTileset::onUndo()
void AddTileset::onRedo()
{
SubObjectsFromSprite io(sprite());
doc::Tileset* tileset = read_tileset(m_stream, &io);
auto sprite = this->sprite();
doc::Tileset* tileset = read_tileset(m_stream, sprite);
addTileset(tileset);

View File

@ -92,6 +92,7 @@ void CopyTileRegion::rehash()
auto tileset = get<Tileset>(m_tilesetId);
ASSERT(tileset);
if (tileset) {
tileset->incrementVersion();
tileset->notifyTileContentChange(m_tileIndex);
// Notify thath the tileset changed

View File

@ -189,6 +189,7 @@ bool BackupObserver::saveDocData(Doc* doc)
diff.frameDuration ? "frameDuration": "",
diff.tags ? "tags": "",
diff.palettes ? "palettes": "",
diff.tilesets ? "tilesets": "",
diff.layers ? "layers": "",
diff.cels ? "cels": "",
diff.images ? "images": "",

View File

@ -0,0 +1,15 @@
// Aseprite
// Copyright (c) 2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CRASH_DOC_FORMAT_H_INCLUDED
#define APP_CRASH_DOC_FORMAT_H_INCLUDED
#pragma once
#define DOC_FORMAT_VERSION_0 0 // Old version
#define DOC_FORMAT_VERSION_1 1 // New version with tilesets
#define DOC_FORMAT_VERSION_LAST 1
#endif

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -12,6 +12,7 @@
#include "app/crash/read_document.h"
#include "app/console.h"
#include "app/crash/doc_format.h"
#include "app/crash/internals.h"
#include "app/doc.h"
#include "base/convert_to.h"
@ -27,6 +28,7 @@
#include "doc/frame.h"
#include "doc/image_io.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/palette.h"
#include "doc/palette_io.h"
#include "doc/slice.h"
@ -36,6 +38,9 @@
#include "doc/subobjects_io.h"
#include "doc/tag.h"
#include "doc/tag_io.h"
#include "doc/tileset.h"
#include "doc/tileset_io.h"
#include "doc/tilesets.h"
#include "fixmath/fixmath.h"
#include <fstream>
@ -54,7 +59,8 @@ class Reader : public SubObjectsIO {
public:
Reader(const std::string& dir,
base::task_token* t)
: m_sprite(nullptr)
: m_docFormatVer(DOC_FORMAT_VERSION_0)
, m_sprite(nullptr)
, m_dir(dir)
, m_docId(0)
, m_docVersions(nullptr)
@ -176,6 +182,10 @@ private:
Doc* readDocument(std::ifstream& s) {
ObjectId sprId = read32(s);
std::string filename = read_string(s);
m_docFormatVer = read16(s);
if (s.eof()) m_docFormatVer = DOC_FORMAT_VERSION_0;
TRACE("RECO: internal format version=%d\n", m_docFormatVer);
// Load DocumentInfo only
if (m_loadInfo) {
@ -197,6 +207,7 @@ private:
}
Sprite* readSprite(std::ifstream& s) {
// Header
ColorMode mode = (ColorMode)read8(s);
int w = read16(s);
int h = read16(s);
@ -240,6 +251,19 @@ private:
Console().printf("Invalid number of frames #%d\n", nframes);
}
// IDs of all tilesets
if (m_docFormatVer >= DOC_FORMAT_VERSION_1) {
int ntilesets = read32(s);
if (ntilesets > 0 && ntilesets < 0xffffff) {
for (int i=0; i<ntilesets; ++i) {
ObjectId tilesetId = read32(s);
Tileset* tileset = loadObject<Tileset*>("tset", tilesetId, &Reader::readTileset);
if (tileset)
spr->tilesets()->add(tileset);
}
}
}
// Read layers
int nlayers = read32(s);
if (nlayers >= 1 && nlayers < 0xfffff) {
@ -383,12 +407,26 @@ private:
LayerFlags flags = (LayerFlags)read32(s);
ObjectType type = (ObjectType)read16(s);
ASSERT(type == ObjectType::LayerImage ||
type == ObjectType::LayerGroup);
type == ObjectType::LayerGroup ||
type == ObjectType::LayerTilemap);
std::string name = read_string(s);
if (type == ObjectType::LayerImage) {
std::unique_ptr<LayerImage> lay(new LayerImage(m_sprite));
if (type == ObjectType::LayerImage ||
type == ObjectType::LayerTilemap) {
std::unique_ptr<LayerImage> lay;
switch (type) {
case ObjectType::LayerImage:
lay.reset(new LayerImage(m_sprite));
break;
case ObjectType::LayerTilemap: {
tileset_index tilesetIndex = read32(s);
lay.reset(new LayerTilemap(m_sprite, tilesetIndex));
break;
}
}
lay->setName(name);
lay->setFlags(flags);
@ -437,6 +475,10 @@ private:
return read_palette(s);
}
Tileset* readTileset(std::ifstream& s) {
return read_tileset(s, m_sprite, false);
}
Tag* readTag(std::ifstream& s) {
return read_tag(s, false);
}
@ -475,6 +517,7 @@ private:
return false;
}
int m_docFormatVer;
Sprite* m_sprite; // Used to pass the sprite in LayerImage() ctor
std::string m_dir;
ObjectVersion m_docId;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -11,6 +11,7 @@
#include "app/crash/write_document.h"
#include "app/crash/doc_format.h"
#include "app/crash/internals.h"
#include "app/doc.h"
#include "base/convert_to.h"
@ -26,6 +27,7 @@
#include "doc/frame.h"
#include "doc/image_io.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/palette.h"
#include "doc/palette_io.h"
#include "doc/slice.h"
@ -34,6 +36,9 @@
#include "doc/string_io.h"
#include "doc/tag.h"
#include "doc/tag_io.h"
#include "doc/tileset.h"
#include "doc/tileset_io.h"
#include "doc/tilesets.h"
#include "fixmath/fixmath.h"
#include <fstream>
@ -71,6 +76,12 @@ public:
if (!saveObject("pal", pal, &Writer::writePalette))
return false;
if (spr->hasTilesets()) {
for (Tileset* tset : *spr->tilesets())
if (!saveObject("tset", tset, &Writer::writeTileset))
return false;
}
for (Tag* frtag : spr->tags())
if (!saveObject("frtag", frtag, &Writer::writeFrameTag))
return false;
@ -134,20 +145,29 @@ private:
bool writeDocumentFile(std::ofstream& s, Doc* doc) {
write32(s, doc->sprite()->id());
write_string(s, doc->filename());
write16(s, DOC_FORMAT_VERSION_LAST);
return true;
}
bool writeSprite(std::ofstream& s, Sprite* spr) {
// Header
write8(s, int(spr->colorMode()));
write16(s, spr->width());
write16(s, spr->height());
write32(s, spr->transparentColor());
write32(s, spr->totalFrames());
// Frame durations
write32(s, spr->totalFrames());
for (frame_t fr = 0; fr < spr->totalFrames(); ++fr)
write32(s, spr->frameDuration(fr));
// IDs of all tilesets
write32(s, spr->hasTilesets() ? spr->tilesets()->size(): 0);
if (spr->hasTilesets()) {
for (Tileset* tileset : *spr->tilesets())
write32(s, tileset->id());
}
// IDs of all main layers
write32(s, spr->allLayersCount());
writeAllLayersID(s, 0, spr->root());
@ -215,7 +235,12 @@ private:
switch (lay->type()) {
case ObjectType::LayerImage: {
case ObjectType::LayerImage:
case ObjectType::LayerTilemap: {
// Tileset index
if (lay->type() == ObjectType::LayerTilemap)
write32(s, static_cast<const LayerTilemap*>(lay)->tilesetIndex());
CelConstIterator it, begin = static_cast<const LayerImage*>(lay)->getCelBegin();
CelConstIterator end = static_cast<const LayerImage*>(lay)->getCelEnd();
@ -259,6 +284,11 @@ private:
return true;
}
bool writeTileset(std::ofstream& s, Tileset* tileset) {
write_tileset(s, tileset);
return true;
}
bool writeFrameTag(std::ofstream& s, Tag* frameTag) {
write_tag(s, frameTag);
return true;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
@ -15,10 +15,13 @@
#include "doc/cel.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/palette.h"
#include "doc/primitives.h"
#include "doc/sprite.h"
#include "doc/tag.h"
#include "doc/tileset.h"
#include "doc/tilesets.h"
namespace app {
@ -70,7 +73,7 @@ DocDiff compare_docs(const Doc* a,
}
}
// Palettes layers
// Palettes
if (a->sprite()->getPalettes().size() != b->sprite()->getPalettes().size()) {
const PalettesList& aPals = a->sprite()->getPalettes();
const PalettesList& bPals = b->sprite()->getPalettes();
@ -88,6 +91,35 @@ DocDiff compare_docs(const Doc* a,
}
}
// Compare tilesets
const tile_index aTilesetSize = (a->sprite()->hasTilesets() ? a->sprite()->tilesets()->size(): 0);
const tile_index bTilesetSize = (b->sprite()->hasTilesets() ? b->sprite()->tilesets()->size(): 0);
if (aTilesetSize != bTilesetSize) {
diff.anything = diff.tilesets = true;
}
else {
for (int i=0; i<aTilesetSize; ++i) {
Tileset* aTileset = a->sprite()->tilesets()->get(i);
Tileset* bTileset = b->sprite()->tilesets()->get(i);
if (aTileset->grid().tileSize() != bTileset->grid().tileSize() ||
aTileset->size() != bTileset->size()) {
diff.anything = diff.tilesets = true;
break;
}
else {
for (tile_index ti=0; ti<aTileset->size(); ++ti) {
if (!is_same_image(aTileset->get(ti).get(),
bTileset->get(ti).get())) {
diff.anything = diff.tilesets = true;
goto done;
}
}
}
}
done:;
}
// Compare layers
if (a->sprite()->allLayersCount() != b->sprite()->allLayersCount()) {
diff.anything = diff.layers = true;
@ -106,7 +138,9 @@ DocDiff compare_docs(const Doc* a,
aLay->name() != bLay->name() ||
aLay->flags() != bLay->flags() ||
(aLay->isImage() && bLay->isImage() &&
(((const LayerImage*)aLay)->opacity() != ((const LayerImage*)bLay)->opacity()))) {
(((const LayerImage*)aLay)->opacity() != ((const LayerImage*)bLay)->opacity())) ||
(aLay->isTilemap() && bLay->isTilemap() &&
(((const LayerTilemap*)aLay)->tilesetIndex() != ((const LayerTilemap*)bLay)->tilesetIndex()))) {
diff.anything = diff.layers = true;
break;
}
@ -128,7 +162,7 @@ DocDiff compare_docs(const Doc* a,
}
if (aCel->image() && bCel->image()) {
if (aCel->image()->bounds() != bCel->image()->bounds() ||
count_diff_between_images(aCel->image(), bCel->image()))
!is_same_image(aCel->image(), bCel->image()))
diff.anything = diff.images = true;
}
else if (aCel->image() != bCel->image())

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
@ -19,6 +19,7 @@ namespace app {
bool frameDuration : 1;
bool tags : 1;
bool palettes : 1;
bool tilesets : 1;
bool layers : 1;
bool cels : 1;
bool images : 1;
@ -32,6 +33,7 @@ namespace app {
frameDuration(false),
tags(false),
palettes(false),
tilesets(false),
layers(false),
cels(false),
images(false),

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -1575,88 +1575,93 @@ void Timeline::onPaint(ui::PaintEvent& ev)
// Draw each visible layer.
DrawCelData data;
for (layer=lastLayer; layer>=firstLayer; --layer) {
{
IntersectClip clip(g, getLayerHeadersBounds());
if (clip)
drawLayer(g, layer);
}
if (layer >= lastLayer &&
layer >= firstLayer) {
for (layer=lastLayer; layer>=firstLayer; --layer) {
ASSERT(layer >= 0 && layer < int(m_rows.size()));
IntersectClip clip(g, getCelsBounds());
if (!clip)
continue;
Layer* layerPtr = m_rows[layer].layer();
if (!layerPtr->isImage()) {
// Draw empty cels
for (frame=firstFrame; frame<=lastFrame; ++frame) {
drawCel(g, layer, frame, nullptr, nullptr);
{
IntersectClip clip(g, getLayerHeadersBounds());
if (clip)
drawLayer(g, layer);
}
continue;
}
// Get the first CelIterator to be drawn (it is the first cel with cel->frame >= first_frame)
LayerImage* layerImagePtr = static_cast<LayerImage*>(layerPtr);
data.begin = layerImagePtr->getCelBegin();
data.end = layerImagePtr->getCelEnd();
data.it = layerImagePtr->findFirstCelIteratorAfter(firstFrame-1);
if (firstFrame > 0 && data.it != data.begin)
data.prevIt = data.it-1;
else
data.prevIt = data.end;
data.nextIt = (data.it != data.end ? data.it+1: data.end);
IntersectClip clip(g, getCelsBounds());
if (!clip)
continue;
// Calculate link range for the active cel
data.firstLink = data.end;
data.lastLink = data.end;
Layer* layerPtr = m_rows[layer].layer();
if (!layerPtr->isImage()) {
// Draw empty cels
for (frame=firstFrame; frame<=lastFrame; ++frame) {
drawCel(g, layer, frame, nullptr, nullptr);
}
continue;
}
if (layerPtr == m_layer) {
data.activeIt = layerImagePtr->findCelIterator(m_frame);
if (data.activeIt != data.end) {
data.firstLink = data.activeIt;
data.lastLink = data.activeIt;
// Get the first CelIterator to be drawn (it is the first cel with cel->frame >= first_frame)
LayerImage* layerImagePtr = static_cast<LayerImage*>(layerPtr);
data.begin = layerImagePtr->getCelBegin();
data.end = layerImagePtr->getCelEnd();
data.it = layerImagePtr->findFirstCelIteratorAfter(firstFrame-1);
if (firstFrame > 0 && data.it != data.begin)
data.prevIt = data.it-1;
else
data.prevIt = data.end;
data.nextIt = (data.it != data.end ? data.it+1: data.end);
ObjectId imageId = (*data.activeIt)->image()->id();
// Calculate link range for the active cel
data.firstLink = data.end;
data.lastLink = data.end;
auto it2 = data.activeIt;
if (it2 != data.begin) {
do {
--it2;
if (layerPtr == m_layer) {
data.activeIt = layerImagePtr->findCelIterator(m_frame);
if (data.activeIt != data.end) {
data.firstLink = data.activeIt;
data.lastLink = data.activeIt;
ObjectId imageId = (*data.activeIt)->image()->id();
auto it2 = data.activeIt;
if (it2 != data.begin) {
do {
--it2;
if ((*it2)->image()->id() == imageId) {
data.firstLink = it2;
if ((*data.firstLink)->frame() < firstFrame)
break;
}
} while (it2 != data.begin);
}
it2 = data.activeIt;
while (it2 != data.end) {
if ((*it2)->image()->id() == imageId) {
data.firstLink = it2;
if ((*data.firstLink)->frame() < firstFrame)
data.lastLink = it2;
if ((*data.lastLink)->frame() > lastFrame)
break;
}
} while (it2 != data.begin);
}
it2 = data.activeIt;
while (it2 != data.end) {
if ((*it2)->image()->id() == imageId) {
data.lastLink = it2;
if ((*data.lastLink)->frame() > lastFrame)
break;
++it2;
}
++it2;
}
}
}
else
data.activeIt = data.end;
else
data.activeIt = data.end;
// Draw every visible cel for each layer.
for (frame=firstFrame; frame<=lastFrame; ++frame) {
Cel* cel =
(data.it != data.end &&
(*data.it)->frame() == frame ? *data.it: nullptr);
// Draw every visible cel for each layer.
for (frame=firstFrame; frame<=lastFrame; ++frame) {
Cel* cel =
(data.it != data.end &&
(*data.it)->frame() == frame ? *data.it: nullptr);
drawCel(g, layer, frame, cel, &data);
drawCel(g, layer, frame, cel, &data);
if (cel) {
data.prevIt = data.it;
data.it = data.nextIt; // Point to next cel
if (data.nextIt != data.end)
++data.nextIt;
if (cel) {
data.prevIt = data.it;
data.it = data.nextIt; // Point to next cel
if (data.nextIt != data.end)
++data.nextIt;
}
}
}
}
@ -2035,6 +2040,10 @@ void Timeline::drawHeaderFrame(ui::Graphics* g, frame_t frame)
void Timeline::drawLayer(ui::Graphics* g, int layerIdx)
{
ASSERT(layerIdx >= 0 && layerIdx < int(m_rows.size()));
if (layerIdx < 0 || layerIdx >= m_rows.size())
return;
auto& styles = skinTheme()->styles;
Layer* layer = m_rows[layerIdx].layer();
bool is_active = isLayerActive(layerIdx);

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -11,7 +11,9 @@
#include "doc/tileset_io.h"
#include "base/serialization.h"
#include "doc/cancel_io.h"
#include "doc/grid_io.h"
#include "doc/image_io.h"
#include "doc/subobjects_io.h"
#include "doc/tileset.h"
@ -28,21 +30,34 @@ bool write_tileset(std::ostream& os,
{
write32(os, tileset->id());
write32(os, tileset->size());
return write_grid(os, tileset->grid());
write_grid(os, tileset->grid());
// TODO save images
for (tile_index ti=0; ti<tileset->size(); ++ti) {
if (cancel && cancel->isCanceled())
return false;
write_image(os, tileset->get(ti).get(), cancel);
}
return true;
}
Tileset* read_tileset(std::istream& is,
SubObjectsFromSprite* subObjects,
Sprite* sprite,
bool setId)
{
ObjectId id = read32(is);
tileset_index ntiles = read32(is);
Grid grid = read_grid(is, setId);
auto tileset = new Tileset(subObjects->sprite(), grid, ntiles);
auto tileset = new Tileset(sprite, grid, ntiles);
if (setId)
tileset->setId(id);
for (tileset_index ti=0; ti<ntiles; ++ti) {
ImageRef image(read_image(is, setId));
tileset->set(ti, image);
}
return tileset;
}

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -13,7 +13,7 @@
namespace doc {
class CancelIO;
class SubObjectsFromSprite;
class Sprite;
class Tileset;
bool write_tileset(std::ostream& os,
@ -21,7 +21,7 @@ namespace doc {
CancelIO* cancel = nullptr);
Tileset* read_tileset(std::istream& is,
SubObjectsFromSprite* subObjects,
Sprite* sprite,
bool setId = true);
} // namespace doc