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