Browse Source

Reduce CLX header size from 10 to 6

We remain compatible with the 10-byte header size,
and actually now support any header size.
pull/7483/head
Gleb Mazovetskiy 1 year ago
parent
commit
aa751ff538
  1. 27
      Source/engine/clx_sprite.hpp
  2. 6
      Source/utils/cel_to_clx.cpp
  3. 6
      Source/utils/cl2_to_clx.cpp
  4. 11
      Source/utils/clx_encode.hpp
  5. 11
      Source/utils/pcx_to_clx.cpp
  6. 10
      Source/utils/surface_to_clx.cpp

27
Source/engine/clx_sprite.hpp

@ -5,21 +5,20 @@
* @brief CLX format sprites. * @brief CLX format sprites.
* *
* CLX is a format used for DevilutionX graphics at runtime. * 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 * CLX frame header is 6 bytes:
* :-----:|:--------:|------------------------------------
* 0..2 | uint16_t | offset to data start (same as CL2)
* 2..4 | uint16_t | width
* 4..6 | uint16_t | height
* 6..10 | - | unused
* *
* The CLX format is otherwise identical to CL2. * Bytes | Type | Value
* * :-----:|:--------:|-------------
* Since the header is identical to CL2, CL2 can be converted to CLX without reallocation. * 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 * 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. * @brief A single CLX sprite.
*/ */
class ClxSprite { class ClxSprite {
static constexpr uint32_t HeaderSize = 10;
public: public:
explicit constexpr ClxSprite(const uint8_t *data, uint32_t dataSize) explicit constexpr ClxSprite(const uint8_t *data, uint32_t dataSize)
: data_(data) : data_(data)
, pixel_data_size_(dataSize - HeaderSize) , pixel_data_size_(dataSize - LoadLE16(data))
{ {
assert(data != nullptr); assert(data != nullptr);
} }
@ -69,7 +66,7 @@ public:
*/ */
[[nodiscard]] constexpr const uint8_t *pixelData() const [[nodiscard]] constexpr const uint8_t *pixelData() const
{ {
return &data_[HeaderSize]; return &data_[LoadLE16(data_)];
} }
[[nodiscard]] constexpr uint32_t pixelDataSize() const [[nodiscard]] constexpr uint32_t pixelDataSize() const

6
Source/utils/cel_to_clx.cpp

@ -86,9 +86,8 @@ OwnedClxSpriteListOrSheet CelToClx(const uint8_t *data, size_t size, PointerOrVa
// CLX frame header. // CLX frame header.
const size_t frameHeaderPos = cl2Data.size(); const size_t frameHeaderPos = cl2Data.size();
constexpr size_t FrameHeaderSize = 10; cl2Data.resize(cl2Data.size() + ClxFrameHeaderSize);
cl2Data.resize(cl2Data.size() + FrameHeaderSize); WriteLE16(&cl2Data[frameHeaderPos], ClxFrameHeaderSize);
WriteLE16(&cl2Data[frameHeaderPos], FrameHeaderSize);
WriteLE16(&cl2Data[frameHeaderPos + 2], frameWidth); WriteLE16(&cl2Data[frameHeaderPos + 2], frameWidth);
unsigned transparentRunWidth = 0; unsigned transparentRunWidth = 0;
@ -111,7 +110,6 @@ OwnedClxSpriteListOrSheet CelToClx(const uint8_t *data, size_t size, PointerOrVa
++frameHeight; ++frameHeight;
} }
WriteLE16(&cl2Data[frameHeaderPos + 4], static_cast<uint16_t>(frameHeight)); WriteLE16(&cl2Data[frameHeaderPos + 4], static_cast<uint16_t>(frameHeight));
memset(&cl2Data[frameHeaderPos + 6], 0, 4);
AppendClxTransparentRun(transparentRunWidth, cl2Data); AppendClxTransparentRun(transparentRunWidth, cl2Data);
} }

6
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 uint16_t frameWidth = widthOrWidths.HoldsPointer() ? widthOrWidths.AsPointer()[frame - 1] : widthOrWidths.AsValue();
const size_t frameHeaderPos = clxData.size(); const size_t frameHeaderPos = clxData.size();
constexpr size_t FrameHeaderSize = 10; clxData.resize(clxData.size() + ClxFrameHeaderSize);
clxData.resize(clxData.size() + FrameHeaderSize); WriteLE16(&clxData[frameHeaderPos], ClxFrameHeaderSize);
WriteLE16(&clxData[frameHeaderPos], FrameHeaderSize);
WriteLE16(&clxData[frameHeaderPos + 2], frameWidth); WriteLE16(&clxData[frameHeaderPos + 2], frameWidth);
unsigned transparentRunWidth = 0; unsigned transparentRunWidth = 0;
@ -104,7 +103,6 @@ uint16_t Cl2ToClx(const uint8_t *data, size_t size,
AppendClxTransparentRun(transparentRunWidth, clxData); AppendClxTransparentRun(transparentRunWidth, clxData);
WriteLE16(&clxData[frameHeaderPos + 4], static_cast<uint16_t>(frameHeight)); WriteLE16(&clxData[frameHeaderPos + 4], static_cast<uint16_t>(frameHeight));
memset(&clxData[frameHeaderPos + 6], 0, 4);
} }
WriteLE32(&clxData[clxDataOffset + 4 * (1 + static_cast<size_t>(numFrames))], static_cast<uint32_t>(clxData.size() - clxDataOffset)); WriteLE32(&clxData[clxDataOffset + 4 * (1 + static_cast<size_t>(numFrames))], static_cast<uint32_t>(clxData.size() - clxDataOffset));

11
Source/utils/clx_encode.hpp

@ -6,6 +6,17 @@
namespace devilution { 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<uint8_t> &out) inline void AppendClxTransparentRun(unsigned width, std::vector<uint8_t> &out)
{ {
while (width >= 0x7F) { while (width >= 0x7F) {

11
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) { for (unsigned frame = 1; frame <= numFrames; ++frame) {
WriteLE32(&cl2Data[4 * static_cast<size_t>(frame)], static_cast<uint32_t>(cl2Data.size())); WriteLE32(&cl2Data[4 * static_cast<size_t>(frame)], static_cast<uint32_t>(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(); const size_t frameHeaderPos = cl2Data.size();
constexpr size_t FrameHeaderSize = 10; cl2Data.resize(cl2Data.size() + ClxFrameHeaderSize);
cl2Data.resize(cl2Data.size() + FrameHeaderSize);
// Frame header: // Frame header:
WriteLE16(&cl2Data[frameHeaderPos], FrameHeaderSize); WriteLE16(&cl2Data[frameHeaderPos], ClxFrameHeaderSize);
WriteLE16(&cl2Data[frameHeaderPos + 2], static_cast<uint16_t>(width)); WriteLE16(&cl2Data[frameHeaderPos + 2], static_cast<uint16_t>(width));
WriteLE16(&cl2Data[frameHeaderPos + 4], static_cast<uint16_t>(frameHeight)); WriteLE16(&cl2Data[frameHeaderPos + 4], static_cast<uint16_t>(frameHeight));
memset(&cl2Data[frameHeaderPos + 6], 0, 4);
for (unsigned j = 0; j < frameHeight; ++j) { for (unsigned j = 0; j < frameHeight; ++j) {
uint8_t *buffer = &frameBuffer[static_cast<size_t>(j) * width]; uint8_t *buffer = &frameBuffer[static_cast<size_t>(j) * width];

10
Source/utils/surface_to_clx.cpp

@ -31,17 +31,11 @@ OwnedClxSpriteList SurfaceToClx(const Surface &surface, unsigned numFrames,
for (unsigned frame = 1; frame <= numFrames; ++frame) { for (unsigned frame = 1; frame <= numFrames; ++frame) {
WriteLE32(&clxData[4 * static_cast<size_t>(frame)], static_cast<uint32_t>(clxData.size())); WriteLE32(&clxData[4 * static_cast<size_t>(frame)], static_cast<uint32_t>(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(); const size_t frameHeaderPos = clxData.size();
constexpr size_t FrameHeaderSize = 10; clxData.resize(clxData.size() + ClxFrameHeaderSize);
clxData.resize(clxData.size() + FrameHeaderSize);
// Frame header: // Frame header:
WriteLE16(&clxData[frameHeaderPos], FrameHeaderSize); WriteLE16(&clxData[frameHeaderPos], ClxFrameHeaderSize);
WriteLE16(&clxData[frameHeaderPos + 2], static_cast<uint16_t>(width)); WriteLE16(&clxData[frameHeaderPos + 2], static_cast<uint16_t>(width));
WriteLE16(&clxData[frameHeaderPos + 4], static_cast<uint16_t>(frameHeight)); WriteLE16(&clxData[frameHeaderPos + 4], static_cast<uint16_t>(frameHeight));
memset(&clxData[frameHeaderPos + 6], 0, 4); memset(&clxData[frameHeaderPos + 6], 0, 4);

Loading…
Cancel
Save