Files
2026-02-02 04:50:13 +01:00

263 lines
6.9 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/textconsole.h"
#include "m4/graphics/gr_sprite.h"
#include "m4/graphics/gr_surface.h"
#include "m4/core/errors.h"
#include "m4/core/imath.h"
#include "m4/core/term.h"
#include "m4/mem/memman.h"
#include "m4/platform/draw.h"
namespace M4 {
/**
* ScaleX and ScaleY are supposed to be percents, where 100 means 100%
* S and D are Raw encoded (unencoded!) buffers.
*/
static uint8 scale_sprite(Buffer *S, Buffer *D, uint32 ScaleX, uint32 ScaleY) {
if (!D)
error_show(FL, 'BUF!', "scale sprite NULL D");
if (!S)
error_show(FL, 'BUF!', "scale sprite h:%d w:%d sx:%uld sy:%uld", D->h, D->w, ScaleX, ScaleY);
uint8 *pData = S->data;
/* calculate new x size */
D->w = S->w * ScaleX / 100;
if (S->w * ScaleX % 100 >= 50)
++D->w;
/* calculate new y size */
D->h = S->h * ScaleY / 100;
if (S->h * ScaleY % 100 >= 50)
++D->h;
D->stride = D->w;
/* allocate 'scaled' buffer */
uint8 *pScaled = (uint8 *)mem_alloc(D->h * D->stride, "scaled buffer");
if (!pScaled)
error_show(FL, 'OOM!', "scaled buffer h:%uld w:%uld", D->h, D->stride);
D->data = pScaled;
uint16 ErrY = 50;
for (uint16 i = 0; i < S->h; ++i) {
ErrY += ScaleY;
while (ErrY >= 100) {
uint16 ErrX = 50;
for (uint16 j = 0; j < S->w; ++j) {
ErrX += ScaleX;
while (ErrX >= 100) {
*pScaled++ = *pData;
ErrX -= 100;
}
++pData;
}
ErrY -= 100;
pData -= S->w;
}
pData += S->w;
}
return 0;
}
#define Scaled ((drawReq->scaleY != 100) || (drawReq->scaleX != 100 && drawReq->scaleX != -100))
#define Rle (source.encoding == RLE8)
#define Clipped ((drawReq->x < 0) || (drawReq->y < 0) || (drawReq->x + source.w > drawReq->Dest->w) || (drawReq->y + source.h > drawReq->Dest->h))
#define Forward (drawReq->scaleX > 0)
#define Depthed (drawReq->srcDepth)
#define Shadow (source.encoding & 0x80)
#define ClipD (leftOffset || rightOffset || bottomCut)
uint8 gr_sprite_draw(DrawRequest *drawReq) {
Buffer source;
uint8 *shadowBuff = nullptr, *scaledBuff = nullptr;
Buffer afterScaled = { 0, 0, nullptr, 0, 0 };
if (!drawReq->Src) {
term_message("nullptr source data in sprite_draw");
return 0;
}
// Negative scaleY means don't bother drawing this sprite
if (drawReq->scaleY <= 0)
return 0;
if (!drawReq->Src->w || !drawReq->Src->h)
return 1;
// Copy DrawReq->Src to source buffer
source = *drawReq->Src;
assert(source.data);
// if it's RLE encoded, ensure the sprite will decode to match the expected size
if (source.encoding & RLE8) {
if (RLE8Decode_Size(source.data, source.stride) != (size_t)(source.stride * source.h))
error_show(FL, 'RLE8', "RLE8 sprite suspected BAD!");
}
// Check for RLE encoding in case of shadows
// There is no RLE shadow draw routine, so we have to decode shadows ahead of time.
if ((source.encoding & RLE8) && (source.encoding & SHADOW)) {
if (!(shadowBuff = (uint8 *)mem_alloc(source.stride * source.h, "shadow buff")))
error_show(FL, 'OOM!', "buffer w:%uld, h:%uld", source.w, source.h);
RLE8Decode(source.data, shadowBuff, source.stride);
source.data = shadowBuff;
source.encoding &= ~RLE8;
}
// Check for scaling
// We scale before we draw
if (Scaled) {
// Check if input is RLE8 encoded
// If it's scaled we decode it first
if (Rle) {
if (!(scaledBuff = (uint8 *)mem_alloc(source.stride * source.h, "scaled buffer")))
error_show(FL, 'OOM!', "no mem: buffer w:%d, h:%d", source.w, source.h);
RLE8Decode(source.data, scaledBuff, source.stride);
source.data = scaledBuff;
source.encoding &= ~RLE8;
}
if (scale_sprite(&source, &afterScaled, imath_abs(drawReq->scaleX), imath_abs(drawReq->scaleY))) {
if (shadowBuff)
mem_free(shadowBuff);
if (scaledBuff)
mem_free(scaledBuff);
error_show(FL, 'SPSF', "gr_sprite_draw");
}
// Preserve encoding
afterScaled.encoding = source.encoding;
// Copy AfterScaled to source buffer
source = afterScaled;
}
const bool shadow = (drawReq->Src->encoding & SHADOW) != 0;
assert(!shadow || drawReq->ICT);
M4Surface dst(*drawReq->Dest);
dst.draw(source, drawReq->x, drawReq->y, drawReq->scaleX > 0,
drawReq->srcDepth ? drawReq->depthCode : nullptr, drawReq->srcDepth,
shadow ? drawReq->ICT : nullptr,
drawReq->Pal);
if (shadowBuff)
mem_free(shadowBuff);
if (scaledBuff)
mem_free(scaledBuff);
if (afterScaled.data)
mem_free(afterScaled.data);
return 0;
}
//----------------------------------------------------------------------------------------
//RLE8 COMPRESSION CODE...
#define ESC ((uint8)0)
#define EOL ((uint8)0)
#define EOB ((uint8)1)
#define DELTA ((uint8)2)
#define OutBuffSize(x) ((x) + (((x) + 254) / 255 + 1) * 2 + 2)
static uint16 EncodeScan(uint8 *pi, uint8 *po, uint16 scanlen, uint8 EndByte) {
uint8 *ps = pi + 1;
uint16 outlen = 0, run;
while (scanlen) {
uint16 limit = (scanlen < 255) ? scanlen : 255;
for (run = 1; run < limit && *pi == *ps; ++run, ++ps) {}
if (run > 1) {
scanlen -= run;
*po++ = run;
*po++ = *pi;
outlen += 2;
pi = ps++;
} else if (scanlen < 3) {
for (; scanlen; --scanlen) {
*po++ = 1;
*po++ = *pi++;
outlen += 2;
}
} else {
--ps;
do {
++ps;
while ((*ps != *(ps + 1) || *ps != *(ps + 2) || *ps != *(ps + 3)) && (ps - pi) < limit)
++ps;
} while ((run = ps - pi) < 3);
scanlen -= run;
*po++ = ESC;
*po++ = run;
outlen += (run + 2);
for (limit = 0; limit < run; ++limit)
*po++ = *pi++;
++ps;
}
}
*po++ = ESC;
*po = EndByte;
outlen += 2;
return outlen;
}
uint32 gr_sprite_RLE8_encode(Buffer *Source, Buffer *Dest) {
int i;
uint32 Offset = 0;
Dest->w = Source->w;
Dest->h = Source->h;
Dest->encoding = RLE8;
Dest->stride = Source->stride;
Dest->data = (uint8 *)mem_alloc(Source->h * OutBuffSize(Source->stride), "sprite data");
if (!Dest->data) {
return 0;
}
for (i = 0; i < Source->h - 1; ++i)
Offset += EncodeScan(Source->data + i * Source->stride, Dest->data + Offset, Source->w, EOL);
Offset += EncodeScan(Source->data + i * Source->stride, Dest->data + Offset, Source->w, EOB);
Dest->data = (uint8 *)mem_realloc(Dest->data, Offset, "rle8 sprite data");
return Offset;
}
} // namespace M4