mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-05 00:36:57 +00:00
477 lines
11 KiB
C++
477 lines
11 KiB
C++
/* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*
|
|
* This file is dual-licensed.
|
|
* In addition to the GPLv3 license mentioned above, this code is also
|
|
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
|
* full text of the license.
|
|
*
|
|
*/
|
|
|
|
#include "common/endian.h"
|
|
|
|
#include "engines/util.h"
|
|
|
|
#include "graphics/cursorman.h"
|
|
#include "graphics/paletteman.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 = nullptr;
|
|
|
|
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 == nullptr;
|
|
}
|
|
|
|
void Font::drawLetter(Surface &surf, uint8 c, uint16 x, uint16 y,
|
|
uint32 color1, uint32 color2, bool transp) const {
|
|
|
|
uint16 data;
|
|
|
|
if (c == '\r' || c == '\n')
|
|
return;
|
|
|
|
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 nullptr;
|
|
}
|
|
|
|
if ((c < _startItem) || (c > _endItem))
|
|
return nullptr;
|
|
|
|
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) && _vm->getGameType() != kGameTypeAdibou2)
|
|
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() {
|
|
if (_vm->isTrueColor())
|
|
initGraphics(_vm->_width, _vm->_height, nullptr);
|
|
else
|
|
initGraphics(_vm->_width, _vm->_height);
|
|
}
|
|
|
|
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<int>(_surfWidth - _scrollOffsetX, _vm->_width);
|
|
int screenHeight = MIN<int>(_surfHeight - _splitHeight2 - _scrollOffsetY, _vm->_height - _splitHeight2);
|
|
|
|
dirtyRectsApply(_scrollOffsetX, _scrollOffsetY, screenWidth, screenHeight,
|
|
screenX, screenY);
|
|
|
|
if (_splitSurf) {
|
|
|
|
screenX = 0;
|
|
screenY = _vm->_height - _splitSurf->getHeight();
|
|
screenWidth = MIN<int>(_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<int>(_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<Common::Rect>::const_iterator it;
|
|
for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
|
|
int l = MAX<int>(left , it->left);
|
|
int t = MAX<int>(top , it->top);
|
|
int r = MIN<int>(right , it->right);
|
|
int b = MIN<int>(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
|