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