]> rtime.felk.cvut.cz Git - hornmich/skoda-qr-demo.git/blob - QRScanner/mobile/jni/xps/xps-gradient.c
Add MuPDF native source codes
[hornmich/skoda-qr-demo.git] / QRScanner / mobile / jni / xps / xps-gradient.c
1 #include "mupdf/xps.h"
2
3 #define MAX_STOPS 256
4
5 enum { SPREAD_PAD, SPREAD_REPEAT, SPREAD_REFLECT };
6
7 /*
8  * Parse a list of GradientStop elements.
9  * Fill the offset and color arrays, and
10  * return the number of stops parsed.
11  */
12
13 struct stop
14 {
15         float offset;
16         float r, g, b, a;
17         int index;
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 astop->index - bstop->index;
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_document *doc, char *base_uri, fz_xml *node,
39         struct stop *stops, int maxcount)
40 {
41         fz_colorspace *colorspace;
42         float sample[FZ_MAX_COLORS];
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 (fz_xml_is_tag(node, "GradientStop"))
55                 {
56                         char *offset = fz_xml_att(node, "Offset");
57                         char *color = fz_xml_att(node, "Color");
58                         if (offset && color)
59                         {
60                                 stops[count].offset = fz_atof(offset);
61                                 stops[count].index = count;
62
63                                 xps_parse_color(doc, base_uri, color, &colorspace, sample);
64
65                                 fz_convert_color(doc->ctx, fz_device_rgb(doc->ctx), rgb, colorspace, sample + 1);
66
67                                 stops[count].r = rgb[0];
68                                 stops[count].g = rgb[1];
69                                 stops[count].b = rgb[2];
70                                 stops[count].a = sample[0];
71
72                                 count ++;
73                         }
74                 }
75                 node = fz_xml_next(node);
76         }
77
78         if (count == 0)
79         {
80                 fz_warn(doc->ctx, "gradient brush has no gradient stops");
81                 stops[0].offset = 0;
82                 stops[0].r = 0;
83                 stops[0].g = 0;
84                 stops[0].b = 0;
85                 stops[0].a = 1;
86                 stops[1].offset = 1;
87                 stops[1].r = 1;
88                 stops[1].g = 1;
89                 stops[1].b = 1;
90                 stops[1].a = 1;
91                 return 2;
92         }
93
94         if (count == maxcount)
95                 fz_warn(doc->ctx, "gradient brush exceeded maximum number of gradient stops");
96
97         /* Postprocess to make sure the range of offsets is 0.0 to 1.0 */
98
99         qsort(stops, count, sizeof(struct stop), cmp_stop);
100
101         before = -1;
102         after = -1;
103
104         for (i = 0; i < count; i++)
105         {
106                 if (stops[i].offset < 0)
107                         before = i;
108                 if (stops[i].offset > 1)
109                 {
110                         after = i;
111                         break;
112                 }
113         }
114
115         /* Remove all stops < 0 except the largest one */
116         if (before > 0)
117         {
118                 memmove(stops, stops + before, (count - before) * sizeof(struct stop));
119                 count -= before;
120         }
121
122         /* Remove all stops > 1 except the smallest one */
123         if (after >= 0)
124                 count = after + 1;
125
126         /* Expand single stop to 0 .. 1 */
127         if (count == 1)
128         {
129                 stops[1] = stops[0];
130                 stops[0].offset = 0;
131                 stops[1].offset = 1;
132                 return 2;
133         }
134
135         /* First stop < 0 -- interpolate value to 0 */
136         if (stops[0].offset < 0)
137         {
138                 float d = -stops[0].offset / (stops[1].offset - stops[0].offset);
139                 stops[0].offset = 0;
140                 stops[0].r = lerp(stops[0].r, stops[1].r, d);
141                 stops[0].g = lerp(stops[0].g, stops[1].g, d);
142                 stops[0].b = lerp(stops[0].b, stops[1].b, d);
143                 stops[0].a = lerp(stops[0].a, stops[1].a, d);
144         }
145
146         /* Last stop > 1 -- interpolate value to 1 */
147         if (stops[count-1].offset > 1)
148         {
149                 float d = (1 - stops[count-2].offset) / (stops[count-1].offset - stops[count-2].offset);
150                 stops[count-1].offset = 1;
151                 stops[count-1].r = lerp(stops[count-2].r, stops[count-1].r, d);
152                 stops[count-1].g = lerp(stops[count-2].g, stops[count-1].g, d);
153                 stops[count-1].b = lerp(stops[count-2].b, stops[count-1].b, d);
154                 stops[count-1].a = lerp(stops[count-2].a, stops[count-1].a, d);
155         }
156
157         /* First stop > 0 -- insert a duplicate at 0 */
158         if (stops[0].offset > 0)
159         {
160                 memmove(stops + 1, stops, count * sizeof(struct stop));
161                 stops[0] = stops[1];
162                 stops[0].offset = 0;
163                 count++;
164         }
165
166         /* Last stop < 1 -- insert a duplicate at 1 */
167         if (stops[count-1].offset < 1)
168         {
169                 stops[count] = stops[count-1];
170                 stops[count].offset = 1;
171                 count++;
172         }
173
174         return count;
175 }
176
177 static void
178 xps_sample_gradient_stops(fz_shade *shade, struct stop *stops, int count)
179 {
180         float offset, d;
181         int i, k;
182
183         k = 0;
184         for (i = 0; i < 256; i++)
185         {
186                 offset = i / 255.0f;
187                 while (k + 1 < count && offset > stops[k+1].offset)
188                         k++;
189
190                 d = (offset - stops[k].offset) / (stops[k+1].offset - stops[k].offset);
191
192                 shade->function[i][0] = lerp(stops[k].r, stops[k+1].r, d);
193                 shade->function[i][1] = lerp(stops[k].g, stops[k+1].g, d);
194                 shade->function[i][2] = lerp(stops[k].b, stops[k+1].b, d);
195                 shade->function[i][3] = lerp(stops[k].a, stops[k+1].a, d);
196         }
197 }
198
199 /*
200  * Radial gradients map more or less to Radial shadings.
201  * The inner circle is always a point.
202  * The outer circle is actually an ellipse,
203  * mess with the transform to squash the circle into the right aspect.
204  */
205
206 static void
207 xps_draw_one_radial_gradient(xps_document *doc, const fz_matrix *ctm,
208         struct stop *stops, int count,
209         int extend,
210         float x0, float y0, float r0,
211         float x1, float y1, float r1)
212 {
213         fz_shade *shade;
214
215         /* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */
216         shade = fz_malloc_struct(doc->ctx, fz_shade);
217         FZ_INIT_STORABLE(shade, 1, fz_free_shade_imp);
218         shade->colorspace = fz_device_rgb(doc->ctx);
219         shade->bbox = fz_infinite_rect;
220         shade->matrix = fz_identity;
221         shade->use_background = 0;
222         shade->use_function = 1;
223         shade->type = FZ_RADIAL;
224         shade->u.l_or_r.extend[0] = extend;
225         shade->u.l_or_r.extend[1] = extend;
226
227         xps_sample_gradient_stops(shade, stops, count);
228
229         shade->u.l_or_r.coords[0][0] = x0;
230         shade->u.l_or_r.coords[0][1] = y0;
231         shade->u.l_or_r.coords[0][2] = r0;
232         shade->u.l_or_r.coords[1][0] = x1;
233         shade->u.l_or_r.coords[1][1] = y1;
234         shade->u.l_or_r.coords[1][2] = r1;
235
236         fz_fill_shade(doc->dev, shade, ctm, 1);
237
238         fz_drop_shade(doc->ctx, shade);
239 }
240
241 /*
242  * Linear gradients.
243  */
244
245 static void
246 xps_draw_one_linear_gradient(xps_document *doc, const fz_matrix *ctm,
247         struct stop *stops, int count,
248         int extend,
249         float x0, float y0, float x1, float y1)
250 {
251         fz_shade *shade;
252
253         /* TODO: this (and the stuff in pdf_shade) should move to res_shade.c */
254         shade = fz_malloc_struct(doc->ctx, fz_shade);
255         FZ_INIT_STORABLE(shade, 1, fz_free_shade_imp);
256         shade->colorspace = fz_device_rgb(doc->ctx);
257         shade->bbox = fz_infinite_rect;
258         shade->matrix = fz_identity;
259         shade->use_background = 0;
260         shade->use_function = 1;
261         shade->type = FZ_LINEAR;
262         shade->u.l_or_r.extend[0] = extend;
263         shade->u.l_or_r.extend[1] = extend;
264
265         xps_sample_gradient_stops(shade, stops, count);
266
267         shade->u.l_or_r.coords[0][0] = x0;
268         shade->u.l_or_r.coords[0][1] = y0;
269         shade->u.l_or_r.coords[0][2] = 0;
270         shade->u.l_or_r.coords[1][0] = x1;
271         shade->u.l_or_r.coords[1][1] = y1;
272         shade->u.l_or_r.coords[1][2] = 0;
273
274         fz_fill_shade(doc->dev, shade, ctm, doc->opacity[doc->opacity_top]);
275
276         fz_drop_shade(doc->ctx, shade);
277 }
278
279 /*
280  * We need to loop and create many shading objects to account
281  * for the Repeat and Reflect SpreadMethods.
282  * I'm not smart enough to calculate this analytically
283  * so we iterate and check each object until we
284  * reach a reasonable limit for infinite cases.
285  */
286
287 static inline float point_inside_circle(float px, float py, float x, float y, float r)
288 {
289         float dx = px - x;
290         float dy = py - y;
291         return dx * dx + dy * dy <= r * r;
292 }
293
294 static void
295 xps_draw_radial_gradient(xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
296         struct stop *stops, int count,
297         fz_xml *root, int spread)
298 {
299         float x0, y0, r0;
300         float x1, y1, r1;
301         float xrad = 1;
302         float yrad = 1;
303         float invscale;
304         int i, ma = 1;
305         fz_matrix local_ctm = *ctm;
306         fz_matrix inv;
307         fz_rect local_area = *area;
308
309         char *center_att = fz_xml_att(root, "Center");
310         char *origin_att = fz_xml_att(root, "GradientOrigin");
311         char *radius_x_att = fz_xml_att(root, "RadiusX");
312         char *radius_y_att = fz_xml_att(root, "RadiusY");
313
314         x0 = y0 = 0.0;
315         x1 = y1 = 1.0;
316         xrad = 1.0;
317         yrad = 1.0;
318
319         if (origin_att)
320                 xps_parse_point(origin_att, &x0, &y0);
321         if (center_att)
322                 xps_parse_point(center_att, &x1, &y1);
323         if (radius_x_att)
324                 xrad = fz_atof(radius_x_att);
325         if (radius_y_att)
326                 yrad = fz_atof(radius_y_att);
327
328         xrad = fz_max(0.01f, xrad);
329         yrad = fz_max(0.01f, yrad);
330
331         /* scale the ctm to make ellipses */
332         if (fz_abs(xrad) > FLT_EPSILON)
333         {
334                 fz_pre_scale(&local_ctm, 1, yrad/xrad);
335         }
336
337         if (yrad != 0.0)
338         {
339                 invscale = xrad / yrad;
340                 y0 = y0 * invscale;
341                 y1 = y1 * invscale;
342         }
343
344         r0 = 0;
345         r1 = xrad;
346
347         fz_transform_rect(&local_area, fz_invert_matrix(&inv, &local_ctm));
348         ma = fz_maxi(ma, ceilf(hypotf(local_area.x0 - x0, local_area.y0 - y0) / xrad));
349         ma = fz_maxi(ma, ceilf(hypotf(local_area.x1 - x0, local_area.y0 - y0) / xrad));
350         ma = fz_maxi(ma, ceilf(hypotf(local_area.x0 - x0, local_area.y1 - y0) / xrad));
351         ma = fz_maxi(ma, ceilf(hypotf(local_area.x1 - x0, local_area.y1 - y0) / xrad));
352
353         if (spread == SPREAD_REPEAT)
354         {
355                 for (i = ma - 1; i >= 0; i--)
356                         xps_draw_one_radial_gradient(doc, &local_ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad);
357         }
358         else if (spread == SPREAD_REFLECT)
359         {
360                 if ((ma % 2) != 0)
361                         ma++;
362                 for (i = ma - 2; i >= 0; i -= 2)
363                 {
364                         xps_draw_one_radial_gradient(doc, &local_ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad);
365                         xps_draw_one_radial_gradient(doc, &local_ctm, stops, count, 0, x0, y0, r0 + (i + 2) * xrad, x1, y1, r1 + i * xrad);
366                 }
367         }
368         else
369         {
370                 xps_draw_one_radial_gradient(doc, &local_ctm, stops, count, 1, x0, y0, r0, x1, y1, r1);
371         }
372 }
373
374 /*
375  * Calculate how many iterations are needed to cover
376  * the bounding box.
377  */
378
379 static void
380 xps_draw_linear_gradient(xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
381         struct stop *stops, int count,
382         fz_xml *root, int spread)
383 {
384         float x0, y0, x1, y1;
385         int i, mi, ma;
386         float dx, dy, x, y, k;
387         fz_point p1, p2;
388         fz_matrix inv;
389         fz_rect local_area = *area;
390
391         char *start_point_att = fz_xml_att(root, "StartPoint");
392         char *end_point_att = fz_xml_att(root, "EndPoint");
393
394         x0 = y0 = 0;
395         x1 = y1 = 1;
396
397         if (start_point_att)
398                 xps_parse_point(start_point_att, &x0, &y0);
399         if (end_point_att)
400                 xps_parse_point(end_point_att, &x1, &y1);
401
402         p1.x = x0; p1.y = y0; p2.x = x1; p2.y = y1;
403         fz_transform_rect(&local_area, fz_invert_matrix(&inv, ctm));
404         x = p2.x - p1.x; y = p2.y - p1.y;
405         k = ((local_area.x0 - p1.x) * x + (local_area.y0 - p1.y) * y) / (x * x + y * y);
406         mi = floorf(k); ma = ceilf(k);
407         k = ((local_area.x1 - p1.x) * x + (local_area.y0 - p1.y) * y) / (x * x + y * y);
408         mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k));
409         k = ((local_area.x0 - p1.x) * x + (local_area.y1 - p1.y) * y) / (x * x + y * y);
410         mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k));
411         k = ((local_area.x1 - p1.x) * x + (local_area.y1 - p1.y) * y) / (x * x + y * y);
412         mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k));
413         dx = x1 - x0; dy = y1 - y0;
414
415         if (spread == SPREAD_REPEAT)
416         {
417                 for (i = mi; i < ma; i++)
418                         xps_draw_one_linear_gradient(doc, ctm, stops, count, 0, x0 + i * dx, y0 + i * dy, x1 + i * dx, y1 + i * dy);
419         }
420         else if (spread == SPREAD_REFLECT)
421         {
422                 if ((mi % 2) != 0)
423                         mi--;
424                 for (i = mi; i < ma; i += 2)
425                 {
426                         xps_draw_one_linear_gradient(doc, ctm, stops, count, 0, x0 + i * dx, y0 + i * dy, x1 + i * dx, y1 + i * dy);
427                         xps_draw_one_linear_gradient(doc, ctm, stops, count, 0, x0 + (i + 2) * dx, y0 + (i + 2) * dy, x1 + i * dx, y1 + i * dy);
428                 }
429         }
430         else
431         {
432                 xps_draw_one_linear_gradient(doc, ctm, stops, count, 1, x0, y0, x1, y1);
433         }
434 }
435
436 /*
437  * Parse XML tag and attributes for a gradient brush, create color/opacity
438  * function objects and call gradient drawing primitives.
439  */
440
441 static void
442 xps_parse_gradient_brush(xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
443         char *base_uri, xps_resource *dict, fz_xml *root,
444         void (*draw)(xps_document *, const fz_matrix*, const fz_rect *, struct stop *, int, fz_xml *, int))
445 {
446         fz_xml *node;
447
448         char *opacity_att;
449         char *spread_att;
450         char *transform_att;
451
452         fz_xml *transform_tag = NULL;
453         fz_xml *stop_tag = NULL;
454
455         struct stop stop_list[MAX_STOPS];
456         int stop_count;
457         fz_matrix transform;
458         int spread_method;
459
460         opacity_att = fz_xml_att(root, "Opacity");
461         spread_att = fz_xml_att(root, "SpreadMethod");
462         transform_att = fz_xml_att(root, "Transform");
463
464         for (node = fz_xml_down(root); node; node = fz_xml_next(node))
465         {
466                 if (fz_xml_is_tag(node, "LinearGradientBrush.Transform"))
467                         transform_tag = fz_xml_down(node);
468                 if (fz_xml_is_tag(node, "RadialGradientBrush.Transform"))
469                         transform_tag = fz_xml_down(node);
470                 if (fz_xml_is_tag(node, "LinearGradientBrush.GradientStops"))
471                         stop_tag = fz_xml_down(node);
472                 if (fz_xml_is_tag(node, "RadialGradientBrush.GradientStops"))
473                         stop_tag = fz_xml_down(node);
474         }
475
476         xps_resolve_resource_reference(doc, dict, &transform_att, &transform_tag, NULL);
477
478         spread_method = SPREAD_PAD;
479         if (spread_att)
480         {
481                 if (!strcmp(spread_att, "Pad"))
482                         spread_method = SPREAD_PAD;
483                 if (!strcmp(spread_att, "Reflect"))
484                         spread_method = SPREAD_REFLECT;
485                 if (!strcmp(spread_att, "Repeat"))
486                         spread_method = SPREAD_REPEAT;
487         }
488
489         transform = fz_identity;
490         if (transform_att)
491                 xps_parse_render_transform(doc, transform_att, &transform);
492         if (transform_tag)
493                 xps_parse_matrix_transform(doc, transform_tag, &transform);
494         fz_concat(&transform, &transform, ctm);
495
496         if (!stop_tag) {
497                 fz_warn(doc->ctx, "missing gradient stops tag");
498                 return;
499         }
500
501         stop_count = xps_parse_gradient_stops(doc, base_uri, stop_tag, stop_list, MAX_STOPS);
502         if (stop_count == 0)
503         {
504                 fz_warn(doc->ctx, "no gradient stops found");
505                 return;
506         }
507
508         xps_begin_opacity(doc, &transform, area, base_uri, dict, opacity_att, NULL);
509
510         draw(doc, &transform, area, stop_list, stop_count, root, spread_method);
511
512         xps_end_opacity(doc, base_uri, dict, opacity_att, NULL);
513 }
514
515 void
516 xps_parse_linear_gradient_brush(xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
517         char *base_uri, xps_resource *dict, fz_xml *root)
518 {
519         xps_parse_gradient_brush(doc, ctm, area, base_uri, dict, root, xps_draw_linear_gradient);
520 }
521
522 void
523 xps_parse_radial_gradient_brush(xps_document *doc, const fz_matrix *ctm, const fz_rect *area,
524         char *base_uri, xps_resource *dict, fz_xml *root)
525 {
526         xps_parse_gradient_brush(doc, ctm, area, base_uri, dict, root, xps_draw_radial_gradient);
527 }