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 "common/file.h"
24 #include "common/system.h"
25 #include "engines/util.h"
26 #include "graphics/cursorman.h"
27 #include "graphics/pixelformat.h"
28 #include "graphics/surface.h"
29
30 #include "trecision/actor.h"
31 #include "trecision/anim.h"
32 #include "trecision/defines.h"
33 #include "trecision/graphics.h"
34 #include "trecision/pathfinding3d.h"
35 #include "trecision/renderer3d.h"
36 #include "trecision/text.h"
37 #include "trecision/trecision.h"
38 #include "trecision/video.h"
39
40 namespace Trecision {
41
GraphicsManager(TrecisionEngine * vm)42 GraphicsManager::GraphicsManager(TrecisionEngine *vm) : _vm(vm), _rgb555Format(2, 5, 5, 5, 0, 10, 5, 0, 0) {
43 for (int i = 0; i < 3; ++i)
44 _bitMask[i] = 0;
45
46 for (int i = 0; i < 256; ++i) {
47 _fonts[i]._width = 0;
48 _fonts[i]._data = nullptr;
49 }
50 }
51
~GraphicsManager()52 GraphicsManager::~GraphicsManager() {
53 _screenBuffer.free();
54 _background.free();
55 _smkBackground.free();
56 _leftInventoryArrow.free();
57 _rightInventoryArrow.free();
58 _inventoryIcons.free();
59 _saveSlotThumbnails.free();
60 _textureMat.free();
61
62 for (int i = 0; i < 256; ++i)
63 delete[] _fonts[i]._data;
64 }
65
init()66 bool GraphicsManager::init() {
67 // Find a suitable 16-bit format, currently we don't support other color depths
68 Common::List<Graphics::PixelFormat> formats = g_system->getSupportedFormats();
69 for (Common::List<Graphics::PixelFormat>::iterator it = formats.begin(); it != formats.end(); ++it) {
70 if (it->bytesPerPixel != 2 || it->aBits()) {
71 it = formats.reverse_erase(it);
72 } else if (*it == _rgb555Format) {
73 formats.clear();
74 formats.push_back(_rgb555Format);
75 break;
76 }
77 }
78
79 if (formats.empty())
80 return false;
81
82 initGraphics(MAXX, MAXY, formats);
83
84 _screenFormat = g_system->getScreenFormat();
85 if (_screenFormat.bytesPerPixel != 2)
86 return false;
87 _bitMask[0] = _screenFormat.rMax() << _screenFormat.rShift;
88 _bitMask[1] = _screenFormat.gMax() << _screenFormat.gShift;
89 _bitMask[2] = _screenFormat.bMax() << _screenFormat.bShift;
90
91 clearScreen();
92
93 _screenBuffer.create(MAXX, MAXY, _screenFormat);
94 _background.create(MAXX, MAXY, _screenFormat);
95 _smkBackground.create(MAXX, AREA, _screenFormat);
96 _saveSlotThumbnails.create(READICON * ICONDX, ICONDY, _screenFormat);
97
98 loadData();
99 initCursor();
100 hideCursor();
101
102 return true;
103 }
104
addDirtyRect(Common::Rect rect,bool translateRect)105 void GraphicsManager::addDirtyRect(Common::Rect rect, bool translateRect) {
106 if (translateRect)
107 rect.translate(0, TOP);
108
109 _dirtyRects.push_back(rect);
110 }
111
drawObj(int index,bool mask,Common::Rect drawRect,Common::Rect drawObjRect,bool includeDirtyRect)112 void GraphicsManager::drawObj(int index, bool mask, Common::Rect drawRect, Common::Rect drawObjRect, bool includeDirtyRect) {
113 if (drawObjRect.left > MAXX || drawObjRect.top > MAXY)
114 return;
115
116 // If we have a valid object, draw it, otherwise erase it
117 // by using the background buffer
118 const uint16 *buf = index >= 0 ? _vm->_objectGraphics[index].buf : (uint16 *)_smkBackground.getPixels();
119 if (mask && index >= 0) {
120 uint8 *maskPtr = _vm->_objectGraphics[index].mask;
121
122 for (uint16 y = drawRect.top; y < drawRect.bottom; ++y) {
123 uint16 x = 0;
124 bool copyBytes = false;
125 while (x < drawRect.width()) {
126 if (!copyBytes) { // jump
127 x += *maskPtr;
128 ++maskPtr;
129
130 copyBytes = true;
131 } else { // copy
132 const uint16 maskOffset = *maskPtr;
133
134 if (maskOffset != 0 && y >= drawRect.top + drawObjRect.top && y < drawRect.top + drawObjRect.bottom) {
135 const void *src = (x >= drawObjRect.left) ? buf : buf + drawObjRect.left - x;
136 int offset = (x >= drawObjRect.left) ? x : drawObjRect.left;
137 void *dst = _screenBuffer.getBasePtr(offset + drawRect.left, y);
138
139 if (x >= drawObjRect.left && x + maskOffset < drawObjRect.right)
140 memcpy(dst, src, maskOffset * 2);
141 else if (x < drawObjRect.left && x + maskOffset < drawObjRect.right && x + maskOffset >= drawObjRect.left)
142 memcpy(dst, src, (maskOffset + x - drawObjRect.left) * 2);
143 else if (x >= drawObjRect.left && x + maskOffset >= drawObjRect.right && x < drawObjRect.right)
144 memcpy(dst, src, (drawObjRect.right - x) * 2);
145 else if (x < drawObjRect.left && x + maskOffset >= drawObjRect.right)
146 memcpy(dst, src, (drawObjRect.right - drawObjRect.left) * 2);
147 }
148 x += *maskPtr;
149 buf += *maskPtr++;
150 copyBytes = false;
151 }
152 }
153 }
154 } else {
155 const uint16 x = drawRect.left + drawObjRect.left;
156
157 if (x + drawObjRect.width() > MAXX || drawObjRect.top + drawObjRect.height() > MAXY) {
158 warning("drawObj: Invalid surface, skipping");
159 return;
160 }
161
162 for (uint16 y = drawObjRect.top; y < drawObjRect.bottom; ++y) {
163 memcpy(_screenBuffer.getBasePtr(x, drawRect.top + y),
164 buf + (y * drawRect.width()) + drawObjRect.left, drawObjRect.width() * 2);
165 }
166 }
167
168 if (includeDirtyRect)
169 addDirtyRect(drawObjRect, true);
170 }
171
eraseObj(Common::Rect drawObjRect)172 void GraphicsManager::eraseObj(Common::Rect drawObjRect) {
173 Common::Rect eraseRect = drawObjRect;
174 eraseRect.translate(0, TOP);
175 if (eraseRect.isValidRect())
176 _screenBuffer.fillRect(eraseRect, 0);
177 }
178
clearScreen()179 void GraphicsManager::clearScreen() {
180 g_system->fillScreen(0);
181 }
182
copyToScreenBuffer(const Graphics::Surface * surface,int x,int y,const byte * palette)183 void GraphicsManager::copyToScreenBuffer(const Graphics::Surface *surface, int x, int y, const byte *palette) {
184 Graphics::Surface *surface16 = surface->convertTo(_screenFormat, palette);
185
186 copyToScreenBufferInner(surface16, x, y);
187
188 surface16->free();
189 delete surface16;
190 }
191
copyToScreenBufferInner(const Graphics::Surface * surface,int x,int y)192 void GraphicsManager::copyToScreenBufferInner(const Graphics::Surface *surface, int x, int y) {
193 if (x + surface->w > MAXX || y + surface->h > MAXY) {
194 warning("copyToScreenBufferInner: Invalid surface, skipping");
195 return;
196 }
197
198 for (int curY = 0; curY < surface->h; ++curY) {
199 // NOTE: We use surface width for the pitch so that memcpy works
200 // correctly with surfaces from getSubArea()
201 memcpy(_screenBuffer.getBasePtr(x, y + curY), surface->getBasePtr(0, curY), surface->w * 2);
202 }
203 }
204
blitToScreenBuffer(const Graphics::Surface * surface,int x,int y,const byte * palette,bool useSmkBg)205 void GraphicsManager::blitToScreenBuffer(const Graphics::Surface *surface, int x, int y, const byte *palette, bool useSmkBg) {
206 if (x + surface->w > MAXX || y + surface->h > MAXY) {
207 warning("blitToScreenBuffer: Invalid surface, skipping");
208 return;
209 }
210
211 const uint16 mask = (uint16)_screenFormat.RGBToColor(palette[0], palette[1], palette[2]);
212 Graphics::Surface *surface16 = surface->convertTo(_screenFormat, palette);
213
214 for (int curY = 0; curY < surface16->h; ++curY) {
215 for (int curX = 0; curX < surface16->w; ++curX) {
216 const int destX = x + curX;
217 const int destY = y + curY;
218 const uint16 pixel = (uint16)surface16->getPixel(curX, curY);
219 if (pixel != mask) {
220 _screenBuffer.setPixel(destX, destY, pixel);
221 if (useSmkBg)
222 _smkBackground.setPixel(destX, destY - TOP, pixel);
223 } else if (useSmkBg) {
224 const uint16 bgPixel = _background.getPixel(destX, destY - TOP);
225 _screenBuffer.setPixel(destX, destY, bgPixel);
226 _smkBackground.setPixel(destX, destY - TOP, bgPixel);
227 }
228 }
229 }
230
231 surface16->free();
232 delete surface16;
233 }
234
copyToScreen(int x,int y,int w,int h)235 void GraphicsManager::copyToScreen(int x, int y, int w, int h) {
236 g_system->copyRectToScreen(
237 _screenBuffer.getBasePtr(x, y),
238 MAXX * 2, x, y, w, h
239 );
240 }
241
readSurface(Common::SeekableReadStream * stream,Graphics::Surface * surface,uint16 width,uint16 height,uint16 count)242 void GraphicsManager::readSurface(Common::SeekableReadStream *stream, Graphics::Surface *surface, uint16 width, uint16 height, uint16 count) {
243 surface->create(width * count, height, _rgb555Format);
244
245 for (uint16 i = 0; i < count; ++i) {
246 for (uint16 y = 0; y < height; ++y) {
247 for (uint16 x = 0; x < width; ++x) {
248 surface->setPixel(width * i + x, y, stream->readUint16LE());
249 }
250 }
251 }
252
253 surface->convertToInPlace(_screenFormat);
254 }
255
readTexture(Common::SeekableReadStream * stream)256 void GraphicsManager::readTexture(Common::SeekableReadStream *stream) {
257 readSurface(stream, &_textureMat, 91, 256);
258 }
259
drawTexturePixel(uint16 textureX,uint16 textureY,uint16 screenX,uint16 screenY)260 void GraphicsManager::drawTexturePixel(uint16 textureX, uint16 textureY, uint16 screenX, uint16 screenY) {
261 const uint16 texturePixel = (uint16)_textureMat.getPixel(textureX, textureY);
262 _screenBuffer.setPixel(screenX, screenY, texturePixel);
263 }
264
loadBackground(Common::SeekableReadStream * stream,uint16 width,uint16 height)265 void GraphicsManager::loadBackground(Common::SeekableReadStream *stream, uint16 width, uint16 height) {
266 readSurface(stream, &_background, width, height);
267 _smkBackground.copyFrom(_background);
268 memcpy(_screenBuffer.getBasePtr(0, TOP), _background.getPixels(), _background.pitch * _background.h);
269 }
270
loadData()271 void GraphicsManager::loadData() {
272 Common::SeekableReadStream *arrowsDataFile = _vm->_dataFile.createReadStreamForMember("frecc.bm");
273 // The data file contains images for deactivated arrows, which aren't used. Skip them.
274 arrowsDataFile->skip(ICONMARGDX * ICONDY * 2 * 3);
275 readSurface(arrowsDataFile, &_leftInventoryArrow, ICONMARGSX, ICONDY);
276 readSurface(arrowsDataFile, &_rightInventoryArrow, ICONMARGSX, ICONDY);
277 delete arrowsDataFile;
278
279 Common::SeekableReadStream *iconsDataFile = _vm->_dataFile.createReadStreamForMember("icone.bm");
280 readSurface(iconsDataFile, &_inventoryIcons, ICONDX, ICONDY, READICON);
281 delete iconsDataFile;
282
283 loadFont();
284 }
285
setSaveSlotThumbnail(byte iconSlot,const Graphics::Surface * thumbnail)286 void GraphicsManager::setSaveSlotThumbnail(byte iconSlot, const Graphics::Surface *thumbnail) {
287 Graphics::Surface *scaled = thumbnail->scale(ICONDX, ICONDY);
288 scaled->convertToInPlace(_screenFormat);
289
290 for (uint16 y = 0; y < ICONDY; ++y) {
291 memcpy(_saveSlotThumbnails.getBasePtr(ICONDX * iconSlot, y), scaled->getBasePtr(0, y), ICONDX * 2);
292 }
293
294 scaled->free();
295 delete scaled;
296 }
297
drawLeftInventoryArrow(byte startLine)298 void GraphicsManager::drawLeftInventoryArrow(byte startLine) {
299 Graphics::Surface arrow = _leftInventoryArrow.getSubArea(Common::Rect(
300 0, startLine, _leftInventoryArrow.w, _leftInventoryArrow.h
301 ));
302 copyToScreenBufferInner(&arrow, 0, FIRSTLINE);
303 }
304
drawRightInventoryArrow(byte startLine)305 void GraphicsManager::drawRightInventoryArrow(byte startLine) {
306 Graphics::Surface arrow = _rightInventoryArrow.getSubArea(Common::Rect(
307 0, startLine, _rightInventoryArrow.w, _rightInventoryArrow.h
308 ));
309 copyToScreenBufferInner(&arrow, MAXX - ICONMARGDX, FIRSTLINE);
310 }
311
drawInventoryIcon(byte iconIndex,byte iconSlot,byte startLine)312 void GraphicsManager::drawInventoryIcon(byte iconIndex, byte iconSlot, byte startLine) {
313 Graphics::Surface icon = _inventoryIcons.getSubArea(Common::Rect(
314 iconIndex * ICONDX,
315 startLine,
316 iconIndex * ICONDX + ICONDX,
317 _inventoryIcons.h
318 ));
319 copyToScreenBufferInner(&icon, iconSlot * ICONDX + ICONMARGSX, FIRSTLINE);
320 }
321
drawSaveSlotThumbnail(byte iconIndex,byte iconSlot,byte startLine)322 void GraphicsManager::drawSaveSlotThumbnail(byte iconIndex, byte iconSlot, byte startLine) {
323 Graphics::Surface icon = _saveSlotThumbnails.getSubArea(Common::Rect(
324 iconIndex * ICONDX,
325 startLine,
326 iconIndex * ICONDX + ICONDX,
327 _saveSlotThumbnails.h
328 ));
329 copyToScreenBufferInner(&icon, iconSlot * ICONDX + ICONMARGSX, FIRSTLINE);
330 }
331
clearScreenBuffer()332 void GraphicsManager::clearScreenBuffer() {
333 _screenBuffer.fillRect(Common::Rect(0, 0, MAXX, MAXY), 0);
334 }
335
clearScreenBufferTop()336 void GraphicsManager::clearScreenBufferTop() {
337 // Clears lines 0 - 60
338 _screenBuffer.fillRect(Common::Rect(0, 0, MAXX, TOP), 0);
339 }
340
clearScreenBufferInventory()341 void GraphicsManager::clearScreenBufferInventory() {
342 // Clears lines 420 - 480
343 _screenBuffer.fillRect(Common::Rect(0, FIRSTLINE, MAXX, MAXY), 0);
344 }
345
clearScreenBufferSaveSlotDescriptions()346 void GraphicsManager::clearScreenBufferSaveSlotDescriptions() {
347 // Clears lines 470 - 480
348 _screenBuffer.fillRect(Common::Rect(0, FIRSTLINE + ICONDY + 10, MAXX, MAXY), 0);
349 }
350
convertToScreenFormat(uint16 color) const351 uint16 GraphicsManager::convertToScreenFormat(uint16 color) const {
352 uint8 r, g, b;
353 _rgb555Format.colorToRGB(color, r, g, b);
354 return (uint16)_screenFormat.RGBToColor(r, g, b);
355 }
356
357 /**
358 * Shadow Pixel
359 * (dark) 0..8 (light)
360 */
shadow(uint16 x,uint16 y,uint8 num)361 void GraphicsManager::shadow(uint16 x, uint16 y, uint8 num) {
362 if (x > MAXX || y > MAXY) {
363 warning("shadow: Invalid pixel, skipping");
364 return;
365 }
366
367 const uint16 val = (uint16)_screenBuffer.getPixel(x, y);
368 const uint16 shadowVal =
369 ((((val & _bitMask[2]) * num >> 7) & _bitMask[2]) |
370 (((val & _bitMask[1]) * num >> 7) & _bitMask[1]) |
371 (((val & _bitMask[0]) * num >> 7) & _bitMask[0]));
372 _screenBuffer.setPixel(x, y, shadowVal);
373 }
374
pixelAliasing(uint16 x,uint16 y)375 void GraphicsManager::pixelAliasing(uint16 x, uint16 y) {
376 if (x > MAXX || y > MAXY) {
377 warning("pixelAliasing: Invalid pixel, skipping");
378 return;
379 }
380
381 int px1 = _screenBuffer.getPixel(x - 1, y);
382 int px2 = _screenBuffer.getPixel(x, y);
383
384 _screenBuffer.setPixel(x - 1, y, aliasing(px1, px2, 6)); // 75% 25%
385 _screenBuffer.setPixel(x, y, aliasing(px1, px2, 2)); // 25% 75%
386 }
387
388 /**
389 * Aliasing Pixel
390 */
aliasing(uint32 val1,uint32 val2,uint8 num)391 uint16 GraphicsManager::aliasing(uint32 val1, uint32 val2, uint8 num) {
392 // 0: 0% val1 100% val2
393 // 1: 12% val1 87% val2
394 // 2: 25% val1 75% val2
395 // 3: 37% val1 62% val2
396 // 4: 50% val1 50% val2
397 // 5: 62% val1 37% val2
398 // 6: 75% val1 25% val2
399 // 7: 87% val1 12% val2
400 // 8: 100% val1 0% val2
401
402 return (((((val1 & _bitMask[2]) * num + (val2 & _bitMask[2]) * (8 - num)) >> 3) & _bitMask[2]) |
403 ((((val1 & _bitMask[1]) * num + (val2 & _bitMask[1]) * (8 - num)) >> 3) & _bitMask[1]) |
404 ((((val1 & _bitMask[0]) * num + (val2 & _bitMask[0]) * (8 - num)) >> 3) & _bitMask[0]));
405 }
406
dissolve()407 void GraphicsManager::dissolve() {
408 const uint16 val = 30;
409 uint16 centerX = MAXX / 2;
410 uint16 centerY = MAXY / 2;
411
412 int lastv = 9000;
413
414 uint32 sv = _vm->readTime();
415 uint32 cv = _vm->readTime();
416
417 while (sv + val > cv) {
418 _vm->checkSystem();
419 if (lastv + cv < sv + val) {
420 cv = _vm->readTime();
421 continue;
422 }
423
424 lastv = (sv - cv) + val;
425
426 const float a = (float)(((centerX + 200) / val) * lastv);
427 const float b = (float)((centerY / val) * lastv);
428
429 float x = 0.0f;
430 float y = b;
431
432 if (centerY - (int)y > TOP)
433 memset(_screenBuffer.getBasePtr(0, TOP), 0, (centerY - (int)y - TOP) * MAXX * 2);
434 if (AREA + TOP > centerY + (int)y)
435 memset(_screenBuffer.getBasePtr(0, centerY + (int)y), 0, (AREA + TOP - (centerY + (int)y)) * MAXX * 2);
436
437 float d1 = b * b - a * a * b + a * a / 4.0f;
438 while (_vm->floatComp(a * a * (y - 0.5f), b * b * (x + 1.0f)) == 1) {
439 if (_vm->floatComp(d1, 0.0f) == -1)
440 d1 += b * b * (2.0f * x + 3.0f);
441 else {
442 d1 += b * b * (2.0f * x + 3.0f) + a * a * (-2.0f * y + 2.0f);
443 y -= 1.0f;
444 }
445 x += 1.0f;
446
447 int rightX = centerX + (int)x;
448 int maxY = centerY + (int)y;
449 int minY = centerY - (int)y;
450 if (rightX < MAXX) {
451 if (maxY < MAXY)
452 memset(_screenBuffer.getBasePtr(rightX, maxY), 0, (MAXX - rightX) * 2);
453 if (minY >= 0)
454 memset(_screenBuffer.getBasePtr(rightX, minY), 0, (MAXX - rightX) * 2);
455 }
456 int leftX = centerX - (int)x;
457 if (leftX > 0) {
458 if (maxY < MAXY)
459 memset(_screenBuffer.getBasePtr(0, maxY), 0, leftX * 2);
460 if (minY >= 0)
461 memset(_screenBuffer.getBasePtr(0, minY), 0, leftX * 2);
462 }
463 }
464
465 float d2 = b * b * (x + 0.5f) * (x + 0.5f) + a * a * (y - 1.0f) * (y - 1.0f) - a * a * b * b;
466 while (_vm->floatComp(y, 0.0f) == 1) {
467 if (_vm->floatComp(d2, 0.0f) == -1) {
468 d2 += b * b * (2.0f * x + 2.0f) + a * a * (-2.0f * y + 3.0f);
469 x += 1.0f;
470 } else
471 d2 += a * a * (-2.0f * y + 3.0f);
472 y -= 1.0f;
473
474 int rightX = centerX + (int)x;
475 int maxY = centerY + (int)y;
476 int minY = centerY - (int)y;
477 if (rightX < MAXX) {
478 if (maxY < MAXY)
479 memset(_screenBuffer.getBasePtr(rightX, maxY), 0, (MAXX - rightX) * 2);
480 if (minY >= 0)
481 memset(_screenBuffer.getBasePtr(rightX, minY), 0, (MAXX - rightX) * 2);
482 }
483 int leftX = centerX - (int)x;
484 if (leftX > 0) {
485 if (maxY < MAXY)
486 memset(_screenBuffer.getBasePtr(0, maxY), 0, leftX * 2);
487 if (minY >= 0)
488 memset(_screenBuffer.getBasePtr(0, minY), 0, leftX * 2);
489 }
490 }
491
492 copyToScreen(0, 0, MAXX, MAXY);
493 cv = _vm->readTime();
494 }
495
496 clearScreen();
497 }
498
paintScreen(bool flag)499 void GraphicsManager::paintScreen(bool flag) {
500 _vm->_animTypeMgr->next();
501
502 _dirtyRects.clear();
503 _vm->_flagPaintCharacter = true; // always redraws the character
504
505 // erase character
506 if (_vm->_flagShowCharacter && _vm->_actor->actorRectIsValid()) { // if a description exists
507 Common::Rect actorRect = _vm->_actor->getActorRect();
508 actorRect.translate(0, -TOP);
509 drawObj(-1, false, Common::Rect(0, TOP, MAXX, AREA + TOP), actorRect);
510 } else if (_vm->_animMgr->_animRect.left != MAXX) {
511 drawObj(-1, false, Common::Rect(0, TOP, MAXX, AREA + TOP), _vm->_animMgr->_animRect);
512 }
513
514 // If there's text to remove
515 if (_vm->_textStatus & TEXT_DEL) {
516 // remove text
517 Common::Rect drawObjRect = _vm->_textMgr->getOldTextRect();
518 drawObjRect.translate(0, -TOP);
519
520 if (drawObjRect.top >= 0 && drawObjRect.bottom < AREA) {
521 drawObj(-1, false, Common::Rect(0, TOP, MAXX, MAXY + TOP), drawObjRect);
522 } else {
523 eraseObj(drawObjRect);
524 }
525 _vm->_textMgr->clearOldText();
526
527 if (!(_vm->_textStatus & TEXT_DRAW)) // if there's no new text
528 _vm->_textStatus = TEXT_OFF; // stop updating text
529 }
530
531 // Suppress all the objects you removed
532 for (Common::List<SSortTable>::iterator it = _vm->_sortTable.begin(); it != _vm->_sortTable.end(); ++it) {
533 if (it->_remove) {
534 drawObj(-1, false, Common::Rect(0, TOP, MAXX, AREA + TOP), _vm->_obj[it->_objectId]._rect);
535 }
536 }
537
538 // Find the position of the character
539 _vm->_pathFind->actorOrder();
540
541 // For every box from the horizon forward...
542 // Copy per level
543 for (int liv = _vm->_pathFind->_numSortPanel; liv >= 0; --liv) {
544 uint16 curBox = _vm->_pathFind->_sortPan[liv]._num;
545
546 // draws all objects and animations that intersect the boundaries and refer to the current box
547 paintObjAnm(curBox);
548 }
549
550 if (_vm->_textStatus & TEXT_DRAW) {
551 _vm->_textMgr->drawCurString();
552 _vm->_textStatus = TEXT_DRAW; // Activate text update
553 }
554
555 _vm->_actor->updateStepSound();
556
557 if (!flag && !_vm->_flagDialogActive) {
558 copyToScreen(0, 0, MAXX, MAXY);
559 }
560
561 _vm->_sortTable.clear();
562
563 _vm->_flagPaintCharacter = false;
564 _vm->_flagWaitRegen = false;
565
566 // Handle papaverine delayed action
567 if (_vm->_curRoom == kRoom4A && _vm->_obj[oCHOCOLATES4A].isFlagExtra()) {
568 if (_vm->_animMgr->smkCurFrame(kSmackerBackground) > 480) {
569 _vm->playScript(s4AHELLEN);
570 _vm->_obj[oCHOCOLATES4A].setFlagExtra(false);
571 }
572 }
573 //
574 }
575
576 /**
577 * Draw all objects and animations that intersect
578 * boundaries belonging to curbox
579 */
paintObjAnm(uint16 curBox)580 void GraphicsManager::paintObjAnm(uint16 curBox) {
581 _vm->_animMgr->refreshAnim(curBox);
582
583 // draws new cards belonging to the current box
584 for (Common::List<SSortTable>::iterator it = _vm->_sortTable.begin(); it != _vm->_sortTable.end(); ++it) {
585 if (!it->_remove && _vm->_obj[it->_objectId]._nbox == curBox) {
586 // the bitmap object at the desired level
587 SObject obj = _vm->_obj[it->_objectId];
588 Common::Rect drawRect = obj._rect;
589 drawRect.translate(0, TOP);
590 drawObj(_vm->getRoomObjectIndex(it->_objectId), obj.isModeMask(), drawRect, Common::Rect(drawRect.width(), drawRect.height()), false);
591 _dirtyRects.push_back(drawRect);
592 }
593 }
594
595 for (DirtyRectsIterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
596 for (int i = 0; i < MAXOBJINROOM; ++i) {
597 const uint16 curObject = _vm->_room[_vm->_curRoom]._object[i];
598 if (!curObject)
599 break;
600
601 SObject obj = _vm->_obj[curObject];
602
603 if ((obj.isModeFull() || obj.isModeMask()) && _vm->isObjectVisible(curObject) && (obj._nbox == curBox)) {
604 Common::Rect r = *it;
605 Common::Rect r2 = obj._rect;
606
607 r2.translate(0, TOP);
608
609 // Include the bottom right of the rect in the intersects() check
610 ++r2.bottom;
611 ++r2.right;
612
613 if (r.intersects(r2)) {
614 Common::Rect drawRect = obj._rect;
615 drawRect.translate(0, TOP);
616
617 // Restore the bottom right of the rect
618 --r2.bottom;
619 --r2.right;
620
621 // TODO: Simplify this?
622 const int16 xr1 = (r2.left > r.left) ? 0 : r.left - r2.left;
623 const int16 yr1 = (r2.top > r.top) ? 0 : r.top - r2.top;
624 const int16 xr2 = MIN<int16>(r.right, r2.right) - r2.left;
625 const int16 yr2 = MIN<int16>(r.bottom, r2.bottom) - r2.top;
626 drawObj(i, obj.isModeMask(), drawRect, Common::Rect(xr1, yr1, xr2, yr2), false);
627 }
628 }
629 }
630 }
631
632 if (_vm->_pathFind->getActorPos() == curBox && _vm->_flagShowCharacter) {
633 _vm->_renderer->drawCharacter(CALCPOINTS);
634
635 if (_vm->_actor->actorRectIsValid()) {
636 const Common::Rect actorRect = _vm->_actor->getActorRect();
637 // enlarge the last dirty rectangle with the actor's rectangle
638 if (!_dirtyRects.empty())
639 _dirtyRects.back().extend(actorRect);
640
641 _vm->_renderer->resetZBuffer(actorRect);
642 }
643
644 _vm->_renderer->drawCharacter(DRAWFACES);
645
646 } else if (_vm->_pathFind->getActorPos() == curBox && !_vm->_flagDialogActive) {
647 _vm->_animMgr->refreshActionAnimation();
648 }
649 }
650
getCharWidth(byte character)651 uint16 GraphicsManager::getCharWidth(byte character) {
652 return _fonts[character]._width;
653 }
654
drawChar(byte curChar,uint16 textColor,uint16 line,Common::Rect rect,Common::Rect subtitleRect,uint16 inc,Graphics::Surface * externalSurface)655 void GraphicsManager::drawChar(byte curChar, uint16 textColor, uint16 line, Common::Rect rect, Common::Rect subtitleRect, uint16 inc, Graphics::Surface *externalSurface) {
656 uint16 fontDataOffset = 0;
657 const uint16 charWidth = getCharWidth(curChar);
658
659 for (uint16 y = line * CARHEI; y < (line + 1) * CARHEI; ++y) {
660 uint16 curPos = 0;
661 uint16 curColor = MASKCOL;
662
663 while (curPos <= charWidth - 1) {
664 if (y >= subtitleRect.top && y < subtitleRect.bottom) {
665 if (curColor != MASKCOL && _fonts[curChar]._data[fontDataOffset]) {
666 const uint16 charLeft = inc + curPos;
667 const uint16 charRight = charLeft + _fonts[curChar]._data[fontDataOffset];
668 drawCharPixel(
669 y,
670 charLeft,
671 charRight,
672 rect,
673 subtitleRect,
674 curColor,
675 externalSurface
676 );
677 }
678 }
679
680 curPos += _fonts[curChar]._data[fontDataOffset];
681 ++fontDataOffset;
682
683 if (curColor == MASKCOL)
684 curColor = 0;
685 else if (curColor == 0)
686 curColor = textColor;
687 else if (curColor == textColor)
688 curColor = MASKCOL;
689 }
690 }
691 }
692
drawCharPixel(uint16 y,uint16 charLeft,uint16 charRight,Common::Rect rect,Common::Rect subtitleRect,uint16 color,Graphics::Surface * externalSurface)693 void GraphicsManager::drawCharPixel(uint16 y, uint16 charLeft, uint16 charRight, Common::Rect rect, Common::Rect subtitleRect, uint16 color, Graphics::Surface *externalSurface) {
694 Graphics::Surface *surface = externalSurface ? externalSurface : &_screenBuffer;
695 uint16 *dst1 = (uint16 *)surface->getBasePtr(rect.left + charLeft, rect.top + y);
696 uint16 *dst2 = (uint16 *)surface->getBasePtr(rect.left + subtitleRect.left, rect.top + y);
697 uint16 *dst = nullptr;
698 uint16 size = 0;
699
700 if (charLeft >= subtitleRect.left && charRight < subtitleRect.right) {
701 dst = dst1;
702 size = charRight - charLeft;
703 } else if (charLeft < subtitleRect.left && charRight < subtitleRect.right && charRight > subtitleRect.left) {
704 dst = dst2;
705 size = charRight - subtitleRect.left;
706 } else if (charLeft >= subtitleRect.left && charRight >= subtitleRect.right && subtitleRect.right > charLeft) {
707 dst = dst1;
708 size = subtitleRect.right - charLeft;
709 } else if (charLeft < subtitleRect.left && charRight >= subtitleRect.right && subtitleRect.right > charLeft) {
710 dst = dst2;
711 size = subtitleRect.right - subtitleRect.left;
712 }
713
714 if (dst && size > 0) {
715 uint16 *d = dst;
716 for (uint32 i = 0; i < size; ++i)
717 *d++ = color;
718 }
719 }
720
initCursor()721 void GraphicsManager::initCursor() {
722 const int cw = 21, ch = 21;
723 const int cx = 10, cy = 10;
724 uint16 cursor[cw * ch];
725 memset(cursor, 0, ARRAYSIZE(cursor) * 2);
726
727 const uint16 cursorColor = (uint16)_screenFormat.RGBToColor(255, 255, 255);
728
729 for (int i = 0; i < cw; ++i) {
730 if (i >= 8 && i <= 12 && i != 10)
731 continue;
732 cursor[cx * cw + i] = cursorColor; // horizontal
733 cursor[cx + cw * i] = cursorColor; // vertical
734 }
735
736 CursorMan.pushCursor(cursor, cw, ch, cx, cy, 0, false, &_screenFormat);
737 }
738
showCursor()739 void GraphicsManager::showCursor() {
740 CursorMan.showMouse(true);
741 }
742
hideCursor()743 void GraphicsManager::hideCursor() {
744 CursorMan.showMouse(false);
745 }
746
loadFont()747 void GraphicsManager::loadFont() {
748 Common::String fileName = "nlfont.fnt";
749 Common::SeekableReadStream *fontStream = _vm->_dataFile.createReadStreamForMember(fileName);
750 if (fontStream == nullptr)
751 error("readData(): File %s not found", fileName.c_str());
752
753 uint16 fontDataOffset = 768;
754
755 for (int i = 0; i < 256; ++i) {
756 uint16 offset = fontStream->readSint16LE();
757 _fonts[i]._width = fontStream->readByte();
758
759 int tmpPos = fontStream->pos();
760 fontStream->seek(offset + fontDataOffset);
761
762 int cpt = 0;
763 for (uint16 y = 0; y < CARHEI; ++y) {
764 uint16 curPos = 0;
765 while (curPos <= _fonts[i]._width - 1) {
766 curPos += fontStream->readByte();
767 ++cpt;
768 }
769 }
770
771 fontStream->seek(offset + fontDataOffset);
772 _fonts[i]._data = new int8[cpt];
773 fontStream->read(_fonts[i]._data, cpt);
774 fontStream->seek(tmpPos);
775 }
776
777 // Fix o+e ligature character (lowercase and uppercase). Ticket #12623
778
779 // Format is :
780 // - Each line represents a line of pixels
781 // - colors are in this order : none, shadow, text. Colors are looping until the total number of pixels corresponds to the character width
782 // - each number correspond to a number of pixels of the corresponding color
783 // So, 1, 6, 0, 2 means : 1 pixel unchanged, 6 pixels shadow, 0 pixel in text color, 2 pixels unchanged
784 static const int8 fix140[67] = {
785 1, 8,
786 0, 2, 2, 0, 1, 3, 0, 1,
787 0, 1, 1, 0, 2, 2, 0, 3,
788 0, 1, 1, 0, 3, 1, 0, 2, 0, 1,
789 0, 1, 1, 0, 3, 2, 0, 1, 0, 1,
790 0, 1, 1, 0, 3, 1, 0, 2, 0, 1,
791 0, 1, 1, 0, 2, 2, 0, 3,
792 0, 2, 2, 0, 1, 3, 0, 1,
793 1, 8,
794 9
795 };
796
797 static const int8 fix156[54] = {
798 9,
799 9,
800 1, 6, 0, 2,
801 0, 2, 2, 0, 1, 2, 0, 1, 0, 1,
802 0, 1, 1, 0, 2, 1, 0, 2, 1, 0, 1,
803 0, 1, 1, 0, 2, 4, 0, 1,
804 0, 1, 1, 0, 2, 1, 0, 4,
805 0, 2, 2, 0, 1, 3, 0, 1,
806 1, 8,
807 9
808 };
809
810 delete _fonts[140]._data;
811 delete _fonts[156]._data;
812 _fonts[140]._width = _fonts[156]._width = 9;
813 _fonts[140]._data = new int8[67];
814 _fonts[156]._data = new int8[54];
815
816 memcpy(_fonts[140]._data, fix140, 67);
817 memcpy(_fonts[156]._data, fix156, 54);
818 }
819
isCursorVisible()820 bool GraphicsManager::isCursorVisible() {
821 return CursorMan.isVisible();
822 }
823
showDemoPic()824 void GraphicsManager::showDemoPic() {
825 Common::File file;
826 if (file.open("EndPic.bm")) {
827 readSurface(&file, &_screenBuffer, MAXX, MAXY);
828 copyToScreen(0, 0, MAXX, MAXY);
829 g_system->updateScreen();
830
831 _vm->freeKey();
832 _vm->_mouseLeftBtn = _vm->_mouseRightBtn = false;
833 _vm->waitKey();
834 }
835 }
836
837 } // End of namespace Trecision
838