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.
 
 
 
 
 
 

325 lines
7.7 KiB

/**
* @file cl2_render.cpp
*
* CL2 rendering.
*/
#include "cl2_render.hpp"
#include "engine/render/common_impl.h"
#include "scrollrt.h"
namespace devilution {
namespace {
constexpr std::uint8_t MaxCl2Width = 65;
/**
* @brief Blit CL2 sprite to the given buffer
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param pRLEBytes CL2 pixel stream (run-length encoded)
* @param nDataSize Size of CL2 in bytes
* @param nWidth Width of sprite
*/
void Cl2BlitSafe(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth)
{
const byte *src = pRLEBytes;
BYTE *dst = out.at(sx, sy);
int w = nWidth;
while (nDataSize > 0) {
auto width = static_cast<std::int8_t>(*src++);
nDataSize--;
if (width < 0) {
width = -width;
if (width > MaxCl2Width) {
width -= MaxCl2Width;
nDataSize--;
const auto fill = static_cast<std::uint8_t>(*src++);
if (dst < out.end() && dst > out.begin()) {
w -= width;
while (width > 0) {
*dst = fill;
dst++;
width--;
}
if (w == 0) {
w = nWidth;
dst -= out.pitch() + w;
}
continue;
}
} else {
nDataSize -= width;
if (dst < out.end() && dst > out.begin()) {
w -= width;
while (width > 0) {
*dst = static_cast<std::uint8_t>(*src);
src++;
dst++;
width--;
}
if (w == 0) {
w = nWidth;
dst -= out.pitch() + w;
}
continue;
}
src += width;
}
}
while (width > 0) {
if (width > w) {
dst += w;
width -= w;
w = 0;
} else {
dst += width;
w -= width;
width = 0;
}
if (w == 0) {
w = nWidth;
dst -= out.pitch() + w;
}
}
}
}
/**
* @brief Blit a solid colder shape one pixel larger then the given sprite shape, to the given buffer
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param pRLEBytes CL2 pixel stream (run-length encoded)
* @param nDataSize Size of CL2 in bytes
* @param nWidth Width of sprite
* @param col Color index from current palette
*/
void Cl2BlitOutlineSafe(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t col)
{
const byte *src = pRLEBytes;
BYTE *dst = out.at(sx, sy);
int w = nWidth;
while (nDataSize > 0) {
auto width = static_cast<std::int8_t>(*src++);
nDataSize--;
if (width < 0) {
width = -width;
if (width > MaxCl2Width) {
width -= MaxCl2Width;
nDataSize--;
if (static_cast<std::uint8_t>(*src++) != 0 && dst < out.end() && dst > out.begin()) {
w -= width;
dst[-1] = col;
dst[width] = col;
while (width > 0) {
dst[-out.pitch()] = col;
dst[out.pitch()] = col;
dst++;
width--;
}
if (w == 0) {
w = nWidth;
dst -= out.pitch() + w;
}
continue;
}
} else {
nDataSize -= width;
if (dst < out.end() && dst > out.begin()) {
w -= width;
while (width > 0) {
if (static_cast<std::uint8_t>(*src) != 0) {
dst[-1] = col;
dst[1] = col;
dst[-out.pitch()] = col;
// BUGFIX: only set `if (dst+out.pitch() < out.end())`
dst[out.pitch()] = col;
}
src++;
dst++;
width--;
}
if (w == 0) {
w = nWidth;
dst -= out.pitch() + w;
}
continue;
}
src += width;
}
}
while (width > 0) {
if (width > w) {
dst += w;
width -= w;
w = 0;
} else {
dst += width;
w -= width;
width = 0;
}
if (w == 0) {
w = nWidth;
dst -= out.pitch() + w;
}
}
}
}
/**
* @brief Blit CL2 sprite, and apply lighting, to the given buffer
* @param out Target buffer
* @param sx Target buffer coordinate
* @param sy Target buffer coordinate
* @param pRLEBytes CL2 pixel stream (run-length encoded)
* @param nDataSize Size of CL2 in bytes
* @param nWidth With of CL2 sprite
* @param pTable Light color table
*/
void Cl2BlitLightSafe(const CelOutputBuffer &out, int sx, int sy, const byte *pRLEBytes, int nDataSize, int nWidth, uint8_t *pTable)
{
const byte *src = pRLEBytes;
BYTE *dst = out.at(sx, sy);
int w = nWidth;
while (nDataSize > 0) {
auto width = static_cast<std::int8_t>(*src++);
nDataSize--;
if (width < 0) {
width = -width;
if (width > MaxCl2Width) {
width -= MaxCl2Width;
nDataSize--;
const uint8_t fill = pTable[static_cast<std::uint8_t>(*src++)];
if (dst < out.end() && dst > out.begin()) {
w -= width;
while (width > 0) {
*dst = fill;
dst++;
width--;
}
if (w == 0) {
w = nWidth;
dst -= out.pitch() + w;
}
continue;
}
} else {
nDataSize -= width;
if (dst < out.end() && dst > out.begin()) {
w -= width;
while (width > 0) {
*dst = pTable[static_cast<std::uint8_t>(*src)];
src++;
dst++;
width--;
}
if (w == 0) {
w = nWidth;
dst -= out.pitch() + w;
}
continue;
}
src += width;
}
}
while (width > 0) {
if (width > w) {
dst += w;
width -= w;
w = 0;
} else {
dst += width;
w -= width;
width = 0;
}
if (w == 0) {
w = nWidth;
dst -= out.pitch() + w;
}
}
}
}
} // namespace
void Cl2ApplyTrans(byte *p, const std::array<uint8_t, 256> &ttbl, int nCel)
{
assert(p != nullptr);
for (int i = 1; i <= nCel; i++) {
int nDataSize;
byte *dst = CelGetFrame(p, i, &nDataSize) + 10;
nDataSize -= 10;
while (nDataSize > 0) {
auto width = static_cast<std::int8_t>(*dst++);
nDataSize--;
assert(nDataSize >= 0);
if (width < 0) {
width = -width;
if (width > MaxCl2Width) {
nDataSize--;
assert(nDataSize >= 0);
*dst = static_cast<byte>(ttbl[static_cast<std::uint8_t>(*dst)]);
dst++;
} else {
nDataSize -= width;
assert(nDataSize >= 0);
for (; width > 0; width--) {
*dst = static_cast<byte>(ttbl[static_cast<std::uint8_t>(*dst)]);
dst++;
}
}
}
}
}
}
void Cl2Draw(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
assert(frame > 0);
int nDataSize;
const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
Cl2BlitSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
void Cl2DrawOutline(const CelOutputBuffer &out, uint8_t col, int sx, int sy, const CelSprite &cel, int frame)
{
assert(frame > 0);
int nDataSize;
const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
const CelOutputBuffer &sub = out.subregionY(0, out.h() - 1);
Cl2BlitOutlineSafe(sub, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), col);
}
void Cl2DrawLightTbl(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame, char light)
{
assert(frame > 0);
int nDataSize;
const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
Cl2BlitLightSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), GetLightTable(light));
}
void Cl2DrawLight(const CelOutputBuffer &out, int sx, int sy, const CelSprite &cel, int frame)
{
assert(frame > 0);
int nDataSize;
const byte *pRLEBytes = CelGetFrameClipped(cel.Data(), frame, &nDataSize);
if (light_table_index != 0)
Cl2BlitLightSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame), &pLightTbl[light_table_index * 256]);
else
Cl2BlitSafe(out, sx, sy, pRLEBytes, nDataSize, cel.Width(frame));
}
} // namespace devilution