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 #define DEVILUTIONX_BLIT_EXECUTION_POLICY
#endif #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) DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitFillDirect(uint8_t *dst, unsigned length, uint8_t color)
{ {
std::memset(dst, color, length); std::memset(dst, color, length);
@ -40,18 +27,13 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitPixelsDirect(uint8_t *DVL_RESTRICT
} }
struct BlitDirect { 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) { BlitFillDirect(dst, length, color);
case BlitType::Fill:
BlitFillDirect(dst, cmd.length, cmd.color);
return;
case BlitType::Pixels:
BlitPixelsDirect(dst, src, cmd.length);
return;
case BlitType::Transparent:
return;
}
} }
}; };
@ -86,18 +68,13 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitPixelsBlended(uint8_t *DVL_RESTRICT
struct BlitWithMap { struct BlitWithMap {
const uint8_t *DVL_RESTRICT colorMap; 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) { BlitFillWithMap(dst, length, color, colorMap);
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;
}
} }
}; };
@ -112,18 +89,13 @@ DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT void BlitPixelsBlendedWithMap(uint8_t *DVL_R
struct BlitBlendedWithMap { struct BlitBlendedWithMap {
const uint8_t *DVL_RESTRICT colorMap; 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) { BlitFillBlended(dst, length, colorMap[color]);
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;
}
} }
}; };

112
Source/engine/render/clx_render.cpp

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

14
Source/utils/clx_decode.hpp

@ -27,18 +27,4 @@ namespace devilution {
return static_cast<int_fast16_t>(ClxFillEnd - control); 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 } // namespace devilution

Loading…
Cancel
Save