Line data Source code
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 : #include "gdrawP.h"
28 : #include "colorP.h"
29 : #include "charset.h"
30 : #include "ustring.h"
31 : #include "gutils.h"
32 :
33 : #if defined(__MINGW32__)
34 : # include "../gutils/divisors.c"
35 : #endif
36 :
37 : struct clutinf {
38 : Color col;
39 : int32 cnt;
40 : struct clutinf *next;
41 : };
42 :
43 : struct clutcube {
44 : int32 cnt;
45 : struct clutinf *ours;
46 : };
47 :
48 : struct clutcnt {
49 : int sofar;
50 : struct clutinf clut[256];
51 : struct clutinf transinf;
52 : struct clutcube cube[16][16][16];
53 : int32 dither[6*6*6];
54 : int size, div;
55 : };
56 :
57 : struct clut_range {
58 : unsigned short red_min;
59 : unsigned short green_min;
60 : unsigned short blue_min;
61 : int shift;
62 : };
63 :
64 : struct colcnt {
65 : Color col;
66 : int32 cnt;
67 : };
68 :
69 0 : static void RevColListFree(struct revcol *rc) {
70 : struct revcol *next;
71 :
72 0 : while ( rc!=NULL ) {
73 0 : next = rc->next;
74 0 : free(rc);
75 0 : rc = next;
76 : }
77 0 : }
78 :
79 0 : static int cccomp(const void *_c1, const void *_c2) {
80 0 : register const struct colcnt *c1 = _c1, *c2 = _c2;
81 :
82 0 : return( c2->cnt-c1->cnt );
83 : }
84 :
85 0 : static int colcomp(const void *_c1, const void *_c2) {
86 0 : register const Color *c1 = _c1, *c2 = _c2;
87 :
88 0 : return( *c1-*c2 );
89 : }
90 :
91 0 : static int cicomp(const void *_c1, const void *_c2) {
92 0 : register const struct clutinf *c1 = _c1, *c2 = _c2;
93 :
94 0 : return( c1->col-c2->col );
95 : }
96 :
97 0 : static int cicntcomp(const void *_c1, const void *_c2) {
98 0 : register const struct clutinf *c1 = _c1, *c2 = _c2;
99 :
100 0 : return( c2->cnt-c1->cnt );
101 : }
102 :
103 : /* we have cnt different colors, the colors and their histogram is given in */
104 : /* clutinf. We want to fit that into clutmax colors */
105 : /* first thing is to create a dithering cube which will fit in clutmax */
106 : /* (if clutmax<8 then we are probably screwed, so just return 8) */
107 : /* Ok figure out what bits of the cube we need (the subcubes we have colors in*/
108 : /* and some of those adjacent (assuming the color doesn't match exactly the */
109 : /* subcube's mid-point we'll need to dither and for that we need the adjacent))*/
110 : /* Then we'll probably have some left over. Well, let's just add as many of the*/
111 : /* most used colors as we can */
112 0 : static GClut *gimage_reduceclut(GClut *clut,int clutmax,struct clutinf *clutinf,
113 : int cnt, struct clutinf *transinf) {
114 : int i,j;
115 : int size, div, s2, s3;
116 : int r,g,b,dindex;
117 : int32 dither[6*6*6];
118 :
119 0 : if ( transinf->cnt )
120 0 : --clutmax;
121 0 : for ( size=6; size>0; --size )
122 0 : if ( size*size*size<=clutmax )
123 0 : break;
124 0 : if ( size<2 ) size=2;
125 0 : div = (255/(size-1));
126 :
127 0 : for ( i=0; i<cnt; ++i ) {
128 0 : Color val = clutinf[i].col;
129 0 : r=COLOR_RED(val), g=COLOR_GREEN(val), b=COLOR_BLUE(val);
130 0 : dindex = ((r/div)*size+(g/div))*size+(b/div);
131 0 : dither[dindex] += clutinf[i].cnt;
132 : }
133 :
134 0 : s2 = size*size;
135 0 : for ( r=0; r<size-1; ++r ) for ( g=0; g<size-1; ++g ) for ( b=0; b<size-1; ++b ) {
136 0 : if ( dither[r*s2+g*size+b] > 0 ) {
137 0 : if ( dither[r*s2+g*size+b+1] == 0 ) dither[r*s2+g*size+b+1] = -1;
138 0 : if ( dither[r*s2+(g+1)*size+b] == 0 ) dither[r*s2+(g+1)*size+b] = -1;
139 0 : if ( dither[(r+1)*s2+g*size+b] == 0 ) dither[(r+1)*s2+g*size+b] = -1;
140 0 : if ( dither[r*s2+(g+1)*size+b+1] == 0 ) dither[r*s2+(g+1)*size+b+1] = -1;
141 0 : if ( dither[(r+1)*s2+(g+1)*size+b] == 0 ) dither[(r+1)*s2+(g+1)*size+b] = -1;
142 0 : if ( dither[(r+1)*s2+g*size+b+1] == 0 ) dither[(r+1)*s2+g*size+b+1] = -1;
143 0 : if ( dither[(r+1)*s2+(g+1)*size+b+1] == 0 ) dither[(r+1)*s2+(g+1)*size+b+1] = -1;
144 : }
145 : }
146 0 : s3 = s2*size;
147 0 : for ( i=j=0; i<s3; ++i ) if ( dither[i]!= 0 ) {
148 0 : r = (i/(s2))*div; if ( r>250 ) r=255;
149 0 : g = (i/size)%size*div; if ( g>250 ) g=255;
150 0 : b = (i%(size))*div; if ( b>250 ) b=255;
151 0 : clut->clut[j++] = COLOR_CREATE(r,g,b);
152 : }
153 :
154 0 : if ( j<clutmax ) {
155 0 : qsort(clutinf,sizeof(clutinf[0]),cnt,cicntcomp);
156 0 : i = 0;
157 0 : while ( j<clutmax && i<cnt ) {
158 0 : r = COLOR_RED(clutinf[i].col); g = COLOR_GREEN(clutinf[i].col);
159 0 : b = COLOR_BLUE(clutinf[i].col);
160 0 : if ( (r!=255 || (r<250 && r%div==0)) &&
161 0 : (g!=255 || (g<250 && g%div==0)) &&
162 0 : (b!=255 || (b<250 && b%div==0)) )
163 0 : clut->clut[j++] = clutinf[i].col;
164 0 : ++i;
165 : }
166 : }
167 0 : if ( transinf->cnt!=0 )
168 0 : clut->clut[j++] = transinf->col;
169 0 : clut->clut_len = j;
170 0 : return( clut );
171 : }
172 :
173 0 : static void gimage_count32(GImage *image, struct clutcnt *clutcnt,int clutmax) {
174 : int i, j, len, size, div;
175 : uint32 *ipt, *istart, *iend;
176 : struct _GImage *base, **bases;
177 0 : int orig_trans = clutcnt->transinf.cnt;
178 :
179 0 : if ( image->list_len ) {
180 0 : len = image->list_len;
181 0 : bases = image->u.images;
182 : } else {
183 0 : bases = &image->u.image;
184 0 : len = 1;
185 : }
186 0 : for ( size=6; size>0; --size )
187 0 : if ( size*size*size<=clutmax )
188 0 : break;
189 0 : if ( size<2 ) size=2;
190 0 : clutcnt->size = size;
191 0 : clutcnt->div = div = (255/(size-1));
192 :
193 0 : for ( j=0; j<len; ++j ) {
194 0 : base = bases[j];
195 0 : ipt = (uint32 *) (base->data);
196 0 : for ( i=0; i<base->height; ++i ) {
197 0 : istart = ipt;
198 0 : for ( iend = ipt + base->width; ipt<iend; ++ipt ) {
199 0 : register int32 val = *ipt;
200 0 : register int r=COLOR_RED(val), g=COLOR_GREEN(val), b=COLOR_BLUE(val);
201 0 : int dindex = (r/div)*size*size+(g/div)*size+(b/div);
202 0 : register struct clutcube *cc = &clutcnt->cube[r>>4][g>>4][b>>4];
203 : register struct clutinf *test;
204 0 : if ( val==base->trans )
205 0 : ++clutcnt->transinf.cnt;
206 : else {
207 0 : ++ cc->cnt;
208 0 : ++clutcnt->dither[dindex];
209 0 : if ( clutcnt->sofar<=256 ) {
210 0 : for ( test = cc->ours; test!=NULL; test=test->next )
211 0 : if ( test->col==val )
212 0 : break;
213 0 : if ( test==NULL ) {
214 0 : if ( clutcnt->sofar<256 ) {
215 0 : test = &clutcnt->clut[clutcnt->sofar];
216 0 : test->next = cc->ours;
217 0 : test->col = val;
218 0 : cc->ours = test;
219 : }
220 0 : ++clutcnt->sofar;
221 : } else
222 0 : ++ test->cnt;
223 : }
224 : }
225 : }
226 0 : ipt = (uint32 *) (((uint8 *) istart) + base->bytes_per_line);
227 : }
228 0 : if ( clutcnt->transinf.cnt!=orig_trans )
229 0 : clutcnt->transinf.col = base->trans;
230 : }
231 0 : }
232 :
233 0 : static GClut *gimage_pickclut32(GClut *clut,int clutmax, struct clutcnt *clutcnt) {
234 0 : struct clutcube *cube = &clutcnt->cube[0][0][0];
235 : struct colcnt cc[4096];
236 : int i,j,r,g,b,incr,size,s2,s3, wantstrans;
237 :
238 0 : size = clutcnt->size; s2 = size*size; s3 = size*s2;
239 0 : incr = clutcnt->div;
240 0 : j = 0;
241 : /* if we have a color in a cube cell then we want to make sure that the */
242 : /* edges of that cell are available to us so we can dither */
243 0 : for ( r=0; r<size-1; ++r ) for ( g=0; g<size-1; ++g ) for ( b=0; b<size-1; ++b ) {
244 0 : if ( clutcnt->dither[r*s2+g*size+b] > 0 ) {
245 0 : if ( clutcnt->dither[r*s2+g*size+b+1] == 0 ) clutcnt->dither[r*s2+g*size+b+1] = -1;
246 0 : if ( clutcnt->dither[r*s2+(g+1)*size+b] == 0 ) clutcnt->dither[r*s2+(g+1)*size+b] = -1;
247 0 : if ( clutcnt->dither[(r+1)*s2+g*size+b] == 0 ) clutcnt->dither[(r+1)*s2+g*size+b] = -1;
248 0 : if ( clutcnt->dither[r*s2+(g+1)*size+b+1] == 0 ) clutcnt->dither[r*s2+(g+1)*size+b+1] = -1;
249 0 : if ( clutcnt->dither[(r+1)*s2+(g+1)*size+b] == 0 ) clutcnt->dither[(r+1)*s2+(g+1)*size+b] = -1;
250 0 : if ( clutcnt->dither[(r+1)*s2+g*size+b+1] == 0 ) clutcnt->dither[(r+1)*s2+g*size+b+1] = -1;
251 0 : if ( clutcnt->dither[(r+1)*s2+(g+1)*size+b+1] == 0 ) clutcnt->dither[(r+1)*s2+(g+1)*size+b+1] = -1;
252 : }
253 : }
254 0 : for ( i=0; i<s3; ++i ) if ( clutcnt->dither[i]!= 0 ) {
255 0 : r = (i/(s2))*incr; if ( r>250 ) r=255;
256 0 : g = (i/size)%size*incr; if ( g>250 ) g=255;
257 0 : b = (i%(size))*incr; if ( b>250 ) b=255;
258 0 : clut->clut[j++] = COLOR_CREATE(r,g,b);
259 : }
260 0 : wantstrans = (clutcnt->transinf.cnt!=0);
261 :
262 0 : for ( i=0; i<16*16*16; ++i ) {
263 0 : if ( cube[i].ours!=NULL && cube[i].ours->cnt>(3*cube[i].cnt)/4 )
264 0 : cc[i].col = cube[i].ours->col;
265 : else
266 0 : cc[i].col = COLOR_CREATE( ((i>>8)<<4) + 8,(((i>>4)&0xf)<<4) + 8, ((i&0xf)<<4)+8 );
267 0 : cc[i].cnt = cube[i].cnt;
268 : }
269 0 : qsort(cc,4096,sizeof(cc[0]),cccomp);
270 0 : for ( i=0; j<clutmax-wantstrans; ++j,++i ) {
271 0 : if ( cc[i].cnt==0 )
272 0 : break;
273 0 : clut->clut[j] = cc[i].col;
274 : }
275 0 : qsort(clut->clut,j,sizeof(Color),colcomp);
276 :
277 0 : if ( wantstrans ) {
278 0 : clut->trans_index = j;
279 0 : clut->clut[j++] = clutcnt->transinf.col/*COLOR_CREATE(clutcnt->transinf.red,clutcnt->transinf.green,clutcnt->transinf.blue)!!!!*/;
280 : }
281 0 : clut->clut_len = j;
282 0 : return( clut );
283 : }
284 :
285 0 : static GClut *gimage_findclut32(GImage *image,GClut *clut,int clutmax) {
286 : struct clutcnt clutcnt;
287 : int i;
288 :
289 0 : memset(&clutcnt,'\0',sizeof(clutcnt));
290 0 : gimage_count32(image, &clutcnt,clutmax);
291 0 : if ( clutcnt.sofar+(clutcnt.transinf.cnt!=0) <=clutmax ) {
292 0 : for ( i=0; i<clutcnt.sofar; ++i )
293 0 : clut->clut[i] = clutcnt.clut[i].col;
294 0 : if ( clutcnt.transinf.cnt!=0 ) {
295 0 : clut->trans_index = i;
296 0 : clut->clut[i++] = clutcnt.transinf.col;
297 : }
298 0 : clut->clut_len = i;
299 0 : return( clut );
300 : }
301 :
302 0 : if ( clutcnt.sofar<=256 )
303 0 : return( gimage_reduceclut(clut,clutmax,clutcnt.clut,clutcnt.sofar,&clutcnt.transinf));
304 :
305 0 : return( gimage_pickclut32(clut,clutmax,&clutcnt));
306 : }
307 :
308 0 : static int gimage_count8(GImage *image, struct clutinf *clutinf, int pos, struct clutinf *transinf) {
309 : int i, j, k, len;
310 : uint8 *ipt, *istart, *iend;
311 : struct _GImage *base, **bases;
312 : GClut *clut;
313 : int clutmax;
314 :
315 0 : if ( image->list_len ) {
316 0 : len = image->list_len;
317 0 : bases = image->u.images;
318 : } else {
319 0 : bases = &image->u.image;
320 0 : len = 1;
321 : }
322 :
323 0 : for ( k=0; k<len; ++k ) {
324 0 : base = bases[k];
325 0 : ipt = (uint8 *) (base->data);
326 0 : clutmax = base->clut->clut_len;
327 0 : for ( i=0; i<base->height; ++i ) {
328 0 : istart = ipt;
329 0 : for ( iend = ipt + base->width; ipt<iend; ++ipt ) {
330 0 : register int val = *ipt;
331 0 : if ( val<clutmax )
332 0 : ++clutinf[val+pos].cnt;
333 : }
334 0 : ipt = istart + base->bytes_per_line;
335 : }
336 :
337 0 : clut = base->clut;
338 0 : if ( base->trans!=COLOR_UNKNOWN ) {
339 0 : if ( transinf->cnt==0 )
340 0 : transinf->col = clut->clut[base->trans];
341 0 : transinf->cnt += clutinf[base->trans+pos].cnt;
342 0 : clutinf[base->trans+pos].cnt = 0;
343 : }
344 0 : for ( i=0; i<clutmax; ++i ) {
345 0 : clutinf[i+pos].col = clut->clut[i];
346 : }
347 0 : pos += clutmax;
348 : }
349 :
350 0 : if ( len>1 ) {
351 : /* remove duplicates (if any), by sorting first so dups are adjacent */
352 0 : qsort(clutinf,sizeof(clutinf[0]),pos,cicomp);
353 0 : for ( i=j=0; i<pos; ++i ) {
354 0 : if ( clutinf[i].cnt==0 )
355 0 : continue;
356 0 : if ( i!=j )
357 0 : clutinf[j++] = clutinf[i];
358 : else
359 0 : j++;
360 0 : for ( k=i+1; k<pos; ++k ) {
361 0 : if ( clutinf[i].col != clutinf[k].col )
362 0 : break;
363 0 : clutinf[i].cnt += clutinf[k].cnt;
364 0 : clutinf[k].cnt = 0;
365 : }
366 0 : i = k-1;
367 : }
368 0 : pos = j;
369 : }
370 :
371 0 : return( pos );
372 : }
373 :
374 0 : static int TickGreyClut(GClut *clut, char *grey_clut) {
375 : int i, r,b,g;
376 :
377 0 : if ( clut==NULL ) {
378 0 : grey_clut[0] = true;
379 0 : grey_clut[255] = true;
380 0 : return( true );
381 : }
382 0 : for ( i=0; i<clut->clut_len; ++i ) {
383 0 : r = COLOR_RED(clut->clut[i]);
384 0 : g = COLOR_GREEN(clut->clut[i]);
385 0 : b = COLOR_BLUE(clut->clut[i]);
386 0 : if ( r!=g || g!=b ) {
387 0 : clut->is_grey = false;
388 0 : return( false );
389 : }
390 0 : grey_clut[r] = true;
391 : }
392 0 : clut->is_grey = true;
393 0 : return( true );
394 : }
395 :
396 0 : static int is_grey(GImage *image, char *grey_clut) {
397 0 : struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
398 : int i;
399 :
400 0 : for ( i=0; i<256; ++i ) grey_clut[i] = 0;
401 0 : if ( base->image_type == it_true )
402 0 : return( false );
403 0 : if ( image->list_len==0 ) {
404 0 : if ( TickGreyClut(base->clut,grey_clut) && base->trans==COLOR_UNKNOWN )
405 0 : return( base->clut->clut_len );
406 :
407 0 : return( 0 );
408 : } else {
409 : int m;
410 0 : struct _GImage **im = image->u.images, **end = im+image->list_len;
411 0 : while ( im<end ) {
412 0 : if ( !TickGreyClut(im[0]->clut,grey_clut) || im[0]->trans != COLOR_UNKNOWN )
413 0 : return( 0 );
414 : }
415 0 : m = 0;
416 0 : for ( i=0; i<256; ++i )
417 0 : if ( grey_clut[i])
418 0 : ++m;
419 0 : return( m );
420 : }
421 : }
422 :
423 0 : static GClut *PickGreyClut(GClut *clut,int clutmax,char *grey_clut,int tot) {
424 : int i,j;
425 :
426 0 : clut->trans_index = COLOR_UNKNOWN;
427 0 : if ( clutmax==256 ) {
428 0 : for ( i=0; i<256; ++i )
429 0 : clut->clut[i] = COLOR_CREATE(i,i,i);
430 0 : clut->clut_len = 256;
431 0 : } else if ( tot<=clutmax ) {
432 0 : for ( i=j=0; i<256; ++i )
433 0 : if ( grey_clut[i] )
434 0 : clut->clut[j++] = COLOR_CREATE(i,i,i);
435 0 : clut->clut_len = j;
436 : } else {
437 : int min_val, max_val;
438 : int incr;
439 :
440 0 : for ( i=0; i<256 && !grey_clut[i]; ++i );
441 0 : min_val = i;
442 0 : for ( i=255; i>=0 && !grey_clut[i]; --i );
443 0 : max_val = i+1;
444 0 : incr = (max_val-min_val)/(clutmax-1);
445 0 : for ( i=0; i<clutmax; ++i )
446 0 : clut->clut[i] = COLOR_CREATE(min_val+i*incr,min_val+i*incr,min_val+i*incr);
447 0 : clut->clut[i-1] = COLOR_CREATE(max_val,max_val,max_val);
448 0 : clut->clut_len = clutmax;
449 : }
450 0 : clut->is_grey = true;
451 0 : return( clut );
452 : }
453 :
454 0 : GClut *GImageFindCLUT(GImage *image,GClut *clut,int clutmax) {
455 : int i,cnt;
456 : struct clutinf *clutinf, transinf;
457 : struct _GImage **list;
458 0 : struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
459 : char grey_clut[256];
460 :
461 0 : if ( clut==NULL )
462 0 : clut = malloc(sizeof(GClut));
463 0 : if ( clutmax<2 || clut==NULL )
464 0 : return( 0 );
465 :
466 0 : clut->clut_len = 0; clut->is_grey = false;
467 0 : if ( clutmax>256 ) clutmax = 256;
468 :
469 0 : if ( base->image_type==it_true )
470 0 : return( gimage_findclut32(image,clut,clutmax));
471 :
472 0 : if ( image->list_len==0 ) {
473 0 : if ( base->clut==NULL ) { /* Black & White */
474 0 : clut->clut[0] = COLOR_CREATE(0,0,0);
475 0 : clut->clut[1] = COLOR_CREATE(255,255,255);
476 0 : clut->clut_len = 2;
477 0 : clut->trans_index = 0;
478 0 : return( clut );
479 0 : } else if ( base->clut->clut_len<=clutmax ) {
480 0 : memcpy(clut->clut,base->clut->clut,base->clut->clut_len*sizeof(Color));
481 0 : clut->clut_len = base->clut->clut_len;
482 0 : clut->trans_index = base->trans;
483 0 : return( clut );
484 : }
485 : }
486 :
487 0 : if ( (cnt = is_grey(image,grey_clut)) )
488 0 : return( PickGreyClut(clut,clutmax,grey_clut,cnt));
489 :
490 0 : if ( image->list_len ) {
491 0 : cnt = 0;
492 0 : for ( i=0, list = image->u.images; i<image->list_len; ++i, ++list)
493 0 : cnt += (*list)->clut->clut_len;
494 : } else
495 0 : cnt = image->u.image->clut->clut_len;
496 :
497 0 : memset(&transinf,'\0',sizeof(transinf));
498 0 : clutinf = calloc(cnt,sizeof(struct clutinf));
499 :
500 0 : cnt = gimage_count8(image, clutinf, 0, &transinf);
501 0 : if ( cnt+(transinf.cnt!=0)<clutmax ) {
502 0 : clut->clut_len = cnt+(transinf.cnt!=0);
503 0 : clut->trans_index = cnt;
504 0 : for ( i=0; i<cnt; ++i )
505 0 : clut->clut[i] = clutinf[i].col;
506 0 : clut->clut[i] = transinf.col;
507 0 : free(clutinf);
508 0 : return(clut);
509 : }
510 :
511 0 : gimage_reduceclut(clut,clutmax,clutinf,cnt,&transinf);
512 0 : free(clutinf);
513 0 : return clut;
514 : }
515 :
516 : static const GCol white = { 0xff, 0xff, 0xff, 1 }, black = { 0, 0, 0, 0 };
517 :
518 0 : const GCol *_GImage_GetIndexedPixel(Color col,RevCMap *rev) {
519 : int val, t, best, r, g, b;
520 : struct revitem *this;
521 : struct revcol *test, *bestcol;
522 :
523 0 : if ( rev==NULL ) { /* Mono */
524 0 : if ( COLOR2WHITE(col) )
525 0 : return( &white );
526 : else
527 0 : return( &black );
528 : }
529 0 : if ( rev->is_grey ) {
530 0 : val = COLOR2GREY(col);
531 0 : return( &rev->greys[val]);
532 : }
533 :
534 : reverse_clut:
535 0 : r = COLOR_RED(col); g = COLOR_GREEN(col); b = COLOR_BLUE(col);
536 0 : if ( rev->div_mul==1 ) {
537 0 : val = r>>rev->div_shift;
538 0 : val = (val<<rev->side_shift)+(g>>rev->div_shift);
539 0 : val = (val<<rev->side_shift)+(b>>rev->div_shift);
540 : } else {
541 0 : val = ((r+rev->div_add)*rev->div_mul)>>rev->div_shift;
542 0 : t = ((g+rev->div_add)*rev->div_mul)>>rev->div_shift;
543 0 : val = (val*rev->side_cnt)+t;
544 0 : t = ((b+rev->div_add)*rev->div_mul)>>rev->div_shift;
545 0 : val = (val*rev->side_cnt)+t;
546 : }
547 0 : this = &rev->cube[val];
548 0 : if ( this->sub!=NULL ) {
549 0 : col &= rev->mask;
550 0 : rev = this->sub;
551 0 : goto reverse_clut;
552 : }
553 :
554 0 : test = this->cols[0];
555 0 : if ( test->next==NULL )
556 0 : return( (GCol *) test );
557 :
558 0 : if (( best = (r-test->red))<0 ) best = -best;
559 0 : if (( t = (g-test->green))<0 ) t = -t; best += t;
560 0 : if (( t = (b-test->blue))<0 ) t = -t; best += t;
561 0 : bestcol = test;
562 0 : for ( test=test->next; test!=NULL; test = test->next ) {
563 0 : if (( val = (r-test->red))<0 ) val = -val;
564 0 : if (( t = (g-test->green))<0 ) t = -t; val += t;
565 0 : if (( t = (b-test->blue))<0 ) t = -t; val += t;
566 0 : if ( val<best ) {
567 0 : val = best;
568 0 : bestcol = test;
569 : }
570 : }
571 0 : return( (GCol *) bestcol );
572 : }
573 :
574 :
575 0 : const GCol *_GImage_GetIndexedPixelPrecise(Color col,RevCMap *rev) {
576 : int val, t, best, r, g, b;
577 : struct revitem *this;
578 : struct revcol *test, *bestcol;
579 :
580 0 : if ( rev==NULL ) { /* Mono */
581 0 : if ( COLOR2WHITE(col) )
582 0 : return( &white );
583 : else
584 0 : return( &black );
585 : }
586 0 : if ( rev->is_grey ) {
587 0 : val = COLOR2GREY(col);
588 0 : return( &rev->greys[val]);
589 : }
590 :
591 : reverse_clut:
592 0 : r = COLOR_RED(col); g = COLOR_GREEN(col); b = COLOR_BLUE(col);
593 0 : if ( rev->div_mul==1 ) {
594 0 : r>>=rev->div_shift;
595 0 : g>>=rev->div_shift;
596 0 : b>>=rev->div_shift;
597 0 : val = (((r<<rev->side_shift) + g)<<rev->side_shift)+ b;
598 : } else {
599 0 : r = ((r+rev->div_add)*rev->div_mul)>>rev->div_shift;
600 0 : g = ((g+rev->div_add)*rev->div_mul)>>rev->div_shift;
601 0 : b = ((b+rev->div_add)*rev->div_mul)>>rev->div_shift;
602 0 : val = ((r*rev->side_cnt)+g)*rev->side_cnt + b;
603 : }
604 0 : this = &rev->cube[val];
605 0 : if ( this->sub!=NULL ) {
606 0 : col &= rev->mask;
607 0 : rev = this->sub;
608 0 : goto reverse_clut;
609 : }
610 :
611 0 : test = this->cols[0];
612 0 : if ( test->next==NULL && this->cols[1]==NULL )
613 0 : return( (GCol *) test );
614 :
615 0 : if (( best = (r-test->red))<0 ) best = -best;
616 0 : if (( t = (g-test->green))<0 ) t = -t; best += t;
617 0 : if (( t = (b-test->blue))<0 ) t = -t; best += t;
618 0 : bestcol = test;
619 0 : for ( test=test->next; test!=NULL; test = test->next ) {
620 0 : if (( val = (r-test->red))<0 ) val = -val;
621 0 : if (( t = (g-test->green))<0 ) t = -t; val += t;
622 0 : if (( t = (b-test->blue))<0 ) t = -t; val += t;
623 0 : if ( val<best ) {
624 0 : val = best;
625 0 : bestcol = test;
626 : }
627 : }
628 :
629 0 : for ( test=this->cols[1]; test!=NULL; test = test->next ) {
630 0 : if (( val = (r-test->red))<0 ) val = -val;
631 0 : if (( t = (g-test->green))<0 ) t = -t; val += t;
632 0 : if (( t = (b-test->blue))<0 ) t = -t; val += t;
633 0 : if ( val<best ) {
634 0 : val = best;
635 0 : bestcol = test;
636 : }
637 : }
638 0 : return( (GCol *) bestcol );
639 : }
640 :
641 0 : static int rccnt( struct revcol *cols) {
642 0 : int cnt = 0;
643 0 : while ( cols!=NULL ) {
644 0 : ++cnt;
645 0 : cols = cols->next;
646 : }
647 0 : return( cnt );
648 : }
649 :
650 0 : static struct revcol *add_adjacent(struct revcol *test, struct revcol *old, Color col, int dmax) {
651 0 : int r=COLOR_RED(col), g=COLOR_GREEN(col), b=COLOR_BLUE(col);
652 : int off, t, bestoff;
653 0 : struct revcol *best=NULL;
654 :
655 0 : if ( test==NULL || test->dist>dmax )
656 0 : return( old );
657 0 : bestoff = 3*255;
658 0 : for ( test=test; test!=NULL; test = test->next ) {
659 0 : if ( (off = (r-test->red))<0 ) off = -off;
660 0 : if ( (t = (g-test->green))<0 ) t = -t; off +=t;
661 0 : if ( (b = (g-test->blue))<0 ) t = -t; off +=t;
662 0 : if ( off<bestoff ) {
663 0 : bestoff = off;
664 0 : best = test;
665 : }
666 : }
667 0 : if ( old!=NULL ) {
668 0 : if ( (off = (r-old->red))<0 ) off = -off;
669 0 : if ( (t = (g-old->green))<0 ) t = -t; off +=t;
670 0 : if ( (b = (g-old->blue))<0 ) t = -t; off +=t;
671 0 : if ( off<bestoff ) {
672 0 : bestoff = off;
673 0 : best = old;
674 : }
675 : }
676 0 : if ( best!=old ) {
677 0 : if ( old==NULL ) old = calloc(1,sizeof(struct revcol));
678 0 : *old = *best;
679 0 : old->next = NULL;
680 0 : ++old->dist;
681 : }
682 0 : return( old );
683 : }
684 :
685 0 : static struct revcol *addrevcol(struct revcol *copy,struct revcol *old, int dist) {
686 0 : struct revcol *rc = malloc(sizeof(struct revcol));
687 :
688 0 : *rc = *copy;
689 0 : rc->next = old;
690 0 : rc->dist = dist;
691 0 : return( rc );
692 : }
693 :
694 0 : static RevCMap *_GClutReverse(int side_cnt,int range,struct revcol *basecol,
695 : struct revcol *cols,struct revcol *outercols) {
696 : RevCMap *rev;
697 : int i, s2, s3, side_size;
698 : struct revcol *test;
699 : int changed, dmax, anynulls;
700 :
701 0 : rev = calloc(1,sizeof(RevCMap));
702 :
703 0 : rev->side_cnt = side_cnt;
704 0 : rev->range = range;
705 0 : if ( side_cnt<0 ) {
706 : /* special cases ... */
707 0 : side_cnt = -side_cnt;
708 0 : rev->side_cnt = side_cnt;
709 0 : if ( side_cnt==6 ) { /* Used for the inefficient standard 6 by cube */
710 0 : rev->div_mul = div_tables[0x33][0];
711 0 : rev->div_shift = div_tables[0x33][1];
712 0 : rev->div_add = 0x19;
713 : }
714 0 : side_size = 0x33;
715 0 : } else if ( div_tables[side_cnt][0]==1 ) {
716 0 : rev->side_shift = div_tables[side_cnt][1];
717 0 : rev->div_shift = div_tables[range][1]-rev->side_shift;
718 0 : rev->div_mul = 1;
719 0 : rev->mask = ( 0x010101 * ((-1<rev->div_shift)&0xff) );
720 0 : side_size = 1<<(rev->div_shift);
721 : } else {
722 0 : side_size = (range+side_cnt-1)/side_cnt;
723 0 : rev->div_mul = div_tables[side_cnt][0];
724 0 : rev->div_shift = div_tables[side_cnt][1];
725 : }
726 :
727 0 : rev->cube = calloc(side_cnt*side_cnt*side_cnt,sizeof(struct revitem));
728 0 : for ( test = cols; test!=NULL; test = test->next ) {
729 : int pos, dist;
730 : int r,g,b;
731 0 : int rbase = (test->red-basecol->red)/side_size, gbase = (test->green-basecol->green)/side_size, bbase = (test->blue-basecol->blue)/side_size;
732 0 : for ( r = (test->red-basecol->red-side_size/2)/side_size;
733 0 : r<=(test->red-basecol->red+side_size/2)/side_size; ++r ) {
734 0 : if ( r<0 || r==side_cnt )
735 0 : continue;
736 0 : for ( g = (test->green-basecol->green-side_size/2)/side_size;
737 0 : g<=(test->green-basecol->green+side_size/2)/side_size; ++g ) {
738 0 : if ( g<0 || g==side_cnt )
739 0 : continue;
740 0 : for ( b = (test->blue-basecol->blue-side_size/2)/side_size;
741 0 : b<=(test->blue-basecol->blue+side_size/2)/side_size; ++b ) {
742 0 : if ( b<0 || b==side_cnt )
743 0 : continue;
744 0 : pos = (r*side_cnt + g)*side_cnt + b;
745 0 : dist = r!=rbase || g!=gbase || b!=bbase;
746 0 : rev->cube[pos].cols[dist] = addrevcol(test,rev->cube[pos].cols[dist],
747 : dist );
748 : }
749 : }
750 : }
751 : }
752 :
753 : /* Ok. We just put every color into the sub-cube that it belongs in */
754 : /* but that's not good enough. Say we are looking for a color that */
755 : /* is on the edge of a sub-cube, then the best match for it may be */
756 : /* in one of the adjacent sub-cubes, rather than within it's own */
757 : /* So we also ran through the list of colors, casting our net a */
758 : /* little wider. Essentially for every sub-cube we found all the colors */
759 : /* within a subcube centered on the color */
760 :
761 : /* Some sub-cubes will probably have no information in them. Fill them */
762 : /* in by looking at near-by cubes */
763 0 : s2 = side_cnt*side_cnt; s3 = s2*side_cnt;
764 0 : for ( i=0; i<s3; ++i ) {
765 0 : if ( rev->cube[i].cols[0]==NULL && rev->cube[i].cols[1]!=NULL ) {
766 0 : rev->cube[i].cols[0] = rev->cube[i].cols[1];
767 0 : rev->cube[i].cols[1] = NULL;
768 : }
769 : }
770 0 : changed = true; anynulls = false; dmax = 0;
771 0 : while ( changed || (anynulls && dmax<256) ) {
772 0 : changed = false; anynulls = false;
773 0 : for ( i=0; i<s3; ++i ) {
774 0 : if ( rev->cube[i].cols[0]==NULL ) {
775 0 : int r = i/s2, g = (i/side_cnt)%side_cnt, b = i%side_cnt;
776 0 : int col = ((r*side_cnt+(side_cnt>>1))<<16) |
777 0 : ((g*side_cnt+(side_cnt>>1))<<8) |
778 0 : ((b*side_cnt+(side_cnt>>1))) ;
779 0 : if ( r>0 ) rev->cube[i].cols[0] = add_adjacent(rev->cube[i-s2].cols[0],NULL,col,dmax);
780 0 : 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);
781 0 : if ( g>0 ) rev->cube[i].cols[0] = add_adjacent(rev->cube[i-side_cnt].cols[0],rev->cube[i].cols[0],col,dmax);
782 0 : 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);
783 0 : if ( b>0 ) rev->cube[i].cols[0] = add_adjacent(rev->cube[i-1].cols[0],rev->cube[i].cols[0],col,dmax);
784 0 : 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);
785 0 : if ( rev->cube[i].cols[0]!=NULL ) changed = true;
786 0 : else anynulls = true;
787 : }
788 : }
789 0 : ++dmax;
790 : }
791 :
792 0 : if ( anynulls ) {
793 0 : fprintf(stderr, "I'm sorry I cannot use this visual, please reconfigure your display\n" );
794 0 : exit(1);
795 : }
796 :
797 0 : if ( rev->side_shift!=0 ) {
798 0 : range >>= rev->side_shift;
799 0 : if ( range>8 ) for ( i=0; i<s3; ++i ) if ( rev->cube[i].cols[0]->dist==0 ) {
800 0 : int ql = rccnt(rev->cube[i].cols[0]);
801 0 : int nr= -1;
802 : struct revcol nbase;
803 0 : if ( ql>128 )
804 0 : nr = 16;
805 0 : else if ( ql>32 )
806 0 : nr = 8;
807 0 : else if ( ql>=8 )
808 0 : nr = 4;
809 0 : if ( nr!=-1 ) {
810 0 : nbase.red = basecol->red+(i/s2)*range;
811 0 : nbase.green = basecol->green+(i/side_cnt)%side_cnt*range;
812 0 : nbase.blue = basecol->blue+(i%side_cnt)*range;
813 0 : while ( nr>range ) nr >>= 1;
814 0 : if ( nr!=1 )
815 0 : rev->cube[i].sub = _GClutReverse(nr,range,&nbase,
816 0 : rev->cube[i].cols[0],rev->cube[i].cols[1]);
817 : }
818 : }
819 : }
820 0 : return( rev );
821 : }
822 :
823 0 : RevCMap *GClutReverse(GClut *clut,int side_cnt) {
824 0 : struct revcol *base = NULL;
825 : int i;
826 : RevCMap *ret;
827 : struct revcol basecol;
828 :
829 0 : if ( GImageGreyClut(clut) ) {
830 : GCol *greys; int changed;
831 0 : ret = calloc(1,sizeof(RevCMap));
832 0 : ret->is_grey = 1;
833 0 : greys = ret->greys = malloc(256*sizeof(GCol));
834 0 : for ( i=0; i<256; ++i ) greys[i].pixel = 0x1000;
835 0 : for ( i=0; i<clut->clut_len; ++i ) {
836 0 : int g = clut->clut[i]&0xff;
837 0 : greys[g].red = greys[g].green = greys[g].blue = g;
838 0 : greys[g].pixel = i;
839 : }
840 0 : changed = true;
841 0 : while ( changed ) {
842 0 : changed = false;
843 0 : for ( i=0; i<256; ++i ) {
844 0 : if ( greys[i].pixel!=0x1000 ) {
845 0 : if ( i!=0 && greys[i-1].pixel == 0x1000 ) {
846 0 : greys[i-1] = greys[i]; changed = true; }
847 0 : if ( i!=255 && greys[i+1].pixel == 0x1000 ) {
848 0 : greys[i+1] = greys[i]; changed = true; }
849 : }
850 : }
851 : }
852 0 : return( ret );
853 : }
854 :
855 0 : for ( i=0; i<clut->clut_len; ++i ) {
856 0 : struct revcol *rc = malloc(sizeof(struct revcol));
857 0 : rc->red = COLOR_RED(clut->clut[i]);
858 0 : rc->green = COLOR_GREEN(clut->clut[i]);
859 0 : rc->blue = COLOR_BLUE(clut->clut[i]);
860 0 : rc->index = i;
861 0 : rc->dist = 0;
862 0 : rc->next = base;
863 0 : base = rc;
864 : }
865 0 : memset(&basecol,'\0',sizeof(basecol));
866 0 : ret = _GClutReverse(side_cnt,256,&basecol,base,base);
867 0 : while ( base!=NULL ) {
868 0 : struct revcol *rc = base->next;
869 0 : free(base);
870 0 : base = rc;
871 : }
872 0 : return( ret );
873 : }
874 :
875 0 : void GClut_RevCMapFree(RevCMap *rev) {
876 : int i;
877 :
878 0 : for ( i=0; i<rev->side_cnt*rev->side_cnt*rev->side_cnt; ++i ) {
879 0 : if ( rev->cube[i].sub!=NULL )
880 0 : GClut_RevCMapFree(rev->cube[i].sub);
881 0 : RevColListFree(rev->cube[i].cols[0]);
882 0 : RevColListFree(rev->cube[i].cols[1]);
883 : }
884 0 : free(rev->cube);
885 0 : free(rev);
886 0 : }
887 :
888 0 : int GImageSameClut(GClut *clut,GClut *nclut) {
889 : static GClut dummy = { 2, true, COLOR_UNKNOWN, GCLUT_CLUT_EMPTY };
890 : int i;
891 :
892 0 : dummy.clut[0] = COLOR_CREATE(0, 0, 0);
893 0 : dummy.clut[1] = COLOR_CREATE(0xff, 0xff, 0xff);
894 :
895 0 : if ( clut==nclut )
896 0 : return( true );
897 0 : if ( clut==NULL )
898 0 : clut = &dummy;
899 0 : if ( nclut==NULL )
900 0 : nclut = &dummy;
901 0 : if ( clut->clut_len!=nclut->clut_len )
902 0 : return( false );
903 0 : for ( i = 0; i<clut->clut_len; ++i )
904 0 : if ( clut->clut[i]!=nclut->clut[i] )
905 0 : return( false );
906 :
907 0 : return( true );
908 : }
909 :
910 0 : int GImageGreyClut(GClut *clut) {
911 : int i, r,b,g;
912 :
913 0 : if ( clut==NULL )
914 0 : return( true );
915 0 : for ( i=0; i<clut->clut_len; ++i ) {
916 0 : r = COLOR_RED(clut->clut[i]);
917 0 : g = COLOR_GREEN(clut->clut[i]);
918 0 : b = COLOR_BLUE(clut->clut[i]);
919 0 : if ( r!=g || g!=b ) {
920 0 : clut->is_grey = false;
921 0 : return( false );
922 : }
923 : }
924 0 : clut->is_grey = true;
925 0 : return( true );
926 : }
927 :
928 : static struct { char *name; long value; } predefn[] = {
929 : { "red", 0xff0000 },
930 : { "green", 0x008000 },
931 : { "blue", 0x0000ff },
932 : { "cyan", 0x00ffff },
933 : { "magenta", 0xff00ff },
934 : { "yellow", 0xffff00 },
935 : { "black", 0x000000 },
936 : { "gray", 0x808080 },
937 : { "grey", 0x808080 },
938 : { "white", 0xffffff },
939 : { "maroon", 0x800000 },
940 : { "olive", 0x808000 },
941 : { "navy", 0x000080 },
942 : { "purple", 0x800080 },
943 : { "lime", 0x00ff00 },
944 : { "aqua", 0x00ffff },
945 : { "teal", 0x008080 },
946 : { "fuchsia", 0xff0080 },
947 : { "silver", 0xcccccc },
948 : { NULL, 0 }
949 : };
950 :
951 0 : Color _GImage_ColourFName(char *name) {
952 : int i;
953 : int r,g,b,a;
954 : double dr,dg,db,da;
955 : struct hslrgb hs;
956 : Color col;
957 :
958 0 : for ( i=0; predefn[i].name!=NULL; ++i )
959 0 : if ( strmatch(name,predefn[i].name)==0 )
960 0 : return( predefn[i].value );
961 :
962 0 : if ( sscanf(name,"%d %d %d", &r, &g, &b )==3 ||
963 0 : sscanf(name,"%x %x %x", (unsigned *) &r, (unsigned *) &g, (unsigned *) &b )==3 ||
964 0 : (strlen(name)==7 && sscanf(name,"#%2x%2x%2x", (unsigned *) &r, (unsigned *) &g, (unsigned *) &b )==3) ) {
965 0 : if ( r>255 ) r=255; else if ( r<0 ) r=0;
966 0 : if ( g>255 ) g=255; else if ( g<0 ) g=0;
967 0 : if ( b>255 ) b=255; else if ( b<0 ) b=0;
968 0 : return( ((long) r<<16) | (g<<8) | b );
969 0 : } else if ( (strlen(name)==9 && sscanf(name,"#%2x%2x%2x%2x",
970 : (unsigned *) &a, (unsigned *) &r, (unsigned *) &g, (unsigned *) &b )==4) ) {
971 0 : if ( a>255 ) a=255; else if ( a<0 ) a=0;
972 0 : if ( r>255 ) r=255; else if ( r<0 ) r=0;
973 0 : if ( g>255 ) g=255; else if ( g<0 ) g=0;
974 0 : if ( b>255 ) b=255; else if ( b<0 ) b=0;
975 0 : col = ((long) a<<24) | ((long) r<<16) | (g<<8) | b;
976 0 : if ( (col&0xfffffff0)==0xfffffff0 )
977 0 : col &= 0xffffff; /* I use these colors internally for things like "Undefined" */
978 : /* but since I also treat colors with 0 alpha channel as fully */
979 : /* opaque, I can just represent this that way */
980 0 : return( col );
981 0 : } else if ( sscanf(name,"rgb(%lg,%lg,%lg)", &dr, &dg, &db)==3 ) {
982 0 : if ( dr>1.0 ) dr=1.0; else if ( dr<0 ) dr= 0;
983 0 : if ( dg>1.0 ) dg=1.0; else if ( dg<0 ) dg= 0;
984 0 : if ( db>1.0 ) db=1.0; else if ( db<0 ) db= 0;
985 0 : r = dr*255 +.5;
986 0 : g = dg*255 +.5;
987 0 : b = db*255 +.5;
988 0 : return( ((long) r<<16) | (g<<8) | b );
989 0 : } else if ( sscanf(name,"argb(%lg,%lg,%lg,%lg)", &da, &dr, &dg, &db)==4 ) {
990 0 : if ( da>1.0 ) da=1.0; else if ( da<0 ) da= 0;
991 0 : if ( dr>1.0 ) dr=1.0; else if ( dr<0 ) dr= 0;
992 0 : if ( dg>1.0 ) dg=1.0; else if ( dg<0 ) dg= 0;
993 0 : if ( db>1.0 ) db=1.0; else if ( db<0 ) db= 0;
994 0 : a = da*255 +.5;
995 0 : r = dr*255 +.5;
996 0 : g = dg*255 +.5;
997 0 : b = db*255 +.5;
998 0 : col = ((long) a<<24) | ((long) r<<16) | (g<<8) | b;
999 0 : if ( (col&0xfffffff0)==0xfffffff0 )
1000 0 : col &= 0xffffff; /* I use these colors internally for things like "Undefined" */
1001 : /* but since I also treat colors with 0 alpha channel as fully */
1002 : /* opaque, I can just represent this that way */
1003 0 : return( col );
1004 0 : } else if ( sscanf(name,"hsv(%lg,%lg,%lg)", &hs.h, &hs.s, &hs.v)==3 ) {
1005 : /* Hue is an angle in degrees. HS?2RGB does appropriate clipping */
1006 0 : if ( hs.s>1.0 ) hs.s=1.0; else if ( hs.s<0 ) hs.s= 0;
1007 0 : if ( hs.v>1.0 ) hs.v=1.0; else if ( hs.v<0 ) hs.v= 0;
1008 0 : gHSV2RGB(&hs);
1009 0 : r = hs.r*255 +.5;
1010 0 : g = hs.g*255 +.5;
1011 0 : b = hs.b*255 +.5;
1012 0 : return( ((long) r<<16) | (g<<8) | b );
1013 0 : } else if ( sscanf(name,"hsl(%lg,%lg,%lg)", &hs.h, &hs.s, &hs.l)==3 ) {
1014 : /* Hue is an angle in degrees. HS?2RGB does appropriate clipping */
1015 0 : if ( hs.s>1.0 ) hs.s=1.0; else if ( hs.s<0 ) hs.s= 0;
1016 0 : if ( hs.l>1.0 ) hs.l=1.0; else if ( hs.l<0 ) hs.l= 0;
1017 0 : gHSL2RGB(&hs);
1018 0 : r = hs.r*255 +.5;
1019 0 : g = hs.g*255 +.5;
1020 0 : b = hs.b*255 +.5;
1021 0 : return( ((long) r<<16) | (g<<8) | b );
1022 0 : } else if ( (strlen(name)==4 && sscanf(name,"#%1x%1x%1x", (unsigned *) &r, (unsigned *) &g, (unsigned *) &b )==3) ) {
1023 0 : if ( r>15 ) r=15; else if ( r<0 ) r=0;
1024 0 : if ( g>15 ) g=15; else if ( g<0 ) g=0;
1025 0 : if ( b>15 ) b=15; else if ( b<0 ) b=0;
1026 0 : return( ((long) (r*0x110000)) | (g*0x1100) | (b*0x11) );
1027 0 : } else if ( (strlen(name)==17 && sscanf(name,"#%4x%4x%4x", (unsigned *) &r, (unsigned *) &g, (unsigned *) &b )==3) ) {
1028 0 : r>>=8; g>>=8; b>>=8;
1029 0 : if ( r>255 ) r=255; else if ( r<0 ) r=0;
1030 0 : if ( g>255 ) g=255; else if ( g<0 ) g=0;
1031 0 : if ( b>255 ) b=255; else if ( b<0 ) b=0;
1032 0 : return( ((long) r<<16) | (g<<8) | b );
1033 0 : } else if ( sscanf(name,"rgb(%lg%%,%lg%%,%lg%%)", &dr, &dg, &db)==3 ) {
1034 0 : if ( dr>100 ) dr=100; else if ( dr<0 ) dr= 0;
1035 0 : if ( dg>100 ) dg=100; else if ( dg<0 ) dg= 0;
1036 0 : if ( db>100 ) db=100; else if ( db<0 ) db= 0;
1037 0 : r = (dr*255+50)/100 +.5;
1038 0 : g = (dg*255+50)/100 +.5;
1039 0 : b = (db*255+50)/100 +.5;
1040 0 : return( ((long) r<<16) | (g<<8) | b );
1041 : }
1042 0 : return( -1 );
1043 : }
1044 :
1045 0 : Color GImageColourFName(unichar_t *name) {
1046 0 : return( _GImage_ColourFName(u_to_c(name)) );
1047 : }
1048 :
1049 0 : char *GImageNameFColour(Color col) {
1050 : int i;
1051 :
1052 0 : col &=0xffffff;
1053 0 : for ( i=0; predefn[i].name!=NULL; ++i )
1054 0 : if ( col == predefn[i].value )
1055 0 : return( predefn[i].name );
1056 :
1057 0 : return(NULL);
1058 : }
1059 :
1060 0 : Color GDrawColorBrighten(Color col, int by) {
1061 : int r, g, b;
1062 0 : if (( r = COLOR_RED(col)+by )>255 ) r=255;
1063 0 : if (( g=COLOR_GREEN(col)+by )>255 ) g=255;
1064 0 : if (( b=COLOR_BLUE(col)+by )>255 ) b=255;
1065 0 : return( COLOR_CREATE(r,g,b));
1066 : }
1067 :
1068 0 : Color GDrawColorDarken(Color col, int by) {
1069 : int r, g, b;
1070 0 : if (( r = COLOR_RED(col)-by )<0 ) r=0;
1071 0 : if (( g=COLOR_GREEN(col)-by )<0 ) g=0;
1072 0 : if (( b=COLOR_BLUE(col)-by )<0 ) b=0;
1073 0 : return( COLOR_CREATE(r,g,b));
1074 : }
|