/* 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 . * */ #include "common/file.h" #include "common/system.h" #include "engines/util.h" #include "graphics/cursorman.h" #include "graphics/pixelformat.h" #include "graphics/surface.h" #include "trecision/actor.h" #include "trecision/animmanager.h" #include "trecision/animtype.h" #include "trecision/defines.h" #include "trecision/graphics.h" #include "trecision/pathfinding3d.h" #include "trecision/renderer3d.h" #include "trecision/text.h" #include "trecision/trecision.h" #include "trecision/video.h" namespace Trecision { GraphicsManager::GraphicsManager(TrecisionEngine *vm) : _vm(vm), _rgb555Format(2, 5, 5, 5, 0, 10, 5, 0, 0) { for (int i = 0; i < 3; ++i) _bitMask[i] = 0; for (int i = 0; i < 256; ++i) { _fonts[i]._width = 0; _fonts[i]._data = nullptr; } } GraphicsManager::~GraphicsManager() { _screenBuffer.free(); _background.free(); _smkBackground.free(); _leftInventoryArrow.free(); _rightInventoryArrow.free(); _inventoryIcons.free(); _saveSlotThumbnails.free(); _textureMat.free(); for (int i = 0; i < 256; ++i) delete[] _fonts[i]._data; } bool GraphicsManager::init() { // Find a suitable 16-bit format, currently we don't support other color depths Common::List formats = g_system->getSupportedFormats(); for (Common::List::iterator it = formats.begin(); it != formats.end(); ++it) { if (it->bytesPerPixel != 2 || it->aBits()) { it = formats.reverse_erase(it); } else if (*it == _rgb555Format) { formats.clear(); formats.push_back(_rgb555Format); break; } } if (formats.empty()) return false; initGraphics(MAXX, MAXY, formats); _screenFormat = g_system->getScreenFormat(); if (_screenFormat.bytesPerPixel != 2) return false; _bitMask[0] = _screenFormat.rMax() << _screenFormat.rShift; _bitMask[1] = _screenFormat.gMax() << _screenFormat.gShift; _bitMask[2] = _screenFormat.bMax() << _screenFormat.bShift; clearScreen(); _screenBuffer.create(MAXX, MAXY, _screenFormat); _background.create(MAXX, MAXY, _screenFormat); _smkBackground.create(MAXX, AREA, _screenFormat); _saveSlotThumbnails.create(READICON * ICONDX, ICONDY, _screenFormat); loadData(); initCursor(); hideCursor(); return true; } void GraphicsManager::addDirtyRect(Common::Rect rect, bool translateRect) { if (translateRect) rect.translate(0, TOP); _dirtyRects.push_back(rect); } void GraphicsManager::drawObj(int index, bool mask, Common::Rect drawRect, Common::Rect drawObjRect, bool includeDirtyRect) { if (drawObjRect.left > MAXX || drawObjRect.top > MAXY) return; // If we have a valid object, draw it, otherwise erase it // by using the background buffer const uint16 *buf = index >= 0 ? _vm->_objectGraphics[index].buf : (uint16 *)_smkBackground.getPixels(); if (mask && index >= 0) { uint8 *maskPtr = _vm->_objectGraphics[index].mask; for (uint16 y = drawRect.top; y < drawRect.bottom; ++y) { uint16 x = 0; bool copyBytes = false; while (x < drawRect.width()) { if (!copyBytes) { // jump x += *maskPtr; ++maskPtr; copyBytes = true; } else { // copy const uint16 maskOffset = *maskPtr; if (maskOffset != 0 && y >= drawRect.top + drawObjRect.top && y < drawRect.top + drawObjRect.bottom) { const void *src = (x >= drawObjRect.left) ? buf : buf + drawObjRect.left - x; int offset = (x >= drawObjRect.left) ? x : drawObjRect.left; void *dst = _screenBuffer.getBasePtr(offset + drawRect.left, y); if (x >= drawObjRect.left && x + maskOffset < drawObjRect.right) memcpy(dst, src, maskOffset * 2); else if (x < drawObjRect.left && x + maskOffset < drawObjRect.right && x + maskOffset >= drawObjRect.left) memcpy(dst, src, (maskOffset + x - drawObjRect.left) * 2); else if (x >= drawObjRect.left && x + maskOffset >= drawObjRect.right && x < drawObjRect.right) memcpy(dst, src, (drawObjRect.right - x) * 2); else if (x < drawObjRect.left && x + maskOffset >= drawObjRect.right) memcpy(dst, src, (drawObjRect.right - drawObjRect.left) * 2); } x += *maskPtr; buf += *maskPtr++; copyBytes = false; } } } } else { const uint16 x = drawRect.left + drawObjRect.left; if (x + drawObjRect.width() > MAXX || drawObjRect.top + drawObjRect.height() > MAXY) { warning("drawObj: Invalid surface, skipping"); return; } for (uint16 y = drawObjRect.top; y < drawObjRect.bottom; ++y) { memcpy(_screenBuffer.getBasePtr(x, drawRect.top + y), buf + (y * drawRect.width()) + drawObjRect.left, drawObjRect.width() * 2); } } if (includeDirtyRect) addDirtyRect(drawObjRect, true); } void GraphicsManager::eraseObj(Common::Rect drawObjRect) { Common::Rect eraseRect = drawObjRect; eraseRect.translate(0, TOP); if (eraseRect.isValidRect()) _screenBuffer.fillRect(eraseRect, 0); } void GraphicsManager::clearScreen() { g_system->fillScreen(0); } void GraphicsManager::copyToScreenBuffer(const Graphics::Surface *surface, int x, int y, const byte *palette) { Graphics::Surface *surface16 = surface->convertTo(_screenFormat, palette); copyToScreenBufferInner(surface16, x, y); surface16->free(); delete surface16; } void GraphicsManager::copyToScreenBufferInner(const Graphics::Surface *surface, int x, int y) { if (x + surface->w > MAXX || y + surface->h > MAXY) { warning("copyToScreenBufferInner: Invalid surface, skipping"); return; } for (int curY = 0; curY < surface->h; ++curY) { // NOTE: We use surface width for the pitch so that memcpy works // correctly with surfaces from getSubArea() memcpy(_screenBuffer.getBasePtr(x, y + curY), surface->getBasePtr(0, curY), surface->w * 2); } } void GraphicsManager::blitToScreenBuffer(const Graphics::Surface *surface, int x, int y, const byte *palette, bool useSmkBg) { if (x + surface->w > MAXX || y + surface->h > MAXY) { warning("blitToScreenBuffer: Invalid surface, skipping"); return; } const uint16 mask = (uint16)_screenFormat.RGBToColor(palette[0], palette[1], palette[2]); Graphics::Surface *surface16 = surface->convertTo(_screenFormat, palette); for (int curY = 0; curY < surface16->h; ++curY) { for (int curX = 0; curX < surface16->w; ++curX) { const int destX = x + curX; const int destY = y + curY; const uint16 pixel = (uint16)surface16->getPixel(curX, curY); if (pixel != mask) { _screenBuffer.setPixel(destX, destY, pixel); if (useSmkBg) _smkBackground.setPixel(destX, destY - TOP, pixel); } else if (useSmkBg) { const uint16 bgPixel = _background.getPixel(destX, destY - TOP); _screenBuffer.setPixel(destX, destY, bgPixel); _smkBackground.setPixel(destX, destY - TOP, bgPixel); } } } surface16->free(); delete surface16; } void GraphicsManager::copyToScreen(int x, int y, int w, int h) { g_system->copyRectToScreen( _screenBuffer.getBasePtr(x, y), MAXX * 2, x, y, w, h ); } void GraphicsManager::readSurface(Common::SeekableReadStream *stream, Graphics::Surface *surface, uint16 width, uint16 height, uint16 count) { surface->create(width * count, height, _rgb555Format); for (uint16 i = 0; i < count; ++i) { for (uint16 y = 0; y < height; ++y) { for (uint16 x = 0; x < width; ++x) { surface->setPixel(width * i + x, y, stream->readUint16LE()); } } } surface->convertToInPlace(_screenFormat); } void GraphicsManager::readTexture(Common::SeekableReadStream *stream) { readSurface(stream, &_textureMat, 91, 256); } void GraphicsManager::drawTexturePixel(uint16 textureX, uint16 textureY, uint16 screenX, uint16 screenY) { const uint16 texturePixel = (uint16)_textureMat.getPixel(textureX, textureY); _screenBuffer.setPixel(screenX, screenY, texturePixel); } void GraphicsManager::loadBackground(Common::SeekableReadStream *stream) { SObject bgInfo; bgInfo.readRect(stream); readSurface(stream, &_background, bgInfo._rect.width(), bgInfo._rect.height()); _smkBackground.copyFrom(_background); memcpy(_screenBuffer.getBasePtr(0, TOP), _background.getPixels(), _background.pitch * _background.h); } void GraphicsManager::loadData() { Common::SeekableReadStream *arrowsDataFile = _vm->_dataFile.createReadStreamForMember("frecc.bm"); // The data file contains images for deactivated arrows, which aren't used. Skip them. arrowsDataFile->skip(ICONMARGDX * ICONDY * 2 * 3); readSurface(arrowsDataFile, &_leftInventoryArrow, ICONMARGSX, ICONDY); readSurface(arrowsDataFile, &_rightInventoryArrow, ICONMARGSX, ICONDY); delete arrowsDataFile; Common::SeekableReadStream *iconsDataFile = _vm->_dataFile.createReadStreamForMember("icone.bm"); readSurface(iconsDataFile, &_inventoryIcons, ICONDX, ICONDY, READICON); delete iconsDataFile; loadFont(); } void GraphicsManager::setSaveSlotThumbnail(byte iconSlot, const Graphics::Surface *thumbnail) { Graphics::Surface *scaled = thumbnail->scale(ICONDX, ICONDY); scaled->convertToInPlace(_screenFormat); for (uint16 y = 0; y < ICONDY; ++y) { memcpy(_saveSlotThumbnails.getBasePtr(ICONDX * iconSlot, y), scaled->getBasePtr(0, y), ICONDX * 2); } scaled->free(); delete scaled; } void GraphicsManager::drawLeftInventoryArrow(byte startLine) { Graphics::Surface arrow = _leftInventoryArrow.getSubArea(Common::Rect( 0, startLine, _leftInventoryArrow.w, _leftInventoryArrow.h )); copyToScreenBufferInner(&arrow, 0, FIRSTLINE); } void GraphicsManager::drawRightInventoryArrow(byte startLine) { Graphics::Surface arrow = _rightInventoryArrow.getSubArea(Common::Rect( 0, startLine, _rightInventoryArrow.w, _rightInventoryArrow.h )); copyToScreenBufferInner(&arrow, MAXX - ICONMARGDX, FIRSTLINE); } void GraphicsManager::drawInventoryIcon(byte iconIndex, byte iconSlot, byte startLine) { Graphics::Surface icon = _inventoryIcons.getSubArea(Common::Rect( iconIndex * ICONDX, startLine, iconIndex * ICONDX + ICONDX, _inventoryIcons.h )); copyToScreenBufferInner(&icon, iconSlot * ICONDX + ICONMARGSX, FIRSTLINE); } void GraphicsManager::drawSaveSlotThumbnail(byte iconIndex, byte iconSlot, byte startLine) { Graphics::Surface icon = _saveSlotThumbnails.getSubArea(Common::Rect( iconIndex * ICONDX, startLine, iconIndex * ICONDX + ICONDX, _saveSlotThumbnails.h )); copyToScreenBufferInner(&icon, iconSlot * ICONDX + ICONMARGSX, FIRSTLINE); } void GraphicsManager::clearScreenBuffer() { _screenBuffer.fillRect(Common::Rect(0, 0, MAXX, MAXY), 0); } void GraphicsManager::clearScreenBufferTop() { // Clears lines 0 - 60 _screenBuffer.fillRect(Common::Rect(0, 0, MAXX, TOP), 0); } void GraphicsManager::clearScreenBufferInventory() { // Clears lines 420 - 480 _screenBuffer.fillRect(Common::Rect(0, FIRSTLINE, MAXX, MAXY), 0); } void GraphicsManager::clearScreenBufferSaveSlotDescriptions() { // Clears lines 470 - 480 _screenBuffer.fillRect(Common::Rect(0, FIRSTLINE + ICONDY + 10, MAXX, MAXY), 0); } uint16 GraphicsManager::convertToScreenFormat(uint16 color) const { uint8 r, g, b; _rgb555Format.colorToRGB(color, r, g, b); return (uint16)_screenFormat.RGBToColor(r, g, b); } /** * Shadow Pixel * (dark) 0..8 (light) */ void GraphicsManager::shadow(uint16 x, uint16 y, uint8 num) { if (x > MAXX || y > MAXY) { warning("shadow: Invalid pixel, skipping"); return; } const uint16 val = (uint16)_screenBuffer.getPixel(x, y); const uint16 shadowVal = ((((val & _bitMask[2]) * num >> 7) & _bitMask[2]) | (((val & _bitMask[1]) * num >> 7) & _bitMask[1]) | (((val & _bitMask[0]) * num >> 7) & _bitMask[0])); _screenBuffer.setPixel(x, y, shadowVal); } void GraphicsManager::pixelAliasing(uint16 x, uint16 y) { if (x > MAXX || y > MAXY) { warning("pixelAliasing: Invalid pixel, skipping"); return; } int px1 = _screenBuffer.getPixel(x - 1, y); int px2 = _screenBuffer.getPixel(x, y); _screenBuffer.setPixel(x - 1, y, aliasing(px1, px2, 6)); // 75% 25% _screenBuffer.setPixel(x, y, aliasing(px1, px2, 2)); // 25% 75% } /** * Aliasing Pixel */ uint16 GraphicsManager::aliasing(uint32 val1, uint32 val2, uint8 num) { // 0: 0% val1 100% val2 // 1: 12% val1 87% val2 // 2: 25% val1 75% val2 // 3: 37% val1 62% val2 // 4: 50% val1 50% val2 // 5: 62% val1 37% val2 // 6: 75% val1 25% val2 // 7: 87% val1 12% val2 // 8: 100% val1 0% val2 return (((((val1 & _bitMask[2]) * num + (val2 & _bitMask[2]) * (8 - num)) >> 3) & _bitMask[2]) | ((((val1 & _bitMask[1]) * num + (val2 & _bitMask[1]) * (8 - num)) >> 3) & _bitMask[1]) | ((((val1 & _bitMask[0]) * num + (val2 & _bitMask[0]) * (8 - num)) >> 3) & _bitMask[0])); } void GraphicsManager::dissolve() { const uint16 val = 30; uint16 centerX = MAXX / 2; uint16 centerY = MAXY / 2; int lastv = 9000; uint32 sv = _vm->readTime(); uint32 cv = _vm->readTime(); while (sv + val > cv) { _vm->checkSystem(); if (lastv + cv < sv + val) { cv = _vm->readTime(); continue; } lastv = (sv - cv) + val; const float a = (float)(((centerX + 200) / val) * lastv); const float b = (float)((centerY / val) * lastv); float x = 0.0f; float y = b; if (centerY - (int)y > TOP) memset(_screenBuffer.getBasePtr(0, TOP), 0, (centerY - (int)y - TOP) * MAXX * 2); if (AREA + TOP > centerY + (int)y) memset(_screenBuffer.getBasePtr(0, centerY + (int)y), 0, (AREA + TOP - (centerY + (int)y)) * MAXX * 2); float d1 = b * b - a * a * b + a * a / 4.0f; while (_vm->floatComp(a * a * (y - 0.5f), b * b * (x + 1.0f)) == 1) { if (_vm->floatComp(d1, 0.0f) == -1) d1 += b * b * (2.0f * x + 3.0f); else { d1 += b * b * (2.0f * x + 3.0f) + a * a * (-2.0f * y + 2.0f); y -= 1.0f; } x += 1.0f; int rightX = centerX + (int)x; int maxY = centerY + (int)y; int minY = centerY - (int)y; if (rightX < MAXX) { if (maxY < MAXY) memset(_screenBuffer.getBasePtr(rightX, maxY), 0, (MAXX - rightX) * 2); if (minY >= 0) memset(_screenBuffer.getBasePtr(rightX, minY), 0, (MAXX - rightX) * 2); } int leftX = centerX - (int)x; if (leftX > 0) { if (maxY < MAXY) memset(_screenBuffer.getBasePtr(0, maxY), 0, leftX * 2); if (minY >= 0) memset(_screenBuffer.getBasePtr(0, minY), 0, leftX * 2); } } float d2 = b * b * (x + 0.5f) * (x + 0.5f) + a * a * (y - 1.0f) * (y - 1.0f) - a * a * b * b; while (_vm->floatComp(y, 0.0f) == 1) { if (_vm->floatComp(d2, 0.0f) == -1) { d2 += b * b * (2.0f * x + 2.0f) + a * a * (-2.0f * y + 3.0f); x += 1.0f; } else d2 += a * a * (-2.0f * y + 3.0f); y -= 1.0f; int rightX = centerX + (int)x; int maxY = centerY + (int)y; int minY = centerY - (int)y; if (rightX < MAXX) { if (maxY < MAXY) memset(_screenBuffer.getBasePtr(rightX, maxY), 0, (MAXX - rightX) * 2); if (minY >= 0) memset(_screenBuffer.getBasePtr(rightX, minY), 0, (MAXX - rightX) * 2); } int leftX = centerX - (int)x; if (leftX > 0) { if (maxY < MAXY) memset(_screenBuffer.getBasePtr(0, maxY), 0, leftX * 2); if (minY >= 0) memset(_screenBuffer.getBasePtr(0, minY), 0, leftX * 2); } } copyToScreen(0, 0, MAXX, MAXY); cv = _vm->readTime(); } clearScreen(); } void GraphicsManager::paintScreen(bool flag) { _vm->_animTypeMgr->next(); _dirtyRects.clear(); _vm->_flagPaintCharacter = true; // always redraws the character // erase character if (_vm->_flagShowCharacter && _vm->_actor->actorRectIsValid()) { // if a description exists Common::Rect actorRect = _vm->_actor->getActorRect(); actorRect.translate(0, -TOP); drawObj(-1, false, Common::Rect(0, TOP, MAXX, AREA + TOP), actorRect); } else if (_vm->_animMgr->_animRect.left != MAXX) { drawObj(-1, false, Common::Rect(0, TOP, MAXX, AREA + TOP), _vm->_animMgr->_animRect); } // If there's text to remove if (_vm->_textStatus & TEXT_DEL) { // remove text Common::Rect drawObjRect = _vm->_textMgr->getOldTextRect(); drawObjRect.translate(0, -TOP); if (drawObjRect.top >= 0 && drawObjRect.bottom < AREA) { drawObj(-1, false, Common::Rect(0, TOP, MAXX, MAXY + TOP), drawObjRect); } else { eraseObj(drawObjRect); } _vm->_textMgr->clearOldText(); if (!(_vm->_textStatus & TEXT_DRAW)) // if there's no new text _vm->_textStatus = TEXT_OFF; // stop updating text } // Suppress all the objects you removed for (Common::List::iterator it = _vm->_sortTable.begin(); it != _vm->_sortTable.end(); ++it) { if (it->_remove) { drawObj(-1, false, Common::Rect(0, TOP, MAXX, AREA + TOP), _vm->_obj[it->_objectId]._rect); } } // Find the position of the character _vm->_pathFind->actorOrder(); // For every box from the horizon forward... // Copy per level for (int liv = _vm->_pathFind->_numSortPanel; liv >= 0; --liv) { uint16 curBox = _vm->_pathFind->_sortPan[liv]._num; // draws all objects and animations that intersect the boundaries and refer to the current box paintObjAnm(curBox); } if (_vm->_textStatus & TEXT_DRAW) { _vm->_textMgr->drawCurString(); _vm->_textStatus = TEXT_DRAW; // Activate text update } _vm->_actor->updateStepSound(); if (!flag && !_vm->_flagDialogActive) { copyToScreen(0, 0, MAXX, MAXY); } _vm->_sortTable.clear(); _vm->_flagPaintCharacter = false; _vm->_flagWaitRegen = false; // Handle papaverine delayed action if (_vm->_curRoom == kRoom4A && _vm->_obj[oCHOCOLATES4A].isFlagExtra()) { if (_vm->_animMgr->smkCurFrame(kSmackerBackground) > 480) { _vm->playScript(s4AHELLEN); _vm->_obj[oCHOCOLATES4A].setFlagExtra(false); } } // } /** * Draw all objects and animations that intersect * boundaries belonging to curbox */ void GraphicsManager::paintObjAnm(uint16 curBox) { _vm->_animMgr->refreshAnim(curBox); // draws new cards belonging to the current box for (Common::List::iterator it = _vm->_sortTable.begin(); it != _vm->_sortTable.end(); ++it) { if (!it->_remove && _vm->_obj[it->_objectId]._nbox == curBox) { // the bitmap object at the desired level SObject obj = _vm->_obj[it->_objectId]; Common::Rect drawRect = obj._rect; drawRect.translate(0, TOP); drawObj(_vm->getRoomObjectIndex(it->_objectId), obj.isModeMask(), drawRect, Common::Rect(drawRect.width(), drawRect.height()), false); _dirtyRects.push_back(drawRect); } } for (DirtyRectsIterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) { for (int i = 0; i < MAXOBJINROOM; ++i) { const uint16 curObject = _vm->_room[_vm->_curRoom]._object[i]; if (!curObject) break; SObject obj = _vm->_obj[curObject]; if ((obj.isModeFull() || obj.isModeMask()) && _vm->isObjectVisible(curObject) && (obj._nbox == curBox)) { Common::Rect r = *it; Common::Rect r2 = obj._rect; r2.translate(0, TOP); // Include the bottom right of the rect in the intersects() check ++r2.bottom; ++r2.right; if (r.intersects(r2)) { Common::Rect drawRect = obj._rect; drawRect.translate(0, TOP); // Restore the bottom right of the rect --r2.bottom; --r2.right; // TODO: Simplify this? const int16 xr1 = (r2.left > r.left) ? 0 : r.left - r2.left; const int16 yr1 = (r2.top > r.top) ? 0 : r.top - r2.top; const int16 xr2 = MIN(r.right, r2.right) - r2.left; const int16 yr2 = MIN(r.bottom, r2.bottom) - r2.top; drawObj(i, obj.isModeMask(), drawRect, Common::Rect(xr1, yr1, xr2, yr2), false); } } } } if (_vm->_pathFind->getActorPos() == curBox && _vm->_flagShowCharacter) { _vm->_renderer->drawCharacter(CALCPOINTS); if (_vm->_actor->actorRectIsValid()) { const Common::Rect actorRect = _vm->_actor->getActorRect(); // enlarge the last dirty rectangle with the actor's rectangle if (!_dirtyRects.empty()) _dirtyRects.back().extend(actorRect); _vm->_renderer->resetZBuffer(actorRect); } _vm->_renderer->drawCharacter(DRAWFACES); } else if (_vm->_pathFind->getActorPos() == curBox && !_vm->_flagDialogActive) { _vm->_animMgr->refreshActionAnimation(); } } uint16 GraphicsManager::getCharWidth(byte character) { return _fonts[character]._width; } void GraphicsManager::drawChar(byte curChar, uint16 textColor, uint16 line, Common::Rect rect, Common::Rect subtitleRect, uint16 inc, Graphics::Surface *externalSurface) { uint16 fontDataOffset = 0; const uint16 charWidth = getCharWidth(curChar); for (uint16 y = line * CARHEI; y < (line + 1) * CARHEI; ++y) { uint16 curPos = 0; uint16 curColor = MASKCOL; while (curPos <= charWidth - 1) { if (y >= subtitleRect.top && y < subtitleRect.bottom) { if (curColor != MASKCOL && _fonts[curChar]._data[fontDataOffset]) { const uint16 charLeft = inc + curPos; const uint16 charRight = charLeft + _fonts[curChar]._data[fontDataOffset]; drawCharPixel( y, charLeft, charRight, rect, subtitleRect, curColor, externalSurface ); } } curPos += _fonts[curChar]._data[fontDataOffset]; ++fontDataOffset; if (curColor == MASKCOL) curColor = 0; else if (curColor == 0) curColor = textColor; else if (curColor == textColor) curColor = MASKCOL; } } } void GraphicsManager::drawCharPixel(uint16 y, uint16 charLeft, uint16 charRight, Common::Rect rect, Common::Rect subtitleRect, uint16 color, Graphics::Surface *externalSurface) { Graphics::Surface *surface = externalSurface ? externalSurface : &_screenBuffer; uint16 *dst1 = (uint16 *)surface->getBasePtr(rect.left + charLeft, rect.top + y); uint16 *dst2 = (uint16 *)surface->getBasePtr(rect.left + subtitleRect.left, rect.top + y); uint16 *dst = nullptr; uint16 size = 0; if (charLeft >= subtitleRect.left && charRight < subtitleRect.right) { dst = dst1; size = charRight - charLeft; } else if (charLeft < subtitleRect.left && charRight < subtitleRect.right && charRight > subtitleRect.left) { dst = dst2; size = charRight - subtitleRect.left; } else if (charLeft >= subtitleRect.left && charRight >= subtitleRect.right && subtitleRect.right > charLeft) { dst = dst1; size = subtitleRect.right - charLeft; } else if (charLeft < subtitleRect.left && charRight >= subtitleRect.right && subtitleRect.right > charLeft) { dst = dst2; size = subtitleRect.right - subtitleRect.left; } if (dst && size > 0) { uint16 *d = dst; for (uint32 i = 0; i < size; ++i) *d++ = color; } } void GraphicsManager::initCursor() { const int cw = 21, ch = 21; const int cx = 10, cy = 10; uint16 cursor[cw * ch]; memset(cursor, 0, ARRAYSIZE(cursor) * 2); const uint16 cursorColor = (uint16)_screenFormat.RGBToColor(255, 255, 255); for (int i = 0; i < cw; ++i) { if (i >= 8 && i <= 12 && i != 10) continue; cursor[cx * cw + i] = cursorColor; // horizontal cursor[cx + cw * i] = cursorColor; // vertical } CursorMan.pushCursor(cursor, cw, ch, cx, cy, 0, false, &_screenFormat); } void GraphicsManager::showCursor() { CursorMan.showMouse(true); } void GraphicsManager::hideCursor() { CursorMan.showMouse(false); } void GraphicsManager::loadFont() { const char *fileName = "nlfont.fnt"; Common::SeekableReadStream *fontStream = _vm->_dataFile.createReadStreamForMember(fileName); if (fontStream == nullptr) error("readData(): File %s not found", fileName); uint16 fontDataOffset = 768; for (int i = 0; i < 256; ++i) { uint16 offset = fontStream->readSint16LE(); _fonts[i]._width = fontStream->readByte(); int tmpPos = fontStream->pos(); fontStream->seek(offset + fontDataOffset); int cpt = 0; for (uint16 y = 0; y < CARHEI; ++y) { uint16 curPos = 0; while (curPos <= _fonts[i]._width - 1) { curPos += fontStream->readByte(); ++cpt; } } fontStream->seek(offset + fontDataOffset); _fonts[i]._data = new int8[cpt]; fontStream->read(_fonts[i]._data, cpt); fontStream->seek(tmpPos); } // Fix o+e ligature character (lowercase and uppercase). Ticket #12623 // Format is : // - Each line represents a line of pixels // - colors are in this order : none, shadow, text. Colors are looping until the total number of pixels corresponds to the character width // - each number correspond to a number of pixels of the corresponding color // So, 1, 6, 0, 2 means : 1 pixel unchanged, 6 pixels shadow, 0 pixel in text color, 2 pixels unchanged static const int8 fix140[67] = { 1, 8, 0, 2, 2, 0, 1, 3, 0, 1, 0, 1, 1, 0, 2, 2, 0, 3, 0, 1, 1, 0, 3, 1, 0, 2, 0, 1, 0, 1, 1, 0, 3, 2, 0, 1, 0, 1, 0, 1, 1, 0, 3, 1, 0, 2, 0, 1, 0, 1, 1, 0, 2, 2, 0, 3, 0, 2, 2, 0, 1, 3, 0, 1, 1, 8, 9 }; static const int8 fix156[54] = { 9, 9, 1, 6, 0, 2, 0, 2, 2, 0, 1, 2, 0, 1, 0, 1, 0, 1, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, 1, 0, 2, 4, 0, 1, 0, 1, 1, 0, 2, 1, 0, 4, 0, 2, 2, 0, 1, 3, 0, 1, 1, 8, 9 }; delete _fonts[140]._data; delete _fonts[156]._data; _fonts[140]._width = _fonts[156]._width = 9; _fonts[140]._data = new int8[67]; _fonts[156]._data = new int8[54]; memcpy(_fonts[140]._data, fix140, 67); memcpy(_fonts[156]._data, fix156, 54); } bool GraphicsManager::isCursorVisible() { return CursorMan.isVisible(); } void GraphicsManager::showDemoPic() { Common::File file; if (file.open("EndPic.bm")) { readSurface(&file, &_screenBuffer, MAXX, MAXY); copyToScreen(0, 0, MAXX, MAXY); g_system->updateScreen(); _vm->freeKey(); _vm->_mouseLeftBtn = _vm->_mouseRightBtn = false; _vm->waitKey(); } } } // End of namespace Trecision