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