You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

97 lines
2.6 KiB

#pragma once
#include <cstddef>
#include <cstdint>
#include <vector>
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) {
out.push_back(0x7F);
width -= 0x7F;
}
if (width == 0)
return;
out.push_back(width);
}
inline void AppendClxFillRun(uint8_t color, unsigned width, std::vector<uint8_t> &out)
{
while (width >= 0x3F) {
out.push_back(0x80);
out.push_back(color);
width -= 0x3F;
}
if (width == 0)
return;
out.push_back(0xBF - width);
out.push_back(color);
}
inline void AppendClxPixelsRun(const uint8_t *src, unsigned width, std::vector<uint8_t> &out)
{
while (width >= 0x41) {
out.push_back(0xBF);
for (size_t i = 0; i < 0x41; ++i)
out.push_back(src[i]);
width -= 0x41;
src += 0x41;
}
if (width == 0)
return;
out.push_back(256 - width);
for (size_t i = 0; i < width; ++i)
out.push_back(src[i]);
}
inline void AppendClxPixelsOrFillRun(const uint8_t *src, size_t length, std::vector<uint8_t> &out)
{
const uint8_t *begin = src;
const uint8_t *prevColorBegin = src;
unsigned prevColorRunLength = 1;
uint8_t prevColor = *src++;
while (--length > 0) {
const uint8_t color = *src;
if (prevColor == color) {
++prevColorRunLength;
} else {
// A tunable parameter that decides at which minimum length we encode a fill run.
// 3 appears to be optimal for most of our data (much better than 2, rarely very slightly worse than 4).
constexpr unsigned MinFillRunLength = 3;
if (prevColorRunLength >= MinFillRunLength) {
AppendClxPixelsRun(begin, static_cast<unsigned>(prevColorBegin - begin), out);
AppendClxFillRun(prevColor, prevColorRunLength, out);
begin = src;
}
prevColorBegin = src;
prevColorRunLength = 1;
prevColor = color;
}
++src;
}
// Here we use 2 instead of `MinFillRunLength` because we know that this run
// is followed by transparent pixels.
// Width=2 Fill command takes 2 bytes, while the Pixels command is 3 bytes.
if (prevColorRunLength >= 2) {
AppendClxPixelsRun(begin, static_cast<unsigned>(prevColorBegin - begin), out);
AppendClxFillRun(prevColor, prevColorRunLength, out);
} else {
AppendClxPixelsRun(begin, static_cast<unsigned>(prevColorBegin - begin + prevColorRunLength), out);
}
}
} // namespace devilution