xps-gradient.c 15 KB

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