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 "image/png.h"
24 
25 #include "sludge/event.h"
26 #include "sludge/fileset.h"
27 #include "sludge/graphics.h"
28 #include "sludge/imgloader.h"
29 #include "sludge/newfatal.h"
30 #include "sludge/speech.h"
31 #include "sludge/statusba.h"
32 #include "sludge/sludge.h"
33 #include "sludge/sludger.h"
34 #include "sludge/variable.h"
35 #include "sludge/version.h"
36 #include "sludge/zbuffer.h"
37 
38 namespace Sludge {
39 
killParallax()40 void GraphicsManager::killParallax() {
41 	if (!_parallaxLayers)
42 		return;
43 
44 	for (ParallaxLayers::iterator it = _parallaxLayers->begin(); it != _parallaxLayers->end(); ++it) {
45 		(*it)->surface.free();
46 		delete (*it);
47 		(*it) = nullptr;
48 	}
49 	_parallaxLayers->clear();
50 
51 	delete _parallaxLayers;
52 	_parallaxLayers = nullptr;
53 }
54 
loadParallax(uint16 v,uint16 fracX,uint16 fracY)55 bool GraphicsManager::loadParallax(uint16 v, uint16 fracX, uint16 fracY) {
56 	if (!_parallaxLayers)
57 		_parallaxLayers = new ParallaxLayers;
58 
59 	setResourceForFatal(v);
60 	if (!g_sludge->_resMan->openFileFromNum(v))
61 		return fatal("Can't open parallax image");
62 
63 	ParallaxLayer *nP = new ParallaxLayer;
64 	if (!checkNew(nP))
65 		return false;
66 
67 	_parallaxLayers->push_back(nP);
68 
69 	if (!ImgLoader::loadImage(v, "parallax", g_sludge->_resMan->getData(), &nP->surface, 0))
70 		return false;
71 
72 	nP->fileNum = v;
73 	nP->fractionX = fracX;
74 	nP->fractionY = fracY;
75 
76 	// 65535 is the value of AUTOFIT constant in Sludge
77 	if (fracX == 65535) {
78 		nP->wrapS = false;
79 		if (nP->surface.w < (int16)_winWidth) {
80 			fatal("For AUTOFIT parallax backgrounds, the image must be at least as wide as the game window/screen.");
81 			return false;
82 		}
83 	} else {
84 		nP->wrapS = true;
85 	}
86 
87 	if (fracY == 65535) {
88 		nP->wrapT = false;
89 		if (nP->surface.h < (int16)_winHeight) {
90 			fatal("For AUTOFIT parallax backgrounds, the image must be at least as tall as the game window/screen.");
91 			return false;
92 		}
93 	} else {
94 		nP->wrapT = true;
95 	}
96 
97 	g_sludge->_resMan->finishAccess();
98 	setResourceForFatal(-1);
99 	return true;
100 }
101 
drawParallax()102 void GraphicsManager::drawParallax() {
103 	if (!_parallaxLayers || _parallaxLayers->empty())
104 		return;
105 
106 	// display parallax from bottom to top
107 	for (ParallaxLayers::iterator it = _parallaxLayers->begin(); it != _parallaxLayers->end(); ++it) {
108 		ParallaxLayer *p = *it;
109 		p->cameraX = sortOutPCamera(_cameraX, p->fractionX, (int)(_sceneWidth - (float)_winWidth / _cameraZoom), (int)(p->surface.w - (float)_winWidth / _cameraZoom));
110 		p->cameraY = sortOutPCamera(_cameraY, p->fractionY, (int)(_sceneHeight - (float)_winHeight / _cameraZoom), (int)(p->surface.h - (float)_winHeight / _cameraZoom));
111 
112 		uint w = p->wrapS ? _sceneWidth : p->surface.w;
113 		uint h = p->wrapT ? _sceneHeight : p->surface.h;
114 
115 		debugC(1, kSludgeDebugGraphics, "drawParallax(): camX: %d camY: %d dims: %d x %d sceneDims: %d x %d winDims: %d x %d surf: %d x %d", p->cameraX, p->cameraY, w, h, _sceneWidth, _sceneHeight, _winWidth, _winHeight, p->surface.w, p->surface.h);
116 
117 		Graphics::TransparentSurface tmp(p->surface, false);
118 		for (uint y = 0; y < _sceneHeight; y += p->surface.h) {
119 			for (uint x = 0; x < _sceneWidth; x += p->surface.w) {
120 				tmp.blit(_renderSurface, x - p->cameraX, y - p->cameraY);
121 				debugC(3, kSludgeDebugGraphics, "drawParallax(): blit to: %d, %d", x - p->cameraX, y - p->cameraY);
122 			}
123 		}
124 	}
125 }
126 
saveParallax(Common::WriteStream * stream)127 void GraphicsManager::saveParallax(Common::WriteStream *stream) {
128 	if (!_parallaxLayers)
129 		return;
130 
131 	ParallaxLayers::iterator it;
132 	for (it = _parallaxLayers->begin(); it != _parallaxLayers->end(); ++it) {
133 		stream->writeByte(1);
134 		stream->writeUint16BE((*it)->fileNum);
135 		stream->writeUint16BE((*it)->fractionX);
136 		stream->writeUint16BE((*it)->fractionY);
137 	}
138 }
139 
nosnapshot()140 void GraphicsManager::nosnapshot() {
141 	if (_snapshotSurface.getPixels())
142 		_snapshotSurface.free();
143 }
144 
saveSnapshot(Common::WriteStream * stream)145 void GraphicsManager::saveSnapshot(Common::WriteStream *stream) {
146 	if (_snapshotSurface.getPixels()) {
147 		stream->writeByte(1);               // 1 for snapshot follows
148 		Image::writePNG(*stream, _snapshotSurface);
149 	} else {
150 		stream->writeByte(0);
151 	}
152 }
153 
snapshot()154 bool GraphicsManager::snapshot() {
155 	nosnapshot();
156 	if (!freeze())
157 		return false;
158 
159 	// draw snapshot to rendersurface
160 	displayBase();
161 	_vm->_speechMan->display();
162 	g_sludge->_statusBar->draw();
163 
164 	// copy backdrop to snapshot
165 	_snapshotSurface.copyFrom(_renderSurface);
166 
167 	unfreeze(false);
168 	return true;
169 }
170 
restoreSnapshot(Common::SeekableReadStream * stream)171 bool GraphicsManager::restoreSnapshot(Common::SeekableReadStream *stream) {
172 	if (!(ImgLoader::loadImage(-1, NULL, stream, &_snapshotSurface))) {
173 		return false;
174 	}
175 	return true;
176 }
177 
killBackDrop()178 void GraphicsManager::killBackDrop() {
179 	if (_backdropSurface.getPixels())
180 		_backdropSurface.free();
181 	_backdropExists = false;
182 }
183 
killLightMap()184 void GraphicsManager::killLightMap() {
185 	if (_lightMap.getPixels()) {
186 		_lightMap.free();
187 	}
188 	_lightMapNumber = 0;
189 }
190 
reserveBackdrop()191 bool GraphicsManager::reserveBackdrop() {
192 	_cameraX = 0;
193 	_cameraY = 0;
194 	_vm->_evtMan->mouseX() = (int)((float)_vm->_evtMan->mouseX() * _cameraZoom);
195 	_vm->_evtMan->mouseY() = (int)((float)_vm->_evtMan->mouseY() * _cameraZoom);
196 	_cameraZoom = 1.0;
197 	_vm->_evtMan->mouseX() = (int)((float)_vm->_evtMan->mouseX() / _cameraZoom);
198 	_vm->_evtMan->mouseY() = (int)((float)_vm->_evtMan->mouseY() / _cameraZoom);
199 
200 	_backdropSurface.create(_sceneWidth, _sceneHeight, *_vm->getScreenPixelFormat());
201 
202 	return true;
203 }
204 
killAllBackDrop()205 void GraphicsManager::killAllBackDrop() {
206 	killLightMap();
207 	killBackDrop();
208 	g_sludge->_gfxMan->killParallax();
209 	killZBuffer();
210 }
211 
resizeBackdrop(int x,int y)212 bool GraphicsManager::resizeBackdrop(int x, int y) {
213 	debugC(1, kSludgeDebugGraphics, "Load HSI");
214 	_sceneWidth = x;
215 	_sceneHeight = y;
216 	return reserveBackdrop();
217 }
218 
killResizeBackdrop(int x,int y)219 bool GraphicsManager::killResizeBackdrop(int x, int y) {
220 	killAllBackDrop();
221 	return resizeBackdrop(x, y);
222 }
223 
loadBackDrop(int fileNum,int x,int y)224 void GraphicsManager::loadBackDrop(int fileNum, int x, int y) {
225 	debugC(1, kSludgeDebugGraphics, "Load back drop of num %i at position %i, %i", fileNum, x, y);
226 	setResourceForFatal(fileNum);
227 	if (!g_sludge->_resMan->openFileFromNum(fileNum)) {
228 		fatal("Can't load overlay image");
229 		return;
230 	}
231 
232 	if (!loadHSI(fileNum, g_sludge->_resMan->getData(), x, y, false)) {
233 		Common::String mess = Common::String::format("Can't paste overlay image outside scene dimensions\n\nX = %i\nY = %i\nWidth = %i\nHeight = %i", x, y, _sceneWidth, _sceneHeight);
234 		fatal(mess);
235 	}
236 
237 	g_sludge->_resMan->finishAccess();
238 	setResourceForFatal(-1);
239 
240 	// reset zBuffer
241 	if (_zBuffer->originalNum >= 0) {
242 		setZBuffer(_zBuffer->originalNum);
243 	}
244 }
245 
mixBackDrop(int fileNum,int x,int y)246 void GraphicsManager::mixBackDrop(int fileNum, int x, int y) {
247 	debugC(1, kSludgeDebugGraphics, "Mix back drop of num %i at position %i, %i", fileNum, x, y);
248 	setResourceForFatal(fileNum);
249 	if (!g_sludge->_resMan->openFileFromNum(fileNum)) {
250 		fatal("Can't load overlay image");
251 		return;
252 	}
253 
254 	if (!mixHSI(fileNum, g_sludge->_resMan->getData(), x, y)) {
255 		fatal("Can't paste overlay image outside screen dimensions");
256 	}
257 
258 	g_sludge->_resMan->finishAccess();
259 	setResourceForFatal(-1);
260 }
261 
blankScreen(int x1,int y1,int x2,int y2)262 void GraphicsManager::blankScreen(int x1, int y1, int x2, int y2) {
263 	// in case of no backdrop added at all, create it
264 	if (!_backdropSurface.getPixels()) {
265 		_backdropSurface.create(_winWidth, _winHeight, _renderSurface.format);
266 	}
267 
268 	if (y1 < 0)
269 		y1 = 0;
270 	if (x1 < 0)
271 		x1 = 0;
272 	if (x2 > (int)_sceneWidth)
273 		x2 = (int)_sceneWidth;
274 	if (y2 > (int)_sceneHeight)
275 		y2 = (int)_sceneHeight;
276 
277 	_backdropSurface.fillRect(Common::Rect(x1, y1, x2, y2), _currentBlankColour);
278 
279 	// reset zBuffer
280 	if (_zBuffer->originalNum >= 0) {
281 		setZBuffer(_zBuffer->originalNum);
282 	}
283 }
284 
blankAllScreen()285 void GraphicsManager::blankAllScreen() {
286 	blankScreen(0, 0, _sceneWidth, _sceneHeight);
287 }
288 
289 // This function is very useful for scrolling credits, but very little else
hardScroll(int distance)290 void GraphicsManager::hardScroll(int distance) {
291 	// scroll 0 distance, return
292 	if (!distance)
293 		return;
294 
295 	// blank screen
296 	blankAllScreen();
297 
298 	// scroll more than backdrop height, screen stay blank
299 	if (ABS(distance) >= (int)_sceneHeight) {
300 		return;
301 	}
302 
303 	// copy part of the backdrop to it
304 	if (distance > 0) {
305 		_backdropSurface.copyRectToSurface(_origBackdropSurface, 0, 0,
306 				Common::Rect(0, distance, _backdropSurface.w, _backdropSurface.h));
307 	} else {
308 		_backdropSurface.copyRectToSurface(_origBackdropSurface, 0, -distance,
309 				Common::Rect(0, 0, _backdropSurface.w, _backdropSurface.h + distance));
310 	}
311 }
312 
drawLine(uint x1,uint y1,uint x2,uint y2)313 void GraphicsManager::drawLine(uint x1, uint y1, uint x2, uint y2) {
314 	_backdropSurface.drawLine(x1, y1, x2, y2, _backdropSurface.format.ARGBToColor(255, 0, 0, 0));
315 }
316 
drawVerticalLine(uint x,uint y1,uint y2)317 void GraphicsManager::drawVerticalLine(uint x, uint y1, uint y2) {
318 	drawLine(x, y1, x, y2);
319 }
320 
drawHorizontalLine(uint x1,uint y,uint x2)321 void GraphicsManager::drawHorizontalLine(uint x1, uint y, uint x2) {
322 	drawLine(x1, y, x2, y);
323 }
324 
darkScreen()325 void GraphicsManager::darkScreen() {
326 	Graphics::TransparentSurface tmp(_backdropSurface, false);
327 	tmp.blit(_backdropSurface, 0, 0, Graphics::FLIP_NONE, nullptr, TS_ARGB(255 >> 1, 0, 0, 0));
328 
329 	// reset zBuffer
330 	if (_zBuffer->originalNum >= 0) {
331 		setZBuffer(_zBuffer->originalNum);
332 	}
333 }
334 
drawBackDrop()335 void GraphicsManager::drawBackDrop() {
336 	// TODO: apply lightmap shader
337 	drawParallax();
338 
339 	if (!_backdropExists)
340 		return;
341 	// draw backdrop
342 	Graphics::TransparentSurface tmp(_backdropSurface, false);
343 	tmp.blit(_renderSurface, -_cameraX, -_cameraY);
344 }
345 
loadLightMap(int v)346 bool GraphicsManager::loadLightMap(int v) {
347 	setResourceForFatal(v);
348 	if (!g_sludge->_resMan->openFileFromNum(v))
349 		return fatal("Can't open light map.");
350 
351 	killLightMap();
352 	_lightMapNumber = v;
353 	_lightMap.create(_sceneWidth, _sceneWidth, *_vm->getScreenPixelFormat());
354 
355 	Graphics::TransparentSurface tmp;
356 
357 	if (!ImgLoader::loadImage(v, "lightmap", g_sludge->_resMan->getData(), &tmp))
358 		return false;
359 
360 	if (tmp.w != (int16)_sceneWidth || tmp.h != (int16)_sceneHeight) {
361 		if (_lightMapMode == LIGHTMAPMODE_HOTSPOT) {
362 			return fatal("Light map width and height don't match scene width and height. That is required for lightmaps in HOTSPOT mode.");
363 		} else if (_lightMapMode == LIGHTMAPMODE_PIXEL) {
364 			tmp.blit(_lightMap, 0, 0, Graphics::FLIP_NONE, nullptr, TS_ARGB((uint)255, (uint)255, (uint)255, (uint)255), (int)_sceneWidth, (int)_sceneHeight);
365 		} else {
366 			_lightMap.copyFrom(tmp);
367 		}
368 	} else {
369 		_lightMap.copyFrom(tmp);
370 	}
371 
372 	tmp.free();
373 	g_sludge->_resMan->finishAccess();
374 	setResourceForFatal(-1);
375 
376 	return true;
377 }
378 
saveLightMap(Common::WriteStream * stream)379 void GraphicsManager::saveLightMap(Common::WriteStream *stream) {
380 	if (_lightMap.getPixels()) {
381 		stream->writeByte(1);
382 		stream->writeUint16BE(_lightMapNumber);
383 	} else {
384 		stream->writeByte(0);
385 	}
386 	stream->writeByte(_lightMapMode);
387 	stream->writeByte(_fadeMode);
388 }
389 
loadLightMap(int ssgVersion,Common::SeekableReadStream * stream)390 bool GraphicsManager::loadLightMap(int ssgVersion, Common::SeekableReadStream *stream) {
391 	if (stream->readByte()) {
392 		if (!loadLightMap(stream->readUint16BE()))
393 			return false;
394 	}
395 
396 	if (ssgVersion >= VERSION(1, 4)) {
397 		_lightMapMode = stream->readByte() % 3;
398 	}
399 
400 	_fadeMode = stream->readByte();
401 
402 	return true;
403 }
404 
loadHSI(int num,Common::SeekableReadStream * stream,int x,int y,bool reserve)405 bool GraphicsManager::loadHSI(int num, Common::SeekableReadStream *stream, int x, int y, bool reserve) {
406 	debugC(1, kSludgeDebugGraphics, "Load HSI");
407 	if (reserve) {
408 		killAllBackDrop(); // kill all
409 	}
410 
411 	Graphics::Surface tmp;
412 
413 	if (!ImgLoader::loadImage(num, "hsi", stream, &tmp, (int)reserve))
414 		return false;
415 
416 	uint realPicWidth = tmp.w;
417 	uint realPicHeight = tmp.h;
418 
419 	// resize backdrop
420 	if (reserve) {
421 		if (!resizeBackdrop(realPicWidth, realPicHeight))
422 			return false;
423 	}
424 
425 	if (x == IN_THE_CENTRE)
426 		x = (_sceneWidth - realPicWidth) >> 1;
427 	if (y == IN_THE_CENTRE)
428 		y = (_sceneHeight - realPicHeight) >> 1;
429 	if (x < 0 || x + realPicWidth > _sceneWidth || y < 0 || y + realPicHeight > _sceneHeight) {
430 		debugC(0, kSludgeDebugGraphics, "Illegal back drop size");
431 		return false;
432 	}
433 
434 	// copy surface loaded to backdrop
435 	Graphics::TransparentSurface tmp_trans(tmp, false);
436 	tmp_trans.blit(_backdropSurface, x, y);
437 	tmp.free();
438 
439 	_origBackdropSurface.copyFrom(_backdropSurface);
440 	_backdropExists = true;
441 
442 	return true;
443 }
444 
mixHSI(int num,Common::SeekableReadStream * stream,int x,int y)445 bool GraphicsManager::mixHSI(int num, Common::SeekableReadStream *stream, int x, int y) {
446 	debugC(1, kSludgeDebugGraphics, "Load mixHSI");
447 	Graphics::Surface mixSurface;
448 	if (!ImgLoader::loadImage(num, "mixhsi", stream, &mixSurface, 0))
449 		return false;
450 
451 	uint realPicWidth = mixSurface.w;
452 	uint realPicHeight = mixSurface.h;
453 
454 	if (x == IN_THE_CENTRE)
455 		x = (_sceneWidth - realPicWidth) >> 1;
456 	if (y == IN_THE_CENTRE)
457 		y = (_sceneHeight - realPicHeight) >> 1;
458 	if (x < 0 || x + realPicWidth > _sceneWidth || y < 0 || y + realPicHeight > _sceneHeight)
459 		return false;
460 
461 	Graphics::TransparentSurface tmp(mixSurface, false);
462 	tmp.blit(_backdropSurface, x, y, Graphics::FLIP_NONE, nullptr, TS_ARGB(255 >> 1, 255, 255, 255));
463 	mixSurface.free();
464 
465 	return true;
466 }
467 
saveHSI(Common::WriteStream * stream)468 void GraphicsManager::saveHSI(Common::WriteStream *stream) {
469 	Image::writePNG(*stream, _backdropSurface);
470 }
471 
saveBackdrop(Common::WriteStream * stream)472 void GraphicsManager::saveBackdrop(Common::WriteStream *stream) {
473 	stream->writeUint16BE(_cameraX);
474 	stream->writeUint16BE(_cameraY);
475 	stream->writeFloatLE(_cameraZoom);
476 	stream->writeByte(_brightnessLevel);
477 	saveHSI(stream);
478 }
479 
loadBackdrop(int ssgVersion,Common::SeekableReadStream * stream)480 void GraphicsManager::loadBackdrop(int ssgVersion, Common::SeekableReadStream *stream) {
481 	_cameraX = stream->readUint16BE();
482 	_cameraY = stream->readUint16BE();
483 	if (ssgVersion >= VERSION(2, 0)) {
484 		_cameraZoom = stream->readFloatLE();
485 	} else {
486 		_cameraZoom = 1.0;
487 	}
488 
489 	_brightnessLevel = stream->readByte();
490 
491 	loadHSI(-1, stream, 0, 0, true);
492 }
493 
getRGBIntoStack(uint x,uint y,StackHandler * sH)494 bool GraphicsManager::getRGBIntoStack(uint x, uint y, StackHandler *sH) {
495 	if (x >= _sceneWidth || y >= _sceneHeight) {
496 		return fatal("Co-ordinates are outside current scene!");
497 	}
498 
499 	Variable newValue;
500 
501 	newValue.varType = SVT_NULL;
502 
503 	byte *target = (byte *)_renderSurface.getBasePtr(x, y);
504 
505 	newValue.setVariable(SVT_INT, target[1]);
506 	if (!addVarToStackQuick(newValue, sH->first)) return false;
507 	sH->last = sH->first;
508 
509 	newValue.setVariable(SVT_INT, target[2]);
510 	if (!addVarToStackQuick(newValue, sH->first)) return false;
511 
512 	newValue.setVariable(SVT_INT, target[3]);
513 	if (!addVarToStackQuick(newValue, sH->first)) return false;
514 
515 	return true;
516 }
517 
518 } // End of namespace Sludge
519