diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5f1a89..73e2839 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,14 +17,15 @@ jobs: env: CC: ${{ matrix.env.CC }} CXX: ${{ matrix.env.CXX }} - run: | - ./.github/actions/build.sh + run: ./.github/actions/build.sh + - name: Test + run: cd build && make test macOS: name: Build on macOS strategy: matrix: - os: [ macos-10.15, macos-11 ] + os: [ macos-12 ] runs-on: ${{ matrix.os }} steps: - name: Checkout @@ -32,6 +33,8 @@ jobs: - name: Build run: | ./.github/actions/build.sh + - name: Test + run: cd build && make test windows: name: Build on Windows @@ -62,3 +65,5 @@ jobs: CXX: ${{ matrix.env.CXX }} run: | ./.github/actions/build.sh + - name: Test + run: cmake --build build --target test \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ec3e065..6cfdc45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,21 +24,29 @@ project(Graphite CXX) set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) if (APPLE) set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64") endif() -include_directories("${PROJECT_SOURCE_DIR}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2 -mavx -msse4.1") -file(GLOB_RECURSE graphite_sources - libGraphite/*.cpp -) -add_library(Graphite ${graphite_sources}) +set(PROJECT_LIBS_DIR ${CMAKE_CURRENT_LIST_DIR}/libs) -file(GLOB_RECURSE graphite_test_sources - GraphiteTest/*.cpp -) -add_executable(GraphiteTest ${graphite_test_sources}) -target_link_libraries(GraphiteTest Graphite) +# Libraries +add_subdirectory(${PROJECT_LIBS_DIR}/libSIMD) +add_subdirectory(${PROJECT_LIBS_DIR}/libHashing) +add_subdirectory(${PROJECT_LIBS_DIR}/libEncoding) +add_subdirectory(${PROJECT_LIBS_DIR}/libData) +add_subdirectory(${PROJECT_LIBS_DIR}/libResourceCore) +add_subdirectory(${PROJECT_LIBS_DIR}/libQuickdraw) +add_subdirectory(${PROJECT_LIBS_DIR}/libSoundCore) +add_subdirectory(${PROJECT_LIBS_DIR}/libSpriteWorld) +add_subdirectory(${PROJECT_LIBS_DIR}/libToolbox) +######################################################################################################################## + +enable_testing() +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + add_subdirectory(tests) +endif() \ No newline at end of file diff --git a/README.md b/README.md index 05328d4..b3128c9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,19 @@ +[![Graphite CI](https://github.com/TheDiamondProject/Graphite/actions/workflows/build.yml/badge.svg)](https://github.com/TheDiamondProject/Graphite/actions/workflows/build.yml) [![Version](https://img.shields.io/badge/version-v1.0-blue.svg)](#) [![Version](https://img.shields.io/badge/license-MIT-blue.svg)](#) + # Graphite The Graphite Library is part of _The Diamond Project_ and aims to provide modern, cross platform implementations of various features and aspects of functionality from the Classic Macintosh OS and Carbon Frameworks. +## Using libGraphite +TBC + +## libTesting +The Graphite library includes a secondary library designed for providing the required functionality for basic unit testing. You can find documentation for this in the `libTesting` directory. Graphite uses this library to test the core functionality and performance of main `libGraphite` library, along with CMake and GitHub Actions to ensure that no regressions occur and that all added functionality is actually correct and verified. + +It is provided in such a way that you can use it in your own projects should you wish to. + +## Contributing +TBC + ## License -Graphite is available under the MIT license. \ No newline at end of file +Graphite is available under the MIT license, which can be [found here](). \ No newline at end of file diff --git a/libGraphite/data/data.cpp b/libGraphite/data/data.cpp deleted file mode 100644 index 984787a..0000000 --- a/libGraphite/data/data.cpp +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include "libGraphite/data/data.hpp" - -// MARK: - Constructor - -graphite::data::data::data(enum graphite::data::byte_order bo) - : m_bo(bo), m_data(std::make_shared>(0)), m_start(0), m_size(0) -{ - -} - -graphite::data::data::data(std::size_t capacity, enum graphite::data::byte_order bo) - : m_bo(bo), m_data(std::make_shared>(0)), m_start(0), m_size(capacity) -{ - -} - -graphite::data::data::data(std::shared_ptr> bytes, std::size_t size, std::size_t start, enum graphite::data::byte_order bo) - : m_bo(bo), m_data(std::move(bytes)), m_size(size), m_start(start) -{ - if ((m_start + m_size) > m_data->size()) { - throw std::out_of_range("Invalid boundaries for data slice."); - } -} - -// MARK: - Offset Calculations - -auto graphite::data::data::relative_offset(int64_t offset) const -> int64_t -{ - if (offset > m_size || offset < 0) { - throw std::out_of_range("Attempted to calculate an offset that went beyond the boundaries of the data slice."); - } - return m_start + offset; -} - -// MARK: - Size - -auto graphite::data::data::size() const -> std::size_t -{ - if (m_size > 0) { - return m_size; - } - else if (m_data != nullptr && m_start == 0) { - return m_data->size(); - } - else { - return 0; - } -} - -auto graphite::data::data::start() const -> std::size_t -{ - return m_start; -} - -auto graphite::data::data::current_byte_order() const -> enum graphite::data::byte_order -{ - return m_bo; -} - -auto graphite::data::data::set_byte_order(enum graphite::data::byte_order bo) -> void -{ - m_bo = bo; -} - - -// MARK: - Plumbing - -auto graphite::data::data::get() -> std::shared_ptr> -{ - return m_data; -} - -auto graphite::data::data::at(std::size_t offset) const -> char -{ - return m_data->at(relative_offset(offset)); -} - -// MARK: - Writer Assistance - -auto graphite::data::data::resync_size() -> void -{ - m_size = m_data->size(); -} - diff --git a/libGraphite/data/data.hpp b/libGraphite/data/data.hpp deleted file mode 100644 index 009c7aa..0000000 --- a/libGraphite/data/data.hpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !defined(GRAPHITE_DATA) -#define GRAPHITE_DATA - -#include -#include -#include -#include -#include - -namespace graphite::data -{ - - /** - * The byte order represents the endianness of a data value. - */ - enum byte_order : int { msb = 0, lsb = 1 }; - - /** - * The `graphite::data::data` class is used to store binary data in memory, - * and can be written to and read from disk. - */ - class data: public std::enable_shared_from_this - { - private: - enum graphite::data::byte_order m_bo { msb }; - std::shared_ptr> m_data { nullptr }; - std::size_t m_start { 0 }; - std::size_t m_size { 0 }; - - public: - /** - * Construct a new empty `graphite::data::data` object. - */ - explicit data(enum graphite::data::byte_order bo = msb); - - /** - * Construct a new `graphite::data::data` object with the specified capacity. - */ - explicit data(std::size_t capacity, enum graphite::data::byte_order bo = msb); - - /** - * Construct a new `graphite::data::data` object with the provided data. - */ - data(std::shared_ptr> bytes, std::size_t size, std::size_t start = 0, enum graphite::data::byte_order bo = msb); - - /** - * Calculate an offset within the receiver's data. - */ - auto relative_offset(int64_t offset) const -> int64_t; - - /** - * Returns the current size of the receiver. - */ - auto size() const -> std::size_t; - - /** - * Returns the start location of the receiver. - */ - auto start() const -> std::size_t; - - /** - * Returns the intended byte order of the receiver. - */ - auto current_byte_order() const -> enum graphite::data::byte_order; - - /** - * Set a new byte order for the receiver. - * - * Warning: This can cause corruption of values being read or written if the - * byte order is set incorrectly. - */ - auto set_byte_order(enum graphite::data::byte_order bo) -> void; - - /** - * Returns a pointer to the internal data vector of the receiver. - */ - auto get() -> std::shared_ptr>; - - /** - * Returns the element at the specified index. - */ - auto at(std::size_t offset) const -> char; - - /** - * Resync the size of the data from the internal data vector. This is required - * when attempting to read back data from a resource that has just been written to. - */ - auto resync_size() -> void; - - }; - -} - -#endif diff --git a/libGraphite/data/reader.cpp b/libGraphite/data/reader.cpp deleted file mode 100644 index 153fce6..0000000 --- a/libGraphite/data/reader.cpp +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "libGraphite/data/reader.hpp" -#include "libGraphite/data/data.hpp" -#include "libGraphite/encoding/macroman/macroman.hpp" -#include -#include -#include - -// MARK: - Constructor - -graphite::data::reader::reader(const std::string& path) -{ - // Attempt to open the file, and throw and exception if we failed to do so. - std::ifstream file(path, std::ios::binary); - if (!file.is_open() || file.fail()) { - throw std::runtime_error("Failed to open resource file: " + path); - } - - // Make sure we don't skip newlines. - file.unsetf(std::ios::skipws); - - // Get the size of the file. - file.seekg(0UL, std::ios::end); - auto file_size = file.tellg(); - file.seekg(0UL, std::ios::beg); - - // Read the contents of the file into the vector. - auto data = std::make_shared>(file_size); - file.read(&(*data)[0], file_size); - m_data = std::make_shared(data, file_size, graphite::data::byte_order::msb); - - // Close the file and clean up. - file.close(); -} - -graphite::data::reader::reader(std::shared_ptr data, uint64_t pos) - : m_data(std::move(data)), m_pos(pos) -{ - -} - -// MARK: - Byte Order - -template::value>::type*> -auto graphite::data::reader::swap( - T value, - enum graphite::data::byte_order value_bo, - enum graphite::data::byte_order result_bo, - uint64_t size -) -> T { - // Return the value immediately if the value byte order matches the result byte order. - if (value_bo == result_bo) { - return value; - } - - T v = 0; - size = size == -1 ? sizeof(T) : size; - - for (decltype(size) i = 0; i < size; ++i) { - auto b = (size - i - 1) << 3ULL; - v |= ((value >> b) & 0xFF) << (i << 3ULL); - } - - return v; -} - - -// MARK: - Internal Data - -auto graphite::data::reader::get() -> std::shared_ptr -{ - return m_data; -} - -// MARK: - Size - -auto graphite::data::reader::size() const -> std::size_t -{ - return m_data->size(); -} - -// MARK: - Position - -auto graphite::data::reader::eof() const -> bool -{ - return (m_pos >= m_data->size()); -} - -auto graphite::data::reader::position() const -> uint64_t -{ - return m_pos; -} - -auto graphite::data::reader::set_position(uint64_t pos) -> void -{ - m_pos = pos; -} - -auto graphite::data::reader::move(int64_t delta) -> void -{ - // TODO: Bounds checking - m_pos += delta; -} - -auto graphite::data::reader::save_position() -> void -{ - m_pos_stack.push_back(m_pos); -} - -auto graphite::data::reader::restore_position() -> void -{ - if (m_pos_stack.empty()) { - throw std::logic_error("Attempted to restore reader position that did not exist."); - } - m_pos = m_pos_stack.back(); - m_pos_stack.pop_back(); -} - -// MARK: - Template Read - -template::value>::type*> -auto graphite::data::reader::read_integer(int64_t offset, graphite::data::reader::mode mode, uint64_t size) -> T -{ - T v = 0; - size = size == -1 ? sizeof(T) : size; - if (size <= 0 || size > sizeof(T)) { - throw std::logic_error("Invalid integer read size specified."); - } - - if (m_data->get() == nullptr) { - throw std::runtime_error("Invalid data being read from."); - } - - for (decltype(size) i = 0; i < size; ++i) { - auto b = static_cast(m_data->at(m_pos + offset + i)); - v |= static_cast(b) << (i << 3ULL); - } - - if (size > 1) { - v = swap(v, m_data->current_byte_order(), m_native_bo, size); - } - - if (mode == graphite::data::reader::mode::advance) { - move(offset + size); - } - - return v; -} - -// MARK: - Read Integer Functions - -auto graphite::data::reader::read_byte(int64_t offset, graphite::data::reader::mode mode) -> uint8_t -{ - return read_integer(offset, mode); -} - -auto graphite::data::reader::read_signed_byte(int64_t offset, graphite::data::reader::mode mode) -> int8_t -{ - return static_cast(read_byte(offset, mode)); -} - -auto graphite::data::reader::read_short(int64_t offset, graphite::data::reader::mode mode) -> uint16_t -{ - return read_integer(offset, mode); -} - -auto graphite::data::reader::read_signed_short(int64_t offset, graphite::data::reader::mode mode) -> int16_t -{ - return static_cast(read_short(offset, mode)); -} - -auto graphite::data::reader::read_triple(int64_t offset, graphite::data::reader::mode mode) -> uint32_t -{ - return read_integer(offset, mode, 3); -} - -auto graphite::data::reader::read_long(int64_t offset, graphite::data::reader::mode mode) -> uint32_t -{ - return read_integer(offset, mode); -} - -auto graphite::data::reader::read_signed_long(int64_t offset, graphite::data::reader::mode mode) -> int32_t -{ - return static_cast(read_long(offset, mode)); -} - -auto graphite::data::reader::read_quad(int64_t offset, graphite::data::reader::mode mode) -> uint64_t -{ - return read_integer(offset, mode); -} - -auto graphite::data::reader::read_signed_quad(int64_t offset, graphite::data::reader::mode mode) -> int64_t -{ - return static_cast(read_quad(offset, mode)); -} - -// MARK: - Read String Functions - -auto graphite::data::reader::read_cstr(int64_t size, int64_t offset, graphite::data::reader::mode mode) -> std::string -{ - if (size == -1) { - // Read until a NUL byte is encountered. This is the slowest form of - // read, and will required stepping through bytes one by one. - auto vec = std::vector(0); - auto i = 0; - while (m_data->at(m_pos + offset + i)) { - vec.push_back(static_cast(m_data->at(m_pos + offset + i++))); - } - - if (mode == reader::mode::advance) { - m_pos += offset + vec.size() + 1; - } - - return graphite::encoding::mac_roman::to_utf8(vec); - } - else { - // Read a fixed chunk of memory and convert it to a string. - auto data = read_bytes(size, offset, mode); - std::vector bytes(data.begin(), data.end()); - while (!bytes.empty() && bytes.back() == 0) { - bytes.pop_back(); - } - return graphite::encoding::mac_roman::to_utf8(bytes); - } -} - -auto graphite::data::reader::read_pstr(int64_t offset, graphite::data::reader::mode mode) -> std::string -{ - switch (mode) { - case advance: { - auto length = read_byte(offset, advance); - return read_cstr(length, 0, advance); - } - - case peek: { - auto length = read_byte(offset, peek); - return read_cstr(length, offset + 1, peek); - } - - default: { - return ""; - } - } -} - -// MARK: - Read Data Functions - -auto graphite::data::reader::read_data(int64_t size, int64_t offset, graphite::data::reader::mode mode) -> std::shared_ptr -{ - auto data = std::make_shared(m_data->get(), size, m_data->start() + m_pos + offset); - move(offset + size); - return data; -} - -auto graphite::data::reader::read_bytes(int64_t size, int64_t offset, graphite::data::reader::mode mode) -> std::vector -{ - char *start = &(*m_data->get())[m_data->relative_offset(m_pos + offset)]; - char *end = &(*m_data->get())[m_data->relative_offset(m_pos + offset + size)]; - - if (mode == graphite::data::reader::mode::advance) { - m_pos += offset + size; - } - - return std::vector(start, end); -} diff --git a/libGraphite/data/reader.hpp b/libGraphite/data/reader.hpp deleted file mode 100644 index 261b8de..0000000 --- a/libGraphite/data/reader.hpp +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#if !defined(GRAPHITE_DATA_READER) -#define GRAPHITE_DATA_READER - -#include -#include "libGraphite/data/data.hpp" - -namespace graphite::data { - -/** - * The `graphite::data::reader` class is used to read values from a - * `graphite::data::data` object. - */ - class reader - { - public: - /** - * The reading mode of the receiver. - * - * ::peek - peek at a value at the specified offset from the current - * position, without affecting the current position. - * ::advance - advance the current position after reading the value at - * the specified offset from the current position. - */ - enum mode { advance, peek }; - - private: - graphite::data::byte_order m_native_bo { lsb }; - std::shared_ptr m_data { nullptr }; - std::vector m_pos_stack; - uint64_t m_pos { 0 }; - - template::value>::type* = nullptr> - auto read_integer(int64_t offset, reader::mode mode = advance, uint64_t size = -1) -> T; - - /** - * Swap the bytes of an integer value from the source byte order to the specified - * destination byte order. - */ - template::value>::type* = nullptr> - auto swap( - T value, - enum graphite::data::byte_order value_bo, - enum graphite::data::byte_order result_bo, - uint64_t size = -1 - ) -> T; - - public: - - /** - * Construct a new `graphite::data::reader` object using the data in - * the specified file. - */ - explicit reader(const std::string& path); - - /** - * Construct a new `graphite::data::reader` object using the - * specified `graphite::data::data` object, constraining the available - * read region of the data. - */ - explicit reader(std::shared_ptr data, uint64_t pos = 0); - - /** - * Returns a shared copy of the raw data being used by the data slice. - */ - auto get() -> std::shared_ptr; - - /** - * Returns the current size of the underlying `graphite::data::data` object - * being referenced by the reader. - * - * Note: This size will be the _constrained_ size, if the reader was - * constrained. - */ - [[nodiscard]] auto size() const -> std::size_t; - - /** - * Reports if the position of the receiver is at or past the end of the - * the underlying data. - */ - [[nodiscard]] auto eof() const -> bool; - - /** - * Reports the current position of the receiver relative to its own - * constraints. - */ - [[nodiscard]] auto position() const -> uint64_t; - - /** - * Set the position of the receiver, relative to its own constraints. - */ - auto set_position(uint64_t pos) -> void; - - /** - * Move the current position of the receiver by the specified amount. - */ - auto move(int64_t delta = 1) -> void; - - /** - * Read a single unsigned byte from data. - */ - auto read_byte(int64_t offset = 0, reader::mode mode = advance) -> uint8_t; - - /** - * Read a single signed byte from data. - */ - auto read_signed_byte(int64_t offset = 0, reader::mode mode = advance) -> int8_t; - - /** - * Read a single unsigned short from data. - */ - auto read_short(int64_t offset = 0, reader::mode mode = advance) -> uint16_t; - - /** - * Read a single signed short from data. - */ - auto read_signed_short(int64_t offset = 0, reader::mode mode = advance) -> int16_t; - - /** - * Read a single unsigned triple from data. (3 bytes) - */ - auto read_triple(int64_t offset = 0, reader::mode mode = advance) -> uint32_t; - - /** - * Read a single unsigned long from data. - */ - auto read_long(int64_t offset = 0, reader::mode mode = advance) -> uint32_t; - - /** - * Read a single signed long from data. - */ - auto read_signed_long(int64_t offset = 0, reader::mode mode = advance) -> int32_t; - - /** - * Read a single unsigned quad from data. - */ - auto read_quad(int64_t offset = 0, reader::mode mode = advance) -> uint64_t; - - /** - * Read a single signed quad from data. - */ - auto read_signed_quad(int64_t offset = 0, reader::mode mode = advance) -> int64_t; - - /** - * Read a C-String from data. - */ - auto read_cstr(int64_t size = -1, int64_t offset = 0, reader::mode mode = advance) -> std::string; - - /** - * Read a Pascal String from data. - */ - auto read_pstr(int64_t offset = 0, reader::mode mode = advance) -> std::string; - - /** - * Read a chunk of data from the source data. - */ - auto read_data(int64_t size, int64_t offset = 0, reader::mode mode = advance) -> std::shared_ptr; - - /** - * Read a series of bytes from the source data. - */ - auto read_bytes(int64_t size, int64_t offset = 0, reader::mode mode = advance) -> std::vector; - - /** - * Save the current position of the reader. - */ - auto save_position() -> void; - - /** - * Restore a previous position of the reader. - */ - auto restore_position() -> void; - - }; - -} - -#endif diff --git a/libGraphite/data/writer.cpp b/libGraphite/data/writer.cpp deleted file mode 100644 index c21db15..0000000 --- a/libGraphite/data/writer.cpp +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "libGraphite/data/writer.hpp" -#include "libGraphite/data/data.hpp" -#include "libGraphite/encoding/macroman/macroman.hpp" -#include -#include -#include - -// MARK: - Constructors - -graphite::data::writer::writer() - : m_data(std::make_shared()) -{ - -} - -graphite::data::writer::writer(std::shared_ptr data) - : m_data(std::move(data)) -{ - -} - -// MARK: - Byte Order - -template::value>::type*> -auto graphite::data::writer::swap( - T value, - enum graphite::data::byte_order value_bo, - enum graphite::data::byte_order result_bo -) -> T { - // Return the value immediately if the value byte order matches the result byte order. - if (value_bo == result_bo) { - return value; - } - - T v = 0; - unsigned int size = sizeof(T); - - for (unsigned int i = 0; i < size; ++i) { - auto b = (size - i - 1) << 3U; - v |= ((value >> b) & 0xFF) << (i << 3U); - } - - return v; -} - -// MARK: - Data - -auto graphite::data::writer::data() -> std::shared_ptr -{ - return m_data; -} - -// MARK: - Size - -auto graphite::data::writer::size() const -> std::size_t -{ - return m_data->size(); -} - -// MARK: - Position - -auto graphite::data::writer::position() const -> uint64_t -{ - return m_pos; -} - -auto graphite::data::writer::set_position(uint64_t pos) -> void -{ - m_pos = pos; -} - -auto graphite::data::writer::move(int64_t delta) -> void -{ - // TODO: Bounds checking - m_pos += delta; -} - -// MARK: - Template Write - -template::value>::type*> -auto graphite::data::writer::write_integer(T value) -> void -{ - unsigned int size = sizeof(T); - auto swapped = swap(value, m_native_bo, m_data->current_byte_order()); - auto data = m_data->get(); - - for (unsigned int i = 0; i < size; ++i) { - auto b = i << 3U; - - // Two modes for writing. If we're at the very end of data then we need to insert. - // If we're not at the end of the data then we can either overwrite the current byte - // or insert a new byte. - if (m_pos >= data->size()) { - data->push_back((swapped >> b) & 0xFF); - m_pos++; - } - else { - (*data)[m_pos++] = (swapped >> b) & 0xFF; - } - } - - m_data->resync_size(); -} - - -// MARK: - Write Integer Functions - -auto graphite::data::writer::write_byte(uint8_t value, std::size_t count) -> void -{ - for (auto i = 0; i < count; ++i) { - write_integer(value); - } -} - -auto graphite::data::writer::write_signed_byte(int8_t value) -> void -{ - write_byte(static_cast(value)); -} - -auto graphite::data::writer::write_short(uint16_t value) -> void -{ - write_integer(value); -} - -auto graphite::data::writer::write_signed_short(int16_t value) -> void -{ - write_short(static_cast(value)); -} - -auto graphite::data::writer::write_long(uint32_t value) -> void -{ - write_integer(value); -} - -auto graphite::data::writer::write_signed_long(int32_t value) -> void -{ - write_long(static_cast(value)); -} - -auto graphite::data::writer::write_quad(uint64_t value) -> void -{ - write_integer(value); -} - -auto graphite::data::writer::write_signed_quad(int64_t value) -> void -{ - write_quad(static_cast(value)); -} - -// MARK: - Write Strings - -auto graphite::data::writer::write_cstr(const std::string& str, std::size_t size) -> void -{ - std::vector bytes; - - if (size == 0) { - // NUL Terminated C-String. - bytes = graphite::encoding::mac_roman::from_utf8(str); - bytes.push_back(0); - } - else { - // Fixed length C-String - bytes = graphite::encoding::mac_roman::from_utf8(str); - bytes.resize(size, 0x00); - } - - write_bytes(bytes); -} - -auto graphite::data::writer::write_pstr(const std::string& str) -> void -{ - auto bytes = graphite::encoding::mac_roman::from_utf8(str); - - if (bytes.size() > 0xFF) { - bytes.resize(0xFF); - } - auto size = static_cast(bytes.size()); - write_byte(static_cast(size)); - write_bytes(bytes); -} - -// MARK: - Write Bytes - - auto graphite::data::writer::write_bytes(const std::vector& bytes) -> void -{ - std::vector converted_bytes(bytes.begin(), bytes.end()); - write_bytes(converted_bytes); -} - - auto graphite::data::writer::write_bytes(const std::vector& bytes) -> void -{ - auto vec = m_data->get(); - vec->insert(vec->end(), bytes.begin(), bytes.end()); - m_pos += bytes.size(); - m_data->resync_size(); -} - -auto graphite::data::writer::write_data(const std::shared_ptr& data) -> void -{ - auto bytes = data->get(); - auto vec = m_data->get(); - vec->insert(vec->end(), bytes->begin() + data->start(), bytes->begin() + data->start() + data->size()); - m_pos += data->size(); - m_data->resync_size(); -} - -auto graphite::data::writer::pad_to_size(std::size_t size) -> void -{ - while (m_data->size() < size) { - write_byte(0); - } -} - -// MARK: - Saving - -auto graphite::data::writer::save(const std::string& path) const -> void -{ - std::ofstream f(path, std::ios::out | std::ios::binary); - auto data = m_data->get()->data(); - f.write(data, m_data->size()); - f.close(); -} diff --git a/libGraphite/data/writer.hpp b/libGraphite/data/writer.hpp deleted file mode 100644 index f65de60..0000000 --- a/libGraphite/data/writer.hpp +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include "libGraphite/data/data.hpp" - -#if !defined(GRAPHITE_DATA_WRITER) -#define GRAPHITE_DATA_WRITER - -namespace graphite::data { - -/** - * The `graphite::data::writer` class is used to write values into a - * `graphite::data::data` object. - */ - class writer - { - private: - graphite::data::byte_order m_native_bo { lsb }; - std::shared_ptr m_data { nullptr }; - uint64_t m_pos { 0 }; - - template::value>::type* = nullptr> - auto write_integer(T value) -> void; - - /** - * Swap the bytes of an integer value from the source byte order to the specified - * destination byte order. - */ - template::value>::type* = nullptr> - auto swap( - T value, - enum graphite::data::byte_order value_bo, - enum graphite::data::byte_order result_bo - ) -> T; - - public: - /** - * Construct a new `graphite::data::writer` object and the underlying - * `graphite::data::data` object. - */ - writer(); - - /** - * Construct a new `graphite::data::writer` object using the specified - * `graphite::data::data` object. - */ - explicit writer(std::shared_ptr data); - - /** - * Returns the internal data object. - */ - auto data() -> std::shared_ptr; - - /** - * Returns the current size of the underlying `graphite::data::data:: object. - */ - [[nodiscard]] auto size() const -> std::size_t; - - /** - * Returns the current insertion position of the receiver within the underlying - * `graphite::data::data` object. - */ - [[nodiscard]] auto position() const -> uint64_t; - - /** - * Set the insertion position of the receiver within the underlying - * `graphite::data::data` object. - */ - auto set_position(uint64_t pos) -> void; - - /** - * Move the current insertion position of the receiver by the specified amount. - */ - auto move(int64_t delta = 1) -> void; - - /** - * Write a single unsigned byte into the data. - */ - auto write_byte(uint8_t value, std::size_t count = 1) -> void; - - /** - * Write a singled signed byte into the data. - */ - auto write_signed_byte(int8_t value) -> void; - - /** - * Write a single unsigned short into the data. - */ - auto write_short(uint16_t value) -> void; - - /** - * Write a singled signed short into the data. - */ - auto write_signed_short(int16_t value) -> void; - - /** - * Write a single unsigned long into the data. - */ - auto write_long(uint32_t value) -> void; - - /** - * Write a singled signed long into the data. - */ - auto write_signed_long(int32_t value) -> void; - - /** - * Write a single unsigned quad into the data. - */ - auto write_quad(uint64_t value) -> void; - - /** - * Write a singled signed quad into the data. - */ - auto write_signed_quad(int64_t value) -> void; - - /** - * Write a C-String into the data. - */ - auto write_cstr(const std::string& str, std::size_t size = 0) -> void; - - /** - * Write a Pascal String into the data. - */ - auto write_pstr(const std::string& str) -> void; - - /** - * Write a series of bytes into the data. - */ - auto write_bytes(const std::vector& bytes) -> void; - auto write_bytes(const std::vector& bytes) -> void; - - /** - * Write the specified `graphite::data::data` object into the output - * data stream. - */ - auto write_data(const std::shared_ptr& data) -> void; - - /** - * Pad the contents of the underlying data object to the specified number of bytes. - */ - auto pad_to_size(std::size_t size) -> void; - - /** - * Write the contents of the data to the specified file. - */ - auto save(const std::string& path) const -> void; - - }; - -} - -#endif diff --git a/libGraphite/quickdraw/cicn.cpp b/libGraphite/quickdraw/cicn.cpp deleted file mode 100644 index 214e965..0000000 --- a/libGraphite/quickdraw/cicn.cpp +++ /dev/null @@ -1,262 +0,0 @@ -// -// Created by Tom Hancocks on 25/03/2020. -// - -#include "libGraphite/quickdraw/cicn.hpp" -#include "libGraphite/rsrc/manager.hpp" -#include -#include - -// MARK: - Constructor - -graphite::qd::cicn::cicn(std::shared_ptr data, int64_t id, std::string name) - : m_id(id), m_name(std::move(name)) -{ - data::reader reader(std::move(data)); - parse(reader); -} - -graphite::qd::cicn::cicn(std::shared_ptr surface) - : m_surface(std::move(surface)) -{ - -} - -auto graphite::qd::cicn::load_resource(int64_t id) -> std::shared_ptr -{ - if (auto res = graphite::rsrc::manager::shared_manager().find("cicn", id).lock()) { - return std::make_shared(res->data(), id, res->name()); - } - return nullptr; -} - - -// MARK: - Accessors - -auto graphite::qd::cicn::surface() const -> std::weak_ptr -{ - return m_surface; -} - -// MARK: - Parser - -auto graphite::qd::cicn::parse(graphite::data::reader& reader) -> void -{ - m_pixmap = graphite::qd::pixmap(reader.read_data(qd::pixmap::length)); - m_mask_base_addr = reader.read_long(); - m_mask_row_bytes = reader.read_short(); - m_mask_bounds = qd::rect::read(reader); - m_bmap_base_addr = reader.read_long(); - m_bmap_row_bytes = reader.read_short(); - m_bmap_bounds = qd::rect::read(reader); - - reader.move(4); - - auto mask_data_size = m_mask_row_bytes * m_mask_bounds.height(); - auto bmap_data_size = m_bmap_row_bytes * m_bmap_bounds.height(); - auto pmap_data_size = m_pixmap.row_bytes() * m_pixmap.bounds().height(); - - auto mask_data = reader.read_data(mask_data_size); - auto bmap_data = reader.read_data(bmap_data_size); - m_clut = qd::clut(reader); - auto pmap_data = reader.read_data(pmap_data_size); - - // Now that all information has been extracted from the resource, proceed and attempt to render it. - m_surface = std::make_shared(m_pixmap.bounds().width(), m_pixmap.bounds().height()); - - if (m_pixmap.cmp_size() == 1 && m_pixmap.cmp_count() == 1) { - - for (auto y = 0; y < m_pixmap.bounds().height(); ++y) { - auto y_offset = (y * m_pixmap.row_bytes()); - auto mask_y_offset = (y * m_mask_row_bytes); - - for (auto x = 0; x < m_pixmap.bounds().width(); ++x) { - auto byte_offset = 7 - (x % 8); - - auto byte = pmap_data->at(y_offset + (x / 8)); - auto mask = mask_data->at(mask_y_offset + (x / 8)); - auto v = (byte >> byte_offset) & 0x1; - - if ((mask >> byte_offset) & 0x1) { - m_surface->set(x, y, m_clut.get(v)); - } - } - } - - } - else if ((m_pixmap.cmp_size() == 1 && m_pixmap.cmp_count() == 2) || (m_pixmap.cmp_size() == 2 && m_pixmap.cmp_count() == 1)) { - - for (auto y = 0; y < m_pixmap.bounds().height(); ++y) { - auto y_offset = (y * m_pixmap.row_bytes()); - auto mask_y_offset = (y * m_mask_row_bytes); - - for (auto x = 0; x < m_pixmap.bounds().width(); ++x) { - auto byte_offset = (3 - (x % 4)) << 1; - auto mask_offset = (7 - (x % 8)); - - auto byte = pmap_data->at(y_offset + (x / 4)); - auto mask = mask_data->at(mask_y_offset + (x / 8)); - auto v = (byte >> byte_offset) & 0x3; - - if ((mask >> mask_offset) & 0x1) { - m_surface->set(x, y, m_clut.get(v)); - } - } - } - - } - else if ((m_pixmap.cmp_size() == 1 && m_pixmap.cmp_count() == 4) || (m_pixmap.cmp_size() == 4 && m_pixmap.cmp_count() == 1)) { - - for (auto y = 0; y < m_pixmap.bounds().height(); ++y) { - auto y_offset = (y * m_pixmap.row_bytes()); - auto mask_y_offset = (y * m_mask_row_bytes); - - for (auto x = 0; x < m_pixmap.bounds().width(); ++x) { - auto byte_offset = (1 - (x % 2)) << 2; - auto mask_offset = (7 - (x % 8)); - - auto byte = pmap_data->at(y_offset + (x / 2)); - auto mask = mask_data->at(mask_y_offset + (x / 8)); - auto v = (byte >> byte_offset) & 0xF; - - if ((mask >> mask_offset) & 0x1) { - m_surface->set(x, y, m_clut.get(v)); - } - } - } - - } - else if ((m_pixmap.cmp_size() == 1 && m_pixmap.cmp_count() == 8) || (m_pixmap.cmp_size() == 8 && m_pixmap.cmp_count() == 1)) { - - for (auto y = 0; y < m_pixmap.bounds().height(); ++y) { - auto y_offset = (y * m_pixmap.row_bytes()); - auto mask_y_offset = (y * m_mask_row_bytes); - - for (auto x = 0; x < m_pixmap.bounds().width(); ++x) { - auto mask_offset = (7 - (x % 8)); - - auto byte = static_cast(pmap_data->at(y_offset + x)); - auto mask = mask_data->at(mask_y_offset + (x / 8)); - - if ((mask >> mask_offset) & 0x1) { - m_surface->set(x, y, m_clut.get(byte)); - } - } - } - - } - else { - throw std::runtime_error("Currently unsupported cicn configuration: cmp_size=" + - std::to_string(m_pixmap.cmp_size()) + - ", cmp_count=" + std::to_string(m_pixmap.cmp_count())); - } -} - -// MARK: - Encoder - -auto graphite::qd::cicn::data() -> std::shared_ptr -{ - auto data = std::make_shared(); - auto writer = graphite::data::writer(data); - auto width = m_surface->size().width(); - auto height = m_surface->size().height(); - - // TODO: This is a brute force method of bringing down the color depth/number of colors required, - // for a cicn image. It doesn't optimise for image quality at all, and should be replaced at somepoint. - std::vector color_values; - std::vector mask_values; - uint8_t pass = 0; - do { - if (pass++ > 0) { - for (auto y = 0; y < height; ++y) { - for (auto x = 0; x < width; ++x) { - auto color = m_surface->at(x, y); - m_surface->set(x, y, qd::color( - color.red_component() & ~(1 << pass), - color.green_component() & ~(1 << pass), - color.blue_component() & ~(1 << pass), - color.alpha_component() - )); - } - } - } - - // Rebuild the Color Table for the surface. To do this we want to create an empty table, and populate it. - m_clut = qd::clut(); - color_values.clear(); - mask_values.clear(); - for (auto y = 0; y < height; ++y) { - for (auto x = 0; x < width; ++x) { - auto color = m_surface->at(x, y); - mask_values.emplace_back((color.alpha_component() & 0x80) != 0); - color_values.emplace_back(m_clut.set(color)); - } - } - } while(m_clut.size() > 256); - - - // Determine what component configuration we need. - m_pixmap = qd::pixmap(); - m_pixmap.set_bounds(qd::rect(point::zero(), m_surface->size())); - graphite::data::writer mask_data(std::make_shared()); - graphite::data::writer bmap_data(std::make_shared()); - std::shared_ptr pmap_data; - m_mask_row_bytes = m_bmap_row_bytes = (width - 1) / 8 + 1; - - bmap_data.write_byte(0, m_bmap_row_bytes * height); - - // Construct the mask data for the image. - for (auto y = 0; y < height; ++y) { - uint8_t scratch = 0; - for (auto x = 0; x < width; ++x) { - // We need to write the scratch byte every 8th bit that is visited, and clear it. - auto bit_offset = x % 8; - if (bit_offset == 0 && x != 0) { - mask_data.write_byte(scratch); - scratch = 0; - } - auto n = y * width + x; - uint8_t value = mask_values[n] ? 1 : 0; - value <<= (7 - bit_offset); - scratch |= value; - } - mask_data.write_byte(scratch); - } - - if (m_clut.size() > 256) { - throw std::runtime_error("Implementation does not currently handle more than 256 colors in a CICN"); - } - else if (m_clut.size() > 16) { - pmap_data = m_pixmap.build_pixel_data(color_values, 8); - } - else if (m_clut.size() > 4) { - pmap_data = m_pixmap.build_pixel_data(color_values, 4); - } - else if (m_clut.size() > 2) { - pmap_data = m_pixmap.build_pixel_data(color_values, 2); - } - else { - pmap_data = m_pixmap.build_pixel_data(color_values, 1); - } - - // Calculate some offsets - m_mask_base_addr = 4; - m_bmap_base_addr = m_mask_base_addr + mask_data.size(); - - // Write out the image data for the cicn. - m_pixmap.write(writer); - writer.write_long(0); - writer.write_short(m_mask_row_bytes); - m_pixmap.bounds().write(writer); - writer.write_long(0); - writer.write_short(m_bmap_row_bytes); - m_pixmap.bounds().write(writer); - writer.write_long(0); - - writer.write_data(mask_data.data()); - writer.write_data(bmap_data.data()); - m_clut.write(writer); - writer.write_data(pmap_data); - - return data; -} diff --git a/libGraphite/quickdraw/cicn.hpp b/libGraphite/quickdraw/cicn.hpp deleted file mode 100644 index 52ef5bc..0000000 --- a/libGraphite/quickdraw/cicn.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// -// Created by Tom Hancocks on 25/03/2020. -// - -#if !defined(GRAPHITE_CICN_HPP) -#define GRAPHITE_CICN_HPP - -#include -#include "libGraphite/quickdraw/internal/surface.hpp" -#include "libGraphite/quickdraw/geometry.hpp" -#include "libGraphite/quickdraw/pixmap.hpp" -#include "libGraphite/quickdraw/clut.hpp" - -namespace graphite::qd { - - class cicn - { - private: - int64_t m_id{}; - std::string m_name; - qd::pixmap m_pixmap; - uint32_t m_mask_base_addr{}; - uint16_t m_mask_row_bytes{}; - qd::rect m_mask_bounds; - uint32_t m_bmap_base_addr{}; - uint16_t m_bmap_row_bytes{}; - qd::rect m_bmap_bounds; - uint32_t m_icon_data{}; - std::shared_ptr m_surface; - qd::clut m_clut; - - auto parse(data::reader& reader) -> void; - - public: - explicit cicn(std::shared_ptr data, int64_t id = 0, std::string name = ""); - explicit cicn(std::shared_ptr surface); - - static auto load_resource(int64_t id) -> std::shared_ptr; - - [[nodiscard]] auto surface() const -> std::weak_ptr; - auto data() -> std::shared_ptr; - }; - -} - -#endif //GRAPHITE_CICN_HPP diff --git a/libGraphite/quickdraw/clut.cpp b/libGraphite/quickdraw/clut.cpp deleted file mode 100644 index 72e5297..0000000 --- a/libGraphite/quickdraw/clut.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// -// Created by Tom Hancocks on 25/03/2020. -// - -#include -#include -#include "libGraphite/quickdraw/clut.hpp" -#include "libGraphite/rsrc/manager.hpp" - -// MARK: - Constructors - - -graphite::qd::clut::clut(std::shared_ptr data, int64_t id, std::string name) - : m_id(id), m_name(std::move(name)) -{ - data::reader reader(std::move(data)); - parse(reader); -} - -graphite::qd::clut::clut(graphite::data::reader& reader) - : m_id(std::numeric_limits::max()), m_name("Embedded `clut` resource") -{ - parse(reader); -} - -auto graphite::qd::clut::load_resource(int64_t id) -> std::shared_ptr -{ - if (auto res = graphite::rsrc::manager::shared_manager().find("clut", id).lock()) { - return std::make_shared(res->data(), id, res->name()); - } - return nullptr; -} - -// MARK: - Accessors - -auto graphite::qd::clut::size() const -> int -{ - return m_size; -} - -auto graphite::qd::clut::at(int index) const -> graphite::qd::color -{ - return m_entries.at(index); -} - -auto graphite::qd::clut::get(int value) const -> graphite::qd::color -{ - return m_entries.at(value); -} - -auto graphite::qd::clut::set(const qd::color& color) -> uint16_t -{ - uint16_t value = 0; - for (auto entry : m_entries) { - if (std::get<1>(entry) == color) { - return std::get<0>(entry); - } - if (std::get<0>(entry) == value) { - ++value; - } - } - m_entries.emplace(value, color); - m_size = m_entries.size(); - return value; -} - -// MARK: - Parser - -auto graphite::qd::clut::parse(graphite::data::reader& reader) -> void -{ - m_seed = reader.read_long(); - m_flags = static_cast(reader.read_short()); - m_size = reader.read_short() + 1; - - for (auto i = 0; i < m_size; ++i) { - auto value = reader.read_short(); - auto r = static_cast((reader.read_short() / 65535.0) * 255); - auto g = static_cast((reader.read_short() / 65535.0) * 255); - auto b = static_cast((reader.read_short() / 65535.0) * 255); - int index = m_flags == device ? i : value; - m_entries.emplace(index, qd::color(r, g, b)); - } -} - -// MARK: - Writer - -auto graphite::qd::clut::write(graphite::data::writer& writer) -> void -{ - writer.write_long(m_seed); - writer.write_short(static_cast(m_flags)); - writer.write_short(m_size - 1); - - for (auto entry : m_entries) { - auto value = std::get<0>(entry); - auto color = std::get<1>(entry); - writer.write_short(value); - writer.write_short(static_cast((color.red_component() / 255.0) * 65535.0)); - writer.write_short(static_cast((color.green_component() / 255.0) * 65535.0)); - writer.write_short(static_cast((color.blue_component() / 255.0) * 65535.0)); - } -} diff --git a/libGraphite/quickdraw/clut.hpp b/libGraphite/quickdraw/clut.hpp deleted file mode 100644 index 3a35441..0000000 --- a/libGraphite/quickdraw/clut.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// -// Created by Tom Hancocks on 25/03/2020. -// - -#if !defined(GRAPHITE_CLUT_HPP) -#define GRAPHITE_CLUT_HPP - -#include -#include -#include "libGraphite/quickdraw/internal/color.hpp" -#include "libGraphite/data/reader.hpp" -#include "libGraphite/data/writer.hpp" - -namespace graphite::qd { - - struct clut - { - public: - enum flags : uint16_t { pixmap = 0x0000, device = 0x8000 }; - - private: - int64_t m_id { 0 }; - std::string m_name; - - uint32_t m_seed { 0 }; - enum flags m_flags { pixmap }; - uint16_t m_size { 0 }; - std::map m_entries; - - auto parse(data::reader& reader) -> void; - - public: - clut() = default; - explicit clut(std::shared_ptr data, int64_t id = 0, std::string name = ""); - explicit clut(data::reader& reader); - - static auto load_resource(int64_t id) -> std::shared_ptr; - - [[nodiscard]] auto size() const -> int; - - [[nodiscard]] auto at(int index) const -> qd::color; - [[nodiscard]] auto get(int value) const -> qd::color; - auto set(const qd::color& color) -> uint16_t; - - auto write(graphite::data::writer& writer) -> void; - }; - -} - -#endif //GRAPHITE_CLUT_HPP diff --git a/libGraphite/quickdraw/geometry.cpp b/libGraphite/quickdraw/geometry.cpp deleted file mode 100644 index e2eea42..0000000 --- a/libGraphite/quickdraw/geometry.cpp +++ /dev/null @@ -1,475 +0,0 @@ -// -// Created by tomhancocks on 18/04/2020. -// - -#include "libGraphite/quickdraw/geometry.hpp" - -// MARK: - Point - -graphite::qd::point::point(int16_t x, int16_t y) - : m_x(x), m_y(y) -{ - -} - -auto graphite::qd::point::zero() -> qd::point -{ - return { 0, 0 }; -} - -auto graphite::qd::point::x() const -> int16_t -{ - return m_x; -} - -auto graphite::qd::point::y() const -> int16_t -{ - return m_y; -} - -auto graphite::qd::point::set_x(const int16_t& x) -> void -{ - m_x = x; -} - -auto graphite::qd::point::set_y(const int16_t& y) -> void -{ - m_y = y; -} - -auto graphite::qd::point::read(graphite::data::reader& reader, enum coding_type type) -> qd::point -{ - switch (type) { - case coding_type::qd: { - auto y = reader.read_signed_short(); - auto x = reader.read_signed_short(); - return qd::point(x, y); - } - case coding_type::pict: { - auto x = reader.read_signed_short(); - auto y = reader.read_signed_short(); - return qd::point(x, y); - } - default: { - return qd::point(0, 0); - } - } -} - -auto graphite::qd::point::write(graphite::data::writer& writer, enum coding_type type) const -> void -{ - switch (type) { - case coding_type::qd: { - writer.write_signed_short(m_y); - writer.write_signed_short(m_x); - break; - } - case coding_type::pict: { - writer.write_signed_short(m_x); - writer.write_signed_short(m_y); - break; - } - } -} - -// MARK: - Fixed Point - -graphite::qd::fixed_point::fixed_point(double x, double y) - : m_x(x), m_y(y) -{ - -}; - -auto graphite::qd::fixed_point::zero() -> qd::fixed_point -{ - return { 0, 0 }; -}; - -auto graphite::qd::fixed_point::x() const -> double -{ - return m_x; -}; - -auto graphite::qd::fixed_point::y() const -> double -{ - return m_y; -}; - -auto graphite::qd::fixed_point::set_x(const double& x) -> void -{ - m_x = x; -}; - -auto graphite::qd::fixed_point::set_y(const double& y) -> void -{ - m_y = y; -}; - -auto graphite::qd::fixed_point::read(graphite::data::reader& reader, enum coding_type type) -> qd::fixed_point -{ - switch (type) { - case coding_type::qd: { - auto y = static_cast(reader.read_signed_long() / static_cast(1 << 16)); - auto x = static_cast(reader.read_signed_long() / static_cast(1 << 16)); - return qd::fixed_point(x, y); - } - default: { - return qd::fixed_point(0, 0); - } - } -} - -auto graphite::qd::fixed_point::write(graphite::data::writer& writer, enum coding_type type) const -> void -{ - switch (type) { - case coding_type::qd: { - writer.write_signed_long(static_cast(m_y * (1 << 16))); - writer.write_signed_long(static_cast(m_x * (1 << 16))); - break; - } - } -} - -// MARK: - Size - -graphite::qd::size::size(int16_t width, int16_t height) - : m_width(width), m_height(height) -{ - -}; - -auto graphite::qd::size::zero() -> qd::size -{ - return size(); -}; - -auto graphite::qd::size::width() const -> int16_t -{ - return m_width; -}; - -auto graphite::qd::size::height() const -> int16_t -{ - return m_height; -}; - -auto graphite::qd::size::set_width(const int16_t& width) -> void -{ - m_width = width; -}; - -auto graphite::qd::size::set_height(const int16_t& height) -> void -{ - m_height = height; -}; - -auto graphite::qd::size::read(graphite::data::reader& reader, enum coding_type type) -> qd::size -{ - switch (type) { - case coding_type::qd: { - auto height = reader.read_signed_short(); - auto width = reader.read_signed_short(); - return qd::size(width, height); - } - case coding_type::pict: { - auto width = reader.read_signed_short(); - auto height = reader.read_signed_short(); - return qd::size(width, height); - } - default: { - return qd::size(0, 0); - } - } -} - -auto graphite::qd::size::write(graphite::data::writer& writer, enum coding_type type) const -> void -{ - switch (type) { - case coding_type::qd: { - writer.write_signed_short(m_height); - writer.write_signed_short(m_width); - break; - } - case coding_type::pict: { - writer.write_signed_short(m_width); - writer.write_signed_short(m_height); - break; - } - } -} - -// MARK: - Fixed Size - -graphite::qd::fixed_size::fixed_size(double width, double height) - : m_width(width), m_height(height) -{ - -} - -auto graphite::qd::fixed_size::zero() -> qd::fixed_size -{ - return { 0, 0 }; -} - -auto graphite::qd::fixed_size::width() const -> double -{ - return m_width; -} - -auto graphite::qd::fixed_size::height() const -> double -{ - return m_height; -} - -auto graphite::qd::fixed_size::set_width(const double& width) -> void -{ - m_width = width; -} - -auto graphite::qd::fixed_size::set_height(const double& height) -> void -{ - m_height = height; -} - -auto graphite::qd::fixed_size::read(graphite::data::reader& reader, enum coding_type type) -> qd::fixed_size -{ - switch (type) { - case coding_type::qd: { - auto height = static_cast(reader.read_signed_long() / static_cast(1 << 16)); - auto width = static_cast(reader.read_signed_long() / static_cast(1 << 16)); - return qd::fixed_size(width, height); - } - default: { - return qd::fixed_size(0, 0); - } - } -} - -auto graphite::qd::fixed_size::write(graphite::data::writer& writer, enum coding_type type) const -> void -{ - switch (type) { - case coding_type::qd: { - writer.write_signed_long(static_cast(m_height * (1 << 16))); - writer.write_signed_long(static_cast(m_width * (1 << 16))); - break; - } - } -} - -// MARK: - Rect - -graphite::qd::rect::rect(const point& origin, const qd::size& sz) - : m_origin(origin), m_size(sz) -{ - -} - -graphite::qd::rect::rect(int16_t x, int16_t y, int16_t width, int16_t height) - : m_origin(x, y), m_size(width, height) -{ - -} - -auto graphite::qd::rect::zero() -> qd::rect -{ - return { 0, 0, 0, 0 }; -} - -auto graphite::qd::rect::x() const -> int16_t -{ - return m_origin.x(); -} - -auto graphite::qd::rect::y() const -> int16_t -{ - return m_origin.y(); -} - -auto graphite::qd::rect::width() const -> int16_t -{ - return m_size.width(); -} - -auto graphite::qd::rect::height() const -> int16_t -{ - return m_size.height(); -} - -auto graphite::qd::rect::origin() const -> qd::point -{ - return m_origin; -} - -auto graphite::qd::rect::size() const -> qd::size -{ - return m_size; -} - -auto graphite::qd::rect::set_x(const int16_t& x) -> void -{ - m_origin.set_x(x); -} - -auto graphite::qd::rect::set_y(const int16_t& y) -> void -{ - m_origin.set_y(y); -} - -auto graphite::qd::rect::set_width(const int16_t& width) -> void -{ - m_size.set_width(width); -} - -auto graphite::qd::rect::set_height(const int16_t& height) -> void -{ - m_size.set_height(height); -} - -auto graphite::qd::rect::set_origin(const qd::point& origin) -> void -{ - m_origin = origin; -} - -auto graphite::qd::rect::set_size(const struct qd::size& size) -> void -{ - m_size = size; -} - -auto graphite::qd::rect::read(graphite::data::reader& reader, enum coding_type type) -> qd::rect -{ - switch (type) { - case coding_type::qd: { - auto origin = qd::point::read(reader, point::qd); - auto opposite = qd::point::read(reader, point::qd); - return qd::rect(origin, qd::size(opposite.x() - origin.x(), opposite.y() - origin.y())); - } - case coding_type::pict: { - auto origin = point::read(reader, point::pict); - auto sz = size::read(reader, size::pict); - return qd::rect(origin, sz); - } - default: { - return qd::rect({0, 0}, {0, 0}); - } - } -} - -auto graphite::qd::rect::write(graphite::data::writer& writer, enum coding_type type) -> void -{ - switch (type) { - case coding_type::qd: { - m_origin.write(writer, point::qd); - m_size.write(writer, size::qd); - break; - } - case coding_type::pict: { - m_origin.write(writer, point::pict); - m_size.write(writer, size::pict); - break; - } - } -} - -// MARK: - Fixed Rect - -graphite::qd::fixed_rect::fixed_rect(const qd::fixed_point& origin, const qd::fixed_size& size) - : m_origin(origin), m_size(size) -{ - -} - -graphite::qd::fixed_rect::fixed_rect(double x, double y, double width, double height) - : m_origin(x, y), m_size(width, height) -{ - -} - -auto graphite::qd::fixed_rect::zero() -> qd::fixed_rect -{ - return { 0, 0, 0, 0 }; -} - -auto graphite::qd::fixed_rect::x() const -> double -{ - return m_origin.x(); -} - -auto graphite::qd::fixed_rect::y() const -> double -{ - return m_origin.y(); -} - -auto graphite::qd::fixed_rect::width() const -> double -{ - return m_size.width(); -} - -auto graphite::qd::fixed_rect::height() const -> double -{ - return m_size.height(); -} - -auto graphite::qd::fixed_rect::origin() const -> qd::fixed_point -{ - return m_origin; -} - -auto graphite::qd::fixed_rect::size() const -> qd::fixed_size -{ - return m_size; -} - -auto graphite::qd::fixed_rect::set_x(const double& x) -> void -{ - m_origin.set_x(x); -} - -auto graphite::qd::fixed_rect::set_y(const double& y) -> void -{ - m_origin.set_y(y); -} - -auto graphite::qd::fixed_rect::set_width(const double& width) -> void -{ - m_size.set_width(width); -} - -auto graphite::qd::fixed_rect::set_height(const double& height) -> void -{ - m_size.set_height(height); -} - -auto graphite::qd::fixed_rect::set_origin(const qd::fixed_point& origin) -> void -{ - m_origin = origin; -} - -auto graphite::qd::fixed_rect::set_size(const qd::fixed_size& size) -> void -{ - m_size = size; -} - -auto graphite::qd::fixed_rect::read(graphite::data::reader& reader, enum coding_type type) -> qd::fixed_rect -{ - switch (type) { - case coding_type::qd: { - auto origin = fixed_point::read(reader, fixed_point::qd); - auto opposite = fixed_point::read(reader, fixed_point::qd); - return qd::fixed_rect(origin, fixed_size(origin.x() + opposite.x(), origin.y() + opposite.y())); - } - default: { - return qd::fixed_rect({0, 0}, {0, 0}); - } - } -} - -auto graphite::qd::fixed_rect::write(graphite::data::writer& writer, enum coding_type type) -> void -{ - switch (type) { - case coding_type::qd: { - m_origin.write(writer, fixed_point::qd); - m_size.write(writer, fixed_size::qd); - break; - } - } -} diff --git a/libGraphite/quickdraw/geometry.hpp b/libGraphite/quickdraw/geometry.hpp deleted file mode 100644 index 56d434e..0000000 --- a/libGraphite/quickdraw/geometry.hpp +++ /dev/null @@ -1,203 +0,0 @@ -// -// Created by Tom Hancocks on 19/03/2020. -// - -#if !defined(GRAPHITE_GEOMETRY_HPP) -#define GRAPHITE_GEOMETRY_HPP - -#include "libGraphite/data/reader.hpp" -#include "libGraphite/data/writer.hpp" - -namespace graphite::qd { - - // MARK: - Point - - struct point - { - public: - enum coding_type { qd, pict }; - - private: - int16_t m_x { 0 }; - int16_t m_y { 0 }; - - public: - point() = default; - point(int16_t x, int16_t y); - point(const qd::point& p) = default; - - static auto zero() -> qd::point; - - [[nodiscard]] auto x() const -> int16_t; - [[nodiscard]] auto y() const -> int16_t; - - auto set_x(const int16_t& x) -> void; - auto set_y(const int16_t& y) -> void; - - static auto read(graphite::data::reader& reader, enum coding_type type = qd) -> qd::point; - auto write(graphite::data::writer& writer, coding_type type = qd) const -> void; - - }; - - // MARK: - Fixed Point - - struct fixed_point - { - public: - enum coding_type { qd }; - - private: - double m_x { 0 }; - double m_y { 0 }; - - public: - fixed_point() = default; - fixed_point(double x, double y); - fixed_point(const fixed_point& p) = default; - - static auto zero() -> fixed_point; - - [[nodiscard]] auto x() const -> double; - [[nodiscard]] auto y() const -> double; - - auto set_x(const double& x) -> void; - auto set_y(const double& y) -> void; - - static auto read(graphite::data::reader& reader, enum coding_type type = qd) -> fixed_point; - auto write(graphite::data::writer& writer, enum coding_type type = qd) const -> void; - - }; - - // MARK: - Size - - struct size - { - public: - enum coding_type { qd, pict }; - - private: - int16_t m_width { 0 }; - int16_t m_height { 0 }; - - public: - size() = default; - size(int16_t width, int16_t height); - size(const size& s) = default; - - static auto zero() -> size; - - [[nodiscard]] auto width() const -> int16_t; - [[nodiscard]] auto height() const -> int16_t; - - auto set_width(const int16_t& width) -> void; - auto set_height(const int16_t& height) -> void; - - static auto read(graphite::data::reader& reader, enum coding_type type = qd) -> size; - auto write(graphite::data::writer& writer, enum coding_type type = qd) const -> void; - }; - - // MARK: - Fixed Size - - struct fixed_size - { - public: - enum coding_type { qd }; - - private: - double m_width { 0 }; - double m_height { 0 }; - - public: - fixed_size() = default; - fixed_size(double width, double height); - fixed_size(const fixed_size& s) = default; - - static auto zero() -> fixed_size; - - [[nodiscard]] auto width() const -> double; - [[nodiscard]] auto height() const -> double; - - auto set_width(const double& width) -> void; - auto set_height(const double& height) -> void; - - static auto read(graphite::data::reader& reader, enum coding_type type = qd) -> fixed_size; - auto write(graphite::data::writer& writer, enum coding_type type = qd) const -> void; - }; - - // MARK: - Rect - - struct rect - { - public: - enum coding_type { qd, pict }; - - private: - qd::point m_origin { point::zero() }; - qd::size m_size { size::zero() }; - - public: - rect() = default; - rect(const qd::point& origin, const qd::size& size); - rect(int16_t x, int16_t y, int16_t width, int16_t height); - rect(const qd::rect& r) = default; - - static auto zero() -> qd::rect; - - [[nodiscard]] auto x() const -> int16_t; - [[nodiscard]] auto y() const -> int16_t; - [[nodiscard]] auto width() const -> int16_t; - [[nodiscard]] auto height() const -> int16_t; - [[nodiscard]] auto origin() const -> qd::point; - [[nodiscard]] auto size() const -> qd::size; - - auto set_x(const int16_t& x) -> void; - auto set_y(const int16_t& y) -> void; - auto set_width(const int16_t& width) -> void; - auto set_height(const int16_t& height) -> void; - auto set_origin(const qd::point& origin) -> void; - auto set_size(const struct qd::size& size) -> void; - - static auto read(graphite::data::reader& reader, enum coding_type type = qd) -> qd::rect; - auto write(graphite::data::writer& writer, enum coding_type type = qd) -> void; - }; - - // MARK: - Fixed Rect - - struct fixed_rect - { - public: - enum coding_type { qd }; - - private: - qd::fixed_point m_origin { fixed_point::zero() }; - qd::fixed_size m_size { fixed_size::zero() }; - - public: - fixed_rect() = default; - fixed_rect(const qd::fixed_point& origin, const qd::fixed_size& size); - fixed_rect(double x, double y, double width, double height); - fixed_rect(const qd::fixed_rect& r) = default; - - static auto zero() -> qd::fixed_rect; - - [[nodiscard]] auto x() const -> double; - [[nodiscard]] auto y() const -> double; - [[nodiscard]] auto width() const -> double; - [[nodiscard]] auto height() const -> double; - [[nodiscard]] auto origin() const -> qd::fixed_point; - [[nodiscard]] auto size() const -> qd::fixed_size; - - auto set_x(const double& x) -> void; - auto set_y(const double& y) -> void; - auto set_width(const double& width) -> void; - auto set_height(const double& height) -> void; - auto set_origin(const qd::fixed_point& origin) -> void; - auto set_size(const qd::fixed_size& size) -> void; - - static auto read(graphite::data::reader& reader, enum coding_type type = qd) -> qd::fixed_rect; - auto write(graphite::data::writer& writer, enum coding_type type = qd) -> void; - }; - -} - -#endif //GRAPHITE_GEOMETRY_HPP diff --git a/libGraphite/quickdraw/internal/color.cpp b/libGraphite/quickdraw/internal/color.cpp deleted file mode 100644 index e74d5e0..0000000 --- a/libGraphite/quickdraw/internal/color.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// -// Created by Tom Hancocks on 20/02/2020. -// - -#include "libGraphite/quickdraw/internal/color.hpp" - -// MARK: - Constructor - -graphite::qd::color::color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) - : m_red(red), m_blue(blue), m_green(green), m_alpha(alpha) -{ - -} - -// MARK: - Accessors - -auto graphite::qd::color::red_component() const -> uint8_t -{ - return m_red; -} - -auto graphite::qd::color::green_component() const -> uint8_t -{ - return m_green; -} - -auto graphite::qd::color::blue_component() const -> uint8_t -{ - return m_blue; -} - -auto graphite::qd::color::alpha_component() const -> uint8_t -{ - return m_alpha; -} - - -// MARK: - Quick Colors - -auto graphite::qd::color::black() -> graphite::qd::color -{ - return graphite::qd::color(0, 0, 0); -} - -auto graphite::qd::color::white() -> graphite::qd::color -{ - return graphite::qd::color(255, 255, 255); -} - -auto graphite::qd::color::red() -> graphite::qd::color -{ - return graphite::qd::color(255, 0, 0); -} - -auto graphite::qd::color::green() -> graphite::qd::color -{ - return graphite::qd::color(0, 255, 0); -} - -auto graphite::qd::color::blue() -> graphite::qd::color -{ - return graphite::qd::color(0, 0, 255); -} - -auto graphite::qd::color::purple() -> graphite::qd::color -{ - return graphite::qd::color(150, 0, 255); -} - -auto graphite::qd::color::orange() -> graphite::qd::color -{ - return graphite::qd::color(255, 150, 0); -} - -auto graphite::qd::color::yellow() -> graphite::qd::color -{ - return graphite::qd::color(255, 255, 0); -} - -auto graphite::qd::color::lightGrey() -> graphite::qd::color -{ - return graphite::qd::color(200, 200, 200); -} - -auto graphite::qd::color::darkGrey() -> graphite::qd::color -{ - return graphite::qd::color(100, 100, 100); -} - -auto graphite::qd::color::clear() -> graphite::qd::color -{ - return graphite::qd::color(0, 0, 0, 0); -} - -// MARK: - Operators - -auto graphite::qd::color::operator==(const graphite::qd::color &rhs) const -> bool -{ - return (m_red == rhs.m_red) && (m_green == rhs.m_green) && (m_blue == rhs.m_blue) && (m_alpha == rhs.m_alpha); -} diff --git a/libGraphite/quickdraw/internal/color.hpp b/libGraphite/quickdraw/internal/color.hpp deleted file mode 100644 index 78e88e8..0000000 --- a/libGraphite/quickdraw/internal/color.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// -// Created by Tom Hancocks on 20/02/2020. -// - -#ifndef GRAPHITE_COLOR_HPP -#define GRAPHITE_COLOR_HPP - -#include - -namespace graphite::qd { - - /** - * A structure representing a 4-component color, as used by the internal QuickDraw surface. - */ - struct color - { - private: - uint8_t m_red; - uint8_t m_green; - uint8_t m_blue; - uint8_t m_alpha; - - public: - - /** - * Construct a new color using the specified color component values. - * @param red The red component of the color - * @param green The green component of the color - * @param blue The blue component of the color - * @param alpha The alpha component of the color - */ - color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255); - - [[nodiscard]] auto red_component() const -> uint8_t; - [[nodiscard]] auto green_component() const -> uint8_t; - [[nodiscard]] auto blue_component() const -> uint8_t; - [[nodiscard]] auto alpha_component() const -> uint8_t; - - static auto black() -> color; - static auto white() -> color; - static auto red() -> color; - static auto green() -> color; - static auto blue() -> color; - static auto purple() -> color; - static auto orange() -> color; - static auto yellow() -> color; - static auto lightGrey() -> color; - static auto darkGrey() -> color; - static auto clear() -> color; - - auto operator== (const color& rhs) const -> bool; - }; - -} - - -#endif //GRAPHITE_COLOR_HPP diff --git a/libGraphite/quickdraw/internal/packbits.cpp b/libGraphite/quickdraw/internal/packbits.cpp deleted file mode 100644 index 0e7cfb4..0000000 --- a/libGraphite/quickdraw/internal/packbits.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// -// Created by Tom Hancocks on 20/02/2020. -// - -#include -#include "libGraphite/quickdraw/internal/packbits.hpp" -#include - -auto graphite::qd::packbits::decode(std::vector &out_data, const std::vector& pack_data, std::size_t value_size) -> std::size_t -{ - std::size_t pos = 0; - - while (pos < pack_data.size()) { - auto count = pack_data[pos++]; - if (count >= 0 && count < 128) { - uint16_t run = (1 + count) * value_size; - if ((pos + run) > pack_data.size()) { - throw std::runtime_error("Unable to decode packbits."); - } - out_data.insert(out_data.end(), std::make_move_iterator(pack_data.begin() + pos), std::make_move_iterator(pack_data.begin() + pos + run)); - pos += run; - } - else if (count >= 128) { - uint8_t run = 256 - count + 1; - for (uint8_t i = 0; i < run; ++i) { - for (uint8_t j = 0; j < value_size; ++j) { - out_data.push_back(pack_data[pos + j]); - } - } - pos += value_size; - } - else { - // No-op - } - } - - return out_data.size(); -} - -auto graphite::qd::packbits::encode(const std::vector& scanline_bytes) -> std::vector -{ - std::vector result; - std::vector buffer(128); - - int offset = 0; - const unsigned long max = scanline_bytes.size() - 1; - const unsigned long max_minus_1 = max - 1; - - while (offset <= max) { - // Compressed run - int run = 1; - uint8_t replicate = scanline_bytes[offset]; - while (run < 127 && offset < max && scanline_bytes[offset] == scanline_bytes[offset + 1]) { - offset++; - run++; - } - - if (run > 1) { - offset++; - result.emplace_back(static_cast(-(run - 1))); - result.emplace_back(replicate); - } - - // Literal run - run = 0; - while ((run < 128 && ((offset < max && scanline_bytes[offset] != scanline_bytes[offset + 1]) - || (offset < max_minus_1 && scanline_bytes[offset] != scanline_bytes[offset + 2])))) { - buffer[run++] = scanline_bytes[offset++]; - } - - if (offset == max && run > 0 && run < 128) { - buffer[run++] = scanline_bytes[offset++]; - } - - if (run > 0) { - result.emplace_back(run - 1); - result.insert(result.end(), buffer.begin(), buffer.begin() + run); - buffer = std::vector(128); - } - - if (offset == max && (run <= 0 || run >= 128)) { - result.emplace_back(0); - result.emplace_back(scanline_bytes[offset++]); - } - } - - return result; -} diff --git a/libGraphite/quickdraw/internal/packbits.hpp b/libGraphite/quickdraw/internal/packbits.hpp deleted file mode 100644 index f2d5e3c..0000000 --- a/libGraphite/quickdraw/internal/packbits.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// -// Created by Tom Hancocks on 20/02/2020. -// - -#if !defined(GRAPHITE_PACKBITS_HPP) -#define GRAPHITE_PACKBITS_HPP - -#include -#include -#include - -namespace graphite::qd { - - struct packbits - { - public: - static auto decode(std::vector &out_data, const std::vector& pack_data, std::size_t value_size) -> std::size_t; - static auto encode(const std::vector& scanline_bytes) -> std::vector; - }; - -} - -#endif //GRAPHITE_PACKBITS_HPP diff --git a/libGraphite/quickdraw/internal/surface.cpp b/libGraphite/quickdraw/internal/surface.cpp deleted file mode 100644 index 78b1db4..0000000 --- a/libGraphite/quickdraw/internal/surface.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// -// Created by Tom Hancocks on 20/02/2020. -// - -#include "libGraphite/quickdraw/internal/surface.hpp" - -// MARK: - Constructor - -graphite::qd::surface::surface(int width, int height) - : m_width(width), m_height(height), m_data(width * height, graphite::qd::color::clear()) -{ -} - -graphite::qd::surface::surface(int width, int height, std::vector rgb) -: m_width(width), m_height(height), m_data(std::move(rgb)) -{ -} - -// MARK: - Surface Access - -auto graphite::qd::surface::raw() const -> std::vector -{ - auto out = std::vector(); - for (const auto& i : m_data) { - uint32_t color = (i.alpha_component() << 24) - | i.red_component() - | (i.green_component() << 8UL) - | (i.blue_component() << 16UL); - out.push_back(color); - } - return out; -} - -auto graphite::qd::surface::size() const -> graphite::qd::size -{ - return graphite::qd::size(m_width, m_height); -} - -auto graphite::qd::surface::at(int x, int y) const -> graphite::qd::color -{ - return m_data[(y * m_width) + x]; -} - -auto graphite::qd::surface::set(int x, int y, graphite::qd::color color) -> void -{ - if (x >= m_width || y >= m_height) { - throw std::runtime_error("Attempted to set pixel beyond bounds of surface."); - } - m_data[(y * m_width) + x] = color; -} - -auto graphite::qd::surface::set(int offset, graphite::qd::color color) -> void -{ - if (offset >= m_data.size()) { - throw std::runtime_error("Attempted to set pixel beyond bounds of surface."); - } - m_data[offset] = color; -} - -// MARK: - Drawing Operations - -auto graphite::qd::surface::draw_line(int x0, int y0, int x1, int y1, graphite::qd::color color) -> void -{ - int delta_x = abs(x1 - x0); - int delta_y = abs(y1 - y0); - int sx = (x0 < x1) ? 1 : -1; - int sy = (y0 < y1) ? 1 : -1; - int err = delta_x - delta_y; - - for (;;) { - if (x0 >= 0 && y0 >= 0 && x0 < m_width && y0 < m_height) { - set(x0, y0, color); - } - if (x0 == x1 & y0 == y1) { - break; - } - int e2 = 2 * err; - if (e2 > -delta_y) { - err -= delta_y; - x0 += sx; - } - if (e2 < delta_x) { - err += delta_x; - y0 += sy; - } - } -} diff --git a/libGraphite/quickdraw/internal/surface.hpp b/libGraphite/quickdraw/internal/surface.hpp deleted file mode 100644 index eac09cf..0000000 --- a/libGraphite/quickdraw/internal/surface.hpp +++ /dev/null @@ -1,98 +0,0 @@ -// -// Created by Tom Hancocks on 20/02/2020. -// - -#if !defined(GRAPHITE_QD_SURFACE) -#define GRAPHITE_QD_SURFACE - -#include -#include -#include -#include "libGraphite/quickdraw/internal/color.hpp" - -namespace graphite::qd -{ - - /** - * The `graphite::qd::surface` class is an internal component of Graphite's QuickDraw implementation - * and the component that provides image drawing functionality. This component should not be interacted - * with directly, and should always go through the "QuickDraw" methods. - */ - class surface - { - private: - int m_width; - int m_height; - std::vector m_data; - - public: - - /** - * Construct a new surface with the specified dimensions. - * @param width The width of the surface in pixels. - * @param height The height of the surface in pixels. - */ - surface(int width, int height); - - /** - * Construct a new surface with the specified dimensions and rgb data. - * @param width The width of the surface in pixels. - * @param height The height of the surface in pixels. - * @param rgb The rgb data of the surface. - */ - surface(int width, int height, std::vector rgb); - - /** - * Export the raw surface data. - */ - [[nodiscard]] auto raw() const -> std::vector; - - /** - * Returns the size of the surface - */ - [[nodiscard]] auto size() const -> qd::size; - - /** - * Returns the color at the specified coordinate within the surface. - * @param x The x position in the surface - * @param y The y position in the surface - * @return The color - * - * @note This method of getting colors is _slow_. Use only for single point lookup. - */ - [[nodiscard]] auto at(int x, int y) const -> graphite::qd::color; - - /** - * Set the color at the specified coordinate within the surface. - * @param x The x position in the surface - * @param y The y position in the surface - * @param color The color - * - * @note This method of setting colors is _slow_. Use only for single point setting. - */ - auto set(int x, int y, graphite::qd::color color) -> void; - - /** - * Set the color at the specified coordinate within the surface. - * @param offset The absolute offset in the surface data - * @param color The color - * - * @note This method of setting colors is _slow_. Use only for single point setting. - */ - auto set(int offset, graphite::qd::color color) -> void; - - /** - * Draw a line from point x0,y0 to x1,y1 using the color specified. - * @param x0 - * @param y0 - * @param x1 - * @param y1 - * @param color - */ - auto draw_line(int x0, int y0, int x1, int y1, graphite::qd::color color) -> void; - - }; - -} - -#endif //GRAPHITE_SURFACE_H diff --git a/libGraphite/quickdraw/pict.cpp b/libGraphite/quickdraw/pict.cpp deleted file mode 100644 index ea558e7..0000000 --- a/libGraphite/quickdraw/pict.cpp +++ /dev/null @@ -1,619 +0,0 @@ -// -// Created by Tom Hancocks on 20/02/2020. -// - -#include -#include -#include "libGraphite/quickdraw/pict.hpp" -#include "libGraphite/quickdraw/internal/packbits.hpp" -#include "libGraphite/quickdraw/clut.hpp" - -// MARK: - Constants - -#define kPICT_V1_MAGIC 0x1101 -#define kPICT_V2_MAGIC 0x001102ff - -// MARK: - Constructors - -graphite::qd::pict::pict(std::shared_ptr data, int64_t id, std::string name) - : m_surface(nullptr), m_frame(0, 0, 100, 100), m_id(id), m_name(std::move(name)) -{ - // Setup a reader for the PICT data, and then parse it. - data::reader pict_reader(std::move(data)); - parse(pict_reader); -} - -graphite::qd::pict::pict(std::shared_ptr surface) - : m_surface(std::move(surface)), m_frame(qd::point::zero(), m_surface->size()), m_id(0), m_name("Picture") -{ - -} - -auto graphite::qd::pict::load_resource(int64_t id) -> std::shared_ptr -{ - if (auto pict_res = graphite::rsrc::manager::shared_manager().find("PICT", id).lock()) { - return std::make_shared(pict_res->data(), id, pict_res->name()); - } - return nullptr; -} - -auto graphite::qd::pict::from_surface(std::shared_ptr surface) -> std::shared_ptr -{ - return std::make_shared(surface); -} - -// MARK: - Accessors - -auto graphite::qd::pict::image_surface() const -> std::weak_ptr -{ - return m_surface; -} - -// MARK: - Helper Functions - -static inline auto read_bytes(graphite::data::reader& pict_reader, std::size_t size) -> std::vector -{ - auto out = std::vector(size); - auto in = pict_reader.read_bytes(size); - for (auto i = 0; i < in.size(); ++i) { - out[i] = in[i]; - } - return out; -} - -// MARK: - Parsing / Reading - -auto graphite::qd::pict::read_region(graphite::data::reader& pict_reader) const -> graphite::qd::rect -{ - auto size = pict_reader.read_short(); - auto rect = graphite::qd::rect::read(pict_reader, qd::rect::qd); - - rect.set_x(rect.x() / m_x_ratio); - rect.set_y(rect.y() / m_y_ratio); - rect.set_width(rect.width() / m_x_ratio); - rect.set_height(rect.height() / m_y_ratio); - - pict_reader.move(size - 10); - - return rect; -} - -auto graphite::qd::pict::read_long_comment(graphite::data::reader& pict_reader) -> void -{ - pict_reader.move(2); - auto length = pict_reader.read_short(); - pict_reader.move(length); -} - -auto graphite::qd::pict::read_indirect_bits_rect(graphite::data::reader& pict_reader, bool packed, bool region) -> void -{ - qd::pixmap pm; - qd::clut color_table; - // Determine if we're dealing a PixMap or an old style BitMap - bool is_pixmap = pict_reader.read_short(0, data::reader::peek) & 0x8000; - if (is_pixmap) { - // The pixmap base address is omitted here, step back when reading - pm = qd::pixmap(pict_reader.read_data(qd::pixmap::length, -sizeof(uint32_t))); - // Color Table - color_table = qd::clut(pict_reader); - } - else { - // Old style bitmap - pm.set_pack_type(1); - pm.set_cmp_count(1); - pm.set_cmp_size(1); - pm.set_row_bytes(pict_reader.read_short()); - pm.set_bounds(qd::rect::read(pict_reader, qd::rect::qd)); - // Monochrome color table - color_table.set(qd::color(255, 255, 255)); - color_table.set(qd::color(0, 0, 0)); - } - - // Read the source and destination bounds - auto source_rect = qd::rect::read(pict_reader, qd::rect::qd); - auto destination_rect = qd::rect::read(pict_reader, qd::rect::qd); - - auto transfer_mode = pict_reader.read_short(); - - if (region) { - read_region(pict_reader); - } - - // Setup pixel buffer for raw values - std::vector raw; - auto row_bytes = pm.row_bytes(); - auto width = pm.bounds().width(); - auto height = pm.bounds().height(); - - if (packed) { - uint16_t packed_bytes_count = 0; - for (auto scanline = 0; scanline < height; ++scanline) { - if (row_bytes > 250) { - packed_bytes_count = pict_reader.read_short(); - } - else { - packed_bytes_count = pict_reader.read_byte(); - } - - auto packed_data = read_bytes(pict_reader, packed_bytes_count); - qd::packbits::decode(raw, packed_data, sizeof(uint8_t)); - } - } - else { - raw = read_bytes(pict_reader, row_bytes * height); - } - - destination_rect.set_x(destination_rect.x() - m_frame.origin().x()); - destination_rect.set_y(destination_rect.y() - m_frame.origin().y()); - pm.build_surface(m_surface, raw, color_table, destination_rect); - m_size += width * height; -} - -auto graphite::qd::pict::read_direct_bits_rect(graphite::data::reader &pict_reader, bool region) -> void -{ - graphite::qd::pixmap pm = graphite::qd::pixmap(pict_reader.read_data(qd::pixmap::length)); - auto pack_type = pm.pack_type(); - auto cmp_count = pm.cmp_count(); - auto color_model = static_cast(pm.pixel_format()); - auto row_bytes = pm.row_bytes(); - auto bounds = pm.bounds(); - - // Read the source and destination bounds - auto source_rect = qd::rect::read(pict_reader, qd::rect::qd); - auto destination_rect = qd::rect::read(pict_reader, qd::rect::qd); - - auto transfer_mode = pict_reader.read_short(); - - if (region) { - read_region(pict_reader); - } - - // Verify the type of PixMap. We can only accept certain types for the time being, until - // support for decoding/rendering other types is added. - std::vector raw; - switch (pack_type) { - case 1: - case 2: - case 3: { - raw.reserve(row_bytes); - break; - } - case 4: { - raw.reserve(cmp_count * row_bytes / 4); - break; - } - default: { - throw std::runtime_error("Unsupported pack type " + std::to_string(pack_type) + " encountered in PICT: " + std::to_string(m_id) + ", " + m_name); - } - } - - // Allocate a private memory buffer before going to the surface. - std::vector px_short_buffer; - std::vector px_long_buffer; - uint32_t px_buffer_offset = 0; - uint16_t packed_bytes_count = 0; - uint32_t height = source_rect.height(); - uint32_t width = source_rect.width(); - uint32_t bounds_width = bounds.width(); - uint32_t source_length = width * height; - - if (pack_type == 3) { - px_short_buffer = std::vector(source_length); - } - else { - px_long_buffer = std::vector(source_length); - } - - auto packing_enabled = ((pack_type == 3 ? 2 : 1) + (row_bytes > 250 ? 2 : 1)) <= bounds_width; - - for (uint32_t scanline = 0; scanline < height; ++scanline) { - raw.clear(); - - if (pack_type > 2 && packing_enabled) { - if (row_bytes > 250) { - packed_bytes_count = pict_reader.read_short(); - } - else { - packed_bytes_count = pict_reader.read_byte(); - } - - // Create a temporary buffer to read the packed data into, on the stack. - auto packed_data = read_bytes(pict_reader, packed_bytes_count); - qd::packbits::decode(raw, packed_data, pack_type == 3 ? sizeof(uint16_t) : sizeof(uint8_t)); - } - else { - raw = read_bytes(pict_reader, row_bytes); - } - - if (pack_type == 3) { - for (uint32_t x = 0; x < width; ++x) { - px_short_buffer[px_buffer_offset + x] = (0xFF & raw[2 * x + 1]) | ((0xFF & raw[2 * x]) << 8); - } - } - else { - if (cmp_count == 3) { - // RGB Formatted Data - for (uint32_t x = 0; x < width; x++) { - px_long_buffer[px_buffer_offset + x] = - 0xFF000000 - | ((raw[x] & 0xFF) << 16) - | ((raw[bounds_width + x] & 0xFF) << 8) - | (raw[2 * bounds_width + x] & 0xFF); - } - } - else if (cmp_count == 4) { - // ARGB Formatted Data - for (uint32_t x = 0; x < width; x++) { - px_long_buffer[px_buffer_offset + x] = - ((raw[x] & 0xFF) << 24) - | ((raw[bounds_width + x] & 0xFF) << 16) - | ((raw[2 * bounds_width + x] & 0xFF) << 8) - | (raw[3 * bounds_width + x] & 0xFF); - } - } - } - - px_buffer_offset += width; - } - - if (pack_type == 3) { - for (auto v : px_short_buffer) { - graphite::qd::color color(static_cast(((v & 0x7c00) >> 10) * 0xFF / 0x1F), - static_cast(((v & 0x03e0) >> 5) * 0xFF / 0x1F), - static_cast((v & 0x001f) * 0xFF / 0x1F)); - m_surface->set(m_size++, color); - } - } - else { - for (auto v : px_long_buffer) { - graphite::qd::color color(static_cast((v & 0xFF0000) >> 16), - static_cast((v & 0xFF00) >> 8), - static_cast(v & 0xFF), - static_cast((v & 0xFF000000) >> 24)); - m_surface->set(m_size++, color); - } - } -} - -auto graphite::qd::pict::read_compressed_quicktime(graphite::data::reader &pict_reader) -> void -{ - auto length = pict_reader.read_long(); - pict_reader.move(38); - auto matte_size = pict_reader.read_long(); - auto matte_rect = qd::rect::read(pict_reader, qd::rect::qd); - pict_reader.move(2); - auto source_rect = qd::rect::read(pict_reader, qd::rect::qd); - pict_reader.move(4); - auto mask_size = pict_reader.read_long(); - - if (matte_size > 0) { - read_image_description(pict_reader); - } - - if (mask_size > 0) { - pict_reader.move(mask_size); - } - - read_image_description(pict_reader); -} - -auto graphite::qd::pict::read_uncompressed_quicktime(graphite::data::reader &pict_reader) -> void -{ - auto length = pict_reader.read_long(); - pict_reader.move(38); - auto matte_size = pict_reader.read_long(); - auto matte_rect = qd::rect::read(pict_reader, qd::rect::qd); - - if (matte_size > 0) { - read_image_description(pict_reader); - } -} - -auto graphite::qd::pict::read_image_description(graphite::data::reader &pict_reader) -> void -{ - auto length = pict_reader.read_long(); - if (length != 86) { - throw std::runtime_error("Invalid QuickTime image description in PICT: " + std::to_string(m_id) + ", " + m_name); - } - auto compressor = pict_reader.read_long(); - pict_reader.move(24); - auto width = pict_reader.read_short(); - auto height = pict_reader.read_short(); - pict_reader.move(8); - auto data_size = pict_reader.read_long(); - pict_reader.move(34); - auto depth = pict_reader.read_short(); - if (depth > 32) { - depth -= 32; // grayscale - } - auto clut = pict_reader.read_signed_short(); - - if (compressor == 'rle ') { - // rle is often garbage or redundant, skip over it and hope we find other image data later. - pict_reader.move(data_size); - return; - } - - switch (compressor) { - default: - std::string comp; - comp.push_back(compressor >> 24); - comp.push_back(compressor >> 16); - comp.push_back(compressor >> 8); - comp.push_back(compressor); - throw std::runtime_error("Unsupported QuickTime compressor '" + comp + "' at offset " + std::to_string(pict_reader.position()) - + " in PICT: " + std::to_string(m_id) + ", " + m_name); - } -} - -auto graphite::qd::pict::parse(graphite::data::reader& pict_reader) -> void -{ - pict_reader.move(2); - - m_frame = qd::rect::read(pict_reader, qd::rect::qd); - bool v1 = false; - - // Check which version of PICT we're dealing with - if (pict_reader.read_short(0, data::reader::peek) == kPICT_V1_MAGIC) { - pict_reader.move(2); - v1 = true; - } else { - if (pict_reader.read_long() != kPICT_V2_MAGIC) { - throw std::runtime_error("Invalid PICT resource. Incorrect header: " + std::to_string(m_id) + ", " + m_name); - } - - // The very first thing we should find is an extended header opcode. Read this - // outside of the main opcode loop as it should only appear once. - if (static_cast(pict_reader.read_short()) != opcode::ext_header) { - throw std::runtime_error("Expected to find PICT Extended Header."); - } - - if ((pict_reader.read_long() >> 16) != 0xFFFE) { - // Standard header variant - auto rect = qd::fixed_rect::read(pict_reader); - m_x_ratio = m_frame.width() / rect.width(); - m_y_ratio = m_frame.height() / rect.height(); - } - else { - // Extended header variant - pict_reader.move(sizeof(uint32_t) * 2); - auto rect = qd::rect::read(pict_reader, qd::rect::qd); - m_x_ratio = static_cast(m_frame.width()) / rect.width(); - m_y_ratio = static_cast(m_frame.height()) / rect.height(); - // This isn't strictly correct but it allows us to decode some images which - // would otherwise fail due to mismatched frame sizes. QuickDraw would normally - // scale such images down to fit the frame but here we just expand the frame. - m_frame.set_size(rect.size()); - } - - if (m_x_ratio <= 0 || m_y_ratio <= 0) { - throw std::runtime_error("Invalid PICT resource. Content aspect ratio is not valid: " + std::to_string(m_id) + ", " + m_name); - } - - pict_reader.move(4); - } - - // Begin parsing PICT opcodes. - qd::rect clip_rect(0, 0, 0, 0); - - m_size = 0; - m_surface = std::make_shared(m_frame.width(), m_frame.height()); - bool has_bits = false; - - opcode op; - while (!pict_reader.eof()) { - if (v1) { - op = static_cast(pict_reader.read_byte()); - } else { - // Make sure we are correctly aligned. - pict_reader.move(pict_reader.position() % sizeof(uint16_t)); - op = static_cast(pict_reader.read_short()); - } - - if (op == opcode::eof) { - break; - } - - switch (op) { - case opcode::clip_region: { - clip_rect = read_region(pict_reader); - break; - } - case opcode::origin: { - auto origin = graphite::qd::point::read(pict_reader, qd::point::pict); - m_frame.set_origin(origin); - break; - } - case opcode::bits_rect: { - read_indirect_bits_rect(pict_reader, false, false); - has_bits = true; - break; - } - case opcode::bits_region: { - read_indirect_bits_rect(pict_reader, false, true); - has_bits = true; - break; - } - case opcode::pack_bits_rect: { - read_indirect_bits_rect(pict_reader, true, false); - has_bits = true; - break; - } - case opcode::pack_bits_region: { - read_indirect_bits_rect(pict_reader, true, true); - has_bits = true; - break; - } - case opcode::direct_bits_rect: { - read_direct_bits_rect(pict_reader, false); - has_bits = true; - break; - } - case opcode::direct_bits_region: { - read_direct_bits_rect(pict_reader, true); - has_bits = true; - break; - } - case opcode::long_comment: { - read_long_comment(pict_reader); - break; - } - case opcode::short_comment: { - pict_reader.move(2); - break; - } - case opcode::rgb_fg_color: - case opcode::rgb_bg_color: - case opcode::hilite_color: - case opcode::op_color: { - pict_reader.move(6); - break; - } - case opcode::frame_region: - case opcode::paint_region: - case opcode::erase_region: - case opcode::invert_region: - case opcode::fill_region: { - read_region(pict_reader); - break; - } - case opcode::nop: - case opcode::eof: - case opcode::ext_header: - case opcode::hilite_mode: - case opcode::def_hilite: { - break; - } - case opcode::compressed_quicktime: { - read_compressed_quicktime(pict_reader); - // Compressed quicktime data is often followed by drawing routines telling you that you need - // quicktime to decode the image. We should skip these and just return after a successful decode. - return; - } - case opcode::uncompressed_quicktime: { - read_uncompressed_quicktime(pict_reader); - break; - } - default: { - throw std::runtime_error("Encountered an incompatible PICT: " + std::to_string(m_id) + ", " + m_name); - } - } - } - - // This is a safety check for QuickTime rle which is skipped over. If no other image data was found, throw an error. - if (!has_bits) { - throw std::runtime_error("Encountered an incompatible PICT: " + std::to_string(m_id) + ", " + m_name); - } -} - -// MARK: - Encoder / Writing - -auto graphite::qd::pict::encode(graphite::data::writer& pict_encoder) -> void -{ - encode_header(pict_encoder); - encode_def_hilite(pict_encoder); - encode_clip_region(pict_encoder); - encode_direct_bits_rect(pict_encoder); - - // Make sure we're word aligned and put out the end of picture opcode. - auto align_adjust = pict_encoder.position() % sizeof(uint16_t); - for (auto n = 0; n < align_adjust; ++n) { - pict_encoder.write_byte(0); - } - pict_encoder.write_short(static_cast(opcode::eof)); -} - -auto graphite::qd::pict::encode_header(graphite::data::writer& pict_encoder) -> void -{ - // Write the size as zero. This seems to be fine. - pict_encoder.write_short(0); - - // Set the image frame. - m_frame.write(pict_encoder, rect::qd); - - // We're only dealing with PICT version 2 currently. - pict_encoder.write_long(kPICT_V2_MAGIC); - pict_encoder.write_short(static_cast(opcode::ext_header)); - pict_encoder.write_long(0xFFFE0000); - - // Image resolution (72dpi) - pict_encoder.write_short(72); - pict_encoder.write_short(0); - pict_encoder.write_short(72); - pict_encoder.write_short(0); - - // Optimal source frame. (identical to the image frame) - m_frame.write(pict_encoder, rect::qd); - - // Reserved - pict_encoder.write_long(0); - - // HEADER ENDS HERE -} - -auto graphite::qd::pict::encode_def_hilite(graphite::data::writer& pict_encoder) -> void -{ - pict_encoder.write_short(static_cast(opcode::def_hilite)); -} - -auto graphite::qd::pict::encode_clip_region(graphite::data::writer& pict_encoder) -> void -{ - pict_encoder.write_short(static_cast(opcode::clip_region)); - pict_encoder.write_short(10); - m_frame.write(pict_encoder, rect::qd); -} - -auto graphite::qd::pict::encode_direct_bits_rect(graphite::data::writer& pict_encoder) -> void -{ - pict_encoder.write_short(static_cast(opcode::direct_bits_rect)); - - qd::pixmap pm(m_frame); - pm.write(pict_encoder); - - // Source and destination frames - identical to the image frame. - m_frame.write(pict_encoder, rect::qd); - m_frame.write(pict_encoder, rect::qd); - - // Specify the transfer mode. - pict_encoder.write_short(0); // Source Copy - - // Prepare to write out the actual image data. - std::vector scanline_bytes(m_frame.width() * pm.cmp_count()); - for (auto scanline = 0; scanline < m_frame.height(); ++scanline) { - - if (pm.cmp_count() == 3) { - for (auto x = 0; x < m_frame.width(); ++x) { - auto pixel = m_surface->at(x, scanline); - scanline_bytes[x] = pixel.red_component(); - scanline_bytes[x + m_frame.width()] = pixel.green_component(); - scanline_bytes[x + m_frame.width() * 2] = pixel.blue_component(); - } - } - else if (pm.cmp_count() == 4) { - for (auto x = 0; x < m_frame.width(); ++x) { - auto pixel = m_surface->at(x, scanline); - scanline_bytes[x] = pixel.alpha_component(); - scanline_bytes[x + m_frame.width()] = pixel.red_component(); - scanline_bytes[x + m_frame.width() * 2] = pixel.green_component(); - scanline_bytes[x + m_frame.width() * 3] = pixel.blue_component(); - } - } - - auto packed = packbits::encode(scanline_bytes); - if (pm.row_bytes() > 250) { - pict_encoder.write_short(packed.size()); - } - else { - pict_encoder.write_byte(packed.size()); - } - pict_encoder.write_bytes(packed); - } -} - -auto graphite::qd::pict::data() -> std::shared_ptr -{ - auto data = std::make_shared(); - graphite::data::writer writer(data); - encode(writer); - return data; -} diff --git a/libGraphite/quickdraw/pict.hpp b/libGraphite/quickdraw/pict.hpp deleted file mode 100644 index c7465fa..0000000 --- a/libGraphite/quickdraw/pict.hpp +++ /dev/null @@ -1,89 +0,0 @@ -// -// Created by Tom Hancocks on 20/02/2020. -// - -#if !defined(GRAPHITE_PICT_HPP) -#define GRAPHITE_PICT_HPP - -#include -#include "libGraphite/quickdraw/internal/surface.hpp" -#include "libGraphite/quickdraw/pixmap.hpp" -#include "libGraphite/data/reader.hpp" - -namespace graphite::qd { - - /** - * The `graphite::qd::pict` class represents a QuickDraw Picture. - */ - class pict - { - public: - enum opcode - { - nop = 0x0000, - clip_region = 0x0001, - origin = 0x000c, - bits_rect = 0x0090, - bits_region = 0x0091, - pack_bits_rect = 0x0098, - pack_bits_region = 0x0099, - direct_bits_rect = 0x009a, - direct_bits_region = 0x009b, - eof = 0x00ff, - rgb_fg_color = 0x001a, - rgb_bg_color = 0x001b, - hilite_mode = 0x001c, - hilite_color = 0x001d, - def_hilite = 0x001e, - op_color = 0x001f, - frame_region = 0x0080, - paint_region = 0x0081, - erase_region = 0x0082, - invert_region = 0x0083, - fill_region = 0x0084, - short_comment = 0x00a0, - long_comment = 0x00a1, - ext_header = 0x0c00, - compressed_quicktime = 0x8200, - uncompressed_quicktime = 0x8201, - }; - - private: - int64_t m_id {}; - std::string m_name; - std::shared_ptr m_surface; - graphite::qd::rect m_frame; - double m_x_ratio {}; - double m_y_ratio {}; - std::size_t m_size; - - auto parse(graphite::data::reader& pict_reader) -> void; - auto read_region(graphite::data::reader& pict_reader) const -> graphite::qd::rect; - auto read_long_comment(graphite::data::reader& pict_reader) -> void; - auto read_direct_bits_rect(graphite::data::reader& pict_reader, bool region) -> void; - auto read_indirect_bits_rect(graphite::data::reader& pict_reader, bool packed, bool region) -> void; - auto read_compressed_quicktime(graphite::data::reader & pict_reader) -> void; - auto read_uncompressed_quicktime(graphite::data::reader & pict_reader) -> void; - auto read_image_description(graphite::data::reader & pict_reader) -> void; - - auto encode(graphite::data::writer& pict_encoder) -> void; - auto encode_header(graphite::data::writer& pict_encoder) -> void; - auto encode_def_hilite(graphite::data::writer& pict_encoder) -> void; - auto encode_clip_region(graphite::data::writer& pict_encoder) -> void; - auto encode_direct_bits_rect(graphite::data::writer& pict_encoder) -> void; - - public: - explicit pict(std::shared_ptr data, int64_t id = 0, std::string name = ""); - explicit pict(std::shared_ptr surface); - - static auto load_resource(int64_t id) -> std::shared_ptr; - static auto from_surface(std::shared_ptr surface) -> std::shared_ptr; - - [[nodiscard]] auto image_surface() const -> std::weak_ptr; - - auto data() -> std::shared_ptr; - }; - -} - -#endif //GRAPHITE_PICT_HPP diff --git a/libGraphite/quickdraw/pixmap.cpp b/libGraphite/quickdraw/pixmap.cpp deleted file mode 100644 index d71ed5f..0000000 --- a/libGraphite/quickdraw/pixmap.cpp +++ /dev/null @@ -1,255 +0,0 @@ -// -// Created by Tom Hancocks on 19/03/2020. -// - -#include "libGraphite/quickdraw/pixmap.hpp" -#include "libGraphite/quickdraw/clut.hpp" -#include "libGraphite/quickdraw/internal/surface.hpp" -#include "libGraphite/data/reader.hpp" - -// MARK: - Constructors - -graphite::qd::pixmap::pixmap() - : m_bounds(0, 0, 100, 100) -{ - -} - -graphite::qd::pixmap::pixmap(qd::rect frame) - : m_base_address(0x000000ff), - m_row_bytes(frame.width() * 4), - m_bounds(frame), - m_pm_version(0), - m_pack_type(4), - m_pack_size(0), - m_h_res(0.001098632812), - m_v_res(0.001098632812), - m_pixel_type(16), - m_pixel_size(32), - m_cmp_count(3), - m_cmp_size(8), - m_pixel_format(unknown), - m_pm_table(0), - m_pm_extension(0) -{ -} - -graphite::qd::pixmap::pixmap(std::shared_ptr px_data) - : m_bounds(0, 0, 0, 0) -{ - // Setup a new data reader for the pixmap - data::reader px_reader(std::move(px_data)); - - // Read each of the member fields for the pixmap. - m_base_address = px_reader.read_long(); - m_row_bytes = static_cast(static_cast(px_reader.read_signed_short()) & 0x7FFFU); - m_bounds = graphite::qd::rect::read(px_reader, qd::rect::qd); - m_pm_version = px_reader.read_signed_short(); - m_pack_type = px_reader.read_signed_short(); - m_pack_size = px_reader.read_signed_long(); - m_h_res = static_cast(px_reader.read_signed_long() / static_cast(1U << 16UL)); - m_v_res = static_cast(px_reader.read_signed_long() / static_cast(1U << 16UL)); - m_pixel_type = px_reader.read_signed_short(); - m_pixel_size = px_reader.read_signed_short(); - m_cmp_count = px_reader.read_signed_short(); - m_cmp_size = px_reader.read_signed_short(); - m_pixel_format = static_cast(px_reader.read_long()); - m_pm_table = px_reader.read_long(); - m_pm_extension = px_reader.read_long(); -} - -// MARK: - Accessors - -auto graphite::qd::pixmap::bounds() const -> graphite::qd::rect -{ - return m_bounds; -} - -auto graphite::qd::pixmap::set_bounds(const graphite::qd::rect& rect) -> void -{ - m_bounds = rect; -} - -auto graphite::qd::pixmap::row_bytes() const -> int16_t -{ - return m_row_bytes; -} - -auto graphite::qd::pixmap::set_row_bytes(const int16_t& row_bytes) -> void -{ - m_row_bytes = row_bytes; -} - -auto graphite::qd::pixmap::pack_type() const -> int16_t -{ - return m_pack_type; -} - -auto graphite::qd::pixmap::set_pack_type(const int16_t& pack_type) -> void -{ - m_pack_type = pack_type; -} - -auto graphite::qd::pixmap::pack_size() const -> int16_t -{ - return m_pack_size; -} - -auto graphite::qd::pixmap::set_pack_size(const int16_t& pack_size) -> void -{ - m_pack_size = pack_size; -} - -auto graphite::qd::pixmap::pixel_type() const -> int16_t -{ - return m_pixel_type; -} - -auto graphite::qd::pixmap::set_pixel_type(const int16_t& pixel_type) -> void -{ - m_pixel_type = pixel_type; -} - -auto graphite::qd::pixmap::pixel_size() const -> int16_t -{ - return m_pixel_size; -} - -auto graphite::qd::pixmap::set_pixel_size(const int16_t& pixel_size) -> void -{ - m_pixel_size = pixel_size; -} - -auto graphite::qd::pixmap::cmp_count() const -> int16_t -{ - return m_cmp_count; -} - -auto graphite::qd::pixmap::set_cmp_count(const int16_t& cmp_count) -> void -{ - m_cmp_count = cmp_count; -} - -auto graphite::qd::pixmap::cmp_size() const -> int16_t -{ - return m_cmp_size; -} - -auto graphite::qd::pixmap::set_cmp_size(const int16_t& cmp_size) -> void -{ - m_cmp_size = cmp_size; -} - -auto graphite::qd::pixmap::pixel_format() const -> enum graphite::qd::pixel_format -{ - return m_pixel_format; -} - -auto graphite::qd::pixmap::pm_table() const -> uint32_t -{ - return m_pm_table; -} - -auto graphite::qd::pixmap::set_pm_table(const uint32_t& pm_table) -> void -{ - m_pm_table = pm_table; -} - -// MARK: - - -auto graphite::qd::pixmap::build_surface( - std::shared_ptr surface, - const std::vector& pixel_data, - const qd::clut& clut, - qd::rect destination) -> void -{ - if (pixel_data.size() < destination.height() * m_row_bytes) { - throw std::runtime_error("Insufficent data to build surface from pixmap."); - } - auto pixel_size = m_cmp_size * m_cmp_count; - - if (pixel_size == 8) { - for (auto y = 0; y < destination.height(); ++y) { - auto y_offset = (y * m_row_bytes); - for (auto x = 0; x < destination.width(); ++x) { - auto byte = pixel_data[y_offset + x]; - surface->set(destination.x() + x, destination.y() + y, clut.get(byte)); - } - } - } - else { - auto mod = 8 / pixel_size; - auto mask = (1 << pixel_size) - 1; - auto diff = 8 - pixel_size; - - for (auto y = 0; y < destination.height(); ++y) { - auto y_offset = (y * m_row_bytes); - for (auto x = 0; x < destination.width(); ++x) { - auto byte = pixel_data[y_offset + (x / mod)]; - auto byte_offset = diff - ((x % mod) * pixel_size); - auto v = (byte >> byte_offset) & mask; - surface->set(destination.x() + x, destination.y() + y, clut.get(v)); - } - } - } -} - -// MARK: - - -auto graphite::qd::pixmap::build_pixel_data(const std::vector& color_values, uint16_t pixel_size) -> std::shared_ptr -{ - graphite::data::writer pmap_data(std::make_shared()); - m_pixel_size = m_cmp_size = pixel_size; - m_cmp_count = 1; - - if (pixel_size == 8) { - m_row_bytes = m_bounds.width(); - for (auto color_value : color_values) { - pmap_data.write_byte(static_cast(color_value & 0xFF)); - } - } - else { - auto width = m_bounds.width(); - auto mod = 8 / pixel_size; - m_row_bytes = (width - 1) / mod + 1; - auto mask = (1 << pixel_size) - 1; - auto diff = 8 - pixel_size; - - for (auto y = 0; y < m_bounds.height(); ++y) { - uint8_t scratch = 0; - for (auto x = 0; x < width; ++x) { - auto bit_offset = x % mod; - if (bit_offset == 0 && x != 0) { - pmap_data.write_byte(scratch); - scratch = 0; - } - auto n = y * width + x; - auto value = static_cast(color_values[n] & mask); - value <<= (diff - (bit_offset * pixel_size)); - scratch |= value; - } - pmap_data.write_byte(scratch); - } - } - - return pmap_data.data(); -} - -auto graphite::qd::pixmap::write(graphite::data::writer& writer) -> void -{ - writer.write_long(m_base_address); - writer.write_short(0x8000 | m_row_bytes); - m_bounds.write(writer, rect::qd); - writer.write_signed_short(m_pm_version); - writer.write_signed_short(m_pack_type); - writer.write_signed_long(m_pack_size); - writer.write_signed_long(static_cast(m_h_res * (1 << 16))); - writer.write_signed_long(static_cast(m_v_res * (1 << 16))); - writer.write_signed_short(m_pixel_type); - writer.write_signed_short(m_pixel_size); - writer.write_signed_short(m_cmp_count); - writer.write_signed_short(m_cmp_size); - writer.write_signed_long(static_cast(m_pixel_format)); - writer.write_long(m_pm_table); - writer.write_signed_long(m_pm_extension); -} diff --git a/libGraphite/quickdraw/pixmap.hpp b/libGraphite/quickdraw/pixmap.hpp deleted file mode 100644 index ee921c7..0000000 --- a/libGraphite/quickdraw/pixmap.hpp +++ /dev/null @@ -1,87 +0,0 @@ -// -// Created by Tom Hancocks on 19/03/2020. -// - -#if !defined(GRAPHITE_PIXMAP_HPP) -#define GRAPHITE_PIXMAP_HPP - -#include -#include "libGraphite/data/reader.hpp" -#include "libGraphite/quickdraw/clut.hpp" -#include "libGraphite/quickdraw/geometry.hpp" -#include "libGraphite/quickdraw/internal/surface.hpp" -#include "libGraphite/data/data.hpp" - -namespace graphite::qd { - - enum pixel_format - { - unknown = 0, - monochrome = 0x01, // 1 bit indexed - indexed_2 = 0x02, // 2 bit indexed - indexed_4 = 0x04, // 4 bit indexed - indexed_8 = 0x08, // 8 bit indexed - b16_rgb555 = 0x10, // 16 bit, RGB 555 (Mac) - true_color = 0x18, // 24 bit RGB - true_color_alpha = 0x20, // 32 bit ARGB - }; - - class pixmap - { - private: - uint32_t m_base_address { 0 }; - int16_t m_row_bytes { 0 }; - graphite::qd::rect m_bounds { rect::zero() }; - int16_t m_pm_version { 0 }; - int16_t m_pack_type { 0 }; - int32_t m_pack_size { 0 }; - double m_h_res { 72 }; - double m_v_res { 72 }; - int16_t m_pixel_type { 0 }; - int16_t m_pixel_size { 0 }; - int16_t m_cmp_count { 0 }; - int16_t m_cmp_size { 0 }; - enum pixel_format m_pixel_format { unknown }; - uint32_t m_pm_table { 0 }; - uint32_t m_pm_extension { 0 }; - public: - static constexpr int length { 50 }; - - pixmap(); - explicit pixmap(qd::rect frame); - explicit pixmap(std::shared_ptr data); - - [[nodiscard]] auto bounds() const -> graphite::qd::rect; - [[nodiscard]] auto row_bytes() const -> int16_t; - [[nodiscard]] auto pack_type() const -> int16_t; - [[nodiscard]] auto pack_size() const -> int16_t; - [[nodiscard]] auto pixel_type() const -> int16_t; - [[nodiscard]] auto pixel_size() const -> int16_t; - [[nodiscard]] auto cmp_count() const -> int16_t; - [[nodiscard]] auto cmp_size() const -> int16_t; - [[nodiscard]] auto pixel_format() const -> enum graphite::qd::pixel_format; - [[nodiscard]] auto pm_table() const -> uint32_t; - - auto set_bounds(const graphite::qd::rect& rect) -> void; - auto set_row_bytes(const int16_t& row_bytes) -> void; - auto set_pack_type(const int16_t& pack_type) -> void; - auto set_pack_size(const int16_t& pack_size) -> void; - auto set_pixel_type(const int16_t& pixel_type) -> void; - auto set_pixel_size(const int16_t& pixel_size) -> void; - auto set_cmp_count(const int16_t& cmp_count) -> void; - auto set_cmp_size(const int16_t& cmp_size) -> void; - auto set_pm_table(const uint32_t& pm_table) -> void; - - auto build_surface( - std::shared_ptr surface, - const std::vector& pixel_data, - const qd::clut& clut, - qd::rect destination - ) -> void; - auto build_pixel_data(const std::vector& color_values, uint16_t pixel_size) -> std::shared_ptr; - auto write(graphite::data::writer& writer) -> void; - }; - -} - -#endif //GRAPHITE_PIXMAP_HPP diff --git a/libGraphite/quickdraw/ppat.cpp b/libGraphite/quickdraw/ppat.cpp deleted file mode 100644 index 976fb8b..0000000 --- a/libGraphite/quickdraw/ppat.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// -// Created by Tom Hancocks on 17/07/2020. -// - -#include "libGraphite/quickdraw/ppat.hpp" -#include "libGraphite/rsrc/manager.hpp" -#include -#include - -// MARK: - Constructor - -graphite::qd::ppat::ppat(std::shared_ptr data, int64_t id, std::string name) - : m_id(id), m_name(std::move(name)) -{ - data::reader reader(std::move(data)); - parse(reader); -} - -graphite::qd::ppat::ppat(std::shared_ptr surface) - : m_surface(std::move(surface)) -{ - -} - -auto graphite::qd::ppat::load_resource(int64_t id) -> std::shared_ptr -{ - if (auto res = graphite::rsrc::manager::shared_manager().find("ppat", id).lock()) { - return std::make_shared(res->data(), id, res->name()); - } - return nullptr; -} - - -// MARK: - Accessors - -auto graphite::qd::ppat::surface() const -> std::weak_ptr -{ - return m_surface; -} - -// MARK: - Parser - -auto graphite::qd::ppat::parse(graphite::data::reader& reader) -> void -{ - m_pat_type = reader.read_short(); - if (m_pat_type != 1) { - throw std::runtime_error("Currently unsupported ppat configuration: pat_type=" + - std::to_string(m_pat_type)); - } - - m_pmap_base_addr = reader.read_long(); - m_pat_base_addr = reader.read_long(); - - reader.set_position(m_pmap_base_addr); - m_pixmap = graphite::qd::pixmap(reader.read_data(qd::pixmap::length)); - - reader.set_position(m_pat_base_addr); - auto pmap_data_size = m_pixmap.row_bytes() * m_pixmap.bounds().height(); - auto pmap_data = reader.read_bytes(pmap_data_size); - - reader.set_position(m_pixmap.pm_table()); - m_clut = qd::clut(reader); - - // Now that all information has been extracted from the resource, proceed and attempt to render it. - m_surface = std::make_shared(m_pixmap.bounds().width(), m_pixmap.bounds().height()); - - m_pixmap.build_surface(m_surface, std::vector(pmap_data.begin(), pmap_data.end()), m_clut, m_pixmap.bounds()); -} - -// MARK: - Encoder - -auto graphite::qd::ppat::data() -> std::shared_ptr -{ - auto data = std::make_shared(); - auto writer = graphite::data::writer(data); - auto width = m_surface->size().width(); - auto height = m_surface->size().height(); - - // TODO: This is a brute force method of bringing down the color depth/number of colors required, - // for a ppat image. It doesn't optimise for image quality at all, and should be replaced at somepoint. - std::vector color_values; - uint8_t pass = 0; - do { - if (pass++ > 0) { - for (auto y = 0; y < height; ++y) { - for (auto x = 0; x < width; ++x) { - auto color = m_surface->at(x, y); - m_surface->set(x, y, qd::color( - color.red_component() & ~(1 << pass), - color.green_component() & ~(1 << pass), - color.blue_component() & ~(1 << pass), - color.alpha_component() - )); - } - } - } - - // Rebuild the Color Table for the surface. To do this we want to create an empty table, and populate it. - m_clut = qd::clut(); - color_values.clear(); - for (auto y = 0; y < height; ++y) { - for (auto x = 0; x < width; ++x) { - auto color = m_surface->at(x, y); - color_values.emplace_back(m_clut.set(color)); - } - } - } while(m_clut.size() > 256); - - - // Determine what component configuration we need. - m_pixmap = qd::pixmap(); - m_pixmap.set_bounds(qd::rect(point::zero(), m_surface->size())); - std::shared_ptr pmap_data; - - if (m_clut.size() > 256) { - throw std::runtime_error("Implementation does not currently handle more than 256 colors in a PPAT"); - } - else if (m_clut.size() > 16) { - pmap_data = m_pixmap.build_pixel_data(color_values, 8); - } - else if (m_clut.size() > 4) { - pmap_data = m_pixmap.build_pixel_data(color_values, 4); - } - else if (m_clut.size() > 2) { - pmap_data = m_pixmap.build_pixel_data(color_values, 2); - } - else { - pmap_data = m_pixmap.build_pixel_data(color_values, 1); - } - - // Calculate some offsets - m_pat_type = 1; - m_pmap_base_addr = 28; - m_pat_base_addr = m_pmap_base_addr + 50; - m_pixmap.set_pm_table(m_pat_base_addr + pmap_data->size()); - - // Write out the image data for the ppat. - writer.write_short(m_pat_type); - writer.write_long(m_pmap_base_addr); - writer.write_long(m_pat_base_addr); - writer.write_long(0); - writer.write_short(0); - writer.write_long(0); - writer.write_quad(0); - m_pixmap.write(writer); - writer.write_data(pmap_data); - m_clut.write(writer); - - return data; -} diff --git a/libGraphite/quickdraw/ppat.hpp b/libGraphite/quickdraw/ppat.hpp deleted file mode 100644 index c187bcb..0000000 --- a/libGraphite/quickdraw/ppat.hpp +++ /dev/null @@ -1,42 +0,0 @@ -// -// Created by Tom Hancocks on 17/07/2020. -// - -#if !defined(GRAPHITE_PPAT_HPP) -#define GRAPHITE_PPAT_HPP - -#include -#include "libGraphite/quickdraw/internal/surface.hpp" -#include "libGraphite/quickdraw/geometry.hpp" -#include "libGraphite/quickdraw/pixmap.hpp" -#include "libGraphite/quickdraw/clut.hpp" - -namespace graphite::qd { - - class ppat - { - private: - int64_t m_id {}; - std::string m_name; - uint16_t m_pat_type {}; - uint32_t m_pmap_base_addr {}; - uint32_t m_pat_base_addr {}; - qd::pixmap m_pixmap; - std::shared_ptr m_surface; - qd::clut m_clut; - - auto parse(data::reader& reader) -> void; - - public: - explicit ppat(std::shared_ptr data, int64_t id = 0, std::string name = ""); - explicit ppat(std::shared_ptr surface); - - static auto load_resource(int64_t id) -> std::shared_ptr; - - [[nodiscard]] auto surface() const -> std::weak_ptr; - auto data() -> std::shared_ptr; - }; - -} - -#endif //GRAPHITE_PPAT_HPP diff --git a/libGraphite/quickdraw/rle.cpp b/libGraphite/quickdraw/rle.cpp deleted file mode 100644 index 63ece27..0000000 --- a/libGraphite/quickdraw/rle.cpp +++ /dev/null @@ -1,371 +0,0 @@ -// -// Created by Tom Hancocks on 24/03/2020. -// - -#include -#include -#include -#include "libGraphite/quickdraw/rle.hpp" -#include "libGraphite/rsrc/manager.hpp" - -static const auto rle_grid_width = 6; - -// MARK: - Constructor - -graphite::qd::rle::rle(std::shared_ptr data, int64_t id, std::string name) - : m_id(id), m_name(std::move(name)) -{ - auto reader = data::reader(std::move(data)); - parse(reader); -} - -graphite::qd::rle::rle(qd::size frame_size, uint16_t frame_count) - : m_id(0), m_name("RLE"), m_frame_size(frame_size), m_frame_count(frame_count), m_bpp(16), m_palette_id(0) -{ - // Determine what the grid will be. We need to round up to the next whole number and have blank tiles - // if the frame count is not divisible by the grid width constant. - auto grid_width = std::min(rle_grid_width, static_cast(m_frame_count)); - m_grid_size = qd::size(static_cast(grid_width), - static_cast(std::ceil(m_frame_count / static_cast(grid_width)))); - - // Create the surface - m_surface = std::make_shared(m_grid_size.width() * m_frame_size.width(), - m_grid_size.height() * m_frame_size.height()); -} - -auto graphite::qd::rle::load_resource(int64_t id) -> std::shared_ptr -{ - if (auto rle_res = graphite::rsrc::manager::shared_manager().find("rlëD", id).lock()) { - return std::make_shared(rle_res->data()); - } - return nullptr; -} - -// MARK: - Accessors - -auto graphite::qd::rle::surface() const -> std::weak_ptr -{ - return m_surface; -} - -auto graphite::qd::rle::frames() const -> std::vector -{ - return m_frames; -} - -auto graphite::qd::rle::frame_count() const -> int -{ - return static_cast(m_frame_count); -} - -auto graphite::qd::rle::frame_rect(int frame) const -> graphite::qd::rect -{ - return qd::rect((frame % rle_grid_width) * m_frame_size.width(), (frame / rle_grid_width) * m_frame_size.height(), - m_frame_size.width(), m_frame_size.height()); -} - -auto graphite::qd::rle::frame_surface(int frame) const -> std::shared_ptr -{ - auto surface = std::make_shared(m_frame_size.width(), m_frame_size.height()); - auto src_rect = frame_rect(frame); - - // Extract the frame area of the origin surface - for (auto x = 0; x < src_rect.width(); x++) { - for (auto y = 0; y < src_rect.height(); y++) { - surface->set(x, y, m_surface->at(x + src_rect.x(), y + src_rect.y())); - } - } - - return surface; -} - -auto graphite::qd::rle::write_frame(int frame, const std::shared_ptr& surface) -> void -{ - auto dst_rect = frame_rect(frame); - auto src_size = surface->size(); - - if (src_size.width() != m_frame_size.width() || src_size.height() != m_frame_size.height()) { - throw std::runtime_error("Incorrect frame dimensions " + std::to_string(src_size.width()) + "x" + std::to_string(src_size.height()) + - ", expected " + std::to_string(m_frame_size.width()) + "x" + std::to_string(m_frame_size.height())); - } - - // Copy from the source surface into the destination frame - for (auto x = 0; x < dst_rect.width(); x++) { - for (auto y = 0; y < dst_rect.height(); y++) { - m_surface->set(x + dst_rect.x(), y + dst_rect.y(), surface->at(x, y)); - } - } -} - -// MARK: - Parsing - -auto graphite::qd::rle::parse(data::reader &reader) -> void -{ - // Read the header of the RLE information. This will tell us what we need to do in order to - // actually decode the frames. - m_frame_size = qd::size::read(reader, qd::size::pict); - m_bpp = reader.read_short(); - m_palette_id = reader.read_short(); - m_frame_count = reader.read_short(); - reader.move(6); - - // Ensure that the RLE has a BPP of 16. This is the only format that we support currently. - if (m_bpp != 16) { - throw std::runtime_error("Incorrect color depth for rlëD resource: " + std::to_string(m_id) + ", " + m_name); - } - - // Determine what the grid will be. We need to round up to the next whole number and have blank tiles - // if the frame count is not divisible by the grid width constant. - auto grid_width = std::min(rle_grid_width, static_cast(m_frame_count)); - m_grid_size = qd::size(static_cast(grid_width), - static_cast(std::ceil(m_frame_count / static_cast(grid_width)))); - - // Create the surface in which all frames will be drawn to, and other working variables required to parse and decode - // the RLE data correctly. - m_surface = std::make_shared(m_grid_size.width() * m_frame_size.width(), - m_grid_size.height() * m_frame_size.height()); - - rle::opcode opcode = eof; - uint64_t position = 0; - uint32_t row_start = 0; - int32_t current_line = -1; - uint64_t current_offset = 0; - int32_t count = 0; - uint16_t pixel = 0; - int32_t current_frame = 0; - uint32_t pixel_run = 0; - - while (!reader.eof()) { - if ((row_start != 0) && ((position - row_start) & 0x03)) { - position += 4 - ((position - row_start) & 0x03); - reader.move(4 - (count & 0x03)); - } - - count = reader.read_long(); - opcode = static_cast(count >> 24); - count &= 0x00FFFFFF; - - switch (opcode) { - case rle::opcode::eof: { - // Check that we're not erroneously encountering an EOF. - if (current_line != static_cast(m_frame_size.height() - 1)) { - throw std::runtime_error("Incorrect number of scanlines in rlëD resource: " + std::to_string(m_id) + ", " + m_name); - } - - // Have we finished decoding the last frame in the data? - if (++current_frame >= m_frame_count) { - goto COMPLETED_LAST_FRAME; - } - - // Prepare for the next frame. - current_line = -1; - break; - } - - case rle::opcode::line_start: { - current_offset = surface_offset(current_frame, ++current_line); - row_start = static_cast(reader.position()); - break; - } - - case rle::opcode::pixel_data: { - for (auto i = 0; i < count; i += 2) { - pixel = reader.read_short(); - write_pixel(pixel, 0xff, current_offset); - ++current_offset; - } - - if (count & 0x03) { - reader.move(4 - (count & 0x03)); - } - - break; - } - - case rle::opcode::pixel_run: { - pixel_run = reader.read_long(); - for (auto i = 0; i < count; i += 4) { - write_pixel_variant1(pixel_run, 0xff, current_offset); - ++current_offset; - - if (i + 2 < count) { - write_pixel_variant2(pixel_run, 0xff, current_offset); - ++current_offset; - } - } - break; - } - - case rle::opcode::transparent_run: { - current_offset += count >> 1; - break; - } - } - } - - COMPLETED_LAST_FRAME: - // Finished decoding rlëD data. - return; -} - -auto graphite::qd::rle::surface_offset(int32_t frame, int32_t line) const -> uint64_t -{ - qd::point fo(frame % rle_grid_width, frame / rle_grid_width); - qd::point p(fo.x() * m_frame_size.width(), (fo.y() * m_frame_size.height()) + line); - return static_cast(p.y() * m_surface->size().width() + p.x()); -} - -auto graphite::qd::rle::write_pixel(uint16_t pixel, uint8_t mask, uint64_t offset) -> void -{ - auto r = static_cast((pixel & 0x7C00) >> 7); - auto g = static_cast((pixel & 0x03E0) >> 2); - auto b = static_cast((pixel & 0x001F) << 3); - m_surface->set(static_cast(offset), qd::color(r, g, b)); -} - -auto graphite::qd::rle::write_pixel_variant1(uint32_t pixel, uint8_t mask, uint64_t offset) -> void -{ - auto r = static_cast((pixel & 0x7C000000) >> 23); - auto g = static_cast((pixel & 0x03E00000) >> 18); - auto b = static_cast((pixel & 0x001F0000) << 13); - m_surface->set(static_cast(offset), qd::color(r, g, b)); -} - -auto graphite::qd::rle::write_pixel_variant2(uint32_t pixel, uint8_t mask, uint64_t offset) -> void -{ - auto r = static_cast((pixel & 0x00007C00) >> 7); - auto g = static_cast((pixel & 0x000003E0) >> 2); - auto b = static_cast((pixel & 0x0000001F) << 3); - m_surface->set(static_cast(offset), qd::color(r, g, b)); -} - -// MARK: - Encoder / Writing - -auto graphite::qd::rle::encode(graphite::data::writer& writer) -> void -{ - // Write out the header - m_frame_size.write(writer, qd::size::pict); - writer.write_short(m_bpp); - writer.write_short(m_palette_id); - writer.write_short(m_frame_count); - - // Reserved fields - writer.write_short(0); - writer.write_short(0); - writer.write_short(0); - - const auto advance = 2; // we only support 16 bits per pixel - - // Write out the RLE frames - for (auto f = 0; f < m_frame_count; f++) { - auto frame = frame_rect(f); - - for (auto y = 0; y < frame.height(); y++) { - auto line_start_pos = writer.position(); - writer.write_long(0); // line start opcode placeholder -- we'll write it later - - opcode run_state = line_start; - auto run_start_pos = line_start_pos + 4; - auto run_count = 0; - - for (auto x = 0; x < frame.width(); x++) { - qd::color pixel = m_surface->at(frame.x() + x, frame.y() + y); - - if (pixel.alpha_component() == 0) { - if (run_state == line_start) { - // Start of a transparent run - run_start_pos = writer.position(); - writer.write_long(0); // opcode placeholder - run_state = transparent_run; - run_count = advance; - } - else if (run_state == transparent_run) { - // Continue transparent run - run_count += advance; - } - else { - // End of pixel run, start of transparent run - auto run_end_pos = writer.position(); - writer.set_position(run_start_pos); - writer.write_long((pixel_data << 24) | (run_count & 0x00FFFFFF)); - writer.set_position(run_end_pos); - - // Pad to nearest 4-byte boundary - if (run_count & 3) { - writer.move(4 - (run_count & 3)); - } - - // Start transparent run - run_start_pos = writer.position(); - writer.write_long(0); // opcode placeholder - run_state = transparent_run; - run_count = advance; - } - } - else { - if (run_state == line_start) { - // Start of a pixel run - run_start_pos = writer.position(); - writer.write_long(0); // opcode placeholder - run_state = pixel_data; - run_count = advance; - } - else if (run_state == transparent_run) { - // End of transparent run, start of pixel run - writer.move(-4); - writer.write_long((transparent_run << 24) | (run_count & 0x00FFFFFF)); - - // Start pixel run - run_start_pos = writer.position(); - writer.write_long(0); // opcode placeholder - run_state = pixel_data; - run_count = advance; - } - else { - // Continue pixel run - run_count += advance; - } - - // Write the pixel - writer.write_short(pixel.blue_component() >> 3 | - (pixel.green_component() >> 3) << 5 | - (pixel.red_component() >> 3) << 10); - } - } - - // Terminate the current opcode - if (run_state == pixel_data) { - auto run_end_pos = writer.position(); - writer.set_position(run_start_pos); - writer.write_long((pixel_data << 24) | (run_count & 0x00FFFFFF)); - writer.set_position(run_end_pos); - - // Pad to nearest 4-byte boundary - if ((run_end_pos - line_start_pos) & 3) { - writer.move(4 - ((run_end_pos - line_start_pos) & 3)); - } - } - else if (run_state == transparent_run) { - // Erase the transparent run opcode placeholder -- remaining data is assumed transparent - writer.set_position(run_start_pos); - } - - // Write out the opcode and size at the start of the line - auto line_end_pos = writer.position(); - writer.set_position(line_start_pos); - writer.write_long((line_start << 24) | ((line_end_pos - line_start_pos - 4) & 0x00FFFFFF)); - writer.set_position(line_end_pos); - } - - // Mark end-of-frame - writer.write_long(eof << 24); - } -} - -auto graphite::qd::rle::data() -> std::shared_ptr -{ - auto data = std::make_shared(); - graphite::data::writer writer(data); - encode(writer); - return data; -} diff --git a/libGraphite/quickdraw/rle.hpp b/libGraphite/quickdraw/rle.hpp deleted file mode 100644 index 75e150c..0000000 --- a/libGraphite/quickdraw/rle.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Created by Tom Hancocks on 24/03/2020. -// - -#if !defined(GRAPHITE_RLE_HPP) -#define GRAPHITE_RLE_HPP - -#include -#include "libGraphite/quickdraw/internal/surface.hpp" -#include "libGraphite/quickdraw/geometry.hpp" - -namespace graphite::qd { - - class rle - { - private: - enum opcode : uint8_t - { - eof = 0x00, - line_start = 0x01, - pixel_data = 0x02, - transparent_run = 0x03, - pixel_run = 0x04, - }; - - int64_t m_id {}; - std::string m_name; - std::vector m_frames; - std::shared_ptr m_surface; - qd::size m_frame_size; - qd::size m_grid_size; - uint16_t m_frame_count {}; - uint16_t m_bpp {}; - uint16_t m_palette_id {}; - - auto parse(data::reader &reader) -> void; - [[nodiscard]] auto surface_offset(int32_t frame, int32_t line) const -> uint64_t; - auto write_pixel(uint16_t pixel, uint8_t mask, uint64_t offset) -> void; - auto write_pixel_variant1(uint32_t pixel, uint8_t mask, uint64_t offset) -> void; - auto write_pixel_variant2(uint32_t pixel, uint8_t mask, uint64_t offset) -> void; - - auto encode(graphite::data::writer& writer) -> void; - - public: - explicit rle(std::shared_ptr data, int64_t id = 0, std::string name = ""); - rle(qd::size frame_size, uint16_t frame_count); - - static auto load_resource(int64_t id) -> std::shared_ptr; - - [[nodiscard]] auto surface() const -> std::weak_ptr; - [[nodiscard]] auto frames() const -> std::vector; - - [[nodiscard]] auto frame_count() const -> int; - [[nodiscard]] auto frame_rect(int frame) const -> qd::rect; - [[nodiscard]] auto frame_surface(int frame) const -> std::shared_ptr; - auto write_frame(int frame, const std::shared_ptr& surface) -> void; - - auto data() -> std::shared_ptr; - }; - -} - -#endif //GRAPHITE_RLE_HPP diff --git a/libGraphite/resources/sound.cpp b/libGraphite/resources/sound.cpp deleted file mode 100644 index 8733b1e..0000000 --- a/libGraphite/resources/sound.cpp +++ /dev/null @@ -1,284 +0,0 @@ -// -// Created by Tom Hancocks on 24/03/2020. -// - -#include "libGraphite/resources/sound.hpp" -#include "libGraphite/rsrc/manager.hpp" -#include "libGraphite/data/reader.hpp" -#include -#include - -// MARK: - IMA4 Decoding - -// Refer to https://wiki.multimedia.cx/index.php?title=IMA_ADPCM, -// http://www.cs.columbia.edu/~hgs/audio/dvi/IMA_ADPCM.pdf - -inline int32_t ima_step_index(int32_t index, int8_t nibble) -{ - static int32_t ima_index_table[16] = { - -1, -1, -1, -1, 2, 4, 6, 8, - -1, -1, -1, -1, 2, 4, 6, 8 - }; - - return std::min(std::max(0, index + ima_index_table[nibble]), 88); -} - -inline int32_t ima_predictor(int32_t predictor, int8_t nibble, int32_t index) -{ - static int32_t ima_step_table[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, - 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, - 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, - 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, - 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, - 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 - }; - - int32_t diff = 0; - auto stepsize = ima_step_table[index]; - - if (nibble & 4) diff += stepsize; - if (nibble & 2) diff += stepsize >> 1; - if (nibble & 1) diff += stepsize >> 2; - diff += stepsize >> 3; - if (nibble & 8) diff = -diff; - - return std::min(std::max(-32768, predictor + diff), 32767); -} - -// MARK: - Constructor - -graphite::resources::sound::sound(std::shared_ptr data, int64_t id, std::string name) - : m_ref_count(0), m_name(std::move(name)), m_id(id) -{ - // Setup a reader for the snd data, and then parse it. - data::reader snd_reader(std::move(data)); - parse(snd_reader); -} - -graphite::resources::sound::sound(uint32_t sample_rate, uint8_t sample_bits, std::vector> sample_data) - : m_ref_count(0), m_name("Sound"), m_id(0), m_sample_rate_int(sample_rate), m_sample_rate_frac(0), m_sample_bits(sample_bits), m_sample_data(std::move(sample_data)) -{ - -} - -auto graphite::resources::sound::load_resource(int64_t id) -> std::shared_ptr -{ - if (auto snd_res = graphite::rsrc::manager::shared_manager().find("snd ", id).lock()) { - return std::make_shared(snd_res->data(), id, snd_res->name()); - } - return nullptr; -} - -// MARK: - Accessors - -auto graphite::resources::sound::sample_bits() const -> uint8_t -{ - return m_sample_bits; -} - -auto graphite::resources::sound::sample_rate() const -> uint32_t -{ - return m_sample_rate_int; -} - -auto graphite::resources::sound::samples() -> std::vector> -{ - return m_sample_data; -} - -auto graphite::resources::sound::data() -> std::shared_ptr -{ - auto data = std::make_shared(); - graphite::data::writer writer(data); - encode(writer); - return data; -} - -// MARK: - Parsing/Reading - -auto graphite::resources::sound::parse(graphite::data::reader& snd_reader) -> void -{ - // Save the position because our buffer commands reference data by offset from the record start - auto reader_pos = snd_reader.position(); - - auto snd_format = static_cast(snd_reader.read_short()); - - uint16_t channel_init_option = 0; - if (snd_format == type1) { - // We only support sampled sounds; validate the format 1 sound type now - auto num_data_formats = snd_reader.read_short(); - if (num_data_formats != 1) { - throw std::runtime_error("Encountered an incompatble snd format: " + std::to_string(num_data_formats) + - " formats, 1 expected, " + m_name); - } - - auto data_format_id = snd_reader.read_short(); - if (data_format_id != sampledSynth) { - throw std::runtime_error("Encountered an incompatble snd format: format " + std::to_string(data_format_id) + - ", 5 expected, " + m_name); - } - - channel_init_option = snd_reader.read_long(); - } - else if (snd_format == type2) { - m_ref_count = snd_reader.read_short(); - } - else { - throw std::runtime_error("Encountered an incompatble snd format: " + std::to_string(snd_format) + ", " + m_name); - } - - // Read command array - auto num_commands = snd_reader.read_short(); - std::vector commands; - for (auto i = 0; i < num_commands; i++) { - auto cmd = snd_reader.read_short(); - commands.emplace_back(static_cast(cmd & 0x7FFF), snd_reader.read_short(), snd_reader.read_long(), cmd & 0x8000); - } - - // We only support sounds with a single buffer command -- validate that now - if (commands.size() != 1 || commands[0].cmd != buffer) { - throw std::runtime_error("Encountered an incompatble snd format: " + std::to_string(commands.size()) + - " commands, first command " + std::to_string(static_cast(commands[0].cmd)) + - ", " + m_name); - } - - // Move the reader to the buffer command's data offset - snd_reader.set_position(reader_pos + commands[0].param2); - - sound_header_record std_header {}; - - std_header.data_pointer = snd_reader.read_long(); - std_header.length = snd_reader.read_long(); - std_header.sample_rate = snd_reader.read_long(); - std_header.loop_start = snd_reader.read_long(); - std_header.loop_end = snd_reader.read_long(); - std_header.sample_encoding = static_cast(snd_reader.read_byte()); - std_header.base_frequency = snd_reader.read_byte(); - - m_sample_rate_int = std_header.sample_rate >> 16; - m_sample_rate_frac = std_header.sample_rate & 0xFFFF; - - if (std_header.sample_encoding == extSH) { - extended_sound_header_record ext_header {}; - ext_header.num_frames = snd_reader.read_long(); - // skip aiff_sample_rate, marker_chunk, instrument_chunks, aes_recording - snd_reader.move(22); - ext_header.sample_size = snd_reader.read_short(); - // skip future_use values - snd_reader.move(14); - - m_sample_bits = ext_header.sample_size; - // Resize the data vector to fit the channel/frame count - m_sample_data.resize(std_header.length, std::vector(ext_header.num_frames)); - - // Raw sound data follows, channels interleaved - for (auto f = 0; f < ext_header.num_frames; f++) { - for (auto c = 0; c < std_header.length; c++) { - m_sample_data[c][f] = ext_header.sample_size == 8 ? snd_reader.read_byte() : snd_reader.read_short(); - } - } - } - else if (std_header.sample_encoding == cmpSH) { - compressed_sound_header_record cmp_header {}; - cmp_header.num_frames = snd_reader.read_long(); - // skip aiff_sample_rate, marker_chunk - snd_reader.move(14); - cmp_header.format = snd_reader.read_long(); - // skip future_use_2, state_vars, leftover_samples - snd_reader.move(12); - cmp_header.compression_id = static_cast(snd_reader.read_signed_short()); - cmp_header.packet_size = snd_reader.read_short(); - // skip snth_id - snd_reader.move(2); - cmp_header.sample_size = snd_reader.read_short(); - - // We only support fixed ima4 compression - if (cmp_header.compression_id != fixedCompression || cmp_header.format != 0x696D6134) { - throw std::runtime_error("Encountered an incompatble snd format: " + std::to_string(cmp_header.compression_id) + - " compression ID, format " + std::to_string(cmp_header.format) + - ", expecting fixed ima4 compression, " + m_name); - } - - m_sample_bits = 16; - // Resize the data vector to fit the channel count; sample count is frame count * 64 - m_sample_data.resize(std_header.length, std::vector(cmp_header.num_frames * 64)); - - // Iterate over frames and expand into samples - for (auto f = 0; f < cmp_header.num_frames; f++) { - for (auto c = 0; c < std_header.length; c++) { - // Apple IMA4 parameters described in TN1081: - // https://www.fenestrated.net/mirrors/Apple%20Technotes%20(As%20of%202002)/tn/tn1081.html - auto preamble = snd_reader.read_short(); - int32_t predictor = static_cast(preamble & 0xFF80); - int32_t step_index = preamble & 0x007F; - - for (auto i = 0; i < 32; i++) { - uint8_t byte = snd_reader.read_byte(); - uint8_t lower_nibble = byte & 0x0F; - uint8_t upper_nibble = byte >> 4; - - predictor = ima_predictor(predictor, lower_nibble, step_index); - step_index = ima_step_index(step_index, lower_nibble); - m_sample_data[c][f * 64 + i * 2] = 32768 + predictor; - - predictor = ima_predictor(predictor, upper_nibble, step_index); - step_index = ima_step_index(step_index, upper_nibble); - m_sample_data[c][f * 64 + i * 2 + 1] = 32768 + predictor; - } - } - } - } - else { - m_sample_bits = 8; - // Resize the data vector to fit the channel/frame count - m_sample_data.resize(1, std::vector(std_header.length)); - - // Raw 8-bit mono sound data follows - for (auto f = 0; f < std_header.length; f++) { - m_sample_data[0][f] = snd_reader.read_byte(); - } - } -} - -// MARK: - Encoder / Writing - -auto graphite::resources::sound::encode(graphite::data::writer& snd_writer) -> void -{ - // Write the snd header for a format 1 sound - snd_writer.write_short(static_cast(type1)); - snd_writer.write_short(1); // num_data_formats - snd_writer.write_short(static_cast(sampledSynth)); // first_data_format_id - snd_writer.write_long(static_cast(initMono)); // channel_init_option - snd_writer.write_short(1); // num_commands - snd_writer.write_short(0x8000 | static_cast(buffer)); // command: cmd, high bit set to indicate offset - snd_writer.write_short(0); // command: param1 - snd_writer.write_long(20); // command: param2, offset from start of record to sound data - - auto num_samples = m_sample_data.size() ? m_sample_data[0].size() : 0; - - // Write the standard sampled sound header fields - snd_writer.write_long(0); // data pointer - snd_writer.write_long(num_samples); // number of samples - snd_writer.write_long(m_sample_rate_int << 16 | m_sample_rate_frac); // sample rate (fixed-point) - snd_writer.write_long(0); // loop start - snd_writer.write_long(0); // loop end - snd_writer.write_byte(static_cast(stdSH)); // encoding option - snd_writer.write_byte(0); // base frequency - - // Truncate and write the sound data as 8-bit mono - for (auto f = 0; f < num_samples; f++) { - snd_writer.write_byte(m_sample_data[0][f] >> (m_sample_bits - 8)); - } -} - -// MARK: - Sound record construction - -graphite::resources::sound::command_record::command_record(command cmd, uint16_t param1, uint32_t param2, bool data_offset_flag) - : cmd(cmd), param1(param1), param2(param2), data_offset_flag(data_offset_flag) -{ - -} diff --git a/libGraphite/resources/sound.hpp b/libGraphite/resources/sound.hpp deleted file mode 100644 index 9a07f60..0000000 --- a/libGraphite/resources/sound.hpp +++ /dev/null @@ -1,176 +0,0 @@ -// -// Created by Tom Hancocks on 24/03/2020. -// - -#if !defined(GRAPHITE_SOUND_HPP) -#define GRAPHITE_SOUND_HPP - -#include -#include -#include -#include "libGraphite/data/data.hpp" -#include "libGraphite/data/reader.hpp" -#include "libGraphite/data/writer.hpp" - -namespace graphite::resources { - - class sound - { - private: - enum format : uint16_t { type1 = 0x0001, type2 = 0x0002 }; - - enum command : uint16_t - { - null = 0, // Do nothing - quiet = 3, // Stop a sound that is playing - flush = 4, // Flush a sound channel - reinit = 5, // Reinitialise a sound channel - wait = 10, // Suspend processing in a channel - pause = 11, // Pause processing in a channel - resume = 12, // Resume processing in a channel - callback = 13, // Execute a callback procedure - sync = 14, // Synchronise channels - available = 24, // See if initialisation options are supported. - version = 25, // Determine version - totalLoad = 26, // Report total cpu load - load = 27, // Report cpu load for a new channel - freqDuration = 40, // Play a note for a duration - rest = 41, // Rest a channel for a duration - freq = 42, // Change pitch of a sound - amp = 43, // Change the amplitude of a sound - timbre = 44, // Change the timbre of a sound - getAmp = 45, // Get the amplitude of a sound - volume = 46, // Set the volume - getVolume = 47, // Get the volume - waveTable = 60, // Install a wave table as a voice - play_sound = 80, // Install a sampled sound as a voice - buffer = 81, // Play a sampled sound - rate = 82, // Set the pitch of a sampled sound - getRate = 83, // Get the pitch of a sampled sound - }; - - enum data_format : uint16_t { - squareWaveSynth = 1, // Square-wave data - waveTableSynth = 3, // Wave-table data - sampledSynth = 5, // Sampled-sound data - }; - - enum channel_init_option : uint32_t { - initChanLeft = 0x0002, // Left stereo channel - initChanRight = 0x0003, // Right stereo channel - waveInitChannel0 = 0x0004, // Wave-table channel 0 - waveInitChannel1 = 0x0005, // Wave-table channel 1 - waveInitChannel2 = 0x0006, // Wave-table channel 2 - waveInitChannel3 = 0x0007, // Wave-table channel 3 - initMono = 0x0080, // Monophonic channel - initStereo = 0x00C0, // Stereo channel - initMACE3 = 0x0300, // 3:1 compression - initMACE6 = 0x0400, // 6:1 compression - initNoInterp = 0x0004, // No linear interpolation - initNoDrop = 0x0008, // No drop-sample conversion - }; - - enum sound_encoding : uint8_t { - stdSH = 0x00, // Standard sound header - extSH = 0xFF, // Extended sound header - cmpSH = 0xFE, // Compressed sound header - }; - - enum compression_id : int16_t { - variableCompression = -2, // Variable-ratio compression - fixedCompression = -1, // Fixed-ratio compression - notCompressed = 0, // Non-compressed samples - threeToOne = 3, // 3:1 compressed samples - sixToOne = 6, // 6:1 compressed samples - }; - - struct command_record - { - public: - enum command cmd; - uint16_t param1; - uint32_t param2; - bool data_offset_flag; - - command_record(command cmd, uint16_t param1, uint32_t param2, bool data_offset_flag); - }; - - struct sound_header_record - { - public: - uint32_t data_pointer; // If nil, samples follow header - uint32_t length; // Number of samples in array - uint32_t sample_rate; // Sample rate (Fixed) - uint32_t loop_start; // Loop start point sample byte number - uint32_t loop_end; // Loop end point sample byte number - enum sound_encoding sample_encoding; // Sample encoding option - uint8_t base_frequency; // Base frequency of sample - }; - - struct extended_sound_header_record - { - public: - // The length field in standard_header is interpreted as num_channels - uint32_t num_frames; // The number of frames in the sampled-sound data. - // Each frame contains num_channels bytes for 8-bit sound data. - uint8_t aiff_sample_rate[10]; // The sample rate at which the frames were sampled before compression, - // as expressed in the 80-bit extended data type representation - uint32_t marker_chunk; // Pointer to synchronization information. Unused, set to NIL. - uint32_t instrument_chunks; // Pointer to instrument information. - uint32_t aes_recording; // Pointer to information related to audio recording devices. - uint16_t sample_size; // The number of bits in each sample frame. - uint16_t future_use_1; // Reserved - uint32_t future_use_2; // Reserved - uint32_t future_use_3; // Reserved - uint32_t future_use_4; // Reserved - }; - - struct compressed_sound_header_record - { - public: - // The length field in standard_header is interpreted as num_channels - uint32_t num_frames; // The number of frames in the sampled-sound data. - // Each frame contains num_channels bytes for 8-bit sound data. - uint8_t aiff_sample_rate[10]; // The sample rate at which the frames were sampled before compression, - // as expressed in the 80-bit extended data type representation - uint32_t marker_chunk; // Pointer to synchronization information. Unused, set to NIL. - uint32_t format; // OSType, e.g. 'MAC3' for MACE3:1 - uint32_t future_use_2; // Reserved - uint32_t state_vars; // Pointer to StateBlock - uint32_t leftover_samples; // Pointer to LeftOverBlock - enum compression_id compression_id; // The compression algorithm used on the samples in the compressed sound header. - uint16_t packet_size; // The size, in bits, of the smallest element that a given expansion algorithm can work with. - uint16_t snth_id; // This field is unused. You should set it to 0. - uint16_t sample_size; // The size of the sample before it was compressed. - }; - - // Basic resource information - int16_t m_id {}; - std::string m_name; - - // Sound information - uint16_t m_ref_count {}; - uint32_t m_sample_rate_int {}; - uint16_t m_sample_rate_frac {}; - uint8_t m_sample_bits {}; - std::vector> m_sample_data; - - auto parse(graphite::data::reader& snd_reader) -> void; - auto encode(graphite::data::writer& snd_writer) -> void; - - public: - explicit sound(std::shared_ptr data, int64_t id = 0, std::string name = ""); - sound(uint32_t sample_rate, uint8_t sample_bits, std::vector> sample_data); - - static auto load_resource(int64_t id) -> std::shared_ptr; - - [[nodiscard]] auto sample_bits() const -> uint8_t; - [[nodiscard]] auto sample_rate() const -> uint32_t; - auto samples() -> std::vector>; - - auto data() -> std::shared_ptr; - }; - -} - -#endif diff --git a/libGraphite/resources/string.cpp b/libGraphite/resources/string.cpp deleted file mode 100644 index f58b017..0000000 --- a/libGraphite/resources/string.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// -// Created by Tom Hancocks on 24/03/2020. -// - -#include "libGraphite/resources/string.hpp" -#include "libGraphite/rsrc/manager.hpp" -#include "libGraphite/data/reader.hpp" - -// MARK: - Constructor - -graphite::resources::string::string(std::string str, std::shared_ptr data, int64_t id, std::string name) - : m_str(std::move(str)), m_name(std::move(name)), m_id(id) -{ - // TODO: Add implementation to extract a new copy of the data that is not a pointer. -} - -auto graphite::resources::string::load_resource(int64_t id) -> std::shared_ptr -{ - if (auto str_res = graphite::rsrc::manager::shared_manager().find("STR ", id).lock()) { - auto reader = graphite::data::reader(str_res->data()); - auto str = reader.read_pstr(); - // TODO: Rest of the resource is data - return std::make_shared(str, nullptr, id, str_res->name()); - } - return nullptr; -} - -// MARK: - Accessor - -auto graphite::resources::string::value() const -> std::string -{ - return m_str; -} - -auto graphite::resources::string::data() const -> graphite::data::data -{ - return m_data; -} - -auto graphite::resources::string::set_string(const std::string& str) -> void -{ - m_str = str; -} - -auto graphite::resources::string::set_data(const data::data& data) -> void -{ - m_data = data; -} diff --git a/libGraphite/resources/string.hpp b/libGraphite/resources/string.hpp deleted file mode 100644 index c408d59..0000000 --- a/libGraphite/resources/string.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// -// Created by Tom Hancocks on 24/03/2020. -// - -#if !defined(GRAPHITE_STRING_HPP) -#define GRAPHITE_STRING_HPP - -#include -#include "libGraphite/data/data.hpp" - -namespace graphite::resources { - - struct string - { - private: - int64_t m_id {}; - std::string m_name; - std::string m_str; - data::data m_data; - - public: - string(std::string str, std::shared_ptr data, int64_t id = 0, std::string name = ""); - - static auto load_resource(int64_t id) -> std::shared_ptr; - - auto value() const -> std::string; - auto data() const -> data::data; - - auto set_string(const std::string& str) -> void; - auto set_data(const data::data& data) -> void; - }; - -} - - -#endif //GRAPHITE_STRING_HPP diff --git a/libGraphite/rsrc/classic.cpp b/libGraphite/rsrc/classic.cpp deleted file mode 100644 index f6e3b49..0000000 --- a/libGraphite/rsrc/classic.cpp +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include -#include "libGraphite/hints.hpp" -#include "libGraphite/rsrc/classic.hpp" -#include "libGraphite/encoding/macroman/macroman.hpp" - -// MARK: - Parsing / Reading - -auto graphite::rsrc::classic::parse(const std::shared_ptr& reader) -> std::vector> -{ - // 1. Resource File preamble, - auto data_offset = reader->read_long(); - auto map_offset = reader->read_long(); - auto data_length = reader->read_long(); - auto map_length = reader->read_long(); - - // Before proceeding any further, we need to verify that the resource file is valid. - // We can do this in two ways. We can check the corresponding second header/preamble - // at the start of the resource map, and we can check the lengths provided equal the - // size of the file. - if (data_offset == 0 || map_offset == 0 || map_length == 0) { - throw std::runtime_error("[Classic Resource File] Invalid Preamble."); - } - - auto rsrc_size = data_offset + data_length + map_length; - if (map_offset != data_offset + data_length) { - throw std::runtime_error("[Classic Resource File] ResourceMap starts at the unexpected location."); - } - - if (rsrc_size > reader->size()) { - throw std::runtime_error("[Classic Resource File] ResourceFile has unexpected length."); - } - - // Now move to the start of the resource map, and verify the contents of the preamble. - reader->set_position(map_offset); - - auto data_offset2 = reader->read_long(); - auto map_offset2 = reader->read_long(); - auto data_length2 = reader->read_long(); - auto map_length2 = reader->read_long(); - - // Ignore second preamble if all zero, as this can happen sometimes. - if (data_offset2 != 0 || map_offset2 != 0 || data_length2 != 0 || map_length2 != 0) { - if (data_offset2 != data_offset) { - throw std::runtime_error("[Classic Resource File] Second Preamble 'data_offset' mismatch."); - } - - if (map_offset2 != map_offset) { - throw std::runtime_error("[Classic Resource File] Second Preamble 'map_offset' mismatch."); - } - - if (data_length2 != data_length) { - throw std::runtime_error("[Classic Resource File] Second Preamble 'data_length' mismatch."); - } - - if (map_length2 != map_length) { - throw std::runtime_error("[Classic Resource File] Second Preamble 'map_length' mismatch."); - } - } - - // 2. Now that the preamble is parsed and verified, parse the contents - // of the ResourceMap. The first two fields are used by the actual Resource Manager in - // the Classic Macintosh OS, but are not used by this implementation. - GRAPHITE_UNUSED auto next_map = reader->read_long(); - GRAPHITE_UNUSED auto reference = reader->read_short(); - - // Start to read the actual content of the resource map. This is the content that - // we actually care about. The first field is the flags/attributes of the resource - // fork. - GRAPHITE_UNUSED auto flags = static_cast(reader->read_short()); - - // The next fields are the offsets of the type list and the name list. - auto type_list_offset = static_cast(reader->read_short()); - auto name_list_offset = static_cast(reader->read_short()); - - // 3. Parse the list of Resource Types. - reader->set_position(map_offset + type_list_offset); - auto type_count = static_cast(reader->read_short() + 1); - std::vector> types; - - for (auto type_idx = 0; type_idx < type_count; ++type_idx) { - auto code = reader->read_cstr(4); - auto count = static_cast(reader->read_short() + 1); - auto first_resource_offset = static_cast(reader->read_short()); - - auto type = std::make_shared(code); - - // 4. Parse the list of Resources for the current resource type. - reader->save_position(); - reader->set_position(map_offset + type_list_offset + first_resource_offset); - - for (auto res_idx = 0; res_idx < count; ++res_idx) { - auto id = static_cast(reader->read_signed_short()); - auto name_offset = reader->read_short(); - GRAPHITE_UNUSED auto flags = reader->read_byte(); - auto resource_data_offset = reader->read_triple(); - GRAPHITE_UNUSED auto handle = reader->read_long(); - - // 5. Parse out of the name of the resource. - std::string name; - if (name_offset != std::numeric_limits::max()) { - reader->save_position(); - reader->set_position(map_offset + name_list_offset + name_offset); - name = reader->read_pstr(); - reader->restore_position(); - } - - // 6. Create a data slice for the resource's data. - reader->save_position(); - reader->set_position(data_offset + resource_data_offset); - auto data_size = reader->read_long(); - auto slice = reader->read_data(data_size); - reader->restore_position(); - - // 7. Construct a new resource instance, and add it to the type. - auto resource = std::make_shared(id, type, name, slice); - type->add_resource(resource); - } - - reader->restore_position(); - - // 8. Save the resource type into the list of types and return it. - types.push_back(type); - } - - return types; -} - -// MARK: - Writing - -auto graphite::rsrc::classic::write(const std::string& path, const std::vector>& types) -> void -{ - auto writer = std::make_shared(); - - // 1. Begin setting up the preamble. - uint32_t data_offset = 256; - uint32_t map_offset = 0; - uint32_t data_length = 0; - uint32_t map_length = 0; - - writer->write_long(data_offset); - writer->write_long(map_offset); - writer->write_long(data_length); - writer->write_long(map_length); - writer->pad_to_size(data_offset); - - // 2. Iterate through all of the resources and write their data blobs to the file data. - // When doing this we need to record the starting points of each resources data, as - uint16_t resource_count = 0; - for (const auto& type : types) { - resource_count += type->count(); - - for (const auto& resource : type->resources()) { - // Get the data for the resource and determine its size. - auto data = resource->data(); - auto size = data->size(); - resource->set_data_offset(writer->size() - data_offset); - writer->write_long(static_cast(size)); - writer->write_data(data); - } - } - - // 3. Start writing the ResourceMap. This consists of several characteristics, - // The first of which is a secondary preamble. We can now calculate the map_offset and - // the data_length, but we're still waiting on the map_length. For now, write these values - // as zeros. - map_offset = static_cast(writer->size()); - data_length = map_offset - data_offset; - - writer->write_long(data_offset); - writer->write_long(map_offset); - writer->write_long(data_length); - writer->write_long(map_length); - - // The next six bytes are used by the MacOS ResourceManager and thus not important to - // us. - writer->write_byte(0x00, 6); - - // 4. We're now writing the primary map information, which includes flags, and offsets for - // the type list and the name list. We can calculate where each of these will be. - const uint16_t resource_type_length = 8; - const uint16_t resource_length = 12; - uint16_t type_list_offset = 28; // The type list is 28 bytes from the start of the resource map. - uint16_t name_list_offset = type_list_offset + (types.size() * resource_type_length) + (resource_count * resource_length) + sizeof(uint16_t); - - writer->write_short(0x0000); - writer->write_short(type_list_offset); - writer->write_short(name_list_offset); - - // Now moving on to actually writing each of the type descriptors into the data. - uint16_t resource_offset = sizeof(uint16_t) + (types.size() * resource_type_length); - writer->write_short(types.size() - 1); - for (const auto& type : types) { - // We need to ensure that the type code is 4 characters -- otherwise this file will be - // massively corrupt when produced. - auto mac_roman = graphite::encoding::mac_roman::from_utf8(type->code()); - if (mac_roman.size() != 4) { - throw std::runtime_error("Attempted to write invalid type code to Resource File '" + type->code() + "'"); - } - writer->write_bytes(mac_roman); - writer->write_short(type->count() - 1); - writer->write_short(resource_offset); - - resource_offset += type->count() * resource_length; - } - - // 5. Now we're writing the actual resource headers. - uint16_t name_offset = 0; - uint16_t name_len = 0; - for (const auto& type : types) { - for (const auto& resource : type->resources()) { - - auto id = resource->id(); - if (id < std::numeric_limits::min() || id > std::numeric_limits::max()) { - throw std::runtime_error("Attempted to write resource id outside of valid range."); - } - writer->write_signed_short(static_cast(id)); - - // The name is actually stored in the name list, and the resource stores an offset - // to that name. If no name is assigned to the resource then the offset is encoded as - // 0xFFFF. - if (resource->name().empty()) { - writer->write_short(0xFFFF); - } - else { - if (name_offset + name_len >= 0xFFFF) { - throw std::runtime_error("Attempted to write name offset exceeding maximum value."); - } - name_offset += name_len; - writer->write_short(name_offset); - - // Convert the name to MacRoman so that we can get the length of it when encoded. - auto mac_roman = graphite::encoding::mac_roman::from_utf8(resource->name()); - name_len = mac_roman.size() + 1; - if (name_len > 0x100) { - name_len = 0x100; - } - } - - // Write the resource attributes - these are currently hard coded as nothing. - writer->write_byte(0x00); - - // The data offset is a 3 byte (24-bit) value. This means the hi-byte needs discarding - // and then a swap performing. - auto offset = static_cast(resource->data_offset()); - if (offset > 0xFFFFFF) { - throw std::runtime_error("Attempted to write resource file exceeding maximum size."); - } - writer->write_byte((offset >> 16) & 0xFF); - writer->write_byte((offset >> 8) & 0xFF); - writer->write_byte((offset >> 0) & 0xFF); - - // Finally this is a reserved field for use by the ResourceManager. - writer->write_long(0x00000000); - - } - } - - // 6. Finally we write out each of the resource names, and calculate the map length. - name_offset = 0; - for (const auto& type : types) { - for (const auto& resource : type->resources()) { - if (resource->name().empty()) { - continue; - } - - auto mac_roman = graphite::encoding::mac_roman::from_utf8(resource->name()); - if (mac_roman.size() >= 0x100) { - mac_roman.resize(0xFF); - } - name_offset += mac_roman.size() + 1; - - writer->write_byte(static_cast(mac_roman.size())); - writer->write_bytes(mac_roman); - } - } - // Even if the data fits the spec, the resource manager will still not read files larger than 16MB - if (writer->size() > 0xFFFFFF) { - throw std::runtime_error("Attempted to write resource file exceeding maximum size."); - } - map_length = static_cast(writer->size() - map_offset); - - // 7. Fix the preamble values. - writer->set_position(0); - writer->write_long(data_offset); - writer->write_long(map_offset); - writer->write_long(data_length); - writer->write_long(map_length); - - writer->set_position(map_offset); - writer->write_long(data_offset); - writer->write_long(map_offset); - writer->write_long(data_length); - writer->write_long(map_length); - - // Finish by writing the contents of the Resource File to disk. - writer->save(path); -} diff --git a/libGraphite/rsrc/extended.cpp b/libGraphite/rsrc/extended.cpp deleted file mode 100644 index 3f2ef77..0000000 --- a/libGraphite/rsrc/extended.cpp +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include -#include "libGraphite/hints.hpp" -#include "libGraphite/rsrc/extended.hpp" -#include "libGraphite/encoding/macroman/macroman.hpp" - -// MARK: - Parsing / Reading - -auto graphite::rsrc::extended::parse(const std::shared_ptr& reader) -> std::vector> -{ - // 1. Resource File preamble, - GRAPHITE_UNUSED auto version = reader->read_quad(); - auto data_offset = reader->read_quad(); - auto map_offset = reader->read_quad(); - auto data_length = reader->read_quad(); - auto map_length = reader->read_quad(); - - // Before proceeding any further, we need to verify that the resource file is valid. - // We can do this in two ways. We can check the corresponding second header/preamble - // at the start of the resource map, and we can check the lengths provided equal the - // size of the file. - auto rsrc_size = data_offset + data_length + map_length; - if (map_offset != data_offset + data_length) { - throw std::runtime_error("[Extended Resource File] ResourceMap starts at the unexpected location."); - } - - if (rsrc_size != reader->size()) { - throw std::runtime_error("[Extended Resource File] ResourceFile has unexpected length."); - } - - // Now move to the start of the resource map, and verify the contents of the preamble. - reader->set_position(map_offset); - - if (reader->read_quad() != data_offset) { - throw std::runtime_error("[Extended Resource File] Second Preamble 'data_offset' mismatch."); - } - - if (reader->read_quad() != map_offset) { - throw std::runtime_error("[Extended Resource File] Second Preamble 'map_offset' mismatch."); - } - - if (reader->read_quad() != data_length) { - throw std::runtime_error("[Extended Resource File] Second Preamble 'data_length' mismatch."); - } - - if (reader->read_quad() != map_length) { - throw std::runtime_error("[Extended Resource File] Second Preamble 'map_length' mismatch."); - } - - // 2. Now that the preamble is parsed and verified, parse the contents - // of the ResourceMap. The first two fields are used by the actual Resource Manager in - // the Classic Macintosh OS, but are not used by this implementation. - GRAPHITE_UNUSED auto next_map = reader->read_long(); - GRAPHITE_UNUSED auto reference = reader->read_short(); - - // Start to read the actual content of the resource map. This is the content that - // we actually care about. The first field is the flags/attributes of the resource - // fork. - GRAPHITE_UNUSED auto flags = static_cast(reader->read_short()); - - // The next fields are the offsets of the type list and the name list. - auto type_list_offset = static_cast(reader->read_quad()); - auto name_list_offset = static_cast(reader->read_quad()); - auto attribute_list_offset = static_cast(reader->read_quad()); - - // 3. Parse the list of Resource Types. - reader->set_position(map_offset + type_list_offset); - auto type_count = static_cast(reader->read_quad() + 1); - std::vector> types; - - for (auto type_idx = 0; type_idx < type_count; ++type_idx) { - auto code = reader->read_cstr(4); - auto count = static_cast(reader->read_quad() + 1); - auto first_resource_offset = static_cast(reader->read_quad()); - auto attribute_count = static_cast(reader->read_quad()); - auto attribute_offset = static_cast(reader->read_quad()); - - // 4. Extract the list of attributes before we create the type, as they are needed for actually building the - // type. - reader->save_position(); - - std::map attributes; - if (attribute_count > 0) { - reader->set_position(attribute_list_offset + attribute_offset); - for (auto i = 0; i < attribute_count; ++i) { - attributes.insert(std::make_pair(reader->read_cstr(), reader->read_cstr())); - } - } - - auto type = std::make_shared(code, attributes); - - // 5. Parse the list of Resources for the current resource type. - reader->set_position(map_offset + type_list_offset + first_resource_offset); - - for (auto res_idx = 0; res_idx < count; ++res_idx) { - auto id = static_cast(reader->read_signed_quad()); - auto name_offset = reader->read_quad(); - GRAPHITE_UNUSED auto flags = reader->read_byte(); - auto resource_data_offset = reader->read_quad(); - GRAPHITE_UNUSED auto handle = reader->read_long(); - - // 6. Parse out of the name of the resource. - std::string name; - if (name_offset != std::numeric_limits::max()) { - reader->save_position(); - reader->set_position(map_offset + name_list_offset + name_offset); - name = reader->read_pstr(); - reader->restore_position(); - } - - // 7. Create a data slice for the resource's data. - reader->save_position(); - reader->set_position(data_offset + resource_data_offset); - auto data_size = reader->read_quad(); - auto slice = reader->read_data(data_size); - reader->restore_position(); - - // 8. Construct a new resource instance, and add it to the type. - auto resource = std::make_shared(id, type, name, slice); - type->add_resource(resource); - } - - reader->restore_position(); - - // 9. Save the resource type into the list of types and return it. - types.push_back(type); - } - - return types; -} - -// MARK: - Writing - -auto graphite::rsrc::extended::write(const std::string& path, const std::vector>& types) -> void -{ - auto writer = std::make_shared(); - - // 1. Begin setting up the preamble. - uint64_t data_offset = 256; - uint64_t map_offset = 0; - uint64_t data_length = 0; - uint64_t map_length = 0; - - writer->write_quad(1); - writer->write_quad(data_offset); - writer->write_quad(map_offset); - writer->write_quad(data_length); - writer->write_quad(map_length); - writer->pad_to_size(data_offset); - - // 2. Iterate through all of the resources and write their data blobs to the file data. - // When doing this we need to record the starting points of each resources data, as - uint16_t resource_count = 0; - for (const auto& type : types) { - resource_count += type->count(); - - for (const auto& resource : type->resources()) { - // Get the data for the resource and determine its size. - auto data = resource->data(); - auto size = data->size(); - resource->set_data_offset(writer->size() - data_offset); - writer->write_quad(size); - writer->write_data(data); - } - } - - // 3. Start writing the ResourceMap. This consists of several characteristics, - // The first of which is a secondary preamble. We can now calculate the map_offset and - // the data_length, but we're still waiting on the map_length. For now, write these values - // as zeros. - map_offset = writer->size(); - data_length = map_offset - data_offset; - - writer->write_quad(data_offset); - writer->write_quad(map_offset); - writer->write_quad(data_length); - writer->write_quad(map_length); - - // The next six bytes are reserved. - writer->write_byte(0x00, 6); - - // 4. We're now writing the primary map information, which includes flags, and offsets for - // the type list and the name list. We can calculate where each of these will be. - const uint64_t resource_type_length = 36; - const uint64_t resource_length = 29; - uint64_t type_list_offset = 64; // The type list is 64 bytes from the start of the resource map. - uint64_t name_list_offset = type_list_offset + (types.size() * resource_type_length) + (resource_count * resource_length) + sizeof(uint64_t); - uint64_t attribute_list_offset_position = 0; - - writer->write_short(0x0000); - writer->write_quad(type_list_offset); - writer->write_quad(name_list_offset); - - attribute_list_offset_position = writer->position(); - writer->write_quad(attribute_list_offset_position); - - // Now moving on to actually writing each of the type descriptors into the data. - uint64_t attribute_offset = 0; - uint64_t resource_offset = sizeof(uint64_t) + (types.size() * resource_type_length); - writer->write_quad(types.size() - 1); - for (const auto& type : types) { - // We need to ensure that the type code is 4 characters -- otherwise this file will be - // massively corrupt when produced. - auto mac_roman = graphite::encoding::mac_roman::from_utf8(type->code()); - if (mac_roman.size() != 4) { - throw std::runtime_error("Attempted to write invalid type code to Resource File '" + type->code() + "'"); - } - writer->write_bytes(mac_roman); - writer->write_quad(type->count() - 1); - writer->write_quad(resource_offset); - writer->write_quad(type->attributes().size()); - writer->write_quad(attribute_offset); - - for (const auto& attribute : type->attributes()) { - attribute_offset += attribute.first.size() + attribute.second.size() + 2; - } - resource_offset += type->count() * resource_length; - } - - // 5. Now we're writing the actual resource headers. - uint64_t name_offset = 0; - for (const auto& type : types) { - for (const auto& resource : type->resources()) { - - writer->write_signed_quad(resource->id()); - - // The name is actually stored in the name list, and the resource stores an offset - // to that name. If no name is assigned to the resource then the offset is encoded as - // 0xFFFFFFFFFFFFFFFF. - if (resource->name().empty()) { - writer->write_quad(std::numeric_limits::max()); - } - else { - // Convert the name to MacRoman so that we can get the length of it when encoded. - auto mac_roman = graphite::encoding::mac_roman::from_utf8(resource->name()); - uint16_t len = mac_roman.size(); - - writer->write_quad(name_offset); - name_offset += (len >= 0x100 ? 0xFF : len) + 1; - } - writer->write_byte(0x00); // Resource Attribtues - writer->write_quad(resource->data_offset()); - - // Finally this is a reserved field for use by the ResourceManager. - writer->write_long(0x00000000); - - } - } - - // 6. Write out each of the resource names, and calculate the map length. - name_offset = 0; - for (const auto& type : types) { - for (const auto& resource : type->resources()) { - if (resource->name().empty()) { - continue; - } - - auto mac_roman = graphite::encoding::mac_roman::from_utf8(resource->name()); - if (mac_roman.size() >= 0x100) { - mac_roman.resize(0xFF); - } - name_offset += mac_roman.size() + 1; - - writer->write_byte(static_cast(mac_roman.size())); - writer->write_bytes(mac_roman); - } - } - - // 7. Finally write out a list of attributes, but make sure the actual location of this attribute list is - // kept correct. - auto pos = writer->position(); - writer->set_position(attribute_list_offset_position); - writer->write_quad(pos); - writer->set_position(pos); - - attribute_offset = 0; - for (const auto& type : types) { - const auto& attributes = type->attributes(); - - auto initial = writer->position(); - for (const auto& attribute : attributes) { - writer->write_cstr(attribute.first); - writer->write_cstr(attribute.second); - } - - attribute_offset += (writer->position() - initial); - } - map_length = static_cast(writer->size() - map_offset); - - // 8. Fix the preamble values. - writer->set_position(sizeof(uint64_t)); - writer->write_quad(data_offset); - writer->write_quad(map_offset); - writer->write_quad(data_length); - writer->write_quad(map_length); - - writer->set_position(map_offset); - writer->write_quad(data_offset); - writer->write_quad(map_offset); - writer->write_quad(data_length); - writer->write_quad(map_length); - - // Finish by writing the contents of the Resource File to disk. - writer->save(path); -} diff --git a/libGraphite/rsrc/file.cpp b/libGraphite/rsrc/file.cpp deleted file mode 100644 index 1a2454d..0000000 --- a/libGraphite/rsrc/file.cpp +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include "libGraphite/rsrc/file.hpp" -#include "libGraphite/data/reader.hpp" -#include "libGraphite/rsrc/classic.hpp" -#include "libGraphite/rsrc/extended.hpp" -#include "libGraphite/rsrc/rez.hpp" - -// MARK: - Construct - -graphite::rsrc::file::file(std::string path) - : m_path(std::move(path)) -{ - read(m_path); -} - -// MARK: - Accessors - -auto graphite::rsrc::file::type_count() const -> std::size_t -{ - return m_types.size(); -} - -auto graphite::rsrc::file::types() const -> std::vector> -{ - return m_types; -} - -auto graphite::rsrc::file::current_format() const -> graphite::rsrc::file::format -{ - return m_format; -} - -auto graphite::rsrc::file::name() const -> std::string -{ - std::string out; - auto path = m_path; - - while (!m_path.empty()) { - if (path.back() == '.') { - out.clear(); - path.pop_back(); - } - else if (path.back() == '/') { - return out; - } - else { - out.insert(out.begin(), path.back()); - path.pop_back(); - } - } - - return out; -} - -auto graphite::rsrc::file::path() const -> std::string -{ - return m_path; -} - - -// MARK: - File Reading - -auto graphite::rsrc::file::read(const std::string& path) -> void -{ - // Load the file data and prepare to parse the contents of the resource - // file. We also need to keep hold of the actual internal data. - auto reader = std::make_shared(path); - m_data = reader->get(); - - // 1. Determine the file format and validity. - if (reader->read_quad(0, graphite::data::reader::mode::peek) == 1) { - m_format = graphite::rsrc::file::format::extended; - } - else if (reader->read_long(0, graphite::data::reader::mode::peek) == 'BRGR') { - m_format = graphite::rsrc::file::format::rez; - } - else { - m_format = graphite::rsrc::file::format::classic; - } - - // 2. Launch the appropriate parser for the current format of the file. - switch (m_format) { - case graphite::rsrc::file::format::classic: { - m_types = graphite::rsrc::classic::parse(reader); - break; - } - case graphite::rsrc::file::format::extended: { - m_types = graphite::rsrc::extended::parse(reader); - break; - } - case graphite::rsrc::file::format::rez: { - m_types = graphite::rsrc::rez::parse(reader); - break; - } - - default: - throw std::runtime_error("Resource File format not currently handled."); - break; - } -} - -// MARK: - File Writing - -auto graphite::rsrc::file::write(const std::string& path, enum graphite::rsrc::file::format fmt) -> void -{ - // Determine the correct location to save to, or throw an error. - auto write_path = path; - if (path.empty()) { - if (m_path.empty()) { - throw std::runtime_error("Unable to write resource file to disk. No save location provided."); - } - write_path = m_path; - } - - // Update the file path accordingly. - m_path = write_path; - - // Perform the write operation... - switch (fmt) { - case graphite::rsrc::file::format::classic: { - graphite::rsrc::classic::write(write_path, m_types); - break; - } - case graphite::rsrc::file::format::extended: { - graphite::rsrc::extended::write(write_path, m_types); - break; - } - case graphite::rsrc::file::format::rez: { - graphite::rsrc::rez::write(write_path, m_types); - break; - } - } - -} - -// MARK: - Resource Managemnet - -auto graphite::rsrc::file::add_resource(const std::string &code, const int64_t &id, const std::string &name, - const std::shared_ptr &data, - const std::map &attributes) -> void -{ - // Get the container - if (auto type = type_container(code, attributes).lock()) { - // Add the resource... - auto resource = std::make_shared(id, type, name, data); - type->add_resource(resource); - return; - } - - throw std::runtime_error("Failed to find or create resource type container for " + code); -} - -auto graphite::rsrc::file::type_container(const std::string &code, - const std::map &attributes) -> std::weak_ptr -{ - for (const auto& type : m_types) { - if (type->code() == code && type->attributes() == attributes) { - // Note: Not sure if this is the correct solution here (if not the solution will need some further though)t - // This currently assumes that the attributes are part of the type definition, and that - // `alph` != `alph:lang=en` != `alph:lang=en:bar=2` - return type; - } - } - - auto type = std::make_shared(code, attributes); - m_types.push_back(type); - return type; -} - -auto graphite::rsrc::file::find(const std::string& type, const int64_t& id, const std::map &attributes) -> std::weak_ptr -{ - if (auto container = type_container(type, attributes).lock()) { - return container->get(id); - } - return {}; -} - -auto graphite::rsrc::file::find(const std::string &type, const std::string &name_prefix, - const std::map &attributes) -> std::vector> -{ - if (auto container = type_container(type, attributes).lock()) { - return container->get(name_prefix); - } - return {}; -} \ No newline at end of file diff --git a/libGraphite/rsrc/file.hpp b/libGraphite/rsrc/file.hpp deleted file mode 100644 index 17b186b..0000000 --- a/libGraphite/rsrc/file.hpp +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include -#include -#include -#include "libGraphite/data/data.hpp" -#include "libGraphite/rsrc/type.hpp" - -#if !defined(GRAPHITE_RSRC_FILE) -#define GRAPHITE_RSRC_FILE - -namespace graphite::rsrc { - - /** - * The `graphite::rsrc::file` represents a ResourceFork file. - */ - class file - { - public: - - enum flags : uint8_t { }; - - /** - * Denotes the format of a resource file being represented. - * - * + classic - * The classic resource file format is that found in the original Macintosh - * operating systems. This is the only format used by the Diamond Project. - * - * + extended - * The extended resource file format is extremely similar to the classic - * format, but has a number of extensions for modern use. This is primarily - * used by Kestrel. - * - * + rez - * The rez resource file format was a format that was created for use within - * EV Nova for Windows. This is used as a compatibility option for Kestrel. - */ - enum format { classic, extended, rez }; - - private: - std::string m_path; - std::vector> m_types; - std::shared_ptr m_data { nullptr }; - format m_format { classic }; - - public: - /** - * Construct a new blank `graphite::rsrc::file`. - */ - file() = default; - - /** - * Construct a new `graphite::rsrc::file` by loading the contents of the specified - * file. - */ - explicit file(std::string path); - - /** - * Read and parse the contents of the resource file at the specified location. - * Warning: This will destroy all existing information in the resource file. - */ - auto read(const std::string& path) -> void; - - /** - * Write the contents of the the resource file to disk. If no location is specified, - * then it will use the original read path (if it exists). - */ - auto write(const std::string& path = "", enum format fmt = classic) -> void; - - /** - * Returns the name of the file. - */ - [[nodiscard]] auto name() const -> std::string; - - /** - * Returns the path of the file. - */ - [[nodiscard]] auto path() const -> std::string; - - /** - * Returns the number of types contained in the resource file. - */ - [[nodiscard]] auto type_count() const -> std::size_t; - - /** - * Returns the list of all types contained in the resource file. - */ - [[nodiscard]] auto types() const -> std::vector>; - - /** - * Reports the current format of the resource file. - */ - [[nodiscard]] auto current_format() const -> format; - - /** - * Add a resource into the receiver. - */ - auto add_resource(const std::string& type, - const int64_t& id, - const std::string& name, - const std::shared_ptr& data, - const std::map& attributes = {}) -> void; - - /** - * Retrieve a type container for the specified type code. If a container - * does not exist then create one. - */ - auto type_container(const std::string& code, - const std::map& attributes = {}) -> std::weak_ptr; - - /** - * Attempt to get the resource of the specified type, id and attributes - */ - auto find(const std::string& type, const int64_t& id, const std::map &attributes) -> std::weak_ptr; - - /** - * Retrieve a set of resources from the file, whose name begin with the specified prefix (or match exactly) - * for the specified type and attributes. - */ - auto find(const std::string& type, const std::string& name_prefix, const std::map& attributes) -> std::vector>; - }; - -} - -#endif diff --git a/libGraphite/rsrc/manager.cpp b/libGraphite/rsrc/manager.cpp deleted file mode 100644 index d87cf1e..0000000 --- a/libGraphite/rsrc/manager.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include -#include "libGraphite/rsrc/manager.hpp" -#include "libGraphite/rsrc/file.hpp" - -// MARK: - Singleton - - -auto graphite::rsrc::manager::shared_manager() -> graphite::rsrc::manager& -{ - static rsrc::manager manager; - return manager; -} - -// MARK: - File Management - -auto graphite::rsrc::manager::import_file(const std::shared_ptr& file) -> void -{ - m_files.push_back(file); -} - -auto graphite::rsrc::manager::files() const -> std::vector> -{ - return m_files; -} - -auto graphite::rsrc::manager::unload_file(const std::string &path) -> void -{ - std::vector> updated_files; - for (const auto& file : m_files) { - if (file->path() != path) { - updated_files.emplace_back(file); - } - } - m_files = updated_files; -} - -// MARK: - Resource Look Up - -auto graphite::rsrc::manager::find(const std::string& type, const int64_t& id, const std::map& attributes) const -> std::weak_ptr -{ - for (auto i = m_files.rbegin(); i != m_files.rend(); ++i) { - auto res = (*i)->find(type, id, attributes); - if (!res.expired()) { - return res; - } - } - return std::weak_ptr(); -} - -auto graphite::rsrc::manager::get_type(const std::string &type, const std::map& attributes) const -> std::vector> -{ - std::vector> v; - for (const auto& file : m_files) { - v.emplace_back(file->type_container(type, attributes)); - } - return v; -} - -auto graphite::rsrc::manager::find(const std::string &type, const std::string &name_prefix, - const std::map &attributes) -> std::vector> -{ - std::vector> v; - for (auto i = m_files.rbegin(); i != m_files.rend(); ++i) { - auto resources = (*i)->find(type, name_prefix, attributes); - for (const auto& r : resources) { - // The resource in the most recently loaded files, wins here. We're traversing the files in reverse order, - // and using the resource, only if it hasn't already been seen. - if (std::none_of(v.cbegin(), v.cend(), [r, v] (const std::shared_ptr& e) { return e->id() == r->id(); })) { - v.emplace_back(r); - } - } - } - return v; -} diff --git a/libGraphite/rsrc/manager.hpp b/libGraphite/rsrc/manager.hpp deleted file mode 100644 index 7b54f33..0000000 --- a/libGraphite/rsrc/manager.hpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include -#include -#include -#include "libGraphite/rsrc/file.hpp" - -#if !defined(GRAPHITE_RSRC_MANAGER) -#define GRAPHITE_RSRC_MANAGER - -namespace graphite::rsrc { - - /** - * The Manager is a shared object within an application that is keeps track of - * all loaded resources. - * - * Resources are loaded in through a resource file and then placed into the - * resource manager to be "managed" if a newly imported resource conflicts with - * an existing resource, the existing resource will be replaced by the newer - */ - class manager - { - private: - std::vector> m_files; - manager() = default; - - public: - manager(const manager&) = delete; - manager& operator=(const manager &) = delete; - manager(manager &&) = delete; - manager & operator=(manager &&) = delete; - - /** - * The Resource Manager is a singleton due to the "resource space" - * of a process being shared. All resource files loaded into a process - * occupy the same space and are able to override each other, depending - * on load order. - */ - static auto shared_manager() -> manager&; - - /** - * Import the specified file into the Manager. This will trigger a parsing - * of the file if it hasn't already occurred. - */ - auto import_file(const std::shared_ptr& file) -> void; - - /** - * Unload the specified file from the Manager. - */ - auto unload_file(const std::string& path) -> void; - - /** - * - */ - [[nodiscard]] auto files() const -> std::vector>; - - /** - * Attempt to get the resource of the specified type and id. - */ - [[nodiscard]] auto find(const std::string& type, const int64_t& id, const std::map& attributes = {}) const -> std::weak_ptr; - - /** - * Returns a list of type containers for the specified type code. - */ - [[nodiscard]] auto get_type(const std::string& type, const std::map& attributes = {}) const -> std::vector>; - - /** - * Retrieve a set of resources from the manager, whose name begin with the specified prefix (or match exactly) - * for the specified type and attributes. - */ - [[nodiscard]] auto find(const std::string& type, const std::string& name_prefix, const std::map& attributes) -> std::vector>; - }; - -} - -#endif \ No newline at end of file diff --git a/libGraphite/rsrc/resource.cpp b/libGraphite/rsrc/resource.cpp deleted file mode 100644 index 844934e..0000000 --- a/libGraphite/rsrc/resource.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "libGraphite/rsrc/resource.hpp" -#include "libGraphite/rsrc/type.hpp" - -// MARK: - Constructor - -graphite::rsrc::resource::resource(int64_t id, std::string name) - : m_id(id), m_name(std::move(name)) -{ - -} - -graphite::rsrc::resource::resource( - int64_t id, - std::weak_ptr type, - std::string name, - std::shared_ptr data -) - : m_id(id), m_name(std::move(name)), m_type(std::move(type)), m_data(std::move(data)) -{ - -} - -// MARK: - Resource Metadata Accessors - -auto graphite::rsrc::resource::id() const -> int64_t -{ - return m_id; -} - -auto graphite::rsrc::resource::set_id(int64_t id) -> void -{ - m_id = id; -} - -auto graphite::rsrc::resource::name() const -> std::string -{ - return m_name; -} - -auto graphite::rsrc::resource::set_name(const std::string& name) -> void -{ - m_name = name; -} - -// MARK: - Resource Type - -auto graphite::rsrc::resource::type() const -> std::weak_ptr -{ - return m_type; -} - -auto graphite::rsrc::resource::set_type(const std::weak_ptr& type) -> void -{ - m_type = type; -} - -auto graphite::rsrc::resource::type_code() const -> std::string -{ - if (auto type = m_type.lock()) { - return type->code(); - } - else { - return "????"; - } -} - - -// MARK: - Data - -auto graphite::rsrc::resource::data() -> std::shared_ptr -{ - return m_data; -} - -auto graphite::rsrc::resource::set_data(const std::shared_ptr& data) -> void -{ - m_data = data; -} - -// MARK: - - -auto graphite::rsrc::resource::set_data_offset(const std::size_t& offset) -> void -{ - m_data_offset = offset; -} - -auto graphite::rsrc::resource::data_offset() const -> std::size_t -{ - return m_data_offset; -} diff --git a/libGraphite/rsrc/resource.hpp b/libGraphite/rsrc/resource.hpp deleted file mode 100644 index c6a55b9..0000000 --- a/libGraphite/rsrc/resource.hpp +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include -#include -#include "libGraphite/data/data.hpp" - -#if !defined(GRAPHITE_RSRC_RESOURCE) -#define GRAPHITE_RSRC_RESOURCE - -namespace graphite::rsrc { - - class type; - - /** - * The `graphite::rsrc::resource` class represents a single resource within - * a resource file. - */ - class resource - { - private: - int64_t m_id {}; - std::weak_ptr m_type; - std::string m_name; - std::shared_ptr m_data; - std::size_t m_data_offset { 0 }; - - public: - /** - * Construct a new resource instance, without an explicit type or - * data. - */ - resource(int64_t id, std::string name); - - /** - * Construct a new resource instance with the specified type, and data. - */ - resource(int64_t id, std::weak_ptr type, std::string name, std::shared_ptr data); - - /** - * Returns the ID of the resource. - */ - [[nodiscard]] auto id() const -> int64_t; - - /** - * Set the ID of the resource. - */ - auto set_id(int64_t id) -> void; - - /** - * Returns the name of the resource. - */ - [[nodiscard]] auto name() const -> std::string; - - /** - * Set the name of the resource. - */ - auto set_name(const std::string& name) -> void; - - /** - * Returns the type container of the resource. - */ - [[nodiscard]] auto type() const -> std::weak_ptr; - - /** - * Set the type container of the resource. - */ - auto set_type(const std::weak_ptr& type) -> void; - - /** - * Returns the type code of the resource. - */ - [[nodiscard]] auto type_code() const -> std::string; - - /** - * Returns a shared pointer to the contained data. - */ - auto data() -> std::shared_ptr; - - /** - * Set the name of the resource. - */ - auto set_data(const std::shared_ptr& data) -> void; - - /** - * Store the location of the data within the resource file. - */ - auto set_data_offset(const std::size_t& offset) -> void; - - /** - * The location of the data within the resource file. - */ - [[nodiscard]] auto data_offset() const -> std::size_t; - }; - -} - -#endif diff --git a/libGraphite/rsrc/rez.cpp b/libGraphite/rsrc/rez.cpp deleted file mode 100644 index 8c550a9..0000000 --- a/libGraphite/rsrc/rez.cpp +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include "libGraphite/rsrc/rez.hpp" -#include "libGraphite/encoding/macroman/macroman.hpp" - -// MARK: - Parsing / Reading - -auto graphite::rsrc::rez::parse(const std::shared_ptr& reader) -> std::vector> -{ - // Read the preamble - if (reader->read_long() != rez_signature) { - throw std::runtime_error("[Rez File] Preamble 'signature' mismatch."); - } - reader->get()->set_byte_order(graphite::data::byte_order::lsb); - if (reader->read_long() != rez_version) { - throw std::runtime_error("[Rez File] Preamble 'version' mismatch."); - } - auto header_length = reader->read_long(); - - // Read the header - reader->move(4); // Unknown value - auto first_index = reader->read_long(); - auto count = reader->read_long(); - uint32_t expected_header_length = 12 + (count * resource_offset_length) + map_name.size()+1; - if (header_length != expected_header_length) { - throw std::runtime_error("[Rez File] Preamble 'header_length' mismatch."); - } - - // Record the offsets - std::vector offsets; - std::vector sizes; - for (auto res_idx = 0; res_idx < count; res_idx++) { - offsets.push_back(static_cast(reader->read_long())); - sizes.push_back(static_cast(reader->read_long())); - reader->move(4); // Unknown value - } - if (reader->read_cstr() != map_name) { - throw std::runtime_error("[Rez File] Header 'map_name' mismatch."); - } - - // Read the resource map header - reader->get()->set_byte_order(graphite::data::byte_order::msb); - auto map_offset = offsets.back(); - reader->set_position(map_offset); - reader->move(4); // Unknown value - auto type_count = reader->read_long(); - - // Read the types - std::vector> types; - for (auto type_idx = 0; type_idx < type_count; type_idx++) { - auto code = reader->read_cstr(4); - auto type_offset = static_cast(reader->read_long()); - auto count = reader->read_long(); - auto type = std::make_shared(code); - - reader->save_position(); - reader->set_position(map_offset + type_offset); - - // Read the resource info - for (auto res_idx = 0; res_idx < count; res_idx++) { - auto index = reader->read_long(); - auto code = reader->read_cstr(4); - if (code != type->code()) { - throw std::runtime_error("[Rez File] Resource 'type' mismatch."); - } - auto id = static_cast(reader->read_signed_short()); - // The name is padded to 256 bytes - note the end position before reading the cstr - auto nextOffset = reader->position() + 256; - auto name = reader->read_cstr(); - - // Read the resource's data - reader->set_position(offsets[index-first_index]); - auto slice = reader->read_data(sizes[index-first_index]); - reader->set_position(nextOffset); - - auto resource = std::make_shared(id, type, name, slice); - type->add_resource(resource); - } - - reader->restore_position(); - types.push_back(type); - } - - return types; -} - -// MARK: - Writing - -auto graphite::rsrc::rez::write(const std::string& path, const std::vector>& types) -> void -{ - auto writer = std::make_shared(); - - // Count up the total number of resources - uint32_t resource_count = 0; - for (const auto& type : types) { - resource_count += type->count(); - } - - // The resource map itself is considered an entry for the offsets in the header - uint32_t entry_count = resource_count + 1; - - // Calculate header length - this is from the end of the preamble to the start of the resource data - uint32_t header_length = 12 + (entry_count * resource_offset_length) + map_name.size()+1; - - // Write the preamble - writer->write_long(rez_signature); - writer->data()->set_byte_order(graphite::data::byte_order::lsb); - writer->write_long(rez_version); - writer->write_long(header_length); - - // Calculate the offset to the first resource data - uint32_t resource_offset = static_cast(writer->size()) + header_length; - - // Write the header - uint32_t index = 1; // Index of first resource, starting at 1 - writer->write_long(1); // Unknown value - writer->write_long(index); - writer->write_long(entry_count); - for (const auto& type : types) { - for (const auto& resource : type->resources()) { - // Get the data for the resource and determine its size. - auto data = resource->data(); - auto size = data->size(); - writer->write_long(resource_offset); - writer->write_long(static_cast(size)); - writer->write_long(0); // Unknown value - resource_offset += size; - } - } - - uint32_t type_count = types.size(); - // Calculate offset within map to start of resource info - uint32_t type_offset = map_header_length + (type_count * type_info_length); - uint32_t map_length = type_offset + (resource_count * resource_info_length); - // Write the offset and size of the resource map - writer->write_long(resource_offset); - writer->write_long(map_length); - writer->write_long(12 + (entry_count * resource_offset_length)); // Unknown value - - // Write the name of the resource map - writer->write_cstr(map_name); - - // Write each resource - for (const auto& type : types) { - for (const auto& resource : type->resources()) { - writer->write_data(resource->data()); - } - } - - // Write the resource map as big endian - // Map header - writer->data()->set_byte_order(graphite::data::byte_order::msb); - writer->write_long(8); // Unknown value - writer->write_long(type_count); - - // Type counts and offsets - for (const auto& type : types) { - auto count = type->count(); - writer->write_cstr(type->code(), 4); - writer->write_long(type_offset); - writer->write_long(count); - type_offset += resource_info_length * count; - } - - // Info for each resource - for (const auto& type : types) { - for (const auto& resource : type->resources()) { - writer->write_long(index++); - writer->write_cstr(type->code(), 4); - writer->write_signed_short(static_cast(resource->id())); - writer->write_cstr(resource->name(), 256); - } - } - - // Finish by writing the contents of the Rez file to disk. - writer->save(path); -} diff --git a/libGraphite/rsrc/type.cpp b/libGraphite/rsrc/type.cpp deleted file mode 100644 index 5276154..0000000 --- a/libGraphite/rsrc/type.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include -#include "libGraphite/rsrc/type.hpp" - - -// MARK: - Constructor - -graphite::rsrc::type::type(std::string code, std::map attributes) - : m_code(std::move(code)), m_attributes(std::move(attributes)) -{ - -} - -// MARK: - Metadata Accessors - -auto graphite::rsrc::type::code() const -> std::string -{ - return m_code; -} - -auto graphite::rsrc::type::attributes() const -> std::map -{ - return m_attributes; -} - -auto graphite::rsrc::type::attributes_string() const -> std::string -{ - std::string text; - - for (const auto& m_attribute : m_attributes) { - text.append(":" + m_attribute.first + "=" + (m_attribute.second)); - } - - return text; -} - -// MARK: - Resource Management - -auto graphite::rsrc::type::count() const -> std::size_t -{ - return m_resources.size(); -} - -auto graphite::rsrc::type::add_resource(const std::shared_ptr& resource) -> void -{ - // Search for an existing instance of this resource (same id) - m_resources.erase(std::remove_if(m_resources.begin(), m_resources.end(), [resource] (const auto& item) { - return item->id() == resource->id(); - }), m_resources.end()); - - m_resources.push_back(resource); -} - -auto graphite::rsrc::type::resources() const -> std::vector> -{ - return m_resources; -} - -auto graphite::rsrc::type::get(int16_t id) const -> std::weak_ptr -{ - for (const auto& resource : m_resources) { - if (resource->id() == id) { - return resource; - } - } - return std::weak_ptr(); -} - -auto graphite::rsrc::type::get(const std::string &name_prefix) const -> std::vector> -{ - std::vector> v; - for (const auto& resource : m_resources) { - const auto& name = resource->name(); - if (name.length() == name_prefix.length() && name == name_prefix) { - v.emplace_back(resource); - } - else if (name.length() >= name_prefix.length() && name.substr(0, name_prefix.length()) == name_prefix) { - v.emplace_back(resource); - } - } - return v; -} diff --git a/libGraphite/rsrc/type.hpp b/libGraphite/rsrc/type.hpp deleted file mode 100644 index f79eec3..0000000 --- a/libGraphite/rsrc/type.hpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2020 Tom Hancocks -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include -#include -#include -#include "libGraphite/rsrc/resource.hpp" - -#if !defined(GRAPHITE_RSRC_TYPE) -#define GRAPHITE_RSRC_TYPE - -namespace graphite::rsrc { - - /** - * - */ - class type - { - private: - std::string m_code; - std::vector> m_resources; - std::map m_attributes; - - public: - /** - * Construct a new a resource type container with the specified - * type code. - */ - explicit type(std::string code, std::map attributes = {}); - - /** - * Returns the type code of the receiver. - */ - [[nodiscard]] auto code() const -> std::string; - - /** - * Returns the attribute map of the receiver. - */ - [[nodiscard]] auto attributes() const -> std::map; - - /** - * Returns the attribute map of the receiver as a string. - */ - [[nodiscard]] auto attributes_string() const -> std::string; - - /** - * Returns a count of the number of resources associated to this type. - */ - [[nodiscard]] auto count() const -> std::size_t; - - /** - * Add a new resource to the receiver. - */ - auto add_resource(const std::shared_ptr& resource) -> void; - - /** - * Returns an vector containing all of the resources - */ - [[nodiscard]] auto resources() const -> std::vector>; - - /** - * Returns the resource with the specified ID. - */ - [[nodiscard]] auto get(int16_t id) const -> std::weak_ptr; - - /** - * Returns a set of resources whose name begins with the specified text, or matches wholly. - */ - [[nodiscard]] auto get(const std::string& name_prefix) const -> std::vector>; - }; - -} - -#endif \ No newline at end of file diff --git a/libs/libCommon/concepts.hpp b/libs/libCommon/concepts.hpp new file mode 100644 index 0000000..b0f8cb8 --- /dev/null +++ b/libs/libCommon/concepts.hpp @@ -0,0 +1,39 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace common +{ + template + concept convertible_to = std::is_convertible_v && requires { + static_cast(std::declval()); + }; + + template + concept constructible_from = std::destructible && std::is_constructible_v; + + template + concept move_constructible = constructible_from && convertible_to; +} \ No newline at end of file diff --git a/libGraphite/hints.hpp b/libs/libCommon/hints.hpp similarity index 69% rename from libGraphite/hints.hpp rename to libs/libCommon/hints.hpp index c7d0272..1874311 100644 --- a/libGraphite/hints.hpp +++ b/libs/libCommon/hints.hpp @@ -2,13 +2,10 @@ // Created by Tom Hancocks on 24/03/2020. // -#if !defined(GRAPHITE_HINTS_HPP) -#define GRAPHITE_HINTS_HPP +#pragma once #if _MSC_VER # define GRAPHITE_UNUSED #else # define GRAPHITE_UNUSED __attribute__((unused)) #endif - -#endif diff --git a/libs/libCommon/memory/alignment.hpp b/libs/libCommon/memory/alignment.hpp new file mode 100644 index 0000000..9f12579 --- /dev/null +++ b/libs/libCommon/memory/alignment.hpp @@ -0,0 +1,43 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace common::memory::alignment +{ + static constexpr std::size_t width = sizeof(std::uintptr_t); + static constexpr std::size_t mask = ~(width - 1); + + template::value>::type* = nullptr> + static inline auto expand_capacity(T capacity) -> T + { + return (capacity + width - 1) & mask; + } + + template::value>::type* = nullptr> + static inline auto align(T ptr) -> T + { + return reinterpret_cast((reinterpret_cast(ptr) + width - 1) & mask); + } +} \ No newline at end of file diff --git a/libs/libCommon/sys/types.hpp b/libs/libCommon/sys/types.hpp new file mode 100644 index 0000000..589e2fb --- /dev/null +++ b/libs/libCommon/sys/types.hpp @@ -0,0 +1,57 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +// MARK: - Helper Macros + +#if defined(GRAPHITE_USE_CAMEL_CASE) +# define GRAPHITE_SYMBOL(_snake, _camel, _posix) _camel +#else +# define GRAPHITE_SYMBOL(_snake, _camel, _posix) _snake +#endif + +// MARK: - Classic Macintosh Integer types + +namespace common +{ + typedef signed char GRAPHITE_SYMBOL(sint8, SInt8, int8_t); + typedef unsigned char GRAPHITE_SYMBOL(uint8, UInt8, uint8_t); + typedef signed short GRAPHITE_SYMBOL(sint16, SInt16, int16_t); + typedef unsigned short GRAPHITE_SYMBOL(uint16, UInt16, uint16_t); + typedef signed long GRAPHITE_SYMBOL(sint32, SInt32, int32_t); + typedef unsigned long GRAPHITE_SYMBOL(uint32, UInt32, uint32_t); + typedef signed long long GRAPHITE_SYMBOL(sint64, SInt64, int64_t); + typedef unsigned long long GRAPHITE_SYMBOL(uint64, UInt64, uint64_t); +} + +// MARK: - ResEdit Integer Types + +namespace graphite +{ + typedef signed char DBYT; + typedef unsigned char HBYT; + typedef signed short DWRD; + typedef unsigned short HWRD; + typedef signed long DLNG; + typedef unsigned long HLNG; + typedef signed long long DQWD; + typedef unsigned long long HQWD; +} \ No newline at end of file diff --git a/libs/libCompression/packbits.hpp b/libs/libCompression/packbits.hpp new file mode 100644 index 0000000..a562278 --- /dev/null +++ b/libs/libCompression/packbits.hpp @@ -0,0 +1,122 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace compression +{ + template + struct packbits + { + static auto decompress(const data::block& compressed) -> data::block + { + std::size_t value_size = Bits >> 3; + data::reader reader(&compressed); + data::writer writer; + + while (!reader.eof()) { + auto count = reader.read_byte(); + if (count < 128) { + std::uint16_t run = (1 + count) * value_size; + if ((reader.position() + run) > reader.size()) { + throw std::runtime_error("Unable to decode packbits"); + } + auto data = std::move(reader.read_data(run)); + writer.write_data(&data); + } + else if (count == 128) { + // No Operation + } + else if (value_size == 1) { + // Run of single bytes (fast) + std::uint8_t run = 256 - count + 1; + writer.write_byte(reader.read_byte(), run); + } + else { + std::uint8_t run = 256 - count + 1; + for (std::uint8_t i = 0; i < run; ++i) { + for (std::uint8_t j = 0; j < value_size; ++j) { + writer.write_byte(reader.read_byte(j, data::reader::mode::peek)); + } + } + reader.move(value_size); + } + } + + return std::move(*const_cast(writer.data())); + } + + static auto compress(const data::block& uncompressed) -> data::block { + data::block buffer(128); + data::writer writer; + + auto offset = 0; + const auto max = uncompressed.size() - 1; + const auto max_minus_1 = max - 1; + + while (offset <= max) { + // Compressed run + auto run = 1; + auto replicate = uncompressed.get(offset); + while (run < 127 && offset < max && uncompressed.get(offset) == uncompressed.get(offset + 1)) { + ++offset; + ++run; + } + + if (run > 1) { + ++offset; + writer.write_byte(static_cast(-(run - 1))); + writer.write_byte(replicate); + } + + // Literal run + run = 0; + while (run < 128 && ((offset < max && uncompressed.get(offset) != uncompressed.get(offset + 1)) + || (offset < max_minus_1 && uncompressed.get(offset) != uncompressed.get(offset + 2)))) + { + buffer.set(uncompressed.get(offset++), 1, run++); + } + + if (offset == max && run > 0 && run < 128) { + buffer.set(uncompressed.get(offset++), 1, run++); + } + + if (run > 0) { + auto sliced = std::move(buffer.slice(0, run)); + writer.write_byte(run - 1); + writer.write_data(&sliced); + buffer.clear(); + } + + if (offset == max && (run <= 0 || run >= 128)) { + writer.write_byte(0); + writer.write_byte(uncompressed.get(offset++)); + } + } + + return std::move(*const_cast(writer.data())); + } + }; +} \ No newline at end of file diff --git a/libs/libData/CMakeLists.txt b/libs/libData/CMakeLists.txt new file mode 100644 index 0000000..0f2c61d --- /dev/null +++ b/libs/libData/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2023 Tom Hancocks +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) + +######################################################################################################################## +## Project +project(Graphite LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) + +######################################################################################################################## +## libSIMD +file(GLOB_RECURSE libData_Sources + *.cpp +) + +add_library(Data ${libData_Sources}) +target_link_libraries(Data SIMD Encoding) +target_include_directories(Data PUBLIC + ${PROJECT_LIBS_DIR} +) diff --git a/libs/libData/block.cpp b/libs/libData/block.cpp new file mode 100644 index 0000000..d67f719 --- /dev/null +++ b/libs/libData/block.cpp @@ -0,0 +1,331 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// MARK: - Construction + +data::block::block(std::size_t capacity, enum byte_order order) + : m_raw_size(common::memory::alignment::expand_capacity(capacity)), + m_data_size(capacity), + m_raw(malloc(m_raw_size)), + m_data(common::memory::alignment::align(m_raw)), + m_allocation_owner(nullptr), + m_byte_order(order), + m_has_ownership(true) +{ +} + +data::block::block(std::size_t capacity, std::size_t allocation_size, enum byte_order order) + : m_raw_size(common::memory::alignment::expand_capacity(allocation_size)), + m_data_size(capacity), + m_raw(malloc(m_raw_size)), + m_data(common::memory::alignment::align(m_raw)), + m_allocation_owner(nullptr), + m_byte_order(order), + m_has_ownership(true) +{ +} + +data::block::block(const std::string &path, enum byte_order order) + : m_byte_order(order), + m_allocation_owner(nullptr), + m_has_ownership(true) +{ + std::ifstream file { path, std::ios::binary }; + if (!file.is_open() || file.fail()) { + throw std::runtime_error("Failed to open data file: " + path); + } + + file.unsetf(std::ios::skipws); + file.seekg(0, std::ios::end); + m_data_size = file.tellg(); + file.seekg(0, std::ios::beg); + + m_raw_size = common::memory::alignment::expand_capacity(m_data_size); + m_raw = malloc(m_raw_size); + m_data = common::memory::alignment::align(m_raw); + file.read(reinterpret_cast(m_data), m_data_size); + + file.close(); +} + +data::block::block(const std::vector& bytes, enum byte_order order) + : m_byte_order(order), + m_allocation_owner(nullptr), + m_has_ownership(true) +{ + m_data_size = bytes.size(); + m_raw_size = common::memory::alignment::expand_capacity(m_data_size); + m_raw = malloc(m_raw_size); + m_data = common::memory::alignment::align(m_raw); + + // TODO: This is slow, and should be speeded up in the future. + auto ptr = static_cast(m_data); + for (const auto& byte : bytes) { + *ptr++ = byte; + } +} + +data::block::block(const block &source, bool copy) + : m_raw_size(source.m_raw_size), + m_data_size(source.m_data_size), + m_start_position(source.m_start_position), + m_count(source.m_count), + m_byte_order(source.m_byte_order), + m_allocation_owner(copy ? nullptr : &source), + m_has_ownership(copy), + m_extended(source.m_extended) +{ + clone_from(source); +} + +data::block::block(const block &source, block::position pos, std::size_t amount, bool copy) + : m_allocation_owner(copy ? nullptr : &source), + m_byte_order(source.m_byte_order), + m_extended(source.m_extended) +{ + if (m_allocation_owner) { + m_raw = source.m_raw; + m_data = source.m_data; + m_raw_size = source.m_raw_size; + m_data_size = source.m_data_size; + m_start_position = pos; + m_count = amount; + m_has_ownership = false; + + const_cast(m_allocation_owner)->m_users++; + } + else { + m_raw_size = common::memory::alignment::expand_capacity(amount); + m_data_size = amount; + m_raw = malloc(m_raw_size); + m_data = common::memory::alignment::align(m_raw); + m_start_position = 0; + m_count = 0; + m_has_ownership = true; + } +} + +data::block::block(const void *data, std::size_t count, bool take_ownership, enum byte_order order) + : m_allocation_owner(nullptr), + m_has_ownership(take_ownership), + m_raw(const_cast(data)), + m_data(const_cast(data)), + m_raw_size(count), + m_data_size(count), + m_start_position(0), + m_count(count), + m_byte_order(order) +{ +} + +data::block::block(const block &data) + : m_raw_size(data.m_raw_size), + m_data_size(data.m_data_size), + m_allocation_owner(nullptr), + m_start_position(data.m_start_position), + m_users(0), + m_count(data.m_count), + m_byte_order(data.m_byte_order), + m_has_ownership(data.m_has_ownership), + m_extended(data.m_extended) +{ + if (m_has_ownership) { + m_raw = malloc(m_raw_size); + m_data = common::memory::alignment::align(m_raw); + copy_from(data); + } + else { + m_raw = data.m_raw; + m_data = data.m_data; + } +} + +data::block::block(block &&data) noexcept + : m_raw_size(data.m_raw_size), + m_data_size(data.m_data_size), + m_raw(data.m_raw), + m_data(data.m_data), + m_allocation_owner(data.m_allocation_owner), + m_start_position(data.m_start_position), + m_users(data.m_users), + m_count(data.m_count), + m_byte_order(data.m_byte_order), + m_has_ownership(data.m_has_ownership), + m_extended(data.m_extended) +{ + data.m_raw = nullptr; + data.m_data = nullptr; + data.m_raw_size = 0; + data.m_data_size = 0; + data.m_users = 0; + data.m_allocation_owner = nullptr; +} + +auto data::block::operator=(const block &data) -> struct block & +{ + if (this == const_cast(&data)) { + return *this; + } + + // Clean up the current data... + if (m_allocation_owner) { + const_cast(m_allocation_owner)->m_users--; + } + else if ((!m_allocation_owner && m_has_ownership) || m_has_ownership) { + assert(m_users == 0); + free(m_raw); + } + + m_raw_size = data.m_raw_size; + m_data_size = data.m_data_size; + m_allocation_owner = nullptr; + m_start_position = data.m_start_position; + m_users = 0; + m_count = data.m_count; + m_byte_order = data.m_byte_order; + m_has_ownership = true; + m_extended = data.m_extended; + + m_raw = malloc(m_raw_size); + m_data = common::memory::alignment::align(m_raw); + copy_from(data); + + return *this; +} + +auto data::block::operator=(block &&data) noexcept -> struct block & +{ + if (this != &data) { + // Clean up the current data... + if (m_allocation_owner) { + const_cast(m_allocation_owner)->m_users--; + } + else if ((!m_allocation_owner && m_has_ownership) || m_has_ownership) { + assert(m_users == 0); + free(m_raw); + } + + // Move over the data... + m_raw_size = data.m_raw_size; + m_data_size = data.m_data_size; + m_raw = data.m_raw; + m_data = data.m_data; + m_allocation_owner = data.m_allocation_owner; + m_start_position = data.m_start_position; + m_users = data.m_users; + m_count = data.m_count; + m_byte_order = data.m_byte_order; + m_has_ownership = data.m_has_ownership; + m_extended = data.m_extended; + + data.m_allocation_owner = nullptr; + data.m_raw = nullptr; + data.m_data = nullptr; + data.m_has_ownership = false; + + } + return *this; +} + +// MARK: - Destruction + +data::block::~block() +{ + if (m_allocation_owner) { + const_cast(m_allocation_owner)->m_users--; + } + else if ((!m_allocation_owner && m_has_ownership) || m_has_ownership) { + assert(m_users == 0); + free(m_raw); + } +} + +// MARK: - Copy/Clone + +auto data::block::clone_from(const data::block &source) -> void +{ + m_extended = source.m_extended; + if (m_allocation_owner) { + m_raw = source.m_raw; + m_data = source.m_data; + m_has_ownership = false; + const_cast(m_allocation_owner)->m_users++; + } + else { + m_raw = malloc(m_raw_size); + m_data = common::memory::alignment::align(m_raw); + m_has_ownership = true; + copy_from(source); + } +} + +__attribute__((optnone)) auto data::block::copy_from(const block &source) -> void +{ + auto source_ptr = source.get(); + auto dest_ptr = get(); + simd::string::copy(dest_ptr, source_ptr, std::min(source.size(), size())); +} + +// MARK: - Operations + +auto data::block::increase_size_to(std::size_t new_size) -> void +{ + if (new_size > m_raw_size) { + throw std::runtime_error("Attempted to increase size of data::block beyond allowed range."); + } + m_data_size = new_size; +} + +auto data::block::clear() -> void +{ + simd::memory::zero(get(), size()); +} + +auto data::block::set(uint8_t value, std::size_t bytes, block::position start) -> void +{ + simd::string::set(get(start), value, std::min(static_cast(size() - start), bytes)); +} + +auto data::block::set(uint16_t value, std::size_t bytes, block::position start) -> void +{ + simd::string::setw(get(start), value, std::min(static_cast(size() - start), bytes)); +} + +auto data::block::set(uint32_t value, std::size_t bytes, block::position start) -> void +{ + simd::string::setl(get(start), value, std::min(static_cast(size() - start), bytes)); +} + +// MARK: - Slicing + +auto data::block::slice(block::position pos, std::size_t size, bool copy) const -> block +{ + return std::move(block(*this, m_start_position + pos, size, copy)); +} diff --git a/libs/libData/block.hpp b/libs/libData/block.hpp new file mode 100644 index 0000000..0987d60 --- /dev/null +++ b/libs/libData/block.hpp @@ -0,0 +1,117 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace data +{ + struct block; + + template + concept compressible_block = requires(const T& block, const data::block& uncompressed) { + { T::compress(uncompressed) } -> std::same_as; + }; + + template + concept decompressible_block = requires(const T& block, const data::block& compressed) { + { T::decompress(compressed) } -> std::same_as; + }; + + struct block + { + public: + typedef std::int64_t position; + + public: + block() = default; + explicit block(std::size_t capacity, enum byte_order order = byte_order::msb); + block(std::size_t capacity, std::size_t allocation_size, enum byte_order order = byte_order::msb); + explicit block(const std::string& path, enum byte_order order = byte_order::msb); + explicit block(const std::vector& bytes, enum byte_order order = byte_order::msb); + block(const void *data, std::size_t count, bool take_ownership = true, enum byte_order order = byte_order::msb); + block(const block& source); + block(const block& source, bool copy); + block(const block& source, block::position pos, std::size_t count, bool copy = true); + + block(block&& data) noexcept; + + auto operator=(const block& data) -> struct block&; + auto operator=(block&& data) noexcept -> struct block&; + + ~block(); + + template::value>::type* = nullptr> + [[nodiscard]] inline auto get(block::position offset = 0) const -> T { return reinterpret_cast(get_offset_data(offset)); } + + template::value>::type* = nullptr> + [[nodiscard]] inline auto get(block::position offset = 0) const -> T { return swap(*get(offset), m_byte_order); } + + [[nodiscard]] inline auto raw_size() const -> std::size_t { return m_raw_size; } + [[nodiscard]] inline auto size() const -> std::size_t { return m_count > 0 ? m_count : m_data_size; } + [[nodiscard]] inline auto start() const -> block::position { return m_start_position; } + [[nodiscard]] inline auto byte_order() const -> byte_order { return m_byte_order; } + [[nodiscard]] inline auto has_ownership() const -> bool { return m_has_ownership; } + + auto originates_from_extended_format() -> void { m_extended = true; } + [[nodiscard]] inline auto is_extended_format() const -> bool { return m_extended; } + + auto change_byte_order(enum byte_order order) -> void { m_byte_order = order; } + auto increase_size_to(std::size_t new_size) -> void; + + auto clear() -> void; + + auto set(std::uint8_t value, std::size_t bytes = 0, block::position start = 0) -> void; + auto set(std::uint16_t value, std::size_t bytes = 0, block::position start = 0) -> void; + auto set(uint32_t value, std::size_t bytes = 0, block::position start = 0) -> void; + + auto copy_from(const block& source) -> void; + + [[nodiscard]] auto slice(block::position pos, std::size_t size, bool copy = false) const -> block; + + private: + enum byte_order m_byte_order { byte_order::msb }; + bool m_extended { false }; + std::size_t m_raw_size { 0 }; + std::size_t m_data_size { 0 }; + block::position m_start_position { 0 }; + std::size_t m_count { 0 }; + void *m_raw { nullptr }; + void *m_data { nullptr }; + + bool m_has_ownership { false }; + const block *m_allocation_owner { nullptr }; + std::uint32_t m_users { 0 }; + + [[nodiscard]] inline auto get_offset_data(block::position offset) const -> void * + { + return reinterpret_cast(reinterpret_cast(m_data) + m_start_position + offset); + } + + auto clone_from(const data::block& source) -> void; + }; +} + diff --git a/libs/libData/encoding.hpp b/libs/libData/encoding.hpp new file mode 100644 index 0000000..03552f9 --- /dev/null +++ b/libs/libData/encoding.hpp @@ -0,0 +1,43 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace data +{ + struct reader; + struct writer; + + template + concept encodable = requires(T& object, data::writer& writer) { + object.encode(writer); + }; + + template + concept decodable = requires(const T& object) { + requires common::constructible_from; + requires common::move_constructible; + }; + +} \ No newline at end of file diff --git a/libs/libData/endianess.hpp b/libs/libData/endianess.hpp new file mode 100644 index 0000000..5e34ebf --- /dev/null +++ b/libs/libData/endianess.hpp @@ -0,0 +1,80 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace data +{ + // MARK: - Byte Order Enumeration + + enum class byte_order : uint8_t { msb, lsb }; + + + // MARK: - Compile Time Constants + +#if __LITTLE_ENDIAN__ + static data::byte_order s_native_byte_order = data::byte_order::lsb; +#else + static data::byte_order s_native_byte_order = data::byte_order::msb; +#endif + + static auto native_byte_order() -> enum byte_order + { + return s_native_byte_order; + } + + + // MARK: - Endian Swap Operations + + template::value>::type* = nullptr> + static auto swap(T value, byte_order from_byte_order, byte_order to_byte_order = native_byte_order(), std::size_t size = sizeof(T)) -> T + { + if (from_byte_order == to_byte_order) { + return value; + } + + T v = 0; + + for (auto i = 0; i < size; ++i) { + auto b = (size - i - 1) << 3ULL; + v |= ((value >> b) & 0xFF) << (i << 3ULL); + } + + return v; + } + + template::value>::type* = nullptr> + static auto fixed_swap(T value, std::size_t size = sizeof(T)) -> T + { + T v = 0; + + for (auto i = 0; i < size; ++i) { + auto b = (size - i - 1) << 3ULL; + v |= ((value >> b) & 0xFF) << (i << 3ULL); + } + + return v; + } + +} \ No newline at end of file diff --git a/libs/libData/internals/specialised_reader.hpp b/libs/libData/internals/specialised_reader.hpp new file mode 100644 index 0000000..a1fe307 --- /dev/null +++ b/libs/libData/internals/specialised_reader.hpp @@ -0,0 +1,75 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace data::internals +{ + + class specialised_reader + { + public: + virtual auto swap(std::uint8_t value, std::size_t size = 1) -> std::uint8_t + { + return value; + }; + + virtual auto swap(std::uint16_t value, std::size_t size = 2) -> std::uint16_t + { + return value; + }; + + virtual auto swap(std::uint32_t value, std::size_t size = 4) -> std::uint32_t + { + return value; + }; + + virtual auto swap(std::uint64_t value, std::size_t size = 1) -> std::uint64_t + { + return value; + }; + + virtual auto swap(std::int8_t value, std::size_t size = 1) -> std::int8_t + { + return value; + }; + + virtual auto swap(std::int16_t value, std::size_t size = 2) -> std::int16_t + { + return value; + }; + + virtual auto swap(std::int32_t value, std::size_t size = 4) -> std::int32_t + { + return value; + }; + + virtual auto swap(std::int64_t value, std::size_t size = 1) -> std::int64_t + { + return value; + }; + + }; + +} \ No newline at end of file diff --git a/libs/libData/internals/swap_reader.hpp b/libs/libData/internals/swap_reader.hpp new file mode 100644 index 0000000..c2c2898 --- /dev/null +++ b/libs/libData/internals/swap_reader.hpp @@ -0,0 +1,76 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace data::internals +{ + + class swap_reader: public specialised_reader + { + public: + + auto swap(std::uint8_t value, std::size_t size = 1) -> std::uint8_t override + { + return data::fixed_swap(value, size); + }; + + auto swap(std::uint16_t value, std::size_t size = 2) -> std::uint16_t override + { + return data::fixed_swap(value, size); + }; + + auto swap(std::uint32_t value, std::size_t size = 4) -> std::uint32_t override + { + return data::fixed_swap(value, size); + }; + + auto swap(std::uint64_t value, std::size_t size = 1) -> std::uint64_t override + { + return data::fixed_swap(value, size); + }; + + auto swap(std::int8_t value, std::size_t size = 1) -> std::int8_t override + { + return data::fixed_swap(value, size); + }; + + auto swap(std::int16_t value, std::size_t size = 2) -> std::int16_t override + { + return data::fixed_swap(value, size); + }; + + auto swap(std::int32_t value, std::size_t size = 4) -> std::int32_t override + { + return data::fixed_swap(value, size); + }; + + auto swap(std::int64_t value, std::size_t size = 8) -> std::int64_t override + { + return data::fixed_swap(value, size); + }; + + }; + +} \ No newline at end of file diff --git a/libs/libData/reader.cpp b/libs/libData/reader.cpp new file mode 100644 index 0000000..3c045d7 --- /dev/null +++ b/libs/libData/reader.cpp @@ -0,0 +1,196 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include +#include + +// MARK: - Construction + +data::reader::reader(const class block *data, block::position pos, bool owns) + : m_data(data), + m_owns_data(owns), + m_position(pos) +{ + update_swap_wrapper(); +} + +auto data::reader::file(const std::string &path, block::position pos) -> reader +{ + auto data = new class block(path, byte_order::msb); + return reader(data, pos, true); +} + +// MARK: - Destruction + +data::reader::~reader() +{ + if (m_owns_data) { + delete m_data; + } +} + +// MARK: - Position Management + +auto data::reader::set_position(block::position pos) -> void +{ + m_position = static_cast(std::max(static_cast(0), std::min(static_cast(pos), size() + 1))); +} + +auto data::reader::move(block::position delta) -> void +{ + set_position(position() + delta); +} + +auto data::reader::save_position() -> void +{ + m_position_stack.emplace_back(m_position); +} + +auto data::reader::restore_position() -> void +{ + if (m_position_stack.empty()) { + throw std::runtime_error("Attempted to restore position of data reader, when no saved positions exist."); + } + set_position(m_position_stack.back()); + m_position_stack.pop_back(); +} + +// MARK: - Read Operations + +auto data::reader::read_byte(block::position offset, mode mode) -> std::uint8_t +{ + return read_integer(offset, mode); +} + +auto data::reader::read_signed_byte(block::position offset, mode mode) -> std::int8_t +{ + return read_integer(offset, mode); +} + +auto data::reader::read_short(block::position offset, mode mode) -> std::uint16_t +{ + return read_integer(offset, mode); +} + +auto data::reader::read_signed_short(block::position offset, mode mode) -> std::int16_t +{ + return read_integer(offset, mode); +} + +auto data::reader::read_fixed_point(block::position offset, mode mode) -> double +{ + return static_cast(read_signed_long(offset, mode)) / static_cast(1 << 16); +} + +auto data::reader::read_triple(block::position offset, mode mode) -> std::uint32_t +{ + return read_integer(offset, mode, 3); +} + +auto data::reader::read_long(block::position offset, mode mode) -> std::uint32_t +{ + return read_integer(offset, mode); +} + +auto data::reader::read_signed_long(block::position offset, mode mode) -> std::int32_t +{ + return read_integer(offset, mode); +} + +auto data::reader::read_quad(block::position offset, mode mode) -> std::uint64_t +{ + return read_integer(offset, mode); +} + +auto data::reader::read_signed_quad(block::position offset, mode mode) -> std::int64_t +{ + return read_integer(offset, mode); +} + +auto data::reader::read_pstr(block::position offset, mode mode) -> std::string +{ + switch (mode) { + case mode::advance: { + auto length = read_byte(offset, mode); + return length == 0 ? "" : read_cstr(length, 0, mode); + } + case mode::peek: { + auto length = read_byte(offset, mode); + return length == 0 ? "" : read_cstr(length, offset + 1, mode); + } + default: { + return ""; + } + } +} + +auto data::reader::read_cstr(std::size_t length, block::position offset, mode mode) -> std::string +{ + std::vector bytes; + + if (length == 0) { + block::position i = 0; + while (read_byte(offset + i, mode::peek)) { + bytes.push_back(read_byte(offset + i++, mode::peek)); + } + + if (mode == mode::advance) { + move(offset + bytes.size() + 1); + } + } + else { + auto data = std::move(read_bytes(length, offset, mode)); + for (const auto& byte : data) { + if (byte == '\0') { + break; + } + bytes.emplace_back(static_cast(byte)); + } + } + + return encoding::mac_roman::to_utf8(bytes); +} + +auto data::reader::read_data(std::size_t length, block::position offset, mode mode) -> class block +{ + auto sliced = std::move(m_data->slice(m_position + offset, length)); + if (mode == mode::advance) { + move(offset + length); + } + return std::move(sliced); +} + +auto data::reader::read_bytes(std::size_t length, block::position offset, mode mode) -> std::vector +{ + auto start = m_data->get(m_position + offset); + move(offset + length); + + std::vector bytes(std::max(length, static_cast(28)), '\0'); + for (auto idx = 0; idx < length; ++idx) { + bytes[idx] = start[idx]; + } + return std::move(bytes); +} \ No newline at end of file diff --git a/libs/libData/reader.hpp b/libs/libData/reader.hpp new file mode 100644 index 0000000..f203dad --- /dev/null +++ b/libs/libData/reader.hpp @@ -0,0 +1,187 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace data +{ + class reader + { + public: + enum class mode { advance, peek }; + + public: + explicit reader(const class block *data, block::position pos = 0, bool owns = false); + ~reader(); + + static auto file(const std::string& path, block::position pos = 0) -> reader; + + [[nodiscard]] inline auto data() const -> const class block * { return m_data; } + [[nodiscard]] inline auto owns_data() const -> bool { return m_owns_data; } + + [[nodiscard]] inline auto position() const -> block::position { return m_position; } + [[nodiscard]] inline auto size() const -> std::size_t { return m_data->size(); } + [[nodiscard]] inline auto eof() const -> bool { return position() >= size(); } + [[nodiscard]] inline auto byte_order() const -> byte_order { return m_data->byte_order(); } + + auto change_byte_order(enum byte_order order) -> void + { + const_cast(m_data)->change_byte_order(order); + update_swap_wrapper(); + } + + auto update_swap_wrapper() -> void + { + if (m_data && byte_order() == native_byte_order()) { + m_swap_wrapper = std::make_shared(); + } + else { + m_swap_wrapper = std::make_shared(); + } + } + + auto set_position(block::position pos) -> void; + auto move(block::position delta = 1) -> void; + + auto save_position() -> void; + auto restore_position() -> void; + + template::value && std::is_signed::value>> + auto read_integer(block::position offset = 0, mode mode = mode::advance) -> T { return 0; } + + template + auto read_integer(block::position offset = 0, mode mode = mode::advance) -> std::int8_t { return read_signed_byte(offset, mode); } + + template + auto read_integer(block::position offset = 0, mode mode = mode::advance) -> std::int16_t { return read_signed_short(offset, mode); } + + template + auto read_integer(block::position offset = 0, mode mode = mode::advance) -> std::int32_t { return read_signed_long(offset, mode); } + + template + auto read_integer(block::position offset = 0, mode mode = mode::advance) -> std::int64_t { return read_signed_quad(offset, mode); } + + template::value>> + auto read_integer(block::position offset = 0, mode mode = mode::advance) -> T { return 0; } + + template + auto read_integer(block::position offset = 0, mode mode = mode::advance) -> std::uint8_t { return read_byte(offset, mode); } + + template + auto read_integer(block::position offset = 0, mode mode = mode::advance) -> std::uint16_t { return read_short(offset, mode); } + + template + auto read_integer(block::position offset = 0, mode mode = mode::advance) -> std::uint32_t { return read_long(offset, mode); } + + template + auto read_integer(block::position offset = 0, mode mode = mode::advance) -> std::uint64_t { return read_quad(offset, mode); } + + auto read_byte(block::position offset = 0, mode mode = mode::advance) -> uint8_t; + auto read_signed_byte(block::position offset = 0, mode mode = mode::advance) -> int8_t; + + auto read_short(block::position offset = 0, mode mode = mode::advance) -> uint16_t; + auto read_signed_short(block::position offset = 0, mode mode = mode::advance) -> int16_t; + + auto read_fixed_point(block::position offset = 0, mode mode = mode::advance) -> double; + + auto read_triple(block::position offset = 0, mode mode = mode::advance) -> uint32_t; + + auto read_long(block::position offset = 0, mode mode = mode::advance) -> uint32_t; + auto read_signed_long(block::position offset = 0, mode mode = mode::advance) -> int32_t; + + auto read_quad(block::position offset = 0, mode mode = mode::advance) -> uint64_t; + auto read_signed_quad(block::position offset = 0, mode mode = mode::advance) -> int64_t; + + auto read_cstr(std::size_t length = 0, block::position offset = 0, mode mode = mode::advance) -> std::string; + auto read_pstr(block::position offset = 0, mode mode = mode::advance) -> std::string; + + auto read_data(std::size_t length, block::position offset = 0, mode mode = mode::advance) -> class block; + auto read_bytes(std::size_t length, block::position offset = 0, mode mode = mode::advance) -> std::vector; + + template + auto read(block::position offset = 0, mode mode = mode::advance) -> T + { + if (mode == mode::peek) { + save_position(); + } + + move(offset); + data::decodable auto object = T(*this); + + if (mode == mode::peek) { + restore_position(); + } + + return std::move(object); + } + + template + auto read_compressed_data(std::size_t length, block::position offset = 0, mode mode = mode::advance) -> class block + { + return T::decompress(std::move(read_data(length, offset, mode))); + } + + template::value>::type* = nullptr> + auto read_integer(block::position offset = 0, mode mode = mode::advance, std::size_t size = sizeof(T)) -> T + { + T v = *reinterpret_cast(m_data->template get(m_position + offset)); + v = m_swap_wrapper->swap(v, size); + + if (mode == mode::advance) { + move(offset + size); + } + + return v; + } + + template::value>::type* = nullptr> + auto read_enum(block::position offset = 0, mode mode = mode::advance, std::size_t size = sizeof(E)) -> E + { + if (sizeof(E) == sizeof(std::uint8_t)) { + return static_cast(read_byte(offset, mode)); + } + else if (sizeof(E) == sizeof(std::uint16_t)) { + return static_cast(read_short(offset, mode)); + } + else if (sizeof(E) == sizeof(std::uint32_t)) { + return static_cast(read_long(offset, mode)); + } + else if (sizeof(E) == sizeof(std::uint64_t)) { + return static_cast(read_quad(offset, mode)); + } + } + + private: + bool m_owns_data { false }; + const class block *m_data { nullptr }; + std::shared_ptr m_swap_wrapper; + std::vector m_position_stack; + block::position m_position; + }; + +} diff --git a/libs/libData/simd.hpp b/libs/libData/simd.hpp new file mode 100644 index 0000000..0e18517 --- /dev/null +++ b/libs/libData/simd.hpp @@ -0,0 +1,114 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace data +{ + class block; +} + +namespace data::simd +{ + +#if __x86_64__ + typedef __int128 int128_t; + typedef unsigned __int128 uint128_t; + + typedef uint128_t wide_type; + typedef wide_type type; + typedef uint64_t wide_field_type; + + static constexpr std::size_t alignment_width = 16; + static constexpr std::size_t field_count = 4; + static constexpr std::size_t wide_field_count = 2; + +#elif __arm64__ + typedef uint64_t wide_type; + typedef wide_type type; + typedef type wide_field_type; + + static constexpr std::size_t alignment_width = 8; + static constexpr std::size_t field_count = 2; + static constexpr std::size_t wide_field_count = 1; + +#else + typedef uint32_t simd_wide_type; + typedef simd_wide_type simd_type; + + static constexpr std::size_t alignment_width = 4; + static constexpr std::size_t field_count = 1; + static constexpr std::size_t wide_field_count = 0; + +#endif + + typedef uint32_t field_type; + typedef uint16_t half_field_type; + static constexpr std::size_t field_size = sizeof(field_type); + static constexpr std::size_t fields_length = field_count * field_size; + + union alignas(alignment_width) value + { + type wide; +#if __x86_64__ || __arm64__ + wide_field_type wide_fields[wide_field_count]; +#endif + field_type fields[field_count]; + uint8_t bytes[sizeof(type)]; + }; + + static constexpr std::size_t alignment_mask = ~(alignment_width - 1); + + template::value>::type* = nullptr> + static inline auto expand_capacity(T capacity) -> T + { + return (capacity + alignment_width - 1) & alignment_mask; + } + + template::value>::type* = nullptr> + static inline auto align(T ptr) -> T + { + return reinterpret_cast((reinterpret_cast(ptr) + alignment_width - 1) & alignment_mask); + } + + __attribute__((optnone)) static inline auto set(std::uint32_t *ptr, std::size_t dst_size, union value v, std::size_t bytes) -> void + { + std::size_t n = 0; + auto len = std::min(dst_size, bytes); + while (n < len) { + if ((reinterpret_cast(ptr) & alignment_width) || (len - n) < fields_length) { + *ptr = v.fields[n & (field_count - 1)]; + ++ptr; + n += field_size; + } + else { + *reinterpret_cast(ptr) = v.wide; + ptr += field_count; + n += alignment_width; + } + } + } + +} \ No newline at end of file diff --git a/libs/libData/writer.cpp b/libs/libData/writer.cpp new file mode 100644 index 0000000..4be77d3 --- /dev/null +++ b/libs/libData/writer.cpp @@ -0,0 +1,236 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include + +// MARK: - Construction + +data::writer::writer(enum byte_order order) + : m_owns_data(true), + m_data(new class block(0, order)) +{ + +} + +data::writer::writer(const class block *data) + : m_owns_data(false), + m_data(data) +{ + +} + +// MARK: - Destruction + +data::writer::~writer() +{ + if (m_owns_data) { + delete m_data; + } +} + +// MARK: - Storage Management + +auto data::writer::expand_storage(std::size_t amount) -> void +{ + // NOTE: We only allow resizing of data objects that we own. + assert(m_owns_data); + + // Construct a new data object with the appropriate new size, and copy in the old data. + auto required_size = size() + amount; + if (required_size < m_data->raw_size()) { + const_cast(m_data)->increase_size_to(required_size); + } + else { + auto allocation_size = size() + std::max(size(), amount); + auto new_data = new class block(required_size, std::max(allocation_size, required_size), m_data->byte_order()); + new_data->set(static_cast(0), allocation_size); + new_data->copy_from(*m_data); + + // Replace the old data with the new object. + delete m_data; + m_data = new_data; + } + +} + +auto data::writer::ensure_required_space(block::position position, std::size_t amount) -> void +{ + auto remaining = static_cast(this->size()) - position; + auto delta = 0; + if (amount > remaining) { + delta = amount - remaining; + expand_storage(delta); + } +} + +// MARK: - Position Management + +auto data::writer::set_position(block::position pos) -> void +{ + if (pos < 0 || pos > size()) { + throw std::runtime_error("Attempted to set position of data reader out of bounds."); + } + m_position = pos; +} + +auto data::writer::move(block::position delta) -> void +{ + set_position(m_position + delta); +} + +// MARK: - Write Operations + +auto data::writer::write_byte(uint8_t value, std::size_t count) -> void +{ + write_integer(value, count); +} + +auto data::writer::write_signed_byte(int8_t value, std::size_t count) -> void +{ + write_integer(static_cast(value), count); +} + +auto data::writer::write_short(uint16_t value, std::size_t count) -> void +{ + write_integer(value, count); +} + +auto data::writer::write_signed_short(int16_t value, std::size_t count) -> void +{ + write_integer(static_cast(value), count); +}; + +auto data::writer::write_fixed_point(double value, std::size_t count) -> void +{ + auto integral_value = static_cast(value * (1 << 16)); + write_integer(integral_value, count); +} + +auto data::writer::write_triple(uint32_t value, std::size_t count) -> void +{ + write_integer(value, count, 3); +}; + +auto data::writer::write_long(uint32_t value, std::size_t count) -> void +{ + write_integer(value, count); +}; + +auto data::writer::write_signed_long(int32_t value, std::size_t count) -> void +{ + write_integer(static_cast(value), count); +}; + +auto data::writer::write_quad(uint64_t value, std::size_t count) -> void +{ + write_integer(value, count); +}; + +auto data::writer::write_signed_quad(int64_t value, std::size_t count) -> void +{ + write_integer(static_cast(value), count); +} + +auto data::writer::write_pstr(const std::string &str) -> std::size_t +{ + auto bytes = encoding::mac_roman::from_utf8(str); + + if (bytes.size() > 0xFF) { + bytes.resize(0xFF); + } + + write_byte(static_cast(bytes.size())); + write_bytes(bytes); + return bytes.size(); +} + +auto data::writer::write_cstr(const std::string &str, std::size_t size) -> std::size_t +{ + std::vector bytes { encoding::mac_roman::from_utf8(str) }; + + if (size == 0) { + // NULL terminated C-String + bytes.push_back( '\0' ); + } + else { + // Fixed length C-String + bytes.resize(size, '\0'); + } + + write_bytes(bytes); + return bytes.size(); +} + +auto data::writer::write_bytes(const std::vector &bytes) -> void +{ + ensure_required_space(position(), bytes.size()); + auto ptr = m_data->template get(position()); + + for (auto v : bytes) { + *ptr++ = v; + move(); + } +} + +auto data::writer::write_bytes(const std::vector &bytes) -> void +{ + write_bytes(std::vector(bytes.begin(), bytes.end())); +} + +auto data::writer::write_data(const class block *data) -> void +{ + assert(data != m_data); + + ensure_required_space(position(), data->size()); + auto dst = m_data->template get(position()); + auto src = data->get(); + + for (auto i = 0; i < data->size(); ++i) { + *dst++ = *src++; + move(); + } +} + +// MARK: - Padding + +auto data::writer::pad_to_size(std::size_t size) -> void +{ + if (this->size() >= size) { + return; + } + + auto required = size - this->size(); + set_position(this->size()); + write_byte(0, required); +} + +// MARK: - Saving / File Access + +auto data::writer::save(const std::string &path, std::size_t size) const -> void +{ + std::ofstream file { path, std::ios::out | std::ios::binary }; + file.write(m_data->get(), size == 0 ? m_data->size() : size); + file.close(); +} \ No newline at end of file diff --git a/libs/libData/writer.hpp b/libs/libData/writer.hpp new file mode 100644 index 0000000..3a4b7b5 --- /dev/null +++ b/libs/libData/writer.hpp @@ -0,0 +1,177 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace data +{ + class writer + { + public: + explicit writer(enum byte_order order = native_byte_order()); + explicit writer(const class block *data); + + ~writer(); + + [[nodiscard]] inline auto data() const -> const class block * { return reinterpret_cast(m_data); }; + [[nodiscard]] inline auto owns_data() const -> bool { return m_owns_data; } + + [[nodiscard]] inline auto position() const -> block::position { return m_position; } + [[nodiscard]] inline auto size() const -> std::size_t { return m_data->size(); } + + auto change_byte_order(enum byte_order order) -> void { const_cast(m_data)->change_byte_order(order); } + + auto expand_storage(std::size_t amount) -> void; + auto ensure_required_space(block::position position, std::size_t amount) -> void; + + auto set_position(block::position pos) -> void; + auto move(block::position delta = 1) -> void; + + template::value && std::is_signed::value>> + auto write_integer(T value, std::size_t count = 1) -> void {} + + template + auto write_integer(std::int8_t value, std::size_t count = 1) -> void { write_signed_byte(value, count); } + + template + auto write_integer(std::int16_t value, std::size_t count = 1) -> void { write_signed_short(value, count); } + + template + auto write_integer(std::int32_t value, std::size_t count = 1) -> void { write_signed_long(value, count); } + + template + auto write_integer(std::int64_t value, std::size_t count = 1) -> void { write_signed_quad(value, count); } + + template::value>> + auto write_integer(T value, std::size_t count = 1) -> void {} + + template + auto write_integer(std::uint8_t value, std::size_t count = 1) -> void { write_byte(value, count); } + + template + auto write_integer(std::uint16_t value, std::size_t count = 1) -> void { write_short(value, count); } + + template + auto write_integer(std::uint32_t value, std::size_t count = 1) -> void { write_long(value, count); } + + template + auto write_integer(std::uint64_t value, std::size_t count = 1) -> void { write_quad(value, count); } + + auto write_byte(std::uint8_t value, std::size_t count = 1) -> void; + auto write_signed_byte(std::int8_t value, std::size_t count = 1) -> void; + + auto write_short(std::uint16_t value, std::size_t count = 1) -> void; + auto write_signed_short(std::int16_t value, std::size_t count = 1) -> void; + + auto write_fixed_point(double value, std::size_t count = 1) -> void; + + auto write_triple(std::uint32_t value, std::size_t count = 1) -> void; + + auto write_long(std::uint32_t value, std::size_t count = 1) -> void; + auto write_signed_long(std::int32_t value, std::size_t count = 1) -> void; + + auto write_quad(std::uint64_t value, std::size_t count = 1) -> void; + auto write_signed_quad(std::int64_t value, std::size_t count = 1) -> void; + + auto write_pstr(const std::string& str) -> std::size_t; + auto write_cstr(const std::string& str, std::size_t size = 0) -> std::size_t; + + auto write_bytes(const std::vector& bytes) -> void; + auto write_bytes(const std::vector& bytes) -> void; + + auto write_data(const class block *data) -> void; + + template + auto write_compressed_data(const class block *data) -> class block + { + return T::compress(data); + } + + template + auto write(T& value) -> void + { + value.encode(*this); + } + + auto pad_to_size(std::size_t size) -> void; + + auto save(const std::string& path, std::size_t size = 0) const -> void; + + template::value>::type* = nullptr> + auto write_integer(T value, std::size_t count = 1, std::size_t size = sizeof(T)) -> void + { + T swapped = swap(value, native_byte_order(), m_data->byte_order()); + + ensure_required_space(position(), size * count); + auto ptr = m_data->template get(position()); + + // Correct the alignment. + if (m_data->byte_order() == byte_order::msb) { + for (auto i = size; i < sizeof(swapped); ++i) { + swapped >>= 8ULL; + } + } + else { + for (auto i = size; i < sizeof(swapped); ++i) { + swapped <<= 8ULL; + } + } + + for (auto n = 0; n < count; ++n) { + for (auto i = 0; i < size; ++i) { + auto b = i << 3ULL; + *ptr++ = (swapped >> b) & 0xFF; + } + } + + move(static_cast(size * count)); + } + + template::value>::type* = nullptr> + auto write_enum(E value, std::size_t count = 1, std::size_t size = sizeof(E)) -> void + { + if (sizeof(E) == sizeof(std::uint8_t)) { + write_byte(static_cast(value), count); + } + else if (sizeof(E) == sizeof(std::uint16_t)) { + write_short(static_cast(value), count); + } + else if (sizeof(E) == sizeof(std::uint32_t)) { + write_long(static_cast(value), count); + } + else if (sizeof(E) == sizeof(std::uint64_t)) { + write_quad(static_cast(value), count); + } + } + + private: + bool m_owns_data { false }; + const class block *m_data { nullptr }; + block::position m_position { 0 }; + }; +} + diff --git a/libs/libEncoding/CMakeLists.txt b/libs/libEncoding/CMakeLists.txt new file mode 100644 index 0000000..81298c5 --- /dev/null +++ b/libs/libEncoding/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (c) 2023 Tom Hancocks +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) + +######################################################################################################################## +## Project +project(Graphite LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) + +######################################################################################################################## +## libSIMD +file(GLOB_RECURSE libEncoding_Sources + *.cpp +) + +add_library(Encoding ${libEncoding_Sources}) +target_include_directories(Encoding PUBLIC + ${PROJECT_LIBS_DIR} +) diff --git a/libGraphite/encoding/macroman/macroman.cpp b/libs/libEncoding/macroman/macroman.cpp similarity index 84% rename from libGraphite/encoding/macroman/macroman.cpp rename to libs/libEncoding/macroman/macroman.cpp index fc1f11c..0a31022 100644 --- a/libGraphite/encoding/macroman/macroman.cpp +++ b/libs/libEncoding/macroman/macroman.cpp @@ -20,11 +20,11 @@ #include #include -#include "libGraphite/encoding/macroman/macroman.hpp" +#include // MARK: - Encoding Tables -static uint16_t cp_table[0x100] = +static std::uint16_t cp_table[0x100] = { // Standard ASCII 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, @@ -72,13 +72,13 @@ namespace unicode struct hint { public: - uint8_t m_mask; - uint8_t m_lead; - uint32_t m_beg; - uint32_t m_end; - size_t m_bits; + std::uint8_t m_mask; + std::uint8_t m_lead; + std::uint32_t m_beg; + std::uint32_t m_end; + std::size_t m_bits; - hint(uint8_t mask, uint8_t lead, uint32_t beg, uint32_t end, size_t bits) + hint(std::uint8_t mask, std::uint8_t lead, std::uint32_t beg, std::uint32_t end, std::size_t bits) : m_mask(mask), m_lead(lead), m_beg(beg), m_end(end), m_bits(bits) { @@ -88,11 +88,11 @@ namespace unicode { static std::vector utf8; if (utf8.empty()) { - utf8.emplace_back(unicode::hint(0b00111111, 0b10000000, 0, 0, 6)); - utf8.emplace_back(unicode::hint(0b01111111, 0b00000000, 0000, 0177, 7)); - utf8.emplace_back(unicode::hint(0b00011111, 0b11000000, 0200, 03777, 5)); - utf8.emplace_back(unicode::hint(0b00001111, 0b11100000, 04000, 0177777, 4)); - utf8.emplace_back(unicode::hint(0b00000111, 0b11110000, 0200000, 04177777, 3)); + utf8.emplace_back(0b00111111, 0b10000000, 0, 0, 6); + utf8.emplace_back(0b01111111, 0b00000000, 0000, 0177, 7); + utf8.emplace_back(0b00011111, 0b11000000, 0200, 03777, 5); + utf8.emplace_back(0b00001111, 0b11100000, 04000, 0177777, 4); + utf8.emplace_back(0b00000111, 0b11110000, 0200000, 04177777, 3); } return utf8; } @@ -102,9 +102,9 @@ namespace unicode // MARK: - Conversion Functions -auto graphite::encoding::mac_roman::from_utf8(const std::string &str) -> std::vector +auto encoding::mac_roman::from_utf8(const std::string &str) -> std::vector { - std::vector mac_roman_bytes; + std::vector mac_roman_bytes; // Convert back to a C-String for easier processing here... const char *s = str.c_str(); @@ -116,7 +116,7 @@ auto graphite::encoding::mac_roman::from_utf8(const std::string &str) -> std::ve // Determine the length of the current character, and then condense it down // into a single codepoint, which can then be mapped to a MacRoman value. size_t n = 0; - auto ch = static_cast(s[i]); + auto ch = static_cast(s[i]); for (auto hint : utf8) { if ((ch & ~hint.m_mask) == hint.m_lead) { break; @@ -130,7 +130,7 @@ auto graphite::encoding::mac_roman::from_utf8(const std::string &str) -> std::ve } auto shift = utf8[0].m_bits * (n - 1); - uint32_t codepoint = (s[i++] & utf8[n].m_mask) << shift; + std::uint32_t codepoint = (s[i++] & utf8[n].m_mask) << shift; for (auto j = 1; j < n; ++i, ++j) { shift -= utf8[0].m_bits; codepoint |= ((char)s[i] & utf8[0].m_mask) << shift; @@ -148,7 +148,7 @@ auto graphite::encoding::mac_roman::from_utf8(const std::string &str) -> std::ve return mac_roman_bytes; } -auto graphite::encoding::mac_roman::to_utf8(const std::vector& bytes) -> std::string +auto encoding::mac_roman::to_utf8(const std::vector& bytes) -> std::string { std::string result; @@ -160,8 +160,8 @@ auto graphite::encoding::mac_roman::to_utf8(const std::vector& bytes) - // Get the codepoint and determine the length of the UTF8 scalar. auto cp = cp_table[c]; - - size_t n = 0; + + std::size_t n = 0; for (auto hint : utf8) { if ((cp >= hint.m_beg) && (cp <= hint.m_end)) { break; diff --git a/libGraphite/encoding/macroman/macroman.hpp b/libs/libEncoding/macroman/macroman.hpp similarity index 83% rename from libGraphite/encoding/macroman/macroman.hpp rename to libs/libEncoding/macroman/macroman.hpp index b8bdc57..0829c9d 100644 --- a/libGraphite/encoding/macroman/macroman.hpp +++ b/libs/libEncoding/macroman/macroman.hpp @@ -20,23 +20,25 @@ #include #include +#include +#include -#if !defined(GRAPHITE_MACROMAN) -#define GRAPHITE_MACROMAN +#if !defined(ENCODING_MACROMAN) +#define ENCODING_MACROMAN -namespace graphite::encoding::mac_roman { +namespace encoding::mac_roman { /** * Convert a sequence of bytes into a UTF-8 string, translating them from a * Mac OS Roman encoding. */ - auto to_utf8(const std::vector& bytes) -> std::string; + auto to_utf8(const std::vector& bytes) -> std::string; /** * Convert a UTF-8 encoded string into a sequence of bytes containing a Mac OS * Roman encoded string. */ - auto from_utf8(const std::string& str) -> std::vector; + auto from_utf8(const std::string& str) -> std::vector; } diff --git a/libs/libHashing/CMakeLists.txt b/libs/libHashing/CMakeLists.txt new file mode 100644 index 0000000..78c2652 --- /dev/null +++ b/libs/libHashing/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (c) 2023 Tom Hancocks +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) + +######################################################################################################################## +## Project +project(Graphite LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) + +######################################################################################################################## +## libSIMD +file(GLOB_RECURSE libHashing_Sources + *.cpp +) + +add_library(Hashing ${libHashing_Sources}) +target_include_directories(Hashing PUBLIC + ${PROJECT_LIBS_DIR} +) diff --git a/libs/libHashing/xxhash/xxhash.cpp b/libs/libHashing/xxhash/xxhash.cpp new file mode 100644 index 0000000..04ac179 --- /dev/null +++ b/libs/libHashing/xxhash/xxhash.cpp @@ -0,0 +1,212 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +// MARK: - Constants + +struct uint64_unaligned { std::uint64_t v; } __attribute__((packed)); +struct uint32_unaligned { std::uint32_t v; } __attribute__((packed)); + +static std::uint64_t xxhash64_seed = 0; +static std::uint64_t xxhash64_p1 = 11400714785074694791ULL; +static std::uint64_t xxhash64_p2 = 14029467366897019727ULL; +static std::uint64_t xxhash64_p3 = 1609587929392839161ULL; +static std::uint64_t xxhash64_p4 = 9650029242287828579ULL; +static std::uint64_t xxhash64_p5 = 2870177450012600261ULL; + +static std::uint32_t xxhash32_seed = 0; +static std::uint32_t xxhash32_p1 = 2654435761U; +static std::uint32_t xxhash32_p2 = 2246822519U; +static std::uint32_t xxhash32_p3 = 3266489917U; +static std::uint32_t xxhash32_p4 = 668265263U; +static std::uint32_t xxhash32_p5 = 374761393U; + +#define ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define ROTL64(x,r) (((x) << (r)) | ((x) >> (64 - (r)))) +#define UNALGINED64(p) (((struct uint64_unaligned *)(p))->v) +#define UNALGINED32(p) (((struct uint32_unaligned *)(p))->v) + +// MARK: - XXHash64 + +auto hashing::xxh64(const void *src, std::size_t length) -> hashing::value64 +{ + uint64_t h64 { 0 }; + auto ptr = const_cast(reinterpret_cast(src)); + auto end = ptr + length; + + if (length >= 32) { + uint8_t *limit = end - 32; + uint64_t v2 = xxhash64_seed + xxhash64_p2; + uint64_t v1 = v2 + xxhash64_p1; + uint64_t v3 = xxhash64_seed; + uint64_t v4 = xxhash64_seed - xxhash64_p1; + + do { + v1 += UNALGINED64(ptr) * xxhash64_p2; + ptr += 8; + v1 = ROTL64(v1, 31); + v1 *= xxhash64_p1; + + v2 += UNALGINED64(ptr) * xxhash64_p2; + ptr += 8; + v2 = ROTL64(v2, 31); + v2 *= xxhash64_p1; + + v3 += UNALGINED64(ptr) * xxhash64_p2; + ptr += 8; + v3 = ROTL64(v3, 31); + v3 *= xxhash64_p1; + + v4 += UNALGINED64(ptr) * xxhash64_p2; + ptr += 8; + v4 = ROTL64(v4, 31); + v4 *= xxhash64_p1; + } + while (ptr <= limit); + + h64 = ROTL64(v1, 1) + ROTL64(v2, 7) + ROTL64(v3, 12) + ROTL64(v4, 18); + + v1 *= xxhash64_p2; + v1 = ROTL64(v1, 31); + v1 *= xxhash64_p1; + h64 ^= v1; + h64 = h64 * xxhash64_p1 + xxhash64_p4; + + v2 *= xxhash64_p2; + v2 = ROTL64(v2, 31); + v2 *= xxhash64_p1; + h64 ^= v2; + h64 = h64 * xxhash64_p1 + xxhash64_p4; + + v3 *= xxhash64_p2; + v3 = ROTL64(v3, 31); + v3 *= xxhash64_p1; + h64 ^= v3; + h64 = h64 * xxhash64_p1 + xxhash64_p4; + + v4 *= xxhash64_p2; + v4 = ROTL64(v4, 31); + v4 *= xxhash64_p1; + h64 ^= v4; + h64 = h64 * xxhash64_p1 + xxhash64_p4; + } + else { + h64 = xxhash64_seed + xxhash64_p5; + } + + h64 += length; + + while (ptr+8 <= end) { + uint64_t k1 = UNALGINED64(ptr); + k1 *= xxhash64_p2; + k1 = ROTL64(k1, 31); + k1 *= xxhash64_p1; + h64 ^= k1; + h64 = ROTL64(h64, 27) * xxhash64_p1 + xxhash64_p4; + ptr += 8; + } + + if (ptr+4 <= end) { + h64 ^= (uint64_t)(UNALGINED32(ptr)) * xxhash64_p1; + ptr += 4; + h64 = ROTL64(h64, 23) * xxhash64_p2 + xxhash64_p3; + } + + while (ptr < end) { + h64 ^= *ptr * xxhash64_p5; + h64 = ROTL64(h64, 11) * xxhash64_p1; + ++ptr; + } + + h64 ^= h64 >> 33; + h64 *= xxhash64_p2; + h64 ^= h64 >> 29; + h64 *= xxhash64_p3; + h64 ^= h64 >> 32; + + return h64; +} + + +auto hashing::xxh32(const void *src, std::size_t length) -> value32 +{ + uint32_t h32 { 0 }; + auto ptr = const_cast(reinterpret_cast(src)); + auto end = ptr + length; + + if (length >= 16) { + uint8_t *limit = end - 16; + uint32_t v2 = xxhash32_seed + xxhash32_p2; + uint32_t v1 = v2 + xxhash32_p1; + uint32_t v3 = xxhash32_seed; + uint32_t v4 = xxhash32_seed - xxhash32_p1; + + do { + v1 += UNALGINED32(ptr) * xxhash32_p2; + ptr += 4; + v1 = ROTL32(v1, 13); + v1 *= xxhash32_p1; + + v2 += UNALGINED32(ptr) * xxhash32_p2; + ptr += 4; + v2 = ROTL32(v2, 13); + v2 *= xxhash32_p1; + + v3 += UNALGINED32(ptr) * xxhash32_p2; + ptr += 4; + v3 = ROTL32(v3, 13); + v3 *= xxhash32_p1; + + v4 += UNALGINED32(ptr) * xxhash32_p2; + ptr += 4; + v4 = ROTL32(v4, 13); + v4 *= xxhash32_p1; + } + while (ptr <= limit); + + h32 = ROTL32(v1, 1) + ROTL32(v2, 7) + ROTL32(v3, 12) + ROTL32(v4, 18); + } + else { + h32 = xxhash32_seed + xxhash32_p5; + } + + h32 += length; + + while (ptr+4 <= end) { + h32 += UNALGINED32(ptr) * xxhash32_p3; + h32 = ROTL32(h32, 17) * xxhash32_p4; + ptr += 4; + } + + while (ptr < end) { + h32 += *ptr * xxhash32_p5; + h32 = ROTL32(h32, 11) * xxhash32_p1; + ++ptr; + } + + h32 ^= h32 >> 15; + h32 *= xxhash32_p2; + h32 ^= h32 >> 13; + h32 *= xxhash32_p3; + h32 ^= h32 >> 16; + + return h32; +} \ No newline at end of file diff --git a/libs/libHashing/xxhash/xxhash.hpp b/libs/libHashing/xxhash/xxhash.hpp new file mode 100644 index 0000000..8fdf02d --- /dev/null +++ b/libs/libHashing/xxhash/xxhash.hpp @@ -0,0 +1,55 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace hashing +{ + /** + * A 64-bit hash value. + * This type definition is intended to help provide semantic context. + */ + typedef std::uint64_t value64; + + /** + * A 32-bit hash value. + * This type definition is intended to help provide semantic context. + */ + typedef std::uint32_t value32; + + /** + * Produce a hash value using the 64-bit variant of the XXHash algorithm, using the specified data. + * @param ptr A pointer to the byte sequence to produce a hash from. + * @param length The length of the data being supplied. + * @return A hash value. + */ + auto xxh64(const void *ptr, std::size_t length) -> hashing::value64; + + /** + * Produce a hash value using the 32-bit variant of the XXHash algorithm, using the specified data. + * @param ptr A pointer to the byte sequence to produce a hash from. + * @param length The length of the data being supplied. + * @return A hash value. + */ + auto xxh32(const void *ptr, std::size_t length) -> hashing::value32; +} diff --git a/libs/libQuickdraw/CMakeLists.txt b/libs/libQuickdraw/CMakeLists.txt new file mode 100644 index 0000000..baa51d1 --- /dev/null +++ b/libs/libQuickdraw/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2023 Tom Hancocks +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) + +######################################################################################################################## +## Project +project(Graphite LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) + +######################################################################################################################## +## libSIMD +file(GLOB_RECURSE libQuickDraw_Sources + *.cpp +) + +add_library(QuickDraw ${libQuickDraw_Sources}) +target_link_libraries(QuickDraw SIMD Data ResourceCore) +target_include_directories(QuickDraw PUBLIC + ${PROJECT_LIBS_DIR} +) diff --git a/libs/libQuickdraw/color/color.cpp b/libs/libQuickdraw/color/color.cpp new file mode 100644 index 0000000..fc6a1d0 --- /dev/null +++ b/libs/libQuickdraw/color/color.cpp @@ -0,0 +1,114 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Construction + +auto quickdraw::rgb(color_component r, color_component g, color_component b, color_component a) -> union color +{ + return { + .components = { + .red = r, .green = g, .blue = b, .alpha = a + } + }; +} + +auto quickdraw::rgb(std::uint16_t rgb555) -> union color +{ + union color c = { + .components = { + .red = static_cast((rgb555 & 0x7c00) >> 7), + .green = static_cast((rgb555 & 0x03e0) >> 2), + .blue = static_cast((rgb555 & 0x001f) << 3), + .alpha = 255 + } + }; + + c.components.red |= c.components.red >> 5; + c.components.green |= c.components.green >> 5; + c.components.blue |= c.components.blue >> 5; + + return c; +} + +// MARK: - Pre-defined Colors + +auto quickdraw::colors::white() -> union color +{ + return rgb(255, 255, 255); +} + +auto quickdraw::colors::black() -> union color +{ + return rgb(0, 0, 0); +} + +auto quickdraw::colors::clear() -> union color +{ + return rgb(0, 0, 0, 0); +} + +// MARK: - YCrCb Color + +auto quickdraw::ycbcr(const union color& rgb) -> union ycbcr +{ + std::uint8_t r = rgb.components.red; + std::uint8_t g = rgb.components.green; + std::uint8_t b = rgb.components.blue; + + auto y = static_cast( 0.299000 * r + 0.587000 * g + 0.114000 * b); + auto cb = static_cast(128 - 0.168736 * r - 0.331264 * g + 0.500000 * b); + auto cr = static_cast(128 + 0.500000 * r - 0.418688 * g - 0.081312 * b); + + std::uint8_t y_clamped = std::clamp(y , 0, 255); + std::uint8_t cb_clamped = std::clamp(cb, 0, 255); + std::uint8_t cr_clamped = std::clamp(cr, 0, 255); + + return (union ycbcr) { + .components = { + .y = y_clamped, + .cb = cb_clamped, + .cr = cr_clamped, + .alpha = rgb.components.alpha + }, + }; +} + +auto quickdraw::rgb(const union ycbcr& color) -> union color +{ + auto color_value = color.value & 0xFFFFFF; + if (color.components.alpha < 0.05) { + return rgb(0, 0, 0, 0); + } + else if (color_value == 0x008080) { + return rgb(0, 0, 0, color.components.alpha); + } + else if (color_value == 0xFF8080) { + return rgb(255, 255, 255, color.components.alpha); + } + + auto r = std::clamp(color.components.y + (1.402000 * (color.components.cr - 128)), 0, 255); + auto g = std::clamp(color.components.y - (0.344136 * (color.components.cb - 128)) - (0.714136 * (color.components.cr - 128)), 0, 255); + auto b = std::clamp(color.components.y + (1.772000 * (color.components.cb - 128)), 0, 255); + + return rgb(r, g, b, color.components.alpha); +} diff --git a/libs/libQuickdraw/color/color.hpp b/libs/libQuickdraw/color/color.hpp new file mode 100644 index 0000000..2f5d014 --- /dev/null +++ b/libs/libQuickdraw/color/color.hpp @@ -0,0 +1,73 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace quickdraw +{ + typedef std::uint8_t color_component; + + union color + { + std::uint32_t value; + struct { + color_component red; + color_component green; + color_component blue; + color_component alpha; + } components; + }; + + union ycbcr + { + std::uint32_t value; + struct { + color_component y; + color_component cb; + color_component cr; + color_component alpha; + } components; + }; + + static auto operator==(const union color& lhs, const union color& rhs) -> bool { return lhs.value == rhs.value; } + static auto operator!=(const union color& lhs, const union color& rhs) -> bool { return lhs.value != rhs.value; } + + [[nodiscard]] auto rgb(color_component r, color_component g, color_component b, color_component a = 255) -> union color; + [[nodiscard]] auto rgb(std::uint16_t rgb555) -> union color; + + namespace constants + { + constexpr std::size_t color_width = sizeof(union color); + constexpr std::size_t rgb555_color_width = 2; + } + + namespace colors + { + [[nodiscard]] auto black() -> union color; + [[nodiscard]] auto white() -> union color; + [[nodiscard]] auto clear() -> union color; + } + + [[nodiscard]] auto ycbcr(const union color& rgb) -> union ycbcr; + [[nodiscard]] auto rgb(const union ycbcr& color) -> union color; +} \ No newline at end of file diff --git a/libs/libQuickdraw/colorspace/depth_2_bpp.cpp b/libs/libQuickdraw/colorspace/depth_2_bpp.cpp new file mode 100644 index 0000000..d185463 --- /dev/null +++ b/libs/libQuickdraw/colorspace/depth_2_bpp.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Drawing + +auto quickdraw::colorspace::depth_2bpp::draw(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface &surface) -> void +{ + if (cfg.mask.data) { + draw_masked(cfg, surface); + return; + } + + for (std::int16_t y = 0; y < cfg.pixmap.bounds.size.height; ++y) { + auto y_offset = y * cfg.pixmap.row_bytes; + + for (std::int16_t x = 0; x < cfg.pixmap.bounds.size.width; ++x) { + auto byte_offset = (3 - (x % 4)) << 1; + + auto byte = cfg.pixmap.data->get(y_offset + (x / 4)); + auto value = (byte >> byte_offset) & 0x3; + + surface.set(x, y, cfg.color_table->at(value)); + } + } +} + +auto quickdraw::colorspace::depth_2bpp::draw_masked(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface &surface) -> void +{ + for (std::int16_t y = 0; y < cfg.pixmap.bounds.size.height; ++y) { + auto y_offset = y * cfg.pixmap.row_bytes; + auto mask_y_offset = y * cfg.mask.row_bytes; + + for (std::int16_t x = 0; x < cfg.pixmap.bounds.size.width; ++x) { + auto byte_offset = (3 - (x % 4)) << 1; + auto mask_offset = (7 - (x % 8)); + + auto byte = cfg.pixmap.data->get(y_offset + (x / 4)); + auto mask = cfg.mask.data->get(mask_y_offset + (x / 8)); + auto value = (byte >> byte_offset) & 0x3; + + if ((mask >> mask_offset) & 0x1) { + surface.set(x, y, cfg.color_table->at(value)); + } + } + } +} diff --git a/libs/libQuickdraw/colorspace/depth_2_bpp.hpp b/libs/libQuickdraw/colorspace/depth_2_bpp.hpp new file mode 100644 index 0000000..cacc337 --- /dev/null +++ b/libs/libQuickdraw/colorspace/depth_2_bpp.hpp @@ -0,0 +1,35 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace quickdraw::colorspace::depth_2bpp +{ + auto draw(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface& surface) -> void; + auto draw_masked(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface& surface) -> void; +} + + + diff --git a/libs/libQuickdraw/colorspace/depth_4bpp.cpp b/libs/libQuickdraw/colorspace/depth_4bpp.cpp new file mode 100644 index 0000000..903e765 --- /dev/null +++ b/libs/libQuickdraw/colorspace/depth_4bpp.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Drawing + +auto quickdraw::colorspace::depth_4bpp::draw(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface &surface) -> void +{ + if (cfg.mask.data) { + draw_masked(cfg, surface); + return; + } + + for (std::int16_t y = 0; y < cfg.pixmap.bounds.size.height; ++y) { + auto y_offset = y * cfg.pixmap.row_bytes; + + for (std::int16_t x = 0; x < cfg.pixmap.bounds.size.width; ++x) { + auto byte_offset = (1 - (x % 2)) << 2; + + auto byte = cfg.pixmap.data->get(y_offset + (x / 2)); + auto value = (byte >> byte_offset) & 0xF; + + surface.set(x, y, cfg.color_table->at(value)); + } + } +} + +auto quickdraw::colorspace::depth_4bpp::draw_masked(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface &surface) -> void +{ + for (std::int16_t y = 0; y < cfg.pixmap.bounds.size.height; ++y) { + auto y_offset = y * cfg.pixmap.row_bytes; + auto mask_y_offset = y * cfg.mask.row_bytes; + + for (std::int16_t x = 0; x < cfg.pixmap.bounds.size.width; ++x) { + auto byte_offset = (1 - (x % 2)) <<2; + auto mask_offset = (7 - (x % 8)); + + auto byte = cfg.pixmap.data->get(y_offset + (x / 2)); + auto mask = cfg.mask.data->get(mask_y_offset + (x / 8)); + auto value = (byte >> byte_offset) & 0xF; + + if ((mask >> mask_offset) & 0x1) { + surface.set(x, y, cfg.color_table->at(value)); + } + } + } +} diff --git a/libs/libQuickdraw/colorspace/depth_4bpp.hpp b/libs/libQuickdraw/colorspace/depth_4bpp.hpp new file mode 100644 index 0000000..d2e6341 --- /dev/null +++ b/libs/libQuickdraw/colorspace/depth_4bpp.hpp @@ -0,0 +1,35 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace quickdraw::colorspace::depth_4bpp +{ + auto draw(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface& surface) -> void; + auto draw_masked(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface& surface) -> void; +} + + + diff --git a/libs/libQuickdraw/colorspace/monochrome.cpp b/libs/libQuickdraw/colorspace/monochrome.cpp new file mode 100644 index 0000000..6b21634 --- /dev/null +++ b/libs/libQuickdraw/colorspace/monochrome.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +#if defined(CHAR_BIT) + static_assert(CHAR_BIT == 8, "We require CHAR_BIT to be equal to 8 here."); +#else +# define CHAR_BIT 8 +#endif + +// MARK: - Drawing + +auto quickdraw::colorspace::monochrome::draw(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface &surface) -> void +{ + if (cfg.mask.data) { + draw_masked(cfg, surface); + return; + } + + for (std::int16_t y = 0; y < cfg.pixmap.bounds.size.height; ++y) { + auto y_offset = y * cfg.pixmap.row_bytes; + for (std::int16_t x = 0; x < cfg.pixmap.bounds.size.width; ++x) { + auto byte_offset = 7 - (x % CHAR_BIT); + auto byte = cfg.pixmap.data->get(y_offset + (x / CHAR_BIT)); + auto value = (byte >> byte_offset) & 0x1; + surface.set(x, y, cfg.color_table->at(value)); + } + } +} + +auto quickdraw::colorspace::monochrome::draw_masked(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface &surface) -> void +{ + for (std::int16_t y = 0; y < cfg.pixmap.bounds.size.height; ++y) { + auto y_offset = y * cfg.pixmap.row_bytes; + auto mask_y_offset = y * cfg.mask.row_bytes; + for (std::int16_t x = 0; x < cfg.pixmap.bounds.size.width; ++x) { + auto byte_offset = 7 - (x % CHAR_BIT); + auto byte = cfg.pixmap.data->get(y_offset + (x / CHAR_BIT)); + auto mask = cfg.mask.data->get(mask_y_offset + (x / CHAR_BIT)); + auto value = (byte >> byte_offset) & 0x1; + + if ((mask >> byte_offset) & 0x1) { + surface.set(x, y, cfg.color_table->at(value)); + } + } + } +} diff --git a/libs/libQuickdraw/colorspace/monochrome.hpp b/libs/libQuickdraw/colorspace/monochrome.hpp new file mode 100644 index 0000000..dd05ee8 --- /dev/null +++ b/libs/libQuickdraw/colorspace/monochrome.hpp @@ -0,0 +1,32 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace quickdraw::colorspace::monochrome +{ + auto draw(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface& surface) -> void; + auto draw_masked(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface& surface) -> void; +} diff --git a/libs/libQuickdraw/colorspace/true_color.cpp b/libs/libQuickdraw/colorspace/true_color.cpp new file mode 100644 index 0000000..494bf6f --- /dev/null +++ b/libs/libQuickdraw/colorspace/true_color.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include + +#if defined(CHAR_BIT) + static_assert(CHAR_BIT == 8, "We require CHAR_BIT to be equal to 8 here."); +#else +# define CHAR_BIT 8 +#endif + +// MARK: - Drawing + +auto quickdraw::colorspace::true_color::draw(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface &surface) -> void +{ + if (cfg.mask.data) { + draw_masked(cfg, surface); + return; + } + + for (std::int16_t y = 0; y < cfg.pixmap.bounds.size.height; ++y) { + auto y_offset = y * cfg.pixmap.row_bytes; + + for (std::int16_t x = 0; x < cfg.pixmap.bounds.size.width; ++x) { + auto byte = cfg.pixmap.data->get(y_offset + x); + surface.set(x, y, cfg.color_table->at(byte)); + } + } +} + +auto quickdraw::colorspace::true_color::draw_masked(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface &surface) -> void +{ + for (std::int16_t y = 0; y < cfg.pixmap.bounds.size.height; ++y) { + auto y_offset = y * cfg.pixmap.row_bytes; + auto mask_y_offset = y * cfg.mask.row_bytes; + + for (std::int16_t x = 0; x < cfg.pixmap.bounds.size.width; ++x) { + auto mask_offset = (7 - (x % CHAR_BIT)); + + auto byte = cfg.pixmap.data->get(y_offset + x); + auto mask = cfg.mask.data->get(mask_y_offset + (x / CHAR_BIT)); + + if ((mask >> mask_offset) & 0x1) { + surface.set(x, y, cfg.color_table->at(byte)); + } + } + } +} diff --git a/libs/libQuickdraw/colorspace/true_color.hpp b/libs/libQuickdraw/colorspace/true_color.hpp new file mode 100644 index 0000000..357e292 --- /dev/null +++ b/libs/libQuickdraw/colorspace/true_color.hpp @@ -0,0 +1,35 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace quickdraw::colorspace::true_color +{ + auto draw(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface& surface) -> void; + auto draw_masked(const quickdraw::pixmap::draw_configuration& cfg, quickdraw::surface& surface) -> void; +} + + + diff --git a/libs/libQuickdraw/format/color_icon.cpp b/libs/libQuickdraw/format/color_icon.cpp new file mode 100644 index 0000000..aa4b163 --- /dev/null +++ b/libs/libQuickdraw/format/color_icon.cpp @@ -0,0 +1,187 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include + +// MARK: - Construction + +quickdraw::color_icon::color_icon(const data::block &data, resource_core::identifier id, const std::string &name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +quickdraw::color_icon::color_icon(data::reader &reader) +{ + decode(reader); +} + +quickdraw::color_icon::color_icon(quickdraw::surface& surface) + : m_surface(std::move(surface)) +{} + +// MARK: - Accessors + +auto quickdraw::color_icon::surface() -> quickdraw::surface& +{ + return m_surface; +} + +auto quickdraw::color_icon::data() -> data::block +{ + data::writer writer; + encode(writer); + return std::move(*const_cast(writer.data())); +} + +// MARK: - Coding + +auto quickdraw::color_icon::encode(data::writer &writer) -> void +{ + auto width = m_surface.size().width; + auto height = m_surface.size().height; + + m_mask_row_bytes = (width - 1) / 8 + 1; + m_bmap_row_bytes = 0; + + data::block color_values(width * height * sizeof(std::uint16_t)); + data::block mask_data(m_mask_row_bytes * height); + data::writer colors(&color_values); + data::writer mask(&mask_data); + + std::uint8_t pass = 0; + std::uint8_t scratch = 0; + + do { + if (pass++ > 0) { + for (std::int16_t y = 0; y < height; ++y) { + for (std::int16_t x = 0; x < width; ++x) { + auto color = m_surface.at(x, y); + m_surface.set(x, y, rgb( + color.components.red & ~(1 << pass), + color.components.green & ~(1 << pass), + color.components.blue & ~(1 << pass), + color.components.alpha + )); + } + } + } + + m_clut = {}; + colors.set_position(0); + mask.set_position(0); + color_values.set(static_cast(0)); + mask_data.set(static_cast(0)); + + for (std::int16_t y = 0; y < height; ++y) { + scratch = 0; + for (std::int16_t x = 0; x < width; ++x) { + auto color = m_surface.at(x, y); + colors.write_short(m_clut.set(color)); + + auto bit_offset = x % 8; + if (bit_offset == 0 && x != 0) { + mask.write_byte(scratch); + scratch = 0; + } + + auto mask_value = (color.components.alpha & 0x80) == 0x1; + mask_value <<= (7 - bit_offset); + scratch |= mask_value; + } + mask.write_byte(scratch); + } + } + while (m_clut.size() > 256); + + // Determine what component configuration we need. + m_pixmap = pixmap(rect({ 0, 0 }, { width, height })); + data::block pmap_data; + + pmap_data = m_pixmap.build_pixel_data(color_values, m_clut.size()); + + // Calculate some offsets + m_mask_base_address = 4; + m_bmap_base_address = m_mask_base_address + mask_data.size(); + + // Write out the image data for the CICN. + writer.write(m_pixmap); + writer.write_long(0); + writer.write_short(m_mask_row_bytes); + m_pixmap.bounds().encode(writer); + writer.write_long(0); + writer.write_short(m_bmap_row_bytes); + m_pixmap.bounds().encode(writer); + writer.write_long(0); + + writer.write_data(&mask_data); + writer.write(m_clut); + writer.write_data(&pmap_data); +} + +auto quickdraw::color_icon::decode(data::reader &reader) -> void +{ + m_pixmap = reader.read(); + + auto cfg = std::move(m_pixmap.basic_draw_configuration()); + cfg.mask.base_address = reader.read_long(); + cfg.mask.row_bytes = reader.read_short(); + cfg.mask.bounds = reader.read>(); + cfg.bitmap.base_address = reader.read_long(); + cfg.bitmap.row_bytes = reader.read_short(); + cfg.bitmap.bounds = reader.read>(); + + reader.move(4); + + auto mask_data = reader.read_data(cfg.mask.expected_data_size()); + auto bmap_data = reader.read_data(cfg.bitmap.expected_data_size()); + cfg.color_table = &(m_clut = reader.read()); + auto pmap_data = reader.read_data(cfg.pixmap.expected_data_size()); + + cfg.mask.data = &mask_data; + cfg.bitmap.data = &bmap_data; + cfg.pixmap.data = &pmap_data; + + m_surface = quickdraw::surface(cfg.pixmap.bounds.size); + + if (m_pixmap.total_component_width() == 1) { + colorspace::monochrome::draw(cfg, m_surface); + } + else if (m_pixmap.total_component_width() == 2) { + colorspace::depth_2bpp::draw(cfg, m_surface); + } + else if (m_pixmap.total_component_width() == 4) { + colorspace::depth_4bpp::draw(cfg, m_surface); + } + else if (m_pixmap.total_component_width() == 8) { + colorspace::true_color::draw(cfg, m_surface); + } + else { + throw std::runtime_error("Currently unsupported cicn configuration: cmp_size=" + + std::to_string(m_pixmap.component_size()) + + ", cmp_count=" + std::to_string(m_pixmap.component_count())); + } +} \ No newline at end of file diff --git a/libs/libQuickdraw/format/color_icon.hpp b/libs/libQuickdraw/format/color_icon.hpp new file mode 100644 index 0000000..1217fc0 --- /dev/null +++ b/libs/libQuickdraw/format/color_icon.hpp @@ -0,0 +1,62 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +namespace quickdraw +{ + struct color_icon + { + public: + static auto type_code() -> std::string { return "cicn"; } + + public: + color_icon() = default; + explicit color_icon(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); + explicit color_icon(data::reader& reader); + explicit color_icon(quickdraw::surface& surface); + + auto surface() -> surface&; + + auto encode(data::writer& writer) -> void; + auto data() -> data::block; + + private: + resource_core::identifier m_id { resource_core::auto_resource_id }; + std::string m_name; + quickdraw::pixmap m_pixmap; + std::uint32_t m_mask_base_address { 0 }; + std::uint16_t m_mask_row_bytes { 0 }; + rect m_mask_bounds; + std::uint32_t m_bmap_base_address { 0 }; + std::uint16_t m_bmap_row_bytes { 0 }; + rect m_bmap_bounds; + quickdraw::surface m_surface; + quickdraw::color_lookup_table m_clut; + + auto decode(data::reader& reader) -> void; + }; +} diff --git a/libs/libQuickdraw/format/color_lookup_table.cpp b/libs/libQuickdraw/format/color_lookup_table.cpp new file mode 100644 index 0000000..600c4ee --- /dev/null +++ b/libs/libQuickdraw/format/color_lookup_table.cpp @@ -0,0 +1,137 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include + +// MARK: - Construction + +quickdraw::color_lookup_table::color_lookup_table(const data::block &data, resource_core::identifier id, const std::string &name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +quickdraw::color_lookup_table::color_lookup_table(data::reader &reader) + : m_id(0), m_name("Embedded `clut` resource") +{ + decode(reader); +} + +// MARK: - Accessors + +auto quickdraw::color_lookup_table::size() const -> size_type +{ + return m_entries.size(); +} + +auto quickdraw::color_lookup_table::at(index_type index) const -> union color +{ + for (const auto& entry : m_entries) { + if (entry.first == index) { + return entry.second; + } + } + return colors::black(); +} + +auto quickdraw::color_lookup_table::set(union color color) -> index_type +{ + index_type value = 0; + for (const auto& entry : m_entries) { + if (entry.second.value == color.value) { + return entry.first; + } + else { + value = std::max(value, entry.first); + } + } + m_entries.emplace_back(value, color); + m_size = m_entries.size(); + return value; +} + +auto quickdraw::color_lookup_table::set(index_type index, union color color) -> void +{ + for (auto& entry : m_entries) { + if (entry.first == index) { + entry.second = color; + return; + } + } + m_entries.emplace_back(index, color); +} + +// MARK: - Coding + +auto quickdraw::color_lookup_table::encode(data::writer &writer) -> void +{ + writer.write_long(m_seed); + writer.write_enum(m_flags); + writer.write_short(m_size - 1); + + for (auto entry : m_entries) { + writer.write_short(entry.first); + writer.write_short(static_cast((entry.second.components.red / 255.0) * 65535.0)); + writer.write_short(static_cast((entry.second.components.green / 255.0) * 65535.0)); + writer.write_short(static_cast((entry.second.components.blue / 255.0) * 65535.0)); + } +} + +auto quickdraw::color_lookup_table::decode(data::reader &reader) -> void +{ + m_seed = reader.read_long(); + m_flags = reader.read_enum(); + m_size = reader.read_short() + 1; + + for (std::uint16_t i = 0; i < m_size; ++i) { + auto value = reader.read_short(); + std::uint16_t index = m_flags == device ? i : value; + m_entries.emplace_back(index, rgb( + static_cast((reader.read_short() / 65535.0) * 255.0), + static_cast((reader.read_short() / 65535.0) * 255.0), + static_cast((reader.read_short() / 65535.0) * 255.0) + )); + } +} + +// MARK: - Iterators + +auto quickdraw::color_lookup_table::begin() -> iterator +{ + return { this, 0 }; +} + +auto quickdraw::color_lookup_table::end() -> iterator +{ + return { this, m_size }; +} + +auto quickdraw::color_lookup_table::begin() const -> iterator +{ + return { const_cast(this), 0 }; +} + +auto quickdraw::color_lookup_table::end() const -> iterator +{ + return { const_cast(this), m_size }; +} \ No newline at end of file diff --git a/libs/libQuickdraw/format/color_lookup_table.hpp b/libs/libQuickdraw/format/color_lookup_table.hpp new file mode 100644 index 0000000..f7f528b --- /dev/null +++ b/libs/libQuickdraw/format/color_lookup_table.hpp @@ -0,0 +1,93 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace quickdraw +{ + struct color_lookup_table + { + public: + typedef std::uint16_t size_type; + typedef std::uint16_t index_type; + + static auto type_code() -> std::string { return "clut"; } + + enum flags : std::uint16_t + { + pixmap = 0x0000, + device = 0x8000 + }; + + struct iterator + { + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = union color; + using pointer = value_type*; + using reference = value_type&; + + iterator(struct color_lookup_table *clut, index_type idx) : m_clut(clut), m_idx(idx) {} + + auto operator*() const -> reference { return m_clut->m_entries[m_idx].second; } + auto operator->() -> pointer { return &m_clut->m_entries[m_idx].second; } + auto operator++() -> iterator& { ++m_idx; return *this; } + auto operator++(int) -> iterator { iterator tmp = *this; ++(*this); return tmp; } + friend auto operator==(const iterator& a, const iterator& b) -> bool { return (a.m_clut == b.m_clut) && (a.m_idx == b.m_idx); } + friend auto operator!=(const iterator& a, const iterator& b) -> bool { return (a.m_clut != b.m_clut) && (a.m_idx == b.m_idx); } + + private: + struct color_lookup_table *m_clut; + index_type m_idx; + }; + + public: + color_lookup_table() = default; + explicit color_lookup_table(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); + explicit color_lookup_table(data::reader& reader); + + [[nodiscard]] auto size() const -> size_type; + + [[nodiscard]] auto at(index_type index) const -> union color; + auto set(union color color) -> index_type; + auto set(index_type index, union color color) -> void; + + auto begin() -> iterator; + auto end() -> iterator; + [[nodiscard]] auto begin() const -> iterator; + [[nodiscard]] auto end() const -> iterator; + + auto encode(data::writer& writer) -> void; + + private: + resource_core::identifier m_id; + std::string m_name; + std::uint32_t m_seed; + enum flags m_flags { pixmap }; + size_type m_size; + std::vector> m_entries; + + auto decode(data::reader& reader) -> void; + }; +} \ No newline at end of file diff --git a/libs/libQuickdraw/format/picture.cpp b/libs/libQuickdraw/format/picture.cpp new file mode 100644 index 0000000..58633d3 --- /dev/null +++ b/libs/libQuickdraw/format/picture.cpp @@ -0,0 +1,629 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include + +// MARK: - Constants + +namespace quickdraw::constants +{ + static constexpr std::uint16_t v1_magic = 0x1101; + static constexpr std::uint32_t v2_magic = 0x001102ff; +} + +// MARK: - Construction + +quickdraw::picture::picture(const data::block &data, resource_core::identifier id, const std::string &name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +quickdraw::picture::picture(data::reader &reader) +{ + decode(reader); +} + +quickdraw::picture::picture(quickdraw::surface &surface) + : m_surface(std::move(surface)), m_dpi(72, 72) +{ + m_frame.size = m_surface.size(); +} + +// MARK: - Accessors + +auto quickdraw::picture::surface() -> quickdraw::surface & +{ + return m_surface; +} + +auto quickdraw::picture::data() -> data::block +{ + data::writer writer; + encode(writer); + return std::move(*const_cast(writer.data())); +} + +// MARK: - Decoding + +auto quickdraw::picture::decode(data::reader &reader) -> void +{ + reader.move(2); + + m_frame = reader.read>(); + bool v1 = false; + + if (reader.read_short(0, data::reader::mode::peek) == constants::v1_magic) { + reader.move(2); + v1 = true; + } + else { + if (reader.read_long() != constants::v2_magic) { + throw std::runtime_error("Invalid PICT resource. Incorrect header: " + std::to_string(m_id) + ", " + m_name); + } + + // The very first thing we should find is an extended header opcode. Read this + // outside of the main opcode loop as it should only appear once. + if (reader.read_enum() != opcode::ext_header) { + throw std::runtime_error("Expected to find PICT Extended Header."); + } + + if ((reader.read_long() >> 16) != 0xFFFE) { + // Standard header variant + auto rect = reader.read>(); + m_dpi.x = m_frame.size.width / rect.size.width; + m_dpi.y = m_frame.size.height / rect.size.height; + } + else { + // Extended header variant + reader.move(sizeof(std::uint32_t) * 2); + auto rect = reader.read>(); + m_dpi.x = static_cast(m_frame.size.width) / rect.size.width; + m_dpi.y = static_cast(m_frame.size.height) / rect.size.height; + + // This isn't strictly correct, but it allows us to decode some images which would otherwise + // fail due to mismatched frame sizes. QuickDraw would normally scale such images down to fit the frame + // but here we just expand the frame. + // TODO: Make this a setting, so that we can handle scaling + m_frame.size = rect.size; + } + + if (m_dpi.x <= 0 || m_dpi.y <= 0) { + throw std::runtime_error("Invalid PICT resource. Content aspect ratio is not valid: " + std::to_string(m_id) + ", " + m_name); + } + + reader.move(4); + } + + // Begin parsing PICT opcodes + auto opcode = opcode::eof; + quickdraw::rect clipping_rect; + m_size = 0; + m_surface = quickdraw::surface(m_frame.size); + + while (!reader.eof()) { + if (v1) { + opcode = static_cast(reader.read_byte()); + } + else { + reader.move(reader.position() % sizeof(std::uint16_t)); + opcode = reader.read_enum(); + } + + if (opcode == opcode::eof) { + break; + } + + switch (opcode) { + case opcode::clip_region: { + clipping_rect = read_region(reader); + break; + } + case opcode::origin: { + m_frame.origin = point::read(reader, coding_type::macintosh); + break; + } + case opcode::bits_rect: { + read_indirect_bits_rect(reader, false, false); + break; + } + case opcode::bits_region: { + read_indirect_bits_rect(reader, false, true); + break; + } + case opcode::pack_bits_rect: { + read_indirect_bits_rect(reader, true, false); + break; + } + case opcode::pack_bits_region: { + read_indirect_bits_rect(reader, true, true); + break; + } + case opcode::direct_bits_rect: { + read_direct_bits_rect(reader, false); + break; + } + case opcode::direct_bits_region: { + read_direct_bits_rect(reader, true); + break; + } + case opcode::long_comment: { + read_long_comment(reader); + break; + } + case opcode::short_comment: { + read_short_comment(reader); + break; + } + case opcode::short_line_from: + case opcode::pen_mode: { + reader.move(2); + break; + } + case opcode::line_from: + case opcode::pen_size: { + reader.move(4); + break; + } + case opcode::short_line: + case opcode::rgb_fg_color: + case opcode::rgb_bg_color: + case opcode::hilite_color: + case opcode::op_color: { + reader.move(6); + break; + } + case opcode::pen_pattern: + case opcode::fill_pattern: + case opcode::line: + case opcode::frame_rect: + case opcode::paint_rect: + case opcode::erase_rect: + case opcode::invert_rect: + case opcode::fill_rect: + case opcode::frame_same_rect: + case opcode::paint_same_rect: + case opcode::erase_same_rect: + case opcode::invert_same_rect: + case opcode::fill_same_rect: { + reader.move(8); + break; + } + case opcode::frame_region: + case opcode::paint_region: + case opcode::erase_region: + case opcode::invert_region: + case opcode::fill_region: { + read_region(reader); + break; + } + case opcode::nop: + case opcode::eof: + case opcode::ext_header: + case opcode::hilite_mode: + case opcode::def_hilite: { + break; + } + case opcode::compressed_quicktime: { + // Compressed QuickTime data is often followed by drawing routines telling you that you need QuickTime + // to decode the image. We should skip these for now and just return after a successful decode. + read_compressed_quicktime(reader); + break; + } + case opcode::uncompressed_quicktime: { + read_uncompressed_quicktime(reader); + break; + } + default: { + throw std::runtime_error("Encountered an incompatible PICT: " + std::to_string(m_id) + ", " + m_name); + } + } + } +} + +// MARK: - Encoding + +auto quickdraw::picture::encode(data::writer &writer) -> void +{ + write_header(writer); + write_def_hilite(writer); + write_clip_region(writer); + write_direct_bits_rect(writer); + + // Make sure we're word aligned and put out the end of picture opcode. + auto align_adjust = writer.position() % sizeof(std::uint16_t); + for (auto n = 0; n < align_adjust; ++n) { + writer.write_byte(0); + } + writer.write_enum(opcode::eof); +} + +// MARK: - Reading Functions + +auto quickdraw::picture::read_region(data::reader &reader) const -> rect +{ + auto size = reader.read_short(); + auto rect = reader.read>(); + + rect.origin.x /= m_dpi.x; + rect.origin.y /= m_dpi.y; + rect.size.width /= m_dpi.x; + rect.size.height /= m_dpi.y; + + reader.move(size - 10); + + return rect; +} + +auto quickdraw::picture::read_long_comment(data::reader &reader) const -> void +{ + reader.move(2); + reader.move(reader.read_short()); +} + +auto quickdraw::picture::read_short_comment(data::reader &reader) const -> void +{ + reader.move(2); +} + +auto quickdraw::picture::read_indirect_bits_rect(data::reader &reader, bool packed, bool region) -> void +{ + quickdraw::pixmap pm; + quickdraw::color_lookup_table color_table; + + // Determine if we're dealing with a PixMap or an old style BitMap. + bool is_pixmap = reader.read_short(0, data::reader::mode::peek) & 0x8000; + if (is_pixmap) { + // The PixMap base address is omitted here, step back when reading. + pm = reader.read(-4); + color_table = reader.read(); + } + else { + // Old style Bitmap + pm.set_pack_type(pixmap::argb); + pm.set_component_count(1); + pm.set_component_size(1); + pm.set_row_bytes(reader.read_short()); + pm.set_bounds(reader.read>()); + + // Monochrome color table + color_table.set(colors::white()); + color_table.set(colors::black()); + } + + m_format = pm.pixel_size(); + + // Read the source and destination bounds + auto source_rect = reader.read>(); + auto destination_rect = reader.read>(); + + auto transfer_mode = reader.read_short(); + if (region) { + read_region(reader); + } + + // Setup pixel buffer for raw values + data::block raw_data; + auto row_bytes = pm.row_bytes(); + auto width = pm.bounds().size.width; + auto height = pm.bounds().size.height; + + if (packed) { + data::writer raw; + for (std::int16_t scanline = 0; scanline < height; ++scanline) { + auto data = reader.read_compressed_data>(row_bytes > 250 ? reader.read_short() : reader.read_byte()); + raw.write_data(&data); + } + raw_data = std::move(*raw.data()); + } + else { + raw_data = reader.read_data(row_bytes * height); + } + + destination_rect.origin.x -= m_frame.origin.x; + destination_rect.origin.y -= m_frame.origin.y; + pm.build_surface(m_surface, raw_data, color_table, destination_rect); + m_size += width * height; +} + +auto quickdraw::picture::read_direct_bits_rect(data::reader &reader, bool region) -> void +{ + auto pm = reader.read(); + m_format = pm.pixel_size() == 16 ? 16 : pm.component_size() * pm.component_count(); + + // Read the source and destination bounds + auto source_rect = reader.read>(); + auto destination_rect = reader.read>(); + auto transfer_mode = reader.read_short(); + + if (region) { + read_region(reader); + } + + auto pack_type = pm.pack_type(); + auto component_count = pm.component_count(); + auto color_model = pm.pixel_format(); + auto row_bytes = pm.row_bytes(); + auto bounds = pm.bounds(); + + + // Calculate the bounds of the pixels we need to copy to the surface, clipping to fit if necessary + auto copy_x = destination_rect.origin.x - m_frame.origin.x; + auto copy_y = destination_rect.origin.y - m_frame.origin.y; + auto copy_w = std::min(destination_rect.size.width, static_cast(m_frame.size.width - copy_x)); + auto copy_h = std::min(destination_rect.size.height, static_cast(m_frame.size.height - copy_y)); + + // When row bytes < 8, data is never packed. Raw format will instead match the pixel size, either 16-bit words or + // 32-bit argb. + auto packed = row_bytes >= 8 && pack_type >= pixmap::packbits_word; + if (row_bytes < 8 && pack_type != pixmap::packbits_word) { + pack_type = pixmap::argb; + } + else if (pack_type == pixmap::none || pack_type == pixmap::rgb) { + // Row bytes is always width * 4, but alpha is omitted here so make sure we only read width * 3. + row_bytes = bounds.size.width * 3; + } + + data::block raw_data; + for (auto y = 0; y < copy_h; ++y) { + if (packed) { + auto pack_bytes_count = row_bytes > 250 ? reader.read_short() : reader.read_byte(); + if (pack_type == pixmap::packbits_word) { + raw_data = std::move(reader.read_compressed_data>(pack_bytes_count)); + } + else { + raw_data = std::move(reader.read_compressed_data>(pack_bytes_count)); + } + } + else { + raw_data = std::move(reader.read_data(row_bytes)); + } + + for (auto x = 0; x < copy_w; ++x) { + switch (pack_type) { + case pixmap::none: + case pixmap::rgb: { + m_surface.set(x + copy_x, y + copy_y, rgb( + raw_data.get(3 * x), + raw_data.get(3 * x + 1), + raw_data.get(3 * x + 2) + )); + break; + } + case pixmap::argb: { + m_surface.set(x + copy_x, y + copy_y, rgb( + raw_data.get(4 * x + 1), + raw_data.get(4 * x + 2), + raw_data.get(4 * x + 3) + )); + break; + } + case pixmap::packbits_word: { + m_surface.set(x + copy_x, y + copy_y, rgb( + (raw_data.get(2 * x) << 8) | (raw_data.get(2 * x + 1)) + )); + break; + } + case pixmap::packbits_component: { + if (component_count == 3) { + m_surface.set(x + copy_x, y + copy_y, rgb( + raw_data.get(x), + raw_data.get(bounds.size.width + x), + raw_data.get(2 * bounds.size.width + x) + )); + } + else if (component_count == 4) { + m_surface.set(x + copy_x, y + copy_y, rgb( + raw_data.get(bounds.size.width + x), + raw_data.get(2 * bounds.size.width + x), + raw_data.get(3 * bounds.size.width + x) + )); + } + break; + } + } + } + } +} + +auto quickdraw::picture::read_compressed_quicktime(data::reader &reader) -> void +{ + auto length = reader.read_long(); + reader.move(38); + auto matte_size = reader.read_long(); + auto matte_rect = reader.read>(); + reader.move(2); + auto resource_rect = reader.read>(); + reader.move(4); + auto mask_size = reader.read_long(); + + if (matte_size > 0) { + auto matte = quicktime::image_description(reader); + } + + if (mask_size > 0) { + reader.move(mask_size); + } + + auto image_description = quicktime::image_description(reader); + m_surface = image_description.surface(); + m_format = static_cast(image_description.compressor()); +} + +auto quickdraw::picture::read_uncompressed_quicktime(data::reader &reader) -> void +{ + auto length = reader.read_long(); + reader.move(38); + auto matte_size = reader.read_long(); + auto matte_rect = reader.read>(); + + if (matte_size > 0) { + read_image_description(reader); + } +} + +auto quickdraw::picture::read_image_description(data::reader &reader) -> void +{ + auto length = reader.read_long(); + if (length != 86) { + throw std::runtime_error("Invalid QuickTime image description in PICT: " + std::to_string(m_id) + ", " + m_name); + } + + auto compressor = reader.read_long(); + reader.move(24); + auto size = reader.read>(); + reader.move(8); + auto data_size = reader.read_long(); + reader.move(34); + auto depth = reader.read_short(); + if (depth > 32) { + depth -= 32; // Grayscale + } + + auto color_table = reader.read(); + + if (compressor == 'rle ') { + // RLE is often garbage or redundant, skip over it and hope we find other image data later. + reader.move(data_size); + return; + } + + switch (compressor) { + default: { + std::string compressor_name; + compressor_name.push_back(compressor >> 24); + compressor_name.push_back(compressor >> 16); + compressor_name.push_back(compressor >> 8); + compressor_name.push_back(compressor); + + throw std::runtime_error("Unsupported QuickTime compressor '" + compressor_name + "' at offset " + std::to_string(reader.position()) + + " in PICT: " + std::to_string(m_id) + ", " + m_name); + } + } +} + +// MARK: - Writing Functions + +auto quickdraw::picture::write_header(data::writer &writer) -> void +{ + // Write the size of zero. This seems to be fine. + writer.write_short(0); + + // Set the image frame + writer.write>(m_frame); + + // We're only dealing with PICT version 2 currently. + writer.write_long(constants::v2_magic); + writer.write_enum(opcode::ext_header); + writer.write_long(0xFFFE0000); + + // Image resolution (72dpi + writer.write_short(static_cast(m_dpi.x)); + writer.write_short(0); + writer.write_short(static_cast(m_dpi.y)); + writer.write_short(0); + + // Optimal source frame. (Identical to the image frame) + writer.write>(m_frame); + + // Reserved + writer.write_long(0); +} + +auto quickdraw::picture::write_def_hilite(data::writer &writer) -> void +{ + writer.write_enum(opcode::def_hilite); +} + +auto quickdraw::picture::write_clip_region(data::writer &writer) -> void +{ + writer.write_enum(opcode::clip_region); + writer.write_short(10); + writer.write>(m_frame); +} + +auto quickdraw::picture::write_direct_bits_rect(data::writer &writer) -> void +{ + writer.write_enum(opcode::direct_bits_rect); + + quickdraw::pixmap pm(m_frame); + writer.write(pm); + + // Source and destination frames - identical to the image frame. + writer.write>(m_frame); + writer.write>(m_frame); + + // Specify the transfer mode. + writer.write_short(0); + + // Prepare to write out the actual image data. + data::block scanline_data(m_frame.size.width * pm.component_count()); + data::writer scanline(&scanline_data); + + for (std::int16_t y = 0; y < m_frame.size.height; ++y) { + scanline_data.set(static_cast(0)); + + if (pm.component_count() == 3) { + for (std::int16_t x = 0; x < m_frame.size.width; ++x) { + auto pixel = m_surface.at(x, y); + + scanline.set_position(x); + scanline.write_byte(pixel.components.red); + + scanline.set_position(x + m_frame.size.width); + scanline.write_byte(pixel.components.green); + + scanline.set_position(x + m_frame.size.width * 2); + scanline.write_byte(pixel.components.blue); + } + } + else if (pm.component_count() == 4) { + for (std::int16_t x = 0; x < m_frame.size.width; ++x) { + auto pixel = m_surface.at(x, y); + + scanline.set_position(x); + scanline.write_byte(pixel.components.alpha); + + scanline.set_position(x + m_frame.size.width); + scanline.write_byte(pixel.components.red); + + scanline.set_position(x + m_frame.size.width * 2); + scanline.write_byte(pixel.components.green); + + scanline.set_position(x + m_frame.size.width * 3); + writer.write_byte(pixel.components.blue); + } + } + + auto packed = std::move(compression::packbits<8>::compress(scanline_data)); + if (pm.row_bytes() > 250) { + writer.write_short(packed.size()); + } + else { + writer.write_byte(packed.size()); + } + writer.write_data(&packed); + } +} \ No newline at end of file diff --git a/libs/libQuickdraw/format/picture.hpp b/libs/libQuickdraw/format/picture.hpp new file mode 100644 index 0000000..3165607 --- /dev/null +++ b/libs/libQuickdraw/format/picture.hpp @@ -0,0 +1,119 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace quickdraw +{ + struct picture + { + public: + static auto type_code() -> std::string { return "PICT"; } + + public: + picture() = default; + explicit picture(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); + explicit picture(data::reader& reader); + explicit picture(quickdraw::surface& surface); + + auto encode(data::writer& writer) -> void; + auto data() -> data::block; + + auto surface() -> quickdraw::surface&; + + private: + enum class opcode : std::uint16_t { + nop = 0x0000, + clip_region = 0x0001, + pen_size = 0x0007, + pen_mode = 0x0008, + pen_pattern = 0x0009, + fill_pattern = 0x000a, + origin = 0x000c, + rgb_fg_color = 0x001a, + rgb_bg_color = 0x001b, + hilite_mode = 0x001c, + hilite_color = 0x001d, + def_hilite = 0x001e, + op_color = 0x001f, + line = 0x0020, + line_from = 0x0021, + short_line = 0x0022, + short_line_from = 0x0023, + frame_rect = 0x0030, + paint_rect = 0x0031, + erase_rect = 0x0032, + invert_rect = 0x0033, + fill_rect = 0x0034, + frame_same_rect = 0x0038, + paint_same_rect = 0x0039, + erase_same_rect = 0x003a, + invert_same_rect = 0x003b, + fill_same_rect = 0x003c, + bits_rect = 0x0090, + bits_region = 0x0091, + pack_bits_rect = 0x0098, + pack_bits_region = 0x0099, + direct_bits_rect = 0x009a, + direct_bits_region = 0x009b, + frame_region = 0x0080, + paint_region = 0x0081, + erase_region = 0x0082, + invert_region = 0x0083, + fill_region = 0x0084, + short_comment = 0x00a0, + long_comment = 0x00a1, + eof = 0x00ff, + ext_header = 0x0c00, + compressed_quicktime = 0x8200, + uncompressed_quicktime = 0x8201, + }; + + private: + resource_core::identifier m_id { 0 }; + std::string m_name; + quickdraw::surface m_surface; + rect m_frame; + point m_dpi; + std::size_t m_size; + std::uint32_t m_format { 0 }; + + auto decode(data::reader& reader) -> void; + auto read_region(data::reader& reader) const -> rect; + auto read_long_comment(data::reader& reader) const -> void; + auto read_short_comment(data::reader& reader) const -> void; + auto read_direct_bits_rect(data::reader& reader, bool region) -> void; + auto read_indirect_bits_rect(data::reader& reader, bool packed, bool region) -> void; + auto read_compressed_quicktime(data::reader& reader) -> void; + auto read_uncompressed_quicktime(data::reader& reader) -> void; + auto read_image_description(data::reader& reader) -> void; + + auto write_header(data::writer& writer) -> void; + auto write_def_hilite(data::writer& writer) -> void; + auto write_clip_region(data::writer& writer) -> void; + auto write_direct_bits_rect(data::writer& writer) -> void; + + }; +} \ No newline at end of file diff --git a/libs/libQuickdraw/format/pixel_pattern.cpp b/libs/libQuickdraw/format/pixel_pattern.cpp new file mode 100644 index 0000000..8a01dd9 --- /dev/null +++ b/libs/libQuickdraw/format/pixel_pattern.cpp @@ -0,0 +1,161 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include + +// MARK: - Construction + +quickdraw::pixel_pattern::pixel_pattern(const data::block &data, resource_core::identifier id, const std::string& name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +quickdraw::pixel_pattern::pixel_pattern(data::reader &reader) +{ + decode(reader); +} + +quickdraw::pixel_pattern::pixel_pattern(quickdraw::surface& surface) + : m_surface(surface) +{ +} + +// MARK: - Accessors + +auto quickdraw::pixel_pattern::surface() -> struct surface & +{ + return m_surface; +} + +auto quickdraw::pixel_pattern::data() -> data::block +{ + data::writer writer; + encode(writer); + return std::move(*const_cast(writer.data())); +} + +// MARK: - Coding + +auto quickdraw::pixel_pattern::encode(data::writer &writer) -> void +{ + auto width = m_surface.size().width; + auto height = m_surface.size().height; + + // TODO: This is a brute force method of bringing down the color depth/number of colors required, + // for a ppat image. It doesn't optimise for image quality at all, and should be replaced at some point. + data::block color_data(width * height * sizeof(std::uint16_t)); + data::writer colors(&color_data); + + std::uint8_t pass = 0; + do { + if (pass++ > 0) { + for (std::int16_t y = 0; y < height; ++y) { + for (std::int16_t x = 0; x < width; ++x) { + auto color = m_surface.at(x, y); + m_surface.set(x, y, rgb( + color.components.red & ~(1 << pass), + color.components.green & ~(1 << pass), + color.components.blue & ~(1 << pass), + color.components.alpha + )); + } + } + } + + // Rebuild the color table for the surface. To do this we want to create an empty table and populate it. + m_clut = {}; + colors.set_position(0); + color_data.set(static_cast(0)); + for (std::int16_t y = 0; y < height; ++y) { + for (std::int16_t x = 0; x < width; ++x) { + auto color = m_surface.at(x, y); + colors.write_short(m_clut.set(color)); + } + } + } + while (m_clut.size() > 256); + + // Determine what component configuration we need + m_pixmap = pixmap(rect({ 0, 0 }, { width, height })); + data::block pmap_data; + + if (m_clut.size() > 256) { + throw std::runtime_error("Implementation does not currently handle more than 256 colors in a PPAT"); + } + else if (m_clut.size() > 16) { + pmap_data = m_pixmap.build_pixel_data(color_data, 8); + } + else if (m_clut.size() > 4) { + pmap_data = m_pixmap.build_pixel_data(color_data, 4); + } + else if (m_clut.size() > 2) { + pmap_data = m_pixmap.build_pixel_data(color_data, 2); + } + else { + pmap_data = m_pixmap.build_pixel_data(color_data, 1); + } + + // Calculate some offsets + m_pat_type = 1; + m_pmap_base_address = 28; + m_pat_base_address = m_pmap_base_address + 50; + m_pixmap.set_pm_table(m_pat_base_address + pmap_data.size()); + + // Write out the image data for the ppat. + writer.write_short(m_pat_type); + writer.write_long(m_pmap_base_address); + writer.write_long(m_pat_base_address); + writer.write_long(0); + writer.write_short(0); + writer.write_long(0); + writer.write_quad(0); + writer.write(m_pixmap); + writer.write_data(&pmap_data); + writer.write(m_clut); +} + +auto quickdraw::pixel_pattern::decode(data::reader &reader) -> void +{ + m_pat_type = reader.read_short(); + if (m_pat_type != 1) { + throw std::runtime_error("Currently unsupported ppat configuration: pat_type=" + std::to_string(m_pat_type)); + } + + m_pmap_base_address = reader.read_long(); + m_pat_base_address = reader.read_long(); + + reader.set_position(m_pmap_base_address); + m_pixmap = reader.read(); + + reader.set_position(m_pat_base_address); + auto pmap_data_size = m_pixmap.row_bytes() * m_pixmap.bounds().size.height; + auto pmap_data = reader.read_data(pmap_data_size); + + reader.set_position(m_pixmap.pm_table()); + m_clut = reader.read(); + + auto surface_size = m_pixmap.bounds().size; + m_surface = quickdraw::surface(surface_size); + m_pixmap.build_surface(m_surface, pmap_data, m_clut, m_pixmap.bounds()); +} \ No newline at end of file diff --git a/libs/libQuickdraw/format/pixel_pattern.hpp b/libs/libQuickdraw/format/pixel_pattern.hpp new file mode 100644 index 0000000..2e32a11 --- /dev/null +++ b/libs/libQuickdraw/format/pixel_pattern.hpp @@ -0,0 +1,59 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +namespace quickdraw +{ + struct pixel_pattern + { + public: + static auto type_code() -> std::string { return "ppat"; } + + public: + pixel_pattern() = default; + explicit pixel_pattern(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); + explicit pixel_pattern(data::reader& reader); + explicit pixel_pattern(surface& surface); + + auto surface() -> quickdraw::surface&; + + auto encode(data::writer& writer) -> void; + auto data() -> data::block; + + private: + resource_core::identifier m_id; + std::string m_name; + std::uint16_t m_pat_type; + std::uint32_t m_pmap_base_address; + std::uint32_t m_pat_base_address; + quickdraw::pixmap m_pixmap; + quickdraw::surface m_surface; + quickdraw::color_lookup_table m_clut; + + auto decode(data::reader& reader) -> void; + }; +} \ No newline at end of file diff --git a/libs/libQuickdraw/pixmap/pixmap.cpp b/libs/libQuickdraw/pixmap/pixmap.cpp new file mode 100644 index 0000000..fdf1e94 --- /dev/null +++ b/libs/libQuickdraw/pixmap/pixmap.cpp @@ -0,0 +1,309 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Construction + +quickdraw::pixmap::pixmap(const rect& frame, bool rgb555) + : m_bounds(frame), + m_row_bytes(frame.size.width * (rgb555 ? constants::rgb555_color_width : constants::color_width)), + m_pack_type(rgb555 ? pack_type::packbits_word : pack_type::packbits_component), + m_pack_size(0), + m_dpi(72, 72), + m_pixel_type(16), + m_pixel_size(rgb555 ? 16 : 32), + m_component_count(3), + m_component_size(rgb555 ? 5 : 8) +{ +} + +quickdraw::pixmap::pixmap(const data::block &data, resource_core::identifier id, const std::string &name) +{ + data::reader reader(&data); + decode(reader); +} + +quickdraw::pixmap::pixmap(data::reader &reader) +{ + decode(reader); +} + +// MARK: - Accessors + +auto quickdraw::pixmap::bounds() const -> rect +{ + return m_bounds; +} + +auto quickdraw::pixmap::row_bytes() const -> std::int16_t +{ + return m_row_bytes; +} + +auto quickdraw::pixmap::pack_type() const -> enum pack_type +{ + return m_pack_type; +} + +auto quickdraw::pixmap::pack_size() const -> std::int16_t +{ + return m_pack_size; +} + +auto quickdraw::pixmap::pixel_type() const -> std::int16_t +{ + return m_pixel_type; +} + +auto quickdraw::pixmap::pixel_size() const -> std::int16_t +{ + return m_pixel_size; +} + +auto quickdraw::pixmap::component_count() const -> std::int16_t +{ + return m_component_count; +} + +auto quickdraw::pixmap::component_size() const -> std::int16_t +{ + return m_component_size; +} + +auto quickdraw::pixmap::pixel_format() const -> enum pixel_format +{ + return m_pixel_format; +} + +auto quickdraw::pixmap::pm_table() const -> std::uint32_t +{ + return m_pm_table; +} + +auto quickdraw::pixmap::set_bounds(const rect& rect) -> void +{ + m_bounds = rect; +} + +auto quickdraw::pixmap::set_row_bytes(std::int16_t row_bytes) -> void +{ + m_row_bytes = row_bytes; +} + +auto quickdraw::pixmap::set_pack_type(enum pack_type pack_type) -> void +{ + m_pack_type = pack_type; +} + +auto quickdraw::pixmap::set_pack_size(std::int16_t pack_size) -> void +{ + m_pack_size = pack_size; +} + +auto quickdraw::pixmap::set_pixel_type(std::int16_t pixel_type) -> void +{ + m_pixel_type = pixel_type; +} + +auto quickdraw::pixmap::set_pixel_size(std::int16_t pixel_size) -> void +{ + m_pixel_size = pixel_size; +} + +auto quickdraw::pixmap::set_component_count(std::int16_t component_count) -> void +{ + m_component_count = component_count; +} + +auto quickdraw::pixmap::set_component_size(std::int16_t component_size) -> void +{ + m_component_size = component_size; +} + +auto quickdraw::pixmap::set_pixel_format(enum pixel_format format) -> void +{ + m_pixel_format = format; +} + +auto quickdraw::pixmap::set_pm_table(std::uint32_t table) -> void +{ + m_pm_table = table; +} + +// MARK: - Calculations + +auto quickdraw::pixmap::total_component_width() const -> std::size_t +{ + return m_component_size * m_component_count; +} + +// MARK: - Draw Configuration + +auto quickdraw::pixmap::basic_draw_configuration() const -> struct draw_configuration +{ + struct draw_configuration cfg; + cfg.pixmap.base_address = m_base_address; // Check that this is actually the correct value here? + cfg.pixmap.row_bytes = m_row_bytes; + cfg.pixmap.bounds = m_bounds; + return std::move(cfg); +} + +auto quickdraw::pixmap::draw_configuration::aspect::expected_data_size() const -> std::size_t +{ + return row_bytes * bounds.size.height; +} + +// MARK: - Encoding / Decoding + +auto quickdraw::pixmap::decode(data::reader &reader) -> void +{ + m_base_address = reader.read_long(); + m_row_bytes = static_cast(reader.read_short() & 0x7FFF); + m_bounds = reader.read>(); + m_pm_version = reader.read_signed_short(); + m_pack_type = reader.read_enum(); + m_pack_size = reader.read_signed_long(); + m_dpi = size::read(reader, coding_type::macintosh); + m_pixel_type = reader.read_signed_short(); + m_pixel_size = reader.read_signed_short(); + m_component_count = reader.read_signed_short(); + m_component_size = reader.read_signed_short(); + m_pixel_format = reader.read_enum(); + m_pm_table = reader.read_long(); + m_pm_extension = reader.read_long(); +} + +auto quickdraw::pixmap::encode(data::writer &writer) -> void +{ + writer.write_long(m_base_address); + writer.write_short(0x8000 | m_row_bytes); + writer.write>(m_bounds); + writer.write_signed_short(m_pm_version); + writer.write_signed_short(m_pack_type); + writer.write_signed_long(m_pack_size); + writer.write>(m_dpi); + writer.write_signed_short(m_pixel_type); + writer.write_signed_short(m_pixel_size); + writer.write_signed_short(m_component_count); + writer.write_signed_short(m_component_size); + writer.write_enum(m_pixel_format); + writer.write_long(m_pm_table); + writer.write_long(m_pm_extension); +} + +// MARK: - Surface / Image + +auto quickdraw::pixmap::build_pixel_data(const data::block& color_data, std::uint16_t pixel_size) -> data::block +{ + data::block pixel_map_data; + data::writer pmap(&pixel_map_data); + data::reader colors(&color_data); + + m_pixel_size = static_cast(pixel_size); + m_component_count = 1; + + if (pixel_size == 8) { + m_row_bytes = m_bounds.size.width; + while (!colors.eof()) { + pmap.write_byte(colors.read_short() & 0xFF); + } + } + else { + auto width = m_bounds.size.width; + auto mod = 8 / pixel_size; + auto mask = (1 << pixel_size) - 1; + auto diff = 8 - pixel_size; + m_row_bytes = (width - 1) / mod + 1; + + for (std::int16_t y = 0; y < m_bounds.size.height; ++y) { + std::uint8_t scratch = 0; + for (std::int16_t x = 0; x < width; ++x) { + auto bit_offset = x % mod; + if (bit_offset == 0 && x != 0) { + pmap.write_byte(scratch); + scratch = 0; + } + + auto value = static_cast(colors.read_short() & mask); + value <<= (diff - (bit_offset * pixel_size)); + scratch |= value; + } + pmap.write_byte(scratch); + } + } + + return std::move(pixel_map_data); +} + +auto quickdraw::pixmap::build_surface(surface &surface, const data::block &pixel_data, const color_lookup_table &clut, const rect &destination) -> void +{ + if (pixel_data.size() < destination.size.height * m_row_bytes) { + throw std::runtime_error("Insufficient data to build surface from PixMap"); + } + auto pixel_size = m_component_count * m_component_size; + + if (pixel_size == 8) { + for (std::int16_t y = 0; y < destination.size.height; ++y) { + auto y_offset = (y * m_row_bytes); + for (std::int16_t x = 0; x < destination.size.width; ++x) { + auto byte = pixel_data.get(y_offset + x); + surface.set(destination.origin.x + x, destination.origin.y + y, clut.at(byte)); + } + } + } + else { + auto mod = 8 / pixel_size; + auto mask = (1 << pixel_size) - 1; + auto diff = 8 - pixel_size; + + for (std::int16_t y = 0; y < destination.size.height; ++y) { + auto y_offset = (y * m_row_bytes); + for (std::int16_t x = 0; x < destination.size.width; ++x) { + auto byte = pixel_data.get(y_offset + (x / mod)); + auto byte_offset = diff - ((x % mod) * pixel_size); + auto v = (byte >> byte_offset) & mask; + surface.set(destination.origin.x + x, destination.origin.y + y, clut.at(v)); + } + } + } +} + +auto quickdraw::pixmap::draw(quickdraw::surface& surface) -> void +{ + if (m_component_size == 1 && m_component_count == 1) { + + } + else if ((m_component_size = 1 && m_component_count == 2) || (m_component_size == 2 && m_component_count == 1)) { + + } + else if ((m_component_size == 1 && m_component_count == 4) || (m_component_size == 4 || m_component_count == 1)) { + + } + else if ((m_component_size == 1 && m_component_count == 8) || (m_component_size == 8 || m_component_count == 1)) { + + } + else { + throw std::runtime_error("Currently unsupported pixel map configuration: " + "cmp_size=" + std::to_string(m_component_size) + + ", cmp_count=" + std::to_string(m_component_count)); + } +} \ No newline at end of file diff --git a/libs/libQuickdraw/pixmap/pixmap.hpp b/libs/libQuickdraw/pixmap/pixmap.hpp new file mode 100644 index 0000000..e1aac1f --- /dev/null +++ b/libs/libQuickdraw/pixmap/pixmap.hpp @@ -0,0 +1,130 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace quickdraw +{ + struct pixmap + { + public: + + struct draw_configuration + { + struct aspect { + data::block *data { nullptr }; + std::uint32_t base_address { 0 }; + std::uint16_t row_bytes { 0 }; + rect bounds; + [[nodiscard]] auto expected_data_size() const -> std::size_t; + }; + + struct aspect pixmap; + struct aspect bitmap; + struct aspect mask; + + struct color_lookup_table *color_table { nullptr }; + }; + + enum pack_type : std::uint16_t + { + none = 0, + argb = 1, + rgb = 2, + packbits_word = 3, + packbits_component = 4 + }; + + public: + static constexpr std::size_t length = 50; + + pixmap() = default; + explicit pixmap(const rect& frame, bool rgb555 = false); + explicit pixmap(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); + explicit pixmap(data::reader& reader); + pixmap(const pixmap&) noexcept = default; + + auto operator=(pixmap&&) -> pixmap& = default; + auto operator=(const pixmap&) -> pixmap& = default; + + [[nodiscard]] auto basic_draw_configuration() const -> struct draw_configuration; + + [[nodiscard]] auto bounds() const -> rect; + [[nodiscard]] auto row_bytes() const -> std::int16_t; + [[nodiscard]] auto pack_type() const -> enum pack_type; + [[nodiscard]] auto pack_size() const -> std::int16_t; + [[nodiscard]] auto pixel_type() const -> std::int16_t; + [[nodiscard]] auto pixel_size() const -> std::int16_t; + [[nodiscard]] auto component_count() const -> std::int16_t; + [[nodiscard]] auto component_size() const -> std::int16_t; + [[nodiscard]] auto pixel_format() const -> enum pixel_format; + [[nodiscard]] auto pm_table() const -> std::uint32_t; + + [[nodiscard]] auto total_component_width() const -> std::size_t; + + auto set_bounds(const rect& rect) -> void; + auto set_row_bytes(std::int16_t row_bytes) -> void; + auto set_pack_type(enum pack_type pack_type) -> void; + auto set_pack_size(std::int16_t pack_size) -> void; + auto set_pixel_type(std::int16_t pixel_type) -> void; + auto set_pixel_size(std::int16_t pixel_size) -> void; + auto set_component_count(std::int16_t component_count) -> void; + auto set_component_size(std::int16_t component_size) -> void; + auto set_pixel_format(enum pixel_format format) -> void; + auto set_pm_table(std::uint32_t table) -> void; + + auto draw(quickdraw::surface& surface) -> void; + + auto build_surface(surface& surface, const data::block& pixel_data, const color_lookup_table& clut, const rect& destination) -> void; + auto build_pixel_data(const data::block& color_data, std::uint16_t pixel_size) -> data::block; + + auto encode(data::writer& writer) -> void; + + private: + // Resource Data + std::uint32_t m_base_address { 0xFF }; + std::int16_t m_row_bytes { 0 }; + rect m_bounds; + std::int16_t m_pm_version { 0 }; + enum pack_type m_pack_type { none }; + std::int32_t m_pack_size { 0 }; + quickdraw::size m_dpi { 0.001098632812 }; + std::int16_t m_pixel_type { 16 }; + std::int16_t m_pixel_size { 32 }; + std::int16_t m_component_count { 3 }; + std::int16_t m_component_size { 8 }; + enum pixel_format m_pixel_format { unknown }; + std::uint32_t m_pm_table { 0 }; + std::uint32_t m_pm_extension { 0 }; + + auto decode(data::reader& reader) -> void; + }; +} diff --git a/libs/libQuickdraw/quicktime/animation.cpp b/libs/libQuickdraw/quicktime/animation.cpp new file mode 100644 index 0000000..96c6567 --- /dev/null +++ b/libs/libQuickdraw/quicktime/animation.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +auto quicktime::format::animation::decode(const image_description &desc, data::reader &reader) -> quickdraw::surface +{ + auto depth = desc.depth(); + if (depth < 8) { + // Depths 1, 2, 4 currently unsupported + throw std::runtime_error("Unsupported rle bit depth: " + std::to_string(depth)); + } + auto clut = desc.clut(); + auto surface = quickdraw::surface(desc.width(), desc.height()); + auto chunk_size = reader.read_long(); + auto header = reader.read_short(); + auto y = 0; + if (header & 0x0008) { + y = reader.read_short(); + reader.move(6); + } + + std::int8_t skip; + std::int8_t code; + auto x = 0; + while ((skip = reader.read_byte())) { + x += skip-1; + while (true) { + code = reader.read_signed_byte(); + if (code == 0) { + // No op + break; + } + else if (code == -1) { + // Next line + x = 0; + y++; + break; + } + else if (code > 0) { + // Literal + switch (depth) { + case 8: { + auto raw = reader.read_data(4 * code); + for (auto i = 0; i < 4 * code; ++i) { + auto color = clut.at(raw.get(i)); + surface.set(x++, y, color); + } + break; + } + case 16: { + auto raw = reader.read_data(2 * code); + for (auto i = 0; i < code; ++i) { + auto color = quickdraw::rgb((raw.get(i * 2) << 8) | (raw.get(i * 2 + 1))); + surface.set(x++, y, color); + } + break; + } + case 24: { + auto raw = reader.read_data(3 * code); + for (auto i = 0; i < code; ++i) { + auto color = quickdraw::rgb(raw.get(i * 3), + raw.get(i * 3 + 1), + raw.get(i * 3 + 2)); + surface.set(x++, y, color); + } + break; + } + case 32: { + auto raw = reader.read_data(4 * code); + for (auto i = 0; i < code; ++i) { + auto color = quickdraw::rgb(raw.get(i * 4 + 1), + raw.get(i * 4 + 2), + raw.get(i * 4 + 3), + raw.get(i * 4)); + surface.set(x++, y, color); + } + break; + } + } + } + else { + // Run + switch (depth) { + case 8: { + auto raw = reader.read_data(4); + for (auto i = 0; i < 4 * -code; ++i) { + auto color = clut.at(raw.get(i % 4)); + surface.set(x++, y, color); + } + break; + } + case 16: { + auto color = quickdraw::rgb(reader.read_short()); + for (auto i = 0; i < -code; ++i) { + surface.set(x++, y, color); + } + break; + } + case 24: { + auto color = quickdraw::rgb(reader.read_byte(), reader.read_byte(), reader.read_byte()); + for (auto i = 0; i < -code; ++i) { + surface.set(x++, y, color); + } + break; + } + case 32: { + auto alpha = reader.read_byte(); + auto color = quickdraw::rgb(reader.read_byte(), reader.read_byte(), reader.read_byte(), alpha); + for (auto i = 0; i < -code; ++i) { + surface.set(x++, y, color); + } + break; + } + } + } + } + } + + return std::move(surface); +} diff --git a/libs/libQuickdraw/quicktime/animation.hpp b/libs/libQuickdraw/quicktime/animation.hpp new file mode 100644 index 0000000..8218098 --- /dev/null +++ b/libs/libQuickdraw/quicktime/animation.hpp @@ -0,0 +1,32 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace quicktime::format::animation +{ + auto decode(const image_description& desc, data::reader& reader) -> quickdraw::surface; +} + + diff --git a/libs/libQuickdraw/quicktime/image_description.cpp b/libs/libQuickdraw/quicktime/image_description.cpp new file mode 100644 index 0000000..37495eb --- /dev/null +++ b/libs/libQuickdraw/quicktime/image_description.cpp @@ -0,0 +1,153 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include + +// MARK: - Construction + +quicktime::image_description::image_description(data::reader &reader) +{ + // http://mirror.informatimago.com/next/developer.apple.com/documentation/QuickTime/INMAC/QT/iqImageCompMgr.17.htm + auto start = reader.position(); + m_length = reader.read_signed_long(); + if (m_length < 86) { + throw std::runtime_error("Invalid QuickTime image description."); + } + + m_compressor = reader.read_enum(); + reader.move(8); + m_version = reader.read_long(); + reader.move(12); + m_width = reader.read_signed_short(); + m_height = reader.read_signed_short(); + reader.move(8); + m_data_size = reader.read_signed_long(); + reader.move(34); + m_depth = reader.read_signed_short(); + if (m_depth > 32) { + m_depth -= 32; // grayscale + } + auto clut = reader.read_signed_short(); + if (clut == 0) { + m_clut = reader.read(); + } else if (clut > 0) { + if (auto resource = resource_core::manager::shared_manager().find(clut)) { + m_clut = quickdraw::color_lookup_table(resource->data()); + } + else { + throw std::runtime_error("Color table not found: clut " + std::to_string(clut)); + } + } + + // Record the number remaining bytes of the image description before the data start + m_data_offset = m_length - static_cast(reader.position() - start); + decode(reader); +} + +// MARK: - Accessors + +auto quicktime::image_description::length() const -> std::int32_t +{ + return m_length; +} + +auto quicktime::image_description::compressor() const -> enum compression_type +{ + return m_compressor; +} + +auto quicktime::image_description::version() const -> std::uint32_t +{ + return m_version; +} + +auto quicktime::image_description::width() const -> std::int16_t +{ + return m_width; +} + +auto quicktime::image_description::height() const -> std::int16_t +{ + return m_height; +} + +auto quicktime::image_description::data_size() const -> std::int32_t +{ + return m_data_size; +} + +auto quicktime::image_description::depth() const -> std::int16_t +{ + return m_depth; +} + +auto quicktime::image_description::data_offset() const -> std::int32_t +{ + return m_data_offset; +} + +auto quicktime::image_description::clut() const -> const quickdraw::color_lookup_table& +{ + return m_clut; +} + +auto quicktime::image_description::surface() const -> const quickdraw::surface& +{ + return m_surface; +} + +// MARK: - Decoding + +auto quicktime::image_description::decode(data::reader &reader) -> void +{ + switch (m_compressor) { + case compression_type::rle: { + m_surface = std::move(format::animation::decode(*this, reader)); + break; + } + case compression_type::planar: { + m_surface = std::move(format::planar::decode(*this, reader)); + break; + } + case compression_type::raw: { + m_surface = std::move(format::raw::decode(*this, reader)); + break; + } + case compression_type::quickdraw: { + auto pict = reader.read(m_data_size); + m_surface = pict.surface(); + break; + } + default: { + std::string compressor_name; + compressor_name.push_back(m_compressor >> 24); + compressor_name.push_back(m_compressor >> 16); + compressor_name.push_back(m_compressor >> 8); + compressor_name.push_back(m_compressor); + + throw std::runtime_error("Unsupported QuickTime compressor '" + compressor_name + "' at offset " + std::to_string(reader.position())); + } + } +} \ No newline at end of file diff --git a/libs/libQuickdraw/quicktime/image_description.hpp b/libs/libQuickdraw/quicktime/image_description.hpp new file mode 100644 index 0000000..1b736a2 --- /dev/null +++ b/libs/libQuickdraw/quicktime/image_description.hpp @@ -0,0 +1,71 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace quicktime +{ + struct image_description + { + public: + enum compression_type : std::uint32_t + { + unknown = 0, + rle = 'rle ', + planar = '8BPS', + raw = 'raw ', + quickdraw = 'qdrw', + }; + + public: + image_description() = default; + explicit image_description(data::reader& reader); + + [[nodiscard]] auto length() const -> std::int32_t; + [[nodiscard]] auto compressor() const -> enum compression_type; + [[nodiscard]] auto version() const -> std::uint32_t; + [[nodiscard]] auto width() const -> std::int16_t; + [[nodiscard]] auto height() const -> std::int16_t; + [[nodiscard]] auto data_size() const -> std::int32_t; + [[nodiscard]] auto depth() const -> std::int16_t; + [[nodiscard]] auto data_offset() const -> std::int32_t; + [[nodiscard]] auto clut() const -> const quickdraw::color_lookup_table&; + [[nodiscard]] auto surface() const -> const quickdraw::surface&; + + private: + std::int32_t m_length { 0 }; + enum compression_type m_compressor { unknown }; + std::uint32_t m_version { 0 }; + std::int16_t m_width { 0 }; + std::int16_t m_height { 0 }; + std::int32_t m_data_size { 0 }; + std::int16_t m_depth { 0 }; + std::int32_t m_data_offset { 0 }; + quickdraw::color_lookup_table m_clut; + quickdraw::surface m_surface; + + auto decode(data::reader& reader) -> void; + }; +} + + diff --git a/libs/libQuickdraw/quicktime/planar.cpp b/libs/libQuickdraw/quicktime/planar.cpp new file mode 100644 index 0000000..4af3633 --- /dev/null +++ b/libs/libQuickdraw/quicktime/planar.cpp @@ -0,0 +1,106 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Decoding + +auto quicktime::format::planar::decode(const quicktime::image_description &desc, data::reader &reader) -> quickdraw::surface +{ + auto depth = desc.depth(); + if (depth != 1 && depth != 8 && depth != 24 && depth != 32) { + throw std::runtime_error("Unsupported planar bit depth: " + std::to_string(depth)); + } + + // Parse the remaining atoms to determine the channel count + auto channel_count = 1; + auto remaining = desc.data_offset(); + while (remaining >= 10) { + auto size = reader.read_long(); + auto type = reader.read_long(); + auto value = reader.read_short(); + remaining -= 10; + if (type == 'chct') { + channel_count = value; + } + } + if (remaining > 0) { + reader.move(remaining); + } + + auto width = desc.width(); + auto height = desc.height(); + auto data_size = desc.data_size(); + auto row_bytes = (width * depth + 7) / 8; // +7 to ensure result is rounded up + auto surface = quickdraw::surface(width, height); + + data::block raw; + if (desc.version() == 0) { + raw = reader.read_data(row_bytes * height); + } else { + // Packbits - all counts are stored first + std::vector pack_counts(height * channel_count); + for (auto i=0; i < pack_counts.size(); ++i) { + pack_counts[i] = reader.read_short(); + } + for (auto count : pack_counts) { + raw = reader.read_compressed_data>(count); + } + } + + if (depth == 1) { + // Monochrome + for (auto y = 0; y < height; ++y) { + for (auto offset = 0; offset < row_bytes; ++offset) { + auto byte = raw.get(y * row_bytes + offset); + for (auto i = 0; i < 8; ++i) { + auto v = byte & (1 << (7 - i)); + surface.set(offset * 8 + i, y, v ? quickdraw::colors::black() : quickdraw::colors::white()); + } + } + } + } + else if (depth == 8) { + // 8-bit indexed + auto clut = desc.clut(); + for (auto y = 0; y < height; ++y) { + for (auto x = 0; x < width; ++x) { + surface.set(x, y, clut.at(raw.get(y * width + x))); + } + } + } + else if (depth == 24 || depth == 32) { + // Planar RGB + auto plane_size = width * height; + for (auto y=0; y < height; ++y) { + for (auto x=0; x < width; ++x) { + auto color = quickdraw::rgb( + raw.get(y * width + x), + raw.get(y * width + x + plane_size), + raw.get(y * width + x + plane_size * 2) + ); + surface.set(x, y, color); + } + } + } + + return std::move(surface); +} \ No newline at end of file diff --git a/libs/libQuickdraw/quicktime/planar.hpp b/libs/libQuickdraw/quicktime/planar.hpp new file mode 100644 index 0000000..ad2f6f5 --- /dev/null +++ b/libs/libQuickdraw/quicktime/planar.hpp @@ -0,0 +1,30 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace quicktime::format::planar +{ + auto decode(const quicktime::image_description& desc, data::reader& reader) -> quickdraw::surface; +} diff --git a/libs/libQuickdraw/quicktime/raw.cpp b/libs/libQuickdraw/quicktime/raw.cpp new file mode 100644 index 0000000..d20575c --- /dev/null +++ b/libs/libQuickdraw/quicktime/raw.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +// MARK: - Decoding + +auto quicktime::format::raw::decode(const quicktime::image_description &desc, data::reader &reader) -> quickdraw::surface +{ + auto depth = desc.depth(); + if (depth > 8) { + throw std::runtime_error("Unsupported raw bit depth: " + std::to_string(depth)); + } + auto width = desc.width(); + auto height = desc.height(); + auto clut = desc.clut(); + auto surface = quickdraw::surface(width, height); + + if (depth == 8) { + for (auto y = 0; y < height; ++y) { + for (auto x = 0; x < width; ++x) { + surface.set(x, y, clut.at(reader.read_byte())); + } + } + } + else { + auto pixels_per_byte = 8 / depth; + auto mask = (1 << depth) - 1; + auto row_bytes = desc.data_size() / height; + + for (auto y = 0; y < height; ++y) { + auto x = 0; + auto raw = reader.read_bytes(row_bytes); + for (auto byte : raw) { + for (auto i = 1; i <= pixels_per_byte; ++i) { + auto byte_offset = 8 - (i * depth); + auto v = (byte >> byte_offset) & mask; + surface.set(x++, y, clut.at(v)); + } + } + } + } + + return std::move(surface); +} \ No newline at end of file diff --git a/libs/libQuickdraw/quicktime/raw.hpp b/libs/libQuickdraw/quicktime/raw.hpp new file mode 100644 index 0000000..f3a23b2 --- /dev/null +++ b/libs/libQuickdraw/quicktime/raw.hpp @@ -0,0 +1,30 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace quicktime::format::raw +{ + auto decode(const image_description& desc, data::reader& reader) -> quickdraw::surface; +} \ No newline at end of file diff --git a/libs/libQuickdraw/surface/surface.cpp b/libs/libQuickdraw/surface/surface.cpp new file mode 100644 index 0000000..c7f100c --- /dev/null +++ b/libs/libQuickdraw/surface/surface.cpp @@ -0,0 +1,161 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +// MARK: - Construction + +quickdraw::surface::surface(const quickdraw::size &size) + : m_size(size), + m_row_bytes(size.width * constants::color_width), + m_data(new data::block(size.width * size.height * constants::color_width)) +{ + m_data->set(static_cast(0), m_data->size()); +} + +quickdraw::surface::surface(std::int16_t width, std::int16_t height) + : m_size(width, height), + m_row_bytes(width * constants::color_width), + m_data(new data::block(width * height * constants::color_width)) +{ + m_data->set(static_cast(0), m_data->size()); +} + +quickdraw::surface::surface(const quickdraw::size& size, union color color) + : m_size(size), + m_row_bytes(size.width * constants::color_width), + m_data(new data::block(size.width * size.height * constants::color_width)) +{ + m_data->set(color.value, m_data->size()); +} + +quickdraw::surface::surface(std::int16_t width, std::int16_t height, union color color) + : m_size(width, height), + m_row_bytes(width * constants::color_width), + m_data(new data::block(width * height * constants::color_width)) +{ + m_data->set(color.value, m_data->size()); +} + +quickdraw::surface::surface(const surface &surface) + : m_size(surface.m_size), + m_row_bytes(surface.m_row_bytes), + m_data(new data::block(*surface.m_data)) +{ +} + +quickdraw::surface::surface(surface &&surface) noexcept + : m_size(surface.m_size), + m_row_bytes(surface.m_row_bytes), + m_data(surface.m_data) +{ + surface.m_data = nullptr; +} + +// MARK: - Destruction + +quickdraw::surface::~surface() +{ + if (!m_weak_data && m_data) { + delete m_data; + } +} + +// MARK: - Operators + +auto quickdraw::surface::operator=(const surface& surface) -> class surface& +{ + if (this == const_cast(&surface)) { + return *this; + } + + m_data = new data::block(*surface.m_data); + m_weak_data = false; + m_size = surface.m_size; + m_row_bytes = surface.m_row_bytes; + + return *this; +} + +auto quickdraw::surface::operator=(class surface&& surface) noexcept -> class surface& +{ + if (this != &surface) { + delete m_data; + + m_data = surface.m_data; + m_weak_data = false; + m_size = surface.m_size; + m_row_bytes = surface.m_row_bytes; + + surface.m_data = nullptr; + } + return *this; +} + +// MARK: - Accessors + +auto quickdraw::surface::raw() const -> const data::block& +{ + return *m_data; +} + +auto quickdraw::surface::size() const -> struct quickdraw::size +{ + return m_size; +} + +// MARK: - Pixel Access + +auto quickdraw::surface::at(const point &p) const -> union color +{ + return at(p.x, p.y); +} + +auto quickdraw::surface::at(std::int16_t x, std::int16_t y) const -> union color +{ + return at((y * m_size.width) + x); +} + +auto quickdraw::surface::at(std::uint32_t offset) const -> union color +{ + return m_data->get()[offset]; +} + +auto quickdraw::surface::set(const point& p, union color color) -> void +{ + set(p.x, p.y, color); +} + +auto quickdraw::surface::set(std::int16_t x, std::int16_t y, union color color) -> void +{ + set((y * m_size.width) + x, color); +} + +auto quickdraw::surface::set(std::uint32_t offset, union color color) -> void +{ + m_data->set(color.value, constants::color_width, offset * constants::color_width); +} + +// MARK: - Operations + +auto quickdraw::surface::clear() -> void +{ + m_data->set(colors::clear().value, m_data->size()); +} \ No newline at end of file diff --git a/libs/libQuickdraw/surface/surface.hpp b/libs/libQuickdraw/surface/surface.hpp new file mode 100644 index 0000000..dada4cc --- /dev/null +++ b/libs/libQuickdraw/surface/surface.hpp @@ -0,0 +1,65 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace quickdraw +{ + struct surface + { + public: + surface() = default; + explicit surface(const size& size); + surface(std::int16_t width, std::int16_t height); + surface(const size& size, union color color); + surface(std::int16_t width, std::int16_t height, union color color); + surface(const surface& surface); + surface(surface&& surface) noexcept; + + ~surface(); + + auto operator=(const surface&) -> surface&; + auto operator=(surface&&) noexcept -> surface&; + + [[nodiscard]] auto raw() const -> const data::block&; + [[nodiscard]] auto size() const -> struct size; + + [[nodiscard]] auto at(const point& p) const -> union color; + [[nodiscard]] auto at(std::uint32_t offset) const -> union color; + [[nodiscard]] auto at(std::int16_t x, std::int16_t y) const -> union color; + + auto set(const point& p, union color color) -> void; + auto set(std::int16_t x, std::int16_t y, union color color) -> void; + auto set(std::uint32_t offset, union color color) -> void; + + auto clear() -> void; + + private: + std::uint32_t m_row_bytes { 0 }; + quickdraw::size m_size; + data::block *m_data { nullptr }; + bool m_weak_data { false }; + }; +} diff --git a/libs/libQuickdraw/type/coding_type.hpp b/libs/libQuickdraw/type/coding_type.hpp new file mode 100644 index 0000000..1f1b44a --- /dev/null +++ b/libs/libQuickdraw/type/coding_type.hpp @@ -0,0 +1,29 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +namespace quickdraw +{ + enum class coding_type + { + quickdraw, macintosh + }; +} diff --git a/libs/libQuickdraw/type/pixel_format.hpp b/libs/libQuickdraw/type/pixel_format.hpp new file mode 100644 index 0000000..b3ce212 --- /dev/null +++ b/libs/libQuickdraw/type/pixel_format.hpp @@ -0,0 +1,38 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +namespace quickdraw +{ + enum pixel_format : std::uint32_t + { + unknown = 0x00, + monochrome = 0x01, + indexed_2bit = 0x02, + indexed_4bit = 0x04, + indexed_8bit = 0x08, + rgb555 = 0x10, + true_color = 0x18, + true_color_alpha = 0x20 + }; +} \ No newline at end of file diff --git a/libs/libQuickdraw/type/point.hpp b/libs/libQuickdraw/type/point.hpp new file mode 100644 index 0000000..04dd2a2 --- /dev/null +++ b/libs/libQuickdraw/type/point.hpp @@ -0,0 +1,163 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace quickdraw +{ + /** + * Represents a 2D point on a surface. + * @tparam T The type being used for the X & Y values of the point. This can be any arithmetic type. + */ + template::value>::type* = nullptr> + struct point + { + public: + /** + * The x-coordinate of the point. + */ + T x { 0 }; + + /** + * The y-coordinate of the point. + */ + T y { 0 }; + + point() = default; + + /** + * Construct a point where the X and Y coordinate are equal. + * @param v The value to set both of the X and Y coordinates to. + */ + explicit point(T v) : x(v), y(v) {} + + /** + * Construct a point. + * @param x The value of the X coordinate. + * @param y The value of the Y coordinate. + */ + point(T x, T y) : x(x), y(y) {} + + point(const point&) = default; + point(point&&) noexcept = default; + + /** + * Construct a point, reading the X and Y coordinates from the specified data reader. + * Defaults to using the QuickDraw ordering for coordinates (Y and then X) + * @param reader The data reader to read the X and Y coordinates from. + * @param type The coding type (coordinate ordering) to use when reading the coordinates. + */ + explicit point(data::reader& reader, coding_type type = coding_type::quickdraw) + { + switch (type) { + case coding_type::macintosh: { + x = read_component(reader); + y = read_component(reader); + break; + } + case coding_type::quickdraw: { + y = read_component(reader); + x = read_component(reader); + break; + } + } + } + + /** + * Construct a point, reading the X and Y coordinates from the specified data reader. + * @param reader The data reader to read the X and Y coordinates from. + * @param type The coding type (coordinate ordering) to use when reading the coordinates. + * @return A point with the X and Y coordinate read from the reader. + */ + static auto read(data::reader& reader, coding_type type) -> point + { + return point(reader, type); + } + + /** + * Write the X and Y coordinates of the point into the provided data writer, using the specified coding type. + * @param writer The data writer to write the X and Y coordinates to. + * @param type The coding type (coordinate ordering) to use when writing the coordinates. + */ + auto encode(data::writer& writer, coding_type type = coding_type::quickdraw) -> void + { + switch (type) { + case coding_type::macintosh: { + write_component(x, writer); + write_component(y, writer); + break; + } + case coding_type::quickdraw: { + write_component(y, writer); + write_component(x, writer); + break; + } + } + } + + auto operator=(const point& p) -> point& { x = p.x; y = p.y; return *this; } + auto operator=(point&& p) noexcept -> point& { x = p.x; y = p.y; return *this; } + + auto operator==(const point& p) const -> bool { return x == p.x && y == p.y; } + auto operator!=(const point& p) const -> bool { return x != p.x && y != p.y; } + + auto operator+(const point& p) const -> point { return point(x + p.x, y + p.y); } + auto operator-(const point& p) const -> point { return point(x - p.x, y - p.y); } + + template::value>::type* = nullptr> + auto operator*(U v) const -> point { return point(x * static_cast(v), y * static_cast(v)); } + + template::value>::type* = nullptr> + auto operator/(U v) const -> point { return point(x / static_cast(v), y / static_cast(v)); } + + /** + * Cast the typename of the point to a different compatible type. + * @tparam U The new arithmetic type in which to cast to. + * @return A point using the new arithmetic type. + */ + template::value>::type* = nullptr> + auto cast() const -> point { return { static_cast(x), static_cast(y) }; } + + private: + static auto read_component(data::reader& reader) -> T { return reader.read_integer(); } + static auto write_component(T value, data::writer& writer) -> void { writer.write_integer(value); } + }; + + + template<> + inline auto point::read_component(data::reader &reader) -> double { return reader.read_fixed_point(); } + + template<> + inline auto point::read_component(data::reader &reader) -> float { return static_cast(reader.read_fixed_point()); } + + template<> + inline auto point::write_component(double value, data::writer &writer) -> void { writer.write_fixed_point(value); } + + template<> + inline auto point::write_component(float value, data::writer &writer) -> void { writer.write_fixed_point(static_cast(value)); } + +} diff --git a/libs/libQuickdraw/type/rect.hpp b/libs/libQuickdraw/type/rect.hpp new file mode 100644 index 0000000..be2d8a0 --- /dev/null +++ b/libs/libQuickdraw/type/rect.hpp @@ -0,0 +1,139 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace quickdraw +{ + /** + * Represents a rectangle or frame within a surface. + * @tparam T The type being used for the origin and size values of the rect. + * This can be any arithmetic type. + */ + template::value>::type* = nullptr> + struct rect + { + public: + /** + * The origin of the rect. + */ + point origin { 0 }; + + /** + * The size of the rect + */ + size size { 0 }; + + rect() = default; + + /** + * Construct a rect where the X and Y coordinates of the origin, as well as the width and height dimensions + * of the size, are all equal. + * @param v The value to set both of the origin and size components to. + */ + explicit rect(T v) : origin(v), size(v) {} + + /** + * Construct a rect. + * @param x The value of the origin X coordinate. + * @param y The value of the origin Y coordinate. + * @param width The value of the width dimension of the size. + * @param height The value of the height dimension of the size. + */ + rect(T x, T y, T width, T height) : origin(x, y), size(width, height) {} + + /** + * Construct a rect. + * @param origin The origin of the rect. + * @param size The size of the rect. + */ + rect(struct point origin, struct quickdraw::size size) : origin(origin), size(size) {} + + rect(const rect&) = default; + rect(rect&&) noexcept = default; + + /** + * Construct a point, reading the origin and the size from the specified data reader. + * Defaults to using the QuickDraw ordering for both the origin and size. + * @param reader The data reader to read the origin and size from. + * @param type The coding type (ordering) to use when reading the origin and size. + */ + explicit rect(data::reader& reader, coding_type type = coding_type::quickdraw) + : origin(reader, type), size(reader, type) + { + size.width -= origin.x; + size.height -= origin.y; + } + + /** + * Construct a rect, reading the origin and size from the specified data reader. + * @param reader The data reader to read the origin and size from. + * @param type The coding type (ordering) to use when reading the origin and size. + * @return A size with the origin and size read from the reader. + */ + static auto read(data::reader& reader, coding_type type) -> rect + { + return rect(reader, type); + } + + /** + * Write the origin and size of the rect into the provided data writer, using the specified coding type. + * @param writer The data writer to write the origin and size to. + * @param type The coding type (ordering) to use when writing the origin and size. + */ + auto encode(data::writer& writer, coding_type type = coding_type::quickdraw) -> void + { + origin.encode(writer, type); + (size + quickdraw::size(origin.x, origin.y)).encode(writer, type); + } + + auto operator=(const rect& r) -> rect& { origin = r.origin; size = r.size; return *this; } + auto operator=(rect&& r) noexcept -> rect& { origin = r.origin; size = r.size; return *this; } + + auto operator==(const rect& r) const -> bool { return origin == r.origin && size == r.size; } + auto operator!=(const rect& r) const -> bool { return origin != r.origin && size != r.size; } + + auto operator+(const rect& r) const -> rect { return rect(origin + r.origin, size + r.size); } + auto operator-(const rect& r) const -> rect { return rect(origin - r.origin, size - r.size); } + + template::value>::type* = nullptr> + auto operator*(U v) const -> rect { return rect(origin * static_cast(v), size * static_cast(v)); } + + template::value>::type* = nullptr> + auto operator/(U v) const -> rect { return rect(origin / static_cast(v), size / static_cast(v)); } + + /** + * Cast the typename of the rect to a different compatible type. + * @tparam U The new arithmetic type in which to cast to. + * @return A rect using the new arithmetic type. + */ + template::value>::type* = nullptr> + auto cast() const -> rect { return rect(origin.template cast(), size.template cast()); } + }; + +} \ No newline at end of file diff --git a/libs/libQuickdraw/type/size.hpp b/libs/libQuickdraw/type/size.hpp new file mode 100644 index 0000000..9ea4d3a --- /dev/null +++ b/libs/libQuickdraw/type/size.hpp @@ -0,0 +1,163 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +namespace quickdraw +{ + /** + * Represents a size of a 2D area on a surface. + * @tparam T The type being used for the Width and Height values of a size. + * This can be any arithmetic type. + */ + template::value>::type* = nullptr> + struct size + { + public: + /** + * The width of the size. + */ + T width { 0 }; + + /** + * The height of the size. + */ + T height { 0 }; + + size() = default; + + /** + * Construct a size where the width and height dimensions are equal. + * @param v The value to set both of the width and height dimensions to. + */ + explicit size(T v) : width(v), height(v) {} + + /** + * Construct a size. + * @param width The value of the width dimension. + * @param height The value of the height dimension. + */ + size(T width, T height) : width(width), height(height) {} + + size(const size&) = default; + size(size&&) noexcept = default; + + /** + * Construct a size, reading the width and height dimensions from the specified data reader. + * Defaults to using the QuickDraw ordering for dimensions (height and then width) + * @param reader The data reader to read the width and height dimensions from. + * @param type The coding type (dimension ordering) to use when reading the dimensions. + */ + explicit size(data::reader& reader, coding_type type = coding_type::quickdraw) + { + switch (type) { + case coding_type::macintosh: { + width = read_component(reader); + height = read_component(reader); + break; + } + case coding_type::quickdraw: { + height = read_component(reader); + width = read_component(reader); + break; + } + } + } + + /** + * Construct a size, reading the width and height dimensions from the specified data reader. + * @param reader The data reader to read the width and height dimensions from. + * @param type The coding type (dimension ordering) to use when reading the dimensions. + * @return A size with the width and height dimensions read from the reader. + */ + static auto read(data::reader& reader, coding_type type) -> size + { + return size(reader, type); + } + + /** + * Write the width and height coordinates of the size into the provided data writer, using the specified coding + * type. + * @param writer The data writer to write the width and height dimensions to. + * @param type The coding type (dimension ordering) to use when writing the dimensions. + */ + auto encode(data::writer& writer, coding_type type = coding_type::quickdraw) -> void + { + switch (type) { + case coding_type::macintosh: { + write_component(width, writer); + write_component(height, writer); + break; + } + case coding_type::quickdraw: { + write_component(height, writer); + write_component(width, writer); + break; + } + } + } + + auto operator=(const size& s) -> size& { width = s.width; height = s.height; return *this; } + auto operator=(size&& s) noexcept -> size& { width = s.width; height = s.height; return *this; } + + auto operator==(const size& s) const -> bool { return width == s.width && height == s.height; } + auto operator!=(const size& s) const -> bool { return width != s.width && height != s.height; } + + auto operator+(const size& s) const -> size { return size(width + s.width, height + s.height); } + auto operator-(const size& s) const -> size { return size(width - s.width, height - s.height); } + + template::value>::type* = nullptr> + auto operator*(U v) const -> size { return size(width * static_cast(v), height * static_cast(v)); } + + template::value>::type* = nullptr> + auto operator/(U v) const -> size { return size(width / static_cast(v), height / static_cast(v)); } + + /** + * Cast the typename of the size to a different compatible type. + * @tparam U The new arithmetic type in which to cast to. + * @return A size using the new arithmetic type. + */ + template::value>::type* = nullptr> + auto cast() const -> size { return size(static_cast(width), static_cast(height)); } + + private: + static auto read_component(data::reader& reader) -> T { return reader.read_integer(); } + static auto write_component(T value, data::writer& writer) -> void { writer.write_integer(value); } + }; + + template<> + inline auto size::read_component(data::reader &reader) -> double { return reader.read_fixed_point(); } + + template<> + inline auto size::read_component(data::reader &reader) -> float { return static_cast(reader.read_fixed_point()); } + + template<> + inline auto size::write_component(double value, data::writer &writer) -> void { writer.write_fixed_point(value); } + + template<> + inline auto size::write_component(float value, data::writer &writer) -> void { writer.write_fixed_point(static_cast(value)); } + +} \ No newline at end of file diff --git a/libs/libResourceCore/CMakeLists.txt b/libs/libResourceCore/CMakeLists.txt new file mode 100644 index 0000000..61506e8 --- /dev/null +++ b/libs/libResourceCore/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2023 Tom Hancocks +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) + +######################################################################################################################## +## Project +project(Graphite LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) + +######################################################################################################################## +## libSIMD +file(GLOB_RECURSE libResourceCore_Sources + *.cpp +) + +add_library(ResourceCore ${libResourceCore_Sources}) +target_link_libraries(ResourceCore SIMD Data Encoding) +target_include_directories(ResourceCore PUBLIC + ${PROJECT_LIBS_DIR} +) diff --git a/libs/libResourceCore/concepts.hpp b/libs/libResourceCore/concepts.hpp new file mode 100644 index 0000000..b04aa29 --- /dev/null +++ b/libs/libResourceCore/concepts.hpp @@ -0,0 +1,35 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +namespace resource_core +{ + template + concept isa_resource_type = requires(const T& type) { + { T::type_code() } -> std::same_as; + }; +} \ No newline at end of file diff --git a/libs/libResourceCore/file.cpp b/libs/libResourceCore/file.cpp new file mode 100644 index 0000000..de18e64 --- /dev/null +++ b/libs/libResourceCore/file.cpp @@ -0,0 +1,326 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include +#include + +// MARK: - Construction + +resource_core::file::file(const std::string &path) +{ + read(path); +} + +resource_core::file::file(const file &file) + : m_format(file.m_format), + m_path(file.m_path), + m_data(new data::block(*file.m_data)) +{ + // Copy the types. + for (const auto& it : file.m_types) { + m_types.emplace(std::pair(it.first, new struct type(*it.second))); + } +} + +resource_core::file::file(file &&file) noexcept + : m_format(file.m_format), + m_path(std::move(file.m_path)), + m_data(file.m_data), + m_types(std::move(file.m_types)) +{ + file.m_data = nullptr; + file.m_types = {}; +} + +// MARK: - Destruction + +resource_core::file::~file() +{ + for (auto& it : m_types) { + delete it.second; + } + delete m_data; +} + +// MARK: - Operators + +auto resource_core::file::operator=(const file &file) -> class file & +{ + if (this == const_cast(&file)) { + return *this; + } + + m_path = file.m_path; + m_format = file.m_format; + m_data = new data::block(*file.m_data); + + // Copy the types. + for (const auto& it : file.m_types) { + m_types.emplace(it.first, new struct type(*it.second)); + } + + return *this; +} + +auto resource_core::file::operator=(file &&file) noexcept -> class file & +{ + if (this != &file) { + m_path = std::move(file.m_path); + m_format = file.m_format; + m_data = file.m_data; + m_types = std::move(file.m_types); + + file.m_data = nullptr; + file.m_types = {}; + } + return *this; +} + +// MARK: - Hashing + +auto resource_core::file::hash_for_path(const std::string &path) -> hash +{ + return hashing::xxh64(path.c_str(), path.size()); +} + +// MARK: - Accessors + +auto resource_core::file::name() const -> std::string +{ + auto pos = m_path.find_last_of('/'); + return m_path.substr(pos + 1); +} + +auto resource_core::file::path() const -> const std::string& +{ + return m_path; +} + +auto resource_core::file::type_count() const -> std::size_t +{ + return m_types.size(); +} + +auto resource_core::file::types() const -> std::vector +{ + std::vector types; + for (const auto& type : m_types) { + types.emplace_back(type.first); + } + return std::move(types); +} + +auto resource_core::file::type_codes() const -> std::vector +{ + std::vector types; + for (const auto& type : m_types) { + types.emplace_back(type.second->code()); + } + return std::move(types); +} + +auto resource_core::file::format() const -> enum format +{ + return m_format; +} + +auto resource_core::file::hash_value() const -> hash +{ + return hash_for_path(m_path); +} + + +// MARK: - Type Management + +auto resource_core::file::add_resource(const std::string &type_code, + identifier id, + const std::string &name, + const data::block &data, + const std::unordered_map &attributes) -> void +{ + auto resource = new struct instance(nullptr, id, name, data); + + std::unordered_map type_attributes; + for (const auto& it : attributes) { + attribute attr(it.first, it.second); + type_attributes.emplace(std::pair(attr.hash_value(), std::move(attr))); + } + + auto type_hash = type::hash_for_type_code(type_code, type_attributes); + auto it = m_types.find(type_hash); + if (it == m_types.end()) { + // The type doesn't exist, so we need to create it. + auto type = new struct type(type_code); + + for (const auto& attr : type_attributes) { + type->add_attribute(attr.second.name(), attr.second.string_value()); + } + + m_types.emplace(std::pair(type_hash, type)); + type->add_resource(resource); + } + else { + // Found the type, add the resource to it. + it->second->add_resource(resource); + } +} + +auto resource_core::file::add_type(struct type *type) -> void +{ + m_types.emplace(std::pair(type->hash_value(), type)); +} + +auto resource_core::file::add_types(const std::vector &types) -> void +{ + for (const auto type : types) { + m_types.emplace(std::pair(type->hash_value(), type)); + } +} + +auto resource_core::file::type(const std::string &code, const std::unordered_map& attributes) const -> const struct type * +{ + // Convert the unordered map to what is required. + std::unordered_map attributes_map; + for (const auto& it : attributes) { + attribute attr(it.first, it.second); + attributes_map.emplace(attr.hash_value(), std::move(attr)); + } + + auto it = m_types.find(type::hash_for_type_code(code, attributes_map)); + return (it == m_types.end()) ? nullptr : it->second; +} + +auto resource_core::file::type(const std::string& code, const std::vector& attributes) const -> const struct type * +{ + bool universal_namespace = false; + std::unordered_map attributes_map; + for (const auto& it : attributes) { + if (it.name() == "namespace" && it.string_value() == "*") { + universal_namespace = true; + } + else { + attributes_map.emplace(std::pair(it.hash_value(), it)); + } + } + + if (universal_namespace) { + // TODO: Merge types together so that we can browse all namespaces. + for (const auto& it : m_types) { + if (it.second->code() == code) { + return it.second; + } + } + } + else { + auto it = m_types.find(type::hash_for_type_code(code, attributes_map)); + return (it == m_types.end()) ? nullptr : it->second; + } + + return nullptr; +} + +auto resource_core::file::type(const std::string& code) const -> const struct type * +{ + auto it = m_types.find(type::hash_for_type_code(code)); + return (it == m_types.end()) ? nullptr : it->second; +} + + +auto resource_core::file::type(type::hash hash) const -> const struct type * +{ + auto it = m_types.find(hash); + return (it == m_types.end()) ? nullptr : it->second; +} + +auto resource_core::file::find(const std::string &type_code, identifier id, const std::unordered_map& attributes) const -> const struct instance * +{ + if (auto type = const_cast<::resource_core::type *>(this->type(type_code, attributes))) { + return type->resource_with_id(id); + } + return nullptr; +} + +auto resource_core::file::find(const std::string &type_code, identifier id, const std::vector& attributes) const -> const struct instance * +{ + if (auto type = const_cast<::resource_core::type *>(this->type(type_code, attributes))) { + return type->resource_with_id(id); + } + return nullptr; +} + +// MARK: - File Access + +auto resource_core::file::read(const std::string &path) -> void +{ + m_path = path; + m_data = new data::block(m_path); + data::reader reader(m_data); + + if (::resource_core::format::extended::parse(reader, *this)) { + m_format = format::extended; + } + else if (::resource_core::format::rez::parse(reader, *this)) { + m_format = format::rez; + } + else if (::resource_core::format::classic::parse(reader, *this)) { + m_format = format::classic; + } + else { + throw std::runtime_error("Failed to read resource file. Format not recognised: " + m_path); + } +} + +auto resource_core::file::write() -> bool +{ + return write(m_path, m_format); +} + +auto resource_core::file::write(const std::string &path) -> bool +{ + return write(path, m_format); +} + +auto resource_core::file::write(const std::string &path, enum format format) -> bool +{ + if (m_path != path) { + m_path = path; + } + m_format = format; + + switch (m_format) { + case format::extended: + return ::resource_core::format::extended::write(*this, m_path); + + case format::rez: + return ::resource_core::format::rez::write(*this, m_path); + + case format::classic: + return ::resource_core::format::classic::write(*this, m_path); + + default: + return false; + } +} \ No newline at end of file diff --git a/libs/libResourceCore/file.hpp b/libs/libResourceCore/file.hpp new file mode 100644 index 0000000..2ed7785 --- /dev/null +++ b/libs/libResourceCore/file.hpp @@ -0,0 +1,102 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace resource_core +{ + class file + { + public: + typedef std::uint64_t hash; + enum class format { classic, extended, rez }; + + public: + file() = default; + explicit file(const std::string& path); + file(const file& file); + file(file&& file) noexcept; + + ~file(); + + auto operator=(const file& file) -> class file&; + auto operator=(file&& file) noexcept -> class file&; + + static auto hash_for_path(const std::string& path) -> hash; + + auto read(const std::string& path) -> void; + auto write() -> bool; + auto write(const std::string& path) -> bool; + auto write(const std::string& path, enum format format) -> bool; + + [[nodiscard]] auto name() const -> std::string; + [[nodiscard]] auto path() const -> const std::string&; + [[nodiscard]] auto type_count() const -> std::size_t; + [[nodiscard]] auto types() const -> std::vector; + [[nodiscard]] auto type_codes() const -> std::vector; + [[nodiscard]] auto format() const -> enum format; + [[nodiscard]] auto hash_value() const -> hash; + + auto add_type(struct type *type) -> void; + auto add_types(const std::vector& types) -> void; + [[nodiscard]] auto type(const std::string& code, const std::unordered_map& attributes) const -> const struct type *; + [[nodiscard]] auto type(const std::string& code, const std::vector& attributes) const -> const struct type *; + [[nodiscard]] auto type(const std::string& code) const -> const struct type *; + [[nodiscard]] auto type(type::hash hash) const -> const struct type *; + + auto add_resource(const std::string& type_code, + identifier id, + const std::string& name, + const data::block& data, + const std::unordered_map& attributes = {}) -> void; + + template + [[nodiscard]] auto find(resource_core::identifier id, const std::unordered_map& attributes = {}) const -> const struct resource * + { + return find(T::type_code(), id, attributes); + } + + [[nodiscard]] auto find(const std::string& type_code, identifier id, const std::unordered_map& attributes = {}) const -> const struct instance *; + [[nodiscard]] auto find(const std::string& type_code, identifier id, const std::vector& attributes) const -> const struct instance *; + + template + [[nodiscard]] auto load(identifier id) const -> T + { + if (const auto resource = find(id)) { + return std::move(T(resource->data(), resource->id(), resource->name())); + } + throw std::runtime_error("Resource not found: " + T::type_code() + ".#" + std::to_string(id)); + } + + private: + std::string m_path; + std::unordered_map m_types; + data::block *m_data { nullptr }; + enum format m_format { format::classic }; + }; +} diff --git a/libs/libResourceCore/format/classic/classic.hpp b/libs/libResourceCore/format/classic/classic.hpp new file mode 100644 index 0000000..f20c599 --- /dev/null +++ b/libs/libResourceCore/format/classic/classic.hpp @@ -0,0 +1,24 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include \ No newline at end of file diff --git a/libs/libResourceCore/format/classic/parser.cpp b/libs/libResourceCore/format/classic/parser.cpp new file mode 100644 index 0000000..5063b22 --- /dev/null +++ b/libs/libResourceCore/format/classic/parser.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include + +// MARK: - Parsing + +auto resource_core::format::classic::parse(data::reader &reader, file& file) -> bool +{ + std::vector types; + + // 1. Resource Preamble + auto data_offset = reader.read_long(); + auto map_offset = reader.read_long(); + auto data_length = reader.read_long(); + auto map_length = reader.read_long(); + + // Verify that the resource file is valid. + if (data_offset == 0 || map_offset == 0 || map_length == 0) { + return false; + } + + auto rsrc_size = data_offset + data_length + map_length; + if (map_offset != data_offset + data_length) { + return false; + } + + if (rsrc_size > reader.size()) { + return false; + } + + // Move to the start of the ResourceMap, and verify the preamble contents. + reader.set_position(map_offset); + + auto data_offset2 = reader.read_long(); + auto map_offset2 = reader.read_long(); + auto data_length2 = reader.read_long(); + auto map_length2 = reader.read_long(); + + // Ignore the second preamble if all values are equal to zero. + if (data_offset2 != 0 || map_offset2 != 0 || data_length2 != 0 || map_length2 != 0) { + if (data_offset2 != data_offset) { + return false; + } + + if (map_offset2 != map_offset) { + return false; + } + + if (data_length2 != data_length) { + return false; + } + + if (map_length2 != map_length) { + return false; + } + } + + // 2. Now that the preamble is parsed and verified, parse the contents + // of the ResourceMap. The first two fields are used by the actual Resource Manager in + // the Classic Macintosh OS, but are not used by this implementation. + GRAPHITE_UNUSED auto next_map = reader.read_long(); + GRAPHITE_UNUSED auto reference = reader.read_short(); + + // Start to read the actual content of the resource map. This is the content that + // we actually care about. The first field is the flags/attributes of the resource + // fork. + GRAPHITE_UNUSED auto flags = reader.read_short(); + + auto type_list_offset = static_cast(reader.read_short()); + auto name_list_offset = static_cast(reader.read_short()); + + // 3. Parse the list of Resource Types. + reader.set_position(map_offset + type_list_offset); + auto type_count = reader.read_short() + 1; + + for (auto type_idx = 0; type_idx < type_count; ++type_idx) { + auto code = reader.read_cstr(4); + auto count = reader.read_short() + 1; + auto first_resource_offset = static_cast(reader.read_short()); + + auto type = new struct type(code); + + // 4. Parse the list of resources for the current resource type. + reader.save_position(); + reader.set_position(map_offset + type_list_offset + first_resource_offset); + + for (auto res_idx = 0; res_idx < count; ++res_idx) { + auto id = static_cast(reader.read_signed_short()); + auto name_offset = reader.read_short(); + GRAPHITE_UNUSED auto flags = reader.read_byte(); + auto resource_data_offset = reader.read_triple(); + GRAPHITE_UNUSED auto handle = reader.read_long(); + + reader.save_position(); + // 5. Parse the name out of the list of resource names. + std::string name; + if (name_offset != std::numeric_limits::max()) { + reader.set_position(map_offset + name_list_offset + name_offset); + name = std::move(reader.read_pstr()); + } + + // 6. Create a data slice for the resources data. + reader.set_position(data_offset + resource_data_offset); + auto data_size = reader.read_long(); + auto slice = std::move(reader.read_data(data_size)); + reader.restore_position(); + + // 7. Construct a new resource instance and add it to the type. + auto resource = new struct instance(type, id, name, std::move(slice)); + type->add_resource(resource); + } + + reader.restore_position(); + types.emplace_back(type); + } + + file.add_types(types); + return true; +} + diff --git a/libs/libResourceCore/format/classic/parser.hpp b/libs/libResourceCore/format/classic/parser.hpp new file mode 100644 index 0000000..7788f09 --- /dev/null +++ b/libs/libResourceCore/format/classic/parser.hpp @@ -0,0 +1,29 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace resource_core::format::classic +{ + auto parse(data::reader& reader, file& file) -> bool; +} diff --git a/libs/libResourceCore/format/classic/writer.cpp b/libs/libResourceCore/format/classic/writer.cpp new file mode 100644 index 0000000..6a825b5 --- /dev/null +++ b/libs/libResourceCore/format/classic/writer.cpp @@ -0,0 +1,220 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include + +// MARK: - Constants + +namespace resource_core::format::classic::constants::defaults +{ + constexpr std::uint32_t data_offset = 256; + constexpr std::uint32_t map_offset = 0; + constexpr std::uint32_t data_length = 0; + constexpr std::uint32_t map_length = 0; +} + +namespace resource_core::format::classic::constants +{ + constexpr std::uint16_t resource_type_length = 8; + constexpr std::uint16_t resource_length = 12; + constexpr std::uint16_t type_list_offset = 28; +} + +// MARK: - Writing + +auto resource_core::format::classic::write(file &file) -> bool +{ + return write(file, file.path()); +} + +auto resource_core::format::classic::write(file &file, const std::string &path) -> bool +{ + data::writer writer(data::byte_order::msb); + + // 1. Begin setting up the preamble + auto data_offset = constants::defaults::data_offset; + auto map_offset = constants::defaults::map_offset; + auto data_length = constants::defaults::data_length; + auto map_length = constants::defaults::map_length; + + writer.write_long(data_offset); + writer.write_long(map_offset); + writer.write_long(data_length); + writer.write_long(map_length); + writer.pad_to_size(data_offset); + + // 2. Iterate through all of the resources and write their data blobs to the file data. + // When doing this we need to record the starting points of each resources data. + std::uint16_t resource_count = 0; + + std::vector types(file.type_count()); + auto type_ptr = types.begin(); + + for (const auto& type_code : file.types()) { + auto type = *type_ptr = const_cast(file.type(type_code)); + resource_count += type->count(); + + // If the type has attributes then abort and return false + if (!type->attributes().empty()) { + return false; + } + + for (auto& resource : *type) { + auto data = resource->data(); + auto size = data.size(); + resource->set_data_offset(writer.size() - data_offset); + writer.write_long(static_cast(size)); + writer.write_data(&data); + } + + type_ptr++; + } + + // 3. Start writing the ResourceMap. This consists of several characteristics, the first of which is a secondary + // preamble. We can now calculate the map offset and data length, but we're still waiting on the map length. For + // now, write these values as zero. + map_offset = static_cast(writer.size()); + data_length = map_offset - data_offset; + writer.write_long(data_offset); + writer.write_long(map_offset); + writer.write_long(data_length); + writer.write_long(map_length); + + // The next size bytes are used by the MacOS ResourceManager and thus not important to us. + writer.write_byte(0x00, 6); + + // 4. We're now writing the primary map information, which includes flags, and offsets for the type list and the + // name list. We can calculate where each of these will be. + auto name_list_offset = constants::type_list_offset + (file.type_count() * constants::resource_type_length); + name_list_offset += (resource_count * constants::resource_length) + sizeof(std::uint16_t); + + writer.write_short(0x0000); + writer.write_short(constants::type_list_offset); + writer.write_short(name_list_offset); + + // Now moving on to actually write each of the type descriptors into the data. + auto resource_offset = sizeof(std::uint16_t) + (file.type_count() * constants::resource_type_length); + writer.write_short(file.type_count() - 1); + for (const auto type : types) { + // We need to ensure that the type code is 4 characters -- otherwise this file will be massively corrupt + // when produced. + auto mac_roman = encoding::mac_roman::from_utf8(type->code()); + if (mac_roman.size() != 4) { + return false; + } + writer.write_bytes(mac_roman); + writer.write_short(type->count() - 1); + writer.write_short(resource_offset); + + resource_offset += type->count() * constants::resource_length; + } + + // 5. Now we're writing the actual resource headers. + std::uint16_t name_offset = 0; + std::uint16_t name_len = 0; + for (const auto type : types) { + for (const auto& resource : *type) { + auto id = resource->id(); + + if (id < std::numeric_limits::min() || id > std::numeric_limits::max()) { + return false; + } + writer.write_signed_short(static_cast(id)); + + // The name is actually stored in the name list, and the resource stores and offset to that name. + // If no name is assigned to the resource then the offset is encoded as 0xFFFF + if (resource->name().empty()) { + writer.write_short(0xFFFF); + } + else { + if (name_offset + name_len >= 0xFFFF) { + return false; + } + name_offset += name_len; + writer.write_short(name_offset); + + // Convert the name to MacRoman so that we can get the length of it when encoded. + auto mac_roman = encoding::mac_roman::from_utf8(resource->name()); + name_len = mac_roman.size() + 1; + if (name_len > 0x100) { + name_len = 0x100; + } + } + + // Write the resource attributes - these are currently hard coded as nothing. + writer.write_byte(0x00); + + // The data offset is a 3 byte (24-bit) value. This means the hi-byte needs discarding and then a swap + // performing. + auto offset = static_cast(resource->data_offset()); + if (offset > 0xFFFFFF) { + return false; + } + writer.write_triple(offset); + + // Finally this is a reserved field for use by the ResourceManager. + writer.write_long(0x0000'0000); + } + } + + // 6. Finally we write out each of the resource names, and calculate the map length. + name_offset = 0; + for (const auto type : types) { + for (const auto& resource : *type) { + if (resource->name().empty()) { + continue; + } + + auto mac_roman = encoding::mac_roman::from_utf8(resource->name()); + if (mac_roman.size() >= 0x100) { + mac_roman.resize(0xFF); + } + name_offset += writer.write_pstr(resource->name()) + 1; + } + } + + // Even if the data fits the spex, the resource manager will still not read files larger than 16MB. + if (writer.size() > 0xFFFFFF) { + return false; + } + map_length = static_cast(writer.size() - map_offset); + + // 7. Fix the preamble values. + writer.set_position(0); + writer.write_long(data_offset); + writer.write_long(map_offset); + writer.write_long(data_length); + writer.write_long(map_length); + + writer.set_position(map_offset); + writer.write_long(data_offset); + writer.write_long(map_offset); + writer.write_long(data_length); + writer.write_long(map_length); + + // Finish by writing the contents of the Resource File to disk. + writer.save(path); + return true; +} diff --git a/libs/libResourceCore/format/classic/writer.hpp b/libs/libResourceCore/format/classic/writer.hpp new file mode 100644 index 0000000..4f7a095 --- /dev/null +++ b/libs/libResourceCore/format/classic/writer.hpp @@ -0,0 +1,31 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace resource_core::format::classic +{ + auto write(file& file) -> bool; + auto write(file& file, const std::string& path) -> bool; +} diff --git a/libs/libResourceCore/format/extended/extended.hpp b/libs/libResourceCore/format/extended/extended.hpp new file mode 100644 index 0000000..f8f32b5 --- /dev/null +++ b/libs/libResourceCore/format/extended/extended.hpp @@ -0,0 +1,24 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include \ No newline at end of file diff --git a/libs/libResourceCore/format/extended/parser.cpp b/libs/libResourceCore/format/extended/parser.cpp new file mode 100644 index 0000000..d79b782 --- /dev/null +++ b/libs/libResourceCore/format/extended/parser.cpp @@ -0,0 +1,170 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include + +// MARK: - Constants + +namespace resource_core::format::extended::constants::defaults +{ + constexpr std::uint32_t signature = 'RSRX'; + constexpr std::uint32_t version = 1; +} + +// MARK: - Parsing + +auto resource_core::format::extended::parse(data::reader &reader, file &file) -> bool +{ + std::vector types; + + // 1. Resource Preamble + if (reader.read_long(0, data::reader::mode::peek) != constants::defaults::signature) { + return false; + } + if (reader.read_long(4, data::reader::mode::peek) != constants::defaults::version) { + return false; + } + + reader.move(sizeof(std::uint64_t)); + auto data_offset = reader.read_quad(); + auto map_offset = reader.read_quad(); + auto data_length = reader.read_quad(); + auto map_length = reader.read_quad(); + + // Verify that the resource file is valid. + if (data_offset == 0 || map_offset == 0 || map_length == 0) { + return false; + } + + auto rsrc_size = data_offset + data_length + map_length; + if (map_offset != data_offset + data_length) { + return false; + } + + if (rsrc_size > reader.size()) { + return false; + } + + // Move to the start of the ResourceMap, and verify the preamble contents. + reader.set_position(map_offset); + auto data_offset2 = reader.read_quad(); + auto map_offset2 = reader.read_quad(); + auto data_length2 = reader.read_quad(); + auto map_length2 = reader.read_quad(); + + if (data_offset2 != data_offset) { + return false; + } + + if (map_offset2 != map_offset) { + return false; + } + + if (data_length2 != data_length) { + return false; + } + + if (map_length2 != map_length) { + return false; + } + + const_cast(reader.data())->originates_from_extended_format(); + + // 2. Now that the preamble is parsed and verified, parse the contents + // of the ResourceMap. The first two fields are used by the actual Resource Manager in + // the Classic Macintosh OS, but are not used by this implementation. + GRAPHITE_UNUSED auto next_map = reader.read_long(); + GRAPHITE_UNUSED auto reference = reader.read_short(); + + // Start to read the actual content of the resource map. This is the content that + // we actually care about. The first field is the flags/attributes of the resource + // fork. + GRAPHITE_UNUSED auto flags = reader.read_short(); + + // The next fields are the offsets of the type list and the name list. + auto type_list_offset = static_cast(reader.read_quad()); + auto name_list_offset = static_cast(reader.read_quad()); + auto attribute_list_offset = static_cast(reader.read_quad()); + + // 3. Parse the list of Resource Types. + reader.set_position(map_offset + type_list_offset); + auto type_count = reader.read_quad() + 1; + + for (auto type_idx = 0; type_idx < type_count; ++type_idx) { + auto code = reader.read_cstr(4); + auto count = reader.read_quad() + 1; + auto first_resource_offset = reader.read_quad(); + auto attribute_count = reader.read_quad(); + auto attribute_offset = reader.read_quad(); + + auto type = new struct type(code); + + // 4. Extract the list of attributes before we create the type, as they are needed for actually building the + // type. + reader.save_position(); + if (attribute_count > 0) { + reader.set_position(attribute_list_offset + attribute_offset); + for (auto i = 0; i < attribute_count; ++i) { + type->add_attribute(reader.read_cstr(), reader.read_cstr()); + } + } + + // 5. Parse the list of resources for the current resource type. + reader.set_position(map_offset + type_list_offset + first_resource_offset); + + for (auto res_idx = 0; res_idx < count; ++res_idx) { + auto id = static_cast(reader.read_signed_quad()); + auto name_offset = reader.read_quad(); + GRAPHITE_UNUSED auto flags = reader.read_byte(); + auto resource_data_offset = reader.read_quad(); + GRAPHITE_UNUSED auto handle = reader.read_long(); + + reader.save_position(); + // 6. Parse the name out of the list of resource names. + std::string name; + if (name_offset != std::numeric_limits::max()) { + reader.set_position(map_offset + name_list_offset + name_offset); + name = std::move(reader.read_pstr()); + } + + // 6. Create a data slice for the resources data. + reader.set_position(data_offset + resource_data_offset); + auto data_size = reader.read_quad(); + auto slice = reader.read_data(data_size); + reader.restore_position(); + + // 7. Construct a new resource instance and add it to the type. + auto resource = new struct instance(type, id, name, std::move(slice)); + type->add_resource(resource); + } + + reader.restore_position(); + types.emplace_back(type); + } + + file.add_types(types); + return true; +} diff --git a/libs/libResourceCore/format/extended/parser.hpp b/libs/libResourceCore/format/extended/parser.hpp new file mode 100644 index 0000000..4d2eb5c --- /dev/null +++ b/libs/libResourceCore/format/extended/parser.hpp @@ -0,0 +1,29 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace resource_core::format::extended +{ + auto parse(data::reader& reader, file& file) -> bool; +} \ No newline at end of file diff --git a/libs/libResourceCore/format/extended/writer.cpp b/libs/libResourceCore/format/extended/writer.cpp new file mode 100644 index 0000000..2be4c3f --- /dev/null +++ b/libs/libResourceCore/format/extended/writer.cpp @@ -0,0 +1,228 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include + +// MARK: - Constants + +namespace resource_core::format::extended::constants::defaults +{ + constexpr std::uint32_t signature = 'RSRX'; + constexpr std::uint32_t version = 1; + constexpr std::uint64_t data_offset = 256; + constexpr std::uint64_t map_offset = 0; + constexpr std::uint64_t data_length = 0; + constexpr std::uint64_t map_length = 0; +} + +namespace resource_core::format::extended::constants +{ + constexpr std::uint16_t resource_type_length = 36; + constexpr std::uint16_t resource_length = 29; + constexpr std::uint16_t type_list_offset = 64; +} + +// MARK: - Writing + +auto resource_core::format::extended::write(file &file) -> bool +{ + return write(file, file.path()); +} + +auto resource_core::format::extended::write(file &file, const std::string &path) -> bool +{ + data::writer writer(data::byte_order::msb); + + // 1. Begin setting up the preamble + auto data_offset = constants::defaults::data_offset; + auto map_offset = constants::defaults::map_offset; + auto data_length = constants::defaults::data_length; + auto map_length = constants::defaults::map_length; + + writer.write_long(constants::defaults::signature); + writer.write_long(constants::defaults::version); + writer.write_quad(data_offset); + writer.write_quad(map_offset); + writer.write_quad(data_length); + writer.write_quad(map_length); + writer.pad_to_size(data_offset); + + // 2. Iterate through all of the resources and write their data blobs to the file data. + // When doing this we need to record the starting points of each resources data. + std::uint64_t resource_count = 0; + + std::vector types(file.type_count()); + auto type_ptr = types.begin(); + + for (const auto& type_code : file.types()) { + auto type = *type_ptr = const_cast(file.type(type_code)); + resource_count += type->count(); + + for (auto& resource : *type) { + auto data = resource->data(); + auto size = data.size(); + resource->set_data_offset(writer.size() - data_offset); + writer.write_quad(size); + writer.write_data(&data); + } + + type_ptr++; + } + + // 3. Start writing the ResourceMap. This consists of several characteristics, + // The first of which is a secondary preamble. We can now calculate the map_offset and + // the data_length, but we're still waiting on the map_length. For now, write these values + // as zeros. + map_offset = writer.size(); + data_length = map_offset - data_offset; + + writer.write_quad(data_offset); + writer.write_quad(map_offset); + writer.write_quad(data_length); + writer.write_quad(map_length); + + // The next six bytes are reserved. + writer.write_byte(0, 6); + + // 4. We're now writing the primary map information, which includes flags and offsets for the + // type list and the name list. We can calculate where each of these will be. + auto name_list_offset = constants::type_list_offset + (types.size() * constants::resource_type_length); + name_list_offset += (resource_count * constants::resource_length) + sizeof(std::uint64_t); + std::uint64_t attribute_list_offset_position = 0; + + writer.write_short(0); + writer.write_quad(constants::type_list_offset); + writer.write_quad(name_list_offset); + + attribute_list_offset_position = writer.position(); + writer.write_quad(attribute_list_offset_position); + + // No moving on to actually writing each of the type descriptors into the data. + std::uint64_t attribute_offset = 0; + std::uint64_t resource_offset = sizeof(std::uint64_t) + (types.size() * constants::resource_type_length); + writer.write_quad(types.size() - 1); + for (const auto type : types) { + // We need to ensure that the type code is 4 characters -- otherwise this file be massively corrupt + // when produced. + auto mac_roman = encoding::mac_roman::from_utf8(type->code()); + if (mac_roman.size() != 4) { + return false; + } + + writer.write_bytes(mac_roman); + writer.write_quad(type->count() - 1); + writer.write_quad(resource_offset); + writer.write_quad(type->attributes().size()); + writer.write_quad(attribute_offset); + + for (const auto& attribute : type->attributes()) { + attribute_offset += attribute.second.name().size() + attribute.second.string_value().size() + 2; + } + resource_offset += type->count() * constants::resource_length; + } + + // 5. Now we're writing the actual resource headers. + std::uint64_t name_offset = 0; + for (const auto type : types) { + for (const auto& resource : *type) { + writer.write_signed_quad(resource->id()); + + // The name is actually stored in the name list, and the resource stores an offset to that name. + // If no name is assigned to the resource then the offset is encoded as 0xFFFFFFFFFFFFFFFF. + if (resource->name().empty()) { + writer.write_quad(std::numeric_limits::max()); + } + else { + // Convert the name to MacRoman so that we can get the length of it when encoded. + auto mac_roman = encoding::mac_roman::from_utf8(resource->name()); + auto len = mac_roman.size(); + + writer.write_quad(name_offset); + name_offset += (len >= 0x100 ? 0xFF : len) + 1; + } + + writer.write_byte(0); + writer.write_quad(resource->data_offset()); + writer.write_long(0); + } + } + + // 6. Write out each of the resource names, and calculate the map length. + name_offset = 0; + for (const auto type : types) { + for (const auto& resource : *type) { + if (resource->name().empty()) { + continue; + } + + auto mac_roman = encoding::mac_roman::from_utf8(resource->name()); + if (mac_roman.size() >= 0x100) { + mac_roman.resize(0xFF); + } + name_offset += mac_roman.size() + 1; + writer.write_byte(static_cast(mac_roman.size())); + writer.write_bytes(mac_roman); + } + } + + // 7. Finally write out a list of attributes, but make sure the actual location of this attribute list is + // kept correct. + auto pos = writer.position(); + writer.set_position(attribute_list_offset_position); + writer.write_quad(pos); + writer.set_position(pos); + + attribute_offset = 0; + for (const auto type : types) { + const auto& attributes = type->attributes(); + + auto initial = writer.position(); + for (const auto& attribute : attributes) { + writer.write_cstr(attribute.second.name()); + writer.write_cstr(attribute.second.string_value()); + } + + attribute_offset += (writer.position() - initial); + } + map_length = static_cast(writer.size() - map_offset); + + // 8. Fix the preamble sizes. + writer.set_position(sizeof(std::uint64_t)); + writer.write_quad(data_offset); + writer.write_quad(map_offset); + writer.write_quad(data_length); + writer.write_quad(map_length); + + writer.set_position(map_offset); + writer.write_quad(data_offset); + writer.write_quad(map_offset); + writer.write_quad(data_length); + writer.write_quad(map_length); + + // Finish by writing the contents of the resource file to disk. + writer.save(path, data_offset + data_length + map_length); + return true; +} diff --git a/libs/libResourceCore/format/extended/writer.hpp b/libs/libResourceCore/format/extended/writer.hpp new file mode 100644 index 0000000..8189a87 --- /dev/null +++ b/libs/libResourceCore/format/extended/writer.hpp @@ -0,0 +1,31 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace resource_core::format::extended +{ + auto write(file& file) -> bool; + auto write(file& file, const std::string& path) -> bool; +} diff --git a/libs/libResourceCore/format/rez/parser.cpp b/libs/libResourceCore/format/rez/parser.cpp new file mode 100644 index 0000000..0faea2b --- /dev/null +++ b/libs/libResourceCore/format/rez/parser.cpp @@ -0,0 +1,130 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include +#include + +// MARK: - Constants + +namespace resource_core::format::rez::constants +{ + const std::string map_name = "resource.map"; + constexpr std::uint32_t signature = 'BRGR'; + constexpr std::uint32_t version = 1; + constexpr std::uint32_t resource_offset_length = 12; + constexpr std::uint32_t map_header_length = 8; + constexpr std::uint32_t type_info_length = 12; + constexpr std::uint32_t resource_info_length = 266; +}; + +// MARK: - Parsing + +auto resource_core::format::rez::parse(data::reader &reader, file &file) -> bool +{ + std::vector types; + + // 1. Read the preamble + if (reader.read_long() != constants::signature) { + reader.set_position(0); + return false; + } + + reader.change_byte_order(data::byte_order::lsb); + if (reader.read_long() != constants::version) { + reader.set_position(0); + return false; + } + + // 2. Read the header + auto header_length = reader.read_long(); + reader.move(4); // Skip over an unknown value. + + auto first_index = reader.read_long(); + auto count = reader.read_long(); + auto expected_header_length = 12 + (count * constants::resource_offset_length) + constants::map_name.size() + 1; + if (header_length != expected_header_length) { + reader.set_position(0); + return false; + } + + // 3. Record the offsets + std::vector offsets; + std::vector sizes; + for (auto res_idx = 0; res_idx < count; ++res_idx) { + offsets.emplace_back(static_cast(reader.read_long())); + sizes.push_back(static_cast(reader.read_long())); + reader.move(4); // Skip over an unknown value. + } + + if (reader.read_cstr() != constants::map_name) { + reader.set_position(0); + return false; + } + + // 4. Read the resource map header. + reader.change_byte_order(data::byte_order::msb); + auto map_offset = offsets.back(); + reader.set_position(map_offset); + reader.move(4); // Skip over an unknown value. + auto type_count = reader.read_long(); + + // 5. Read the resource types. + for (auto type_idx = 0; type_idx < type_count; ++type_idx) { + auto code = reader.read_cstr(4); + auto type_offset = static_cast(reader.read_long()); + auto count = reader.read_long(); + + auto type = new struct type(code); + reader.save_position(); + reader.set_position(map_offset + type_offset); + + // 6. Read the resource info. + for (auto res_idx = 0; res_idx < count; ++res_idx) { + auto index = reader.read_long(); + if (code != reader.read_cstr(4)) { + reader.set_position(0); + return false; + } + + auto id = static_cast(reader.read_signed_short()); + auto next_offset = reader.position() + 256; + auto name = reader.read_cstr(); + + reader.set_position(offsets[index - first_index]); + auto slice = reader.read_data(sizes[index - first_index]); + reader.set_position(next_offset); + + // 7. Construct a new resource instance and add it to the type. + auto resource = new struct instance(type, id, name, std::move(slice)); + type->add_resource(resource); + } + + reader.restore_position(); + types.emplace_back(type); + } + + file.add_types(types); + return true; +} diff --git a/libs/libResourceCore/format/rez/parser.hpp b/libs/libResourceCore/format/rez/parser.hpp new file mode 100644 index 0000000..d285054 --- /dev/null +++ b/libs/libResourceCore/format/rez/parser.hpp @@ -0,0 +1,29 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace resource_core::format::rez +{ + auto parse(data::reader& reader, file& file) -> bool; +} \ No newline at end of file diff --git a/libs/libResourceCore/format/rez/rez.hpp b/libs/libResourceCore/format/rez/rez.hpp new file mode 100644 index 0000000..9890e57 --- /dev/null +++ b/libs/libResourceCore/format/rez/rez.hpp @@ -0,0 +1,24 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include \ No newline at end of file diff --git a/libs/libResourceCore/format/rez/writer.cpp b/libs/libResourceCore/format/rez/writer.cpp new file mode 100644 index 0000000..1f065b2 --- /dev/null +++ b/libs/libResourceCore/format/rez/writer.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include +#include + +// MARK: - Constants + +namespace resource_core::format::rez::constants +{ + const std::string map_name = "resource.map"; + constexpr std::uint32_t signature = 'BRGR'; + constexpr std::uint32_t version = 1; + constexpr std::uint32_t header_length = 12; + constexpr std::uint32_t resource_offset_length = 12; + constexpr std::uint32_t map_header_length = 8; + constexpr std::uint32_t type_info_length = 12; + constexpr std::uint32_t resource_info_length = 266; +} + +// MARK: - Writing + +auto resource_core::format::rez::write(file &file) -> bool +{ + return write(file, file.path()); +} + +auto resource_core::format::rez::write(file &file, const std::string &path) -> bool +{ + data::writer writer(data::byte_order::msb); + + // Count up the total number of resources + std::uint32_t resource_count = 0; + for (const auto type_hash : file.types()) { + auto type = file.type(type_hash); + resource_count += type->count(); + } + + // The resource map itself is considered an entry for the offsets in the header. + std::uint32_t entry_count = resource_count + 1; + + // Calculate header length - this is from the end of the preamble to the start of the resource data + std::uint32_t header_length = constants::header_length + (entry_count * constants::resource_offset_length) + constants::map_name.size() + 1; + + // Write the preamble + writer.write_long(constants::signature); + writer.change_byte_order(data::byte_order::lsb); + writer.write_long(constants::version); + writer.write_long(header_length); + + // Calculate the offset to the first resource data + std::uint32_t resource_offset = writer.size() + header_length; + + // Write the header + std::uint32_t index = 1; // Index of the first resource, starting at 1. + writer.write_long(1); // Unknown value + writer.write_long(index); + writer.write_long(entry_count); + for (const auto type_hash : file.types()) { + auto type = const_cast(file.type(type_hash)); + + // If the type has attributes then abort and return false + if (!type->attributes().empty()) { + return false; + } + + for (const auto& resource : *type) { + // Get the data for the resource and determine its size. + auto size = resource->data().size(); + writer.write_long(resource_offset); + writer.write_long(static_cast(size)); + writer.write_long(0); + resource_offset += size; + } + } + + std::uint32_t type_count = file.type_count(); + + // Calculate the offset within map to start of resource info + std::uint32_t type_offset = constants::map_header_length + (type_count * constants::type_info_length); + std::uint32_t map_length = type_offset + (resource_count & constants::resource_info_length); + + // Write the offset and size of the resource map + writer.write_long(resource_offset); + writer.write_long(map_length); + writer.write_long(12 + (entry_count * constants::resource_offset_length)); // Unknown value? + writer.write_cstr(constants::map_name); + + // Write each of the resources + for (const auto type_hash : file.types()) { + auto type = const_cast(file.type(type_hash)); + for (const auto& resource : *type) { + writer.write_data(&resource->data()); + } + } + + // Write the resource map as big endian + writer.change_byte_order(data::byte_order::msb); + writer.write_long(0); // Unknown value + writer.write_long(type_count); + + for (const auto type_hash : file.types()) { + auto type = file.type(type_hash); + auto count = type->count(); + writer.write_cstr(type->code(), 4); + writer.write_long(type_offset); + writer.write_long(count); + type_offset += constants::resource_info_length * count; + } + + for (const auto type_hash : file.types()) { + auto type = const_cast(file.type(type_hash)); + for (const auto& resource : *type) { + writer.write_long(index++); + writer.write_cstr(type->code(), 4); + writer.write_signed_short(static_cast(resource->id())); + writer.write_cstr(resource->name(), 256); + } + } + + writer.save(path); + return true; +} \ No newline at end of file diff --git a/libGraphite/rsrc/extended.hpp b/libs/libResourceCore/format/rez/writer.hpp similarity index 57% rename from libGraphite/rsrc/extended.hpp rename to libs/libResourceCore/format/rez/writer.hpp index 758dac1..71c8dac 100644 --- a/libGraphite/rsrc/extended.hpp +++ b/libs/libResourceCore/format/rez/writer.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Tom Hancocks +// Copyright (c) 2022 Tom Hancocks // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -18,30 +18,15 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include -#include -#include -#include "libGraphite/rsrc/file.hpp" -#include "libGraphite/data/reader.hpp" -#include "libGraphite/data/writer.hpp" - -#if !defined(GRAPHITE_RSRC_EXTENDED) -#define GRAPHITE_RSRC_EXTENDED - -namespace graphite::rsrc::extended { +#pragma once - /** - * Parse the specified/provided data object that represents a resource file - * into a list of resource types. - */ - auto parse(const std::shared_ptr& reader) -> std::vector>; - - /** - * Build a data object that represents a resource file from the provided list - * of resource types. - */ - auto write(const std::string& path, const std::vector>& types) -> void; +#include +#include +#include +namespace resource_core::format::rez +{ + auto write(file& file) -> bool; + auto write(file& file, const std::string& path) -> bool; } -#endif \ No newline at end of file diff --git a/libs/libResourceCore/identifier.hpp b/libs/libResourceCore/identifier.hpp new file mode 100644 index 0000000..031cbfd --- /dev/null +++ b/libs/libResourceCore/identifier.hpp @@ -0,0 +1,31 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace resource_core +{ + typedef std::int64_t identifier; + constexpr identifier auto_resource_id = std::numeric_limits::min(); + constexpr identifier default_resource_id = 128; +} \ No newline at end of file diff --git a/libs/libResourceCore/manager.cpp b/libs/libResourceCore/manager.cpp new file mode 100644 index 0000000..3416d7b --- /dev/null +++ b/libs/libResourceCore/manager.cpp @@ -0,0 +1,165 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +// MARK: - Singleton / Construction + +auto resource_core::manager::shared_manager() -> manager & +{ + static ::resource_core::manager manager; + return manager; +} + +// MARK: - File Management + +auto resource_core::manager::import_file(class file *file) -> class file * +{ + m_files.emplace(file->hash_value(), file); + m_file_load_order.insert(m_file_load_order.begin(), file->hash_value()); + return file; +} + +auto resource_core::manager::import_file(const std::string &path) -> class file * +{ + auto file = new ::resource_core::file(path); + return import_file(file); +} + +auto resource_core::manager::unload_file(file::hash file) -> void +{ + auto it = m_files.find(file); + if (it != m_files.end()) { + delete it->second; + m_files.erase(file); + } +} + +auto resource_core::manager::unload_file(class file *file) -> void +{ + if (file) { + unload_file(file->hash_value()); + } +} + +auto resource_core::manager::unload_file(const std::string &path) -> void +{ + unload_file(file::hash_for_path(path)); +} + +auto resource_core::manager::file(file::hash file) -> class file * +{ + auto it = m_files.find(file); + if (it != m_files.end()) { + return it->second; + } + return nullptr; +} + +auto resource_core::manager::file(file::hash file) const -> class file * +{ + auto it = m_files.find(file); + if (it != m_files.end()) { + return it->second; + } + return nullptr; +} + +auto resource_core::manager::file(const std::string &path) -> class file * +{ + return file(file::hash_for_path(path)); +} + +auto resource_core::manager::files() const -> const std::vector& +{ + return m_file_load_order; +} + +auto resource_core::manager::file_references() const -> std::vector +{ + std::vector files; + for (auto hash : this->files()) { + files.emplace_back(this->file(hash)); + } + return std::move(files); +} + +// MARK: - Searching + +auto resource_core::manager::all_types(const std::string &type_code, const std::vector &attributes) const -> std::vector +{ + std::vector types; + for (const auto& it : m_files) { + auto type = it.second->type(type_code, attributes); + if (type) { + types.emplace_back(type); + } + } + return types; +} + +auto resource_core::manager::find(const std::string &type_code, const std::vector &attributes) const -> result +{ + std::unordered_map resources; + result result; + + // Gather all of the resources for the type into a single location to draw upon. + const auto& types = all_types(type_code, attributes); + for (const auto type : types) { + for (const auto& resource : *type) { + result.add(resource); + } + } + + return std::move(result); +} + +auto resource_core::manager::find(const std::string &type_code, identifier id, const std::vector &attributes) const -> struct instance * +{ + auto resources = std::move(find(type_code, attributes)); + return resources.resource(type_code, id); +} + +auto resource_core::manager::find(const std::string &type_code, const std::string &name_prefix, const std::vector &attributes) const -> result +{ + auto resources = std::move(find(type_code, attributes)); + return std::move(resources.filter([&] (struct instance *subject) -> bool { + const auto& name = subject->name(); + if (name.length() == name_prefix.length() && name == name_prefix) { + return true; + } + else if (name.length() >= name_prefix.length() && name.substr(0, name_prefix.length()) == name_prefix) { + return true; + } + else { + return false; + } + })); +} + +// MARK: - Tear Down + +auto resource_core::manager::tear_down() -> void +{ + for (const auto& file : m_files) { + delete file.second; + } + m_files = {}; +} \ No newline at end of file diff --git a/libs/libResourceCore/manager.hpp b/libs/libResourceCore/manager.hpp new file mode 100644 index 0000000..f4e6d0e --- /dev/null +++ b/libs/libResourceCore/manager.hpp @@ -0,0 +1,106 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace resource_core +{ + class manager + { + public: + manager(const manager&) = delete; + manager(manager&&) = delete; + auto operator=(const manager&) -> manager& = delete; + auto operator=(manager&&) -> manager& = delete; + + static auto shared_manager() -> manager&; + + auto tear_down() -> void; + + auto import_file(class file *file) -> class file *; + auto import_file(const std::string& path) -> class file *; + + auto unload_file(class file *file) -> void; + auto unload_file(file::hash file) -> void; + auto unload_file(const std::string& path) -> void; + + auto file(file::hash file) -> class file *; + auto file(file::hash file) const -> class file *; + auto file(const std::string& path) -> class file *; + + [[nodiscard]] auto files() const -> const std::vector&; + [[nodiscard]] auto file_references() const -> std::vector; + + [[nodiscard]] auto find(const std::string& type_code, const std::vector& attributes = {}) const -> result; + [[nodiscard]] auto find(const std::string& type_code, identifier id, const std::vector& attributes = {}) const -> struct instance *; + [[nodiscard]] auto find(const std::string& type_code, const std::string& name_prefix, const std::vector& attributes = {}) const -> result; + + template + [[nodiscard]] auto find(const std::vector& attributes = {}) const -> result + { + return find(T::type_code(), attributes); + } + + template + [[nodiscard]] auto find(identifier id, const std::vector& attributes = {}) const -> struct instance * + { + return find(T::type_code(), id, attributes); + } + + template + [[nodiscard]] auto find(const std::string& name_prefix, const std::vector& attributes = {}) const -> result + { + return find(T::type_code(), name_prefix, attributes); + } + + [[nodiscard]] auto all_types(const std::string& type_code, const std::vector& attributes = {}) const -> std::vector; + + template + [[nodiscard]] auto all_types(const std::vector& attributes = {}) const -> std::vector + { + return all_types(T::type_code(), attributes); + } + + template + [[nodiscard]] auto load(identifier id, const std::vector& attributes = {}) const -> T + { + if (const auto resource = find(id, attributes)) { + return std::move(T(resource->data(), resource->id(), resource->name())); + } + throw std::runtime_error("Resource not found: " + T::type_code() + ".#" + std::to_string(id)); + }; + + private: + std::vector m_file_load_order; + std::unordered_map m_files; + + manager() = default; + }; +} + diff --git a/libs/libResourceCore/result.cpp b/libs/libResourceCore/result.cpp new file mode 100644 index 0000000..ba4a5dc --- /dev/null +++ b/libs/libResourceCore/result.cpp @@ -0,0 +1,132 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include + +// MARK: - Hashing + +auto resource_core::result::sort_key(struct instance *resource) -> hash +{ + return sort_key(resource->type_code(), resource->id()); +} + +auto resource_core::result::sort_key(const std::string& type_code, identifier id) -> hash +{ + std::string key = type_code + "." + std::to_string(id); + return hashing::xxh64(key.c_str(), key.size()); +} + +// MARK: - Item Management + +auto resource_core::result::add(struct instance *instance) -> void +{ + if (!instance) { + return; + } + + if (m_finalized) { + // TODO: Issue a warning + return; + } + + auto key = sort_key(instance); + m_resources.emplace(key, instance); + m_sorted_keys.emplace_back(key); +} + +auto resource_core::result::finalize() -> void +{ + m_finalized = true; + sort(); +} + +auto resource_core::result::size() const -> std::size_t +{ + return m_resources.size(); +} + +// MARK: - Sorting + +auto resource_core::result::sort() -> void +{ + if (!m_finalized) { + // TODO: Issue a warning + return; + } + + // TODO: Make this customisable in the future and more advanced? + std::sort(m_sorted_keys.begin(), m_sorted_keys.end(), [&] (resource_sort_key lhs, resource_sort_key rhs) { + const auto& lhs_resource = m_resources.at(lhs); + const auto& rhs_resource = m_resources.at(rhs); + return lhs_resource->id() < rhs_resource->id(); + }); +} + +// MARK: - Look up + +auto resource_core::result::begin() -> iterator +{ + return { this, 0 }; +} + +auto resource_core::result::end() -> iterator +{ + return { this, std::numeric_limits::max() }; +} + +auto resource_core::result::at(std::uint64_t idx) const -> struct instance * +{ + auto it = m_resources.find(m_sorted_keys.at(idx)); + return (it != m_resources.end()) ? it->second : nullptr; +} + +auto resource_core::result::id(identifier id) const -> struct instance * +{ + for (const auto resource : m_resources) { + if (resource.second->id() == id) { + return resource.second; + } + } + return nullptr; +} + +auto resource_core::result::resource(const std::string &type_code, identifier id) const -> struct instance * +{ + auto key = sort_key(type_code, id); + auto it = m_resources.find(key); + return (it != m_resources.end()) ? it->second : nullptr; +} + +auto resource_core::result::filter(const std::functionbool>& fn) const -> result +{ + result result; + + for (const auto resource : m_resources) { + if (fn(resource.second)) { + result.add(resource.second); + } + } + result.sort(); + + return std::move(result); +} diff --git a/libs/libResourceCore/result.hpp b/libs/libResourceCore/result.hpp new file mode 100644 index 0000000..4626cc4 --- /dev/null +++ b/libs/libResourceCore/result.hpp @@ -0,0 +1,112 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace resource_core +{ + struct result + { + public: + typedef std::uint64_t hash; + + struct iterator + { + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = struct instance; + using pointer = value_type*; + using reference = value_type&; + + iterator(result *ptr, std::uint64_t index) : m_ptr(ptr), m_index(index) {}; + + auto operator*() const -> reference { return *get(); } + auto operator->() -> pointer { return const_cast(get()); } + + auto operator++() -> iterator& { + m_index++; + if (m_index >= m_ptr->m_sorted_keys.size()) { + m_index = std::numeric_limits::max(); + } + return *this; + } + auto operator++(int) -> iterator { auto tmp = *this; ++(*this); return tmp; } + + friend auto operator==(const iterator& lhs, const iterator& rhs) -> bool + { + return (lhs.m_ptr == rhs.m_ptr) && (lhs.m_index == rhs.m_index); + } + + friend auto operator!= (const iterator& lhs, const iterator& rhs) -> bool + { + return (lhs.m_ptr != rhs.m_ptr) || (lhs.m_index != rhs.m_index); + } + + private: + result *m_ptr { nullptr }; + std::uint64_t m_index { 0 }; + + [[nodiscard]] auto get() const -> pointer + { + if (m_index == std::numeric_limits::max()) { + return nullptr; + } + auto key = m_ptr->m_sorted_keys.at(m_index); + auto value = m_ptr->m_resources.at(key); + return value; + }; + }; + + public: + result() = default; + + static auto sort_key(struct instance *instance) -> hash; + static auto sort_key(const std::string& type_code, identifier id) -> hash; + + auto add(struct instance *instance) -> void; + + auto finalize() -> void; + auto sort() -> void; + + auto begin() -> iterator; + auto end() -> iterator; + + [[nodiscard]] auto filter(const std::functionbool>& fn) const -> result; + + [[nodiscard]] auto size() const -> std::size_t; + [[nodiscard]] auto at(std::uint64_t idx) const -> struct instance *; + [[nodiscard]] auto id(identifier id) const -> struct instance *; + [[nodiscard]] auto resource(const std::string& type_code, identifier id) const -> struct instance *; + + private: + typedef std::uint64_t resource_sort_key; + bool m_finalized { false }; + std::unordered_map m_resources; + std::vector m_sorted_keys; + }; + +} diff --git a/libs/libResourceCore/structure/attribute.cpp b/libs/libResourceCore/structure/attribute.cpp new file mode 100644 index 0000000..26f789f --- /dev/null +++ b/libs/libResourceCore/structure/attribute.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Construction + +resource_core::attribute::attribute(const std::string &name, const std::string &value) + : m_name(name), m_value(value) +{ +} + +// MARK: - Accessors + +auto resource_core::attribute::hash_value() const -> hash +{ + return hash_for_name(m_name); +} + +auto resource_core::attribute::name() const -> const std::string& +{ + return m_name; +} + +auto resource_core::attribute::string_value() const -> const std::string& +{ + return m_value; +} + +// MARK: - Helpers + +auto resource_core::attribute::hash_for_name(const std::string& name) -> hash +{ + return hashing::xxh64(name.c_str(), name.size()); +} diff --git a/libs/libResourceCore/structure/attribute.hpp b/libs/libResourceCore/structure/attribute.hpp new file mode 100644 index 0000000..6c65bf7 --- /dev/null +++ b/libs/libResourceCore/structure/attribute.hpp @@ -0,0 +1,58 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace resource_core +{ + struct attribute + { + public: + typedef std::uint64_t hash; + + static auto hash_for_name(const std::string& name) -> hash; + + public: + attribute(const std::string& name, const std::string& value); + + template::value>::type* = nullptr> + attribute(const std::string& name, T value) : m_name(name), m_value(std::to_string(value)) {} + + [[nodiscard]] auto hash_value() const -> hash; + [[nodiscard]] auto name() const -> const std::string&; + [[nodiscard]] auto string_value() const -> const std::string&; + + template::value>::type* = nullptr> + [[nodiscard]] auto value() const -> T + { + return std::stoi(m_value); + } + + private: + std::string m_name; + std::string m_value; + }; +} + diff --git a/libs/libResourceCore/structure/instance.cpp b/libs/libResourceCore/structure/instance.cpp new file mode 100644 index 0000000..4296bda --- /dev/null +++ b/libs/libResourceCore/structure/instance.cpp @@ -0,0 +1,157 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include + +// MARK: - Construction + +resource_core::instance::instance(resource_core::identifier id, const std::string &name) + : m_id(id), m_name(name) +{} + +resource_core::instance::instance(struct type *type, resource_core::identifier id, const std::string &name, data::block data) + : m_type(type), m_id(id), m_name(name), m_data(std::move(data)) +{ +} + +resource_core::instance::instance(const instance &instance) + : m_type(instance.m_type), + m_id(instance.m_id), + m_name(instance.m_name), + m_data(data::block(instance.m_data, true)) +{ +} + +resource_core::instance::instance(instance &&instance) noexcept + : m_type(instance.m_type), + m_id(instance.m_id), + m_name(std::move(instance.m_name)), + m_data(std::move(instance.m_data)) +{ + instance.m_type = nullptr; +} + +// MARK: - Operators + +auto resource_core::instance::operator=(const instance &instance) -> struct instance& +{ + if (this == const_cast(&instance)) { + return *this; + } + + m_id = instance.m_id; + m_type = instance.m_type; + m_name = instance.m_name; + m_data = instance.m_data; + m_data_offset = instance.m_data_offset; + + return *this; +} + +auto resource_core::instance::operator=(instance &&instance) noexcept -> struct instance& +{ + if (this != &instance) { + m_id = instance.m_id; + m_type = instance.m_type; + m_name = std::move(instance.m_name); + m_data = std::move(instance.m_data); + m_data_offset = instance.m_data_offset; + } + return *this; +} + +// MARK: - Accessors + +auto resource_core::instance::id() const -> identifier +{ + return m_id; +} + +auto resource_core::instance::type() const -> struct type * +{ + return m_type; +} + +auto resource_core::instance::name() const -> const std::string& +{ + return m_name; +} + +auto resource_core::instance::type_code() const -> std::string +{ + if (m_type) { + return m_type->code(); + } + else { + return type::unknown_type_code; + } +} + +auto resource_core::instance::data() const -> const data::block& +{ + return m_data; +} + +auto resource_core::instance::set_id(identifier id) -> void +{ + m_id = id; +} + +auto resource_core::instance::set_name(const std::string &name) -> void +{ + m_name = name; +} + +auto resource_core::instance::set_type(struct type *type) -> void +{ + m_type = type; +} + +auto resource_core::instance::set_data(data::block& data) -> void +{ + m_data = data; + m_data_offset = 0; +} + +// MARK: - Hashing + +auto resource_core::instance::hash(identifier id) -> identifier_hash +{ + return hashing::xxh64(&id, sizeof(id)); +} + +auto resource_core::instance::hash(const std::string &name) -> name_hash +{ + return hashing::xxh64(name.c_str(), name.size()); +} + +// MARK: - Data Offsets + +auto resource_core::instance::set_data_offset(std::size_t offset) -> void +{ + m_data_offset = offset; +} + +auto resource_core::instance::data_offset() const -> std::size_t +{ + return m_data_offset; +} diff --git a/libs/libResourceCore/structure/instance.hpp b/libs/libResourceCore/structure/instance.hpp new file mode 100644 index 0000000..903a502 --- /dev/null +++ b/libs/libResourceCore/structure/instance.hpp @@ -0,0 +1,71 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace resource_core +{ + struct type; + + struct instance + { + public: + typedef std::uint64_t identifier_hash; + typedef std::uint64_t name_hash; + + public: + explicit instance(identifier id = default_resource_id, const std::string& name = ""); + explicit instance(struct type *type, identifier id = default_resource_id, const std::string& name = "", data::block data = {}); + instance(const instance& resource); + instance(instance&& resource) noexcept; + + auto operator=(const instance& resource) -> struct instance&; + auto operator=(instance&& resource) noexcept -> struct instance&; + + [[nodiscard]] auto id() const -> identifier; + [[nodiscard]] auto type() const -> struct type *; + [[nodiscard]] auto name() const -> const std::string&; + [[nodiscard]] auto type_code() const -> std::string; + [[nodiscard]] auto data() const -> const data::block&; + + auto set_id(identifier id) -> void; + auto set_name(const std::string& name) -> void; + auto set_type(struct type *type) -> void; + auto set_data(data::block& data) -> void; + + static auto hash(identifier id) -> identifier_hash; + static auto hash(const std::string& name) -> name_hash; + + auto set_data_offset(std::size_t offset) -> void; + [[nodiscard]] auto data_offset() const -> std::size_t; + + private: + identifier m_id { default_resource_id }; + struct type *m_type { nullptr }; + std::string m_name; + data::block m_data; + std::size_t m_data_offset { 0 }; + }; +} diff --git a/libs/libResourceCore/structure/type.cpp b/libs/libResourceCore/structure/type.cpp new file mode 100644 index 0000000..fa8258f --- /dev/null +++ b/libs/libResourceCore/structure/type.cpp @@ -0,0 +1,242 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include + +// MARK: - Construction + +resource_core::type::type(const std::string &code) + : m_code(code) +{ +} + +resource_core::type::type(const type &type) + : m_code(type.m_code), + m_attributes(type.m_attributes) +{ + for (const auto& resource : type.m_resources) { + add_resource(new instance(*resource)); + } +} + +resource_core::type::type(type &&type) noexcept + : m_code(std::move(type.m_code)), + m_resources(std::move(type.m_resources)), + m_resource_id_map(std::move(type.m_resource_id_map)), + m_resource_name_map(std::move(type.m_resource_name_map)), + m_attributes(std::move(type.m_attributes)) +{ + type.m_resources = {}; + type.m_resource_id_map = {}; + type.m_resource_name_map = {}; +} + +// MARK: - Destruction + +resource_core::type::~type() +{ + for (auto it : m_resources) { + delete it; + } +} + +// MARK: - Operators + +auto resource_core::type::operator=(const type &type) -> struct type& +{ + if (this == const_cast(&type)) { + return *this; + } + + m_code = type.m_code; + m_attributes = type.m_attributes; + + for (const auto& resource : type.m_resources) { + add_resource(new instance(*resource)); + } + + return *this; +} + +auto resource_core::type::operator=(type &&type) noexcept -> struct type& +{ + if (this != &type) { + m_code = std::move(type.m_code); + m_resources = std::move(type.m_resources); + m_resource_id_map = std::move(type.m_resource_id_map); + m_resource_name_map = std::move(type.m_resource_name_map); + m_attributes = std::move(type.m_attributes); + + type.m_resources = {}; + type.m_resource_id_map = {}; + type.m_resource_name_map = {}; + } + return *this; +} + +// MARK: - Accessors + +auto resource_core::type::attribute_string(const std::unordered_map &attributes) -> std::string +{ + std::string descriptor; + for (const auto& attribute : attributes) { + descriptor += "<" + attribute.second.name() + ":" + attribute.second.string_value() + ">"; + } + return std::move(descriptor); +} + +auto resource_core::type::hash_for_type_code(const std::string &code) -> hash +{ + return hashing::xxh64(code.c_str(), code.size()); +} + +auto resource_core::type::hash_for_type_code(const std::string &code, const std::unordered_map &attributes) -> resource_core::type::hash +{ + std::string assembled_code { code }; + if (!attributes.empty()) { + assembled_code += ":" + attribute_string(attributes); + } + return hash_for_type_code(assembled_code); +} + +auto resource_core::type::hash_value() const -> hash +{ + std::string code { m_code }; + if (!m_attributes.empty()) { + code += ":" + attribute_descriptor_string(); + } + return hash_for_type_code(code); +} + +auto resource_core::type::code() const -> const std::string& +{ + return m_code; +} + +auto resource_core::type::attributes() const -> const std::unordered_map& +{ + return m_attributes; +} + +auto resource_core::type::count() const -> std::size_t +{ + return m_resources.size(); +} + +auto resource_core::type::attribute_descriptor_string() const -> std::string +{ + return std::move(attribute_string(m_attributes)); +} + +// MARK: - Attribute Management + +auto resource_core::type::add_attribute(const std::string& name, const std::string& value) -> void +{ + attribute attr { name, value }; + m_attributes.emplace(std::pair(attr.hash_value(), std::move(attr))); +} + +template::value>::type*> +auto resource_core::type::add_attribute(const std::string &name, T value) -> void +{ + attribute attr { name, value }; + m_attributes.template emplace(std::pair(attr.hash_value(), std::move(attr))); +} + +// MARK: - Resource Management + +auto resource_core::type::has_resource(resource_core::identifier id) const -> bool +{ + auto hash = hashing::xxh64(&id, sizeof(id)); + return (m_resource_id_map.find(hash) != m_resource_id_map.end()); +} + +auto resource_core::type::has_resource(const std::string &name) const -> bool +{ + auto hash = hashing::xxh64(name.c_str(), name.size()); + return (m_resource_name_map.find(hash) != m_resource_name_map.end()); +} + +auto resource_core::type::add_resource(instance *resource) -> void +{ + m_resources.emplace_back(resource); + resource->set_type(this); + + auto id_hash = instance::hash(resource->id()); + auto name_hash = instance::hash(resource->name()); + + m_resource_id_map.emplace(std::pair(id_hash, resource)); + m_resource_name_map.emplace(std::pair(name_hash, resource)); +} + +auto resource_core::type::remove_resource(resource_core::identifier id) -> void +{ + // TODO: +} + +auto resource_core::type::resource_with_id(resource_core::identifier id) const -> instance * +{ + for (const auto& it : m_resources) { + if (it->id() == id) { + return it; + } + } + return nullptr; +} + +auto resource_core::type::resource_with_name(const std::string &name) const -> instance * +{ + for (const auto& it : m_resources) { + if (it->name() == name) { + return it; + } + } + return nullptr; +} + +auto resource_core::type::begin() -> std::vector::iterator +{ + return m_resources.begin(); +} + +auto resource_core::type::end() -> std::vector::iterator +{ + return m_resources.end(); +} + +auto resource_core::type::begin() const -> std::vector::const_iterator +{ + return m_resources.cbegin(); +} + +auto resource_core::type::end() const -> std::vector::const_iterator +{ + return m_resources.end(); +} + +auto resource_core::type::at(int64_t idx) -> instance * +{ + if (idx < 0 || idx >= m_resources.size()) { + return nullptr; + } + return m_resources.at(idx); +} diff --git a/libs/libResourceCore/structure/type.hpp b/libs/libResourceCore/structure/type.hpp new file mode 100644 index 0000000..f44a12e --- /dev/null +++ b/libs/libResourceCore/structure/type.hpp @@ -0,0 +1,86 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +namespace resource_core +{ + struct type + { + public: + typedef std::uint64_t hash; + + static constexpr const char *unknown_type_code = "????"; + + public: + explicit type(const std::string& code); + type(const type& type); + type(type&& type) noexcept; + + ~type(); + + auto operator=(const type& type) -> struct type&; + auto operator=(type&& type) noexcept -> struct type&; + + static auto attribute_string(const std::unordered_map& attributes) -> std::string; + static auto hash_for_type_code(const std::string& code) -> hash; + static auto hash_for_type_code(const std::string& code, const std::unordered_map& attributes) -> hash; + + [[nodiscard]] auto hash_value() const -> hash; + [[nodiscard]] auto code() const -> const std::string&; + [[nodiscard]] auto attributes() const -> const std::unordered_map&; + [[nodiscard]] auto count() const -> std::size_t; + [[nodiscard]] auto attribute_descriptor_string() const -> std::string; + + auto add_attribute(const std::string& name, const std::string& value) -> void; + + template::value>::type* = nullptr> + auto add_attribute(const std::string& name, T value) -> void; + + [[nodiscard]] auto has_resource(identifier id) const -> bool; + [[nodiscard]] auto has_resource(const std::string& name) const -> bool; + + auto add_resource(instance *resource) -> void; + auto remove_resource(identifier id) -> void; + + [[nodiscard]] auto resource_with_id(identifier id) const -> instance *; + [[nodiscard]] auto resource_with_name(const std::string& name) const -> instance *; + + auto begin() -> std::vector::iterator; + auto end() -> std::vector::iterator; + [[nodiscard]] auto begin() const -> std::vector::const_iterator; + [[nodiscard]] auto end() const-> std::vector::const_iterator; + auto at(std::int64_t idx) -> instance *; + + private: + std::string m_code; + std::vector m_resources; + std::unordered_map m_resource_id_map {}; + std::unordered_map m_resource_name_map {}; + std::unordered_map m_attributes {}; + + }; +} diff --git a/libs/libSIMD/CMakeLists.txt b/libs/libSIMD/CMakeLists.txt new file mode 100644 index 0000000..084e705 --- /dev/null +++ b/libs/libSIMD/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (c) 2023 Tom Hancocks +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) + +######################################################################################################################## +## Project +project(SIMD LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) + +######################################################################################################################## +## libSIMD +file(GLOB_RECURSE libSIMD_Sources + *.cpp +) + +add_library(SIMD ${libSIMD_Sources}) +target_include_directories(SIMD PUBLIC + ${PROJECT_LIBS_DIR} +) diff --git a/libs/libSIMD/SIMD.hpp b/libs/libSIMD/SIMD.hpp new file mode 100644 index 0000000..a758687 --- /dev/null +++ b/libs/libSIMD/SIMD.hpp @@ -0,0 +1,75 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#if (__arm64 || __arm64__) && (__ARM_NEON || __ARM_NEON__) +# define ARM true +# define ARM_NEON true +# if __APPLE__ +# define APPLE_SILICON true +# endif +# define INTEL_SIMD false +#elif (__x86_64__) +# define ARM false +# define ARM_NEON false +# define APPLE_SILICON false +# define INTEL_SIMD true +#endif + +#if ARM_NEON +# include + typedef ::int8x16_t i8x16; + typedef ::int16x8_t i16x8; + typedef ::int32x4_t i32x4; + typedef ::int64x2_t i64x2; + typedef ::float32x4_t f32x4; + typedef ::float64x2_t f64x2; +#endif + +#if INTEL_SIMD +# include +# include +# include +# include +# include +# include + typedef __m128i i8x16; + typedef __m128i i16x8; + typedef __m128i i32x4; + typedef __m128i i64x2; + typedef __m128 f32x4; + typedef __m128 f64x2; +#endif + +/* Not all targets implement `memcpy`, `memset`, etc with SIMD operations + * and as such we need to provide an implementation for these functions. + */ +#if !defined(USE_TARGET_MEMORY_FUNCTIONS) +# if __APPLE__ +# define USE_TARGET_MEMORY_FUNCTIONS true +# else +# define USE_TARGET_MEMORY_FUNCTIONS false +# endif +#endif + +#if !defined(APPROX_VALUE) +# define APPROX_VALUE +#endif \ No newline at end of file diff --git a/libs/libSIMD/arm/arm_neon.hpp b/libs/libSIMD/arm/arm_neon.hpp new file mode 100644 index 0000000..53f106d --- /dev/null +++ b/libs/libSIMD/arm/arm_neon.hpp @@ -0,0 +1,211 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#if (__ARM_NEON || __ARM_NEON__) +#include +#include + +// Hints / Macros +#define SIMD_FUNCTION __attribute__((__always_inline__, __nodebug__, __min_vector_width__(128))) + +// Types +typedef float f32x4 __attribute__((neon_vector_type((4)))); + +namespace simd +{ + + SIMD_FUNCTION + static inline auto store_vector(float *p, f32x4 a) -> void + { + vst1q_f32(p, a); + } + + SIMD_FUNCTION + static inline auto load_vector(float *p) -> f32x4 + { + return vld1q_f32(p); + } + + SIMD_FUNCTION + static inline auto single_value_vector(float w) -> f32x4 + { + return vdupq_n_f32(w); + } + + SIMD_FUNCTION + static inline auto vector(float z, float y, float x, float w) -> f32x4 + { + float __attribute__((__aligned__(16))) data[4] = { z, y, x, w }; + return vld1q_f32(data); + } + + SIMD_FUNCTION + static inline auto vector_shuffle_lower_higher(f32x4 a, f32x4 b) -> f32x4 + { + return vcombine_f32(vget_low_f32(a), vget_high_f32(b)); + } + + SIMD_FUNCTION + static inline auto vector_slice_lower(f32x4 a) -> f32x4 + { + return vcombine_f32(vget_low_f32(a), vget_low_f32(a)); + } + + SIMD_FUNCTION + static inline auto vector_slice_upper(f32x4 a) -> f32x4 + { + return vcombine_f32(vget_high_f32(a), vget_high_f32(a)); + } + + SIMD_FUNCTION + static inline auto swap_lower_upper(f32x4 a) -> f32x4 + { + return vector_shuffle_lower_higher(a, a); + } + + SIMD_FUNCTION + static inline auto reverse(f32x4 a) -> f32x4 + { + return vcombine_f32(vget_high_f32(a), vget_low_f32(a)); + } + + SIMD_FUNCTION + static inline auto add(f32x4 a, f32x4 b) -> f32x4 + { + return vaddq_f32(a, b); + } + + SIMD_FUNCTION + static inline auto sub(f32x4 a, f32x4 b) -> f32x4 + { + return vsubq_f32(a, b); + } + + SIMD_FUNCTION + static inline auto mul(f32x4 a, f32x4 b) -> f32x4 + { + return vmulq_f32(a, b); + } + + SIMD_FUNCTION + static inline auto div(f32x4 a, f32x4 b) -> f32x4 + { + return vdivq_f32(a, b); + } + + SIMD_FUNCTION + static inline auto abs(f32x4 a) -> f32x4 + { + return vabsq_f32(a); + } + + SIMD_FUNCTION + static inline auto round(f32x4 a) -> f32x4 + { + auto r = vcvtq_n_s32_f32(a, 1); + r = vsraq_n_s32(r, r, 31); + r = vrshrq_n_s32(r, 1); + return vcvtq_f32_s32(r); + } + + SIMD_FUNCTION + static inline auto floor(f32x4 a) -> f32x4 + { + return vrndmq_f32(a); + } + + SIMD_FUNCTION + static inline auto ceil(f32x4 a) -> f32x4 + { + auto r = vaddq_f32(a, vdupq_n_f32(0.999999f)); + return vreinterpretq_f32_s32(vreinterpretq_s32_f32(r)); + } + + SIMD_FUNCTION + static inline auto pow(f32x4 a, float exp) -> f32x4 + { + if (exp == 2.0) { + return vmulq_f32(a, a); + } + else { + f32x4 v = a; + for (auto i = 0; i < 4; ++i) { + v[i] = std::powf(v[i], exp); + } + return v; + } + } + + SIMD_FUNCTION + static inline auto sqrt(f32x4 a) -> f32x4 + { + return vsqrtq_f32(a); + } + + SIMD_FUNCTION + static inline auto rsqrt(f32x4 a) -> f32x4 + { + return vrsqrteq_f32(a); + } + + SIMD_FUNCTION + static inline auto sin(f32x4 a) -> f32x4 + { + f32x4 v = a; + for (auto i = 0; i < 4; ++i) { + v[i] = std::sinf(v[i]); + } + return v; + } + + SIMD_FUNCTION + static inline auto cos(f32x4 a) -> f32x4 + { + f32x4 v = a; + for (auto i = 0; i < 4; ++i) { + v[i] = std::cosf(v[i]); + } + return v; + } + + SIMD_FUNCTION + static inline auto rcp(f32x4 a) -> f32x4 + { + auto r = vrecpeq_f32(a); + r = vmulq_f32(r, vrecpsq_f32(r, a)); + return r; + } + + SIMD_FUNCTION + static inline auto min(f32x4 a, f32x4 b) -> f32x4 + { + return vminq_f32(a, b); + } + + SIMD_FUNCTION + static inline auto max(f32x4 a, f32x4 b) -> f32x4 + { + return vmaxq_f32(a, b); + } +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/arm/float64.cpp b/libs/libSIMD/arm/float64.cpp new file mode 100644 index 0000000..98288a1 --- /dev/null +++ b/libs/libSIMD/arm/float64.cpp @@ -0,0 +1,252 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +#if ARM_NEON +#include +#include +#include + +// MARK: - Construction + +simd::float64_s::float64_s(double f0, double f1) +{ + double __attribute__((__aligned__(16))) data[4] = { f0, f1 }; + vst1q_f64(&m_values[0], vld1q_f64(data)); +} + +simd::float64_s::float64_s(f64x2 v) +{ + vst1q_f64(&m_values[0], v); +} + +auto simd::float64_s::constant(double v) -> float64 +{ + return float64(vdupq_n_f64(v)); +} + +// MARK: - Accessors + +auto simd::float64_s::operator[](int i) const -> double +{ + assert(i >= 0 && i < element_count); + return m_values[i]; +} + +auto simd::float64_s::set(int i, double f) -> float64& +{ + assert(i >= 0 && i < element_count); + m_values[i] = f; + return *this; +} + +auto simd::float64_s::vector() const -> f64x2 +{ + return vld1q_f64(&m_values[0]); +} + +auto simd::float64_s::set(f64x2 v) -> void +{ + vst1q_f64(&m_values[0], v); +} + +// MARK: - Operators + +auto simd::float64_s::operator+ (const float64& other) const -> float64 +{ + return float64(vaddq_f64(vector(), other.vector())); +} + +auto simd::float64_s::operator+ (double f) const -> float64 +{ + return *this + float64::constant(f); +} + +auto simd::float64_s::operator+=(const float64& other) -> float64& +{ + set((*this + other).vector()); + return *this; +} + +auto simd::float64_s::operator+=(double f) -> float64& +{ + set((*this + float64::constant(f)).vector()); + return *this; +} + +auto simd::float64_s::operator- (const float64& other) const -> float64 +{ + return float64(vsubq_f32(vector(), other.vector())); +} + +auto simd::float64_s::operator- (double f) const -> float64 +{ + return *this - float64::constant(f); +} + +auto simd::float64_s::operator-=(const float64& other) -> float64& +{ + set((*this - other).vector()); + return *this; +} + +auto simd::float64_s::operator-=(double f) -> float64& +{ + set((*this - float64::constant(f)).vector()); + return *this; +} + +auto simd::float64_s::operator* (const float64& other) const -> float64 +{ + return float64(vmulq_f64(vector(), other.vector())); +} + +auto simd::float64_s::operator* (double f) const -> float64 +{ + return *this * float64::constant(f); +} + +auto simd::float64_s::operator*=(const float64& other) -> float64& +{ + set((*this * other).vector()); + return *this; +} + +auto simd::float64_s::operator*=(double f) -> float64& +{ + set((*this * float64::constant(f)).vector()); + return *this; +} + +auto simd::float64_s::operator/ (const float64& other) const -> float64 +{ + return float64(vdivq_f64(vector(), other.vector())); +} + +auto simd::float64_s::operator/ (double f) const -> float64 +{ + return *this / float64::constant(f); +} + +auto simd::float64_s::operator/=(const float64& other) -> float64& +{ + set((*this / other).vector()); + return *this; +} + +auto simd::float64_s::operator/=(double f) -> float64& +{ + set((*this / float64::constant(f)).vector()); + return *this; +} + +// MARK: - Operations + +auto simd::float64_s::abs() const -> float64 +{ + return float64(vabsq_f64(vector())); +} + +auto simd::float64_s::round() const -> float64 +{ + return float64(vrndnq_f64(vector())); +} + +auto simd::float64_s::floor() const -> float64 +{ + return float64(vrndmq_f64(vector())); +} + +auto simd::float64_s::ceil() const -> float64 +{ + return float64(vrndpq_f64(vector())); +} + +auto simd::float64_s::pow(double exp) const -> float64 +{ + if (exp == 2.0) { + auto v = vector(); + return float64(vmulq_f64(v, v)); + } + else { + // TODO: Find an implementation that uses SSE/AVX in the future + double v[element_count]; + for (auto n = 0; n < element_count; ++n) { + v[n] = std::powf(m_values[n], m_values[n]); + } + return float64(v[0], v[1]); + } +} + +auto simd::float64_s::sqrt() const -> float64 +{ + return float64(vsqrtq_f64(vector())); +} + +auto simd::float64_s::sin() const -> float64 +{ + // TODO: Find an implementation that uses SSE/AVX in the future + double v[element_count]; + for (auto n = 0; n < element_count; ++n) { + v[n] = std::sin(m_values[n]); + } + return float64(v[0], v[1]); +} + +auto simd::float64_s::cos() const -> float64 +{ + // TODO: Find an implementation that uses SSE/AVX in the future + double v[element_count]; + for (auto n = 0; n < element_count; ++n) { + v[n] = std::cos(m_values[n]); + } + return float64(v[0], v[1]); +} + +auto simd::float64_s::rcp() const -> float64 +{ + auto recip = vrecpeq_f64(vector()); + recip = vmulq_f64(recip, vrecpsq_f64(recip, vector())); + return float64(recip); +} + +auto simd::float64_s::min(const float64 &other) const -> float64 +{ + return float64(vminq_f64(vector(), other.vector())); +} + +auto simd::float64_s::min(double f) const -> float64 +{ + return float64(vminq_f64(vector(), float64::constant(f).vector())); +} + +auto simd::float64_s::max(const float64 &other) const -> float64 +{ + return float64(vmaxq_f64(vector(), other.vector())); +} + +auto simd::float64_s::max(double f) const -> float64 +{ + return float64(vmaxq_f64(vector(), float64::constant(f).vector())); +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/arm/integer16.cpp b/libs/libSIMD/arm/integer16.cpp new file mode 100644 index 0000000..ec095ba --- /dev/null +++ b/libs/libSIMD/arm/integer16.cpp @@ -0,0 +1,337 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +#if ARM_NEON +#include +#include +#include + +// MARK: - Construction + +simd::integer16_s::integer16_s(std::int8_t i0, std::int8_t i1, std::int8_t i2, std::int8_t i3, std::int8_t i4, + std::int8_t i5, std::int8_t i6, std::int8_t i7, std::int8_t i8, std::int8_t i9, + std::int8_t i10, std::int8_t i11, std::int8_t i12, std::int8_t i13, std::int8_t i14, + std::int8_t i15) +{ + std::int8_t __attribute__((__aligned__(16))) data[16] = { + i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15 + }; + vst1q_s16(&m_values[0], vld1q_s8(data)); +} + +simd::integer16_s::integer16_s(std::int16_t i0, std::int16_t i1, std::int16_t i2, std::int16_t i3, std::int16_t i4, + std::int16_t i5, std::int16_t i6, std::int16_t i7) +{ + std::int16_t __attribute__((__aligned__(16))) data[8] = { + i0, i1, i2, i3, i4, i5, i6, i7 + }; + vst1q_s16(&m_values[0], vld1q_s16(data)); +} + +simd::integer16_s::integer16_s(std::int32_t i0, std::int32_t i1, std::int32_t i2, std::int32_t i3) +{ + std::int32_t __attribute__((__aligned__(16))) data[4] = { + i0, i1, i2, i3 + }; + vst1q_s16(&m_values[0], vld1q_s32(data)); +} + +simd::integer16_s::integer16_s(std::int64_t i0, std::int64_t i1) +{ + std::int64_t __attribute__((__aligned__(16))) data[2] = { + i0, i1 + }; + vst1q_s16(&m_values[0], vld1q_s64(data)); +} + +simd::integer16_s::integer16_s(i16x8 v) +{ + vst1q_s16(&m_values[0], v); +} + +auto simd::integer16_s::constant(std::int16_t v) -> integer16 +{ + return integer16(vdupq_n_s16(v)); +} + +// MARK: - Accessors + +auto simd::integer16_s::operator[](int i) const -> std::int16_t +{ + assert(i >= 0 && i < element_count); + return m_values[i]; +} + +auto simd::integer16_s::set(int i, std::int16_t v) -> integer16& +{ + assert(i >= 0 && i < element_count); + m_values[i] = v; + return *this; +} + +auto simd::integer16_s::vector() const -> i16x8 +{ + return vld1q_s16(&m_values[0]); +} + +auto simd::integer16_s::set(i16x8 v) -> void +{ + vst1q_s16(&m_values[0], v); +} + +// MARK: - Operators + +auto simd::integer16_s::operator+(const integer16& other) const -> integer16 +{ + return integer16(vaddq_s16(vector(), other.vector())); +} + +auto simd::integer16_s::operator+(std::int16_t i) const -> integer16 +{ + return *this + integer16::constant(i); +} + +auto simd::integer16_s::operator+=(const integer16& other) -> integer16& +{ + set(vaddq_s16(vector(), other.vector())); + return *this; +} + +auto simd::integer16_s::operator+=(std::int16_t i) -> integer16 & +{ + set(vaddq_s16(vector(), integer16::constant(i).vector())); + return *this; +} + +auto simd::integer16_s::operator-(const integer16& other) const -> integer16 +{ + return integer16(vsubq_s16(vector(), other.vector())); +} + +auto simd::integer16_s::operator-(std::int16_t i) const -> integer16 +{ + return *this - integer16::constant(i); +} + +auto simd::integer16_s::operator-=(const integer16& other) -> integer16& +{ + set(vsubq_s16(vector(), other.vector())); + return *this; +} + +auto simd::integer16_s::operator-=(std::int16_t i) -> integer16 & +{ + set(vsubq_s16(vector(), integer16::constant(i).vector())); + return *this; +} + +auto simd::integer16_s::operator*(const integer16& other) const -> integer16 +{ + return integer16(vmulq_s16(vector(), other.vector())); +} + +auto simd::integer16_s::operator*(std::int16_t i) const -> integer16 +{ + return *this * integer16::constant(i); +} + +auto simd::integer16_s::operator*=(const integer16& other) -> integer16& +{ + set((*this * other).vector()); + return *this; +} + +auto simd::integer16_s::operator*=(std::int16_t i) -> integer16 & +{ + set((*this * integer16::constant(i)).vector()); + return *this; +} + +auto simd::integer16::operator*(float f) const -> integer16 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer16_s::operator*=(float f) -> integer16& +{ + set((*this * f).vector()); + return *this; +} + +auto simd::integer16_s::operator/(const integer16& other) const -> integer16 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer16_s::operator/(std::int16_t i) const -> integer16 +{ + return *this / integer16::constant(i); +} + +auto simd::integer16_s::operator/=(const integer16& other) -> integer16& +{ + set((*this / other).vector()); + return *this; +} + +auto simd::integer16_s::operator/=(std::int16_t i) -> integer16 & +{ + set((*this / integer16::constant(i)).vector()); + return *this; +} + +auto simd::integer16_s::operator/(float f) const -> integer16 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer16_s::operator/=(float f) -> integer16& +{ + set((*this / f).vector()); + return *this; +} + +auto simd::integer16_s::operator&(const integer16& other) const -> integer16 +{ + return integer16(vandq_u8(vector(), other.vector())); +} + +auto simd::integer16_s::operator&(std::int16_t i) const -> integer16 +{ + return *this & integer16::constant(i); +} + +auto simd::integer16_s::operator&=(const integer16& other) -> integer16& +{ + set((*this & other).vector()); + return *this; +} + +auto simd::integer16_s::operator&=(std::int16_t i) -> integer16& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer16_s::operator|(const integer16& other) const -> integer16 +{ + return integer16(vorrq_u16(vector(), other.vector())); +} + +auto simd::integer16_s::operator|(std::int16_t i) const -> integer16 +{ + return *this | integer16::constant(i); +} + +auto simd::integer16_s::operator|=(const integer16& other) -> integer16& +{ + set((*this | other).vector()); + return *this; +} + +auto simd::integer16_s::operator|=(std::int16_t i) -> integer16& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer16_s::operator^(const integer16& other) const -> integer16 +{ + return integer16(veorq_u8(vector(), other.vector())); +} + +auto simd::integer16_s::operator^(std::int16_t i) const -> integer16 +{ + return *this ^ integer16::constant(i); +} + +auto simd::integer16_s::operator^=(const integer16& other) -> integer16& +{ + set((*this ^ other).vector()); + return *this; +} + +auto simd::integer16_s::operator^=(std::int16_t i) -> integer16& +{ + set((*this ^ i).vector()); + return *this; +} + +auto simd::integer16_s::operator~() const -> integer16 +{ + return integer16(veorq_u16(vector(), vdupq_n_u16(0xFFFF))); +} + +auto simd::integer16_s::operator<<(const integer16& other) const -> integer16 +{ + return integer16(vshlq_u8(vector(), other.vector())); +} + +auto simd::integer16_s::operator<<(std::int16_t i) const -> integer16 +{ + return *this << integer16::constant(i); +} + +auto simd::integer16_s::operator<<=(const integer16& other) -> integer16& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer16_s::operator<<=(std::int16_t i) -> integer16& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer16_s::operator>>(const integer16& other) const -> integer16 +{ + // TODO: Implement this + return *this; +} + +auto simd::integer16_s::operator>>(std::int16_t i) const -> integer16 +{ + return *this << integer16::constant(i); +} + +auto simd::integer16_s::operator>>=(const integer16& other) -> integer16& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer16_s::operator>>=(std::int16_t i) -> integer16& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer16_s::abs() const -> integer16 +{ + return integer16(vabsq_s16(vector())); +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/arm/integer32.cpp b/libs/libSIMD/arm/integer32.cpp new file mode 100644 index 0000000..b35e4ad --- /dev/null +++ b/libs/libSIMD/arm/integer32.cpp @@ -0,0 +1,337 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +#if ARM_NEON +#include +#include +#include + +// MARK: - Construction + +simd::integer32_s::integer32_s(std::int8_t i0, std::int8_t i1, std::int8_t i2, std::int8_t i3, std::int8_t i4, + std::int8_t i5, std::int8_t i6, std::int8_t i7, std::int8_t i8, std::int8_t i9, + std::int8_t i10, std::int8_t i11, std::int8_t i12, std::int8_t i13, std::int8_t i14, + std::int8_t i15) +{ + std::int8_t __attribute__((__aligned__(16))) data[16] = { + i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15 + }; + vst1q_s32(&m_values[0], vld1q_s8(data)); +} + +simd::integer32_s::integer32_s(std::int16_t i0, std::int16_t i1, std::int16_t i2, std::int16_t i3, std::int16_t i4, + std::int16_t i5, std::int16_t i6, std::int16_t i7) +{ + std::int16_t __attribute__((__aligned__(16))) data[8] = { + i0, i1, i2, i3, i4, i5, i6, i7 + }; + vst1q_s32(&m_values[0], vld1q_s16(data)); +} + +simd::integer32_s::integer32_s(std::int32_t i0, std::int32_t i1, std::int32_t i2, std::int32_t i3) +{ + std::int32_t __attribute__((__aligned__(16))) data[4] = { + i0, i1, i2, i3 + }; + vst1q_s32(&m_values[0], vld1q_s32(data)); +} + +simd::integer32_s::integer32_s(std::int64_t i0, std::int64_t i1) +{ + std::int64_t __attribute__((__aligned__(16))) data[2] = { + i0, i1 + }; + vst1q_s32(&m_values[0], vld1q_s64(data)); +} + +simd::integer32_s::integer32_s(i32x4 v) +{ + vst1q_s32(&m_values[0], v); +} + +auto simd::integer32_s::constant(std::int32_t v) -> integer32 +{ + return integer32(vdupq_n_s32(v)); +} + +// MARK: - Accessors + +auto simd::integer32_s::operator[](int i) const -> std::int32_t +{ + assert(i >= 0 && i < element_count); + return m_values[i]; +} + +auto simd::integer32_s::set(int i, std::int32_t v) -> integer32& +{ + assert(i >= 0 && i < element_count); + m_values[i] = v; + return *this; +} + +auto simd::integer32_s::vector() const -> i32x4 +{ + return vld1q_s32(&m_values[0]); +} + +auto simd::integer32_s::set(i32x4 v) -> void +{ + vst1q_s32(&m_values[0], v); +} + +// MARK: - Operators + +auto simd::integer32_s::operator+(const integer32& other) const -> integer32 +{ + return integer32(vaddq_s32(vector(), other.vector())); +} + +auto simd::integer32_s::operator+(std::int32_t i) const -> integer32 +{ + return *this + integer32::constant(i); +} + +auto simd::integer32_s::operator+=(const integer32& other) -> integer32& +{ + set(vaddq_s32(vector(), other.vector())); + return *this; +} + +auto simd::integer32_s::operator+=(std::int32_t i) -> integer32 & +{ + set(vaddq_s32(vector(), integer32::constant(i).vector())); + return *this; +} + +auto simd::integer32_s::operator-(const integer32& other) const -> integer32 +{ + return integer32(vsubq_s32(vector(), other.vector())); +} + +auto simd::integer32_s::operator-(std::int32_t i) const -> integer32 +{ + return *this - integer32::constant(i); +} + +auto simd::integer32_s::operator-=(const integer32& other) -> integer32& +{ + set(vsubq_s32(vector(), other.vector())); + return *this; +} + +auto simd::integer32_s::operator-=(std::int32_t i) -> integer32 & +{ + set(vsubq_s32(vector(), integer32::constant(i).vector())); + return *this; +} + +auto simd::integer32_s::operator*(const integer32& other) const -> integer32 +{ + return integer32(vmulq_s32(vector(), other.vector())); +} + +auto simd::integer32_s::operator*(std::int32_t i) const -> integer32 +{ + return *this * integer32::constant(i); +} + +auto simd::integer32_s::operator*=(const integer32& other) -> integer32& +{ + set((*this * other).vector()); + return *this; +} + +auto simd::integer32_s::operator*=(std::int32_t i) -> integer32 & +{ + set((*this * integer32::constant(i)).vector()); + return *this; +} + +auto simd::integer32::operator*(float f) const -> integer32 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer32_s::operator*=(float f) -> integer32& +{ + set((*this * f).vector()); + return *this; +} + +auto simd::integer32_s::operator/(const integer32& other) const -> integer32 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer32_s::operator/(std::int32_t i) const -> integer32 +{ + return *this / integer32::constant(i); +} + +auto simd::integer32_s::operator/=(const integer32& other) -> integer32& +{ + set((*this / other).vector()); + return *this; +} + +auto simd::integer32_s::operator/=(std::int32_t i) -> integer32 & +{ + set((*this / integer32::constant(i)).vector()); + return *this; +} + +auto simd::integer32_s::operator/(float f) const -> integer32 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer32_s::operator/=(float f) -> integer32& +{ + set((*this / f).vector()); + return *this; +} + +auto simd::integer32_s::operator&(const integer32& other) const -> integer32 +{ + return integer32(vandq_u8(vector(), other.vector())); +} + +auto simd::integer32_s::operator&(std::int32_t i) const -> integer32 +{ + return *this & integer32::constant(i); +} + +auto simd::integer32_s::operator&=(const integer32& other) -> integer32& +{ + set((*this & other).vector()); + return *this; +} + +auto simd::integer32_s::operator&=(std::int32_t i) -> integer32& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer32_s::operator|(const integer32& other) const -> integer32 +{ + return integer32(vorrq_u16(vector(), other.vector())); +} + +auto simd::integer32_s::operator|(std::int32_t i) const -> integer32 +{ + return *this | integer32::constant(i); +} + +auto simd::integer32_s::operator|=(const integer32& other) -> integer32& +{ + set((*this | other).vector()); + return *this; +} + +auto simd::integer32_s::operator|=(std::int32_t i) -> integer32& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer32_s::operator^(const integer32& other) const -> integer32 +{ + return integer32(veorq_u8(vector(), other.vector())); +} + +auto simd::integer32_s::operator^(std::int32_t i) const -> integer32 +{ + return *this ^ integer32::constant(i); +} + +auto simd::integer32_s::operator^=(const integer32& other) -> integer32& +{ + set((*this ^ other).vector()); + return *this; +} + +auto simd::integer32_s::operator^=(std::int32_t i) -> integer32& +{ + set((*this ^ i).vector()); + return *this; +} + +auto simd::integer32_s::operator~() const -> integer32 +{ + return integer32(veorq_u16(vector(), vdupq_n_u16(0xFFFF))); +} + +auto simd::integer32_s::operator<<(const integer32& other) const -> integer32 +{ + return integer32(vshlq_u8(vector(), other.vector())); +} + +auto simd::integer32_s::operator<<(std::int32_t i) const -> integer32 +{ + return *this << integer32::constant(i); +} + +auto simd::integer32_s::operator<<=(const integer32& other) -> integer32& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer32_s::operator<<=(std::int32_t i) -> integer32& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer32_s::operator>>(const integer32& other) const -> integer32 +{ + // TODO: Implement this + return *this; +} + +auto simd::integer32_s::operator>>(std::int32_t i) const -> integer32 +{ + return *this << integer32::constant(i); +} + +auto simd::integer32_s::operator>>=(const integer32& other) -> integer32& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer32_s::operator>>=(std::int32_t i) -> integer32& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer32_s::abs() const -> integer32 +{ + return integer32(vabsq_s32(vector())); +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/arm/integer64.cpp b/libs/libSIMD/arm/integer64.cpp new file mode 100644 index 0000000..3f4763a --- /dev/null +++ b/libs/libSIMD/arm/integer64.cpp @@ -0,0 +1,338 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +#if ARM_NEON +#include +#include +#include + +// MARK: - Construction + +simd::integer64_s::integer64_s(std::int8_t i0, std::int8_t i1, std::int8_t i2, std::int8_t i3, std::int8_t i4, + std::int8_t i5, std::int8_t i6, std::int8_t i7, std::int8_t i8, std::int8_t i9, + std::int8_t i10, std::int8_t i11, std::int8_t i12, std::int8_t i13, std::int8_t i14, + std::int8_t i15) +{ + std::int8_t __attribute__((__aligned__(16))) data[16] = { + i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15 + }; + vst1q_s64(&m_values[0], vld1q_s8(data)); +} + +simd::integer64_s::integer64_s(std::int16_t i0, std::int16_t i1, std::int16_t i2, std::int16_t i3, std::int16_t i4, + std::int16_t i5, std::int16_t i6, std::int16_t i7) +{ + std::int16_t __attribute__((__aligned__(16))) data[8] = { + i0, i1, i2, i3, i4, i5, i6, i7 + }; + vst1q_s64(&m_values[0], vld1q_s16(data)); +} + +simd::integer64_s::integer64_s(std::int32_t i0, std::int32_t i1, std::int32_t i2, std::int32_t i3) +{ + std::int32_t __attribute__((__aligned__(16))) data[4] = { + i0, i1, i2, i3 + }; + vst1q_s64(&m_values[0], vld1q_s32(data)); +} + +simd::integer64_s::integer64_s(std::int64_t i0, std::int64_t i1) +{ + std::int64_t __attribute__((__aligned__(16))) data[2] = { + i0, i1 + }; + vst1q_s64(&m_values[0], vld1q_s64(data)); +} + +simd::integer64_s::integer64_s(i64x2 v) +{ + vst1q_s64(&m_values[0], v); +} + +auto simd::integer64_s::constant(std::int64_t v) -> integer64 +{ + return integer64(vdupq_n_s64(v)); +} + +// MARK: - Accessors + +auto simd::integer64_s::operator[](int i) const -> std::int64_t +{ + assert(i >= 0 && i < element_count); + return m_values[i]; +} + +auto simd::integer64_s::set(int i, std::int64_t v) -> integer64& +{ + assert(i >= 0 && i < element_count); + m_values[i] = v; + return *this; +} + +auto simd::integer64_s::vector() const -> i64x2 +{ + return vld1q_s64(&m_values[0]); +} + +auto simd::integer64_s::set(i64x2 v) -> void +{ + vst1q_s64(&m_values[0], v); +} + +// MARK: - Operators + +auto simd::integer64_s::operator+(const integer64& other) const -> integer64 +{ + return integer64(vaddq_s64(vector(), other.vector())); +} + +auto simd::integer64_s::operator+(std::int64_t i) const -> integer64 +{ + return *this + integer64::constant(i); +} + +auto simd::integer64_s::operator+=(const integer64& other) -> integer64& +{ + set(vaddq_s64(vector(), other.vector())); + return *this; +} + +auto simd::integer64_s::operator+=(std::int64_t i) -> integer64 & +{ + set(vaddq_s64(vector(), integer64::constant(i).vector())); + return *this; +} + +auto simd::integer64_s::operator-(const integer64& other) const -> integer64 +{ + return integer64(vsubq_s64(vector(), other.vector())); +} + +auto simd::integer64_s::operator-(std::int64_t i) const -> integer64 +{ + return *this - integer64::constant(i); +} + +auto simd::integer64_s::operator-=(const integer64& other) -> integer64& +{ + set(vsubq_s64(vector(), other.vector())); + return *this; +} + +auto simd::integer64_s::operator-=(std::int64_t i) -> integer64 & +{ + set(vsubq_s64(vector(), integer64::constant(i).vector())); + return *this; +} + +auto simd::integer64_s::operator*(const integer64& other) const -> integer64 +{ + // TODO: Implement this... + return *this; +} + +auto simd::integer64_s::operator*(std::int64_t i) const -> integer64 +{ + return *this * integer64::constant(i); +} + +auto simd::integer64_s::operator*=(const integer64& other) -> integer64& +{ + set((*this * other).vector()); + return *this; +} + +auto simd::integer64_s::operator*=(std::int64_t i) -> integer64 & +{ + set((*this * integer64::constant(i)).vector()); + return *this; +} + +auto simd::integer64::operator*(float f) const -> integer64 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer64_s::operator*=(float f) -> integer64& +{ + set((*this * f).vector()); + return *this; +} + +auto simd::integer64_s::operator/(const integer64& other) const -> integer64 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer64_s::operator/(std::int64_t i) const -> integer64 +{ + return *this / integer64::constant(i); +} + +auto simd::integer64_s::operator/=(const integer64& other) -> integer64& +{ + set((*this / other).vector()); + return *this; +} + +auto simd::integer64_s::operator/=(std::int64_t i) -> integer64 & +{ + set((*this / integer64::constant(i)).vector()); + return *this; +} + +auto simd::integer64_s::operator/(float f) const -> integer64 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer64_s::operator/=(float f) -> integer64& +{ + set((*this / f).vector()); + return *this; +} + +auto simd::integer64_s::operator&(const integer64& other) const -> integer64 +{ + return integer64(vandq_u8(vector(), other.vector())); +} + +auto simd::integer64_s::operator&(std::int64_t i) const -> integer64 +{ + return *this & integer64::constant(i); +} + +auto simd::integer64_s::operator&=(const integer64& other) -> integer64& +{ + set((*this & other).vector()); + return *this; +} + +auto simd::integer64_s::operator&=(std::int64_t i) -> integer64& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer64_s::operator|(const integer64& other) const -> integer64 +{ + return integer64(vorrq_u16(vector(), other.vector())); +} + +auto simd::integer64_s::operator|(std::int64_t i) const -> integer64 +{ + return *this | integer64::constant(i); +} + +auto simd::integer64_s::operator|=(const integer64& other) -> integer64& +{ + set((*this | other).vector()); + return *this; +} + +auto simd::integer64_s::operator|=(std::int64_t i) -> integer64& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer64_s::operator^(const integer64& other) const -> integer64 +{ + return integer64(veorq_u8(vector(), other.vector())); +} + +auto simd::integer64_s::operator^(std::int64_t i) const -> integer64 +{ + return *this ^ integer64::constant(i); +} + +auto simd::integer64_s::operator^=(const integer64& other) -> integer64& +{ + set((*this ^ other).vector()); + return *this; +} + +auto simd::integer64_s::operator^=(std::int64_t i) -> integer64& +{ + set((*this ^ i).vector()); + return *this; +} + +auto simd::integer64_s::operator~() const -> integer64 +{ + return integer64(veorq_u16(vector(), vdupq_n_u16(0xFFFF))); +} + +auto simd::integer64_s::operator<<(const integer64& other) const -> integer64 +{ + return integer64(vshlq_u8(vector(), other.vector())); +} + +auto simd::integer64_s::operator<<(std::int64_t i) const -> integer64 +{ + return *this << integer64::constant(i); +} + +auto simd::integer64_s::operator<<=(const integer64& other) -> integer64& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer64_s::operator<<=(std::int64_t i) -> integer64& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer64_s::operator>>(const integer64& other) const -> integer64 +{ + // TODO: Implement this + return *this; +} + +auto simd::integer64_s::operator>>(std::int64_t i) const -> integer64 +{ + return *this << integer64::constant(i); +} + +auto simd::integer64_s::operator>>=(const integer64& other) -> integer64& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer64_s::operator>>=(std::int64_t i) -> integer64& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer64_s::abs() const -> integer64 +{ + return integer64(vabsq_s64(vector())); +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/arm/integer8.cpp b/libs/libSIMD/arm/integer8.cpp new file mode 100644 index 0000000..22ef0ef --- /dev/null +++ b/libs/libSIMD/arm/integer8.cpp @@ -0,0 +1,292 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +#if ARM_NEON +#include +#include +#include + +// MARK: - Construction + +simd::integer8_s::integer8_s(std::int8_t i0, std::int8_t i1, std::int8_t i2, std::int8_t i3, std::int8_t i4, + std::int8_t i5, std::int8_t i6, std::int8_t i7, std::int8_t i8, std::int8_t i9, + std::int8_t i10, std::int8_t i11, std::int8_t i12, std::int8_t i13, std::int8_t i14, + std::int8_t i15) +{ + std::int8_t __attribute__((__aligned__(16))) data[16] = { + i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15 + }; + vst1q_s8(&m_values[0], vld1q_s8(data)); +} + +simd::integer8_s::integer8_s(std::int16_t i0, std::int16_t i1, std::int16_t i2, std::int16_t i3, std::int16_t i4, + std::int16_t i5, std::int16_t i6, std::int16_t i7) +{ + std::int16_t __attribute__((__aligned__(16))) data[8] = { + i0, i1, i2, i3, i4, i5, i6, i7 + }; + vst1q_s8(&m_values[0], vld1q_s16(data)); +} + +simd::integer8_s::integer8_s(std::int32_t i0, std::int32_t i1, std::int32_t i2, std::int32_t i3) +{ + std::int32_t __attribute__((__aligned__(16))) data[4] = { + i0, i1, i2, i3 + }; + vst1q_s8(&m_values[0], vld1q_s32(data)); +} + +simd::integer8_s::integer8_s(std::int64_t i0, std::int64_t i1) +{ + std::int64_t __attribute__((__aligned__(16))) data[2] = { + i0, i1 + }; + vst1q_s8(&m_values[0], vld1q_s64(data)); +} + +simd::integer8_s::integer8_s(i8x16 v) +{ + vst1q_s8(&m_values[0], v); +} + +auto simd::integer8_s::constant(std::int8_t v) -> integer8 +{ + return integer8(vdupq_n_s8(v)); +} + +// MARK: - Accessors + +auto simd::integer8_s::operator[](int i) const -> std::int8_t +{ + assert(i >= 0 && i < element_count); + return m_values[i]; +} + +auto simd::integer8_s::set(int i, std::int8_t v) -> integer8& +{ + assert(i >= 0 && i < element_count); + m_values[i] = v; + return *this; +} + +auto simd::integer8_s::vector() const -> i8x16 +{ + return vld1q_s8(&m_values[0]); +} + +auto simd::integer8_s::set(i8x16 v) -> void +{ + vst1q_s8(&m_values[0], v); +} + +// MARK: - Operators + +auto simd::integer8_s::operator+(const integer8& other) const -> integer8 +{ + return integer8(vaddq_s8(vector(), other.vector())); +} + +auto simd::integer8_s::operator+(std::int8_t i) const -> integer8 +{ + return *this + integer8::constant(i); +} + +auto simd::integer8_s::operator+=(const integer8& other) -> integer8& +{ + set(vaddq_s8(vector(), other.vector())); + return *this; +} + +auto simd::integer8_s::operator+=(std::int8_t i) -> integer8 & +{ + set(vaddq_s8(vector(), integer8::constant(i).vector())); + return *this; +} + +auto simd::integer8_s::operator-(const integer8& other) const -> integer8 +{ + return integer8(vsubq_s8(vector(), other.vector())); +} + +auto simd::integer8_s::operator-(std::int8_t i) const -> integer8 +{ + return *this - integer8::constant(i); +} + +auto simd::integer8_s::operator-=(const integer8& other) -> integer8& +{ + set(vsubq_s8(vector(), other.vector())); + return *this; +} + +auto simd::integer8_s::operator-=(std::int8_t i) -> integer8 & +{ + set(vsubq_s8(vector(), integer8::constant(i).vector())); + return *this; +} + +auto simd::integer8_s::operator*(const integer8& other) const -> integer8 +{ + return integer8(vmulq_s8(vector(), other.vector())); +} + +auto simd::integer8_s::operator*(std::int8_t i) const -> integer8 +{ + return *this * integer8::constant(i); +} + +auto simd::integer8_s::operator*=(const integer8& other) -> integer8& +{ + set((*this * other).vector()); + return *this; +} + +auto simd::integer8_s::operator*=(std::int8_t i) -> integer8 & +{ + set((*this * integer8::constant(i)).vector()); + return *this; +} + +auto simd::integer8::operator*(float f) const -> integer8 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer8_s::operator*=(float f) -> integer8& +{ + set((*this * f).vector()); + return *this; +} + +auto simd::integer8_s::operator/(const integer8& other) const -> integer8 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer8_s::operator/(std::int8_t i) const -> integer8 +{ + return *this / integer8::constant(i); +} + +auto simd::integer8_s::operator/=(const integer8& other) -> integer8& +{ + set((*this / other).vector()); + return *this; +} + +auto simd::integer8_s::operator/=(std::int8_t i) -> integer8 & +{ + set((*this / integer8::constant(i)).vector()); + return *this; +} + +auto simd::integer8_s::operator/(float f) const -> integer8 +{ + // TODO: Implement this. + return *this; +} + +auto simd::integer8_s::operator/=(float f) -> integer8& +{ + set((*this / f).vector()); + return *this; +} + +auto simd::integer8_s::operator&(const integer8& other) const -> integer8 +{ + return integer8(vandq_u8(vector(), other.vector())); +} + +auto simd::integer8_s::operator&(std::int8_t i) const -> integer8 +{ + return *this & integer8::constant(i); +} + +auto simd::integer8_s::operator&=(const integer8& other) -> integer8& +{ + set((*this & other).vector()); + return *this; +} + +auto simd::integer8_s::operator&=(std::int8_t i) -> integer8& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer8_s::operator|(const integer8& other) const -> integer8 +{ + return integer8(vorrq_u8(vector(), other.vector())); +} + +auto simd::integer8_s::operator|(std::int8_t i) const -> integer8 +{ + return *this | integer8::constant(i); +} + +auto simd::integer8_s::operator|=(const integer8& other) -> integer8& +{ + set((*this | other).vector()); + return *this; +} + +auto simd::integer8_s::operator|=(std::int8_t i) -> integer8& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer8_s::operator^(const integer8& other) const -> integer8 +{ + return integer8(veorq_u8(vector(), other.vector())); +} + +auto simd::integer8_s::operator^(std::int8_t i) const -> integer8 +{ + return *this ^ integer8::constant(i); +} + +auto simd::integer8_s::operator^=(const integer8& other) -> integer8& +{ + set((*this ^ other).vector()); + return *this; +} + +auto simd::integer8_s::operator^=(std::int8_t i) -> integer8& +{ + set((*this ^ i).vector()); + return *this; +} + +auto simd::integer8_s::operator~() const -> integer8 +{ + return integer8(veorq_u8(vector(), vdupq_n_u8(0xFF))); +} + +auto simd::integer8_s::abs() const -> integer8 +{ + return integer8(vabsq_s8(vector())); +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/cpu.cpp b/libs/libSIMD/cpu.cpp new file mode 100644 index 0000000..38e5a20 --- /dev/null +++ b/libs/libSIMD/cpu.cpp @@ -0,0 +1,105 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +// MARK: - Static Information + +static struct +{ + std::string vendor { "Unknown" }; + std::string name { "Unknown" }; + struct { + bool mmx { false }; + bool sse { false }; + bool sse2 { false }; + bool sse3 { false }; + bool ssse3 { false }; + bool sse4_1 { false }; + bool sse4_2 { false }; + bool sse4_a { false }; + bool avx { false }; + bool avx2 { false }; + bool neon { false }; + } features; +} s_cpu_info; + +// MARK: - Initialisation and Setup + + +#if INTEL_SIMD +#include + +__attribute__((constructor)) +static auto simd_initialise_cpu_info() -> void +{ + s_cpu_info.vendor = "???"; // TODO: Find the vendor + s_cpu_info.name = "???"; // TODO: Find the name of the CPU. + + std::uint32_t info[4]; + __cpuid(0x00000000, info[0], info[1], info[2], info[3]); + auto nIds = info[0]; + + __cpuid(0x80000000, info[0], info[1], info[2], info[3]); + auto nExIds = info[0]; + + // Features + if (nIds & 0x00000001) { + __cpuid(0x00000001, info[0], info[1], info[2], info[3]); + s_cpu_info.features.mmx = (info[3] & ((std::uint32_t)1 << 23)) != 0; + s_cpu_info.features.sse = (info[3] & ((std::uint32_t)1 << 25)) != 0; + s_cpu_info.features.sse2 = (info[3] & ((std::uint32_t)1 << 26)) != 0; + s_cpu_info.features.sse3 = (info[2] & ((std::uint32_t)1 << 0)) != 0; + s_cpu_info.features.ssse3 = (info[2] & ((std::uint32_t)1 << 9)) != 0; + s_cpu_info.features.sse4_1 = (info[2] & ((std::uint32_t)1 << 19)) != 0; + s_cpu_info.features.sse4_2 = (info[2] & ((std::uint32_t)1 << 20)) != 0; + s_cpu_info.features.avx = (info[2] & ((std::uint32_t)1 << 28)) != 0; + } + + if (nIds & 0x00000007) { + __cpuid(0x00000007, info[0], info[1], info[2], info[3]); + s_cpu_info.features.avx2 = (info[2] & ((std::uint32_t)1 << 28)) != 0; + } + + if (nExIds & 0x80000001) { + __cpuid(0x80000001, info[0], info[1], info[2], info[3]); + s_cpu_info.features.sse4_a = (info[2] & ((std::uint32_t)1 << 6)) != 0; + } +} + +#elif APPLE_SILICON +__attribute__((constructor)) +static auto simd_initialise_cpu_info() -> void +{ + s_cpu_info.vendor = "Apple Silcon"; + s_cpu_info.name = "???"; // TODO: Find the name of the CPU. + s_cpu_info.features.neon = true; +} + +#elif ARM +__attribute__((constructor)) +static auto simd_initialise_cpu_info() -> void +{ + s_cpu_info.vendor = "ARM"; + s_cpu_info.name = "???"; // TODO: Find the name of the CPU. + s_cpu_info.features.neon = true; +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/cpu.hpp b/libs/libSIMD/cpu.hpp new file mode 100644 index 0000000..f24f259 --- /dev/null +++ b/libs/libSIMD/cpu.hpp @@ -0,0 +1,36 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +namespace simd::cpu +{ + enum class feature + { + SSE, AVX, NEON + }; + + auto vendor() -> std::string; + auto name() -> std::string; + + auto has(enum feature f) -> bool; +} \ No newline at end of file diff --git a/libs/libSIMD/float32.hpp b/libs/libSIMD/float32.hpp new file mode 100644 index 0000000..6696c80 --- /dev/null +++ b/libs/libSIMD/float32.hpp @@ -0,0 +1,135 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace simd +{ + struct float32_s; + typedef struct float32_s float32; + + struct float32_s + { + // MARK: - Construction + + explicit inline float32_s(float f0 = 0.f, float f1 = 0.f, float f2 = 0.f, float f3 = 0.f) + { + m_values = vector(f0, f1, f2, f3); + } + + explicit inline float32_s(f32x4 v) + : m_values(v) + {} + + inline float32_s(const float32&) = default; + inline float32_s(float32&&) = default; + auto operator=(const float32&) -> float32& = default; + + static inline auto constant(float v) -> float32 + { + return float32(single_value_vector(v)); + } + + static inline auto lower_upper_merge(const float32& lower, const float32& upper) -> float32 + { + return float32(vector_shuffle_lower_higher(lower.m_values, upper.m_values)); + } + + static inline auto lower(const float32& lower) -> float32 { return float32(vector_slice_lower(lower.m_values)); } + static inline auto upper(const float32& upper) -> float32 { return float32(vector_slice_upper(upper.m_values)); } + + // MARK: - Accessors + inline auto operator[] (int i) const -> float { return m_values[i]; }; + inline auto set(int i, float v) -> float32& { m_values[i] = v; return *this; } + + // MARK: - Operators + inline auto operator==(const float32& other) const -> bool + { + return (m_values[0] == other.m_values[0]) + && (m_values[1] == other.m_values[1]) + && (m_values[2] == other.m_values[2]) + && (m_values[3] == other.m_values[3]); + } + + inline auto operator+ (const float32& other) const -> float32 { return float32(simd::add(m_values, other.m_values)); } + inline auto operator+ (float f) const -> float32 { return float32(simd::add(m_values, simd::single_value_vector(f))); } + inline auto operator+=(const float32& other) -> float32& { m_values = simd::add(m_values, other.m_values); return *this; } + inline auto operator+=(float f) -> float32& { m_values = simd::add(m_values, simd::single_value_vector(f)); return *this; } + + inline auto operator- (const float32& other) const -> float32 { return float32(simd::sub(m_values, other.m_values)); } + inline auto operator- (float f) const -> float32 { return float32(simd::sub(m_values, simd::single_value_vector(f))); } + inline auto operator-=(const float32& other) -> float32& { m_values = simd::sub(m_values, other.m_values); return *this; } + inline auto operator-=(float f) -> float32& { m_values = simd::sub(m_values, simd::single_value_vector(f)); return *this; } + + inline auto operator* (const float32& other) const -> float32 { return float32(simd::mul(m_values, other.m_values)); } + inline auto operator* (float f) const -> float32 { return float32(simd::mul(m_values, simd::single_value_vector(f))); } + inline auto operator*=(const float32& other) -> float32& { m_values = simd::mul(m_values, other.m_values); return *this; } + inline auto operator*=(float f) -> float32& { m_values = simd::mul(m_values, simd::single_value_vector(f)); return *this; } + + inline auto operator/ (const float32& other) const -> float32 { return float32(simd::div(m_values, other.m_values)); } + inline auto operator/ (float f) const -> float32 { return float32(simd::div(m_values, simd::single_value_vector(f))); } + inline auto operator/=(const float32& other) -> float32& { m_values =simd::div(m_values, other.m_values); return *this; } + inline auto operator/=(float f) -> float32& { m_values = simd::div(m_values, simd::single_value_vector(f)); return *this; } + + [[nodiscard]] inline auto abs() const -> float32 { return float32(simd::abs(m_values)); } + [[nodiscard]] inline auto round() const -> float32 { return float32(simd::round(m_values)); } + [[nodiscard]] inline auto floor() const -> float32 { return float32(simd::floor(m_values)); } + [[nodiscard]] inline auto ceil() const -> float32 { return float32(simd::ceil(m_values)); } + [[nodiscard]] inline auto pow(float exp) const -> float32 { return float32(simd::pow(m_values, exp)); } + [[nodiscard]] inline auto sqrt() const -> float32 { return float32(simd::sqrt(m_values)); } + [[nodiscard]] inline auto rsqrt() const -> float32 { return float32(simd::rsqrt(m_values)); } + [[nodiscard]] inline auto sin() const -> float32 { return float32(simd::sin(m_values)); } + [[nodiscard]] inline auto cos() const -> float32 { return float32(simd::cos(m_values)); } + [[nodiscard]] inline auto rcp() const -> float32 { return float32(simd::rcp(m_values)); } + [[nodiscard]] inline auto min(const float32& other) const -> float32 { return float32(simd::min(m_values, other.m_values)); } + [[nodiscard]] inline auto min(float f) const -> float32 { return float32(simd::min(m_values, simd::single_value_vector(f))); } + [[nodiscard]] inline auto max(const float32& other) const -> float32 { return float32(simd::max(m_values, other.m_values)); } + [[nodiscard]] inline auto max(float f) const -> float32 { return float32(simd::max(m_values, simd::single_value_vector(f))); } + + [[nodiscard]] inline auto upper() const -> float32 { return float32(simd::vector_slice_upper(m_values)); } + [[nodiscard]] inline auto lower() const -> float32 { return float32(simd::vector_slice_lower(m_values)); } + [[nodiscard]] inline auto swapped() const -> float32 { return float32(simd::swap_lower_upper(m_values)); } + [[nodiscard]] inline auto reversed() const -> float32 { return float32(simd::reverse(m_values)); } + + inline auto set_lower(const float32& lower) -> void + { + m_values[0] = lower[0]; m_values[1] = lower[1]; + } + + inline auto set_upper(const float32& upper) -> void + { + m_values[2] = upper[2]; m_values[3] = upper[3]; + } + + friend struct integer16_s; + friend struct integer32_s; + friend struct integer64_s; + friend struct float32_s; + friend struct double64_s; + + private: + f32x4 m_values; + }; +} \ No newline at end of file diff --git a/libs/libSIMD/float64.hpp b/libs/libSIMD/float64.hpp new file mode 100644 index 0000000..ffd102b --- /dev/null +++ b/libs/libSIMD/float64.hpp @@ -0,0 +1,119 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace simd +{ + struct float64_s; + typedef struct float64_s float64; + + struct float64_s + { + static constexpr std::int32_t element_count = 2; + + // MARK: - Construction + + /** + * Creates a 128-bit SIMD Vector from 3 single precision floats. + * @param f0 Element 1 + * @param f1 Element 2 + */ + explicit float64_s(double f0 = 0.f, double f1 = 0.f); + + explicit float64_s(f64x2 v); + + float64_s(const float64_s&) = default; + float64_s(float64_s&&) = default; + auto operator=(const float64&) -> float64& = default; + + /** + * Creates a 128-bit SIMD Vector using one single precision float for each of the elements. + * @param v Value to duplicate into all elements. + */ + static auto constant(double v) -> float64; + + static auto lower_upper_merge(const float64& lower, const float64& upper) -> float64; + static auto lower(const float64& lower) -> float64; + static auto upper(const float64& upper) -> float64; + + // MARK: - Accessors + auto operator[] (int i) const -> double; + auto set(int i, double v) -> float64&; + + // MARK: - Operators + auto operator+ (const float64& other) const -> float64; + auto operator+ (double f) const -> float64; + auto operator+=(const float64& other) -> float64&; + auto operator+=(double f) -> float64&; + + auto operator- (const float64& other) const -> float64; + auto operator- (double f) const -> float64; + auto operator-=(const float64& other) -> float64&; + auto operator-=(double f) -> float64&; + + auto operator* (const float64& other) const -> float64; + auto operator* (double f) const -> float64; + auto operator*=(const float64& other) -> float64&; + auto operator*=(double f) -> float64&; + + auto operator/ (const float64& other) const -> float64; + auto operator/ (double f) const -> float64; + auto operator/=(const float64& other) -> float64&; + auto operator/=(double f) -> float64&; + + [[nodiscard]] auto abs() const -> float64; + [[nodiscard]] auto round() const -> float64; + [[nodiscard]] auto floor() const -> float64; + [[nodiscard]] auto ceil() const -> float64; + [[nodiscard]] auto pow(double exp) const -> float64; + [[nodiscard]] auto sqrt() const -> float64; + [[nodiscard]] auto sin() const -> float64; + [[nodiscard]] auto cos() const -> float64; + [[nodiscard]] auto rcp() const -> float64; + [[nodiscard]] auto min(const float64& other) const -> float64; + [[nodiscard]] auto min(double f) const -> float64; + [[nodiscard]] auto max(const float64& other) const -> float64; + [[nodiscard]] auto max(double f) const -> float64; + + [[nodiscard]] auto upper() const -> float64; + [[nodiscard]] auto lower() const -> float64; + [[nodiscard]] auto swapped() const -> float64; + [[nodiscard]] auto reversed() const -> float64; + + auto set_lower(const float64& lower) -> void; + auto set_upper(const float64& upper) -> void; + + friend struct integer16_s; + friend struct integer32_s; + friend struct integer64_s; + friend struct float64_s; + friend struct float32_s; + + private: + double __attribute__((__aligned__(16))) m_values[element_count] { 0 }; + + [[nodiscard]] auto vector() const -> f64x2; + auto set(f64x2 v) -> void; + }; +} \ No newline at end of file diff --git a/libs/libSIMD/integer16.hpp b/libs/libSIMD/integer16.hpp new file mode 100644 index 0000000..e83d911 --- /dev/null +++ b/libs/libSIMD/integer16.hpp @@ -0,0 +1,176 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +namespace simd +{ + struct integer16_s; + typedef struct integer16_s integer16; + + struct integer16_s + { + static constexpr std::int32_t element_count = 8; + + // MARK: - Construction + + /** + * Creates a 128-bit SIMD Vector from 16 8-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + * @param i4 Element 5 + * @param i5 Element 6 + * @param i6 Element 7 + * @param i7 Element 8 + * @param i8 Element 9 + * @param i9 Element 10 + * @param i10 Element 11 + * @param i11 Element 12 + * @param i12 Element 13 + * @param i13 Element 14 + * @param i14 Element 15 + * @param i16 Element 16 + */ + explicit integer16_s( + std::int8_t i0 = 0, std::int8_t i1 = 0, std::int8_t i2 = 0, std::int8_t i3 = 0, + std::int8_t i4 = 0, std::int8_t i5 = 0, std::int8_t i6 = 0, std::int8_t i7 = 0, + std::int8_t i8 = 0, std::int8_t i9 = 0, std::int8_t i10 = 0, std::int8_t i11 = 0, + std::int8_t i12 = 0, std::int8_t i13 = 0, std::int8_t i14 = 0, std::int8_t i15 = 0 + ); + + /** + * Creates a 128-bit SIMD Vector from 8 16-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + * @param i4 Element 5 + * @param i5 Element 6 + * @param i6 Element 7 + * @param i7 Element 8 + */ + explicit integer16_s( + std::int16_t i0 = 0, std::int16_t i1 = 0, std::int16_t i2 = 0, std::int16_t i3 = 0, + std::int16_t i4 = 0, std::int16_t i5 = 0, std::int16_t i6 = 0, std::int16_t i7 = 0 + ); + + /** + * Creates a 128-bit SIMD Vector from 4 32-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + */ + explicit integer16_s(std::int32_t i0 = 0, std::int32_t i1 = 0, std::int32_t i2 = 0, std::int32_t i3 = 0); + + /** + * Creates a 128-bit SIMD Vector from 2 64-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + */ + explicit integer16_s(std::int64_t i0 = 0, std::int64_t i1 = 0); + + explicit integer16_s(i16x8 v); + + integer16_s(const integer16&) = default; + integer16_s(integer16&&) = default; + auto operator=(const integer16&) -> integer16& = default; + + /** + * Creates a 128-bit SIMD Vector using a single 8-bit integer for each of the elements. + * @param v Value to duplicate into all elements. + */ + static auto constant(std::int16_t v) -> integer16_s; + + // MARK: - Accessors + auto operator[] (int i) const -> std::int16_t; + auto set(int i, std::int16_t v) -> integer16_s&; + + // MARK: - Operators + auto operator+ (const integer16& other) const -> integer16_s; + auto operator+ (std::int16_t i) const -> integer16; + auto operator+=(const integer16& other) -> integer16&; + auto operator+=(std::int16_t i) -> integer16&; + + auto operator- (const integer16& other) const -> integer16; + auto operator- (std::int16_t i) const -> integer16; + auto operator-=(const integer16& other) -> integer16&; + auto operator-=(std::int16_t i) -> integer16&; + + auto operator* (const integer16& other) const -> integer16; + auto operator* (std::int16_t i) const -> integer16; + auto operator* (float f) const -> integer16; + auto operator*=(const integer16& other) -> integer16&; + auto operator*=(std::int16_t i) -> integer16&; + auto operator*=(float f) -> integer16&; + + auto operator/ (const integer16& other) const -> integer16; + auto operator/ (std::int16_t i) const -> integer16; + auto operator/ (float f) const -> integer16; + auto operator/=(const integer16& other) -> integer16&; + auto operator/=(std::int16_t i) -> integer16&; + auto operator/=(float f) -> integer16&; + + auto operator& (const integer16& other) const -> integer16; + auto operator& (std::int16_t i) const -> integer16; + auto operator&=(const integer16& other) -> integer16&; + auto operator&=(std::int16_t i) -> integer16&; + + auto operator| (const integer16& other) const -> integer16; + auto operator| (std::int16_t i) const -> integer16; + auto operator|=(const integer16& other) -> integer16&; + auto operator|=(std::int16_t i) -> integer16&; + + auto operator^ (const integer16& other) const -> integer16; + auto operator^ (std::int16_t i) const -> integer16; + auto operator^=(const integer16& other) -> integer16&; + auto operator^=(std::int16_t i) -> integer16&; + + auto operator<<(const integer16& other) const -> integer16; + auto operator<<(std::int16_t i) const -> integer16; + auto operator<<=(const integer16& other) -> integer16&; + auto operator<<=(std::int16_t i) -> integer16&; + + auto operator>>(const integer16& other) const -> integer16; + auto operator>>(std::int16_t i) const -> integer16; + auto operator>>=(const integer16& other) -> integer16&; + auto operator>>=(std::int16_t i) -> integer16&; + + auto operator~() const -> integer16; + + [[nodiscard]] auto abs() const -> integer16; + + friend struct integer8_s; + friend struct integer32_s; + friend struct integer64_s; + friend struct float_s; + friend struct double_s; + + private: + std::int16_t __attribute__((__aligned__(16))) m_values[element_count]; + + [[nodiscard]] auto vector() const -> i16x8; + auto set(i16x8 v) -> void; + }; +} \ No newline at end of file diff --git a/libs/libSIMD/integer32.hpp b/libs/libSIMD/integer32.hpp new file mode 100644 index 0000000..3559868 --- /dev/null +++ b/libs/libSIMD/integer32.hpp @@ -0,0 +1,176 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +namespace simd +{ + struct integer32_s; + typedef struct integer32_s integer32; + + struct integer32_s + { + static constexpr std::int32_t element_count = 4; + + // MARK: - Construction + + /** + * Creates a 128-bit SIMD Vector from 16 8-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + * @param i4 Element 5 + * @param i5 Element 6 + * @param i6 Element 7 + * @param i7 Element 8 + * @param i8 Element 9 + * @param i9 Element 10 + * @param i10 Element 11 + * @param i11 Element 12 + * @param i12 Element 13 + * @param i13 Element 14 + * @param i14 Element 15 + * @param i16 Element 16 + */ + explicit integer32_s( + std::int8_t i0 = 0, std::int8_t i1 = 0, std::int8_t i2 = 0, std::int8_t i3 = 0, + std::int8_t i4 = 0, std::int8_t i5 = 0, std::int8_t i6 = 0, std::int8_t i7 = 0, + std::int8_t i8 = 0, std::int8_t i9 = 0, std::int8_t i10 = 0, std::int8_t i11 = 0, + std::int8_t i12 = 0, std::int8_t i13 = 0, std::int8_t i14 = 0, std::int8_t i15 = 0 + ); + + /** + * Creates a 128-bit SIMD Vector from 8 16-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + * @param i4 Element 5 + * @param i5 Element 6 + * @param i6 Element 7 + * @param i7 Element 8 + */ + explicit integer32_s( + std::int16_t i0 = 0, std::int16_t i1 = 0, std::int16_t i2 = 0, std::int16_t i3 = 0, + std::int16_t i4 = 0, std::int16_t i5 = 0, std::int16_t i6 = 0, std::int16_t i7 = 0 + ); + + /** + * Creates a 128-bit SIMD Vector from 4 32-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + */ + explicit integer32_s(std::int32_t i0 = 0, std::int32_t i1 = 0, std::int32_t i2 = 0, std::int32_t i3 = 0); + + /** + * Creates a 128-bit SIMD Vector from 2 64-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + */ + explicit integer32_s(std::int64_t i0 = 0, std::int64_t i1 = 0); + + explicit integer32_s(i32x4 v); + + integer32_s(const integer32&) = default; + integer32_s(integer32&&) = default; + auto operator=(const integer32&) -> integer32& = default; + + /** + * Creates a 128-bit SIMD Vector using a single 32-bit integer for each of the elements. + * @param v Value to duplicate into all elements. + */ + static auto constant(std::int32_t v) -> integer32_s; + + // MARK: - Accessors + auto operator[] (int i) const -> std::int32_t; + auto set(int i, std::int32_t v) -> integer32_s&; + + // MARK: - Operators + auto operator+ (const integer32& other) const -> integer32_s; + auto operator+ (std::int32_t i) const -> integer32; + auto operator+=(const integer32& other) -> integer32&; + auto operator+=(std::int32_t i) -> integer32&; + + auto operator- (const integer32& other) const -> integer32; + auto operator- (std::int32_t i) const -> integer32; + auto operator-=(const integer32& other) -> integer32&; + auto operator-=(std::int32_t i) -> integer32&; + + auto operator* (const integer32& other) const -> integer32; + auto operator* (std::int32_t i) const -> integer32; + auto operator* (float f) const -> integer32; + auto operator*=(const integer32& other) -> integer32&; + auto operator*=(std::int32_t i) -> integer32&; + auto operator*=(float f) -> integer32&; + + auto operator/ (const integer32& other) const -> integer32; + auto operator/ (std::int32_t i) const -> integer32; + auto operator/ (float f) const -> integer32; + auto operator/=(const integer32& other) -> integer32&; + auto operator/=(std::int32_t i) -> integer32&; + auto operator/=(float f) -> integer32&; + + auto operator& (const integer32& other) const -> integer32; + auto operator& (std::int32_t i) const -> integer32; + auto operator&=(const integer32& other) -> integer32&; + auto operator&=(std::int32_t i) -> integer32&; + + auto operator| (const integer32& other) const -> integer32; + auto operator| (std::int32_t i) const -> integer32; + auto operator|=(const integer32& other) -> integer32&; + auto operator|=(std::int32_t i) -> integer32&; + + auto operator^ (const integer32& other) const -> integer32; + auto operator^ (std::int32_t i) const -> integer32; + auto operator^=(const integer32& other) -> integer32&; + auto operator^=(std::int32_t i) -> integer32&; + + auto operator<<(const integer32& other) const -> integer32; + auto operator<<(std::int32_t i) const -> integer32; + auto operator<<=(const integer32& other) -> integer32&; + auto operator<<=(std::int32_t i) -> integer32&; + + auto operator>>(const integer32& other) const -> integer32; + auto operator>>(std::int32_t i) const -> integer32; + auto operator>>=(const integer32& other) -> integer32&; + auto operator>>=(std::int32_t i) -> integer32&; + + auto operator~() const -> integer32; + + [[nodiscard]] auto abs() const -> integer32; + + friend struct integer8_s; + friend struct integer16_s; + friend struct integer64_s; + friend struct float_s; + friend struct double_s; + + private: + std::int32_t __attribute__((__aligned__(16))) m_values[element_count]; + + [[nodiscard]] auto vector() const -> i32x4; + auto set(i32x4 v) -> void; + }; +} \ No newline at end of file diff --git a/libs/libSIMD/integer64.hpp b/libs/libSIMD/integer64.hpp new file mode 100644 index 0000000..bae63a6 --- /dev/null +++ b/libs/libSIMD/integer64.hpp @@ -0,0 +1,176 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +namespace simd +{ + struct integer64_s; + typedef struct integer64_s integer64; + + struct integer64_s + { + static constexpr std::int32_t element_count = 2; + + // MARK: - Construction + + /** + * Creates a 128-bit SIMD Vector from 16 8-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + * @param i4 Element 5 + * @param i5 Element 6 + * @param i6 Element 7 + * @param i7 Element 8 + * @param i8 Element 9 + * @param i9 Element 10 + * @param i10 Element 11 + * @param i11 Element 12 + * @param i12 Element 13 + * @param i13 Element 14 + * @param i14 Element 15 + * @param i16 Element 16 + */ + explicit integer64_s( + std::int8_t i0 = 0, std::int8_t i1 = 0, std::int8_t i2 = 0, std::int8_t i3 = 0, + std::int8_t i4 = 0, std::int8_t i5 = 0, std::int8_t i6 = 0, std::int8_t i7 = 0, + std::int8_t i8 = 0, std::int8_t i9 = 0, std::int8_t i10 = 0, std::int8_t i11 = 0, + std::int8_t i12 = 0, std::int8_t i13 = 0, std::int8_t i14 = 0, std::int8_t i15 = 0 + ); + + /** + * Creates a 128-bit SIMD Vector from 8 16-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + * @param i4 Element 5 + * @param i5 Element 6 + * @param i6 Element 7 + * @param i7 Element 8 + */ + explicit integer64_s( + std::int16_t i0 = 0, std::int16_t i1 = 0, std::int16_t i2 = 0, std::int16_t i3 = 0, + std::int16_t i4 = 0, std::int16_t i5 = 0, std::int16_t i6 = 0, std::int16_t i7 = 0 + ); + + /** + * Creates a 128-bit SIMD Vector from 4 32-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + */ + explicit integer64_s(std::int32_t i0 = 0, std::int32_t i1 = 0, std::int32_t i2 = 0, std::int32_t i3 = 0); + + /** + * Creates a 128-bit SIMD Vector from 2 64-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + */ + explicit integer64_s(std::int64_t i0 = 0, std::int64_t i1 = 0); + + explicit integer64_s(i64x2 v); + + integer64_s(const integer64&) = default; + integer64_s(integer64&&) = default; + auto operator=(const integer64&) -> integer64& = default; + + /** + * Creates a 128-bit SIMD Vector using a single 64-bit integer for each of the elements. + * @param v Value to duplicate into all elements. + */ + static auto constant(std::int64_t v) -> integer64_s; + + // MARK: - Accessors + auto operator[] (int i) const -> std::int64_t; + auto set(int i, std::int64_t v) -> integer64_s&; + + // MARK: - Operators + auto operator+ (const integer64& other) const -> integer64_s; + auto operator+ (std::int64_t i) const -> integer64; + auto operator+=(const integer64& other) -> integer64&; + auto operator+=(std::int64_t i) -> integer64&; + + auto operator- (const integer64& other) const -> integer64; + auto operator- (std::int64_t i) const -> integer64; + auto operator-=(const integer64& other) -> integer64&; + auto operator-=(std::int64_t i) -> integer64&; + + auto operator* (const integer64& other) const -> integer64; + auto operator* (std::int64_t i) const -> integer64; + auto operator* (float f) const -> integer64; + auto operator*=(const integer64& other) -> integer64&; + auto operator*=(std::int64_t i) -> integer64&; + auto operator*=(float f) -> integer64&; + + auto operator/ (const integer64& other) const -> integer64; + auto operator/ (std::int64_t i) const -> integer64; + auto operator/ (float f) const -> integer64; + auto operator/=(const integer64& other) -> integer64&; + auto operator/=(std::int64_t i) -> integer64&; + auto operator/=(float f) -> integer64&; + + auto operator& (const integer64& other) const -> integer64; + auto operator& (std::int64_t i) const -> integer64; + auto operator&=(const integer64& other) -> integer64&; + auto operator&=(std::int64_t i) -> integer64&; + + auto operator| (const integer64& other) const -> integer64; + auto operator| (std::int64_t i) const -> integer64; + auto operator|=(const integer64& other) -> integer64&; + auto operator|=(std::int64_t i) -> integer64&; + + auto operator^ (const integer64& other) const -> integer64; + auto operator^ (std::int64_t i) const -> integer64; + auto operator^=(const integer64& other) -> integer64&; + auto operator^=(std::int64_t i) -> integer64&; + + auto operator<<(const integer64& other) const -> integer64; + auto operator<<(std::int64_t i) const -> integer64; + auto operator<<=(const integer64& other) -> integer64&; + auto operator<<=(std::int64_t i) -> integer64&; + + auto operator>>(const integer64& other) const -> integer64; + auto operator>>(std::int64_t i) const -> integer64; + auto operator>>=(const integer64& other) -> integer64&; + auto operator>>=(std::int64_t i) -> integer64&; + + auto operator~() const -> integer64; + + [[nodiscard]] auto abs() const -> integer64; + + friend struct integer8_s; + friend struct integer16_s; + friend struct integer32_s; + friend struct float_s; + friend struct double_s; + + private: + std::int64_t __attribute__((__aligned__(16))) m_values[element_count]; + + [[nodiscard]] auto vector() const -> i64x2; + auto set(i64x2 v) -> void; + }; +} \ No newline at end of file diff --git a/libs/libSIMD/integer8.hpp b/libs/libSIMD/integer8.hpp new file mode 100644 index 0000000..763f6ca --- /dev/null +++ b/libs/libSIMD/integer8.hpp @@ -0,0 +1,169 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace simd +{ + struct integer8_s; + typedef struct integer8_s integer8; + + struct integer8_s + { + static constexpr std::int32_t element_count = 16; + + // MARK: - Construction + + integer8_s() = default; + + /** + * Creates a 128-bit SIMD Vector from 16 8-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + * @param i4 Element 5 + * @param i5 Element 6 + * @param i6 Element 7 + * @param i7 Element 8 + * @param i8 Element 9 + * @param i9 Element 10 + * @param i10 Element 11 + * @param i11 Element 12 + * @param i12 Element 13 + * @param i13 Element 14 + * @param i14 Element 15 + * @param i16 Element 16 + */ + explicit integer8_s( + std::int8_t i0, std::int8_t i1 = 0, std::int8_t i2 = 0, std::int8_t i3 = 0, + std::int8_t i4 = 0, std::int8_t i5 = 0, std::int8_t i6 = 0, std::int8_t i7 = 0, + std::int8_t i8 = 0, std::int8_t i9 = 0, std::int8_t i10 = 0, std::int8_t i11 = 0, + std::int8_t i12 = 0, std::int8_t i13 = 0, std::int8_t i14 = 0, std::int8_t i15 = 0 + ); + + /** + * Creates a 128-bit SIMD Vector from 8 16-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + * @param i4 Element 5 + * @param i5 Element 6 + * @param i6 Element 7 + * @param i7 Element 8 + */ + explicit integer8_s( + std::int16_t i0, std::int16_t i1 = 0, std::int16_t i2 = 0, std::int16_t i3 = 0, + std::int16_t i4 = 0, std::int16_t i5 = 0, std::int16_t i6 = 0, std::int16_t i7 = 0 + ); + + /** + * Creates a 128-bit SIMD Vector from 4 32-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + * @param i2 Element 3 + * @param i3 Element 4 + */ + explicit integer8_s(std::int32_t i0, std::int32_t i1 = 0, std::int32_t i2 = 0, std::int32_t i3 = 0); + + /** + * Creates a 128-bit SIMD Vector from 2 64-bit integers. + * @param i0 Element 1 + * @param i1 Element 2 + */ + explicit integer8_s(std::int64_t i0, std::int64_t i1 = 0); + + explicit integer8_s(i8x16 v); + + integer8_s(const integer8_s&) = default; + integer8_s(integer8_s&&) = default; + auto operator=(const integer8&) -> integer8& = default; + + /** + * Creates a 128-bit SIMD Vector using a single 8-bit integer for each of the elements. + * @param v Value to duplicate into all elements. + */ + static auto constant(std::int8_t v) -> integer8; + + // MARK: - Accessors + auto operator[] (int i) const -> std::int8_t; + auto set(int i, std::int8_t v) -> integer8&; + + // MARK: - Operators + auto operator+ (const integer8& other) const -> integer8; + auto operator+ (std::int8_t i) const -> integer8; + auto operator+=(const integer8& other) -> integer8&; + auto operator+=(std::int8_t i) -> integer8&; + + auto operator- (const integer8& other) const -> integer8; + auto operator- (std::int8_t i) const -> integer8; + auto operator-=(const integer8& other) -> integer8&; + auto operator-=(std::int8_t i) -> integer8&; + + APPROX_VALUE auto operator* (const integer8& other) const -> integer8; + APPROX_VALUE auto operator* (std::int8_t i) const -> integer8; + APPROX_VALUE auto operator* (float f) const -> integer8; + APPROX_VALUE auto operator*=(const integer8& other) -> integer8&; + APPROX_VALUE auto operator*=(std::int8_t i) -> integer8&; + APPROX_VALUE auto operator*=(float f) -> integer8&; + + APPROX_VALUE auto operator/ (const integer8& other) const -> integer8; + APPROX_VALUE auto operator/ (std::int8_t i) const -> integer8; + APPROX_VALUE auto operator/ (float f) const -> integer8; + APPROX_VALUE auto operator/=(const integer8& other) -> integer8&; + APPROX_VALUE auto operator/=(std::int8_t i) -> integer8&; + APPROX_VALUE auto operator/=(float f) -> integer8&; + + auto operator& (const integer8& other) const -> integer8; + auto operator& (std::int8_t i) const -> integer8; + auto operator&=(const integer8& other) -> integer8&; + auto operator&=(std::int8_t i) -> integer8&; + + auto operator| (const integer8& other) const -> integer8; + auto operator| (std::int8_t i) const -> integer8; + auto operator|=(const integer8& other) -> integer8&; + auto operator|=(std::int8_t i) -> integer8&; + + auto operator^ (const integer8& other) const -> integer8; + auto operator^ (std::int8_t i) const -> integer8; + auto operator^=(const integer8& other) -> integer8&; + auto operator^=(std::int8_t i) -> integer8&; + + auto operator~() const -> integer8; + + [[nodiscard]] auto abs() const -> integer8; + + friend struct integer16_s; + friend struct integer32_s; + friend struct integer64_s; + friend struct float32_s; + friend struct double64_s; + + private: + std::int8_t __attribute__((__aligned__(16))) m_values[element_count] { 0 }; + + [[nodiscard]] auto vector() const -> i8x16; + auto set(i8x16 v) -> void; + }; +} \ No newline at end of file diff --git a/libs/libSIMD/intel/float64.cpp b/libs/libSIMD/intel/float64.cpp new file mode 100644 index 0000000..6edfad7 --- /dev/null +++ b/libs/libSIMD/intel/float64.cpp @@ -0,0 +1,297 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +#if INTEL_SIMD +#include +#include +#include + +// MARK: - Construction + +simd::float64_s::float64_s(double f0, double f1) +{ + m_values[0] = f0; + m_values[1] = f1; +} + +simd::float64_s::float64_s(f64x2 v) +{ + _mm_store_pd(&m_values[0], v); +} + +auto simd::float64_s::constant(double v) -> float64 +{ + return float64(_mm_set1_ps(v)); +} + +auto simd::float64_s::lower_upper_merge(const float64 &lower, const float64 &upper) -> float64 +{ + return float64(lower[0], upper[1]); +} + +auto simd::float64_s::lower(const float64& lower) -> float64 +{ + return float64(lower[0], lower[0]); +} + +auto simd::float64_s::upper(const float64& upper) -> float64 +{ + return float64(upper[1], upper[1]); +} + +// MARK: - Accessors + +auto simd::float64_s::operator[](int i) const -> double +{ + assert(i >= 0 && i < element_count); + return m_values[element_count - 1 - i]; +} + +auto simd::float64_s::set(int i, double f) -> float64& +{ + assert(i >= 0 && i < element_count); + m_values[element_count - 1 - i] = f; + return *this; +} + +auto simd::float64_s::vector() const -> f64x2 +{ + return _mm_loadu_pd(&m_values[0]); +} + +auto simd::float64_s::set(f64x2 v) -> void +{ + _mm_storeu_pd(&m_values[0], v); +} + +// MARK: - Operators + +auto simd::float64_s::operator+ (const float64& other) const -> float64 +{ + return float64(_mm_add_pd(vector(), other.vector())); +} + +auto simd::float64_s::operator+ (double f) const -> float64 +{ + return *this + float64::constant(f); +} + +auto simd::float64_s::operator+=(const float64& other) -> float64& +{ + set((*this + other).vector()); + return *this; +} + +auto simd::float64_s::operator+=(double f) -> float64& +{ + set((*this + float64::constant(f)).vector()); + return *this; +} + +auto simd::float64_s::operator- (const float64& other) const -> float64 +{ + return float64(_mm_sub_pd(vector(), other.vector())); +} + +auto simd::float64_s::operator- (double f) const -> float64 +{ + return *this - float64::constant(f); +} + +auto simd::float64_s::operator-=(const float64& other) -> float64& +{ + set((*this - other).vector()); + return *this; +} + +auto simd::float64_s::operator-=(double f) -> float64& +{ + set((*this - float64::constant(f)).vector()); + return *this; +} + +auto simd::float64_s::operator* (const float64& other) const -> float64 +{ + return float64(_mm_mul_pd(vector(), other.vector())); +} + +auto simd::float64_s::operator* (double f) const -> float64 +{ + return *this * float64::constant(f); +} + +auto simd::float64_s::operator*=(const float64& other) -> float64& +{ + set((*this * other).vector()); + return *this; +} + +auto simd::float64_s::operator*=(double f) -> float64& +{ + set((*this * float64::constant(f)).vector()); + return *this; +} + +auto simd::float64_s::operator/ (const float64& other) const -> float64 +{ + return float64(_mm_div_pd(vector(), other.vector())); +} + +auto simd::float64_s::operator/ (double f) const -> float64 +{ + return *this / float64::constant(f); +} + +auto simd::float64_s::operator/=(const float64& other) -> float64& +{ + set((*this / other).vector()); + return *this; +} + +auto simd::float64_s::operator/=(double f) -> float64& +{ + set((*this / float64::constant(f)).vector()); + return *this; +} + +// MARK: - Operations + +auto simd::float64_s::abs() const -> float64 +{ + // TODO: Implement this + return *this; +} + +auto simd::float64_s::round() const -> float64 +{ + return float64(_mm_round_pd(vector(), _MM_ROUND_NEAREST)); +} + +auto simd::float64_s::floor() const -> float64 +{ + return float64(_mm_floor_pd(vector())); +} + +auto simd::float64_s::ceil() const -> float64 +{ + return float64(_mm_ceil_pd(vector())); +} + +auto simd::float64_s::pow(double exp) const -> float64 +{ + if (exp == 2.0) { + auto v = vector(); + return float64(_mm_mul_pd(v, v)); + } + else { + // TODO: Find an implementation that uses SSE/AVX in the future + double v[element_count]; + for (auto n = 0; n < element_count; ++n) { + v[n] = std::pow(m_values[n], m_values[n]); + } + return float64(v[0], v[1]); + } +} + +auto simd::float64_s::sqrt() const -> float64 +{ + return float64(_mm_sqrt_pd(vector())); +} + +auto simd::float64_s::sin() const -> float64 +{ + // TODO: Find an implementation that uses SSE/AVX in the future + double v[element_count]; + for (auto n = 0; n < element_count; ++n) { + v[n] = std::sin(m_values[n]); + } + return float64(v[0], v[1]); +} + +auto simd::float64_s::cos() const -> float64 +{ + // TODO: Find an implementation that uses SSE/AVX in the future + double v[element_count]; + for (auto n = 0; n < element_count; ++n) { + v[n] = std::cos(m_values[n]); + } + return float64(v[0], v[1]); +} + +auto simd::float64_s::rcp() const -> float64 +{ + // TODO: Implement this + return *this; +} + +auto simd::float64_s::min(const float64 &other) const -> float64 +{ + return float64(_mm_min_pd(vector(), other.vector())); +} + +auto simd::float64_s::min(double f) const -> float64 +{ + return float64(_mm_min_pd(vector(), float64::constant(f).vector())); +} + +auto simd::float64_s::max(const float64 &other) const -> float64 +{ + return float64(_mm_max_pd(vector(), other.vector())); +} + +auto simd::float64_s::max(double f) const -> float64 +{ + return float64(_mm_max_pd(vector(), float64::constant(f).vector())); +} + +auto simd::float64_s::upper() const -> float64 +{ + return float64(m_values[1], m_values[1]); +} + +auto simd::float64_s::lower() const -> float64 +{ + return float64(m_values[0], m_values[0]); +} + +auto simd::float64_s::swapped() const -> float64 +{ + return float64(m_values[1], m_values[0]); +} + +auto simd::float64_s::reversed() const -> float64 +{ + return float64(m_values[1], m_values[0]); +} + +auto simd::float64_s::set_lower(const float64& lower) -> void +{ + m_values[0] = lower[0]; +} + +auto simd::float64_s::set_upper(const float64& upper) -> void +{ + m_values[1] = upper[1]; +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/intel/integer16.cpp b/libs/libSIMD/intel/integer16.cpp new file mode 100644 index 0000000..e5a1e7d --- /dev/null +++ b/libs/libSIMD/intel/integer16.cpp @@ -0,0 +1,374 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +#if INTEL_SIMD +#include +#include +#include + +// MARK: - Construction + +simd::integer16_s::integer16_s(std::int8_t i0, std::int8_t i1, std::int8_t i2, std::int8_t i3, std::int8_t i4, + std::int8_t i5, std::int8_t i6, std::int8_t i7, std::int8_t i8, std::int8_t i9, + std::int8_t i10, std::int8_t i11, std::int8_t i12, std::int8_t i13, std::int8_t i14, + std::int8_t i15) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi8(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15)); +} + +simd::integer16_s::integer16_s(std::int16_t i0, std::int16_t i1, std::int16_t i2, std::int16_t i3, std::int16_t i4, + std::int16_t i5, std::int16_t i6, std::int16_t i7) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi16(i0, i1, i2, i3, i4, i5, i6, i7)); +} + +simd::integer16_s::integer16_s(std::int32_t i0, std::int32_t i1, std::int32_t i2, std::int32_t i3) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi32(i0, i1, i2, i3)); +} + +simd::integer16_s::integer16_s(std::int64_t i0, std::int64_t i1) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi64x(i0, i1)); +} + +simd::integer16_s::integer16_s(i16x8 v) +{ + _mm_store_si128((__m128i *)&m_values[0], v); +} + +auto simd::integer16_s::constant(std::int16_t v) -> integer16 +{ + return integer16(_mm_set1_epi16(v)); +} + +// MARK: - Accessors + +auto simd::integer16_s::operator[](int i) const -> std::int16_t +{ + assert(i >= 0 && i < element_count); + return m_values[element_count - 1 - i]; +} + +auto simd::integer16_s::set(int i, std::int16_t v) -> integer16& +{ + assert(i >= 0 && i < element_count); + m_values[element_count - 1 - i] = v; + return *this; +} + +auto simd::integer16_s::vector() const -> i16x8 +{ + return _mm_loadu_si16((__m128i *)&m_values[0]); +} + +auto simd::integer16_s::set(i8x16 v) -> void +{ + _mm_store_si128((__m128i *)&m_values[0], v); +} + +// MARK: - Operators + +auto simd::integer16_s::operator+(const integer16& other) const -> integer16 +{ + return integer16(_mm_add_epi16(vector(), other.vector())); +} + +auto simd::integer16_s::operator+(std::int16_t i) const -> integer16 +{ + return *this + integer16::constant(i); +} + +auto simd::integer16_s::operator+=(const integer16& other) -> integer16& +{ + set(_mm_add_epi16(vector(), other.vector())); + return *this; +} + +auto simd::integer16_s::operator+=(std::int16_t i) -> integer16 & +{ + set(_mm_add_epi16(vector(), integer16::constant(i).vector())); + return *this; +} + +auto simd::integer16_s::operator-(const integer16& other) const -> integer16 +{ + return integer16(_mm_sub_epi16(vector(), other.vector())); +} + +auto simd::integer16_s::operator-(std::int16_t i) const -> integer16 +{ + return *this - integer16::constant(i); +} + +auto simd::integer16_s::operator-=(const integer16& other) -> integer16& +{ + set(_mm_sub_epi16(vector(), other.vector())); + return *this; +} + +auto simd::integer16_s::operator-=(std::int16_t i) -> integer16 & +{ + set(_mm_sub_epi16(vector(), integer16::constant(i).vector())); + return *this; +} + +auto simd::integer16_s::operator*(const integer16& other) const -> integer16 +{ + // Load the source and destinations + auto s1 = _mm_loadu_si16((__m128i *)&m_values[0]); + auto d1 = _mm_loadu_si16((__m128i *)&other.m_values[0]); + auto r1 = _mm_mullo_epi16(s1, d1); + return integer16(r1); +} + +auto simd::integer16_s::operator*(std::int16_t i) const -> integer16 +{ + return *this * integer16::constant(i); +} + +auto simd::integer16_s::operator*=(const integer16& other) -> integer16& +{ + set((*this * other).vector()); + return *this; +} + +auto simd::integer16_s::operator*=(std::int16_t i) -> integer16 & +{ + set((*this * integer16::constant(i)).vector()); + return *this; +} + +auto simd::integer16::operator*(float f) const -> integer16 +{ + // Load the source and destinations + __m128i r1; + auto op = _mm_set1_ps(f); + auto s1 = _mm_castsi128_ps(_mm_loadu_si32((__m128i *)&m_values[0])); + auto s2 = _mm_castsi128_ps(_mm_loadu_si32((__m128i *)&m_values[4])); + s1 = _mm_mul_ps(s1, op); + s2 = _mm_mul_ps(s2, op); + + auto d1 = _mm_castps_si128(s1); + auto d2 = _mm_castps_si128(s2); + d1 = _mm_and_si128(d1, _mm_set1_epi32(0x0000FFFF)); + d2 = _mm_and_si128(d2, _mm_set1_epi32(0x0000FFFF)); + d2 = _mm_sll_epi32(d2, _mm_set1_epi32(16)); + r1 = _mm_or_si128(d1, d2); + return integer16(r1); +} + +auto simd::integer16_s::operator*=(float f) -> integer16& +{ + set((*this * f).vector()); + return *this; +} + +auto simd::integer16_s::operator/(const integer16& other) const -> integer16 +{ + // Load the source and destinations + __m128i r1; + auto s1 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&m_values[0])); + auto s2 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&m_values[4])); + auto op1 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&other.m_values[0])); + auto op2 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&other.m_values[4])); + s1 = _mm_div_ps(s1, op1); + s2 = _mm_div_ps(s2, op2); + + auto d1 = _mm_castps_si128(s1); + auto d2 = _mm_castps_si128(s2); + d1 = _mm_and_si128(d1, _mm_set1_epi32(0x0000FFFF)); + d2 = _mm_and_si128(d2, _mm_set1_epi32(0x0000FFFF)); + d2 = _mm_sll_epi32(d2, _mm_set1_epi32(16)); + r1 = _mm_or_si128(d1, d2); + return integer16(r1); +} + +auto simd::integer16_s::operator/(std::int16_t i) const -> integer16 +{ + return *this / integer16::constant(i); +} + +auto simd::integer16_s::operator/=(const integer16& other) -> integer16& +{ + set((*this / other).vector()); + return *this; +} + +auto simd::integer16_s::operator/=(std::int16_t i) -> integer16 & +{ + set((*this / integer16::constant(i)).vector()); + return *this; +} + +auto simd::integer16_s::operator/(float f) const -> integer16 +{ + // Load the source and destinations + __m128i r1; + auto op = _mm_set1_ps(f); + auto s1 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&m_values[0])); + auto s2 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&m_values[4])); + s1 = _mm_div_ps(s1, op); + s2 = _mm_div_ps(s2, op); + + auto d1 = _mm_castps_si128(s1); + auto d2 = _mm_castps_si128(s2); + d1 = _mm_and_si128(d1, _mm_set1_epi32(0x0000FFFF)); + d2 = _mm_and_si128(d2, _mm_set1_epi32(0x0000FFFF)); + d2 = _mm_sll_epi32(d2, _mm_set1_epi32(16)); + r1 = _mm_or_si128(d1, d2); + return integer16(r1); +} + +auto simd::integer16_s::operator/=(float f) -> integer16& +{ + set((*this / f).vector()); + return *this; +} + +auto simd::integer16_s::operator&(const integer16& other) const -> integer16 +{ + return integer16(_mm_and_si128(vector(), other.vector())); +} + +auto simd::integer16_s::operator&(std::int16_t i) const -> integer16 +{ + return *this & integer16::constant(i); +} + +auto simd::integer16_s::operator&=(const integer16& other) -> integer16& +{ + set((*this & other).vector()); + return *this; +} + +auto simd::integer16_s::operator&=(std::int16_t i) -> integer16& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer16_s::operator|(const integer16& other) const -> integer16 +{ + return integer16(_mm_or_si128(vector(), other.vector())); +} + +auto simd::integer16_s::operator|(std::int16_t i) const -> integer16 +{ + return *this | integer16::constant(i); +} + +auto simd::integer16_s::operator|=(const integer16& other) -> integer16& +{ + set((*this | other).vector()); + return *this; +} + +auto simd::integer16_s::operator|=(std::int16_t i) -> integer16& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer16_s::operator^(const integer16& other) const -> integer16 +{ + return integer16(_mm_or_si128(vector(), other.vector())); +} + +auto simd::integer16_s::operator^(std::int16_t i) const -> integer16 +{ + return *this ^ integer16::constant(i); +} + +auto simd::integer16_s::operator^=(const integer16& other) -> integer16& +{ + set((*this ^ other).vector()); + return *this; +} + +auto simd::integer16_s::operator^=(std::int16_t i) -> integer16& +{ + set((*this ^ i).vector()); + return *this; +} + +auto simd::integer16_s::operator~() const -> integer16 +{ + return integer16(_mm_xor_si128(vector(), _mm_set1_epi16(0xFF))); +} + +auto simd::integer16_s::operator<<(const integer16& other) const -> integer16 +{ + auto s1 = _mm_loadu_si128((__m128i *)&m_values[0]); + auto d1 = _mm_loadu_si128((__m128i *)&other.m_values[0]); + auto r1 = _mm_sll_epi16(s1, d1); + return integer16(r1); +} + +auto simd::integer16_s::operator<<(std::int16_t i) const -> integer16 +{ + return *this << integer16::constant(i); +} + +auto simd::integer16_s::operator<<=(const integer16& other) -> integer16& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer16_s::operator<<=(std::int16_t i) -> integer16& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer16_s::operator>>(const integer16& other) const -> integer16 +{ + auto s1 = _mm_loadu_si128((__m128i *)&m_values[0]); + auto d1 = _mm_loadu_si128((__m128i *)&other.m_values[0]); + auto r1 = _mm_sra_epi16(s1, d1); + return integer16(r1); +} + +auto simd::integer16_s::operator>>(std::int16_t i) const -> integer16 +{ + return *this << integer16::constant(i); +} + +auto simd::integer16_s::operator>>=(const integer16& other) -> integer16& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer16_s::operator>>=(std::int16_t i) -> integer16& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer16_s::abs() const -> integer16 +{ + return integer16(_mm_abs_epi16(vector())); +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/intel/integer32.cpp b/libs/libSIMD/intel/integer32.cpp new file mode 100644 index 0000000..f090817 --- /dev/null +++ b/libs/libSIMD/intel/integer32.cpp @@ -0,0 +1,343 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +#if INTEL_SIMD +#include +#include +#include + +// MARK: - Construction + +simd::integer32_s::integer32_s(std::int8_t i0, std::int8_t i1, std::int8_t i2, std::int8_t i3, std::int8_t i4, + std::int8_t i5, std::int8_t i6, std::int8_t i7, std::int8_t i8, std::int8_t i9, + std::int8_t i10, std::int8_t i11, std::int8_t i12, std::int8_t i13, std::int8_t i14, + std::int8_t i15) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi8(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15)); +} + +simd::integer32_s::integer32_s(std::int16_t i0, std::int16_t i1, std::int16_t i2, std::int16_t i3, std::int16_t i4, + std::int16_t i5, std::int16_t i6, std::int16_t i7) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi16(i0, i1, i2, i3, i4, i5, i6, i7)); +} + +simd::integer32_s::integer32_s(std::int32_t i0, std::int32_t i1, std::int32_t i2, std::int32_t i3) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi32(i0, i1, i2, i3)); +} + +simd::integer32_s::integer32_s(std::int64_t i0, std::int64_t i1) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi64x(i0, i1)); +} + +simd::integer32_s::integer32_s(i32x4 v) +{ + _mm_store_si128((__m128i *)&m_values[0], v); +} + +auto simd::integer32_s::constant(std::int32_t v) -> integer32 +{ + return integer32(_mm_set1_epi32(v)); +} + +// MARK: - Accessors + +auto simd::integer32_s::operator[](int i) const -> std::int32_t +{ + assert(i >= 0 && i < element_count); + return m_values[element_count - 1 - i]; +} + +auto simd::integer32_s::set(int i, std::int32_t v) -> integer32& +{ + assert(i >= 0 && i < element_count); + m_values[element_count - 1 - i] = v; + return *this; +} + +auto simd::integer32_s::vector() const -> i32x4 +{ + return _mm_loadu_si32((__m128i *)&m_values[0]); +} + +auto simd::integer32_s::set(i32x4 v) -> void +{ + _mm_store_si128((__m128i *)&m_values[0], v); +} + +// MARK: - Operators + +auto simd::integer32_s::operator+(const integer32& other) const -> integer32 +{ + return integer32(_mm_add_epi32(vector(), other.vector())); +} + +auto simd::integer32_s::operator+(std::int32_t i) const -> integer32 +{ + return *this + integer32::constant(i); +} + +auto simd::integer32_s::operator+=(const integer32& other) -> integer32& +{ + set(_mm_add_epi32(vector(), other.vector())); + return *this; +} + +auto simd::integer32_s::operator+=(std::int32_t i) -> integer32 & +{ + set(_mm_add_epi32(vector(), integer32::constant(i).vector())); + return *this; +} + +auto simd::integer32_s::operator-(const integer32& other) const -> integer32 +{ + return integer32(_mm_sub_epi32(vector(), other.vector())); +} + +auto simd::integer32_s::operator-(std::int32_t i) const -> integer32 +{ + return *this - integer32::constant(i); +} + +auto simd::integer32_s::operator-=(const integer32& other) -> integer32& +{ + set(_mm_sub_epi16(vector(), other.vector())); + return *this; +} + +auto simd::integer32_s::operator-=(std::int32_t i) -> integer32 & +{ + set(_mm_sub_epi32(vector(), integer32::constant(i).vector())); + return *this; +} + +auto simd::integer32_s::operator*(const integer32& other) const -> integer32 +{ + // Load the source and destinations + auto s1 = _mm_loadu_si32((__m128i *)&m_values[0]); + auto d1 = _mm_loadu_si32((__m128i *)&other.m_values[0]); + auto r1 = _mm_mullo_epi32(s1, d1); + return integer32(r1); +} + +auto simd::integer32_s::operator*(std::int32_t i) const -> integer32 +{ + return *this * integer32::constant(i); +} + +auto simd::integer32_s::operator*=(const integer32& other) -> integer32& +{ + set((*this * other).vector()); + return *this; +} + +auto simd::integer32_s::operator*=(std::int32_t i) -> integer32 & +{ + set((*this * integer32::constant(i)).vector()); + return *this; +} + +auto simd::integer32::operator*(float f) const -> integer32 +{ + // Load the source and destinations + auto s1 = _mm_castsi128_ps(_mm_loadu_si32((__m128i *)&m_values[0])); + auto op = _mm_set1_ps(f); + auto d1 = _mm_mul_ps(s1, op); + return integer32(_mm_castps_si128(d1)); +} + +auto simd::integer32_s::operator*=(float f) -> integer32& +{ + set((*this * f).vector()); + return *this; +} + +auto simd::integer32_s::operator/(const integer32& other) const -> integer32 +{ + // Load the source and destinations + auto s1 = _mm_castsi128_ps(_mm_loadu_si32((__m128i *)&m_values[0])); + auto op = _mm_castsi128_ps(_mm_loadu_si32((__m128i *)&other.m_values[0])); + auto d1 = _mm_div_ps(s1, op); + return integer32(_mm_castps_si128(d1)); +} + +auto simd::integer32_s::operator/(std::int32_t i) const -> integer32 +{ + return *this / integer32::constant(i); +} + +auto simd::integer32_s::operator/=(const integer32& other) -> integer32& +{ + set((*this / other).vector()); + return *this; +} + +auto simd::integer32_s::operator/=(std::int32_t i) -> integer32 & +{ + set((*this / integer32::constant(i)).vector()); + return *this; +} + +auto simd::integer32_s::operator/(float f) const -> integer32 +{ + // Load the source and destinations + auto s1 = _mm_castsi128_ps(_mm_loadu_si32((__m128i *)&m_values[0])); + auto op = _mm_set1_ps(f); + auto d1 = _mm_div_ps(s1, op); + return integer32(_mm_castps_si128(d1)); +} + +auto simd::integer32_s::operator/=(float f) -> integer32& +{ + set((*this / f).vector()); + return *this; +} + +auto simd::integer32_s::operator&(const integer32& other) const -> integer32 +{ + return integer32(_mm_and_si128(vector(), other.vector())); +} + +auto simd::integer32_s::operator&(std::int32_t i) const -> integer32 +{ + return *this & integer32::constant(i); +} + +auto simd::integer32_s::operator&=(const integer32& other) -> integer32& +{ + set((*this & other).vector()); + return *this; +} + +auto simd::integer32_s::operator&=(std::int32_t i) -> integer32& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer32_s::operator|(const integer32& other) const -> integer32 +{ + return integer32(_mm_or_si128(vector(), other.vector())); +} + +auto simd::integer32_s::operator|(std::int32_t i) const -> integer32 +{ + return *this | integer32::constant(i); +} + +auto simd::integer32_s::operator|=(const integer32& other) -> integer32& +{ + set((*this | other).vector()); + return *this; +} + +auto simd::integer32_s::operator|=(std::int32_t i) -> integer32& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer32_s::operator^(const integer32& other) const -> integer32 +{ + return integer32(_mm_or_si128(vector(), other.vector())); +} + +auto simd::integer32_s::operator^(std::int32_t i) const -> integer32 +{ + return *this ^ integer32::constant(i); +} + +auto simd::integer32_s::operator^=(const integer32& other) -> integer32& +{ + set((*this ^ other).vector()); + return *this; +} + +auto simd::integer32_s::operator^=(std::int32_t i) -> integer32& +{ + set((*this ^ i).vector()); + return *this; +} + +auto simd::integer32_s::operator~() const -> integer32 +{ + return integer32(_mm_xor_si128(vector(), _mm_set1_epi32(0xFFFFFFFF))); +} + +auto simd::integer32_s::operator<<(const integer32& other) const -> integer32 +{ + auto s1 = _mm_loadu_si32((__m128i *)&m_values[0]); + auto d1 = _mm_loadu_si32((__m128i *)&other.m_values[0]); + auto r1 = _mm_sll_epi32(s1, d1); + return integer32(r1); +} + +auto simd::integer32_s::operator<<(std::int32_t i) const -> integer32 +{ + return *this << integer32::constant(i); +} + +auto simd::integer32_s::operator<<=(const integer32& other) -> integer32& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer32_s::operator<<=(std::int32_t i) -> integer32& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer32_s::operator>>(const integer32& other) const -> integer32 +{ + auto s1 = _mm_loadu_si32((__m128i *)&m_values[0]); + auto d1 = _mm_loadu_si32((__m128i *)&other.m_values[0]); + auto r1 = _mm_sra_epi32(s1, d1); + return integer32(r1); +} + +auto simd::integer32_s::operator>>(std::int32_t i) const -> integer32 +{ + return *this << integer32::constant(i); +} + +auto simd::integer32_s::operator>>=(const integer32& other) -> integer32& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer32_s::operator>>=(std::int32_t i) -> integer32& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer32_s::abs() const -> integer32 +{ + return integer32(_mm_abs_epi32(vector())); +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/intel/integer64.cpp b/libs/libSIMD/intel/integer64.cpp new file mode 100644 index 0000000..7512e18 --- /dev/null +++ b/libs/libSIMD/intel/integer64.cpp @@ -0,0 +1,339 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +#if INTEL_SIMD +#include +#include +#include + +// MARK: - Construction + +simd::integer64_s::integer64_s(std::int8_t i0, std::int8_t i1, std::int8_t i2, std::int8_t i3, std::int8_t i4, + std::int8_t i5, std::int8_t i6, std::int8_t i7, std::int8_t i8, std::int8_t i9, + std::int8_t i10, std::int8_t i11, std::int8_t i12, std::int8_t i13, std::int8_t i14, + std::int8_t i15) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi8(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15)); +} + +simd::integer64_s::integer64_s(std::int16_t i0, std::int16_t i1, std::int16_t i2, std::int16_t i3, std::int16_t i4, + std::int16_t i5, std::int16_t i6, std::int16_t i7) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi16(i0, i1, i2, i3, i4, i5, i6, i7)); +} + +simd::integer64_s::integer64_s(std::int32_t i0, std::int32_t i1, std::int32_t i2, std::int32_t i3) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi32(i0, i1, i2, i3)); +} + +simd::integer64_s::integer64_s(std::int64_t i0, std::int64_t i1) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi64x(i0, i1)); +} + +simd::integer64_s::integer64_s(i64x2 v) +{ + _mm_store_si128((__m128i *)&m_values[0], v); +} + +auto simd::integer64_s::constant(std::int64_t v) -> integer64 +{ + return integer64(_mm_set1_epi64x(v)); +} + +// MARK: - Accessors + +auto simd::integer64_s::operator[](int i) const -> std::int64_t +{ + assert(i >= 0 && i < element_count); + return m_values[element_count - 1 - i]; +} + +auto simd::integer64_s::set(int i, std::int64_t v) -> integer64& +{ + assert(i >= 0 && i < element_count); + m_values[element_count - 1 - i] = v; + return *this; +} + +auto simd::integer64_s::vector() const -> i64x2 +{ + return _mm_loadu_si64((__m128i *)&m_values[0]); +} + +auto simd::integer64_s::set(i64x2 v) -> void +{ + _mm_store_si128((__m128i *)&m_values[0], v); +} + +// MARK: - Operators + +auto simd::integer64_s::operator+(const integer64& other) const -> integer64 +{ + return integer64(_mm_add_epi64(vector(), other.vector())); +} + +auto simd::integer64_s::operator+(std::int64_t i) const -> integer64 +{ + return *this + integer64::constant(i); +} + +auto simd::integer64_s::operator+=(const integer64& other) -> integer64& +{ + set(_mm_add_epi64(vector(), other.vector())); + return *this; +} + +auto simd::integer64_s::operator+=(std::int64_t i) -> integer64 & +{ + set(_mm_add_epi64(vector(), integer64::constant(i).vector())); + return *this; +} + +auto simd::integer64_s::operator-(const integer64& other) const -> integer64 +{ + return integer64(_mm_sub_epi64(vector(), other.vector())); +} + +auto simd::integer64_s::operator-(std::int64_t i) const -> integer64 +{ + return *this - integer64::constant(i); +} + +auto simd::integer64_s::operator-=(const integer64& other) -> integer64& +{ + set(_mm_sub_epi16(vector(), other.vector())); + return *this; +} + +auto simd::integer64_s::operator-=(std::int64_t i) -> integer64 & +{ + set(_mm_sub_epi64(vector(), integer64::constant(i).vector())); + return *this; +} + +auto simd::integer64_s::operator*(const integer64& other) const -> integer64 +{ + // TODO: Implement this + return *this; +} + +auto simd::integer64_s::operator*(std::int64_t i) const -> integer64 +{ + return *this * integer64::constant(i); +} + +auto simd::integer64_s::operator*=(const integer64& other) -> integer64& +{ + set((*this * other).vector()); + return *this; +} + +auto simd::integer64_s::operator*=(std::int64_t i) -> integer64 & +{ + set((*this * integer64::constant(i)).vector()); + return *this; +} + +auto simd::integer64::operator*(float f) const -> integer64 +{ + // Load the source and destinations + auto s1 = _mm_castsi128_pd(_mm_loadu_si128((__m128i *)&m_values[0])); + auto op = _mm_set1_pd(f); + auto d1 = _mm_mul_pd(s1, op); + return integer64(_mm_castpd_si128(d1)); +} + +auto simd::integer64_s::operator*=(float f) -> integer64& +{ + set((*this * f).vector()); + return *this; +} + +auto simd::integer64_s::operator/(const integer64& other) const -> integer64 +{ + // Load the source and destinations + auto s1 = _mm_castsi128_pd(_mm_loadu_si128((__m128i *)&m_values[0])); + auto op = _mm_castsi128_pd(_mm_loadu_si128((__m128i *)&other.m_values[0])); + auto d1 = _mm_div_pd(s1, op); + return integer64(_mm_castpd_si128(d1)); +} + +auto simd::integer64_s::operator/(std::int64_t i) const -> integer64 +{ + return *this / integer64::constant(i); +} + +auto simd::integer64_s::operator/=(const integer64& other) -> integer64& +{ + set((*this / other).vector()); + return *this; +} + +auto simd::integer64_s::operator/=(std::int64_t i) -> integer64 & +{ + set((*this / integer64::constant(i)).vector()); + return *this; +} + +auto simd::integer64_s::operator/(float f) const -> integer64 +{ + // Load the source and destinations + auto s1 = _mm_castsi128_pd(_mm_loadu_si128((__m128i *)&m_values[0])); + auto op = _mm_set1_pd(f); + auto d1 = _mm_div_pd(s1, op); + return integer64(_mm_castpd_si128(d1)); +} + +auto simd::integer64_s::operator/=(float f) -> integer64& +{ + set((*this / f).vector()); + return *this; +} + +auto simd::integer64_s::operator&(const integer64& other) const -> integer64 +{ + return integer64(_mm_and_si128(vector(), other.vector())); +} + +auto simd::integer64_s::operator&(std::int64_t i) const -> integer64 +{ + return *this & integer64::constant(i); +} + +auto simd::integer64_s::operator&=(const integer64& other) -> integer64& +{ + set((*this & other).vector()); + return *this; +} + +auto simd::integer64_s::operator&=(std::int64_t i) -> integer64& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer64_s::operator|(const integer64& other) const -> integer64 +{ + return integer64(_mm_or_si128(vector(), other.vector())); +} + +auto simd::integer64_s::operator|(std::int64_t i) const -> integer64 +{ + return *this | integer64::constant(i); +} + +auto simd::integer64_s::operator|=(const integer64& other) -> integer64& +{ + set((*this | other).vector()); + return *this; +} + +auto simd::integer64_s::operator|=(std::int64_t i) -> integer64& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer64_s::operator^(const integer64& other) const -> integer64 +{ + return integer64(_mm_or_si128(vector(), other.vector())); +} + +auto simd::integer64_s::operator^(std::int64_t i) const -> integer64 +{ + return *this ^ integer64::constant(i); +} + +auto simd::integer64_s::operator^=(const integer64& other) -> integer64& +{ + set((*this ^ other).vector()); + return *this; +} + +auto simd::integer64_s::operator^=(std::int64_t i) -> integer64& +{ + set((*this ^ i).vector()); + return *this; +} + +auto simd::integer64_s::operator~() const -> integer64 +{ + return integer64(_mm_xor_si128(vector(), _mm_set1_epi64x(0xFFFFFFFF'FFFFFFFF))); +} + +auto simd::integer64_s::operator<<(const integer64& other) const -> integer64 +{ + auto s1 = _mm_loadu_si128((__m128i *)&m_values[0]); + auto d1 = _mm_loadu_si128((__m128i *)&other.m_values[0]); + auto r1 = _mm_sll_epi64(s1, d1); + return integer64(r1); +} + +auto simd::integer64_s::operator<<(std::int64_t i) const -> integer64 +{ + return *this << integer64::constant(i); +} + +auto simd::integer64_s::operator<<=(const integer64& other) -> integer64& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer64_s::operator<<=(std::int64_t i) -> integer64& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer64_s::operator>>(const integer64& other) const -> integer64 +{ + // TODO: Implement this + return *this; +} + +auto simd::integer64_s::operator>>(std::int64_t i) const -> integer64 +{ + return *this << integer64::constant(i); +} + +auto simd::integer64_s::operator>>=(const integer64& other) -> integer64& +{ + set((*this << other).vector()); + return *this; +} + +auto simd::integer64_s::operator>>=(std::int64_t i) -> integer64& +{ + set((*this << i).vector()); + return *this; +} + +auto simd::integer64_s::abs() const -> integer64 +{ + // TODO: Implement this... + return *this; +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/intel/integer8.cpp b/libs/libSIMD/intel/integer8.cpp new file mode 100644 index 0000000..371f5af --- /dev/null +++ b/libs/libSIMD/intel/integer8.cpp @@ -0,0 +1,416 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +#if INTEL_SIMD +#include +#include +#include + +// MARK: - Construction + +simd::integer8_s::integer8_s(std::int8_t i0, std::int8_t i1, std::int8_t i2, std::int8_t i3, std::int8_t i4, + std::int8_t i5, std::int8_t i6, std::int8_t i7, std::int8_t i8, std::int8_t i9, + std::int8_t i10, std::int8_t i11, std::int8_t i12, std::int8_t i13, std::int8_t i14, + std::int8_t i15) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi8(i0, i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15)); +} + +simd::integer8_s::integer8_s(std::int16_t i0, std::int16_t i1, std::int16_t i2, std::int16_t i3, std::int16_t i4, + std::int16_t i5, std::int16_t i6, std::int16_t i7) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi16(i0, i1, i2, i3, i4, i5, i6, i7)); +} + +simd::integer8_s::integer8_s(std::int32_t i0, std::int32_t i1, std::int32_t i2, std::int32_t i3) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi32(i0, i1, i2, i3)); +} + +simd::integer8_s::integer8_s(std::int64_t i0, std::int64_t i1) +{ + _mm_store_si128((__m128i *)&m_values[0], _mm_set_epi64x(i0, i1)); +} + +simd::integer8_s::integer8_s(i8x16 v) +{ + _mm_store_si128((__m128i *)&m_values[0], v); +} + +auto simd::integer8_s::constant(std::int8_t v) -> integer8 +{ + return integer8(_mm_set1_epi8(v)); +} + +// MARK: - Accessors + +auto simd::integer8_s::operator[](int i) const -> std::int8_t +{ + assert(i >= 0 && i < element_count); + return m_values[element_count - 1 - i]; +} + +auto simd::integer8_s::set(int i, std::int8_t v) -> integer8& +{ + assert(i >= 0 && i < element_count); + m_values[element_count - 1 - i] = v; + return *this; +} + +auto simd::integer8_s::vector() const -> i8x16 +{ + return _mm_load_si128((__m128i *)&m_values[0]); +} + +auto simd::integer8_s::set(i8x16 v) -> void +{ + _mm_store_si128((__m128i *)&m_values[0], v); +} + +// MARK: - Operators + +auto simd::integer8_s::operator+(const integer8& other) const -> integer8 +{ + return integer8(_mm_add_epi8(vector(), other.vector())); +} + +auto simd::integer8_s::operator+(std::int8_t i) const -> integer8 +{ + return *this + integer8::constant(i); +} + +auto simd::integer8_s::operator+=(const integer8& other) -> integer8& +{ + set(_mm_add_epi8(vector(), other.vector())); + return *this; +} + +auto simd::integer8_s::operator+=(std::int8_t i) -> integer8 & +{ + set(_mm_add_epi8(vector(), integer8::constant(i).vector())); + return *this; +} + +auto simd::integer8_s::operator-(const integer8& other) const -> integer8 +{ + return integer8(_mm_sub_epi8(vector(), other.vector())); +} + +auto simd::integer8_s::operator-(std::int8_t i) const -> integer8 +{ + return *this - integer8::constant(i); +} + +auto simd::integer8_s::operator-=(const integer8& other) -> integer8& +{ + set(_mm_sub_epi8(vector(), other.vector())); + return *this; +} + +auto simd::integer8_s::operator-=(std::int8_t i) -> integer8 & +{ + set(_mm_sub_epi8(vector(), integer8::constant(i).vector())); + return *this; +} + +auto simd::integer8_s::operator*(const integer8& other) const -> integer8 +{ + std::int16_t words[element_count << 1] = { + // Source - Op1 + m_values[0], m_values[1], m_values[2], m_values[3], + m_values[4], m_values[5], m_values[6], m_values[7], + + // Source Op2 + m_values[8], m_values[9], m_values[10], m_values[11], + m_values[12], m_values[13], m_values[14], m_values[15], + + // Destination Op1 + other.m_values[0], other.m_values[1], other.m_values[2], other.m_values[3], + other.m_values[4], other.m_values[5], other.m_values[6], other.m_values[7], + + // Destination Op2 + other.m_values[8], other.m_values[9], other.m_values[10], other.m_values[11], + other.m_values[12], other.m_values[13], other.m_values[14], other.m_values[15], + }; + + // Load the source and destinations + auto s1 = _mm_loadu_si16((__m128i *)&words[0]); + auto s2 = _mm_loadu_si16((__m128i *)&words[8]); + auto d1 = _mm_loadu_si16((__m128i *)&words[16]); + auto d2 = _mm_loadu_si16((__m128i *)&words[24]); + + // Perform the calculations, and clip down to single byte values. + // Shift the second result to occupy the hi byte, and the first result to occupy the lo byte + auto r1 = _mm_and_si128(_mm_mullo_epi16(s1, d1), _mm_set1_epi32(0x00FF00FF)); + auto r2 = _mm_sll_epi32(_mm_and_si128(_mm_mullo_epi16(s2, d2), _mm_set1_epi32(0x00FF00FF)), _mm_set1_epi32(16)); + + // Merge the results + auto r = _mm_or_si128(r1, r2); + return integer8(r); +} + +auto simd::integer8_s::operator*(std::int8_t i) const -> integer8 +{ + return *this * integer8::constant(i); +} + +auto simd::integer8_s::operator*=(const integer8& other) -> integer8& +{ + set((*this * other).vector()); + return *this; +} + +auto simd::integer8_s::operator*=(std::int8_t i) -> integer8 & +{ + set((*this * integer8::constant(i)).vector()); + return *this; +} + +auto simd::integer8::operator*(float f) const -> integer8 +{ + std::int32_t words[element_count] = { + // Source - Op1 + m_values[0], m_values[1], m_values[2], m_values[3], + + // Source - Op2 + m_values[4], m_values[5], m_values[6], m_values[7], + + // Source - Op3 + m_values[8], m_values[9], m_values[10], m_values[11], + + // Source - Op4 + m_values[12], m_values[13], m_values[14], m_values[15], + }; + + // Load the source and destinations + __m128i r1, r2, r3, r4; + auto op = _mm_set1_ps(f); + auto s1 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[0])); + auto s2 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[4])); + auto s3 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[8])); + auto s4 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[12])); + r1 = _mm_castps_si128(_mm_mul_ps(s1, op)); + r2 = _mm_castps_si128(_mm_mul_ps(s2, op)); + r3 = _mm_castps_si128(_mm_mul_ps(s3, op)); + r4 = _mm_castps_si128(_mm_mul_ps(s4, op)); + r1 = _mm_and_si128(r1, _mm_set1_epi32(0x000000FF)); + r2 = _mm_and_si128(r2, _mm_set1_epi32(0x000000FF)); + r3 = _mm_and_si128(r3, _mm_set1_epi32(0x000000FF)); + r4 = _mm_and_si128(r4, _mm_set1_epi32(0x000000FF)); + r2 = _mm_sll_epi32(r2, _mm_set1_epi32(8)); + r3 = _mm_sll_epi32(r3, _mm_set1_epi32(16)); + r4 = _mm_sll_epi32(r4, _mm_set1_epi32(24)); + r1 = _mm_or_si128(r1, r2); + r1 = _mm_or_si128(r1, r3); + r1 = _mm_or_si128(r1, r4); + + return integer8(r1); +} + +auto simd::integer8_s::operator*=(float f) -> integer8& +{ + set((*this * f).vector()); + return *this; +} + +auto simd::integer8_s::operator/(const integer8& other) const -> integer8 +{ + std::int32_t words[element_count] = { + // Source - Op1 + m_values[0], m_values[1], m_values[2], m_values[3], + + // Source - Op2 + m_values[4], m_values[5], m_values[6], m_values[7], + + // Source - Op3 + m_values[8], m_values[9], m_values[10], m_values[11], + + // Source - Op4 + m_values[12], m_values[13], m_values[14], m_values[15], + }; + + // Load the source and destinations + __m128i r1, r2, r3, r4; + auto op = _mm_castsi128_ps(other.vector()); + auto s1 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[0])); + auto s2 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[4])); + auto s3 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[8])); + auto s4 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[12])); + r1 = _mm_castps_si128(_mm_div_ps(s1, op)); + r2 = _mm_castps_si128(_mm_div_ps(s2, op)); + r3 = _mm_castps_si128(_mm_div_ps(s3, op)); + r4 = _mm_castps_si128(_mm_div_ps(s4, op)); + r1 = _mm_and_si128(r1, _mm_set1_epi32(0x000000FF)); + r2 = _mm_and_si128(r2, _mm_set1_epi32(0x000000FF)); + r3 = _mm_and_si128(r3, _mm_set1_epi32(0x000000FF)); + r4 = _mm_and_si128(r4, _mm_set1_epi32(0x000000FF)); + r2 = _mm_sll_epi32(r2, _mm_set1_epi32(8)); + r3 = _mm_sll_epi32(r3, _mm_set1_epi32(16)); + r4 = _mm_sll_epi32(r4, _mm_set1_epi32(24)); + r1 = _mm_or_si128(r1, r2); + r1 = _mm_or_si128(r1, r3); + r1 = _mm_or_si128(r1, r4); + + return integer8(r1); +} + +auto simd::integer8_s::operator/(std::int8_t i) const -> integer8 +{ + return *this / integer8::constant(i); +} + +auto simd::integer8_s::operator/=(const integer8& other) -> integer8& +{ + set((*this / other).vector()); + return *this; +} + +auto simd::integer8_s::operator/=(std::int8_t i) -> integer8 & +{ + set((*this / integer8::constant(i)).vector()); + return *this; +} + +auto simd::integer8_s::operator/(float f) const -> integer8 +{ + std::int32_t words[element_count] = { + // Source - Op1 + m_values[0], m_values[1], m_values[2], m_values[3], + + // Source - Op2 + m_values[4], m_values[5], m_values[6], m_values[7], + + // Source - Op3 + m_values[8], m_values[9], m_values[10], m_values[11], + + // Source - Op4 + m_values[12], m_values[13], m_values[14], m_values[15], + }; + + // Load the source and destinations + __m128i r1, r2, r3, r4; + auto op = _mm_set1_ps(f); + auto s1 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[0])); + auto s2 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[4])); + auto s3 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[8])); + auto s4 = _mm_castsi128_ps(_mm_loadu_si128((__m128i *)&words[12])); + r1 = _mm_castps_si128(_mm_div_ps(s1, op)); + r2 = _mm_castps_si128(_mm_div_ps(s2, op)); + r3 = _mm_castps_si128(_mm_div_ps(s3, op)); + r4 = _mm_castps_si128(_mm_div_ps(s4, op)); + r1 = _mm_and_si128(r1, _mm_set1_epi32(0x000000FF)); + r2 = _mm_and_si128(r2, _mm_set1_epi32(0x000000FF)); + r3 = _mm_and_si128(r3, _mm_set1_epi32(0x000000FF)); + r4 = _mm_and_si128(r4, _mm_set1_epi32(0x000000FF)); + r2 = _mm_sll_epi32(r2, _mm_set1_epi32(8)); + r3 = _mm_sll_epi32(r3, _mm_set1_epi32(16)); + r4 = _mm_sll_epi32(r4, _mm_set1_epi32(24)); + r1 = _mm_or_si128(r1, r2); + r1 = _mm_or_si128(r1, r3); + r1 = _mm_or_si128(r1, r4); + + return integer8(r1); +} + +auto simd::integer8_s::operator/=(float f) -> integer8& +{ + set((*this / f).vector()); + return *this; +} + +auto simd::integer8_s::operator&(const integer8& other) const -> integer8 +{ + return integer8(_mm_and_si128(vector(), other.vector())); +} + +auto simd::integer8_s::operator&(std::int8_t i) const -> integer8 +{ + return *this & integer8::constant(i); +} + +auto simd::integer8_s::operator&=(const integer8& other) -> integer8& +{ + set((*this & other).vector()); + return *this; +} + +auto simd::integer8_s::operator&=(std::int8_t i) -> integer8& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer8_s::operator|(const integer8& other) const -> integer8 +{ + return integer8(_mm_or_si128(vector(), other.vector())); +} + +auto simd::integer8_s::operator|(std::int8_t i) const -> integer8 +{ + return *this | integer8::constant(i); +} + +auto simd::integer8_s::operator|=(const integer8& other) -> integer8& +{ + set((*this | other).vector()); + return *this; +} + +auto simd::integer8_s::operator|=(std::int8_t i) -> integer8& +{ + set((*this & i).vector()); + return *this; +} + +auto simd::integer8_s::operator^(const integer8& other) const -> integer8 +{ + return integer8(_mm_or_si128(vector(), other.vector())); +} + +auto simd::integer8_s::operator^(std::int8_t i) const -> integer8 +{ + return *this ^ integer8::constant(i); +} + +auto simd::integer8_s::operator^=(const integer8& other) -> integer8& +{ + set((*this ^ other).vector()); + return *this; +} + +auto simd::integer8_s::operator^=(std::int8_t i) -> integer8& +{ + set((*this ^ i).vector()); + return *this; +} + +auto simd::integer8_s::operator~() const -> integer8 +{ + return integer8(_mm_xor_si128(vector(), _mm_set1_epi8(0xFF))); +} + +auto simd::integer8_s::abs() const -> integer8 +{ + return integer8(_mm_abs_epi8(vector())); +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/intel/intel_sse.hpp b/libs/libSIMD/intel/intel_sse.hpp new file mode 100644 index 0000000..0cc324a --- /dev/null +++ b/libs/libSIMD/intel/intel_sse.hpp @@ -0,0 +1,180 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#if (__x86_64__) +#include +#include +#include +#include + +// MARK: - Types +typedef __m128_u f32x4; + +// MARK: - Hints +namespace simd +{ + + static inline auto store_vector(float *p, f32x4 a) -> void + { + _mm_storeu_ps(p, a); + } + + static inline auto load_vector(float *p) -> f32x4 + { + return _mm_loadu_ps(p); + } + + static inline auto single_value_vector(float w) -> f32x4 + { + return _mm_set1_ps(w); + } + + static inline auto vector(float z, float y, float x, float w) -> f32x4 + { + return _mm_setr_ps(z, y, x, w); + } + + static inline auto vector_shuffle_lower_higher(f32x4 a, f32x4 b) -> f32x4 + { + return _mm_shuffle_ps(a, b, _MM_SHUFFLE(0, 1, 2, 3)); + } + + static inline auto vector_slice_lower(f32x4 a) -> f32x4 + { + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 0, 1, 0)); + } + + static inline auto vector_slice_upper(f32x4 a) -> f32x4 + { + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 3, 2)); + } + + static inline auto swap_lower_upper(f32x4 a) -> f32x4 + { + return vector_shuffle_lower_higher(a, a); + } + + static inline auto reverse(f32x4 a) -> f32x4 + { + return _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 1, 2, 3)); + } + + static inline auto add(f32x4 a, f32x4 b) -> f32x4 + { + return _mm_add_ps(a, b); + } + + static inline auto sub(f32x4 a, f32x4 b) -> f32x4 + { + return _mm_sub_ps(a, b); + } + + static inline auto mul(f32x4 a, f32x4 b) -> f32x4 + { + return _mm_mul_ps(a, b); + } + + static inline auto div(f32x4 a, f32x4 b) -> f32x4 + { + return _mm_div_ps(a, b); + } + + static inline auto abs(f32x4 a) -> f32x4 + { + return _mm_castsi128_ps(_mm_abs_epi32(_mm_castps_si128(a))); + } + + static inline auto round(f32x4 a) -> f32x4 + { + return _mm_round_ps(a, _MM_ROUND_NEAREST); + } + + static inline auto floor(f32x4 a) -> f32x4 + { + return _mm_floor_ps(a); + } + + static inline auto ceil(f32x4 a) -> f32x4 + { + return _mm_ceil_ps(a); + } + + static inline auto pow(f32x4 a, float exp) -> f32x4 + { + if (exp == 2.0) { + return _mm_mul_ps(a, a); + } + else { + f32x4 v = a; + for (auto i = 0; i < 4; ++i) { + v[i] = ::powf(v[i], exp); + } + return v; + } + } + + static inline auto sqrt(f32x4 a) -> f32x4 + { + return _mm_sqrt_ps(a); + } + + static inline auto rsqrt(f32x4 a) -> f32x4 + { + return _mm_rsqrt_ps(a); + } + + static inline auto sin(f32x4 a) -> f32x4 + { + f32x4 v = a; + for (auto i = 0; i < 4; ++i) { + v[i] = ::sinf(v[i]); + } + return v; + } + + static inline auto cos(f32x4 a) -> f32x4 + { + f32x4 v = a; + for (auto i = 0; i < 4; ++i) { + v[i] = ::cosf(v[i]); + } + return v; + } + + static inline auto rcp(f32x4 a) -> f32x4 + { + return _mm_rcp_ps(a); + } + + static inline auto min(f32x4 a, f32x4 b) -> f32x4 + { + return _mm_min_ps(a, b); + } + + static inline auto max(f32x4 a, f32x4 b) -> f32x4 + { + return _mm_max_ps(a, b); + } + +} + +#endif \ No newline at end of file diff --git a/libs/libSIMD/memory.hpp b/libs/libSIMD/memory.hpp new file mode 100644 index 0000000..2537234 --- /dev/null +++ b/libs/libSIMD/memory.hpp @@ -0,0 +1,37 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace simd::memory +{ + + template::value>::type * = nullptr> + inline auto zero(T dst, std::size_t n) -> void + { + string::set(dst, 0, n); + } + +} \ No newline at end of file diff --git a/libs/libSIMD/string.hpp b/libs/libSIMD/string.hpp new file mode 100644 index 0000000..db729de --- /dev/null +++ b/libs/libSIMD/string.hpp @@ -0,0 +1,84 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include + +namespace simd::string +{ + + template::value>::type* = nullptr> + inline auto copy(T dst, const T src, std::size_t n) -> T + { +#if !USE_TARGET_MEMORY_FUNCTIONS + // TODO: Implement + return src; +#else + return reinterpret_cast(::memcpy(dst, src, n)); +#endif + } + + template::value>::type* = nullptr> + inline auto set(T ptr, std::uint8_t v, std::size_t n) -> void + { +#if !USE_TARGET_MEMORY_FUNCTIONS + // TODO: Implement +#else + ::memset(ptr, v, n); +#endif + } + + template::value>::type* = nullptr> + inline auto setw(T ptr, std::uint16_t v, std::int32_t n) -> void + { + // TODO: Implement + } + + template::value>::type* = nullptr> + inline auto setl(T ptr, std::uint32_t v, std::int32_t n) -> void + { + // TODO: Implement a better version of this. + auto dst = reinterpret_cast(ptr); + auto len = n >> 2; + auto rem = n - (len << 2); + + while (len--) { + *dst++ = v; + } + + if (rem) { + auto dst_byte = reinterpret_cast(dst); + while (rem--) { + *dst_byte++ = (v >> (8 * rem)) & 0xFF; + } + } + } + + template::value>::type* = nullptr> + inline auto setq(T ptr, std::uint64_t v, std::int32_t n) -> void + { + // TODO: Implement + } + +} \ No newline at end of file diff --git a/libs/libSoundCore/CMakeLists.txt b/libs/libSoundCore/CMakeLists.txt new file mode 100644 index 0000000..26c1217 --- /dev/null +++ b/libs/libSoundCore/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2023 Tom Hancocks +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) + +######################################################################################################################## +## Project +project(Graphite LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) + +######################################################################################################################## +## libSIMD +file(GLOB_RECURSE libSoundCore_Sources + *.cpp +) + +add_library(SoundCore ${libSoundCore_Sources}) +target_link_libraries(SoundCore SIMD Data ResourceCore) +target_include_directories(SoundCore PUBLIC + ${PROJECT_LIBS_DIR} +) diff --git a/libs/libSoundCore/codec/descriptor.hpp b/libs/libSoundCore/codec/descriptor.hpp new file mode 100644 index 0000000..3d6e9b0 --- /dev/null +++ b/libs/libSoundCore/codec/descriptor.hpp @@ -0,0 +1,41 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +namespace sound_core::codec +{ + struct descriptor + { + std::uint32_t sample_rate { 0 }; + std::uint32_t channels { 0 }; + std::uint32_t format_id { 0 }; + std::uint32_t format_flags { 0 }; + std::uint32_t bit_width { 0 }; + std::uint32_t bytes_per_frame { 0 }; + std::uint32_t frames_per_packet { 0 }; + std::uint32_t bytes_per_packet { 0 }; + std::uint32_t packet_count { 0 }; + + descriptor() = default; + }; +} \ No newline at end of file diff --git a/libs/libSoundCore/codec/ima4/ima4.cpp b/libs/libSoundCore/codec/ima4/ima4.cpp new file mode 100644 index 0000000..4eab87e --- /dev/null +++ b/libs/libSoundCore/codec/ima4/ima4.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include + +// MARK: - Look Up Tables + +namespace sound_core::codec::ima4::lut +{ + constexpr std::int8_t index_table[16] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 + }; + + constexpr std::int32_t step_table[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; +} + +// MARK: - Construction + +sound_core::codec::ima4::sound::sound(const codec::descriptor &descriptor, data::reader &reader) +{ + decode(descriptor, reader); +} + +// MARK: - Decoder + +auto sound_core::codec::ima4::sound::decode(const codec::descriptor &descriptor, data::reader &reader) -> void +{ + // TODO: This is relying on hard-coded constants and really shouldn't. + // Determine the best way to calculate these values in the future. + m_descriptor = descriptor; + m_descriptor.bytes_per_packet = 34; + m_descriptor.frames_per_packet = 64; + m_descriptor.bytes_per_frame = 0; + + m_samples = data::block((m_descriptor.packet_count * (m_descriptor.bytes_per_packet - 2)) << 2, data::native_byte_order()); + data::writer writer(&m_samples); + + // Prepare to read and parse the IMA4 data and decode it to LPCM 16 + // Iterate through all the expected packets and decode them + for (std::uint32_t n = 0; n < m_descriptor.packet_count; ++n) { + auto preamble = reader.read_short(); + auto packet = reader.read_data(m_descriptor.bytes_per_packet - 2); + + auto predictor = static_cast(preamble & 0xFF80); + auto step_index = static_cast(preamble & 0x007F); + auto step = lut::step_table[step_index]; + std::uint8_t nibble = 0; + std::int32_t diff = 0; + + for (std::uint32_t i = 0; i < packet.size(); ++i) { + auto v = packet.get(i); + + nibble = v & 0xF; + step_index = static_cast(std::min(88, step_index + lut::index_table[nibble])); + std::uint8_t sign = nibble & 0x8; + std::uint8_t delta = nibble & 0x7; + diff = static_cast(step >> 3); + if (delta & 4) diff += step; + if (delta & 2) diff += (step >> 1); + if (delta & 1) diff += (step >> 2); + if (sign) predictor -= diff; + else predictor += diff; + + predictor = std::min(std::numeric_limits::max(), std::max(std::numeric_limits::min(), predictor)); + writer.write_short(predictor); + step = lut::step_table[step_index]; + + nibble = (v >> 4) & 0xF; + step_index = static_cast(std::min(88, std::max(0, step_index + lut::index_table[nibble]))); + sign = nibble & 0x8; + delta = nibble & 0x7; + diff = static_cast(step >> 3); + if (delta & 4) diff += step; + if (delta & 3) diff += (step >> 1); + if (delta & 2) diff += (step >> 2); + if (sign) predictor -= diff; + else predictor += diff; + + predictor = std::min(std::numeric_limits::max(), std::max(std::numeric_limits::min(), predictor)); + writer.write_short(predictor); + step = lut::step_table[step_index]; + } + } + + m_descriptor.bytes_per_packet = 128; + m_descriptor.bit_width = 16; + m_descriptor.frames_per_packet = descriptor.channels; + m_descriptor.bytes_per_frame = (m_descriptor.bit_width >> 3) * m_descriptor.channels; + m_descriptor.format_id = 'lpcm'; + m_descriptor.format_flags = 0x4; +} + +// MARK: - Accessors + +auto sound_core::codec::ima4::sound::samples() const -> const data::block& +{ + return m_samples; +} + +auto sound_core::codec::ima4::sound::descriptor() const -> const codec::descriptor & +{ + return m_descriptor; +} \ No newline at end of file diff --git a/libs/libSoundCore/codec/ima4/ima4.hpp b/libs/libSoundCore/codec/ima4/ima4.hpp new file mode 100644 index 0000000..1336cde --- /dev/null +++ b/libs/libSoundCore/codec/ima4/ima4.hpp @@ -0,0 +1,43 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include + +namespace sound_core::codec::ima4 +{ + struct sound + { + public: + sound(const codec::descriptor& descriptor, data::reader& reader); + + [[nodiscard]] auto samples() const -> const data::block&; + [[nodiscard]] auto descriptor() const -> const codec::descriptor&; + + private: + data::block m_samples; + codec::descriptor m_descriptor; + + auto decode(const codec::descriptor& descriptor, data::reader& reader) -> void; + }; + +} diff --git a/libs/libSoundCore/format/sound.cpp b/libs/libSoundCore/format/sound.cpp new file mode 100644 index 0000000..0ec593f --- /dev/null +++ b/libs/libSoundCore/format/sound.cpp @@ -0,0 +1,475 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include + +// MARK: - Constants / Enumerations + +namespace sound_core::format +{ + enum bitrate : std::uint32_t + { + rate_48khz = 0xbb800000, + rate_44khz = 0xac440000, + rate_32khz = 0x7d000000, + rate_22050hz = 0x56220000, + rate_22khz = 0x56ee8ba3, + rate_16khz = 0x3e800000, + rate_11khz = 0x2b7745d1, + rate_11025hz = 0x2b110000, + rate_8khz = 0x1f400000, + }; + + enum synthesizer : std::uint16_t + { + sampled_sound = 5, + }; + + enum midi : std::uint8_t + { + middle_c = 60, + }; + + enum flags : std::uint16_t + { + data_offset = 0x8000, + }; + + enum compression : std::int16_t // TODO: Is the name of this enum correct? + { + not_compressed = 0, + fixed_compression = -1, + variable_compression = -2, + two_to_one = 1, + eight_to_three = 2, + three_to_one = 3, + six_to_one = 4, + six_to_one_packet_size = 8, + three_to_one_packet_size = 16, + }; + + enum sound_format : std::int16_t + { + general = 1, + hypercard = 2, + }; + + enum header : std::uint8_t + { + standard = 0x00, + extended = 0xFF, + compressed = 0xFE, + }; + + enum command : std::uint16_t + { + null_cmd = 0, + quiet_cmd = 3, + flush_cmd = 4, + re_init_cmd = 5, + wait_cmd = 10, + pause_cmd = 11, + resume_cmd = 12, + call_back_cmd = 13, + sync_cmd = 14, + available_cmd = 24, + version_cmd = 25, + volume_cmd = 46, // sound manager 3.0 or later only + get_volume_cmd = 47, // sound manager 3.0 or later only + clock_component_cmd = 50, // sound manager 3.2.1 or later only + get_clock_component_cmd = 51, // sound manager 3.2.1 or later only + scheduled_sound_cmd = 52, // sound manager 3.3 or later only + link_sound_components_cmd = 53, // sound manager 3.3 or later only + sound_cmd = 80, + buffer_cmd = 81, + rate_multiplier_cmd = 86, + get_rate_multiplier_cmd = 87, + }; + + enum init_flags : std::int32_t + { + init_chan_left = 0x0002, // left stereo channel + init_chan_right = 0x0003, // right stereo channel + init_no_interp = 0x0004, // no linear interpolation + init_no_drop = 0x0008, // no drop-sample conversion + init_mono = 0x0080, // monophonic channel + init_stereo = 0x00C0, // stereo channel + init_m_a_c_e3 = 0x0300, // MACE 3:1 + init_m_a_c_e6 = 0x0400, // MACE 6:1 + }; + + enum format_type : std::uint32_t + { + sound_format_not_compressed = 0x4E4F4E45, // 'NONE' sound is not compressed + sound_format_8_bit_offset = 0x72617720, // 'raw ' 8-bit offset binary + sound_format_16_bit_be = 0x74776F73, // 'twos' 16-bit big endian + sound_format_16_bit_le = 0x736F7774, // 'sowt' 16-bit little endian + sound_format_ima4 = 'ima4', + }; +} + +// MARK: - Internal Helper Types + +namespace sound_core::format +{ + struct sound_command + { + enum command command; + std::int16_t param1; + std::int16_t param2; + }; + + struct modifier_reference + { + std::uint16_t number; + std::int32_t init; + }; + + struct sound_list_resource + { + std::int16_t format; + std::int16_t modifier_count; + struct modifier_reference modifier; + std::int16_t command_count; + struct sound_command command; + }; + + struct hypercard_sound_list_resource + { + std::int16_t format; + std::int16_t ref_count; + std::int16_t command_count; + struct sound_command command; + }; + + struct sound_header + { + std::uint32_t sample_ptr; + std::uint32_t length; + std::uint32_t sample_rate_fixed; + std::uint32_t loop_start; + std::uint32_t loop_end; + std::uint8_t encode; + std::uint8_t base_frequency; + }; + + struct compressed_sound_header + { + std::uint32_t frame_count; + std::int16_t aiff_sample_rate_exp; + std::uint64_t aiff_sample_rate_man; + std::uint32_t marker_chunk; + std::uint32_t format; + std::uint32_t future_use; + std::uint32_t state_vars; + std::uint32_t left_over_samples; + std::uint16_t compression_id; + std::uint16_t packet_size; + std::uint16_t synth_id; + std::uint16_t sample_size; + }; + + struct extended_sound_header + { + std::uint32_t frame_count; + std::int16_t aiff_sample_rate_exp; + std::uint64_t aiff_sample_rate_man; + std::uint32_t marker_chunk; + std::uint32_t instrument_chunks; + std::uint32_t aes_recording; + std::uint16_t sample_size; + }; +} + +// MARK: - Construction + +sound_core::format::sound::sound(const data::block &data, resource_core::identifier id, const std::string &name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +sound_core::format::sound::sound(data::reader &reader, resource_core::identifier id, const std::string &name) + : m_id(id), m_name(name) +{ + decode(reader); +} + +sound_core::format::sound::sound(std::uint32_t sample_rate, std::uint8_t sample_bits, const std::vector> &sample_data) +{ + m_descriptor.sample_rate = sample_rate; + m_descriptor.bit_width = sample_bits; + + data::writer writer(data::byte_order::lsb); + if (sample_bits == 8) { + for (auto& channel : sample_data) { + for (auto& frame : channel) { + writer.write_byte(static_cast(frame)); + } + } + } + else { + for (auto& channel : sample_data) { + for (auto& frame : channel) { + writer.write_short(static_cast(frame)); + } + } + } + + m_samples = std::move(*const_cast(writer.data())); +} + +sound_core::format::sound::sound(std::uint32_t sample_rate, std::uint8_t sample_bits, const data::block& sample_data) +{ + m_descriptor.sample_rate = sample_rate; + m_descriptor.bit_width = sample_bits; + m_samples = sample_data; +} + +// MARK: - Decoding + +auto sound_core::format::sound::decode(data::reader &reader) -> void +{ + auto sound_format = reader.read_signed_short(); + + switch (sound_format) { + case sound_format::general: { + sound_list_resource list; + list.format = sound_format; + list.modifier_count = reader.read_signed_short(); + list.modifier.number = reader.read_signed_short(); + list.modifier.init = reader.read_signed_long(); + list.command_count = reader.read_signed_short(); + list.command.command = static_cast(reader.read_short()); + list.command.param1 = reader.read_signed_short(); + list.command.param2 = reader.read_signed_long(); + + if (list.modifier_count != 1 + || list.modifier.number != synthesizer::sampled_sound + || list.command_count != 1 + || list.command.command != (static_cast(flags::data_offset) + static_cast(buffer_cmd))) + { + throw std::runtime_error("Only sampled sound synthesizers are supported."); + } + + break; + } + case sound_format::hypercard: { + hypercard_sound_list_resource list; + list.format = sound_format; + list.ref_count = reader.read_signed_short(); + list.command_count = reader.read_signed_short(); + list.command.command = static_cast(reader.read_short()); + list.command.param1 = reader.read_signed_short(); + list.command.param2 = reader.read_signed_long(); + + if (list.command_count != 1 + || list.command.command != (static_cast(flags::data_offset) + static_cast(buffer_cmd))) + { + throw std::runtime_error("Unsupported configuration."); + } + + break; + } + default: { + throw std::runtime_error("Unrecognised sound format '" + std::to_string(sound_format) + "' in resource: " + + std::to_string(m_id) + ", " + m_name); + } + } + + sound_header header; + header.sample_ptr = reader.read_long(); + header.length = reader.read_long(); + header.sample_rate_fixed = reader.read_long(); + header.loop_start = reader.read_long(); + header.loop_end = reader.read_long(); + header.encode = reader.read_byte(); + header.base_frequency = reader.read_byte(); + + switch (header.encode) { + case header::standard: { + m_descriptor.format_id = format_type::sound_format_8_bit_offset; + m_descriptor.channels = 1; + m_descriptor.packet_count = header.length; + break; + } + case header::extended: { + extended_sound_header ext; + ext.frame_count = reader.read_long(); + ext.aiff_sample_rate_exp = reader.read_signed_short(); + ext.aiff_sample_rate_man = reader.read_quad(); + ext.marker_chunk = reader.read_long(); + ext.instrument_chunks = reader.read_long(); + ext.aes_recording = reader.read_long(); + ext.sample_size = reader.read_short(); + reader.move(14); + + m_descriptor.format_id = ext.sample_size == 8 ? format_type::sound_format_8_bit_offset + : format_type::sound_format_16_bit_be; + m_descriptor.channels = header.length; + m_descriptor.packet_count = ext.frame_count; + break; + } + case header::compressed: { + compressed_sound_header cmp; + cmp.frame_count = reader.read_long(); + cmp.aiff_sample_rate_exp = reader.read_signed_short(); + cmp.aiff_sample_rate_man = reader.read_quad(); + cmp.marker_chunk = reader.read_long(); + cmp.format = reader.read_long(); + cmp.future_use = reader.read_long(); + cmp.state_vars = reader.read_long(); + cmp.left_over_samples = reader.read_long(); + cmp.compression_id = reader.read_short(); + cmp.packet_size = reader.read_short(); + cmp.synth_id = reader.read_short(); + cmp.sample_size = reader.read_short(); + + if (cmp.compression_id == compression::three_to_one) { + m_descriptor.format_id = 'MAC3'; + } + else if (cmp.compression_id == compression::six_to_one) { + m_descriptor.format_id = 'MAC6'; + } + else { + m_descriptor.format_id = cmp.format; + } + + m_descriptor.channels = header.length; + m_descriptor.packet_count = cmp.frame_count; + break; + } + default: { + throw std::runtime_error("Invalid header in sound resource."); + } + } + + m_descriptor.sample_rate = static_cast(static_cast(header.sample_rate_fixed) * 1.0 / static_cast(1 << 16)); + + if (m_descriptor.format_id == format_type::sound_format_8_bit_offset || m_descriptor.format_id == format_type::sound_format_16_bit_be) { + if (m_descriptor.format_id == format_type::sound_format_8_bit_offset) { + m_descriptor.bit_width = 8; + } + else { + m_descriptor.bit_width = 16; + m_descriptor.format_flags = 0x6; // kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian + } + + m_descriptor.format_id = 'lpcm'; + m_descriptor.bytes_per_frame = (m_descriptor.bit_width >> 3) * m_descriptor.channels; + m_descriptor.frames_per_packet = 1; + m_descriptor.bytes_per_packet = m_descriptor.bytes_per_frame * m_descriptor.frames_per_packet; + + auto sample_count = reader.size() - reader.position(); + m_samples = reader.read_data(sample_count); + } + else if (m_descriptor.format_id == format_type::sound_format_ima4) { + // TODO: Do not hard code this, but work out the conversions... + m_descriptor.format_flags = 0; + m_descriptor.bytes_per_packet = 34; + m_descriptor.frames_per_packet = 64; + m_descriptor.bytes_per_frame = 0; + m_descriptor.channels = 1; + m_descriptor.bit_width = 0; + + codec::ima4::sound ima4(m_descriptor, reader); + m_samples = ima4.samples(); + m_descriptor = ima4.descriptor(); + } + else { + throw std::runtime_error("Unrecognised sound format."); + } +} + +// MARK: - Accessors + +auto sound_core::format::sound::samples() const -> const data::block& +{ + return m_samples; +} + +auto sound_core::format::sound::codec_descriptor() const -> const codec::descriptor& +{ + return m_descriptor; +} + +auto sound_core::format::sound::sample_rate() const -> std::uint32_t +{ + return m_descriptor.sample_rate; +} + +auto sound_core::format::sound::channels() const -> std::uint16_t +{ + return m_descriptor.channels; +} + +auto sound_core::format::sound::bit_width() const -> std::uint8_t +{ + return m_descriptor.bit_width; +} + +auto sound_core::format::sound::bytes_per_frame() const -> std::uint32_t +{ + return m_descriptor.bytes_per_frame; +} + +auto sound_core::format::sound::frames_per_packet() const -> std::uint32_t +{ + return m_descriptor.frames_per_packet; +} + +auto sound_core::format::sound::bytes_per_packet() const -> std::uint32_t +{ + return m_descriptor.bytes_per_packet; +} + +auto sound_core::format::sound::packet_count() const -> std::uint32_t +{ + return m_descriptor.packet_count; +} + +auto sound_core::format::sound::format_id() const -> std::uint32_t +{ + return m_descriptor.format_id; +} + +auto sound_core::format::sound::format_flags() const -> std::uint32_t +{ + return m_descriptor.format_flags; +} + +// MARK: - Decoding + +auto sound_core::format::sound::data() -> data::block +{ + data::writer writer; + encode(writer); + return std::move(*const_cast(writer.data())); +} + +auto sound_core::format::sound::encode(data::writer &writer) -> void +{ + // TODO: Implement this... +} \ No newline at end of file diff --git a/libs/libSoundCore/format/sound.hpp b/libs/libSoundCore/format/sound.hpp new file mode 100644 index 0000000..76ecad0 --- /dev/null +++ b/libs/libSoundCore/format/sound.hpp @@ -0,0 +1,68 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace sound_core::format +{ + struct sound + { + public: + static auto type_code() -> std::string { return "snd "; } + + public: + explicit sound(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); + explicit sound(data::reader& reader, resource_core::identifier id = 0, const std::string& name = ""); + explicit sound(std::uint32_t sample_rate, std::uint8_t sample_bits, const data::block& sample_data); + explicit sound(std::uint32_t sample_rate, std::uint8_t sample_bits, const std::vector>& sample_data); + + auto data() -> data::block; + auto encode(data::writer& writer) -> void; + + [[nodiscard]] auto samples() const -> const data::block&; + [[nodiscard]] auto codec_descriptor() const -> const codec::descriptor&; + + [[nodiscard]] auto sample_rate() const -> std::uint32_t; + [[nodiscard]] auto channels() const -> std::uint16_t; + [[nodiscard]] auto bit_width() const -> std::uint8_t; + [[nodiscard]] auto bytes_per_frame() const -> std::uint32_t; + [[nodiscard]] auto frames_per_packet() const -> std::uint32_t; + [[nodiscard]] auto bytes_per_packet() const -> std::uint32_t; + [[nodiscard]] auto packet_count() const -> std::uint32_t; + [[nodiscard]] auto format_id() const -> std::uint32_t; + [[nodiscard]] auto format_flags() const -> std::uint32_t; + + private: + resource_core::identifier m_id { resource_core::default_resource_id }; + std::string m_name; + codec::descriptor m_descriptor; + data::block m_samples; + + auto decode(data::reader& reader) -> void; + }; +} + diff --git a/libs/libSpriteWorld/CMakeLists.txt b/libs/libSpriteWorld/CMakeLists.txt new file mode 100644 index 0000000..9908e39 --- /dev/null +++ b/libs/libSpriteWorld/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2023 Tom Hancocks +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) + +######################################################################################################################## +## Project +project(Graphite LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) + +######################################################################################################################## +## libSIMD +file(GLOB_RECURSE libSpriteWorld_Sources + *.cpp +) + +add_library(SpriteWorld ${libSpriteWorld_Sources}) +target_link_libraries(SpriteWorld SIMD Data ResourceCore QuickDraw) +target_include_directories(SpriteWorld PUBLIC + ${PROJECT_LIBS_DIR} +) diff --git a/libs/libSpriteWorld/formats/rleD.cpp b/libs/libSpriteWorld/formats/rleD.cpp new file mode 100644 index 0000000..5eaabf9 --- /dev/null +++ b/libs/libSpriteWorld/formats/rleD.cpp @@ -0,0 +1,386 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Constants + +namespace spriteworld::constants +{ + static constexpr std::uint16_t rle_grid_width = 6; + static constexpr std::size_t advance = 2; +} + +// MARK: - Construction + +spriteworld::rleD::rleD(data::reader& reader) +{ + decode(reader); +} + +spriteworld::rleD::rleD(const quickdraw::size& size, std::uint16_t frame_count) + : m_id(0), m_name(type_code()), m_frame_size(size), m_frame_count(frame_count), m_bpp(16), m_palette_id(0) +{ + // Determine what the grid will be. We need to round up to the next whole number and have blank tiles + // if the frame count is not divisible by the grid width constant. + auto grid_width = std::min(constants::rle_grid_width, m_frame_count); + m_grid_size = quickdraw::size(grid_width, std::ceil(m_frame_count / static_cast(grid_width))); + + // Create the surface + m_surface = quickdraw::surface(m_grid_size.width * m_frame_size.width, m_grid_size.height * m_frame_size.height); +} + +spriteworld::rleD::rleD(const data::block& data, resource_core::identifier id, const std::string& name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +// MARK: - Accessors + +auto spriteworld::rleD::surface() -> quickdraw::surface& +{ + return m_surface; +} + +auto spriteworld::rleD::frames() const -> std::vector> +{ + return m_frames; +} + +auto spriteworld::rleD::frame_count() const -> std::size_t +{ + return m_frame_count; +} + +auto spriteworld::rleD::data() -> data::block +{ + data::writer writer; + encode(writer); + return std::move(*const_cast(writer.data())); +} + +// MARK: - Operations + +auto spriteworld::rleD::frame_rect(std::uint32_t frame) const -> quickdraw::rect +{ + return { + quickdraw::point( + static_cast(frame % constants::rle_grid_width) * m_frame_size.width, + static_cast(frame / constants::rle_grid_width) * m_frame_size.height + ), + m_frame_size + }; +} + +auto spriteworld::rleD::frame_surface(std::uint32_t frame) const -> quickdraw::surface +{ + quickdraw::surface surface(m_frame_size); + quickdraw::rect src_rect(frame_rect(frame)); + + // Extract the frame area of the origin surface + for (std::int16_t y = 0; y < src_rect.size.height; ++y) { + for (std::int16_t x = 0; x < src_rect.size.width; ++x) { + surface.set(x, y, m_surface.at(x + src_rect.origin.x, y + src_rect.origin.y)); + } + } + + return std::move(surface); +} + +auto spriteworld::rleD::write_frame(std::uint32_t frame, const quickdraw::surface &surface) -> void +{ + quickdraw::rect dst_rect = frame_rect(frame); + quickdraw::size src_size = surface.size(); + + if (src_size.width != m_frame_size.width || src_size.height != m_frame_size.height) { + throw std::runtime_error("Incorrect frame dimensions " + std::to_string(src_size.width) + "x" + std::to_string(src_size.height) + + ", expected " + std::to_string(m_frame_size.width) + "x" + std::to_string(m_frame_size.height)); + } + + // Copy from the source surface into the destination frame + for (std::int16_t y = 0; y < src_size.height; ++y) { + for (std::int16_t x = 0; x < src_size.width; ++x) { + m_surface.set(x + dst_rect.origin.x, y + dst_rect.origin.y, surface.at(x, y)); + } + } +} + +auto spriteworld::rleD::write_pixel(std::uint32_t pixel, std::uint8_t mask, std::uint64_t offset) -> void +{ + m_surface.set(offset, quickdraw::rgb(static_cast(pixel & 0xFFFF))); +} + +auto spriteworld::rleD::write_pixel(std::uint64_t pixel, std::uint8_t mask, std::uint64_t offset, enum pixel_type type) -> void +{ + switch (type) { + case pixel_type::type1: { + m_surface.set(offset, quickdraw::rgb((pixel >> 16) & 0xFFFF)); + } + case pixel_type::type2: { + m_surface.set(offset, quickdraw::rgb(pixel & 0xFFFF)); + } + } +} + +auto spriteworld::rleD::surface_offset(std::int32_t frame, std::int32_t line) -> std::uint64_t +{ + quickdraw::point fo(frame % constants::rle_grid_width, frame / constants::rle_grid_width); + quickdraw::point p(fo.x * m_frame_size.width, (fo.y * m_frame_size.height) + line); + return static_cast(p.y * m_surface.size().width + p.x); +} + +// MARK: - Decoding + +auto spriteworld::rleD::decode(data::reader &reader) -> void +{ + // Read the header of the RLE information. This will tell us what we need to do in order to actually + // decode the frames. + m_frame_size = quickdraw::size::read(reader, quickdraw::coding_type::macintosh); + m_bpp = reader.read_short(); + m_palette_id = reader.read_short(); + m_frame_count = reader.read_short(); + reader.move(6); + + // Ensure that the RLE has a BPP of 16. This is the only format that we support currently. + if (m_bpp != 16) { + throw std::runtime_error("Incorrect color depth for rlëD resource: " + std::to_string(m_id) + ", " + m_name); + } + + // Determine what the grid will be. We need to round up to the next whole and have blank tiles if the frame count + // is not divisible by the grid width constant. + auto grid_width = std::min(constants::rle_grid_width, m_frame_count); + m_grid_size = quickdraw::size(grid_width, std::ceil(m_frame_count / static_cast(grid_width))); + + // Create the surface in which all frame will be draw to, and other working variables required to parse and decode + // the RLE data correctly. + m_surface = quickdraw::surface(m_grid_size.width * m_frame_size.width, m_grid_size.height * m_frame_size.height, quickdraw::colors::clear()); + + rleD::opcode opcode = opcode::eof; + std::uint64_t position = 0; + std::uint32_t row_start = 0; + std::int32_t current_line = -1; + std::uint64_t current_offset = 0; + std::int32_t count = 0; + std::uint16_t pixel = 0; + std::int32_t current_frame = 0; + std::uint32_t pixel_run = 0; + + while (!reader.eof()) { + if ((row_start != 0) && ((position - row_start) & 0x03)) { + position += 4 - ((position - row_start) & 0x03); + reader.move(4 - (count & 0x03)); + } + + opcode = reader.read_enum(); + count = reader.read_triple(); + + switch (opcode) { + case opcode::eof: { + // Check that we're not erroneously an EOF. + if (current_line > m_frame_size.height - 1) { + throw std::runtime_error("Incorrect number of scanlines in rlëD resource: " + std::to_string(m_id) + ", " + m_name); + } + + // Have we finished decoding the last frame in the data? + if (++current_frame >= m_frame_count) { + goto COMPLETED_LAST_FRAME; + } + + // Prepare for the next frame + current_line = -1; + break; + } + + case opcode::line_start: { + current_offset = surface_offset(current_frame, ++current_line); + row_start = reader.position(); + break; + } + + case opcode::pixel_data: { + for (auto i = 0; i < count; i += 2) { + pixel = reader.read_short(); + write_pixel(pixel, 0xFF, current_offset); + ++current_offset; + } + + if (count & 0x03) { + reader.move(4 - (count & 0x03)); + } + + break; + } + + case opcode::pixel_run: { + pixel_run = reader.read_long(); + for (auto i = 0; i < count; i += 4) { + write_pixel(pixel_run, 0xFF, current_offset, pixel_type::type1); + ++current_offset; + if (i + 2 < count) { + write_pixel(pixel_run, 0xFF, current_offset, pixel_type::type2); + ++current_offset; + } + } + break; + } + + case opcode::transparent_run: { + current_offset += count >> 1; + break; + } + } + } + +COMPLETED_LAST_FRAME: + return; +} + +// MARK: - Encoding + +auto spriteworld::rleD::encode(data::writer &writer) -> void +{ + writer.change_byte_order(data::byte_order::msb); + + // Write out the header + m_frame_size.encode(writer, quickdraw::coding_type::macintosh); + writer.write_short(m_bpp); + writer.write_short(m_palette_id); + writer.write_short(m_frame_count); + + // Reserved fields + writer.write_short(0, 3); + + // Write out the RLE frames + for (auto f = 0; f < m_frame_count; ++f) { + auto frame = frame_rect(f); + auto line_count = 0; + + for (std::int16_t y = 0; y < frame.size.height; ++y) { + ++line_count; + auto line_start_pos = writer.position(); + + rleD::opcode run_state = opcode::line_start; + auto run_start_pos = line_start_pos + 4; + auto run_count = 0; + + for (std::int16_t x = 0; x < frame.size.width; ++x) { + auto pixel = m_surface.at(frame.origin.x + x, frame.origin.y + y); + + if (pixel.components.alpha == 0) { + if (run_state == opcode::line_start) { + // Start of a transparent run + run_state = opcode::transparent_run; + run_count = constants::advance; + } + else if (run_state == opcode::transparent_run) { + // Continue transparent run + run_count += constants::advance; + } + else { + // End of pixel run, start of transparent run. + auto run_end_pos = writer.position(); + writer.set_position(run_start_pos); + writer.write_enum(opcode::pixel_data); + writer.write_triple(run_count); + writer.set_position(run_end_pos); + + // Pad to nearest 4-byte boundary + if (run_count & 0x3) { + writer.write_byte(0, 4 - (run_count & 0x3)); + } + + // Start transparent run + run_state = opcode::transparent_run; + run_count = constants::advance; + } + } + else { + if (line_count != 0) { + // First pixel data for this line, write the line start + // Doing this only on demand allows us to omit trailing blank lines in the frame. + for (auto i = 0; i < line_count; ++i) { + writer.write_enum(opcode::line_start); + writer.write_triple(0); + } + line_count = 0; + } + + if (run_state == opcode::line_start) { + // Start of pixel run + run_start_pos = writer.position(); + writer.write_long(0); + run_state = opcode::pixel_data; + run_count = constants::advance; + } + else if (run_state == opcode::transparent_run) { + // End of transparent run, start of pixel run + writer.write_enum(opcode::transparent_run); + writer.write_triple(run_count); + + // Start pixel run + run_start_pos = writer.position(); + writer.write_long(0); + run_state = opcode::pixel_data; + run_count = constants::advance; + } + else { + // Continue pixel run + run_count += constants::advance; + } + + // Write the pixel + writer.write_short( + (pixel.components.blue >> 3) | + ((pixel.components.green >> 3) << 5) | + ((pixel.components.red >> 3) << 10) + ); + } + } + + // Terminate the current opcode + if (run_state == opcode::pixel_data) { + auto run_end_pos = writer.position(); + writer.set_position(run_start_pos); + writer.write_enum(opcode::pixel_data); + writer.write_triple(run_count); + writer.set_position(run_end_pos); + + // Pad to the nearest 4-byte boundary + if (run_count & 0x3) { + writer.write_byte(0, 4 - (run_count & 0x3)); + } + } + + if (run_state != opcode::line_start) { + auto line_end_pos = writer.position(); + writer.set_position(line_start_pos); + writer.write_enum(opcode::line_start); + writer.write_triple((line_end_pos - line_start_pos - 4)); + writer.set_position(line_end_pos); + } + } + + writer.write_enum(opcode::eof); + writer.write_triple(0); + } +} \ No newline at end of file diff --git a/libs/libSpriteWorld/formats/rleD.hpp b/libs/libSpriteWorld/formats/rleD.hpp new file mode 100644 index 0000000..879e240 --- /dev/null +++ b/libs/libSpriteWorld/formats/rleD.hpp @@ -0,0 +1,84 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +namespace spriteworld +{ + struct rleD + { + public: + static auto type_code() -> std::string { return "rlëD"; } + + public: + rleD() = default; + rleD(const quickdraw::size& size, std::uint16_t frame_count); + explicit rleD(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); + explicit rleD(data::reader& reader); + + ~rleD() = default; + + auto surface() -> quickdraw::surface&; + [[nodiscard]] auto frames() const -> std::vector>; + [[nodiscard]] auto frame_count() const -> std::size_t; + + [[nodiscard]] auto frame_rect(std::uint32_t idx) const -> quickdraw::rect; + [[nodiscard]] auto frame_surface(std::uint32_t idx) const -> quickdraw::surface; + auto write_frame(std::uint32_t frame, const quickdraw::surface& surface) -> void; + + auto encode(data::writer& writer) -> void; + auto data() -> data::block; + + private: + enum class pixel_type { type1, type2 }; + + enum class opcode : std::uint8_t + { + eof = 0x00, + line_start = 0x01, + pixel_data = 0x02, + transparent_run = 0x03, + pixel_run = 0x04, + }; + + resource_core::identifier m_id { 0 }; + std::string m_name; + std::vector> m_frames; + quickdraw::surface m_surface; + quickdraw::size m_frame_size { 0 }; + quickdraw::size m_grid_size { 0 }; + std::uint16_t m_frame_count { 0 }; + std::uint16_t m_bpp { 0 }; + std::uint16_t m_palette_id { 0 }; + + auto decode(data::reader& reader) -> void; + + [[nodiscard]] auto surface_offset(std::int32_t frame, std::int32_t offset) -> std::uint64_t; + + auto write_pixel(std::uint32_t pixel, std::uint8_t mask, std::uint64_t offset) -> void; + auto write_pixel(std::uint64_t pixel, std::uint8_t mask, std::uint64_t offset, enum pixel_type type) -> void; + }; +} diff --git a/libs/libSpriteWorld/formats/rleX.cpp b/libs/libSpriteWorld/formats/rleX.cpp new file mode 100644 index 0000000..06439cc --- /dev/null +++ b/libs/libSpriteWorld/formats/rleX.cpp @@ -0,0 +1,465 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Constants + +namespace spriteworld::constants +{ + static constexpr std::uint16_t rle_grid_width = 6; + static constexpr std::size_t advance = 2; +} + +// MARK: - Construction + +spriteworld::rleX::rleX(data::reader& reader) +{ + decode(reader); +} + +spriteworld::rleX::rleX(const quickdraw::size &size, std::uint16_t frame_count) + : m_id(0), m_name(type_code()), m_frame_size(size), m_frame_count(frame_count), m_bpp(32), m_palette_id(0) +{ + // Determine what the grid will be. We need to round up to the next whole number and have blank tiles + // if the frame count is not divisible by the grid width constant. + auto dim = static_cast(std::ceil(std::sqrt(m_frame_count))); + auto grid_width = std::min(dim, m_frame_count); + m_grid_size = quickdraw::size(grid_width, std::ceil(m_frame_count / static_cast(grid_width))); + + // Create the surface + m_surface = quickdraw::surface(m_grid_size.width * m_frame_size.width, m_grid_size.height * m_frame_size.height); +} + +spriteworld::rleX::rleX(const data::block& data, resource_core::identifier id, const std::string& name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +// MARK: - Accessors + +auto spriteworld::rleX::surface() -> quickdraw::surface& +{ + return m_surface; +} + +auto spriteworld::rleX::frames() const -> std::vector> +{ + return m_frames; +} + +auto spriteworld::rleX::frame_count() const -> std::size_t +{ + return m_frame_count; +} + +auto spriteworld::rleX::data() -> data::block +{ + data::writer writer; + encode(writer); + return std::move(*const_cast(writer.data())); +} + +// MARK: - Operations + +auto spriteworld::rleX::frame_rect(std::uint32_t frame) const -> quickdraw::rect +{ + return { + quickdraw::point( + static_cast(frame % m_grid_size.width) * m_frame_size.width, + static_cast(frame / m_grid_size.width) * m_frame_size.height + ), + m_frame_size + }; +} + +auto spriteworld::rleX::frame_surface(std::uint32_t frame) const -> quickdraw::surface +{ + quickdraw::surface surface(m_frame_size); + quickdraw::rect src_rect(frame_rect(frame)); + + // Extract the frame area of the origin surface + for (std::int16_t y = 0; y < src_rect.size.height; ++y) { + for (std::int16_t x = 0; x < src_rect.size.width; ++x) { + surface.set(x, y, m_surface.at(x + src_rect.origin.x, y + src_rect.origin.y)); + } + } + + return std::move(surface); +} + +auto spriteworld::rleX::write_frame(std::uint32_t frame, const quickdraw::surface &surface) -> void +{ + quickdraw::rect dst_rect = frame_rect(frame); + quickdraw::size src_size = surface.size(); + + if (src_size.width != m_frame_size.width || src_size.height != m_frame_size.height) { + throw std::runtime_error("Incorrect frame dimensions " + std::to_string(src_size.width) + "x" + std::to_string(src_size.height) + + ", expected " + std::to_string(m_frame_size.width) + "x" + std::to_string(m_frame_size.height)); + } + + // Copy from the source surface into the destination frame + for (std::int16_t y = 0; y < src_size.height; ++y) { + for (std::int16_t x = 0; x < src_size.width; ++x) { + m_surface.set(x + dst_rect.origin.x, y + dst_rect.origin.y, surface.at(x, y)); + } + } +} + +auto spriteworld::rleX::surface_offset(std::int32_t frame, std::int32_t line) -> std::uint64_t +{ + quickdraw::point fo(frame % constants::rle_grid_width, frame / constants::rle_grid_width); + quickdraw::point p(fo.x * m_frame_size.width, (fo.y * m_frame_size.height) + line); + return static_cast(p.y * m_surface.size().width + p.x); +} + +// MARK: - Decoding + +static inline auto rleX_calculate_sprite_geometry( + spriteworld::rleX *sprite, + quickdraw::surface& surface, + quickdraw::rect& rect, + std::uint32_t& offset, + std::uint32_t& right_offset_bound, + std::uint32_t& pitch, + std::uint32_t& frame +) -> void { + rect = sprite->frame_rect(frame); + offset = (rect.origin.y * surface.size().width) + rect.origin.x; + right_offset_bound = offset + rect.size.width; + pitch = surface.size().width - rect.size.width; +} + +static inline auto rleX_draw_color( + quickdraw::surface& surface, + union quickdraw::ycbcr& color, + quickdraw::rect& rect, + std::uint32_t& offset, + std::uint32_t& right_offset_bound, + std::uint32_t& pitch, + std::uint32_t count +) -> void { + auto rgb = quickdraw::rgb(color); + for (auto i = 0; i < count; ++i) { + surface.set(offset, rgb); + if (++offset >= right_offset_bound) { + offset += pitch; + right_offset_bound = offset + rect.size.width; + } + } +} + +static auto rleX_opcode_handler_eof( + spriteworld::rleX *sprite, + quickdraw::surface& surface, + data::reader& reader, + quickdraw::rect& rect, + std::uint32_t& offset, + std::uint32_t& right_offset_bound, + std::uint32_t& pitch, + std::uint32_t& frame, + std::uint16_t frame_count, + bool& completed, + union quickdraw::ycbcr& color +) -> void { + if (++frame >= frame_count) { + completed = true; + return; + } + rleX_calculate_sprite_geometry(sprite, surface, rect, offset, right_offset_bound, pitch, frame); +} + +static auto rleX_opcode_handler_set_luma( + spriteworld::rleX *sprite, + quickdraw::surface& surface, + data::reader& reader, + quickdraw::rect& rect, + std::uint32_t& offset, + std::uint32_t& right_offset_bound, + std::uint32_t& pitch, + std::uint32_t& frame, + std::uint16_t frame_count, + bool& completed, + union quickdraw::ycbcr& color +) -> void { + color.components.y = reader.read_byte(); +} + +static auto rleX_opcode_handler_set_cr( + spriteworld::rleX *sprite, + quickdraw::surface& surface, + data::reader& reader, + quickdraw::rect& rect, + std::uint32_t& offset, + std::uint32_t& right_offset_bound, + std::uint32_t& pitch, + std::uint32_t& frame, + std::uint16_t frame_count, + bool& completed, + union quickdraw::ycbcr& color +) -> void { + color.components.cr = reader.read_byte(); +} + +static auto rleX_opcode_handler_set_cb( + spriteworld::rleX *sprite, + quickdraw::surface& surface, + data::reader& reader, + quickdraw::rect& rect, + std::uint32_t& offset, + std::uint32_t& right_offset_bound, + std::uint32_t& pitch, + std::uint32_t& frame, + std::uint16_t frame_count, + bool& completed, + union quickdraw::ycbcr& color +) -> void { + color.components.cb = reader.read_byte(); +} + +static auto rleX_opcode_handler_set_alpha( + spriteworld::rleX *sprite, + quickdraw::surface& surface, + data::reader& reader, + quickdraw::rect& rect, + std::uint32_t& offset, + std::uint32_t& right_offset_bound, + std::uint32_t& pitch, + std::uint32_t& frame, + std::uint16_t frame_count, + bool& completed, + union quickdraw::ycbcr& color +) -> void { + color.components.alpha = reader.read_byte(); +} + +static auto rleX_opcode_handler_advance( + spriteworld::rleX *sprite, + quickdraw::surface& surface, + data::reader& reader, + quickdraw::rect& rect, + std::uint32_t& offset, + std::uint32_t& right_offset_bound, + std::uint32_t& pitch, + std::uint32_t& frame, + std::uint16_t frame_count, + bool& completed, + union quickdraw::ycbcr& color +) -> void { + auto count = reader.read_long(); + rleX_draw_color(surface, color, rect, offset, right_offset_bound, pitch, count); +} + +static auto rleX_opcode_handler_short_advance( + spriteworld::rleX *sprite, + quickdraw::surface& surface, + data::reader& reader, + quickdraw::rect& rect, + std::uint32_t& offset, + std::uint32_t& right_offset_bound, + std::uint32_t& pitch, + std::uint32_t& frame, + std::uint16_t frame_count, + bool& completed, + union quickdraw::ycbcr& color +) -> void { + auto count = reader.read_byte(); + rleX_draw_color(surface, color, rect, offset, right_offset_bound, pitch, static_cast(count)); +} + +typedef void(*rleX_opcode_handler)( + spriteworld::rleX*, + quickdraw::surface&, + data::reader&, + quickdraw::rect&, + std::uint32_t&, + std::uint32_t&, + std::uint32_t&, + std::uint32_t&, + std::uint16_t, + bool&, + union quickdraw::ycbcr& +); + +auto spriteworld::rleX::decode(data::reader &reader) -> void +{ + reader.change_byte_order(data::byte_order::lsb); + + // Read the header of the RLE information. This will tell us what we need to do in order to actually + // decode the frame. + m_frame_size = quickdraw::size::read(reader, quickdraw::coding_type::macintosh); + m_bpp = reader.read_short(); + m_palette_id = reader.read_short(); + m_frame_count = reader.read_short(); + reader.move(6); + + // Ensure that the RLE has a BPP of 32. This is the only format that we support currently. + if (m_bpp != 32) { + throw std::runtime_error("Incorrect color depth for rlëX resource: " + std::to_string(m_id) + ", " + m_name); + } + + // Determine what the grid will be. We need to round up to the next whole and have blank tiles if the frame count + // is not divisible by the grid width constant. + auto dim = static_cast(std::ceil(std::sqrt(m_frame_count))); + auto grid_width = std::min(dim, m_frame_count); + m_grid_size = quickdraw::size(grid_width, std::ceil(m_frame_count / static_cast(grid_width))); + + // Create the surface in which all frames will be drawn to, and other working variables required to parse and + // decode the RLE data correctly. + m_surface = quickdraw::surface(dim * m_frame_size.width, dim * m_frame_size.height, quickdraw::colors::clear()); + + std::uint32_t current_frame = 0; + std::uint32_t count = 0; + std::uint32_t current_offset = 0; + std::uint32_t right_bound = 0; + std::uint32_t pitch = 0; + + bool completed_last_frame = false; + auto frame = frame_rect(0); + + union quickdraw::ycbcr yuv { + .components = { + .y = 0, + .cb = 128, + .cr = 128, + .alpha = 255 + }, + }; + + // NOTE: This is a very _hot_ code path and thus we need to squeeze out as much performance as possible. + // Build a lookup table of opcodes, in order to reduce the number of comparisons. + /* quickdraw::surface&, data::reader&, union quickdraw::ycbcr&, std::int32_t&, std::uint32_t&, quickdraw::rect& */ + rleX_opcode_handler opcode_lut[] = { + rleX_opcode_handler_eof, + rleX_opcode_handler_set_luma, + rleX_opcode_handler_set_cr, + rleX_opcode_handler_set_cb, + rleX_opcode_handler_set_alpha, + rleX_opcode_handler_advance, + rleX_opcode_handler_short_advance + }; + + rleX_calculate_sprite_geometry(this, m_surface, frame, current_offset, right_bound, pitch, current_frame); + + while (!completed_last_frame) { + opcode_lut[reader.read_byte()]( + this, + m_surface, + reader, + frame, + current_offset, + right_bound, + pitch, + current_frame, + m_frame_count, + completed_last_frame, + yuv + ); + } +} + +// MARK: - Encoding + +auto spriteworld::rleX::encode(data::writer &writer) -> void +{ + writer.change_byte_order(data::byte_order::lsb); + + // Write out the header + m_frame_size.encode(writer, quickdraw::coding_type::macintosh); + writer.write_short(m_bpp); + writer.write_short(m_palette_id); + writer.write_short(m_frame_count); + + // Reserved fields. + writer.write_short(0, 3); + + // Write out the RLE frames + for (auto f = 0; f < m_frame_count; ++f) { + auto frame = frame_rect(f); + + union quickdraw::ycbcr yuv { + .components = { + .y = 0, + .cb = 128, + .cr = 128, + .alpha = 255 + }, + }; + + std::uint32_t count = 0; + + for (std::int16_t y = 0; y < frame.size.height; ++y) { + for (std::int16_t x = 0; x < frame.size.width; ++x) { + auto next_yuv = quickdraw::ycbcr(m_surface.at(frame.origin.x + x, frame.origin.y + y)); + + if (next_yuv.value != yuv.value) { + if (count > 0) { + if (count < 256) { + writer.write_enum(opcode::short_advance); + writer.write_byte(count); + } else { + writer.write_enum(opcode::advance); + writer.write_long(count); + } + count = 0; + } + + if (next_yuv.components.y != yuv.components.y) { + writer.write_enum(rleX::opcode::set_luma); + writer.write_byte(next_yuv.components.y); + } + + if (next_yuv.components.cr != yuv.components.cr) { + writer.write_enum(rleX::opcode::set_cr); + writer.write_byte(next_yuv.components.cr); + } + + if (next_yuv.components.cb != yuv.components.cb) { + writer.write_enum(rleX::opcode::set_cb); + writer.write_byte(next_yuv.components.cb); + } + + if (next_yuv.components.alpha != yuv.components.alpha) { + writer.write_enum(rleX::opcode::set_alpha); + writer.write_byte(next_yuv.components.alpha); + } + + yuv.value = next_yuv.value; + } + + count++; + } + } + + if (count < 256) { + writer.write_enum(opcode::short_advance); + writer.write_byte(count); + } + else { + writer.write_enum(opcode::advance); + writer.write_long(count); + } + + writer.write_enum(opcode::eof); + } +} \ No newline at end of file diff --git a/libs/libSpriteWorld/formats/rleX.hpp b/libs/libSpriteWorld/formats/rleX.hpp new file mode 100644 index 0000000..8738a69 --- /dev/null +++ b/libs/libSpriteWorld/formats/rleX.hpp @@ -0,0 +1,85 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +namespace spriteworld +{ + /* + * This 'rleX' variant of the 'rleD' format, is a custom type created for Graphite/Kestrel. + * It is not part of the original Sprite World. + */ + struct rleX + { + public: + static auto type_code() -> std::string { return "rlëX"; } + + public: + rleX() = default; + rleX(const quickdraw::size& size, std::uint16_t frame_count); + explicit rleX(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); + explicit rleX(data::reader& reader); + + ~rleX() = default; + + auto surface() -> quickdraw::surface&; + [[nodiscard]] auto frames() const -> std::vector>; + [[nodiscard]] auto frame_count() const -> std::size_t; + + [[nodiscard]] auto frame_rect(std::uint32_t idx) const -> quickdraw::rect; + [[nodiscard]] auto frame_surface(std::uint32_t idx) const -> quickdraw::surface; + auto write_frame(std::uint32_t frame, const quickdraw::surface& surface) -> void; + + auto encode(data::writer& writer) -> void; + auto data() -> data::block; + + private: + enum class opcode : std::uint8_t + { + eof = 0x00, + set_luma = 0x01, + set_cr = 0x02, + set_cb = 0x03, + set_alpha = 0x04, + advance = 0x05, + short_advance = 0x06, + }; + + resource_core::identifier m_id { 0 }; + std::string m_name; + std::vector> m_frames; + quickdraw::surface m_surface; + quickdraw::size m_frame_size { 0 }; + quickdraw::size m_grid_size { 0 }; + std::uint16_t m_frame_count { 0 }; + std::uint16_t m_bpp { 0 }; + std::uint16_t m_palette_id { 0 }; + + auto decode(data::reader& reader) -> void; + + [[nodiscard]] auto surface_offset(std::int32_t frame, std::int32_t offset) -> std::uint64_t; + }; +} diff --git a/libs/libTesting/runner.cpp b/libs/libTesting/runner.cpp new file mode 100644 index 0000000..1539ae7 --- /dev/null +++ b/libs/libTesting/runner.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include + +// MARK: - Test Runner Support + +struct test_failure +{ + std::string expression; + std::string text; + std::string file; + std::uint32_t line; +}; + +struct test_case +{ + enum class result { not_run, passed, failed }; + std::string name; + test_function_t impl; + enum result result { result::not_run }; + std::vector reasons; +}; + +struct test_suite +{ + static auto instance() -> test_suite& + { + static struct test_suite _instance; + return _instance; + } + std::uint32_t test_count { 0 }; + std::uint32_t tests_run { 0 }; + std::uint32_t tests_passed { 0 }; + std::uint32_t tests_failed { 0 }; + std::vector tests; + struct test_case *current_test { nullptr }; +}; + +// MARK: - Test Entry Point + +auto main(int argc, const char *argv[]) -> int +{ + // Build a list of the desired tests to be run. + bool test_logs = true; + std::unordered_set enabled_tests; + if (argc > 1) { + for (auto i = 1; i < argc; ++i) { + if (std::string(argv[i]) == "-s") { + test_logs = false; + continue; + } + enabled_tests.emplace(argv[i]); + } + } + + // Process each of the test cases. + std::uint32_t test_number = 0; + for (auto it : test_suite::instance().tests) { + if (!enabled_tests.empty()) { + const auto& flag = enabled_tests.find(it.name); + if (flag == enabled_tests.end()) { + continue; + } + } + + test_number++; + test_suite::instance().current_test = ⁢ + + if (test_logs) { + std::cout << "[" << test_number << "/" << test_suite::instance().test_count << "] " + << it.name << "... "; + } + + // Execute the test... + it.result = test_case::result::passed; + it.impl(); + test_suite::instance().tests_run++; + + // Report the result to the host + if (it.result == test_case::result::not_run) { + if (test_logs) { + std::cout << "Not Run" << std::endl; + } + } + else if (it.result == test_case::result::failed) { + if (test_logs) { + std::cout << "Failed" << std::endl; + } + test_suite::instance().tests_failed++; + } + else if (it.result == test_case::result::passed) { + if (test_logs) { + std::cout << "Passed" << std::endl; + } + test_suite::instance().tests_passed++; + } + + if (!it.reasons.empty() && test_logs) { + for (const auto& reason : it.reasons) { + std::cout << "\t" << reason.file << " - L" << reason.line << std::endl; + if (!reason.text.empty()) { + std::cout << "\t\t" << reason.text << std::endl; + } + if (!reason.expression.empty()) { + std::cout << "\t\t" << reason.expression << std::endl; + } + } + + } + + test_suite::instance().current_test = nullptr; + } + + // Construct a report about tests passed/failed + if (enabled_tests.empty() && test_logs) { + std::cout << std::endl; + std::cout << test_suite::instance().tests_passed << " tests passed." << std::endl; + std::cout << test_suite::instance().tests_failed << " tests failed." << std::endl; + } + + // Return the result of the tests to the host. + return (test_suite::instance().tests_failed > 0) ? 1 : 0; +} + +// MARK: - Harness Setup Function + +auto register_unit_test(const std::string& name, test_function_t test) -> void +{ + struct test_case test_case; + test_case.name = name; + test_case.impl = test; + test_suite::instance().tests.emplace_back(test_case); + test_suite::instance().test_count++; +} + +// MARK: - Test Assertions + +auto test::fail(const std::string &reason, const char *file, int line) -> void +{ + if (test_suite::instance().current_test) { + struct test_failure failure; + failure.file = file; + failure.line = line; + failure.text = reason; + + test_suite::instance().current_test->result = test_case::result::failed; + test_suite::instance().current_test->reasons.emplace_back(failure); + } +} \ No newline at end of file diff --git a/libs/libTesting/testing.cmake b/libs/libTesting/testing.cmake new file mode 100644 index 0000000..2da038f --- /dev/null +++ b/libs/libTesting/testing.cmake @@ -0,0 +1,60 @@ +enable_testing() +set(LIB_TESTING_PATH ${CMAKE_CURRENT_LIST_DIR}) + +if(NOT DEFINED CMAKE_OUTPUT_PATH) + set(CMAKE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin) +endif() + +# libTesting +function(build_testing_library) + message("libTesting @ ${LIB_TESTING_PATH}") + file(GLOB_RECURSE testing_sources + ${LIB_TESTING_PATH}/*.cpp + ) + add_library(Testing ${testing_sources}) + target_include_directories(Testing PUBLIC ${LIB_TESTING_PATH}/..) +endfunction() + +set(_TESTING_CURRENT_TEST_TARGET "" CACHE INTERNAL "") +set(_TESTING_CURRENT_TEST_SUITE "" CACHE INTERNAL "") +set(_TESTING_CURRENT_TEST_CASE "" CACHE INTERNAL "") + +function(add_test_target name dir ) + message("Adding test target: ${name} @ ${dir}") + set(_TESTING_CURRENT_TEST_TARGET ${name} CACHE INTERNAL "") + + file(GLOB_RECURSE ${name}_test_sources ${dir}/*.cpp) + add_executable(${name}_TestRunner ${${name}_test_sources}) + target_link_libraries(${name}_TestRunner ${name} Testing) + target_include_directories(${name}_TestRunner PUBLIC ${dir}) + + include(${dir}/CMakeLists.txt) +endfunction() + +function(test_include_directory dir) + target_include_directories(${_TESTING_CURRENT_TEST_TARGET}_TestRunner PUBLIC ${dir}) +endfunction() + +function(test_suite name) + set(_TESTING_CURRENT_TEST_SUITE ${name} CACHE INTERNAL "") + message(" Test Suite '${name}'") +endfunction() + +function(end_test_suite) + set(_TESTING_CURRENT_TEST_SUITE "" CACHE INTERNAL "") + set(_TESTING_CURRENT_TEST_CASE "" CACHE INTERNAL "") +endfunction() + +function(test_case name) + set(_TESTING_CURRENT_TEST_CASE ${name} CACHE INTERNAL "") + message(" Test case '${name}'") +endfunction() + +function(end_test_case) + set(_TESTING_CURRENT_TEST_CASE "" CACHE INTERNAL "") +endfunction() + +function(test name) + message(" - ${name}") + add_test(NAME ${_TESTING_CURRENT_TEST_TARGET}_${name} COMMAND ${CMAKE_OUTPUT_PATH}/${_TESTING_CURRENT_TEST_TARGET}_TestRunner ${name}) +endfunction() diff --git a/libs/libTesting/testing.hpp b/libs/libTesting/testing.hpp new file mode 100644 index 0000000..c659977 --- /dev/null +++ b/libs/libTesting/testing.hpp @@ -0,0 +1,254 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +typedef void(*test_function_t)(); + +/** + * Register a new unit test. + * Must be called prior to main() being called. Typically this isn't done directly, but through the use of + * the TEST() macro. + * @param test_name The name of the test. + * @param fn The test code to be executed. + */ +auto register_unit_test(const std::string& test_name, test_function_t fn) -> void; + +#define XSTR(_s) STR(_s) +#define STR(_s) #_s + +/* + * Unit Tests need to be setup and registered when the binary undergoes its initial + * static construction as it launches. Unit tests should be declared using the TEST() + * macro and specifying a plain English name to describe the purpose of the test. + */ +#define TEST(_name) auto _name () -> void; \ + __attribute__((constructor)) auto _name##_trampoline() -> void { \ + register_unit_test(XSTR(_name), _name); \ + } \ + auto _name () -> void + +// MARK: - Test Assertions + +namespace test +{ + /** + * Indicate that the current test has failed. + * @param reason The message to be shown in the test log describing the failure. + */ + auto fail(const std::string &reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void; + + template::value>::type* = nullptr> + static auto is_null(T ptr, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + if (ptr != nullptr) { + fail(reason, file, line); + } + } + + template::value>::type* = nullptr> + static auto not_null(T ptr, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + if (ptr == nullptr) { + fail(reason, file, line); + } + } + + static auto equal(const std::string& lhs, const std::string& rhs, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + /* in order to make sure the implementation of a custom '==' operator is valid, we need to explicitly call + * it, and not rely on shorthand. */ + bool result = (lhs == rhs); + if (!result) { + fail(reason, file, line); + } + } + + template::value>::type* = nullptr> + static auto equal(const T& lhs, const U& rhs, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + /* in order to make sure the implementation of a custom '==' operator is valid, we need to explicitly call + * it, and not rely on shorthand. */ + bool result = (lhs == rhs); + if (!result) { + fail(reason, file, line); + } + } + + template< + typename T, typename U, + typename std::enable_if::value>::type* = nullptr, + typename std::enable_if::value>::type* = nullptr + > + static auto equal(const T& lhs, const U& rhs, const T& tolerance, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + /* in order to make sure the implementation of a custom '==' operator is valid, we need to explicitly call + * it, and not rely on shorthand. */ + bool result = (lhs - rhs) < tolerance; + if (!result) { + fail(reason, file, line); + } + } + + + static auto not_equal(const std::string& lhs, const std::string& rhs, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + /* in order to make sure the implementation of a custom '==' operator is valid, we need to explicitly call + * it, and not rely on shorthand. */ + bool result = (lhs != rhs); + if (!result) { + fail(reason, file, line); + } + } + + template::value>::type* = nullptr> + static auto not_equal(const T& lhs, const U& rhs, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + /* in order to make sure the implementation of a custom '!=' operator is valid, we need to explicitly call + * it, and not rely on shorthand. */ + bool result = (lhs != rhs); + if (!result) { + fail(reason, file, line); + } + } + + template::value>::type* = nullptr> + static auto less_than(const T& lhs, const U& rhs, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + /* in order to make sure the implementation of a custom '<' operator is valid, we need to explicitly call + * it, and not rely on shorthand. */ + bool result = (lhs < rhs); + if (!result) { + fail(reason, file, line); + } + } + + template::value>::type* = nullptr> + static auto less_than_equal(const T& lhs, const U& rhs, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + /* in order to make sure the implementation of a custom '<=' operator is valid, we need to explicitly call + * it, and not rely on shorthand. */ + bool result = (lhs <= rhs); + if (!result) { + fail(reason, file, line); + } + } + + template::value>::type* = nullptr> + static auto greater_than(const T& lhs, const U& rhs, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + /* in order to make sure the implementation of a custom '>' operator is valid, we need to explicitly call + * it, and not rely on shorthand. */ + bool result = (lhs > rhs); + if (!result) { + fail(reason, file, line); + } + } + + template::value>::type* = nullptr> + static auto greater_than_equal(const T& lhs, const U& rhs, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + /* in order to make sure the implementation of a custom '>=' operator is valid, we need to explicitly call + * it, and not rely on shorthand. */ + bool result = (lhs >= rhs); + if (!result) { + fail(reason, file, line); + } + } + + static auto is_true(bool condition, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + if (!condition) { + fail(reason, file, line); + } + } + + static auto is_false(bool condition, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + if (condition) { + fail(reason, file, line); + } + } + + static auto bytes_equal(const std::uint8_t *lhs, const std::uint8_t *rhs, std::size_t count, + const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + for (auto i = 0; i < count; ++i) { + if (lhs[i] != rhs[i]) { + fail(reason, file, line); + } + } + } + + template::value>::type* = nullptr> + static auto does_throw(const std::functionvoid>& fn, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + bool should_fail = true; + + try { + fn(); + } + catch (const T& e) { + should_fail = false; + } + catch (...) { + // Still fail but warn? + fail("Unexpected exception was reached.", file, line); + } + + if (should_fail) { + fail(reason, file, line); + } + } + + static auto does_not_throw(const std::functionvoid>& fn, const std::string& reason = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + try { + fn(); + } + catch (...) { + fail(reason, file, line); + } + } + + static auto measure(const std::functionvoid>& fn, const std::string& name = "", const char *file = __builtin_FILE(), int line = __builtin_LINE()) -> void + { + auto start = std::chrono::high_resolution_clock::now(); + fn(); + auto elapsed = std::chrono::high_resolution_clock::now() - start; + + // Report the time + std::int64_t microseconds = std::chrono::duration_cast(elapsed).count(); + std::string report; + + if (!name.empty()) { + report += name + ": "; + } + + report += std::to_string(microseconds) + "µs"; + std::cout << report << std::endl; + } +} \ No newline at end of file diff --git a/libs/libToolbox/CMakeLists.txt b/libs/libToolbox/CMakeLists.txt new file mode 100644 index 0000000..ced17e4 --- /dev/null +++ b/libs/libToolbox/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (c) 2023 Tom Hancocks +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) + +######################################################################################################################## +## Project +project(Graphite LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) + +######################################################################################################################## +## libSIMD +file(GLOB_RECURSE libToolbox_Sources + *.cpp +) + +add_library(ToolBox ${libToolbox_Sources}) +target_link_libraries(ToolBox SIMD Data ResourceCore) +target_include_directories(ToolBox PUBLIC + ${PROJECT_LIBS_DIR} +) diff --git a/libs/libToolbox/font/fond.cpp b/libs/libToolbox/font/fond.cpp new file mode 100644 index 0000000..d83704f --- /dev/null +++ b/libs/libToolbox/font/fond.cpp @@ -0,0 +1,162 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Construction + +toolbox::font::descriptor::descriptor(const data::block &data, resource_core::identifier id, const std::string &name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +toolbox::font::descriptor::descriptor(data::reader &reader) +{ + decode(reader); +} + +// MARK: - Decoding + +auto toolbox::font::descriptor::decode(data::reader &reader) -> void +{ + m_fixed = reader.read_short() & 0x8000 ? true : false; + m_family_id = reader.read_short(); + m_first = reader.read_short(); + m_last = reader.read_short(); + + m_ascent = reader.read_short(); + m_descent = static_cast(reader.read_short()); + m_leading = reader.read_short(); + m_widmax = reader.read_short(); + + m_widoff = reader.read_signed_long(); + m_kernoff = reader.read_signed_long(); + m_styleoff = reader.read_signed_long(); + + reader.move(sizeof(std::uint16_t) * 9); + reader.move(sizeof(std::uint32_t)); + reader.move(sizeof(std::uint16_t)); + + m_assoc_count = reader.read_short() + 1; + m_assoc = std::vector(m_assoc_count); + for (auto i = 0; i < m_assoc_count; ++i) { + m_assoc[i].size = reader.read_short(); + m_assoc[i].style = reader.read_short(); + m_assoc[i].id = reader.read_short(); + } + + if (m_widoff != 0) { + reader.set_position(m_widoff); + m_style_width_count = reader.read_short() + 1; + m_style_widths = std::vector(m_style_width_count); + for (auto i = 0; i < m_style_width_count; ++i) { + m_style_widths[i].style = reader.read_short(); + m_style_widths[i].width_tabs = std::vector((m_last - m_first + 3) * sizeof(std::uint16_t)); + for (auto j = m_first; j <= m_last + 2; ++j) { + m_style_widths[i].width_tabs[j] = reader.read_short(); + } + } + } + + if (m_kernoff != 0) { + reader.set_position(m_kernoff); + m_style_kern_count = reader.read_short() + 1; + m_style_kerns = std::vector(m_style_kern_count); + for (auto i = 0; i < m_style_kern_count; ++i) { + m_style_kerns[i].style = reader.read_short(); + m_style_kerns[i].kern_pairs = reader.read_short(); + m_style_kerns[i].kerns = std::vector(m_style_kerns[i].kern_pairs); + for (auto j = 0; j < m_style_kerns[i].kern_pairs; ++j) { + m_style_kerns[i].kerns[j].ch1 = reader.read_byte(); + m_style_kerns[i].kerns[j].ch2 = reader.read_byte(); + m_style_kerns[i].kerns[j].offset = reader.read_short(); + } + } + } + + if (m_styleoff != 0) { + reader.set_position(m_styleoff); + m_style_class = reader.read_short(); + m_glyph_encoding = reader.read_short(); + reader.move(sizeof(std::uint32_t)); + + std::uint8_t offsets[48] = { 0 }; + for (unsigned char& offset : offsets) { + offset = reader.read_byte(); + } + + std::uint16_t string_count = reader.read_short(); + char **strings = (char **)malloc(string_count * sizeof(char *)); + for (auto j = 0; j < string_count; ++j) { + std::uint8_t len = reader.read_byte(); + strings[j] = (char *)malloc(len + 2); + strings[j][0] = len; + strings[j][len + 1] = '\0'; + for (auto k = 0; k < len; ++k) { + strings[j][k + 1] = reader.read_byte(); + } + } + + for (auto j = 0; j < 48; ++j) { + auto k = j - 1; + for (; k >= 0; --k) { + if (offsets[j] == offsets[k]) { + break; + } + } + + if (k != -1 || offsets[j] == 0) { + continue; + } + + std::int32_t format = offsets[j] - 1; + std::uint8_t len = strings[0][0]; + if (format != 0 && format != -1) { + for (k =0; k < strings[format][0]; ++k) { + len += strings[strings[format][k + 1] - 1][0]; + } + } + + char *ptr_str = (char *)malloc(len + 1); + char *pt = ptr_str; + strncpy(pt, strings[0] + 1, len + 1); + pt += strings[0][0]; + if (format != 0 && format != -1) { + for (k = 0; k < strings[format][0]; ++k) { + strncpy(pt, strings[strings[format][k + 1] - 1] + 1, len + 1); + pt += strings[strings[format][k + 1] - 1][0]; + } + } + *pt = '\0'; + + m_ps_names[j] = std::string(ptr_str); + free(ptr_str); + } + + m_family = std::string(strdup(strings[0])); + for (auto j = 0; j < string_count; ++j) { + free(strings[j]); + } + free(strings); + } +} \ No newline at end of file diff --git a/libs/libToolbox/font/fond.hpp b/libs/libToolbox/font/fond.hpp new file mode 100644 index 0000000..89297ec --- /dev/null +++ b/libs/libToolbox/font/fond.hpp @@ -0,0 +1,103 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace toolbox::font +{ + struct descriptor + { + public: + static auto type_code() -> std::string { return "FOND"; } + + public: + descriptor() = default; + explicit descriptor(const data::block& data, resource_core::identifier = 0, const std::string& name = ""); + explicit descriptor(data::reader& reader); + + private: + struct assoc + { + std::uint16_t size { 0 }; + std::uint16_t style { 0 }; + std::uint16_t id { 0 }; + }; + + struct style_widths + { + std::uint16_t style { 0 }; + std::vector width_tabs; + }; + + struct kern + { + std::uint8_t ch1 { 0 }; + std::uint8_t ch2 { 0 }; + std::uint16_t offset { 0 }; + }; + + struct style_kern + { + std::uint16_t style { 0 }; + std::uint16_t kern_pairs { 0 }; + std::vector kerns; + }; + + resource_core::identifier m_id { resource_core::auto_resource_id }; + std::string m_name; + bool m_fixed { false }; + std::uint16_t m_family_id { 0 }; + std::uint16_t m_first { 0 }; + std::uint16_t m_last { 0 }; + + std::uint16_t m_ascent { 0 }; + std::int16_t m_descent { 0 }; + std::uint16_t m_leading { 0 }; + std::uint16_t m_widmax { 0 }; + + std::int32_t m_widoff { 0 }; + std::int32_t m_kernoff { 0 }; + std::int32_t m_styleoff { 0 }; + + std::uint16_t m_assoc_count { 0 }; + std::vector m_assoc; + + std::uint16_t m_style_width_count { 0 }; + std::vector m_style_widths; + + std::uint16_t m_style_kern_count { 0 }; + std::vector m_style_kerns; + + std::uint16_t m_style_class { 0 }; + std::uint16_t m_glyph_encoding { 0 }; + + std::array m_ps_names; + std::string m_family; + + auto decode(data::reader& reader) -> void; + }; +} \ No newline at end of file diff --git a/libs/libToolbox/font/manager.cpp b/libs/libToolbox/font/manager.cpp new file mode 100644 index 0000000..479b6b8 --- /dev/null +++ b/libs/libToolbox/font/manager.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include +#include +#include + +// MARK: - Singleton + +auto toolbox::font::manager::shared_manager() -> manager & +{ + static manager instance; + return instance; +} + +// MARK: - Font Management + +auto toolbox::font::manager::update_font_table() -> void +{ + auto sfnt_resources = ::resource_core::manager::shared_manager().find(); + if (sfnt_resources.size() == 0) { + return; + } + + for (const auto& res : sfnt_resources) { + auto font_name = res.name(); + auto it = m_fonts.find(font_name); + if (it != m_fonts.end()) { + // Already aware of font... ignore it. + continue; + } + + struct font ref; + ref.name = font_name; + + struct outline_font sfnt(res.data()); + ref.ttf = sfnt.ttf_data(); + + m_fonts.emplace(std::pair(font_name, std::move(ref))); + } +} + +auto toolbox::font::manager::has_font_named(const std::string &name) const -> bool +{ + return m_fonts.find(name) != m_fonts.end(); +} + +auto toolbox::font::manager::font_has_bitmap(const std::string &name) const -> bool +{ + if (!has_font_named(name)) { + return false; + } + + auto& font = m_fonts.find(name)->second; + return !font.m_bitmaps.empty(); +} + +auto toolbox::font::manager::font_has_truetype(const std::string &name) const -> bool +{ + if (!has_font_named(name)) { + return false; + } + + auto& font = m_fonts.find(name)->second; + return font.ttf.size() > 0; +} + +auto toolbox::font::manager::ttf_font_named(const std::string &name) const -> const data::block * +{ + auto it = m_fonts.find(name); + if (it == m_fonts.end()) { + return nullptr; + } + return &it->second.ttf; +} diff --git a/libs/libToolbox/font/manager.hpp b/libs/libToolbox/font/manager.hpp new file mode 100644 index 0000000..e0149ae --- /dev/null +++ b/libs/libToolbox/font/manager.hpp @@ -0,0 +1,62 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +namespace toolbox::font +{ + class manager + { + public: + struct font + { + std::string name; + descriptor m_bitmap_descriptor; + std::unordered_map m_bitmaps; + data::block ttf; + }; + + public: + manager(const manager&) = delete; + manager(manager&&) = delete; + auto operator=(const manager&) -> manager& = delete; + auto operator=(manager&&) -> manager& = delete; + + static auto shared_manager() -> manager&; + + auto update_font_table() -> void; + + [[nodiscard]] auto has_font_named(const std::string& name) const -> bool; + [[nodiscard]] auto font_has_bitmap(const std::string& name) const -> bool; + [[nodiscard]] auto font_has_truetype(const std::string& name) const -> bool; + [[nodiscard]] auto ttf_font_named(const std::string& name) const -> const data::block *; + + private: + std::unordered_map m_fonts; + + manager() = default; + }; +} \ No newline at end of file diff --git a/libs/libToolbox/font/nfnt.cpp b/libs/libToolbox/font/nfnt.cpp new file mode 100644 index 0000000..9b1f6a8 --- /dev/null +++ b/libs/libToolbox/font/nfnt.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +// MARK: - Construction + +toolbox::font::bitmapped_font::bitmapped_font(const data::block &data, resource_core::identifier id, const std::string &name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +toolbox::font::bitmapped_font::bitmapped_font(data::reader &reader) +{ + decode(reader); +} + +// MARK: - Decoding + +auto toolbox::font::bitmapped_font::decode(data::reader &reader) -> void +{ + m_font_type = reader.read_signed_short(); + m_first_char_code = reader.read_signed_short(); + m_last_char_code = reader.read_signed_short(); + m_max_width = reader.read_signed_short(); + m_max_kerning = reader.read_signed_short(); + m_descent = reader.read_signed_short(); + m_font_rect_width = reader.read_signed_short(); + m_font_rect_height = reader.read_signed_short(); + m_width_table_offset = reader.read_signed_short(); + m_max_ascent = reader.read_signed_short(); + m_max_descent = reader.read_signed_short(); + m_leading = reader.read_signed_short(); + m_bit_image_row_width = reader.read_signed_short(); +} diff --git a/libs/libToolbox/font/nfnt.hpp b/libs/libToolbox/font/nfnt.hpp new file mode 100644 index 0000000..18b37aa --- /dev/null +++ b/libs/libToolbox/font/nfnt.hpp @@ -0,0 +1,59 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include + +namespace toolbox::font +{ + struct bitmapped_font + { + public: + static auto type_code() -> std::string { return "nfnt"; } + + public: + bitmapped_font() = default; + explicit bitmapped_font(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); + explicit bitmapped_font(data::reader& reader); + + private: + resource_core::identifier m_id { resource_core::auto_resource_id }; + std::string m_name; + + std::int16_t m_font_type { 0 }; + std::int16_t m_first_char_code { 0 }; + std::int16_t m_last_char_code { 0 }; + std::int16_t m_max_width { 0 }; + std::int16_t m_max_kerning { 0 }; + std::int16_t m_descent { 0 }; + std::int16_t m_font_rect_width { 0 }; + std::int16_t m_font_rect_height { 0 }; + std::int16_t m_width_table_offset { 0 }; + std::int16_t m_max_ascent { 0 }; + std::int16_t m_max_descent { 0 }; + std::int16_t m_leading { 0 }; + std::int16_t m_bit_image_row_width { 0 }; + + auto decode(data::reader& reader) -> void; + }; +} \ No newline at end of file diff --git a/libs/libToolbox/font/sfnt.cpp b/libs/libToolbox/font/sfnt.cpp new file mode 100644 index 0000000..db0e217 --- /dev/null +++ b/libs/libToolbox/font/sfnt.cpp @@ -0,0 +1,49 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Construction + +toolbox::font::outline_font::outline_font(const data::block &data, resource_core::identifier id, const std::string &name) + : m_id(id), m_name(name) +{ + m_ttf = data; +} + +toolbox::font::outline_font::outline_font(data::reader &reader) +{ + decode(reader); +} + +// MARK: - Decoding + +auto toolbox::font::outline_font::decode(data::reader &reader) -> void +{ + m_ttf = std::move(*const_cast(reader.data())); +} + +// MARK: - Accessors + +auto toolbox::font::outline_font::ttf_data() const -> const data::block& +{ + return m_ttf; +} \ No newline at end of file diff --git a/libGraphite/rsrc/classic.hpp b/libs/libToolbox/font/sfnt.hpp similarity index 58% rename from libGraphite/rsrc/classic.hpp rename to libs/libToolbox/font/sfnt.hpp index 583c995..d75a56e 100644 --- a/libGraphite/rsrc/classic.hpp +++ b/libs/libToolbox/font/sfnt.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Tom Hancocks +// Copyright (c) 2022 Tom Hancocks // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -18,30 +18,34 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#pragma once + #include +#include #include -#include -#include "libGraphite/rsrc/file.hpp" -#include "libGraphite/data/reader.hpp" -#include "libGraphite/data/writer.hpp" - -#if !defined(GRAPHITE_RSRC_CLASSIC) -#define GRAPHITE_RSRC_CLASSIC +#include +#include +#include -namespace graphite::rsrc::classic { +namespace toolbox::font +{ + struct outline_font + { + public: + static auto type_code() -> std::string { return "sfnt"; } - /** - * Parse the specified/provided data object that represents a resource file - * into a list of resource types. - */ - auto parse(const std::shared_ptr& reader) -> std::vector>; + public: + outline_font() = default; + explicit outline_font(const data::block& data, resource_core::identifier = 0, const std::string& name = ""); + explicit outline_font(data::reader& reader); - /** - * Build a data object that represents a resource file from the provided list - * of resource types. - */ - auto write(const std::string& path, const std::vector>& types) -> void; + [[nodiscard]] auto ttf_data() const -> const data::block&; -} + private: + resource_core::identifier m_id { resource_core::auto_resource_id }; + std::string m_name; + data::block m_ttf; -#endif \ No newline at end of file + auto decode(data::reader& reader) -> void; + }; +} \ No newline at end of file diff --git a/libs/libToolbox/strings/string.cpp b/libs/libToolbox/strings/string.cpp new file mode 100644 index 0000000..718c2f4 --- /dev/null +++ b/libs/libToolbox/strings/string.cpp @@ -0,0 +1,38 @@ +// +// Created by Tom Hancocks on 24/03/2020. +// + +#include +#include + +// MARK: - Constructor + +toolbox::string::string(const data::block& data, resource_core::identifier id, const std::string& name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +// MARK: - Accessor + +auto toolbox::string::value() const -> const std::string& +{ + return m_str; +} + +auto toolbox::string::data() const -> const data::block& +{ + return m_data; +} + +// MARK: - Decoder + +auto toolbox::string::decode(data::reader &reader) -> void +{ + m_str = reader.read_pstr(); + auto length = reader.size() - reader.position(); + if (length > 0) { + m_data = reader.read_data(length); + } +} \ No newline at end of file diff --git a/libs/libToolbox/strings/string.hpp b/libs/libToolbox/strings/string.hpp new file mode 100644 index 0000000..c61afa0 --- /dev/null +++ b/libs/libToolbox/strings/string.hpp @@ -0,0 +1,33 @@ +// +// Created by Tom Hancocks on 24/03/2020. +// + +#pragma once + +#include +#include +#include + +namespace toolbox +{ + struct string + { + public: + static auto type_code() -> std::string { return "STR "; } + + public: + string() = default; + explicit string(const data::block &data, resource_core::identifier id = 0, const std::string& name = ""); + + [[nodiscard]] auto value() const -> const std::string&; + [[nodiscard]] auto data() const -> const data::block&; + + private: + resource_core::identifier m_id { resource_core::auto_resource_id }; + std::string m_name; + std::string m_str; + data::block m_data; + + auto decode(data::reader& reader) -> void; + }; +} diff --git a/GraphiteTest/main.cpp b/libs/libToolbox/strings/string_list.cpp similarity index 50% rename from GraphiteTest/main.cpp rename to libs/libToolbox/strings/string_list.cpp index 96d3db8..47f564e 100644 --- a/GraphiteTest/main.cpp +++ b/libs/libToolbox/strings/string_list.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Tom Hancocks +// Copyright (c) 2022 Tom Hancocks // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -18,33 +18,50 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "libGraphite/rsrc/file.hpp" -#include "libGraphite/data/writer.hpp" -#include +#include +#include -int main(int argc, char const *argv[]) +// MARK: - Construction + +toolbox::string_list::string_list(const data::block &data, resource_core::identifier id, const std::string& name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +// MARK: - Decoder + +auto toolbox::string_list::decode(data::reader &reader) -> void { - auto rf = std::make_shared(); - - auto english = std::make_shared(); - english->write_cstr("Hello, World!"); - rf->add_resource("test", 128, "test resource", english->data(), { - std::make_pair("lang", "en") - }); - - auto french = std::make_shared(); - french->write_cstr("Bonjour, Monde!"); - rf->add_resource("test", 128, "test resource", french->data(), { - std::make_pair("lang", "fr") - }); - - // The resource file should be assembled at this point and just needs writing to disk. - rf->write("test.cdat", graphite::rsrc::file::format::extended); - - - auto in_rf = std::make_shared("test.cdat"); - for (const auto& type : in_rf->types()) { - std::cout << "reading type: " << type->code() << type->attributes_string() << std::endl; + auto count = static_cast(reader.read_signed_short()); + m_strings = { count, "" }; + + for (std::int32_t i = 0; i < count; ++i) { + m_strings[i] = std::move(reader.read_pstr()); } - return 0; } + +// MARK: - Access + +auto toolbox::string_list::string_count() const -> std::size_t +{ + return m_strings.size(); +} + +auto toolbox::string_list::at(std::uint32_t idx) const -> std::string +{ + return m_strings.at(idx); +} + +// MARK: - Iterator + +auto toolbox::string_list::begin() noexcept -> iterator +{ + return m_strings.begin(); +} + +auto toolbox::string_list::end() noexcept -> iterator +{ + return m_strings.end(); +} \ No newline at end of file diff --git a/libGraphite/rsrc/rez.hpp b/libs/libToolbox/strings/string_list.hpp similarity index 51% rename from libGraphite/rsrc/rez.hpp rename to libs/libToolbox/strings/string_list.hpp index 926dd78..ab9155b 100644 --- a/libGraphite/rsrc/rez.hpp +++ b/libs/libToolbox/strings/string_list.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020 Tom Hancocks +// Copyright (c) 2022 Tom Hancocks // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -18,38 +18,37 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#pragma once + #include #include -#include -#include "libGraphite/rsrc/file.hpp" -#include "libGraphite/data/reader.hpp" -#include "libGraphite/data/writer.hpp" - -#if !defined(GRAPHITE_RSRC_REZ) -#define GRAPHITE_RSRC_REZ - -namespace graphite::rsrc::rez { - - const std::string map_name = "resource.map"; - const uint32_t rez_signature = 'BRGR'; - const uint32_t rez_version = 1; - const uint32_t resource_offset_length = 12; - const uint32_t map_header_length = 8; - const uint32_t type_info_length = 12; - const uint32_t resource_info_length = 266; - - /** - * Parse the specified/provided data object that represents a resource file - * into a list of resource types. - */ - auto parse(const std::shared_ptr& reader) -> std::vector>; - - /** - * Build a data object that represents a resource file from the provided list - * of resource types. - */ - auto write(const std::string& path, const std::vector>& types) -> void; +#include +#include -} +namespace toolbox +{ + struct string_list + { + public: + static auto type_code() -> std::string { return "STR#"; } + + using iterator = std::vector::iterator; + + public: + string_list() = default; + explicit string_list(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); -#endif + [[nodiscard]] auto string_count() const -> std::size_t; + [[nodiscard]] auto at(std::uint32_t idx) const -> std::string; + + auto begin() noexcept -> iterator; + auto end() noexcept -> iterator; + + private: + resource_core::identifier m_id { 0 }; + std::string m_name; + std::vector m_strings; + + auto decode(data::reader& reader) -> void; + }; +} diff --git a/libs/libToolbox/ui/dialog.cpp b/libs/libToolbox/ui/dialog.cpp new file mode 100644 index 0000000..46e26f9 --- /dev/null +++ b/libs/libToolbox/ui/dialog.cpp @@ -0,0 +1,152 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Construction + +toolbox::dialog::dialog(const data::block &data, resource_core::identifier id, const std::string &name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +toolbox::dialog::dialog(data::reader &reader) +{ + decode(reader); +} + +// MARK: - Accessors + +auto toolbox::dialog::bounds() const -> quickdraw::rect +{ + return m_bounds; +} + +auto toolbox::dialog::proc_id() const -> std::int16_t +{ + return m_proc_id; +} + +auto toolbox::dialog::visible() const -> bool +{ + return m_visible; +} + +auto toolbox::dialog::go_away() const -> bool +{ + return m_go_away; +} + +auto toolbox::dialog::ref_con() const -> std::int32_t +{ + return m_ref_con; +} + +auto toolbox::dialog::interface_list() const -> resource_core::identifier +{ + return m_ditl_id; +} + +auto toolbox::dialog::auto_position() const -> std::uint16_t +{ + return m_auto_position; +} + +auto toolbox::dialog::title() const -> std::string +{ + return m_title; +} + +auto toolbox::dialog::set_bounds(const quickdraw::rect& bounds) -> void +{ + m_bounds = bounds; +} + +auto toolbox::dialog::set_proc_id(std::int16_t id) -> void +{ + m_proc_id = id; +} + +auto toolbox::dialog::set_visible(bool visible) -> void +{ + m_visible = visible; +} + +auto toolbox::dialog::set_go_away(bool go_away) -> void +{ + m_go_away = go_away; +} + +auto toolbox::dialog::set_ref_con(std::int32_t ref_con) -> void +{ + m_ref_con = ref_con; +} + +auto toolbox::dialog::set_interface_list(resource_core::identifier id) -> void +{ + m_ditl_id = id; +} + +auto toolbox::dialog::set_auto_position(std::uint16_t position) -> void +{ + m_auto_position = position; +} + +auto toolbox::dialog::set_title(const std::string& title) -> void +{ + m_title = title; +} + +// MARK: - Decoder + +auto toolbox::dialog::decode(data::reader &reader) -> void +{ + m_bounds = quickdraw::rect::read(reader, quickdraw::coding_type::macintosh); + + m_proc_id = reader.read_signed_short(); + m_visible = reader.read_short() != 0; + m_go_away = reader.read_short() != 0; + m_ref_con = reader.read_signed_long(); + m_ditl_id = reader.read_short(); + m_title = reader.read_pstr(); + + if (reader.position() % 2 != 0) { + reader.move(); + } + + m_auto_position = reader.read_short(); +} + +// MARK: - Encoder + +auto toolbox::dialog::encode(data::writer &writer) -> void +{ + +} + +auto toolbox::dialog::data() -> data::block +{ + data::writer writer; + encode(writer); + return std::move(*const_cast(writer.data())); +} diff --git a/libs/libToolbox/ui/dialog.hpp b/libs/libToolbox/ui/dialog.hpp new file mode 100644 index 0000000..c5c1e01 --- /dev/null +++ b/libs/libToolbox/ui/dialog.hpp @@ -0,0 +1,76 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +namespace toolbox +{ + struct dialog + { + public: + static auto type_code() -> std::string { return "DLOG"; } + + public: + dialog() = default; + explicit dialog(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); + explicit dialog(data::reader& reader); + + [[nodiscard]] auto bounds() const -> quickdraw::rect; + [[nodiscard]] auto proc_id() const -> std::int16_t; + [[nodiscard]] auto visible() const -> bool; + [[nodiscard]] auto go_away() const -> bool; + [[nodiscard]] auto ref_con() const -> std::int32_t; + [[nodiscard]] auto interface_list() const -> resource_core::identifier; + [[nodiscard]] auto auto_position() const -> std::uint16_t; + [[nodiscard]] auto title() const -> std::string; + + auto set_bounds(const quickdraw::rect& bounds) -> void; + auto set_proc_id(std::int16_t id) -> void; + auto set_visible(bool visible) -> void; + auto set_go_away(bool go_away) -> void; + auto set_ref_con(std::int32_t ref_con) -> void; + auto set_interface_list(resource_core::identifier id) -> void; + auto set_auto_position(std::uint16_t position) -> void; + auto set_title(const std::string& title) -> void; + + auto encode(data::writer& writer) -> void; + auto data() -> data::block; + + private: + resource_core::identifier m_id { resource_core::auto_resource_id }; + std::string m_name; + std::string m_title; + quickdraw::rect m_bounds; + std::int16_t m_proc_id { 0 }; + bool m_visible { true }; + bool m_go_away { true }; + std::int32_t m_ref_con { 0 }; + resource_core::identifier m_ditl_id { 0 }; + std::uint16_t m_auto_position { 0 }; + + auto decode(data::reader& reader) -> void; + }; +} diff --git a/libs/libToolbox/ui/dialog_item_list.cpp b/libs/libToolbox/ui/dialog_item_list.cpp new file mode 100644 index 0000000..00a56e0 --- /dev/null +++ b/libs/libToolbox/ui/dialog_item_list.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +// MARK: - Construction + +toolbox::dialog_item_list::dialog_item_list(const data::block &data, resource_core::identifier id, const std::string &name) + : m_id(id), m_name(name) +{ + data::reader reader(&data); + decode(reader); +} + +toolbox::dialog_item_list::dialog_item_list(data::reader &reader) +{ + decode(reader); +} + +// MARK: - Encoder + +auto toolbox::dialog_item_list::encode(data::writer &writer) -> void +{ + writer.write_short(m_items.size() - 1); + + for (const auto& item : m_items) { + writer.write_long(0); + + auto frame = item.frame; + frame.encode(writer, quickdraw::coding_type::macintosh); + + writer.write_enum(item.type); + writer.write_pstr(item.info); + + if (writer.position() % 2 == 1) { + writer.write_byte(0); + } + } +} + +auto toolbox::dialog_item_list::data() -> data::block +{ + data::writer writer; + encode(writer); + return std::move(*const_cast(writer.data())); +} + +// MARK: - Decoder + +auto toolbox::dialog_item_list::decode(data::reader &reader) -> void +{ + auto count = reader.read_short(); + + for (auto i = 0; i <= count; ++i) { + reader.move(4); + + struct item item; + item.frame = quickdraw::rect::read(reader, quickdraw::coding_type::quickdraw); + item.type = static_cast(reader.read_byte()); + item.info = reader.read_pstr(); + + if (reader.position() % 2 == 1) { + reader.move(); + } + + m_items.emplace_back(std::move(item)); + } +} \ No newline at end of file diff --git a/libs/libToolbox/ui/dialog_item_list.hpp b/libs/libToolbox/ui/dialog_item_list.hpp new file mode 100644 index 0000000..37414af --- /dev/null +++ b/libs/libToolbox/ui/dialog_item_list.hpp @@ -0,0 +1,79 @@ +// Copyright (c) 2022 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include +#include +#include +#include +#include + +namespace toolbox +{ + struct dialog_item_list + { + public: + static auto type_code() -> std::string { return "DITL"; } + + enum class item_type : std::uint8_t + { + user_item = 0, + help_item = 1, + button = 4, + checkbox = 5, + radio = 6, + control = 7, + static_text = 8, + edit_text = 16, + icon = 32, + picture = 64, + disable = 128 + }; + + struct item + { + quickdraw::rect frame; + enum item_type type { item_type::user_item }; + std::string info; + }; + + public: + dialog_item_list() = default; + explicit dialog_item_list(const data::block& data, resource_core::identifier id = 0, const std::string& name = ""); + explicit dialog_item_list(data::reader& reader); + + auto encode(data::writer& writer) -> void; + auto data() -> data::block; + + [[nodiscard]] auto item_count() const -> std::uint16_t { return m_items.size(); } + [[nodiscard]] auto at(std::uint16_t idx) const -> const item& { return m_items[idx]; } + + auto begin() -> std::vector::iterator { return m_items.begin(); } + auto end() -> std::vector::iterator { return m_items.end(); } + + private: + resource_core::identifier m_id { resource_core::auto_resource_id }; + std::string m_name; + std::vector m_items; + + auto decode(data::reader& reader) -> void; + }; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..77563e0 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (c) 2023 Tom Hancocks +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + + +######################################################################################################################## +## libTesting +include(${PROJECT_SOURCE_DIR}/libs/libTesting/testing.cmake) +build_testing_library() + +######################################################################################################################## +## Preparation + +set(TESTS ${PROJECT_SOURCE_DIR}/tests) +add_test_target(Hashing ${TESTS}/libs/libHashing) diff --git a/tests/libs/libHashing/CMakeLists.txt b/tests/libs/libHashing/CMakeLists.txt new file mode 100644 index 0000000..97c845a --- /dev/null +++ b/tests/libs/libHashing/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (c) 2023 Tom Hancocks +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +test_suite(Hashing) + test_case(XXHash) + test(hashed_string_returnsCorrectValue) + end_test_case() +end_test_suite() diff --git a/tests/libs/libHashing/xxhash.cpp b/tests/libs/libHashing/xxhash.cpp new file mode 100644 index 0000000..1433ffc --- /dev/null +++ b/tests/libs/libHashing/xxhash.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2023 Tom Hancocks +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +TEST(hashed_string_returnsCorrectValue) +{ + // Additional values could be added here to help ensure edge cases are correctly + // hashed. + // Any expected hash values should be generated by a third party hashing algorithm + // to ensure we aren't validating against our self. These were generated using + // https://asecuritysite.com/encryption/xxHash + test::equal(hashing::xxh64("apple", 5), 0x5889a1c15c94729f); + test::equal(hashing::xxh64("banana", 6), 0xcef162e1813c8ce2); + test::equal(hashing::xxh64("orange", 6), 0xc23e954aef3ce5a9); +} \ No newline at end of file