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 * This code contains portions of Libart_LGPL - library of basic graphic primitives
25 *
26 * Copyright (c) 1998 Raph Levien
27 *
28 * Licensed under GNU LGPL v2
29 *
30 */
31
32 /*
33 * This code contains portions of Swfdec
34 *
35 * Copyright (c) 2004-2006 David Schleef <ds@schleef.org>
36 *
37 * Licensed under GNU GPL v2
38 *
39 */
40
41 #include "sword25/gfx/image/art.h"
42 #include "sword25/gfx/image/vectorimage.h"
43 #include "graphics/colormasks.h"
44
45 namespace Sword25 {
46
art_rgb_fill_run1(byte * buf,byte r,byte g,byte b,int n)47 void art_rgb_fill_run1(byte *buf, byte r, byte g, byte b, int n) {
48 int i;
49
50 if (r == g && g == b && r == 255) {
51 memset(buf, g, n + n + n + n);
52 } else {
53 uint32 *alt = (uint32 *)buf;
54 uint32 color = Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(r, g, b, 0xff);
55
56 for (i = 0; i < n; i++)
57 *alt++ = color;
58 }
59 }
60
art_rgb_run_alpha1(byte * buf,byte r,byte g,byte b,int alpha,int n)61 void art_rgb_run_alpha1(byte *buf, byte r, byte g, byte b, int alpha, int n) {
62 int i;
63 int v;
64
65 for (i = 0; i < n; i++) {
66 #if defined(SCUMM_LITTLE_ENDIAN)
67 v = *buf;
68 *buf++ = MIN(v + alpha, 0xff);
69 v = *buf;
70 *buf++ = v + (((b - v) * alpha + 0x80) >> 8);
71 v = *buf;
72 *buf++ = v + (((g - v) * alpha + 0x80) >> 8);
73 v = *buf;
74 *buf++ = v + (((r - v) * alpha + 0x80) >> 8);
75 #else
76 v = *buf;
77 *buf++ = v + (((r - v) * alpha + 0x80) >> 8);
78 v = *buf;
79 *buf++ = v + (((g - v) * alpha + 0x80) >> 8);
80 v = *buf;
81 *buf++ = v + (((b - v) * alpha + 0x80) >> 8);
82 v = *buf;
83 *buf++ = MIN(v + alpha, 0xff);
84 #endif
85 }
86 }
87
88 typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData;
89
90 struct _ArtRgbSVPAlphaData {
91 int alphatab[256];
92 byte r, g, b, alpha;
93 byte *buf;
94 int rowstride;
95 int x0, x1;
96 };
97
art_rgb_svp_alpha_callback1(void * callback_data,int y,int start,ArtSVPRenderAAStep * steps,int n_steps)98 static void art_rgb_svp_alpha_callback1(void *callback_data, int y,
99 int start, ArtSVPRenderAAStep *steps, int n_steps) {
100 ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
101 byte *linebuf;
102 int run_x0, run_x1;
103 uint32 running_sum = start;
104 int x0, x1;
105 int k;
106 byte r, g, b;
107 int *alphatab;
108 int alpha;
109
110 linebuf = data->buf;
111 x0 = data->x0;
112 x1 = data->x1;
113
114 r = data->r;
115 g = data->g;
116 b = data->b;
117 alphatab = data->alphatab;
118
119 if (n_steps > 0) {
120 run_x1 = steps[0].x;
121 if (run_x1 > x0) {
122 alpha = (running_sum >> 16) & 0xff;
123 if (alpha)
124 art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
125 }
126
127 for (k = 0; k < n_steps - 1; k++) {
128 running_sum += steps[k].delta;
129 run_x0 = run_x1;
130 run_x1 = steps[k + 1].x;
131 if (run_x1 > run_x0) {
132 alpha = (running_sum >> 16) & 0xff;
133 if (alpha)
134 art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
135 }
136 }
137 running_sum += steps[k].delta;
138 if (x1 > run_x1) {
139 alpha = (running_sum >> 16) & 0xff;
140 if (alpha)
141 art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
142 }
143 } else {
144 alpha = (running_sum >> 16) & 0xff;
145 if (alpha)
146 art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
147 }
148
149 data->buf += data->rowstride;
150 }
151
art_rgb_svp_alpha_opaque_callback1(void * callback_data,int y,int start,ArtSVPRenderAAStep * steps,int n_steps)152 static void art_rgb_svp_alpha_opaque_callback1(void *callback_data, int y,
153 int start,
154 ArtSVPRenderAAStep *steps, int n_steps) {
155 ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
156 byte *linebuf;
157 int run_x0, run_x1;
158 uint32 running_sum = start;
159 int x0, x1;
160 int k;
161 byte r, g, b;
162 int *alphatab;
163 int alpha;
164
165 linebuf = data->buf;
166 x0 = data->x0;
167 x1 = data->x1;
168
169 r = data->r;
170 g = data->g;
171 b = data->b;
172 alphatab = data->alphatab;
173
174 if (n_steps > 0) {
175 run_x1 = steps[0].x;
176 if (run_x1 > x0) {
177 alpha = running_sum >> 16;
178 if (alpha) {
179 if (alpha >= 255)
180 art_rgb_fill_run1(linebuf, r, g, b, run_x1 - x0);
181 else
182 art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], run_x1 - x0);
183 }
184 }
185
186 for (k = 0; k < n_steps - 1; k++) {
187 running_sum += steps[k].delta;
188 run_x0 = run_x1;
189 run_x1 = steps[k + 1].x;
190 if (run_x1 > run_x0) {
191 alpha = running_sum >> 16;
192 if (alpha) {
193 if (alpha >= 255)
194 art_rgb_fill_run1(linebuf + (run_x0 - x0) * 4, r, g, b, run_x1 - run_x0);
195 else
196 art_rgb_run_alpha1(linebuf + (run_x0 - x0) * 4, r, g, b, alphatab[alpha], run_x1 - run_x0);
197 }
198 }
199 }
200 running_sum += steps[k].delta;
201 if (x1 > run_x1) {
202 alpha = running_sum >> 16;
203 if (alpha) {
204 if (alpha >= 255)
205 art_rgb_fill_run1(linebuf + (run_x1 - x0) * 4, r, g, b, x1 - run_x1);
206 else
207 art_rgb_run_alpha1(linebuf + (run_x1 - x0) * 4, r, g, b, alphatab[alpha], x1 - run_x1);
208 }
209 }
210 } else {
211 alpha = running_sum >> 16;
212 if (alpha) {
213 if (alpha >= 255)
214 art_rgb_fill_run1(linebuf, r, g, b, x1 - x0);
215 else
216 art_rgb_run_alpha1(linebuf, r, g, b, alphatab[alpha], x1 - x0);
217 }
218 }
219
220 data->buf += data->rowstride;
221 }
222
art_rgb_svp_alpha1(const ArtSVP * svp,int x0,int y0,int x1,int y1,uint32 color,byte * buf,int rowstride)223 void art_rgb_svp_alpha1(const ArtSVP *svp,
224 int x0, int y0, int x1, int y1,
225 uint32 color,
226 byte *buf, int rowstride) {
227 ArtRgbSVPAlphaData data;
228 byte r, g, b, alpha;
229 int i;
230 int a, da;
231
232 Graphics::colorToARGB<Graphics::ColorMasks<8888> >(color, alpha, r, g, b);
233
234 data.r = r;
235 data.g = g;
236 data.b = b;
237 data.alpha = alpha;
238
239 a = 0x8000;
240 da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
241
242 for (i = 0; i < 256; i++) {
243 data.alphatab[i] = a >> 16;
244 a += da;
245 }
246
247 data.buf = buf;
248 data.rowstride = rowstride;
249 data.x0 = x0;
250 data.x1 = x1;
251 if (alpha == 255)
252 art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback1, &data);
253 else
254 art_svp_render_aa(svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback1, &data);
255 }
256
art_vpath_len(ArtVpath * a)257 static int art_vpath_len(ArtVpath *a) {
258 int i = 0;
259 while (a[i].code != ART_END)
260 i++;
261 return i;
262 }
263
art_vpath_cat(ArtVpath * a,ArtVpath * b)264 ArtVpath *art_vpath_cat(ArtVpath *a, ArtVpath *b) {
265 ArtVpath *dest;
266 ArtVpath *p;
267 int len_a, len_b;
268
269 len_a = art_vpath_len(a);
270 len_b = art_vpath_len(b);
271 dest = art_new(ArtVpath, len_a + len_b + 1);
272 if (!dest)
273 error("[art_vpath_cat] Cannot allocate memory");
274
275 p = dest;
276
277 for (int i = 0; i < len_a; i++)
278 *p++ = *a++;
279 for (int i = 0; i <= len_b; i++)
280 *p++ = *b++;
281
282 return dest;
283 }
284
art_svp_make_convex(ArtSVP * svp)285 void art_svp_make_convex(ArtSVP *svp) {
286 int i;
287
288 if (svp->n_segs > 0 && svp->segs[0].dir == 0) {
289 for (i = 0; i < svp->n_segs; i++) {
290 svp->segs[i].dir = !svp->segs[i].dir;
291 }
292 }
293 }
294
art_vpath_reverse(ArtVpath * a)295 ArtVpath *art_vpath_reverse(ArtVpath *a) {
296 ArtVpath *dest;
297 ArtVpath it;
298 int len;
299 int state = 0;
300 int i;
301
302 len = art_vpath_len(a);
303 dest = art_new(ArtVpath, len + 1);
304 if (!dest)
305 error("[art_vpath_reverse] Cannot allocate memory");
306
307 for (i = 0; i < len; i++) {
308 it = a[len - i - 1];
309 if (state) {
310 it.code = ART_LINETO;
311 } else {
312 it.code = ART_MOVETO_OPEN;
313 state = 1;
314 }
315 if (a[len - i - 1].code == ART_MOVETO ||
316 a[len - i - 1].code == ART_MOVETO_OPEN) {
317 state = 0;
318 }
319 dest[i] = it;
320 }
321 dest[len] = a[len];
322
323 return dest;
324 }
325
art_vpath_reverse_free(ArtVpath * a)326 ArtVpath *art_vpath_reverse_free(ArtVpath *a) {
327 ArtVpath *dest;
328
329 dest = art_vpath_reverse(a);
330 free(a);
331
332 return dest;
333 }
334
drawBez(ArtBpath * bez1,ArtBpath * bez2,byte * buffer,int width,int height,int deltaX,int deltaY,double scaleX,double scaleY,double penWidth,unsigned int color)335 void drawBez(ArtBpath *bez1, ArtBpath *bez2, byte *buffer, int width, int height, int deltaX, int deltaY, double scaleX, double scaleY, double penWidth, unsigned int color) {
336 ArtVpath *vec = NULL;
337 ArtVpath *vec1 = NULL;
338 ArtVpath *vec2 = NULL;
339 ArtSVP *svp = NULL;
340
341 #if 0
342 const char *codes[] = {"ART_MOVETO", "ART_MOVETO_OPEN", "ART_CURVETO", "ART_LINETO", "ART_END"};
343 for (int i = 0;; i++) {
344 debugN(" bez[%d].code = %s;\n", i, codes[bez[i].code]);
345 if (bez[i].code == ART_END)
346 break;
347 if (bez[i].code == ART_CURVETO) {
348 debugN(" bez[%d].x1 = %f; bez[%d].y1 = %f;\n", i, bez[i].x1, i, bez[i].y1);
349 debugN(" bez[%d].x2 = %f; bez[%d].y2 = %f;\n", i, bez[i].x2, i, bez[i].y2);
350 }
351 debugN(" bez[%d].x3 = %f; bez[%d].y3 = %f;\n", i, bez[i].x3, i, bez[i].y3);
352 }
353
354 debugN(" drawBez(bez, buffer, 1.0, 1.0, %f, 0x%08x);\n", penWidth, color);
355 #endif
356
357 // HACK: Some frames have green bounding boxes drawn.
358 // Perhaps they were used by original game artist Umriss
359 // We skip them just like the original
360 if (bez2 == 0 && color == Graphics::ARGBToColor<Graphics::ColorMasks<8888> >(0xff, 0x00, 0xff, 0x00)) {
361 return;
362 }
363
364 vec1 = art_bez_path_to_vec(bez1, 0.5);
365 if (bez2 != 0) {
366 vec2 = art_bez_path_to_vec(bez2, 0.5);
367 vec2 = art_vpath_reverse_free(vec2);
368 vec = art_vpath_cat(vec1, vec2);
369
370 free(vec1);
371 free(vec2);
372 } else {
373 vec = vec1;
374 }
375
376 int size = art_vpath_len(vec);
377 ArtVpath *vect = art_new(ArtVpath, size + 1);
378 if (!vect)
379 error("[drawBez] Cannot allocate memory");
380
381 int k;
382 for (k = 0; k < size; k++) {
383 vect[k].code = vec[k].code;
384 vect[k].x = (vec[k].x - deltaX) * scaleX;
385 vect[k].y = (vec[k].y - deltaY) * scaleY;
386 }
387 vect[k].code = ART_END;
388
389 if (bez2 == 0) { // Line drawing
390 svp = art_svp_vpath_stroke(vect, ART_PATH_STROKE_JOIN_ROUND, ART_PATH_STROKE_CAP_ROUND, penWidth, 1.0, 0.5);
391 } else {
392 svp = art_svp_from_vpath(vect);
393 art_svp_make_convex(svp);
394 }
395
396 art_rgb_svp_alpha1(svp, 0, 0, width, height, color, buffer, width * 4);
397
398 free(vect);
399 art_svp_free(svp);
400 free(vec);
401 }
402
render(int width,int height)403 void VectorImage::render(int width, int height) {
404 double scaleX = (width == - 1) ? 1 : static_cast<double>(width) / static_cast<double>(getWidth());
405 double scaleY = (height == - 1) ? 1 : static_cast<double>(height) / static_cast<double>(getHeight());
406
407 debug(3, "VectorImage::render(%d, %d) %s", width, height, _fname.c_str());
408
409 if (_pixelData)
410 free(_pixelData);
411
412 _pixelData = (byte *)malloc(width * height * 4);
413 memset(_pixelData, 0, width * height * 4);
414
415 for (uint e = 0; e < _elements.size(); e++) {
416
417 //// Draw shapes
418 for (uint s = 0; s < _elements[e].getFillStyleCount(); s++) {
419 int fill0len = 0;
420 int fill1len = 0;
421
422 // Count vector sizes in order to minimize memory
423 // fragmentation
424 for (uint p = 0; p < _elements[e].getPathCount(); p++) {
425 if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1)
426 fill0len += _elements[e].getPathInfo(p).getVecLen();
427
428 if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1)
429 fill1len += _elements[e].getPathInfo(p).getVecLen();
430 }
431
432 // Now lump together vectors
433 ArtBpath *fill1 = art_new(ArtBpath, fill1len + 1);
434 ArtBpath *fill0 = art_new(ArtBpath, fill0len + 1);
435 ArtBpath *fill1pos = fill1;
436 ArtBpath *fill0pos = fill0;
437
438 for (uint p = 0; p < _elements[e].getPathCount(); p++) {
439 if (_elements[e].getPathInfo(p).getFillStyle0() == s + 1) {
440 for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
441 *fill0pos++ = _elements[e].getPathInfo(p).getVec()[i];
442 }
443
444 if (_elements[e].getPathInfo(p).getFillStyle1() == s + 1) {
445 for (int i = 0; i < _elements[e].getPathInfo(p).getVecLen(); i++)
446 *fill1pos++ = _elements[e].getPathInfo(p).getVec()[i];
447 }
448 }
449
450 // Close vectors
451 (*fill0pos).code = ART_END;
452 (*fill1pos).code = ART_END;
453
454 drawBez(fill1, fill0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, -1, _elements[e].getFillStyleColor(s));
455
456 free(fill0);
457 free(fill1);
458 }
459
460 //// Draw strokes
461 for (uint s = 0; s < _elements[e].getLineStyleCount(); s++) {
462 double penWidth = _elements[e].getLineStyleWidth(s);
463 penWidth *= sqrt(fabs(scaleX * scaleY));
464
465 for (uint p = 0; p < _elements[e].getPathCount(); p++) {
466 if (_elements[e].getPathInfo(p).getLineStyle() == s + 1) {
467 drawBez(_elements[e].getPathInfo(p).getVec(), 0, _pixelData, width, height, _boundingBox.left, _boundingBox.top, scaleX, scaleY, penWidth, _elements[e].getLineStyleColor(s));
468 }
469 }
470 }
471 }
472 }
473
474
475 } // End of namespace Sword25
476