Browse Source

Simplify CLX rendering

Inlines blit command parsing.

We previously had blit commands because we supported rendering multiple
formats (CEL, CL2, CLX) but now we only ever render CLX, so this is
no longer necessary.
pull/7140/head
Gleb Mazovetskiy 2 years ago
parent
commit
bae4030d7b
  1. 64
      Source/engine/render/blit_impl.hpp
  2. 112
      Source/engine/render/clx_render.cpp
  3. 33
      Source/utils/cl2_to_clx.cpp
  4. 14
      Source/utils/clx_decode.hpp

64
Source/engine/render/blit_impl.hpp

@ -16,19 +16,6 @@ namespace devilution {
#define DEVILUTIONX_BLIT_EXECUTION_POLICY
#endif
enum class BlitType : uint8_t {
Transparent,
Pixels,
Fill
};
struct BlitCommand {
BlitType type;
const uint8_t *srcEnd; // Pointer past the end of the command.
unsigned length; // Number of pixels this command will write.
uint8_t color; // For `BlitType::Pixel` and `BlitType::Fill` only.
};
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitFillDirect(uint8_t *dst, unsigned length, uint8_t color)
{
std::memset(dst, color, length);
@ -40,18 +27,13 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitPixelsDirect(uint8_t *DVL_RESTRICT
}
struct BlitDirect {
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(BlitCommand cmd, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src)
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src) const
{
BlitPixelsDirect(dst, src, length);
}
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t color, uint8_t *DVL_RESTRICT dst) const
{
switch (cmd.type) {
case BlitType::Fill:
BlitFillDirect(dst, cmd.length, cmd.color);
return;
case BlitType::Pixels:
BlitPixelsDirect(dst, src, cmd.length);
return;
case BlitType::Transparent:
return;
}
BlitFillDirect(dst, length, color);
}
};
@ -86,18 +68,13 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitPixelsBlended(uint8_t *DVL_RESTRICT
struct BlitWithMap {
const uint8_t *DVL_RESTRICT colorMap;
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(BlitCommand cmd, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src) const
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src) const
{
BlitPixelsWithMap(dst, src, length, colorMap);
}
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t color, uint8_t *DVL_RESTRICT dst) const
{
switch (cmd.type) {
case BlitType::Fill:
BlitFillWithMap(dst, cmd.length, cmd.color, colorMap);
return;
case BlitType::Pixels:
BlitPixelsWithMap(dst, src, cmd.length, colorMap);
return;
case BlitType::Transparent:
return;
}
BlitFillWithMap(dst, length, color, colorMap);
}
};
@ -112,18 +89,13 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitPixelsBlendedWithMap(uint8_t *DVL_R
struct BlitBlendedWithMap {
const uint8_t *DVL_RESTRICT colorMap;
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(BlitCommand cmd, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src) const
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t *DVL_RESTRICT dst, const uint8_t *DVL_RESTRICT src) const
{
BlitPixelsBlendedWithMap(dst, src, length, colorMap);
}
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void operator()(unsigned length, uint8_t color, uint8_t *DVL_RESTRICT dst) const
{
switch (cmd.type) {
case BlitType::Fill:
BlitFillBlended(dst, cmd.length, colorMap[cmd.color]);
return;
case BlitType::Pixels:
BlitPixelsBlendedWithMap(dst, src, cmd.length, colorMap);
return;
case BlitType::Transparent:
return;
}
BlitFillBlended(dst, length, colorMap[color]);
}
};

112
Source/engine/render/clx_render.cpp

@ -30,6 +30,24 @@ namespace {
* indicates a fill-N command.
*/
struct BlitCommandInfo {
const uint8_t *srcEnd;
unsigned length;
};
BlitCommandInfo ClxBlitInfo(const uint8_t *src)
{
const uint8_t control = *src;
if (!IsClxOpaque(control))
return { src + 1, control };
if (IsClxOpaqueFill(control)) {
const uint8_t width = GetClxOpaqueFillWidth(control);
return { src + 2, width };
}
const uint8_t width = GetClxOpaquePixelsWidth(control);
return { src + 1 + width, width };
}
struct ClipX {
int_fast16_t left;
int_fast16_t right;
@ -76,9 +94,9 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT const uint8_t *SkipRestOfLineWithOverrun(
{
int_fast16_t remainingWidth = srcWidth - skipSize.xOffset;
while (remainingWidth > 0) {
const BlitCommand cmd = ClxGetBlitCommand(src);
src = cmd.srcEnd;
remainingWidth -= cmd.length;
const auto [srcEnd, length] = ClxBlitInfo(src);
src = srcEnd;
remainingWidth -= length;
}
skipSize = GetSkipSize(remainingWidth, srcWidth);
return src;
@ -113,11 +131,20 @@ void DoRenderBackwardsClipY(
auto remainingWidth = static_cast<int_fast16_t>(src.width) - xOffset;
dst += xOffset;
while (remainingWidth > 0) {
BlitCommand cmd = ClxGetBlitCommand(src.begin);
blitFn(cmd, dst, src.begin + 1);
src.begin = cmd.srcEnd;
dst += cmd.length;
remainingWidth -= cmd.length;
uint8_t v = *src.begin++;
if (IsClxOpaque(v)) {
if (IsClxOpaqueFill(v)) {
v = GetClxOpaqueFillWidth(v);
const uint8_t color = *src.begin++;
blitFn(v, color, dst);
} else {
v = GetClxOpaquePixelsWidth(v);
blitFn(v, dst, src.begin);
src.begin += v;
}
}
dst += v;
remainingWidth -= v;
}
const SkipSize skipSize = GetSkipSize(remainingWidth, static_cast<int_fast16_t>(src.width));
@ -150,26 +177,40 @@ void DoRenderBackwardsClipXY(
remainingWidth += remainingLeftClip;
}
while (remainingLeftClip > 0) {
BlitCommand cmd = ClxGetBlitCommand(src.begin);
if (static_cast<int_fast16_t>(cmd.length) > remainingLeftClip) {
const auto overshoot = static_cast<int>(cmd.length - remainingLeftClip);
cmd.length = std::min<unsigned>(remainingWidth, overshoot);
blitFn(cmd, dst, src.begin + 1 + remainingLeftClip);
dst += cmd.length;
auto [srcEnd, length] = ClxBlitInfo(src.begin);
if (static_cast<int_fast16_t>(length) > remainingLeftClip) {
const uint8_t control = *src.begin;
const auto overshoot = static_cast<int>(length - remainingLeftClip);
length = std::min<unsigned>(remainingWidth, overshoot);
if (IsClxOpaque(control)) {
if (IsClxOpaqueFill(control)) {
blitFn(length, src.begin[1], dst);
} else {
blitFn(length, dst, src.begin + 1 + remainingLeftClip);
}
}
dst += length;
remainingWidth -= overshoot;
src.begin = cmd.srcEnd;
src.begin = srcEnd;
break;
}
src.begin = cmd.srcEnd;
remainingLeftClip -= cmd.length;
src.begin = srcEnd;
remainingLeftClip -= length;
}
while (remainingWidth > 0) {
BlitCommand cmd = ClxGetBlitCommand(src.begin);
const unsigned unclippedLength = cmd.length;
cmd.length = std::min<unsigned>(remainingWidth, cmd.length);
blitFn(cmd, dst, src.begin + 1);
src.begin = cmd.srcEnd;
dst += cmd.length;
auto [srcEnd, length] = ClxBlitInfo(src.begin);
const uint8_t control = *src.begin;
const unsigned unclippedLength = length;
length = std::min<unsigned>(remainingWidth, length);
if (IsClxOpaque(control)) {
if (IsClxOpaqueFill(control)) {
blitFn(length, src.begin[1], dst);
} else {
blitFn(length, dst, src.begin + 1);
}
}
src.begin = srcEnd;
dst += length;
remainingWidth -= unclippedLength; // result can be negative
}
@ -763,19 +804,20 @@ std::string ClxDescribe(ClxSprite clx)
const uint8_t *src = clx.pixelData();
const uint8_t *end = src + clx.pixelDataSize();
while (src < end) {
BlitCommand cmd = ClxGetBlitCommand(src);
switch (cmd.type) {
case BlitType::Transparent:
out.append(fmt::format("Transp. | {:>5} | {:>5} |\n", cmd.length, cmd.srcEnd - src));
break;
case BlitType::Fill:
out.append(fmt::format("Fill | {:>5} | {:>5} | {}\n", cmd.length, cmd.srcEnd - src, cmd.color));
break;
case BlitType::Pixels:
out.append(fmt::format("Pixels | {:>5} | {:>5} | {}\n", cmd.length, cmd.srcEnd - src, fmt::join(src + 1, src + 1 + cmd.length, " ")));
break;
const uint8_t control = *src++;
if (IsClxOpaque(control)) {
if (IsClxOpaqueFill(control)) {
const uint8_t length = GetClxOpaqueFillWidth(control);
out.append(fmt::format("Fill | {:>5} | {:>5} | {}\n", length, 2, src[1]));
++src;
} else {
const uint8_t length = GetClxOpaquePixelsWidth(control);
out.append(fmt::format("Pixels | {:>5} | {:>5} | {}\n", length, length + 1, fmt::join(src + 1, src + 1 + length, " ")));
src += length;
}
} else {
out.append(fmt::format("Transp. | {:>5} | {:>5} |\n", control, 1));
}
src = cmd.srcEnd;
}
return out;
}

33
Source/utils/cl2_to_clx.cpp

@ -85,30 +85,29 @@ uint16_t Cl2ToClx(const uint8_t *data, size_t size,
while (src != frameEnd) {
auto remainingWidth = static_cast<int_fast16_t>(frameWidth) - xOffset;
while (remainingWidth > 0) {
const BlitCommand cmd = ClxGetBlitCommand(src);
switch (cmd.type) {
case BlitType::Transparent:
const uint8_t control = *src++;
if (!IsClxOpaque(control)) {
if (!pixels.empty()) {
AppendClxPixelsOrFillRun(pixels.data(), pixels.size(), clxData);
pixels.clear();
}
transparentRunWidth += cmd.length;
break;
case BlitType::Fill:
case BlitType::Pixels:
transparentRunWidth += control;
remainingWidth -= control;
} else if (IsClxOpaqueFill(control)) {
AppendClxTransparentRun(transparentRunWidth, clxData);
transparentRunWidth = 0;
if (cmd.type == BlitType::Fill) {
pixels.insert(pixels.end(), cmd.length, cmd.color);
} else { // BlitType::Pixels
pixels.insert(pixels.end(), src + 1, cmd.srcEnd);
}
break;
const uint8_t width = GetClxOpaqueFillWidth(control);
const uint8_t color = *src++;
pixels.insert(pixels.end(), width, color);
remainingWidth -= width;
} else {
AppendClxTransparentRun(transparentRunWidth, clxData);
transparentRunWidth = 0;
const uint8_t width = GetClxOpaquePixelsWidth(control);
pixels.insert(pixels.end(), src, src + width);
src += width;
remainingWidth -= width;
}
src = cmd.srcEnd;
remainingWidth -= cmd.length;
}
++frameHeight;

14
Source/utils/clx_decode.hpp

@ -27,18 +27,4 @@ namespace devilution {
return static_cast<int_fast16_t>(ClxFillEnd - control);
}
[[nodiscard]] constexpr BlitCommand ClxGetBlitCommand(const uint8_t *src)
{
const uint8_t control = *src++;
if (!IsClxOpaque(control))
return BlitCommand { BlitType::Transparent, src, control, 0 };
if (IsClxOpaqueFill(control)) {
const uint8_t width = GetClxOpaqueFillWidth(control);
const uint8_t color = *src++;
return BlitCommand { BlitType::Fill, src, width, color };
}
const uint8_t width = GetClxOpaquePixelsWidth(control);
return BlitCommand { BlitType::Pixels, src + width, width, 0 };
}
} // namespace devilution

Loading…
Cancel
Save