mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-18 07:39:08 +00:00
930 lines
21 KiB
C++
930 lines
21 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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "common/endian.h"
|
|
#include "common/system.h"
|
|
#include "mortevielle/graphics.h"
|
|
#include "mortevielle/mortevielle.h"
|
|
|
|
namespace Mortevielle {
|
|
|
|
/*-------------------------------------------------------------------------*
|
|
* Image decoding
|
|
*
|
|
* The code in this section is responsible for decoding image resources.
|
|
* Images are broken down into rectangular sections, which can use one
|
|
* of 18 different encoding methods.
|
|
*-------------------------------------------------------------------------*/
|
|
|
|
#define INCR_TAIX { if (_xSize & 1) ++_xSize; }
|
|
#define DEFAULT_WIDTH (SCREEN_WIDTH / 2)
|
|
#define BUFFER_SIZE 8192
|
|
|
|
void GfxSurface::decode(const byte *pSrc) {
|
|
_width = _height = 0;
|
|
_var1 = *pSrc++;
|
|
int entryCount = *pSrc++;
|
|
pSrc += 2;
|
|
|
|
if (!_var1)
|
|
pSrc += 30;
|
|
|
|
// First run through the data to calculate starting offsets
|
|
const byte *p = pSrc;
|
|
_xOffset = _yOffset = 0xffff;
|
|
|
|
assert(entryCount > 0);
|
|
for (int idx = 0; idx < entryCount; ++idx) {
|
|
_xp = READ_BE_UINT16(p + 4);
|
|
if (_xp < _xOffset)
|
|
_xOffset = _xp;
|
|
|
|
_yp = READ_BE_UINT16(p + 6);
|
|
if (_yp < _yOffset)
|
|
_yOffset = _yp;
|
|
|
|
// Move to next entry
|
|
int size = READ_BE_UINT16(p) + READ_BE_UINT16(p + 2);
|
|
if ((size % 2) == 1)
|
|
++size;
|
|
|
|
p += size + 14;
|
|
}
|
|
|
|
// Temporary output buffer
|
|
byte outputBuffer[65536];
|
|
Common::fill(&outputBuffer[0], &outputBuffer[65536], 0);
|
|
|
|
byte *pDest = &outputBuffer[0];
|
|
const byte *pSrcStart = pSrc;
|
|
const byte *pLookup = NULL;
|
|
|
|
byte lookupTable[BUFFER_SIZE];
|
|
byte srcBuffer[BUFFER_SIZE];
|
|
|
|
// Main processing loop
|
|
for (int entryIndex = 0; entryIndex < entryCount; ++entryIndex) {
|
|
int lookupBytes = READ_BE_UINT16(pSrc);
|
|
int srcSize = READ_BE_UINT16(pSrc + 2);
|
|
_xp = READ_BE_UINT16(pSrc + 4) - _xOffset;
|
|
_yp = READ_BE_UINT16(pSrc + 6) - _yOffset;
|
|
pSrc += 8;
|
|
|
|
int decomCode = READ_BE_UINT16(pSrc);
|
|
_xSize = READ_BE_UINT16(pSrc + 2) + 1;
|
|
_ySize = READ_BE_UINT16(pSrc + 4) + 1;
|
|
majTtxTty();
|
|
|
|
pSrc += 6;
|
|
pDest = &outputBuffer[0];
|
|
|
|
_var18 = 0;
|
|
_nibbleFlag = false;
|
|
|
|
int decomIndex = 0;
|
|
if (decomCode >> 8) {
|
|
// Build up reference table
|
|
int tableOffset = 0;
|
|
|
|
if (decomCode & 1) {
|
|
// Handle decompression of the pattern lookup table
|
|
do {
|
|
_var12 = desanalyse(pSrc);
|
|
_var14 = desanalyse(pSrc);
|
|
|
|
const byte *pSrcSaved = pSrc;
|
|
bool savedNibbleFlag = _nibbleFlag;
|
|
int savedVar18 = _var18;
|
|
|
|
do {
|
|
pSrc = pSrcSaved;
|
|
_nibbleFlag = savedNibbleFlag;
|
|
_var18 = savedVar18;
|
|
|
|
assert(_var14 < 256);
|
|
for (int idx = 0; idx < _var14; ++idx, ++tableOffset) {
|
|
assert(tableOffset < BUFFER_SIZE);
|
|
lookupTable[tableOffset] = suiv(pSrc);
|
|
}
|
|
} while (--_var12 > 0);
|
|
} while (_var18 < (lookupBytes - 1));
|
|
|
|
} else {
|
|
assert(lookupBytes < BUFFER_SIZE);
|
|
for (int idx = 0; idx < (lookupBytes * 2); ++idx)
|
|
lookupTable[idx] = suiv(pSrc);
|
|
}
|
|
|
|
if (_nibbleFlag) {
|
|
++pSrc;
|
|
_nibbleFlag = false;
|
|
}
|
|
if ((lookupBytes + srcSize) & 1)
|
|
++pSrc;
|
|
|
|
tableOffset = 0;
|
|
_var18 = 0;
|
|
|
|
if (decomCode & 2) {
|
|
// Handle decompression of the temporary source buffer
|
|
do {
|
|
_var12 = desanalyse(pSrc);
|
|
_var14 = desanalyse(pSrc);
|
|
_var18 += _var14;
|
|
|
|
if (_nibbleFlag) {
|
|
++pSrc;
|
|
++_var18;
|
|
_nibbleFlag = false;
|
|
}
|
|
|
|
const byte *pStart = pSrc;
|
|
do {
|
|
pSrc = pStart;
|
|
for (int idx = 0; idx < _var14; ++idx) {
|
|
assert(tableOffset < BUFFER_SIZE);
|
|
srcBuffer[tableOffset++] = *pSrc++;
|
|
}
|
|
} while (--_var12 > 0);
|
|
} while (_var18 < (srcSize - 1));
|
|
} else {
|
|
assert(srcSize < BUFFER_SIZE);
|
|
for (int idx = 0; idx < srcSize; ++idx)
|
|
srcBuffer[idx] = *pSrc++;
|
|
}
|
|
|
|
if (_nibbleFlag)
|
|
++pSrc;
|
|
|
|
// Switch over to using the decompressed source and lookup buffers
|
|
pSrcStart = pSrc;
|
|
pDest = &outputBuffer[_yp * DEFAULT_WIDTH + _xp];
|
|
pSrc = &srcBuffer[0];
|
|
pLookup = &lookupTable[0] - 1;
|
|
|
|
_lookupValue = _var18 = 0;
|
|
_nibbleFlag = false;
|
|
decomIndex = decomCode >> 8;
|
|
}
|
|
|
|
// Main decompression switch
|
|
switch (decomIndex) {
|
|
case 0:
|
|
// Draw rect at pos
|
|
pDest = &outputBuffer[_yp * DEFAULT_WIDTH + _xp];
|
|
pSrcStart = pSrc;
|
|
INCR_TAIX;
|
|
|
|
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest += DEFAULT_WIDTH) {
|
|
byte *pDestLine = pDest;
|
|
for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
|
|
*pDestLine++ = suiv(pSrc);
|
|
}
|
|
}
|
|
|
|
pSrc = pSrcStart + lookupBytes + ((lookupBytes & 1) ? 1 : 0);
|
|
break;
|
|
|
|
case 1:
|
|
// Draw rect using horizontal lines alternating left to right, then right to left
|
|
INCR_TAIX;
|
|
for (int yCtr = 0; yCtr < _ySize; ++yCtr) {
|
|
if ((yCtr % 2) == 0) {
|
|
for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
|
|
*pDest++ = csuiv(pSrc, pLookup);
|
|
}
|
|
} else {
|
|
for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
|
|
*--pDest = csuiv(pSrc, pLookup);
|
|
}
|
|
}
|
|
pDest += DEFAULT_WIDTH;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
// Draw rect alternating top to bottom, bottom to top
|
|
INCR_TAIX;
|
|
for (int xCtr = 0; xCtr < _xSize; ++xCtr) {
|
|
if ((xCtr % 2) == 0) {
|
|
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest += DEFAULT_WIDTH) {
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
}
|
|
} else {
|
|
for (int yCtr = 0; yCtr < _ySize; ++yCtr) {
|
|
pDest -= DEFAULT_WIDTH;
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
}
|
|
}
|
|
++pDest;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
// Draw horizontal area?
|
|
_thickness = 2;
|
|
horizontal(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 4:
|
|
// Draw vertical area?
|
|
_thickness = 2;
|
|
vertical(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 5:
|
|
_thickness = 3;
|
|
horizontal(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 6:
|
|
_thickness = 4;
|
|
vertical(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 7:
|
|
// Draw rect using horizontal lines left to right
|
|
INCR_TAIX;
|
|
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDest += DEFAULT_WIDTH) {
|
|
byte *pDestLine = pDest;
|
|
for (int xCtr = 0; xCtr < _xSize; ++xCtr)
|
|
*pDestLine++ = csuiv(pSrc, pLookup);
|
|
}
|
|
break;
|
|
|
|
case 8:
|
|
// Draw box
|
|
for (int xCtr = 0; xCtr < _xSize; ++xCtr, ++pDest) {
|
|
byte *pDestLine = pDest;
|
|
for (int yCtr = 0; yCtr < _ySize; ++yCtr, pDestLine += DEFAULT_WIDTH)
|
|
*pDestLine = csuiv(pSrc, pLookup);
|
|
}
|
|
break;
|
|
|
|
case 9:
|
|
_thickness = 4;
|
|
horizontal(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 10:
|
|
_thickness = 6;
|
|
horizontal(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 11:
|
|
decom11(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 12:
|
|
INCR_TAIX;
|
|
_thickness = _xInc = 1;
|
|
_yInc = DEFAULT_WIDTH;
|
|
_var20 = _ySize;
|
|
_var24 = _xSize;
|
|
diag(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 13:
|
|
INCR_TAIX;
|
|
_thickness = _xSize;
|
|
_yInc = 1;
|
|
_var20 = _xSize;
|
|
_xInc = DEFAULT_WIDTH;
|
|
_var24 = _ySize;
|
|
diag(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 14:
|
|
_thickness = _yInc = 1;
|
|
_var20 = _xSize;
|
|
_xInc = DEFAULT_WIDTH;
|
|
_var24 = _ySize;
|
|
diag(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 15:
|
|
INCR_TAIX;
|
|
_thickness = 2;
|
|
_yInc = DEFAULT_WIDTH;
|
|
_var20 = _ySize;
|
|
_xInc = 1;
|
|
_var24 = _xSize;
|
|
diag(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 16:
|
|
_thickness = 3;
|
|
_yInc = 1;
|
|
_var20 = _xSize;
|
|
_xInc = DEFAULT_WIDTH;
|
|
_var24 = _ySize;
|
|
diag(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 17:
|
|
INCR_TAIX;
|
|
_thickness = 3;
|
|
_yInc = DEFAULT_WIDTH;
|
|
_var20 = _ySize;
|
|
_xInc = 1;
|
|
_var24 = _xSize;
|
|
diag(pSrc, pDest, pLookup);
|
|
break;
|
|
|
|
case 18:
|
|
INCR_TAIX;
|
|
_thickness = 5;
|
|
_yInc = DEFAULT_WIDTH;
|
|
_var20 = _ySize;
|
|
_xInc = 1;
|
|
_var24 = _xSize;
|
|
diag(pSrc, pDest, pLookup);
|
|
break;
|
|
}
|
|
|
|
pSrc = pSrcStart;
|
|
debugC(1, kMortevielleGraphics, "Decoding image block %d position %d,%d size %d,%d method %d",
|
|
entryIndex + 1, _xp, _yp, _width, _height, decomIndex);
|
|
}
|
|
|
|
// At this point, the outputBuffer has the data for the image. Initialise the surface
|
|
// with the calculated size for the full image, and copy the lines to the surface
|
|
create(_xOffset + _width, _yOffset + _height, Graphics::PixelFormat::createFormatCLUT8());
|
|
|
|
for (int yCtr = 0; yCtr < _height; ++yCtr) {
|
|
const byte *copySrc = &outputBuffer[yCtr * DEFAULT_WIDTH];
|
|
byte *copyDest = (byte *)getBasePtr(_xOffset, yCtr + _yOffset);
|
|
|
|
Common::copy(copySrc, copySrc + _width, copyDest);
|
|
}
|
|
}
|
|
|
|
void GfxSurface::majTtxTty() {
|
|
if (!_yp)
|
|
_width += _xSize;
|
|
|
|
if (!_xp)
|
|
_height += _ySize;
|
|
}
|
|
|
|
byte GfxSurface::suiv(const byte *&pSrc) {
|
|
int v = *pSrc;
|
|
if (_nibbleFlag) {
|
|
++pSrc;
|
|
++_var18;
|
|
_nibbleFlag = false;
|
|
return v & 0xf;
|
|
} else {
|
|
_nibbleFlag = !_nibbleFlag;
|
|
return v >> 4;
|
|
}
|
|
}
|
|
|
|
byte GfxSurface::csuiv(const byte *&pSrc, const byte *&pLookup) {
|
|
assert(pLookup);
|
|
|
|
while (!_lookupValue) {
|
|
int v;
|
|
do {
|
|
v = suiv(pSrc) & 0xff;
|
|
_lookupValue += v;
|
|
} while (v == 0xf);
|
|
++pLookup;
|
|
}
|
|
|
|
--_lookupValue;
|
|
return *pLookup;
|
|
}
|
|
|
|
int GfxSurface::desanalyse(const byte *&pSrc) {
|
|
int total = 0;
|
|
int v = suiv(pSrc);
|
|
if (v == 15) {
|
|
int v2;
|
|
do {
|
|
v2 = suiv(pSrc);
|
|
total += v2;
|
|
} while (v2 == 15);
|
|
|
|
total *= 15;
|
|
v = suiv(pSrc);
|
|
}
|
|
|
|
total += v;
|
|
return total;
|
|
}
|
|
|
|
void GfxSurface::horizontal(const byte *&pSrc, byte *&pDest, const byte *&pLookup) {
|
|
INCR_TAIX;
|
|
byte *pDestEnd = pDest + (_ySize - 1) * DEFAULT_WIDTH + _xSize;
|
|
|
|
for (;;) {
|
|
// If position is past end point, then skip this line
|
|
if (((_thickness - 1) * DEFAULT_WIDTH) + pDest >= pDestEnd) {
|
|
if (--_thickness == 0) break;
|
|
continue;
|
|
}
|
|
|
|
bool continueFlag = false;
|
|
do {
|
|
for (int xIndex = 0; xIndex < _xSize; ++xIndex) {
|
|
if ((xIndex % 2) == 0) {
|
|
if (xIndex != 0)
|
|
++pDest;
|
|
|
|
// Write out vertical slice top to bottom
|
|
for (int yIndex = 0; yIndex < _thickness; ++yIndex, pDest += DEFAULT_WIDTH)
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
|
|
++pDest;
|
|
} else {
|
|
// Write out vertical slice bottom to top
|
|
for (int yIndex = 0; yIndex < _thickness; ++yIndex, pDest -= DEFAULT_WIDTH)
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
}
|
|
}
|
|
|
|
if ((_xSize % 2) == 0) {
|
|
int blockSize = _thickness * DEFAULT_WIDTH;
|
|
pDest += blockSize;
|
|
blockSize -= DEFAULT_WIDTH;
|
|
|
|
if (pDestEnd < (pDest + blockSize)) {
|
|
do {
|
|
if (--_thickness == 0)
|
|
return;
|
|
} while ((pDest + (_thickness - 1) * DEFAULT_WIDTH) >= pDestEnd);
|
|
}
|
|
} else {
|
|
while ((pDest + (_thickness - 1) * DEFAULT_WIDTH) >= pDestEnd) {
|
|
if (--_thickness == 0)
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (int xIndex = 0; xIndex < _xSize; ++xIndex, --pDest) {
|
|
if ((xIndex % 2) == 0) {
|
|
// Write out vertical slice top to bottom
|
|
for (int yIndex = 0; yIndex < _thickness; ++yIndex, pDest += DEFAULT_WIDTH)
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
} else {
|
|
// Write out vertical slice top to bottom
|
|
for (int yIndex = 0; yIndex < _thickness; ++yIndex, pDest -= DEFAULT_WIDTH)
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
}
|
|
}
|
|
|
|
if ((_xSize % 2) == 1) {
|
|
++pDest;
|
|
|
|
if ((pDest + (_thickness - 1) * DEFAULT_WIDTH) < pDestEnd) {
|
|
continueFlag = true;
|
|
break;
|
|
}
|
|
} else {
|
|
pDest += _thickness * DEFAULT_WIDTH + 1;
|
|
continueFlag = true;
|
|
break;
|
|
}
|
|
|
|
++pDest;
|
|
} while (((_thickness - 1) * DEFAULT_WIDTH + pDest) < pDestEnd);
|
|
|
|
if (continueFlag)
|
|
continue;
|
|
|
|
// Move to next line
|
|
if (--_thickness == 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GfxSurface::vertical(const byte *&pSrc, byte *&pDest, const byte *&pLookup) {
|
|
// byte *pDestEnd = pDest + (_ySize - 1) * DEFAULT_WIDTH + _xSize;
|
|
int var28 = 0;
|
|
|
|
for (;;) {
|
|
// Reduce thickness as necessary
|
|
while ((var28 + _thickness) > _xSize) {
|
|
if (--_thickness == 0)
|
|
return;
|
|
}
|
|
|
|
// Loop
|
|
for (int yCtr = 0; yCtr < _ySize; ++yCtr) {
|
|
if ((yCtr % 2) == 0) {
|
|
if (yCtr > 0)
|
|
pDest += DEFAULT_WIDTH;
|
|
|
|
var28 += _thickness;
|
|
for (int xCtr = 0; xCtr < _thickness; ++xCtr)
|
|
*pDest++ = csuiv(pSrc, pLookup);
|
|
} else {
|
|
pDest += DEFAULT_WIDTH;
|
|
var28 -= _thickness;
|
|
for (int xCtr = 0; xCtr < _thickness; ++xCtr)
|
|
*--pDest = csuiv(pSrc, pLookup);
|
|
}
|
|
}
|
|
if ((_ySize % 2) == 0) {
|
|
pDest += _thickness;
|
|
var28 += _thickness;
|
|
}
|
|
|
|
while (_xSize < (var28 + _thickness)) {
|
|
if (--_thickness == 0)
|
|
return;
|
|
}
|
|
|
|
// Loop
|
|
for (int yCtr = 0; yCtr < _ySize; ++yCtr) {
|
|
if ((yCtr % 2) == 0) {
|
|
if (yCtr > 0)
|
|
pDest -= DEFAULT_WIDTH;
|
|
|
|
var28 += _thickness;
|
|
|
|
for (int xCtr = 0; xCtr < _thickness; ++xCtr)
|
|
*pDest++ = csuiv(pSrc, pLookup);
|
|
} else {
|
|
pDest -= DEFAULT_WIDTH;
|
|
var28 -= _thickness;
|
|
|
|
for (int xCtr = 0; xCtr < _thickness; ++xCtr)
|
|
*--pDest = csuiv(pSrc, pLookup);
|
|
}
|
|
}
|
|
if ((_ySize % 2) == 0) {
|
|
pDest += _thickness;
|
|
var28 += _thickness;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GfxSurface::decom11(const byte *&pSrc, byte *&pDest, const byte *&pLookup) {
|
|
int var26 = 0, var28 = 0;
|
|
_yInc = DEFAULT_WIDTH;
|
|
_xInc = -1;
|
|
--_xSize;
|
|
--_ySize;
|
|
|
|
int areaNum = 0;
|
|
while (areaNum != -1) {
|
|
switch (areaNum) {
|
|
case 0:
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
areaNum = 1;
|
|
break;
|
|
|
|
case 1:
|
|
increments(pDest);
|
|
|
|
if (!var28) {
|
|
NIH();
|
|
NIV();
|
|
|
|
if (var26 == _ySize) {
|
|
increments(pDest);
|
|
++var28;
|
|
} else {
|
|
++var26;
|
|
}
|
|
|
|
*++pDest = csuiv(pSrc, pLookup);
|
|
areaNum = 2;
|
|
} else if (var26 != _ySize) {
|
|
++var26;
|
|
--var28;
|
|
areaNum = 0;
|
|
} else {
|
|
NIH();
|
|
NIV();
|
|
increments(pDest);
|
|
++var28;
|
|
|
|
*++pDest = csuiv(pSrc, pLookup);
|
|
|
|
if (var28 == _xSize) {
|
|
areaNum = -1;
|
|
} else {
|
|
areaNum = 2;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
increments(pDest);
|
|
|
|
if (!var26) {
|
|
NIH();
|
|
NIV();
|
|
|
|
if (var28 == _xSize) {
|
|
increments(pDest);
|
|
++var26;
|
|
} else {
|
|
++var28;
|
|
}
|
|
|
|
pDest += DEFAULT_WIDTH;
|
|
areaNum = 0;
|
|
} else if (var28 != _xSize) {
|
|
++var28;
|
|
--var26;
|
|
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
areaNum = 2;
|
|
} else {
|
|
pDest += DEFAULT_WIDTH;
|
|
++var26;
|
|
NIH();
|
|
NIV();
|
|
increments(pDest);
|
|
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
|
|
if (var26 == _ySize)
|
|
areaNum = -1;
|
|
else
|
|
areaNum = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GfxSurface::diag(const byte *&pSrc, byte *&pDest, const byte *&pLookup) {
|
|
int var26 = 0, var28 = 0;
|
|
--_var24;
|
|
|
|
while (!TFP(var26)) {
|
|
for (;;) {
|
|
NIH();
|
|
for (int idx = 0; idx <= _thickness; ++idx) {
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
NIH();
|
|
increments(pDest);
|
|
}
|
|
|
|
NIV();
|
|
pDest += _yInc;
|
|
|
|
for (int idx = 0; idx <= _thickness; ++idx) {
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
NIH();
|
|
increments(pDest);
|
|
}
|
|
|
|
NIH();
|
|
NIV();
|
|
increments(pDest);
|
|
|
|
++var28;
|
|
if (_var24 < (var28 + 1)) {
|
|
TF1(pDest, var26);
|
|
break;
|
|
}
|
|
|
|
pDest += _xInc;
|
|
++var28;
|
|
if (_var24 < (var28 + 1)) {
|
|
TF2(pSrc, pDest, pLookup, var26);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (TFP(var26))
|
|
break;
|
|
|
|
for (;;) {
|
|
for (int idx = 0; idx <= _thickness; ++idx) {
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
NIH();
|
|
increments(pDest);
|
|
}
|
|
|
|
NIV();
|
|
pDest += _yInc;
|
|
|
|
for (int idx = 0; idx <= _thickness; ++idx) {
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
NIH();
|
|
increments(pDest);
|
|
}
|
|
|
|
NIH();
|
|
NIV();
|
|
increments(pDest);
|
|
|
|
if (--var28 == 0) {
|
|
TF1(pDest, var26);
|
|
NIH();
|
|
break;
|
|
} else {
|
|
pDest += _xInc;
|
|
|
|
if (--var28 == 0) {
|
|
TF2(pSrc, pDest, pLookup, var26);
|
|
NIH();
|
|
break;
|
|
}
|
|
}
|
|
|
|
NIH();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void GfxSurface::increments(byte *&pDest) {
|
|
pDest += _xInc + _yInc;
|
|
}
|
|
|
|
void GfxSurface::NIH() {
|
|
_xInc = -_xInc;
|
|
}
|
|
|
|
void GfxSurface::NIV() {
|
|
_yInc = -_yInc;
|
|
}
|
|
|
|
bool GfxSurface::TFP(int v) {
|
|
int diff = _var20 - v;
|
|
if (!diff)
|
|
// Time to finish loop in outer method
|
|
return true;
|
|
|
|
if (diff < (_thickness + 1))
|
|
_thickness = diff - 1;
|
|
|
|
return false;
|
|
}
|
|
|
|
void GfxSurface::TF1(byte *&pDest, int &v) {
|
|
v += _thickness + 1;
|
|
pDest += (_thickness + 1) * _yInc;
|
|
}
|
|
|
|
void GfxSurface::TF2(const byte *&pSrc, byte *&pDest, const byte *&pLookup, int &v) {
|
|
v += _thickness + 1;
|
|
|
|
for (int idx = 0; idx <= _thickness; ++idx) {
|
|
*pDest = csuiv(pSrc, pLookup);
|
|
pDest += _yInc;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
GfxSurface::~GfxSurface() {
|
|
free();
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*
|
|
* Screen surface
|
|
*-------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Called to populate the font data from the passed file
|
|
*/
|
|
void ScreenSurface::readFontData(Common::File &f, int dataSize) {
|
|
assert(dataSize == (FONT_NUM_CHARS * FONT_HEIGHT));
|
|
f.read(_fontData, FONT_NUM_CHARS * FONT_HEIGHT);
|
|
}
|
|
|
|
/**
|
|
* Returns a graphics surface representing a subset of the screen. The affected area
|
|
* is also marked as dirty
|
|
*/
|
|
Graphics::Surface ScreenSurface::lockArea(const Common::Rect &bounds) {
|
|
_dirtyRects.push_back(bounds);
|
|
|
|
Graphics::Surface s;
|
|
s.pixels = getBasePtr(bounds.left, bounds.top);
|
|
s.pitch = pitch;
|
|
s.w = bounds.width();
|
|
s.h = bounds.height();
|
|
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Updates the affected areas of the surface to the underlying physical screen
|
|
*/
|
|
void ScreenSurface::updateScreen() {
|
|
// Iterate through copying dirty areas to the screen
|
|
for (Common::List<Common::Rect>::iterator i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) {
|
|
Common::Rect r = *i;
|
|
g_system->copyRectToScreen((const byte *)getBasePtr(r.left, r.top), pitch,
|
|
r.left, r.top, r.width(), r.height());
|
|
}
|
|
_dirtyRects.clear();
|
|
|
|
// Update the screen
|
|
g_system->updateScreen();
|
|
}
|
|
|
|
/**
|
|
* Draws a decoded picture on the screen
|
|
* @remarks Because the ScummVM surface is using a double height 640x200 surface to
|
|
* simulate the original 640x200 surface, all Y values have to be doubled.
|
|
* Also, image resources are stored at 320x200, so when drawn onto the screen every
|
|
* other column is interpolated.
|
|
*/
|
|
void ScreenSurface::drawPicture(GfxSurface &surface, int x, int y) {
|
|
// Lock the affected area of the surface to write to
|
|
Graphics::Surface destSurface = lockArea(Common::Rect(x, y,
|
|
x + surface.w * 2, y + surface.h * 2));
|
|
|
|
// Loop through writing
|
|
for (int yp = 0; yp < surface.h; ++yp) {
|
|
if (((y + yp) < 0) || ((y + yp) >= 200))
|
|
continue;
|
|
|
|
const byte *pSrc = (const byte *)surface.getBasePtr(0, yp);
|
|
byte *pDest = (byte *)destSurface.getBasePtr(0, yp * 2);
|
|
|
|
for (int xp = 0; xp < surface.w; ++xp, ++pSrc) {
|
|
// Draw pixel from source image
|
|
*pDest = *pSrc;
|
|
*(pDest + SCREEN_WIDTH) = *pSrc;
|
|
++pDest;
|
|
|
|
// TODO: I'm not sure what algorithm the original uses to calculate
|
|
// which pixel to use on the alternate columns, so for now I'm doing
|
|
// a simple output of null values. This should be revisited once we've
|
|
// got the palette loading so we can compare palettes. In fact, the
|
|
// original had the alternate columns very noticablely striped. With
|
|
// the larger 256 colour palette, it may be worthwhile to offer a
|
|
// better blended graphics mode as an option.
|
|
*pDest = 0;
|
|
*(pDest + SCREEN_WIDTH) = 0;
|
|
++pDest;
|
|
}
|
|
}
|
|
|
|
// TODO: Remove this once we have a proper game loop
|
|
updateScreen();
|
|
}
|
|
|
|
/**
|
|
* Draws a character at the specified co-ordinates
|
|
* @remarks Because the ScummVM surface is using a double height 640x200 surface to
|
|
* simulate the original 640x200 surface, all Y values have to be doubled
|
|
*/
|
|
void ScreenSurface::writeCharacter(const Common::Point &pt, unsigned char ch, int palIndex) {
|
|
Graphics::Surface destSurface = lockArea(Common::Rect(pt.x, pt.y * 2,
|
|
pt.x + FONT_WIDTH, (pt.y + FONT_HEIGHT) * 2));
|
|
|
|
// Get the start of the character to use
|
|
assert((ch >= ' ') && (ch <= (unsigned char)(32 + FONT_NUM_CHARS)));
|
|
const byte *charData = &_fontData[((int)ch - 32) * FONT_HEIGHT];
|
|
|
|
// Loop through decoding each character's data
|
|
for (int yp = 0; yp < FONT_HEIGHT; ++yp) {
|
|
byte *lineP = (byte *)destSurface.getBasePtr(0, yp * 2);
|
|
byte byteVal = *charData++;
|
|
|
|
for (int xp = 0; xp < FONT_WIDTH; ++xp, ++lineP, byteVal <<= 1) {
|
|
if (byteVal & 0x80) {
|
|
*lineP = palIndex;
|
|
*(lineP + SCREEN_WIDTH) = palIndex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets a single pixel at the specified co-ordinates
|
|
* @remarks Because the ScummVM surface is using a double height 640x200 surface to
|
|
* simulate the original 640x200 surface, all Y values have to be doubled
|
|
*/
|
|
void ScreenSurface::setPixel(const Common::Point &pt, int palIndex) {
|
|
Graphics::Surface destSurface = lockArea(Common::Rect(pt.x, pt.y * 2, pt.x + 1, (pt.y + 1) * 2));
|
|
|
|
byte *destP = (byte *)destSurface.pixels;
|
|
*destP = palIndex;
|
|
*(destP + SCREEN_WIDTH) = palIndex;
|
|
}
|
|
|
|
} // End of namespace Mortevielle
|