6 #include <exec/types.h>
7 #include <graphics/gels.h>
8 #include <graphics/rastport.h>
9 #include <graphics/scale.h>
10 #include <cybergraphx/cybergraphics.h>
11 #include <datatypes/datatypes.h>
12 #include <datatypes/pictureclass.h>
13 #include <datatypes/iconobject.h>
14 #include <libraries/mcpgfx.h>
15 #include <scalos/scalosgfx.h>
19 #include <proto/dos.h>
20 #include <proto/exec.h>
21 #include <proto/graphics.h>
22 #include <proto/utility.h>
23 #include <proto/cybergraphics.h>
24 #include <proto/datatypes.h>
25 #include <proto/intuition.h>
26 #include <proto/scalos.h>
28 #include <clib/alib_protos.h>
38 #include "scalosgfx.h"
40 //-----------------------------------------------------------------------
42 #define HASH_SIZE 20023
44 #define MAXCOLORS 32767
46 /* #define LARGE_NORM */
49 /* #define REP_CENTER_BOX */
50 /* #define REP_AVERAGE_COLORS */
51 #define REP_AVERAGE_PIXELS
53 //-----------------------------------------------------------------------
55 #define PPM_MAXMAXVAL PGM_MAXMAXVAL
57 #define PPM_GETR(p) ((p).Red)
58 #define PPM_GETG(p) ((p).Green)
59 #define PPM_GETB(p) ((p).Blue)
61 /************* added definitions *****************/
63 #define PPM_EQUAL(p,q) ( (p).Red == (q).Red \
64 && (p).Green == (q).Green \
65 && (p).Blue == (q).Blue )
67 /* Color scaling macro -- to make writing ppmtowhatever easier. */
69 #define PPM_DEPTH(newp, p, oldmaxval, newmaxval) \
70 (newp).Red = ( (int) (p).Red * (newmaxval) + (oldmaxval) / 2 ) / (oldmaxval); \
71 (newp).Green = ( (int) (p).Green * (newmaxval) + (oldmaxval) / 2 ) / (oldmaxval); \
72 (newp).Blue = ( (int) (p).Blue * (newmaxval) + (oldmaxval) / 2 ) / (oldmaxval)
75 /* Luminance macro. */
77 #define PPM_LUMIN(p) ( 0.299 * (p).Red + 0.587 * (p).Green + 0.114 * (p).Blue )
86 /* Color histogram stuff. */
94 struct colorhist_list_item
96 struct colorhist_item ch
;
97 struct colorhist_list_item
* next
;
107 //-----------------------------------------------------------------------
109 static struct colorhist_item
*mediancut( struct colorhist_item
* chv
, int colors
,
110 int sum
, ULONG maxval
, int newcolors
, struct ScalosGfxBase
*ScalosGfxBase
);
111 static int redcompare(const void *c1
, const void *c2
);
112 static int greencompare(const void *c1
, const void *c2
);
113 static int bluecompare(const void *c1
, const void *c2
);
114 static int sumcompare(const void *c1
, const void *c2
);
115 static long ppm_hashpixel(const struct ARGB
*p
);
116 static struct colorhist_item
* ppm_computecolorhist(const struct ARGBHeader
*argbh
,
117 int maxcolors
, int *colorsP
, struct ScalosGfxBase
*ScalosGfxBase
);
118 static struct colorhist_list_item
**ppm_computecolorhash(const struct ARGBHeader
*argbh
,
119 int maxcolors
, int *colorsP
, struct ScalosGfxBase
*ScalosGfxBase
);
120 static struct colorhist_list_item
** ppm_alloccolorhash(struct ScalosGfxBase
*ScalosGfxBase
);
121 static int ppm_addtocolorhash(struct colorhist_list_item
**cht
, const struct ARGB
*colorP
, int value
, struct ScalosGfxBase
*ScalosGfxBase
);
122 static struct colorhist_item
* ppm_colorhashtocolorhist(struct colorhist_list_item
** cht
, int maxcolors
, struct ScalosGfxBase
*ScalosGfxBase
);
123 static int ppm_lookupcolor(struct colorhist_list_item
** cht
, const struct ARGB
*colorP
);
124 static void ppm_freecolorhist(struct colorhist_item
* chv
, struct ScalosGfxBase
*ScalosGfxBase
);
125 static void ppm_freecolorhash(struct colorhist_list_item
** cht
, struct ScalosGfxBase
*ScalosGfxBase
);
126 static void *pm_allocrow(int cols
, int size
, struct ScalosGfxBase
*ScalosGfxBase
);
127 static void pm_freerow(void *itrow
, struct ScalosGfxBase
*ScalosGfxBase
);
129 //-----------------------------------------------------------------------
132 ** Copyright (C) 1989, 1991 by Jef Poskanzer.
134 ** Permission to use, copy, modify, and distribute this software and its
135 ** documentation for any purpose and without fee is hereby granted, provided
136 ** that the above copyright notice appear in all copies and that both that
137 ** copyright notice and this permission notice appear in supporting
138 ** documentation. This software is provided "as is" without express or
143 struct ScalosBitMapAndColor
*MedianCut(struct ARGBHeader
*argbh
,
144 ULONG Depth
, ULONG newcolors
,
145 BOOL floyd
, struct BitMap
*FriendBM
,
146 struct ScalosGfxBase
*ScalosGfxBase
)
148 struct ScalosBitMapAndColor
*sac
= NULL
;
149 struct ARGBHeader mappixels
;
151 UBYTE
*AllocPixelLine
= NULL
;
152 struct BitMap
*TempBM
= NULL
;
155 ULONG maxval
, newmaxval
;
158 struct colorhist_item
*chv
= NULL
;
159 struct colorhist_item
*colormap
= NULL
;
160 struct colorhist_list_item
**cht
= NULL
;
162 struct longRGB
*thiserr
= NULL
;
163 struct longRGB
*nexterr
= NULL
;
164 #define FS_SCALE 1024
165 BOOL fs_direction
= FALSE
;
166 BOOL Success
= FALSE
;
168 d1(KPrintF(__FILE__
"/" "%s/%ld: START w=%ld h=%ld floyd=%ld\n", \
169 __LINE__
, argbh
->argb_Width
, argbh
->argb_Height
, floyd
));
171 d1(KPrintF(__FILE__
"/" "%s/%ld: argb_ImageData: A=%ld R=%ld G=%ld B=%ld\n", \
172 __LINE__
, argbh
->argb_ImageData
->Alpha
, \
173 argbh
->argb_ImageData
->Red
, argbh
->argb_ImageData
->Green
, \
174 argbh
->argb_ImageData
->Blue
));
179 struct RastPort TempRp
;
180 struct longRGB s
= { 0, 0, 0 };
183 InitRastPort(&TempRp
);
185 memset(&mappixels
, 0, sizeof(mappixels
));
189 d1(KPrintF(__FILE__
"/" "%s/%ld:number of colors must be > 2\n", __FUNC__
, __LINE__
));
194 d1(KPrintF(__FILE__
"/" "%s/%ld:number of colors must be < 256\n", __FUNC__
, __LINE__
));
200 sac
= AllocSAC(argbh
->argb_Width
, argbh
->argb_Height
, Depth
, FriendBM
, NULL
, ScalosGfxBase
);
204 sac
->sac_TransparentColor
= SAC_TRANSPARENT_NONE
;
206 rp
.BitMap
= sac
->sac_BitMap
;
207 d1(KPrintF(__FILE__
"/" "%s/%ld: rp.BitMap=%08lx\n", __FUNC__
, __LINE__
, rp
.BitMap
));
209 // setup temp. RastPort for use by WritePixelLine8()
211 TempRp
.BitMap
= TempBM
= AllocBitMap(TEMPRP_WIDTH(argbh
->argb_Width
), 1, 8, 0, NULL
);
215 // allocate pens for 1 line (1 byte per pixel)
216 AllocPixelLine
= ScalosGfxAllocVecPooled(ScalosGfxBase
, ((argbh
->argb_Width
+ 15) >> 4) << 4);
217 if (NULL
== AllocPixelLine
)
221 ** Step 2: attempt to make a histogram of the colors, unclustered.
222 ** If at first we don't succeed, lower maxval to increase color
223 ** coherence and try again. This will eventually terminate, with
224 ** maxval at worst 15, since 32^3 is approximately MAXCOLORS.
228 d1(KPrintF(__FILE__
"/" "%s/%ld: making histogram...\n", __FUNC__
, __LINE__
));
229 chv
= ppm_computecolorhist(argbh
, MAXCOLORS
, &colors
, ScalosGfxBase
);
230 if ( chv
!= (struct colorhist_item
*) NULL
)
233 d1(KPrintF(__FILE__
"/" "%s/%ld: too many colors!\n", __FUNC__
, __LINE__
));
235 newmaxval
= maxval
/ 2;
236 d1(KPrintF(__FILE__
"/" "%s/%ld: scaling colors from maxval=%d to maxval=%d to improve clustering...\n",
237 __LINE__
, maxval
, newmaxval
));
238 for ( row
= 0, pixels
= argbh
->argb_ImageData
;
239 row
< argbh
->argb_Height
;
240 ++row
, pixels
+= argbh
->argb_Width
)
242 struct ARGB
*pP
= pixels
;
243 for ( col
= 0; col
< argbh
->argb_Width
; ++col
, ++pP
)
245 PPM_DEPTH(*pP
, *pP
, maxval
, newmaxval
);
250 d1(KPrintF(__FILE__
"/" "%s/%ld: %d colors found\n", __FUNC__
, __LINE__
, colors
));
253 ** Step 3: apply median-cut to histogram, making the new colormap.
255 d1(KPrintF(__FILE__
"/" "%s/%ld: choosing %d colors...\n", __FUNC__
, __LINE__
, newcolors
));
256 colormap
= mediancut( chv
, colors
, argbh
->argb_Height
* argbh
->argb_Width
, maxval
, newcolors
, ScalosGfxBase
);
257 if (NULL
== colormap
)
260 d1(KPrintF(__FILE__
"/" "%s/%ld: colormap: %08lx %08lx %08lx %08lx\n", \
261 __LINE__
, *((ULONG
*) &colormap
[0].color
), \
262 *((ULONG
*) &colormap
[1].color
), \
263 *((ULONG
*) &colormap
[2].color
), \
264 *((ULONG
*) &colormap
[3].color
)));
267 ** Step 4: map the colors in the image to their closest match in the
268 ** new colormap, and write 'em out.
270 d1(KPrintF(__FILE__
"/" "%s/%ld: mapping image to new colors...\n", __FUNC__
, __LINE__
));
271 cht
= ppm_alloccolorhash(ScalosGfxBase
);
272 d1(KPrintF(__FILE__
"/" "%s/%ld: cht=%08lx\n", __FUNC__
, __LINE__
, cht
));
277 //+++ ppm_writeppminit( stdout, argbh->argb_Width, argbh->argb_Height, maxval, 0 );
282 /* Initialize Floyd-Steinberg error vectors. */
283 thiserr
= (struct longRGB
*) pm_allocrow( argbh
->argb_Width
+ 2, sizeof(struct longRGB
), ScalosGfxBase
);
286 nexterr
= (struct longRGB
*) pm_allocrow( argbh
->argb_Width
+ 2, sizeof(struct longRGB
), ScalosGfxBase
);
293 d1(KPrintF(__FILE__
"/" "%s/%ld: thiserr=%08lx nexterr=%08lx\n", \
294 __LINE__
, thiserr
, nexterr
));
296 for ( col
= 0; col
< argbh
->argb_Width
+ 2; ++col
)
298 thiserr
[col
].Red
= rand() % ( FS_SCALE
* 2 ) - FS_SCALE
;
299 thiserr
[col
].Green
= rand() % ( FS_SCALE
* 2 ) - FS_SCALE
;
300 thiserr
[col
].Blue
= rand() % ( FS_SCALE
* 2 ) - FS_SCALE
;
301 /* (random errors in [-1 .. 1]) */
302 d1(KPrintF(__FILE__
"/" "%s/%ld: F/S error[%ld]: R=%08lx G=%08lx B=%08lx\n", \
303 __LINE__
, col
, thiserr
[col
].Red
, thiserr
[col
].Green
, thiserr
[col
].Blue
));
309 d1(KPrintF(__FILE__
"/" "%s/%ld: \n", __FUNC__
, __LINE__
));
311 for ( row
= 0, pixels
= argbh
->argb_ImageData
;
312 row
< argbh
->argb_Height
;
313 ++row
, pixels
+= argbh
->argb_Width
)
318 d1(KPrintF(__FILE__
"/" "%s/%ld: row=%ld pixels=%08lx\n", __FUNC__
, __LINE__
, row
, pixels
));
322 for ( col
= 0; col
< argbh
->argb_Width
+ 2; ++col
)
323 nexterr
[col
].Red
= nexterr
[col
].Green
= nexterr
[col
].Blue
= 0;
325 if ( ( ! floyd
) || fs_direction
)
328 limitcol
= argbh
->argb_Width
;
330 pPixelLine
= AllocPixelLine
;
334 col
= argbh
->argb_Width
- 1;
337 pPixelLine
= AllocPixelLine
+ col
;
340 d1(KPrintF(__FILE__
"/" "%s/%ld: pP=%08lx\n", __FUNC__
, __LINE__
, pP
));
345 /* Use Floyd-Steinberg errors to adjust actual color. */
346 d1(KPrintF(__FILE__
"/" "%s/%ld: thiserr[%ld]: R=%4ld G=%4ld B=%4ld\n", \
347 __LINE__
, col
+ 1, thiserr
[col
+ 1].Red
, thiserr
[col
+ 1].Green
, thiserr
[col
+ 1].Blue
));
349 s
.Red
= pP
->Red
+ thiserr
[col
+ 1].Red
/ FS_SCALE
;
350 s
.Green
= pP
->Green
+ thiserr
[col
+ 1].Green
/ FS_SCALE
;
351 s
.Blue
= pP
->Blue
+ thiserr
[col
+ 1].Blue
/ FS_SCALE
;
355 else if ( s
.Red
> maxval
)
359 else if ( s
.Green
> maxval
)
363 else if ( s
.Blue
> maxval
)
366 d1(KPrintF(__FILE__
"/" "%s/%ld: Red=%3d s.Red=%ld\n", __FUNC__
, __LINE__
, pP
->Red
, s
.Red
));
367 d1(KPrintF(__FILE__
"/" "%s/%ld: Green=%3d s.Green=%ld\n", __FUNC__
, __LINE__
, pP
->Green
, s
.Green
));
368 d1(KPrintF(__FILE__
"/" "%s/%ld: Blue=%3d s.Blue=%ld\n", __FUNC__
, __LINE__
, pP
->Blue
, s
.Blue
));
375 /* Check hash table to see if we have already matched this color. */
376 ind
= ppm_lookupcolor( cht
, pP
);
377 d1(KPrintF(__FILE__
"/" "%s/%ld: ind=%ld\n", __FUNC__
, __LINE__
, ind
));
380 /* No; search colormap for closest match. */
388 for ( i
= 0; i
< newcolors
; ++i
)
393 r2
= colormap
[i
].color
.Red
;
394 g2
= colormap
[i
].color
.Green
;
395 b2
= colormap
[i
].color
.Blue
;
397 newdist
= ( r1
- r2
) * ( r1
- r2
) +
398 ( g1
- g2
) * ( g1
- g2
) +
399 ( b1
- b2
) * ( b1
- b2
);
400 if ( newdist
< dist
)
408 if ( ppm_addtocolorhash( cht
, pP
, ind
, ScalosGfxBase
) < 0 )
410 d1(KPrintF(__FILE__
"/" "%s/%ld: out of memory adding to hash table, proceeding without it\n", __FUNC__
, __LINE__
));
418 /* Propagate Floyd-Steinberg error terms. */
423 err
= (s
.Red
- (long) colormap
[ind
].color
.Red
) * FS_SCALE
;
424 d1(KPrintF(__FILE__
"/" "%s/%ld: Red err=%ld\n", __FUNC__
, __LINE__
, err
));
425 thiserr
[col
+ 2].Red
+= ( err
* 7 ) / 16;
426 nexterr
[col
].Red
+= ( err
* 3 ) / 16;
427 nexterr
[col
+ 1].Red
+= ( err
* 5 ) / 16;
428 nexterr
[col
+ 2].Red
+= ( err
) / 16;
429 err
= (s
.Green
- (long) colormap
[ind
].color
.Green
) * FS_SCALE
;
430 d1(KPrintF(__FILE__
"/" "%s/%ld: Green err=%ld\n", __FUNC__
, __LINE__
, err
));
431 thiserr
[col
+ 2].Green
+= ( err
* 7 ) / 16;
432 nexterr
[col
].Green
+= ( err
* 3 ) / 16;
433 nexterr
[col
+ 1].Green
+= ( err
* 5 ) / 16;
434 nexterr
[col
+ 2].Green
+= ( err
) / 16;
435 err
= (s
.Blue
- (long) colormap
[ind
].color
.Blue
) * FS_SCALE
;
436 d1(KPrintF(__FILE__
"/" "%s/%ld: Blue err=%ld\n", __FUNC__
, __LINE__
, err
));
437 thiserr
[col
+ 2].Blue
+= ( err
* 7 ) / 16;
438 nexterr
[col
].Blue
+= ( err
* 3 ) / 16;
439 nexterr
[col
+ 1].Blue
+= ( err
* 5 ) / 16;
440 nexterr
[col
+ 2].Blue
+= ( err
) / 16;
444 err
= (s
.Red
- (long) colormap
[ind
].color
.Red
) * FS_SCALE
;
445 d1(KPrintF(__FILE__
"/" "%s/%ld: Red err=%ld\n", __FUNC__
, __LINE__
, err
));
446 thiserr
[col
].Red
+= ( err
* 7 ) / 16;
447 nexterr
[col
+ 2].Red
+= ( err
* 3 ) / 16;
448 nexterr
[col
+ 1].Red
+= ( err
* 5 ) / 16;
449 nexterr
[col
].Red
+= ( err
) / 16;
450 err
= (s
.Green
- (long) colormap
[ind
].color
.Green
) * FS_SCALE
;
451 d1(KPrintF(__FILE__
"/" "%s/%ld: Green err=%ld\n", __FUNC__
, __LINE__
, err
));
452 thiserr
[col
].Green
+= ( err
* 7 ) / 16;
453 nexterr
[col
+ 2].Green
+= ( err
* 3 ) / 16;
454 nexterr
[col
+ 1].Green
+= ( err
* 5 ) / 16;
455 nexterr
[col
].Green
+= ( err
) / 16;
456 err
= (s
.Blue
- (long) colormap
[ind
].color
.Blue
) * FS_SCALE
;
457 d1(KPrintF(__FILE__
"/" "%s/%ld: Blue err=%ld\n", __FUNC__
, __LINE__
, err
));
458 thiserr
[col
].Blue
+= ( err
* 7 ) / 16;
459 nexterr
[col
+ 2].Blue
+= ( err
* 3 ) / 16;
460 nexterr
[col
+ 1].Blue
+= ( err
* 5 ) / 16;
461 nexterr
[col
].Blue
+= ( err
) / 16;
465 pP
->Red
= colormap
[ind
].color
.Red
;
466 pP
->Green
= colormap
[ind
].color
.Green
;
467 pP
->Blue
= colormap
[ind
].color
.Blue
;
469 // Check transparency, and set color index to 0 for transparent pixels
470 if (pP
->Alpha
>= 128)
471 *pPixelLine
= 1 + ind
;
474 // mark this pixel as transparent
475 *pPixelLine
= sac
->sac_TransparentColor
= 0;
478 d1(KPrintF(__FILE__
"/" "%s/%ld: row=%ld col=%ld ind=%ld color=%08lx\n", \
479 __LINE__
, row
, col
, ind
, *((ULONG
*) pP
)));
481 if ( ( ! floyd
) || fs_direction
)
493 } while ( col
!= limitcol
);
497 struct longRGB
*temperr
;
502 fs_direction
= !fs_direction
;
505 d1(KPrintF(__FILE__
"/" "%s/%ld: row=%ld\n", __FUNC__
, __LINE__
, row
));
507 WritePixelLine8(&rp
, 0, row
,
508 argbh
->argb_Width
, AllocPixelLine
, &TempRp
);
511 d1(KPrintF(__FILE__
"/" "%s/%ld: \n", __FUNC__
, __LINE__
));
513 // fill sac_ColorTable
514 // leave first entry in sac_ColorTable free since
515 // it is used as transparency indicator
516 for (ind
= 0, pColorTable
= sac
->sac_ColorTable
+ 3;
517 ind
< sac
->sac_NumColors
- 1; ind
++)
519 *pColorTable
++ = colormap
[ind
].color
.Red
<< 24;
520 *pColorTable
++ = colormap
[ind
].color
.Green
<< 24;
521 *pColorTable
++ = colormap
[ind
].color
.Blue
<< 24;
526 d1(KPrintF(__FILE__
"/" "%s/%ld: \n", __FUNC__
, __LINE__
));
529 FreeSAC(sac
, ScalosGfxBase
);
531 ScalosGfxFreeVecPooled(ScalosGfxBase
, colormap
);
533 ppm_freecolorhash(cht
, ScalosGfxBase
);
535 ppm_freecolorhist( chv
, ScalosGfxBase
);
537 pm_freerow(thiserr
, ScalosGfxBase
);
539 pm_freerow(nexterr
, ScalosGfxBase
);
541 ScalosGfxFreeVecPooled(ScalosGfxBase
, AllocPixelLine
);
545 d1(KPrintF(__FILE__
"/" "%s/%ld: END Success=%ld\n", __FUNC__
, __LINE__
, Success
));
552 ** Here is the fun part, the median-cut colormap generator. This is based
553 ** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
554 ** Display", SIGGRAPH '82 Proceedings, page 297.
556 static struct colorhist_item
*mediancut( struct colorhist_item
* chv
,
557 int colors
, int sum
, ULONG maxval
, int newcolors
,
558 struct ScalosGfxBase
*ScalosGfxBase
)
560 struct colorhist_item
* colormap
;
565 bv
= (struct box
*) ScalosGfxAllocVecPooled(ScalosGfxBase
, sizeof(struct box
) * newcolors
);
566 colormap
= (struct colorhist_item
*) ScalosGfxAllocVecPooled(ScalosGfxBase
, sizeof(struct colorhist_item
) * newcolors
);
568 if (bv
== NULL
|| colormap
== NULL
)
570 d1(KPrintF(__FILE__
"/" "%s/%ld: out of memory\n", __FUNC__
, __LINE__
));
574 for ( i
= 0; i
< newcolors
; ++i
)
576 colormap
[i
].color
.Alpha
= 0xff;
577 colormap
[i
].color
.Red
= 0;
578 colormap
[i
].color
.Green
= 0;
579 colormap
[i
].color
.Blue
= 0;
583 ** Set up the initial box.
586 bv
[0].colors
= colors
;
591 ** Main loop: split boxes until we have enough.
593 while ( boxes
< newcolors
)
597 int minr
, maxr
, ming
, maxg
, minb
, maxb
, v
;
598 int halfsum
, lowersum
;
601 ** Find the first splittable box.
603 for ( bi
= 0; bi
< boxes
; ++bi
)
605 if ( bv
[bi
].colors
>= 2 )
609 break; /* ran out of colors! */
612 clrs
= bv
[bi
].colors
;
616 ** Go through the box finding the minimum and maximum of each
617 ** component - the boundaries of the box.
619 minr
= maxr
= chv
[indx
].color
.Red
;
620 ming
= maxg
= chv
[indx
].color
.Green
;
621 minb
= maxb
= chv
[indx
].color
.Blue
;
623 for ( i
= 1; i
< clrs
; ++i
)
625 v
= chv
[indx
+ i
].color
.Red
;
630 v
= chv
[indx
+ i
].color
.Green
;
635 v
= chv
[indx
+ i
].color
.Blue
;
643 ** Find the largest dimension, and sort by that component. I have
644 ** included two methods for determining the "largest" dimension;
645 ** first by simply comparing the range in RGB space, and second
646 ** by transforming into luminosities before the comparison. You
647 ** can switch which method is used by switching the commenting on
648 ** the LARGE_ defines at the beginning of this source file.
651 if ( maxr
- minr
>= maxg
- ming
&& maxr
- minr
>= maxb
- minb
)
652 qsort((char*) &(chv
[indx
]), clrs
, sizeof(struct colorhist_item
), redcompare
);
653 else if ( maxg
- ming
>= maxb
- minb
)
654 qsort((char*) &(chv
[indx
]), clrs
, sizeof(struct colorhist_item
), greencompare
);
656 qsort((char*) &(chv
[indx
]), clrs
, sizeof(struct colorhist_item
), bluecompare
);
657 #endif /*LARGE_NORM*/
671 p
.Green
= maxg
- ming
;
677 p
.Blue
= maxb
- minb
;
680 if ( rl
>= gl
&& rl
>= bl
)
681 qsort((char*) &(chv
[indx
]), clrs
, sizeof(struct colorhist_item
), redcompare
);
683 qsort((char*) &(chv
[indx
]), clrs
, sizeof(struct colorhist_item
), greencompare
);
685 qsort((char*) &(chv
[indx
]), clrs
, sizeof(struct colorhist_item
), bluecompare
);
690 ** Now find the median based on the counts, so that about half the
691 ** pixels (not colors, pixels) are in each subdivision.
693 lowersum
= chv
[indx
].value
;
695 for ( i
= 1; i
< clrs
- 1; ++i
)
697 if ( lowersum
>= halfsum
)
699 lowersum
+= chv
[indx
+ i
].value
;
703 ** Split the box, and sort to bring the biggest boxes to the top.
706 bv
[bi
].sum
= lowersum
;
707 bv
[boxes
].ind
= indx
+ i
;
708 bv
[boxes
].colors
= clrs
- i
;
709 bv
[boxes
].sum
= sm
- lowersum
;
712 qsort( (char*) bv
, boxes
, sizeof(struct box
), sumcompare
);
716 ** Ok, we've got enough boxes. Now choose a representative color for
717 ** each box. There are a number of possible ways to make this choice.
718 ** One would be to choose the center of the box; this ignores any structure
719 ** within the boxes. Another method would be to average all the colors in
720 ** the box - this is the method specified in Heckbert's paper. A third
721 ** method is to average all the pixels in the box. You can switch which
722 ** method is used by switching the commenting on the REP_ defines at
723 ** the beginning of this source file.
725 for ( bi
= 0; bi
< boxes
; ++bi
)
727 #ifdef REP_CENTER_BOX
728 int indx
= bv
[bi
].ind
;
729 int clrs
= bv
[bi
].colors
;
730 int minr
, maxr
, ming
, maxg
, minb
, maxb
, v
;
732 minr
= maxr
= chv
[indx
].color
.Red
;
733 ming
= maxg
= chv
[indx
].color
.Green
;
734 minb
= maxb
= chv
[indx
].color
.Blue
;
735 for ( i
= 1; i
< clrs
; ++i
)
737 v
= chv
[indx
+ i
].color
.Red
;
738 minr
= min( minr
, v
);
739 maxr
= max( maxr
, v
);
740 v
= chv
[indx
+ i
].color
.Green
;
741 ming
= min( ming
, v
);
742 maxg
= max( maxg
, v
);
743 v
= chv
[indx
+ i
].color
.Blue
;
744 minb
= min( minb
, v
);
745 maxb
= max( maxb
, v
);
748 colormap
[bi
].color
.Red
= (minr
+ maxr
) / 2;
749 colormap
[bi
].color
.Green
= (ming
+ maxg
) / 2;
750 colormap
[bi
].color
.Blue
= (minb
+ maxb
) / 2;
752 #endif /*REP_CENTER_BOX*/
753 #ifdef REP_AVERAGE_COLORS
754 int indx
= bv
[bi
].ind
;
755 int clrs
= bv
[bi
].colors
;
756 long r
= 0, g
= 0, b
= 0;
758 for ( i
= 0; i
< clrs
; ++i
)
760 r
+= chv
[indx
+ i
].color
.Red
;
761 g
+= chv
[indx
+ i
].color
.Green
;
762 b
+= chv
[indx
+ i
].color
.Blue
;
764 colormap
[bi
].color
.Red
= r
/ clrs
;
765 colormap
[bi
].color
.Green
= g
/ clrs
;
766 colormap
[bi
].color
.Blue
= b
/ clrs
;
767 #endif /*REP_AVERAGE_COLORS*/
769 #ifdef REP_AVERAGE_PIXELS
770 int indx
= bv
[bi
].ind
;
771 int clrs
= bv
[bi
].colors
;
772 long r
= 0, g
= 0, b
= 0, sum
= 0;
774 for ( i
= 0; i
< clrs
; ++i
)
776 r
+= chv
[indx
+ i
].color
.Red
* chv
[indx
+ i
].value
;
777 g
+= chv
[indx
+ i
].color
.Green
* chv
[indx
+ i
].value
;
778 b
+= chv
[indx
+ i
].color
.Blue
* chv
[indx
+ i
].value
;
779 sum
+= chv
[indx
+ i
].value
;
783 r
= maxval
; /* avoid math errors */
791 colormap
[bi
].color
.Red
= r
;
792 colormap
[bi
].color
.Green
= g
;
793 colormap
[bi
].color
.Blue
= b
;
794 #endif /*REP_AVERAGE_PIXELS*/
801 ScalosGfxFreeVecPooled(ScalosGfxBase
, (char*) bv
); //+++
806 static int redcompare(const void *c1
, const void *c2
)
808 const struct colorhist_item
* ch1
= c1
;
809 const struct colorhist_item
* ch2
= c2
;
811 return (int) (ch1
->color
.Red
- ch2
->color
.Red
);
814 static int greencompare(const void *c1
, const void *c2
)
816 const struct colorhist_item
* ch1
= c1
;
817 const struct colorhist_item
* ch2
= c2
;
819 return (int) (ch1
->color
.Green
- ch2
->color
.Green
);
822 static int bluecompare(const void *c1
, const void *c2
)
824 const struct colorhist_item
* ch1
= c1
;
825 const struct colorhist_item
* ch2
= c2
;
827 return (int) (ch1
->color
.Blue
- ch2
->color
.Blue
);
830 static int sumcompare(const void *c1
, const void *c2
)
832 const struct box
*b1
= c1
;
833 const struct box
*b2
= c2
;
835 return b2
->sum
- b1
->sum
;
839 static long ppm_hashpixel(const struct ARGB
*p
)
841 return (long) ((p
->Red
* 33023 + p
->Green
* 30013 + p
->Blue
* 27011) & 0x7fffffff ) % HASH_SIZE
;
845 static struct colorhist_item
* ppm_computecolorhist(const struct ARGBHeader
*argbh
,
846 int maxcolors
, int *colorsP
, struct ScalosGfxBase
*ScalosGfxBase
)
848 struct colorhist_list_item
** cht
;
849 struct colorhist_item
* chv
;
851 cht
= ppm_computecolorhash(argbh
, maxcolors
, colorsP
, ScalosGfxBase
);
852 if ( cht
== (struct colorhist_list_item
**) 0 )
853 return (struct colorhist_item
*) 0;
855 chv
= ppm_colorhashtocolorhist( cht
, maxcolors
, ScalosGfxBase
);
856 ppm_freecolorhash( cht
, ScalosGfxBase
);
862 static struct colorhist_list_item
** ppm_computecolorhash(const struct ARGBHeader
*argbh
,
863 int maxcolors
, int *colorsP
, struct ScalosGfxBase
*ScalosGfxBase
)
865 struct colorhist_list_item
** cht
;
866 struct colorhist_list_item
* chl
;
868 const struct ARGB
*argb
= argbh
->argb_ImageData
;
870 cht
= ppm_alloccolorhash(ScalosGfxBase
);
873 /* Go through the entire image, building a hash table of colors. */
874 for (row
= 0; row
< argbh
->argb_Height
; ++row
)
877 const struct ARGB
*pP
= argb
;
879 argb
+= argbh
->argb_Width
;
881 for (col
= 0; col
< argbh
->argb_Width
; ++col
, ++pP
)
885 hash
= ppm_hashpixel(pP
);
887 for ( chl
= cht
[hash
]; chl
!= NULL
; chl
= chl
->next
)
889 if ( PPM_EQUAL( chl
->ch
.color
, *pP
) )
897 if ( ++(*colorsP
) > maxcolors
)
899 ppm_freecolorhash( cht
, ScalosGfxBase
);
902 chl
= (struct colorhist_list_item
*) ScalosGfxAllocVecPooled(ScalosGfxBase
, sizeof(struct colorhist_list_item
) );
905 d1(KPrintF(__FILE__
"/" "%s/%ld: out of memory computing hash table\n", __FUNC__
, __LINE__
));
910 chl
->next
= cht
[hash
];
920 static struct colorhist_list_item
** ppm_alloccolorhash(struct ScalosGfxBase
*ScalosGfxBase
)
922 struct colorhist_list_item
** cht
;
925 cht
= (struct colorhist_list_item
**) ScalosGfxAllocVecPooled(ScalosGfxBase
, HASH_SIZE
* sizeof(struct colorhist_list_item
*) );
928 d1(KPrintF(__FILE__
"/" "%s/%ld: out of memory allocating hash table\n", __FUNC__
, __LINE__
));
932 for ( i
= 0; i
< HASH_SIZE
; ++i
)
939 static int ppm_addtocolorhash(struct colorhist_list_item
**cht
, const struct ARGB
*colorP
, int value
, struct ScalosGfxBase
*ScalosGfxBase
)
942 struct colorhist_list_item
* chl
;
944 chl
= (struct colorhist_list_item
*) ScalosGfxAllocVecPooled(ScalosGfxBase
, sizeof(struct colorhist_list_item
) );
947 hash
= ppm_hashpixel(colorP
);
948 chl
->ch
.color
= *colorP
;
949 chl
->ch
.value
= value
;
950 chl
->next
= cht
[hash
];
957 static struct colorhist_item
* ppm_colorhashtocolorhist(struct colorhist_list_item
** cht
, int maxcolors
, struct ScalosGfxBase
*ScalosGfxBase
)
959 struct colorhist_item
* chv
;
960 struct colorhist_list_item
* chl
;
963 /* Now collate the hash table into a simple colorhist array. */
964 chv
= (struct colorhist_item
*) ScalosGfxAllocVecPooled(ScalosGfxBase
, maxcolors
* sizeof(struct colorhist_item
) );
965 /* (Leave room for expansion by caller.) */
968 d1(KPrintF(__FILE__
"/" "%s/%ld: out of memory generating histogram\n", __FUNC__
, __LINE__
));
972 /* Loop through the hash table. */
974 for ( i
= 0; i
< HASH_SIZE
; ++i
)
976 for ( chl
= cht
[i
]; chl
!= (struct colorhist_list_item
*) 0; chl
= chl
->next
)
978 /* Add the new entry. */
989 static int ppm_lookupcolor(struct colorhist_list_item
** cht
, const struct ARGB
*colorP
)
992 struct colorhist_list_item
* chl
;
994 hash
= ppm_hashpixel(colorP
);
996 for ( chl
= cht
[hash
]; chl
!= (struct colorhist_list_item
*) 0; chl
= chl
->next
)
998 if ( PPM_EQUAL( chl
->ch
.color
, *colorP
) )
999 return chl
->ch
.value
;
1006 static void ppm_freecolorhist(struct colorhist_item
*chv
, struct ScalosGfxBase
*ScalosGfxBase
)
1008 ScalosGfxFreeVecPooled(ScalosGfxBase
, (char*) chv
);
1012 static void ppm_freecolorhash(struct colorhist_list_item
**cht
, struct ScalosGfxBase
*ScalosGfxBase
)
1015 struct colorhist_list_item
*chl
, *chlnext
;
1017 for ( i
= 0; i
< HASH_SIZE
; ++i
)
1019 for ( chl
= cht
[i
]; chl
!= NULL
; chl
= chlnext
)
1021 chlnext
= chl
->next
;
1022 ScalosGfxFreeVecPooled(ScalosGfxBase
, (char*) chl
);
1025 ScalosGfxFreeVecPooled(ScalosGfxBase
, (char*) cht
);
1029 static void *pm_allocrow(int cols
, int size
, struct ScalosGfxBase
*ScalosGfxBase
)
1033 itrow
= ScalosGfxAllocVecPooled(ScalosGfxBase
, cols
* size
);
1035 d1(KPrintF(__FILE__
"/" "%s/%ld: out of memory allocating a row\n", __FUNC__
, __LINE__
));
1041 static void pm_freerow(void *itrow
, struct ScalosGfxBase
*ScalosGfxBase
)
1043 ScalosGfxFreeVecPooled(ScalosGfxBase
, itrow
);