1 /*
2 FLAM3 - cosmic recursive fractal flames
3 Copyright (C) 1992-2009 Spotworks LLC
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "private.h"
20 #include "palettes.h"
21
22 lib_palette *the_palettes = NULL;
23 int npalettes;
24
parse_palettes(xmlNode * node)25 static void parse_palettes(xmlNode *node) {
26 xmlAttrPtr attr;
27 char *val;
28 lib_palette *pal;
29 int hex_error = 0;
30
31 while (node) {
32 if (node->type == XML_ELEMENT_NODE && !xmlStrcmp(node->name, (const xmlChar *)"palette")) {
33 attr = node->properties;
34 pal = &the_palettes[npalettes];
35 memset(pal, 0, sizeof(lib_palette));
36
37 while (attr) {
38 val = (char *) xmlGetProp(node, attr->name);
39 if (!xmlStrcmp(attr->name, (const xmlChar *)"data")) {
40 int count = 256;
41 int c_idx = 0;
42 int r,g,b;
43 int col_count = 0;
44 int sscanf_ret;
45
46 c_idx=0;
47 col_count = 0;
48 hex_error = 0;
49
50 do {
51 sscanf_ret = sscanf((char *)&(val[c_idx]),"00%2x%2x%2x",&r,&g,&b);
52 if (sscanf_ret != 3) {
53 fprintf(stderr,"error: problem reading hexadecimal color data '%8s'\n",&val[c_idx]);
54 hex_error = 1;
55 break;
56 }
57 c_idx += 8;
58 while (isspace( (int)val[c_idx]))
59 c_idx++;
60
61 pal->colors[col_count][0] = r;
62 pal->colors[col_count][1] = g;
63 pal->colors[col_count][2] = b;
64
65 col_count++;
66 } while (col_count<count);
67 } else if (!xmlStrcmp(attr->name, (const xmlChar *)"number")) {
68 pal->number = atoi(val);
69 } else if (!xmlStrcmp(attr->name, (const xmlChar *)"name")) {
70 strncpy(pal->name, val, flam3_name_len);
71 pal->name[flam3_name_len-1] = 0;
72 }
73
74 xmlFree(val);
75 attr = attr->next;
76 }
77
78 if (hex_error == 0) {
79 npalettes++;
80 the_palettes = realloc(the_palettes, (1 + npalettes) * sizeof(lib_palette));
81 }
82 } else
83 parse_palettes(node->children);
84
85 node = node->next;
86 }
87 }
88
init_palettes(char * filename)89 static int init_palettes(char *filename) {
90 FILE *fp;
91 xmlDocPtr doc;
92 xmlNode *rootnode;
93 int i, c, slen = 5000;
94 char *s;
95
96 fp = fopen(filename, "rb");
97 if (NULL == fp) {
98 fprintf(stderr, "flam3: could not open palette file ");
99 perror(filename);
100 return(-1);
101 }
102
103 /* Incrementally read XML file into a string */
104 s = malloc(slen);
105 i = 0;
106 do {
107 c = getc(fp);
108 if (EOF == c) {
109 if (ferror(fp)) {
110 perror(filename);
111 return(-1);
112 }
113 break;
114 }
115 s[i++] = c;
116 if (i == slen-1) {
117 slen *= 2;
118 s = realloc(s, slen);
119 }
120 } while (1);
121
122 fclose(fp);
123 s[i] = 0;
124
125 doc = xmlReadMemory(s, (int)strlen(s), filename, NULL, XML_PARSE_NONET);
126 if (NULL == doc) {
127 fprintf(stderr, "error parsing %s (%s).\n", filename, s);
128 return(-1);
129 }
130 rootnode = xmlDocGetRootElement(doc);
131 the_palettes = malloc(sizeof(lib_palette));
132 npalettes = 0;
133 parse_palettes(rootnode);
134 xmlFreeDoc(doc);
135
136 free(s);
137 xmlCleanupParser();
138 return(1);
139 }
140
flam3_get_palette(int n,flam3_palette c,double hue_rotation)141 int flam3_get_palette(int n, flam3_palette c, double hue_rotation) {
142 int cmap_len = 256;
143 int idx, i, j, rcode;
144
145 // set palette to all white in case there are problems
146 for (i = 0; i < cmap_len; i++) {
147 c[i].index = i;
148 for (j = 0; j < 4; j++)
149 c[i].color[j] = 1.0;
150 }
151
152 if (NULL == the_palettes) {
153 char *d = getenv("flam3_palettes");
154 rcode = init_palettes(d ? d : (PACKAGE_DATA_DIR "/flam3-palettes.xml"));
155 if (rcode<0) {
156 fprintf(stderr,"error reading xml palette file, setting to all white\n");
157 return(-1);
158 }
159 }
160
161 if (flam3_palette_random == n)
162 n = the_palettes[random()%npalettes].number;
163
164 for (idx = 0; idx < npalettes; idx++) {
165
166 if (n == the_palettes[idx].number) {
167 /* Loop over elements of cmap */
168 for (i = 0; i < cmap_len; i++) {
169 int ii = (i * 256) / cmap_len;
170 double rgb[3], hsv[3];
171
172 /* Colors are in 0-1 space */
173 for (j = 0; j < 3; j++)
174 rgb[j] = the_palettes[idx].colors[ii][j] / 255.0;
175
176 rgb2hsv(rgb, hsv);
177 hsv[0] += hue_rotation * 6.0;
178 hsv2rgb(hsv, rgb);
179
180 c[i].index = i;
181
182 for (j = 0; j < 3; j++)
183 c[i].color[j] = rgb[j];
184
185 c[i].color[3] = 1.0;
186 }
187
188 return n;
189 }
190 }
191
192 fprintf(stderr, "warning: palette number %d not found, using white.\n", n);
193
194 return(-1);
195 }
196
197 /* rgb 0 - 1,
198 h 0 - 6, s 0 - 1, v 0 - 1 */
rgb2hsv(rgb,hsv)199 void rgb2hsv(rgb, hsv)
200 double *rgb; double *hsv;
201 {
202 double rd, gd, bd, h, s, v, max, min, del, rc, gc, bc;
203
204 rd = rgb[0];
205 gd = rgb[1];
206 bd = rgb[2];
207
208 /* compute maximum of rd,gd,bd */
209 if (rd>=gd) { if (rd>=bd) max = rd; else max = bd; }
210 else { if (gd>=bd) max = gd; else max = bd; }
211
212 /* compute minimum of rd,gd,bd */
213 if (rd<=gd) { if (rd<=bd) min = rd; else min = bd; }
214 else { if (gd<=bd) min = gd; else min = bd; }
215
216 del = max - min;
217 v = max;
218 if (max != 0.0) s = (del) / max;
219 else s = 0.0;
220
221 h = 0;
222 if (s != 0.0) {
223 rc = (max - rd) / del;
224 gc = (max - gd) / del;
225 bc = (max - bd) / del;
226
227 if (rd==max) h = bc - gc;
228 else if (gd==max) h = 2 + rc - bc;
229 else if (bd==max) h = 4 + gc - rc;
230
231 if (h<0) h += 6;
232 }
233
234 hsv[0] = h;
235 hsv[1] = s;
236 hsv[2] = v;
237 }
238
239
240 /* h 0 - 6, s 0 - 1, v 0 - 1
241 rgb 0 - 1 */
hsv2rgb(hsv,rgb)242 void hsv2rgb(hsv, rgb)
243 double *hsv;
244 double *rgb;
245 {
246 double h = hsv[0], s = hsv[1], v = hsv[2];
247 int j;
248 double rd, gd, bd;
249 double f, p, q, t;
250
251 while (h >= 6.0) h = h - 6.0;
252 while (h < 0.0) h = h + 6.0;
253 j = (int) floor(h);
254 f = h - j;
255 p = v * (1-s);
256 q = v * (1 - (s*f));
257 t = v * (1 - (s*(1 - f)));
258
259 switch (j) {
260 case 0: rd = v; gd = t; bd = p; break;
261 case 1: rd = q; gd = v; bd = p; break;
262 case 2: rd = p; gd = v; bd = t; break;
263 case 3: rd = p; gd = q; bd = v; break;
264 case 4: rd = t; gd = p; bd = v; break;
265 case 5: rd = v; gd = p; bd = q; break;
266 default: rd = v; gd = t; bd = p; break;
267 }
268
269 rgb[0] = rd;
270 rgb[1] = gd;
271 rgb[2] = bd;
272 }
273
flam3_calc_alpha(double density,double gamma,double linrange)274 double flam3_calc_alpha(double density, double gamma, double linrange) {
275
276 double dnorm = density;
277 double funcval = pow(linrange, gamma);
278 double frac,alpha;
279
280 if (dnorm>0) {
281 if (dnorm < linrange) {
282 frac = dnorm/linrange;
283 alpha = (1.0-frac) * dnorm * (funcval / linrange) + frac * pow(dnorm,gamma);
284 } else
285 alpha = pow(dnorm,gamma);
286 } else
287 alpha = 0;
288
289 return(alpha);
290 }
291
flam3_calc_newrgb(double * cbuf,double ls,double highpow,double * newrgb)292 void flam3_calc_newrgb(double *cbuf, double ls, double highpow, double *newrgb) {
293
294 int rgbi;
295 double newls,lsratio;
296 double newhsv[3];
297 double a, maxa=-1.0, maxc=0;
298 double adjhlp;
299
300 if (ls==0.0 || (cbuf[0]==0.0 && cbuf[1]==0.0 && cbuf[2]==0.0)) {
301 newrgb[0] = 0.0;
302 newrgb[1] = 0.0;
303 newrgb[2] = 0.0;
304 return;
305 }
306
307 /* Identify the most saturated channel */
308 for (rgbi=0;rgbi<3;rgbi++) {
309 a = ls * (cbuf[rgbi]/PREFILTER_WHITE);
310 if (a>maxa) {
311 maxa = a;
312 maxc = cbuf[rgbi]/PREFILTER_WHITE;
313 }
314 }
315
316 /* If a channel is saturated and we have a non-negative highlight power */
317 /* modify the color to prevent hue shift */
318 if (maxa>255 && highpow>=0.0) {
319 newls = 255.0/maxc;
320 lsratio = pow(newls/ls,highpow);
321
322 /* Calculate the max-value color (ranged 0 - 1) */
323 for (rgbi=0;rgbi<3;rgbi++)
324 newrgb[rgbi] = newls*(cbuf[rgbi]/PREFILTER_WHITE)/255.0;
325
326 /* Reduce saturation by the lsratio */
327 rgb2hsv(newrgb,newhsv);
328 newhsv[1] *= lsratio;
329 hsv2rgb(newhsv,newrgb);
330
331 for (rgbi=0;rgbi<3;rgbi++)
332 newrgb[rgbi] *= 255.0;
333
334 } else {
335 newls = 255.0/maxc;
336 adjhlp = -highpow;
337 if (adjhlp>1)
338 adjhlp=1;
339 if (maxa<=255)
340 adjhlp=1.0;
341
342 /* Calculate the max-value color (ranged 0 - 1) interpolated with the old behaviour */
343 for (rgbi=0;rgbi<3;rgbi++)
344 newrgb[rgbi] = ((1.0-adjhlp)*newls + adjhlp*ls)*(cbuf[rgbi]/PREFILTER_WHITE);
345
346 // for (rgbi=0;rgbi<3;rgbi++)
347 // newrgb[rgbi] = ls*(cbuf[rgbi]/PREFILTER_WHITE);
348 }
349 }
350
random_xform(flam3_genome * g,int excluded)351 static int random_xform(flam3_genome *g, int excluded) {
352 int ntries = 0;
353 while (ntries++ < 100) {
354 int i = random() % g->num_xforms;
355 if (g->xform[i].density > 0.0 && i != excluded)
356 return i;
357 }
358 return -1;
359 }
360
361
try_colors(flam3_genome * g,int color_resolution)362 static double try_colors(flam3_genome *g, int color_resolution) {
363 int *hist;
364 int i, hits, res = color_resolution;
365 int res3 = res * res * res;
366 flam3_frame f;
367 unsigned char *image, *p;
368 flam3_genome saved;
369 double scalar;
370 int pixtotal;
371 stat_struct stats;
372
373 memset(&saved, 0, sizeof(flam3_genome));
374
375 flam3_copy(&saved, g);
376
377 g->sample_density = 1;
378 g->spatial_oversample = 1;
379 g->estimator = 0.0;
380
381 /* Scale the image so that the total number of pixels is ~10000 */
382 pixtotal = g->width * g->height;
383 scalar = sqrt( 10000.0 / (double)pixtotal);
384 g->width *= scalar;
385 g->height *= scalar;
386 g->pixels_per_unit *= scalar;
387
388 // g->width = 100; // XXX keep aspect ratio
389 // g->height = 100;
390 // g->pixels_per_unit = 50;
391 g->nbatches = 1;
392 g->ntemporal_samples = 1;
393
394 // f.temporal_filter_radius = 0.0;
395 flam3_init_frame(&f);
396 f.bits = 33;
397 f.bytes_per_channel=1;
398 f.verbose = 0;
399 f.genomes = g;
400 f.ngenomes = 1;
401 f.earlyclip = 1;
402 f.pixel_aspect_ratio = 1.0;
403 f.progress = 0;
404 f.nthreads = 1;
405 f.sub_batch_size = 10000;
406
407 image = (unsigned char *) calloc(g->width * g->height, 3);
408 if (flam3_render(&f, image, flam3_field_both, 3, 0, &stats)) {
409 fprintf(stderr,"Error rendering test image for trycolors. Aborting.\n");
410 return(-1);
411 }
412
413 hist = calloc(sizeof(int), res3);
414 p = image;
415 for (i = 0; i < g->height * g->width; i++) {
416 hist[(p[0] * res / 256) +
417 (p[1] * res / 256) * res +
418 (p[2] * res / 256) * res * res]++;
419 p += 3;
420 }
421
422 if (0) {
423 int j, k;
424 for (i = 0; i < res; i++) {
425 fprintf(stderr, "\ni=%d: \n", i);
426 for (j = 0; j < res; j++) {
427 for (k = 0; k < res; k++) {
428 fprintf(stderr, " %5d", hist[i * res * res + j * res + k]);
429 }
430 fprintf(stderr, "\n");
431 }
432 }
433 }
434
435 hits = 0;
436 for (i = 0; i < res3; i++) {
437 if (hist[i]) hits++;
438 }
439
440 free(hist);
441 free(image);
442
443 g->sample_density = saved.sample_density;
444 g->width = saved.width;
445 g->height = saved.height;
446 g->spatial_oversample = saved.spatial_oversample;
447 g->pixels_per_unit = saved.pixels_per_unit;
448 g->nbatches = saved.nbatches;
449 g->ntemporal_samples = saved.ntemporal_samples;
450 g->estimator = saved.estimator;
451
452 /* Free xform storage */
453 clear_cp(&saved,flam3_defaults_on);
454
455 return (double) (hits / res3);
456 }
457
change_colors(flam3_genome * g,int change_palette)458 static void change_colors(flam3_genome *g, int change_palette) {
459 int i;
460 int x0, x1;
461 if (change_palette) {
462 g->hue_rotation = 0.0;
463 g->palette_index = flam3_get_palette(flam3_palette_random, g->palette, 0.0);
464 if (g->palette_index < 0)
465 fprintf(stderr,"error retrieving random palette, setting to all white\n");
466 }
467 for (i = 0; i < g->num_xforms; i++) {
468 g->xform[i].color = flam3_random01();
469 }
470 x0 = random_xform(g, -1);
471 x1 = random_xform(g, x0);
472 if (x0 >= 0 && (random()&1)) g->xform[x0].color = 0.0;
473 if (x1 >= 0 && (random()&1)) g->xform[x1].color = 1.0;
474 }
475
flam3_improve_colors(flam3_genome * g,int ntries,int change_palette,int color_resolution)476 void flam3_improve_colors(flam3_genome *g, int ntries, int change_palette, int color_resolution) {
477 int i;
478 double best, b;
479 flam3_genome best_genome;
480
481 memset(&best_genome, 0, sizeof(flam3_genome));
482
483 best = try_colors(g, color_resolution);
484 if (best<0) {
485 fprintf(stderr,"error in try_colors, skipping flam3_improve_colors\n");
486 return;
487 }
488
489 flam3_copy(&best_genome,g);
490 for (i = 0; i < ntries; i++) {
491 change_colors(g, change_palette);
492 b = try_colors(g, color_resolution);
493 if (b < 0) {
494 fprintf(stderr,"error in try_colors, aborting tries\n");
495 break;
496 }
497 if (b > best) {
498 best = b;
499 flam3_copy(&best_genome,g);
500 }
501 }
502
503 flam3_copy(g,&best_genome);
504 clear_cp(&best_genome,flam3_defaults_on);
505 }
506
507