Initial commit
This commit is contained in:
262
engines/m4/graphics/gr_sprite.cpp
Normal file
262
engines/m4/graphics/gr_sprite.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
/* 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
|
||||
Reference in New Issue
Block a user