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 * Additional copyright for this file:
8 * Copyright (C) 1994-1998 Revolution Software Ltd.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25 #include "common/endian.h"
26
27 #include "sword2/sword2.h"
28 #include "sword2/defs.h"
29 #include "sword2/screen.h"
30
31 namespace Sword2 {
32
33 /**
34 * This function takes a sprite and creates a mirror image of it.
35 * @param dst destination buffer
36 * @param src source buffer
37 * @param w width of the sprite
38 * @param h height of the sprite
39 */
40
mirrorSprite(byte * dst,byte * src,int16 w,int16 h)41 void Screen::mirrorSprite(byte *dst, byte *src, int16 w, int16 h) {
42 for (int y = 0; y < h; y++) {
43 for (int x = 0; x < w; x++) {
44 *dst++ = *(src + w - x - 1);
45 }
46 src += w;
47 }
48 }
49
50 /**
51 * This function takes a compressed frame of a sprite with up to 256 colors
52 * and decompresses it.
53 * @param dst destination buffer
54 * @param src source buffer
55 * @param decompSize the expected size of the decompressed sprite
56 */
57
decompressRLE256(byte * dst,byte * src,int32 decompSize)58 int32 Screen::decompressRLE256(byte *dst, byte *src, int32 decompSize) {
59 // PARAMETERS:
60 // source points to the start of the sprite data for input
61 // decompSize gives size of decompressed data in bytes
62 // dest points to start of destination buffer for decompressed
63 // data
64
65 byte headerByte; // block header byte
66 byte *endDest = dst + decompSize; // pointer to byte after end of decomp buffer
67 int32 rv;
68
69 while (1) {
70 // FLAT block
71 // read FLAT block header & increment 'scan' to first pixel
72 // of block
73 headerByte = *src++;
74
75 // if this isn't a zero-length block
76 if (headerByte) {
77 if (dst + headerByte > endDest) {
78 rv = 1;
79 break;
80 }
81
82 // set the next 'headerByte' pixels to the next color
83 // at 'source'
84 memset(dst, *src, headerByte);
85
86 // increment destination pointer to just after this
87 // block
88 dst += headerByte;
89
90 // increment source pointer to just after this color
91 src++;
92
93 // if we've decompressed all of the data
94 if (dst == endDest) {
95 rv = 0; // return "OK"
96 break;
97 }
98 }
99
100 // RAW block
101 // read RAW block header & increment 'scan' to first pixel of
102 // block
103 headerByte = *src++;
104
105 // if this isn't a zero-length block
106 if (headerByte) {
107 if (dst + headerByte > endDest) {
108 rv = 1;
109 break;
110 }
111
112 // copy the next 'headerByte' pixels from source to
113 // destination
114 memcpy(dst, src, headerByte);
115
116 // increment destination pointer to just after this
117 // block
118 dst += headerByte;
119
120 // increment source pointer to just after this block
121 src += headerByte;
122
123 // if we've decompressed all of the data
124 if (dst == endDest) {
125 rv = 0; // return "OK"
126 break;
127 }
128 }
129 }
130
131 return rv;
132 }
133
134 /**
135 * Unwinds a run of 16-color data into 256-color palette data.
136 */
137
unwindRaw16(byte * dst,byte * src,uint16 blockSize,byte * colTable)138 void Screen::unwindRaw16(byte *dst, byte *src, uint16 blockSize, byte *colTable) {
139 // for each pair of pixels
140 while (blockSize > 1) {
141
142 if (Sword2Engine::isPsx()) {
143 // 1st color = number in table at position given by upper
144 // nibble of source byte
145 *dst++ = colTable[(*src) & 0x0f];
146
147 // 2nd color = number in table at position given by lower
148 // nibble of source byte
149 *dst++ = colTable[(*src) >> 4];
150 } else {
151 *dst++ = colTable[(*src) >> 4];
152 *dst++ = colTable[(*src) & 0x0f];
153 }
154
155
156 // point to next source byte
157 src++;
158
159 // decrement count of how many pixels left to read
160 blockSize -= 2;
161 }
162
163 // if there's a final odd pixel
164 if (blockSize) {
165 // color = number in table at position given by upper nibble
166 // of source byte
167 *dst++ = colTable[(*src) >> 4];
168 }
169 }
170
171 /**
172 * This function takes a compressed frame of a sprite (with up to 16 colors)
173 * and decompresses it.
174 * @param dst destination buffer
175 * @param src source buffer
176 * @param decompSize the expected size of the uncompressed sprite
177 * @param colTable mapping from the 16 encoded colors to the current palette
178 */
179
decompressRLE16(byte * dst,byte * src,int32 decompSize,byte * colTable)180 int32 Screen::decompressRLE16(byte *dst, byte *src, int32 decompSize, byte *colTable) {
181 byte headerByte; // block header byte
182 byte *endDest = dst + decompSize; // pointer to byte after end of decomp buffer
183 int32 rv;
184
185 while (1) {
186 // FLAT block
187 // read FLAT block header & increment 'scan' to first pixel
188 // of block
189 headerByte = *src++;
190
191 // if this isn't a zero-length block
192 if (headerByte) {
193 if (dst + headerByte > endDest) {
194 rv = 1;
195 break;
196 }
197
198 // set the next 'headerByte' pixels to the next
199 // color at 'source'
200 memset(dst, *src, headerByte);
201
202 // increment destination pointer to just after this
203 // block
204 dst += headerByte;
205
206 // increment source pointer to just after this color
207 src++;
208
209 // if we've decompressed all of the data
210 if (dst == endDest) {
211 rv = 0; // return "OK"
212 break;
213 }
214 }
215
216 // RAW block
217 // read RAW block header & increment 'scan' to first pixel of
218 // block
219 headerByte = *src++;
220
221 // if this isn't a zero-length block
222 if (headerByte) {
223 if (dst + headerByte > endDest) {
224 rv = 1;
225 break;
226 }
227
228 // copy the next 'headerByte' pixels from source to
229 // destination (NB. 2 pixels per byte)
230 unwindRaw16(dst, src, headerByte, colTable);
231
232 // increment destination pointer to just after this
233 // block
234 dst += headerByte;
235
236 // increment source pointer to just after this block
237 // (NB. headerByte gives pixels, so /2 for bytes)
238 src += (headerByte + 1) / 2;
239
240 // if we've decompressed all of the data
241 if (dst >= endDest) {
242 rv = 0; // return "OK"
243 break;
244 }
245 }
246 }
247
248 return rv;
249 }
250
251 /**
252 * This function takes a compressed HIF image and decompresses it.
253 * Used for PSX version sprites.
254 * @param dst destination buffer
255 * @param src source buffer
256 * @param skipData if pointer != NULL, value of pointed var
257 * is set to number of bytes composing the compressed data.
258 */
259
decompressHIF(byte * src,byte * dst,uint32 * skipData)260 uint32 Screen::decompressHIF(byte *src, byte *dst, uint32 *skipData) {
261 uint32 decompSize = 0;
262 uint32 readByte = 0;
263
264 for (;;) { // Main loop
265 byte control_byte = *src++;
266 readByte++;
267 uint32 byte_count = 0;
268 while (byte_count < 8) {
269 if (control_byte & 0x80) {
270 uint16 info_word = READ_BE_UINT16(src); // Read the info word
271 src += 2;
272 readByte += 2;
273 if (info_word == 0xFFFF) { // Got 0xFFFF code, finished.
274 if (skipData != NULL) *(skipData) = readByte;
275 return decompSize;
276 }
277
278 int32 repeat_count = (info_word >> 12) + 2; // How many time data needs to be refetched
279 while (repeat_count >= 0) {
280 uint16 refetchData = (info_word & 0xFFF) + 1;
281 if (refetchData > decompSize) return 0; // We have a problem here...
282 uint8 *old_data_src = dst - refetchData;
283 *dst++ = *old_data_src;
284 decompSize++;
285 repeat_count--;
286 }
287 } else {
288 *dst++ = *src++;
289 readByte++;
290 decompSize++;
291 }
292 byte_count++;
293 control_byte <<= 1; // Shifting left the control code one bit
294 }
295 }
296 }
297
298 // Double line image to keep aspect ratio.
299 // Used in PSX version.
resizePsxSprite(byte * dst,byte * src,uint16 destW,uint16 destH)300 void Screen::resizePsxSprite(byte *dst, byte *src, uint16 destW, uint16 destH) {
301 for (int i = 0; i < destH / 2; i++) {
302 memcpy(dst + i * destW * 2, src + i * destW, destW);
303 memcpy(dst + i * destW * 2 + destW, src + i * destW, destW);
304 }
305 }
306
307 // Sprites wider than 254px in PSX version are divided
308 // into slices, this recomposes the image.
recomposePsxSprite(SpriteInfo * s)309 void Screen::recomposePsxSprite(SpriteInfo *s) {
310 if (!s)
311 return;
312
313 uint16 noStripes = (s->w / 254) + ((s->w % 254) ? 1 : 0);
314 uint16 lastStripeSize = (s->w % 254) ? s->w % 254 : 254;
315 byte *buffer = (byte *)malloc(s->w * s->h / 2);
316
317 memset(buffer, 0, s->w * s->h / 2);
318
319 for (int idx = 0; idx < noStripes; idx++) {
320 uint16 stripeSize = (idx == noStripes - 1) ? lastStripeSize : 254;
321 for (int line = 0; line < s->h / 2; line++) {
322 memcpy(buffer + idx * 254 + line * s->w, s->data, stripeSize);
323 s->data += stripeSize;
324 }
325 }
326
327 s->data = buffer;
328
329 }
330
331 // Recomposes sprites wider than 254 pixels but also
332 // compressed with HIF.
333 // Used in PSX version.
recomposeCompPsxSprite(SpriteInfo * s)334 void Screen::recomposeCompPsxSprite(SpriteInfo *s) {
335 if (!s)
336 return;
337
338 uint16 noStripes = (s->w / 254) + ((s->w % 254) ? 1 : 0);
339 uint16 lastStripeSize = (s->w % 254) ? s->w % 254 : 254;
340 byte *buffer = (byte *)malloc(s->w * s->h / 2);
341 byte *stripeBuffer = (byte *)malloc(254 * s->h);
342
343 memset(buffer, 0, s->w * s->h / 2);
344 uint32 skipData = 0;
345 uint32 compBytes = 0;
346
347 for (int idx = 0; idx < noStripes; idx++) {
348 uint16 stripeSize = (idx == noStripes - 1) ? lastStripeSize : 254;
349
350 decompressHIF((s->data) + skipData, stripeBuffer, &compBytes);
351 skipData += compBytes;
352
353 for (int line = 0; line < s->h / 2; line++) {
354 memcpy(buffer + idx * 254 + line * s->w, stripeBuffer + line * stripeSize, stripeSize);
355 }
356 }
357
358 free(stripeBuffer);
359 s->data = buffer;
360
361 }
362
363 /**
364 * Creates a sprite surface. Sprite surfaces are used by the in-game dialogs
365 * and for displaying cutscene subtitles, which makes them much easier to draw
366 * than standard sprites.
367 * @param s information about how to decode the sprite
368 * @param sprite the buffer that will be created to store the surface
369 * @return RD_OK, or an error code
370 */
371
createSurface(SpriteInfo * s,byte ** sprite)372 int32 Screen::createSurface(SpriteInfo *s, byte **sprite) {
373 *sprite = (byte *)malloc(s->w * s->h);
374 if (!*sprite)
375 return RDERR_OUTOFMEMORY;
376
377 // Surfaces are either uncompressed or RLE256-compressed. No need to
378 // test for anything else.
379
380 if (s->type & RDSPR_NOCOMPRESSION) {
381 memcpy(*sprite, s->data, s->w * s->h);
382 } else if (decompressRLE256(*sprite, s->data, s->w * s->h)) {
383 free(*sprite);
384 return RDERR_DECOMPRESSION;
385 }
386
387 return RD_OK;
388 }
389
390 /**
391 * Draws the sprite surface created earlier.
392 * @param s information about how to place the sprite
393 * @param surface pointer to the surface created earlier
394 * @param clipRect the clipping rectangle
395 */
396
drawSurface(SpriteInfo * s,byte * surface,Common::Rect * clipRect)397 void Screen::drawSurface(SpriteInfo *s, byte *surface, Common::Rect *clipRect) {
398 Common::Rect rd, rs;
399 uint16 x, y;
400 byte *src, *dst;
401
402 rs.left = 0;
403 rs.right = s->w;
404 rs.top = 0;
405 rs.bottom = s->h;
406
407 rd.left = s->x;
408 rd.right = rd.left + rs.right;
409 rd.top = s->y;
410 rd.bottom = rd.top + rs.bottom;
411
412 Common::Rect defClipRect(0, 0, _screenWide, _screenDeep);
413
414 if (!clipRect) {
415 clipRect = &defClipRect;
416 }
417
418 if (clipRect->left > rd.left) {
419 rs.left += (clipRect->left - rd.left);
420 rd.left = clipRect->left;
421 }
422
423 if (clipRect->top > rd.top) {
424 rs.top += (clipRect->top - rd.top);
425 rd.top = clipRect->top;
426 }
427
428 if (clipRect->right < rd.right) {
429 rd.right = clipRect->right;
430 }
431
432 if (clipRect->bottom < rd.bottom) {
433 rd.bottom = clipRect->bottom;
434 }
435
436 if (rd.width() <= 0 || rd.height() <= 0)
437 return;
438
439 src = surface + rs.top * s->w + rs.left;
440 dst = _buffer + _screenWide * rd.top + rd.left;
441
442 // Surfaces are always transparent.
443
444 for (y = 0; y < rd.height(); y++) {
445 for (x = 0; x < rd.width(); x++) {
446 if (src[x])
447 dst[x] = src[x];
448 }
449 src += s->w;
450 dst += _screenWide;
451 }
452
453 updateRect(&rd);
454 }
455
456 /**
457 * Destroys a surface.
458 */
459
deleteSurface(byte * surface)460 void Screen::deleteSurface(byte *surface) {
461 free(surface);
462 }
463
464 /**
465 * Draws a sprite onto the screen. The type of the sprite can be a combination
466 * of the following flags, some of which are mutually exclusive:
467 * RDSPR_DISPLAYALIGN The sprite is drawn relative to the top left corner
468 * of the screen
469 * RDSPR_FLIP The sprite is mirrored
470 * RDSPR_TRANS The sprite has a transparent color zero
471 * RDSPR_BLEND The sprite is translucent
472 * RDSPR_SHADOW The sprite is affected by the light mask. (Scaled
473 * sprites always are.)
474 * RDSPR_NOCOMPRESSION The sprite data is not compressed
475 * RDSPR_RLE16 The sprite data is a 16-color compressed sprite
476 * RDSPR_RLE256 The sprite data is a 256-color compressed sprite
477 * @param s all the information needed to draw the sprite
478 * @warning Sprites will only be drawn onto the background, not over menubar
479 * areas.
480 */
481
482 // FIXME: I'm sure this could be optimized. There's plenty of data copying and
483 // mallocing here.
484
drawSprite(SpriteInfo * s)485 int32 Screen::drawSprite(SpriteInfo *s) {
486 byte *src, *dst;
487 byte *sprite, *newSprite;
488 uint16 scale;
489 int16 i, j;
490 uint16 srcPitch;
491 bool freeSprite = false;
492 Common::Rect rd, rs;
493
494 // -----------------------------------------------------------------
495 // Decompression and mirroring
496 // -----------------------------------------------------------------
497 if (s->type & RDSPR_NOCOMPRESSION) {
498 if (Sword2Engine::isPsx()) { // PSX Uncompressed sprites
499 if (s->w > 254 && !s->isText) { // We need to recompose these frames
500 recomposePsxSprite(s);
501 }
502
503 // If the height is not an even value, fix it.
504 // Apparently it's a problem in the data of a few sprites
505 // of the PSX version. This should fix an evident problem
506 // in the foyer at the beginning of the game, where a line
507 // of pixels is missing near the stairs. But it should also
508 // fix a more subtle one in the glease gallery and in quaramonte
509 // police office.
510 if (s->h % 2)
511 s->h++;
512
513 freeSprite = true;
514 byte *tempBuf = (byte *)malloc(s->w * s->h * 2);
515 memset(tempBuf, 0, s->w * s->h * 2);
516 resizePsxSprite(tempBuf, s->data, s->w, s->h);
517
518 if (s->w > 254 && !s->isText) {
519 free(s->data);
520 }
521
522 sprite = tempBuf;
523 } else { // PC Uncompressed sprites
524 sprite = s->data;
525 }
526 } else {
527 freeSprite = true;
528
529 if ((s->type & 0xff00) == RDSPR_RLE16) {
530 if (Sword2Engine::isPsx()) { // PSX HIF16 sprites
531 uint32 decompData;
532 byte *tempBuf = (byte *)malloc(s->w * s->h);
533 memset(tempBuf, 0, s->w * s->h);
534
535 decompData = decompressHIF(s->data, tempBuf);
536
537 // Check that we correctly decompressed data
538 if (!decompData) {
539 free(tempBuf);
540
541 return RDERR_DECOMPRESSION;
542 }
543
544 s->w = (decompData / (s->h / 2)) * 2;
545 byte *tempBuf2 = (byte *)malloc(s->w * s->h * 10);
546 memset(tempBuf2, 0, s->w * s->h * 2);
547
548 unwindRaw16(tempBuf2, tempBuf, (s->w * (s->h / 2)), s->colorTable);
549 sprite = (byte *)malloc(s->w * s->h);
550
551 if (!sprite) {
552 free(tempBuf2);
553 free(tempBuf);
554 return RDERR_OUTOFMEMORY;
555 }
556
557 resizePsxSprite(sprite, tempBuf2, s->w, s->h);
558
559 free(tempBuf2);
560 free(tempBuf);
561 } else { // PC RLE16 sprites
562 sprite = (byte *)malloc(s->w * s->h);
563
564 if (!sprite)
565 return RDERR_OUTOFMEMORY;
566
567 if (decompressRLE16(sprite, s->data, s->w * s->h, s->colorTable)) {
568 free(sprite);
569 return RDERR_DECOMPRESSION;
570 }
571 }
572 } else {
573 if (Sword2Engine::isPsx()) { // PSX HIF256 sprites
574 if (s->w > 255) {
575 sprite = (byte *)malloc(s->w * s->h);
576 recomposeCompPsxSprite(s);
577 resizePsxSprite(sprite, s->data, s->w, s->h);
578 free(s->data);
579 } else {
580 byte *tempBuf = (byte *)malloc(s->w * s->h);
581 uint32 decompData = decompressHIF(s->data, tempBuf);
582
583 // Check that we correctly decompressed data
584 if (!decompData) {
585 free(tempBuf);
586
587 return RDERR_DECOMPRESSION;
588 }
589
590 s->w = (decompData / (s->h / 2));
591 sprite = (byte *)malloc(s->w * s->h);
592
593 if (!sprite) {
594 free(tempBuf);
595
596 return RDERR_OUTOFMEMORY;
597 }
598
599 resizePsxSprite(sprite, tempBuf, s->w, s->h);
600 free(tempBuf);
601 }
602 } else { // PC RLE256 sprites
603 sprite = (byte *)malloc(s->w * s->h);
604
605 if (!sprite)
606 return RDERR_OUTOFMEMORY;
607
608 if (decompressRLE256(sprite, s->data, s->w * s->h)) {
609 free(sprite);
610 return RDERR_DECOMPRESSION;
611 }
612 }
613 }
614 }
615
616 if (s->type & RDSPR_FLIP) {
617 newSprite = (byte *)malloc(s->w * s->h);
618 if (newSprite == NULL) {
619 if (freeSprite)
620 free(sprite);
621 return RDERR_OUTOFMEMORY;
622 }
623 mirrorSprite(newSprite, sprite, s->w, s->h);
624 if (freeSprite)
625 free(sprite);
626 sprite = newSprite;
627 freeSprite = true;
628 }
629
630 // -----------------------------------------------------------------
631 // Positioning and clipping.
632 // -----------------------------------------------------------------
633
634 int16 spriteX = s->x;
635 int16 spriteY = s->y;
636
637 if (!(s->type & RDSPR_DISPLAYALIGN)) {
638 spriteX += _parallaxScrollX;
639 spriteY += _parallaxScrollY;
640 }
641
642 spriteY += MENUDEEP;
643
644 // A scale factor 0 or 256 means don't scale. Why do they use two
645 // different values to mean the same thing? Normalize it here for
646 // convenience.
647
648 scale = (s->scale == 0) ? 256 : s->scale;
649
650 rs.top = 0;
651 rs.left = 0;
652
653 if (scale != 256) {
654 rs.right = s->scaledWidth;
655 rs.bottom = s->scaledHeight;
656 srcPitch = s->scaledWidth;
657 } else {
658 rs.right = s->w;
659 rs.bottom = s->h;
660 srcPitch = s->w;
661 }
662
663 rd.top = spriteY;
664 rd.left = spriteX;
665
666 if (!(s->type & RDSPR_DISPLAYALIGN)) {
667 rd.top -= _scrollY;
668 rd.left -= _scrollX;
669 }
670
671 rd.right = rd.left + rs.right;
672 rd.bottom = rd.top + rs.bottom;
673
674 // Check if the sprite would end up completely outside the screen.
675
676 if (rd.left > RENDERWIDE || rd.top > RENDERDEEP + MENUDEEP || rd.right < 0 || rd.bottom < MENUDEEP) {
677 if (freeSprite)
678 free(sprite);
679 return RD_OK;
680 }
681
682 if (rd.top < MENUDEEP) {
683 rs.top = MENUDEEP - rd.top;
684 rd.top = MENUDEEP;
685 }
686 if (rd.bottom > RENDERDEEP + MENUDEEP) {
687 rd.bottom = RENDERDEEP + MENUDEEP;
688 rs.bottom = rs.top + (rd.bottom - rd.top);
689 }
690 if (rd.left < 0) {
691 rs.left = -rd.left;
692 rd.left = 0;
693 }
694 if (rd.right > RENDERWIDE) {
695 rd.right = RENDERWIDE;
696 rs.right = rs.left + (rd.right - rd.left);
697 }
698
699 // -----------------------------------------------------------------
700 // Scaling
701 // -----------------------------------------------------------------
702
703 if (scale != 256) {
704 if (s->scaledWidth > SCALE_MAXWIDTH || s->scaledHeight > SCALE_MAXHEIGHT) {
705 if (freeSprite)
706 free(sprite);
707 return RDERR_NOTIMPLEMENTED;
708 }
709
710 newSprite = (byte *)malloc(s->scaledWidth * s->scaledHeight);
711 if (newSprite == NULL) {
712 if (freeSprite)
713 free(sprite);
714 return RDERR_OUTOFMEMORY;
715 }
716
717 // We cannot use good scaling for PSX version, as we are missing
718 // some required data.
719 if (_renderCaps & RDBLTFX_EDGEBLEND && !Sword2Engine::isPsx())
720 scaleImageGood(newSprite, s->scaledWidth, s->scaledWidth, s->scaledHeight, sprite, s->w, s->w, s->h, _buffer, rd.left, rd.top);
721 else
722 scaleImageFast(newSprite, s->scaledWidth, s->scaledWidth, s->scaledHeight, sprite, s->w, s->w, s->h);
723
724 if (freeSprite)
725 free(sprite);
726 sprite = newSprite;
727 freeSprite = true;
728 }
729
730 // -----------------------------------------------------------------
731 // Light masking
732 // -----------------------------------------------------------------
733
734 // The light mask is an optional layer that covers the entire room
735 // and which is used to simulate light and shadows. Scaled sprites
736 // (actors, presumably) are always affected.
737 // Light masking makes use of palette match table, so it's unavailable
738 // in PSX version.
739
740 if ((_renderCaps & RDBLTFX_SHADOWBLEND) && _lightMask && (scale != 256 || ((s->type & RDSPR_SHADOW) && !Sword2Engine::isPsx()) )) {
741 byte *lightMap;
742
743 // Make sure that we never apply the shadow to the original
744 // resource data. This could only ever happen in the
745 // RDSPR_NOCOMPRESSION case.
746
747 if (!freeSprite) {
748 newSprite = (byte *)malloc(s->w * s->h);
749 memcpy(newSprite, sprite, s->w * s->h);
750 sprite = newSprite;
751 freeSprite = true;
752 }
753
754 src = sprite + rs.top * srcPitch + rs.left;
755 lightMap = _lightMask + (rd.top + _scrollY - MENUDEEP) * _locationWide + rd.left + _scrollX;
756
757 for (i = 0; i < rs.height(); i++) {
758 for (j = 0; j < rs.width(); j++) {
759 if (src[j] && lightMap[j]) {
760 uint8 r = ((32 - lightMap[j]) * _palette[src[j] * 3 + 0]) >> 5;
761 uint8 g = ((32 - lightMap[j]) * _palette[src[j] * 3 + 1]) >> 5;
762 uint8 b = ((32 - lightMap[j]) * _palette[src[j] * 3 + 2]) >> 5;
763 src[j] = quickMatch(r, g, b);
764 }
765 }
766 src += srcPitch;
767 lightMap += _locationWide;
768 }
769 }
770
771 // -----------------------------------------------------------------
772 // Drawing
773 // -----------------------------------------------------------------
774
775 src = sprite + rs.top * srcPitch + rs.left;
776 dst = _buffer + _screenWide * rd.top + rd.left;
777
778 if (s->type & RDSPR_BLEND) {
779 // The original code had two different blending cases. One for
780 // s->blend & 0x01 and one for s->blend & 0x02. However, the
781 // only values that actually appear in the cluster files are
782 // 0, 513 and 1025 so the s->blend & 0x02 case was never used.
783 // Which is just as well since that code made no sense to me.
784
785 // TODO: In PSX version, blending is done through hardware transparency.
786 // The only correct way to simulate this would be using 16-bit mode.
787 // As this is not yet available for this engine, fake transparency is used
788 // as placeholder.
789 if (!(_renderCaps & RDBLTFX_SPRITEBLEND) || Sword2Engine::isPsx()) {
790 for (i = 0; i < rs.height(); i++) {
791 for (j = 0; j < rs.width(); j++) {
792 if (src[j] && ((i & 1) == (j & 1)))
793 dst[j] = src[j];
794 }
795 src += srcPitch;
796 dst += _screenWide;
797 }
798 } else {
799 uint8 n = s->blend >> 8;
800
801 for (i = 0; i < rs.height(); i++) {
802 for (j = 0; j < rs.width(); j++) {
803 if (src[j]) {
804 uint8 r1 = _palette[src[j] * 3 + 0];
805 uint8 g1 = _palette[src[j] * 3 + 1];
806 uint8 b1 = _palette[src[j] * 3 + 2];
807 uint8 r2 = _palette[dst[j] * 3 + 0];
808 uint8 g2 = _palette[dst[j] * 3 + 1];
809 uint8 b2 = _palette[dst[j] * 3 + 2];
810
811 uint8 r = (r1 * n + r2 * (8 - n)) >> 3;
812 uint8 g = (g1 * n + g2 * (8 - n)) >> 3;
813 uint8 b = (b1 * n + b2 * (8 - n)) >> 3;
814 dst[j] = quickMatch(r, g, b);
815 }
816 }
817 src += srcPitch;
818 dst += _screenWide;
819 }
820 }
821 } else {
822 if (s->type & RDSPR_TRANS) {
823 for (i = 0; i < rs.height(); i++) {
824 for (j = 0; j < rs.width(); j++) {
825 if (src[j])
826 dst[j] = src[j];
827 }
828 src += srcPitch;
829 dst += _screenWide;
830 }
831 } else {
832 for (i = 0; i < rs.height(); i++) {
833 memcpy(dst, src, rs.width());
834 src += srcPitch;
835 dst += _screenWide;
836 }
837 }
838 }
839
840 if (freeSprite)
841 free(sprite);
842
843 markAsDirty(rd.left, rd.top, rd.right - 1, rd.bottom - 1);
844 return RD_OK;
845 }
846
847 /**
848 * Opens the light masking sprite for a room.
849 */
850
openLightMask(SpriteInfo * s)851 int32 Screen::openLightMask(SpriteInfo *s) {
852 // FIXME: The light mask is only needed on higher graphics detail
853 // settings, so to save memory we could simply ignore it on lower
854 // settings. But then we need to figure out how to ensure that it
855 // is properly loaded if the user changes the settings in mid-game.
856
857 if (_lightMask)
858 return RDERR_NOTCLOSED;
859
860 _lightMask = (byte *)malloc(s->w * s->h);
861 if (!_lightMask)
862 return RDERR_OUTOFMEMORY;
863
864 if (s->data == NULL) // Check, as there's no mask in psx version
865 return RDERR_NOTOPEN;
866
867 if (decompressRLE256(_lightMask, s->data, s->w * s->h))
868 return RDERR_DECOMPRESSION;
869
870 return RD_OK;
871 }
872
873 /**
874 * Closes the light masking sprite for a room.
875 */
876
closeLightMask()877 int32 Screen::closeLightMask() {
878 if (!_lightMask)
879 return RDERR_NOTOPEN;
880
881 free(_lightMask);
882 _lightMask = NULL;
883 return RD_OK;
884 }
885
886 } // End of namespace Sword2
887