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