1 /****************************************************************************
2 * Copyright (C) 2008 by Matteo Franchin *
3 * *
4 * This file is part of Box. *
5 * *
6 * Box is free software: you can redistribute it and/or modify it *
7 * under the terms of the GNU Lesser General Public License as published *
8 * by the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * Box is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU Lesser General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Lesser General Public *
17 * License along with Box. If not, see <http://www.gnu.org/licenses/>. *
18 ****************************************************************************/
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <math.h>
23
24 #include "types.h"
25 #include <box/vm_priv.h>
26 #include "str.h"
27 #include "i_gradient.h"
28 #include "i_window.h"
29 #include "graphic.h"
30 #include "g.h"
31
gradient_begin(BoxVMX * vmp)32 BoxTask gradient_begin(BoxVMX *vmp) {
33 GradientPtr *g_ptr = BOX_VM_THIS_PTR(vmp, GradientPtr);
34 Gradient *g;
35 g = *g_ptr = (Gradient *) malloc(sizeof(Gradient));
36 if (g == (Gradient *) NULL) return BOXTASK_FAILURE;
37 if (!buff_create(& g->items, sizeof(ColorGradItem), 8))
38 return BOXTASK_FAILURE;
39 g->got.type = 0;
40 g->got.point1 = 0;
41 g->got.point2 = 0;
42 g->got.radius1 = 0;
43 g->got.radius2 = 0;
44 g->got.pause = 0;
45 g->got.pos = 0;
46 g->this_item.position = -1.0;
47 g->gradient.extend = COLOR_GRAD_EXT_PAD;
48 return BOXTASK_OK;
49 }
50
gradient_destroy(BoxVMX * vmp)51 BoxTask gradient_destroy(BoxVMX *vmp) {
52 Gradient *g = BOX_VM_THIS(vmp, GradientPtr);
53 buff_free(& g->items);
54 free(g);
55 return BOXTASK_OK;
56 }
57
set_gradient_type(Gradient * g,ColorGradType t)58 static BoxTask set_gradient_type(Gradient *g, ColorGradType t) {
59 if (g->got.type && g->gradient.type != t) {
60 g_error("Cannot change the gradient type!");
61 return BOXTASK_FAILURE;
62 }
63 g->got.type = 1;
64 g->gradient.type = t;
65 return BOXTASK_OK;
66 }
67
gradient_string(BoxVMX * vm)68 BoxTask gradient_string(BoxVMX *vm) {
69 Gradient *g = BOX_VM_THIS(vm, GradientPtr);
70 BoxStr *box_string = BOX_VM_ARG_PTR(vm, BoxStr);
71 const char *ext_str = (char *) box_string->ptr;
72 char *ext_styles[] = {"single", "repeated", "repeat",
73 "reflected", "reflect",
74 "padded", "pad", (char *) NULL};
75 ColorGradExt es[] = {COLOR_GRAD_EXT_NONE,
76 COLOR_GRAD_EXT_REPEAT, COLOR_GRAD_EXT_REPEAT,
77 COLOR_GRAD_EXT_REFLECT, COLOR_GRAD_EXT_REFLECT,
78 COLOR_GRAD_EXT_PAD, COLOR_GRAD_EXT_PAD};
79 int index = g_string_find_in_list(ext_styles, ext_str);
80 if (index < 0) {
81 printf("Invalid extend style for color gradient. Available styles are: ");
82 g_string_list_print(stdout, ext_styles, ", ");
83 printf(".\n");
84 return BOXTASK_FAILURE;
85 }
86 g->gradient.extend = es[index];
87 return BOXTASK_OK;
88 }
89
gradient_line_point(BoxVMX * vmp)90 BoxTask gradient_line_point(BoxVMX *vmp) {
91 Gradient *g = BOX_VM_SUB_PARENT(vmp, GradientPtr);
92 BoxPoint *p = BOX_VM_ARG1_PTR(vmp, BoxPoint);
93 set_gradient_type(g, COLOR_GRAD_TYPE_LINEAR);
94 if (!g->got.point1) {
95 g->gradient.point1 = *p;
96 g->got.point1 = 1;
97 return BOXTASK_OK;
98
99 } else if (!g->got.point2) {
100 g->gradient.point2 = *p;
101 g->got.point2 = 1;
102 return BOXTASK_OK;
103
104 } else {
105 g_warning("Gradient.Line takes just two points: ignoring other "
106 "given points!");
107 return BOXTASK_OK;
108 }
109 }
110
gradient_circle_point(BoxVMX * vmp)111 BoxTask gradient_circle_point(BoxVMX *vmp) {
112 Gradient *g = BOX_VM_SUB_PARENT(vmp, GradientPtr);
113 BoxPoint *p = BOX_VM_ARG1_PTR(vmp, BoxPoint);
114 set_gradient_type(g, COLOR_GRAD_TYPE_RADIAL);
115 if (!g->got.pause) {
116 if (g->got.point1) {
117 g_warning("Already got the center for the first circle: "
118 "ignoring this other value!");
119 return BOXTASK_OK;
120 }
121 g->gradient.point2 = g->gradient.point1 = *p;
122 g->got.point1 = 1;
123
124 } else {
125 if (g->got.point2) {
126 g_warning("Already got the center for the second circle: "
127 "ignoring this other value!");
128 return BOXTASK_OK;
129 }
130 g->gradient.point2 = *p;
131 g->got.point2 = 1;
132 }
133 return BOXTASK_OK;
134 }
135
gradient_circle_real(BoxVMX * vmp)136 BoxTask gradient_circle_real(BoxVMX *vmp) {
137 Gradient *g = BOX_VM_SUB_PARENT(vmp, GradientPtr);
138 BoxReal r = fabs(BOX_VM_ARG1(vmp, BoxReal));
139
140 set_gradient_type(g, COLOR_GRAD_TYPE_RADIAL);
141 if (!g->got.pause) {
142 if (g->got.radius1) {
143 g_warning("Already got the radius of the first circle: "
144 "ignoring this other value!");
145 return BOXTASK_OK;
146 }
147 g->gradient.radius2 = g->gradient.radius1 = r;
148 g->got.radius1 = 1;
149
150 } else {
151 if (g->got.radius2) {
152 g_warning("Already got the radius of the second circle: "
153 "ignoring this other value!");
154 return BOXTASK_OK;
155 }
156 g->gradient.radius2 = r;
157 g->got.radius2 = 1;
158 }
159 return BOXTASK_OK;
160 }
161
gradient_circle_pause(BoxVMX * vmp)162 BoxTask gradient_circle_pause(BoxVMX *vmp) {
163 Gradient *g = BOX_VM_SUB_PARENT(vmp, GradientPtr);
164 if (!(g->got.point1 && g->got.radius1)) {
165 g_error("Gradient.Circle[] should get the center and radius "
166 "of the first circle, before getting ';'.");
167 return BOXTASK_FAILURE;
168 }
169 g->got.pause = 1;
170 return BOXTASK_OK;
171 }
172
gradient_color(BoxVMX * vmp)173 BoxTask gradient_color(BoxVMX *vmp) {
174 Gradient *g = BOX_VM_THIS(vmp, GradientPtr);
175 Color *c = BOX_VM_ARG1_PTR(vmp, Color);
176 g->this_item.color = *c;
177 if (!buff_push(& g->items, & g->this_item)) return BOXTASK_FAILURE;
178 g->got.pos = 0;
179 g->this_item.position = -1.0;
180 return BOXTASK_OK;
181 }
182
gradient_real(BoxVMX * vmp)183 BoxTask gradient_real(BoxVMX *vmp) {
184 Gradient *g = BOX_VM_THIS(vmp, GradientPtr);
185 BoxReal r = BOX_VM_ARG1(vmp, BoxReal);
186 if (g->got.pos) {
187 g_warning("Real@Gradient: You already specified a position "
188 "for this Color: ignoring this other value!");
189 return BOXTASK_OK;
190 }
191 if (r < 0.0 || r > 1.0) {
192 g_error("Real@Gradient: The color position should be a "
193 "real number between 0.0 and 1.0!");
194 return BOXTASK_FAILURE;
195 }
196 g->got.pos = 1;
197 g->this_item.position = r;
198 return BOXTASK_OK;
199 }
200
gradient_end(BoxVMX * vmp)201 BoxTask gradient_end(BoxVMX *vmp) {
202 Gradient *g = BOX_VM_THIS(vmp, GradientPtr);
203 ColorGradItem *cgi;
204 BoxInt n = buff_numitems(& g->items);
205
206 if (n < 2) {
207 g_error("(])@Gradient: Incomplete gradient specification: "
208 "Gradient should get at least two colors!");
209 return BOXTASK_FAILURE;
210 }
211
212 if (!g->got.type) {
213 g_error("(])@Gradient: Incomplete gradient specification: "
214 "You should use Gradient.Line or Gradient.Circle!");
215 return BOXTASK_FAILURE;
216 }
217
218 /* Reference points for the gradient (useful to transform correctly
219 * the gradient when applying transformations of the coordinate system)
220 */
221 g->gradient.ref2 = g->gradient.ref1 = g->gradient.point1;
222 g->gradient.ref1.x += 1.0;
223 g->gradient.ref2.y += 1.0;
224
225 if (n == 1) {
226 cgi = buff_firstitemptr(& g->items, ColorGradItem);
227 cgi->position = 0.5;
228
229 } else {
230 BoxInt i;
231 cgi = buff_lastitemptr(& g->items, ColorGradItem);
232 if (cgi->position < 0.0) cgi->position = 1.0;
233 cgi = buff_firstitemptr(& g->items, ColorGradItem);
234 if (cgi->position < 0.0) cgi->position = 0.0;
235 for(i = 1; i < n;) {
236 BoxInt a;
237 BoxReal pos_a, delta_pos;
238 for(; i < n; i++) if (cgi[i].position < 0.0) break;
239 a = i;
240 for(; i < n; i++) if (cgi[i].position >= 0.0) break;
241 pos_a = cgi[a-1].position;
242 delta_pos = (cgi[i].position - pos_a)/(i - a + 1);
243 for(; a < i; a++)
244 cgi[a].position = (pos_a += delta_pos);
245 }
246 }
247 g->gradient.num_items = n;
248 g->gradient.items = buff_firstitemptr(& g->items, ColorGradItem);
249 return BOXTASK_OK;
250 }
251
print_gradient(BoxVMX * vmp)252 BoxTask print_gradient(BoxVMX *vmp) {
253 Gradient *g = BOX_VM_ARG1(vmp, GradientPtr);
254 ColorGradItem *cgi = g->gradient.items;
255 BoxInt n = g->gradient.num_items, i;
256 FILE *out = stdout;
257
258 fprintf(out, "Gradient[");
259 if (g->got.type) {
260 if (g->gradient.type == COLOR_GRAD_TYPE_LINEAR) {
261 fprintf(out, ".Line[");
262 if (g->got.point1)
263 fprintf(out, "("SReal", "SReal")",
264 g->gradient.point1.x, g->gradient.point1.y);
265 if (g->got.point2)
266 fprintf(out, ", ("SReal", "SReal")",
267 g->gradient.point2.x, g->gradient.point2.y);
268 fprintf(out, "]");
269
270 } else {
271 fprintf(out, ".Circle[");
272 if (g->got.point1)
273 fprintf(out, "("SReal", "SReal"), "SReal,
274 g->gradient.point1.x, g->gradient.point1.y,
275 g->gradient.radius1);
276 if (g->got.point2)
277 fprintf(out, "; ("SReal", "SReal"), "SReal,
278 g->gradient.point2.x, g->gradient.point2.y,
279 g->gradient.radius2);
280 fprintf(out, "]");
281 }
282 }
283
284 for(i = 0; i < n; i++) {
285 fprintf(out, ", "SReal", Color["SReal", "SReal", "SReal", "SReal"]",
286 cgi[i].position, cgi[i].color.r, cgi[i].color.g,
287 cgi[i].color.b, cgi[i].color.a);
288 }
289 fprintf(out, "]");
290 return BOXTASK_OK;
291 }
292
x_gradient(BoxVMX * vmp)293 BoxTask x_gradient(BoxVMX *vmp) {
294 Window *w = BOX_VM_SUB_PARENT(vmp, WindowPtr);
295 Gradient *g = BOX_VM_ARG1(vmp, GradientPtr);
296 BoxGWin_Set_Gradient(w->window, & g->gradient);
297 return BOXTASK_OK;
298 }
299