diff --git a/src/doc/CMakeLists.txt b/src/doc/CMakeLists.txt index 63a32b10d..944458869 100644 --- a/src/doc/CMakeLists.txt +++ b/src/doc/CMakeLists.txt @@ -1,5 +1,5 @@ # Aseprite Document Library -# Copyright (C) 2019-2022 Igara Studio S.A. +# Copyright (C) 2019-2023 Igara Studio S.A. # Copyright (C) 2001-2018 David Capello if(WIN32) @@ -74,6 +74,7 @@ add_library(doc-lib tileset.cpp tileset_io.cpp tilesets.cpp + user_data.cpp user_data_io.cpp util.cpp) diff --git a/src/doc/user_data.cpp b/src/doc/user_data.cpp new file mode 100644 index 000000000..025e15c37 --- /dev/null +++ b/src/doc/user_data.cpp @@ -0,0 +1,218 @@ +// Aseprite Document Library +// Copyright (c) 2023 Igara Studio S.A. +// +// This file is released under the terms of the MIT license. +// Read LICENSE.txt for more information. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doc/user_data.h" + +namespace doc { + +size_t count_nonempty_properties_maps(const UserData::PropertiesMaps& propertiesMaps) +{ + size_t i = 0; + for (const auto& it : propertiesMaps) + if (!it.second.empty()) + ++i; + return i; +} + +static bool is_negative(const UserData::Variant& value) +{ + switch (value.type()) { + case USER_DATA_PROPERTY_TYPE_INT8: { + auto v = get_value(value); + return v < 0; + } + case USER_DATA_PROPERTY_TYPE_INT16: { + auto v = get_value(value); + return v < 0; + } + case USER_DATA_PROPERTY_TYPE_INT32: { + auto v = get_value(value); + return v < 0; + } + case USER_DATA_PROPERTY_TYPE_INT64: { + auto v = get_value(value); + return v < 0; + } + } + return false; +} + +uint16_t all_elements_of_same_type(const UserData::Vector& vector) +{ + uint16_t type = vector.empty() ? 0 : vector.front().type(); + uint16_t commonReducedType = 0; + bool hasNegativeNumbers = false; + for (auto value : vector) { + if (type != value.type()) { + return 0; + } + else if (IS_REDUCIBLE_INT(value.type())) { + auto t = reduce_int_type_size(value).type(); + hasNegativeNumbers |= is_negative(value); + if (t > commonReducedType) { + commonReducedType = t; + } + } + } + + // TODO: The following check probably is not useful right now, I believe this could + // become useful if at some point we want to try to find a common integer type for vectors + // that contains elements of different integer types only. + + // If our common reduced type is unsigned and we have negative numbers + // in our vector we should select the next signed type that includes it. + if (commonReducedType != 0 && + (commonReducedType & 1) && + hasNegativeNumbers) { + commonReducedType++; + // We couldn't find one type that satisfies all the integers. This shouldn't ever happen. + if (commonReducedType >= USER_DATA_PROPERTY_TYPE_UINT64) commonReducedType = 0; + } + + return commonReducedType ? commonReducedType : type; +} + +UserData::Variant cast_to_smaller_int_type(const UserData::Variant& value, uint16_t type) +{ + ASSERT(type < value.type()); + switch (value.type()) { + case USER_DATA_PROPERTY_TYPE_INT16: { + auto v = get_value(value); + if (type == USER_DATA_PROPERTY_TYPE_INT8) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT8) + return static_cast(v); + break; + } + case USER_DATA_PROPERTY_TYPE_UINT16: { + auto v = get_value(value); + if (type == USER_DATA_PROPERTY_TYPE_INT8) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT8) + return static_cast(v); + break; + } + case USER_DATA_PROPERTY_TYPE_INT32: { + auto v = get_value(value); + if (type == USER_DATA_PROPERTY_TYPE_INT8) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT8) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_INT16) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT16) + return static_cast(v); + break; + } + case USER_DATA_PROPERTY_TYPE_UINT32: { + auto v = get_value(value); + if (type == USER_DATA_PROPERTY_TYPE_INT8) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT8) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_INT16) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT16) + return static_cast(v); + break; + } + case USER_DATA_PROPERTY_TYPE_INT64: { + auto v = get_value(value); + if (type == USER_DATA_PROPERTY_TYPE_INT8) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT8) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_INT16) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT16) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_INT32) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT32) + return static_cast(v); + break; + } + case USER_DATA_PROPERTY_TYPE_UINT64: { + auto v = get_value(value); + if (type == USER_DATA_PROPERTY_TYPE_INT8) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT8) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_INT16) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT16) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_INT32) + return static_cast(v); + else if (type == USER_DATA_PROPERTY_TYPE_UINT32) + return static_cast(v); + break; + } + } + return value; +} + +UserData::Variant reduce_int_type_size(const UserData::Variant& value) +{ + switch (value.type()) { + case USER_DATA_PROPERTY_TYPE_INT16: { + auto v = get_value(value); + if (INT8_COMPATIBLE(v)) return static_cast(v); + else if (UINT8_COMPATIBLE(v)) return static_cast(v); + return v; + } + case USER_DATA_PROPERTY_TYPE_UINT16: { + auto v = get_value(value); + if (INT8_COMPATIBLE(v)) return static_cast(v); + else if (UINT8_COMPATIBLE(v)) return static_cast(v); + return v; + } + case USER_DATA_PROPERTY_TYPE_INT32: { + auto v = get_value(value); + if (INT8_COMPATIBLE(v)) return static_cast(v); + else if (UINT8_COMPATIBLE(v)) return static_cast(v); + else if (INT16_COMPATIBLE(v)) return static_cast(v); + else if (UINT16_COMPATIBLE(v)) return static_cast(v); + return v; + } + case USER_DATA_PROPERTY_TYPE_UINT32: { + auto v = get_value(value); + if (INT8_COMPATIBLE(v)) return static_cast(v); + else if (UINT8_COMPATIBLE(v)) return static_cast(v); + else if (INT16_COMPATIBLE(v)) return static_cast(v); + else if (UINT16_COMPATIBLE(v)) return static_cast(v); + return v; + } + case USER_DATA_PROPERTY_TYPE_INT64: { + auto v = get_value(value); + if (INT8_COMPATIBLE(v)) return static_cast(v); + else if (UINT8_COMPATIBLE(v)) return static_cast(v); + else if (INT16_COMPATIBLE(v)) return static_cast(v); + else if (UINT16_COMPATIBLE(v)) return static_cast(v); + else if (INT32_COMPATIBLE(v)) return static_cast(v); + else if (UINT32_COMPATIBLE(v)) return static_cast(v); + return v; + } + case USER_DATA_PROPERTY_TYPE_UINT64: { + auto v = get_value(value); + if (INT8_COMPATIBLE(v)) return static_cast(v); + else if (UINT8_COMPATIBLE(v)) return static_cast(v); + else if (INT16_COMPATIBLE(v)) return static_cast(v); + else if (UINT16_COMPATIBLE(v)) return static_cast(v); + else if (INT32_COMPATIBLE(v)) return static_cast(v); + else if (UINT32_COMPATIBLE(v)) return static_cast(v); + return v; + } + default: + return value; + } +} + +} diff --git a/src/doc/user_data.h b/src/doc/user_data.h index b5de92607..b55e17b1a 100644 --- a/src/doc/user_data.h +++ b/src/doc/user_data.h @@ -160,13 +160,13 @@ namespace doc { size_t count_nonempty_properties_maps(const UserData::PropertiesMaps& propertiesMaps); - // If all the vector elements are of the same type returns such type. - // Otherwise it returns -1. + // If all the elements of vector have the same type, returns that type, also + // if this type is an integer, it tries to reduce it to the minimum int type + // capable of storing all the vector values. + // If all the elements of vector doesn't have the same type, returns 0. uint16_t all_elements_of_same_type(const UserData::Vector& vector); - - UserData::Variant reduce_int_type_size(const UserData::Variant& value); - UserData::Variant cast_to_smaller_int_type(const UserData::Variant& value, uint16_t type); + UserData::Variant reduce_int_type_size(const UserData::Variant& value); } // namespace doc diff --git a/src/doc/user_data_io.cpp b/src/doc/user_data_io.cpp index 8ec776b76..3b9d416e6 100644 --- a/src/doc/user_data_io.cpp +++ b/src/doc/user_data_io.cpp @@ -1,4 +1,5 @@ // Aseprite Document Library +// Copyright (c) 2023 Igara Studio S.A. // Copyright (c) 2001-2015 David Capello // // This file is released under the terms of the MIT license. @@ -21,253 +22,19 @@ namespace doc { using namespace base::serialization; using namespace base::serialization::little_endian; -void write_properties_maps(std::ostream& os, const UserData::PropertiesMaps& propertiesMaps); -UserData::PropertiesMaps read_properties_maps(std::istream& is); - -void write_user_data(std::ostream& os, const UserData& userData) -{ - write_string(os, userData.text()); - write32(os, userData.color()); - write_properties_maps(os, userData.propertiesMaps()); -} - -UserData read_user_data(std::istream& is) -{ - UserData userData; - userData.setText(read_string(is)); - // This check is here because we've been restoring sprites from - // old sessions where the color is restored incorrectly if we - // don't check if there is enough space to read from the file - // (e.g. reading a random color or just white, maybe -1 which is - // 0xffffffff in 32-bit). - if (!is.eof()) { - userData.setColor(read32(is)); - userData.propertiesMaps() = read_properties_maps(is); - } - return userData; -} - -size_t count_nonempty_properties_maps(const UserData::PropertiesMaps& propertiesMaps) -{ - size_t i = 0; - for (const auto& it : propertiesMaps) - if (!it.second.empty()) - ++i; - return i; -} - -bool is_negative(const UserData::Variant& value) -{ - switch (value.type()) { - case USER_DATA_PROPERTY_TYPE_INT8: { - auto v = get_value(value); - return v < 0; - } - case USER_DATA_PROPERTY_TYPE_INT16: { - auto v = get_value(value); - return v < 0; - } - case USER_DATA_PROPERTY_TYPE_INT32: { - auto v = get_value(value); - return v < 0; - } - case USER_DATA_PROPERTY_TYPE_INT64: { - auto v = get_value(value); - return v < 0; - } - } - return false; -} - - -// If all the elements of vector have the same type, returns that type, also -// if this type is an integer, it tries to reduce it to the minimum int type -// capable of storing all the vector values. -// If all the elements of vector doesn't have the same type, returns 0. -uint16_t all_elements_of_same_type(const UserData::Vector& vector) -{ - uint16_t type = vector.empty() ? 0 : vector.front().type(); - uint16_t commonReducedType = 0; - bool hasNegativeNumbers = false; - for (auto value : vector) { - if (type != value.type()) { - return 0; - } - else if (IS_REDUCIBLE_INT(value.type())) { - auto t = reduce_int_type_size(value).type(); - hasNegativeNumbers |= is_negative(value); - if (t > commonReducedType) { - commonReducedType = t; - } - } - } - - // TODO: The following check probably is not useful right now, I believe this could - // become useful if at some point we want to try to find a common integer type for vectors - // that contains elements of different integer types only. - - // If our common reduced type is unsigned and we have negative numbers - // in our vector we should select the next signed type that includes it. - if (commonReducedType != 0 && - (commonReducedType & 1) && - hasNegativeNumbers) { - commonReducedType++; - // We couldn't find one type that satisfies all the integers. This shouldn't ever happen. - if (commonReducedType >= USER_DATA_PROPERTY_TYPE_UINT64) commonReducedType = 0; - } - - return commonReducedType ? commonReducedType : type; -} - -UserData::Variant cast_to_smaller_int_type(const UserData::Variant& value, uint16_t type) -{ - ASSERT(type < value.type()); - switch (value.type()) { - case USER_DATA_PROPERTY_TYPE_INT16: { - auto v = get_value(value); - if (type == USER_DATA_PROPERTY_TYPE_INT8) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT8) - return static_cast(v); - break; - } - case USER_DATA_PROPERTY_TYPE_UINT16: { - auto v = get_value(value); - if (type == USER_DATA_PROPERTY_TYPE_INT8) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT8) - return static_cast(v); - break; - } - case USER_DATA_PROPERTY_TYPE_INT32: { - auto v = get_value(value); - if (type == USER_DATA_PROPERTY_TYPE_INT8) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT8) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_INT16) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT16) - return static_cast(v); - break; - } - case USER_DATA_PROPERTY_TYPE_UINT32: { - auto v = get_value(value); - if (type == USER_DATA_PROPERTY_TYPE_INT8) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT8) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_INT16) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT16) - return static_cast(v); - break; - } - case USER_DATA_PROPERTY_TYPE_INT64: { - auto v = get_value(value); - if (type == USER_DATA_PROPERTY_TYPE_INT8) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT8) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_INT16) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT16) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_INT32) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT32) - return static_cast(v); - break; - } - case USER_DATA_PROPERTY_TYPE_UINT64: { - auto v = get_value(value); - if (type == USER_DATA_PROPERTY_TYPE_INT8) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT8) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_INT16) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT16) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_INT32) - return static_cast(v); - else if (type == USER_DATA_PROPERTY_TYPE_UINT32) - return static_cast(v); - break; - } - } - return value; -} - -UserData::Variant reduce_int_type_size(const UserData::Variant& value) -{ - switch (value.type()) { - case USER_DATA_PROPERTY_TYPE_INT16: { - auto v = get_value(value); - if (INT8_COMPATIBLE(v)) return static_cast(v); - else if (UINT8_COMPATIBLE(v)) return static_cast(v); - return v; - } - case USER_DATA_PROPERTY_TYPE_UINT16: { - auto v = get_value(value); - if (INT8_COMPATIBLE(v)) return static_cast(v); - else if (UINT8_COMPATIBLE(v)) return static_cast(v); - return v; - } - case USER_DATA_PROPERTY_TYPE_INT32: { - auto v = get_value(value); - if (INT8_COMPATIBLE(v)) return static_cast(v); - else if (UINT8_COMPATIBLE(v)) return static_cast(v); - else if (INT16_COMPATIBLE(v)) return static_cast(v); - else if (UINT16_COMPATIBLE(v)) return static_cast(v); - return v; - } - case USER_DATA_PROPERTY_TYPE_UINT32: { - auto v = get_value(value); - if (INT8_COMPATIBLE(v)) return static_cast(v); - else if (UINT8_COMPATIBLE(v)) return static_cast(v); - else if (INT16_COMPATIBLE(v)) return static_cast(v); - else if (UINT16_COMPATIBLE(v)) return static_cast(v); - return v; - } - case USER_DATA_PROPERTY_TYPE_INT64: { - auto v = get_value(value); - if (INT8_COMPATIBLE(v)) return static_cast(v); - else if (UINT8_COMPATIBLE(v)) return static_cast(v); - else if (INT16_COMPATIBLE(v)) return static_cast(v); - else if (UINT16_COMPATIBLE(v)) return static_cast(v); - else if (INT32_COMPATIBLE(v)) return static_cast(v); - else if (UINT32_COMPATIBLE(v)) return static_cast(v); - return v; - } - case USER_DATA_PROPERTY_TYPE_UINT64: { - auto v = get_value(value); - if (INT8_COMPATIBLE(v)) return static_cast(v); - else if (UINT8_COMPATIBLE(v)) return static_cast(v); - else if (INT16_COMPATIBLE(v)) return static_cast(v); - else if (UINT16_COMPATIBLE(v)) return static_cast(v); - else if (INT32_COMPATIBLE(v)) return static_cast(v); - else if (UINT32_COMPATIBLE(v)) return static_cast(v); - return v; - } - default: - return value; - } -} - -void write_point(std::ostream& os, const gfx::Point& point) +static void write_point(std::ostream& os, const gfx::Point& point) { write32(os, point.x); write32(os, point.y); } -void write_size(std::ostream& os, const gfx::Size& size) +static void write_size(std::ostream& os, const gfx::Size& size) { write32(os, size.w); write32(os, size.h); } -void write_property_value(std::ostream& os, const UserData::Variant& variant) +static void write_property_value(std::ostream& os, const UserData::Variant& variant) { switch (variant.type()) { @@ -348,7 +115,7 @@ void write_property_value(std::ostream& os, const UserData::Variant& variant) } } -void write_properties_maps(std::ostream& os, const UserData::PropertiesMaps& propertiesMaps) +static void write_properties_maps(std::ostream& os, const UserData::PropertiesMaps& propertiesMaps) { write32(os, propertiesMaps.size()); for (auto propertiesMap : propertiesMaps) { @@ -359,7 +126,14 @@ void write_properties_maps(std::ostream& os, const UserData::PropertiesMaps& pro } } -UserData::Variant read_property_value(std::istream& is, uint16_t type) +void write_user_data(std::ostream& os, const UserData& userData) +{ + write_string(os, userData.text()); + write32(os, userData.color()); + write_properties_maps(os, userData.propertiesMaps()); +} + +static UserData::Variant read_property_value(std::istream& is, uint16_t type) { switch (type) { case USER_DATA_PROPERTY_TYPE_NULLPTR: { @@ -458,7 +232,7 @@ UserData::Variant read_property_value(std::istream& is, uint16_t type) return doc::UserData::Variant{}; } -UserData::PropertiesMaps read_properties_maps(std::istream& is) +static UserData::PropertiesMaps read_properties_maps(std::istream& is) { doc::UserData::PropertiesMaps propertiesMaps; size_t nmaps = read32(is); @@ -470,4 +244,20 @@ UserData::PropertiesMaps read_properties_maps(std::istream& is) return propertiesMaps; } +UserData read_user_data(std::istream& is) +{ + UserData userData; + userData.setText(read_string(is)); + // This check is here because we've been restoring sprites from + // old sessions where the color is restored incorrectly if we + // don't check if there is enough space to read from the file + // (e.g. reading a random color or just white, maybe -1 which is + // 0xffffffff in 32-bit). + if (!is.eof()) { + userData.setColor(read32(is)); + userData.propertiesMaps() = read_properties_maps(is); + } + return userData; +} + }