void GfxPicture::drawCelData(const SciSpan<const byte> &inbuffer, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX, int16 pictureY, bool isEGA) { const SciSpan<const byte> headerPtr = inbuffer.subspan(headerPos); const SciSpan<const byte> rlePtr = inbuffer.subspan(rlePos); // displaceX, displaceY fields are ignored, and may contain garbage // (e.g. pic 261 in Dr. Brain 1 Spanish - bug #3614914) //int16 displaceX, displaceY; byte priority = _priority; byte clearColor; bool compression = true; byte curByte; int16 y, lastY, x, leftX, rightX; int pixelCount; uint16 width, height; // if the picture is not an overlay and we are also not in EGA mode, use priority 0 if (!isEGA && !_addToFlag) priority = 0; // Width/height here are always LE, even in Mac versions width = headerPtr.getUint16LEAt(0); height = headerPtr.getUint16LEAt(2); //displaceX = (signed char)headerPtr[4]; //displaceY = (unsigned char)headerPtr[5]; if (_resourceType == SCI_PICTURE_TYPE_SCI11) // SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise clearColor = _screen->getColorWhite(); else clearColor = headerPtr[6]; //if (displaceX || displaceY) // error("unsupported embedded cel-data in picture"); // We will unpack cel-data into a temporary buffer and then plot it to screen // That needs to be done cause a mirrored picture may be requested pixelCount = width * height; Common::SpanOwner<SciSpan<byte> > celBitmap; celBitmap->allocate(pixelCount, _resource->name()); if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2) { // See GfxView::unpackCel() for why this black/white swap is done // This picture swap is only needed in SCI32, not SCI1.1 if (clearColor == 0) clearColor = 0xff; else if (clearColor == 0xff) clearColor = 0; } if (compression) { unpackCelData(inbuffer, *celBitmap, clearColor, rlePos, literalPos, _resMan->getViewType(), width, false); } else // No compression (some SCI32 pictures) memcpy(celBitmap->getUnsafeDataAt(0, pixelCount), rlePtr.getUnsafeDataAt(0, pixelCount), pixelCount); Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea(); // Horizontal clipping uint16 skipCelBitmapPixels = 0; int16 displayWidth = width; if (pictureX) { // horizontal scroll position for picture active, we need to adjust drawX accordingly drawX -= pictureX; if (drawX < 0) { skipCelBitmapPixels = -drawX; displayWidth -= skipCelBitmapPixels; drawX = 0; } } // Vertical clipping uint16 skipCelBitmapLines = 0; int16 displayHeight = height; if (pictureY) { // vertical scroll position for picture active, we need to adjust drawY accordingly // TODO: Finish this /*drawY -= pictureY; if (drawY < 0) { skipCelBitmapLines = -drawY; displayHeight -= skipCelBitmapLines; drawY = 0; }*/ } if (displayWidth > 0 && displayHeight > 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 won't 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 > 15 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY; SciSpan<const byte> ptr = *celBitmap; ptr += skipCelBitmapPixels; ptr += skipCelBitmapLines * width; if ((!isEGA) || (priority < 16)) { // VGA + EGA, EGA only checks priority, when given priority is below 16 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--; } } } else { // EGA, when priority is above 15 // we don't check priority and also won't set priority at all // fixes picture 48 of kq5 (island overview). Bug #5182 if (!_mirroredFlag) { // EGA+priority>15: Draw bitmap to screen x = leftX; while (y < lastY) { curByte = *ptr++; if (curByte != clearColor) _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL, curByte, 0, 0); x++; if (x >= rightX) { ptr += sourcePixelSkipPerRow; x = leftX; y++; } } } else { // EGA+priority>15: Draw bitmap to screen (mirrored) x = rightX - 1; while (y < lastY) { curByte = *ptr++; if (curByte != clearColor) _screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL, curByte, 0, 0); if (x == leftX) { ptr += sourcePixelSkipPerRow; x = rightX; y++; } x--; } } } } }