From 5e52b0a5d46e4c5b230cba7464301484d27804f7 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sat, 10 Mar 2012 13:50:27 -0500 Subject: MOHAWK: Split the graphics classes into their own files --- engines/mohawk/myst_graphics.cpp | 493 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 engines/mohawk/myst_graphics.cpp (limited to 'engines/mohawk/myst_graphics.cpp') diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp new file mode 100644 index 0000000000..151390580f --- /dev/null +++ b/engines/mohawk/myst_graphics.cpp @@ -0,0 +1,493 @@ +/* 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. + * + */ + +#include "mohawk/myst.h" +#include "mohawk/myst_graphics.h" +#include "mohawk/resource.h" + +#include "common/substream.h" +#include "common/system.h" +#include "common/textconsole.h" +#include "engines/util.h" +#include "graphics/jpeg.h" +#include "graphics/pict.h" + +namespace Mohawk { + +MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { + _bmpDecoder = new MystBitmap(); + + _viewport = Common::Rect(544, 332); + + // The original version of Myst could run in 8bpp color too. + // However, it dithered videos to 8bpp and they looked considerably + // worse (than they already did :P). So we're not even going to + // support 8bpp mode in Myst (Myst ME required >8bpp anyway). + initGraphics(_viewport.width(), _viewport.height(), true, NULL); // What an odd screen size! + + _pixelFormat = _vm->_system->getScreenFormat(); + + if (_pixelFormat.bytesPerPixel == 1) + error("Myst requires greater than 256 colors to run"); + + if (_vm->getFeatures() & GF_ME) { + _jpegDecoder = new Graphics::JPEG(); + _pictDecoder = new Graphics::PictDecoder(_pixelFormat); + } else { + _jpegDecoder = NULL; + _pictDecoder = NULL; + } + + _pictureFile.entries = NULL; + + // Initialize our buffer + _backBuffer = new Graphics::Surface(); + _backBuffer->create(_vm->_system->getWidth(), _vm->_system->getHeight(), _pixelFormat); + + _nextAllowedDrawTime = _vm->_system->getMillis(); + _enableDrawingTimeSimulation = 0; +} + +MystGraphics::~MystGraphics() { + delete _bmpDecoder; + delete _jpegDecoder; + delete _pictDecoder; + delete[] _pictureFile.entries; + + _backBuffer->free(); + delete _backBuffer; +} + +static const char *s_picFileNames[] = { + "CHpics", + "", + "", + "DUpics", + "INpics", + "", + "MEpics", + "MYpics", + "SEpics", + "", + "", + "STpics" +}; + +void MystGraphics::loadExternalPictureFile(uint16 stack) { + if (_vm->getPlatform() != Common::kPlatformMacintosh) + return; + + if (_pictureFile.picFile.isOpen()) + _pictureFile.picFile.close(); + delete[] _pictureFile.entries; + + if (!scumm_stricmp(s_picFileNames[stack], "")) + return; + + if (!_pictureFile.picFile.open(s_picFileNames[stack])) + error ("Could not open external picture file \'%s\'", s_picFileNames[stack]); + + _pictureFile.pictureCount = _pictureFile.picFile.readUint32BE(); + _pictureFile.entries = new PictureFile::PictureEntry[_pictureFile.pictureCount]; + + for (uint32 i = 0; i < _pictureFile.pictureCount; i++) { + _pictureFile.entries[i].offset = _pictureFile.picFile.readUint32BE(); + _pictureFile.entries[i].size = _pictureFile.picFile.readUint32BE(); + _pictureFile.entries[i].id = _pictureFile.picFile.readUint16BE(); + _pictureFile.entries[i].type = _pictureFile.picFile.readUint16BE(); + _pictureFile.entries[i].width = _pictureFile.picFile.readUint16BE(); + _pictureFile.entries[i].height = _pictureFile.picFile.readUint16BE(); + } +} + +MohawkSurface *MystGraphics::decodeImage(uint16 id) { + MohawkSurface *mhkSurface = 0; + + // Myst ME uses JPEG/PICT images instead of compressed Windows Bitmaps for room images, + // though there are a few weird ones that use that format. For further nonsense with images, + // the Macintosh version stores images in external "picture files." We check them before + // going to check for a PICT resource. + if (_vm->getFeatures() & GF_ME && _vm->getPlatform() == Common::kPlatformMacintosh && _pictureFile.picFile.isOpen()) { + for (uint32 i = 0; i < _pictureFile.pictureCount; i++) + if (_pictureFile.entries[i].id == id) { + if (_pictureFile.entries[i].type == 0) { + Common::SeekableReadStream *stream = new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size); + + if (!_jpegDecoder->read(stream)) + error("Could not decode Myst ME Mac JPEG"); + + mhkSurface = new MohawkSurface(_jpegDecoder->getSurface(_pixelFormat)); + delete stream; + } else if (_pictureFile.entries[i].type == 1) { + mhkSurface = new MohawkSurface(_pictDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size))); + } else + error ("Unknown Picture File type %d", _pictureFile.entries[i].type); + break; + } + } + + // We're not using the external Mac files, so it's time to delve into the main Mohawk + // archives. However, we still don't know if it's a PICT or WDIB resource. If it's Myst + // ME it's most likely a PICT, and if it's original it's definitely a WDIB. However, + // Myst ME throws us another curve ball in that PICT resources can contain WDIB's instead + // of PICT's. + if (!mhkSurface) { + bool isPict = false; + Common::SeekableReadStream *dataStream = NULL; + + if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, id)) { + // The PICT resource exists. However, it could still contain a MystBitmap + // instead of a PICT image... + dataStream = _vm->getResource(ID_PICT, id); + } else // No PICT, so the WDIB must exist. Let's go grab it. + dataStream = _vm->getResource(ID_WDIB, id); + + if (_vm->getFeatures() & GF_ME) { + // Here we detect whether it's really a PICT or a WDIB. Since a MystBitmap + // would be compressed, there's no way to detect for the BM without a hack. + // So, we search for the PICT version opcode for detection. + dataStream->seek(512 + 10); // 512 byte pict header + isPict = (dataStream->readUint32BE() == 0x001102FF); + dataStream->seek(0); + } + + if (isPict) + mhkSurface = new MohawkSurface(_pictDecoder->decodeImage(dataStream)); + else { + mhkSurface = _bmpDecoder->decodeImage(dataStream); + mhkSurface->convertToTrueColor(); + } + } + + assert(mhkSurface); + return mhkSurface; +} + +void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest) { + Graphics::Surface *surface = findImage(image)->getSurface(); + + // Make sure the image is bottom aligned in the dest rect + dest.top = dest.bottom - MIN(surface->h, dest.height()); + + // Convert from bitmap coordinates to surface coordinates + uint16 top = surface->h - (src.top + MIN(surface->h, dest.height())); + + // Do not draw the top pixels if the image is too tall + if (dest.height() > _viewport.height()) + top += dest.height() - _viewport.height(); + + // Clip the destination rect to the screen + if (dest.right > _vm->_system->getWidth() || dest.bottom > _vm->_system->getHeight()) + dest.debugPrint(4, "Clipping destination rect to the screen"); + dest.right = CLIP(dest.right, 0, _vm->_system->getWidth()); + dest.bottom = CLIP(dest.bottom, 0, _vm->_system->getHeight()); + + uint16 width = MIN(surface->w, dest.width()); + uint16 height = MIN(surface->h, dest.height()); + + // Clamp Width and Height to within src surface dimensions + if (src.left + width > surface->w) + width = surface->w - src.left; + if (src.top + height > surface->h) + height = surface->h - src.top; + + debug(3, "MystGraphics::copyImageSectionToScreen()"); + debug(3, "\tImage: %d", image); + debug(3, "\tsrc.left: %d", src.left); + debug(3, "\tsrc.top: %d", src.top); + debug(3, "\tdest.left: %d", dest.left); + debug(3, "\tdest.top: %d", dest.top); + debug(3, "\twidth: %d", width); + debug(3, "\theight: %d", height); + + simulatePreviousDrawDelay(dest); + + _vm->_system->copyRectToScreen((byte *)surface->getBasePtr(src.left, top), surface->pitch, dest.left, dest.top, width, height); +} + +void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest) { + Graphics::Surface *surface = findImage(image)->getSurface(); + + // Make sure the image is bottom aligned in the dest rect + dest.top = dest.bottom - MIN(surface->h, dest.height()); + + // Convert from bitmap coordinates to surface coordinates + uint16 top = surface->h - (src.top + MIN(surface->h, dest.height())); + + // Do not draw the top pixels if the image is too tall + if (dest.height() > _viewport.height()) { + top += dest.height() - _viewport.height(); + } + + // Clip the destination rect to the screen + if (dest.right > _vm->_system->getWidth() || dest.bottom > _vm->_system->getHeight()) + dest.debugPrint(4, "Clipping destination rect to the screen"); + dest.right = CLIP(dest.right, 0, _vm->_system->getWidth()); + dest.bottom = CLIP(dest.bottom, 0, _vm->_system->getHeight()); + + uint16 width = MIN(surface->w, dest.width()); + uint16 height = MIN(surface->h, dest.height()); + + // Clamp Width and Height to within src surface dimensions + if (src.left + width > surface->w) + width = surface->w - src.left; + if (src.top + height > surface->h) + height = surface->h - src.top; + + debug(3, "MystGraphics::copyImageSectionToBackBuffer()"); + debug(3, "\tImage: %d", image); + debug(3, "\tsrc.left: %d", src.left); + debug(3, "\tsrc.top: %d", src.top); + debug(3, "\tdest.left: %d", dest.left); + debug(3, "\tdest.top: %d", dest.top); + debug(3, "\twidth: %d", width); + debug(3, "\theight: %d", height); + + for (uint16 i = 0; i < height; i++) + memcpy(_backBuffer->getBasePtr(dest.left, i + dest.top), surface->getBasePtr(src.left, top + i), width * surface->format.bytesPerPixel); +} + +void MystGraphics::copyImageToScreen(uint16 image, Common::Rect dest) { + copyImageSectionToScreen(image, Common::Rect(544, 333), dest); +} + +void MystGraphics::copyImageToBackBuffer(uint16 image, Common::Rect dest) { + copyImageSectionToBackBuffer(image, Common::Rect(544, 333), dest); +} + +void MystGraphics::copyBackBufferToScreen(Common::Rect r) { + r.clip(_viewport); + + simulatePreviousDrawDelay(r); + + _vm->_system->copyRectToScreen((byte *)_backBuffer->getBasePtr(r.left, r.top), _backBuffer->pitch, r.left, r.top, r.width(), r.height()); +} + +void MystGraphics::runTransition(uint16 type, Common::Rect rect, uint16 steps, uint16 delay) { + + // Do not artificially delay during transitions + int oldEnableDrawingTimeSimulation = _enableDrawingTimeSimulation; + _enableDrawingTimeSimulation = 0; + + switch (type) { + case 0: { + debugC(kDebugScript, "Left to Right"); + + uint16 step = (rect.right - rect.left) / steps; + Common::Rect area = rect; + for (uint i = 0; i < steps; i++) { + area.left = rect.left + step * i; + area.right = area.left + step; + + _vm->_system->delayMillis(delay); + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + if (area.right < rect.right) { + area.left = area.right; + area.right = rect.right; + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + } + break; + case 1: { + debugC(kDebugScript, "Right to Left"); + + uint16 step = (rect.right - rect.left) / steps; + Common::Rect area = rect; + for (uint i = 0; i < steps; i++) { + area.right = rect.right - step * i; + area.left = area.right - step; + + _vm->_system->delayMillis(delay); + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + if (area.left > rect.left) { + area.right = area.left; + area.left = rect.left; + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + } + break; + case 5: { + debugC(kDebugScript, "Top to Bottom"); + + uint16 step = (rect.bottom - rect.top) / steps; + Common::Rect area = rect; + for (uint i = 0; i < steps; i++) { + area.top = rect.top + step * i; + area.bottom = area.top + step; + + _vm->_system->delayMillis(delay); + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + if (area.bottom < rect.bottom) { + area.top = area.bottom; + area.bottom = rect.bottom; + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + } + break; + case 6: { + debugC(kDebugScript, "Bottom to Top"); + + uint16 step = (rect.bottom - rect.top) / steps; + Common::Rect area = rect; + for (uint i = 0; i < steps; i++) { + area.bottom = rect.bottom - step * i; + area.top = area.bottom - step; + + _vm->_system->delayMillis(delay); + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + if (area.top > rect.top) { + area.bottom = area.top; + area.top = rect.top; + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + } + break; + default: + warning("Unknown Update Direction"); + + //TODO: Replace minimal implementation + copyBackBufferToScreen(rect); + _vm->_system->updateScreen(); + break; + } + + _enableDrawingTimeSimulation = oldEnableDrawingTimeSimulation; +} + +void MystGraphics::drawRect(Common::Rect rect, RectState state) { + rect.clip(_viewport); + + // Useful with debugging. Shows where hotspots are on the screen and whether or not they're active. + if (!rect.isValidRect() || rect.width() == 0 || rect.height() == 0) + return; + + Graphics::Surface *screen = _vm->_system->lockScreen(); + + if (state == kRectEnabled) + screen->frameRect(rect, _pixelFormat.RGBToColor(0, 255, 0)); + else if (state == kRectUnreachable) + screen->frameRect(rect, _pixelFormat.RGBToColor(0, 0, 255)); + else + screen->frameRect(rect, _pixelFormat.RGBToColor(255, 0, 0)); + + _vm->_system->unlockScreen(); +} + +void MystGraphics::drawLine(const Common::Point &p1, const Common::Point &p2, uint32 color) { + _backBuffer->drawLine(p1.x, p1.y, p2.x, p2.y, color); +} + +void MystGraphics::enableDrawingTimeSimulation(bool enable) { + if (enable) + _enableDrawingTimeSimulation++; + else + _enableDrawingTimeSimulation--; + + if (_enableDrawingTimeSimulation < 0) + _enableDrawingTimeSimulation = 0; +} + +void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) { + uint32 time = 0; + + if (_enableDrawingTimeSimulation) { + time = _vm->_system->getMillis(); + + // Do not draw anything new too quickly after the previous draw call + // so that images stay at least a little while on screen + // This is enabled only for scripted draw calls + if (time < _nextAllowedDrawTime) + _vm->_system->delayMillis(_nextAllowedDrawTime - time); + } + + // Next draw call allowed at DELAY + AERA * COEFF milliseconds from now + time = _vm->_system->getMillis(); + _nextAllowedDrawTime = time + _constantDrawDelay + dest.height() * dest.width() / _proportionalDrawDelay; +} + +void MystGraphics::copyBackBufferToScreenWithSaturation(int16 saturation) { + Graphics::Surface *screen = _vm->_system->lockScreen(); + + for (uint16 y = 0; y < _viewport.height(); y++) + for (uint16 x = 0; x < _viewport.width(); x++) { + uint32 color; + uint8 r, g, b; + + if (_pixelFormat.bytesPerPixel == 2) + color = *(const uint16 *)_backBuffer->getBasePtr(x, y); + else + color = *(const uint32 *)_backBuffer->getBasePtr(x, y); + + _pixelFormat.colorToRGB(color, r, g, b); + + r = CLIP((int16)r - saturation, 0, 255); + g = CLIP((int16)g - saturation, 0, 255); + b = CLIP((int16)b - saturation, 0, 255); + + color = _pixelFormat.RGBToColor(r, g, b); + + if (_pixelFormat.bytesPerPixel == 2) { + uint16 *dst = (uint16 *)screen->getBasePtr(x, y); + *dst = color; + } else { + uint32 *dst = (uint32 *)screen->getBasePtr(x, y); + *dst = color; + } + } + + _vm->_system->unlockScreen(); + _vm->_system->updateScreen(); +} + +void MystGraphics::fadeToBlack() { + for (int16 i = 0; i < 256; i += 32) { + copyBackBufferToScreenWithSaturation(i); + } +} + +void MystGraphics::fadeFromBlack() { + for (int16 i = 256; i >= 0; i -= 32) { + copyBackBufferToScreenWithSaturation(i); + } +} + +} // End of namespace Mohawk -- cgit v1.2.3