7fd59977 |
1 | /* |
2 | * Created: Oleg Zelenkov (zov) : 1-Apr-1998 |
3 | * Portions Copyright (C) 1989, 1991 by Jef Poskanzer. See copyright notice |
4 | * below, at the beginning of the relevant code. |
5 | * |
6 | * Modified: Apr 20 1998 (zov) |
7 | * |
8 | |
9 | |
10 | XW_STATUS Xw_save_gif_image (awindow,aimage,filename): |
11 | XW_EXT_WINDOW *awindow |
12 | XW_EXT_IMAGEDATA *aimage |
13 | char *filename GIF Image name |
14 | |
15 | Save an image to a GIF file. |
16 | |
17 | returns SUCCESS if successfull |
18 | returns ERROR if something is wrong |
19 | |
20 | */ |
21 | |
22 | #define PRO16753 /* GG 261198 |
23 | // Don't free pname because this pointer is a |
24 | // static address in Xw_get_filename() space. |
25 | */ |
26 | |
27 | #include <Xw_Extension.h> |
28 | #include <X11/Xfuncs.h> |
29 | |
30 | #define PRO5356 /* GG_130996: Compatibillite vis a vis de XReadDisplay (IRIX) |
31 | GG_240898 Compatibillite vis a vis de XReadScreen (SOLARIS) |
32 | */ |
33 | |
34 | #ifdef TRACE |
35 | #define TRACE_SAVE_IMAGE |
36 | #endif |
37 | |
38 | #define DECOSF1_COMPATIBILITY |
39 | #define CONV24_BEST /* Defines conversion of color map to 256 colors */ |
40 | /* Use CONV24_FAST to do it quickly; |
41 | otherwise 'BEST' method is used. */ |
42 | #undef PARM |
43 | #if defined(__STDC__) || defined(HPUX) || defined(__hpux) || defined(__cplusplus) |
44 | # define PARM(a) a |
45 | #else |
46 | # define PARM(a) () |
47 | #endif |
48 | |
49 | |
50 | |
51 | typedef unsigned int DWORD; /* 32-bit signed */ |
52 | typedef int LONG; /* 32-bit unsigned */ |
53 | typedef unsigned int ULONG, UINT; /* 32-bit signed */ |
54 | typedef unsigned short WORD; /* 16-bit unsigned */ |
55 | |
56 | |
57 | #define LOWBIT(x) ((x) & (~(x) + 1)) |
58 | |
59 | /* RANGE forces a to be in the range b..c (inclusive) */ |
60 | #define RANGE(a,b,c) { if (a < b) a = b; if (a > c) a = c; } |
61 | |
62 | |
63 | /* NOTE, different platforms store WORDs in different ways */ |
64 | /* You have to select from the given macros those that you need */ |
65 | #if defined(__osf__) || defined(DECOSF1) |
66 | |
67 | #define SWAP_WORD(__w) (__w) |
68 | #define SWAP_DWORD(__w) (__w) |
69 | |
70 | #else |
71 | |
72 | #define SWAP_WORD(__w) ((((__w)&0xFF) << 8) | (((__w)&0xFF00) >> 8)) |
73 | #define SWAP_DWORD(__w) ((((__w)&0x000000FF) << 24) | (((__w)&0x0000FF00) << 8) \ |
74 | | (((__w)&0xFF0000) >> 8) | (((__w)&0xFF000000) >> 24) ) |
75 | |
76 | #endif |
77 | |
78 | |
79 | typedef struct _screen_descr { |
80 | char gifID[ 6 ]; |
81 | WORD scrnWidth; |
82 | WORD scrnHeight; |
83 | BYTE scrnFlag; |
84 | } SCREEN_DESCR; |
85 | |
86 | typedef struct _image_descr { |
87 | WORD imgX; |
88 | WORD imgY; |
89 | WORD imgWidth; |
90 | WORD imgHeight; |
91 | BYTE imgFlag; |
92 | } IMAGE_DESCR; |
93 | |
94 | typedef struct _lzw_dict { |
95 | int code; |
96 | int prnt; |
97 | BYTE byte; |
98 | } LZWDict; |
99 | |
100 | static LZWDict* dict; |
101 | static int startBits, codeBits, nextCode, bumpCode, rack, mask, putIdx, |
102 | ClearCode, EOFCode, FreeCode; |
103 | |
104 | #define NBITS 12 |
105 | #define TBL_SIZE 5021 |
106 | #define MAX_CODE ( ( 1 << NBITS ) - 1 ) |
107 | #define BUFF_SIZE 255 |
108 | #define UNUSED -1 |
109 | #define TRUE 1 |
110 | #define FALSE 0 |
111 | |
112 | static int _lzw_encode PARM((FILE*, BYTE*, int, int, int)); |
113 | static void _init_dict PARM((void)); |
114 | static int _find_child PARM((int, int)); |
115 | static BOOL _put_bits PARM((BYTE *, ULONG, UINT, FILE *)); |
116 | static BOOL _flush_bits PARM((BYTE *, FILE *)); |
117 | |
118 | static BOOL _convert_image_to_256_RGB_indices PARM((XImage*, XColor*, BYTE*, BYTE*)); |
119 | static int quick_check PARM((BYTE*, int, int, BYTE*, BYTE*)); |
120 | #if defined(CONV24_FAST) /* Use quick color transformation */ |
121 | static int quick_quant PARM((BYTE*, int, int, BYTE*, BYTE*)); |
122 | #endif |
123 | static int ppm_quant PARM((BYTE*, int, int, BYTE*, BYTE*)); |
124 | |
125 | static int _lzw_encode |
126 | #ifdef XW_PROTOTYPE |
127 | (FILE* fimage, BYTE* pData, int width, int height, int inc) |
128 | #else |
129 | (fimage, pData, width, height, inc) |
130 | FILE* fimage; |
131 | BYTE* pData; |
132 | int width, height, inc; |
133 | #endif |
134 | { |
135 | |
136 | int i, x, y; |
137 | BYTE byte, *pLine, OutBuff [BUFF_SIZE]; |
138 | int strCode, chr; |
139 | |
140 | |
141 | dict = (LZWDict *) Xw_malloc (sizeof (LZWDict)*TBL_SIZE); |
142 | if (dict == NULL) |
143 | goto _ExitError; |
144 | |
145 | |
146 | x = y = 0; |
147 | pLine = pData; |
148 | |
149 | OutBuff[ 0 ] = 0; |
150 | putIdx = 1; |
151 | mask = 0x01; |
152 | rack = 0; |
153 | startBits = 8; |
154 | ClearCode = 1 << startBits; |
155 | EOFCode = ClearCode + 1; |
156 | FreeCode = EOFCode + 1; |
157 | |
158 | _init_dict (); |
159 | |
160 | byte = startBits; |
161 | |
162 | if ((int)fwrite (&byte, 1, 1, fimage) < 1) |
163 | goto _ExitError; |
164 | |
165 | strCode = pLine[ x++ ]; |
166 | |
167 | if (!_put_bits (OutBuff, (ULONG) ClearCode, codeBits, fimage)) |
168 | goto _ExitError; |
169 | |
170 | |
171 | while (y < height) { |
172 | |
173 | chr = pLine[ x++ ]; |
174 | |
175 | i = _find_child ( strCode, chr ); |
176 | |
177 | |
178 | if ( dict[ i ].code != UNUSED ) |
179 | strCode = dict[ i ].code; |
180 | |
181 | else { |
182 | dict[ i ].code = nextCode++; |
183 | dict[ i ].prnt = strCode; |
184 | dict[ i ].byte = chr; |
185 | |
186 | if (!_put_bits (OutBuff, (ULONG) strCode, codeBits, fimage)) |
187 | goto _ExitError; |
188 | |
189 | strCode = chr; |
190 | |
191 | if (nextCode > MAX_CODE) { |
192 | |
193 | if (!_put_bits (OutBuff, (ULONG) ClearCode, codeBits, fimage)) |
194 | goto _ExitError; |
195 | |
196 | _init_dict (); |
197 | |
198 | } |
199 | else if (nextCode > bumpCode) { |
200 | |
201 | ++codeBits; |
202 | bumpCode <<= 1; |
203 | } |
204 | } |
205 | |
206 | if (x == width) { |
207 | |
208 | x = 0; |
209 | ++y; |
210 | pLine += inc; |
211 | } |
212 | } |
213 | |
214 | if (!_put_bits (OutBuff, (ULONG) strCode, codeBits, fimage)) goto _ExitError; |
215 | if (!_put_bits (OutBuff, (ULONG) EOFCode, codeBits, fimage)) goto _ExitError; |
216 | if (!_flush_bits (OutBuff, fimage)) goto _ExitError; |
217 | |
218 | |
219 | if (dict) |
220 | Xw_free (dict); |
221 | |
222 | return XW_SUCCESS; |
223 | |
224 | |
225 | |
226 | _ExitError: |
227 | |
228 | if (dict) |
229 | Xw_free (dict); |
230 | |
231 | return XW_ERROR; |
232 | } |
233 | /* end _lzw_encode */ |
234 | |
235 | |
236 | static void _init_dict ( ) |
237 | { |
238 | memset (dict, UNUSED, sizeof (LZWDict)*TBL_SIZE); |
239 | |
240 | nextCode = FreeCode; |
241 | codeBits = startBits + 1; |
242 | bumpCode = 1 << codeBits; |
243 | } |
244 | |
245 | static int _find_child |
246 | #ifdef XW_PROTOTYPE |
247 | (int prntCode, int chr) |
248 | #else |
249 | (prntCode, chr) |
250 | int prntCode; int chr; |
251 | #endif |
252 | { |
253 | int idx, offset; |
254 | |
255 | idx = ( chr << ( NBITS - 8 ) ) ^ prntCode; |
256 | offset = idx ? TBL_SIZE - idx : 1; |
257 | |
258 | for (;;) { |
259 | |
260 | if (dict[ idx ].code == UNUSED || |
261 | dict[ idx ].prnt == prntCode && dict[ idx ].byte == ( BYTE )chr) |
262 | return idx; |
263 | |
264 | idx = ( idx >= offset ) ? idx - offset : idx + TBL_SIZE - offset; |
265 | } |
266 | } |
267 | |
268 | static BOOL _put_bits |
269 | #ifdef XW_PROTOTYPE |
270 | (BOOL* OutBuff, ULONG code, UINT nBits, FILE* fimage) |
271 | #else |
272 | (OutBuff, code, nBits, fimage) |
273 | BYTE *OutBuff; ULONG code; UINT nBits; FILE *fimage; |
274 | #endif |
275 | { |
276 | BOOL retVal = TRUE; |
277 | ULONG msk; |
278 | |
279 | msk = 1; |
280 | |
281 | while (msk != (ULONG) (1 << nBits)) { |
282 | |
283 | if ( msk & code ) |
284 | rack |= mask; |
285 | |
286 | mask <<= 1; |
287 | |
288 | if ((mask & 0xFF) == 0) { |
289 | OutBuff[ putIdx++ ] = rack; |
290 | ++OutBuff[ 0 ]; |
291 | |
292 | if (putIdx == BUFF_SIZE) { |
293 | |
294 | if ((int)fwrite (OutBuff, BUFF_SIZE, 1, fimage) < 1) { |
295 | |
296 | retVal = FALSE; |
297 | break; |
298 | } |
299 | |
300 | putIdx = 1; |
301 | OutBuff[ 0 ] = 0; |
302 | } |
303 | |
304 | rack = 0; |
305 | mask = 0x01; |
306 | } |
307 | |
308 | msk <<= 1; |
309 | } |
310 | |
311 | return retVal; |
312 | } |
313 | |
314 | static BOOL _flush_bits |
315 | #ifdef XW_PROTOTYPE |
316 | (BYTE* OutBuff, FILE* fimage) |
317 | #else |
318 | (OutBuff, fimage) |
319 | BYTE *OutBuff; |
320 | FILE *fimage; |
321 | #endif |
322 | { |
323 | |
324 | BOOL retVal = TRUE; |
325 | BYTE byte; |
326 | // DWORD dwBytesWritten; |
327 | |
328 | if ( mask != 0x01 ) { |
329 | |
330 | OutBuff[ putIdx++ ] = rack; |
331 | ++OutBuff[ 0 ]; |
332 | } |
333 | |
334 | if (putIdx != 1) { |
335 | |
336 | if ((int)fwrite (OutBuff, putIdx, 1, fimage) < 1) |
337 | retVal = FALSE; |
338 | } |
339 | |
340 | if (retVal) { |
341 | |
342 | byte = 0; |
343 | |
344 | if ((int)fwrite (&byte, 1, 1, fimage) < 1) |
345 | retVal = FALSE; |
346 | } |
347 | |
348 | return retVal; |
349 | } |
350 | |
351 | static BOOL _convert_image_to_256_RGB_indices |
352 | #ifdef XW_PROTOTYPE |
353 | (XImage* pximage, XColor* pcolors, BYTE* colors256, BYTE* pIndices) |
354 | #else |
355 | (pximage, pcolors, colors256, pIndices) |
356 | XImage *pximage; |
357 | XColor *pcolors; |
358 | BYTE* colors256; |
359 | BYTE* pIndices; |
360 | #endif |
361 | { |
362 | int mult_r, mult_g, mult_b, blue_mask, red_mask, green_mask; |
363 | int i, x, y; |
364 | // BYTE rmap[256],gmap[256],bmap[256]; |
365 | // BYTE imap[256], nc; |
366 | BYTE *pic24 = (BYTE*) malloc (pximage->width*pximage->height*3); |
367 | BYTE *pic8 = pIndices, *pbCell; |
368 | |
369 | |
370 | |
371 | if (pic24 == NULL) |
372 | return FALSE; |
373 | |
374 | |
375 | if (pcolors == NULL) { |
376 | |
377 | blue_mask = pximage->blue_mask; |
378 | green_mask = pximage->green_mask; |
379 | red_mask = pximage->red_mask; |
380 | |
381 | if (!red_mask || !green_mask || !blue_mask) |
382 | return FALSE; |
383 | |
384 | |
385 | mult_r = mult_g = mult_b = 0; |
386 | |
387 | for (i=red_mask; (i&1)^1; i >>= 1) |
388 | mult_r++; |
389 | for (i=green_mask; (i&1)^1; i >>= 1) |
390 | mult_g++; |
391 | for (i=blue_mask; (i&1)^1; i >>= 1) |
392 | mult_b++; |
393 | |
394 | |
395 | /* Prepare 24-bit picture format (in order R-byte, G-byte, B-byte). */ |
396 | pbCell = pic24; |
397 | for (y=0; y<pximage->height; y++) |
398 | for (x=0; x<pximage->width; x++) { |
399 | |
400 | unsigned long pix = XGetPixel (pximage, x, y); |
401 | |
402 | *pbCell++ = (pix & red_mask) >> mult_r; |
403 | *pbCell++ = (pix & green_mask) >> mult_g; |
404 | *pbCell++ = (pix & blue_mask) >> mult_b; |
405 | } |
406 | } |
407 | else { /* using PseudoColors indices from pcolors to convert to 24-bit format */ |
408 | |
409 | /* Prepare 24-bit picture format using given colors */ |
410 | pbCell = pic24; |
411 | for (y=0; y<pximage->height; y++) |
412 | for (x=0; x<pximage->width; x++) { |
413 | |
414 | unsigned long pix = XGetPixel (pximage, x, y); |
415 | |
416 | /* Assume pix is the index to colormap */ |
417 | *pbCell++ = pcolors[pix].flags & DoRed? pcolors[pix].red>>8: 0; |
418 | *pbCell++ = pcolors[pix].flags & DoGreen? pcolors[pix].green>>8: 0; |
419 | *pbCell++ = pcolors[pix].flags & DoBlue? pcolors[pix].blue>>8: 0; |
420 | } |
421 | } |
422 | |
423 | |
424 | /* How much colors do we have used in the image ? */ |
425 | if (quick_check(pic24, pximage->width, pximage->height, pic8, colors256)) { |
426 | |
427 | free (pic24); |
428 | return TRUE; /* less than 256 colors found. No conversion is necessary. */ |
429 | } |
430 | |
431 | |
432 | /* Over 256 colors used! We need to convert them. */ |
433 | |
434 | #if defined(CONV24_FAST) /* Use quick color transformation */ |
435 | |
436 | #ifdef DEBUG |
437 | fprintf(stderr,"Xw_save_gif_image: Doing 'quick' 24-bit to 8-bit conversion.\r\n"); |
438 | #endif |
439 | |
440 | i = quick_quant(pic24, pximage->width, pximage->height, pic8, colors256); |
441 | |
442 | #else /* assume defined(CONV24_BEST) */ /* Do it slowly */ |
443 | |
444 | #ifdef DEBUG |
445 | fprintf(stderr,"Xw_save_gif_image: Doing 'best' 24-bit to 8-bit conversion.\r\n"); |
446 | #endif |
447 | |
448 | i = ppm_quant(pic24, pximage->width, pximage->height, pic8, colors256); |
449 | #endif /* CONV24_FAST */ |
450 | |
451 | free (pic24); |
452 | |
453 | return (!i); |
454 | } |
455 | #ifdef XW_PROTOTYPE |
456 | XW_STATUS Xw_save_gif_image_adv (Display *aDisplay,Window aWindow,XWindowAttributes aWinAttr,XImage *aPximage,Colormap aColormap,int aNcolors,char *filename) |
457 | #else |
458 | XW_STATUS Xw_save_gif_image_adv (aDisplay,aWindow,aWinAttr,aPximage,aColormap,aNcolors,filename) |
459 | Display *aDisplay; |
460 | Window aWindow; |
461 | XWindowAttributes aWinAttr; |
462 | XImage *aPximage; |
463 | Colormap aColormap; |
464 | int ncolors; |
465 | char *filename; |
466 | #endif /*XW_PROTOTYPE*/ |
467 | { |
468 | BYTE colors256[256][3]; |
469 | BYTE *pBits = NULL; |
470 | char *wname = NULL,*pname = NULL ; |
471 | int i,isize,lname,iclass,ncolors, x,y; |
472 | Visual *pvisual = aWinAttr.visual; |
473 | XColor *pcolors = NULL; |
474 | XWDColor *qcolors = NULL; |
475 | FILE *fimage = NULL ; |
476 | XW_STATUS status = XW_SUCCESS; |
477 | BYTE image_sep = 0x2C; /* gif colormap delimiter */ |
478 | WORD wZero = 0x00; |
479 | #if defined(LIN) || defined(linux) |
480 | SCREEN_DESCR* sd; |
481 | IMAGE_DESCR* id; |
482 | #else |
483 | SCREEN_DESCR sd; |
484 | IMAGE_DESCR id; |
485 | #endif |
486 | BYTE *pbCell; |
487 | |
488 | /* Open GIF File */ |
489 | pname = Xw_get_filename (filename, "gif"); |
490 | if (pname) |
491 | fimage = fopen(pname,"w") ; |
492 | |
493 | if( !fimage ) { /*ERROR*Bad Filename*/ |
494 | Xw_set_error(55,"Xw_save_gif_image",filename) ; |
495 | return (XW_ERROR) ; |
496 | } |
497 | |
498 | /* Get Window name */ |
499 | XFetchName(aDisplay,aWindow,&wname); |
500 | |
501 | if (!wname || !strlen (wname)) |
502 | wname = (char*) strdup(pname); |
503 | lname = strlen (wname) + 1; |
504 | |
505 | iclass = pvisual->c_class; |
506 | #ifdef PRO5356 |
507 | if( (iclass == PseudoColor) && (aPximage->red_mask > 0) ) iclass = TrueColor; |
508 | /* iclass = (aPximage->depth > 8) ? TrueColor : pvisual->c_class; GG_240898 */ |
509 | #endif |
510 | |
511 | #ifdef DEBUG |
512 | fprintf (stderr, "Xw_save_gif_image: max colors available = %d\r\n", |
513 | aColormap? aNcolors: 0); |
514 | #endif |
515 | |
516 | /* Get color table */ |
517 | switch (iclass) { |
518 | |
519 | case PseudoColor : |
520 | ncolors = aNcolors; |
521 | if ((pcolors = (XColor*) Xw_calloc(ncolors,sizeof(XColor)))) { |
522 | for (i=0 ; i<ncolors ; i++) { |
523 | pcolors[i].pixel = i; |
524 | pcolors[i].pad = 0; |
525 | } |
526 | XQueryColors(aDisplay,aColormap,pcolors,ncolors); |
527 | } |
528 | else { |
529 | /*ERROR*Bad Image allocation*/ |
530 | Xw_set_error(60,"Xw_save_gif_image",0) ; |
531 | Xw_free(wname) ; |
532 | #ifndef PRO16753 |
533 | Xw_free(pname) ; |
534 | #endif |
535 | fclose(fimage) ; |
536 | return (XW_ERROR) ; |
537 | } |
538 | break ; |
539 | |
540 | |
541 | case DirectColor : |
542 | ncolors = aNcolors; |
543 | |
544 | if (ncolors > 256) { |
545 | ncolors = 0; |
546 | break; |
547 | } |
548 | |
549 | if(( pcolors = (XColor*) Xw_calloc(ncolors,sizeof(XColor)) )) { |
550 | unsigned long red = 0,lred = LOWBIT(pvisual->red_mask); |
551 | unsigned long green = 0,lgreen = LOWBIT(pvisual->green_mask); |
552 | unsigned long blue = 0,lblue = LOWBIT(pvisual->blue_mask); |
553 | for( i=0 ; i<ncolors ; i++) { |
554 | pcolors[i].pixel = red|green|blue; |
555 | pcolors[i].pad = 0; |
556 | red += lred; |
557 | if( red > pvisual->red_mask ) red = 0; |
558 | green += lgreen; |
559 | if( green > pvisual->green_mask ) green = 0; |
560 | blue += lblue; |
561 | if( blue > pvisual->blue_mask ) blue = 0; |
562 | } |
563 | XQueryColors(aDisplay,aColormap,pcolors,ncolors); |
564 | } |
565 | else { /*ERROR*Bad Image allocation*/ |
566 | Xw_set_error(60,"Xw_save_gif_image",0) ; |
567 | Xw_free(wname) ; |
568 | #ifndef PRO16753 |
569 | Xw_free(pname) ; |
570 | #endif |
571 | fclose(fimage) ; |
572 | return (XW_ERROR) ; |
573 | } |
574 | break ; |
575 | |
576 | |
577 | case TrueColor : |
578 | ncolors = 0; |
579 | break; |
580 | |
581 | |
582 | default : /*ERROR*Unimplemented Image Visual class*/ |
583 | Xw_set_error(59,"Xw_save_bmp_image",&iclass); |
584 | #ifndef PRO16753 |
585 | Xw_free(pname); |
586 | #endif |
587 | Xw_free(wname); |
588 | fclose(fimage); |
589 | return (XW_ERROR); |
590 | } |
591 | |
592 | isize = aPximage->width * aPximage->height; /* size of array of pixel color indices */ |
593 | pBits = (BYTE *) Xw_malloc (isize); |
594 | |
595 | if (pBits == NULL) { /*ERROR*Bad Image allocation*/ |
596 | Xw_set_error(60,"Xw_save_gif_image",0) ; |
597 | Xw_free(wname); |
598 | #ifndef PRO16753 |
599 | Xw_free(pname); |
600 | #endif |
601 | fclose(fimage); |
602 | return (XW_ERROR) ; |
603 | } |
604 | |
605 | /* Build file header */ |
606 | #if defined(LIN) || defined(linux) |
607 | sd = (SCREEN_DESCR *) Xw_malloc(11); |
608 | memcpy (sd->gifID, "GIF87a", 6); |
609 | sd->scrnWidth = DisplayWidth(aDisplay, DefaultScreen(aDisplay)); |
610 | sd->scrnHeight = DisplayHeight(aDisplay, DefaultScreen(aDisplay)); |
611 | sd->scrnFlag = 0x80 | ( ( 7/*[=depth-1]*/ << 4 ) & 0x70 ) | 0x07; |
612 | |
613 | id = (IMAGE_DESCR*) Xw_malloc(9); |
614 | id->imgX = 0; |
615 | id->imgY = 0; |
616 | id->imgWidth = (WORD) aPximage->width; |
617 | id->imgHeight = (WORD) aPximage->height; |
618 | #else |
619 | memcpy (sd.gifID, "GIF87a", 6); |
620 | sd.scrnWidth = SWAP_WORD (DisplayWidth(aDisplay, DefaultScreen(aDisplay))); |
621 | sd.scrnHeight = SWAP_WORD (DisplayHeight(aDisplay, DefaultScreen(aDisplay))); |
622 | sd.scrnFlag = 0x80 | ( ( 7/*[=depth-1]*/ << 4 ) & 0x70 ) | 0x07; |
623 | |
624 | id.imgX = 0; |
625 | id.imgY = 0; |
626 | id.imgWidth = SWAP_WORD ((WORD) aPximage->width); |
627 | id.imgHeight = SWAP_WORD ((WORD) aPximage->height); |
628 | #endif |
629 | |
630 | /* |
631 | imgFlag |
632 | +-+-+-+-+-+-----+ M=0 - Use global color map, ignore 'pixel' |
633 | |M|I|0|0|0|pixel| 10 M=1 - Local color map follows, use 'pixel' |
634 | +-+-+-+-+-+-----+ I=0 - Image formatted in Sequential order |
635 | I=1 - Image formatted in Interlaced order |
636 | pixel+1 - # bits per pixel for this image |
637 | */ |
638 | #if defined(LIN) || defined(linux) |
639 | id->imgFlag = 0x07; /* Global color map, Sequential order, 8 bits per pixel */ |
640 | #else |
641 | id.imgFlag = 0x07; |
642 | #endif |
643 | |
644 | /* Prepare color table for saving (256 RGB tripplets) and array of pixel |
645 | that refers to that table. |
646 | Note, that the table is limited up to 256 entries. |
647 | So, we'll probably have to ignore some rairly used colors. |
648 | In the case we have less than 256 colors in the image, |
649 | we'll expand them up to 256 using 'black' RGB (i.e. 000). |
650 | */ |
651 | memset (colors256, 0, sizeof (colors256)); /* black if unused */ |
652 | |
653 | if (ncolors > 0 && ncolors <= 256) |
654 | { |
655 | /* Use just all colors that have been specified in pximage */ |
656 | |
657 | for (i=0; i<ncolors; i++) { |
658 | colors256[i] [0/*R*/] = pcolors[i].flags & DoRed ? pcolors[i].red>>8: 0; |
659 | colors256[i] [1/*G*/] = pcolors[i].flags & DoGreen ? pcolors[i].green>>8: 0; |
660 | colors256[i] [2/*B*/] = pcolors[i].flags & DoBlue ? pcolors[i].blue>>8: 0; |
661 | } |
662 | |
663 | pbCell = pBits; |
664 | for (y=0; y<aPximage->height; y++) |
665 | for (x=0; x<aPximage->width; x++) { |
666 | unsigned long pixel = XGetPixel (aPximage, x, y); |
667 | *pbCell++ = pixel; |
668 | } |
669 | } |
670 | else { |
671 | |
672 | /* Too big color map */ |
673 | /* Substitute rairly used colors in order to have exactly 256 colors */ |
674 | |
675 | if (iclass == PseudoColor && pcolors == NULL) { |
676 | fprintf (stderr, "%s: Impossible situation at line %d! Abort.\n", |
677 | __FILE__, __LINE__); |
678 | exit (1); /* Impossible situation! */ |
679 | } |
680 | if (iclass != PseudoColor && pcolors != NULL) { |
681 | fprintf (stderr, "%s: Impossible situation at line %d! Abort.\n", |
682 | __FILE__, __LINE__); |
683 | exit (1); /* Impossible situation! */ |
684 | } |
685 | |
686 | /* Search for the top 256 colors and convert pixels to indices */ |
687 | if (! _convert_image_to_256_RGB_indices (aPximage, |
688 | pcolors, |
689 | (BYTE*)colors256, pBits)) { |
690 | |
691 | /*ERROR*Bad Image allocation*/ |
692 | Xw_set_error(60,"Xw_save_gif_image",0) ; |
693 | Xw_free(wname); |
694 | #ifndef PRO16753 |
695 | Xw_free(pname); |
696 | #endif |
697 | Xw_free(pBits); |
698 | fclose(fimage); |
699 | return (XW_ERROR); |
700 | } |
701 | } |
702 | |
703 | /* write off the buffers */ |
704 | #if defined(LIN) || defined(linux) |
705 | if (status && (int)fwrite(sd, 11, 1, fimage) < 1) |
706 | status = XW_ERROR; |
707 | Xw_free(sd); |
708 | #else |
709 | if (status && (int)fwrite(&sd, 11, 1, fimage) < 1) |
710 | status = XW_ERROR; |
711 | #endif |
712 | |
713 | if (status && (int)fwrite(&wZero, 2, 1, fimage) < 1) /*screen bacground byte folowed by zero*/ |
714 | status = XW_ERROR; |
715 | |
716 | if (status && (int)fwrite(&colors256, 256*3, 1, fimage) < 1) |
717 | status = XW_ERROR; |
718 | |
719 | |
720 | /* Write comment blocks (the window name) |
721 | |
722 | if (lname > 1) { |
723 | char *sp; |
724 | int i, blen; |
725 | |
726 | fputc(0x21, fimage); |
727 | fputc(0xFE, fimage); |
728 | |
729 | sp = wname; |
730 | while ( (blen=strlen(sp)) > 0) { |
731 | if (blen>255) blen = 255; |
732 | fputc(blen, fimage); |
733 | for (i=0; i<blen; i++, sp++) fputc(*sp, fimage); |
734 | } |
735 | fputc(0, fimage); |
736 | } |
737 | */ |
738 | |
739 | if (status && (int)fwrite(&image_sep, 1, 1, fimage) < 1) |
740 | status = XW_ERROR; |
741 | #if defined(LIN) || defined(linux) |
742 | if (status && (int)fwrite(id, 9, 1, fimage) < 1) |
743 | status = XW_ERROR; |
744 | Xw_free(id); |
745 | #else |
746 | if (status && (int)fwrite(&id, 9, 1, fimage) < 1) |
747 | status = XW_ERROR; |
748 | #endif |
749 | // status && (status |
750 | // = _lzw_encode (fimage, pBits, aPximage->width, aPximage->height, aPximage->width)); |
751 | _lzw_encode (fimage, pBits, aPximage->width, aPximage->height, aPximage->width); |
752 | |
753 | fclose(fimage); |
754 | |
755 | #ifndef PRO16753 |
756 | Xw_free(pname); |
757 | #endif |
758 | Xw_free(wname); |
759 | if (pcolors) |
760 | Xw_free (pcolors); |
761 | if (pBits) |
762 | Xw_free (pBits); |
763 | |
764 | return status; |
765 | } |
766 | |
767 | /*********************************************************************/ |
768 | /* Xw_save_gif_image */ |
769 | /* */ |
770 | /*********************************************************************/ |
771 | XW_STATUS Xw_save_gif_image |
772 | #ifdef XW_PROTOTYPE |
773 | (void *awindow,void *aimage,char *filename) |
774 | #else |
775 | (awindow,aimage,filename) |
776 | void *awindow; |
777 | void *aimage; |
778 | char *filename ; |
779 | #endif /*XW_PROTOTYPE*/ |
780 | { |
781 | XW_EXT_WINDOW *pwindow = (XW_EXT_WINDOW*)awindow; |
782 | XW_EXT_IMAGEDATA *pimage = (XW_EXT_IMAGEDATA*) aimage; |
783 | XImage *pximage = NULL; |
784 | XW_STATUS status = XW_SUCCESS; |
785 | |
786 | if (!Xw_isdefine_window(pwindow)) { /*ERROR*Bad EXT_WINDOW Address*/ |
787 | Xw_set_error(24,"Xw_save_gif_image",pwindow) ; |
788 | return (XW_ERROR) ; |
789 | } |
790 | |
791 | if( !Xw_isdefine_image(pimage) ) { /*ERROR*Bad EXT_IMAGEDATA Address*/ |
792 | Xw_set_error(25,"Xw_save_gif_image",pimage) ; |
793 | return (XW_ERROR) ; |
794 | } |
795 | |
796 | pximage = (_ZIMAGE) ? _ZIMAGE : _IIMAGE; |
797 | |
798 | status = Xw_save_gif_image_adv(_DISPLAY,_WINDOW,_ATTRIBUTES,pximage,_COLORMAP->info.colormap,_COLORMAP->maxhcolor,filename); |
799 | |
800 | #ifdef TRACE_SAVE_IMAGE |
801 | if (Xw_get_trace ()) |
802 | printf (" %d = Xw_save_gif_image(%lx,%lx,'%s')\n", |
803 | status, (long ) pwindow, (long ) pimage, filename); |
804 | #endif |
805 | |
806 | return (XW_STATUS) status; |
807 | } |
808 | |
809 | |
810 | |
811 | /* |
812 | colormap conversion routines |
813 | |
814 | */ |
815 | |
816 | static int quick_check |
817 | #ifdef XW_PROTOTYPE |
818 | (BYTE* pic24, int w, int h, BYTE* pic8, BYTE* rgbmap) |
819 | #else |
820 | (pic24, w,h, pic8, rgbmap) |
821 | BYTE *pic24, *pic8, *rgbmap; |
822 | int w,h; |
823 | #endif |
824 | { |
825 | /* scans picture until it finds more than 256 different colors. If it |
826 | finds more than 256 colors, it returns '0'. If it DOESN'T, it does |
827 | the 24-to-8 conversion by simply sticking the colors it found into |
828 | a colormap, and changing instances of a color in pic24 into colormap |
829 | indicies (in pic8) */ |
830 | |
831 | unsigned long colors[256],col; |
832 | int i, nc, low, high, mid; |
833 | BYTE *p, *pix; |
834 | |
835 | |
836 | /* put the first color in the table by hand */ |
837 | nc = 0; mid = 0; |
838 | |
839 | for (i=w*h,p=pic24; i; i--) { |
840 | col = (((unsigned long) *p++) << 16); |
841 | col += (((unsigned long) *p++) << 8); |
842 | col += *p++; |
843 | |
844 | /* binary search the 'colors' array to see if it's in there */ |
845 | low = 0; high = nc-1; |
846 | while (low <= high) { |
847 | mid = (low+high)/2; |
848 | if (col < colors[mid]) high = mid - 1; |
849 | else if (col > colors[mid]) low = mid + 1; |
850 | else break; |
851 | } |
852 | |
853 | if (high < low) { /* didn't find color in list, add it. */ |
854 | |
855 | if (nc>=256) |
856 | return FALSE; /* over 256 colors used! */ |
857 | |
858 | #if defined (__hpux) || defined(HPUX) |
859 | //JR/Hp : bcopy |
860 | memmove((char *) &colors[low+1], (char *) &colors[low], |
861 | (nc - low) * sizeof(unsigned long)); |
862 | #else |
863 | bcopy((char *) &colors[low], (char *) &colors[low+1], |
864 | (nc - low) * sizeof(unsigned long)); |
865 | #endif |
866 | |
867 | colors[low] = col; |
868 | nc++; |
869 | } |
870 | } |
871 | #ifdef DEBUG |
872 | fprintf(stderr,"quick_check: there're %d colors used in the image.\r\n", nc); |
873 | #endif |
874 | |
875 | /* run through the data a second time, this time mapping pixel values in |
876 | pic24 into colormap offsets into 'colors' */ |
877 | |
878 | for (i=w*h,p=pic24, pix=pic8; i; i--,pix++) { |
879 | col = (((unsigned long) *p++) << 16); |
880 | col += (((unsigned long) *p++) << 8); |
881 | col += *p++; |
882 | |
883 | /* binary search the 'colors' array. It *IS* in there */ |
884 | low = 0; high = nc-1; |
885 | while (low <= high) { |
886 | mid = (low+high)/2; |
887 | if (col < colors[mid]) high = mid - 1; |
888 | else if (col > colors[mid]) low = mid + 1; |
889 | else break; |
890 | } |
891 | |
892 | if (high < low) { /* no RGB ? */ |
893 | fprintf(stderr,"quick_check: impossible situation!\r\n"); |
894 | exit(1); |
895 | } |
896 | *pix = mid; |
897 | } |
898 | |
899 | /* and load up the 'desired colormap' */ |
900 | for (i=0, p=rgbmap; i<nc; i++) { |
901 | *p++ /*i-th R*/ = colors[i]>>16; |
902 | *p++ /*i-th G*/ = (colors[i]>>8) & 0xff; |
903 | *p++ /*i-th B*/ = colors[i] & 0xff; |
904 | } |
905 | |
906 | return nc; /* # colors udsed */ |
907 | } |
908 | |
909 | |
910 | |
911 | |
912 | /************************************/ |
913 | #if defined(CONV24_FAST) /* Use quick color transformation */ |
914 | static int quick_quant |
915 | #ifdef XW_PROTOTYPE |
916 | (BYTE* p24, int w, int h, BYTE* p8, BYTE* rgbmap) |
917 | #else |
918 | (p24,w,h, p8, rgbmap) |
919 | BYTE *p24, *p8, *rgbmap; |
920 | int w,h; |
921 | #endif |
922 | { |
923 | /* called after 'pic8' has been alloced */ |
924 | |
925 | /* up to 256 colors: 3 bits R, 3 bits G, 2 bits B (RRRGGGBB) */ |
926 | #define RMASK 0xe0 |
927 | #define RSHIFT 0 |
928 | #define GMASK 0xe0 |
929 | #define GSHIFT 3 |
930 | #define BMASK 0xc0 |
931 | #define BSHIFT 6 |
932 | |
933 | BYTE *pp; |
934 | int r1, g1, b1; |
935 | int *thisline, *nextline, *thisptr, *nextptr, *tmpptr; |
936 | int i, j, val, pwide3; |
937 | int imax, jmax; |
938 | |
939 | pp = p8; pwide3 = w * 3; imax = h-1; jmax = w-1; |
940 | |
941 | |
942 | /* load up colormap: |
943 | * note that 0 and 255 of each color are always in the map; |
944 | * intermediate values are evenly spaced. |
945 | */ |
946 | |
947 | for (i=0; i<256; i++) { |
948 | rgbmap[i*3+0] = (((i<<RSHIFT) & RMASK) * 255 + RMASK/2) / RMASK; |
949 | rgbmap[i*3+1] = (((i<<GSHIFT) & GMASK) * 255 + GMASK/2) / GMASK; |
950 | rgbmap[i*3+2] = (((i<<BSHIFT) & BMASK) * 255 + BMASK/2) / BMASK; |
951 | } |
952 | |
953 | |
954 | thisline = (int *) malloc(pwide3 * sizeof(int)); |
955 | nextline = (int *) malloc(pwide3 * sizeof(int)); |
956 | if (!thisline || !nextline) { |
957 | if (thisline) free(thisline); |
958 | if (nextline) free(nextline); |
959 | fprintf(stderr,"quick_quant: unable to allocate memory in quick_quant()\r\n"); |
960 | return(1); |
961 | } |
962 | |
963 | /* get first line of picture */ |
964 | for (j=pwide3, tmpptr=nextline; j; j--) *tmpptr++ = (int) *p24++; |
965 | |
966 | for (i=0; i<h; i++) { |
967 | tmpptr = thisline; thisline = nextline; nextline = tmpptr; /* swap */ |
968 | |
969 | if (i!=imax) /* get next line */ |
970 | for (j=pwide3, tmpptr=nextline; j; j--) |
971 | *tmpptr++ = (int) *p24++; |
972 | |
973 | for (j=0, thisptr=thisline, nextptr=nextline; j<w; j++,pp++) { |
974 | r1 = *thisptr++; g1 = *thisptr++; b1 = *thisptr++; |
975 | RANGE(r1,0,255); RANGE(g1,0,255); RANGE(b1,0,255); |
976 | |
977 | /* choose actual pixel value */ |
978 | val = (((r1&RMASK)>>RSHIFT) | ((g1&GMASK)>>GSHIFT) | |
979 | ((b1&BMASK)>>BSHIFT)); |
980 | *pp = val; |
981 | |
982 | /* compute color errors */ |
983 | r1 -= rgbmap[val*3+0]; |
984 | g1 -= rgbmap[val*3+1]; |
985 | b1 -= rgbmap[val*3+2]; |
986 | |
987 | /* Add fractions of errors to adjacent pixels */ |
988 | if (j!=jmax) { /* adjust RIGHT pixel */ |
989 | thisptr[0] += (r1*7) / 16; |
990 | thisptr[1] += (g1*7) / 16; |
991 | thisptr[2] += (b1*7) / 16; |
992 | } |
993 | |
994 | if (i!=imax) { /* do BOTTOM pixel */ |
995 | nextptr[0] += (r1*5) / 16; |
996 | nextptr[1] += (g1*5) / 16; |
997 | nextptr[2] += (b1*5) / 16; |
998 | |
999 | if (j>0) { /* do BOTTOM LEFT pixel */ |
1000 | nextptr[-3] += (r1*3) / 16; |
1001 | nextptr[-2] += (g1*3) / 16; |
1002 | nextptr[-1] += (b1*3) / 16; |
1003 | } |
1004 | |
1005 | if (j!=jmax) { /* do BOTTOM RIGHT pixel */ |
1006 | nextptr[3] += (r1)/16; |
1007 | nextptr[4] += (g1)/16; |
1008 | nextptr[5] += (b1)/16; |
1009 | } |
1010 | nextptr += 3; |
1011 | } |
1012 | } |
1013 | } |
1014 | |
1015 | free(thisline); |
1016 | free(nextline); |
1017 | return 0; |
1018 | |
1019 | |
1020 | #undef RMASK |
1021 | #undef RSHIFT |
1022 | #undef GMASK |
1023 | #undef GSHIFT |
1024 | #undef BMASK |
1025 | #undef BSHIFT |
1026 | } |
1027 | #endif |
1028 | |
1029 | |
1030 | |
1031 | |
1032 | |
1033 | |
1034 | /***************************************************************/ |
1035 | /* The following code based on code from the 'pbmplus' package */ |
1036 | /* written by Jef Poskanzer */ |
1037 | /***************************************************************/ |
1038 | |
1039 | |
1040 | /* ppmquant.c - quantize the colors in a pixmap down to a specified number |
1041 | ** |
1042 | ** Copyright (C) 1989, 1991 by Jef Poskanzer. |
1043 | ** |
1044 | ** Permission to use, copy, modify, and distribute this software and its |
1045 | ** documentation for any purpose and without fee is hereby granted, provided |
1046 | ** that the above copyright notice appear in all copies and that both that |
1047 | ** copyright notice and this permission notice appear in supporting |
1048 | ** documentation. This software is provided "as is" without express or |
1049 | ** implied warranty. |
1050 | */ |
1051 | |
1052 | |
1053 | typedef unsigned char pixval; |
1054 | |
1055 | #define PPM_MAXMAXVAL 255 |
1056 | typedef struct { pixval r, g, b; } pixel; |
1057 | |
1058 | #define PPM_GETR(p) ((p).r) |
1059 | #define PPM_GETG(p) ((p).g) |
1060 | #define PPM_GETB(p) ((p).b) |
1061 | |
1062 | #define PPM_ASSIGN(p,red,grn,blu) \ |
1063 | { (p).r = (red); (p).g = (grn); (p).b = (blu); } |
1064 | |
1065 | #define PPM_EQUAL(p,q) ( (p).r == (q).r && (p).g == (q).g && (p).b == (q).b ) |
1066 | |
1067 | |
1068 | /* Color scaling macro -- to make writing ppmtowhatever easier. */ |
1069 | |
1070 | #define PPM_DEPTH(newp,p,oldmaxval,newmaxval) \ |
1071 | PPM_ASSIGN( (newp), \ |
1072 | ((int) PPM_GETR(p)) * ((int)newmaxval) / ((int)oldmaxval), \ |
1073 | ((int) PPM_GETG(p)) * ((int)newmaxval) / ((int)oldmaxval), \ |
1074 | ((int) PPM_GETB(p)) * ((int)newmaxval) / ((int)oldmaxval) ) |
1075 | |
1076 | |
1077 | /* Luminance macro. */ |
1078 | |
1079 | /* |
1080 | * #define PPM_LUMIN(p) \ |
1081 | * ( 0.299 * PPM_GETR(p) + 0.587 * PPM_GETG(p) + 0.114 * PPM_GETB(p) ) |
1082 | */ |
1083 | |
1084 | /* Luminance macro, using only integer ops. Returns an int (*256) JHB */ |
1085 | #define PPM_LUMIN(p) \ |
1086 | ( 77 * PPM_GETR(p) + 150 * PPM_GETG(p) + 29 * PPM_GETB(p) ) |
1087 | |
1088 | /* Color histogram stuff. */ |
1089 | |
1090 | typedef struct chist_item* chist_vec; |
1091 | struct chist_item { pixel color; |
1092 | int value; |
1093 | }; |
1094 | |
1095 | typedef struct chist_list_item* chist_list; |
1096 | struct chist_list_item { struct chist_item ch; |
1097 | chist_list next; |
1098 | }; |
1099 | |
1100 | typedef chist_list* chash_table; |
1101 | |
1102 | typedef struct box* box_vector; |
1103 | struct box { |
1104 | int index; |
1105 | int colors; |
1106 | int sum; |
1107 | }; |
1108 | |
1109 | |
1110 | #define MAXCOLORS 32767 |
1111 | #define CLUSTER_MAXVAL 63 |
1112 | |
1113 | #define LARGE_LUM |
1114 | #define REP_AVERAGE_PIXELS |
1115 | |
1116 | #define FS_SCALE 1024 |
1117 | |
1118 | #define HASH_SIZE 6553 |
1119 | |
1120 | #define ppm_hashpixel(p) ((((int) PPM_GETR(p) * 33023 + \ |
1121 | (int) PPM_GETG(p) * 30013 + \ |
1122 | (int) PPM_GETB(p) * 27011) & 0x7fffffff) \ |
1123 | % HASH_SIZE) |
1124 | |
1125 | |
1126 | |
1127 | /*** function defs ***/ |
1128 | |
1129 | static chist_vec mediancut PARM((chist_vec, int, int, int, int)); |
1130 | static int redcompare PARM((const void *, const void *)); |
1131 | static int greencompare PARM((const void *, const void *)); |
1132 | static int bluecompare PARM((const void *, const void *)); |
1133 | static int sumcompare PARM((const void *, const void *)); |
1134 | static chist_vec ppm_computechist PARM((pixel **, int,int,int,int *)); |
1135 | static chash_table ppm_computechash PARM((pixel **, int,int,int,int *)); |
1136 | static chist_vec ppm_chashtochist PARM((chash_table, int)); |
1137 | static chash_table ppm_allocchash PARM((void)); |
1138 | static void ppm_freechist PARM((chist_vec)); |
1139 | static void ppm_freechash PARM((chash_table)); |
1140 | |
1141 | |
1142 | /****************************************************************************/ |
1143 | static int ppm_quant |
1144 | #ifdef XW_PROTOTYPE |
1145 | (BYTE* pic24, int cols, int rows, BYTE* pic8, BYTE* rgbmap) |
1146 | #else |
1147 | (pic24, cols, rows, pic8, rgbmap) |
1148 | BYTE *pic24, *pic8, *rgbmap; |
1149 | int cols, rows; |
1150 | #endif |
1151 | { |
1152 | pixel** pixels; |
1153 | register pixel* pP; |
1154 | int row; |
1155 | register int col, limitcol; |
1156 | pixval maxval, newmaxval; |
1157 | int colors; |
1158 | register int index; |
1159 | chist_vec chv, colormap; |
1160 | chash_table cht; |
1161 | int i; |
1162 | unsigned char *picptr; |
1163 | static const char *fn = "ppmquant()"; |
1164 | BYTE* pByte; |
1165 | |
1166 | index = 0; |
1167 | maxval = 255; |
1168 | |
1169 | /* |
1170 | * reformat 24-bit pic24 image (3 bytes per pixel) into 2-dimensional |
1171 | * array of pixel structures |
1172 | */ |
1173 | |
1174 | #ifdef DEBUG |
1175 | fprintf(stderr,"%s: remapping to ppm-style internal fmt\r\n", fn); |
1176 | #endif |
1177 | |
1178 | pixels = (pixel **) malloc(rows * sizeof(pixel *)); |
1179 | if (!pixels) { |
1180 | fprintf (stderr, "%s: couldn't allocate 'pixels' array\r\n", fn); |
1181 | return -1; |
1182 | } |
1183 | |
1184 | for (row=0; row<rows; row++) { |
1185 | pixels[row] = (pixel *) malloc(cols * sizeof(pixel)); |
1186 | |
1187 | if (!pixels[row]) { |
1188 | fprintf (stderr, "%s: couldn't allocate a row of pixels array\r\n", fn); |
1189 | while (--row >=0 ) |
1190 | free (pixels[row]); |
1191 | free (pixels); |
1192 | return -1; |
1193 | } |
1194 | |
1195 | for (col=0, pP=pixels[row]; col<cols; col++, pP++) { |
1196 | pP->r = *pic24++; |
1197 | pP->g = *pic24++; |
1198 | pP->b = *pic24++; |
1199 | } |
1200 | } |
1201 | #ifdef DEBUG |
1202 | fprintf(stderr,"%s: done format remapping\r\n", fn); |
1203 | #endif |
1204 | |
1205 | |
1206 | |
1207 | /* |
1208 | * attempt to make a histogram of the colors, unclustered. |
1209 | * If at first we don't succeed, lower maxval to increase color |
1210 | * coherence and try again. This will eventually terminate, with |
1211 | * maxval at worst 15, since 32^3 is approximately MAXCOLORS. |
1212 | */ |
1213 | |
1214 | for ( ; ; ) { |
1215 | #ifdef DEBUG |
1216 | fprintf(stderr, "%s: making histogram\r\n", fn); |
1217 | #endif |
1218 | chv = ppm_computechist(pixels, cols, rows, MAXCOLORS, &colors); |
1219 | if (chv != (chist_vec) 0) break; |
1220 | |
1221 | #ifdef DEB |
1222 | fprintf(stderr, "%s: too many colors!\r\n", fn); |
1223 | #endif |
1224 | newmaxval = maxval / 2; |
1225 | #ifdef DEB |
1226 | fprintf(stderr, "%s: rescaling colors (maxval=%d) %s\r\n", |
1227 | fn, newmaxval, "to improve clustering"); |
1228 | #endif |
1229 | for (row=0; row<rows; ++row) |
1230 | for (col=0, pP=pixels[row]; col<cols; ++col, ++pP) |
1231 | PPM_DEPTH( *pP, *pP, maxval, newmaxval ); |
1232 | maxval = newmaxval; |
1233 | } |
1234 | |
1235 | #ifdef DEBUG |
1236 | fprintf(stderr,"%s: %d colors found\r\n", fn, colors); |
1237 | #endif |
1238 | |
1239 | |
1240 | /* |
1241 | * Step 3: apply median-cut to histogram, making the new colormap. |
1242 | */ |
1243 | |
1244 | #ifdef DEBUG |
1245 | fprintf(stderr, "%s: choosing %d colors\r\n", fn, 256); |
1246 | #endif |
1247 | colormap = mediancut(chv, colors, rows * cols, maxval, 256); |
1248 | if (colormap == (chist_vec) NULL) |
1249 | return -1; |
1250 | ppm_freechist(chv); |
1251 | |
1252 | |
1253 | |
1254 | /* |
1255 | * Step 4: map the colors in the image to their closest match in the |
1256 | * new colormap, and write 'em out. |
1257 | */ |
1258 | |
1259 | #ifdef DEBUG |
1260 | fprintf(stderr,"%s: mapping image to new colors\r\n", fn); |
1261 | #endif |
1262 | cht = ppm_allocchash(); |
1263 | |
1264 | picptr = pic8; |
1265 | for (row = 0; row < rows; ++row) { |
1266 | col = 0; limitcol = cols; pP = pixels[row]; |
1267 | |
1268 | do { |
1269 | int hash; |
1270 | chist_list chl; |
1271 | |
1272 | /* Check hash table to see if we have already matched this color. */ |
1273 | |
1274 | hash = ppm_hashpixel(*pP); |
1275 | for (chl = cht[hash]; chl; chl = chl->next) |
1276 | if (PPM_EQUAL(chl->ch.color, *pP)) {index = chl->ch.value; break;} |
1277 | |
1278 | if (!chl /*index = -1*/) {/* No; search colormap for closest match. */ |
1279 | register int i, r1, g1, b1, r2, g2, b2; |
1280 | register long dist, newdist; |
1281 | |
1282 | r1 = PPM_GETR( *pP ); |
1283 | g1 = PPM_GETG( *pP ); |
1284 | b1 = PPM_GETB( *pP ); |
1285 | dist = 2000000000; |
1286 | |
1287 | for (i=0; i<256; i++) { |
1288 | r2 = PPM_GETR( colormap[i].color ); |
1289 | g2 = PPM_GETG( colormap[i].color ); |
1290 | b2 = PPM_GETB( colormap[i].color ); |
1291 | |
1292 | newdist = ( r1 - r2 ) * ( r1 - r2 ) + |
1293 | ( g1 - g2 ) * ( g1 - g2 ) + |
1294 | ( b1 - b2 ) * ( b1 - b2 ); |
1295 | |
1296 | if (newdist<dist) { index = i; dist = newdist; } |
1297 | } |
1298 | |
1299 | hash = ppm_hashpixel(*pP); |
1300 | chl = (chist_list) malloc(sizeof(struct chist_list_item)); |
1301 | if (!chl) { |
1302 | fprintf (stderr, "%s: ran out of memory adding to hash table!\r\n", fn); |
1303 | return -1; |
1304 | } |
1305 | |
1306 | chl->ch.color = *pP; |
1307 | chl->ch.value = index; |
1308 | chl->next = cht[hash]; |
1309 | cht[hash] = chl; |
1310 | } |
1311 | |
1312 | *picptr++ = index; |
1313 | |
1314 | ++col; |
1315 | ++pP; |
1316 | } |
1317 | while (col != limitcol); |
1318 | } |
1319 | |
1320 | /* rescale the colormap and load the XV colormap */ |
1321 | for (i=0, pByte=rgbmap; i<256; i++) { |
1322 | PPM_DEPTH(colormap[i].color, colormap[i].color, maxval, 255); |
1323 | *pByte++ /*i-th R*/ = PPM_GETR( colormap[i].color ); |
1324 | *pByte++ /*i-th G*/ = PPM_GETG( colormap[i].color ); |
1325 | *pByte++ /*i-th B*/ = PPM_GETB( colormap[i].color ); |
1326 | } |
1327 | |
1328 | /* free the pixels array */ |
1329 | for (i=0; i<rows; i++) free(pixels[i]); |
1330 | free(pixels); |
1331 | |
1332 | /* free cht and colormap */ |
1333 | ppm_freechist(colormap); |
1334 | ppm_freechash(cht); |
1335 | |
1336 | return 0; |
1337 | } |
1338 | |
1339 | |
1340 | |
1341 | /* |
1342 | ** Here is the fun part, the median-cut colormap generator. This is based |
1343 | ** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer |
1344 | ** Display", SIGGRAPH '82 Proceedings, page 297. |
1345 | */ |
1346 | |
1347 | |
1348 | |
1349 | /****************************************************************************/ |
1350 | static chist_vec mediancut |
1351 | #ifdef XW_PROTOTYPE |
1352 | ( chist_vec chv, int colors, int sum, int maxval, int newcolors ) |
1353 | #else |
1354 | ( chv, colors, sum, maxval, newcolors ) |
1355 | chist_vec chv; |
1356 | int colors, sum, newcolors; |
1357 | int maxval; |
1358 | #endif |
1359 | { |
1360 | chist_vec colormap; |
1361 | box_vector bv; |
1362 | register int bi, i; |
1363 | int boxes; |
1364 | |
1365 | bv = (box_vector) malloc(sizeof(struct box) * newcolors); |
1366 | colormap = (chist_vec) |
1367 | malloc(sizeof(struct chist_item) * newcolors ); |
1368 | |
1369 | if (!bv || !colormap) { |
1370 | fprintf (stderr, "mediancut(): unable to malloc!\r\n"); |
1371 | free (bv); |
1372 | free (colormap); |
1373 | return (chist_vec)NULL; |
1374 | } |
1375 | |
1376 | for (i=0; i<newcolors; i++) |
1377 | PPM_ASSIGN(colormap[i].color, 0, 0, 0); |
1378 | |
1379 | /* |
1380 | * Set up the initial box. |
1381 | */ |
1382 | bv[0].index = 0; |
1383 | bv[0].colors = colors; |
1384 | bv[0].sum = sum; |
1385 | boxes = 1; |
1386 | |
1387 | |
1388 | /* |
1389 | ** Main loop: split boxes until we have enough. |
1390 | */ |
1391 | |
1392 | while ( boxes < newcolors ) { |
1393 | register int indx, clrs; |
1394 | int sm; |
1395 | register int minr, maxr, ming, maxg, minb, maxb, v; |
1396 | int halfsum, lowersum; |
1397 | |
1398 | /* |
1399 | ** Find the first splittable box. |
1400 | */ |
1401 | for (bi=0; bv[bi].colors<2 && bi<boxes; bi++) ; |
1402 | if (bi == boxes) break; /* ran out of colors! */ |
1403 | |
1404 | indx = bv[bi].index; |
1405 | clrs = bv[bi].colors; |
1406 | sm = bv[bi].sum; |
1407 | |
1408 | /* |
1409 | ** Go through the box finding the minimum and maximum of each |
1410 | ** component - the boundaries of the box. |
1411 | */ |
1412 | minr = maxr = PPM_GETR( chv[indx].color ); |
1413 | ming = maxg = PPM_GETG( chv[indx].color ); |
1414 | minb = maxb = PPM_GETB( chv[indx].color ); |
1415 | |
1416 | for (i=1; i<clrs; i++) { |
1417 | v = PPM_GETR( chv[indx + i].color ); |
1418 | if (v < minr) minr = v; |
1419 | if (v > maxr) maxr = v; |
1420 | |
1421 | v = PPM_GETG( chv[indx + i].color ); |
1422 | if (v < ming) ming = v; |
1423 | if (v > maxg) maxg = v; |
1424 | |
1425 | v = PPM_GETB( chv[indx + i].color ); |
1426 | if (v < minb) minb = v; |
1427 | if (v > maxb) maxb = v; |
1428 | } |
1429 | |
1430 | /* |
1431 | ** Find the largest dimension, and sort by that component. I have |
1432 | ** included two methods for determining the "largest" dimension; |
1433 | ** first by simply comparing the range in RGB space, and second |
1434 | ** by transforming into luminosities before the comparison. You |
1435 | ** can switch which method is used by switching the commenting on |
1436 | ** the LARGE_ defines at the beginning of this source file. |
1437 | */ |
1438 | { |
1439 | /* LARGE_LUM version */ |
1440 | |
1441 | pixel p; |
1442 | int rl, gl, bl; |
1443 | |
1444 | PPM_ASSIGN(p, maxr - minr, 0, 0); |
1445 | rl = PPM_LUMIN(p); |
1446 | |
1447 | PPM_ASSIGN(p, 0, maxg - ming, 0); |
1448 | gl = PPM_LUMIN(p); |
1449 | |
1450 | PPM_ASSIGN(p, 0, 0, maxb - minb); |
1451 | bl = PPM_LUMIN(p); |
1452 | |
1453 | if (rl >= gl && rl >= bl) |
1454 | qsort((char*) &(chv[indx]), (size_t) clrs, sizeof(struct chist_item), |
1455 | redcompare ); |
1456 | else if (gl >= bl) |
1457 | qsort((char*) &(chv[indx]), (size_t) clrs, sizeof(struct chist_item), |
1458 | greencompare ); |
1459 | else |
1460 | qsort((char*) &(chv[indx]), (size_t) clrs, sizeof(struct chist_item), |
1461 | bluecompare ); |
1462 | } |
1463 | |
1464 | /* |
1465 | ** Now find the median based on the counts, so that about half the |
1466 | ** pixels (not colors, pixels) are in each subdivision. |
1467 | */ |
1468 | lowersum = chv[indx].value; |
1469 | halfsum = sm / 2; |
1470 | for (i=1; i<clrs-1; i++) { |
1471 | if (lowersum >= halfsum) break; |
1472 | lowersum += chv[indx + i].value; |
1473 | } |
1474 | |
1475 | /* |
1476 | ** Split the box, and sort to bring the biggest boxes to the top. |
1477 | */ |
1478 | bv[bi].colors = i; |
1479 | bv[bi].sum = lowersum; |
1480 | bv[boxes].index = indx + i; |
1481 | bv[boxes].colors = clrs - i; |
1482 | bv[boxes].sum = sm - lowersum; |
1483 | ++boxes; |
1484 | qsort((char*) bv, (size_t) boxes, sizeof(struct box), sumcompare); |
1485 | } /* while (boxes ... */ |
1486 | |
1487 | /* |
1488 | ** Ok, we've got enough boxes. Now choose a representative color for |
1489 | ** each box. There are a number of possible ways to make this choice. |
1490 | ** One would be to choose the center of the box; this ignores any structure |
1491 | ** within the boxes. Another method would be to average all the colors in |
1492 | ** the box - this is the method specified in Heckbert's paper. A third |
1493 | ** method is to average all the pixels in the box. You can switch which |
1494 | ** method is used by switching the commenting on the REP_ defines at |
1495 | ** the beginning of this source file. |
1496 | */ |
1497 | |
1498 | for (bi=0; bi<boxes; bi++) { |
1499 | /* REP_AVERAGE_PIXELS version */ |
1500 | register int indx = bv[bi].index; |
1501 | register int clrs = bv[bi].colors; |
1502 | register long r = 0, g = 0, b = 0, sum = 0; |
1503 | |
1504 | for (i=0; i<clrs; i++) { |
1505 | r += PPM_GETR( chv[indx + i].color ) * chv[indx + i].value; |
1506 | g += PPM_GETG( chv[indx + i].color ) * chv[indx + i].value; |
1507 | b += PPM_GETB( chv[indx + i].color ) * chv[indx + i].value; |
1508 | sum += chv[indx + i].value; |
1509 | } |
1510 | |
1511 | r = r / sum; if (r>maxval) r = maxval; /* avoid math errors */ |
1512 | g = g / sum; if (g>maxval) g = maxval; |
1513 | b = b / sum; if (b>maxval) b = maxval; |
1514 | |
1515 | PPM_ASSIGN( colormap[bi].color, r, g, b ); |
1516 | } |
1517 | |
1518 | free(bv); |
1519 | return colormap; |
1520 | } |
1521 | |
1522 | |
1523 | /**********************************/ |
1524 | static int redcompare |
1525 | #ifdef XW_PROTOTYPE |
1526 | (const void *p1, const void *p2) |
1527 | #else |
1528 | (p1, p2) |
1529 | const void *p1, *p2; |
1530 | #endif |
1531 | { |
1532 | return (int) PPM_GETR( ((chist_vec)p1)->color ) - |
1533 | (int) PPM_GETR( ((chist_vec)p2)->color ); |
1534 | } |
1535 | |
1536 | /**********************************/ |
1537 | static int greencompare |
1538 | #ifdef XW_PROTOTYPE |
1539 | (const void *p1, const void *p2) |
1540 | #else |
1541 | (p1, p2) |
1542 | const void *p1, *p2; |
1543 | #endif |
1544 | { |
1545 | return (int) PPM_GETG( ((chist_vec)p1)->color ) - |
1546 | (int) PPM_GETG( ((chist_vec)p2)->color ); |
1547 | } |
1548 | |
1549 | /**********************************/ |
1550 | static int bluecompare |
1551 | #ifdef XW_PROTOTYPE |
1552 | (const void *p1, const void *p2) |
1553 | #else |
1554 | (p1, p2) |
1555 | const void *p1, *p2; |
1556 | #endif |
1557 | { |
1558 | return (int) PPM_GETB( ((chist_vec)p1)->color ) - |
1559 | (int) PPM_GETB( ((chist_vec)p2)->color ); |
1560 | } |
1561 | |
1562 | /**********************************/ |
1563 | static int sumcompare |
1564 | #ifdef XW_PROTOTYPE |
1565 | (const void *p1, const void *p2) |
1566 | #else |
1567 | (p1, p2) |
1568 | const void *p1, *p2; |
1569 | #endif |
1570 | { |
1571 | return ((box_vector) p2)->sum - ((box_vector) p1)->sum; |
1572 | } |
1573 | |
1574 | |
1575 | |
1576 | /****************************************************************************/ |
1577 | static chist_vec ppm_computechist |
1578 | #ifdef XW_PROTOTYPE |
1579 | (pixel** pixels, int cols, int rows, int maxcolors, int* colorsP) |
1580 | #else |
1581 | (pixels, cols, rows, maxcolors, colorsP) |
1582 | pixel** pixels; |
1583 | int cols, rows, maxcolors; |
1584 | int* colorsP; |
1585 | #endif |
1586 | { |
1587 | chash_table cht; |
1588 | chist_vec chv; |
1589 | |
1590 | cht = ppm_computechash(pixels, cols, rows, maxcolors, colorsP); |
1591 | if (!cht) return (chist_vec) NULL; |
1592 | |
1593 | chv = ppm_chashtochist(cht, maxcolors); |
1594 | ppm_freechash(cht); |
1595 | return chv; |
1596 | } |
1597 | |
1598 | |
1599 | /****************************************************************************/ |
1600 | static chash_table ppm_computechash |
1601 | #ifdef XW_PROTOTYPE |
1602 | (pixel** pixels, int cols, int rows, int maxcolors, int* colorsP ) |
1603 | #else |
1604 | (pixels, cols, rows, maxcolors, colorsP ) |
1605 | pixel** pixels; |
1606 | int cols, rows, maxcolors; |
1607 | int* colorsP; |
1608 | #endif |
1609 | { |
1610 | chash_table cht; |
1611 | register pixel* pP; |
1612 | chist_list chl; |
1613 | int col, row, hash; |
1614 | |
1615 | cht = ppm_allocchash( ); |
1616 | *colorsP = 0; |
1617 | |
1618 | /* Go through the entire image, building a hash table of colors. */ |
1619 | for (row=0; row<rows; row++) |
1620 | for (col=0, pP=pixels[row]; col<cols; col++, pP++) { |
1621 | hash = ppm_hashpixel(*pP); |
1622 | |
1623 | for (chl = cht[hash]; chl != (chist_list) 0; chl = chl->next) |
1624 | if (PPM_EQUAL(chl->ch.color, *pP)) break; |
1625 | |
1626 | if (chl != (chist_list) 0) ++(chl->ch.value); |
1627 | else { |
1628 | if ((*colorsP)++ > maxcolors) { |
1629 | ppm_freechash(cht); |
1630 | return (chash_table) NULL; |
1631 | } |
1632 | |
1633 | chl = (chist_list) malloc(sizeof(struct chist_list_item)); |
1634 | if (!chl) { |
1635 | fprintf (stderr, |
1636 | "ppm_computechash(): ran out of memory computing hash table!\r\n"); |
1637 | return (chash_table)(NULL); |
1638 | } |
1639 | |
1640 | chl->ch.color = *pP; |
1641 | chl->ch.value = 1; |
1642 | chl->next = cht[hash]; |
1643 | cht[hash] = chl; |
1644 | } |
1645 | } |
1646 | |
1647 | return cht; |
1648 | } |
1649 | |
1650 | |
1651 | /****************************************************************************/ |
1652 | static chash_table ppm_allocchash() |
1653 | { |
1654 | chash_table cht; |
1655 | int i; |
1656 | |
1657 | cht = (chash_table) malloc( HASH_SIZE * sizeof(chist_list) ); |
1658 | if (!cht) { |
1659 | fprintf (stderr, "ppm_allochash(): ran out of memory allocating hash table!\r\n"); |
1660 | return cht; |
1661 | } |
1662 | |
1663 | for (i=0; i<HASH_SIZE; i++ ) |
1664 | cht[i] = (chist_list) 0; |
1665 | |
1666 | return cht; |
1667 | } |
1668 | |
1669 | |
1670 | /****************************************************************************/ |
1671 | static chist_vec ppm_chashtochist |
1672 | #ifdef XW_PROTOTYPE |
1673 | ( chash_table cht, int maxcolors ) |
1674 | #else |
1675 | ( cht, maxcolors ) |
1676 | chash_table cht; |
1677 | int maxcolors; |
1678 | #endif |
1679 | { |
1680 | chist_vec chv; |
1681 | chist_list chl; |
1682 | int i, j; |
1683 | |
1684 | /* Now collate the hash table into a simple chist array. */ |
1685 | chv = (chist_vec) malloc( maxcolors * sizeof(struct chist_item) ); |
1686 | |
1687 | /* (Leave room for expansion by caller.) */ |
1688 | if (!chv) { |
1689 | fprintf (stderr, |
1690 | "ppm_chashtochist(): ran out of memory generating histogram!\r\n"); |
1691 | return chv; |
1692 | } |
1693 | |
1694 | /* Loop through the hash table. */ |
1695 | j = 0; |
1696 | for (i=0; i<HASH_SIZE; i++) |
1697 | for (chl = cht[i]; chl != (chist_list) 0; chl = chl->next) { |
1698 | /* Add the new entry. */ |
1699 | chv[j] = chl->ch; |
1700 | ++j; |
1701 | } |
1702 | |
1703 | return chv; |
1704 | } |
1705 | |
1706 | |
1707 | /****************************************************************************/ |
1708 | static void |
1709 | #ifdef XW_PROTOTYPE |
1710 | ppm_freechist( chist_vec chv ) |
1711 | #else |
1712 | ppm_freechist( chv ) |
1713 | chist_vec chv; |
1714 | #endif |
1715 | { |
1716 | free( (char*) chv ); |
1717 | } |
1718 | |
1719 | |
1720 | /****************************************************************************/ |
1721 | static void |
1722 | #ifdef XW_PROTOTYPE |
1723 | ppm_freechash( chash_table cht ) |
1724 | #else |
1725 | ppm_freechash( cht ) |
1726 | chash_table cht; |
1727 | #endif |
1728 | { |
1729 | int i; |
1730 | chist_list chl, chlnext; |
1731 | |
1732 | for (i=0; i<HASH_SIZE; i++) |
1733 | for (chl = cht[i]; chl != (chist_list) 0; chl = chlnext) { |
1734 | chlnext = chl->next; |
1735 | free( (char*) chl ); |
1736 | } |
1737 | |
1738 | free( (char*) cht ); |
1739 | } |
1740 | |
1741 | #undef PARM |
1742 | #undef CONV24_FAST |
1743 | #undef CONV24_BEST |
1744 | #undef MAXCOLORS |
1745 | #undef HASH_SIZE |