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