From aa751ff538d8896a3847ee4ed4e00a3e9b36b7c4 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sat, 12 Oct 2024 09:30:35 +0100 Subject: [PATCH] Reduce CLX header size from 10 to 6 We remain compatible with the 10-byte header size, and actually now support any header size. --- Source/engine/clx_sprite.hpp | 27 ++++++++++++--------------- Source/utils/cel_to_clx.cpp | 6 ++---- Source/utils/cl2_to_clx.cpp | 6 ++---- Source/utils/clx_encode.hpp | 11 +++++++++++ Source/utils/pcx_to_clx.cpp | 11 ++--------- Source/utils/surface_to_clx.cpp | 10 ++-------- 6 files changed, 31 insertions(+), 40 deletions(-) diff --git a/Source/engine/clx_sprite.hpp b/Source/engine/clx_sprite.hpp index e8957ed4e..91a254b47 100644 --- a/Source/engine/clx_sprite.hpp +++ b/Source/engine/clx_sprite.hpp @@ -5,21 +5,20 @@ * @brief CLX format sprites. * * CLX is a format used for DevilutionX graphics at runtime. + * CLX encodes pixel in the same way CL2 but encodes metadata differently. * - * It is identical to CL2, except we use the frame header to store the frame's width and height. + * Unlike CL2: * - * CLX frame header (10 bytes, same as CL2): + * 1. CLX frame header stores frame width and height. + * 2. CLX frame header does not store 32-pixel block offsets. * - * Bytes | Type | Value - * :-----:|:--------:|------------------------------------ - * 0..2 | uint16_t | offset to data start (same as CL2) - * 2..4 | uint16_t | width - * 4..6 | uint16_t | height - * 6..10 | - | unused + * CLX frame header is 6 bytes: * - * The CLX format is otherwise identical to CL2. - * - * Since the header is identical to CL2, CL2 can be converted to CLX without reallocation. + * Bytes | Type | Value + * :-----:|:--------:|------------- + * 0..2 | uint16_t | header size + * 2..4 | uint16_t | width + * 4..6 | uint16_t | height * * CL2 reference: https://github.com/savagesteel/d1-file-formats/blob/master/PC-Mac/CL2.md#2-file-structure */ @@ -42,12 +41,10 @@ class OptionalClxSprite; * @brief A single CLX sprite. */ class ClxSprite { - static constexpr uint32_t HeaderSize = 10; - public: explicit constexpr ClxSprite(const uint8_t *data, uint32_t dataSize) : data_(data) - , pixel_data_size_(dataSize - HeaderSize) + , pixel_data_size_(dataSize - LoadLE16(data)) { assert(data != nullptr); } @@ -69,7 +66,7 @@ public: */ [[nodiscard]] constexpr const uint8_t *pixelData() const { - return &data_[HeaderSize]; + return &data_[LoadLE16(data_)]; } [[nodiscard]] constexpr uint32_t pixelDataSize() const diff --git a/Source/utils/cel_to_clx.cpp b/Source/utils/cel_to_clx.cpp index 76fdcc3aa..b1865ab30 100644 --- a/Source/utils/cel_to_clx.cpp +++ b/Source/utils/cel_to_clx.cpp @@ -86,9 +86,8 @@ OwnedClxSpriteListOrSheet CelToClx(const uint8_t *data, size_t size, PointerOrVa // CLX frame header. const size_t frameHeaderPos = cl2Data.size(); - constexpr size_t FrameHeaderSize = 10; - cl2Data.resize(cl2Data.size() + FrameHeaderSize); - WriteLE16(&cl2Data[frameHeaderPos], FrameHeaderSize); + cl2Data.resize(cl2Data.size() + ClxFrameHeaderSize); + WriteLE16(&cl2Data[frameHeaderPos], ClxFrameHeaderSize); WriteLE16(&cl2Data[frameHeaderPos + 2], frameWidth); unsigned transparentRunWidth = 0; @@ -111,7 +110,6 @@ OwnedClxSpriteListOrSheet CelToClx(const uint8_t *data, size_t size, PointerOrVa ++frameHeight; } WriteLE16(&cl2Data[frameHeaderPos + 4], static_cast(frameHeight)); - memset(&cl2Data[frameHeaderPos + 6], 0, 4); AppendClxTransparentRun(transparentRunWidth, cl2Data); } diff --git a/Source/utils/cl2_to_clx.cpp b/Source/utils/cl2_to_clx.cpp index 1acff2428..12eae863f 100644 --- a/Source/utils/cl2_to_clx.cpp +++ b/Source/utils/cl2_to_clx.cpp @@ -56,9 +56,8 @@ uint16_t Cl2ToClx(const uint8_t *data, size_t size, const uint16_t frameWidth = widthOrWidths.HoldsPointer() ? widthOrWidths.AsPointer()[frame - 1] : widthOrWidths.AsValue(); const size_t frameHeaderPos = clxData.size(); - constexpr size_t FrameHeaderSize = 10; - clxData.resize(clxData.size() + FrameHeaderSize); - WriteLE16(&clxData[frameHeaderPos], FrameHeaderSize); + clxData.resize(clxData.size() + ClxFrameHeaderSize); + WriteLE16(&clxData[frameHeaderPos], ClxFrameHeaderSize); WriteLE16(&clxData[frameHeaderPos + 2], frameWidth); unsigned transparentRunWidth = 0; @@ -104,7 +103,6 @@ uint16_t Cl2ToClx(const uint8_t *data, size_t size, AppendClxTransparentRun(transparentRunWidth, clxData); WriteLE16(&clxData[frameHeaderPos + 4], static_cast(frameHeight)); - memset(&clxData[frameHeaderPos + 6], 0, 4); } WriteLE32(&clxData[clxDataOffset + 4 * (1 + static_cast(numFrames))], static_cast(clxData.size() - clxDataOffset)); diff --git a/Source/utils/clx_encode.hpp b/Source/utils/clx_encode.hpp index 337e6b9c7..bce740e74 100644 --- a/Source/utils/clx_encode.hpp +++ b/Source/utils/clx_encode.hpp @@ -6,6 +6,17 @@ namespace devilution { +/** + * CLX frame header is 6 bytes: + * + * Bytes | Type | Value + * :-----:|:--------:|------------- + * 0..2 | uint16_t | header size + * 2..4 | uint16_t | width + * 4..6 | uint16_t | height + */ +constexpr size_t ClxFrameHeaderSize = 6; + inline void AppendClxTransparentRun(unsigned width, std::vector &out) { while (width >= 0x7F) { diff --git a/Source/utils/pcx_to_clx.cpp b/Source/utils/pcx_to_clx.cpp index cb94648cc..baba20b5f 100644 --- a/Source/utils/pcx_to_clx.cpp +++ b/Source/utils/pcx_to_clx.cpp @@ -98,20 +98,13 @@ OptionalOwnedClxSpriteList PcxToClx(AssetHandle &handle, size_t fileSize, int nu for (unsigned frame = 1; frame <= numFrames; ++frame) { WriteLE32(&cl2Data[4 * static_cast(frame)], static_cast(cl2Data.size())); - // Frame header: 5 16-bit values: - // 1. Offset to start of the pixel data. - // 2. Width - // 3. Height - // 4..5. Unused (0) const size_t frameHeaderPos = cl2Data.size(); - constexpr size_t FrameHeaderSize = 10; - cl2Data.resize(cl2Data.size() + FrameHeaderSize); + cl2Data.resize(cl2Data.size() + ClxFrameHeaderSize); // Frame header: - WriteLE16(&cl2Data[frameHeaderPos], FrameHeaderSize); + WriteLE16(&cl2Data[frameHeaderPos], ClxFrameHeaderSize); WriteLE16(&cl2Data[frameHeaderPos + 2], static_cast(width)); WriteLE16(&cl2Data[frameHeaderPos + 4], static_cast(frameHeight)); - memset(&cl2Data[frameHeaderPos + 6], 0, 4); for (unsigned j = 0; j < frameHeight; ++j) { uint8_t *buffer = &frameBuffer[static_cast(j) * width]; diff --git a/Source/utils/surface_to_clx.cpp b/Source/utils/surface_to_clx.cpp index 82460554f..b6bef980a 100644 --- a/Source/utils/surface_to_clx.cpp +++ b/Source/utils/surface_to_clx.cpp @@ -31,17 +31,11 @@ OwnedClxSpriteList SurfaceToClx(const Surface &surface, unsigned numFrames, for (unsigned frame = 1; frame <= numFrames; ++frame) { WriteLE32(&clxData[4 * static_cast(frame)], static_cast(clxData.size())); - // Frame header: 5 16-bit values: - // 1. Offset to start of the pixel data. - // 2. Width - // 3. Height - // 4..5. Unused (0) const size_t frameHeaderPos = clxData.size(); - constexpr size_t FrameHeaderSize = 10; - clxData.resize(clxData.size() + FrameHeaderSize); + clxData.resize(clxData.size() + ClxFrameHeaderSize); // Frame header: - WriteLE16(&clxData[frameHeaderPos], FrameHeaderSize); + WriteLE16(&clxData[frameHeaderPos], ClxFrameHeaderSize); WriteLE16(&clxData[frameHeaderPos + 2], static_cast(width)); WriteLE16(&clxData[frameHeaderPos + 4], static_cast(frameHeight)); memset(&clxData[frameHeaderPos + 6], 0, 4);