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.
*
* 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

6
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<uint16_t>(frameHeight));
memset(&cl2Data[frameHeaderPos + 6], 0, 4);
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 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<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));

11
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<uint8_t> &out)
{
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) {
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();
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<uint16_t>(width));
WriteLE16(&cl2Data[frameHeaderPos + 4], static_cast<uint16_t>(frameHeight));
memset(&cl2Data[frameHeaderPos + 6], 0, 4);
for (unsigned j = 0; j < frameHeight; ++j) {
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) {
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();
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<uint16_t>(width));
WriteLE16(&clxData[frameHeaderPos + 4], static_cast<uint16_t>(frameHeight));
memset(&clxData[frameHeaderPos + 6], 0, 4);

Loading…
Cancel
Save