1 #include "compat.h"
2 
3 struct hsv {
4     float h, s, v;
5 };
6 
7 struct rgb {
8     float r, g, b;
9 };
10 
11 struct gradient {
12     int start, len, chompends;
13     struct hsv startcolour, endcolour;
14 };
15 
16 float min2(float x, float y);
17 float max2(float x, float y);
18 void convertHSVtoRGB(struct hsv *hsv, struct rgb *rgb);
19 int showusage(void);
20 int readscript(char *fn);
21 
22 struct gradient ramps[256];
23 int nramps = 0;
24 
main(int argc,char ** argv)25 int main(int argc, char **argv)
26 {
27     struct hsv palette[256], lerpstep, lerped;
28     struct rgb rgbidx;
29     unsigned char rgbout[3];
30     int idx, step, rampnum;
31     FILE* fh;
32     char const * outfile = "palette.dat";
33 
34     memset(palette,0,sizeof(palette));
35 
36     if (argc < 2) return showusage();
37     if (readscript(argv[1])) return 1;
38     if (argc >= 3) outfile = argv[2];
39 
40     for (rampnum = 0; rampnum < nramps; rampnum++) {
41         idx = ramps[rampnum].start;
42         step = ramps[rampnum].len;
43         if (ramps[rampnum].chompends & 1) step++;
44         if (ramps[rampnum].chompends & 2) step++;
45         lerpstep.h = (ramps[rampnum].endcolour.h - ramps[rampnum].startcolour.h) / (float)step;
46         lerpstep.s = (ramps[rampnum].endcolour.s - ramps[rampnum].startcolour.s) / (float)step;
47         lerpstep.v = (ramps[rampnum].endcolour.v - ramps[rampnum].startcolour.v) / (float)step;
48         lerped = ramps[rampnum].startcolour;
49         if (ramps[rampnum].chompends & 1) {
50             step--;
51             lerped.h += lerpstep.h;
52             lerped.s += lerpstep.s;
53             lerped.v += lerpstep.v;
54         }
55         if (ramps[rampnum].chompends & 2) step--;
56 
57         for (; step > 0; step--,idx++) {
58             palette[idx].h = lerped.h;
59             palette[idx].s = lerped.s;
60             palette[idx].v = lerped.v;
61             lerped.h += lerpstep.h;
62             lerped.s += lerpstep.s;
63             lerped.v += lerpstep.v;
64         }
65     }
66 
67     fh = fopen(outfile,"wb");
68     if (!fh) return 1;
69 
70     for (idx=0; idx<256; idx++) {
71         convertHSVtoRGB(&palette[idx], &rgbidx);
72         //printf("Index %d: r=%g g=%g b=%g\n",idx,rgbidx.r,rgbidx.g,rgbidx.b);
73         rgbout[0] = (unsigned char)min2(255,max2(0,(int)(rgbidx.r * 255.0))) >> 2;
74         rgbout[1] = (unsigned char)min2(255,max2(0,(int)(rgbidx.g * 255.0))) >> 2;
75         rgbout[2] = (unsigned char)min2(255,max2(0,(int)(rgbidx.b * 255.0))) >> 2;
76         fwrite(rgbout,3,1,fh);
77     }
78 
79     fclose(fh);
80 
81     return 0;
82 }
83 
min2(float x,float y)84 float min2(float x, float y)
85 {
86     return x < y ? x : y;
87 }
88 
max2(float x,float y)89 float max2(float x, float y)
90 {
91     return x > y ? x : y;
92 }
93 
94 // http://www.cs.rit.edu/~ncs/color/t_convert.html
convertHSVtoRGB(struct hsv * hsv,struct rgb * rgb)95 void convertHSVtoRGB(struct hsv *hsv, struct rgb *rgb)
96 {
97     int i;
98     float f, p, q, t;
99     if( hsv->s == 0 ) {
100         // achromatic (grey)
101         rgb->r = rgb->g = rgb->b = hsv->v;
102         return;
103     }
104     hsv->h /= 60;            // sector 0 to 5
105     i = floor( hsv->h );
106     f = hsv->h - i;            // factorial part of h
107     p = hsv->v * ( 1 - hsv->s );
108     q = hsv->v * ( 1 - hsv->s * f );
109     t = hsv->v * ( 1 - hsv->s * ( 1 - f ) );
110     switch( i ) {
111         case 0:
112             rgb->r = hsv->v;
113             rgb->g = t;
114             rgb->b = p;
115             break;
116         case 1:
117             rgb->r = q;
118             rgb->g = hsv->v;
119             rgb->b = p;
120             break;
121         case 2:
122             rgb->r = p;
123             rgb->g = hsv->v;
124             rgb->b = t;
125             break;
126         case 3:
127             rgb->r = p;
128             rgb->g = q;
129             rgb->b = hsv->v;
130             break;
131         case 4:
132             rgb->r = t;
133             rgb->g = p;
134             rgb->b = hsv->v;
135             break;
136         default:        // case 5:
137             rgb->r = hsv->v;
138             rgb->g = p;
139             rgb->b = q;
140             break;
141     }
142 }
143 
showusage(void)144 int showusage(void)
145 {
146     puts("mkpalette <palettescript.txt> [outputfile]");
147     puts("If outputfile is not given, palette.dat is assumed");
148 
149     puts("\nPalette script format:\n"
150     "  A line beginning with # is a comment, otherwise each line contains none\n"
151     "values separated by spaces defining the gradient:\n"
152     "\n"
153     "  startindex rangesize skip starthue startsat startval endhue endsat endval\n"
154     "\n"
155     "Any text after the end of a gradient description is ignored, so may use it\n"
156     "to describe the colour.\n"
157     "\n"
158     "* 'startindex' specifies the first palette index to write to\n"
159     "* 'rangesize' specifies the length of the gradient\n"
160     "* 'skip' specifies whether the first and/or last elements of the range should\n"
161     "  be ignored and with 'rangesize' elements interpolated between. This is so\n"
162     "  you can have a gradient starting at (potentially) pure black and ending at\n"
163     "  (potentially) pure white but without wasting a palette index on those colours\n"
164     "  if they already exist, eg. in a grey ramp.\n"
165     "  Legal values are 0 (no skipping), 1 (skip start), 2 (skip end),\n"
166     "  or 3 (skip start and end).\n"
167     "* 'starthue', 'startsat', 'startval' are integers specifying the beginning\n"
168     "  colour in Hue-Saturation-Value format.\n"
169     "  'starthue' should be in the range 0-360 indicating a degrees value\n"
170     "  'startsat' should be in the range 0-100 indicating a saturation percentage\n"
171     "  'startval' should be in the range 0-100 indicating an intensity percentage\n"
172     "* 'endhue', 'endsat', 'endval' specify the ending colour."
173     );
174     return 0;
175 }
176 
readscript(char * fn)177 int readscript(char *fn)
178 {
179     int start, len, skip, shue, ssat, sval, ehue, esat, eval;
180     FILE *fp;
181     char line[1024];
182 
183     fp = fopen(fn,"rt");
184     if (!fp) {
185         puts("Error opening palette script");
186         return 1;
187     }
188 
189     while (fgets(line,sizeof(line),fp)) {
190         {
191             // test for comment
192             char *p = line;
193             while (*p && (*p == ' ' || *p == '\t')) p++;
194             if (*p == '#') continue;
195         }
196 
197         if (sscanf(line, "%d %d %d %d %d %d %d %d %d", &start,&len,&skip,&shue,&ssat,&sval,&ehue,&esat,&eval) < 9)
198             continue;
199 
200         if (start < 0 || start > 255) {
201             printf("start index of %d is out of range 0-255\n", start);
202             continue;
203         }
204         if (len < 1 || len > 255) {
205             printf("length %d is out of range 1-255\n", len);
206             continue;
207         }
208         if (skip != (skip&3)) {
209             printf("skip value of %d is out of range 0-3\n", skip);
210             continue;
211         }
212         if (shue < 0 || shue > 360) {
213             printf("start hue %d is out of range 0-360\n", shue);
214             continue;
215         }
216         if (ssat < 0 || ssat > 100) {
217             printf("start saturation %d is out of range 0-100\n", ssat);
218             continue;
219         }
220         if (sval < 0 || sval > 100) {
221             printf("start value %d is out of range 0-100\n", sval);
222             continue;
223         }
224         if (ehue < 0 || ehue > 360) {
225             printf("end hue %d is out of range 0-360\n", shue);
226             continue;
227         }
228         if (esat < 0 || esat > 100) {
229             printf("end saturation %d is out of range 0-100\n", ssat);
230             continue;
231         }
232         if (eval < 0 || eval > 100) {
233             printf("end value %d is out of range 0-100\n", sval);
234             continue;
235         }
236 
237         ramps[nramps].start = start;
238         ramps[nramps].len = len;
239         ramps[nramps].chompends = skip;
240         ramps[nramps].startcolour.h = (float)shue;
241         ramps[nramps].startcolour.s = (float)ssat / 100.0;
242         ramps[nramps].startcolour.v = (float)sval / 100.0;
243         ramps[nramps].endcolour.h = (float)ehue;
244         ramps[nramps].endcolour.s = (float)esat / 100.0;
245         ramps[nramps].endcolour.v = (float)eval / 100.0;
246         nramps++;
247     }
248 
249     fclose(fp);
250     return 0;
251 }
252