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