diff options
Diffstat (limited to 'engines/sci/graphics/picture.cpp')
-rw-r--r-- | engines/sci/graphics/picture.cpp | 230 |
1 files changed, 151 insertions, 79 deletions
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index 7cd37b6f46..e568316919 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -43,10 +43,11 @@ GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, } GfxPicture::~GfxPicture() { + _resMan->unlockResource(_resource); } void GfxPicture::initData(GuiResourceId resourceId) { - _resource = _resMan->findResource(ResourceId(kResourceTypePic, resourceId), false); + _resource = _resMan->findResource(ResourceId(kResourceTypePic, resourceId), true); if (!_resource) { error("picture resource %d not found", resourceId); } @@ -56,7 +57,9 @@ GuiResourceId GfxPicture::getResourceId() { return _resourceId; } -// TODO: subclass this +// differentiation between various picture formats can NOT get done using sci-version checks. +// Games like PQ1 use the "old" vector data picture format, but are actually SCI1.1 +// We should leave this that way to decide the format on-the-fly instead of hardcoding it in any way void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) { uint16 headerSize; @@ -70,14 +73,12 @@ void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int1 switch (headerSize) { case 0x26: // SCI 1.1 VGA picture _resourceType = SCI_PICTURE_TYPE_SCI11; - if (_addToFlag) - _priority = 15; drawSci11Vga(); break; #ifdef ENABLE_SCI32 case 0x0e: // SCI32 VGA picture _resourceType = SCI_PICTURE_TYPE_SCI32; - drawSci32Vga(); + //drawSci32Vga(); break; #endif default: @@ -99,28 +100,46 @@ void GfxPicture::reset() { void GfxPicture::drawSci11Vga() { byte *inbuffer = _resource->data; int size = _resource->size; - int has_cel = READ_LE_UINT16(inbuffer + 4); - int vector_dataPos = READ_LE_UINT16(inbuffer + 16); + int priorityBandsCount = inbuffer[3]; + int has_cel = inbuffer[4]; + int vector_dataPos = READ_LE_UINT32(inbuffer + 16); int vector_size = size - vector_dataPos; - int palette_data_ptr = READ_LE_UINT16(inbuffer + 28); - int cel_headerPos = READ_LE_UINT16(inbuffer + 32); - int cel_RlePos = READ_LE_UINT16(inbuffer + cel_headerPos + 24); - int cel_LiteralPos = READ_LE_UINT16(inbuffer + cel_headerPos + 28); + int palette_data_ptr = READ_LE_UINT32(inbuffer + 28); + int cel_headerPos = READ_LE_UINT32(inbuffer + 32); + int cel_RlePos = READ_LE_UINT32(inbuffer + cel_headerPos + 24); + int cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28); Palette palette; + // Header + // [headerSize:WORD] [unknown:BYTE] [priorityBandCount:BYTE] [hasCel:BYTE] [unknown:BYTE] + // [unknown:WORD] [unknown:WORD] [unknown:WORD] [unknown:WORD] [unknown:WORD] + // Offset 16 + // [vectorDataOffset:DWORD] [unknown:DWORD] [unknown:DWORD] [paletteDataOffset:DWORD] + // Offset 32 + // [celHeaderOffset:DWORD] [unknown:DWORD] + // [priorityBandData:WORD] * priorityBandCount + // [priority:BYTE] [unknown:BYTE] + // Create palette and set it _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette); _palette->set(&palette, true); + // priority bands are supposed to be 14 for sci1.1 pictures + assert(priorityBandsCount == 14); + + if (_addToFlag) { + _priority = inbuffer[40 + priorityBandsCount * 2] & 0xF; + } + // display Cel-data if (has_cel) - drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0); + drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0, 0); // process vector data drawVectorData(inbuffer + vector_dataPos, vector_size); - // Remember priority band information for later - _ports->priorityBandsRemember(inbuffer + 40); + // Set priority band information + _ports->priorityBandsInitSci11(inbuffer + 40); } #ifdef ENABLE_SCI32 @@ -129,45 +148,80 @@ int16 GfxPicture::getSci32celCount() { return inbuffer[2]; } -void GfxPicture::drawSci32Vga(int16 celNo) { +int16 GfxPicture::getSci32celY(int16 celNo) { + byte *inbuffer = _resource->data; + int header_size = READ_LE_UINT16(inbuffer); + int cel_headerPos = header_size + 42 * celNo; + return READ_LE_UINT16(inbuffer + cel_headerPos + 40); +} + +int16 GfxPicture::getSci32celX(int16 celNo) { + byte *inbuffer = _resource->data; + int header_size = READ_LE_UINT16(inbuffer); + int cel_headerPos = header_size + 42 * celNo; + return READ_LE_UINT16(inbuffer + cel_headerPos + 38); +} + +int16 GfxPicture::getSci32celWidth(int16 celNo) { + byte *inbuffer = _resource->data; + int header_size = READ_LE_UINT16(inbuffer); + int cel_headerPos = header_size + 42 * celNo; + return READ_LE_UINT16(inbuffer + cel_headerPos + 0); +} + +int16 GfxPicture::getSci32celPriority(int16 celNo) { + byte *inbuffer = _resource->data; + int header_size = READ_LE_UINT16(inbuffer); + int cel_headerPos = header_size + 42 * celNo; + return READ_LE_UINT16(inbuffer + cel_headerPos + 36); +} + +void GfxPicture::drawSci32Vga(int16 celNo, int16 drawX, int16 drawY, int16 pictureX, bool mirrored) { byte *inbuffer = _resource->data; int size = _resource->size; int header_size = READ_LE_UINT16(inbuffer); int palette_data_ptr = READ_LE_UINT16(inbuffer + 6); - int celCount = inbuffer[2]; +// int celCount = inbuffer[2]; int cel_headerPos = header_size; int cel_RlePos, cel_LiteralPos; - int cel_relXpos, cel_relYpos; Palette palette; // HACK - _mirroredFlag = false; + _mirroredFlag = mirrored; _addToFlag = false; _resourceType = SCI_PICTURE_TYPE_SCI32; - if ((celNo == -1) || (celNo == 0)) { + if (celNo == 0) { // Create palette and set it _palette->createFromData(inbuffer + palette_data_ptr, size - palette_data_ptr, &palette); _palette->set(&palette, true); } - if (celNo != -1) { - cel_headerPos += 42 * celNo; - celCount = 1; - } - while (celCount > 0) { - cel_RlePos = READ_LE_UINT32(inbuffer + cel_headerPos + 24); - cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28); - cel_relXpos = READ_LE_UINT16(inbuffer + cel_headerPos + 38); - cel_relYpos = READ_LE_UINT16(inbuffer + cel_headerPos + 40); - drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, cel_relXpos, cel_relYpos); - cel_headerPos += 42; - celCount--; + // Header + // [headerSize:WORD] [celCount:BYTE] [Unknown:BYTE] [Unknown:WORD] [paletteOffset:DWORD] [Unknown:DWORD] + // cel-header follow afterwards, each is 42 bytes + // Cel-Header + // [width:WORD] [height:WORD] [displaceX:WORD] [displaceY:WORD] [clearColor:BYTE] [compressed:BYTE] + // offset 10-23 is unknown + // [rleOffset:DWORD] [literalOffset:DWORD] [Unknown:WORD] [Unknown:WORD] [priority:WORD] [relativeXpos:WORD] [relativeYpos:WORD] + + cel_headerPos += 42 * celNo; + + if (mirrored) { + // switch around relativeXpos + Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea(); + drawX = displayArea.width() - drawX - READ_LE_UINT16(inbuffer + cel_headerPos + 0); } + + cel_RlePos = READ_LE_UINT32(inbuffer + cel_headerPos + 24); + cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28); + + drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, drawX, drawY, pictureX); + cel_headerPos += 42; } #endif -void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY) { +void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX) { byte *celBitmap = NULL; byte *ptr = NULL; byte *headerPtr = inbuffer + headerPos; @@ -295,52 +349,71 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea(); - y = callerY + displayArea.top; - lastY = MIN<int16>(height + y, displayArea.bottom); - leftX = callerX + displayArea.left; - rightX = MIN<int16>(width + leftX, displayArea.right); - - // Change clearcolor to white, if we dont add to an existing picture. That way we will paint everything on screen - // but white and that wont matter because the screen is supposed to be already white. It seems that most (if not all) - // SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint - // the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra - if (!_addToFlag) - clearColor = _screen->getColorWhite(); - - ptr = celBitmap; - if (!_mirroredFlag) { - // Draw bitmap to screen - x = leftX; - while (y < lastY) { - curByte = *ptr++; - if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y))) - _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY, curByte, priority, 0); - - x++; - - if (x >= rightX) { - if (width > rightX - leftX) // Skip extra pixels at the end of the row - ptr += width - (rightX - leftX); - x = leftX; - y++; - } + uint16 skipCelBitmapPixels = 0; + int16 displayWidth = width; + if (pictureX) { + // scroll position for picture active, we need to adjust drawX accordingly + drawX -= pictureX; + if (drawX < 0) { + skipCelBitmapPixels = -drawX; + displayWidth -= skipCelBitmapPixels; + drawX = 0; } - } else { - // Draw bitmap to screen (mirrored) - x = rightX - 1; - while (y < lastY) { - curByte = *ptr++; - if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y))) - _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY, curByte, priority, 0); - - if (x == leftX) { - if (width > rightX - leftX) // Skip extra pixels at the end of the row - ptr += width - (rightX - leftX); - x = rightX; - y++; + } + + if (displayWidth > 0) { + y = displayArea.top + drawY; + lastY = MIN<int16>(height + y, displayArea.bottom); + leftX = displayArea.left + drawX; + rightX = MIN<int16>(displayWidth + leftX, displayArea.right); + + uint16 sourcePixelSkipPerRow = 0; + if (width > rightX - leftX) + sourcePixelSkipPerRow = width - (rightX - leftX); + + // Change clearcolor to white, if we dont add to an existing picture. That way we will paint everything on screen + // but white and that wont matter because the screen is supposed to be already white. It seems that most (if not all) + // SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint + // the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra + if (!_addToFlag) + clearColor = _screen->getColorWhite(); + + byte drawMask = priority == 255 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY; + + ptr = celBitmap; + ptr += skipCelBitmapPixels; + if (!_mirroredFlag) { + // Draw bitmap to screen + x = leftX; + while (y < lastY) { + curByte = *ptr++; + if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y))) + _screen->putPixel(x, y, drawMask, curByte, priority, 0); + + x++; + + if (x >= rightX) { + ptr += sourcePixelSkipPerRow; + x = leftX; + y++; + } } + } else { + // Draw bitmap to screen (mirrored) + x = rightX - 1; + while (y < lastY) { + curByte = *ptr++; + if ((curByte != clearColor) && (priority >= _screen->getPriority(x, y))) + _screen->putPixel(x, y, drawMask, curByte, priority, 0); + + if (x == leftX) { + ptr += sourcePixelSkipPerRow; + x = rightX; + y++; + } - x--; + x--; + } } } @@ -537,6 +610,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { // This picture includes garbage data, first a set pattern w/o parameter and then short pattern // I guess that garbage is a left over from the sq4-floppy (sci1) to sq4-cd (sci1.1) conversion switch (_resourceId) { + case 35: case 381: case 376: return; @@ -618,7 +692,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { vectorGetAbsCoordsNoMirror(data, curPos, x, y); size = READ_LE_UINT16(data + curPos); curPos += 2; _priority = pic_priority; // set global priority so the cel gets drawn using current priority as well - drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y); + drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, 0); curPos += size; break; case PIC_OPX_EGA_SET_PRIORITY_TABLE: @@ -658,9 +732,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { vectorGetAbsCoordsNoMirror(data, curPos, x, y); size = READ_LE_UINT16(data + curPos); curPos += 2; _priority = pic_priority; // set global priority so the cel gets drawn using current priority as well - if (pic_priority == 255) - _priority = 0; // if priority not set, use priority 0 - drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y); + drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, 0); curPos += size; break; case PIC_OPX_VGA_PRIORITY_TABLE_EQDIST: |