Add io part for layers UUIDs

This commit is contained in:
Martín Capello 2025-01-22 10:14:26 -03:00
parent 86ad09291f
commit c3cfae666b
12 changed files with 173 additions and 76 deletions

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -44,6 +44,7 @@
#include "doc/tilesets.h" #include "doc/tilesets.h"
#include "doc/user_data_io.h" #include "doc/user_data_io.h"
#include "doc/util.h" #include "doc/util.h"
#include "doc/uuid_io.h"
#include "fixmath/fixmath.h" #include "fixmath/fixmath.h"
#include <fstream> #include <fstream>
@ -450,6 +451,10 @@ private:
type == ObjectType::LayerTilemap); type == ObjectType::LayerTilemap);
std::string name = read_string(s); std::string name = read_string(s);
base::Uuid uuid;
if (m_serial >= SerialFormat::Ver3)
uuid = read_uuid(s);
std::unique_ptr<Layer> lay; std::unique_ptr<Layer> lay;
switch (type) { switch (type) {
@ -495,13 +500,15 @@ private:
break; break;
} }
if (lay) { if (!lay)
UserData userData = read_user_data(s, m_serial);
lay->setUserData(userData);
return lay.release();
}
else
return nullptr; return nullptr;
if (m_serial >= SerialFormat::Ver3)
lay->setUuid(uuid);
UserData userData = read_user_data(s, m_serial);
lay->setUserData(userData);
return lay.release();
} }
Cel* readCel(std::ifstream& s) { return read_cel(s, this, false); } Cel* readCel(std::ifstream& s) { return read_cel(s, this, false); }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -41,6 +41,7 @@
#include "doc/tileset_io.h" #include "doc/tileset_io.h"
#include "doc/tilesets.h" #include "doc/tilesets.h"
#include "doc/user_data_io.h" #include "doc/user_data_io.h"
#include "doc/uuid_io.h"
#include "fixmath/fixmath.h" #include "fixmath/fixmath.h"
#include <fstream> #include <fstream>
@ -250,6 +251,7 @@ private:
write32(s, static_cast<int>(lay->flags())); // Flags write32(s, static_cast<int>(lay->flags())); // Flags
write16(s, static_cast<int>(lay->type())); // Type write16(s, static_cast<int>(lay->type())); // Type
write_string(s, lay->name()); write_string(s, lay->name());
write_uuid(s, lay->uuid());
switch (lay->type()) { switch (lay->type()) {
case ObjectType::LayerImage: case ObjectType::LayerImage:

View File

@ -20,6 +20,7 @@
#include "base/file_handle.h" #include "base/file_handle.h"
#include "base/fs.h" #include "base/fs.h"
#include "base/mem_utils.h" #include "base/mem_utils.h"
#include "base/uuid.h"
#include "dio/aseprite_common.h" #include "dio/aseprite_common.h"
#include "dio/aseprite_decoder.h" #include "dio/aseprite_decoder.h"
#include "dio/decode_delegate.h" #include "dio/decode_delegate.h"
@ -542,7 +543,8 @@ static void ase_file_prepare_header(FILE* f,
sprite->pixelFormat() == IMAGE_INDEXED ? 8 : sprite->pixelFormat() == IMAGE_INDEXED ? 8 :
0); 0);
header->flags = (ASE_FILE_FLAG_LAYER_WITH_OPACITY | header->flags = (ASE_FILE_FLAG_LAYER_WITH_OPACITY |
(composeGroups ? ASE_FILE_FLAG_COMPOSITE_GROUPS : 0)); (composeGroups ? ASE_FILE_FLAG_COMPOSITE_GROUPS : 0) |
(sprite->useUuidsForLayers() ? ASE_FILE_FLAG_LAYER_WITH_UUID : 0));
header->speed = sprite->frameDuration(firstFrame); header->speed = sprite->frameDuration(firstFrame);
header->next = 0; header->next = 0;
header->frit = 0; header->frit = 0;
@ -786,6 +788,13 @@ static void ase_file_write_palette_chunk(FILE* f,
} }
} }
static void ase_file_write_uuid(FILE* f, const base::Uuid& uuid)
{
for (int i = 0; i < 16; ++i) {
fputc(uuid[i], f);
}
}
static void ase_file_write_layer_chunk(FILE* f, static void ase_file_write_layer_chunk(FILE* f,
const dio::AsepriteHeader* header, const dio::AsepriteHeader* header,
dio::AsepriteFrameHeader* frame_header, dio::AsepriteFrameHeader* frame_header,
@ -835,6 +844,9 @@ static void ase_file_write_layer_chunk(FILE* f,
// Tileset index // Tileset index
if (layer->isTilemap()) if (layer->isTilemap())
fputl(static_cast<const LayerTilemap*>(layer)->tilesetIndex(), f); fputl(static_cast<const LayerTilemap*>(layer)->tilesetIndex(), f);
if ((header->flags & ASE_FILE_FLAG_LAYER_WITH_UUID) == ASE_FILE_FLAG_LAYER_WITH_UUID)
ase_file_write_uuid(f, layer->uuid());
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -1676,9 +1688,7 @@ static void ase_file_write_property_value(FILE* f, const UserData::Variant& valu
} }
case USER_DATA_PROPERTY_TYPE_UUID: { case USER_DATA_PROPERTY_TYPE_UUID: {
auto& uuid = *std::get_if<base::Uuid>(&value); auto& uuid = *std::get_if<base::Uuid>(&value);
for (int i = 0; i < 16; ++i) { ase_file_write_uuid(f, uuid);
fputc(uuid[i], f);
}
break; break;
} }
} }

View File

@ -1,5 +1,5 @@
// Aseprite Document IO Library // Aseprite Document IO Library
// Copyright (c) 2018-2023 Igara Studio S.A. // Copyright (c) 2018-2025 Igara Studio S.A.
// Copyright (c) 2001-2018 David Capello // Copyright (c) 2001-2018 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -19,6 +19,7 @@
#define ASE_FILE_FLAG_LAYER_WITH_OPACITY 1 #define ASE_FILE_FLAG_LAYER_WITH_OPACITY 1
#define ASE_FILE_FLAG_COMPOSITE_GROUPS 2 #define ASE_FILE_FLAG_COMPOSITE_GROUPS 2
#define ASE_FILE_FLAG_LAYER_WITH_UUID 4
#define ASE_FILE_CHUNK_FLI_COLOR2 4 #define ASE_FILE_CHUNK_FLI_COLOR2 4
#define ASE_FILE_CHUNK_FLI_COLOR 11 #define ASE_FILE_CHUNK_FLI_COLOR 11

View File

@ -75,6 +75,9 @@ bool AsepriteDecoder::decode()
sprite->setGridBounds( sprite->setGridBounds(
gfx::Rect(header.grid_x, header.grid_y, header.grid_width, header.grid_height)); gfx::Rect(header.grid_x, header.grid_y, header.grid_width, header.grid_height));
sprite->setUseUuidsForLayers((header.flags & ASE_FILE_FLAG_LAYER_WITH_UUID) ==
ASE_FILE_FLAG_LAYER_WITH_UUID);
// Prepare variables for layer chunks // Prepare variables for layer chunks
doc::Layer* last_layer = sprite->root(); doc::Layer* last_layer = sprite->root();
doc::WithUserData* last_object_with_user_data = sprite.get(); doc::WithUserData* last_object_with_user_data = sprite.get();
@ -577,48 +580,53 @@ doc::Layer* AsepriteDecoder::readLayerChunk(AsepriteHeader* header,
break; break;
} }
if (layer) { if (!layer)
const bool composeGroups = (header->flags & ASE_FILE_FLAG_COMPOSITE_GROUPS); return nullptr;
if ((layer->isImage() || (layer->isGroup() && composeGroups)) && // Read UUID if usage is enabled
// Only transparent layers can have blend mode and opacity if (sprite->useUuidsForLayers())
!(flags & int(doc::LayerFlags::Background))) { layer->setUuid(readUuid());
layer->setBlendMode((doc::BlendMode)blendmode);
if (header->flags & ASE_FILE_FLAG_LAYER_WITH_OPACITY)
layer->setOpacity(opacity);
}
// flags const bool composeGroups = (header->flags & ASE_FILE_FLAG_COMPOSITE_GROUPS);
layer->setFlags(
static_cast<doc::LayerFlags>(flags & static_cast<int>(doc::LayerFlags::PersistentFlagsMask)));
// name if ((layer->isImage() || (layer->isGroup() && composeGroups)) &&
layer->setName(name.c_str()); // Only transparent layers can have blend mode and opacity
!(flags & int(doc::LayerFlags::Background))) {
// Child level layer->setBlendMode((doc::BlendMode)blendmode);
if (child_level == *current_level) if (header->flags & ASE_FILE_FLAG_LAYER_WITH_OPACITY)
(*previous_layer)->parent()->addLayer(layer); layer->setOpacity(opacity);
else if (child_level > *current_level)
static_cast<doc::LayerGroup*>(*previous_layer)->addLayer(layer);
else if (child_level < *current_level) {
doc::LayerGroup* parent = (*previous_layer)->parent();
ASSERT(parent);
if (parent) {
int levels = (*current_level - child_level);
while (levels--) {
ASSERT(parent->parent());
if (!parent->parent())
break;
parent = parent->parent();
}
parent->addLayer(layer);
}
}
*previous_layer = layer;
*current_level = child_level;
} }
// flags
layer->setFlags(
static_cast<doc::LayerFlags>(flags & static_cast<int>(doc::LayerFlags::PersistentFlagsMask)));
// name
layer->setName(name.c_str());
// Child level
if (child_level == *current_level)
(*previous_layer)->parent()->addLayer(layer);
else if (child_level > *current_level)
static_cast<doc::LayerGroup*>(*previous_layer)->addLayer(layer);
else if (child_level < *current_level) {
doc::LayerGroup* parent = (*previous_layer)->parent();
ASSERT(parent);
if (parent) {
int levels = (*current_level - child_level);
while (levels--) {
ASSERT(parent->parent());
if (!parent->parent())
break;
parent = parent->parent();
}
parent->addLayer(layer);
}
}
*previous_layer = layer;
*current_level = child_level;
return layer; return layer;
} }
@ -1433,12 +1441,7 @@ const doc::UserData::Variant AsepriteDecoder::readPropertyValue(uint16_t type)
return value; return value;
} }
case USER_DATA_PROPERTY_TYPE_UUID: { case USER_DATA_PROPERTY_TYPE_UUID: {
base::Uuid value; return readUuid();
uint8_t* bytes = value.bytes();
for (int i = 0; i < 16; ++i) {
bytes[i] = read8();
}
return value;
} }
default: { default: {
throw base::Exception( throw base::Exception(
@ -1474,4 +1477,14 @@ void AsepriteDecoder::readTilesData(doc::Tileset* tileset, const AsepriteExterna
} }
} }
base::Uuid AsepriteDecoder::readUuid()
{
base::Uuid value;
uint8_t* bytes = value.bytes();
for (int i = 0; i < 16; ++i) {
bytes[i] = read8();
}
return value;
}
} // namespace dio } // namespace dio

View File

@ -9,6 +9,7 @@
#define DIO_ASEPRITE_DECODER_H_INCLUDED #define DIO_ASEPRITE_DECODER_H_INCLUDED
#pragma once #pragma once
#include "base/uuid.h"
#include "dio/aseprite_common.h" #include "dio/aseprite_common.h"
#include "dio/decoder.h" #include "dio/decoder.h"
#include "doc/frame.h" #include "doc/frame.h"
@ -77,6 +78,7 @@ private:
const AsepriteExternalFiles& extFiles); const AsepriteExternalFiles& extFiles);
const doc::UserData::Variant readPropertyValue(uint16_t type); const doc::UserData::Variant readPropertyValue(uint16_t type);
void readTilesData(doc::Tileset* tileset, const AsepriteExternalFiles& extFiles); void readTilesData(doc::Tileset* tileset, const AsepriteExternalFiles& extFiles);
base::Uuid readUuid();
doc::LayerList m_allLayers; doc::LayerList m_allLayers;
std::vector<uint32_t> m_tilesetFlags; std::vector<uint32_t> m_tilesetFlags;

View File

@ -82,7 +82,8 @@ add_library(doc-lib
tilesets.cpp tilesets.cpp
user_data.cpp user_data.cpp
user_data_io.cpp user_data_io.cpp
util.cpp) util.cpp
uuid_io.cpp)
target_link_libraries(doc-lib target_link_libraries(doc-lib
laf-gfx laf-gfx

View File

@ -1,10 +1,11 @@
// Aseprite Document Library // Aseprite Document Library
// Copyright (c) 2019 Igara Studio S.A. // Copyright (c) 2019-2025 Igara Studio S.A.
// Copyright (c) 2001-2018 David Capello // Copyright (c) 2001-2018 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information. // Read LICENSE.txt for more information.
#include "doc/uuid_io.h"
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
#endif #endif
@ -40,6 +41,7 @@ void write_layer(std::ostream& os, const Layer* layer)
{ {
write32(os, layer->id()); write32(os, layer->id());
write_string(os, layer->name()); write_string(os, layer->name());
write_uuid(os, layer->uuid());
write32(os, static_cast<int>(layer->flags())); // Flags write32(os, static_cast<int>(layer->flags())); // Flags
write16(os, static_cast<int>(layer->type())); // Type write16(os, static_cast<int>(layer->type())); // Type
@ -112,6 +114,10 @@ Layer* read_layer(std::istream& is, SubObjectsFromSprite* subObjects, const Seri
{ {
ObjectId id = read32(is); ObjectId id = read32(is);
std::string name = read_string(is); std::string name = read_string(is);
base::Uuid uuid;
if (serial >= SerialFormat::Ver3)
uuid = read_uuid(is);
uint32_t flags = read32(is); // Flags uint32_t flags = read32(is); // Flags
uint16_t layer_type = read16(is); // Type uint16_t layer_type = read16(is); // Type
std::unique_ptr<Layer> layer; std::unique_ptr<Layer> layer;
@ -187,12 +193,16 @@ Layer* read_layer(std::istream& is, SubObjectsFromSprite* subObjects, const Seri
const UserData userData = read_user_data(is, serial); const UserData userData = read_user_data(is, serial);
if (layer) { if (!layer)
layer->setName(name); return nullptr;
layer->setFlags(static_cast<LayerFlags>(flags));
layer->setId(id); if (serial >= SerialFormat::Ver3)
layer->setUserData(userData); layer->setUuid(uuid);
}
layer->setName(name);
layer->setFlags(static_cast<LayerFlags>(flags));
layer->setId(id);
layer->setUserData(userData);
return layer.release(); return layer.release();
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (c) 2020-2024 Igara Studio S.A. // Copyright (c) 2020-2025 Igara Studio S.A.
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information. // Read LICENSE.txt for more information.
@ -19,7 +19,8 @@ enum class SerialFormat : uint16_t {
Ver0 = 0, // Old version Ver0 = 0, // Old version
Ver1 = 1, // New version with tilesets Ver1 = 1, // New version with tilesets
Ver2 = 2, // Version 2 adds custom properties to user data Ver2 = 2, // Version 2 adds custom properties to user data
LastVer = Ver2 Ver3 = 3, // Version 3 adds UUIDs to layers
LastVer = Ver3
}; };
} // namespace doc } // namespace doc

View File

@ -1,5 +1,5 @@
// Aseprite Document Library // Aseprite Document Library
// Copyright (c) 2023 Igara Studio S.A. // Copyright (c) 2023-2025 Igara Studio S.A.
// Copyright (c) 2001-2015 David Capello // Copyright (c) 2001-2015 David Capello
// //
// This file is released under the terms of the MIT license. // This file is released under the terms of the MIT license.
@ -14,6 +14,7 @@
#include "base/serialization.h" #include "base/serialization.h"
#include "doc/string_io.h" #include "doc/string_io.h"
#include "doc/user_data.h" #include "doc/user_data.h"
#include "doc/uuid_io.h"
#include <iostream> #include <iostream>
@ -86,9 +87,7 @@ static void write_property_value(std::ostream& os, const UserData::Variant& vari
} }
case USER_DATA_PROPERTY_TYPE_UUID: { case USER_DATA_PROPERTY_TYPE_UUID: {
auto uuid = get_value<base::Uuid>(variant); auto uuid = get_value<base::Uuid>(variant);
for (int i = 0; i < 16; ++i) { write_uuid(os, uuid);
write8(os, uuid[i]);
}
break; break;
} }
} }
@ -207,12 +206,7 @@ static UserData::Variant read_property_value(std::istream& is, uint16_t type)
return value; return value;
} }
case USER_DATA_PROPERTY_TYPE_UUID: { case USER_DATA_PROPERTY_TYPE_UUID: {
base::Uuid value; return read_uuid(is);
uint8_t* bytes = value.bytes();
for (int i = 0; i < 16; ++i) {
bytes[i] = read8(is);
}
return value;
} }
} }

33
src/doc/uuid_io.cpp Normal file
View File

@ -0,0 +1,33 @@
// Aseprite Document Library
// Copyright (c) 2025 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#include "doc/uuid_io.h"
#include "base/serialization.h"
#include "base/uuid.h"
namespace doc {
using namespace base::serialization;
base::Uuid read_uuid(std::istream& is)
{
base::Uuid value;
uint8_t* bytes = value.bytes();
for (int i = 0; i < 16; ++i) {
bytes[i] = read8(is);
}
return value;
}
void write_uuid(std::ostream& os, const base::Uuid& uuid)
{
for (int i = 0; i < 16; ++i) {
write8(os, uuid[i]);
}
}
} // namespace doc

23
src/doc/uuid_io.h Normal file
View File

@ -0,0 +1,23 @@
// Aseprite Document Library
// Copyright (c) 2025 Igara Studio S.A.
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOC_UUID_IO_H_INCLUDED
#define DOC_UUID_IO_H_INCLUDED
#pragma once
#include "base/uuid.h"
#include <iostream>
namespace doc {
base::Uuid read_uuid(std::istream& is);
void write_uuid(std::ostream& os, const base::Uuid& uuid);
} // namespace doc
#endif