diff options
Diffstat (limited to 'scumm/wiz_he.cpp')
-rw-r--r-- | scumm/wiz_he.cpp | 1392 |
1 files changed, 1392 insertions, 0 deletions
diff --git a/scumm/wiz_he.cpp b/scumm/wiz_he.cpp new file mode 100644 index 0000000000..6e68454a7d --- /dev/null +++ b/scumm/wiz_he.cpp @@ -0,0 +1,1392 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2004 The ScummVM project + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "stdafx.h" + +#include "scumm/intern.h" +#include "scumm/resource.h" +#include "scumm/scumm.h" +#include "scumm/wiz_he.h" + +namespace Scumm { + +Wiz::Wiz() { + _imagesNum = 0; + memset(&_images, 0, sizeof(_images)); + memset(&_polygons, 0, sizeof(_polygons)); +} + +void Wiz::polygonStore(int id, bool flag, int vert1x, int vert1y, int vert2x, int vert2y, int vert3x, int vert3y, int vert4x, int vert4y) { + WizPolygon *wp = NULL; + for (int i = 0; i < ARRAYSIZE(_polygons); ++i) { + if (_polygons[i].id == 0) { + wp = &_polygons[i]; + break; + } + } + if (!wp) { + error("Wiz::polygonStore: out of polygon slot, max = %d", ARRAYSIZE(_polygons)); + } + + wp->vert[0].x = vert1x; + wp->vert[0].y = vert1y; + wp->vert[1].x = vert2x; + wp->vert[1].y = vert2y; + wp->vert[2].x = vert3x; + wp->vert[2].y = vert3y; + wp->vert[3].x = vert4x; + wp->vert[3].y = vert4y; + wp->vert[4].x = vert1x; + wp->vert[4].y = vert1y; + wp->id = id; + wp->numVerts = 5; + wp->flag = flag; + + wp->bound.left = 10000; + wp->bound.top = 10000; + wp->bound.right = -10000; + wp->bound.bottom = -10000; + + // compute bounding box + for (int j = 0; j < wp->numVerts; j++) { + Common::Rect r(wp->vert[j].x, wp->vert[j].y, wp->vert[j].x + 1, wp->vert[j].y + 1); + wp->bound.extend(r); + } +} + +void Wiz::polygonErase(int fromId, int toId) { + for (int i = 0; i < ARRAYSIZE(_polygons); i++) { + if (_polygons[i].id >= fromId && _polygons[i].id <= toId) + memset(&_polygons[i], 0, sizeof(WizPolygon)); + } +} + +int Wiz::polygonHit(int id, int x, int y) { + for (int i = 0; i < ARRAYSIZE(_polygons); i++) { + if ((id == 0 || _polygons[i].id == id) && _polygons[i].bound.contains(x, y)) { + if (polygonContains(_polygons[i], x, y)) { + return _polygons[i].id; + } + } + } + return 0; +} + +bool Wiz::polygonDefined(int id) { + for (int i = 0; i < ARRAYSIZE(_polygons); i++) + if (_polygons[i].id == id) + return true; + return false; +} + +bool Wiz::polygonContains(const WizPolygon &pol, int x, int y) { + int pi = pol.numVerts - 1; + bool diry = (y < pol.vert[pi].y); + bool curdir; + bool r = false; + + for (int i = 0; i < pol.numVerts; i++) { + curdir = (y <= pol.vert[i].y); + + if (curdir != diry) { + if (((pol.vert[pi].y - pol.vert[i].y) * (pol.vert[i].x - x) <= + (pol.vert[pi].x - pol.vert[i].x) * (pol.vert[i].y - y)) == diry) + r = !r; + } + + pi = i; + diry = curdir; + } + + return r; +} + +void Wiz::copyAuxImage(uint8 *dst1, uint8 *dst2, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch) { + Common::Rect dstRect(srcx, srcy, srcx + srcw, srcy + srch); + dstRect.clip(dstw, dsth); + + int rw = dstRect.width(); + int rh = dstRect.height(); + if (rh <= 0 || rw <= 0) + return; + + uint8 *dst1Ptr = dst1 + dstRect.left + dstRect.top * dstw; + uint8 *dst2Ptr = dst2 + dstRect.left + dstRect.top * dstw; + const uint8 *dataPtr = src; + + while (rh--) { + uint16 off = READ_LE_UINT16(dataPtr); dataPtr += 2; + const uint8 *dataPtrNext = off + dataPtr; + uint8 *dst1PtrNext = dst1Ptr + dstw; + uint8 *dst2PtrNext = dst2Ptr + dstw; + if (off != 0) { + int w = rw; + while (w > 0) { + uint8 code = *dataPtr++; + if (code & 1) { + code >>= 1; + dst1Ptr += code; + dst2Ptr += code; + w -= code; + } else if (code & 2) { + code = (code >> 2) + 1; + w -= code; + if (w >= 0) { + memset(dst1Ptr, *dataPtr++, code); + dst1Ptr += code; + dst2Ptr += code; + } else { + code += w; + memset(dst1Ptr, *dataPtr, code); + } + } else { + code = (code >> 2) + 1; + w -= code; + if (w >= 0) { + memcpy(dst1Ptr, dst2Ptr, code); + dst1Ptr += code; + dst2Ptr += code; + } else { + code += w; + memcpy(dst1Ptr, dst2Ptr, code); + } + } + } + } + dataPtr = dataPtrNext; + dst1Ptr = dst1PtrNext; + dst2Ptr = dst2PtrNext; + } +} + +static bool calcClipRects(int dst_w, int dst_h, int src_x, int src_y, int src_w, int src_h, const Common::Rect *rect, Common::Rect &srcRect, Common::Rect &dstRect) { + Common::Rect r3; + if (rect) { + r3 = *rect; + Common::Rect r4(dst_w, dst_h); + if (r3.intersects(r4)) { + r3.clip(r4); + } else { + return false; + } + } else { + r3 = Common::Rect(dst_w, dst_h); + } + dstRect = Common::Rect(src_x, src_y, src_x + src_w, src_y + src_h); + dstRect.clip(r3); + srcRect = dstRect; + srcRect.moveTo(0, 0); + return srcRect.isValidRect() && dstRect.isValidRect(); +} + +void Wiz::copyWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect) { + Common::Rect r1, r2; + if (calcClipRects(dstw, dsth, srcx, srcy, srcw, srch, rect, r1, r2)) { + dst += r2.left + r2.top * dstw; + decompressWizImage(dst, dstw, r2, src, r1); + } +} + +void Wiz::copyRawWizImage(uint8 *dst, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr, int transColor) { + Common::Rect r1, r2; + if (calcClipRects(dstw, dsth, srcx, srcy, srcw, srch, rect, r1, r2)) { + if (flags & 0x400) { + int l = r1.left; + int r = r1.right; + r1.left = srcw - r; + r1.right = srcw - l; + } + if (flags & 0x800) { + int t = r1.top; + int b = r1.bottom; + r1.top = srch - b; + r1.bottom = srch - t; + } + byte imagePal[256]; + if (!palPtr) { + for (int i = 0; i < 256; i++) { + imagePal[i] = i; + } + palPtr = imagePal; + } + int h = r1.height(); + int w = r1.width(); + dst += r2.left + r2.top * dstw; + while (h--) { + for (int i = 0; i < w; ++i) { + uint8 col = *src++; + if (transColor == -1 || transColor != col) { + dst[i] = palPtr[col]; + } + } + dst += dstw; + } + } +} + +void Wiz::decompressWizImage(uint8 *dst, int dstPitch, const Common::Rect &dstRect, const uint8 *src, const Common::Rect &srcRect, const uint8 *imagePal) { + const uint8 *dataPtr, *dataPtrNext; + uint8 *dstPtr, *dstPtrNext; + uint32 code; + uint8 databit; + int h, w, xoff; + uint16 off; + + dstPtr = dst; + dataPtr = src; + + // Skip over the first 'srcRect->top' lines in the data + h = srcRect.top; + while (h--) { + dataPtr += READ_LE_UINT16(dataPtr) + 2; + } + h = srcRect.height(); + w = srcRect.width(); + if (h <= 0 || w <= 0) + return; + + while (h--) { + xoff = srcRect.left; + off = READ_LE_UINT16(dataPtr); + w = srcRect.right - srcRect.left; + dstPtrNext = dstPitch + dstPtr; + dataPtrNext = off + 2 + dataPtr; + dataPtr += 2; + if (off == 0) + goto dec_next; + + // Skip over the leftmost 'srcRect->left' pixels. + // TODO: This code could be merged (at a loss of efficency) with the + // loop below which does the actual drawing. + while (xoff > 0) { + code = *dataPtr++; + databit = code & 1; + code >>= 1; + if (databit) { + xoff -= code; + if (xoff < 0) { + code = -xoff; + goto dec_sub1; + } + } else { + databit = code & 1; + code = (code >> 1) + 1; + if (databit) { + ++dataPtr; + xoff -= code; + if (xoff < 0) { + code = -xoff; + --dataPtr; + goto dec_sub2; + } + } else { + dataPtr += code; + xoff -= code; + if (xoff < 0) { + dataPtr += xoff; + code = -xoff; + goto dec_sub3; + } + } + } + } + + while (w > 0) { + code = *dataPtr++; + databit = code & 1; + code >>= 1; + if (databit) { +dec_sub1: dstPtr += code; + w -= code; + } else { + databit = code & 1; + code = (code >> 1) + 1; + if (databit) { +dec_sub2: w -= code; + if (w < 0) { + code += w; + } + uint8 color = *dataPtr++; + if (imagePal) { + color = imagePal[color]; + } + memset(dstPtr, color, code); + dstPtr += code; + } else { +dec_sub3: w -= code; + if (w < 0) { + code += w; + } + if (imagePal) { + while (code--) { + *dstPtr++ = imagePal[*dataPtr++]; + } + } else { + memcpy(dstPtr, dataPtr, code); + dstPtr += code; + dataPtr += code; + } + } + } + } +dec_next: + dataPtr = dataPtrNext; + dstPtr = dstPtrNext; + } +} + +int Wiz::isWizPixelNonTransparent(const uint8 *data, int x, int y, int w, int h) { + int ret = 0; + while (y != 0) { + data += READ_LE_UINT16(data) + 2; + --y; + } + uint16 off = READ_LE_UINT16(data); data += 2; + if (off != 0) { + if (x == 0) { + ret = (~*data) & 1; + } else { + do { + uint8 code = *data++; + if (code & 1) { + code >>= 1; + if (code > x) { + ret = 0; + break; + } + x -= code; + } else if (code & 2) { + code = (code >> 2) + 1; + if (code > x) { + ret = 1; + break; + } + x -= code; + ++data; + } else { + code = (code >> 2) + 1; + if (code > x) { + ret = 1; + break; + } + x -= code; + data += code; + } + } while (x > 0); + } + } + return ret; +} + +uint8 Wiz::getWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 color) { + uint8 c = color; + if (x >= 0 && x < w && y >= 0 && y < h) { + while (y != 0) { + data += READ_LE_UINT16(data) + 2; + --y; + } + uint16 off = READ_LE_UINT16(data); data += 2; + if (off != 0) { + if (x == 0) { + c = (*data & 1) ? color : *data; + } else { + do { + uint8 code = *data++; + if (code & 1) { + code >>= 1; + if (code > x) { + c = color; + break; + } + x -= code; + } else if (code & 2) { + code = (code >> 2) + 1; + if (code > x) { + c = *data; + break; + } + x -= code; + ++data; + } else { + code = (code >> 2) + 1; + if (code > x) { + c = *(data + x); + break; + } + x -= code; + data += code; + } + } while (x > 0); + } + } + } + return c; +} + +uint8 Wiz::getRawWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 color) { + uint8 c; + if (x >= 0 && x < w && y >= 0 && y < h) { + c = data[y * w + x]; + } else { + c = color; + } + return c; +} + +void Wiz::computeWizHistogram(uint32 *histogram, const uint8 *data, const Common::Rect *srcRect) { + int y = srcRect->top; + while (y != 0) { + data += READ_LE_UINT16(data) + 2; + --y; + } + int ih = srcRect->height(); + while (ih--) { + uint16 off = READ_LE_UINT16(data); data += 2; + if (off != 0) { + const uint8 *p = data; + int x1 = srcRect->left; + int x2 = srcRect->right; + uint8 code; + while (x1 > 0) { + code = *p++; + if (code & 1) { + code >>= 1; + if (code > x1) { + code -= x1; + x2 -= code; + break; + } + x1 -= code; + } else if (code & 2) { + code = (code >> 2) + 1; + if (code > x1) { + code -= x1; + goto dec_sub2; + } + x1 -= code; + ++p; + } else { + code = (code >> 2) + 1; + if (code > x1) { + code -= x1; + p += x1; + goto dec_sub3; + } + x1 -= code; + p += code; + } + } + while (x2 > 0) { + code = *p++; + if (code & 1) { + code >>= 1; + x2 -= code; + } else if (code & 2) { + code = (code >> 2) + 1; +dec_sub2: x2 -= code; + if (x2 < 0) { + code += x2; + } + histogram[*p++] += code; + } else { + code = (code >> 2) + 1; +dec_sub3: x2 -= code; + if (x2 < 0) { + code += x2; + } + int n = code; + while (n--) { + ++histogram[*p++]; + } + } + } + data += off; + } + } +} + +void Wiz::computeRawWizHistogram(uint32 *histogram, const uint8 *data, int srcPitch, const Common::Rect *srcRect) { + data += srcRect->top * srcPitch + srcRect->left; + int iw = srcRect->width(); + int ih = srcRect->height(); + while (ih--) { + for (int i = 0; i < iw; ++i) { + ++histogram[data[i]]; + } + data += srcPitch; + } +} + +struct wizPackCtx { + uint32 len; + uint8 saveCode; + uint8 saveBuf[0x100]; +}; + +static void wizPackType1Helper1(uint8 *&dst, int len, byte newColor, byte prevColor, wizPackCtx *ctx) { + assert(len > 0); + if (newColor == prevColor) { + do { + int blockLen = MIN(len, 0x7F); + len -= blockLen; + if (dst) { + *dst++ = (blockLen * 2) | 1; + } + ++ctx->len; + } while (len > 0); + } else { + do { + int blockLen = MIN(len, 0x40); + len -= blockLen; + if (dst) { + *dst++ = ((blockLen - 1) * 4) | 2; + } + ++ctx->len; + if (dst) { + *dst++ = newColor; + } + ++ctx->len; + } while (len > 0); + } +} + +static void wizPackType1Helper2(uint8 *&dst, int len, wizPackCtx *ctx) { + assert(len > 0); + const uint8 *src = ctx->saveBuf; + do { + int blockLen = MIN(len, 0x40); + len -= blockLen; + if (dst) { + *dst++ = (blockLen - 1) * 4; + } + ++ctx->len; + while (blockLen--) { + if (dst) { + *dst++ = *src++; + } + ++ctx->len; + } + } while (len > 0); +} + +static int wizPackType1(uint8 *dst, const uint8 *src, int srcPitch, const Common::Rect& rCapt, uint8 tColor) { + debug(1, "wizPackType1(%d, [%d,%d,%d,%d])", tColor, rCapt.left, rCapt.top, rCapt.right, rCapt.bottom); + wizPackCtx ctx; + memset(&ctx, 0, sizeof(ctx)); + + src += rCapt.top * srcPitch + rCapt.left; + int w = rCapt.width(); + int h = rCapt.height(); + + uint8 *nextDstPtr, *curDstPtr; + uint8 curColor, prevColor; + int saveBufPos; + + nextDstPtr = curDstPtr = 0; + + int dataSize = 0; + while (h--) { + if (dst) { + curDstPtr = dst; + nextDstPtr = dst; + dst += 2; + } + dataSize += 2; + int numBytes = 0; + + int i, code; + for (i = 0; i < w; ++i) { + if (src[i] != tColor) + break; + } + if (i != w) { + curDstPtr = dst; + ctx.len = 0; + prevColor = ctx.saveBuf[0] = *src; + const uint8 *curSrcPtr = src + 1; + saveBufPos = 1; + code = (tColor - ctx.saveBuf[0] == 0) ? 1 : 0; + int curw = w; + while (curw--) { + ctx.saveBuf[saveBufPos] = curColor = *curSrcPtr++; + ++saveBufPos; + if (code == 0) { + if (curColor == tColor) { + --saveBufPos; + wizPackType1Helper2(curDstPtr, saveBufPos, &ctx); + code = saveBufPos = 1; + ctx.saveBuf[0] = curColor; + numBytes = 0; + prevColor = curColor; + continue; + } + if (saveBufPos > 0x80) { + --saveBufPos; + wizPackType1Helper2(curDstPtr, saveBufPos, &ctx); + saveBufPos = 1; + ctx.saveBuf[0] = curColor; + numBytes = 0; + prevColor = curColor; + continue; + } + if (prevColor != curColor) { + numBytes = saveBufPos - 1; + prevColor = curColor; + continue; + } + code = 1; + if (numBytes != 0) { + if (saveBufPos - numBytes < 3) { + code = 0; + } else { + wizPackType1Helper2(curDstPtr, numBytes, &ctx); + } + } + } + if (prevColor != curColor || saveBufPos - numBytes > 0x80) { + saveBufPos -= numBytes; + --saveBufPos; + wizPackType1Helper1(curDstPtr, saveBufPos, prevColor, tColor, &ctx); + saveBufPos = 1; + numBytes = 0; + ctx.saveBuf[0] = curColor; + code = (tColor - ctx.saveBuf[0] == 0) ? 1 : 0; + } + prevColor = curColor; + } + if (code == 0) { + wizPackType1Helper2(curDstPtr, saveBufPos, &ctx); + } else { + saveBufPos -= numBytes; + wizPackType1Helper1(curDstPtr, saveBufPos, prevColor, tColor, &ctx); + } + dataSize += ctx.len; + src += srcPitch; + if (dst) { + dst += ctx.len; + *(uint16 *)nextDstPtr = TO_LE_16(ctx.len); + } + } + } + return dataSize; +} + +static int wizPackType0(uint8 *dst, const uint8 *src, int srcPitch, const Common::Rect& rCapt, uint8 tColor) { + int w = rCapt.width(); + int h = rCapt.height(); + int size = w * h; + if (dst) { + src += rCapt.top * srcPitch + rCapt.left; + while (h--) { + memcpy(dst, src, w); + dst += w; + src += srcPitch; + } + } + return size; +} + +void ScummEngine_v72he::captureWizImage(int resType, int resNum, const Common::Rect& r, bool frontBuffer, int compType) { + debug(1, "ScummEngine_v72he::captureWizImage(%d, %d, %d, [%d,%d,%d,%d])", resType, resNum, compType, r.left, r.top, r.right, r.bottom); + uint8 *src = NULL; + VirtScreen *pvs = &virtscr[kMainVirtScreen]; + if (frontBuffer) { + src = pvs->getPixels(0, 0); + } else { + src = pvs->getBackPixels(0, 0); + } + Common::Rect rCapt(0, 0, pvs->w, pvs->h); + if (rCapt.intersects(r)) { + rCapt.clip(r); + const uint8 *palPtr = _currentPalette; + + int w = rCapt.width(); + int h = rCapt.height(); + int tColor = (VAR_WIZ_TCOLOR != 0xFF) ? VAR(VAR_WIZ_TCOLOR) : 5; + + // compute compressed size + int dataSize = 0; + int headerSize = palPtr ? 1080 : 36; + switch (compType) { + case 1: + dataSize = wizPackType1(0, src, pvs->pitch, rCapt, tColor); + break; + case 0: + dataSize = wizPackType0(0, src, pvs->pitch, rCapt, tColor); + break; + default: + warning("unhandled compression type %d", compType); + break; + } + + // alignment + dataSize = (dataSize + 1) & ~1; + int wizSize = headerSize + dataSize; + // write header + uint8 *wizImg = createResource(resType, resNum, dataSize + headerSize); + *(uint32 *)(wizImg + 0x00) = MKID('AWIZ'); + *(uint32 *)(wizImg + 0x04) = TO_BE_32(wizSize); + *(uint32 *)(wizImg + 0x08) = MKID('WIZH'); + *(uint32 *)(wizImg + 0x0C) = TO_BE_32(0x14); + *(uint32 *)(wizImg + 0x10) = TO_LE_32(compType); + *(uint32 *)(wizImg + 0x14) = TO_LE_32(w); + *(uint32 *)(wizImg + 0x18) = TO_LE_32(h); + int curSize = 0x1C; + if (palPtr) { + *(uint32 *)(wizImg + 0x1C) = MKID('RGBS'); + *(uint32 *)(wizImg + 0x20) = TO_BE_32(0x308); + memcpy(wizImg + 0x24, palPtr, 0x300); + *(uint32 *)(wizImg + 0x324) = MKID('RMAP'); + *(uint32 *)(wizImg + 0x328) = TO_BE_32(0x10C); + *(uint32 *)(wizImg + 0x32C) = 0; + curSize = 0x330; + for (int i = 0; i < 256; ++i) { + wizImg[curSize] = i; + ++curSize; + } + } + *(uint32 *)(wizImg + curSize + 0x0) = MKID('WIZD'); + *(uint32 *)(wizImg + curSize + 0x4) = TO_BE_32(dataSize + 8); + curSize += 8; + + // write compressed data + switch (compType) { + case 1: + wizPackType1(wizImg + headerSize, src, pvs->pitch, rCapt, tColor); + break; + case 0: + wizPackType0(wizImg + headerSize, src, pvs->pitch, rCapt, tColor); + break; + default: + break; + } + } +} + +void ScummEngine_v72he::displayWizImage(const WizImage *pwi) { + if (_fullRedraw) { + assert(_wiz._imagesNum < ARRAYSIZE(_wiz._images)); + memcpy(&_wiz._images[_wiz._imagesNum], pwi, sizeof(WizImage)); + ++_wiz._imagesNum; + } else if (pwi->flags & 0x40) { + drawWizPolygon(pwi->resNum, pwi->state, pwi->x1, pwi->flags); + } else { + drawWizImage(rtImage, pwi); + } +} + +void ScummEngine_v72he::getWizImageDim(int resnum, int state, int32 &w, int32 &h) { + const uint8 *dataPtr = getResourceAddress(rtImage, resnum); + if (dataPtr) { + const uint8 *wizh = findWrappedBlock(MKID('WIZH'), dataPtr, state, 0); + w = READ_LE_UINT32(wizh + 0x4); + h = READ_LE_UINT32(wizh + 0x8); + } else { + w = 0; + h = 0; + } +} + +uint8 *ScummEngine_v72he::drawWizImage(int restype, const WizImage *pwi) { + debug(1, "drawWizImage(%d, %d, %d, %d, 0x%X)", restype, pwi->resNum, pwi->x1, pwi->y1, pwi->flags); + uint8 *dst = NULL; + const uint8 *dataPtr = getResourceAddress(restype, pwi->resNum); + if (dataPtr) { + const uint8 *rmap = NULL; + const uint8 *xmap = findWrappedBlock(MKID('XMAP'), dataPtr, pwi->state, 0); + + const uint8 *wizh = findWrappedBlock(MKID('WIZH'), dataPtr, pwi->state, 0); + assert(wizh); + uint32 comp = READ_LE_UINT32(wizh + 0x0); + uint32 width = READ_LE_UINT32(wizh + 0x4); + uint32 height = READ_LE_UINT32(wizh + 0x8); + assert(comp == 0 || comp == 1 || comp == 2 || comp == 3 || comp == 10 || comp == 11); + + const uint8 *wizd = findWrappedBlock(MKID('WIZD'), dataPtr, pwi->state, 0); + assert(wizd); + if (pwi->flags & 1) { + const uint8 *pal = findWrappedBlock(MKID('RGBS'), dataPtr, pwi->state, 0); + assert(pal); + setPaletteFromPtr(pal, 256); + } + if (pwi->flags & 2) { + rmap = findWrappedBlock(MKID('RMAP'), dataPtr, pwi->state, 0); + assert(rmap); + const uint8 *rgbs = findWrappedBlock(MKID('RGBS'), dataPtr, pwi->state, 0); + assert(rgbs); + warning("drawWizImage() unhandled flag 0x2"); + // XXX modify 'RMAP' buffer + } + if (pwi->flags & 4) { + warning("WizImage printing is unimplemented"); + return NULL; + } + uint32 cw, ch; + if (pwi->flags & 0x20) { + dst = (uint8 *)malloc(width * height); + int color = 255; // FIXME: should be (VAR_WIZ_TCOLOR != 0xFF) ? VAR(VAR_WIZ_TCOLOR) : 5; + memset(dst, color, width * height); + cw = width; + ch = height; + } else { + VirtScreen *pvs = &virtscr[kMainVirtScreen]; + if (pwi->flags & 0x10) { + dst = pvs->getPixels(0, pvs->topline); + } else { + dst = pvs->getBackPixels(0, pvs->topline); + } + cw = pvs->w; + ch = pvs->h; + } + Common::Rect rScreen(cw, ch); + // XXX handle 'XMAP' / 'RMAP' data + if (comp == 1) { + if (pwi->flags & 0x80) { + warning("drawWizImage() unhandled flag 0x80"); + } else if (pwi->flags & 0x100) { + warning("drawWizImage() unhandled flag 0x100"); + } else { + _wiz.copyWizImage(dst, wizd, cw, ch, pwi->x1, pwi->y1, width, height, &rScreen); + } + } else if (comp == 0 || comp == 2 || comp == 3) { + const uint8 *trns = findWrappedBlock(MKID('TRNS'), dataPtr, pwi->state, 0); + int color = (trns == NULL) ? VAR(VAR_WIZ_TCOLOR) : -1; + const uint8 *pal = xmap; + if (pwi->flags & 2) { + pal = rmap + 4; + } + _wiz.copyRawWizImage(dst, wizd, cw, ch, pwi->x1, pwi->y1, width, height, &rScreen, pwi->flags, pal, color); + } else { + warning("unhandled wiz compression type %d", comp); + } + + if (!(pwi->flags & 0x20)) { + Common::Rect rImage(pwi->x1, pwi->y1, pwi->x1 + width, pwi->y1 + height); + if (rImage.intersects(rScreen)) { + rImage.clip(rScreen); + if (!(pwi->flags & 8) && pwi->flags & 0x18) { + ++rImage.bottom; + markRectAsDirty(kMainVirtScreen, rImage); + } else { + gdi.copyVirtScreenBuffers(rImage); + } + } + } + } + return dst; +} + +struct PolygonDrawData { + struct InterArea { + bool valid; + int32 xmin; + int32 xmax; + int32 x1; + int32 y1; + int32 x2; + int32 y2; + }; + Common::Point pto; + InterArea *ia; + int areasNum; + + PolygonDrawData(int n) { + areasNum = n; + ia = new InterArea[areasNum]; + memset(ia, 0, sizeof(InterArea) * areasNum); + } + + ~PolygonDrawData() { + delete[] ia; + } + + void calcIntersection(const Common::Point *p1, const Common::Point *p2, const Common::Point *p3, const Common::Point *p4) { + int32 x1_acc = p1->x << 0x10; + int32 x3_acc = p3->x << 0x10; + int32 y3_acc = p3->y << 0x10; + uint16 dy = ABS(p2->y - p1->y) + 1; + int32 x1_step = ((p2->x - p1->x) << 0x10) / dy; + int32 x3_step = ((p4->x - p3->x) << 0x10) / dy; + int32 y3_step = ((p4->y - p3->y) << 0x10) / dy; + + int iaidx = p1->y - pto.y; + while (dy--) { + assert(iaidx >= 0 && iaidx < areasNum); + InterArea *pia = &ia[iaidx]; + int32 tx1 = x1_acc >> 0x10; + int32 tx3 = x3_acc >> 0x10; + int32 ty3 = y3_acc >> 0x10; + + if (!pia->valid || pia->xmin > tx1) { + pia->xmin = tx1; + pia->x1 = tx3; + pia->y1 = ty3; + } + if (!pia->valid || pia->xmax < tx1) { + pia->xmax = tx1; + pia->x2 = tx3; + pia->y2 = ty3; + } + pia->valid = true; + + x1_acc += x1_step; + x3_acc += x3_step; + y3_acc += y3_step; + + if (p2->y <= p1->y) { + --iaidx; + } else { + ++iaidx; + } + } + } +}; + +void ScummEngine_v72he::drawWizPolygon(int resnum, int state, int id, int flags) { + int i; + WizPolygon *wp = NULL; + for (i = 0; i < ARRAYSIZE(_wiz._polygons); ++i) { + if (_wiz._polygons[i].id == id) { + wp = &_wiz._polygons[i]; + break; + } + } + if (!wp) { + error("Polygon %d is not defined", id); + } + if (wp->numVerts != 5) { + error("Invalid point count %d for Polygon %d", wp->numVerts, id); + } + WizImage wi; + wi.resNum = resnum; + wi.state = state; + wi.x1 = wi.y1 = 0; + wi.flags = 0x20; + uint8 *srcWizBuf = drawWizImage(rtImage, &wi); + if (srcWizBuf) { + uint8 *dst; + VirtScreen *pvs = &virtscr[kMainVirtScreen]; + if (flags & 0x10) { + dst = pvs->getPixels(0, 0); + } else { + dst = pvs->getBackPixels(0, 0); + } + if (wp->bound.left < 0 || wp->bound.top < 0 || wp->bound.right >= pvs->w || wp->bound.bottom >= pvs->h) { + error("Invalid coords polygon %d", wp->id); + } + + int32 wizW, wizH; + getWizImageDim(resnum, state, wizW, wizH); + Common::Point bbox[4]; + bbox[0].x = 0; + bbox[0].y = 0; + bbox[1].x = wizW - 1; + bbox[1].y = 0; + bbox[2].x = wizW - 1; + bbox[2].y = wizH - 1; + bbox[3].x = 0; + bbox[3].y = wizH - 1; + + int16 xmin_p, xmax_p, ymin_p, ymax_p; + xmin_p = xmax_p = wp->vert[0].x; + ymin_p = ymax_p = wp->vert[0].y; + for (i = 1; i < 4; ++i) { + xmin_p = MIN(wp->vert[i].x, xmin_p); + xmax_p = MAX(wp->vert[i].x, xmax_p); + ymin_p = MIN(wp->vert[i].y, ymin_p); + ymax_p = MAX(wp->vert[i].y, ymax_p); + } + + int16 xmin_b, xmax_b, ymin_b, ymax_b; + xmin_b = 0; + xmax_b = wizW - 1; + ymin_b = 0; + ymax_b = wizH - 1; + + PolygonDrawData pdd(ymax_p - ymin_p + 1); + pdd.pto.x = xmin_p; + pdd.pto.y = ymin_p; + + for (i = 0; i < 3; ++i) { + pdd.calcIntersection(&wp->vert[i], &wp->vert[i + 1], &bbox[i], &bbox[i + 1]); + } + pdd.calcIntersection(&wp->vert[3], &wp->vert[0], &bbox[3], &bbox[0]); + + uint yoff = pdd.pto.y * pvs->w; + for (i = 0; i < pdd.areasNum; ++i) { + PolygonDrawData::InterArea *pia = &pdd.ia[i]; + uint16 dx = pia->xmax - pia->xmin + 1; + uint8 *dstPtr = dst + pia->xmin + yoff; + int32 x_acc = pia->x1 << 0x10; + int32 y_acc = pia->y1 << 0x10; + int32 x_step = ((pia->x2 - pia->x1) << 0x10) / dx; + int32 y_step = ((pia->y2 - pia->y1) << 0x10) / dx; + while (dx--) { + uint srcWizOff = (y_acc >> 0x10) * wizW + (x_acc >> 0x10); + assert(srcWizOff < (uint32)(wizW * wizH)); + x_acc += x_step; + y_acc += y_step; + *dstPtr++ = srcWizBuf[srcWizOff]; + } + yoff += pvs->pitch; + } + + if (flags & 0x10) { + markRectAsDirty(kMainVirtScreen, wp->bound); + } else { + gdi.copyVirtScreenBuffers(wp->bound); + } + + free(srcWizBuf); + } +} + +void ScummEngine_v72he::flushWizBuffer() { + for (int i = 0; i < _wiz._imagesNum; ++i) { + WizImage *pwi = &_wiz._images[i]; + if (pwi->flags & 0x40) { + drawWizPolygon(pwi->resNum, pwi->state, pwi->x1, pwi->flags); + } else { + drawWizImage(rtImage, pwi); + } + } + _wiz._imagesNum = 0; +} + +void ScummEngine_v80he::loadImgSpot(int resId, int state, int16 &x, int16 &y) { + const uint8 *dataPtr = getResourceAddress(rtImage, resId); + if (!dataPtr) { + warning("loadImgSpot: unknown Image %d", resId); + x = y = 0; + return; + } + + const uint8 *spotPtr = findWrappedBlock(MKID('SPOT'), dataPtr, state, 0); + if (spotPtr) { + x = (int16)READ_LE_UINT32(spotPtr + 0); + y = (int16)READ_LE_UINT32(spotPtr + 4); + } else { + x = 0; + y = 0; + } +} + +void ScummEngine_v80he::loadWizCursor(int resId, int resType, bool state) { + int16 x, y; + loadImgSpot(resId, 0, x, y); + if (x < 0) { + x = 0; + } else if (x > 32) { + x = 32; + } + if (y < 0) { + y = 0; + } else if (y > 32) { + y = 32; + } + + WizImage wi; + wi.resNum = resId; + wi.x1 = wi.y1 = 0; + wi.state = 0; + wi.flags = 0x20; + uint8 *cursor = drawWizImage(rtImage, &wi); + int32 cw, ch; + getWizImageDim(resId, 0, cw, ch); + setCursorFromBuffer(cursor, cw, ch, cw); + setCursorHotspot(x, y); + free(cursor); +} + +void ScummEngine_v90he::drawWizComplexPolygon(int resnum, int state, int po_x, int po_y, int arg14, int angle, int zoom, const Common::Rect *r) { + Common::Point pts[4]; + int32 w, h; + getWizImageDim(resnum, state, w, h); + + pts[1].x = pts[2].x = w / 2 - 1; + pts[0].x = pts[0].y = pts[1].y = pts[3].x = -w / 2; + pts[2].y = pts[3].y = h / 2 - 1; + + // transform points + if (zoom != 256) { + for (int i = 0; i < 4; ++i) { + pts[i].x = pts[i].x * zoom / 256; + pts[i].y = pts[i].y * zoom / 256; + } + } + if (angle != 0) { + double alpha = angle * PI / 180.; + double cos_alpha = cos(alpha); + double sin_alpha = sin(alpha); + for (int i = 0; i < 4; ++i) { + int16 x = pts[i].x; + int16 y = pts[i].y; + pts[i].x = (int16)(x * cos_alpha - y * sin_alpha); + pts[i].y = (int16)(y * cos_alpha + x * sin_alpha); + } + } + for (int i = 0; i < 4; ++i) { + pts[i].x += po_x; + pts[i].y += po_y; + } + // XXX drawWizPolygonPoints(resnum, state, pts, r, VAR(117)); + warning("ScummEngine_v90he::drawWizComplexPolygon() partially implemented"); +} + +void ScummEngine_v90he::displayWizComplexImage(const WizParameters *params) { + // XXX merge with ScummEngine_v72he::displayWizImage + int zoom = 256; + if (params->processFlags & 0x8) { + zoom = params->zoom; + } + int rotationAngle = 0; + if (params->processFlags & 0x10) { + rotationAngle = params->angle; + } + int state = 0; + if (params->processFlags & 0x400) { + state = params->img.state; + } + int flags = 0; + if (params->processFlags & 0x20) { + flags = params->img.flags; + } + int po_x = 0; + int po_y = 0; + if (params->processFlags & 0x1) { + po_x = params->img.x1; + po_y = params->img.y1; + } + int unk = 0; + if (params->processFlags & 0x4) { + unk = params->unk_15C; + } + const Common::Rect *r = NULL; + if (params->processFlags & 0x200) { + r = ¶ms->box; + } + + if (_fullRedraw) { + assert(_wiz._imagesNum < ARRAYSIZE(_wiz._images)); + WizImage *pwi = &_wiz._images[_wiz._imagesNum]; + pwi->resNum = params->img.resNum; + pwi->x1 = po_x; + pwi->y1 = po_y; + pwi->state = state; + pwi->flags = flags; + pwi->unk = unk; + ++_wiz._imagesNum; + } else if (params->processFlags & 0x18) { + drawWizComplexPolygon(params->img.resNum, state, po_x, po_y, unk, rotationAngle, zoom, r); + } else if (flags & 0x40) { + drawWizPolygon(params->img.resNum, state, po_x, flags); // XXX , VAR(117)); + } else { + if ((flags & 0x200) || (flags & 0x24)) { + warning("ScummEngine_v90he::displayWizComplexImage() unhandled flags = 0x%X", flags); + } + // XXX flags 0x200, 0x24 + WizImage wi; + wi.resNum = params->img.resNum; + wi.x1 = po_x; + wi.y1 = po_y; + wi.state = state; + wi.flags = flags; + wi.unk = unk; + drawWizImage(rtImage, &wi); + } +} + +void ScummEngine_v90he::processWizImage(const WizParameters *params) { + debug(1, "ScummEngine_v90he::processWizImage()"); + switch (params->processMode) { + case 1: + displayWizComplexImage(params); + break; + case 2: + captureWizImage(rtImage, params->img.resNum, params->box, (params->img.flags & 8) == 8, params->compType); + break; + case 3: + if (params->processFlags & 0x800) { + File f; + if (!f.open((const char *)params->filename, File::kFileReadMode)) { + warning("Unable to open for read '%s'", params->filename); + } else { + uint32 id = f.readUint32BE(); + if (id != MKID('AWIZ') && id != MKID('MULT')) { + VAR(VAR_GAME_LOADED) = -1; + } else { + uint32 size = f.readUint32BE(); + f.seek(0, SEEK_SET); + byte *p = createResource(rtImage, params->img.resNum, size); + if (f.read(p, size) != size) { + nukeResource(rtImage, params->img.resNum); + warning("i/o error when reading '%s'", params->filename); + VAR(VAR_GAME_LOADED) = -2; + } else { + VAR(VAR_GAME_LOADED) = 0; + } + } + f.close(); + } + } + break; + case 4: + if (params->processFlags & 0x800) { + if (params->unk_14C != 0) { + VAR(119) = -1; + } else { + File f; + if (!f.open((const char *)params->filename, File::kFileWriteMode)) { + warning("Unable to open for write '%s'", params->filename); + VAR(119) = -3; + } else { + byte *p = getResourceAddress(rtImage, params->img.resNum); + uint32 size = READ_BE_UINT32(p + 4); + if (f.write(p, size) != size) { + warning("i/o error when writing '%s'", params->filename); + VAR(119) = -2; + } else { + VAR(119) = 0; + } + f.close(); + } + } + } + break; + case 6: + // HE 99+ + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + warning("unhandled processWizImage mode %d", params->processMode); + break; + default: + warning("invalid processWizImage mode %d", params->processMode); + } +} + +int ScummEngine_v90he::getWizImageStates(int resnum) { + const uint8 *dataPtr = getResourceAddress(rtImage, resnum); + assert(dataPtr); + if (READ_UINT32(dataPtr) == MKID('MULT')) { + const byte *offs, *wrap; + + wrap = findResource(MKID('WRAP'), dataPtr); + if (wrap == NULL) + return 1; + + offs = findResourceData(MKID('OFFS'), wrap); + if (offs == NULL) + return 1; + + return getResourceDataSize(offs) / 4; + } else { + return 1; + } +} + +int ScummEngine_v90he::isWizPixelNonTransparent(int restype, int resnum, int state, int x, int y, int flags) { + int ret = 0; + const uint8 *data = getResourceAddress(restype, resnum); + assert(data); + const uint8 *wizh = findWrappedBlock(MKID('WIZH'), data, state, 0); + assert(wizh); + uint32 c = READ_LE_UINT32(wizh + 0x0); + int w = READ_LE_UINT32(wizh + 0x4); + int h = READ_LE_UINT32(wizh + 0x8); + const uint8 *wizd = findWrappedBlock(MKID('WIZD'), data, state, 0); + assert(wizd); + if (x >= 0 && x < w && y >= 0 && y < h) { + if (flags & 0x400) { + x = w - x - 1; + } + if (flags & 0x800) { + y = h - y - 1; + } + if (c == 1) { + ret = _wiz.isWizPixelNonTransparent(wizd, x, y, w, h); + } else if (c == 0 || c == 2 || c == 3) { + ret = _wiz.getRawWizPixelColor(wizd, x, y, w, h, VAR(VAR_WIZ_TCOLOR)) != VAR(VAR_WIZ_TCOLOR) ? 1 : 0; + } + } + return ret; +} + +uint8 ScummEngine_v90he::getWizPixelColor(int restype, int resnum, int state, int x, int y, int flags) { + uint8 color; + const uint8 *data = getResourceAddress(restype, resnum); + assert(data); + const uint8 *wizh = findWrappedBlock(MKID('WIZH'), data, state, 0); + assert(wizh); + uint32 c = READ_LE_UINT32(wizh + 0x0); + uint32 w = READ_LE_UINT32(wizh + 0x4); + uint32 h = READ_LE_UINT32(wizh + 0x8); + const uint8 *wizd = findWrappedBlock(MKID('WIZD'), data, state, 0); + assert(wizd); + if (c == 1) { + color = _wiz.getWizPixelColor(wizd, x, y, w, h, VAR(VAR_WIZ_TCOLOR)); + } else if (c == 0 || c == 2 || c == 3) { + color = _wiz.getRawWizPixelColor(wizd, x, y, w, h, VAR(VAR_WIZ_TCOLOR)); + } else { + color = VAR(VAR_WIZ_TCOLOR); + } + return color; +} + +int ScummEngine_v90he::computeWizHistogram(int resnum, int state, int x, int y, int w, int h) { + writeVar(0, 0); + defineArray(0, kDwordArray, 0, 0, 0, 255); + if (readVar(0) != 0) { + const uint8 *data = getResourceAddress(rtImage, resnum); + assert(data); + const uint8 *wizh = findWrappedBlock(MKID('WIZH'), data, state, 0); + assert(wizh); + uint32 ic = READ_LE_UINT32(wizh + 0x0); + uint32 iw = READ_LE_UINT32(wizh + 0x4); + uint32 ih = READ_LE_UINT32(wizh + 0x8); + const uint8 *wizd = findWrappedBlock(MKID('WIZD'), data, state, 0); + assert(wizd); + Common::Rect rWiz(iw, ih); + Common::Rect rCap(x, y, w + 1, h + 1); + if (rCap.intersects(rWiz)) { + rCap.clip(rWiz); + uint32 histogram[0x100]; + memset(histogram, 0, sizeof(histogram)); + if (ic == 1) { + _wiz.computeWizHistogram(histogram, wizd, &rCap); + } else if (ic == 0 || ic == 2 || ic == 3) { + _wiz.computeRawWizHistogram(histogram, wizd, w, &rCap); + } else { + warning("Unable to return histogram for type %d", ic); + } + for (int i = 0; i < 0x100; ++i) { + writeArray(0, 0, i, histogram[i]); + } + } + } + return readVar(0); +} + +} // End of namespace Scumm |