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