aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics/view.cpp
diff options
context:
space:
mode:
authorFilippos Karapetis2010-01-05 01:37:57 +0000
committerFilippos Karapetis2010-01-05 01:37:57 +0000
commitd99aa0f12634bed7b5101b4b5a64f50ee744961b (patch)
tree037401495ebf794eaa3b7386b49ee03dbe1cdac1 /engines/sci/graphics/view.cpp
parent2f1a909696838533758acdb026b1fd9c3631d6f0 (diff)
downloadscummvm-rg350-d99aa0f12634bed7b5101b4b5a64f50ee744961b.tar.gz
scummvm-rg350-d99aa0f12634bed7b5101b4b5a64f50ee744961b.tar.bz2
scummvm-rg350-d99aa0f12634bed7b5101b4b5a64f50ee744961b.zip
More renaming
svn-id: r47009
Diffstat (limited to 'engines/sci/graphics/view.cpp')
-rw-r--r--engines/sci/graphics/view.cpp513
1 files changed, 513 insertions, 0 deletions
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
new file mode 100644
index 0000000000..068ff24b02
--- /dev/null
+++ b/engines/sci/graphics/view.cpp
@@ -0,0 +1,513 @@
+/* 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"
+#include "sci/engine/state.h"
+#include "sci/graphics/gfx.h"
+#include "sci/graphics/screen.h"
+#include "sci/graphics/palette.h"
+#include "sci/graphics/view.h"
+
+namespace Sci {
+
+View::View(ResourceManager *resMan, Screen *screen, SciPalette *palette, GuiResourceId resourceId)
+ : _resMan(resMan), _screen(screen), _palette(palette), _resourceId(resourceId) {
+ assert(resourceId != -1);
+ initData(resourceId);
+}
+
+View::~View() {
+ // 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++) {
+ delete[] _loop[loopNum].cel[celNum].rawBitmap;
+ }
+ delete[] _loop[loopNum].cel;
+ }
+ delete[] _loop;
+
+ _resMan->unlockResource(_resource);
+}
+
+static const byte EGAmappingStraight[SCI_VIEW_EGAMAPPING_SIZE] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+};
+
+void View::initData(GuiResourceId resourceId) {
+ _resource = _resMan->findResource(ResourceId(kResourceTypeView, resourceId), true);
+ if (!_resource) {
+ error("view resource %d not found", resourceId);
+ }
+ _resourceData = _resource->data;
+
+ byte *celData, *loopData;
+ uint16 celOffset;
+ CelInfo *cel;
+ uint16 celCount = 0;
+ uint16 mirrorBits = 0;
+ uint16 palOffset = 0;
+ uint16 headerSize = 0;
+ uint16 loopSize = 0, celSize = 0;
+ int loopNo, celNo, EGAmapNr;
+ byte seekEntry;
+ bool isEGA = false;
+ bool isCompressed = true;
+
+ _loopCount = 0;
+ _embeddedPal = false;
+ _EGAmapping = NULL;
+
+ switch (_resMan->getViewType()) {
+ case kViewEga: // View-format SCI0 (and Amiga 16 colors)
+ isEGA = true;
+ case kViewAmiga: // View-format Amiga (32 colors)
+ case kViewVga: // View-format SCI1
+ // LoopCount:WORD MirrorMask:WORD Version:WORD PaletteOffset:WORD LoopOffset0:WORD LoopOffset1:WORD...
+
+ _loopCount = _resourceData[0];
+ // bit 0x8000 of _resourceData[1] means palette is set
+ if (_resourceData[1] & 0x40)
+ isCompressed = false;
+ mirrorBits = READ_LE_UINT16(_resourceData + 2);
+ palOffset = READ_LE_UINT16(_resourceData + 6);
+
+ if (palOffset && palOffset != 0x100) {
+ // 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
+ if (!isEGA) {
+ _palette->createFromData(&_resourceData[palOffset], &_viewPalette);
+ _embeddedPal = true;
+ } else {
+ // Only use the EGA-mapping, when being SCI1
+ if (getSciVersion() >= SCI_VERSION_1_EGA) {
+ _EGAmapping = &_resourceData[palOffset];
+ for (EGAmapNr = 0; EGAmapNr < SCI_VIEW_EGAMAPPING_COUNT; EGAmapNr++) {
+ if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE)!=0)
+ 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];
+ }
+ }
+ }
+
+ _loop = new LoopInfo[_loopCount];
+ for (loopNo = 0; loopNo < _loopCount; loopNo++) {
+ loopData = _resourceData + READ_LE_UINT16(_resourceData + 8 + loopNo * 2);
+ // CelCount:WORD Unknown:WORD CelOffset0:WORD CelOffset1:WORD...
+
+ celCount = READ_LE_UINT16(loopData);
+ _loop[loopNo].celCount = celCount;
+ _loop[loopNo].mirrorFlag = mirrorBits & 1 ? true : false;
+ mirrorBits >>= 1;
+
+ // read cel info
+ _loop[loopNo].cel = new CelInfo[celCount];
+ for (celNo = 0; celNo < celCount; celNo++) {
+ celOffset = READ_LE_UINT16(loopData + 4 + celNo * 2);
+ celData = _resourceData + celOffset;
+
+ // 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
+ cel = &_loop[loopNo].cel[celNo];
+ cel->width = READ_LE_UINT16(celData);
+ cel->height = READ_LE_UINT16(celData + 2);
+ cel->displaceX = celData[4];
+ cel->displaceY = celData[5];
+ cel->clearKey = celData[6];
+ if (isEGA) {
+ cel->offsetEGA = celOffset + 7;
+ cel->offsetRLE = 0;
+ cel->offsetLiteral = 0;
+ } else {
+ cel->offsetEGA = 0;
+ if (isCompressed) {
+ cel->offsetRLE = celOffset + 8;
+ cel->offsetLiteral = 0;
+ } else {
+ cel->offsetRLE = 0;
+ cel->offsetLiteral = celOffset + 8;
+ }
+ }
+ cel->rawBitmap = 0;
+ if (_loop[loopNo].mirrorFlag)
+ cel->displaceX = -cel->displaceX;
+ }
+ }
+ break;
+
+ case kViewVga11: // View-format SCI1.1
+ // LoopCount:WORD MirrorMask:WORD Version:WORD PaletteOffset:WORD LoopOffset0:WORD LoopOffset1:WORD...
+ // HeaderSize:WORD LoopCount:BYTE Unknown:BYTE Version:WORD Unknown:WORD PaletteOffset:WORD
+ headerSize = READ_LE_UINT16(_resourceData + 0);
+ _loopCount = _resourceData[2];
+ palOffset = READ_LE_UINT16(_resourceData + 8);
+ // FIXME: After LoopCount there is another byte and its set for view 50 within Laura Bow 2 CD, check what it means
+
+ loopData = _resourceData + headerSize;
+ loopSize = _resourceData[12];
+ celSize = _resourceData[13];
+
+ if (palOffset) {
+ _palette->createFromData(&_resourceData[palOffset], &_viewPalette);
+ _embeddedPal = true;
+ }
+
+ _loop = new LoopInfo[_loopCount];
+ for (loopNo = 0; loopNo < _loopCount; loopNo++) {
+ loopData = _resourceData + headerSize + (loopNo * loopSize);
+
+ seekEntry = loopData[2];
+ if (seekEntry != 255) {
+ if (seekEntry >= _loopCount)
+ error("Bad loop-pointer in sci 1.1 view");
+ _loop[loopNo].mirrorFlag = true;
+ loopData = _resourceData + headerSize + (seekEntry * loopSize);
+ } else {
+ _loop[loopNo].mirrorFlag = false;
+ }
+
+ celCount = loopData[4];
+ _loop[loopNo].celCount = celCount;
+
+ celData = _resourceData + READ_LE_UINT16(loopData + 14);
+
+ // read cel info
+ _loop[loopNo].cel = new CelInfo[celCount];
+ for (celNo = 0; celNo < celCount; celNo++) {
+ cel = &_loop[loopNo].cel[celNo];
+ cel->width = READ_LE_UINT16(celData);
+ cel->height = READ_LE_UINT16(celData + 2);
+ cel->displaceX = READ_LE_UINT16(celData + 4);
+ cel->displaceY = READ_LE_UINT16(celData + 6);
+ cel->clearKey = celData[8];
+ cel->offsetEGA = 0;
+ cel->offsetRLE = READ_LE_UINT16(celData + 24);
+ cel->offsetLiteral = READ_LE_UINT16(celData + 28);
+ cel->rawBitmap = 0;
+ if (_loop[loopNo].mirrorFlag)
+ cel->displaceX = -cel->displaceX;
+
+ celData += celSize;
+ }
+ }
+ break;
+
+ default:
+ error("ViewType was not detected, can't continue");
+ }
+}
+
+GuiResourceId View::getResourceId() {
+ return _resourceId;
+}
+
+int16 View::getWidth(LoopNo loopNo, CelNo celNo) {
+ loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
+ celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
+ return _loopCount ? _loop[loopNo].cel[celNo].width : 0;
+}
+
+int16 View::getHeight(LoopNo loopNo, CelNo celNo) {
+ loopNo = CLIP<int16>(loopNo, 0, _loopCount -1);
+ celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
+ return _loopCount ? _loop[loopNo].cel[celNo].height : 0;
+}
+
+CelInfo *View::getCelInfo(LoopNo loopNo, CelNo celNo) {
+ loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
+ celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
+ return _loopCount ? &_loop[loopNo].cel[celNo] : NULL;
+}
+
+LoopInfo *View::getLoopInfo(LoopNo loopNo) {
+ loopNo = CLIP<int16>(loopNo, 0, _loopCount - 1);
+ return _loopCount ? &_loop[loopNo] : NULL;
+}
+
+void View::getCelRect(LoopNo loopNo, CelNo celNo, int16 x, int16 y, int16 z, Common::Rect *outRect) {
+ CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ if (celInfo) {
+ outRect->left = x + celInfo->displaceX - (celInfo->width >> 1);
+ outRect->right = outRect->left + celInfo->width;
+ outRect->bottom = y + celInfo->displaceY - z + 1;
+ outRect->top = outRect->bottom - celInfo->height;
+ }
+}
+
+void View::unpackCel(LoopNo loopNo, CelNo celNo, byte *outPtr, uint16 pixelCount) {
+ CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ byte *rlePtr;
+ byte *literalPtr;
+ uint16 pixelNo = 0, runLength;
+ byte pixel;
+
+ if (celInfo->offsetEGA) {
+ // decompression for EGA views
+ literalPtr = _resourceData + _loop[loopNo].cel[celNo].offsetEGA;
+ while (pixelNo < pixelCount) {
+ pixel = *literalPtr++;
+ runLength = pixel >> 4;
+ memset(outPtr + pixelNo, pixel & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNo));
+ pixelNo += runLength;
+ }
+ return;
+ }
+
+ rlePtr = _resourceData + celInfo->offsetRLE;
+ if (!celInfo->offsetLiteral) { // no additional literal data
+ if (_resMan->getViewType() == kViewAmiga) {
+ // decompression for amiga views
+ while (pixelNo < pixelCount) {
+ pixel = *rlePtr++;
+ if (pixel & 0x07) { // fill with color
+ runLength = pixel & 0x07;
+ pixel = pixel >> 3;
+ while (runLength-- && pixelNo < pixelCount) {
+ outPtr[pixelNo++] = pixel;
+ }
+ } else { // fill with transparent
+ runLength = pixel >> 3;
+ pixelNo += runLength;
+ }
+ }
+ return;
+ } else {
+ // decompression for data that has just one combined stream
+ while (pixelNo < pixelCount) {
+ pixel = *rlePtr++;
+ runLength = pixel & 0x3F;
+ switch (pixel & 0xC0) {
+ case 0: // copy bytes as-is
+ while (runLength-- && pixelNo < pixelCount)
+ outPtr[pixelNo++] = *rlePtr++;
+ break;
+ case 0x80: // fill with color
+ memset(outPtr + pixelNo, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNo));
+ pixelNo += runLength;
+ break;
+ case 0xC0: // fill with transparent
+ pixelNo += runLength;
+ break;
+ }
+ }
+ return;
+ }
+ } else {
+ literalPtr = _resourceData + celInfo->offsetLiteral;
+ if (celInfo->offsetRLE) {
+ // decompression for data that has separate rle and literal streams
+ while (pixelNo < pixelCount) {
+ pixel = *rlePtr++;
+ runLength = pixel & 0x3F;
+ switch (pixel & 0xC0) {
+ case 0: // copy bytes as-is
+ while (runLength-- && pixelNo < pixelCount)
+ outPtr[pixelNo++] = *literalPtr++;
+ break;
+ case 0x80: // fill with color
+ memset(outPtr + pixelNo, *literalPtr++, MIN<uint16>(runLength, pixelCount - pixelNo));
+ pixelNo += runLength;
+ break;
+ case 0xC0: // fill with transparent
+ pixelNo += runLength;
+ break;
+ }
+ }
+ } else {
+ // literal stream only, so no compression
+ memcpy(outPtr, literalPtr, pixelCount);
+ }
+ return;
+ }
+ error("Unable to decompress view");
+}
+
+byte *View::getBitmap(LoopNo loopNo, CelNo celNo) {
+ loopNo = CLIP<int16>(loopNo, 0, _loopCount -1);
+ celNo = CLIP<int16>(celNo, 0, _loop[loopNo].celCount - 1);
+ if (_loop[loopNo].cel[celNo].rawBitmap)
+ return _loop[loopNo].cel[celNo].rawBitmap;
+
+ uint16 width = _loop[loopNo].cel[celNo].width;
+ uint16 height = _loop[loopNo].cel[celNo].height;
+ // allocating memory to store cel's bitmap
+ assert(width * height <= 64000);
+ uint16 pixelCount = width * height;
+ _loop[loopNo].cel[celNo].rawBitmap = new byte[pixelCount];
+ byte *pBitmap = _loop[loopNo].cel[celNo].rawBitmap;
+
+ // Some RLE compressed cels end with the last non-transparent pixel, thats why we fill it up here
+ // FIXME: change this to fill the remaining bytes within unpackCel()
+ memset(pBitmap, _loop[loopNo].cel[celNo].clearKey, pixelCount);
+ unpackCel(loopNo, celNo, pBitmap, pixelCount);
+
+ if (!_resMan->isVGA()) {
+ unditherBitmap(pBitmap, width, height, _loop[loopNo].cel[celNo].clearKey);
+ }
+
+ // mirroring the cel if needed
+ if (_loop[loopNo].mirrorFlag) {
+ for (int i = 0; i < height; i++, pBitmap += width)
+ for (int j = 0; j < width / 2; j++)
+ SWAP(pBitmap[j], pBitmap[width - j - 1]);
+ }
+ return _loop[loopNo].cel[celNo].rawBitmap;
+}
+
+// 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
+void View::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte clearKey) {
+ int16 *unditherMemorial = _screen->unditherGetMemorial();
+
+ // It makes no sense to go further, if no memorial data from current picture is available
+ if (!unditherMemorial)
+ return;
+
+ // Makes no sense to process bitmaps that are 3 pixels wide or less
+ if (width <= 3) return;
+
+ // If EGA mapping is used for this view, dont do undithering as well
+ if (_EGAmapping)
+ return;
+
+ // Walk through the bitmap and remember all combinations of colors
+ int16 bitmapMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE];
+ byte *curPtr;
+ byte color1, color2;
+ int16 y, x;
+
+ memset(&bitmapMemorial, 0, sizeof(bitmapMemorial));
+
+ // Count all seemingly dithered pixel-combinations as soon as at least 4 pixels are adjacent
+ curPtr = bitmapPtr;
+ for (y = 0; y < height; y++) {
+ color1 = curPtr[0]; color2 = (curPtr[1] << 4) | curPtr[2];
+ curPtr += 3;
+ for (x = 3; x < width; x++) {
+ color1 = (color1 << 4) | (color2 >> 4);
+ color2 = (color2 << 4) | *curPtr++;
+ if (color1 == color2)
+ bitmapMemorial[color1]++;
+ }
+ }
+
+ // Now compare both memorial tables to find out matching dithering-combinations
+ 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)) {
+ // match found, check if colorKey is contained -> if so, we ignore of course
+ color1 = color & 0x0F; color2 = color >> 4;
+ if ((color1 != clearKey) && (color2 != clearKey) && (color1 != color2)) {
+ // so set this and the reversed color-combination for undithering
+ unditherTable[color] = true; unditherTable[(color1 << 4) | color2] = true;
+ 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]) {
+ // some color with black? turn colors around otherwise it wont be the right color at all
+ if ((color & 0xF0)==0)
+ color = (color << 4) | (color >> 4);
+ curPtr[0] = color; curPtr[1] = color;
+ }
+ curPtr++;
+ }
+ curPtr++;
+ }
+}
+
+void View::draw(Common::Rect rect, Common::Rect clipRect, Common::Rect clipRectTranslated, LoopNo loopNo, CelNo celNo, byte priority, uint16 EGAmappingNr, int16 origHeight) {
+ Palette *palette = _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
+ CelInfo *celInfo = getCelInfo(loopNo, celNo);
+ byte *bitmap = getBitmap(loopNo, celNo);
+ int16 celHeight = celInfo->height, celWidth = celInfo->width;
+ int16 width, height;
+ byte clearKey = celInfo->clearKey;
+ byte color;
+ byte drawMask = priority == 255 ? SCI_SCREEN_MASK_VISUAL : SCI_SCREEN_MASK_VISUAL|SCI_SCREEN_MASK_PRIORITY;
+ int x, y;
+
+ if (_embeddedPal) {
+ // Merge view palette in...
+ _palette->set(&_viewPalette, 1);
+ }
+
+ width = MIN(clipRect.width(), celWidth);
+ height = MIN(clipRect.height(), celHeight);
+
+ bitmap += (clipRect.top - rect.top) * celWidth + (clipRect.left - rect.left);
+
+ if (!_EGAmapping) {
+ for (y = clipRectTranslated.top; y < clipRectTranslated.top + height; y++, bitmap += celWidth) {
+ for (x = 0; x < width; x++) {
+ color = bitmap[x];
+ if (color != clearKey && priority >= _screen->getPriority(clipRectTranslated.left + x, y)) {
+ if (origHeight == -1) // HACK: this parameter is passed for already scaled views, but we're not actually using it
+ _screen->putPixel(clipRectTranslated.left + x, y, drawMask, palette->mapping[color], priority, 0);
+ else
+ _screen->putPixelOnDisplay(clipRectTranslated.left + x, y, palette->mapping[color]);
+ }
+ }
+ }
+ } else {
+ byte *EGAmapping = _EGAmapping + (EGAmappingNr * SCI_VIEW_EGAMAPPING_SIZE);
+ for (y = clipRectTranslated.top; y < clipRectTranslated.top + height; y++, bitmap += celWidth) {
+ for (x = 0; x < width; x++) {
+ color = EGAmapping[bitmap[x]];
+ if (color != clearKey && priority >= _screen->getPriority(clipRectTranslated.left + x, y))
+ _screen->putPixel(clipRectTranslated.left + x, y, drawMask, color, priority, 0);
+ }
+ }
+ }
+}
+
+Palette *View::getPalette() {
+ return _embeddedPal ? &_viewPalette : &_palette->_sysPalette;
+}
+
+} // End of namespace Sci