7 changed files with 35 additions and 743 deletions
@ -1,534 +0,0 @@
|
||||
/**
|
||||
* @file cel_render.cpp |
||||
* |
||||
* CEL rendering. |
||||
*/ |
||||
#include "engine/render/cel_render.hpp" |
||||
|
||||
#include <cstdint> |
||||
#include <cstring> |
||||
|
||||
#include "engine/cel_header.hpp" |
||||
#include "engine/palette.h" |
||||
#include "engine/render/common_impl.h" |
||||
#include "engine/render/scrollrt.h" |
||||
#include "engine/trn.hpp" |
||||
#include "options.h" |
||||
#include "utils/attributes.h" |
||||
|
||||
namespace devilution { |
||||
|
||||
namespace { |
||||
|
||||
constexpr bool IsCelTransparent(uint8_t control) |
||||
{ |
||||
constexpr uint8_t CelTransparentMin = 0x80; |
||||
return control >= CelTransparentMin; |
||||
} |
||||
|
||||
constexpr uint8_t GetCelTransparentWidth(uint8_t control) |
||||
{ |
||||
return -static_cast<std::int8_t>(control); |
||||
} |
||||
|
||||
DVL_ALWAYS_INLINE DVL_ATTRIBUTE_HOT BlitCommand CelGetBlitCommand(const uint8_t *src) |
||||
{ |
||||
const uint8_t control = *src++; |
||||
if (IsCelTransparent(control)) |
||||
return BlitCommand { BlitType::Transparent, src, GetCelTransparentWidth(control), 0 }; |
||||
return BlitCommand { BlitType::Pixels, src + control, control, 0 }; |
||||
} |
||||
|
||||
template <bool North, bool West, bool South, bool East, bool SkipColorIndexZero> |
||||
uint8_t *RenderCelOutlinePixelsCheckFirstColumn( |
||||
uint8_t *dst, int dstPitch, int dstX, |
||||
const uint8_t *src, uint8_t width, uint8_t color) |
||||
{ |
||||
if (dstX == -1) { |
||||
RenderOutlineForPixel</*North=*/false, /*West=*/false, /*South=*/false, East, SkipColorIndexZero>( |
||||
dst++, dstPitch, *src++, color); |
||||
--width; |
||||
} |
||||
if (width > 0) { |
||||
RenderOutlineForPixel<North, /*West=*/false, South, East, SkipColorIndexZero>(dst++, dstPitch, *src++, color); |
||||
--width; |
||||
} |
||||
if (width > 0) { |
||||
RenderOutlineForPixels<North, West, South, East, SkipColorIndexZero>(dst, dstPitch, width, src, color); |
||||
dst += width; |
||||
} |
||||
return dst; |
||||
} |
||||
|
||||
template <bool North, bool West, bool South, bool East, bool SkipColorIndexZero> |
||||
uint8_t *RenderCelOutlinePixelsCheckLastColumn( |
||||
uint8_t *dst, int dstPitch, int dstX, int dstW, |
||||
const uint8_t *src, uint8_t width, uint8_t color) |
||||
{ |
||||
const bool lastPixel = dstX < dstW && width >= 1; |
||||
const bool oobPixel = dstX + width > dstW; |
||||
const int numSpecialPixels = (lastPixel ? 1 : 0) + (oobPixel ? 1 : 0); |
||||
if (width > numSpecialPixels) { |
||||
width -= numSpecialPixels; |
||||
RenderOutlineForPixels<North, West, South, East, SkipColorIndexZero>(dst, dstPitch, width, src, color); |
||||
src += width; |
||||
dst += width; |
||||
} |
||||
if (lastPixel) |
||||
RenderOutlineForPixel<North, West, South, /*East=*/false, SkipColorIndexZero>(dst++, dstPitch, *src++, color); |
||||
if (oobPixel) |
||||
RenderOutlineForPixel</*North=*/false, West, /*South=*/false, /*East=*/false, SkipColorIndexZero>(dst++, dstPitch, *src++, color); |
||||
return dst; |
||||
} |
||||
|
||||
template <bool SkipColorIndexZero, bool North, bool West, bool South, bool East, bool CheckFirstColumn, bool CheckLastColumn> |
||||
uint8_t *RenderCelOutlinePixels( |
||||
uint8_t *dst, int dstPitch, int dstX, int dstW, |
||||
const uint8_t *src, uint8_t width, uint8_t color) |
||||
{ |
||||
if (CheckFirstColumn && dstX <= 0) { |
||||
return RenderCelOutlinePixelsCheckFirstColumn<North, West, South, East, SkipColorIndexZero>( |
||||
dst, dstPitch, dstX, src, width, color); |
||||
} |
||||
if (CheckLastColumn && dstX + width >= dstW) { |
||||
return RenderCelOutlinePixelsCheckLastColumn<North, West, South, East, SkipColorIndexZero>( |
||||
dst, dstPitch, dstX, dstW, src, width, color); |
||||
} |
||||
RenderOutlineForPixels<North, West, South, East, SkipColorIndexZero>(dst, dstPitch, width, src, color); |
||||
return dst + width; |
||||
} |
||||
|
||||
template <bool SkipColorIndexZero, bool North, bool West, bool South, bool East, |
||||
bool ClipWidth = false, bool CheckFirstColumn = false, bool CheckLastColumn = false> |
||||
const uint8_t *RenderCelOutlineRowClipped( // NOLINT(readability-function-cognitive-complexity,misc-no-recursion)
|
||||
const Surface &out, Point position, const uint8_t *src, ClipX clipX, uint8_t color) |
||||
{ |
||||
int_fast16_t remainingWidth = clipX.width; |
||||
uint8_t v; |
||||
|
||||
auto *dst = &out[position]; |
||||
const auto dstPitch = out.pitch(); |
||||
const auto dstW = out.w(); |
||||
|
||||
if (ClipWidth) { |
||||
auto remainingLeftClip = clipX.left; |
||||
while (remainingLeftClip > 0) { |
||||
v = static_cast<uint8_t>(*src++); |
||||
if (!IsCelTransparent(v)) { |
||||
if (v > remainingLeftClip) { |
||||
RenderCelOutlinePixels<SkipColorIndexZero, North, West, South, East, CheckFirstColumn, CheckLastColumn>( |
||||
dst, dstPitch, position.x, dstW, src, v - remainingLeftClip, color); |
||||
} |
||||
src += v; |
||||
} else { |
||||
v = GetCelTransparentWidth(v); |
||||
} |
||||
remainingLeftClip -= v; |
||||
} |
||||
dst -= static_cast<int>(remainingLeftClip); |
||||
position.x -= static_cast<int>(remainingLeftClip); |
||||
remainingWidth += remainingLeftClip; |
||||
} |
||||
|
||||
while (remainingWidth > 0) { |
||||
v = static_cast<uint8_t>(*src++); |
||||
if (!IsCelTransparent(v)) { |
||||
dst = RenderCelOutlinePixels<SkipColorIndexZero, North, West, South, East, CheckFirstColumn, CheckLastColumn>( |
||||
dst, dstPitch, position.x, dstW, src, |
||||
std::min(remainingWidth, static_cast<int_fast16_t>(v)), color); |
||||
src += v; |
||||
} else { |
||||
v = GetCelTransparentWidth(v); |
||||
dst += v; |
||||
} |
||||
remainingWidth -= v; |
||||
position.x += v; |
||||
} |
||||
|
||||
src = SkipRestOfLine<CelGetBlitCommand>(src, clipX.right + remainingWidth); |
||||
|
||||
return src; |
||||
} |
||||
|
||||
template <bool SkipColorIndexZero> |
||||
void RenderCelOutlineClippedY(const Surface &out, Point position, RenderSrcBackwards src, uint8_t color) // NOLINT(readability-function-cognitive-complexity)
|
||||
{ |
||||
// Skip the bottom clipped lines.
|
||||
const auto dstHeight = out.h(); |
||||
SkipLinesForRenderBackwards<CelGetBlitCommand>(position, src, dstHeight); |
||||
if (src.begin == src.end) |
||||
return; |
||||
|
||||
const ClipX clipX = { 0, 0, static_cast<decltype(ClipX {}.width)>(src.width) }; |
||||
|
||||
if (position.y == dstHeight) { |
||||
// After-bottom line - can only draw north.
|
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/false, /*South=*/false, /*East=*/false>( |
||||
out, position, src.begin, clipX, color); |
||||
--position.y; |
||||
} |
||||
if (src.begin == src.end) |
||||
return; |
||||
|
||||
if (position.y + 1 == dstHeight) { |
||||
// Bottom line - cannot draw south.
|
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/true, /*South=*/false, /*East=*/true>( |
||||
out, position, src.begin, clipX, color); |
||||
--position.y; |
||||
} |
||||
|
||||
while (position.y > 0 && src.begin != src.end) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/true, /*South=*/true, /*East=*/true>( |
||||
out, position, src.begin, clipX, color); |
||||
--position.y; |
||||
} |
||||
if (src.begin == src.end) |
||||
return; |
||||
|
||||
if (position.y == 0) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/false, /*West=*/true, /*South=*/true, /*East=*/true>( |
||||
out, position, src.begin, clipX, color); |
||||
--position.y; |
||||
} |
||||
if (src.begin == src.end) |
||||
return; |
||||
|
||||
if (position.y == -1) { |
||||
// Special case: the top of the sprite is 1px below the last line, render just the outline above.
|
||||
RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/false, /*West=*/false, /*South=*/true, /*East=*/false>( |
||||
out, position, src.begin, clipX, color); |
||||
} |
||||
} |
||||
|
||||
template <bool SkipColorIndexZero> |
||||
void RenderCelOutlineClippedXY(const Surface &out, Point position, RenderSrcBackwards src, uint8_t color) // NOLINT(readability-function-cognitive-complexity)
|
||||
{ |
||||
// Skip the bottom clipped lines.
|
||||
const auto dstHeight = out.h(); |
||||
SkipLinesForRenderBackwards<CelGetBlitCommand>(position, src, dstHeight); |
||||
if (src.begin == src.end) |
||||
return; |
||||
|
||||
ClipX clipX = CalculateClipX(position.x, src.width, out); |
||||
if (clipX.width < 0) |
||||
return; |
||||
if (clipX.left > 0) { |
||||
--clipX.left, ++clipX.width; |
||||
} else if (clipX.right > 0) { |
||||
--clipX.right, ++clipX.width; |
||||
} |
||||
position.x += static_cast<int>(clipX.left); |
||||
|
||||
if (position.y == dstHeight) { |
||||
// After-bottom line - can only draw north.
|
||||
if (position.x <= 0) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/false, /*South=*/false, /*East=*/false, |
||||
/*ClipWidth=*/true, /*CheckFirstColumn=*/true, /*CheckLastColumn=*/false>(out, position, src.begin, clipX, color); |
||||
} else if (position.x + clipX.width >= out.w()) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/false, /*South=*/false, /*East=*/false, |
||||
/*ClipWidth=*/true, /*CheckFirstColumn=*/false, /*CheckLastColumn=*/true>(out, position, src.begin, clipX, color); |
||||
} else { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/false, /*South=*/false, /*East=*/false, |
||||
/*ClipWidth=*/true>(out, position, src.begin, clipX, color); |
||||
} |
||||
|
||||
--position.y; |
||||
} |
||||
if (src.begin == src.end) |
||||
return; |
||||
|
||||
if (position.y + 1 == dstHeight) { |
||||
// Bottom line - cannot draw south.
|
||||
if (position.x <= 0) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/true, /*South=*/false, /*East=*/true, |
||||
/*ClipWidth=*/true, /*CheckFirstColumn=*/true, /*CheckLastColumn=*/false>( |
||||
out, position, src.begin, clipX, color); |
||||
} else if (position.x + clipX.width >= out.w()) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/true, /*South=*/false, /*East=*/true, |
||||
/*ClipWidth=*/true, /*CheckFirstColumn=*/false, /*CheckLastColumn=*/true>( |
||||
out, position, src.begin, clipX, color); |
||||
} else { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/true, /*South=*/false, /*East=*/true, |
||||
/*ClipWidth=*/true>( |
||||
out, position, src.begin, clipX, color); |
||||
} |
||||
--position.y; |
||||
} |
||||
|
||||
if (position.x <= 0) { |
||||
while (position.y > 0 && src.begin != src.end) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/true, /*South=*/true, /*East=*/true, |
||||
/*ClipWidth=*/true, /*CheckFirstColumn=*/true, /*CheckLastColumn=*/false>( |
||||
out, position, src.begin, clipX, color); |
||||
--position.y; |
||||
} |
||||
} else if (position.x + clipX.width >= out.w()) { |
||||
while (position.y > 0 && src.begin != src.end) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/true, /*South=*/true, /*East=*/true, |
||||
/*ClipWidth=*/true, /*CheckFirstColumn=*/false, /*CheckLastColumn=*/true>( |
||||
out, position, src.begin, clipX, color); |
||||
--position.y; |
||||
} |
||||
} else { |
||||
while (position.y > 0 && src.begin != src.end) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/true, /*West=*/true, /*South=*/true, /*East=*/true, |
||||
/*ClipWidth=*/true>( |
||||
out, position, src.begin, clipX, color); |
||||
--position.y; |
||||
} |
||||
} |
||||
if (src.begin == src.end) |
||||
return; |
||||
|
||||
if (position.y == 0) { |
||||
if (position.x <= 0) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/false, /*West=*/true, /*South=*/true, /*East=*/true, |
||||
/*ClipWidth=*/true, /*CheckFirstColumn=*/true, /*CheckLastColumn=*/false>( |
||||
out, position, src.begin, clipX, color); |
||||
} else if (position.x + clipX.width >= out.w()) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/false, /*West=*/true, /*South=*/true, /*East=*/true, |
||||
/*ClipWidth=*/true, /*CheckFirstColumn=*/false, /*CheckLastColumn=*/true>( |
||||
out, position, src.begin, clipX, color); |
||||
} else { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/false, /*West=*/true, /*South=*/true, /*East=*/true, |
||||
/*ClipWidth=*/true>( |
||||
out, position, src.begin, clipX, color); |
||||
} |
||||
--position.y; |
||||
} |
||||
if (src.begin == src.end) |
||||
return; |
||||
|
||||
if (position.y == -1) { |
||||
// After-bottom line - can only draw south.
|
||||
if (position.x <= 0) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/false, /*West=*/false, /*South=*/true, /*East=*/false, |
||||
/*ClipWidth=*/true, /*CheckFirstColumn=*/true, /*CheckLastColumn=*/false>(out, position, src.begin, clipX, color); |
||||
} else if (position.x + clipX.width >= out.w()) { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/false, /*West=*/false, /*South=*/true, /*East=*/false, |
||||
/*ClipWidth=*/true, /*CheckFirstColumn=*/false, /*CheckLastColumn=*/true>(out, position, src.begin, clipX, color); |
||||
} else { |
||||
src.begin = RenderCelOutlineRowClipped<SkipColorIndexZero, /*North=*/false, /*West=*/false, /*South=*/true, /*East=*/false, |
||||
/*ClipWidth=*/true>(out, position, src.begin, clipX, color); |
||||
} |
||||
} |
||||
} |
||||
|
||||
template <bool SkipColorIndexZero> |
||||
void RenderCelOutline(const Surface &out, Point position, const uint8_t *src, std::size_t srcSize, |
||||
std::size_t srcWidth, uint8_t color) |
||||
{ |
||||
RenderSrcBackwards srcForBackwards { src, src + srcSize, static_cast<uint_fast16_t>(srcWidth) }; |
||||
if (position.x > 0 && position.x + static_cast<int>(srcWidth) < static_cast<int>(out.w())) { |
||||
RenderCelOutlineClippedY<SkipColorIndexZero>(out, position, srcForBackwards, color); |
||||
} else { |
||||
RenderCelOutlineClippedXY<SkipColorIndexZero>(out, position, srcForBackwards, color); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* @brief Blit CEL sprite to the given buffer, checks for drawing outside the buffer. |
||||
* @param out Target buffer |
||||
* @param position Target buffer coordinate |
||||
* @param pRLEBytes CEL pixel stream (run-length encoded) |
||||
* @param nDataSize Size of CEL in bytes |
||||
* @param nWidth Width of sprite in pixels |
||||
*/ |
||||
void CelBlitSafeTo(const Surface &out, Point position, const byte *pRLEBytes, int nDataSize, int nWidth) |
||||
{ |
||||
assert(pRLEBytes != nullptr); |
||||
DoRenderBackwards</*TransparentCommandCanCrossLines=*/false, CelGetBlitCommand>( |
||||
out, position, reinterpret_cast<const uint8_t *>(pRLEBytes), nDataSize, nWidth, BlitDirect {}); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Same as CelBlitLightSafe, with blended transparency applied |
||||
* @param out Target buffer |
||||
* @param position Target buffer coordinate |
||||
* @param pRLEBytes CEL pixel stream (run-length encoded) |
||||
* @param nDataSize Size of CEL in bytes |
||||
* @param nWidth Width of sprite in pixels |
||||
* @param tbl Palette translation table |
||||
*/ |
||||
void CelBlitLightBlendedSafeTo(const Surface &out, Point position, const byte *pRLEBytes, int nDataSize, int nWidth, const uint8_t *tbl) |
||||
{ |
||||
assert(pRLEBytes != nullptr); |
||||
if (tbl == nullptr) |
||||
tbl = &LightTables[LightTableIndex * 256]; |
||||
|
||||
DoRenderBackwards</*TransparentCommandCanCrossLines=*/false, CelGetBlitCommand>( |
||||
out, position, reinterpret_cast<const uint8_t *>(pRLEBytes), nDataSize, nWidth, BlitBlendedWithMap { tbl }); |
||||
} |
||||
|
||||
void RenderCelWithLightTable(const Surface &out, Point position, const byte *src, std::size_t srcSize, std::size_t srcWidth, const uint8_t *tbl) |
||||
{ |
||||
DoRenderBackwards</*TransparentCommandCanCrossLines=*/false, CelGetBlitCommand>( |
||||
out, position, reinterpret_cast<const uint8_t *>(src), srcSize, srcWidth, BlitWithMap { tbl }); |
||||
} |
||||
|
||||
/**
|
||||
* @brief Blit CEL sprite, and apply lighting, to the given buffer, checks for drawing outside the buffer |
||||
* @param out Target buffer |
||||
* @param position Target buffer coordinate |
||||
* @param pRLEBytes CEL pixel stream (run-length encoded) |
||||
* @param nDataSize Size of CEL in bytes |
||||
* @param nWidth Width of sprite in pixels |
||||
* @param tbl Palette translation table |
||||
*/ |
||||
void CelBlitLightSafeTo(const Surface &out, Point position, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *tbl) |
||||
{ |
||||
assert(pRLEBytes != nullptr); |
||||
if (tbl == nullptr) |
||||
tbl = &LightTables[LightTableIndex * 256]; |
||||
RenderCelWithLightTable(out, position, pRLEBytes, nDataSize, nWidth, tbl); |
||||
} |
||||
|
||||
} // namespace
|
||||
|
||||
void CelDrawTo(const Surface &out, Point position, CelSprite cel, int frame) |
||||
{ |
||||
int nDataSize; |
||||
const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize); |
||||
CelBlitSafeTo(out, position, pRLEBytes, nDataSize, cel.Width(frame)); |
||||
} |
||||
|
||||
void CelClippedDrawTo(const Surface &out, Point position, CelSprite cel, int frame) |
||||
{ |
||||
int nDataSize; |
||||
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); |
||||
|
||||
CelBlitSafeTo(out, position, pRLEBytes, nDataSize, cel.Width(frame)); |
||||
} |
||||
|
||||
void CelDrawLightTo(const Surface &out, Point position, CelSprite cel, int frame, uint8_t *tbl) |
||||
{ |
||||
int nDataSize; |
||||
const auto *pRLEBytes = CelGetFrame(cel.Data(), frame, &nDataSize); |
||||
|
||||
if (LightTableIndex != 0 || tbl != nullptr) |
||||
CelBlitLightSafeTo(out, position, pRLEBytes, nDataSize, cel.Width(frame), tbl); |
||||
else |
||||
CelBlitSafeTo(out, position, pRLEBytes, nDataSize, cel.Width(frame)); |
||||
} |
||||
|
||||
void CelClippedDrawLightTo(const Surface &out, Point position, CelSprite cel, int frame) |
||||
{ |
||||
int nDataSize; |
||||
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); |
||||
|
||||
if (LightTableIndex != 0) |
||||
CelBlitLightSafeTo(out, position, pRLEBytes, nDataSize, cel.Width(frame), nullptr); |
||||
else |
||||
CelBlitSafeTo(out, position, pRLEBytes, nDataSize, cel.Width(frame)); |
||||
} |
||||
|
||||
void CelDrawLightRedTo(const Surface &out, Point position, CelSprite cel, int frame) |
||||
{ |
||||
int nDataSize; |
||||
const auto *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); |
||||
RenderCelWithLightTable(out, position, pRLEBytes, nDataSize, cel.Width(frame), GetInfravisionTRN()); |
||||
} |
||||
|
||||
void CelDrawItem(const Item &item, const Surface &out, Point position, CelSprite cel, int frame) |
||||
{ |
||||
bool usable = item._iStatFlag; |
||||
if (!usable) { |
||||
CelDrawLightRedTo(out, position, cel, frame); |
||||
} else { |
||||
CelClippedDrawTo(out, position, cel, frame); |
||||
} |
||||
} |
||||
|
||||
void CelClippedBlitLightTransTo(const Surface &out, Point position, CelSprite cel, int frame) |
||||
{ |
||||
int nDataSize; |
||||
const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize); |
||||
|
||||
if (cel_transparency_active) { |
||||
CelBlitLightBlendedSafeTo(out, position, pRLEBytes, nDataSize, cel.Width(frame), nullptr); |
||||
} else if (LightTableIndex != 0) |
||||
CelBlitLightSafeTo(out, position, pRLEBytes, nDataSize, cel.Width(frame), nullptr); |
||||
else |
||||
CelBlitSafeTo(out, position, pRLEBytes, nDataSize, cel.Width(frame)); |
||||
} |
||||
|
||||
void CelDrawUnsafeTo(const Surface &out, Point position, CelSprite cel, int frame) |
||||
{ |
||||
int srcSize; |
||||
const auto *srcBegin = reinterpret_cast<const uint8_t *>(CelGetFrame(cel.Data(), frame, &srcSize)); |
||||
RenderSrcBackwards src { srcBegin, srcBegin + srcSize, cel.Width(frame) }; |
||||
DoRenderBackwardsClipY</*TransparentCommandCanCrossLines=*/false, &CelGetBlitCommand>( |
||||
out, position, src, BlitDirect {}); |
||||
} |
||||
|
||||
void CelBlitOutlineTo(const Surface &out, uint8_t col, Point position, CelSprite cel, int frame, bool skipColorIndexZero) |
||||
{ |
||||
int nDataSize; |
||||
const uint8_t *src = reinterpret_cast<const uint8_t *>(CelGetFrameClipped(cel.Data(), frame, &nDataSize)); |
||||
if (skipColorIndexZero) |
||||
RenderCelOutline<true>(out, position, src, nDataSize, cel.Width(frame), col); |
||||
else |
||||
RenderCelOutline<false>(out, position, src, nDataSize, cel.Width(frame), col); |
||||
} |
||||
|
||||
std::pair<int, int> MeasureSolidHorizontalBounds(CelSprite cel, int frame) |
||||
{ |
||||
int nDataSize; |
||||
const byte *src = CelGetFrameClipped(cel.Data(), frame, &nDataSize); |
||||
const auto *end = &src[nDataSize]; |
||||
const int celWidth = cel.Width(frame); |
||||
|
||||
int xBegin = celWidth; |
||||
int xEnd = 0; |
||||
while (src < end) { |
||||
int xCur = 0; |
||||
while (xCur < celWidth) { |
||||
const auto val = static_cast<uint8_t>(*src++); |
||||
if (IsCelTransparent(val)) { |
||||
const int width = GetCelTransparentWidth(val); |
||||
xCur += width; |
||||
} else { |
||||
xBegin = std::min(xBegin, xCur); |
||||
xCur += val; |
||||
xEnd = std::max(xEnd, xCur); |
||||
src += val; |
||||
} |
||||
} |
||||
if (xBegin == 0 && xEnd == celWidth) |
||||
break; |
||||
} |
||||
return { xBegin, xEnd }; |
||||
} |
||||
|
||||
void CelApplyTrans(byte *p, const std::array<uint8_t, 256> &translation) |
||||
{ |
||||
assert(p != nullptr); |
||||
const uint32_t numFrames = LoadLE32(p); |
||||
const byte *frameOffsets = p + 4; |
||||
p += 4 * (2 + static_cast<size_t>(numFrames)); |
||||
|
||||
uint32_t frameEnd = LoadLE32(&frameOffsets[0]); |
||||
for (uint32_t i = 0; i < numFrames; ++i) { |
||||
const uint32_t frameBegin = frameEnd; |
||||
frameEnd = LoadLE32(&frameOffsets[4 * (static_cast<size_t>(i) + 1)]); |
||||
|
||||
const byte *end = p + (frameEnd - frameBegin); |
||||
const bool frameHasHeader = static_cast<uint8_t>(*p) == 0; |
||||
if (frameHasHeader) { |
||||
constexpr uint32_t FrameHeaderSize = 5 * 2; |
||||
p += FrameHeaderSize; |
||||
} |
||||
while (p != end) { |
||||
const auto val = static_cast<uint8_t>(*p++); |
||||
if (IsCelTransparent(val)) { |
||||
continue; |
||||
} |
||||
for (unsigned i = 0; i < val; ++i) { |
||||
const auto color = static_cast<uint8_t>(*p); |
||||
*p++ = static_cast<byte>(translation[color]); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
} // namespace devilution
|
||||
@ -1,116 +0,0 @@
|
||||
/**
|
||||
* @file cel_render.hpp |
||||
* |
||||
* CEL rendering. |
||||
*/ |
||||
#pragma once |
||||
|
||||
#include <utility> |
||||
|
||||
#include "engine.h" |
||||
#include "engine/cel_sprite.hpp" |
||||
#include "engine/point.hpp" |
||||
#include "items.h" |
||||
|
||||
namespace devilution { |
||||
|
||||
/**
|
||||
* Returns a pair of X coordinates containing the start (inclusive) and end (exclusive) |
||||
* of fully transparent columns in the sprite. |
||||
*/ |
||||
std::pair<int, int> MeasureSolidHorizontalBounds(CelSprite cel, int frame = 0); |
||||
|
||||
/**
|
||||
* @brief Apply the color swaps to a CEL sprite |
||||
* |
||||
* @param p CEL buffer |
||||
* @param translation Palette translation table |
||||
*/ |
||||
void CelApplyTrans(byte *p, const std::array<uint8_t, 256> &translation); |
||||
|
||||
/**
|
||||
* @brief Blit CEL sprite to the back buffer at the given coordinates |
||||
* @param out Target buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CEL sprite |
||||
* @param frame CEL frame number |
||||
*/ |
||||
void CelDrawTo(const Surface &out, Point position, CelSprite cel, int frame); |
||||
|
||||
/**
|
||||
* @brief Blit CEL sprite to the given buffer, does not perform bounds-checking. |
||||
* @param out Target buffer |
||||
* @param position Coordinate in the target buffer coordinate |
||||
* @param cel CEL sprite |
||||
* @param frame CEL frame number |
||||
*/ |
||||
void CelDrawUnsafeTo(const Surface &out, Point position, CelSprite cel, int frame); |
||||
|
||||
/**
|
||||
* @brief Same as CelDrawTo but with the option to skip parts of the top and bottom of the sprite |
||||
* @param out Target buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CEL sprite |
||||
* @param frame CEL frame number |
||||
*/ |
||||
void CelClippedDrawTo(const Surface &out, Point position, CelSprite cel, int frame); |
||||
|
||||
/**
|
||||
* @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates |
||||
* @param out Target buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CEL sprite |
||||
* @param frame CEL frame number |
||||
* @param tbl Palette translation table |
||||
*/ |
||||
void CelDrawLightTo(const Surface &out, Point position, CelSprite cel, int frame, uint8_t *tbl); |
||||
|
||||
/**
|
||||
* @brief Same as CelDrawLightTo but with the option to skip parts of the top and bottom of the sprite |
||||
* @param out Target buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CEL sprite |
||||
* @param frame CEL frame number |
||||
*/ |
||||
void CelClippedDrawLightTo(const Surface &out, Point position, CelSprite cel, int frame); |
||||
|
||||
/**
|
||||
* @brief Same as CelBlitLightSafeTo but with transparency applied |
||||
* @param out Target buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CEL sprite |
||||
* @param frame CEL frame number |
||||
*/ |
||||
void CelClippedBlitLightTransTo(const Surface &out, Point position, CelSprite cel, int frame); |
||||
|
||||
/**
|
||||
* @brief Blit CEL sprite, and apply lighting, to the back buffer at the given coordinates, translated to a red hue |
||||
* @param out Target buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CEL sprite |
||||
* @param frame CEL frame number |
||||
*/ |
||||
void CelDrawLightRedTo(const Surface &out, Point position, CelSprite cel, int frame); |
||||
|
||||
/**
|
||||
* @brief Blit item's CEL sprite recolored red if not usable, normal if usable |
||||
* @param item Item being drawn |
||||
* @param out Target buffer |
||||
* @param position Target buffer coordinate |
||||
* @param cel CEL sprite |
||||
* @param frame CEL frame number |
||||
*/ |
||||
void CelDrawItem(const Item &item, const Surface &out, Point position, CelSprite cel, int frame); |
||||
|
||||
/**
|
||||
* @brief Blit a solid colder shape one pixel larger than the given sprite shape, to the target buffer at the given coordianates |
||||
* @param out Target buffer |
||||
* @param col Color index from current palette |
||||
* @param position Target buffer coordinate |
||||
* @param cel CEL sprite |
||||
* @param frame CEL frame number |
||||
* @param skipColorIndexZero If true, color in index 0 will be treated as transparent (these are typically used for shadows in sprites) |
||||
*/ |
||||
void Cl2DrawOutline(const Surface &out, uint8_t col, Point position, CelSprite cel, int frame, bool skipColorIndexZero = true); |
||||
|
||||
} // namespace devilution
|
||||
Loading…
Reference in new issue