2009-10-03 20:49:18 +00:00
|
|
|
/* 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.
|
|
|
|
*
|
|
|
|
* $URL$
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "sci/sci.h"
|
2010-05-18 04:17:58 +00:00
|
|
|
#include "sci/util.h"
|
2009-10-03 20:49:18 +00:00
|
|
|
#include "sci/engine/state.h"
|
2010-01-05 01:37:57 +00:00
|
|
|
#include "sci/graphics/screen.h"
|
|
|
|
#include "sci/graphics/palette.h"
|
2010-07-25 16:37:36 +00:00
|
|
|
#include "sci/graphics/coordadjuster.h"
|
2010-01-05 01:37:57 +00:00
|
|
|
#include "sci/graphics/view.h"
|
2009-10-03 20:49:18 +00:00
|
|
|
|
|
|
|
namespace Sci {
|
|
|
|
|
2010-02-04 22:17:58 +00:00
|
|
|
GfxView::GfxView(ResourceManager *resMan, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId)
|
2009-10-06 16:14:40 +00:00
|
|
|
: _resMan(resMan), _screen(screen), _palette(palette), _resourceId(resourceId) {
|
2009-10-03 20:49:18 +00:00
|
|
|
assert(resourceId != -1);
|
2010-07-25 16:37:36 +00:00
|
|
|
_coordAdjuster = g_sci->_gfxCoordAdjuster;
|
2009-10-03 20:49:18 +00:00
|
|
|
initData(resourceId);
|
|
|
|
}
|
|
|
|
|
2010-02-04 22:17:58 +00:00
|
|
|
GfxView::~GfxView() {
|
2009-10-07 17:29:37 +00:00
|
|
|
// Iterate through the loops
|
|
|
|
for (uint16 loopNum = 0; loopNum < _loopCount; loopNum++) {
|
|
|
|
// and through the cells of each loop
|
|
|
|
for (uint16 celNum = 0; celNum < _loop[loopNum].celCount; celNum++) {
|
2009-10-07 17:46:17 +00:00
|
|
|
delete[] _loop[loopNum].cel[celNum].rawBitmap;
|
2009-10-07 17:29:37 +00:00
|
|
|
}
|
2009-10-07 17:46:17 +00:00
|
|
|
delete[] _loop[loopNum].cel;
|
2009-10-07 17:29:37 +00:00
|
|
|
}
|
2009-10-07 17:46:17 +00:00
|
|
|
delete[] _loop;
|
2009-10-09 12:10:17 +00:00
|
|
|
|
|
|
|
_resMan->unlockResource(_resource);
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
|
2009-10-25 20:46:14 +00:00
|
|
|
static const byte EGAmappingStraight[SCI_VIEW_EGAMAPPING_SIZE] = {
|
|
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
|
|
|
};
|
|
|
|
|
2010-02-04 22:17:58 +00:00
|
|
|
void GfxView::initData(GuiResourceId resourceId) {
|
2009-10-09 12:10:17 +00:00
|
|
|
_resource = _resMan->findResource(ResourceId(kResourceTypeView, resourceId), true);
|
|
|
|
if (!_resource) {
|
2009-10-03 20:49:18 +00:00
|
|
|
error("view resource %d not found", resourceId);
|
|
|
|
}
|
2009-10-09 12:10:17 +00:00
|
|
|
_resourceData = _resource->data;
|
2010-06-23 11:47:14 +00:00
|
|
|
_resourceSize = _resource->size;
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2009-10-05 07:10:01 +00:00
|
|
|
byte *celData, *loopData;
|
|
|
|
uint16 celOffset;
|
2010-01-05 01:37:57 +00:00
|
|
|
CelInfo *cel;
|
2009-10-05 07:10:01 +00:00
|
|
|
uint16 celCount = 0;
|
2009-10-03 20:49:18 +00:00
|
|
|
uint16 mirrorBits = 0;
|
2010-05-12 21:28:38 +00:00
|
|
|
uint32 palOffset = 0;
|
2009-10-03 20:49:18 +00:00
|
|
|
uint16 headerSize = 0;
|
2009-10-05 07:10:01 +00:00
|
|
|
uint16 loopSize = 0, celSize = 0;
|
2009-10-25 20:46:14 +00:00
|
|
|
int loopNo, celNo, EGAmapNr;
|
2009-10-03 20:49:18 +00:00
|
|
|
byte seekEntry;
|
2009-12-30 14:13:25 +00:00
|
|
|
bool isEGA = false;
|
|
|
|
bool isCompressed = true;
|
2010-06-18 17:40:13 +00:00
|
|
|
ViewType curViewType = _resMan->getViewType();
|
2009-10-03 20:49:18 +00:00
|
|
|
|
|
|
|
_loopCount = 0;
|
2009-10-24 19:29:06 +00:00
|
|
|
_embeddedPal = false;
|
|
|
|
_EGAmapping = NULL;
|
2010-06-30 14:26:47 +00:00
|
|
|
_isSci2Hires = false;
|
2010-08-02 21:04:09 +00:00
|
|
|
_isScaleable = true;
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2010-07-31 13:13:46 +00:00
|
|
|
// we adjust inside getCelRect for SCI0EARLY (that version didn't have the +1 when calculating bottom)
|
2010-08-02 21:04:09 +00:00
|
|
|
_adjustForSci0Early = getSciVersion() == SCI_VERSION_0_EARLY ? -1 : 0;
|
2010-07-31 13:13:46 +00:00
|
|
|
|
2010-06-28 11:20:14 +00:00
|
|
|
// If we find an SCI1/SCI1.1 view (not amiga), we switch to that type for
|
|
|
|
// EGA. This could get used to make view patches for EGA games, where the
|
|
|
|
// new views include more colors. Users could manually adjust old views to
|
|
|
|
// make them look better (like removing dithered colors that aren't caught
|
|
|
|
// by our undithering or even improve the graphics overall).
|
2010-06-18 17:40:13 +00:00
|
|
|
if (curViewType == kViewEga) {
|
|
|
|
if (_resourceData[1] == 0x80) {
|
2010-06-18 18:55:17 +00:00
|
|
|
curViewType = kViewVga;
|
|
|
|
} else {
|
|
|
|
if (READ_LE_UINT16(_resourceData + 4) == 1)
|
2010-06-18 17:40:13 +00:00
|
|
|
curViewType = kViewVga11;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (curViewType) {
|
2011-03-04 19:49:19 +00:00
|
|
|
case kViewEga: // SCI0 (and Amiga 16 colors)
|
2009-12-30 14:13:25 +00:00
|
|
|
isEGA = true;
|
2011-03-04 19:49:19 +00:00
|
|
|
case kViewAmiga: // Amiga (32 colors)
|
2009-10-04 21:30:13 +00:00
|
|
|
case kViewVga: // View-format SCI1
|
2009-10-03 20:49:18 +00:00
|
|
|
// LoopCount:WORD MirrorMask:WORD Version:WORD PaletteOffset:WORD LoopOffset0:WORD LoopOffset1:WORD...
|
2010-01-25 01:39:44 +00:00
|
|
|
|
2009-10-03 20:49:18 +00:00
|
|
|
_loopCount = _resourceData[0];
|
2009-12-30 14:13:25 +00:00
|
|
|
// bit 0x8000 of _resourceData[1] means palette is set
|
|
|
|
if (_resourceData[1] & 0x40)
|
|
|
|
isCompressed = false;
|
2009-10-03 20:49:18 +00:00
|
|
|
mirrorBits = READ_LE_UINT16(_resourceData + 2);
|
|
|
|
palOffset = READ_LE_UINT16(_resourceData + 6);
|
|
|
|
|
|
|
|
if (palOffset && palOffset != 0x100) {
|
2010-06-28 11:20:14 +00:00
|
|
|
// Some SCI0/SCI01 games also have an offset set. It seems that it
|
|
|
|
// points to a 16-byte mapping table but on those games using that
|
|
|
|
// mapping will actually screw things up. On the other side: VGA
|
|
|
|
// SCI1 games have this pointing to a VGA palette and EGA SCI1 games
|
|
|
|
// have this pointing to a 8x16 byte mapping table that needs to get
|
|
|
|
// applied then.
|
2009-12-30 14:13:25 +00:00
|
|
|
if (!isEGA) {
|
2010-06-23 11:47:14 +00:00
|
|
|
_palette->createFromData(&_resourceData[palOffset], _resourceSize - palOffset, &_viewPalette);
|
2009-10-04 22:15:19 +00:00
|
|
|
_embeddedPal = true;
|
2009-10-24 19:29:06 +00:00
|
|
|
} else {
|
2010-08-19 22:21:03 +00:00
|
|
|
// Only use the EGA-mapping, when being SCI1 EGA
|
|
|
|
// SCI1 VGA conversion games (which will get detected as SCI1EARLY/MIDDLE/LATE) have some views
|
|
|
|
// with broken mapping tables. I guess those games won't use the mapping, so I rather disable it
|
|
|
|
// for them
|
2011-02-27 14:48:53 +00:00
|
|
|
if (getSciVersion() == SCI_VERSION_1_EGA_ONLY) {
|
2009-10-24 19:29:06 +00:00
|
|
|
_EGAmapping = &_resourceData[palOffset];
|
2009-10-25 20:46:14 +00:00
|
|
|
for (EGAmapNr = 0; EGAmapNr < SCI_VIEW_EGAMAPPING_COUNT; EGAmapNr++) {
|
2010-06-28 11:20:14 +00:00
|
|
|
if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE) != 0)
|
2009-10-25 20:46:14 +00:00
|
|
|
break;
|
|
|
|
_EGAmapping += SCI_VIEW_EGAMAPPING_SIZE;
|
|
|
|
}
|
|
|
|
// If all mappings are "straight", then we actually ignore the mapping
|
|
|
|
if (EGAmapNr == SCI_VIEW_EGAMAPPING_COUNT)
|
|
|
|
_EGAmapping = NULL;
|
|
|
|
else
|
|
|
|
_EGAmapping = &_resourceData[palOffset];
|
|
|
|
}
|
2009-10-04 21:57:31 +00:00
|
|
|
}
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
|
2010-01-05 01:37:57 +00:00
|
|
|
_loop = new LoopInfo[_loopCount];
|
2009-10-03 20:49:18 +00:00
|
|
|
for (loopNo = 0; loopNo < _loopCount; loopNo++) {
|
|
|
|
loopData = _resourceData + READ_LE_UINT16(_resourceData + 8 + loopNo * 2);
|
2009-10-05 07:10:01 +00:00
|
|
|
// CelCount:WORD Unknown:WORD CelOffset0:WORD CelOffset1:WORD...
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2009-10-05 07:10:01 +00:00
|
|
|
celCount = READ_LE_UINT16(loopData);
|
|
|
|
_loop[loopNo].celCount = celCount;
|
2009-10-03 20:49:18 +00:00
|
|
|
_loop[loopNo].mirrorFlag = mirrorBits & 1 ? true : false;
|
|
|
|
mirrorBits >>= 1;
|
|
|
|
|
|
|
|
// read cel info
|
2010-01-05 01:37:57 +00:00
|
|
|
_loop[loopNo].cel = new CelInfo[celCount];
|
2009-10-05 07:10:01 +00:00
|
|
|
for (celNo = 0; celNo < celCount; celNo++) {
|
|
|
|
celOffset = READ_LE_UINT16(loopData + 4 + celNo * 2);
|
|
|
|
celData = _resourceData + celOffset;
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2009-10-04 21:57:31 +00:00
|
|
|
// For VGA
|
|
|
|
// Width:WORD Height:WORD DisplaceX:BYTE DisplaceY:BYTE ClearKey:BYTE Unknown:BYTE RLEData starts now directly
|
|
|
|
// For EGA
|
|
|
|
// Width:WORD Height:WORD DisplaceX:BYTE DisplaceY:BYTE ClearKey:BYTE EGAData starts now directly
|
2009-10-05 07:10:01 +00:00
|
|
|
cel = &_loop[loopNo].cel[celNo];
|
2010-07-24 15:15:38 +00:00
|
|
|
cel->scriptWidth = cel->width = READ_LE_UINT16(celData);
|
|
|
|
cel->scriptHeight = cel->height = READ_LE_UINT16(celData + 2);
|
2010-01-11 20:31:12 +00:00
|
|
|
cel->displaceX = (signed char)celData[4];
|
2009-10-05 07:10:01 +00:00
|
|
|
cel->displaceY = celData[5];
|
2009-10-05 21:38:51 +00:00
|
|
|
cel->clearKey = celData[6];
|
2009-12-30 14:13:25 +00:00
|
|
|
if (isEGA) {
|
2009-10-05 07:10:01 +00:00
|
|
|
cel->offsetEGA = celOffset + 7;
|
|
|
|
cel->offsetRLE = 0;
|
2009-12-30 14:13:25 +00:00
|
|
|
cel->offsetLiteral = 0;
|
2009-10-04 21:57:31 +00:00
|
|
|
} else {
|
2009-10-05 07:10:01 +00:00
|
|
|
cel->offsetEGA = 0;
|
2009-12-30 14:13:25 +00:00
|
|
|
if (isCompressed) {
|
|
|
|
cel->offsetRLE = celOffset + 8;
|
|
|
|
cel->offsetLiteral = 0;
|
|
|
|
} else {
|
|
|
|
cel->offsetRLE = 0;
|
|
|
|
cel->offsetLiteral = celOffset + 8;
|
|
|
|
}
|
2009-10-04 21:57:31 +00:00
|
|
|
}
|
2009-10-05 07:10:01 +00:00
|
|
|
cel->rawBitmap = 0;
|
2009-10-03 20:49:18 +00:00
|
|
|
if (_loop[loopNo].mirrorFlag)
|
2009-10-05 07:10:01 +00:00
|
|
|
cel->displaceX = -cel->displaceX;
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2010-05-12 21:28:38 +00:00
|
|
|
case kViewVga11: // View-format SCI1.1+
|
2010-08-02 21:04:09 +00:00
|
|
|
// HeaderSize:WORD LoopCount:BYTE Flags:BYTE Version:WORD Unknown:WORD PaletteOffset:WORD
|
2010-06-28 11:20:14 +00:00
|
|
|
headerSize = READ_SCI11ENDIAN_UINT16(_resourceData + 0) + 2; // headerSize is not part of the header, so it's added
|
2010-05-12 21:28:38 +00:00
|
|
|
assert(headerSize >= 16);
|
2009-10-06 07:25:07 +00:00
|
|
|
_loopCount = _resourceData[2];
|
2010-05-13 15:39:20 +00:00
|
|
|
assert(_loopCount);
|
2010-06-30 14:26:47 +00:00
|
|
|
_isSci2Hires = _resourceData[5] == 1 ? true : false;
|
2010-05-18 04:17:58 +00:00
|
|
|
palOffset = READ_SCI11ENDIAN_UINT32(_resourceData + 8);
|
2010-08-02 21:04:09 +00:00
|
|
|
// flags is actually a bit-mask
|
|
|
|
// it seems it was only used for some early sci1.1 games (or even just laura bow 2)
|
|
|
|
// later interpreters dont support it at all anymore
|
|
|
|
// we assume that if flags is 0h the view does not support flags and default to scaleable
|
|
|
|
// if it's 1h then we assume that the view is not to be scaled
|
|
|
|
// if it's 40h then we assume that the view is scaleable
|
|
|
|
switch (_resourceData[3]) {
|
|
|
|
case 1:
|
|
|
|
_isScaleable = false;
|
|
|
|
break;
|
|
|
|
case 0x40:
|
|
|
|
case 0:
|
|
|
|
break; // don't do anything, we already have _isScaleable set
|
|
|
|
default:
|
2010-11-20 00:20:05 +00:00
|
|
|
error("unsupported flags byte (%d) inside sci1.1 view", _resourceData[3]);
|
2010-08-02 21:04:09 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-10-03 20:49:18 +00:00
|
|
|
|
|
|
|
loopData = _resourceData + headerSize;
|
|
|
|
loopSize = _resourceData[12];
|
2010-05-12 21:28:38 +00:00
|
|
|
assert(loopSize >= 16);
|
2009-10-05 07:10:01 +00:00
|
|
|
celSize = _resourceData[13];
|
2010-05-13 15:39:20 +00:00
|
|
|
assert(celSize >= 32);
|
2009-10-03 20:49:18 +00:00
|
|
|
|
|
|
|
if (palOffset) {
|
2010-06-23 11:47:14 +00:00
|
|
|
_palette->createFromData(&_resourceData[palOffset], _resourceSize - palOffset, &_viewPalette);
|
2009-10-03 20:49:18 +00:00
|
|
|
_embeddedPal = true;
|
|
|
|
}
|
|
|
|
|
2010-01-05 01:37:57 +00:00
|
|
|
_loop = new LoopInfo[_loopCount];
|
2009-10-03 20:49:18 +00:00
|
|
|
for (loopNo = 0; loopNo < _loopCount; loopNo++) {
|
|
|
|
loopData = _resourceData + headerSize + (loopNo * loopSize);
|
2009-10-06 07:12:09 +00:00
|
|
|
|
2010-05-12 21:28:38 +00:00
|
|
|
seekEntry = loopData[0];
|
2009-10-03 20:49:18 +00:00
|
|
|
if (seekEntry != 255) {
|
2009-10-06 07:25:07 +00:00
|
|
|
if (seekEntry >= _loopCount)
|
|
|
|
error("Bad loop-pointer in sci 1.1 view");
|
2009-10-05 17:57:06 +00:00
|
|
|
_loop[loopNo].mirrorFlag = true;
|
2009-10-06 07:12:09 +00:00
|
|
|
loopData = _resourceData + headerSize + (seekEntry * loopSize);
|
2009-10-05 17:57:06 +00:00
|
|
|
} else {
|
|
|
|
_loop[loopNo].mirrorFlag = false;
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
|
2010-05-12 21:28:38 +00:00
|
|
|
celCount = loopData[2];
|
2009-10-05 07:10:01 +00:00
|
|
|
_loop[loopNo].celCount = celCount;
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2010-05-18 04:17:58 +00:00
|
|
|
celData = _resourceData + READ_SCI11ENDIAN_UINT32(loopData + 12);
|
2009-10-03 20:49:18 +00:00
|
|
|
|
|
|
|
// read cel info
|
2010-01-05 01:37:57 +00:00
|
|
|
_loop[loopNo].cel = new CelInfo[celCount];
|
2009-10-05 07:10:01 +00:00
|
|
|
for (celNo = 0; celNo < celCount; celNo++) {
|
|
|
|
cel = &_loop[loopNo].cel[celNo];
|
2010-07-24 15:15:38 +00:00
|
|
|
cel->scriptWidth = cel->width = READ_SCI11ENDIAN_UINT16(celData);
|
|
|
|
cel->scriptHeight = cel->height = READ_SCI11ENDIAN_UINT16(celData + 2);
|
2010-05-18 04:17:58 +00:00
|
|
|
cel->displaceX = READ_SCI11ENDIAN_UINT16(celData + 4);
|
|
|
|
cel->displaceY = READ_SCI11ENDIAN_UINT16(celData + 6);
|
2010-08-10 18:00:27 +00:00
|
|
|
if (cel->displaceY < 0)
|
2010-08-10 18:05:04 +00:00
|
|
|
cel->displaceY += 255; // sierra did this adjust in their sci1.1 getCelRect() - not sure about sci32
|
2010-05-18 04:17:58 +00:00
|
|
|
|
|
|
|
assert(cel->width && cel->height);
|
|
|
|
|
2009-10-05 07:10:01 +00:00
|
|
|
cel->clearKey = celData[8];
|
|
|
|
cel->offsetEGA = 0;
|
2010-05-18 04:17:58 +00:00
|
|
|
cel->offsetRLE = READ_SCI11ENDIAN_UINT32(celData + 24);
|
|
|
|
cel->offsetLiteral = READ_SCI11ENDIAN_UINT32(celData + 28);
|
2011-02-08 05:01:42 +00:00
|
|
|
|
2010-06-30 14:26:47 +00:00
|
|
|
// GK1-hires content is actually uncompressed, we need to swap both so that we process it as such
|
|
|
|
if ((cel->offsetRLE) && (!cel->offsetLiteral))
|
|
|
|
SWAP(cel->offsetRLE, cel->offsetLiteral);
|
2010-05-18 04:17:58 +00:00
|
|
|
|
2009-10-05 07:10:01 +00:00
|
|
|
cel->rawBitmap = 0;
|
2009-10-03 20:49:18 +00:00
|
|
|
if (_loop[loopNo].mirrorFlag)
|
2009-10-05 07:10:01 +00:00
|
|
|
cel->displaceX = -cel->displaceX;
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2009-10-05 07:10:01 +00:00
|
|
|
celData += celSize;
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
}
|
2010-07-24 15:15:38 +00:00
|
|
|
#ifdef ENABLE_SCI32
|
|
|
|
// adjust width/height returned to scripts
|
2011-02-02 22:29:14 +00:00
|
|
|
if (_isSci2Hires) {
|
|
|
|
for (loopNo = 0; loopNo < _loopCount; loopNo++)
|
|
|
|
for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++)
|
|
|
|
_screen->adjustBackUpscaledCoordinates(_loop[loopNo].cel[celNo].scriptWidth, _loop[loopNo].cel[celNo].scriptHeight);
|
|
|
|
} else if (getSciVersion() == SCI_VERSION_2_1) {
|
|
|
|
for (loopNo = 0; loopNo < _loopCount; loopNo++)
|
|
|
|
for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++)
|
2010-07-25 16:37:36 +00:00
|
|
|
_coordAdjuster->fromDisplayToScript(_loop[loopNo].cel[celNo].scriptHeight, _loop[loopNo].cel[celNo].scriptWidth);
|
2010-07-24 15:15:38 +00:00
|
|
|
}
|
|
|
|
#endif
|
2009-10-03 20:49:18 +00:00
|
|
|
break;
|
2009-10-04 21:30:13 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
error("ViewType was not detected, can't continue");
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-28 11:20:14 +00:00
|
|
|
GuiResourceId GfxView::getResourceId() const {
|
2009-10-03 20:49:18 +00:00
|
|
|
return _resourceId;
|
|
|
|
}
|
|
|
|
|
2010-06-28 11:20:14 +00:00
|
|
|
int16 GfxView::getWidth(int16 loopNo, int16 celNo) const {
|
2010-06-30 15:26:45 +00:00
|
|
|
return _loopCount ? getCelInfo(loopNo, celNo)->width : 0;
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
|
2010-06-28 11:20:14 +00:00
|
|
|
int16 GfxView::getHeight(int16 loopNo, int16 celNo) const {
|
2010-06-30 15:26:45 +00:00
|
|
|
return _loopCount ? getCelInfo(loopNo, celNo)->height : 0;
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
|
2010-06-28 11:20:14 +00:00
|
|
|
const CelInfo *GfxView::getCelInfo(int16 loopNo, int16 celNo) const {
|
2010-06-28 11:20:33 +00:00
|
|
|
assert(_loopCount);
|
2009-10-04 15:34:43 +00:00
|
|
|
loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
|
2009-12-02 21:30:43 +00:00
|
|
|
celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
|
2010-06-28 11:20:33 +00:00
|
|
|
return &_loop[loopNo].cel[celNo];
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
|
2010-06-28 11:20:33 +00:00
|
|
|
uint16 GfxView::getCelCount(int16 loopNo) const {
|
2010-06-28 16:56:15 +00:00
|
|
|
assert(_loopCount);
|
|
|
|
loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
|
2010-06-28 11:20:33 +00:00
|
|
|
return _loop[loopNo].celCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
Palette *GfxView::getPalette() {
|
|
|
|
return _embeddedPal ? &_viewPalette : NULL;
|
2009-10-04 15:34:43 +00:00
|
|
|
}
|
|
|
|
|
2010-06-30 14:26:47 +00:00
|
|
|
bool GfxView::isSci2Hires() {
|
|
|
|
return _isSci2Hires;
|
|
|
|
}
|
|
|
|
|
2010-08-02 21:04:09 +00:00
|
|
|
bool GfxView::isScaleable() {
|
|
|
|
return _isScaleable;
|
|
|
|
}
|
|
|
|
|
2010-06-28 11:20:14 +00:00
|
|
|
void GfxView::getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const {
|
|
|
|
const CelInfo *celInfo = getCelInfo(loopNo, celNo);
|
2010-06-28 11:20:33 +00:00
|
|
|
outRect.left = x + celInfo->displaceX - (celInfo->width >> 1);
|
|
|
|
outRect.right = outRect.left + celInfo->width;
|
2010-08-02 21:04:09 +00:00
|
|
|
outRect.bottom = y + celInfo->displaceY - z + 1 + _adjustForSci0Early;
|
2010-06-28 11:20:33 +00:00
|
|
|
outRect.top = outRect.bottom - celInfo->height;
|
2009-10-04 15:34:43 +00:00
|
|
|
}
|
|
|
|
|
2010-10-26 20:19:17 +00:00
|
|
|
void GfxView::getCelSpecialHoyle4Rect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const {
|
|
|
|
const CelInfo *celInfo = getCelInfo(loopNo, celNo);
|
2011-02-20 10:20:40 +00:00
|
|
|
int16 adjustY = y + celInfo->displaceY - celInfo->height + 1;
|
|
|
|
int16 adjustX = x + celInfo->displaceX - ((celInfo->width - 1) >> 1);
|
|
|
|
outRect.translate(adjustX, adjustY);
|
2010-10-26 20:19:17 +00:00
|
|
|
}
|
|
|
|
|
2010-06-28 11:20:14 +00:00
|
|
|
void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const {
|
2010-01-16 14:20:00 +00:00
|
|
|
int16 scaledDisplaceX, scaledDisplaceY;
|
|
|
|
int16 scaledWidth, scaledHeight;
|
2010-06-28 11:20:14 +00:00
|
|
|
const CelInfo *celInfo = getCelInfo(loopNo, celNo);
|
2010-06-28 11:20:33 +00:00
|
|
|
|
|
|
|
// Scaling displaceX/Y, Width/Height
|
|
|
|
scaledDisplaceX = (celInfo->displaceX * scaleX) >> 7;
|
|
|
|
scaledDisplaceY = (celInfo->displaceY * scaleY) >> 7;
|
|
|
|
scaledWidth = (celInfo->width * scaleX) >> 7;
|
|
|
|
scaledHeight = (celInfo->height * scaleY) >> 7;
|
|
|
|
scaledWidth = CLIP<int16>(scaledWidth, 0, _screen->getWidth());
|
|
|
|
scaledHeight = CLIP<int16>(scaledHeight, 0, _screen->getHeight());
|
|
|
|
|
|
|
|
outRect.left = x + scaledDisplaceX - (scaledWidth >> 1);
|
|
|
|
outRect.right = outRect.left + scaledWidth;
|
|
|
|
outRect.bottom = y + scaledDisplaceY - z + 1;
|
|
|
|
outRect.top = outRect.bottom - scaledHeight;
|
2010-01-16 14:20:00 +00:00
|
|
|
}
|
|
|
|
|
2011-03-06 17:22:32 +00:00
|
|
|
void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCount, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData) {
|
|
|
|
byte *outPtr = celBitmap;
|
|
|
|
byte curByte, runLength;
|
|
|
|
byte *rlePtr = inBuffer + rlePos;
|
|
|
|
byte *literalPtr = inBuffer + literalPos;
|
|
|
|
int pixelNr = 0;
|
|
|
|
|
|
|
|
memset(celBitmap, clearColor, pixelCount);
|
|
|
|
|
|
|
|
if (!literalPos) {
|
|
|
|
// decompression for data that has only one combined stream
|
|
|
|
switch (viewType) {
|
|
|
|
case kViewEga:
|
|
|
|
while (pixelNr < pixelCount) {
|
|
|
|
curByte = *rlePtr++;
|
|
|
|
runLength = curByte >> 4;
|
|
|
|
memset(outPtr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr));
|
|
|
|
pixelNr += runLength;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kViewAmiga:
|
|
|
|
while (pixelNr < pixelCount) {
|
|
|
|
curByte = *rlePtr++;
|
|
|
|
if (curByte & 0x07) { // fill with color
|
|
|
|
runLength = curByte & 0x07;
|
|
|
|
curByte = curByte >> 3;
|
|
|
|
while (runLength-- && pixelNr < pixelCount) {
|
|
|
|
outPtr[pixelNr++] = curByte;
|
|
|
|
}
|
|
|
|
} else { // fill with transparent
|
|
|
|
runLength = curByte >> 3;
|
|
|
|
pixelNr += runLength;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kViewVga:
|
|
|
|
case kViewVga11:
|
|
|
|
while (pixelNr < pixelCount) {
|
|
|
|
curByte = *rlePtr++;
|
|
|
|
runLength = curByte & 0x3F;
|
|
|
|
switch (curByte & 0xC0) {
|
|
|
|
case 0: // copy bytes as-is
|
|
|
|
while (runLength-- && pixelNr < pixelCount)
|
|
|
|
outPtr[pixelNr++] = *rlePtr++;
|
|
|
|
break;
|
|
|
|
case 0x40: // copy bytes as is (In copy case, runLength can go upto 127 i.e. pixel & 0x40). Fixes bug #3135872.
|
|
|
|
runLength += 64;
|
|
|
|
break;
|
|
|
|
case 0x80: // fill with color
|
|
|
|
memset(outPtr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr));
|
|
|
|
pixelNr += runLength;
|
|
|
|
break;
|
|
|
|
case 0xC0: // fill with transparent
|
|
|
|
pixelNr += runLength;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error("Unsupported picture viewtype");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (isMacSci11ViewData) {
|
|
|
|
// KQ6/Freddy Pharkas use byte lengths, all others use uint16
|
|
|
|
// The SCI devs must have realized that a max of 255 pixels wide
|
|
|
|
// was not very good for 320 or 640 width games.
|
|
|
|
bool hasByteLengths = (g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_FREDDYPHARKAS);
|
|
|
|
|
|
|
|
// compression for SCI1.1+ Mac
|
|
|
|
while (pixelNr < pixelCount) {
|
|
|
|
uint32 pixelLine = pixelNr;
|
|
|
|
|
|
|
|
if (hasByteLengths) {
|
|
|
|
pixelNr += *rlePtr++;
|
|
|
|
runLength = *rlePtr++;
|
|
|
|
} else {
|
|
|
|
pixelNr += READ_BE_UINT16(rlePtr);
|
|
|
|
runLength = READ_BE_UINT16(rlePtr + 2);
|
|
|
|
rlePtr += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (runLength-- && pixelNr < pixelCount)
|
|
|
|
outPtr[pixelNr++] = *literalPtr++;
|
|
|
|
|
|
|
|
pixelNr = pixelLine + width;
|
|
|
|
}
|
2011-03-06 17:44:11 +00:00
|
|
|
} else {
|
|
|
|
// decompression for data that has two separate streams (probably SCI 1.1 view)
|
|
|
|
while (pixelNr < pixelCount) {
|
|
|
|
curByte = *rlePtr++;
|
|
|
|
runLength = curByte & 0x3F;
|
|
|
|
switch (curByte & 0xC0) {
|
|
|
|
case 0: // copy bytes as-is
|
|
|
|
while (runLength-- && pixelNr < pixelCount)
|
|
|
|
outPtr[pixelNr++] = *literalPtr++;
|
|
|
|
break;
|
|
|
|
case 0x80: // fill with color
|
|
|
|
memset(outPtr + pixelNr, *literalPtr++, MIN<uint16>(runLength, pixelCount - pixelNr));
|
|
|
|
pixelNr += runLength;
|
|
|
|
break;
|
|
|
|
case 0xC0: // fill with transparent
|
|
|
|
pixelNr += runLength;
|
|
|
|
break;
|
|
|
|
}
|
2011-03-06 17:22:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-04 22:17:58 +00:00
|
|
|
void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount) {
|
2010-06-28 11:20:14 +00:00
|
|
|
const CelInfo *celInfo = getCelInfo(loopNo, celNo);
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2009-10-06 07:37:08 +00:00
|
|
|
if (celInfo->offsetEGA) {
|
|
|
|
// decompression for EGA views
|
2011-03-06 17:22:32 +00:00
|
|
|
unpackCelData(_resourceData, outPtr, 0, pixelCount, celInfo->offsetEGA, 0, _resMan->getViewType(), celInfo->width, false);
|
2009-10-03 20:49:18 +00:00
|
|
|
} else {
|
2010-06-28 18:10:21 +00:00
|
|
|
// We fill the buffer with transparent pixels, so that we can later skip
|
|
|
|
// over pixels to automatically have them transparent
|
2010-06-28 11:20:14 +00:00
|
|
|
// Also some RLE compressed cels are possibly ending with the last
|
|
|
|
// non-transparent pixel (is this even possible with the current code?)
|
2011-02-08 14:26:39 +00:00
|
|
|
byte clearColor = _loop[loopNo].cel[celNo].clearKey;
|
|
|
|
|
|
|
|
// Since Mac OS required palette index 0 to be white and 0xff to be black, the
|
|
|
|
// Mac SCI devs decided that rather than change scripts and various pieces of
|
|
|
|
// code, that they would just put a little snippet of code to swap these colors
|
|
|
|
// in various places around the SCI codebase. We figured that it would be less
|
|
|
|
// hacky to swap pixels instead and run the Mac games with a PC palette.
|
2011-02-10 15:12:06 +00:00
|
|
|
if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) {
|
2011-02-08 14:26:39 +00:00
|
|
|
// clearColor is based on PC palette, but the literal data is not.
|
|
|
|
// We flip clearColor here to make it match the literal data. All
|
|
|
|
// these pixels will be flipped back again below.
|
|
|
|
if (clearColor == 0)
|
|
|
|
clearColor = 0xff;
|
|
|
|
else if (clearColor == 0xff)
|
|
|
|
clearColor = 0;
|
|
|
|
}
|
|
|
|
|
2011-03-06 17:22:32 +00:00
|
|
|
bool isMacSci11ViewData = g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() == SCI_VERSION_1_1;
|
|
|
|
unpackCelData(_resourceData, outPtr, clearColor, pixelCount, celInfo->offsetRLE, celInfo->offsetLiteral, _resMan->getViewType(), celInfo->width, isMacSci11ViewData);
|
2011-02-08 05:01:42 +00:00
|
|
|
|
2011-02-08 14:26:39 +00:00
|
|
|
// Swap 0 and 0xff pixels for Mac SCI1.1+ games (see above)
|
2011-02-08 05:01:42 +00:00
|
|
|
if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) {
|
|
|
|
for (uint32 i = 0; i < pixelCount; i++) {
|
2011-02-08 14:26:39 +00:00
|
|
|
if (outPtr[i] == 0)
|
|
|
|
outPtr[i] = 0xff;
|
|
|
|
else if (outPtr[i] == 0xff)
|
|
|
|
outPtr[i] = 0;
|
2011-02-08 05:01:42 +00:00
|
|
|
}
|
|
|
|
}
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-28 11:20:14 +00:00
|
|
|
const byte *GfxView::getBitmap(int16 loopNo, int16 celNo) {
|
2009-10-03 20:49:18 +00:00
|
|
|
loopNo = CLIP<int16>(loopNo, 0, _loopCount -1);
|
2009-12-02 21:30:43 +00:00
|
|
|
celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
|
2009-10-05 07:10:01 +00:00
|
|
|
if (_loop[loopNo].cel[celNo].rawBitmap)
|
|
|
|
return _loop[loopNo].cel[celNo].rawBitmap;
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2009-10-05 07:10:01 +00:00
|
|
|
uint16 width = _loop[loopNo].cel[celNo].width;
|
|
|
|
uint16 height = _loop[loopNo].cel[celNo].height;
|
2009-10-03 20:49:18 +00:00
|
|
|
// allocating memory to store cel's bitmap
|
2010-01-05 19:55:05 +00:00
|
|
|
int pixelCount = width * height;
|
2009-10-05 07:10:01 +00:00
|
|
|
_loop[loopNo].cel[celNo].rawBitmap = new byte[pixelCount];
|
2009-10-10 17:40:29 +00:00
|
|
|
byte *pBitmap = _loop[loopNo].cel[celNo].rawBitmap;
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2010-06-19 17:12:17 +00:00
|
|
|
// unpack the actual cel bitmap data
|
2009-10-10 17:40:29 +00:00
|
|
|
unpackCel(loopNo, celNo, pBitmap, pixelCount);
|
|
|
|
|
2011-03-04 19:49:19 +00:00
|
|
|
if (_resMan->getViewType() == kViewEga)
|
2009-10-10 17:40:29 +00:00
|
|
|
unditherBitmap(pBitmap, width, height, _loop[loopNo].cel[celNo].clearKey);
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2009-10-05 16:51:01 +00:00
|
|
|
// mirroring the cel if needed
|
2009-10-03 20:49:18 +00:00
|
|
|
if (_loop[loopNo].mirrorFlag) {
|
2009-10-10 17:40:29 +00:00
|
|
|
for (int i = 0; i < height; i++, pBitmap += width)
|
2009-10-03 20:49:18 +00:00
|
|
|
for (int j = 0; j < width / 2; j++)
|
2009-10-10 17:40:29 +00:00
|
|
|
SWAP(pBitmap[j], pBitmap[width - j - 1]);
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
2009-10-05 07:10:01 +00:00
|
|
|
return _loop[loopNo].cel[celNo].rawBitmap;
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
|
2010-06-28 11:20:14 +00:00
|
|
|
/**
|
|
|
|
* Called after unpacking an EGA cel, this will try to undither (parts) of the
|
|
|
|
* cel if the dithering in here matches dithering used by the current picture.
|
|
|
|
*/
|
2010-02-04 22:17:58 +00:00
|
|
|
void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte clearKey) {
|
2009-10-10 17:40:29 +00:00
|
|
|
int16 *unditherMemorial = _screen->unditherGetMemorial();
|
|
|
|
|
2010-06-28 11:21:23 +00:00
|
|
|
// It makes no sense to go further, if no memorial data from current picture
|
|
|
|
// is available
|
2009-10-10 17:40:29 +00:00
|
|
|
if (!unditherMemorial)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Makes no sense to process bitmaps that are 3 pixels wide or less
|
2010-06-28 11:20:14 +00:00
|
|
|
if (width <= 3)
|
|
|
|
return;
|
2009-10-10 17:40:29 +00:00
|
|
|
|
2010-10-15 21:12:42 +00:00
|
|
|
// We need at least 2 pixel lines
|
|
|
|
if (height < 2)
|
|
|
|
return;
|
|
|
|
|
2009-10-25 20:46:14 +00:00
|
|
|
// If EGA mapping is used for this view, dont do undithering as well
|
2009-10-25 19:49:09 +00:00
|
|
|
if (_EGAmapping)
|
|
|
|
return;
|
|
|
|
|
2009-10-10 17:40:29 +00:00
|
|
|
// Walk through the bitmap and remember all combinations of colors
|
|
|
|
int16 bitmapMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
|
|
|
|
byte *curPtr;
|
|
|
|
byte color1, color2;
|
2010-10-15 21:12:42 +00:00
|
|
|
byte nextColor1, nextColor2;
|
2009-10-10 17:40:29 +00:00
|
|
|
int16 y, x;
|
|
|
|
|
|
|
|
memset(&bitmapMemorial, 0, sizeof(bitmapMemorial));
|
|
|
|
|
2010-06-28 11:21:23 +00:00
|
|
|
// Count all seemingly dithered pixel-combinations as soon as at least 4
|
2010-10-15 21:16:36 +00:00
|
|
|
// pixels are adjacent and check pixels in the following line as well to
|
|
|
|
// be the reverse pixel combination
|
2010-10-15 21:12:42 +00:00
|
|
|
int16 checkHeight = height - 1;
|
2009-10-10 17:40:29 +00:00
|
|
|
curPtr = bitmapPtr;
|
2010-10-15 21:12:42 +00:00
|
|
|
byte *nextPtr = curPtr + width;
|
|
|
|
for (y = 0; y < checkHeight; y++) {
|
2009-10-25 19:49:09 +00:00
|
|
|
color1 = curPtr[0]; color2 = (curPtr[1] << 4) | curPtr[2];
|
2010-10-15 21:12:42 +00:00
|
|
|
nextColor1 = nextPtr[0] << 4; nextColor2 = (nextPtr[2] << 4) | nextPtr[1];
|
2009-10-10 17:40:29 +00:00
|
|
|
curPtr += 3;
|
2010-10-15 21:12:42 +00:00
|
|
|
nextPtr += 3;
|
2009-10-10 17:40:29 +00:00
|
|
|
for (x = 3; x < width; x++) {
|
|
|
|
color1 = (color1 << 4) | (color2 >> 4);
|
|
|
|
color2 = (color2 << 4) | *curPtr++;
|
2010-10-15 21:12:42 +00:00
|
|
|
nextColor1 = (nextColor1 >> 4) | (nextColor2 << 4);
|
|
|
|
nextColor2 = (nextColor2 >> 4) | *nextPtr++ << 4;
|
|
|
|
if ((color1 == color2) && (color1 == nextColor1) && (color1 == nextColor2))
|
2009-10-10 17:40:29 +00:00
|
|
|
bitmapMemorial[color1]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-28 11:21:23 +00:00
|
|
|
// Now compare both memorial tables to find out matching
|
|
|
|
// dithering-combinations
|
2009-10-10 17:40:29 +00:00
|
|
|
bool unditherTable[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
|
|
|
|
byte color, unditherCount = 0;
|
|
|
|
memset(&unditherTable, false, sizeof(unditherTable));
|
|
|
|
for (color = 0; color < 255; color++) {
|
|
|
|
if ((bitmapMemorial[color] > 5) && (unditherMemorial[color] > 200)) {
|
2010-06-28 11:21:23 +00:00
|
|
|
// match found, check if colorKey is contained -> if so, we ignore
|
|
|
|
// of course
|
2009-10-10 17:40:29 +00:00
|
|
|
color1 = color & 0x0F; color2 = color >> 4;
|
|
|
|
if ((color1 != clearKey) && (color2 != clearKey) && (color1 != color2)) {
|
|
|
|
// so set this and the reversed color-combination for undithering
|
2010-06-28 11:20:14 +00:00
|
|
|
unditherTable[color] = true;
|
|
|
|
unditherTable[(color1 << 4) | color2] = true;
|
2009-10-10 17:40:29 +00:00
|
|
|
unditherCount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Nothing found to undither -> exit straight away
|
|
|
|
if (!unditherCount)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// We now need to replace color-combinations
|
|
|
|
curPtr = bitmapPtr;
|
|
|
|
for (y = 0; y < height; y++) {
|
|
|
|
color = *curPtr;
|
|
|
|
for (x = 1; x < width; x++) {
|
|
|
|
color = (color << 4) | curPtr[1];
|
|
|
|
if (unditherTable[color]) {
|
2010-06-28 11:20:14 +00:00
|
|
|
// Some color with black? Turn colors around, otherwise it won't
|
|
|
|
// be the right color at all.
|
2010-10-15 22:19:27 +00:00
|
|
|
byte unditheredColor = color;
|
2010-06-28 11:20:14 +00:00
|
|
|
if ((color & 0xF0) == 0)
|
2010-10-15 22:19:27 +00:00
|
|
|
unditheredColor = (color << 4) | (color >> 4);
|
|
|
|
curPtr[0] = unditheredColor; curPtr[1] = unditheredColor;
|
2009-10-10 17:40:29 +00:00
|
|
|
}
|
|
|
|
curPtr++;
|
|
|
|
}
|
|
|
|
curPtr++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-28 11:21:23 +00:00
|
|
|
void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated,
|
|
|
|
int16 loopNo, int16 celNo, byte priority, uint16 EGAmappingNr, bool upscaledHires) {
|
|
|
|
const Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
|
2010-06-28 11:20:14 +00:00
|
|
|
const CelInfo *celInfo = getCelInfo(loopNo, celNo);
|
|
|
|
const byte *bitmap = getBitmap(loopNo, celNo);
|
2010-06-28 11:21:23 +00:00
|
|
|
const int16 celHeight = celInfo->height;
|
|
|
|
const int16 celWidth = celInfo->width;
|
|
|
|
const byte clearKey = celInfo->clearKey;
|
|
|
|
const byte drawMask = (priority == 255) ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
|
2009-10-03 20:49:18 +00:00
|
|
|
int x, y;
|
|
|
|
|
2011-02-20 10:20:40 +00:00
|
|
|
if (_embeddedPal)
|
2009-10-06 16:14:40 +00:00
|
|
|
// Merge view palette in...
|
2010-01-26 22:45:52 +00:00
|
|
|
_palette->set(&_viewPalette, false);
|
2009-10-06 16:14:40 +00:00
|
|
|
|
2010-06-28 11:21:23 +00:00
|
|
|
const int16 width = MIN(clipRect.width(), celWidth);
|
|
|
|
const int16 height = MIN(clipRect.height(), celHeight);
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2009-10-05 07:10:01 +00:00
|
|
|
bitmap += (clipRect.top - rect.top) * celWidth + (clipRect.left - rect.left);
|
2009-10-03 20:49:18 +00:00
|
|
|
|
2009-10-24 19:29:06 +00:00
|
|
|
if (!_EGAmapping) {
|
2010-01-07 13:56:56 +00:00
|
|
|
for (y = 0; y < height; y++, bitmap += celWidth) {
|
2009-10-24 19:29:06 +00:00
|
|
|
for (x = 0; x < width; x++) {
|
2010-06-28 11:21:23 +00:00
|
|
|
const byte color = bitmap[x];
|
2010-05-15 14:17:50 +00:00
|
|
|
if (color != clearKey) {
|
2010-06-28 11:21:23 +00:00
|
|
|
const int x2 = clipRectTranslated.left + x;
|
|
|
|
const int y2 = clipRectTranslated.top + y;
|
2010-05-15 14:17:50 +00:00
|
|
|
if (!upscaledHires) {
|
2010-06-28 11:21:23 +00:00
|
|
|
if (priority >= _screen->getPriority(x2, y2))
|
|
|
|
_screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
|
2010-05-15 14:17:50 +00:00
|
|
|
} else {
|
2010-06-28 11:21:23 +00:00
|
|
|
// UpscaledHires means view is hires and is supposed to
|
|
|
|
// get drawn onto lowres screen.
|
|
|
|
// FIXME(?): we can't read priority directly with the
|
2011-02-20 10:20:40 +00:00
|
|
|
// hires coordinates. May not be needed at all in kq6
|
2010-06-28 11:21:23 +00:00
|
|
|
_screen->putPixelOnDisplay(x2, y2, palette->mapping[color]);
|
2010-05-15 14:17:50 +00:00
|
|
|
}
|
2009-10-31 18:12:19 +00:00
|
|
|
}
|
2009-10-24 19:29:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2009-10-25 20:46:14 +00:00
|
|
|
byte *EGAmapping = _EGAmapping + (EGAmappingNr * SCI_VIEW_EGAMAPPING_SIZE);
|
2010-01-07 13:56:56 +00:00
|
|
|
for (y = 0; y < height; y++, bitmap += celWidth) {
|
2009-10-24 19:29:06 +00:00
|
|
|
for (x = 0; x < width; x++) {
|
2010-06-28 11:21:23 +00:00
|
|
|
const byte color = EGAmapping[bitmap[x]];
|
|
|
|
const int x2 = clipRectTranslated.left + x;
|
|
|
|
const int y2 = clipRectTranslated.top + y;
|
|
|
|
if (color != clearKey && priority >= _screen->getPriority(x2, y2))
|
|
|
|
_screen->putPixel(x2, y2, drawMask, color, priority, 0);
|
2009-10-24 19:29:06 +00:00
|
|
|
}
|
2009-10-03 20:49:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-28 11:20:14 +00:00
|
|
|
/**
|
|
|
|
* We don't fully follow sierra sci here, I did the scaling algo myself and it
|
|
|
|
* is definitely not pixel-perfect with the one sierra is using. It shouldn't
|
|
|
|
* matter because the scaled cel rect is definitely the same as in sierra sci.
|
|
|
|
*/
|
2010-06-28 11:21:23 +00:00
|
|
|
void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, const Common::Rect &clipRectTranslated,
|
|
|
|
int16 loopNo, int16 celNo, byte priority, int16 scaleX, int16 scaleY) {
|
|
|
|
const Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
|
2010-06-28 11:20:14 +00:00
|
|
|
const CelInfo *celInfo = getCelInfo(loopNo, celNo);
|
|
|
|
const byte *bitmap = getBitmap(loopNo, celNo);
|
2010-06-28 11:21:23 +00:00
|
|
|
const int16 celHeight = celInfo->height;
|
|
|
|
const int16 celWidth = celInfo->width;
|
|
|
|
const byte clearKey = celInfo->clearKey;
|
|
|
|
const byte drawMask = (priority == 255) ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY;
|
2010-06-27 13:15:01 +00:00
|
|
|
uint16 scalingX[640];
|
|
|
|
uint16 scalingY[480];
|
2010-01-16 19:21:06 +00:00
|
|
|
int16 scaledWidth, scaledHeight;
|
2010-07-30 22:44:23 +00:00
|
|
|
int pixelNo, scaledPixel, scaledPixelNo, prevScaledPixelNo;
|
2010-01-16 16:17:45 +00:00
|
|
|
|
2011-02-20 10:20:40 +00:00
|
|
|
if (_embeddedPal)
|
2010-01-16 16:17:45 +00:00
|
|
|
// Merge view palette in...
|
2010-01-26 22:45:52 +00:00
|
|
|
_palette->set(&_viewPalette, false);
|
2010-01-16 16:17:45 +00:00
|
|
|
|
2010-01-16 17:30:17 +00:00
|
|
|
scaledWidth = (celInfo->width * scaleX) >> 7;
|
|
|
|
scaledHeight = (celInfo->height * scaleY) >> 7;
|
|
|
|
scaledWidth = CLIP<int16>(scaledWidth, 0, _screen->getWidth());
|
|
|
|
scaledHeight = CLIP<int16>(scaledHeight, 0, _screen->getHeight());
|
|
|
|
|
2010-01-16 19:21:06 +00:00
|
|
|
// Do we really need to do this?!
|
2010-07-30 22:44:23 +00:00
|
|
|
//memset(scalingX, 0, sizeof(scalingX));
|
|
|
|
//memset(scalingY, 0, sizeof(scalingY));
|
2010-01-16 16:17:45 +00:00
|
|
|
|
2010-01-16 17:30:17 +00:00
|
|
|
// Create height scaling table
|
|
|
|
pixelNo = 0;
|
2010-01-16 19:21:06 +00:00
|
|
|
scaledPixel = scaledPixelNo = prevScaledPixelNo = 0;
|
2010-01-16 17:30:17 +00:00
|
|
|
while (pixelNo < celHeight) {
|
2010-01-16 19:21:06 +00:00
|
|
|
scaledPixelNo = scaledPixel >> 7;
|
2010-06-28 11:21:23 +00:00
|
|
|
assert(scaledPixelNo < ARRAYSIZE(scalingY));
|
2010-07-30 22:44:23 +00:00
|
|
|
for (; prevScaledPixelNo <= scaledPixelNo; prevScaledPixelNo++)
|
|
|
|
scalingY[prevScaledPixelNo] = pixelNo;
|
2010-01-16 17:30:17 +00:00
|
|
|
pixelNo++;
|
|
|
|
scaledPixel += scaleY;
|
|
|
|
}
|
2010-07-30 22:44:23 +00:00
|
|
|
pixelNo--;
|
2010-01-16 19:21:06 +00:00
|
|
|
scaledPixelNo++;
|
2010-07-31 07:37:06 +00:00
|
|
|
for (; scaledPixelNo < scaledHeight; scaledPixelNo++)
|
2010-08-01 16:43:49 +00:00
|
|
|
scalingY[scaledPixelNo] = pixelNo;
|
2010-01-16 16:17:45 +00:00
|
|
|
|
2010-01-16 17:30:17 +00:00
|
|
|
// Create width scaling table
|
|
|
|
pixelNo = 0;
|
2010-01-16 19:21:06 +00:00
|
|
|
scaledPixel = scaledPixelNo = prevScaledPixelNo = 0;
|
2010-01-16 17:30:17 +00:00
|
|
|
while (pixelNo < celWidth) {
|
2010-01-16 19:21:06 +00:00
|
|
|
scaledPixelNo = scaledPixel >> 7;
|
2010-06-28 11:21:23 +00:00
|
|
|
assert(scaledPixelNo < ARRAYSIZE(scalingX));
|
2010-07-30 22:44:23 +00:00
|
|
|
for (; prevScaledPixelNo <= scaledPixelNo; prevScaledPixelNo++)
|
|
|
|
scalingX[prevScaledPixelNo] = pixelNo;
|
2010-01-16 17:30:17 +00:00
|
|
|
pixelNo++;
|
|
|
|
scaledPixel += scaleX;
|
|
|
|
}
|
2010-07-30 22:44:23 +00:00
|
|
|
pixelNo--;
|
2010-01-16 19:21:06 +00:00
|
|
|
scaledPixelNo++;
|
2010-07-30 22:44:23 +00:00
|
|
|
for (; scaledPixelNo < scaledWidth; scaledPixelNo++)
|
2010-08-01 16:43:49 +00:00
|
|
|
scalingX[scaledPixelNo] = pixelNo;
|
2010-01-16 19:21:06 +00:00
|
|
|
|
|
|
|
scaledWidth = MIN(clipRect.width(), scaledWidth);
|
|
|
|
scaledHeight = MIN(clipRect.height(), scaledHeight);
|
2010-01-16 17:30:17 +00:00
|
|
|
|
2010-06-29 20:13:57 +00:00
|
|
|
const int16 offsetY = clipRect.top - rect.top;
|
|
|
|
const int16 offsetX = clipRect.left - rect.left;
|
|
|
|
|
|
|
|
// Happens in SQ6, first room
|
|
|
|
if (offsetX < 0 || offsetY < 0)
|
|
|
|
return;
|
2010-06-28 11:21:23 +00:00
|
|
|
|
|
|
|
assert(scaledHeight + offsetY <= ARRAYSIZE(scalingY));
|
|
|
|
assert(scaledWidth + offsetX <= ARRAYSIZE(scalingX));
|
|
|
|
for (int y = 0; y < scaledHeight; y++) {
|
|
|
|
for (int x = 0; x < scaledWidth; x++) {
|
|
|
|
const byte color = bitmap[scalingY[y + offsetY] * celWidth + scalingX[x + offsetX]];
|
|
|
|
const int x2 = clipRectTranslated.left + x;
|
|
|
|
const int y2 = clipRectTranslated.top + y;
|
|
|
|
if (color != clearKey && priority >= _screen->getPriority(x2, y2)) {
|
|
|
|
_screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
|
2010-01-16 16:17:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-04 21:26:33 +00:00
|
|
|
} // End of namespace Sci
|