Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4680 right-hear 1
#include "fitz.h"
2
#include "muxps.h"
3
 
4
#define MAX_STOPS 256
5
 
6
enum { SPREAD_PAD, SPREAD_REPEAT, SPREAD_REFLECT };
7
 
8
/*
9
 * Parse a list of GradientStop elements.
10
 * Fill the offset and color arrays, and
11
 * return the number of stops parsed.
12
 */
13
 
14
struct stop
15
{
16
	float offset;
17
	float r, g, b, a;
18
};
19
 
20
static int cmp_stop(const void *a, const void *b)
21
{
22
	const struct stop *astop = a;
23
	const struct stop *bstop = b;
24
	float diff = astop->offset - bstop->offset;
25
	if (diff < 0)
26
		return -1;
27
	if (diff > 0)
28
		return 1;
29
	return 0;
30
}
31
 
32
static inline float lerp(float a, float b, float x)
33
{
34
	return a + (b - a) * x;
35
}
36
 
37
static int
38
xps_parse_gradient_stops(xps_context *ctx, char *base_uri, xml_element *node,
39
	struct stop *stops, int maxcount)
40
{
41
	fz_colorspace *colorspace;
42
	float sample[8];
43
	float rgb[3];
44
	int before, after;
45
	int count;
46
	int i;
47
 
48
	/* We may have to insert 2 extra stops when postprocessing */
49
	maxcount -= 2;
50
 
51
	count = 0;
52
	while (node && count < maxcount)
53
	{
54
		if (!strcmp(xml_tag(node), "GradientStop"))
55
		{
56
			char *offset = xml_att(node, "Offset");
57
			char *color = xml_att(node, "Color");
58
			if (offset && color)
59
			{
60
				stops[count].offset = fz_atof(offset);
61
 
62
				xps_parse_color(ctx, base_uri, color, &colorspace, sample);
63
 
64
				fz_convert_color(colorspace, sample + 1, fz_device_rgb, rgb);
65
 
66
				stops[count].r = rgb[0];
67
				stops[count].g = rgb[1];
68
				stops[count].b = rgb[2];
69
				stops[count].a = sample[0];
70
 
71
				count ++;
72
			}
73
		}
74
		node = xml_next(node);
75
	}
76
 
77
	if (count == 0)
78
	{
79
		fz_warn("gradient brush has no gradient stops");
80
		stops[0].offset = 0;
81
		stops[0].r = 0;
82
		stops[0].g = 0;
83
		stops[0].b = 0;
84
		stops[0].a = 1;
85
		stops[1].offset = 1;
86
		stops[1].r = 1;
87
		stops[1].g = 1;
88
		stops[1].b = 1;
89
		stops[1].a = 1;
90
		return 2;
91
	}
92
 
93
	if (count == maxcount)
94
		fz_warn("gradient brush exceeded maximum number of gradient stops");
95
 
96
	/* Postprocess to make sure the range of offsets is 0.0 to 1.0 */
97
 
98
	qsort(stops, count, sizeof(struct stop), cmp_stop);
99
 
100
	before = -1;
101
	after = -1;
102
 
103
	for (i = 0; i < count; i++)
104
	{
105
		if (stops[i].offset < 0)
106
			before = i;
107
		if (stops[i].offset > 1)
108
		{
109
			after = i;
110
			break;
111
		}
112
	}
113
 
114
	/* Remove all stops < 0 except the largest one */
115
	if (before > 0)
116
	{
117
		memmove(stops, stops + before, (count - before) * sizeof(struct stop));
118
		count -= before;
119
	}
120
 
121
	/* Remove all stops > 1 except the smallest one */
122
	if (after >= 0)
123
		count = after + 1;
124
 
125
	/* Expand single stop to 0 .. 1 */
126
	if (count == 1)
127
	{
128
		stops[1] = stops[0];
129
		stops[0].offset = 0;
130
		stops[1].offset = 1;
131
		return 2;
132
	}
133
 
134
	/* First stop < 0 -- interpolate value to 0 */
135
	if (stops[0].offset < 0)
136
	{
137
		float d = -stops[0].offset / (stops[1].offset - stops[0].offset);
138
		stops[0].offset = 0;
139
		stops[0].r = lerp(stops[0].r, stops[1].r, d);
140
		stops[0].g = lerp(stops[0].g, stops[1].g, d);
141
		stops[0].b = lerp(stops[0].b, stops[1].b, d);
142
		stops[0].a = lerp(stops[0].a, stops[1].a, d);
143
	}
144
 
145
	/* Last stop > 1 -- interpolate value to 1 */
146
	if (stops[count-1].offset > 1)
147
	{
148
		float d = (1 - stops[count-2].offset) / (stops[count-1].offset - stops[count-2].offset);
149
		stops[count-1].offset = 1;
150
		stops[0].r = lerp(stops[count-2].r, stops[count-1].r, d);
151
		stops[0].g = lerp(stops[count-2].g, stops[count-1].g, d);
152
		stops[0].b = lerp(stops[count-2].b, stops[count-1].b, d);
153
		stops[0].a = lerp(stops[count-2].a, stops[count-1].a, d);
154
	}
155
 
156
	/* First stop > 0 -- insert a duplicate at 0 */
157
	if (stops[0].offset > 0)
158
	{
159
		memmove(stops + 1, stops, count * sizeof(struct stop));
160
		stops[0] = stops[1];
161
		stops[0].offset = 0;
162
		count++;
163
	}
164
 
165
	/* Last stop < 1 -- insert a duplicate at 1 */
166
	if (stops[count-1].offset < 1)
167
	{
168
		stops[count] = stops[count-1];
169
		stops[count].offset = 1;
170
		count++;
171
	}
172
 
173
	return count;
174
}
175
 
176
static void
177
xps_sample_gradient_stops(fz_shade *shade, struct stop *stops, int count)
178
{
179
	float offset, d;
180
	int i, k;
181
 
182
	k = 0;
183
	for (i = 0; i < 256; i++)
184
	{
185
		offset = i / 255.0f;
186
		while (k + 1 < count && offset > stops[k+1].offset)
187
			k++;
188
 
189
		d = (offset - stops[k].offset) / (stops[k+1].offset - stops[k].offset);
190
 
191
		shade->function[i][0] = lerp(stops[k].r, stops[k+1].r, d);
192
		shade->function[i][1] = lerp(stops[k].g, stops[k+1].g, d);
193
		shade->function[i][2] = lerp(stops[k].b, stops[k+1].b, d);
194
		shade->function[i][3] = lerp(stops[k].a, stops[k+1].a, d);
195
	}
196
}
197
 
198
/*
199
 * Radial gradients map more or less to Radial shadings.
200
 * The inner circle is always a point.
201
 * The outer circle is actually an ellipse,
202
 * mess with the transform to squash the circle into the right aspect.
203
 */
204
 
205
static void
206
xps_draw_one_radial_gradient(xps_context *ctx, fz_matrix ctm,
207
	struct stop *stops, int count,
208
	int extend,
209
	float x0, float y0, float r0,
210
	float x1, float y1, float r1)
211
{
212
	fz_shade *shade;
213
 
214
	/* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */
215
	shade = fz_malloc(sizeof(fz_shade));
216
	shade->refs = 1;
217
	shade->colorspace = fz_device_rgb;
218
	shade->bbox = fz_infinite_rect;
219
	shade->matrix = fz_identity;
220
	shade->use_background = 0;
221
	shade->use_function = 1;
222
	shade->type = FZ_RADIAL;
223
	shade->extend[0] = extend;
224
	shade->extend[1] = extend;
225
 
226
	xps_sample_gradient_stops(shade, stops, count);
227
 
228
	shade->mesh_len = 6;
229
	shade->mesh_cap = 6;
230
	shade->mesh = fz_calloc(shade->mesh_cap, sizeof(float));
231
	shade->mesh[0] = x0;
232
	shade->mesh[1] = y0;
233
	shade->mesh[2] = r0;
234
	shade->mesh[3] = x1;
235
	shade->mesh[4] = y1;
236
	shade->mesh[5] = r1;
237
 
238
	fz_fill_shade(ctx->dev, shade, ctm, 1);
239
 
240
	fz_drop_shade(shade);
241
}
242
 
243
/*
244
 * Linear gradients map to Axial shadings.
245
 */
246
 
247
static void
248
xps_draw_one_linear_gradient(xps_context *ctx, fz_matrix ctm,
249
	struct stop *stops, int count,
250
	int extend,
251
	float x0, float y0, float x1, float y1)
252
{
253
	fz_shade *shade;
254
 
255
	/* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */
256
	shade = fz_malloc(sizeof(fz_shade));
257
	shade->refs = 1;
258
	shade->colorspace = fz_device_rgb;
259
	shade->bbox = fz_infinite_rect;
260
	shade->matrix = fz_identity;
261
	shade->use_background = 0;
262
	shade->use_function = 1;
263
	shade->type = FZ_LINEAR;
264
	shade->extend[0] = extend;
265
	shade->extend[1] = extend;
266
 
267
	xps_sample_gradient_stops(shade, stops, count);
268
 
269
	shade->mesh_len = 6;
270
	shade->mesh_cap = 6;
271
	shade->mesh = fz_calloc(shade->mesh_cap, sizeof(float));
272
	shade->mesh[0] = x0;
273
	shade->mesh[1] = y0;
274
	shade->mesh[2] = 0;
275
	shade->mesh[3] = x1;
276
	shade->mesh[4] = y1;
277
	shade->mesh[5] = 0;
278
 
279
	fz_fill_shade(ctx->dev, shade, ctm, 1);
280
 
281
	fz_drop_shade(shade);
282
}
283
 
284
/*
285
 * We need to loop and create many shading objects to account
286
 * for the Repeat and Reflect SpreadMethods.
287
 * I'm not smart enough to calculate this analytically
288
 * so we iterate and check each object until we
289
 * reach a reasonable limit for infinite cases.
290
 */
291
 
292
static inline float point_inside_circle(float px, float py, float x, float y, float r)
293
{
294
	float dx = px - x;
295
	float dy = py - y;
296
	return dx * dx + dy * dy <= r * r;
297
}
298
 
299
static void
300
xps_draw_radial_gradient(xps_context *ctx, fz_matrix ctm,
301
	struct stop *stops, int count,
302
	xml_element *root, int spread)
303
{
304
	float x0, y0, r0;
305
	float x1, y1, r1;
306
	float xrad = 1;
307
	float yrad = 1;
308
	float invscale;
309
 
310
	char *center_att = xml_att(root, "Center");
311
	char *origin_att = xml_att(root, "GradientOrigin");
312
	char *radius_x_att = xml_att(root, "RadiusX");
313
	char *radius_y_att = xml_att(root, "RadiusY");
314
 
315
	if (origin_att)
316
		sscanf(origin_att, "%g,%g", &x0, &y0);
317
	if (center_att)
318
		sscanf(center_att, "%g,%g", &x1, &y1);
319
	if (radius_x_att)
320
		xrad = fz_atof(radius_x_att);
321
	if (radius_y_att)
322
		yrad = fz_atof(radius_y_att);
323
 
324
	/* scale the ctm to make ellipses */
325
	ctm = fz_concat(fz_scale(1, yrad / xrad), ctm);
326
 
327
	invscale = xrad / yrad;
328
	y0 = y0 * invscale;
329
	y1 = y1 * invscale;
330
 
331
	r0 = 0;
332
	r1 = xrad;
333
 
334
	xps_draw_one_radial_gradient(ctx, ctm, stops, count, 1, x0, y0, r0, x1, y1, r1);
335
}
336
 
337
/*
338
 * Calculate how many iterations are needed to cover
339
 * the bounding box.
340
 */
341
 
342
static void
343
xps_draw_linear_gradient(xps_context *ctx, fz_matrix ctm,
344
	struct stop *stops, int count,
345
	xml_element *root, int spread)
346
{
347
	float x0, y0, x1, y1;
348
 
349
	char *start_point_att = xml_att(root, "StartPoint");
350
	char *end_point_att = xml_att(root, "EndPoint");
351
 
352
	x0 = y0 = 0;
353
	x1 = y1 = 1;
354
 
355
	if (start_point_att)
356
		sscanf(start_point_att, "%g,%g", &x0, &y0);
357
	if (end_point_att)
358
		sscanf(end_point_att, "%g,%g", &x1, &y1);
359
 
360
	xps_draw_one_linear_gradient(ctx, ctm, stops, count, 1, x0, y0, x1, y1);
361
}
362
 
363
/*
364
 * Parse XML tag and attributes for a gradient brush, create color/opacity
365
 * function objects and call gradient drawing primitives.
366
 */
367
 
368
static void
369
xps_parse_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area,
370
	char *base_uri, xps_resource *dict, xml_element *root,
371
	void (*draw)(xps_context *, fz_matrix, struct stop *, int, xml_element *, int))
372
{
373
	xml_element *node;
374
 
375
	char *opacity_att;
376
	char *interpolation_att;
377
	char *spread_att;
378
	char *mapping_att;
379
	char *transform_att;
380
 
381
	xml_element *transform_tag = NULL;
382
	xml_element *stop_tag = NULL;
383
 
384
	struct stop stop_list[MAX_STOPS];
385
	int stop_count;
386
	fz_matrix transform;
387
	int spread_method;
388
 
389
	opacity_att = xml_att(root, "Opacity");
390
	interpolation_att = xml_att(root, "ColorInterpolationMode");
391
	spread_att = xml_att(root, "SpreadMethod");
392
	mapping_att = xml_att(root, "MappingMode");
393
	transform_att = xml_att(root, "Transform");
394
 
395
	for (node = xml_down(root); node; node = xml_next(node))
396
	{
397
		if (!strcmp(xml_tag(node), "LinearGradientBrush.Transform"))
398
			transform_tag = xml_down(node);
399
		if (!strcmp(xml_tag(node), "RadialGradientBrush.Transform"))
400
			transform_tag = xml_down(node);
401
		if (!strcmp(xml_tag(node), "LinearGradientBrush.GradientStops"))
402
			stop_tag = xml_down(node);
403
		if (!strcmp(xml_tag(node), "RadialGradientBrush.GradientStops"))
404
			stop_tag = xml_down(node);
405
	}
406
 
407
	xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
408
 
409
	spread_method = SPREAD_PAD;
410
	if (spread_att)
411
	{
412
		if (!strcmp(spread_att, "Pad"))
413
			spread_method = SPREAD_PAD;
414
		if (!strcmp(spread_att, "Reflect"))
415
			spread_method = SPREAD_REFLECT;
416
		if (!strcmp(spread_att, "Repeat"))
417
			spread_method = SPREAD_REPEAT;
418
	}
419
 
420
	transform = fz_identity;
421
	if (transform_att)
422
		xps_parse_render_transform(ctx, transform_att, &transform);
423
	if (transform_tag)
424
		xps_parse_matrix_transform(ctx, transform_tag, &transform);
425
	ctm = fz_concat(transform, ctm);
426
 
427
	if (!stop_tag) {
428
		fz_warn("missing gradient stops tag");
429
		return;
430
	}
431
 
432
	stop_count = xps_parse_gradient_stops(ctx, base_uri, stop_tag, stop_list, MAX_STOPS);
433
	if (stop_count == 0)
434
	{
435
		fz_warn("no gradient stops found");
436
		return;
437
	}
438
 
439
	xps_begin_opacity(ctx, ctm, area, base_uri, dict, opacity_att, NULL);
440
 
441
	draw(ctx, ctm, stop_list, stop_count, root, spread_method);
442
 
443
	xps_end_opacity(ctx, base_uri, dict, opacity_att, NULL);
444
}
445
 
446
void
447
xps_parse_linear_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area,
448
	char *base_uri, xps_resource *dict, xml_element *root)
449
{
450
	xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_linear_gradient);
451
}
452
 
453
void
454
xps_parse_radial_gradient_brush(xps_context *ctx, fz_matrix ctm, fz_rect area,
455
	char *base_uri, xps_resource *dict, xml_element *root)
456
{
457
	xps_parse_gradient_brush(ctx, ctm, area, base_uri, dict, root, xps_draw_radial_gradient);
458
}