1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "kyra/graphics/screen.h"
24 #include "kyra/kyra_v1.h"
25 #include "kyra/resource/resource.h"
26
27 #include "common/endian.h"
28 #include "common/memstream.h"
29 #include "common/system.h"
30 #include "common/config-manager.h"
31
32 #include "engines/util.h"
33
34 #include "graphics/cursorman.h"
35 #include "graphics/palette.h"
36 #include "graphics/sjis.h"
37
38 namespace Kyra {
39
Screen(KyraEngine_v1 * vm,OSystem * system,const ScreenDim * dimTable,const int dimTableSize)40 Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, const int dimTableSize)
41 : _system(system), _vm(vm), _sjisInvisibleColor(0), _dimTable(dimTable), _dimTableCount(dimTableSize),
42 _cursorColorKey((vm->game() == GI_KYRA1 || vm->game() == GI_EOB1 || vm->game() == GI_EOB2) ? 0xFF : 0),
43 _screenHeight(vm->gameFlags().platform == Common::kPlatformSegaCD ? SCREEN_H_SEGA_NTSC : SCREEN_H) {
44 _debugEnabled = false;
45 _maskMinY = _maskMaxY = -1;
46
47 _drawShapeVar1 = 0;
48 _drawShapeVar3 = 1;
49 _drawShapeVar4 = 0;
50 _drawShapeVar5 = 0;
51
52 memset(_fonts, 0, sizeof(_fonts));
53
54 memset(_pagePtrs, 0, sizeof(_pagePtrs));
55 memset(_pageMapping, 0, sizeof(_pageMapping));
56 memset(_sjisOverlayPtrs, 0, sizeof(_sjisOverlayPtrs));
57
58 _renderMode = Common::kRenderDefault;
59 _sjisMixedFontMode = false;
60
61 _screenPalette = _internFadePalette = 0;
62 _animBlockPtr = _textRenderBuffer = 0;
63 _textRenderBufferSize = 0;
64
65 _useHiColorScreen = _vm->gameFlags().useHiColorMode;
66 _useShapeShading = true;
67 _screenPageSize = SCREEN_PAGE_SIZE;
68 _16bitPalette = 0;
69 _16bitConversionPalette = 0;
70 _16bitShadingLevel = 0;
71 _bytesPerPixel = 1;
72 _4bitPixelPacking = _useAmigaExtraColors = _isAmiga = _isSegaCD = _use16ColorMode = false;
73 _useSJIS = _useOverlays = false;
74
75 _currentFont = FID_8_FNT;
76 _fontStyles = 0;
77 _paletteChanged = true;
78 _textMarginRight = SCREEN_W;
79 _customDimTable = 0;
80 _curDim = 0;
81
82 _yTransOffs = 0;
83 }
84
~Screen()85 Screen::~Screen() {
86 for (int i = 0; i < SCREEN_OVLS_NUM; ++i)
87 delete[] _sjisOverlayPtrs[i];
88
89 delete[] _pagePtrs[0];
90
91 for (int f = 0; f < ARRAYSIZE(_fonts); ++f)
92 delete _fonts[f];
93
94 delete _screenPalette;
95 delete _internFadePalette;
96 delete[] _animBlockPtr;
97 delete[] _16bitPalette;
98 delete[] _16bitConversionPalette;
99
100 _sjisFontShared.reset();
101
102 for (uint i = 0; i < _palettes.size(); ++i)
103 delete _palettes[i];
104
105 if (_customDimTable) {
106 for (int i = 0; i < _dimTableCount; ++i)
107 delete _customDimTable[i];
108 delete[] _customDimTable;
109 }
110 }
111
init()112 bool Screen::init() {
113 _debugEnabled = false;
114 _useOverlays = false;
115 _useSJIS = false;
116 _use16ColorMode = _vm->gameFlags().use16ColorMode;
117 _4bitPixelPacking = (_use16ColorMode && _vm->game() == GI_LOL);
118 _isAmiga = (_vm->gameFlags().platform == Common::kPlatformAmiga);
119 _isSegaCD = (_vm->gameFlags().platform == Common::kPlatformSegaCD);
120 // Amiga copper palette magic requires the use of more than 32 colors for some purposes.
121 _useAmigaExtraColors = (_isAmiga && _vm->game() == GI_EOB2);
122
123 // We only check the "render_mode" setting for both Eye of the Beholder
124 // games here, since all the other games do not support the render_mode
125 // setting or handle it differently, like Kyra 1 PC-98. This avoids
126 // graphics glitches and crashes in other games, when the user sets his
127 // global render_mode setting to EGA for example.
128 // TODO/FIXME: It would be nice not to hardcode this. But there is no
129 // trivial/non annoying way to do mode checks in an easy fashion right
130 // now.
131 // In a more general sense, we might want to think about a way to only
132 // pass valid config values, as in values which the engine can work with,
133 // to the engines. We already limit the selection via our GUIO flags in
134 // the game specific settings, but this is not enough due to global
135 // settings allowing everything.
136 if (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2) {
137 if (ConfMan.hasKey("render_mode"))
138 _renderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
139 }
140
141 // In VGA mode the odd and even page pointers point to the same buffers.
142 for (int i = 0; i < SCREEN_PAGE_NUM; i++)
143 _pageMapping[i] = i & ~1;
144 // CGA and EGA modes use additional pages to do the CGA/EGA specific graphics conversions.
145 if (_vm->game() == GI_EOB1 && (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA)) {
146 for (int i = 0; i < 8; i++)
147 _pageMapping[i] = i;
148 }
149
150 memset(_fonts, 0, sizeof(_fonts));
151
152 _useOverlays = (_vm->gameFlags().useHiRes && _renderMode != Common::kRenderEGA);
153
154 if (_useOverlays) {
155 _useSJIS = (_vm->gameFlags().lang == Common::JA_JPN);
156 _sjisInvisibleColor = (_vm->game() == GI_KYRA1) ? 0x80 : 0xF6;
157 _sjisMixedFontMode = !_use16ColorMode;
158
159 if (!_sjisOverlayPtrs[0]) {
160 // We alway assume 2 bytes per pixel here when the backend is in hicolor mode, since this is the surface that is passed to the backend.
161 // We do this regardsless of the paramater sent to enableHiColorMode() so as not to have to change the backend color mode.
162 // Conversions from 8bit to 16bit have to take place when copying data to this surface here.
163 int bpp = _useHiColorScreen ? 2 : 1;
164 _sjisOverlayPtrs[0] = new uint8[SCREEN_OVL_SJIS_SIZE * bpp];
165 assert(_sjisOverlayPtrs[0]);
166 memset(_sjisOverlayPtrs[0], _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE * bpp);
167 }
168
169 for (int i = 1; i < SCREEN_OVLS_NUM; ++i) {
170 if (!_sjisOverlayPtrs[i]) {
171 _sjisOverlayPtrs[i] = new uint8[SCREEN_OVL_SJIS_SIZE];
172 assert(_sjisOverlayPtrs[i]);
173 memset(_sjisOverlayPtrs[i], _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
174 }
175 }
176
177 if (_useSJIS) {
178 _sjisFontShared = Common::SharedPtr<Graphics::FontSJIS>(Graphics::FontSJIS::createFont(_vm->gameFlags().platform));
179 if (!_sjisFontShared.get())
180 error("Could not load any SJIS font, neither the original nor ScummVM's 'SJIS.FNT'");
181
182 if (_use16ColorMode)
183 _fonts[FID_SJIS_TEXTMODE_FNT] = new SJISFont(_sjisFontShared, _sjisInvisibleColor, true, false, 0);
184 else
185 _fonts[FID_SJIS_FNT] = new SJISFont(_sjisFontShared, _sjisInvisibleColor, false, _vm->game() != GI_LOL && _vm->game() != GI_EOB2, _vm->game() == GI_LOL ? 1 : 0);
186 }
187 }
188
189 _curPage = 0;
190
191 enableHiColorMode(false);
192
193 memset(_shapePages, 0, sizeof(_shapePages));
194
195 const int paletteCount = _isAmiga ? 13 : 4;
196 // We allow 256 color palettes in EGA mode, since original EOB II code does the same and requires it
197 const int numColors = _use16ColorMode ? 16 : (_isAmiga ? 32 : (_renderMode == Common::kRenderCGA ? 4 : 256));
198 const int numColorsInternal = _useAmigaExtraColors ? 64 : numColors;
199
200 _dualPaletteModeSplitY = 0;
201
202 _screenPalette = new Palette(numColorsInternal);
203 assert(_screenPalette);
204
205 _palettes.resize(paletteCount);
206 _palettes[0] = new Palette(numColorsInternal);
207 assert(_palettes[0]);
208
209 for (int i = 1; i < paletteCount; ++i) {
210 _palettes[i] = new Palette(numColors);
211 assert(_palettes[i]);
212 }
213
214 // Setup CGA colors (if CGA mode is selected)
215 if (_renderMode == Common::kRenderCGA) {
216 Palette pal(5);
217 pal.setCGAPalette(1, Palette::kIntensityHigh);
218 // create additional black color 4 for use with the mouse cursor manager
219 pal.fill(4, 1, 0);
220 Screen::setScreenPalette(pal);
221 }
222
223 _internFadePalette = new Palette(numColorsInternal);
224 assert(_internFadePalette);
225
226 setScreenPalette(getPalette(0));
227
228 // We setup the PC98 text mode palette at [16, 24], since that will be used
229 // for KANJI characters in Lands of Lore.
230 if (_use16ColorMode && _vm->gameFlags().platform == Common::kPlatformPC98) {
231 uint8 palette[8 * 3];
232
233 for (int i = 0; i < 8; ++i) {
234 palette[i * 3 + 0] = ((i >> 1) & 1) * 0xFF;
235 palette[i * 3 + 1] = ((i >> 2) & 1) * 0xFF;
236 palette[i * 3 + 2] = ((i >> 0) & 1) * 0xFF;
237 }
238
239 _system->getPaletteManager()->setPalette(palette, 16, 8);
240 }
241
242 _customDimTable = new ScreenDim *[_dimTableCount];
243 memset(_customDimTable, 0, sizeof(ScreenDim *) * _dimTableCount);
244
245 _curDimIndex = -1;
246 _curDim = 0;
247 _charSpacing = 0;
248 _lineSpacing = 0;
249 for (int i = 0; i < ARRAYSIZE(_textColorsMap); ++i)
250 _textColorsMap[i] = i;
251 _textColorsMap16bit[0] = _textColorsMap16bit[1] = 0;
252 _animBlockPtr = NULL;
253 _animBlockSize = 0;
254 _mouseLockCount = 1;
255 CursorMan.showMouse(false);
256
257 _forceFullUpdate = false;
258
259 return true;
260 }
261
enableScreenDebug(bool enable)262 bool Screen::enableScreenDebug(bool enable) {
263 bool temp = _debugEnabled;
264
265 if (_debugEnabled != enable) {
266 _debugEnabled = enable;
267 setResolution();
268 _forceFullUpdate = true;
269 updateScreen();
270 }
271
272 return temp;
273 }
274
setResolution()275 void Screen::setResolution() {
276 byte palette[3 * 256];
277 if (!_useHiColorScreen)
278 _system->getPaletteManager()->grabPalette(palette, 0, 256);
279
280 int width = 320, height = 200;
281
282 if (_vm->gameFlags().useHiRes) {
283 height = 400;
284
285 if (_debugEnabled)
286 width = 960;
287 else
288 width = 640;
289 } else {
290 if (_debugEnabled)
291 width = 640;
292 else
293 width = 320;
294 }
295
296 if (_vm->gameFlags().platform == Common::kPlatformSegaCD)
297 height = 224;
298
299 if (_useHiColorScreen) {
300 Graphics::PixelFormat px(2, 5, 5, 5, 0, 10, 5, 0, 0);
301 Common::List<Graphics::PixelFormat> tryModes = _system->getSupportedFormats();
302 for (Common::List<Graphics::PixelFormat>::iterator g = tryModes.begin(); g != tryModes.end(); ++g) {
303 if (g->bytesPerPixel != 2 || g->aBits()) {
304 g = tryModes.reverse_erase(g);
305 } else if (*g == px) {
306 tryModes.clear();
307 tryModes.push_back(px);
308 break;
309 }
310 }
311 initGraphics(width, height, tryModes);
312 if (_system->getScreenFormat().bytesPerPixel != 2)
313 error("Required graphics mode not supported by platform.");
314
315 } else {
316 initGraphics(width, height);
317 _system->getPaletteManager()->setPalette(palette, 0, 256);
318 }
319 }
320
enableHiColorMode(bool enabled)321 void Screen::enableHiColorMode(bool enabled) {
322 if (_useHiColorScreen && enabled) {
323 if (!_16bitPalette)
324 _16bitPalette = new uint16[1024];
325 memset(_16bitPalette, 0, 1024 * sizeof(uint16));
326 delete[] _16bitConversionPalette;
327 _16bitConversionPalette = 0;
328 _bytesPerPixel = 2;
329 } else {
330 if (_useHiColorScreen) {
331 if (!_16bitConversionPalette)
332 _16bitConversionPalette = new uint16[256];
333 memset(_16bitConversionPalette, 0, 256 * sizeof(uint16));
334 }
335
336 delete[] _16bitPalette;
337 _16bitPalette = 0;
338 _bytesPerPixel = 1;
339 }
340
341 resetPagePtrsAndBuffers(_isSegaCD ? SCREEN_W * _screenHeight : SCREEN_PAGE_SIZE * _bytesPerPixel);
342 }
343
updateScreen()344 void Screen::updateScreen() {
345 bool needRealUpdate = _forceFullUpdate || !_dirtyRects.empty() || _paletteChanged;
346 _paletteChanged = false;
347
348 if (_useOverlays)
349 updateDirtyRectsOvl();
350 else if (_isAmiga && _dualPaletteModeSplitY)
351 updateDirtyRectsAmiga();
352 else
353 updateDirtyRects();
354
355 if (_debugEnabled) {
356 needRealUpdate = true;
357
358 if (!_useOverlays)
359 _system->copyRectToScreen(getPagePtr(2), SCREEN_W, 320, 0, SCREEN_W, SCREEN_H);
360 else
361 _system->copyRectToScreen(getPagePtr(2), SCREEN_W, 640, 0, SCREEN_W, SCREEN_H);
362 }
363
364 if (needRealUpdate)
365 _system->updateScreen();
366 }
367
updateDirtyRects()368 void Screen::updateDirtyRects() {
369 if (_forceFullUpdate) {
370 _system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, _yTransOffs, SCREEN_W, _screenHeight - _yTransOffs);
371 } else {
372 const byte *page0 = getCPagePtr(0);
373 Common::List<Common::Rect>::iterator it;
374 for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
375 _system->copyRectToScreen(page0 + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top + _yTransOffs, it->width(), it->height());
376 }
377 }
378 _forceFullUpdate = false;
379 _dirtyRects.clear();
380 }
381
updateDirtyRectsAmiga()382 void Screen::updateDirtyRectsAmiga() {
383 if (_forceFullUpdate) {
384 uint32 *pos = (uint32*)(_pagePtrs[0] + _dualPaletteModeSplitY * SCREEN_W);
385 uint16 h = (SCREEN_H - _dualPaletteModeSplitY) * (SCREEN_W >> 2);
386 while (h--)
387 *pos++ |= 0x20202020;
388 _system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, SCREEN_H);
389 } else {
390 Common::List<Common::Rect>::iterator it;
391 for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
392 if (it->bottom >= _dualPaletteModeSplitY) {
393 int16 startY = MAX<int16>(_dualPaletteModeSplitY, it->top);
394 int16 h = it->bottom - startY + 1;
395 int16 w = it->width();
396 uint8 *pos = _pagePtrs[0] + startY * SCREEN_W + it->left;
397 while (h--) {
398 for (int x = 0; x < w; ++x)
399 *pos++ |= 0x20;
400 pos += (SCREEN_W - w);
401 }
402 }
403 _system->copyRectToScreen(_pagePtrs[0] + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top, it->width(), it->height());
404 }
405 }
406
407 _forceFullUpdate = false;
408 _dirtyRects.clear();
409 }
410
411 #define mScale2x(dst, dstPitch, src, srcPitch, w, h) \
412 if (!_useHiColorScreen) \
413 scale2x<uint8, uint16>(dst, dstPitch, src, srcPitch, w, h); \
414 else if (_bytesPerPixel == 2) \
415 scale2x<uint16, uint32>(dst, dstPitch, src, srcPitch, w, h); \
416 else \
417 scale2x<uint8, uint32>(dst, dstPitch, src, srcPitch, w, h)
418
updateDirtyRectsOvl()419 void Screen::updateDirtyRectsOvl() {
420 if (_forceFullUpdate) {
421 const byte *src = getCPagePtr(0);
422 byte *dst = _sjisOverlayPtrs[0];
423 mScale2x(dst, 640, src, SCREEN_W, SCREEN_W, SCREEN_H);
424 mergeOverlay(0, 0, 640, 400);
425 _system->copyRectToScreen(dst, _useHiColorScreen ? 1280 : 640, 0, 0, 640, 400);
426 } else {
427 const byte *page0 = getCPagePtr(0);
428 byte *ovl0 = _sjisOverlayPtrs[0];
429 int dstBpp = _useHiColorScreen ? 2 : 1;
430
431 Common::List<Common::Rect>::iterator it;
432 for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
433 byte *dst = ovl0 + it->top * 1280 * dstBpp + (it->left << dstBpp);
434 const byte *src = page0 + it->top * SCREEN_W * _bytesPerPixel + it->left * _bytesPerPixel;
435 mScale2x(dst, 640, src, SCREEN_W, it->width(), it->height());
436 mergeOverlay(it->left<<1, it->top<<1, it->width()<<1, it->height()<<1);
437 _system->copyRectToScreen(dst, _useHiColorScreen ? 1280 : 640, it->left << 1, it->top << 1, it->width() << 1, it->height() << 1);
438 }
439 }
440
441 _forceFullUpdate = false;
442 _dirtyRects.clear();
443 }
444
445 #undef mScale2x
446
447 template<typename srcType, typename scaleToType>
scale2x(uint8 * dst,int dstPitch,const uint8 * src,int srcPitch,int w,int h)448 void Screen::scale2x(uint8 *dst, int dstPitch, const uint8 *src, int srcPitch, int w, int h) {
449 int dstAdd = dstPitch - w;
450 int srcAdd = srcPitch - w;
451 scaleToType *dstL1 = (scaleToType*)dst;
452 scaleToType *dstL2 = (scaleToType*)(dst + dstPitch * (sizeof(scaleToType) >> 1));
453 const srcType *src1 = (const srcType*)src;
454
455 while (h--) {
456 for (int x = 0; x < w; x++) {
457 scaleToType col = (sizeof(srcType) == 1 && sizeof(scaleToType) == 4) ? _16bitConversionPalette[*src1++] : *src1++;
458 *dstL1++ = *dstL2++ = col | (col << (sizeof(scaleToType) << 2));
459 }
460 dstL1 += dstAdd; dstL2 += dstAdd;
461 src1 += srcAdd;
462 }
463 }
464
465 template void Screen::scale2x<uint8, uint16>(uint8 *dst, int dstPitch, const uint8 *src, int srcPitch, int w, int h);
466 template void Screen::scale2x<uint16, uint32>(uint8 *dst, int dstPitch, const uint8 *src, int srcPitch, int w, int h);
467 template void Screen::scale2x<uint8, uint32>(uint8 *dst, int dstPitch, const uint8 *src, int srcPitch, int w, int h);
468
469 template<typename pixelType>
mergeOverlayImpl(int x,int y,int w,int h)470 void Screen::mergeOverlayImpl(int x, int y, int w, int h) {
471 const uint8 *src = _sjisOverlayPtrs[1] + y * 640 + x;
472 uint16 *p16 = _16bitPalette ? _16bitPalette : _16bitConversionPalette;
473 pixelType *dst = (pixelType*)(_sjisOverlayPtrs[0] + y * 640 * sizeof(pixelType) + x * sizeof(pixelType));
474 int add = 640 - w;
475
476 while (h--) {
477 for (x = 0; x < w; ++x) {
478 uint8 col = *src++;
479 if (col != _sjisInvisibleColor)
480 *dst = (sizeof(pixelType) == 2) ? p16[col] : col;
481 dst++;
482 }
483 dst += add;
484 src += add;
485 }
486 }
487
488 template void Screen::mergeOverlayImpl<uint8>(int x, int y, int w, int h);
489 template void Screen::mergeOverlayImpl<uint16>(int x, int y, int w, int h);
490
getScreenDim(int dim) const491 const ScreenDim *Screen::getScreenDim(int dim) const {
492 assert(dim < _dimTableCount);
493 return _customDimTable[dim] ? _customDimTable[dim] : &_dimTable[dim];
494 }
495
modifyScreenDim(int dim,int x,int y,int w,int h)496 void Screen::modifyScreenDim(int dim, int x, int y, int w, int h) {
497 if (!_customDimTable[dim])
498 _customDimTable[dim] = new ScreenDim;
499
500 memcpy(_customDimTable[dim], &_dimTable[dim], sizeof(ScreenDim));
501 _customDimTable[dim]->sx = x;
502 _customDimTable[dim]->sy = y;
503 _customDimTable[dim]->w = w;
504 _customDimTable[dim]->h = h;
505 if (dim == _curDimIndex || _vm->game() == GI_LOL)
506 setScreenDim(dim);
507 }
508
setScreenDim(int dim)509 void Screen::setScreenDim(int dim) {
510 _curDim = getScreenDim(dim);
511 _curDimIndex = dim;
512 }
513
resetPagePtrsAndBuffers(int pageSize)514 void Screen::resetPagePtrsAndBuffers(int pageSize) {
515 _screenPageSize = pageSize;
516
517 delete[] _pagePtrs[0];
518 memset(_pagePtrs, 0, sizeof(_pagePtrs));
519
520 Common::Array<uint8> realPages;
521 for (int i = 0; i < SCREEN_PAGE_NUM; i++) {
522 if (Common::find(realPages.begin(), realPages.end(), _pageMapping[i]) == realPages.end())
523 realPages.push_back(_pageMapping[i]);
524 }
525
526 int numPages = realPages.size();
527 uint32 bufferSize = numPages * _screenPageSize;
528
529 uint8 *pagePtr = new uint8[bufferSize];
530 memset(pagePtr, 0, bufferSize);
531
532 memset(_pagePtrs, 0, sizeof(_pagePtrs));
533 for (int i = 0; i < SCREEN_PAGE_NUM; i++) {
534 if (_pagePtrs[_pageMapping[i]]) {
535 _pagePtrs[i] = _pagePtrs[_pageMapping[i]];
536 } else {
537 _pagePtrs[i] = pagePtr;
538 pagePtr += _screenPageSize;
539 }
540 }
541 }
542
getPagePtr(int pageNum)543 uint8 *Screen::getPagePtr(int pageNum) {
544 assert(pageNum < SCREEN_PAGE_NUM);
545 return _pagePtrs[pageNum];
546 }
547
getCPagePtr(int pageNum) const548 const uint8 *Screen::getCPagePtr(int pageNum) const {
549 assert(pageNum < SCREEN_PAGE_NUM);
550 return _pagePtrs[pageNum];
551 }
552
getPageRect(int pageNum,int x,int y,int w,int h)553 uint8 *Screen::getPageRect(int pageNum, int x, int y, int w, int h) {
554 assert(pageNum < SCREEN_PAGE_NUM);
555 if (pageNum == 0 || pageNum == 1)
556 addDirtyRect(x, y, w, h);
557 return _pagePtrs[pageNum] + y * SCREEN_W + x;
558 }
559
clearPage(int pageNum)560 void Screen::clearPage(int pageNum) {
561 assert(pageNum < SCREEN_PAGE_NUM);
562 if (pageNum == 0 || pageNum == 1)
563 _forceFullUpdate = true;
564 memset(getPagePtr(pageNum), 0, _screenPageSize);
565 clearOverlayPage(pageNum);
566 }
567
setCurPage(int pageNum)568 int Screen::setCurPage(int pageNum) {
569 assert(pageNum < SCREEN_PAGE_NUM);
570 int previousPage = _curPage;
571 _curPage = pageNum;
572 return previousPage;
573 }
574
clearCurPage()575 void Screen::clearCurPage() {
576 if (_curPage == 0 || _curPage == 1)
577 _forceFullUpdate = true;
578 memset(getPagePtr(_curPage), 0, _screenPageSize);
579 clearOverlayPage(_curPage);
580 }
581
copyWsaRect(int x,int y,int w,int h,int dimState,int plotFunc,const uint8 * src,int unk1,const uint8 * unkPtr1,const uint8 * unkPtr2)582 void Screen::copyWsaRect(int x, int y, int w, int h, int dimState, int plotFunc, const uint8 *src,
583 int unk1, const uint8 *unkPtr1, const uint8 *unkPtr2) {
584 uint8 *dstPtr = getPagePtr(_curPage);
585 uint8 *origDst = dstPtr;
586
587 const ScreenDim *dim = getScreenDim(dimState);
588 int dimX1 = dim->sx << 3;
589 int dimX2 = dim->w << 3;
590 dimX2 += dimX1;
591
592 int dimY1 = dim->sy;
593 int dimY2 = dim->h;
594 dimY2 += dimY1;
595
596 int temp = y - dimY1;
597 if (temp < 0) {
598 if ((temp += h) <= 0)
599 return;
600 else {
601 SWAP(temp, h);
602 y += temp - h;
603 src += (temp - h) * w;
604 }
605 }
606
607 temp = dimY2 - y;
608 if (temp <= 0)
609 return;
610
611 if (temp < h)
612 h = temp;
613
614 int srcOffset = 0;
615 temp = x - dimX1;
616 if (temp < 0) {
617 temp = -temp;
618 srcOffset = temp;
619 x += temp;
620 w -= temp;
621 }
622
623 int srcAdd = 0;
624
625 temp = dimX2 - x;
626 if (temp <= 0)
627 return;
628
629 if (temp < w) {
630 SWAP(w, temp);
631 temp -= w;
632 srcAdd = temp;
633 }
634
635 dstPtr += y * SCREEN_W + x;
636 uint8 *dst = dstPtr;
637
638 if (_curPage == 0 || _curPage == 1)
639 addDirtyRect(x, y, w, h);
640
641 if (!_use16ColorMode)
642 clearOverlayRect(_curPage, x, y, w, h);
643
644 temp = h;
645 int curY = y;
646 while (h--) {
647 src += srcOffset;
648 ++curY;
649 int cW = w;
650
651 switch (plotFunc) {
652 case 0:
653 memcpy(dst, src, cW);
654 dst += cW; src += cW;
655 break;
656
657 case 1:
658 while (cW--) {
659 uint8 d = *src++;
660 uint8 t = unkPtr1[d];
661 if (t != 0xFF)
662 d = unkPtr2[*dst + (t << 8)];
663 *dst++ = d;
664 }
665 break;
666
667 case 4:
668 while (cW--) {
669 uint8 d = *src++;
670 if (d)
671 *dst = d;
672 ++dst;
673 }
674 break;
675
676 case 5:
677 while (cW--) {
678 uint8 d = *src++;
679 if (d) {
680 uint8 t = unkPtr1[d];
681 if (t != 0xFF)
682 d = unkPtr2[*dst + (t << 8)];
683 *dst = d;
684 }
685 ++dst;
686 }
687 break;
688
689 case 8:
690 case 9:
691 while (cW--) {
692 uint8 d = *src++;
693 uint8 t = _shapePages[0][dst - origDst] & 7;
694 if (unk1 < t && (curY > _maskMinY && curY < _maskMaxY))
695 d = _shapePages[1][dst - origDst];
696 *dst++ = d;
697 }
698 break;
699
700 case 12:
701 case 13:
702 while (cW--) {
703 uint8 d = *src++;
704 if (d) {
705 uint8 t = _shapePages[0][dst - origDst] & 7;
706 if (unk1 < t && (curY > _maskMinY && curY < _maskMaxY))
707 d = _shapePages[1][dst - origDst];
708 *dst++ = d;
709 } else {
710 d = _shapePages[1][dst - origDst];
711 *dst++ = d;
712 }
713 }
714 break;
715
716 default:
717 break;
718 }
719
720 dst = (dstPtr += SCREEN_W);
721 src += srcAdd;
722 }
723 }
724
getPagePixel(int pageNum,int x,int y)725 int Screen::getPagePixel(int pageNum, int x, int y) {
726 assert(pageNum < SCREEN_PAGE_NUM);
727 assert(x >= 0 && x < SCREEN_W && y >= 0 && y < _screenHeight);
728 if (_bytesPerPixel == 1)
729 return _pagePtrs[pageNum][y * SCREEN_W + x];
730 else
731 return ((uint16*)_pagePtrs[pageNum])[y * SCREEN_W + x];
732 }
733
setPagePixel(int pageNum,int x,int y,uint8 color)734 void Screen::setPagePixel(int pageNum, int x, int y, uint8 color) {
735 assert(pageNum < SCREEN_PAGE_NUM);
736 assert(x >= 0 && x < SCREEN_W && y >= 0 && y < _screenHeight);
737
738 if (pageNum == 0 || pageNum == 1)
739 addDirtyRect(x, y, 1, 1);
740
741 if (_4bitPixelPacking) {
742 color &= 0x0F;
743 color |= (color << 4);
744 } else if (_renderMode == Common::kRenderCGA) {
745 color &= 0x03;
746 } else if (_use16ColorMode || (_renderMode == Common::kRenderEGA && !_useHiResEGADithering)) {
747 color &= 0x0F;
748 }
749
750 if (_bytesPerPixel == 2) {
751 ((uint16*)_pagePtrs[pageNum])[y * SCREEN_W + x] = _16bitPalette[color];
752 } else {
753 _pagePtrs[pageNum][y * SCREEN_W + x] = color;
754 }
755 }
756
fadeFromBlack(int delay,const UpdateFunctor * upFunc)757 void Screen::fadeFromBlack(int delay, const UpdateFunctor *upFunc) {
758 fadePalette(getPalette(0), delay, upFunc);
759 }
760
fadeToBlack(int delay,const UpdateFunctor * upFunc)761 void Screen::fadeToBlack(int delay, const UpdateFunctor *upFunc) {
762 if (_renderMode == Common::kRenderEGA)
763 return;
764
765 Palette pal(getPalette(0).getNumColors());
766 fadePalette(pal, delay, upFunc);
767 }
768
fadePalette(const Palette & pal,int delay,const UpdateFunctor * upFunc)769 void Screen::fadePalette(const Palette &pal, int delay, const UpdateFunctor *upFunc) {
770 if (_renderMode == Common::kRenderEGA || _bytesPerPixel == 2)
771 setScreenPalette(pal);
772
773 updateScreen();
774
775 if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA || _bytesPerPixel == 2)
776 return;
777
778 int diff = 0, delayInc = 0;
779 getFadeParams(pal, delay, delayInc, diff);
780
781 int delayAcc = 0;
782 while (!_vm->shouldQuit()) {
783 delayAcc += delayInc;
784
785 int refreshed = fadePalStep(pal, diff);
786
787 if (upFunc && upFunc->isValid())
788 (*upFunc)();
789 else if (_useHiColorScreen)
790 updateScreen();
791 else
792 _system->updateScreen();
793
794 if (!refreshed)
795 break;
796
797 _vm->delay((delayAcc >> 8) * 1000 / 60);
798 delayAcc &= 0xFF;
799 }
800
801 // In case we should quit we setup the final palette here. This avoids
802 // ugly palette glitches when quitting while fading. This can for example
803 // be noticed when quitting while viewing the family album in Kyra3.
804 if (_vm->shouldQuit()) {
805 setScreenPalette(pal);
806 }
807 }
808
getFadeParams(const Palette & pal,int delay,int & delayInc,int & diff)809 void Screen::getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff) {
810 uint8 maxDiff = 0;
811
812 for (int i = 0; i < pal.getNumColors() * 3; ++i) {
813 diff = ABS(pal[i] - (*_screenPalette)[i]);
814 maxDiff = MAX<uint8>(maxDiff, diff);
815 }
816
817 delayInc = (delay << 8) & 0x7FFF;
818 if (maxDiff != 0)
819 delayInc /= maxDiff;
820
821 delay = delayInc;
822 for (diff = 1; diff <= maxDiff; ++diff) {
823 if (delayInc >= 512)
824 break;
825 delayInc += delay;
826 }
827 }
828
fadePalStep(const Palette & pal,int diff)829 int Screen::fadePalStep(const Palette &pal, int diff) {
830 _internFadePalette->copy(*_screenPalette);
831
832 bool needRefresh = false;
833
834 for (int i = 0; i < pal.getNumColors() * 3; ++i) {
835 int c1 = pal[i];
836 int c2 = (*_internFadePalette)[i];
837 if (c1 != c2) {
838 needRefresh = true;
839 if (c1 > c2) {
840 c2 += diff;
841 if (c1 < c2)
842 c2 = c1;
843 }
844
845 if (c1 < c2) {
846 c2 -= diff;
847 if (c1 > c2)
848 c2 = c1;
849 }
850
851 (*_internFadePalette)[i] = (uint8)c2;
852 }
853 }
854
855 if (needRefresh)
856 setScreenPalette(*_internFadePalette);
857
858 return needRefresh ? 1 : 0;
859 }
860
setPaletteIndex(uint8 index,uint8 red,uint8 green,uint8 blue)861 void Screen::setPaletteIndex(uint8 index, uint8 red, uint8 green, uint8 blue) {
862 Palette &pal = getPalette(0);
863
864 const int offset = index * 3;
865
866 if (pal[offset + 0] == red && pal[offset + 1] == green && pal[offset + 2] == blue)
867 return;
868
869 pal[offset + 0] = red;
870 pal[offset + 1] = green;
871 pal[offset + 2] = blue;
872
873 setScreenPalette(pal);
874 }
875
getRealPalette(int num,uint8 * dst)876 void Screen::getRealPalette(int num, uint8 *dst) {
877 const int colors = _use16ColorMode ? 16 : (_isAmiga ? 32 : 256);
878 const uint8 *palData = getPalette(num).getData();
879
880 if (!palData) {
881 memset(dst, 0, colors * 3);
882 return;
883 }
884
885 for (int i = 0; i < colors; ++i) {
886 dst[0] = (palData[0] * 0xFF) / 0x3F;
887 dst[1] = (palData[1] * 0xFF) / 0x3F;
888 dst[2] = (palData[2] * 0xFF) / 0x3F;
889 dst += 3;
890 palData += 3;
891 }
892 }
893
setScreenPalette(const Palette & pal)894 void Screen::setScreenPalette(const Palette &pal) {
895 uint8 screenPal[256 * 3];
896 _screenPalette->copy(pal);
897
898 for (int i = 0; i < pal.getNumColors(); ++i) {
899 screenPal[3 * i + 0] = (pal[i * 3 + 0] * 0xFF) / 0x3F;
900 screenPal[3 * i + 1] = (pal[i * 3 + 1] * 0xFF) / 0x3F;
901 screenPal[3 * i + 2] = (pal[i * 3 + 2] * 0xFF) / 0x3F;
902 }
903
904 _paletteChanged = true;
905
906 if (_useHiColorScreen) {
907 if (_16bitPalette)
908 memcpy(_16bitPalette, pal.getData(), 512);
909
910 // Generate 16bit palette for the 8bit/16 bit conversion in scale2x()
911 if (_16bitConversionPalette) {
912 Graphics::PixelFormat pixelFormat = _system->getScreenFormat();
913 for (int i = 0; i < 256; ++i)
914 _16bitConversionPalette[i] = pixelFormat.RGBToColor(screenPal[i * 3], screenPal[i * 3 + 1], screenPal[i * 3 + 2]);
915 // The whole Surface has to be converted again after each palette chance
916 _forceFullUpdate = true;
917 }
918 return;
919 }
920
921 _system->getPaletteManager()->setPalette(screenPal, 0, pal.getNumColors());
922 }
923
transposeScreenOutputY(int yAdd)924 void Screen::transposeScreenOutputY(int yAdd) {
925 updateScreen();
926 _yTransOffs = yAdd;
927 }
928
enableDualPaletteMode(int splitY)929 void Screen::enableDualPaletteMode(int splitY) {
930 _dualPaletteModeSplitY = splitY;
931
932 _forceFullUpdate = true;
933 _dirtyRects.clear();
934
935 // TODO: We might need to reset the mouse cursor
936
937 updateScreen();
938 }
939
disableDualPaletteMode()940 void Screen::disableDualPaletteMode() {
941 _dualPaletteModeSplitY = 0;
942 _forceFullUpdate = true;
943 }
944
copyToPage0(int y,int h,uint8 page,uint8 * seqBuf)945 void Screen::copyToPage0(int y, int h, uint8 page, uint8 *seqBuf) {
946 assert(y + h <= _screenHeight);
947 const uint8 *src = getPagePtr(page) + y * SCREEN_W;
948 uint8 *dstPage = getPagePtr(0) + y * SCREEN_W;
949 for (int i = 0; i < h; ++i) {
950 for (int x = 0; x < SCREEN_W; ++x) {
951 if (seqBuf[x] != src[x]) {
952 seqBuf[x] = src[x];
953 dstPage[x] = src[x];
954 }
955 }
956 src += SCREEN_W;
957 seqBuf += SCREEN_W;
958 dstPage += SCREEN_W;
959 }
960 addDirtyRect(0, y, SCREEN_W, h);
961 // This would remove the text in the end sequence of
962 // the (Kyrandia 1) FM-TOWNS version.
963 // Since this method is just used for the Seqplayer
964 // this shouldn't be a problem anywhere else, so it's
965 // safe to disable the call here.
966 //clearOverlayRect(0, 0, y, SCREEN_W, h);
967 }
968
copyRegion(int x1,int y1,int x2,int y2,int w,int h,int srcPage,int dstPage,int flags)969 void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage, int flags) {
970 if (x2 < 0) {
971 if (x2 <= -w)
972 return;
973 w += x2;
974 x1 -= x2;
975 x2 = 0;
976 } else if (x2 + w >= SCREEN_W) {
977 if (x2 > SCREEN_W)
978 return;
979 w = SCREEN_W - x2;
980 }
981
982 if (y2 < 0) {
983 if (y2 <= -h)
984 return;
985 h += y2;
986 y1 -= y2;
987 y2 = 0;
988 } else if (y2 + h >= _screenHeight) {
989 if (y2 > _screenHeight)
990 return;
991 h = _screenHeight - y2;
992 }
993
994 const uint8 *src = getPagePtr(srcPage) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel;
995 uint8 *dst = getPagePtr(dstPage) + y2 * SCREEN_W * _bytesPerPixel + x2 * _bytesPerPixel;
996
997 if (src == dst)
998 return;
999
1000 if (dstPage == 0 || dstPage == 1)
1001 addDirtyRect(x2, y2, w, h);
1002
1003 copyOverlayRegion(x1, y1, x2, y2, w, h, srcPage, dstPage);
1004
1005 if (flags & CR_NO_P_CHECK) {
1006 while (h--) {
1007 memmove(dst, src, w * _bytesPerPixel);
1008 src += SCREEN_W * _bytesPerPixel;
1009 dst += SCREEN_W * _bytesPerPixel;
1010 }
1011 } else {
1012 while (h--) {
1013 for (int i = 0; i < w; ++i) {
1014 if (_bytesPerPixel == 2) {
1015 uint px = *(const uint16*)&src[i << 1];
1016 if (px)
1017 *(uint16*)&dst[i << 1] = px;
1018 } else {
1019 if (src[i])
1020 dst[i] = src[i];
1021 }
1022 }
1023 src += SCREEN_W * _bytesPerPixel;
1024 dst += SCREEN_W * _bytesPerPixel;
1025 }
1026 }
1027 }
1028
copyRegionToBuffer(int pageNum,int x,int y,int w,int h,uint8 * dest)1029 void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest) {
1030 if (y < 0) {
1031 dest += (-y) * w * _bytesPerPixel;
1032 h += y;
1033 y = 0;
1034 } else if (y + h > _screenHeight) {
1035 h = _screenHeight - y;
1036 }
1037
1038 int pitch = w;
1039 if (x < 0) {
1040 dest += -x * _bytesPerPixel;
1041 w += x;
1042 x = 0;
1043 } else if (x + w > SCREEN_W) {
1044 w = SCREEN_W - x;
1045 }
1046
1047 if (w < 0 || h < 0)
1048 return;
1049
1050 uint8 *pagePtr = getPagePtr(pageNum);
1051
1052 for (int i = y; i < y + h; ++i)
1053 memcpy(dest + (i - y) * pitch * _bytesPerPixel, pagePtr + i * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel, w * _bytesPerPixel);
1054 }
1055
copyPage(uint8 srcPage,uint8 dstPage)1056 void Screen::copyPage(uint8 srcPage, uint8 dstPage) {
1057 uint8 *src = getPagePtr(srcPage);
1058 uint8 *dst = getPagePtr(dstPage);
1059 if (src != dst)
1060 memcpy(dst, src, SCREEN_W * _screenHeight * _bytesPerPixel);
1061 copyOverlayRegion(0, 0, 0, 0, SCREEN_W, _screenHeight, srcPage, dstPage);
1062
1063 if (dstPage == 0 || dstPage == 1)
1064 _forceFullUpdate = true;
1065 }
1066
copyBlockToPage(int pageNum,int x,int y,int w,int h,const uint8 * src)1067 void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint8 *src) {
1068 if (y < 0) {
1069 src += (-y) * w * _bytesPerPixel;
1070 h += y;
1071 y = 0;
1072 } else if (y + h > _screenHeight) {
1073 h = _screenHeight - y;
1074 }
1075
1076 int pitch = w;
1077 if (x < 0) {
1078 src += -x * _bytesPerPixel;
1079 w += x;
1080 x = 0;
1081 } else if (x + w > SCREEN_W) {
1082 w = SCREEN_W - x;
1083 }
1084
1085 if (w < 0 || h < 0)
1086 return;
1087
1088 uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel;
1089
1090 if (pageNum == 0 || pageNum == 1)
1091 addDirtyRect(x, y, w, h);
1092
1093 clearOverlayRect(pageNum, x, y, w, h);
1094
1095 while (h--) {
1096 memcpy(dst, src, w * _bytesPerPixel);
1097 dst += SCREEN_W * _bytesPerPixel;
1098 src += pitch * _bytesPerPixel;
1099 }
1100 }
1101
shuffleScreen(int sx,int sy,int w,int h,int srcPage,int dstPage,int ticks,bool transparent)1102 void Screen::shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent) {
1103 assert(sx >= 0 && w <= SCREEN_W);
1104 int x;
1105 uint16 x_offs[SCREEN_W];
1106 for (x = 0; x < SCREEN_W; ++x)
1107 x_offs[x] = x;
1108
1109 for (x = 0; x < w; ++x) {
1110 int i = _vm->_rnd.getRandomNumber(w - 1);
1111 SWAP(x_offs[x], x_offs[i]);
1112 }
1113
1114 assert(sy >= 0 && h <= SCREEN_H);
1115 int y;
1116 uint8 y_offs[SCREEN_H];
1117 for (y = 0; y < SCREEN_H; ++y)
1118 y_offs[y] = y;
1119
1120 for (y = 0; y < h; ++y) {
1121 int i = _vm->_rnd.getRandomNumber(h - 1);
1122 SWAP(y_offs[y], y_offs[i]);
1123 }
1124
1125 int32 start, now;
1126 int wait;
1127 for (y = 0; y < h && !_vm->shouldQuit(); ++y) {
1128 start = (int32)_system->getMillis();
1129 int y_cur = y;
1130 for (x = 0; x < w; ++x) {
1131 int i = sx + x_offs[x];
1132 int j = sy + y_offs[y_cur];
1133 ++y_cur;
1134 if (y_cur >= h)
1135 y_cur = 0;
1136
1137 uint8 color = getPagePixel(srcPage, i, j);
1138 if (!transparent || color != 0)
1139 setPagePixel(dstPage, i, j, color);
1140 }
1141 // forcing full update for now
1142 _forceFullUpdate = true;
1143 updateScreen();
1144 now = (int32)_system->getMillis();
1145 wait = ticks * _vm->tickLength() - (now - start);
1146 if (wait > 0)
1147 _vm->delay(wait);
1148 }
1149
1150 copyOverlayRegion(sx, sy, sx, sy, w, h, srcPage, dstPage);
1151
1152 if (_vm->shouldQuit()) {
1153 copyRegion(sx, sy, sx, sy, w, h, srcPage, dstPage);
1154 _system->updateScreen();
1155 }
1156 }
1157
fillRect(int x1,int y1,int x2,int y2,uint8 color,int pageNum,bool xored)1158 void Screen::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum, bool xored) {
1159 assert(x2 < SCREEN_W && y2 < _screenHeight);
1160 uint16 color16 = 0;
1161 if (pageNum == -1)
1162 pageNum = _curPage;
1163
1164 uint8 *dst = getPagePtr(pageNum) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel;
1165
1166 if (pageNum == 0 || pageNum == 1)
1167 addDirtyRect(x1, y1, x2-x1+1, y2-y1+1);
1168
1169 clearOverlayRect(pageNum, x1, y1, x2-x1+1, y2-y1+1);
1170
1171 if (_4bitPixelPacking) {
1172 color &= 0x0F;
1173 color |= (color << 4);
1174 } else if (_renderMode == Common::kRenderCGA) {
1175 color &= 0x03;
1176 } else if (_use16ColorMode || (_renderMode == Common::kRenderEGA && !_useHiResEGADithering)) {
1177 color &= 0x0F;
1178 } else if (_bytesPerPixel == 2)
1179 color16 = shade16bitColor(_16bitPalette[color]);
1180
1181 if (xored) {
1182 // no 16 bit support for this (unneeded)
1183 for (; y1 <= y2; ++y1) {
1184 for (int x = x1; x <= x2; ++x)
1185 dst[x] ^= color;
1186 dst += SCREEN_W;
1187 }
1188 } else {
1189 for (; y1 <= y2; ++y1) {
1190 if (_bytesPerPixel == 2) {
1191 uint16 *ptr = (uint16*)dst;
1192 for (int i = 0; i < x2 - x1 + 1; i++)
1193 *ptr++ = color16;
1194 } else {
1195 memset(dst, color, x2 - x1 + 1);
1196 }
1197 dst += SCREEN_W * _bytesPerPixel;
1198 }
1199 }
1200 }
1201
drawBox(int x1,int y1,int x2,int y2,int color)1202 void Screen::drawBox(int x1, int y1, int x2, int y2, int color) {
1203 drawClippedLine(x1, y1, x2, y1, color);
1204 drawClippedLine(x1, y1, x1, y2, color);
1205 drawClippedLine(x2, y1, x2, y2, color);
1206 drawClippedLine(x1, y2, x2, y2, color);
1207 }
1208
drawShadedBox(int x1,int y1,int x2,int y2,int color1,int color2)1209 void Screen::drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2) {
1210 assert(x1 >= 0 && y1 >= 0);
1211 fillRect(x1, y1, x2, y1 + 1, color1);
1212 fillRect(x2 - 1, y1, x2, y2, color1);
1213
1214 drawClippedLine(x1, y1, x1, y2, color2);
1215 drawClippedLine(x1 + 1, y1 + 1, x1 + 1, y2 - 1, color2);
1216 drawClippedLine(x1, y2 - 1, x2 - 1, y2 - 1, color2);
1217 drawClippedLine(x1, y2, x2, y2, color2);
1218 }
1219
drawClippedLine(int x1,int y1,int x2,int y2,int color)1220 void Screen::drawClippedLine(int x1, int y1, int x2, int y2, int color) {
1221 if (x1 < 0)
1222 x1 = 0;
1223 else if (x1 > 319)
1224 x1 = 319;
1225
1226 if (x2 < 0)
1227 x2 = 0;
1228 else if (x2 > 319)
1229 x2 = 319;
1230
1231 if (y1 < 0)
1232 y1 = 0;
1233 else if (y1 > 199)
1234 y1 = 199;
1235
1236 if (y2 < 0)
1237 y2 = 0;
1238 else if (y2 > 199)
1239 y2 = 199;
1240
1241 if (x1 == x2)
1242 if (y1 > y2)
1243 drawLine(true, x1, y2, y1 - y2 + 1, color);
1244 else
1245 drawLine(true, x1, y1, y2 - y1 + 1, color);
1246 else
1247 if (x1 > x2)
1248 drawLine(false, x2, y1, x1 - x2 + 1, color);
1249 else
1250 drawLine(false, x1, y1, x2 - x1 + 1, color);
1251 }
1252
drawLine(bool vertical,int x,int y,int length,int color)1253 void Screen::drawLine(bool vertical, int x, int y, int length, int color) {
1254 uint8 *ptr = getPagePtr(_curPage) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel;
1255
1256 if (_4bitPixelPacking) {
1257 color &= 0x0F;
1258 color |= (color << 4);
1259 } else if (_renderMode == Common::kRenderCGA) {
1260 color &= 0x03;
1261 } else if (_use16ColorMode || (_renderMode == Common::kRenderEGA && !_useHiResEGADithering)) {
1262 color &= 0x0F;
1263 } else if (_bytesPerPixel == 2)
1264 color = shade16bitColor(_16bitPalette[color]);
1265
1266 if (vertical) {
1267 assert((y + length) <= SCREEN_H);
1268 int currLine = 0;
1269 while (currLine < length) {
1270 if (_bytesPerPixel == 2)
1271 *(uint16*)ptr = color;
1272 else
1273 *ptr = color;
1274 ptr += SCREEN_W * _bytesPerPixel;
1275 currLine++;
1276 }
1277 } else {
1278 assert((x + length) <= SCREEN_W);
1279 if (_bytesPerPixel == 2) {
1280 for (int i = 0; i < length; i++) {
1281 *(uint16*)ptr = color;
1282 ptr += 2;
1283 }
1284 } else {
1285 memset(ptr, color, length);
1286 }
1287 }
1288
1289 if (_curPage == 0 || _curPage == 1)
1290 addDirtyRect(x, y, (vertical) ? 1 : length, (vertical) ? length : 1);
1291
1292 clearOverlayRect(_curPage, x, y, (vertical) ? 1 : length, (vertical) ? length : 1);
1293 }
1294
setAnimBlockPtr(int size)1295 void Screen::setAnimBlockPtr(int size) {
1296 delete[] _animBlockPtr;
1297 _animBlockPtr = new uint8[size];
1298 assert(_animBlockPtr);
1299 memset(_animBlockPtr, 0, size);
1300 _animBlockSize = size;
1301 }
1302
setTextColor(const uint8 * cmap8,int a,int b)1303 void Screen::setTextColor(const uint8 *cmap8, int a, int b) {
1304 memcpy(&_textColorsMap[a], cmap8, (b - a + 1));
1305 // We need to update the color tables of all fonts, we
1306 // setup so far here.
1307 for (int i = 0; i < FID_NUM; ++i) {
1308 if (_fonts[i])
1309 _fonts[i]->setColorMap(_textColorsMap);
1310 }
1311 }
1312
setTextColor16bit(const uint16 * cmap16)1313 void Screen::setTextColor16bit(const uint16 *cmap16) {
1314 assert(cmap16);
1315 _textColorsMap16bit[0] = cmap16[0];
1316 _textColorsMap16bit[1] = cmap16[1];
1317 // We need to update the color tables of all fonts, we
1318 // setup so far here.
1319 for (int i = 0; i < FID_NUM; ++i) {
1320 if (_fonts[i])
1321 _fonts[i]->set16bitColorMap(_textColorsMap16bit);
1322 }
1323 }
1324
setFontStyles(FontId fontId,int styles)1325 int Screen::setFontStyles(FontId fontId, int styles) {
1326 assert(_fonts[fontId]);
1327 SWAP(_fontStyles, styles);
1328 _fonts[fontId]->setStyles(_fontStyles);
1329 return styles;
1330 }
1331
loadFont(FontId fontId,const char * filename)1332 bool Screen::loadFont(FontId fontId, const char *filename) {
1333 if (fontId == FID_SJIS_FNT) {
1334 warning("Trying to replace system SJIS font");
1335 return true;
1336 }
1337
1338 Font *&fnt = _fonts[fontId];
1339 int temp = 0;
1340
1341 if (!fnt) {
1342 if (_vm->game() == GI_KYRA1 && _isAmiga)
1343 fnt = new AMIGAFont();
1344 else if (_vm->game() == GI_KYRA3 && fontId == FID_CHINESE_FNT)
1345 fnt = new Big5Font(_vm->staticres()->loadRawData(k3FontData, temp), SCREEN_W);
1346 else
1347 fnt = new DOSFont();
1348
1349 assert(fnt);
1350 }
1351
1352 Common::SeekableReadStream *file = _vm->resource()->createReadStream(filename);
1353 if (!file)
1354 error("Font file '%s' is missing", filename);
1355
1356 bool ret = fnt->load(*file);
1357 fnt->setColorMap(_textColorsMap);
1358 delete file;
1359 return ret;
1360 }
1361
setFont(FontId fontId)1362 Screen::FontId Screen::setFont(FontId fontId) {
1363 FontId prev = _currentFont;
1364 _currentFont = fontId;
1365
1366 assert(_fonts[_currentFont]);
1367 return prev;
1368 }
1369
getFontHeight() const1370 int Screen::getFontHeight() const {
1371 return _fonts[_currentFont]->getHeight();
1372 }
1373
getFontWidth() const1374 int Screen::getFontWidth() const {
1375 return _fonts[_currentFont]->getWidth();
1376 }
1377
getCharWidth(uint16 c) const1378 int Screen::getCharWidth(uint16 c) const {
1379 const int width = _fonts[_currentFont]->getCharWidth(c);
1380 return width + (_isSegaCD || _fonts[_currentFont]->getType() == Font::kASCII ? _charSpacing : 0);
1381 }
1382
getCharHeight(uint16 c) const1383 int Screen::getCharHeight(uint16 c) const {
1384 return _fonts[_currentFont]->getCharHeight(c);
1385 }
1386
getTextWidth(const char * str,bool nextWordOnly)1387 int Screen::getTextWidth(const char *str, bool nextWordOnly) {
1388 int curLineLen = 0;
1389 int maxLineLen = 0;
1390
1391 FontId curFont = _currentFont;
1392 Font::Type curType = _fonts[curFont]->getType();
1393
1394 while (1) {
1395 if (_sjisMixedFontMode && curType == Font::kASCII)
1396 setFont((*str & 0x80) ? ((_vm->game() == GI_EOB2 && curFont == FID_6_FNT) ? FID_SJIS_SMALL_FNT : FID_SJIS_FNT) : curFont);
1397
1398 uint c = fetchChar(str);
1399
1400 if (c == 0 || (nextWordOnly && (c == 2 || c == 6 || c == 13 || c == 32 || c == 0x4081))) {
1401 break;
1402 } else if (c == '\r') {
1403 if (curLineLen > maxLineLen)
1404 maxLineLen = curLineLen;
1405 else
1406 curLineLen = 0;
1407 } else {
1408 curLineLen += getCharWidth(c);
1409 }
1410 }
1411
1412 return MAX(curLineLen, maxLineLen);
1413 }
1414
getNumberOfCharacters(const char * str)1415 int Screen::getNumberOfCharacters(const char *str) {
1416 int res = 0;
1417 while (fetchChar(str))
1418 ++res;
1419 return res;
1420 }
1421
printText(const char * str,int x,int y,uint8 color1,uint8 color2,int pitch)1422 void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2, int pitch) {
1423 uint16 cmap16[2];
1424 if (_16bitPalette) {
1425 cmap16[0] = color2 ? shade16bitColor(_16bitPalette[color2]) : 0xFFFF;
1426 cmap16[1] = _16bitPalette[color1];
1427 setTextColor16bit(cmap16);
1428 }
1429
1430 uint8 cmap8[2];
1431 cmap8[0] = color2;
1432 cmap8[1] = color1;
1433 setTextColor(cmap8, 0, 1);
1434
1435 FontId curFont = _currentFont;
1436 Font::Type curType = _fonts[curFont]->getType();
1437
1438 if (x < 0)
1439 x = 0;
1440 else if (x >= SCREEN_W)
1441 return;
1442
1443 int x_start = x;
1444 if (y < 0)
1445 y = 0;
1446 else if (y >= _screenHeight)
1447 return;
1448
1449 int charHeight = 0;
1450 bool enableWordWrap = _isSegaCD && _vm->gameFlags().lang != Common::JA_JPN;
1451
1452 while (1) {
1453 if (_sjisMixedFontMode && curType == Font::kASCII)
1454 setFont((*str & 0x80) ? ((_vm->game() == GI_EOB2 && curFont == FID_6_FNT) ? FID_SJIS_SMALL_FNT : FID_SJIS_FNT) : curFont);
1455
1456 uint c = fetchChar(str);
1457 charHeight = MAX<int>(charHeight, getCharHeight(c));
1458
1459 if (c == 0) {
1460 break;
1461 } else if (c == '\r') {
1462 x = x_start;
1463 y += (charHeight + _lineSpacing);
1464 } else {
1465 int charWidth = getCharWidth(c);
1466 int needSpace = enableWordWrap ? getTextWidth(str, true) + charWidth : charWidth;
1467 if (x + needSpace > _textMarginRight) {
1468 x = x_start;
1469 y += (charHeight + _lineSpacing);
1470 if (enableWordWrap) {
1471 // skip space at beginning of the line
1472 c = fetchChar(str);
1473 if (c == 0)
1474 return;
1475 charWidth = getCharWidth(c);
1476 }
1477 if (y >= _screenHeight)
1478 break;
1479 }
1480
1481 drawChar(c, x, y, pitch);
1482 x += charWidth;
1483 }
1484 }
1485 }
1486
fetchChar(const char * & s) const1487 uint16 Screen::fetchChar(const char *&s) const {
1488 if (_fonts[_currentFont]->getType() == Font::kASCII)
1489 return (uint8)*s++;
1490
1491 uint16 ch = (uint8)*s++;
1492
1493 if ((_fonts[_currentFont]->getType() == Font::kSJIS && (ch <= 0x7F || (ch >= 0xA1 && ch <= 0xDF))) || (_fonts[_currentFont]->getType() == Font::kBIG5 && ch < 0x7F))
1494 return ch;
1495
1496 ch |= (uint8)(*s++) << 8;
1497 return ch;
1498 }
1499
drawChar(uint16 c,int x,int y,int pitch)1500 void Screen::drawChar(uint16 c, int x, int y, int pitch) {
1501 Font *fnt = _fonts[_currentFont];
1502 assert(fnt);
1503
1504 const bool useOverlay = fnt->usesOverlay();
1505 const int charWidth = fnt->getCharWidth(c);
1506 const int charHeight = fnt->getHeight();
1507
1508 if (x < 0 || y < 0)
1509 return;
1510 if (x + charWidth > SCREEN_W || y + charHeight > _screenHeight)
1511 return;
1512
1513 if (_isSegaCD) {
1514 fnt->drawChar(c, _textRenderBuffer + (((y >> 3) * pitch + (x >> 3)) << 5) + ((y & 7) << 2) + ((x & 7) >> 1), pitch, x & 7, y & 7);
1515 } else if (useOverlay) {
1516 uint8 *destPage = getOverlayPtr(_curPage);
1517 if (!destPage) {
1518 warning("trying to draw SJIS char on unsupported page %d", _curPage);
1519 return;
1520 }
1521
1522 int bpp = (_currentFont == Screen::FID_SJIS_LARGE_FNT) ? 2 : 1;
1523 destPage += (y * 2) * 640 * bpp + (x * 2 * bpp);
1524
1525 fnt->drawChar(c, destPage, 640, bpp);
1526 } else {
1527 fnt->drawChar(c, getPagePtr(_curPage) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel, SCREEN_W, _bytesPerPixel);
1528 }
1529
1530 if (!_isSegaCD && (_curPage == 0 || _curPage == 1))
1531 addDirtyRect(x, y, charWidth, charHeight);
1532 }
1533
drawShape(uint8 pageNum,const uint8 * shapeData,int x,int y,int sd,int flags,...)1534 void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) {
1535 if (!shapeData)
1536 return;
1537
1538 if (_vm->gameFlags().useAltShapeHeader)
1539 shapeData += 2;
1540
1541 if (*shapeData & 1)
1542 flags |= 0x400;
1543
1544 va_list args;
1545 va_start(args, flags);
1546
1547 static const int drawShapeVar2[] = {
1548 1, 3, 2, 5, 4, 3, 2, 1
1549 };
1550
1551 _dsShapeFadingTable = 0;
1552 _dsShapeFadingLevel = 0;
1553 _dsColorTable = 0;
1554 _dsTransparencyTable1 = 0;
1555 _dsTransparencyTable2 = 0;
1556 _dsBackgroundFadingTable = 0;
1557 _dsDrawLayer = 0;
1558
1559 if (flags & DSF_CUSTOM_PALETTE) {
1560 _dsColorTable = va_arg(args, uint8 *);
1561 }
1562
1563 if (flags & DSF_SHAPE_FADING) {
1564 _dsShapeFadingTable = va_arg(args, uint8 *);
1565 _dsShapeFadingLevel = va_arg(args, int);
1566 if (!_dsShapeFadingLevel)
1567 flags &= ~DSF_SHAPE_FADING;
1568 }
1569
1570 if (flags & DSF_TRANSPARENCY) {
1571 _dsTransparencyTable1 = va_arg(args, uint8 *);
1572 _dsTransparencyTable2 = va_arg(args, uint8 *);
1573 }
1574
1575 if (flags & 0x200) {
1576 _drawShapeVar1 = (_drawShapeVar1 + 1) & 0x7;
1577 _drawShapeVar3 = drawShapeVar2[_drawShapeVar1];
1578 _drawShapeVar4 = 0;
1579 _drawShapeVar5 = 256;
1580 }
1581
1582 if (flags & 0x4000)
1583 _drawShapeVar5 = va_arg(args, int);
1584
1585 if (flags & 0x800)
1586 _dsDrawLayer = va_arg(args, int);
1587
1588 if (flags & DSF_SCALE) {
1589 _dsScaleW = va_arg(args, int);
1590 _dsScaleH = va_arg(args, int);
1591 } else {
1592 _dsScaleW = 0x100;
1593 _dsScaleH = 0x100;
1594 }
1595
1596 if ((flags & DSF_BACKGROUND_FADING) && _vm->game() != GI_KYRA1)
1597 _dsBackgroundFadingTable = va_arg(args, uint8 *);
1598
1599 va_end(args);
1600
1601 static const DsMarginSkipFunc dsMarginFunc[] = {
1602 &Screen::drawShapeMarginNoScaleUpwind,
1603 &Screen::drawShapeMarginNoScaleDownwind,
1604 &Screen::drawShapeMarginNoScaleUpwind,
1605 &Screen::drawShapeMarginNoScaleDownwind,
1606 &Screen::drawShapeMarginScaleUpwind,
1607 &Screen::drawShapeMarginScaleDownwind,
1608 &Screen::drawShapeMarginScaleUpwind,
1609 &Screen::drawShapeMarginScaleDownwind
1610 };
1611
1612 static const DsMarginSkipFunc dsSkipFunc[] = {
1613 &Screen::drawShapeMarginNoScaleUpwind,
1614 &Screen::drawShapeMarginNoScaleDownwind,
1615 &Screen::drawShapeMarginNoScaleUpwind,
1616 &Screen::drawShapeMarginNoScaleDownwind,
1617 &Screen::drawShapeSkipScaleUpwind,
1618 &Screen::drawShapeSkipScaleDownwind,
1619 &Screen::drawShapeSkipScaleUpwind,
1620 &Screen::drawShapeSkipScaleDownwind
1621 };
1622
1623 static const DsLineFunc dsLineFunc[] = {
1624 &Screen::drawShapeProcessLineNoScaleUpwind,
1625 &Screen::drawShapeProcessLineNoScaleDownwind,
1626 &Screen::drawShapeProcessLineNoScaleUpwind,
1627 &Screen::drawShapeProcessLineNoScaleDownwind,
1628 &Screen::drawShapeProcessLineScaleUpwind,
1629 &Screen::drawShapeProcessLineScaleDownwind,
1630 &Screen::drawShapeProcessLineScaleUpwind,
1631 &Screen::drawShapeProcessLineScaleDownwind
1632 };
1633
1634 static const DsPlotFunc dsPlotFunc[] = {
1635 &Screen::drawShapePlotType0, // used by Kyra 1 + 2
1636 &Screen::drawShapePlotType1, // used by Kyra 3
1637 0,
1638 &Screen::drawShapePlotType3_7, // used by Kyra 3 (shadow)
1639 &Screen::drawShapePlotType4, // used by Kyra 1, 2 + 3
1640 &Screen::drawShapePlotType5, // used by Kyra 1
1641 &Screen::drawShapePlotType6, // used by Kyra 1 (invisibility)
1642 &Screen::drawShapePlotType3_7, // used by Kyra 1 (invisibility)
1643 &Screen::drawShapePlotType8, // used by Kyra 2
1644 &Screen::drawShapePlotType9, // used by Kyra 1 + 3
1645 0,
1646 &Screen::drawShapePlotType11_15, // used by Kyra 1 (invisibility) + Kyra 3 (shadow)
1647 &Screen::drawShapePlotType12, // used by Kyra 2
1648 &Screen::drawShapePlotType13, // used by Kyra 1
1649 &Screen::drawShapePlotType14, // used by Kyra 1 (invisibility)
1650 &Screen::drawShapePlotType11_15, // used by Kyra 1 (invisibility)
1651 &Screen::drawShapePlotType16, // used by LoL PC-98/16 Colors (teleporters),
1652 0, 0, 0,
1653 &Screen::drawShapePlotType20, // used by LoL (heal spell effect)
1654 &Screen::drawShapePlotType21, // used by LoL (white tower spirits)
1655 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1656 0,
1657 &Screen::drawShapePlotType33, // used by LoL (blood spots on the floor)
1658 0, 0, 0,
1659 &Screen::drawShapePlotType37, // used by LoL (monsters)
1660 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1661 &Screen::drawShapePlotType48, // used by LoL (slime spots on the floor)
1662 0, 0, 0,
1663 &Screen::drawShapePlotType52, // used by LoL (projectiles)
1664 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1665 0
1666 };
1667
1668 int scaleCounterV = 0;
1669
1670 const int drawFunc = flags & 0x0F;
1671 _dsProcessMargin = dsMarginFunc[drawFunc];
1672 _dsScaleSkip = dsSkipFunc[drawFunc];
1673 _dsProcessLine = dsLineFunc[drawFunc];
1674
1675 const int ppc = (flags >> 8) & 0x3F;
1676 _dsPlot = dsPlotFunc[ppc];
1677 DsPlotFunc dsPlot2 = dsPlotFunc[ppc], dsPlot3 = dsPlotFunc[ppc];
1678 if (flags & 0x800)
1679 dsPlot3 = dsPlotFunc[((flags >> 8) & 0xF7) & 0x3F];
1680
1681 if (!_dsPlot || !dsPlot2 || !dsPlot3) {
1682 if (!dsPlot2)
1683 warning("Missing drawShape plotting method type %d", ppc);
1684 if (dsPlot3 != dsPlot2 && !dsPlot3)
1685 warning("Missing drawShape plotting method type %d", (((flags >> 8) & 0xF7) & 0x3F));
1686 return;
1687 }
1688
1689 int curY = y;
1690 const uint8 *src = shapeData;
1691 uint8 *dst = _dsDstPage = getPagePtr(pageNum);
1692
1693 const ScreenDim *dsDim = getScreenDim(sd);
1694 dst += (dsDim->sx << 3);
1695
1696 if (!(flags & 0x10))
1697 x -= (dsDim->sx << 3);
1698
1699 int x2 = (dsDim->w << 3);
1700 int y1 = dsDim->sy;
1701 if (flags & 0x10)
1702 y += y1;
1703
1704 int y2 = y1 + dsDim->h;
1705
1706 uint16 shapeFlags = READ_LE_UINT16(src); src += 2;
1707
1708 int shapeHeight = *src++;
1709 uint16 shapeWidth = READ_LE_UINT16(src); src += 2;
1710
1711 int shpWidthScaled1 = shapeWidth;
1712 int shpWidthScaled2 = shapeWidth;
1713
1714 if (flags & DSF_SCALE) {
1715 shapeHeight = (shapeHeight * _dsScaleH) >> 8;
1716 shpWidthScaled1 = shpWidthScaled2 = (shapeWidth * _dsScaleW) >> 8;
1717
1718 if (!shapeHeight || !shpWidthScaled1)
1719 return;
1720 }
1721
1722 if (flags & DSF_CENTER) {
1723 x -= (shpWidthScaled1 >> 1);
1724 y -= (shapeHeight >> 1);
1725 }
1726
1727 src += 3;
1728
1729 uint16 frameSize = READ_LE_UINT16(src); src += 2;
1730
1731 int colorTableColors = ((_vm->game() != GI_KYRA1) && (shapeFlags & 4)) ? *src++ : 16;
1732
1733 if (!(flags & 0x8000) && (shapeFlags & 1))
1734 _dsColorTable = src;
1735
1736 if (flags & 0x400)
1737 src += colorTableColors;
1738
1739 if (!(shapeFlags & 2)) {
1740 decodeFrame4(src, _animBlockPtr, frameSize);
1741 src = _animBlockPtr;
1742 }
1743
1744 int t = (flags & 2) ? y2 - y - shapeHeight : y - y1;
1745
1746 if (t < 0) {
1747 shapeHeight += t;
1748 if (shapeHeight <= 0) {
1749 return;
1750 }
1751
1752 t *= -1;
1753 const uint8 *srcBackUp = 0;
1754
1755 do {
1756 _dsOffscreenScaleVal1 = 0;
1757 srcBackUp = src;
1758 _dsTmpWidth = shapeWidth;
1759
1760 int cnt = shapeWidth;
1761 (this->*_dsScaleSkip)(dst, src, cnt);
1762
1763 scaleCounterV += _dsScaleH;
1764
1765 if (scaleCounterV & 0xFF00) {
1766 uint8 r = scaleCounterV >> 8;
1767 scaleCounterV &= 0xFF;
1768 t -= r;
1769 }
1770 } while (!(scaleCounterV & 0xFF00) && (t > 0));
1771
1772 if (t < 0) {
1773 src = srcBackUp;
1774 scaleCounterV += (-t << 8);
1775 }
1776
1777 if (!(flags & 2))
1778 y = y1;
1779 }
1780
1781 t = (flags & 2) ? y + shapeHeight - y1 : y2 - y;
1782 if (t <= 0)
1783 return;
1784
1785 if (t < shapeHeight) {
1786 shapeHeight = t;
1787 if (flags & 2)
1788 y = y1;
1789 }
1790
1791 _dsOffscreenLeft = 0;
1792 if (x < 0) {
1793 shpWidthScaled1 += x;
1794 _dsOffscreenLeft = -x;
1795 if (_dsOffscreenLeft >= shpWidthScaled2)
1796 return;
1797 x = 0;
1798 }
1799
1800 _dsOffscreenRight = 0;
1801 t = x2 - x;
1802
1803 if (t <= 0)
1804 return;
1805
1806 if (t < shpWidthScaled1) {
1807 shpWidthScaled1 = t;
1808 _dsOffscreenRight = shpWidthScaled2 - _dsOffscreenLeft - shpWidthScaled1;
1809 }
1810
1811 int dsPitch = 320;
1812 int ty = y;
1813
1814 if (flags & 2) {
1815 dsPitch *= -1;
1816 ty = ty - 1 + shapeHeight;
1817 }
1818
1819 if (flags & DSF_X_FLIPPED) {
1820 SWAP(_dsOffscreenLeft, _dsOffscreenRight);
1821 dst += (shpWidthScaled1 - 1);
1822 }
1823
1824 dst += (320 * ty + x);
1825
1826 if (flags & DSF_SCALE) {
1827 _dsOffscreenRight = 0;
1828 _dsOffscreenScaleVal2 = _dsOffscreenLeft;
1829 _dsOffscreenLeft <<= 8;
1830 _dsOffscreenScaleVal1 = (_dsOffscreenLeft % _dsScaleW) * -1;
1831 _dsOffscreenLeft /= _dsScaleW;
1832 }
1833
1834 if (shapeHeight <= 0 || shpWidthScaled1 <= 0)
1835 return;
1836
1837 if (pageNum == 0 || pageNum == 1)
1838 addDirtyRect(x, y, shpWidthScaled1, shapeHeight);
1839 clearOverlayRect(pageNum, x, y, shpWidthScaled1, shapeHeight);
1840
1841 uint8 *d = dst;
1842
1843 bool normalPlot = true;
1844 while (true) {
1845 while (!(scaleCounterV & 0xFF00)) {
1846 scaleCounterV += _dsScaleH;
1847 if (!(scaleCounterV & 0xFF00)) {
1848 _dsTmpWidth = shapeWidth;
1849 int cnt = shapeWidth;
1850 (this->*_dsScaleSkip)(d, src, cnt);
1851 }
1852 }
1853
1854 const uint8 *b_src = src;
1855
1856 do {
1857 src = b_src;
1858 _dsTmpWidth = shapeWidth;
1859 int cnt = _dsOffscreenLeft;
1860 int scaleState = (this->*_dsProcessMargin)(d, src, cnt);
1861
1862 if (_dsTmpWidth) {
1863 cnt += shpWidthScaled1;
1864 if (cnt > 0) {
1865 if (flags & 0x800)
1866 normalPlot = (curY > _maskMinY && curY < _maskMaxY);
1867 _dsPlot = normalPlot ? dsPlot2 : dsPlot3;
1868 (this->*_dsProcessLine)(d, src, cnt, scaleState);
1869 }
1870 cnt += _dsOffscreenRight;
1871 if (cnt)
1872 (this->*_dsScaleSkip)(d, src, cnt);
1873 }
1874 dst += dsPitch;
1875 d = dst;
1876 ++curY;
1877
1878 if (!--shapeHeight)
1879 return;
1880
1881 scaleCounterV -= 0x100;
1882 } while (scaleCounterV & 0xFF00);
1883 }
1884 }
1885
drawShapeMarginNoScaleUpwind(uint8 * & dst,const uint8 * & src,int & cnt)1886 int Screen::drawShapeMarginNoScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1887 while (cnt-- > 0) {
1888 if (*src++)
1889 continue;
1890 cnt = cnt + 1 - (*src++);
1891 }
1892
1893 cnt++;
1894 dst -= cnt;
1895 return 0;
1896 }
1897
drawShapeMarginNoScaleDownwind(uint8 * & dst,const uint8 * & src,int & cnt)1898 int Screen::drawShapeMarginNoScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1899 while (cnt-- > 0) {
1900 if (*src++)
1901 continue;
1902 cnt = cnt + 1 - (*src++);
1903 }
1904
1905 cnt++;
1906 dst += cnt;
1907 return 0;
1908 }
1909
drawShapeMarginScaleUpwind(uint8 * & dst,const uint8 * & src,int & cnt)1910 int Screen::drawShapeMarginScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1911 _dsTmpWidth -= cnt;
1912
1913 while (cnt > 0) {
1914 --cnt;
1915 if (*src++)
1916 continue;
1917
1918 cnt = cnt + 1 - (*src++);
1919 }
1920
1921 if (!cnt)
1922 return _dsOffscreenScaleVal1;
1923
1924 _dsTmpWidth += cnt;
1925
1926 int i = (_dsOffscreenLeft - cnt) * _dsScaleW;
1927 int res = i & 0xFF;
1928 i >>= 8;
1929 i -= _dsOffscreenScaleVal2;
1930 dst += i;
1931 cnt = -i;
1932
1933 return res;
1934 }
1935
drawShapeMarginScaleDownwind(uint8 * & dst,const uint8 * & src,int & cnt)1936 int Screen::drawShapeMarginScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1937 _dsTmpWidth -= cnt;
1938
1939 while (cnt > 0) {
1940 --cnt;
1941 if (*src++)
1942 continue;
1943
1944 cnt = cnt + 1 - (*src++);
1945 }
1946
1947 if (!cnt)
1948 return _dsOffscreenScaleVal1;
1949
1950 _dsTmpWidth += cnt;
1951
1952 int i = (_dsOffscreenLeft - cnt) * _dsScaleW;
1953 int res = i & 0xFF;
1954 i >>= 8;
1955 i -= _dsOffscreenScaleVal2;
1956 dst -= i;
1957 cnt = -i;
1958
1959 return res;
1960 }
1961
drawShapeSkipScaleUpwind(uint8 * & dst,const uint8 * & src,int & cnt)1962 int Screen::drawShapeSkipScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1963 cnt = _dsTmpWidth;
1964
1965 if (cnt <= 0)
1966 return 0;
1967
1968 do {
1969 --cnt;
1970 if (*src++)
1971 continue;
1972 cnt = cnt + 1 - (*src++);
1973 } while (cnt > 0);
1974
1975 return 0;
1976 }
1977
drawShapeSkipScaleDownwind(uint8 * & dst,const uint8 * & src,int & cnt)1978 int Screen::drawShapeSkipScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
1979 cnt = _dsTmpWidth;
1980 bool found = false;
1981
1982 if (cnt == 0)
1983 return 0;
1984
1985 do {
1986 --cnt;
1987 if (*src++)
1988 continue;
1989 found = true;
1990 cnt = cnt + 1 - (*src++);
1991 } while (cnt > 0);
1992
1993 return found ? 0 : _dsOffscreenScaleVal1;
1994 }
1995
drawShapeProcessLineNoScaleUpwind(uint8 * & dst,const uint8 * & src,int & cnt,int16)1996 void Screen::drawShapeProcessLineNoScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt, int16) {
1997 do {
1998 uint8 c = *src++;
1999 if (c) {
2000 uint8 *d = dst++;
2001 (this->*_dsPlot)(d, c);
2002 cnt--;
2003 } else {
2004 c = *src++;
2005 dst += c;
2006 cnt -= c;
2007 }
2008 } while (cnt > 0);
2009 }
2010
drawShapeProcessLineNoScaleDownwind(uint8 * & dst,const uint8 * & src,int & cnt,int16)2011 void Screen::drawShapeProcessLineNoScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt, int16) {
2012 do {
2013 uint8 c = *src++;
2014 if (c) {
2015 uint8 *d = dst--;
2016 (this->*_dsPlot)(d, c);
2017 cnt--;
2018 } else {
2019 c = *src++;
2020 dst -= c;
2021 cnt -= c;
2022 }
2023 } while (cnt > 0);
2024 }
2025
drawShapeProcessLineScaleUpwind(uint8 * & dst,const uint8 * & src,int & cnt,int16 scaleState)2026 void Screen::drawShapeProcessLineScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState) {
2027 int c = 0;
2028
2029 do {
2030 if ((scaleState & 0x8000) || !(scaleState & 0xFF00)) {
2031 c = *src++;
2032 _dsTmpWidth--;
2033 if (c) {
2034 scaleState += _dsScaleW;
2035 } else {
2036 _dsTmpWidth++;
2037 c = *src++;
2038 _dsTmpWidth -= c;
2039 int r = c * _dsScaleW + scaleState;
2040 dst += (r >> 8);
2041 cnt -= (r >> 8);
2042 scaleState = r & 0xFF;
2043 }
2044 } else if (scaleState) {
2045 (this->*_dsPlot)(dst++, c);
2046 scaleState -= 0x100;
2047 cnt--;
2048 }
2049 } while (cnt > 0);
2050
2051 cnt = -1;
2052 }
2053
drawShapeProcessLineScaleDownwind(uint8 * & dst,const uint8 * & src,int & cnt,int16 scaleState)2054 void Screen::drawShapeProcessLineScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState) {
2055 int c = 0;
2056
2057 do {
2058 if ((scaleState & 0x8000) || !(scaleState & 0xFF00)) {
2059 c = *src++;
2060 _dsTmpWidth--;
2061 if (c) {
2062 scaleState += _dsScaleW;
2063 } else {
2064 _dsTmpWidth++;
2065 c = *src++;
2066 _dsTmpWidth -= c;
2067 int r = c * _dsScaleW + scaleState;
2068 dst -= (r >> 8);
2069 cnt -= (r >> 8);
2070 scaleState = r & 0xFF;
2071 }
2072 } else {
2073 (this->*_dsPlot)(dst--, c);
2074 scaleState -= 0x100;
2075 cnt--;
2076 }
2077 } while (cnt > 0);
2078
2079 cnt = -1;
2080 }
2081
drawShapePlotType0(uint8 * dst,uint8 cmd)2082 void Screen::drawShapePlotType0(uint8 *dst, uint8 cmd) {
2083 *dst = cmd;
2084 }
2085
drawShapePlotType1(uint8 * dst,uint8 cmd)2086 void Screen::drawShapePlotType1(uint8 *dst, uint8 cmd) {
2087 for (int i = 0; i < _dsShapeFadingLevel; ++i)
2088 cmd = _dsShapeFadingTable[cmd];
2089
2090 if (cmd)
2091 *dst = cmd;
2092 }
2093
drawShapePlotType3_7(uint8 * dst,uint8 cmd)2094 void Screen::drawShapePlotType3_7(uint8 *dst, uint8 cmd) {
2095 cmd = *dst;
2096 for (int i = 0; i < _dsShapeFadingLevel; ++i)
2097 cmd = _dsShapeFadingTable[cmd];
2098
2099 if (cmd)
2100 *dst = cmd;
2101 }
2102
drawShapePlotType4(uint8 * dst,uint8 cmd)2103 void Screen::drawShapePlotType4(uint8 *dst, uint8 cmd) {
2104 *dst = _dsColorTable[cmd];
2105 }
2106
drawShapePlotType5(uint8 * dst,uint8 cmd)2107 void Screen::drawShapePlotType5(uint8 *dst, uint8 cmd) {
2108 cmd = _dsColorTable[cmd];
2109 for (int i = 0; i < _dsShapeFadingLevel; ++i)
2110 cmd = _dsShapeFadingTable[cmd];
2111
2112 if (cmd)
2113 *dst = cmd;
2114 }
2115
drawShapePlotType6(uint8 * dst,uint8 cmd)2116 void Screen::drawShapePlotType6(uint8 *dst, uint8 cmd) {
2117 int t = _drawShapeVar4 + _drawShapeVar5;
2118 if (t & 0xFF00) {
2119 cmd = dst[_drawShapeVar3];
2120 t &= 0xFF;
2121 } else {
2122 cmd = _dsColorTable[cmd];
2123 }
2124
2125 _drawShapeVar4 = t;
2126 *dst = cmd;
2127 }
2128
drawShapePlotType8(uint8 * dst,uint8 cmd)2129 void Screen::drawShapePlotType8(uint8 *dst, uint8 cmd) {
2130 uint32 relOffs = dst - _dsDstPage;
2131 int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2132 if (_dsDrawLayer < t)
2133 cmd = _shapePages[1][relOffs];
2134
2135 *dst = cmd;
2136 }
2137
drawShapePlotType9(uint8 * dst,uint8 cmd)2138 void Screen::drawShapePlotType9(uint8 *dst, uint8 cmd) {
2139 uint32 relOffs = dst - _dsDstPage;
2140 int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2141 if (_dsDrawLayer < t) {
2142 cmd = _shapePages[1][relOffs];
2143 } else {
2144 for (int i = 0; i < _dsShapeFadingLevel; ++i)
2145 cmd = _dsShapeFadingTable[cmd];
2146 }
2147
2148 if (cmd)
2149 *dst = cmd;
2150 }
2151
drawShapePlotType11_15(uint8 * dst,uint8 cmd)2152 void Screen::drawShapePlotType11_15(uint8 *dst, uint8 cmd) {
2153 uint32 relOffs = dst - _dsDstPage;
2154 int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2155
2156 if (_dsDrawLayer < t) {
2157 cmd = _shapePages[1][relOffs];
2158 } else {
2159 cmd = *dst;
2160 for (int i = 0; i < _dsShapeFadingLevel; ++i)
2161 cmd = _dsShapeFadingTable[cmd];
2162 }
2163
2164 if (cmd)
2165 *dst = cmd;
2166 }
2167
drawShapePlotType12(uint8 * dst,uint8 cmd)2168 void Screen::drawShapePlotType12(uint8 *dst, uint8 cmd) {
2169 uint32 relOffs = dst - _dsDstPage;
2170 int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2171 if (_dsDrawLayer < t) {
2172 cmd = _shapePages[1][relOffs];
2173 } else {
2174 cmd = _dsColorTable[cmd];
2175 }
2176
2177 *dst = cmd;
2178 }
2179
drawShapePlotType13(uint8 * dst,uint8 cmd)2180 void Screen::drawShapePlotType13(uint8 *dst, uint8 cmd) {
2181 uint32 relOffs = dst - _dsDstPage;
2182 int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2183 if (_dsDrawLayer < t) {
2184 cmd = _shapePages[1][relOffs];
2185 } else {
2186 cmd = _dsColorTable[cmd];
2187 for (int i = 0; i < _dsShapeFadingLevel; ++i)
2188 cmd = _dsShapeFadingTable[cmd];
2189 }
2190
2191 if (cmd)
2192 *dst = cmd;
2193 }
2194
drawShapePlotType14(uint8 * dst,uint8 cmd)2195 void Screen::drawShapePlotType14(uint8 *dst, uint8 cmd) {
2196 uint32 relOffs = dst - _dsDstPage;
2197 int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
2198 if (_dsDrawLayer < t) {
2199 cmd = _shapePages[1][relOffs];
2200 } else {
2201 t = _drawShapeVar4 + _drawShapeVar5;
2202 if (t & 0xFF00) {
2203 cmd = dst[_drawShapeVar3];
2204 t &= 0xFF;
2205 } else {
2206 cmd = _dsColorTable[cmd];
2207 }
2208 }
2209
2210 _drawShapeVar4 = t;
2211 *dst = cmd;
2212 }
2213
drawShapePlotType16(uint8 * dst,uint8 cmd)2214 void Screen::drawShapePlotType16(uint8 *dst, uint8 cmd) {
2215 uint8 tOffs = _dsTransparencyTable1[cmd];
2216 if (!(tOffs & 0x80))
2217 cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
2218 *dst = cmd;
2219 }
2220
drawShapePlotType20(uint8 * dst,uint8 cmd)2221 void Screen::drawShapePlotType20(uint8 *dst, uint8 cmd) {
2222 cmd = _dsColorTable[cmd];
2223 uint8 tOffs = _dsTransparencyTable1[cmd];
2224 if (!(tOffs & 0x80))
2225 cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
2226
2227 *dst = cmd;
2228 }
2229
drawShapePlotType21(uint8 * dst,uint8 cmd)2230 void Screen::drawShapePlotType21(uint8 *dst, uint8 cmd) {
2231 cmd = _dsColorTable[cmd];
2232 uint8 tOffs = _dsTransparencyTable1[cmd];
2233 if (!(tOffs & 0x80))
2234 cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
2235
2236 for (int i = 0; i < _dsShapeFadingLevel; ++i)
2237 cmd = _dsShapeFadingTable[cmd];
2238
2239 if (cmd)
2240 *dst = cmd;
2241 }
2242
drawShapePlotType33(uint8 * dst,uint8 cmd)2243 void Screen::drawShapePlotType33(uint8 *dst, uint8 cmd) {
2244 if (cmd == 255) {
2245 *dst = _dsBackgroundFadingTable[*dst];
2246 } else {
2247 for (int i = 0; i < _dsShapeFadingLevel; ++i)
2248 cmd = _dsShapeFadingTable[cmd];
2249 if (cmd)
2250 *dst = cmd;
2251 }
2252 }
2253
drawShapePlotType37(uint8 * dst,uint8 cmd)2254 void Screen::drawShapePlotType37(uint8 *dst, uint8 cmd) {
2255 cmd = _dsColorTable[cmd];
2256
2257 if (cmd == 255) {
2258 cmd = _dsBackgroundFadingTable[*dst];
2259 } else {
2260 for (int i = 0; i < _dsShapeFadingLevel; ++i)
2261 cmd = _dsShapeFadingTable[cmd];
2262 }
2263
2264 if (cmd)
2265 *dst = cmd;
2266 }
2267
drawShapePlotType48(uint8 * dst,uint8 cmd)2268 void Screen::drawShapePlotType48(uint8 *dst, uint8 cmd) {
2269 uint8 offs = _dsTransparencyTable1[cmd];
2270 if (!(offs & 0x80))
2271 cmd = _dsTransparencyTable2[(offs << 8) | *dst];
2272 *dst = cmd;
2273 }
2274
drawShapePlotType52(uint8 * dst,uint8 cmd)2275 void Screen::drawShapePlotType52(uint8 *dst, uint8 cmd) {
2276 cmd = _dsColorTable[cmd];
2277 uint8 offs = _dsTransparencyTable1[cmd];
2278
2279 if (!(offs & 0x80))
2280 cmd = _dsTransparencyTable2[(offs << 8) | *dst];
2281
2282 *dst = cmd;
2283 }
2284
decodeFrame1(const uint8 * src,uint8 * dst,uint32 size)2285 void Screen::decodeFrame1(const uint8 *src, uint8 *dst, uint32 size) {
2286 const uint8 *dstEnd = dst + size;
2287
2288 struct Pattern {
2289 const uint8 *pos;
2290 uint16 len;
2291 };
2292
2293 Pattern *patterns = new Pattern[3840];
2294 uint16 numPatterns = 0;
2295 uint8 nib = 0;
2296
2297 uint16 code = decodeEGAGetCode(src, nib);
2298 uint8 last = code & 0xFF;
2299
2300 uint8 *dstPrev = dst;
2301 uint16 count = 1;
2302 uint16 countPrev = 1;
2303
2304 *dst++ = last;
2305
2306 while (dst < dstEnd) {
2307 code = decodeEGAGetCode(src, nib);
2308 uint8 cmd = code >> 8;
2309
2310 if (cmd--) {
2311 code = (cmd << 8) | (code & 0xFF);
2312 uint8 *tmpDst = dst;
2313
2314 if (code < numPatterns) {
2315 const uint8 *tmpSrc = patterns[code].pos;
2316 countPrev = patterns[code].len;
2317 last = *tmpSrc;
2318 for (int i = 0; i < countPrev; i++)
2319 *dst++ = *tmpSrc++;
2320
2321 } else {
2322 const uint8 *tmpSrc = dstPrev;
2323 count = countPrev;
2324 for (int i = 0; i < countPrev; i++)
2325 *dst++ = *tmpSrc++;
2326 *dst++ = last;
2327 countPrev++;
2328 }
2329
2330 if (numPatterns < 3840) {
2331 patterns[numPatterns].pos = dstPrev;
2332 patterns[numPatterns++].len = ++count;
2333 }
2334
2335 dstPrev = tmpDst;
2336 count = countPrev;
2337
2338 } else {
2339 *dst++ = last = (code & 0xFF);
2340
2341 if (numPatterns < 3840) {
2342 patterns[numPatterns].pos = dstPrev;
2343 patterns[numPatterns++].len = ++count;
2344 }
2345
2346 dstPrev = dst - 1;
2347 count = 1;
2348 countPrev = 1;
2349 }
2350 }
2351 delete[] patterns;
2352 }
2353
decodeEGAGetCode(const uint8 * & pos,uint8 & nib)2354 uint16 Screen::decodeEGAGetCode(const uint8 *&pos, uint8 &nib) {
2355 uint16 res = READ_BE_UINT16(pos++);
2356 if ((++nib) & 1) {
2357 res >>= 4;
2358 } else {
2359 pos++;
2360 res &= 0xFFF;
2361 }
2362 return res;
2363 }
2364
decodeFrame3(const uint8 * src,uint8 * dst,uint32 size,bool isAmiga)2365 void Screen::decodeFrame3(const uint8 *src, uint8 *dst, uint32 size, bool isAmiga) {
2366 const uint8 *dstEnd = dst + size;
2367 while (dst < dstEnd) {
2368 int8 code = *src++;
2369 if (code == 0) {
2370 uint16 sz = isAmiga ? READ_LE_UINT16(src) : READ_BE_UINT16(src);
2371 src += 2;
2372 memset(dst, *src++, sz);
2373 dst += sz;
2374 } else if (code < 0) {
2375 memset(dst, *src++, -code);
2376 dst -= code;
2377 } else {
2378 memcpy(dst, src, code);
2379 dst += code;
2380 src += code;
2381 }
2382 }
2383 }
2384
decodeFrame4(const uint8 * src,uint8 * dst,uint32 dstSize)2385 uint Screen::decodeFrame4(const uint8 *src, uint8 *dst, uint32 dstSize) {
2386 uint8 *dstOrig = dst;
2387 uint8 *dstEnd = dst + dstSize;
2388 while (1) {
2389 int count = dstEnd - dst;
2390 if (count == 0)
2391 break;
2392
2393 uint8 code = *src++;
2394 if (!(code & 0x80)) { // 8th bit isn't set
2395 int len = MIN(count, (code >> 4) + 3); //upper half of code is the length
2396 int offs = ((code & 0xF) << 8) | *src++; //lower half of code as byte 2 of offset.
2397 const uint8 *dstOffs = dst - offs;
2398 while (len--)
2399 *dst++ = *dstOffs++;
2400 } else if (code & 0x40) { // 7th bit is set
2401 int len = (code & 0x3F) + 3;
2402 if (code == 0xFE) {
2403 len = READ_LE_UINT16(src); src += 2;
2404 if (len > count)
2405 len = count;
2406
2407 memset(dst, *src++, len); dst += len;
2408 } else {
2409 if (code == 0xFF) {
2410 len = READ_LE_UINT16(src);
2411 src += 2;
2412 }
2413
2414 int offs = READ_LE_UINT16(src); src += 2;
2415 if (len > count)
2416 len = count;
2417
2418 const uint8 *dstOffs = dstOrig + offs;
2419 while (len--)
2420 *dst++ = *dstOffs++;
2421 }
2422 } else if (code != 0x80) { // not just the 8th bit set.
2423 //Copy some bytes from source to dest.
2424 int len = MIN(count, code & 0x3F);
2425 while (len--)
2426 *dst++ = *src++;
2427 } else {
2428 break;
2429 }
2430 }
2431 return dst - dstOrig;
2432 }
2433
decodeFrameDelta(uint8 * dst,const uint8 * src,bool noXor)2434 void Screen::decodeFrameDelta(uint8 *dst, const uint8 *src, bool noXor) {
2435 if (noXor)
2436 wrapped_decodeFrameDelta<true>(dst, src);
2437 else
2438 wrapped_decodeFrameDelta<false>(dst, src);
2439 }
2440
2441 template<bool noXor>
wrapped_decodeFrameDelta(uint8 * dst,const uint8 * src)2442 void Screen::wrapped_decodeFrameDelta(uint8 *dst, const uint8 *src) {
2443 while (1) {
2444 uint8 code = *src++;
2445 if (code == 0) {
2446 uint8 len = *src++;
2447 code = *src++;
2448 while (len--) {
2449 if (noXor)
2450 *dst++ = code;
2451 else
2452 *dst++ ^= code;
2453 }
2454 } else if (code & 0x80) {
2455 code -= 0x80;
2456 if (code != 0) {
2457 dst += code;
2458 } else {
2459 uint16 subcode = READ_LE_UINT16(src); src += 2;
2460 if (subcode == 0) {
2461 break;
2462 } else if (subcode & 0x8000) {
2463 subcode -= 0x8000;
2464 if (subcode & 0x4000) {
2465 uint16 len = subcode - 0x4000;
2466 code = *src++;
2467 while (len--) {
2468 if (noXor)
2469 *dst++ = code;
2470 else
2471 *dst++ ^= code;
2472 }
2473 } else {
2474 while (subcode--) {
2475 if (noXor)
2476 *dst++ = *src++;
2477 else
2478 *dst++ ^= *src++;
2479 }
2480 }
2481 } else {
2482 dst += subcode;
2483 }
2484 }
2485 } else {
2486 while (code--) {
2487 if (noXor)
2488 *dst++ = *src++;
2489 else
2490 *dst++ ^= *src++;
2491 }
2492 }
2493 }
2494 }
2495
decodeFrameDeltaPage(uint8 * dst,const uint8 * src,int pitch,bool noXor)2496 void Screen::decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch, bool noXor) {
2497 if (noXor)
2498 wrapped_decodeFrameDeltaPage<true>(dst, src, pitch);
2499 else
2500 wrapped_decodeFrameDeltaPage<false>(dst, src, pitch);
2501 }
2502
convertAmigaGfx(uint8 * data,int w,int h,int depth,bool wsa,int bytesPerPlane)2503 void Screen::convertAmigaGfx(uint8 *data, int w, int h, int depth, bool wsa, int bytesPerPlane) {
2504 const int planeWidth = (bytesPerPlane == -1) ? (w + 7) / 8 : bytesPerPlane;
2505 const int planeSize = planeWidth * h;
2506 const uint imageSize = planeSize * depth;
2507
2508 // Our static buffer which holds the plane data. We need this
2509 // because the "data" pointer is both source and destination pointer.
2510 // The buffer has enough space to fit the AMIGA MSC files, which are
2511 // the biggest graphics files found in the AMIGA version.
2512 static uint8 temp[40320];
2513 assert(imageSize <= sizeof(temp));
2514
2515 // WSA files store their graphics data in a little different format, than
2516 // the usual AMIGA graphics format used in BitMaps. Thus we need to do
2517 // some special handling for them here. Means we convert them into
2518 // the usual format.
2519 //
2520 // TODO: We might think of moving this conversion into the WSAMovieAmiga
2521 // class.
2522 if (wsa) {
2523 const byte *src = data;
2524 for (int y = 0; y < h; ++y) {
2525 for (int x = 0; x < planeWidth; ++x)
2526 for (int i = 0; i < depth; ++i)
2527 temp[y * planeWidth + x + planeSize * i] = *src++;
2528 }
2529 } else {
2530 memcpy(temp, data, imageSize);
2531 }
2532
2533 for (int y = 0; y < h; ++y) {
2534 for (int x = 0; x < w; ++x) {
2535 const int bytePos = x / 8 + y * planeWidth;
2536 const int bitPos = 7 - (x & 7); // x & 7 == x % 8
2537
2538 byte col = 0;
2539
2540 for (int i = 0; i < depth; ++i)
2541 col |= ((temp[bytePos + planeSize * i] >> bitPos) & 1) << i;
2542
2543 *data++ = col;
2544 }
2545 }
2546 }
2547
convertAmigaMsc(uint8 * data)2548 void Screen::convertAmigaMsc(uint8 *data) {
2549 // MSC files are always 320x144, thus we can safely assume
2550 // this to be correct. Also they contain 7 planes instead
2551 // of the normal 5 planes, which is used in 32 color mode.
2552 // The need for 7 planes can be explained, because the MSC
2553 // files have 6 bits for the layer number (bits 1 to 6)
2554 // and one bit for the "blocked" flag (bit 0), and every
2555 // plane contains one bit per pixel.
2556 convertAmigaGfx(data, 320, 144, 7);
2557
2558 // We need to do some post conversion, since
2559 // the AMIGA MSC format is different from the DOS
2560 // one we use internally for our code.That is even
2561 // after converting it from the AMIGA plane based
2562 // approach to one byte per pixel approach.
2563 for (int i = 0; i < 320 * 144; ++i) {
2564 // The lowest bit indicates, whether the position
2565 // is walkable or not. If the bit is set, the
2566 // position is walkable, elsewise it is blocked.
2567 if (data[i] & 1)
2568 data[i] &= 0xFE;
2569 else
2570 data[i] |= 0x80;
2571
2572 // The graphics layer for the pixel is saved
2573 // in the following format:
2574 // The highest bit set indicates the number of
2575 // the graphics layer. We count the first
2576 // bit as 0 here, thus we need to add one,
2577 // to get the correct number.
2578 //
2579 // Funnily since the first bit (bit 0) is
2580 // resevered for testing whether the position
2581 // is walkable or not, there is no possibility
2582 // for layer 1 to be present.
2583 int layer = 0;
2584 for (int k = 0; k < 7; ++k)
2585 if (data[i] & (1 << k))
2586 layer = k + 1;
2587
2588 data[i] &= 0x80;
2589 data[i] |= layer;
2590 }
2591 }
2592
2593 template<bool noXor>
wrapped_decodeFrameDeltaPage(uint8 * dst,const uint8 * src,int pitch)2594 void Screen::wrapped_decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch) {
2595 int count = 0;
2596 uint8 *dstNext = dst;
2597 while (1) {
2598 uint8 code = *src++;
2599 if (code == 0) {
2600 uint8 len = *src++;
2601 code = *src++;
2602 while (len--) {
2603 if (noXor)
2604 *dst++ = code;
2605 else
2606 *dst++ ^= code;
2607
2608 if (++count == pitch) {
2609 count = 0;
2610 dstNext += SCREEN_W;
2611 dst = dstNext;
2612 }
2613 }
2614 } else if (code & 0x80) {
2615 code -= 0x80;
2616 if (code != 0) {
2617 dst += code;
2618
2619 count += code;
2620 while (count >= pitch) {
2621 count -= pitch;
2622 dstNext += SCREEN_W;
2623 dst = dstNext + count;
2624 }
2625 } else {
2626 uint16 subcode = READ_LE_UINT16(src); src += 2;
2627 if (subcode == 0) {
2628 break;
2629 } else if (subcode & 0x8000) {
2630 subcode -= 0x8000;
2631 if (subcode & 0x4000) {
2632 uint16 len = subcode - 0x4000;
2633 code = *src++;
2634 while (len--) {
2635 if (noXor)
2636 *dst++ = code;
2637 else
2638 *dst++ ^= code;
2639
2640 if (++count == pitch) {
2641 count = 0;
2642 dstNext += SCREEN_W;
2643 dst = dstNext;
2644 }
2645 }
2646 } else {
2647 while (subcode--) {
2648 if (noXor)
2649 *dst++ = *src++;
2650 else
2651 *dst++ ^= *src++;
2652
2653 if (++count == pitch) {
2654 count = 0;
2655 dstNext += SCREEN_W;
2656 dst = dstNext;
2657 }
2658 }
2659 }
2660 } else {
2661 dst += subcode;
2662
2663 count += subcode;
2664 while (count >= pitch) {
2665 count -= pitch;
2666 dstNext += SCREEN_W;
2667 dst = dstNext + count;
2668 }
2669
2670 }
2671 }
2672 } else {
2673 while (code--) {
2674 if (noXor)
2675 *dst++ = *src++;
2676 else
2677 *dst++ ^= *src++;
2678
2679 if (++count == pitch) {
2680 count = 0;
2681 dstNext += SCREEN_W;
2682 dst = dstNext;
2683 }
2684 }
2685 }
2686 }
2687 }
2688
encodeShape(int x,int y,int w,int h,int flags)2689 uint8 *Screen::encodeShape(int x, int y, int w, int h, int flags) {
2690 uint8 *srcPtr = &_pagePtrs[_curPage][y * SCREEN_W + x];
2691 int16 shapeSize = 0;
2692 uint8 *tmp = srcPtr;
2693 int xpos = w;
2694
2695 for (int i = h; i > 0; --i) {
2696 uint8 *start = tmp;
2697 shapeSize += w;
2698 xpos = w;
2699 while (xpos) {
2700 uint8 value = *tmp++;
2701 --xpos;
2702
2703 if (!value) {
2704 shapeSize += 2;
2705 int16 curX = xpos;
2706 bool skip = false;
2707
2708 while (xpos) {
2709 value = *tmp++;
2710 --xpos;
2711
2712 if (value) {
2713 skip = true;
2714 break;
2715 }
2716 }
2717
2718 if (!skip)
2719 ++curX;
2720
2721 curX -= xpos;
2722 shapeSize -= curX;
2723
2724 while (curX > 0xFF) {
2725 curX -= 0xFF;
2726 shapeSize += 2;
2727 }
2728 }
2729 }
2730
2731 tmp = start + SCREEN_W;
2732 }
2733
2734 int16 shapeSize2 = shapeSize;
2735 if (_vm->gameFlags().useAltShapeHeader)
2736 shapeSize += 12;
2737 else
2738 shapeSize += 10;
2739
2740 if (flags & 1)
2741 shapeSize += 16;
2742
2743 uint8 table[274];
2744 int tableIndex = 0;
2745
2746 uint8 *newShape = 0;
2747 newShape = new uint8[shapeSize+16];
2748 assert(newShape);
2749
2750 byte *dst = newShape;
2751
2752 if (_vm->gameFlags().useAltShapeHeader)
2753 dst += 2;
2754
2755 WRITE_LE_UINT16(dst, (flags & 3)); dst += 2;
2756 *dst = h; dst += 1;
2757 WRITE_LE_UINT16(dst, w); dst += 2;
2758 *dst = h; dst += 1;
2759 WRITE_LE_UINT16(dst, shapeSize); dst += 2;
2760 WRITE_LE_UINT16(dst, shapeSize2); dst += 2;
2761
2762 byte *src = srcPtr;
2763 if (flags & 1) {
2764 dst += 16;
2765 memset(table, 0, sizeof(table));
2766 tableIndex = 1;
2767 }
2768
2769 for (int ypos = h; ypos > 0; --ypos) {
2770 uint8 *srcBackUp = src;
2771 xpos = w;
2772 while (xpos) {
2773 uint8 value = *src++;
2774 if (value) {
2775 if (flags & 1) {
2776 if (!table[value]) {
2777 if (tableIndex == 16) {
2778 value = 1;
2779 } else {
2780 table[0x100+tableIndex] = value;
2781 table[value] = tableIndex;
2782 ++tableIndex;
2783 value = table[value];
2784 }
2785 } else {
2786 value = table[value];
2787 }
2788 }
2789 --xpos;
2790 *dst++ = value;
2791 } else {
2792 int16 temp = 1;
2793 --xpos;
2794
2795 while (xpos) {
2796 if (*src)
2797 break;
2798 ++src;
2799 ++temp;
2800 --xpos;
2801 }
2802
2803 while (temp > 0xFF) {
2804 *dst++ = 0;
2805 *dst++ = 0xFF;
2806 temp -= 0xFF;
2807 }
2808
2809 if (temp & 0xFF) {
2810 *dst++ = 0;
2811 *dst++ = temp & 0xFF;
2812 }
2813 }
2814 }
2815 src = srcBackUp + SCREEN_W;
2816 }
2817
2818 if (!(flags & 2)) {
2819 if (shapeSize > _animBlockSize) {
2820 dst = newShape;
2821 if (_vm->gameFlags().useAltShapeHeader)
2822 dst += 2;
2823
2824 flags = READ_LE_UINT16(dst);
2825 flags |= 2;
2826 WRITE_LE_UINT16(dst, flags);
2827 } else {
2828 src = newShape;
2829 if (_vm->gameFlags().useAltShapeHeader)
2830 src += 2;
2831 if (flags & 1)
2832 src += 16;
2833
2834 src += 10;
2835 uint8 *shapePtrBackUp = src;
2836 dst = _animBlockPtr;
2837 memcpy(dst, src, shapeSize2);
2838
2839 int16 size = encodeShapeAndCalculateSize(_animBlockPtr, shapePtrBackUp, shapeSize2);
2840 if (size > shapeSize2) {
2841 shapeSize -= shapeSize2 - size;
2842 uint8 *newShape2 = new uint8[shapeSize];
2843 assert(newShape2);
2844 memcpy(newShape2, newShape, shapeSize);
2845 delete[] newShape;
2846 newShape = newShape2;
2847 } else {
2848 dst = shapePtrBackUp;
2849 src = _animBlockPtr;
2850 memcpy(dst, src, shapeSize2);
2851 dst = newShape;
2852 if (_vm->gameFlags().useAltShapeHeader)
2853 dst += 2;
2854 flags = READ_LE_UINT16(dst);
2855 flags |= 2;
2856 WRITE_LE_UINT16(dst, flags);
2857 }
2858 }
2859 }
2860
2861 dst = newShape;
2862 if (_vm->gameFlags().useAltShapeHeader)
2863 dst += 2;
2864 WRITE_LE_UINT16((dst + 6), shapeSize);
2865
2866 if (flags & 1) {
2867 dst = newShape + 10;
2868 if (_vm->gameFlags().useAltShapeHeader)
2869 dst += 2;
2870 src = &table[0x100];
2871 memcpy(dst, src, sizeof(uint8)*16);
2872 }
2873
2874 return newShape;
2875 }
2876
encodeShapeAndCalculateSize(uint8 * from,uint8 * to,int size_to)2877 int16 Screen::encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int size_to) {
2878 byte *fromPtrEnd = from + size_to;
2879 bool skipPixel = true;
2880 byte *tempPtr = 0;
2881 byte *toPtr = to;
2882 byte *fromPtr = from;
2883 byte *toPtr2 = to;
2884
2885 *to++ = 0x81;
2886 *to++ = *from++;
2887
2888 while (from < fromPtrEnd) {
2889 byte *curToPtr = to;
2890 to = fromPtr;
2891 int size = 1;
2892
2893 while (true) {
2894 byte curPixel = *from;
2895 if (curPixel == *(from+0x40)) {
2896 byte *toBackUp = to;
2897 to = from;
2898
2899 for (int i = 0; i < (fromPtrEnd - from); ++i) {
2900 if (*to++ != curPixel)
2901 break;
2902 }
2903 --to;
2904 uint16 diffSize = (to - from);
2905 if (diffSize >= 0x41) {
2906 skipPixel = false;
2907 from = to;
2908 to = curToPtr;
2909 *to++ = 0xFE;
2910 WRITE_LE_UINT16(to, diffSize); to += 2;
2911 *to++ = curPixel;
2912 curToPtr = to;
2913 to = toBackUp;
2914 continue;
2915 } else {
2916 to = toBackUp;
2917 }
2918 }
2919
2920 bool breakLoop = false;
2921 while (true) {
2922 if ((from - to) == 0) {
2923 breakLoop = true;
2924 break;
2925 }
2926 for (int i = 0; i < (from - to); ++i) {
2927 if (*to++ == curPixel)
2928 break;
2929 }
2930 if (*(to-1) == curPixel) {
2931 if (*(from+size-1) != *(to+size-2))
2932 continue;
2933
2934 byte *fromBackUp = from;
2935 byte *toBackUp = to;
2936 --to;
2937 const int checkSize = fromPtrEnd - from;
2938 for (int i = 0; i < checkSize; ++i) {
2939 if (*from++ != *to++)
2940 break;
2941 }
2942 if (*(from - 1) == *(to - 1))
2943 ++to;
2944 from = fromBackUp;
2945 int temp = to - toBackUp;
2946 to = toBackUp;
2947 if (temp >= size) {
2948 size = temp;
2949 tempPtr = toBackUp - 1;
2950 }
2951 break;
2952 } else {
2953 breakLoop = true;
2954 break;
2955 }
2956 }
2957
2958 if (breakLoop)
2959 break;
2960 }
2961
2962 to = curToPtr;
2963 if (size > 2) {
2964 uint16 word = 0;
2965 if (size <= 0x0A) {
2966 uint16 diffSize = from - tempPtr;
2967 if (diffSize <= 0x0FFF) {
2968 byte highByte = ((diffSize & 0xFF00) >> 8) + (((size & 0xFF) - 3) << 4);
2969 word = ((diffSize & 0xFF) << 8) | highByte;
2970 WRITE_LE_UINT16(to, word); to += 2;
2971 from += size;
2972 skipPixel = false;
2973 continue;
2974 }
2975 }
2976
2977 if (size > 0x40) {
2978 *to++ = 0xFF;
2979 WRITE_LE_UINT16(to, size); to += 2;
2980 } else {
2981 *to++ = ((size & 0xFF) - 3) | 0xC0;
2982 }
2983
2984 word = tempPtr - fromPtr;
2985 WRITE_LE_UINT16(to, word); to += 2;
2986 from += size;
2987 skipPixel = false;
2988 } else {
2989 if (!skipPixel) {
2990 toPtr2 = to;
2991 *to++ = 0x80;
2992 }
2993
2994 if (*toPtr2 == 0xBF) {
2995 toPtr2 = to;
2996 *to++ = 0x80;
2997 }
2998
2999 ++(*toPtr2);
3000 *to++ = *from++;
3001 skipPixel = true;
3002 }
3003 }
3004 *to++ = 0x80;
3005
3006 return (to - toPtr);
3007 }
3008
shade16bitColor(uint16 col)3009 uint16 Screen::shade16bitColor(uint16 col) {
3010 uint8 r = (col & 0x1f);
3011 uint8 g = (col & 0x3E0) >> 5;
3012 uint8 b = (col & 0x7C00) >> 10;
3013
3014 r = (r > _16bitShadingLevel) ? r - _16bitShadingLevel : 0;
3015 g = (g > _16bitShadingLevel) ? g - _16bitShadingLevel : 0;
3016 b = (b > _16bitShadingLevel) ? b - _16bitShadingLevel : 0;
3017
3018 return (b << 10) | (g << 5) | r;
3019 }
3020
hideMouse()3021 void Screen::hideMouse() {
3022 ++_mouseLockCount;
3023 CursorMan.showMouse(false);
3024 }
3025
showMouse()3026 void Screen::showMouse() {
3027 if (_mouseLockCount == 1) {
3028 CursorMan.showMouse(true);
3029
3030 // We need to call OSystem::updateScreen here, else the mouse cursor
3031 // will only be visible on mouse movment.
3032 _system->updateScreen();
3033 }
3034
3035 if (_mouseLockCount > 0)
3036 _mouseLockCount--;
3037 }
3038
3039
isMouseVisible() const3040 bool Screen::isMouseVisible() const {
3041 return _mouseLockCount == 0;
3042 }
3043
setShapePages(int page1,int page2,int minY,int maxY)3044 void Screen::setShapePages(int page1, int page2, int minY, int maxY) {
3045 _shapePages[0] = _pagePtrs[page1];
3046 _shapePages[1] = _pagePtrs[page2];
3047 _maskMinY = minY;
3048 _maskMaxY = maxY;
3049 }
3050
setMouseCursor(int x,int y,const byte * shape)3051 void Screen::setMouseCursor(int x, int y, const byte *shape) {
3052 if (!shape)
3053 return;
3054
3055 if (_vm->gameFlags().useAltShapeHeader)
3056 shape += 2;
3057
3058 int mouseHeight = *(shape + 2);
3059 int mouseWidth = (READ_LE_UINT16(shape + 3)) + 2;
3060
3061 if (_vm->gameFlags().useAltShapeHeader)
3062 shape -= 2;
3063
3064 if (_vm->gameFlags().useHiRes) {
3065 x <<= 1;
3066 y <<= 1;
3067 mouseWidth <<= 1;
3068 mouseHeight <<= 1;
3069 }
3070
3071 uint8 *cursor = new uint8[mouseHeight * mouseWidth];
3072 fillRect(0, 0, mouseWidth, mouseHeight, _cursorColorKey, 8);
3073 drawShape(8, shape, 0, 0, 0, 0);
3074
3075 int xOffset = 0;
3076
3077 if (_vm->gameFlags().useHiRes) {
3078 xOffset = mouseWidth;
3079 scale2x<uint8, uint16>(getPagePtr(8) + mouseWidth, SCREEN_W, getPagePtr(8), SCREEN_W, mouseWidth, mouseHeight);
3080 postProcessCursor(getPagePtr(8) + mouseWidth, mouseWidth, mouseHeight, SCREEN_W);
3081 } else {
3082 postProcessCursor(getPagePtr(8), mouseWidth, mouseHeight, SCREEN_W);
3083 }
3084
3085 CursorMan.showMouse(false);
3086 copyRegionToBuffer(8, xOffset, 0, mouseWidth, mouseHeight, cursor);
3087 CursorMan.replaceCursor(cursor, mouseWidth, mouseHeight, x, y, _cursorColorKey);
3088 if (isMouseVisible())
3089 CursorMan.showMouse(true);
3090 delete[] cursor;
3091
3092 // makes sure that the cursor is drawn
3093 // we do not use Screen::updateScreen here
3094 // so we can be sure that changes to page 0
3095 // are NOT updated on the real screen here
3096 _system->updateScreen();
3097 }
3098
getPalette(int num)3099 Palette &Screen::getPalette(int num) {
3100 assert(num >= 0 && (uint)num < _palettes.size());
3101 return *_palettes[num];
3102 }
3103
copyPalette(const int dst,const int src)3104 void Screen::copyPalette(const int dst, const int src) {
3105 getPalette(dst).copy(getPalette(src));
3106 }
3107
getShapeFlag1(int x,int y)3108 byte Screen::getShapeFlag1(int x, int y) {
3109 uint8 color = _shapePages[0][y * SCREEN_W + x];
3110 color &= 0x80;
3111 color ^= 0x80;
3112
3113 if (color & 0x80)
3114 return 1;
3115 return 0;
3116 }
3117
getShapeFlag2(int x,int y)3118 byte Screen::getShapeFlag2(int x, int y) {
3119 uint8 color = _shapePages[0][y * SCREEN_W + x];
3120 color &= 0x7F;
3121 color &= 0x87;
3122 return color;
3123 }
3124
getDrawLayer(int x,int y)3125 int Screen::getDrawLayer(int x, int y) {
3126 int xpos = x - 8;
3127 int ypos = y - 1;
3128 int layer = 1;
3129
3130 for (int curX = xpos; curX < xpos + 16; ++curX) {
3131 int tempLayer = getShapeFlag2(curX, ypos);
3132
3133 if (layer < tempLayer)
3134 layer = tempLayer;
3135
3136 if (layer >= 7)
3137 return 7;
3138 }
3139 return layer;
3140 }
3141
getDrawLayer2(int x,int y,int height)3142 int Screen::getDrawLayer2(int x, int y, int height) {
3143 int xpos = x - 8;
3144 int ypos = y - 1;
3145 int layer = 1;
3146
3147 for (int useX = xpos; useX < xpos + 16; ++useX) {
3148 for (int useY = ypos - height; useY < ypos; ++useY) {
3149 int tempLayer = getShapeFlag2(useX, useY);
3150
3151 if (tempLayer > layer)
3152 layer = tempLayer;
3153
3154 if (tempLayer >= 7)
3155 return 7;
3156 }
3157 }
3158 return layer;
3159 }
3160
3161
setNewShapeHeight(uint8 * shape,int height)3162 int Screen::setNewShapeHeight(uint8 *shape, int height) {
3163 if (_vm->gameFlags().useAltShapeHeader)
3164 shape += 2;
3165
3166 int oldHeight = shape[2];
3167 shape[2] = height;
3168 return oldHeight;
3169 }
3170
resetShapeHeight(uint8 * shape)3171 int Screen::resetShapeHeight(uint8 *shape) {
3172 if (_vm->gameFlags().useAltShapeHeader)
3173 shape += 2;
3174
3175 int oldHeight = shape[2];
3176 shape[2] = shape[5];
3177 return oldHeight;
3178 }
3179
blockInRegion(int x,int y,int width,int height)3180 void Screen::blockInRegion(int x, int y, int width, int height) {
3181 assert(_shapePages[0]);
3182 byte *toPtr = _shapePages[0] + (y * 320 + x);
3183 for (int i = 0; i < height; ++i) {
3184 byte *backUpTo = toPtr;
3185 for (int i2 = 0; i2 < width; ++i2)
3186 *toPtr++ &= 0x7F;
3187 toPtr = (backUpTo + 320);
3188 }
3189 }
3190
blockOutRegion(int x,int y,int width,int height)3191 void Screen::blockOutRegion(int x, int y, int width, int height) {
3192 assert(_shapePages[0]);
3193 byte *toPtr = _shapePages[0] + (y * 320 + x);
3194 for (int i = 0; i < height; ++i) {
3195 byte *backUpTo = toPtr;
3196 for (int i2 = 0; i2 < width; ++i2)
3197 *toPtr++ |= 0x80;
3198 toPtr = (backUpTo + 320);
3199 }
3200 }
3201
rectClip(int & x,int & y,int w,int h)3202 void Screen::rectClip(int &x, int &y, int w, int h) {
3203 if (x < 0)
3204 x = 0;
3205 else if (x + w >= 320)
3206 x = 320 - w;
3207
3208 if (y < 0)
3209 y = 0;
3210 else if (y + h >= 200)
3211 y = 200 - h;
3212 }
3213
shakeScreen(int times)3214 void Screen::shakeScreen(int times) {
3215 static const int8 _shakeParaPC[] = { 32, 0, -4, 32, 0, 0 };
3216 static const int8 _shakeParaFMTOWNS[] = { 32, 0, -4, 48, 0, 4, 32, -4, 0, 32, 4, 0, 32, 0, 0 };
3217
3218 const int8 *shakeData = _shakeParaPC;
3219 int steps = ARRAYSIZE(_shakeParaPC) / 3;
3220
3221 // The FM-TOWNS version has a slightly better shake animation
3222 // TODO: check PC-98 version
3223 if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
3224 shakeData = _shakeParaFMTOWNS;
3225 steps = ARRAYSIZE(_shakeParaFMTOWNS) / 3;
3226 }
3227
3228 Common::Event event;
3229
3230 while (times--) {
3231 const int8 *data = shakeData;
3232 for (int i = 0; i < steps; ++i) {
3233 // The original PC version did not need an artificial delay, but we do or the shake will be
3234 // too fast to be actually seen.
3235 uint32 end = _system->getMillis() + data[0];
3236 _system->setShakePos(data[1], data[2]);
3237
3238 for (uint32 now = _system->getMillis(); now < end; ) {
3239 // Update the event manager to keep smooth mouse pointer movement.
3240 while (_vm->getEventManager()->pollEvent(event)) {
3241 if (event.type == Common::EVENT_KEYDOWN) {
3242 // This is really the only thing that should be handled.
3243 if (event.kbd.keycode == Common::KEYCODE_q && event.kbd.hasFlags(Common::KBD_CTRL))
3244 _vm->quitGame();
3245 }
3246 }
3247 _system->updateScreen();
3248 now = _system->getMillis();
3249 _system->delayMillis(MIN<uint>(end - now, 10));
3250 }
3251 data += 3;
3252 }
3253 }
3254 }
3255
loadBitmap(const char * filename,int tempPage,int dstPage,Palette * pal,bool skip)3256 void Screen::loadBitmap(const char *filename, int tempPage, int dstPage, Palette *pal, bool skip) {
3257 uint32 fileSize;
3258 uint8 *srcData = _vm->resource()->fileData(filename, &fileSize);
3259
3260 if (!srcData) {
3261 warning("couldn't load bitmap: '%s'", filename);
3262 return;
3263 }
3264
3265 if (skip)
3266 srcData += 4;
3267
3268 const char *ext = filename + strlen(filename) - 3;
3269 uint8 compType = srcData[2];
3270 uint32 imgSize = (_vm->game() == GI_KYRA2 && !scumm_stricmp(ext, "CMP")) ? READ_LE_UINT16(srcData) : READ_LE_UINT32(srcData + 4);
3271 uint16 palSize = READ_LE_UINT16(srcData + 8);
3272
3273 if (pal && palSize)
3274 loadPalette(srcData + 10, *pal, palSize);
3275
3276 uint8 *srcPtr = srcData + 10 + palSize;
3277 uint8 *dstData = getPagePtr(dstPage);
3278 memset(dstData, 0, _screenPageSize);
3279 if (dstPage == 0 || tempPage == 0)
3280 _forceFullUpdate = true;
3281
3282 switch (compType) {
3283 case 0:
3284 memcpy(dstData, srcPtr, imgSize);
3285 break;
3286 case 1:
3287 Screen::decodeFrame1(srcPtr, dstData, imgSize);
3288 break;
3289 case 3:
3290 Screen::decodeFrame3(srcPtr, dstData, imgSize, _isAmiga);
3291 break;
3292 case 4:
3293 Screen::decodeFrame4(srcPtr, dstData, imgSize);
3294 break;
3295 default:
3296 error("Unhandled bitmap compression %d", compType);
3297 }
3298
3299 if (skip)
3300 srcData -= 4;
3301
3302 delete[] srcData;
3303 }
3304
loadPalette(const char * filename,Palette & pal)3305 bool Screen::loadPalette(const char *filename, Palette &pal) {
3306 if (_renderMode == Common::kRenderCGA)
3307 return true;
3308
3309 Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
3310
3311 if (!stream)
3312 return false;
3313
3314 debugC(3, kDebugLevelScreen, "Screen::loadPalette('%s', %p)", filename, (const void *)&pal);
3315
3316 const int maxCols = _16bitPalette ? 256 : pal.getNumColors();
3317 int numCols = 0;
3318
3319 if (_isAmiga) {
3320 numCols = stream->size() / Palette::kAmigaBytesPerColor;
3321 pal.loadAmigaPalette(*stream, 0, MIN(maxCols, numCols));
3322 } else if (_vm->gameFlags().platform == Common::kPlatformPC98 && _use16ColorMode) {
3323 numCols = stream->size() / Palette::kPC98BytesPerColor;
3324 pal.loadPC98Palette(*stream, 0, MIN(maxCols, numCols));
3325 } else if (_renderMode == Common::kRenderEGA) {
3326 numCols = stream->size();
3327 // There aren't any 16 color EGA palette files. So this shouldn't ever get triggered.
3328 assert (numCols != 16);
3329 numCols /= Palette::kVGABytesPerColor;
3330 pal.loadVGAPalette(*stream, 0, numCols);
3331 } else {
3332 if (_bytesPerPixel == 2) {
3333 numCols = stream->size() / 2;
3334 pal.loadHiColorPalette(*stream, 0, numCols);
3335 } else if (!_16bitPalette) {
3336 numCols = stream->size() / Palette::kVGABytesPerColor;
3337 pal.loadVGAPalette(*stream, 0, MIN(maxCols, numCols));
3338 } else {
3339 error("Screen::loadPalette(): Failed to load file '%s' with invalid size %d in HiColor mode", filename, (int)stream->size());
3340 }
3341 }
3342
3343 if (numCols > maxCols)
3344 warning("Palette file '%s' includes %d colors, but the target palette only support %d colors", filename, numCols, maxCols);
3345
3346 delete stream;
3347 return true;
3348 }
3349
loadPaletteTable(const char * filename,int firstPalette)3350 bool Screen::loadPaletteTable(const char *filename, int firstPalette) {
3351 Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
3352
3353 if (!stream)
3354 return false;
3355
3356 debugC(3, kDebugLevelScreen, "Screen::loadPaletteTable('%s', %d)", filename, firstPalette);
3357
3358 if (_isAmiga) {
3359 const int numColors = getPalette(firstPalette).getNumColors();
3360 const int palSize = getPalette(firstPalette).getNumColors() * Palette::kAmigaBytesPerColor;
3361 const int numPals = stream->size() / palSize;
3362
3363 for (int i = 0; i < numPals; ++i)
3364 getPalette(i + firstPalette).loadAmigaPalette(*stream, 0, numColors);
3365 } else {
3366 const int numColors = getPalette(firstPalette).getNumColors();
3367 const int palSize = getPalette(firstPalette).getNumColors() * Palette::kVGABytesPerColor;
3368 const int numPals = stream->size() / palSize;
3369
3370 for (int i = 0; i < numPals; ++i)
3371 getPalette(i + firstPalette).loadVGAPalette(*stream, 0, numColors);
3372 }
3373
3374 delete stream;
3375 return true;
3376 }
3377
loadPalette(const byte * data,Palette & pal,int bytes)3378 void Screen::loadPalette(const byte *data, Palette &pal, int bytes) {
3379 Common::MemoryReadStream stream(data, bytes, DisposeAfterUse::NO);
3380
3381 if (_isAmiga) {
3382 // EOB II Amiga sometimes has multiple palettes here one after
3383 // the other (64 bytes each). We only load the first one here.
3384 pal.loadAmigaPalette(stream, 0, MIN<int>(32, stream.size() / Palette::kAmigaBytesPerColor));
3385 } else if (_vm->gameFlags().platform == Common::kPlatformPC98 && _use16ColorMode) {
3386 pal.loadPC98Palette(stream, 0, stream.size() / Palette::kPC98BytesPerColor);
3387 } else if (_renderMode == Common::kRenderEGA) {
3388 // EOB II checks the number of palette bytes to distinguish between real EGA palettes
3389 // and normal palettes (which are used to generate a color map).
3390 if (stream.size() == 16)
3391 pal.loadEGAPalette(stream, 0, stream.size());
3392 else
3393 pal.loadVGAPalette(stream, 0, stream.size() / Palette::kVGABytesPerColor);
3394 } else
3395 pal.loadVGAPalette(stream, 0, stream.size() / Palette::kVGABytesPerColor);
3396 }
3397
3398 // dirty rect handling
3399
addDirtyRect(int x,int y,int w,int h)3400 void Screen::addDirtyRect(int x, int y, int w, int h) {
3401 if (_dirtyRects.size() >= kMaxDirtyRects || _forceFullUpdate) {
3402 _forceFullUpdate = true;
3403 return;
3404 }
3405
3406 Common::Rect r(x, y, x + w, y + h);
3407
3408 // Clip rectangle
3409 r.clip(SCREEN_W, _screenHeight - _yTransOffs);
3410
3411 // If it is empty after clipping, we are done
3412 if (r.isEmpty())
3413 return;
3414
3415 // Check if the new rectangle is contained within another in the list
3416 Common::List<Common::Rect>::iterator it;
3417 for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ) {
3418 // If we find a rectangle which fully contains the new one,
3419 // we can abort the search.
3420 if (it->contains(r))
3421 return;
3422
3423 // Conversely, if we find rectangles which are contained in
3424 // the new one, we can remove them
3425 if (r.contains(*it))
3426 it = _dirtyRects.erase(it);
3427 else
3428 ++it;
3429 }
3430
3431 // If we got here, we can safely add r to the list of dirty rects.
3432 _dirtyRects.push_back(r);
3433 }
3434
3435 // overlay functions
3436
getOverlayPtr(int page)3437 byte *Screen::getOverlayPtr(int page) {
3438 if (page == 0 || page == 1)
3439 return _sjisOverlayPtrs[1];
3440 else if (page == 2 || page == 3)
3441 return _sjisOverlayPtrs[2];
3442
3443 if (_vm->game() == GI_KYRA2) {
3444 if (page == 12 || page == 13)
3445 return _sjisOverlayPtrs[3];
3446 } else if (_vm->game() == GI_LOL) {
3447 if (page == 4 || page == 5)
3448 return _sjisOverlayPtrs[3];
3449 if (page == 6 || page == 7)
3450 return _sjisOverlayPtrs[4];
3451 if (page == 12 || page == 13)
3452 return _sjisOverlayPtrs[5];
3453 }
3454
3455 return 0;
3456 }
3457
clearOverlayPage(int page)3458 void Screen::clearOverlayPage(int page) {
3459 byte *dst = getOverlayPtr(page);
3460 if (!dst)
3461 return;
3462 memset(dst, _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
3463 }
3464
clearOverlayRect(int page,int x,int y,int w,int h)3465 void Screen::clearOverlayRect(int page, int x, int y, int w, int h) {
3466 byte *dst = getOverlayPtr(page);
3467
3468 if (!dst || w < 0 || h < 0)
3469 return;
3470
3471 x <<= 1; y <<= 1;
3472 w <<= 1; h <<= 1;
3473
3474 dst += y * 640 + x;
3475
3476 if (w == 640 && h == 400) {
3477 memset(dst, _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
3478 } else {
3479 while (h--) {
3480 memset(dst, _sjisInvisibleColor, w);
3481 dst += 640;
3482 }
3483 }
3484 }
3485
copyOverlayRegion(int x,int y,int x2,int y2,int w,int h,int srcPage,int dstPage)3486 void Screen::copyOverlayRegion(int x, int y, int x2, int y2, int w, int h, int srcPage, int dstPage) {
3487 byte *dst = getOverlayPtr(dstPage);
3488 const byte *src = getOverlayPtr(srcPage);
3489
3490 if (!dst || !src)
3491 return;
3492
3493 x <<= 1; x2 <<= 1;
3494 y <<= 1; y2 <<= 1;
3495 w <<= 1; h <<= 1;
3496
3497 if (w == 640 && h == 400) {
3498 memcpy(dst, src, SCREEN_OVL_SJIS_SIZE);
3499 } else {
3500 dst += y2 * 640 + x2;
3501 src += y * 640 + x;
3502
3503 while (h--) {
3504 for (x = 0; x < w; ++x)
3505 memmove(dst, src, w);
3506 dst += 640;
3507 src += 640;
3508 }
3509 }
3510 }
3511
crossFadeRegion(int x1,int y1,int x2,int y2,int w,int h,int srcPage,int dstPage)3512 void Screen::crossFadeRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage) {
3513 if (srcPage > 13 || dstPage > 13)
3514 error("Screen::crossFadeRegion(): attempting to use temp page as source or dest page.");
3515
3516 hideMouse();
3517
3518 uint16 *wB = (uint16 *)_pagePtrs[14];
3519 uint8 *hB = _pagePtrs[14] + 640 * _bytesPerPixel;
3520
3521 for (int i = 0; i < w; i++)
3522 wB[i] = i;
3523
3524 for (int i = 0; i < h; i++)
3525 hB[i] = i;
3526
3527 for (int i = 0; i < w; i++)
3528 SWAP(wB[_vm->_rnd.getRandomNumberRng(0, w - 1)], wB[i]);
3529
3530 for (int i = 0; i < h; i++)
3531 SWAP(hB[_vm->_rnd.getRandomNumberRng(0, h - 1)], hB[i]);
3532
3533 uint8 *s = _pagePtrs[srcPage];
3534 uint8 *d = _pagePtrs[dstPage];
3535
3536 for (int i = 0; i < h; i++) {
3537 int iH = i;
3538 uint32 end = _system->getMillis() + 3;
3539 for (int ii = 0; ii < w; ii++) {
3540 int sX = (x1 + wB[ii]);
3541 int sY = (y1 + hB[iH]);
3542 int dX = (x2 + wB[ii]);
3543 int dY = (y2 + hB[iH]);
3544
3545 if (++iH >= h)
3546 iH = 0;
3547
3548 if (_bytesPerPixel == 2)
3549 ((uint16*)d)[dY * 320 + dX] = ((uint16*)s)[sY * 320 + sX];
3550 else
3551 d[dY * 320 + dX] = s[sY * 320 + sX];
3552 addDirtyRect(dX, dY, 1, 1);
3553 }
3554
3555 // This tries to speed things up, to get similiar speeds as in DOSBox etc.
3556 // We can't write single pixels directly into the video memory like the original did.
3557 // We also (unlike the original) want to aim at similiar speeds for all platforms.
3558 if (!(i % 10))
3559 updateScreen();
3560
3561 uint32 cur = _system->getMillis();
3562 if (end > cur)
3563 _system->delayMillis(end - cur);
3564 }
3565
3566 updateScreen();
3567 showMouse();
3568 }
3569
3570 #pragma mark -
3571
DOSFont()3572 DOSFont::DOSFont() {
3573 _data = _widthTable = _heightTable = 0;
3574 _colorMap = 0;
3575 _width = _height = _numGlyphs = 0;
3576 _bitmapOffsets = 0;
3577 }
3578
load(Common::SeekableReadStream & file)3579 bool DOSFont::load(Common::SeekableReadStream &file) {
3580 unload();
3581
3582 _data = new uint8[file.size()];
3583 assert(_data);
3584
3585 file.read(_data, file.size());
3586 if (file.err())
3587 return false;
3588
3589 const uint16 fontSig = READ_LE_UINT16(_data + 2);
3590
3591 if (fontSig != 0x0500) {
3592 warning("DOSFont: invalid font: %.04X)", fontSig);
3593 return false;
3594 }
3595
3596 const uint16 descOffset = READ_LE_UINT16(_data + 4);
3597
3598 _width = _data[descOffset + 5];
3599 _height = _data[descOffset + 4];
3600 _numGlyphs = _data[descOffset + 3] + 1;
3601
3602 _bitmapOffsets = (uint16 *)(_data + READ_LE_UINT16(_data + 6));
3603 _widthTable = _data + READ_LE_UINT16(_data + 8);
3604 _heightTable = _data + READ_LE_UINT16(_data + 12);
3605
3606 for (int i = 0; i < _numGlyphs; ++i)
3607 _bitmapOffsets[i] = READ_LE_UINT16(&_bitmapOffsets[i]);
3608
3609 return true;
3610 }
3611
getCharWidth(uint16 c) const3612 int DOSFont::getCharWidth(uint16 c) const {
3613 if (c >= _numGlyphs)
3614 return 0;
3615 return _widthTable[c];
3616 }
3617
drawChar(uint16 c,byte * dst,int pitch,int) const3618 void DOSFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
3619 if (c >= _numGlyphs)
3620 return;
3621
3622 if (!_bitmapOffsets[c])
3623 return;
3624
3625 const uint8 *src = _data + _bitmapOffsets[c];
3626 const uint8 charWidth = _widthTable[c];
3627
3628 if (!charWidth)
3629 return;
3630
3631 pitch -= charWidth;
3632
3633 uint8 charH1 = _heightTable[c * 2 + 0];
3634 uint8 charH2 = _heightTable[c * 2 + 1];
3635 uint8 charH0 = _height - (charH1 + charH2);
3636
3637 while (charH1--) {
3638 uint8 col = _colorMap[0];
3639 for (int i = 0; i < charWidth; ++i) {
3640 if (col != 0)
3641 *dst = col;
3642 ++dst;
3643 }
3644 dst += pitch;
3645 }
3646
3647 while (charH2--) {
3648 uint8 b = 0;
3649 for (int i = 0; i < charWidth; ++i) {
3650 uint8 col;
3651 if (i & 1) {
3652 col = _colorMap[b >> 4];
3653 } else {
3654 b = *src++;
3655 col = _colorMap[b & 0xF];
3656 }
3657 if (col != 0) {
3658 *dst = col;
3659 }
3660 ++dst;
3661 }
3662 dst += pitch;
3663 }
3664
3665 while (charH0--) {
3666 uint8 col = _colorMap[0];
3667 for (int i = 0; i < charWidth; ++i) {
3668 if (col != 0)
3669 *dst = col;
3670 ++dst;
3671 }
3672 dst += pitch;
3673 }
3674 }
3675
unload()3676 void DOSFont::unload() {
3677 delete[] _data;
3678 _data = _widthTable = _heightTable = 0;
3679 _colorMap = 0;
3680 _width = _height = _numGlyphs = 0;
3681 _bitmapOffsets = 0;
3682 }
3683
3684
AMIGAFont()3685 AMIGAFont::AMIGAFont() {
3686 _width = _height = 0;
3687 memset(_chars, 0, sizeof(_chars));
3688 }
3689
load(Common::SeekableReadStream & file)3690 bool AMIGAFont::load(Common::SeekableReadStream &file) {
3691 const uint16 dataSize = file.readUint16BE();
3692 if (dataSize + 2 != file.size())
3693 return false;
3694
3695 _width = file.readByte();
3696 _height = file.readByte();
3697
3698 // Read the character definition offset table
3699 uint16 offsets[ARRAYSIZE(_chars)];
3700 for (int i = 0; i < ARRAYSIZE(_chars); ++i)
3701 offsets[i] = file.readUint16BE() + 4;
3702
3703 if (file.err())
3704 return false;
3705
3706 for (int i = 0; i < ARRAYSIZE(_chars); ++i) {
3707 file.seek(offsets[i], SEEK_SET);
3708
3709 _chars[i].yOffset = file.readByte();
3710 _chars[i].xOffset = file.readByte();
3711 _chars[i].width = file.readByte();
3712 file.readByte(); // unused
3713
3714 // If the y offset is 255, then the character
3715 // does not have any bitmap representation
3716 if (_chars[i].yOffset != 255) {
3717 Character::Graphics &g = _chars[i].graphics;
3718
3719 g.width = file.readUint16BE();
3720 g.height = file.readUint16BE();
3721
3722 int depth = file.readByte();
3723 int specialWidth = file.readByte();
3724 int flags = file.readByte();
3725 int bytesPerPlane = file.readByte();
3726
3727 assert(depth != 0 && specialWidth == 0 && flags == 0 && bytesPerPlane != 0);
3728
3729 // Allocate a temporary buffer to store the plane data
3730 const int planesSize = bytesPerPlane * g.height * depth;
3731 uint8 *tempData = new uint8[MAX(g.width * g.height, planesSize)];
3732 assert(tempData);
3733
3734 file.read(tempData, planesSize);
3735
3736 // Convert the plane based graphics to our graphic format
3737 Screen::convertAmigaGfx(tempData, g.width, g.height, depth, false, bytesPerPlane);
3738
3739 // Create a buffer perfectly fitting the character
3740 g.bitmap = new uint8[g.width * g.height];
3741 assert(g.bitmap);
3742
3743 memcpy(g.bitmap, tempData, g.width * g.height);
3744 delete[] tempData;
3745 }
3746
3747 if (file.err())
3748 return false;
3749 }
3750
3751 return !file.err();
3752 }
3753
getCharWidth(uint16 c) const3754 int AMIGAFont::getCharWidth(uint16 c) const {
3755 if (c >= 255)
3756 return 0;
3757 return _chars[c].width;
3758 }
3759
drawChar(uint16 c,byte * dst,int pitch,int) const3760 void AMIGAFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
3761 if (c >= 255)
3762 return;
3763
3764 if (_chars[c].yOffset == 255)
3765 return;
3766
3767 dst += _chars[c].yOffset * pitch;
3768 dst += _chars[c].xOffset;
3769
3770 pitch -= _chars[c].graphics.width;
3771
3772 const uint8 *src = _chars[c].graphics.bitmap;
3773 assert(src);
3774
3775 for (int y = 0; y < _chars[c].graphics.height; ++y) {
3776 for (int x = 0; x < _chars[c].graphics.width; ++x) {
3777 if (*src)
3778 *dst = *src;
3779 ++src;
3780 ++dst;
3781 }
3782
3783 dst += pitch;
3784 }
3785 }
3786
unload()3787 void AMIGAFont::unload() {
3788 _width = _height = 0;
3789 for (int i = 0; i < ARRAYSIZE(_chars); ++i)
3790 delete[] _chars[i].graphics.bitmap;
3791 memset(_chars, 0, sizeof(_chars));
3792 }
3793
SJISFont(Common::SharedPtr<Graphics::FontSJIS> & font,const uint8 invisColor,bool is16Color,bool drawOutline,int extraSpacing)3794 SJISFont::SJISFont(Common::SharedPtr<Graphics::FontSJIS> &font, const uint8 invisColor, bool is16Color, bool drawOutline, int extraSpacing)
3795 : _colorMap(0), _font(font), _invisColor(invisColor), _isTextMode(is16Color), _style(kStyleNone), _drawOutline(drawOutline), _sjisWidthOffset(extraSpacing) {
3796 assert(_font);
3797 }
3798
getHeight() const3799 int SJISFont::getHeight() const {
3800 return _font->getFontHeight() >> 1;
3801 }
3802
getWidth() const3803 int SJISFont::getWidth() const {
3804 return (_font->getMaxFontWidth() >> 1) + _sjisWidthOffset;
3805 }
3806
getCharWidth(uint16 c) const3807 int SJISFont::getCharWidth(uint16 c) const {
3808 if (c <= 0x7F || (c >= 0xA1 && c <= 0xDF))
3809 return _font->getCharWidth('a') >> 1;
3810 else
3811 return getWidth();
3812 }
3813
setColorMap(const uint8 * src)3814 void SJISFont::setColorMap(const uint8 *src) {
3815 _colorMap = src;
3816 }
3817
drawChar(uint16 c,byte * dst,int pitch,int) const3818 void SJISFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
3819 uint8 color1, color2;
3820
3821 if (_isTextMode) {
3822 // PC98 16 color games specify a color value which is for the
3823 // PC98 text mode palette, thus we need to remap it.
3824 color1 = ((_colorMap[1] >> 5) & 0x7) + 16;
3825 color2 = ((_colorMap[0] >> 5) & 0x7) + 16;
3826 } else {
3827 color1 = _colorMap[1];
3828 color2 = _colorMap[0];
3829 }
3830
3831 if (!_isTextMode && _colorMap[0] == _invisColor)
3832 _font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
3833 else
3834 _font->setDrawingMode(_drawOutline ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode);
3835
3836 _font->toggleFatPrint(_style == kStyleFat);
3837 _font->drawChar(dst, c, 640, 1, color1, color2, 640, 400);
3838 }
3839
3840 #pragma mark -
3841
Palette(const int numColors)3842 Palette::Palette(const int numColors) : _palData(0), _numColors(numColors) {
3843 _palData = new uint8[numColors * 3];
3844 assert(_palData);
3845
3846 memset(_palData, 0, numColors * 3);
3847 }
3848
~Palette()3849 Palette::~Palette() {
3850 delete[] _palData;
3851 _palData = 0;
3852 }
3853
loadVGAPalette(Common::ReadStream & stream,int startIndex,int colors)3854 void Palette::loadVGAPalette(Common::ReadStream &stream, int startIndex, int colors) {
3855 assert(startIndex + colors <= _numColors);
3856
3857 uint8 *pos = _palData + startIndex * 3;
3858 for (int i = 0 ; i < colors * 3; i++)
3859 *pos++ = stream.readByte() & 0x3F;
3860 }
3861
loadHiColorPalette(Common::ReadStream & stream,int startIndex,int colors)3862 void Palette::loadHiColorPalette(Common::ReadStream &stream, int startIndex, int colors) {
3863 uint16 *pos = (uint16*)(_palData + startIndex * 2);
3864
3865 Graphics::PixelFormat currentFormat = g_system->getScreenFormat();
3866 Graphics::PixelFormat originalFormat(2, 5, 5, 5, 0, 5, 10, 0, 0);
3867
3868 for (int i = 0; i < colors; i++) {
3869 uint8 r, g, b;
3870 originalFormat.colorToRGB(stream.readUint16LE(), r, g, b);
3871 *pos++ = currentFormat.RGBToColor(r, g, b);
3872 }
3873 }
3874
loadEGAPalette(Common::ReadStream & stream,int startIndex,int colors)3875 void Palette::loadEGAPalette(Common::ReadStream &stream, int startIndex, int colors) {
3876 assert(startIndex + colors <= 16);
3877
3878 uint8 *dst = _palData + startIndex * 3;
3879 for (int i = 0; i < colors; i++) {
3880 uint8 index = stream.readByte();
3881 assert(index < _egaNumColors);
3882 memcpy(dst, &_egaColors[index * 3], 3);
3883 dst += 3;
3884 }
3885 }
3886
setCGAPalette(int palIndex,CGAIntensity intensity)3887 void Palette::setCGAPalette(int palIndex, CGAIntensity intensity) {
3888 assert(_numColors >= _cgaNumColors);
3889 assert(!(palIndex & ~1));
3890 memcpy(_palData, _cgaColors[palIndex * 2 + intensity], _cgaNumColors * 3);
3891 }
3892
loadAmigaPalette(Common::ReadStream & stream,int startIndex,int colors)3893 void Palette::loadAmigaPalette(Common::ReadStream &stream, int startIndex, int colors) {
3894 assert(startIndex + colors <= _numColors);
3895
3896 for (int i = 0; i < colors; ++i) {
3897 uint16 col = stream.readUint16BE();
3898 _palData[(i + startIndex) * 3 + 2] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
3899 _palData[(i + startIndex) * 3 + 1] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
3900 _palData[(i + startIndex) * 3 + 0] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
3901 }
3902 }
3903
loadPC98Palette(Common::ReadStream & stream,int startIndex,int colors)3904 void Palette::loadPC98Palette(Common::ReadStream &stream, int startIndex, int colors) {
3905 assert(startIndex + colors <= _numColors);
3906
3907 for (int i = 0; i < colors; ++i) {
3908 const byte g = stream.readByte(), r = stream.readByte(), b = stream.readByte();
3909
3910 _palData[(i + startIndex) * 3 + 0] = ((r & 0xF) * 0x3F) / 0xF;
3911 _palData[(i + startIndex) * 3 + 1] = ((g & 0xF) * 0x3F) / 0xF;
3912 _palData[(i + startIndex) * 3 + 2] = ((b & 0xF) * 0x3F) / 0xF;
3913 }
3914 }
3915
clear()3916 void Palette::clear() {
3917 memset(_palData, 0, _numColors * 3);
3918 }
3919
fill(int firstCol,int numCols,uint8 value)3920 void Palette::fill(int firstCol, int numCols, uint8 value) {
3921 assert(firstCol >= 0 && firstCol + numCols <= _numColors);
3922
3923 memset(_palData + firstCol * 3, CLIP<int>(value, 0, 63), numCols * 3);
3924 }
3925
copy(const Palette & source,int firstCol,int numCols,int dstStart)3926 void Palette::copy(const Palette &source, int firstCol, int numCols, int dstStart) {
3927 if (numCols == -1)
3928 numCols = MIN(source.getNumColors(), _numColors) - firstCol;
3929 if (dstStart == -1)
3930 dstStart = firstCol;
3931
3932 assert(numCols >= 0 && numCols <= _numColors);
3933 assert(firstCol >= 0 && firstCol <= source.getNumColors());
3934 assert(dstStart >= 0 && dstStart + numCols <= _numColors);
3935
3936 memmove(_palData + dstStart * 3, source._palData + firstCol * 3, numCols * 3);
3937 }
3938
copy(const uint8 * source,int firstCol,int numCols,int dstStart)3939 void Palette::copy(const uint8 *source, int firstCol, int numCols, int dstStart) {
3940 if (dstStart == -1)
3941 dstStart = firstCol;
3942
3943 assert(numCols >= 0 && numCols <= _numColors);
3944 assert(firstCol >= 0);
3945 assert(dstStart >= 0 && dstStart + numCols <= _numColors);
3946
3947 memmove(_palData + dstStart * 3, source + firstCol * 3, numCols * 3);
3948 }
3949
fetchRealPalette() const3950 uint8 *Palette::fetchRealPalette() const {
3951 uint8 *buffer = new uint8[_numColors * 3];
3952 assert(buffer);
3953
3954 uint8 *dst = buffer;
3955 const uint8 *palData = _palData;
3956
3957 for (int i = 0; i < _numColors; ++i) {
3958 dst[0] = (palData[0] << 2) | (palData[0] & 3);
3959 dst[1] = (palData[1] << 2) | (palData[1] & 3);
3960 dst[2] = (palData[2] << 2) | (palData[2] & 3);
3961
3962 dst += 3;
3963 palData += 3;
3964 }
3965
3966 return buffer;
3967 }
3968
3969 const uint8 Palette::_egaColors[] = {
3970 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
3971 0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
3972 0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
3973 0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
3974 };
3975
3976 const int Palette::_egaNumColors = ARRAYSIZE(_egaColors) / 3;
3977
3978 const uint8 Palette::_cgaColors[4][12] = {
3979 { 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x2A, 0x15, 0x00 },
3980 { 0x00, 0x00, 0x00, 0x15, 0x3F, 0x15, 0x3F, 0x15, 0x15, 0x3F, 0x3F, 0x15 },
3981 { 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x00, 0x2A, 0x2A, 0x2A, 0x2A },
3982 { 0x00, 0x00, 0x00, 0x15, 0x3F, 0x3F, 0x3F, 0x15, 0x3F, 0x3F, 0x3F, 0x3F }
3983 };
3984
3985 const int Palette::_cgaNumColors = ARRAYSIZE(_cgaColors[0]) / 3;
3986
3987 } // End of namespace Kyra
3988