/* 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 3 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, see . * */ #ifdef ENABLE_HE #include "common/system.h" #include "scumm/he/intern_he.h" #include "scumm/he/wiz_he.h" namespace Scumm { #define WARP_FRAC_SIZE (_vm->_game.heversion > 98 ? 20 : 16) #define WARP_FRAC_VALUE (1 << (WARP_FRAC_SIZE)) #define WARP_TEXTURE_LIMIT (1 << (31 - (WARP_FRAC_SIZE))) #define WARP_TO_FRAC(_x_) ((_x_) << (WARP_FRAC_SIZE)) #define WARP_FROM_FRAC(_x_) ((_x_) >> (WARP_FRAC_SIZE)) bool Wiz::warpDrawWiz(int image, int state, int polygon, int32 flags, int transparentColor, WizSimpleBitmap *optionalDestBitmap, const WizRawPixel *optionalColorConversionTable, int shadowImage) { const byte *xmapColorTable; int polyIndex; // Parameters check... for (polyIndex = 0; polyIndex < ARRAYSIZE(_polygons); polyIndex++) { if (_polygons[polyIndex].id == polygon) { break; } } if (ARRAYSIZE(_polygons) <= polyIndex) { error("Wiz::warpDrawWiz(): Polygon %d not defined", polygon); } if (_polygons[polyIndex].numPoints != 5) { error("Wiz::warpDrawWiz(): Invalid point count"); } if (shadowImage) { xmapColorTable = getColorMixBlockPtrForWiz(shadowImage); if (xmapColorTable) { xmapColorTable += _vm->_resourceHeaderSize; } } else { xmapColorTable = nullptr; } // Call the actual warping primitive... WarpWizPoint polypoints[5]; for (int i = 0; i < 5; i++) { WarpWizPoint tmp(_polygons[polyIndex].points[i]); polypoints[i] = tmp; } return warpDrawWizTo4Points( image, state, polypoints, flags, transparentColor, nullptr, optionalDestBitmap, optionalColorConversionTable, xmapColorTable); } bool Wiz::warpDrawWizTo4Points(int image, int state, const WarpWizPoint *dstPoints, int32 flags, int transparentColor, const Common::Rect *optionalClipRect, WizSimpleBitmap *optionalDestBitmap, const WizRawPixel *optionalColorConversionTable, const byte *colorMixTable) { WizSimpleBitmap dstBitmap, srcBitmap; bool rValue; Common::Rect updateRect; int x, y; WarpWizPoint srcPoints[4]; byte *ptr; // Set the optional remap table up to the default if one isn't specified... if (!optionalColorConversionTable && _uses16BitColor) { optionalColorConversionTable = (WizRawPixel *)_vm->getHEPaletteSlot(1); } // Decompress the wiz into a WizSimpleBitmap... (Always, if using a remap table)... if ((getWizCompressionType(image, state) != kWCTNone) || (optionalColorConversionTable != nullptr) || (flags & (kWRFHFlip | kWRFVFlip | kWRFRemap))) { srcBitmap.bufferPtr = drawAWizPrim(image, state, 0, 0, 0, 0, 0, 0, kWRFAlloc | flags, 0, optionalColorConversionTable); if (!srcBitmap.bufferPtr()) { return false; } } else { ptr = (byte *)getWizStateDataPrim(image, state); if (!ptr) error("Wiz::warpDrawWizTo4Points(): Image %d missing data", image); // Map the srcBitmap to the Wiz data... srcBitmap.bufferPtr = WizPxShrdBuffer(ptr + _vm->_resourceHeaderSize, false); } // Fill in the dest bitmap structure... if (optionalDestBitmap) { dstBitmap = *optionalDestBitmap; } else { VirtScreen *pvs = &_vm->_virtscr[kMainVirtScreen]; dstBitmap.bitmapWidth = pvs->w; dstBitmap.bitmapHeight = pvs->h; if (flags & kWRFForeground) { dstBitmap.bufferPtr = WizPxShrdBuffer(pvs->getPixels(0, pvs->topline), false); } else { dstBitmap.bufferPtr = WizPxShrdBuffer(pvs->getBackPixels(0, pvs->topline), false); } if (!dstBitmap.bufferPtr()) { error("Wiz::warpDrawWizTo4Points(): Missing drawing buffer?"); } dstBitmap.bufferPtr += (pvs->xstart * (_uses16BitColor ? 2 : 1)); } // Find the bounding rect and double check the coords... updateRect.left = 12345; updateRect.top = 12345; updateRect.right = -12345; updateRect.bottom = -12345; for (int i = 0; i < 4; i++) { x = dstPoints[i].x; y = dstPoints[i].y; updateRect.left = MIN(updateRect.left, x); updateRect.top = MIN(updateRect.top, y); updateRect.right = MAX(updateRect.right, x); updateRect.bottom = MAX(updateRect.bottom, y); } updateRect.left = MAX(0, MIN((dstBitmap.bitmapWidth - 1), updateRect.left)); updateRect.top = MAX(0, MIN((dstBitmap.bitmapHeight - 1), updateRect.top)); updateRect.right = MAX(0, MIN((dstBitmap.bitmapWidth - 1), updateRect.right)); updateRect.bottom = MAX(0, MIN((dstBitmap.bitmapHeight - 1), updateRect.bottom)); getWizImageDim(image, state, srcBitmap.bitmapWidth, srcBitmap.bitmapHeight); srcPoints[0].x = 0; srcPoints[0].y = 0; srcPoints[1].x = srcBitmap.bitmapWidth - 1; srcPoints[1].y = 0; srcPoints[2].x = srcBitmap.bitmapWidth - 1; srcPoints[2].y = srcBitmap.bitmapHeight - 1; srcPoints[3].x = 0; srcPoints[3].y = srcBitmap.bitmapHeight - 1; // Call the warping primitive... if (_vm->_game.heversion >= 95 && colorMixTable) { rValue = warpNPt2NPtClippedWarpMixColors( &dstBitmap, dstPoints, &srcBitmap, srcPoints, 4, transparentColor, optionalClipRect, colorMixTable); } else { if (_vm->_game.heversion >= 99) { // This shouldn't be a HE99 branch, but some later HE99 executables do this... rValue = warpNPt2NPtWarpCORE( &dstBitmap, dstPoints, &srcBitmap, srcPoints, 4, transparentColor, optionalClipRect, flags); } else { if (optionalClipRect) { rValue = warpNPt2NPtClippedWarp( &dstBitmap, dstPoints, &srcBitmap, srcPoints, 4, transparentColor, optionalClipRect); } else { rValue = warpNPt2NPtNonClippedWarp( &dstBitmap, dstPoints, &srcBitmap, srcPoints, 4, transparentColor); } } } // Update the screen? (If not writing to another bitmap...) if (rValue && !optionalDestBitmap) { if (!(flags & kWRFForeground)) { _vm->backgroundToForegroundBlit(updateRect); } else { ++updateRect.bottom; _vm->markRectAsDirty(kMainVirtScreen, updateRect); } } // Clean up... srcBitmap.bufferPtr = WizPxShrdBuffer(); return rValue; } WarpWizOneSpanTable *Wiz::warpCreateSpanTable(int spanCount) { WarpWizOneSpanTable *spanTable; WarpWizOneSpan *spanPtr; int counter; spanTable = (WarpWizOneSpanTable *)malloc(sizeof(WarpWizOneSpanTable)); if (!spanTable) { return nullptr; } // Clear spanTable structure spanTable->dstMinPt = WarpWizPoint(); spanTable->dstMaxPt = WarpWizPoint(); spanTable->srcMinPt = WarpWizPoint(); spanTable->srcMaxPt = WarpWizPoint(); spanTable->drawSpans = nullptr; spanTable->spans = nullptr; spanTable->drawSpanCount = 0; spanTable->spanCount = 0; spanTable->spanCount = spanCount; spanTable->spans = (WarpWizOneSpan *)malloc(spanCount * sizeof(WarpWizOneSpan)); if (!spanTable->spans) { warpDestroySpanTable(spanTable); return nullptr; } // Clear spanTable spans structure for (counter = 0; counter < spanCount; counter++) { spanTable->spans[counter].dstLeft = 0; spanTable->spans[counter].dstRight = 0; spanTable->spans[counter].srcLeft = WarpWizPoint(); spanTable->spans[counter].srcRight = WarpWizPoint(); } spanPtr = spanTable->spans; for (counter = 0; counter < spanCount; counter++) { spanPtr->dstLeft = 0x7FFFFFFF; spanPtr->dstRight = (-0x7FFFFFFF - 1); spanPtr++; } spanTable->drawSpans = (WarpWizOneDrawSpan *)malloc(spanCount * sizeof(WarpWizOneDrawSpan)); if (!spanTable->drawSpans) { warpDestroySpanTable(spanTable); return nullptr; } memset(spanTable->drawSpans, 0, spanCount * sizeof(WarpWizOneDrawSpan)); return spanTable; } void Wiz::warpDestroySpanTable(WarpWizOneSpanTable *spanTable) { if (spanTable->drawSpans) { free(spanTable->drawSpans); spanTable->drawSpans = nullptr; } if (spanTable->spans) { free(spanTable->spans); spanTable->spans = nullptr; } free(spanTable); spanTable = nullptr; } WarpWizOneSpanTable *Wiz::warpBuildSpanTable(WizSimpleBitmap *dstBitmap, const WizSimpleBitmap *srcBitmap, const WarpWizPoint *dstPts, const WarpWizPoint *srcPts, int npoints, const Common::Rect *clipRectPtr) { int offset, dw, nonClippedWidth, cl, cr, cy, dl, cw; WarpWizPoint srcPt, dstPt, dstMinPt, dstMaxPt, srcMinPt, srcMaxPt, clippedPt; WarpWizOneDrawSpan *drawSpan; Common::Rect clippingRect; WarpWizOneSpanTable *st; WarpWizOneSpan *span; bool ignoreAll; // Setup the clipping rect clippingRect.left = 0; clippingRect.top = 0; clippingRect.right = dstBitmap->bitmapWidth - 1; clippingRect.bottom = dstBitmap->bitmapHeight - 1; ignoreAll = false; if (clipRectPtr) { if (!findRectOverlap(&clippingRect, clipRectPtr)) { ignoreAll = true; } } // Find the span table fun parts... warpFindMinMaxpoints(&dstMinPt, &dstMaxPt, dstPts, npoints); warpFindMinMaxpoints(&srcMinPt, &srcMaxPt, srcPts, npoints); st = warpCreateSpanTable((dstMaxPt.y - dstMinPt.y) + 1); if (!st) { return nullptr; } st->dstMinPt = dstMinPt; st->dstMaxPt = dstMaxPt; st->srcMinPt = srcMinPt; st->srcMaxPt = srcMaxPt; dstPt = *dstPts; srcPt = *srcPts; // Scan convert the polygon... for (int i = 1; i < npoints; i++) { warpFillSpanWithLine(st, dstPts, dstPts + 1, srcPts, srcPts + 1); dstPts++; srcPts++; } warpFillSpanWithLine(st, dstPts, &dstPt, srcPts, &srcPt); // Build the draw span table... drawSpan = st->drawSpans; dw = dstBitmap->bitmapWidth; span = st->spans; offset = st->dstMinPt.y * dw; st->drawSpanCount = 0; cy = st->dstMinPt.y; if (ignoreAll) { return st; } for (int i = st->spanCount; --i >= 0; ++cy, ++span, offset += dw) { // Clip vertical? if ((cy < clippingRect.top) || (cy > clippingRect.bottom)) { continue; } // Clip horizontal? cl = MAX(clippingRect.left, span->dstLeft); cr = MIN(clippingRect.right, span->dstRight); if ((cw = (cr - cl + 1)) <= 0) { continue; } // Calc the step values... nonClippedWidth = (span->dstRight) - (span->dstLeft) + 1; drawSpan->xSrcStep = WARP_TO_FRAC((span->srcRight.x - span->srcLeft.x)) / nonClippedWidth; drawSpan->ySrcStep = WARP_TO_FRAC((span->srcRight.y - span->srcLeft.y)) / nonClippedWidth; // Calc the clipped values... drawSpan->dstOffset = offset + cl; drawSpan->dstWidth = cw; drawSpan->xSrcOffset = WARP_TO_FRAC(span->srcLeft.x); drawSpan->ySrcOffset = WARP_TO_FRAC(span->srcLeft.y); if ((dl = (cl - span->dstLeft)) != 0) { drawSpan->xSrcOffset += (dl * drawSpan->xSrcStep); drawSpan->ySrcOffset += (dl * drawSpan->ySrcStep); } st->drawSpanCount++; drawSpan++; } return st; } void Wiz::warpFindMinMaxpoints(WarpWizPoint *minPtr, WarpWizPoint *maxPtr, const WarpWizPoint *points, int npoints) { WarpWizPoint minPt, maxPt, pt; // Find the limits... maxPt.x = (-0x7FFFFFFF - 1); maxPt.y = (-0x7FFFFFFF - 1); minPt.x = 0x7FFFFFFF; minPt.y = 0x7FFFFFFF; for (int i = 0; i < npoints; i++) { pt = *points++; if (pt.x < minPt.x) { minPt.x = pt.x; } if (pt.y < minPt.y) { minPt.y = pt.y; } if (pt.x > maxPt.x) { maxPt.x = pt.x; } if (pt.y > maxPt.y) { maxPt.y = pt.y; } } *minPtr = minPt; *maxPtr = maxPt; } void Wiz::warpProcessDrawSpansA(WizSimpleBitmap *dstBitmap, const WizSimpleBitmap *srcBitmap, const WarpWizOneDrawSpan *drawSpans, int count) { int xStep, yStep, sw, xOffset, yOffset; const WizRawPixel8 *src8; const WizRawPixel16 *src16; WizRawPixel8 *dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); WizRawPixel16 *dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); sw = srcBitmap->bitmapWidth; src8 = (WizRawPixel8 *)srcBitmap->bufferPtr(); src16 = (WizRawPixel16 *)srcBitmap->bufferPtr(); for (int yCounter = count; --yCounter >= 0;) { if (!_uses16BitColor) { dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); dst8 += drawSpans->dstOffset; } else { dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); dst16 += drawSpans->dstOffset; } xOffset = drawSpans->xSrcOffset; yOffset = drawSpans->ySrcOffset; xStep = drawSpans->xSrcStep; yStep = drawSpans->ySrcStep; for (int xCounter = drawSpans->dstWidth; --xCounter >= 0;) { if (!_uses16BitColor) { *dst8++ = *(src8 + (sw * WARP_FROM_FRAC(yOffset)) + WARP_FROM_FRAC(xOffset)); } else { *dst16++ = *(src16 + (sw * WARP_FROM_FRAC(yOffset)) + WARP_FROM_FRAC(xOffset)); } xOffset += xStep; yOffset += yStep; } drawSpans++; } } void Wiz::warpProcessDrawSpansTransparent(WizSimpleBitmap *dstBitmap, const WizSimpleBitmap *srcBitmap, const WarpWizOneDrawSpan *drawSpans, int count, WizRawPixel transparentColor) { int xStep, yStep, sw, xOffset, yOffset; const WizRawPixel8 *src8 = (WizRawPixel8 *)srcBitmap->bufferPtr(); const WizRawPixel16 *src16 = (WizRawPixel16 *)srcBitmap->bufferPtr(); WizRawPixel8 *dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); WizRawPixel16 *dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); WizRawPixel srcColor; sw = srcBitmap->bitmapWidth; for (int yCounter = count; --yCounter >= 0;) { if (!_uses16BitColor) { dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); dst8 += drawSpans->dstOffset; } else { dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); dst16 += drawSpans->dstOffset; } xOffset = drawSpans->xSrcOffset; yOffset = drawSpans->ySrcOffset; xStep = drawSpans->xSrcStep; yStep = drawSpans->ySrcStep; for (int xCounter = drawSpans->dstWidth; --xCounter >= 0;) { if (!_uses16BitColor) { srcColor = *(src8 + (sw * WARP_FROM_FRAC(yOffset)) + WARP_FROM_FRAC(xOffset)); if (srcColor != transparentColor) { *dst8++ = (WizRawPixel8)srcColor; } else { dst8++; } } else { srcColor = *(src16 + (sw * WARP_FROM_FRAC(yOffset)) + WARP_FROM_FRAC(xOffset)); if (srcColor != transparentColor) { *dst16++ = (WizRawPixel16)srcColor; } else { dst16++; } } xOffset += xStep; yOffset += yStep; } drawSpans++; } } void Wiz::warpProcessDrawSpansTransparentFiltered(WizSimpleBitmap *dstBitmap, const WizSimpleBitmap *srcBitmap, const WarpWizOneDrawSpan *drawSpans, int count, WizRawPixel transparentColor, const byte *pXmapColorTable, bool bIsHintColor, WizRawPixel hintColor) { int srcWidth = srcBitmap->bitmapWidth; const WizRawPixel8 *src8 = (WizRawPixel8 *)srcBitmap->bufferPtr(); const WizRawPixel16 *src16 = (WizRawPixel16 *)srcBitmap->bufferPtr(); bool bSkipFilter = false; int xStep, yStep, xOffset, yOffset; WizRawPixel srcColor, srcColorN, srcColorS, srcColorE, srcColorW; int iCurrentX, iCurrentY, iXScan, iYScan; WizRawPixel8 *dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); WizRawPixel16 *dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); // Set up a rect for clipping if needed Common::Rect aSrcRect; // Source rectangle for clipping... Common::Rect aScanRect; // Dest rectangle for clipping... aSrcRect.left = 0; aSrcRect.top = 0; aSrcRect.right = srcWidth; aSrcRect.bottom = srcBitmap->bitmapHeight; iYScan = aSrcRect.bottom / count; for (int yCounter = count; --yCounter >= 0;) { if (!_uses16BitColor) { dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); dst8 += drawSpans->dstOffset; } else { dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); dst16 += drawSpans->dstOffset; } xOffset = drawSpans->xSrcOffset; yOffset = drawSpans->ySrcOffset; xStep = drawSpans->xSrcStep; yStep = drawSpans->ySrcStep; iXScan = WARP_FROM_FRAC(xStep); // The width of the search should be the x step size... for (int xCounter = drawSpans->dstWidth; --xCounter >= 0;) { iCurrentX = WARP_FROM_FRAC(xOffset); iCurrentY = WARP_FROM_FRAC(yOffset); // Get the current color and the surrounding colors... if (!_uses16BitColor) { srcColor = *(src8 + (srcWidth * iCurrentY) + iCurrentX); } else { srcColor = *(src16 + (srcWidth * iCurrentY) + iCurrentX); } bSkipFilter = false; if (bIsHintColor) { // Check if we need to clip our scan rectangle... aScanRect.left = iCurrentX - iXScan; aScanRect.top = iCurrentY - iYScan; aScanRect.right = iCurrentX + iXScan; aScanRect.bottom = iCurrentY + iYScan; findRectOverlap(&aScanRect, &aSrcRect); // Scan through rect looking for hint color... for (int yScan = aScanRect.top; yScan < aScanRect.bottom; ++yScan) { for (int xScan = aScanRect.left; xScan < aScanRect.right; ++xScan) { if (!_uses16BitColor) { if ((*(src8 + (yScan * srcWidth) + xScan)) == hintColor) { srcColor = hintColor; bSkipFilter = true; break; } } else { if ((*(src16 + (yScan * srcWidth) + xScan)) == hintColor) { srcColor = hintColor; bSkipFilter = true; break; } } } if (bSkipFilter) { break; } } } if ((srcColor != transparentColor) && !bSkipFilter) { // Check if top... if (iCurrentY != 0) { if (!_uses16BitColor) { srcColorN = *(src8 + (srcWidth * (iCurrentY - 1)) + iCurrentX); } else { srcColorN = *(src16 + (srcWidth * (iCurrentY - 1)) + iCurrentX); } } else { srcColorN = transparentColor; } // Check if bottom... if (iCurrentY != aSrcRect.bottom) { if (!_uses16BitColor) { srcColorS = *(src8 + (srcWidth * (iCurrentY + 1)) + iCurrentX); } else { srcColorS = *(src16 + (srcWidth * (iCurrentY + 1)) + iCurrentX); } } else { srcColorS = transparentColor; } // Check for left edge... if (iCurrentX != 0) { if (!_uses16BitColor) { srcColorW = *(src8 + (srcWidth * iCurrentY) + (iCurrentX - 1)); } else { srcColorW = *(src16 + (srcWidth * iCurrentY) + (iCurrentX - 1)); } } else { srcColorW = transparentColor; } // Check for right edge... if (iCurrentX != aSrcRect.right) { if (!_uses16BitColor) { srcColorE = *(src8 + (srcWidth * iCurrentY) + (iCurrentX + 1)); } else { srcColorE = *(src16 + (srcWidth * iCurrentY) + (iCurrentX + 1)); } } else { srcColorE = transparentColor; } // Make transparent color black... if (srcColorN == transparentColor) { srcColorN = srcColor; } if (srcColorS == transparentColor) { srcColorS = srcColor; } if (srcColorW == transparentColor) { srcColorW = srcColor; } if (srcColorE == transparentColor) { srcColorE = srcColor; } if (!_uses16BitColor) { // In 8 bit mode go around the pixel adding in colors (1/8 of the surrounding colors) // from the xmap color table; this ignores transparent pixels... srcColor = *(pXmapColorTable + (srcColorN * 256) + srcColor); srcColor = *(pXmapColorTable + (srcColorS * 256) + srcColor); srcColor = *(pXmapColorTable + (srcColorE * 256) + srcColor); srcColor = *(pXmapColorTable + (srcColorW * 256) + srcColor); } else { int rs = 0, gs = 0, bs = 0; float rd = 0, gd = 0, bd = 0; // Get 1/4 of each surrounding pixel and add into source pixel... rawPixelExtractComponents(srcColorN, rs, gs, bs); rd += rs; gd += gs; bd += bs; rawPixelExtractComponents(srcColorS, rs, gs, bs); rd += rs; gd += gs; bd += bs; rawPixelExtractComponents(srcColorW, rs, gs, bs); rd += rs; gd += gs; bd += bs; rawPixelExtractComponents(srcColorE, rs, gs, bs); rd += rs; gd += gs; bd += bs; // Average pixels... rd /= 4.0f; gd /= 4.0f; bd /= 4.0f; // Get source pixel colors... rawPixelExtractComponents(srcColor, rs, gs, bs); rd += rs; gd += gs; bd += bs; // Now average the two... rd /= 2.0f; gd /= 2.0f; bd /= 2.0f; // Pack the pixel back up... rawPixelPackComponents(srcColor, (int)rd, (int)gd, (int)bd); } } if (srcColor != transparentColor) { if (!_uses16BitColor) { *dst8++ = srcColor; } else { *dst16++ = srcColor; } } else { if (!_uses16BitColor) { dst8++; } else { dst16++; } } xOffset += xStep; yOffset += yStep; } drawSpans++; } } void Wiz::warpProcessDrawSpansMixColors(WizSimpleBitmap *dstBitmap, const WizSimpleBitmap *srcBitmap, const WarpWizOneDrawSpan *drawSpans, int count, WizRawPixel transparentColor, const byte *tablePtr) { int xStep, yStep, sw, xOffset, yOffset; WizRawPixel srcColor; const WizRawPixel8 *src8 = (WizRawPixel8 *)srcBitmap->bufferPtr(); const WizRawPixel16 *src16 = (WizRawPixel16 *)srcBitmap->bufferPtr(); WizRawPixel8 *dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); WizRawPixel16 *dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); sw = srcBitmap->bitmapWidth; for (int yCounter = count; --yCounter >= 0;) { if (!_uses16BitColor) { dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); dst8 += drawSpans->dstOffset; } else { dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); dst16 += drawSpans->dstOffset; } xOffset = drawSpans->xSrcOffset; yOffset = drawSpans->ySrcOffset; xStep = drawSpans->xSrcStep; yStep = drawSpans->ySrcStep; for (int xCounter = drawSpans->dstWidth; --xCounter >= 0;) { if (!_uses16BitColor) { srcColor = (*(src8 + (sw * WARP_FROM_FRAC(yOffset)) + WARP_FROM_FRAC(xOffset))); if (srcColor != transparentColor) { *dst8 = *(tablePtr + (srcColor * 256) + (*dst8)); dst8++; } else { dst8++; } } else { srcColor = *(src16 + (sw * WARP_FROM_FRAC(yOffset)) + WARP_FROM_FRAC(xOffset)); *dst16 = WIZRAWPIXEL_50_50_MIX(WIZRAWPIXEL_50_50_PREMIX_COLOR(srcColor), WIZRAWPIXEL_50_50_PREMIX_COLOR(*dst16)); dst16++; } xOffset += xStep; yOffset += yStep; } drawSpans++; } } void Wiz::warpFillSpanWithLine(WarpWizOneSpanTable *st, const WarpWizPoint *dstA, const WarpWizPoint *dstB, const WarpWizPoint *srcA, const WarpWizPoint *srcB) { WarpWizPoint dstStep, srcStep, dstPt, srcPt, dpt, spt; int h, lx1, ly1, lx2, ly2; WarpWizOneSpan *spanPtr; dstPt.x = WARP_TO_FRAC(dstA->x); srcPt.x = WARP_TO_FRAC(srcA->x); srcPt.y = WARP_TO_FRAC(srcA->y); h = abs(dstB->y - dstA->y) + 1; dstStep.x = WARP_TO_FRAC((dstB->x - dstA->x)) / h; srcStep.x = WARP_TO_FRAC((srcB->x - srcA->x)) / h; srcStep.y = WARP_TO_FRAC((srcB->y - srcA->y)) / h; spanPtr = &st->spans[(dstA->y - st->dstMinPt.y)]; if (srcA->x <= srcB->x) { lx1 = srcA->x; lx2 = srcB->x; } else { lx1 = srcB->x; lx2 = srcA->x; } if (srcA->y <= srcB->y) { ly1 = srcA->y; ly2 = srcB->y; } else { ly1 = srcB->y; ly2 = srcA->y; } for (int i = h; --i >= 0;) { dpt.x = WARP_FROM_FRAC(dstPt.x + (WARP_FRAC_VALUE / 2)); spt.x = MAX(lx1, MIN(lx2, WARP_FROM_FRAC(srcPt.x + (WARP_FRAC_VALUE / 2)))); spt.y = MAX(ly1, MIN(ly2, WARP_FROM_FRAC(srcPt.y + (WARP_FRAC_VALUE / 2)))); if (dpt.x < spanPtr->dstLeft) { spanPtr->dstLeft = dpt.x; spanPtr->srcLeft = spt; } if (dpt.x > spanPtr->dstRight) { spanPtr->dstRight = dpt.x; spanPtr->srcRight = spt; } dstPt.x += dstStep.x; srcPt.x += srcStep.x; srcPt.y += srcStep.y; if (dstA->y < dstB->y) { spanPtr++; } else { spanPtr--; } } } void Wiz::warpProcessDrawSpansSampled(WizSimpleBitmap *dstBitmap, const WizSimpleBitmap *srcBitmap, const WarpWizOneDrawSpan *drawSpans, int count) { // Setup read pointer and clipping limits for the sampling rect... const WizRawPixel8 *src8 = (WizRawPixel8 *)srcBitmap->bufferPtr(); const WizRawPixel16 *src16 = (WizRawPixel16 *)srcBitmap->bufferPtr(); WizRawPixel8 *dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); WizRawPixel16 *dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); int sw = srcBitmap->bitmapWidth; int srcXLimit = (srcBitmap->bitmapWidth - 1); int srcYLimit = (srcBitmap->bitmapHeight - 1); // Process all of the spans in this span collection... for (int yCounter = count; --yCounter >= 0;) { if (!_uses16BitColor) { dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); dst8 += drawSpans->dstOffset; } else { dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); dst16 += drawSpans->dstOffset; } int xOffset = drawSpans->xSrcOffset; int yOffset = drawSpans->ySrcOffset; int xStep = drawSpans->xSrcStep; int yStep = drawSpans->ySrcStep; // Figure out the sample rect for this "slope"... int sampleCx = (WARP_FROM_FRAC(xStep)); int sampleCy = (WARP_FROM_FRAC(yStep)); if (sampleCx < 0) { sampleCx = -sampleCx; } if (sampleCy < 0) { sampleCy = -sampleCy; } // Make the sampling area square using the largest delta... if (sampleCx > sampleCy) { sampleCy = sampleCx; } else { sampleCx = sampleCy; } int sampleXOffset = sampleCx; int sampleYOffset = sampleCy; // Process all pixels covered by this "span"... for (int xCounter = drawSpans->dstWidth; --xCounter >= 0;) { if (!_uses16BitColor) { *dst8++ = (*(src8 + (sw * WARP_FROM_FRAC(yOffset)) + WARP_FROM_FRAC(xOffset))); } else { // Do a really lame sampling of the potential source pixels... int sx = WARP_FROM_FRAC(xOffset); int sy = WARP_FROM_FRAC(yOffset); int sx1 = sx; int sy1 = sy; int sx2 = sx + sampleXOffset; int sy2 = sy + sampleYOffset; // Clip the source sample coords to the bitmap limits... sx1 = MAX(0, MIN(srcXLimit, sx1)); sy1 = MAX(0, MIN(srcYLimit, sy1)); sx2 = MAX(0, MIN(srcXLimit, sx2)); sy2 = MAX(0, MIN(srcYLimit, sy2)); // Now that the clipping is done figure out the sampling area... int sxc = ((sx2 - sx1) + 1); int syc = ((sy2 - sy1) + 1); int total = (sxc * syc); // Sample pixels from the source potential sampling area... if (total > 1) { int totalR, totalG, totalB; const WizRawPixel *samplePtr = (const WizRawPixel *)(src16 + ((sw * sy1) + sx1)); rawPixelExtractComponents(*samplePtr, totalR, totalG, totalB); int sampleStep = sw - sxc; ++total; for (sy = 0; sy < syc; sy++) { for (sx = 0; sx < sxc; sx++) { int r, g, b; WizRawPixel srcColor = *samplePtr++; rawPixelExtractComponents(srcColor, r, g, b); totalR += r; totalG += g; totalB += b; } samplePtr += sampleStep; } rawPixelPackComponents(*dst16, totalR / total, totalG / total, totalB / total); } else { *dst16 = (*(src16 + (sw * sy1) + sx1)); } } xOffset += xStep; yOffset += yStep; } drawSpans++; } } void Wiz::warpProcessDrawSpansTransparentSampled(WizSimpleBitmap *dstBitmap, const WizSimpleBitmap *srcBitmap, const WarpWizOneDrawSpan *drawSpans, int count, WizRawPixel transparentColor) { // Setup read pointer and clipping limits for the sampling rect... const WizRawPixel8 *src8 = (WizRawPixel8 *)srcBitmap->bufferPtr(); const WizRawPixel16 *src16 = (WizRawPixel16 *)srcBitmap->bufferPtr(); WizRawPixel8 *dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); WizRawPixel16 *dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); int sw = srcBitmap->bitmapWidth; int srcXLimit = (srcBitmap->bitmapWidth - 1); int srcYLimit = (srcBitmap->bitmapHeight - 1); // Process all of the spans in this span collection... for (int yCounter = count; --yCounter >= 0;) { if (!_uses16BitColor) { dst8 = (WizRawPixel8 *)dstBitmap->bufferPtr(); dst8 += drawSpans->dstOffset; } else { dst16 = (WizRawPixel16 *)dstBitmap->bufferPtr(); dst16 += drawSpans->dstOffset; } int xOffset = drawSpans->xSrcOffset; int yOffset = drawSpans->ySrcOffset; int xStep = drawSpans->xSrcStep; int yStep = drawSpans->ySrcStep; // Figure out the sample rect for this "slope"... int sampleCx = (WARP_FROM_FRAC(xStep)); int sampleCy = (WARP_FROM_FRAC(yStep)); if (sampleCx < 0) { sampleCx = -sampleCx; } if (sampleCy < 0) { sampleCy = -sampleCy; } // Make the sampling area square using the largest delta... if (sampleCx > sampleCy) { sampleCy = sampleCx; } else { sampleCx = sampleCy; } int sampleXOffset = sampleCx; int sampleYOffset = sampleCy; // Process all pixels covered by this "span"... for (int xCounter = drawSpans->dstWidth; --xCounter >= 0;) { if (!_uses16BitColor) { WizRawPixel srcColor = (*(src8 + (sw * WARP_FROM_FRAC(yOffset)) + WARP_FROM_FRAC(xOffset))); if (srcColor != transparentColor) { *dst8++ = srcColor; } else { dst8++; } } else { // Do a really lame sampling of the potential source pixels... int sx = WARP_FROM_FRAC(xOffset); int sy = WARP_FROM_FRAC(yOffset); int sx1 = sx; int sy1 = sy; int sx2 = sx + sampleXOffset; int sy2 = sy + sampleYOffset; // Clip the source sample coords to the bitmap limits... sx1 = MAX(0, MIN(srcXLimit, sx1)); sy1 = MAX(0, MIN(srcYLimit, sy1)); sx2 = MAX(0, MIN(srcXLimit, sx2)); sy2 = MAX(0, MIN(srcYLimit, sy2)); // Now that the clipping is done figure out the sampling area... int sxc = ((sx2 - sx1) + 1); int syc = ((sy2 - sy1) + 1); int total = (sxc * syc); // Sample pixels from the source potential sampling area... if (total > 1) { int totalR, totalG, totalB; const WizRawPixel *samplePtr = (const WizRawPixel *)(src16 + ((sw * sy1) + sx1)); WizRawPixel firstColor = (*(src16 + (sw * sy1) + sx1)); if (firstColor != transparentColor) { rawPixelExtractComponents(*samplePtr, totalR, totalG, totalB); ++total; } else { totalR = 0; totalG = 0; totalB = 0; } int sampleStep = sw - sxc; for (sy = 0; sy < syc; sy++) { for (sx = 0; sx < sxc; sx++) { WizRawPixel srcColor = *samplePtr++; if (srcColor != transparentColor) { int r, g, b; rawPixelExtractComponents(srcColor, r, g, b); totalR += r; totalG += g; totalB += b; } else { --total; } } samplePtr += sampleStep; } if (total) { WizRawPixel srcColor; rawPixelPackComponents(srcColor, totalR / total, totalG / total, totalB / total); if (transparentColor != srcColor) { *dst16 = srcColor; } else { *dst16 = 4; } } } else { WizRawPixel srcColor = (*(src16 + (sw * sy1) + sx1)); if (srcColor != transparentColor) { *dst16 = srcColor; } } dst16++; } xOffset += xStep; yOffset += yStep; } drawSpans++; } } bool Wiz::warpNPt2NPtWarpCORE(WizSimpleBitmap *dstBitmap, const WarpWizPoint *dstpoints, const WizSimpleBitmap *srcBitmap, const WarpWizPoint *srcpoints, int npoints, int transparentColor, const Common::Rect *optionalClipRect, int32 wizFlags) { WarpWizOneSpanTable *st; if ((srcBitmap->bitmapWidth >= WARP_TEXTURE_LIMIT) || (srcBitmap->bitmapHeight >= WARP_TEXTURE_LIMIT)) { error("Wiz::warpNPt2NPtWarpCORE(): Source bitmap (%d,%d) too big limit (%d,%d)", srcBitmap->bitmapWidth, srcBitmap->bitmapHeight, WARP_TEXTURE_LIMIT, WARP_TEXTURE_LIMIT); } st = warpBuildSpanTable(dstBitmap, srcBitmap, dstpoints, srcpoints, npoints, optionalClipRect); if (st) { if (st->drawSpanCount) { if (transparentColor != -1) { if (wizFlags & kWRFAreaSampleDuringWarp) { warpProcessDrawSpansTransparentSampled( dstBitmap, srcBitmap, st->drawSpans, st->drawSpanCount, (WizRawPixel)transparentColor); } else { warpProcessDrawSpansTransparent( dstBitmap, srcBitmap, st->drawSpans, st->drawSpanCount, (WizRawPixel)transparentColor); } } else { if (wizFlags & kWRFAreaSampleDuringWarp) { warpProcessDrawSpansSampled( dstBitmap, srcBitmap, st->drawSpans, st->drawSpanCount); } else { warpProcessDrawSpansA( dstBitmap, srcBitmap, st->drawSpans, st->drawSpanCount); } } } warpDestroySpanTable(st); return true; } warning("Wiz::warpNPt2NPtWarpCORE(): Unable to warp bitmap"); return false; } bool Wiz::warpNPt2NPtNonClippedWarp(WizSimpleBitmap *dstBitmap, const WarpWizPoint *dstpoints, const WizSimpleBitmap *srcBitmap, const WarpWizPoint *srcpoints, int npoints, int transparentColor) { if (_vm->_game.heversion > 99) { return warpNPt2NPtWarpCORE( dstBitmap, dstpoints, srcBitmap, srcpoints, npoints, transparentColor, nullptr, 0); } else { WarpWizOneSpanTable *st; if ((srcBitmap->bitmapWidth >= WARP_TEXTURE_LIMIT) || (srcBitmap->bitmapHeight >= WARP_TEXTURE_LIMIT)) { error("Wiz::warpNPt2NPtNonClippedWarp(): Source bitmap (%d,%d) too big limit (%d,%d)", srcBitmap->bitmapWidth, srcBitmap->bitmapHeight, WARP_TEXTURE_LIMIT, WARP_TEXTURE_LIMIT); } st = warpBuildSpanTable(dstBitmap, srcBitmap, dstpoints, srcpoints, npoints, nullptr); if (st) { if (st->drawSpanCount) { if (transparentColor != -1) { warpProcessDrawSpansTransparent( dstBitmap, srcBitmap, st->drawSpans, st->drawSpanCount, (WizRawPixel)transparentColor); } else { warpProcessDrawSpansA(dstBitmap, srcBitmap, st->drawSpans, st->drawSpanCount); } } warpDestroySpanTable(st); return true; } else { warning("Wiz::warpNPt2NPtNonClippedWarp(): Unable to warp bitmap"); return false; } } } bool Wiz::warpNPt2NPtClippedWarp(WizSimpleBitmap *dstBitmap, const WarpWizPoint *dstpoints, const WizSimpleBitmap *srcBitmap, const WarpWizPoint *srcpoints, int npoints, int transparentColor, const Common::Rect *optionalClipRect) { if (_vm->_game.heversion > 99) { return warpNPt2NPtWarpCORE( dstBitmap, dstpoints, srcBitmap, srcpoints, npoints, transparentColor, nullptr, 0); } else { WarpWizOneSpanTable *st; if ((srcBitmap->bitmapWidth >= WARP_TEXTURE_LIMIT) || (srcBitmap->bitmapHeight >= WARP_TEXTURE_LIMIT)) { error("Wiz::warpNPt2NPtClippedWarp(): Source bitmap (%d,%d) too big limit (%d,%d)", srcBitmap->bitmapWidth, srcBitmap->bitmapHeight, WARP_TEXTURE_LIMIT, WARP_TEXTURE_LIMIT); } st = warpBuildSpanTable(dstBitmap, srcBitmap, dstpoints, srcpoints, npoints, optionalClipRect); if (st) { if (st->drawSpanCount) { if (transparentColor != -1) { warpProcessDrawSpansTransparent( dstBitmap, srcBitmap, st->drawSpans, st->drawSpanCount, (WizRawPixel)transparentColor); } else { warpProcessDrawSpansA(dstBitmap, srcBitmap, st->drawSpans, st->drawSpanCount); } } warpDestroySpanTable(st); return true; } else { warning("Wiz::warpNPt2NPtClippedWarp(): Unable to warp bitmap"); return false; } } } bool Wiz::warpNPt2NPtClippedWarpMixColors(WizSimpleBitmap *dstBitmap, const WarpWizPoint *dstpoints, const WizSimpleBitmap *srcBitmap, const WarpWizPoint *srcpoints, int npoints, int transparentColor, const Common::Rect *optionalClipRect, const byte *colorMixTable) { WarpWizOneSpanTable *st; if ((srcBitmap->bitmapWidth >= WARP_TEXTURE_LIMIT) || (srcBitmap->bitmapHeight >= WARP_TEXTURE_LIMIT)) { error("Wiz::warpNPt2NPtClippedWarpMixColors(): Source bitmap (%d,%d) too big limit (%d,%d)", srcBitmap->bitmapWidth, srcBitmap->bitmapHeight, WARP_TEXTURE_LIMIT, WARP_TEXTURE_LIMIT); } st = warpBuildSpanTable( dstBitmap, srcBitmap, dstpoints, srcpoints, npoints, optionalClipRect); if (st) { if (st->drawSpanCount) { warpProcessDrawSpansMixColors( dstBitmap, srcBitmap, st->drawSpans, st->drawSpanCount, transparentColor, colorMixTable); } warpDestroySpanTable(st); return true; } else { warning("Wiz::warpNPt2NPtClippedWarpMixColors(): Unable to warp bitmap"); return false; } } bool Wiz::warpNPt2NPtNonClippedWarpFiltered(WizSimpleBitmap *dstBitmap, const WarpWizPoint *dstpoints, const WizSimpleBitmap *srcBitmap, const WarpWizPoint *srcpoints, int npoints, int transparentColor, const byte *pXmapColorTable, bool bIsHintColor, WizRawPixel hintColor) { WarpWizOneSpanTable *st; if ((srcBitmap->bitmapWidth >= WARP_TEXTURE_LIMIT) || (srcBitmap->bitmapHeight >= WARP_TEXTURE_LIMIT)) { error("Wiz::warpNPt2NPtNonClippedWarpFiltered(): Source bitmap (%d,%d) too big limit (%d,%d)", srcBitmap->bitmapWidth, srcBitmap->bitmapHeight, WARP_TEXTURE_LIMIT, WARP_TEXTURE_LIMIT); } st = warpBuildSpanTable(dstBitmap, srcBitmap, dstpoints, srcpoints, npoints, 0); if (st) { if (st->drawSpanCount) { warpProcessDrawSpansTransparentFiltered(dstBitmap, srcBitmap, st->drawSpans, st->drawSpanCount,(WizRawPixel)transparentColor, pXmapColorTable, bIsHintColor, hintColor); } warpDestroySpanTable(st); return true; } else { warning("Wiz::warpNPt2NPtNonClippedWarpFiltered(): Unable to warp bitmap"); return false; } } } // End of namespace Scumm #endif // ENABLE_HE