Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  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. }
  459.