1 /*
2   grdxsvg.c
3 
4   Copyright (c) J.J. Green 2014
5 */
6 
7 #include <stdio.h>
8 
9 #include "grdxsvg.h"
10 #include "btrace.h"
11 
12 /* convert intermediate types to svg values */
13 
svg_it_rgb(double x)14 static unsigned char svg_it_rgb(double x)
15 {
16   x *= 256;
17   if (x > 255) x = 255;
18   if (x < 0) x = 0;
19 
20   return x;
21 }
22 
svg_it_op(double x)23 static double svg_it_op(double x)
24 {
25   return x;
26 }
27 
svg_it_z(double x)28 static double svg_it_z(double x)
29 {
30   return x  / 4096.0;
31 }
32 
33 
stop_merge(rgb_stop_t rs,op_stop_t os)34 static rgbop_stop_t stop_merge(rgb_stop_t rs, op_stop_t os)
35 {
36   rgbop_stop_t ros;
37 
38   ros.r  = rs.r;
39   ros.g  = rs.g;
40   ros.b  = rs.b;
41   ros.op = os.op;
42   ros.z  = rs.z;
43 
44   return ros;
45 }
46 
rgb_stop_interp(rgb_stop_t rs0,rgb_stop_t rs1,unsigned int z)47 static rgb_stop_t rgb_stop_interp(rgb_stop_t rs0,
48 				  rgb_stop_t rs1,
49 				  unsigned int z)
50 {
51   double M =
52     ((double)z - (double)(rs0.z))/((double)(rs1.z) - (double)(rs0.z));
53   rgb_stop_t rs;
54 
55   rs.z = z;
56   rs.r = rs0.r + M*(rs1.r - rs0.r);
57   rs.g = rs0.g + M*(rs1.g - rs0.g);
58   rs.b = rs0.b + M*(rs1.b - rs0.b);
59 
60   return rs;
61 }
62 
op_stop_interp(op_stop_t os0,op_stop_t os1,unsigned int z)63 static op_stop_t op_stop_interp(op_stop_t os0,
64 				op_stop_t os1,
65 				unsigned int z)
66 {
67   double M =
68     ((double)z -(double)(os0.z))/((double)(os1.z) - (double)(os0.z));
69   op_stop_t os;
70 
71   os.z = z;
72   os.op = os0.op + M*(os1.op - os0.op);
73 
74   return os;
75 }
76 
77 /*
78    merge the independent rgb and opacity channels into
79    a single rgbop channel. This means we interpolate
80    each of the channels at the z-values of the other
81 */
82 
merge(gstack_t * rss,gstack_t * oss)83 static gstack_t* merge(gstack_t *rss, gstack_t *oss)
84 {
85   gstack_t *ross;
86   int err = 0;
87   size_t n = gstack_size(rss) + gstack_size(oss);
88 
89   /* get the first two of each type of stop */
90 
91   rgb_stop_t rs0, rs1;
92 
93   err += gstack_pop(rss, &rs0);
94   err += gstack_pop(rss, &rs1);
95 
96   if (err)
97     {
98       btrace("%i errors rgb", err);
99       return NULL;
100     }
101 
102   op_stop_t os0, os1;
103 
104   err += gstack_pop(oss, &os0);
105   err += gstack_pop(oss, &os1);
106 
107   if (err)
108     {
109       btrace("%i errors op", err);
110       return NULL;
111     }
112 
113   if ((rs0.z != 0) || (os0.z != 0))
114     {
115       btrace("nonzero initial stop");
116       btrace("RGB     %.3f", svg_it_z(rs0.z));
117       btrace("Opacity %.3f", svg_it_z(os0.z));
118       return NULL;
119     }
120 
121   /* merged stack to return */
122 
123   if ((ross = gstack_new(sizeof(rgbop_stop_t), n, n)) == NULL)
124     return NULL;
125 
126   rgbop_stop_t ros;
127 
128   while (1)
129     {
130       ros = stop_merge(rs0, os0);
131       gstack_push(ross, &ros);
132 
133       if (rs1.z > os1.z)
134 	{
135 	  rs0 = rgb_stop_interp(rs0, rs1, os1.z);
136 	  os0 = os1;
137 
138 	  if (gstack_pop(oss, &os1) != 0)
139 	    {
140 	      btrace("early termination of opacity channel");
141 	      break;
142 	    }
143 	}
144       else if (rs1.z < os1.z)
145 	{
146 	  os0 = op_stop_interp(os0, os1, rs1.z);
147 	  rs0 = rs1;
148 
149 	  if (gstack_pop(rss, &rs1) != 0)
150 	    {
151 	      btrace("early termination of rgb channel");
152 	      break;
153 	    }
154 	}
155       else
156 	{
157 	  rs0 = rs1;
158 	  os0 = os1;
159 
160 	  int
161             odone = gstack_pop(oss, &os1),
162             rdone = gstack_pop(rss, &rs1);
163 
164 	  if (odone && rdone)
165 	    {
166 	      ros = stop_merge(rs0, os0);
167 	      gstack_push(ross, &ros);
168 	      gstack_reverse(ross);
169 
170 	      return ross;
171 	    }
172 	  else if (odone && !rdone)
173 	    {
174 	      btrace("early termination of opacity channel");
175 	      break;
176 	    }
177 	  else if (rdone && ! odone)
178 	    {
179 	      btrace("early termination of rgb channel");
180 	      break;
181 	    }
182 	  else
183 	    {
184 	      /* OK, so now we continue */
185 	    }
186 	}
187     }
188 
189   /* something has gone pear-shaped */
190 
191   gstack_destroy(ross);
192 
193   btrace("merge failed");
194 
195   return NULL;
196 }
197 
198 
merged_svg(gstack_t * ross,svg_t * svg)199 static int merged_svg(gstack_t *ross, svg_t *svg)
200 {
201   svg_stop_t ss;
202   rgbop_stop_t ros;
203   int errs = 0;
204 
205   while (gstack_pop(ross, &ros) == 0)
206     {
207       ss.colour.red   = svg_it_rgb(ros.r);
208       ss.colour.green = svg_it_rgb(ros.g);
209       ss.colour.blue  = svg_it_rgb(ros.b);
210       ss.opacity      = svg_it_op(ros.op);
211       ss.value        = svg_it_z(ros.z);
212       if (svg_append(ss,svg) != 0)
213 	errs++;
214     }
215 
216   return (errs ? 1 : 0);
217 }
218 
219 
grdxsvg(gstack_t * rgb_stops,gstack_t * op_stops,svg_t * svg)220 extern int grdxsvg(gstack_t *rgb_stops,
221 		   gstack_t *op_stops,
222 		   svg_t *svg)
223 {
224   gstack_t *merged_stops;
225 
226   if ((merged_stops = merge(rgb_stops, op_stops)) == NULL)
227     {
228       btrace("no merged stops");
229       return 1;
230     }
231 
232   int err = (merged_svg(merged_stops, svg) != 0);
233 
234   if (err)
235     btrace("failed conversion of merged stops to svg");
236 
237   gstack_destroy(merged_stops);
238 
239   return err;
240 }
241