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
24 #include "scumm/scumm.h"
25 #include "scumm/akos.h"
26 #include "scumm/bomp.h"
27 #include "scumm/util.h"
28
29
30 namespace Scumm {
31
32 static int32 setupBompScale(byte *scaling, int32 size, byte scale);
33
34 static void bompScaleFuncX(byte *line_buffer, byte *scaling_x_ptr, byte skip, int32 size);
35
36 static void bompApplyShadow0(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency, bool HE7Check);
37 static void bompApplyShadow1(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency);
38 static void bompApplyShadow3(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency);
39 static void bompApplyActorPalette(uint16 *actorPalette, byte *line_buffer, int32 size);
40
41
42
decompressBomp(byte * dst,const byte * src,int w,int h)43 void decompressBomp(byte *dst, const byte *src, int w, int h) {
44 assert(w > 0);
45 assert(h > 0);
46
47 do {
48 bompDecodeLine(dst, src + 2, w);
49 src += READ_LE_UINT16(src) + 2;
50 dst += w;
51 } while (--h);
52 }
53
bompDecodeLine(byte * dst,const byte * src,int len)54 void bompDecodeLine(byte *dst, const byte *src, int len) {
55 assert(len > 0);
56
57 int num;
58 byte code, color;
59
60 while (len > 0) {
61 code = *src++;
62 num = (code >> 1) + 1;
63 if (num > len)
64 num = len;
65 len -= num;
66 if (code & 1) {
67 color = *src++;
68 memset(dst, color, num);
69 } else {
70 memcpy(dst, src, num);
71 src += num;
72 }
73 dst += num;
74 }
75 }
76
bompDecodeLineReverse(byte * dst,const byte * src,int len)77 void bompDecodeLineReverse(byte *dst, const byte *src, int len) {
78 assert(len > 0);
79
80 dst += len;
81
82 int num;
83 byte code, color;
84
85 while (len > 0) {
86 code = *src++;
87 num = (code >> 1) + 1;
88 if (num > len)
89 num = len;
90 len -= num;
91 dst -= num;
92 if (code & 1) {
93 color = *src++;
94 memset(dst, color, num);
95 } else {
96 memcpy(dst, src, num);
97 src += num;
98 }
99 }
100 }
101
bompApplyMask(byte * line_buffer,byte * mask,byte maskbit,int32 size,byte transparency)102 void bompApplyMask(byte *line_buffer, byte *mask, byte maskbit, int32 size, byte transparency) {
103 while (1) {
104 do {
105 if (size-- == 0)
106 return;
107 if (*mask & maskbit) {
108 *line_buffer = transparency;
109 }
110 line_buffer++;
111 maskbit >>= 1;
112 } while (maskbit);
113 mask++;
114 maskbit = 128;
115 }
116 }
117
bompApplyShadow(int shadowMode,const byte * shadowPalette,const byte * line_buffer,byte * dst,int32 size,byte transparency,bool HE7Check)118 void bompApplyShadow(int shadowMode, const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency, bool HE7Check) {
119 assert(size > 0);
120 switch (shadowMode) {
121 case 0:
122 bompApplyShadow0(shadowPalette, line_buffer, dst, size, transparency, HE7Check);
123 break;
124 case 1:
125 bompApplyShadow1(shadowPalette, line_buffer, dst, size, transparency);
126 break;
127 case 3:
128 bompApplyShadow3(shadowPalette, line_buffer, dst, size, transparency);
129 break;
130 default:
131 error("Unknown shadow mode %d", shadowMode);
132 }
133 }
bompApplyShadow0(const byte * shadowPalette,const byte * line_buffer,byte * dst,int32 size,byte transparency,bool HE7Check)134 void bompApplyShadow0(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency, bool HE7Check) {
135 while (size-- > 0) {
136 byte tmp = *line_buffer++;
137 if (tmp != transparency) {
138 if (HE7Check)
139 *dst = shadowPalette[tmp];
140 else
141 *dst = tmp;
142 }
143 dst++;
144 }
145 }
146
bompApplyShadow1(const byte * shadowPalette,const byte * line_buffer,byte * dst,int32 size,byte transparency)147 void bompApplyShadow1(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency) {
148 while (size-- > 0) {
149 byte tmp = *line_buffer++;
150 if (tmp != transparency) {
151 if (tmp == 13) {
152 tmp = shadowPalette[*dst];
153 }
154 *dst = tmp;
155 }
156 dst++;
157 }
158 }
159
bompApplyShadow3(const byte * shadowPalette,const byte * line_buffer,byte * dst,int32 size,byte transparency)160 void bompApplyShadow3(const byte *shadowPalette, const byte *line_buffer, byte *dst, int32 size, byte transparency) {
161 while (size-- > 0) {
162 byte tmp = *line_buffer++;
163 if (tmp != transparency) {
164 if (tmp < 8) {
165 tmp = shadowPalette[*dst + (tmp << 8)];
166 }
167 *dst = tmp;
168 }
169 dst++;
170 }
171 }
172
bompApplyActorPalette(uint16 * actorPalette,byte * line_buffer,int32 size)173 void bompApplyActorPalette(uint16 *actorPalette, byte *line_buffer, int32 size) {
174 actorPalette[255] = 255;
175 while (size-- > 0) {
176 *line_buffer = actorPalette[*line_buffer];
177 line_buffer++;
178 }
179 }
180
bompScaleFuncX(byte * line_buffer,byte * scaling_x_ptr,byte skip,int32 size)181 void bompScaleFuncX(byte *line_buffer, byte *scaling_x_ptr, byte skip, int32 size) {
182 byte *line_ptr1 = line_buffer;
183 byte *line_ptr2 = line_buffer;
184
185 byte tmp = *scaling_x_ptr++;
186
187 while (size--) {
188 if ((skip & tmp) == 0) {
189 *line_ptr1++ = *line_ptr2;
190 }
191 line_ptr2++;
192 skip >>= 1;
193 if (skip == 0) {
194 skip = 128;
195 tmp = *scaling_x_ptr++;
196 }
197 }
198 }
199
drawBomp(const BompDrawData & bd)200 void drawBomp(const BompDrawData &bd) {
201 const byte *src;
202 byte *dst;
203 byte *mask = 0;
204 Common::Rect clip;
205 byte *scalingYPtr = 0;
206 byte skip_y_bits = 0x80;
207 byte skip_y_new = 0;
208 byte tmp;
209 byte bomp_scaling_x[64];
210 byte bomp_scaling_y[64];
211
212 if (bd.x < 0) {
213 clip.left = -bd.x;
214 } else {
215 clip.left = 0;
216 }
217
218 if (bd.y < 0) {
219 clip.top = -bd.y;
220 } else {
221 clip.top = 0;
222 }
223
224 clip.right = bd.srcwidth;
225 if (clip.right > bd.dst.w - bd.x) {
226 clip.right = bd.dst.w - bd.x;
227 }
228
229 clip.bottom = bd.srcheight;
230 if (clip.bottom > bd.dst.h - bd.y) {
231 clip.bottom = bd.dst.h - bd.y;
232 }
233
234 src = bd.src;
235 // FIXME: This gets passed a const destination Surface. Intuitively this
236 // should never get written to. But sadly it does... For now we simply
237 // cast the const qualifier away.
238 dst = (byte *)const_cast<void *>(bd.dst.getBasePtr((bd.x + clip.left), bd.y));
239
240 const byte maskbit = revBitMask((bd.x + clip.left) & 7);
241
242 // Mask against any additionally imposed mask
243 if (bd.maskPtr) {
244 mask = bd.maskPtr + (bd.y * bd.numStrips) + ((bd.x + clip.left) / 8);
245 }
246
247 // Setup vertical scaling
248 if (bd.scale_y != 255) {
249 int scaleBottom = setupBompScale(bomp_scaling_y, bd.srcheight, bd.scale_y);
250 scalingYPtr = bomp_scaling_y;
251
252 skip_y_new = *scalingYPtr++;
253 skip_y_bits = 0x80;
254
255 if (clip.bottom > scaleBottom) {
256 clip.bottom = scaleBottom;
257 }
258 }
259
260 // Setup horizontal scaling
261 if (bd.scale_x != 255) {
262 int scaleRight = setupBompScale(bomp_scaling_x, bd.srcwidth, bd.scale_x);
263
264 if (clip.right > scaleRight) {
265 clip.right = scaleRight;
266 }
267 }
268
269 const int width = clip.right - clip.left;
270
271 if (width <= 0)
272 return;
273
274 int pos_y = 0;
275 byte line_buffer[1024];
276
277 byte *line_ptr = line_buffer + clip.left;
278
279 // Loop over all lines
280 while (pos_y < clip.bottom) {
281 // Decode a single (bomp encoded) line, reversed if we are in mirror mode
282 if (bd.mirror)
283 bompDecodeLineReverse(line_buffer, src + 2, bd.srcwidth);
284 else
285 bompDecodeLine(line_buffer, src + 2, bd.srcwidth);
286 src += READ_LE_UINT16(src) + 2;
287
288 // If vertical scaling is enabled, do it
289 if (bd.scale_y != 255) {
290 // A bit set means we should skip this line...
291 tmp = skip_y_new & skip_y_bits;
292
293 // Advance the scale-skip bit mask, if it's 0, get the next scale-skip byte
294 skip_y_bits /= 2;
295 if (skip_y_bits == 0) {
296 skip_y_bits = 0x80;
297 skip_y_new = *scalingYPtr++;
298 }
299
300 // Skip the current line if the above check tells us to
301 if (tmp != 0)
302 continue;
303 }
304
305 // Perform horizontal scaling
306 if (bd.scale_x != 255) {
307 bompScaleFuncX(line_buffer, bomp_scaling_x, 0x80, bd.srcwidth);
308 }
309
310 // The first clip.top lines are to be clipped, i.e. not drawn
311 if (clip.top > 0) {
312 clip.top--;
313 } else {
314 // Replace the parts of the line which are masked with the transparency color
315 if (bd.maskPtr)
316 bompApplyMask(line_ptr, mask, maskbit, width, 255);
317
318 // Apply custom color map, if available
319 if (bd.actorPalette)
320 bompApplyActorPalette(bd.actorPalette, line_ptr, width);
321
322 // Finally, draw the decoded, scaled, masked and recolored line onto
323 // the target surface, using the specified shadow mode
324 bompApplyShadow(bd.shadowMode, bd.shadowPalette, line_ptr, dst, width, 255);
325 }
326
327 // Advance to the next line
328 pos_y++;
329 mask += bd.numStrips;
330 dst += bd.dst.pitch;
331 }
332 }
333
setupBompScale(byte * scaling,int32 size,byte scale)334 int32 setupBompScale(byte *scaling, int32 size, byte scale) {
335 static const int offsets[8] = { 3, 2, 1, 0, 7, 6, 5, 4 };
336 int32 count;
337 byte bitsCount = 0;
338
339 count = (256 - size / 2);
340 assert(0 <= count && count < 768);
341 const byte *scaleTable = bigCostumeScaleTable + count;
342
343 count = (size + 7) / 8;
344 while (count--) {
345 byte scaleMask = 0;
346 for (int i = 0; i < 8; i++) {
347 byte scaleTest = *(scaleTable + offsets[i]);
348 scaleMask <<= 1;
349 if (scale < scaleTest) {
350 scaleMask |= 1;
351 } else {
352 bitsCount++;
353 }
354 }
355 scaleTable += 8;
356
357 *scaling++ = scaleMask;
358 }
359 size &= 7;
360 if (size != 0) {
361 --scaling;
362 if ((*scaling & revBitMask(size)) == 0) {
363 *scaling |= revBitMask(size);
364 bitsCount--;
365 }
366 }
367
368 return bitsCount;
369 }
370
371 } // End of namespace Scumm
372