/* 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 "common/endian.h" #include "engines/util.h" #include "graphics/cursorman.h" #include "graphics/palette.h" #include "graphics/surface.h" #include "gob/gob.h" #include "gob/video.h" #include "gob/global.h" #include "gob/util.h" #include "gob/dataio.h" #include "gob/draw.h" namespace Gob { Font::Font(const byte *data) : _dataPtr(data) { assert(data); bool hasWidths = _dataPtr[0] & 0x80; _data = _dataPtr + 4; _itemWidth = _dataPtr[0] & 0x7F; _itemHeight = _dataPtr[1]; _startItem = _dataPtr[2]; _endItem = _dataPtr[3]; _charWidths = 0; uint8 rowAlignedBits = (_itemWidth - 1) / 8 + 1; _itemSize = rowAlignedBits * _itemHeight; _bitWidth = _itemWidth; if (hasWidths) _charWidths = _dataPtr + 4 + _itemSize * getCharCount(); } Font::~Font() { delete[] _dataPtr; } uint8 Font::getCharWidth(uint8 c) const { if (!_charWidths || (_endItem == 0)) return _itemWidth; if ((c < _startItem) || (c > _endItem)) return _itemWidth; return _charWidths[c - _startItem]; } uint8 Font::getCharWidth() const { return _itemWidth; } uint8 Font::getCharHeight() const { return _itemHeight; } uint16 Font::getCharCount() const { return _endItem - _startItem + 1; } bool Font::hasChar(uint8 c) const { return (c >= _startItem) && (c <= _endItem); } bool Font::isMonospaced() const { return _charWidths == 0; } void Font::drawLetter(Surface &surf, uint8 c, uint16 x, uint16 y, uint32 color1, uint32 color2, bool transp) const { uint16 data; const byte *src = getCharData(c); if (!src) { warning("Font::drawLetter(): getCharData() == 0"); return; } Pixel dst = surf.get(x, y); int nWidth = _itemWidth; if (nWidth & 7) nWidth = (nWidth & 0xF8) + 8; nWidth >>= 3; for (int i = 0; (i < _itemHeight) && dst.isValid(); i++) { int width = _itemWidth; for (int k = 0; k < nWidth; k++) { data = *src++; for (int j = 0; j < MIN(8, width); j++) { if (dst.isValid()) { if (data & 0x80) dst.set(color1); else if (!transp) dst.set(color2); } dst++; data <<= 1; } width -= 8; } dst += surf.getWidth() - _itemWidth; } } void Font::drawString(const Common::String &str, int16 x, int16 y, int16 color1, int16 color2, bool transp, Surface &dest) const { const char *s = str.c_str(); while (*s != '\0') { const int16 charRight = x + getCharWidth(*s); const int16 charBottom = y + getCharHeight(); if ((x >= 0) && (y >= 0) && (charRight <= dest.getWidth()) && (charBottom <= dest.getHeight())) drawLetter(dest, *s, x, y, color1, color2, transp); x += getCharWidth(*s); s++; } } const byte *Font::getCharData(uint8 c) const { if (_endItem == 0) { warning("Font::getCharData(): _endItem == 0"); return 0; } if ((c < _startItem) || (c > _endItem)) return 0; return _data + (c - _startItem) * _itemSize; } Video::Video(GobEngine *vm) : _vm(vm) { _doRangeClamp = false; _surfWidth = 320; _surfHeight = 200; _scrollOffsetX = 0; _scrollOffsetY = 0; _splitHeight1 = 200; _splitHeight2 = 0; _splitStart = 0; _screenDeltaX = 0; _screenDeltaY = 0; _curSparse = 0; _lastSparse = 0xFFFFFFFF; _dirtyAll = false; } Video::~Video() { } void Video::initPrimary(int16 mode) { if ((mode != 3) && (mode != -1)) _vm->validateVideoMode(mode); _vm->validateVideoMode(_vm->_global->_videoMode); if (mode == -1) mode = 3; _vm->_global->_oldMode = mode; if (mode != 3) { initSurfDesc(_surfWidth, _surfHeight, PRIMARY_SURFACE); if (!_vm->_global->_dontSetPalette) Video::setFullPalette(_vm->_global->_pPaletteDesc); } } SurfacePtr Video::initSurfDesc(int16 width, int16 height, int16 flags) { SurfacePtr descPtr; if (flags & PRIMARY_SURFACE) assert((width == _surfWidth) && (height == _surfHeight)); if (flags & PRIMARY_SURFACE) { _vm->_global->_primaryWidth = width; _vm->_global->_primaryHeight = height; descPtr = _vm->_global->_primarySurfDesc; descPtr->resize(width, height); } else { assert(!(flags & DISABLE_SPR_ALLOC)); if (!(flags & SCUMMVM_CURSOR)) width = (width + 7) & 0xFFF8; descPtr = SurfacePtr(new Surface(width, height, _vm->getPixelFormat().bytesPerPixel)); } return descPtr; } void Video::clearScreen() { g_system->fillScreen(0); } void Video::setSize(bool defaultTo1XScaler) { if (_vm->isTrueColor()) initGraphics(_vm->_width, _vm->_height, defaultTo1XScaler, 0); else initGraphics(_vm->_width, _vm->_height, defaultTo1XScaler); } void Video::retrace(bool mouse) { if (mouse) CursorMan.showMouse((_vm->_draw->_showCursor & 6) != 0); if (_vm->_global->_primarySurfDesc) { int screenX = _screenDeltaX; int screenY = _screenDeltaY; int screenWidth = MIN(_surfWidth - _scrollOffsetX, _vm->_width); int screenHeight = MIN(_surfHeight - _splitHeight2 - _scrollOffsetY, _vm->_height - _splitHeight2); dirtyRectsApply(_scrollOffsetX, _scrollOffsetY, screenWidth, screenHeight, screenX, screenY); if (_splitSurf) { screenX = 0; screenY = _vm->_height - _splitSurf->getHeight(); screenWidth = MIN(_vm->_width, _splitSurf->getWidth()); screenHeight = _splitSurf->getHeight(); _splitSurf->blitToScreen(0, 0, screenWidth - 1, screenHeight - 1, screenX, screenY); } else if (_splitHeight2 > 0) { screenX = 0; screenY = _vm->_height - _splitHeight2; screenWidth = MIN(_surfWidth, _vm->_width); screenHeight = _splitHeight2; dirtyRectsApply(0, _splitStart, screenWidth, screenHeight, screenX, screenY); } dirtyRectsClear(); g_system->updateScreen(); } } void Video::waitRetrace(bool mouse) { uint32 time = _vm->_util->getTimeKey(); retrace(mouse); _vm->_util->delay(MAX(1, 10 - (int)(_vm->_util->getTimeKey() - time))); } void Video::sparseRetrace(int max) { uint32 timeKey = _vm->_util->getTimeKey(); if ((_curSparse++ > max) || ((timeKey - _lastSparse) > 1000)) { _curSparse = 0; retrace(false); } _lastSparse = timeKey; } void Video::drawPacked(byte *sprBuf, int16 width, int16 height, int16 x, int16 y, byte transp, Surface &dest) { int destRight = x + width; int destBottom = y + height; Pixel dst = dest.get(x, y); int curx = x; int cury = y; while (1) { uint8 val = *sprBuf++; unsigned int repeat = val & 7; val &= 0xF8; if (!(val & 8)) { repeat <<= 8; repeat |= *sprBuf++; } repeat++; val >>= 4; for (unsigned int i = 0; i < repeat; ++i) { if (curx < dest.getWidth() && cury < dest.getHeight()) if (!transp || val) dst.set(val); dst++; curx++; if (curx == destRight) { dst += dest.getWidth() + x - curx; curx = x; cury++; if (cury == destBottom) return; } } } } void Video::drawPackedSprite(byte *sprBuf, int16 width, int16 height, int16 x, int16 y, int16 transp, Surface &dest) { if (spriteUncompressor(sprBuf, width, height, x, y, transp, dest)) return; drawPacked(sprBuf, width, height, x, y, transp, dest); } void Video::drawPackedSprite(const char *path, Surface &dest, int width) { int32 size; byte *data = _vm->_dataIO->getFile(path, size); if (!data) { warning("Video::drawPackedSprite(): Failed to open sprite \"%s\"", path); return; } drawPackedSprite(data, width, dest.getHeight(), 0, 0, 0, dest); delete[] data; } void Video::setPalElem(int16 index, char red, char green, char blue, int16 unused, int16 vidMode) { byte pal[3]; _vm->validateVideoMode(vidMode); _vm->_global->_redPalette[index] = red; _vm->_global->_greenPalette[index] = green; _vm->_global->_bluePalette[index] = blue; setPalColor(pal, red, green, blue); if (_vm->getPixelFormat().bytesPerPixel == 1) g_system->getPaletteManager()->setPalette(pal, index, 1); } void Video::setPalette(PalDesc *palDesc) { byte pal[768]; int16 numcolors; _vm->validateVideoMode(_vm->_global->_videoMode); numcolors = _vm->_global->_setAllPalette ? 256 : 16; for (int i = 0; i < numcolors; i++) setPalColor(pal + i * 3, palDesc->vgaPal[i]); if (_vm->getPixelFormat().bytesPerPixel == 1) g_system->getPaletteManager()->setPalette(pal, 0, numcolors); } void Video::setFullPalette(PalDesc *palDesc) { if (_vm->_global->_setAllPalette) { byte pal[768]; Color *colors = palDesc->vgaPal; for (int i = 0; i < 256; i++) { _vm->_global->_redPalette[i] = colors[i].red; _vm->_global->_greenPalette[i] = colors[i].green; _vm->_global->_bluePalette[i] = colors[i].blue; setPalColor(pal + i * 3, colors[i]); } if (_vm->getPixelFormat().bytesPerPixel == 1) g_system->getPaletteManager()->setPalette(pal, 0, 256); } else Video::setPalette(palDesc); } void Video::setPalette(Color *palette) { Color *palBak; bool setAllPalBak; palBak = _vm->_global->_pPaletteDesc->vgaPal; setAllPalBak = _vm->_global->_setAllPalette; _vm->_global->_pPaletteDesc->vgaPal = palette; _vm->_global->_setAllPalette = true; setFullPalette(_vm->_global->_pPaletteDesc); _vm->_global->_setAllPalette = setAllPalBak; _vm->_global->_pPaletteDesc->vgaPal = palBak; } void Video::dirtyRectsClear() { _dirtyRects.clear(); _dirtyAll = false; } void Video::dirtyRectsAll() { _dirtyRects.clear(); _dirtyAll = true; } void Video::dirtyRectsAdd(int16 left, int16 top, int16 right, int16 bottom) { if (_dirtyAll) return; _dirtyRects.push_back(Common::Rect(left, top, right + 1, bottom + 1)); } void Video::dirtyRectsApply(int left, int top, int width, int height, int x, int y) { if (_dirtyAll) { _vm->_global->_primarySurfDesc->blitToScreen(left, top, left + width - 1, top + height - 1, x, y); return; } int right = left + width; int bottom = top + height; Common::List::const_iterator it; for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) { int l = MAX(left , it->left); int t = MAX(top , it->top); int r = MIN(right , it->right); int b = MIN(bottom, it->bottom); if ((r <= l) || (b <= t)) continue; _vm->_global->_primarySurfDesc->blitToScreen(l, t, r - 1, b - 1, x + (l - left), y + (t - top)); } } } // End of namespace Gob