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