1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * IfsCompose is a interface for creating IFS fractals by
5  * direct manipulation.
6  * Copyright (C) 1997 Owen Taylor
7  *
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTBILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <string.h> /* strlen */
24 
25 #include <gdk/gdk.h>
26 
27 #include <libgimp/gimp.h>
28 
29 #include "ifs-compose.h"
30 
31 
32 typedef enum {
33   TOKEN_INVALID = G_TOKEN_LAST,
34   TOKEN_ITERATIONS,
35   TOKEN_MAX_MEMORY,
36   TOKEN_SUBDIVIDE,
37   TOKEN_RADIUS,
38   TOKEN_ASPECT_RATIO,
39   TOKEN_CENTER_X,
40   TOKEN_CENTER_Y,
41   TOKEN_ELEMENT,
42   TOKEN_X,
43   TOKEN_Y,
44   TOKEN_THETA,
45   TOKEN_SCALE,
46   TOKEN_ASYM,
47   TOKEN_SHEAR,
48   TOKEN_FLIP,
49   TOKEN_RED_COLOR,
50   TOKEN_GREEN_COLOR,
51   TOKEN_BLUE_COLOR,
52   TOKEN_BLACK_COLOR,
53   TOKEN_TARGET_COLOR,
54   TOKEN_HUE_SCALE,
55   TOKEN_VALUE_SCALE,
56   TOKEN_SIMPLE_COLOR,
57   TOKEN_PROB
58 } IfsComposeToken;
59 
60 static struct
61 {
62   const gchar     *name;
63   IfsComposeToken  token;
64 } symbols[] = {
65   { "iterations",   TOKEN_ITERATIONS },
66   { "max_memory",   TOKEN_MAX_MEMORY },
67   { "subdivide",    TOKEN_SUBDIVIDE },
68   { "radius",       TOKEN_RADIUS },
69   { "aspect_ratio", TOKEN_ASPECT_RATIO },
70   { "center_x",     TOKEN_CENTER_X },
71   { "center_y",     TOKEN_CENTER_Y },
72   { "element",      TOKEN_ELEMENT },
73   { "x",            TOKEN_X },
74   { "y",            TOKEN_Y },
75   { "theta",        TOKEN_THETA },
76   { "scale",        TOKEN_SCALE },
77   { "asym",         TOKEN_ASYM },
78   { "shear",        TOKEN_SHEAR },
79   { "flip",         TOKEN_FLIP },
80   { "red_color",    TOKEN_RED_COLOR },
81   { "green_color",  TOKEN_GREEN_COLOR },
82   { "blue_color",   TOKEN_BLUE_COLOR },
83   { "black_color",  TOKEN_BLACK_COLOR },
84   { "target_color", TOKEN_TARGET_COLOR },
85   { "hue_scale",    TOKEN_HUE_SCALE },
86   { "value_scale",  TOKEN_VALUE_SCALE },
87   { "simple_color", TOKEN_SIMPLE_COLOR },
88   { "prob",         TOKEN_PROB }
89 };
90 
91 static GTokenType
ifsvals_parse_color(GScanner * scanner,GimpRGB * result)92 ifsvals_parse_color (GScanner *scanner,
93 		     GimpRGB  *result)
94 {
95   GTokenType token;
96 
97   token = g_scanner_get_next_token (scanner);
98   if (token != G_TOKEN_LEFT_CURLY)
99     return G_TOKEN_LEFT_CURLY;
100 
101   token = g_scanner_get_next_token (scanner);
102   if (token == G_TOKEN_FLOAT)
103     result->r = scanner->value.v_float;
104   else if (token == G_TOKEN_INT)
105     result->r = scanner->value.v_int;
106   else
107     return G_TOKEN_FLOAT;
108 
109   token = g_scanner_get_next_token (scanner);
110   if (token != G_TOKEN_COMMA)
111     return G_TOKEN_COMMA;
112 
113   token = g_scanner_get_next_token (scanner);
114   if (token == G_TOKEN_FLOAT)
115     result->g = scanner->value.v_float;
116   else if (token == G_TOKEN_INT)
117     result->g = scanner->value.v_int;
118   else
119     return G_TOKEN_FLOAT;
120 
121   token = g_scanner_get_next_token (scanner);
122   if (token != G_TOKEN_COMMA)
123     return G_TOKEN_COMMA;
124 
125   token = g_scanner_get_next_token (scanner);
126   if (token == G_TOKEN_FLOAT)
127     result->b = scanner->value.v_float;
128   else if (token == G_TOKEN_INT)
129     result->b = scanner->value.v_int;
130   else
131     return G_TOKEN_FLOAT;
132 
133   token = g_scanner_get_next_token (scanner);
134   if (token != G_TOKEN_RIGHT_CURLY)
135     return G_TOKEN_RIGHT_CURLY;
136 
137   return G_TOKEN_NONE;
138 }
139 
140 /* Parse a float which (unlike G_TOKEN_FLOAT) can be negative
141  */
142 static GTokenType
parse_genuine_float(GScanner * scanner,gdouble * result)143 parse_genuine_float (GScanner *scanner,
144                      gdouble  *result)
145 {
146   gboolean negate = FALSE;
147   GTokenType token;
148 
149   token = g_scanner_get_next_token (scanner);
150 
151   if (token == '-')
152     {
153       negate = TRUE;
154       token = g_scanner_get_next_token (scanner);
155     }
156 
157   if (token == G_TOKEN_FLOAT)
158     {
159       *result = negate ? -scanner->value.v_float : scanner->value.v_float;
160       return G_TOKEN_NONE;
161     }
162   else if (token == G_TOKEN_INT)
163     {
164       *result = negate ? -scanner->value.v_int : scanner->value.v_int;
165       return G_TOKEN_NONE;
166     }
167   else
168     return G_TOKEN_FLOAT;
169 }
170 
171 static GTokenType
ifsvals_parse_element(GScanner * scanner,AffElementVals * result)172 ifsvals_parse_element (GScanner       *scanner,
173                        AffElementVals *result)
174 {
175   GTokenType token;
176   GTokenType expected_token;
177 
178   token = g_scanner_get_next_token (scanner);
179   if (token != G_TOKEN_LEFT_CURLY)
180     return G_TOKEN_LEFT_CURLY;
181 
182   token = g_scanner_get_next_token (scanner);
183   while (token != G_TOKEN_RIGHT_CURLY)
184     {
185       switch ((IfsComposeToken) token)
186 	{
187 	case TOKEN_X:
188 	  expected_token = parse_genuine_float (scanner, &result->x);
189 	  if (expected_token != G_TOKEN_NONE)
190 	    return expected_token;
191 	  break;
192 
193 	case TOKEN_Y:
194 	  expected_token = parse_genuine_float (scanner, &result->y);
195 	  if (expected_token != G_TOKEN_NONE)
196 	    return expected_token;
197 	  break;
198 
199 	case TOKEN_THETA:
200 	  expected_token = parse_genuine_float (scanner, &result->theta);
201 	  if (expected_token != G_TOKEN_NONE)
202 	    return expected_token;
203 	  break;
204 
205 	case TOKEN_SCALE:
206 	  expected_token = parse_genuine_float (scanner, &result->scale);
207 	  if (expected_token != G_TOKEN_NONE)
208 	    return expected_token;
209 	  break;
210 
211 	case TOKEN_ASYM:
212 	  expected_token = parse_genuine_float (scanner, &result->asym);
213 	  if (expected_token != G_TOKEN_NONE)
214 	    return expected_token;
215 	  break;
216 
217 	case TOKEN_SHEAR:
218 	  expected_token = parse_genuine_float (scanner, &result->shear);
219 	  if (expected_token != G_TOKEN_NONE)
220 	    return expected_token;
221 	  break;
222 
223 	case TOKEN_FLIP:
224 	  token = g_scanner_get_next_token (scanner);
225 	  if (token != G_TOKEN_INT)
226 	    return G_TOKEN_INT;
227 
228 	  result->flip = scanner->value.v_int;
229 	  break;
230 
231 	case TOKEN_RED_COLOR:
232 	  token = ifsvals_parse_color (scanner, &result->red_color);
233 	  if (token != G_TOKEN_NONE)
234 	    return token;
235 	  break;
236 
237 	case TOKEN_GREEN_COLOR:
238 	  token = ifsvals_parse_color (scanner, &result->green_color);
239 	  if (token != G_TOKEN_NONE)
240 	    return token;
241 	  break;
242 
243 	case TOKEN_BLUE_COLOR:
244 	  token = ifsvals_parse_color (scanner, &result->blue_color);
245 	  if (token != G_TOKEN_NONE)
246 	    return token;
247 	  break;
248 
249 	case TOKEN_BLACK_COLOR:
250 	  token = ifsvals_parse_color (scanner, &result->black_color);
251 	  if (token != G_TOKEN_NONE)
252 	    return token;
253 	  break;
254 
255 	case TOKEN_TARGET_COLOR:
256 	  token = ifsvals_parse_color (scanner, &result->target_color);
257 	  if (token != G_TOKEN_NONE)
258 	    return token;
259 	  break;
260 
261 	case TOKEN_HUE_SCALE:
262 	  expected_token = parse_genuine_float (scanner, &result->hue_scale);
263 	  if (expected_token != G_TOKEN_NONE)
264 	    return expected_token;
265 	  break;
266 
267 	case TOKEN_VALUE_SCALE:
268 	  expected_token = parse_genuine_float (scanner, &result->value_scale);
269 	  if (expected_token != G_TOKEN_NONE)
270 	    return expected_token;
271 	  break;
272 
273 	case TOKEN_SIMPLE_COLOR:
274 	  token = g_scanner_get_next_token (scanner);
275 	  if (token != G_TOKEN_INT)
276 	    return G_TOKEN_INT;
277 
278 	  result->simple_color = scanner->value.v_int;
279 	  break;
280 
281 	case TOKEN_PROB:
282 	  token = g_scanner_get_next_token (scanner);
283 	  if (token == G_TOKEN_FLOAT)
284             result->prob = scanner->value.v_float;
285           else if (token == G_TOKEN_INT)
286             result->prob = scanner->value.v_int;
287           else
288 	    return G_TOKEN_FLOAT;
289 
290 	  break;
291 
292 	default:
293 	  return G_TOKEN_SYMBOL;
294 	}
295 
296       token = g_scanner_get_next_token (scanner);
297     }
298 
299   return G_TOKEN_NONE;
300 }
301 
302 /*************************************************************
303  * ifsvals_parse:
304  *     Read in ifsvalues from a GScanner
305  *   arguments:
306  *     scanner:
307  *     vals:
308  *     elements:
309  *
310  *   results:
311  *     If parsing succeeded, TRUE; otherwise FALSE, in which
312  *     case vals and elements are unchanged
313  *************************************************************/
314 
315 static gboolean
ifsvals_parse(GScanner * scanner,IfsComposeVals * vals,AffElement *** elements)316 ifsvals_parse (GScanner         *scanner,
317                IfsComposeVals   *vals,
318                AffElement     ***elements)
319 {
320   GTokenType      token, expected_token;
321   AffElement     *el;
322   IfsComposeVals  new_vals;
323   GimpRGB         color;
324 
325   GList *el_list = NULL;
326   GList *tmp_list;
327   gint   i;
328 
329   new_vals = *vals;
330   new_vals.num_elements = 0;
331   i = 0;
332 
333   expected_token = G_TOKEN_NONE;
334   while (expected_token == G_TOKEN_NONE)
335     {
336       token = g_scanner_get_next_token (scanner);
337 
338       if (g_scanner_eof (scanner))
339 	  break;
340 
341       switch ((IfsComposeToken) token)
342 	{
343 	case TOKEN_ITERATIONS:
344 	  token = g_scanner_get_next_token (scanner);
345 	  if (token == G_TOKEN_INT)
346 	    new_vals.iterations = scanner->value.v_int;
347 	  else
348 	    expected_token = G_TOKEN_INT;
349 	  break;
350 
351 	case TOKEN_MAX_MEMORY:
352 	  token = g_scanner_get_next_token (scanner);
353 	  if (token == G_TOKEN_INT)
354 	    new_vals.max_memory = scanner->value.v_int;
355 	  else
356 	    expected_token = G_TOKEN_INT;
357 	  break;
358 
359 	case TOKEN_SUBDIVIDE:
360 	  token = g_scanner_get_next_token (scanner);
361 	  if (token == G_TOKEN_INT)
362 	    new_vals.subdivide = scanner->value.v_int;
363 	  else
364 	    expected_token = G_TOKEN_INT;
365 	  break;
366 
367 	case TOKEN_RADIUS:
368 	  expected_token = parse_genuine_float (scanner, &new_vals.radius);
369 	  break;
370 
371 	case TOKEN_ASPECT_RATIO:
372 	  expected_token = parse_genuine_float (scanner, &new_vals.aspect_ratio);
373 	  break;
374 
375 	case TOKEN_CENTER_X:
376 	  expected_token = parse_genuine_float (scanner, &new_vals.center_x);
377 	  break;
378 
379 	case TOKEN_CENTER_Y:
380 	  expected_token = parse_genuine_float (scanner, &new_vals.center_y);
381 	  break;
382 
383 	case TOKEN_ELEMENT:
384 	  el = aff_element_new (0.0,0.0, &color, ++i);
385 	  expected_token = ifsvals_parse_element (scanner, &el->v);
386 
387 	  if (expected_token == G_TOKEN_NONE)
388 	    {
389 	      el_list = g_list_prepend (el_list, el);
390 	      new_vals.num_elements++;
391 	    }
392 	  else
393 	    aff_element_free (el);
394 
395 	  break;
396 
397 	default:
398 	  expected_token = G_TOKEN_SYMBOL;
399 	}
400     }
401 
402   if (expected_token != G_TOKEN_NONE)
403     {
404       g_scanner_unexp_token (scanner,
405 			     expected_token,
406 			     NULL,
407 			     NULL,
408 			     NULL,
409 			     "using default values...",
410 			     TRUE);
411       g_list_free_full (el_list, (GDestroyNotify) g_free);
412       return FALSE;
413     }
414 
415   *vals = new_vals;
416 
417   el_list = g_list_reverse (el_list);
418   *elements = g_new (AffElement *, new_vals.num_elements);
419 
420   tmp_list = el_list;
421   for (i=0; i<new_vals.num_elements; i++)
422     {
423       (*elements)[i] = tmp_list->data;
424       tmp_list = tmp_list->next;
425     }
426 
427   g_list_free (el_list);
428 
429   return TRUE;
430 }
431 
432 gboolean
ifsvals_parse_string(const gchar * str,IfsComposeVals * vals,AffElement *** elements)433 ifsvals_parse_string (const gchar      *str,
434                       IfsComposeVals   *vals,
435                       AffElement     ***elements)
436 {
437   GScanner *scanner = g_scanner_new (NULL);
438   gboolean  result;
439   gint      i;
440 
441   scanner->config->symbol_2_token = TRUE;
442   scanner->config->scan_identifier_1char = TRUE;
443   scanner->input_name = "IfsCompose Saved Data";
444 
445   for (i = 0; i < G_N_ELEMENTS (symbols); i++)
446     g_scanner_scope_add_symbol (scanner, 0,
447                                 symbols[i].name,
448                                 GINT_TO_POINTER (symbols[i].token));
449 
450   g_scanner_input_text (scanner, str, strlen (str));
451 
452   result = ifsvals_parse (scanner, vals, elements);
453 
454   g_scanner_destroy (scanner);
455 
456   return result;
457 }
458 
459 /*************************************************************
460  * ifsvals_stringify:
461  *     Stringify a set of vals and elements
462  *   arguments:
463  *     vals:
464  *     elements
465  *   results:
466  *     The stringified result (free with g_free)
467  *************************************************************/
468 
469 gchar *
ifsvals_stringify(IfsComposeVals * vals,AffElement ** elements)470 ifsvals_stringify (IfsComposeVals  *vals,
471                    AffElement     **elements)
472 {
473   gint     i;
474   gchar    buf[G_ASCII_DTOSTR_BUF_SIZE];
475   gchar    cbuf[3][G_ASCII_DTOSTR_BUF_SIZE];
476   GString *result;
477 
478   result = g_string_new (NULL);
479 
480   g_string_append_printf (result, "iterations %d\n", vals->iterations);
481   g_string_append_printf (result, "max_memory %d\n", vals->max_memory);
482   g_string_append_printf (result, "subdivide %d\n", vals->subdivide);
483   g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, vals->radius);
484   g_string_append_printf (result, "radius %s\n", buf);
485   g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, vals->aspect_ratio);
486   g_string_append_printf (result, "aspect_ratio %s\n", buf);
487   g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, vals->center_x);
488   g_string_append_printf (result, "center_x %s\n", buf);
489   g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, vals->center_y);
490   g_string_append_printf (result, "center_y %s\n", buf);
491 
492   for (i=0; i<vals->num_elements; i++)
493     {
494       g_string_append (result, "element {\n");
495       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.x);
496       g_string_append_printf (result, "    x %s\n", buf);
497       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.y);
498       g_string_append_printf (result, "    y %s\n", buf);
499       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.theta);
500       g_string_append_printf (result, "    theta %s\n", buf);
501       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.scale);
502       g_string_append_printf (result, "    scale %s\n", buf);
503       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.asym);
504       g_string_append_printf (result, "    asym %s\n", buf);
505       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.shear);
506       g_string_append_printf (result, "    shear %s\n", buf);
507       g_string_append_printf (result, "    flip %d\n", elements[i]->v.flip);
508 
509       g_ascii_dtostr (cbuf[0], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.red_color.r);
510       g_ascii_dtostr (cbuf[1], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.red_color.g);
511       g_ascii_dtostr (cbuf[2], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.red_color.b);
512       g_string_append_printf (result, "    red_color { %s,%s,%s }\n",
513                               cbuf[0], cbuf[1], cbuf[2]);
514 
515       g_ascii_dtostr (cbuf[0], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.green_color.r);
516       g_ascii_dtostr (cbuf[1], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.green_color.g);
517       g_ascii_dtostr (cbuf[2], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.green_color.b);
518       g_string_append_printf (result, "    green_color { %s,%s,%s }\n",
519                               cbuf[0], cbuf[1], cbuf[2]);
520 
521       g_ascii_dtostr (cbuf[0], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.blue_color.r);
522       g_ascii_dtostr (cbuf[1], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.blue_color.g);
523       g_ascii_dtostr (cbuf[2], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.blue_color.b);
524       g_string_append_printf (result, "    blue_color { %s,%s,%s }\n",
525                               cbuf[0], cbuf[1], cbuf[2]);
526 
527       g_ascii_dtostr (cbuf[0], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.black_color.r);
528       g_ascii_dtostr (cbuf[1], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.black_color.g);
529       g_ascii_dtostr (cbuf[2], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.black_color.b);
530       g_string_append_printf (result, "    black_color { %s,%s,%s }\n",
531                               cbuf[0], cbuf[1], cbuf[2]);
532 
533       g_ascii_dtostr (cbuf[0], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.target_color.r);
534       g_ascii_dtostr (cbuf[1], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.target_color.g);
535       g_ascii_dtostr (cbuf[2], G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.target_color.b);
536       g_string_append_printf (result, "    target_color { %s,%s,%s }\n",
537                               cbuf[0], cbuf[1], cbuf[2]);
538 
539       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.hue_scale);
540       g_string_append_printf (result, "    hue_scale %s\n", buf);
541       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.value_scale);
542       g_string_append_printf (result, "    value_scale %s\n", buf);
543       g_string_append_printf (result, "    simple_color %d\n",
544                               elements[i]->v.simple_color);
545       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, elements[i]->v.prob);
546       g_string_append_printf (result, "    prob %s\n", buf);
547       g_string_append (result, "}\n");
548     }
549 
550   return g_string_free (result, FALSE);
551 }
552