1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5
6 * Redistributions of source code must retain the above copyright notice, this
7 * list of conditions and the following disclaimer.
8
9 * Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12
13 * The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <fontforge-config.h>
29
30 #include "charset.h"
31 #include "colorP.h"
32 #include "gdrawP.h"
33 #include "ustring.h"
34
RevColListFree(struct revcol * rc)35 static void RevColListFree(struct revcol *rc) {
36 struct revcol *next;
37
38 while ( rc!=NULL ) {
39 next = rc->next;
40 free(rc);
41 rc = next;
42 }
43 }
44
45 static const GCol white = { 0xff, 0xff, 0xff, 1 }, black = { 0, 0, 0, 0 };
46
_GImage_GetIndexedPixel(Color col,RevCMap * rev)47 const GCol *_GImage_GetIndexedPixel(Color col,RevCMap *rev) {
48 int val, t, best, r, g, b;
49 struct revitem *this;
50 struct revcol *test, *bestcol;
51
52 if ( rev==NULL ) { /* Mono */
53 if ( COLOR2WHITE(col) )
54 return( &white );
55 else
56 return( &black );
57 }
58 if ( rev->is_grey ) {
59 val = COLOR2GREY(col);
60 return( &rev->greys[val]);
61 }
62
63 reverse_clut:
64 r = COLOR_RED(col); g = COLOR_GREEN(col); b = COLOR_BLUE(col);
65 if ( rev->div_mul==1 ) {
66 val = r>>rev->div_shift;
67 val = (val<<rev->side_shift)+(g>>rev->div_shift);
68 val = (val<<rev->side_shift)+(b>>rev->div_shift);
69 } else {
70 val = ((r+rev->div_add)*rev->div_mul)>>rev->div_shift;
71 t = ((g+rev->div_add)*rev->div_mul)>>rev->div_shift;
72 val = (val*rev->side_cnt)+t;
73 t = ((b+rev->div_add)*rev->div_mul)>>rev->div_shift;
74 val = (val*rev->side_cnt)+t;
75 }
76 this = &rev->cube[val];
77 if ( this->sub!=NULL ) {
78 col &= rev->mask;
79 rev = this->sub;
80 goto reverse_clut;
81 }
82
83 test = this->cols[0];
84 if ( test->next==NULL )
85 return( (GCol *) test );
86
87 if (( best = (r-test->red))<0 ) best = -best;
88 if (( t = (g-test->green))<0 ) t = -t; best += t;
89 if (( t = (b-test->blue))<0 ) t = -t; best += t;
90 bestcol = test;
91 for ( test=test->next; test!=NULL; test = test->next ) {
92 if (( val = (r-test->red))<0 ) val = -val;
93 if (( t = (g-test->green))<0 ) t = -t; val += t;
94 if (( t = (b-test->blue))<0 ) t = -t; val += t;
95 if ( val<best ) {
96 val = best;
97 bestcol = test;
98 }
99 }
100 return( (GCol *) bestcol );
101 }
102
rccnt(struct revcol * cols)103 static int rccnt( struct revcol *cols) {
104 int cnt = 0;
105 while ( cols!=NULL ) {
106 ++cnt;
107 cols = cols->next;
108 }
109 return( cnt );
110 }
111
add_adjacent(struct revcol * test,struct revcol * old,Color col,int dmax)112 static struct revcol *add_adjacent(struct revcol *test, struct revcol *old, Color col, int dmax) {
113 int r=COLOR_RED(col), g=COLOR_GREEN(col), b=COLOR_BLUE(col);
114 int off, t, bestoff;
115 struct revcol *best=NULL;
116
117 if ( test==NULL || test->dist>dmax )
118 return( old );
119 bestoff = 3*255;
120 for ( test=test; test!=NULL; test = test->next ) {
121 if ( (off = (r-test->red))<0 ) off = -off;
122 if ( (t = (g-test->green))<0 ) t = -t; off +=t;
123 if ( (b = (g-test->blue))<0 ) t = -t; off +=t;
124 if ( off<bestoff ) {
125 bestoff = off;
126 best = test;
127 }
128 }
129 if ( old!=NULL ) {
130 if ( (off = (r-old->red))<0 ) off = -off;
131 if ( (t = (g-old->green))<0 ) t = -t; off +=t;
132 if ( (b = (g-old->blue))<0 ) t = -t; off +=t;
133 if ( off<bestoff ) {
134 bestoff = off;
135 best = old;
136 }
137 }
138 if ( best!=old ) {
139 if ( old==NULL ) old = calloc(1,sizeof(struct revcol));
140 *old = *best;
141 old->next = NULL;
142 ++old->dist;
143 }
144 return( old );
145 }
146
addrevcol(struct revcol * copy,struct revcol * old,int dist)147 static struct revcol *addrevcol(struct revcol *copy,struct revcol *old, int dist) {
148 struct revcol *rc = malloc(sizeof(struct revcol));
149
150 *rc = *copy;
151 rc->next = old;
152 rc->dist = dist;
153 return( rc );
154 }
155
_GClutReverse(int side_cnt,int range,struct revcol * basecol,struct revcol * cols,struct revcol * outercols)156 static RevCMap *_GClutReverse(int side_cnt,int range,struct revcol *basecol,
157 struct revcol *cols,struct revcol *outercols) {
158 RevCMap *rev;
159 int i, s2, s3, side_size;
160 struct revcol *test;
161 int changed, dmax, anynulls;
162
163 rev = calloc(1,sizeof(RevCMap));
164
165 rev->side_cnt = side_cnt;
166 rev->range = range;
167 if ( side_cnt<0 ) {
168 /* special cases ... */
169 side_cnt = -side_cnt;
170 rev->side_cnt = side_cnt;
171 if ( side_cnt==6 ) { /* Used for the inefficient standard 6 by cube */
172 rev->div_mul = div_tables[0x33][0];
173 rev->div_shift = div_tables[0x33][1];
174 rev->div_add = 0x19;
175 }
176 side_size = 0x33;
177 } else if ( div_tables[side_cnt][0]==1 ) {
178 rev->side_shift = div_tables[side_cnt][1];
179 rev->div_shift = div_tables[range][1]-rev->side_shift;
180 rev->div_mul = 1;
181 rev->mask = ( 0x010101 * ((-1<rev->div_shift)&0xff) );
182 side_size = 1<<(rev->div_shift);
183 } else {
184 side_size = (range+side_cnt-1)/side_cnt;
185 rev->div_mul = div_tables[side_cnt][0];
186 rev->div_shift = div_tables[side_cnt][1];
187 }
188
189 rev->cube = calloc(side_cnt*side_cnt*side_cnt,sizeof(struct revitem));
190 for ( test = cols; test!=NULL; test = test->next ) {
191 int pos, dist;
192 int r,g,b;
193 int rbase = (test->red-basecol->red)/side_size, gbase = (test->green-basecol->green)/side_size, bbase = (test->blue-basecol->blue)/side_size;
194 for ( r = (test->red-basecol->red-side_size/2)/side_size;
195 r<=(test->red-basecol->red+side_size/2)/side_size; ++r ) {
196 if ( r<0 || r==side_cnt )
197 continue;
198 for ( g = (test->green-basecol->green-side_size/2)/side_size;
199 g<=(test->green-basecol->green+side_size/2)/side_size; ++g ) {
200 if ( g<0 || g==side_cnt )
201 continue;
202 for ( b = (test->blue-basecol->blue-side_size/2)/side_size;
203 b<=(test->blue-basecol->blue+side_size/2)/side_size; ++b ) {
204 if ( b<0 || b==side_cnt )
205 continue;
206 pos = (r*side_cnt + g)*side_cnt + b;
207 dist = r!=rbase || g!=gbase || b!=bbase;
208 rev->cube[pos].cols[dist] = addrevcol(test,rev->cube[pos].cols[dist],
209 dist );
210 }
211 }
212 }
213 }
214
215 /* Ok. We just put every color into the sub-cube that it belongs in */
216 /* but that's not good enough. Say we are looking for a color that */
217 /* is on the edge of a sub-cube, then the best match for it may be */
218 /* in one of the adjacent sub-cubes, rather than within it's own */
219 /* So we also ran through the list of colors, casting our net a */
220 /* little wider. Essentially for every sub-cube we found all the colors */
221 /* within a subcube centered on the color */
222
223 /* Some sub-cubes will probably have no information in them. Fill them */
224 /* in by looking at near-by cubes */
225 s2 = side_cnt*side_cnt; s3 = s2*side_cnt;
226 for ( i=0; i<s3; ++i ) {
227 if ( rev->cube[i].cols[0]==NULL && rev->cube[i].cols[1]!=NULL ) {
228 rev->cube[i].cols[0] = rev->cube[i].cols[1];
229 rev->cube[i].cols[1] = NULL;
230 }
231 }
232 changed = true; anynulls = false; dmax = 0;
233 while ( changed || (anynulls && dmax<256) ) {
234 changed = false; anynulls = false;
235 for ( i=0; i<s3; ++i ) {
236 if ( rev->cube[i].cols[0]==NULL ) {
237 int r = i/s2, g = (i/side_cnt)%side_cnt, b = i%side_cnt;
238 int col = ((r*side_cnt+(side_cnt>>1))<<16) |
239 ((g*side_cnt+(side_cnt>>1))<<8) |
240 ((b*side_cnt+(side_cnt>>1))) ;
241 if ( r>0 ) rev->cube[i].cols[0] = add_adjacent(rev->cube[i-s2].cols[0],NULL,col,dmax);
242 if ( r+1<side_cnt ) rev->cube[i].cols[0] = add_adjacent(rev->cube[i+s2].cols[0],rev->cube[i].cols[0],col,dmax);
243 if ( g>0 ) rev->cube[i].cols[0] = add_adjacent(rev->cube[i-side_cnt].cols[0],rev->cube[i].cols[0],col,dmax);
244 if ( g+1<side_cnt ) rev->cube[i].cols[0] = add_adjacent(rev->cube[i+side_cnt].cols[0],rev->cube[i].cols[0],col,dmax);
245 if ( b>0 ) rev->cube[i].cols[0] = add_adjacent(rev->cube[i-1].cols[0],rev->cube[i].cols[0],col,dmax);
246 if ( b+1<side_cnt ) rev->cube[i].cols[0] = add_adjacent(rev->cube[i+1].cols[0],rev->cube[i].cols[0],col,dmax);
247 if ( rev->cube[i].cols[0]!=NULL ) changed = true;
248 else anynulls = true;
249 }
250 }
251 ++dmax;
252 }
253
254 if ( anynulls ) {
255 fprintf(stderr, "I'm sorry I cannot use this visual, please reconfigure your display\n" );
256 exit(1);
257 }
258
259 if ( rev->side_shift!=0 ) {
260 range >>= rev->side_shift;
261 if ( range>8 ) for ( i=0; i<s3; ++i ) if ( rev->cube[i].cols[0]->dist==0 ) {
262 int ql = rccnt(rev->cube[i].cols[0]);
263 int nr= -1;
264 struct revcol nbase;
265 if ( ql>128 )
266 nr = 16;
267 else if ( ql>32 )
268 nr = 8;
269 else if ( ql>=8 )
270 nr = 4;
271 if ( nr!=-1 ) {
272 nbase.red = basecol->red+(i/s2)*range;
273 nbase.green = basecol->green+(i/side_cnt)%side_cnt*range;
274 nbase.blue = basecol->blue+(i%side_cnt)*range;
275 while ( nr>range ) nr >>= 1;
276 if ( nr!=1 )
277 rev->cube[i].sub = _GClutReverse(nr,range,&nbase,
278 rev->cube[i].cols[0],rev->cube[i].cols[1]);
279 }
280 }
281 }
282 return( rev );
283 }
284
GClutReverse(GClut * clut,int side_cnt)285 RevCMap *GClutReverse(GClut *clut,int side_cnt) {
286 struct revcol *base = NULL;
287 int i;
288 RevCMap *ret;
289 struct revcol basecol;
290
291 if ( GImageGreyClut(clut) ) {
292 GCol *greys; int changed;
293 ret = calloc(1,sizeof(RevCMap));
294 ret->is_grey = 1;
295 greys = ret->greys = malloc(256*sizeof(GCol));
296 for ( i=0; i<256; ++i ) greys[i].pixel = 0x1000;
297 for ( i=0; i<clut->clut_len; ++i ) {
298 int g = clut->clut[i]&0xff;
299 greys[g].red = greys[g].green = greys[g].blue = g;
300 greys[g].pixel = i;
301 }
302 changed = true;
303 while ( changed ) {
304 changed = false;
305 for ( i=0; i<256; ++i ) {
306 if ( greys[i].pixel!=0x1000 ) {
307 if ( i!=0 && greys[i-1].pixel == 0x1000 ) {
308 greys[i-1] = greys[i]; changed = true; }
309 if ( i!=255 && greys[i+1].pixel == 0x1000 ) {
310 greys[i+1] = greys[i]; changed = true; }
311 }
312 }
313 }
314 return( ret );
315 }
316
317 for ( i=0; i<clut->clut_len; ++i ) {
318 struct revcol *rc = malloc(sizeof(struct revcol));
319 rc->red = COLOR_RED(clut->clut[i]);
320 rc->green = COLOR_GREEN(clut->clut[i]);
321 rc->blue = COLOR_BLUE(clut->clut[i]);
322 rc->index = i;
323 rc->dist = 0;
324 rc->next = base;
325 base = rc;
326 }
327 memset(&basecol,'\0',sizeof(basecol));
328 ret = _GClutReverse(side_cnt,256,&basecol,base,base);
329 while ( base!=NULL ) {
330 struct revcol *rc = base->next;
331 free(base);
332 base = rc;
333 }
334 return( ret );
335 }
336
GClut_RevCMapFree(RevCMap * rev)337 void GClut_RevCMapFree(RevCMap *rev) {
338 int i;
339
340 for ( i=0; i<rev->side_cnt*rev->side_cnt*rev->side_cnt; ++i ) {
341 if ( rev->cube[i].sub!=NULL )
342 GClut_RevCMapFree(rev->cube[i].sub);
343 RevColListFree(rev->cube[i].cols[0]);
344 RevColListFree(rev->cube[i].cols[1]);
345 }
346 free(rev->cube);
347 free(rev);
348 }
349
GImageGreyClut(GClut * clut)350 int GImageGreyClut(GClut *clut) {
351 int i, r,b,g;
352
353 if ( clut==NULL )
354 return( true );
355 for ( i=0; i<clut->clut_len; ++i ) {
356 r = COLOR_RED(clut->clut[i]);
357 g = COLOR_GREEN(clut->clut[i]);
358 b = COLOR_BLUE(clut->clut[i]);
359 if ( r!=g || g!=b ) {
360 clut->is_grey = false;
361 return( false );
362 }
363 }
364 clut->is_grey = true;
365 return( true );
366 }
367
368 static struct { char *name; long value; } predefn[] = {
369 { "red", 0xff0000 },
370 { "green", 0x008000 },
371 { "blue", 0x0000ff },
372 { "cyan", 0x00ffff },
373 { "magenta", 0xff00ff },
374 { "yellow", 0xffff00 },
375 { "black", 0x000000 },
376 { "gray", 0x808080 },
377 { "grey", 0x808080 },
378 { "white", 0xffffff },
379 { "maroon", 0x800000 },
380 { "olive", 0x808000 },
381 { "navy", 0x000080 },
382 { "purple", 0x800080 },
383 { "lime", 0x00ff00 },
384 { "aqua", 0x00ffff },
385 { "teal", 0x008080 },
386 { "fuchsia", 0xff0080 },
387 { "silver", 0xcccccc },
388 { NULL, 0 }
389 };
390
_GImage_ColourFName(char * name)391 Color _GImage_ColourFName(char *name) {
392 int i;
393 int r,g,b,a;
394 double dr,dg,db,da;
395 struct hslrgb hs;
396 Color col;
397
398 for ( i=0; predefn[i].name!=NULL; ++i )
399 if ( strmatch(name,predefn[i].name)==0 )
400 return( predefn[i].value );
401
402 if ( sscanf(name,"%d %d %d", &r, &g, &b )==3 ||
403 sscanf(name,"%x %x %x", (unsigned *) &r, (unsigned *) &g, (unsigned *) &b )==3 ||
404 (strlen(name)==7 && sscanf(name,"#%2x%2x%2x", (unsigned *) &r, (unsigned *) &g, (unsigned *) &b )==3) ) {
405 if ( r>255 ) r=255; else if ( r<0 ) r=0;
406 if ( g>255 ) g=255; else if ( g<0 ) g=0;
407 if ( b>255 ) b=255; else if ( b<0 ) b=0;
408 return( ((long) r<<16) | (g<<8) | b );
409 } else if ( (strlen(name)==9 && sscanf(name,"#%2x%2x%2x%2x",
410 (unsigned *) &a, (unsigned *) &r, (unsigned *) &g, (unsigned *) &b )==4) ) {
411 if ( a>255 ) a=255; else if ( a<0 ) a=0;
412 if ( r>255 ) r=255; else if ( r<0 ) r=0;
413 if ( g>255 ) g=255; else if ( g<0 ) g=0;
414 if ( b>255 ) b=255; else if ( b<0 ) b=0;
415 col = ((long) a<<24) | ((long) r<<16) | (g<<8) | b;
416 if ( (col&0xfffffff0)==0xfffffff0 )
417 col &= 0xffffff; /* I use these colors internally for things like "Undefined" */
418 /* but since I also treat colors with 0 alpha channel as fully */
419 /* opaque, I can just represent this that way */
420 return( col );
421 } else if ( sscanf(name,"rgb(%lg,%lg,%lg)", &dr, &dg, &db)==3 ) {
422 if ( dr>1.0 ) dr=1.0; else if ( dr<0 ) dr= 0;
423 if ( dg>1.0 ) dg=1.0; else if ( dg<0 ) dg= 0;
424 if ( db>1.0 ) db=1.0; else if ( db<0 ) db= 0;
425 r = dr*255 +.5;
426 g = dg*255 +.5;
427 b = db*255 +.5;
428 return( ((long) r<<16) | (g<<8) | b );
429 } else if ( sscanf(name,"argb(%lg,%lg,%lg,%lg)", &da, &dr, &dg, &db)==4 ) {
430 if ( da>1.0 ) da=1.0; else if ( da<0 ) da= 0;
431 if ( dr>1.0 ) dr=1.0; else if ( dr<0 ) dr= 0;
432 if ( dg>1.0 ) dg=1.0; else if ( dg<0 ) dg= 0;
433 if ( db>1.0 ) db=1.0; else if ( db<0 ) db= 0;
434 a = da*255 +.5;
435 r = dr*255 +.5;
436 g = dg*255 +.5;
437 b = db*255 +.5;
438 col = ((long) a<<24) | ((long) r<<16) | (g<<8) | b;
439 if ( (col&0xfffffff0)==0xfffffff0 )
440 col &= 0xffffff; /* I use these colors internally for things like "Undefined" */
441 /* but since I also treat colors with 0 alpha channel as fully */
442 /* opaque, I can just represent this that way */
443 return( col );
444 } else if ( sscanf(name,"hsv(%lg,%lg,%lg)", &hs.h, &hs.s, &hs.v)==3 ) {
445 /* Hue is an angle in degrees. HS?2RGB does appropriate clipping */
446 if ( hs.s>1.0 ) hs.s=1.0; else if ( hs.s<0 ) hs.s= 0;
447 if ( hs.v>1.0 ) hs.v=1.0; else if ( hs.v<0 ) hs.v= 0;
448 gHSV2RGB(&hs);
449 r = hs.r*255 +.5;
450 g = hs.g*255 +.5;
451 b = hs.b*255 +.5;
452 return( ((long) r<<16) | (g<<8) | b );
453 } else if ( sscanf(name,"hsl(%lg,%lg,%lg)", &hs.h, &hs.s, &hs.l)==3 ) {
454 /* Hue is an angle in degrees. HS?2RGB does appropriate clipping */
455 if ( hs.s>1.0 ) hs.s=1.0; else if ( hs.s<0 ) hs.s= 0;
456 if ( hs.l>1.0 ) hs.l=1.0; else if ( hs.l<0 ) hs.l= 0;
457 gHSL2RGB(&hs);
458 r = hs.r*255 +.5;
459 g = hs.g*255 +.5;
460 b = hs.b*255 +.5;
461 return( ((long) r<<16) | (g<<8) | b );
462 } else if ( (strlen(name)==4 && sscanf(name,"#%1x%1x%1x", (unsigned *) &r, (unsigned *) &g, (unsigned *) &b )==3) ) {
463 if ( r>15 ) r=15; else if ( r<0 ) r=0;
464 if ( g>15 ) g=15; else if ( g<0 ) g=0;
465 if ( b>15 ) b=15; else if ( b<0 ) b=0;
466 return( ((long) (r*0x110000)) | (g*0x1100) | (b*0x11) );
467 } else if ( (strlen(name)==17 && sscanf(name,"#%4x%4x%4x", (unsigned *) &r, (unsigned *) &g, (unsigned *) &b )==3) ) {
468 r>>=8; g>>=8; b>>=8;
469 if ( r>255 ) r=255; else if ( r<0 ) r=0;
470 if ( g>255 ) g=255; else if ( g<0 ) g=0;
471 if ( b>255 ) b=255; else if ( b<0 ) b=0;
472 return( ((long) r<<16) | (g<<8) | b );
473 } else if ( sscanf(name,"rgb(%lg%%,%lg%%,%lg%%)", &dr, &dg, &db)==3 ) {
474 if ( dr>100 ) dr=100; else if ( dr<0 ) dr= 0;
475 if ( dg>100 ) dg=100; else if ( dg<0 ) dg= 0;
476 if ( db>100 ) db=100; else if ( db<0 ) db= 0;
477 r = (dr*255+50)/100 +.5;
478 g = (dg*255+50)/100 +.5;
479 b = (db*255+50)/100 +.5;
480 return( ((long) r<<16) | (g<<8) | b );
481 }
482 return( -1 );
483 }
484
GDrawColorBrighten(Color col,int by)485 Color GDrawColorBrighten(Color col, int by) {
486 int r, g, b;
487 if (( r = COLOR_RED(col)+by )>255 ) r=255;
488 if (( g=COLOR_GREEN(col)+by )>255 ) g=255;
489 if (( b=COLOR_BLUE(col)+by )>255 ) b=255;
490 return( COLOR_CREATE(r,g,b));
491 }
492
GDrawColorDarken(Color col,int by)493 Color GDrawColorDarken(Color col, int by) {
494 int r, g, b;
495 if (( r = COLOR_RED(col)-by )<0 ) r=0;
496 if (( g=COLOR_GREEN(col)-by )<0 ) g=0;
497 if (( b=COLOR_BLUE(col)-by )<0 ) b=0;
498 return( COLOR_CREATE(r,g,b));
499 }
500