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