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

View File

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

View File

@ -20,6 +20,7 @@
#include "base/file_handle.h"
#include "base/fs.h"
#include "base/mem_utils.h"
#include "base/uuid.h"
#include "dio/aseprite_common.h"
#include "dio/aseprite_decoder.h"
#include "dio/decode_delegate.h"
@ -542,7 +543,8 @@ static void ase_file_prepare_header(FILE* f,
sprite->pixelFormat() == IMAGE_INDEXED ? 8 :
0);
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->next = 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,
const dio::AsepriteHeader* header,
dio::AsepriteFrameHeader* frame_header,
@ -835,6 +844,9 @@ static void ase_file_write_layer_chunk(FILE* f,
// Tileset index
if (layer->isTilemap())
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: {
auto& uuid = *std::get_if<base::Uuid>(&value);
for (int i = 0; i < 16; ++i) {
fputc(uuid[i], f);
}
ase_file_write_uuid(f, uuid);
break;
}
}

View File

@ -1,5 +1,5 @@
// 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
//
// 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_COMPOSITE_GROUPS 2
#define ASE_FILE_FLAG_LAYER_WITH_UUID 4
#define ASE_FILE_CHUNK_FLI_COLOR2 4
#define ASE_FILE_CHUNK_FLI_COLOR 11

View File

@ -75,6 +75,9 @@ bool AsepriteDecoder::decode()
sprite->setGridBounds(
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
doc::Layer* last_layer = sprite->root();
doc::WithUserData* last_object_with_user_data = sprite.get();
@ -577,48 +580,53 @@ doc::Layer* AsepriteDecoder::readLayerChunk(AsepriteHeader* header,
break;
}
if (layer) {
const bool composeGroups = (header->flags & ASE_FILE_FLAG_COMPOSITE_GROUPS);
if (!layer)
return nullptr;
if ((layer->isImage() || (layer->isGroup() && composeGroups)) &&
// Only transparent layers can have blend mode and opacity
!(flags & int(doc::LayerFlags::Background))) {
layer->setBlendMode((doc::BlendMode)blendmode);
if (header->flags & ASE_FILE_FLAG_LAYER_WITH_OPACITY)
layer->setOpacity(opacity);
}
// Read UUID if usage is enabled
if (sprite->useUuidsForLayers())
layer->setUuid(readUuid());
// flags
layer->setFlags(
static_cast<doc::LayerFlags>(flags & static_cast<int>(doc::LayerFlags::PersistentFlagsMask)));
const bool composeGroups = (header->flags & ASE_FILE_FLAG_COMPOSITE_GROUPS);
// 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;
if ((layer->isImage() || (layer->isGroup() && composeGroups)) &&
// Only transparent layers can have blend mode and opacity
!(flags & int(doc::LayerFlags::Background))) {
layer->setBlendMode((doc::BlendMode)blendmode);
if (header->flags & ASE_FILE_FLAG_LAYER_WITH_OPACITY)
layer->setOpacity(opacity);
}
// 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;
}
@ -1433,12 +1441,7 @@ const doc::UserData::Variant AsepriteDecoder::readPropertyValue(uint16_t type)
return value;
}
case USER_DATA_PROPERTY_TYPE_UUID: {
base::Uuid value;
uint8_t* bytes = value.bytes();
for (int i = 0; i < 16; ++i) {
bytes[i] = read8();
}
return value;
return readUuid();
}
default: {
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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
// 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.
// Read LICENSE.txt for more information.
@ -19,7 +19,8 @@ enum class SerialFormat : uint16_t {
Ver0 = 0, // Old version
Ver1 = 1, // New version with tilesets
Ver2 = 2, // Version 2 adds custom properties to user data
LastVer = Ver2
Ver3 = 3, // Version 3 adds UUIDs to layers
LastVer = Ver3
};
} // namespace doc

View File

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

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