Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/source/xps/xps-gradient.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 // Copyright (C) 2004-2021 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 #include "mupdf/fitz.h" | |
| 24 #include "xps-imp.h" | |
| 25 | |
| 26 #include <string.h> | |
| 27 #include <math.h> | |
| 28 #include <float.h> | |
| 29 #include <stdlib.h> | |
| 30 | |
| 31 #define MAX_STOPS 256 | |
| 32 | |
| 33 enum { SPREAD_PAD, SPREAD_REPEAT, SPREAD_REFLECT }; | |
| 34 | |
| 35 /* | |
| 36 * Parse a list of GradientStop elements. | |
| 37 * Fill the offset and color arrays, and | |
| 38 * return the number of stops parsed. | |
| 39 */ | |
| 40 | |
| 41 struct stop | |
| 42 { | |
| 43 float offset; | |
| 44 float r, g, b, a; | |
| 45 int index; | |
| 46 }; | |
| 47 | |
| 48 static int cmp_stop(const void *a, const void *b) | |
| 49 { | |
| 50 const struct stop *astop = a; | |
| 51 const struct stop *bstop = b; | |
| 52 float diff = astop->offset - bstop->offset; | |
| 53 if (diff < 0) | |
| 54 return -1; | |
| 55 if (diff > 0) | |
| 56 return 1; | |
| 57 return astop->index - bstop->index; | |
| 58 } | |
| 59 | |
| 60 static inline float lerp(float a, float b, float x) | |
| 61 { | |
| 62 return a + (b - a) * x; | |
| 63 } | |
| 64 | |
| 65 static int | |
| 66 xps_parse_gradient_stops(fz_context *ctx, xps_document *doc, char *base_uri, fz_xml *node, | |
| 67 struct stop *stops, int maxcount) | |
| 68 { | |
| 69 fz_colorspace *colorspace; | |
| 70 float sample[FZ_MAX_COLORS]; | |
| 71 float rgb[3]; | |
| 72 int before, after; | |
| 73 int count; | |
| 74 int i; | |
| 75 | |
| 76 /* We may have to insert 2 extra stops when postprocessing */ | |
| 77 maxcount -= 2; | |
| 78 | |
| 79 count = 0; | |
| 80 while (node && count < maxcount) | |
| 81 { | |
| 82 if (fz_xml_is_tag(node, "GradientStop")) | |
| 83 { | |
| 84 char *offset = fz_xml_att(node, "Offset"); | |
| 85 char *color = fz_xml_att(node, "Color"); | |
| 86 if (offset && color) | |
| 87 { | |
| 88 stops[count].offset = fz_atof(offset); | |
| 89 stops[count].index = count; | |
| 90 | |
| 91 xps_parse_color(ctx, doc, base_uri, color, &colorspace, sample); | |
| 92 | |
| 93 fz_convert_color(ctx, colorspace, sample+1, fz_device_rgb(ctx), rgb, NULL, fz_default_color_params); | |
| 94 | |
| 95 stops[count].r = rgb[0]; | |
| 96 stops[count].g = rgb[1]; | |
| 97 stops[count].b = rgb[2]; | |
| 98 stops[count].a = sample[0]; | |
| 99 | |
| 100 count ++; | |
| 101 } | |
| 102 } | |
| 103 node = fz_xml_next(node); | |
| 104 } | |
| 105 | |
| 106 if (count == 0) | |
| 107 { | |
| 108 fz_warn(ctx, "gradient brush has no gradient stops"); | |
| 109 stops[0].offset = 0; | |
| 110 stops[0].r = 0; | |
| 111 stops[0].g = 0; | |
| 112 stops[0].b = 0; | |
| 113 stops[0].a = 1; | |
| 114 stops[1].offset = 1; | |
| 115 stops[1].r = 1; | |
| 116 stops[1].g = 1; | |
| 117 stops[1].b = 1; | |
| 118 stops[1].a = 1; | |
| 119 return 2; | |
| 120 } | |
| 121 | |
| 122 if (count == maxcount) | |
| 123 fz_warn(ctx, "gradient brush exceeded maximum number of gradient stops"); | |
| 124 | |
| 125 /* Postprocess to make sure the range of offsets is 0.0 to 1.0 */ | |
| 126 | |
| 127 qsort(stops, count, sizeof(struct stop), cmp_stop); | |
| 128 | |
| 129 before = -1; | |
| 130 after = -1; | |
| 131 | |
| 132 for (i = 0; i < count; i++) | |
| 133 { | |
| 134 if (stops[i].offset < 0) | |
| 135 before = i; | |
| 136 if (stops[i].offset > 1) | |
| 137 { | |
| 138 after = i; | |
| 139 break; | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 /* Remove all stops < 0 except the largest one */ | |
| 144 if (before > 0) | |
| 145 { | |
| 146 memmove(stops, stops + before, (count - before) * sizeof(struct stop)); | |
| 147 count -= before; | |
| 148 } | |
| 149 | |
| 150 /* Remove all stops > 1 except the smallest one */ | |
| 151 if (after >= 0) | |
| 152 count = after + 1; | |
| 153 | |
| 154 /* Expand single stop to 0 .. 1 */ | |
| 155 if (count == 1) | |
| 156 { | |
| 157 stops[1] = stops[0]; | |
| 158 stops[0].offset = 0; | |
| 159 stops[1].offset = 1; | |
| 160 return 2; | |
| 161 } | |
| 162 | |
| 163 /* First stop < 0 -- interpolate value to 0 */ | |
| 164 if (stops[0].offset < 0) | |
| 165 { | |
| 166 float d = -stops[0].offset / (stops[1].offset - stops[0].offset); | |
| 167 stops[0].offset = 0; | |
| 168 stops[0].r = lerp(stops[0].r, stops[1].r, d); | |
| 169 stops[0].g = lerp(stops[0].g, stops[1].g, d); | |
| 170 stops[0].b = lerp(stops[0].b, stops[1].b, d); | |
| 171 stops[0].a = lerp(stops[0].a, stops[1].a, d); | |
| 172 } | |
| 173 | |
| 174 /* Last stop > 1 -- interpolate value to 1 */ | |
| 175 if (stops[count-1].offset > 1) | |
| 176 { | |
| 177 float d = (1 - stops[count-2].offset) / (stops[count-1].offset - stops[count-2].offset); | |
| 178 stops[count-1].offset = 1; | |
| 179 stops[count-1].r = lerp(stops[count-2].r, stops[count-1].r, d); | |
| 180 stops[count-1].g = lerp(stops[count-2].g, stops[count-1].g, d); | |
| 181 stops[count-1].b = lerp(stops[count-2].b, stops[count-1].b, d); | |
| 182 stops[count-1].a = lerp(stops[count-2].a, stops[count-1].a, d); | |
| 183 } | |
| 184 | |
| 185 /* First stop > 0 -- insert a duplicate at 0 */ | |
| 186 if (stops[0].offset > 0) | |
| 187 { | |
| 188 memmove(stops + 1, stops, count * sizeof(struct stop)); | |
| 189 stops[0] = stops[1]; | |
| 190 stops[0].offset = 0; | |
| 191 count++; | |
| 192 } | |
| 193 | |
| 194 /* Last stop < 1 -- insert a duplicate at 1 */ | |
| 195 if (stops[count-1].offset < 1) | |
| 196 { | |
| 197 stops[count] = stops[count-1]; | |
| 198 stops[count].offset = 1; | |
| 199 count++; | |
| 200 } | |
| 201 | |
| 202 return count; | |
| 203 } | |
| 204 | |
| 205 static void | |
| 206 xps_sample_gradient_stops(fz_context *ctx, xps_document *doc, fz_shade *shade, struct stop *stops, int count) | |
| 207 { | |
| 208 float offset, d; | |
| 209 int i, k; | |
| 210 | |
| 211 shade->function = fz_malloc(ctx, sizeof(float) * 256 * 4); | |
| 212 | |
| 213 k = 0; | |
| 214 for (i = 0; i < 256; i++) | |
| 215 { | |
| 216 offset = i / 255.0f; | |
| 217 while (k + 1 < count && offset > stops[k+1].offset) | |
| 218 k++; | |
| 219 | |
| 220 d = (offset - stops[k].offset) / (stops[k+1].offset - stops[k].offset); | |
| 221 | |
| 222 shade->function[4*i + 0] = lerp(stops[k].r, stops[k+1].r, d); | |
| 223 shade->function[4*i + 1] = lerp(stops[k].g, stops[k+1].g, d); | |
| 224 shade->function[4*i + 2] = lerp(stops[k].b, stops[k+1].b, d); | |
| 225 shade->function[4*i + 3] = lerp(stops[k].a, stops[k+1].a, d); | |
| 226 } | |
| 227 } | |
| 228 | |
| 229 /* | |
| 230 * Radial gradients map more or less to Radial shadings. | |
| 231 * The inner circle is always a point. | |
| 232 * The outer circle is actually an ellipse, | |
| 233 * mess with the transform to squash the circle into the right aspect. | |
| 234 */ | |
| 235 | |
| 236 static void | |
| 237 xps_draw_one_radial_gradient(fz_context *ctx, xps_document *doc, fz_matrix ctm, | |
| 238 struct stop *stops, int count, | |
| 239 int extend, | |
| 240 float x0, float y0, float r0, | |
| 241 float x1, float y1, float r1) | |
| 242 { | |
| 243 fz_device *dev = doc->dev; | |
| 244 fz_shade *shade; | |
| 245 | |
| 246 shade = fz_malloc_struct(ctx, fz_shade); | |
| 247 FZ_INIT_STORABLE(shade, 1, fz_drop_shade_imp); | |
| 248 shade->colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); | |
| 249 shade->bbox = fz_infinite_rect; | |
| 250 shade->matrix = fz_identity; | |
| 251 shade->use_background = 0; | |
| 252 shade->function_stride = 4; | |
| 253 shade->type = FZ_RADIAL; | |
| 254 shade->u.l_or_r.extend[0] = extend; | |
| 255 shade->u.l_or_r.extend[1] = extend; | |
| 256 | |
| 257 shade->u.l_or_r.coords[0][0] = x0; | |
| 258 shade->u.l_or_r.coords[0][1] = y0; | |
| 259 shade->u.l_or_r.coords[0][2] = r0; | |
| 260 shade->u.l_or_r.coords[1][0] = x1; | |
| 261 shade->u.l_or_r.coords[1][1] = y1; | |
| 262 shade->u.l_or_r.coords[1][2] = r1; | |
| 263 | |
| 264 fz_try(ctx) | |
| 265 { | |
| 266 xps_sample_gradient_stops(ctx, doc, shade, stops, count); | |
| 267 fz_fill_shade(ctx, dev, shade, ctm, 1, fz_default_color_params); | |
| 268 } | |
| 269 fz_always(ctx) | |
| 270 fz_drop_shade(ctx, shade); | |
| 271 fz_catch(ctx) | |
| 272 fz_rethrow(ctx); | |
| 273 } | |
| 274 | |
| 275 /* | |
| 276 * Linear gradients. | |
| 277 */ | |
| 278 | |
| 279 static void | |
| 280 xps_draw_one_linear_gradient(fz_context *ctx, xps_document *doc, fz_matrix ctm, | |
| 281 struct stop *stops, int count, | |
| 282 int extend, | |
| 283 float x0, float y0, float x1, float y1) | |
| 284 { | |
| 285 fz_device *dev = doc->dev; | |
| 286 fz_shade *shade; | |
| 287 | |
| 288 shade = fz_malloc_struct(ctx, fz_shade); | |
| 289 FZ_INIT_STORABLE(shade, 1, fz_drop_shade_imp); | |
| 290 shade->colorspace = fz_keep_colorspace(ctx, fz_device_rgb(ctx)); | |
| 291 shade->bbox = fz_infinite_rect; | |
| 292 shade->matrix = fz_identity; | |
| 293 shade->use_background = 0; | |
| 294 shade->function_stride = 4; | |
| 295 shade->type = FZ_LINEAR; | |
| 296 shade->u.l_or_r.extend[0] = extend; | |
| 297 shade->u.l_or_r.extend[1] = extend; | |
| 298 | |
| 299 shade->u.l_or_r.coords[0][0] = x0; | |
| 300 shade->u.l_or_r.coords[0][1] = y0; | |
| 301 shade->u.l_or_r.coords[0][2] = 0; | |
| 302 shade->u.l_or_r.coords[1][0] = x1; | |
| 303 shade->u.l_or_r.coords[1][1] = y1; | |
| 304 shade->u.l_or_r.coords[1][2] = 0; | |
| 305 | |
| 306 fz_try(ctx) | |
| 307 { | |
| 308 xps_sample_gradient_stops(ctx, doc, shade, stops, count); | |
| 309 fz_fill_shade(ctx, dev, shade, ctm, doc->opacity[doc->opacity_top], fz_default_color_params); | |
| 310 } | |
| 311 fz_always(ctx) | |
| 312 fz_drop_shade(ctx, shade); | |
| 313 fz_catch(ctx) | |
| 314 fz_rethrow(ctx); | |
| 315 } | |
| 316 | |
| 317 /* | |
| 318 * We need to loop and create many shading objects to account | |
| 319 * for the Repeat and Reflect SpreadMethods. | |
| 320 * I'm not smart enough to calculate this analytically | |
| 321 * so we iterate and check each object until we | |
| 322 * reach a reasonable limit for infinite cases. | |
| 323 */ | |
| 324 | |
| 325 static void | |
| 326 xps_draw_radial_gradient(fz_context *ctx, xps_document *doc, fz_matrix ctm, fz_rect area, | |
| 327 struct stop *stops, int count, | |
| 328 fz_xml *root, int spread) | |
| 329 { | |
| 330 float x0, y0, r0; | |
| 331 float x1, y1, r1; | |
| 332 float xrad = 1; | |
| 333 float yrad = 1; | |
| 334 float invscale; | |
| 335 int i, ma = 1; | |
| 336 fz_matrix inv; | |
| 337 | |
| 338 char *center_att = fz_xml_att(root, "Center"); | |
| 339 char *origin_att = fz_xml_att(root, "GradientOrigin"); | |
| 340 char *radius_x_att = fz_xml_att(root, "RadiusX"); | |
| 341 char *radius_y_att = fz_xml_att(root, "RadiusY"); | |
| 342 | |
| 343 x0 = y0 = 0.0f; | |
| 344 x1 = y1 = 1.0f; | |
| 345 xrad = 1.0f; | |
| 346 yrad = 1.0f; | |
| 347 | |
| 348 if (origin_att) | |
| 349 xps_parse_point(ctx, doc, origin_att, &x0, &y0); | |
| 350 if (center_att) | |
| 351 xps_parse_point(ctx, doc, center_att, &x1, &y1); | |
| 352 if (radius_x_att) | |
| 353 xrad = fz_atof(radius_x_att); | |
| 354 if (radius_y_att) | |
| 355 yrad = fz_atof(radius_y_att); | |
| 356 | |
| 357 xrad = fz_max(0.01f, xrad); | |
| 358 yrad = fz_max(0.01f, yrad); | |
| 359 | |
| 360 /* scale the ctm to make ellipses */ | |
| 361 if (fz_abs(xrad) > FLT_EPSILON) | |
| 362 { | |
| 363 ctm = fz_pre_scale(ctm, 1, yrad/xrad); | |
| 364 } | |
| 365 | |
| 366 if (yrad != 0.0f) | |
| 367 { | |
| 368 invscale = xrad / yrad; | |
| 369 y0 = y0 * invscale; | |
| 370 y1 = y1 * invscale; | |
| 371 } | |
| 372 | |
| 373 r0 = 0; | |
| 374 r1 = xrad; | |
| 375 | |
| 376 inv = fz_invert_matrix(ctm); | |
| 377 area = fz_transform_rect(area, inv); | |
| 378 ma = fz_maxi(ma, ceilf(hypotf(area.x0 - x0, area.y0 - y0) / xrad)); | |
| 379 ma = fz_maxi(ma, ceilf(hypotf(area.x1 - x0, area.y0 - y0) / xrad)); | |
| 380 ma = fz_maxi(ma, ceilf(hypotf(area.x0 - x0, area.y1 - y0) / xrad)); | |
| 381 ma = fz_maxi(ma, ceilf(hypotf(area.x1 - x0, area.y1 - y0) / xrad)); | |
| 382 | |
| 383 if (spread == SPREAD_REPEAT) | |
| 384 { | |
| 385 for (i = ma - 1; i >= 0; i--) | |
| 386 xps_draw_one_radial_gradient(ctx, doc, ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad); | |
| 387 } | |
| 388 else if (spread == SPREAD_REFLECT) | |
| 389 { | |
| 390 if ((ma % 2) != 0) | |
| 391 ma++; | |
| 392 for (i = ma - 2; i >= 0; i -= 2) | |
| 393 { | |
| 394 xps_draw_one_radial_gradient(ctx, doc, ctm, stops, count, 0, x0, y0, r0 + i * xrad, x1, y1, r1 + i * xrad); | |
| 395 xps_draw_one_radial_gradient(ctx, doc, ctm, stops, count, 0, x0, y0, r0 + (i + 2) * xrad, x1, y1, r1 + i * xrad); | |
| 396 } | |
| 397 } | |
| 398 else | |
| 399 { | |
| 400 xps_draw_one_radial_gradient(ctx, doc, ctm, stops, count, 1, x0, y0, r0, x1, y1, r1); | |
| 401 } | |
| 402 } | |
| 403 | |
| 404 /* | |
| 405 * Calculate how many iterations are needed to cover | |
| 406 * the bounding box. | |
| 407 */ | |
| 408 | |
| 409 static void | |
| 410 xps_draw_linear_gradient(fz_context *ctx, xps_document *doc, fz_matrix ctm, fz_rect area, | |
| 411 struct stop *stops, int count, | |
| 412 fz_xml *root, int spread) | |
| 413 { | |
| 414 float x0, y0, x1, y1; | |
| 415 int i, mi, ma; | |
| 416 float dx, dy, x, y, k; | |
| 417 fz_point p1, p2; | |
| 418 fz_matrix inv; | |
| 419 | |
| 420 char *start_point_att = fz_xml_att(root, "StartPoint"); | |
| 421 char *end_point_att = fz_xml_att(root, "EndPoint"); | |
| 422 | |
| 423 x0 = y0 = 0; | |
| 424 x1 = y1 = 1; | |
| 425 | |
| 426 if (start_point_att) | |
| 427 xps_parse_point(ctx, doc, start_point_att, &x0, &y0); | |
| 428 if (end_point_att) | |
| 429 xps_parse_point(ctx, doc, end_point_att, &x1, &y1); | |
| 430 | |
| 431 p1.x = x0; p1.y = y0; p2.x = x1; p2.y = y1; | |
| 432 inv = fz_invert_matrix(ctm); | |
| 433 area = fz_transform_rect(area, inv); | |
| 434 x = p2.x - p1.x; y = p2.y - p1.y; | |
| 435 k = ((area.x0 - p1.x) * x + (area.y0 - p1.y) * y) / (x * x + y * y); | |
| 436 mi = floorf(k); ma = ceilf(k); | |
| 437 k = ((area.x1 - p1.x) * x + (area.y0 - p1.y) * y) / (x * x + y * y); | |
| 438 mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k)); | |
| 439 k = ((area.x0 - p1.x) * x + (area.y1 - p1.y) * y) / (x * x + y * y); | |
| 440 mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k)); | |
| 441 k = ((area.x1 - p1.x) * x + (area.y1 - p1.y) * y) / (x * x + y * y); | |
| 442 mi = fz_mini(mi, floorf(k)); ma = fz_maxi(ma, ceilf(k)); | |
| 443 dx = x1 - x0; dy = y1 - y0; | |
| 444 | |
| 445 if (spread == SPREAD_REPEAT) | |
| 446 { | |
| 447 for (i = mi; i < ma; i++) | |
| 448 xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 0, x0 + i * dx, y0 + i * dy, x1 + i * dx, y1 + i * dy); | |
| 449 } | |
| 450 else if (spread == SPREAD_REFLECT) | |
| 451 { | |
| 452 if ((mi % 2) != 0) | |
| 453 mi--; | |
| 454 for (i = mi; i < ma; i += 2) | |
| 455 { | |
| 456 xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 0, x0 + i * dx, y0 + i * dy, x1 + i * dx, y1 + i * dy); | |
| 457 xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 0, x0 + (i + 2) * dx, y0 + (i + 2) * dy, x1 + i * dx, y1 + i * dy); | |
| 458 } | |
| 459 } | |
| 460 else | |
| 461 { | |
| 462 xps_draw_one_linear_gradient(ctx, doc, ctm, stops, count, 1, x0, y0, x1, y1); | |
| 463 } | |
| 464 } | |
| 465 | |
| 466 /* | |
| 467 * Parse XML tag and attributes for a gradient brush, create color/opacity | |
| 468 * function objects and call gradient drawing primitives. | |
| 469 */ | |
| 470 | |
| 471 static void | |
| 472 xps_parse_gradient_brush(fz_context *ctx, xps_document *doc, fz_matrix ctm, fz_rect area, | |
| 473 char *base_uri, xps_resource *dict, fz_xml *root, | |
| 474 void (*draw)(fz_context *ctx, xps_document *, fz_matrix, fz_rect, struct stop *, int, fz_xml *, int)) | |
| 475 { | |
| 476 fz_xml *node; | |
| 477 | |
| 478 char *opacity_att; | |
| 479 char *spread_att; | |
| 480 char *transform_att; | |
| 481 | |
| 482 fz_xml *transform_tag = NULL; | |
| 483 fz_xml *stop_tag = NULL; | |
| 484 | |
| 485 struct stop stop_list[MAX_STOPS]; | |
| 486 int stop_count; | |
| 487 int spread_method; | |
| 488 | |
| 489 opacity_att = fz_xml_att(root, "Opacity"); | |
| 490 spread_att = fz_xml_att(root, "SpreadMethod"); | |
| 491 transform_att = fz_xml_att(root, "Transform"); | |
| 492 | |
| 493 for (node = fz_xml_down(root); node; node = fz_xml_next(node)) | |
| 494 { | |
| 495 if (fz_xml_is_tag(node, "LinearGradientBrush.Transform")) | |
| 496 transform_tag = fz_xml_down(node); | |
| 497 if (fz_xml_is_tag(node, "RadialGradientBrush.Transform")) | |
| 498 transform_tag = fz_xml_down(node); | |
| 499 if (fz_xml_is_tag(node, "LinearGradientBrush.GradientStops")) | |
| 500 stop_tag = fz_xml_down(node); | |
| 501 if (fz_xml_is_tag(node, "RadialGradientBrush.GradientStops")) | |
| 502 stop_tag = fz_xml_down(node); | |
| 503 } | |
| 504 | |
| 505 xps_resolve_resource_reference(ctx, doc, dict, &transform_att, &transform_tag, NULL); | |
| 506 | |
| 507 spread_method = SPREAD_PAD; | |
| 508 if (spread_att) | |
| 509 { | |
| 510 if (!strcmp(spread_att, "Pad")) | |
| 511 spread_method = SPREAD_PAD; | |
| 512 if (!strcmp(spread_att, "Reflect")) | |
| 513 spread_method = SPREAD_REFLECT; | |
| 514 if (!strcmp(spread_att, "Repeat")) | |
| 515 spread_method = SPREAD_REPEAT; | |
| 516 } | |
| 517 | |
| 518 ctm = xps_parse_transform(ctx, doc, transform_att, transform_tag, ctm); | |
| 519 | |
| 520 if (!stop_tag) { | |
| 521 fz_warn(ctx, "missing gradient stops tag"); | |
| 522 return; | |
| 523 } | |
| 524 | |
| 525 stop_count = xps_parse_gradient_stops(ctx, doc, base_uri, stop_tag, stop_list, MAX_STOPS); | |
| 526 if (stop_count == 0) | |
| 527 { | |
| 528 fz_warn(ctx, "no gradient stops found"); | |
| 529 return; | |
| 530 } | |
| 531 | |
| 532 xps_begin_opacity(ctx, doc, ctm, area, base_uri, dict, opacity_att, NULL); | |
| 533 | |
| 534 draw(ctx, doc, ctm, area, stop_list, stop_count, root, spread_method); | |
| 535 | |
| 536 xps_end_opacity(ctx, doc, base_uri, dict, opacity_att, NULL); | |
| 537 } | |
| 538 | |
| 539 void | |
| 540 xps_parse_linear_gradient_brush(fz_context *ctx, xps_document *doc, fz_matrix ctm, fz_rect area, | |
| 541 char *base_uri, xps_resource *dict, fz_xml *root) | |
| 542 { | |
| 543 xps_parse_gradient_brush(ctx, doc, ctm, area, base_uri, dict, root, xps_draw_linear_gradient); | |
| 544 } | |
| 545 | |
| 546 void | |
| 547 xps_parse_radial_gradient_brush(fz_context *ctx, xps_document *doc, fz_matrix ctm, fz_rect area, | |
| 548 char *base_uri, xps_resource *dict, fz_xml *root) | |
| 549 { | |
| 550 xps_parse_gradient_brush(ctx, doc, ctm, area, base_uri, dict, root, xps_draw_radial_gradient); | |
| 551 } |
