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 "kyra/graphics/screen_v2.h"
24 
25 #include "common/endian.h"
26 
27 namespace Kyra {
28 
Screen_v2(KyraEngine_v1 * vm,OSystem * system,const ScreenDim * dimTable,const int dimTableSize)29 Screen_v2::Screen_v2(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, const int dimTableSize)
30 	: Screen(vm, system, dimTable, dimTableSize), _wsaFrameAnimBuffer(0) {
31 	_wsaFrameAnimBuffer = new uint8[1024];
32 	assert(_wsaFrameAnimBuffer);
33 }
34 
~Screen_v2()35 Screen_v2::~Screen_v2() {
36 	delete[] _wsaFrameAnimBuffer;
37 }
38 
generateOverlay(const Palette & pal,uint8 * buffer,int opColor,uint weight,int maxColor)39 uint8 *Screen_v2::generateOverlay(const Palette &pal, uint8 *buffer, int opColor, uint weight, int maxColor) {
40 	if (!buffer)
41 		return buffer;
42 
43 	weight = MIN<uint>(weight, 255) >> 1;
44 
45 	const byte opR = pal[opColor * 3 + 0];
46 	const byte opG = pal[opColor * 3 + 1];
47 	const byte opB = pal[opColor * 3 + 2];
48 
49 	uint8 *dst = buffer;
50 	*dst++ = 0;
51 
52 	int maxIndex = maxColor;
53 	if (maxIndex == -1) {
54 		if (_vm->game() == GI_LOL) {
55 			if (_use16ColorMode)
56 				maxIndex = 255;
57 			else
58 				maxIndex = 127;
59 		} else {
60 			maxIndex = 255;
61 		}
62 	}
63 
64 	for (int i = 1; i != 256; ++i) {
65 		const byte curR = pal[i * 3 + 0] - (((pal[i * 3 + 0] - opR) * weight) >> 7);
66 		const byte curG = pal[i * 3 + 1] - (((pal[i * 3 + 1] - opG) * weight) >> 7);
67 		const byte curB = pal[i * 3 + 2] - (((pal[i * 3 + 2] - opB) * weight) >> 7);
68 
69 		uint16 idxSum = _use16ColorMode ? 0xFFFF : 0x7FFF;
70 		byte index = opColor;
71 
72 		for (int curIdx = 1; curIdx <= maxIndex; ++curIdx) {
73 			if (!_use16ColorMode && i == curIdx)
74 				continue;
75 
76 			int diff = 0;
77 			uint16 sum = 0;
78 
79 			diff = pal[curIdx * 3 + 0] - curR;
80 			sum += diff * diff;
81 			diff = pal[curIdx * 3 + 1] - curG;
82 			sum += diff * diff;
83 			diff = pal[curIdx * 3 + 2] - curB;
84 			sum += diff * diff;
85 
86 			if (!sum) {
87 				index = curIdx;
88 				break;
89 			}
90 
91 			if (sum <= idxSum) {
92 				if (!_use16ColorMode || (curIdx == opColor || curIdx != i)) {
93 					idxSum = sum;
94 					index = curIdx;
95 				}
96 			}
97 		}
98 
99 		*dst++ = index;
100 	}
101 
102 	return buffer;
103 }
104 
applyOverlay(int x,int y,int w,int h,int pageNum,const uint8 * overlay)105 void Screen_v2::applyOverlay(int x, int y, int w, int h, int pageNum, const uint8 *overlay) {
106 	if (pageNum == 0 || pageNum == 1)
107 		addDirtyRect(x, y, w, h);
108 
109 	uint8 *dst = getPagePtr(pageNum) + y * 320 + x;
110 	while (h--) {
111 		for (int wi = 0; wi < w; ++wi) {
112 			uint8 index = *dst;
113 			*dst++ = overlay[index];
114 		}
115 		dst += 320 - w;
116 	}
117 }
118 
findLeastDifferentColor(const uint8 * paletteEntry,const Palette & pal,uint8 firstColor,uint16 numColors,bool skipSpecialColors)119 int Screen_v2::findLeastDifferentColor(const uint8 *paletteEntry, const Palette &pal, uint8 firstColor, uint16 numColors, bool skipSpecialColors) {
120 	int m = 0x7FFF;
121 	int r = 0x101;
122 
123 	for (int i = 0; i < numColors; i++) {
124 		if (skipSpecialColors && i >= 0xC0 && i <= 0xC3)
125 			continue;
126 
127 		int v = paletteEntry[0] - pal[(i + firstColor) * 3 + 0];
128 		int c = v * v;
129 		v = paletteEntry[1] - pal[(i + firstColor) * 3 + 1];
130 		c += (v * v);
131 		v = paletteEntry[2] - pal[(i + firstColor) * 3 + 2];
132 		c += (v * v);
133 
134 		if (c <= m) {
135 			m = c;
136 			r = i;
137 		}
138 	}
139 
140 	return r;
141 }
142 
getFadeParams(const Palette & pal,int delay,int & delayInc,int & diff)143 void Screen_v2::getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff) {
144 	int maxDiff = 0;
145 	diff = 0;
146 	for (int i = 0; i < pal.getNumColors() * 3; ++i) {
147 		diff = ABS(pal[i] - (*_screenPalette)[i]);
148 		maxDiff = MAX(maxDiff, diff);
149 	}
150 
151 	delayInc = delay << 8;
152 	if (maxDiff != 0) {
153 		delayInc /= maxDiff;
154 		delayInc = MIN(delayInc, 0x7FFF);
155 	}
156 
157 	delay = delayInc;
158 	for (diff = 1; diff <= maxDiff; ++diff) {
159 		if (delayInc >= 256)
160 			break;
161 		delayInc += delay;
162 	}
163 }
164 
timedPaletteFadeStep(uint8 * pal1,uint8 * pal2,uint32 elapsedTime,uint32 totalTime)165 bool Screen_v2::timedPaletteFadeStep(uint8 *pal1, uint8 *pal2, uint32 elapsedTime, uint32 totalTime) {
166 	Palette &p1 = getPalette(1);
167 
168 	bool res = false;
169 	for (int i = 0; i < p1.getNumColors() * 3; i++) {
170 		uint8 out = 0;
171 
172 		if (elapsedTime < totalTime) {
173 			int32 d = ((pal2[i] & 0x3F) - (pal1[i] & 0x3F));
174 			if (d)
175 				res = true;
176 
177 			int32 val = ((((d << 8) / (int32)totalTime) * (int32)elapsedTime) >> 8);
178 			out = ((pal1[i] & 0x3F) + (int8)val);
179 		} else {
180 			out = p1[i] = (pal2[i] & 0x3F);
181 			res = false;
182 		}
183 
184 		(*_internFadePalette)[i] = out;
185 	}
186 
187 	setScreenPalette(*_internFadePalette);
188 	updateScreen();
189 
190 	return res;
191 }
192 
getPtrToShape(const uint8 * shpFile,int shape)193 const uint8 *Screen_v2::getPtrToShape(const uint8 *shpFile, int shape) {
194 	uint16 shapes = READ_LE_UINT16(shpFile);
195 
196 	if (shapes <= shape)
197 		return 0;
198 
199 	uint32 offset = READ_LE_UINT32(shpFile + (shape << 2) + 2);
200 
201 	return shpFile + offset + 2;
202 }
203 
getPtrToShape(uint8 * shpFile,int shape)204 uint8 *Screen_v2::getPtrToShape(uint8 *shpFile, int shape) {
205 	uint16 shapes = READ_LE_UINT16(shpFile);
206 
207 	if (shapes <= shape)
208 		return 0;
209 
210 	uint32 offset = READ_LE_UINT32(shpFile + (shape << 2) + 2);
211 
212 	return shpFile + offset + 2;
213 }
214 
getShapeScaledWidth(const uint8 * shpFile,int scale)215 int Screen_v2::getShapeScaledWidth(const uint8 *shpFile, int scale) {
216 	if (!shpFile)
217 		return 0;
218 	int width = READ_LE_UINT16(shpFile + 3);
219 	return (width * scale) >> 8;
220 }
221 
getShapeScaledHeight(const uint8 * shpFile,int scale)222 int Screen_v2::getShapeScaledHeight(const uint8 *shpFile, int scale) {
223 	if (!shpFile)
224 		return 0;
225 	int height = shpFile[2];
226 	return (height * scale) >> 8;
227 }
228 
getShapeSize(const uint8 * shp)229 uint16 Screen_v2::getShapeSize(const uint8 *shp) {
230 	if (!shp)
231 		return 0;
232 	return READ_LE_UINT16(shp + 6);
233 }
234 
makeShapeCopy(const uint8 * src,int index)235 uint8 *Screen_v2::makeShapeCopy(const uint8 *src, int index) {
236 	const uint8 *shape = getPtrToShape(src, index);
237 	if (!shape)
238 		return 0;
239 
240 	int size = getShapeSize(shape);
241 
242 	uint8 *copy = new uint8[size];
243 	assert(copy);
244 	memcpy(copy, shape, size);
245 
246 	return copy;
247 }
248 
getLayer(int x,int y)249 int Screen_v2::getLayer(int x, int y) {
250 	if (x < 0)
251 		x = 0;
252 	else if (x >= 320)
253 		x = 319;
254 	if (y < 0)
255 		y = 0;
256 	else if (y >= 144)
257 		y = 143;
258 
259 	uint8 pixel = *(getCPagePtr(5) + y * 320 + x);
260 	pixel &= 0x7F;
261 	pixel >>= 3;
262 
263 	if (pixel < 1)
264 		pixel = 1;
265 	else if (pixel > 15)
266 		pixel = 15;
267 	return pixel;
268 }
269 
getRectSize(int w,int h)270 int Screen_v2::getRectSize(int w, int h) {
271 	if (w > 320 || h > 200)
272 		return 0;
273 	return w * h;
274 }
275 
setTextColorMap(const uint8 * cmap)276 void Screen_v2::setTextColorMap(const uint8 *cmap) {
277 	setTextColor(cmap, 0, 15);
278 }
279 
wsaFrameAnimationStep(int x1,int y1,int x2,int y2,int w1,int h1,int w2,int h2,int srcPage,int dstPage,int dim)280 void Screen_v2::wsaFrameAnimationStep(int x1, int y1, int x2, int y2,
281                                       int w1, int h1, int w2, int h2, int srcPage, int dstPage, int dim) {
282 
283 	if (!w1 || !h1 || !w2 || !h2)
284 		return;
285 
286 	ScreenDim cdm = *getScreenDim(dim);
287 	cdm.sx <<= 3;
288 	cdm.w <<= 3;
289 
290 	int na = 0, nb = 0, nc = w2;
291 
292 	if (!calcBounds(cdm.w, cdm.h, x2, y2, w2, h2, na, nb, nc))
293 		return;
294 
295 	const uint8 *src = getPagePtr(srcPage) + y1 * 320;
296 	uint8 *dst = getPagePtr(dstPage) + (y2 + cdm.sy) * 320;
297 
298 	int u = -1;
299 
300 	do {
301 		int t = (nb * h1) / h2;
302 		if (t != u) {
303 			u = t;
304 			const uint8 *s = src + x1 + t * 320;
305 			uint8 *dt = (uint8 *)_wsaFrameAnimBuffer;
306 
307 			t = w2 - w1;
308 			if (!t) {
309 				memcpy(dt, s, w2);
310 			} else if (t > 0) {
311 				if (w1 == 1) {
312 					memset(dt, *s, w2);
313 				} else {
314 					t = ((((((w2 - w1 + 1) & 0xFFFF) << 8) / w1) + 0x100) & 0xFFFF) << 8;
315 					int bp = 0;
316 					for (int i = 0; i < w1; i++) {
317 						int cnt = (t >> 16);
318 						bp += (t & 0xFFFF);
319 						if (bp > 0xFFFF) {
320 							bp -= 0xFFFF;
321 							cnt++;
322 						}
323 						memset(dt, *s++, cnt);
324 						dt += cnt;
325 					}
326 				}
327 			} else {
328 				if (w2 == 1) {
329 					*dt = *s;
330 				} else {
331 					t = (((((w1 - w2) & 0xFFFF) << 8) / w2) & 0xFFFF) << 8;
332 					int bp = 0;
333 					for (int i = 0; i < w2; i++) {
334 						*dt++ = *s++;
335 						bp += (t & 0xFFFF);
336 						if (bp > 0xFFFF) {
337 							bp -= 0xFFFF;
338 							s++;
339 						}
340 						s += (t >> 16);
341 					}
342 				}
343 			}
344 		}
345 		memcpy(dst + x2 + cdm.sx, _wsaFrameAnimBuffer + na, w2);
346 		dst += 320;
347 	} while (++nb < h2);
348 
349 	if (!dstPage)
350 		addDirtyRect(x2, y2, w2, h2);
351 }
352 
copyPageMemory(int srcPage,int srcPos,int dstPage,int dstPos,int numBytes)353 void Screen_v2::copyPageMemory(int srcPage, int srcPos, int dstPage, int dstPos, int numBytes) {
354 	const uint8 *src = getPagePtr(srcPage) + srcPos;
355 	uint8 *dst = getPagePtr(dstPage) + dstPos;
356 	memcpy(dst, src, numBytes);
357 }
358 
copyRegionEx(int srcPage,int srcW,int srcH,int dstPage,int dstX,int dstY,int dstW,int dstH,const ScreenDim * dim,bool flag)359 void Screen_v2::copyRegionEx(int srcPage, int srcW, int srcH, int dstPage, int dstX, int dstY, int dstW, int dstH, const ScreenDim *dim, bool flag) {
360 	int x0 = dim->sx << 3;
361 	int y0 = dim->sy;
362 	int w0 = dim->w << 3;
363 	int h0 = dim->h;
364 
365 	int x1 = dstX;
366 	int y1 = dstY;
367 	int w1 = dstW;
368 	int h1 = dstH;
369 
370 	int x2, y2, w2;
371 
372 	calcBounds(w0, h0, x1, y1, w1, h1, x2, y2, w2);
373 
374 	const uint8 *src = getPagePtr(srcPage) + (320 * srcH) + srcW;
375 	uint8 *dst = getPagePtr(dstPage) + 320 * (y0 + y1);
376 
377 	for (int y = 0; y < h1; y++) {
378 		const uint8 *s = src + x2;
379 		uint8 *d = dst + x0 + x1;
380 
381 		if (flag)
382 			d += (h1 >> 1);
383 
384 		for (int x = 0; x < w1; x++) {
385 			if (*s)
386 				*d = *s;
387 			s++;
388 			d++;
389 		}
390 		dst += 320;
391 		src += 320;
392 	}
393 }
394 
calcBounds(int w0,int h0,int & x1,int & y1,int & w1,int & h1,int & x2,int & y2,int & w2)395 bool Screen_v2::calcBounds(int w0, int h0, int &x1, int &y1, int &w1, int &h1, int &x2, int &y2, int &w2) {
396 	x2 = 0;
397 	y2 = 0;
398 	w2 = w1;
399 
400 	int t = x1 + w1;
401 	if (t < 1) {
402 		w1 = h1 = -1;
403 	} else {
404 		if (t <= x1) {
405 			x2 = w1 - t;
406 			w1 = t;
407 			x1 = 0;
408 		}
409 		t = w0 - x1;
410 		if (t < 1) {
411 			w1 = h1 = -1;
412 		} else {
413 			if (t <= w1) {
414 				w1 = t;
415 			}
416 			w2 -= w1;
417 			t = h1 + y1;
418 			if (t < 1) {
419 				w1 = h1 = -1;
420 			} else {
421 				if (t <= y1) {
422 					y2 = h1 - t;
423 					h1 = t;
424 					y1 = 0;
425 				}
426 				t = h0 - y1;
427 				if (t < 1) {
428 					w1 = h1 = -1;
429 				} else {
430 					if (t <= h1) {
431 						h1 = t;
432 					}
433 				}
434 			}
435 		}
436 	}
437 
438 	return w1 != -1;
439 }
440 
checkedPageUpdate(int srcPage,int dstPage)441 void Screen_v2::checkedPageUpdate(int srcPage, int dstPage) {
442 	const uint32 *src = (const uint32 *)getPagePtr(srcPage);
443 	uint32 *dst = (uint32 *)getPagePtr(dstPage);
444 	uint32 *page0 = (uint32 *)getPagePtr(0);
445 
446 	bool updated = false;
447 
448 	for (int y = 0; y < 200; ++y) {
449 		for (int x = 0; x < 80; ++x, ++src, ++dst, ++page0) {
450 			if (*src != *dst) {
451 				updated = true;
452 				*dst = *page0 = *src;
453 			}
454 		}
455 	}
456 
457 	if (updated)
458 		addDirtyRect(0, 0, 320, 200);
459 }
460 
461 } // End of namespace Kyra
462