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/endian.h"
24 
25 #include "engines/util.h"
26 
27 #include "graphics/cursorman.h"
28 #include "graphics/palette.h"
29 #include "graphics/surface.h"
30 
31 #include "gob/gob.h"
32 #include "gob/video.h"
33 #include "gob/global.h"
34 #include "gob/util.h"
35 #include "gob/dataio.h"
36 #include "gob/draw.h"
37 
38 namespace Gob {
39 
Font(const byte * data)40 Font::Font(const byte *data) : _dataPtr(data) {
41 	assert(data);
42 
43 	bool hasWidths = _dataPtr[0] & 0x80;
44 
45 	_data       = _dataPtr + 4;
46 	_itemWidth  = _dataPtr[0] & 0x7F;
47 	_itemHeight = _dataPtr[1];
48 	_startItem  = _dataPtr[2];
49 	_endItem    = _dataPtr[3];
50 	_charWidths = 0;
51 
52 	uint8 rowAlignedBits = (_itemWidth - 1) / 8 + 1;
53 
54 	_itemSize = rowAlignedBits * _itemHeight;
55 	_bitWidth = _itemWidth;
56 
57 	if (hasWidths)
58 		_charWidths = _dataPtr + 4 + _itemSize * getCharCount();
59 }
60 
~Font()61 Font::~Font() {
62 	delete[] _dataPtr;
63 }
64 
getCharWidth(uint8 c) const65 uint8 Font::getCharWidth(uint8 c) const {
66 	if (!_charWidths || (_endItem == 0))
67 		return _itemWidth;
68 
69 	if ((c < _startItem) || (c > _endItem))
70 		return _itemWidth;
71 
72 	return _charWidths[c - _startItem];
73 }
74 
getCharWidth() const75 uint8 Font::getCharWidth() const {
76 	return _itemWidth;
77 }
78 
getCharHeight() const79 uint8 Font::getCharHeight() const {
80 	return _itemHeight;
81 }
82 
getCharCount() const83 uint16 Font::getCharCount() const {
84 	return _endItem - _startItem + 1;
85 }
86 
hasChar(uint8 c) const87 bool Font::hasChar(uint8 c) const {
88 	return (c >= _startItem) && (c <= _endItem);
89 }
90 
isMonospaced() const91 bool Font::isMonospaced() const {
92 	return _charWidths == 0;
93 }
94 
drawLetter(Surface & surf,uint8 c,uint16 x,uint16 y,uint32 color1,uint32 color2,bool transp) const95 void Font::drawLetter(Surface &surf, uint8 c, uint16 x, uint16 y,
96 		uint32 color1, uint32 color2, bool transp) const {
97 
98 	uint16 data;
99 
100 	const byte *src = getCharData(c);
101 	if (!src) {
102 		warning("Font::drawLetter(): getCharData() == 0");
103 		return;
104 	}
105 
106 	Pixel dst = surf.get(x, y);
107 
108 	int nWidth = _itemWidth;
109 	if (nWidth & 7)
110 		nWidth = (nWidth & 0xF8) + 8;
111 
112 	nWidth >>= 3;
113 
114 	for (int i = 0; (i < _itemHeight) && dst.isValid(); i++) {
115 		int width = _itemWidth;
116 
117 		for (int k = 0; k < nWidth; k++) {
118 
119 			data = *src++;
120 			for (int j = 0; j < MIN(8, width); j++) {
121 
122 				if (dst.isValid()) {
123 					if (data & 0x80)
124 						dst.set(color1);
125 					else if (!transp)
126 						dst.set(color2);
127 				}
128 
129 				dst++;
130 				data <<= 1;
131 			}
132 
133 			width -= 8;
134 
135 		}
136 
137 		dst += surf.getWidth() - _itemWidth;
138 	}
139 }
140 
drawString(const Common::String & str,int16 x,int16 y,int16 color1,int16 color2,bool transp,Surface & dest) const141 void Font::drawString(const Common::String &str, int16 x, int16 y, int16 color1, int16 color2,
142 					  bool transp, Surface &dest) const {
143 
144 	const char *s = str.c_str();
145 
146 	while (*s != '\0') {
147 		const int16 charRight  = x + getCharWidth(*s);
148 		const int16 charBottom = y + getCharHeight();
149 
150 		if ((x >= 0) && (y >= 0) && (charRight <= dest.getWidth()) && (charBottom <= dest.getHeight()))
151 			drawLetter(dest, *s, x, y, color1, color2, transp);
152 
153 		x += getCharWidth(*s);
154 		s++;
155 	}
156 }
157 
getCharData(uint8 c) const158 const byte *Font::getCharData(uint8 c) const {
159 	if (_endItem == 0) {
160 		warning("Font::getCharData(): _endItem == 0");
161 		return 0;
162 	}
163 
164 	if ((c < _startItem) || (c > _endItem))
165 		return 0;
166 
167 	return _data + (c - _startItem) * _itemSize;
168 }
169 
170 
Video(GobEngine * vm)171 Video::Video(GobEngine *vm) : _vm(vm) {
172 	_doRangeClamp = false;
173 
174 	_surfWidth = 320;
175 	_surfHeight = 200;
176 
177 	_scrollOffsetX = 0;
178 	_scrollOffsetY = 0;
179 
180 	_splitHeight1 = 200;
181 	_splitHeight2 = 0;
182 	_splitStart = 0;
183 
184 	_screenDeltaX = 0;
185 	_screenDeltaY = 0;
186 
187 	_curSparse = 0;
188 	_lastSparse = 0xFFFFFFFF;
189 
190 	_dirtyAll = false;
191 }
192 
~Video()193 Video::~Video() {
194 }
195 
initPrimary(int16 mode)196 void Video::initPrimary(int16 mode) {
197 	if ((mode != 3) && (mode != -1))
198 		_vm->validateVideoMode(mode);
199 	_vm->validateVideoMode(_vm->_global->_videoMode);
200 
201 	if (mode == -1)
202 		mode = 3;
203 	_vm->_global->_oldMode = mode;
204 
205 	if (mode != 3) {
206 		initSurfDesc(_surfWidth, _surfHeight, PRIMARY_SURFACE);
207 
208 		if (!_vm->_global->_dontSetPalette)
209 			Video::setFullPalette(_vm->_global->_pPaletteDesc);
210 	}
211 }
212 
initSurfDesc(int16 width,int16 height,int16 flags)213 SurfacePtr Video::initSurfDesc(int16 width, int16 height, int16 flags) {
214 	SurfacePtr descPtr;
215 
216 	if (flags & PRIMARY_SURFACE)
217 		assert((width == _surfWidth) && (height == _surfHeight));
218 
219 	if (flags & PRIMARY_SURFACE) {
220 		_vm->_global->_primaryWidth = width;
221 		_vm->_global->_primaryHeight = height;
222 
223 		descPtr = _vm->_global->_primarySurfDesc;
224 		descPtr->resize(width, height);
225 	} else {
226 		assert(!(flags & DISABLE_SPR_ALLOC));
227 
228 		if (!(flags & SCUMMVM_CURSOR))
229 			width = (width + 7) & 0xFFF8;
230 
231 		descPtr = SurfacePtr(new Surface(width, height, _vm->getPixelFormat().bytesPerPixel));
232 	}
233 	return descPtr;
234 }
235 
clearScreen()236 void Video::clearScreen() {
237 	g_system->fillScreen(0);
238 }
239 
setSize()240 void Video::setSize() {
241 	if (_vm->isTrueColor())
242 		initGraphics(_vm->_width, _vm->_height, nullptr);
243 	else
244 		initGraphics(_vm->_width, _vm->_height);
245 }
246 
retrace(bool mouse)247 void Video::retrace(bool mouse) {
248 	if (mouse)
249 		CursorMan.showMouse((_vm->_draw->_showCursor & 6) != 0);
250 
251 	if (_vm->_global->_primarySurfDesc) {
252 		int screenX = _screenDeltaX;
253 		int screenY = _screenDeltaY;
254 		int screenWidth = MIN<int>(_surfWidth - _scrollOffsetX, _vm->_width);
255 		int screenHeight = MIN<int>(_surfHeight - _splitHeight2 - _scrollOffsetY, _vm->_height - _splitHeight2);
256 
257 		dirtyRectsApply(_scrollOffsetX, _scrollOffsetY, screenWidth, screenHeight,
258 				screenX, screenY);
259 
260 		if (_splitSurf) {
261 
262 			screenX = 0;
263 			screenY = _vm->_height - _splitSurf->getHeight();
264 			screenWidth = MIN<int>(_vm->_width, _splitSurf->getWidth());
265 			screenHeight = _splitSurf->getHeight();
266 
267 			_splitSurf->blitToScreen(0, 0, screenWidth - 1, screenHeight - 1, screenX, screenY);
268 
269 		} else if (_splitHeight2 > 0) {
270 
271 			screenX = 0;
272 			screenY = _vm->_height - _splitHeight2;
273 			screenWidth = MIN<int>(_surfWidth, _vm->_width);
274 			screenHeight = _splitHeight2;
275 
276 			dirtyRectsApply(0, _splitStart, screenWidth, screenHeight, screenX, screenY);
277 		}
278 
279 		dirtyRectsClear();
280 		g_system->updateScreen();
281 	}
282 
283 }
284 
waitRetrace(bool mouse)285 void Video::waitRetrace(bool mouse) {
286 	uint32 time = _vm->_util->getTimeKey();
287 	retrace(mouse);
288 	_vm->_util->delay(MAX(1, 10 - (int)(_vm->_util->getTimeKey() - time)));
289 }
290 
sparseRetrace(int max)291 void Video::sparseRetrace(int max) {
292 	uint32 timeKey = _vm->_util->getTimeKey();
293 
294 	if ((_curSparse++ > max) || ((timeKey - _lastSparse) > 1000)) {
295 		_curSparse = 0;
296 		retrace(false);
297 	}
298 
299 	_lastSparse = timeKey;
300 }
301 
drawPacked(byte * sprBuf,int16 width,int16 height,int16 x,int16 y,byte transp,Surface & dest)302 void Video::drawPacked(byte *sprBuf, int16 width, int16 height,
303 		int16 x, int16 y, byte transp, Surface &dest) {
304 
305 	int destRight = x + width;
306 	int destBottom = y + height;
307 
308 	Pixel dst = dest.get(x, y);
309 
310 	int curx = x;
311 	int cury = y;
312 
313 	while (1) {
314 		uint8 val = *sprBuf++;
315 		unsigned int repeat = val & 7;
316 		val &= 0xF8;
317 
318 		if (!(val & 8)) {
319 			repeat <<= 8;
320 			repeat |= *sprBuf++;
321 		}
322 		repeat++;
323 		val >>= 4;
324 
325 		for (unsigned int i = 0; i < repeat; ++i) {
326 			if (curx < dest.getWidth() && cury < dest.getHeight())
327 				if (!transp || val)
328 					dst.set(val);
329 
330 			dst++;
331 			curx++;
332 			if (curx == destRight) {
333 				dst += dest.getWidth() + x - curx;
334 				curx = x;
335 				cury++;
336 				if (cury == destBottom)
337 					return;
338 			}
339 		}
340 
341 	}
342 }
343 
drawPackedSprite(byte * sprBuf,int16 width,int16 height,int16 x,int16 y,int16 transp,Surface & dest)344 void Video::drawPackedSprite(byte *sprBuf, int16 width, int16 height,
345 		int16 x, int16 y, int16 transp, Surface &dest) {
346 
347 	if (spriteUncompressor(sprBuf, width, height, x, y, transp, dest))
348 		return;
349 
350 	drawPacked(sprBuf, width, height, x, y, transp, dest);
351 }
352 
drawPackedSprite(const char * path,Surface & dest,int width)353 void Video::drawPackedSprite(const char *path, Surface &dest, int width) {
354 	int32 size;
355 	byte *data = _vm->_dataIO->getFile(path, size);
356 	if (!data) {
357 		warning("Video::drawPackedSprite(): Failed to open sprite \"%s\"", path);
358 		return;
359 	}
360 
361 	drawPackedSprite(data, width, dest.getHeight(), 0, 0, 0, dest);
362 	delete[] data;
363 }
364 
setPalElem(int16 index,char red,char green,char blue,int16 unused,int16 vidMode)365 void Video::setPalElem(int16 index, char red, char green, char blue,
366 		int16 unused, int16 vidMode) {
367 	byte pal[3];
368 
369 	_vm->validateVideoMode(vidMode);
370 
371 	_vm->_global->_redPalette[index] = red;
372 	_vm->_global->_greenPalette[index] = green;
373 	_vm->_global->_bluePalette[index] = blue;
374 	setPalColor(pal, red, green, blue);
375 
376 	if (_vm->getPixelFormat().bytesPerPixel == 1)
377 		g_system->getPaletteManager()->setPalette(pal, index, 1);
378 }
379 
setPalette(PalDesc * palDesc)380 void Video::setPalette(PalDesc *palDesc) {
381 	byte pal[768];
382 	int16 numcolors;
383 
384 	_vm->validateVideoMode(_vm->_global->_videoMode);
385 
386 	numcolors = _vm->_global->_setAllPalette ? 256 : 16;
387 	for (int i = 0; i < numcolors; i++)
388 		setPalColor(pal + i * 3, palDesc->vgaPal[i]);
389 
390 	if (_vm->getPixelFormat().bytesPerPixel == 1)
391 		g_system->getPaletteManager()->setPalette(pal, 0, numcolors);
392 }
393 
setFullPalette(PalDesc * palDesc)394 void Video::setFullPalette(PalDesc *palDesc) {
395 	if (_vm->_global->_setAllPalette) {
396 		byte pal[768];
397 		Color *colors = palDesc->vgaPal;
398 
399 		for (int i = 0; i < 256; i++) {
400 			_vm->_global->_redPalette[i] = colors[i].red;
401 			_vm->_global->_greenPalette[i] = colors[i].green;
402 			_vm->_global->_bluePalette[i] = colors[i].blue;
403 			setPalColor(pal + i * 3, colors[i]);
404 		}
405 
406 		if (_vm->getPixelFormat().bytesPerPixel == 1)
407 			g_system->getPaletteManager()->setPalette(pal, 0, 256);
408 	} else
409 		Video::setPalette(palDesc);
410 }
411 
setPalette(Color * palette)412 void Video::setPalette(Color *palette) {
413 	Color *palBak;
414 	bool setAllPalBak;
415 
416 	palBak = _vm->_global->_pPaletteDesc->vgaPal;
417 	setAllPalBak = _vm->_global->_setAllPalette;
418 
419 	_vm->_global->_pPaletteDesc->vgaPal = palette;
420 	_vm->_global->_setAllPalette = true;
421 	setFullPalette(_vm->_global->_pPaletteDesc);
422 
423 	_vm->_global->_setAllPalette = setAllPalBak;
424 	_vm->_global->_pPaletteDesc->vgaPal = palBak;
425 }
426 
dirtyRectsClear()427 void Video::dirtyRectsClear() {
428 	_dirtyRects.clear();
429 	_dirtyAll = false;
430 }
431 
dirtyRectsAll()432 void Video::dirtyRectsAll() {
433 	_dirtyRects.clear();
434 	_dirtyAll = true;
435 }
436 
dirtyRectsAdd(int16 left,int16 top,int16 right,int16 bottom)437 void Video::dirtyRectsAdd(int16 left, int16 top, int16 right, int16 bottom) {
438 	if (_dirtyAll)
439 		return;
440 
441 	_dirtyRects.push_back(Common::Rect(left, top, right + 1, bottom + 1));
442 }
443 
dirtyRectsApply(int left,int top,int width,int height,int x,int y)444 void Video::dirtyRectsApply(int left, int top, int width, int height, int x, int y) {
445 	if (_dirtyAll) {
446 		_vm->_global->_primarySurfDesc->blitToScreen(left, top, left + width - 1, top + height - 1, x, y);
447 		return;
448 	}
449 
450 	int right  = left + width;
451 	int bottom = top  + height;
452 
453 	Common::List<Common::Rect>::const_iterator it;
454 	for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
455 		int l = MAX<int>(left  , it->left);
456 		int t = MAX<int>(top   , it->top);
457 		int r = MIN<int>(right , it->right);
458 		int b = MIN<int>(bottom, it->bottom);
459 
460 		if ((r <= l) || (b <= t))
461 			continue;
462 
463 		_vm->_global->_primarySurfDesc->blitToScreen(l, t, r - 1, b - 1, x + (l - left), y + (t - top));
464 	}
465 }
466 
467 } // End of namespace Gob
468