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.
188 lines
6.1 KiB
188 lines
6.1 KiB
#include "engine/render/pcx_render.hpp" |
|
|
|
#include <algorithm> |
|
#include <cstring> |
|
|
|
#include "engine/render/common_impl.h" |
|
#include "utils/log.hpp" |
|
|
|
namespace devilution { |
|
namespace { |
|
|
|
constexpr uint8_t PcxMaxSinglePixel = 0xBF; |
|
constexpr uint8_t PcxRunLengthMask = 0x3F; |
|
|
|
const uint8_t *SkipRestOfPcxLine(const uint8_t *src, unsigned remainingWidth) |
|
{ |
|
while (remainingWidth > 0) { |
|
const uint8_t value = *src++; |
|
if (value <= PcxMaxSinglePixel) { |
|
--remainingWidth; |
|
} else { |
|
remainingWidth -= value & PcxRunLengthMask; |
|
++src; |
|
} |
|
} |
|
return src; |
|
} |
|
template <bool UseColorMap, bool HasTransparency> |
|
void BlitPcxClipY(const Surface &out, Point position, const uint8_t *src, unsigned srcWidth, unsigned srcHeight, const uint8_t *colorMap, uint8_t transparentColor) |
|
{ |
|
if (position.y >= out.h()) |
|
return; |
|
while (position.y < 0 && srcHeight != 0) { |
|
src = SkipRestOfPcxLine(src, srcWidth); |
|
++position.y; |
|
--srcHeight; |
|
} |
|
srcHeight = static_cast<unsigned>(std::min<int>(out.h() - position.y, srcHeight)); |
|
|
|
const auto dstSkip = static_cast<unsigned>(out.pitch() - srcWidth); |
|
const unsigned srcSkip = srcWidth % 2; |
|
uint8_t *dst = &out[position]; |
|
for (unsigned y = 0; y < srcHeight; y++) { |
|
for (unsigned x = 0; x < srcWidth;) { |
|
const uint8_t value = *src++; |
|
if (value <= PcxMaxSinglePixel) { |
|
if (!(HasTransparency && value == transparentColor)) { |
|
*dst = UseColorMap ? colorMap[value] : value; |
|
} |
|
++dst; |
|
++x; |
|
} else { |
|
const uint8_t runLength = value & PcxRunLengthMask; |
|
const uint8_t color = *src++; |
|
if (!(HasTransparency && color == transparentColor)) { |
|
std::memset(dst, UseColorMap ? colorMap[color] : color, runLength); |
|
} |
|
dst += runLength; |
|
x += runLength; |
|
} |
|
} |
|
dst += dstSkip; |
|
src += srcSkip; |
|
} |
|
} |
|
|
|
template <bool UseColorMap, bool HasTransparency> |
|
void BlitPcxClipXY(const Surface &out, Point position, const uint8_t *src, unsigned srcWidth, unsigned srcHeight, ClipX clipX, const uint8_t *colorMap, uint8_t transparentColor) |
|
{ |
|
if (position.y >= out.h() || position.x >= out.w()) |
|
return; |
|
while (position.y < 0 && srcHeight != 0) { |
|
src = SkipRestOfPcxLine(src, srcWidth); |
|
++position.y; |
|
--srcHeight; |
|
} |
|
srcHeight = static_cast<unsigned>(std::min<int>(out.h() - position.y, srcHeight)); |
|
|
|
position.x += static_cast<int>(clipX.left); |
|
|
|
const auto dstSkip = static_cast<unsigned>(out.pitch() - clipX.width); |
|
const unsigned srcSkip = srcWidth % 2; |
|
uint8_t *dst = &out[position]; |
|
for (unsigned y = 0; y < srcHeight; y++) { |
|
// Skip initial src if clipping on the left. |
|
// Handles overshoot, i.e. when the RLE segment goes into the unclipped area. |
|
auto remainingWidth = clipX.width; |
|
auto remainingLeftClip = clipX.left; |
|
while (remainingLeftClip > 0) { |
|
const uint8_t value = *src++; |
|
if (value <= PcxMaxSinglePixel) { |
|
--remainingLeftClip; |
|
} else { |
|
const uint8_t runLength = value & PcxRunLengthMask; |
|
if (runLength > remainingLeftClip) { |
|
const uint8_t overshoot = runLength - remainingLeftClip; |
|
const uint8_t originalColor = *src++; |
|
const uint8_t color = UseColorMap ? colorMap[originalColor] : originalColor; |
|
if (overshoot > remainingWidth) { |
|
if (!(HasTransparency && originalColor == transparentColor)) { |
|
std::memset(dst, color, remainingWidth); |
|
} |
|
dst += remainingWidth; |
|
remainingWidth = 0; |
|
} else { |
|
if (!(HasTransparency && originalColor == transparentColor)) { |
|
std::memset(dst, color, overshoot); |
|
} |
|
dst += overshoot; |
|
remainingWidth -= overshoot; |
|
} |
|
remainingLeftClip = 0; |
|
break; |
|
} |
|
++src; |
|
remainingLeftClip -= runLength; |
|
} |
|
} |
|
|
|
while (remainingWidth > 0) { |
|
const uint8_t value = *src++; |
|
if (value <= PcxMaxSinglePixel) { |
|
if (!(HasTransparency && value == transparentColor)) { |
|
*dst = UseColorMap ? colorMap[value] : value; |
|
} |
|
++dst; |
|
--remainingWidth; |
|
continue; |
|
} |
|
const uint8_t runLength = value & PcxRunLengthMask; |
|
const uint8_t originalColor = *src++; |
|
const uint8_t color = UseColorMap ? colorMap[originalColor] : originalColor; |
|
if (runLength > remainingWidth) { |
|
if (!(HasTransparency && originalColor == transparentColor)) { |
|
std::memset(dst, color, remainingWidth); |
|
} |
|
dst += remainingWidth; |
|
remainingWidth -= runLength; |
|
break; |
|
} |
|
if (!(HasTransparency && originalColor == transparentColor)) { |
|
std::memset(dst, color, runLength); |
|
} |
|
dst += runLength; |
|
remainingWidth -= runLength; |
|
} |
|
|
|
src = SkipRestOfPcxLine(src, clipX.right + remainingWidth); |
|
|
|
dst += dstSkip; |
|
src += srcSkip; |
|
} |
|
} |
|
|
|
template <bool UseColorMap> |
|
void BlitPcxSprite(const Surface &out, Point position, PcxSprite sprite, const uint8_t *colorMap) |
|
{ |
|
const ClipX clipX = CalculateClipX(position.x, sprite.width(), out); |
|
if (clipX.width <= 0) |
|
return; |
|
if (static_cast<unsigned>(clipX.width) == sprite.width()) { |
|
if (sprite.transparentColor()) { |
|
BlitPcxClipY<UseColorMap, /*HasTransparency=*/true>(out, position, sprite.data(), sprite.width(), sprite.height(), colorMap, *sprite.transparentColor()); |
|
} else { |
|
BlitPcxClipY<UseColorMap, /*HasTransparency=*/false>(out, position, sprite.data(), sprite.width(), sprite.height(), colorMap, 0); |
|
} |
|
} else { |
|
if (sprite.transparentColor()) { |
|
BlitPcxClipXY<UseColorMap, /*HasTransparency=*/true>(out, position, sprite.data(), sprite.width(), sprite.height(), clipX, colorMap, *sprite.transparentColor()); |
|
} else { |
|
BlitPcxClipXY<UseColorMap, /*HasTransparency=*/false>(out, position, sprite.data(), sprite.width(), sprite.height(), clipX, colorMap, 0); |
|
} |
|
} |
|
} |
|
|
|
} // namespace |
|
|
|
void RenderPcxSprite(const Surface &out, PcxSprite sprite, Point position) |
|
{ |
|
BlitPcxSprite</*UseColorMap=*/false>(out, position, sprite, /*colorMap=*/nullptr); |
|
} |
|
|
|
void RenderPcxSpriteWithColorMap(const Surface &out, PcxSprite sprite, Point position, const std::array<uint8_t, 256> &colorMap) |
|
{ |
|
BlitPcxSprite</*UseColorMap=*/true>(out, position, sprite, colorMap.data()); |
|
} |
|
|
|
} // namespace devilution
|
|
|