diff --git a/Source/engine/render/blit_impl.hpp b/Source/engine/render/blit_impl.hpp index 4d2e0d268..4c2fa8cbe 100644 --- a/Source/engine/render/blit_impl.hpp +++ b/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]); } }; diff --git a/Source/engine/render/clx_render.cpp b/Source/engine/render/clx_render.cpp index e538749f6..9d75f3560 100644 --- a/Source/engine/render/clx_render.cpp +++ b/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(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(src.width)); @@ -150,26 +177,40 @@ void DoRenderBackwardsClipXY( remainingWidth += remainingLeftClip; } while (remainingLeftClip > 0) { - BlitCommand cmd = ClxGetBlitCommand(src.begin); - if (static_cast(cmd.length) > remainingLeftClip) { - const auto overshoot = static_cast(cmd.length - remainingLeftClip); - cmd.length = std::min(remainingWidth, overshoot); - blitFn(cmd, dst, src.begin + 1 + remainingLeftClip); - dst += cmd.length; + auto [srcEnd, length] = ClxBlitInfo(src.begin); + if (static_cast(length) > remainingLeftClip) { + const uint8_t control = *src.begin; + const auto overshoot = static_cast(length - remainingLeftClip); + length = std::min(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(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(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; } diff --git a/Source/utils/cl2_to_clx.cpp b/Source/utils/cl2_to_clx.cpp index 4c8204502..d1c4ae7cb 100644 --- a/Source/utils/cl2_to_clx.cpp +++ b/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(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; diff --git a/Source/utils/clx_decode.hpp b/Source/utils/clx_decode.hpp index 9d7e5d80b..f9e6e8341 100644 --- a/Source/utils/clx_decode.hpp +++ b/Source/utils/clx_decode.hpp @@ -27,18 +27,4 @@ namespace devilution { return static_cast(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