|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
#include <cstdint>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
namespace devilution {
|
|
|
|
|
|
|
|
|
|
inline void AppendCl2TransparentRun(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 AppendCl2FillRun(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 AppendCl2PixelsRun(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 AppendCl2PixelsOrFillRun(const uint8_t *src, unsigned 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) {
|
|
|
|
|
AppendCl2PixelsRun(begin, prevColorBegin - begin, out);
|
|
|
|
|
AppendCl2FillRun(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) {
|
|
|
|
|
AppendCl2PixelsRun(begin, prevColorBegin - begin, out);
|
|
|
|
|
AppendCl2FillRun(prevColor, prevColorRunLength, out);
|
|
|
|
|
} else {
|
|
|
|
|
AppendCl2PixelsRun(begin, prevColorBegin - begin + prevColorRunLength, out);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace devilution
|