зеркало из https://github.com/mozilla/gecko-dev.git
598 строки
20 KiB
C
598 строки
20 KiB
C
|
#include "xp_core.h" //used to make library compile faster on win32 do not ifdef this or it wont work
|
||
|
|
||
|
/* taken from libppm3.c - ppm utility library part 3
|
||
|
**
|
||
|
** Colormap routines.
|
||
|
**
|
||
|
** Copyright (C) 1989, 1991 by Jef Poskanzer.
|
||
|
**
|
||
|
** Permission to use, copy, modify, and distribute this software and its
|
||
|
** documentation for any purpose and without fee is hereby granted, provided
|
||
|
** that the above copyright notice appear in all copies and that both that
|
||
|
** copyright notice and this permission notice appear in supporting
|
||
|
** documentation. This software is provided "as is" without express or
|
||
|
** implied warranty.
|
||
|
*/
|
||
|
#include <stdio.h>
|
||
|
#include <memory.h>
|
||
|
#include <malloc.h>
|
||
|
#include <process.h>
|
||
|
#include <search.h>
|
||
|
#include <stdlib.h>
|
||
|
#include "xp_core.h"/*defines of int32, etc.*/
|
||
|
#include "xp_mem.h"/*defines of XP_ALLOC, etc.*/
|
||
|
#include "xp_str.h"/*defines of XP_MEMCPY, etc.*/
|
||
|
#include "xpassert.h"
|
||
|
#include "xp_qsort.h"
|
||
|
#include "ntypes.h" /* for MWContext to include libcnv.h*/
|
||
|
|
||
|
#include "libcnv.h"
|
||
|
#include "pbmplus.h"
|
||
|
#include "ppm.h"
|
||
|
#include "ppmcmap.h"
|
||
|
#include "colorqnt.h"
|
||
|
|
||
|
#define LARGE_NORM 1
|
||
|
#define REP_AVERAGE_COLORS 1
|
||
|
|
||
|
#define MAXCOLORS 256
|
||
|
|
||
|
#define FLOYD_DEFAULT 1/*use floyd algorithm*/
|
||
|
#define FS_SCALE 1024
|
||
|
|
||
|
typedef struct box* box_vector;
|
||
|
struct box
|
||
|
{
|
||
|
int16 ind;
|
||
|
int16 colors;
|
||
|
int32 sum;
|
||
|
};
|
||
|
|
||
|
static colorhist_vector mediancut ARGS(( colorhist_vector chv, int16 colors, int32 sum, pixval maxval, int16 newcolors ));
|
||
|
static int redcompare ARGS(( const colorhist_vector ch1, const colorhist_vector ch2 ));
|
||
|
static int greencompare ARGS(( const colorhist_vector ch1, const colorhist_vector ch2 ));
|
||
|
static int bluecompare ARGS(( const colorhist_vector ch1, const colorhist_vector ch2 ));
|
||
|
static int sumcompare ARGS(( const box_vector b1, const box_vector b2 ));
|
||
|
|
||
|
/*used for quick sort of color table*/
|
||
|
typedef int (* qsortprototype)(const void *, const void *);
|
||
|
|
||
|
/*EFFECTS:modifies imagearray to reflect colors in colorhistogram
|
||
|
MODIFIES:imagearray, pchv,maxcolorvalue,colors
|
||
|
RETURNS:CONV_OK if success
|
||
|
INPUT:
|
||
|
imagearray= RGB tripples
|
||
|
imagewidth= width of image
|
||
|
imageheight=height of image
|
||
|
maxcolorvalue=passed in as maximum value for each R,G or B
|
||
|
colors= valid pointer to int16
|
||
|
maxcolorvalue= valid pointer to int16
|
||
|
OUTPUT:
|
||
|
maxcolorvalue=leaves with what was assigned as the maximum value
|
||
|
colors= number of colors in the mapping
|
||
|
pchv= leaves pointing to the new color table. calling function reponsible for deleting!
|
||
|
SPECIAL NOTE!
|
||
|
the returned color table is in the range of 0 to maxcolorvalue.
|
||
|
you must divide my maxcolorvalue and multiply times the input maxcolorvalue to get
|
||
|
colors in the range of 0-MAXCOLORS-1
|
||
|
*/
|
||
|
CONVERT_IMAGERESULT
|
||
|
quantize_colors(CONVERT_IMG_ARRAY imagearray,int16 imagewidth,int16 imageheight,int16 *maxcolorvalue,int16 *colors,colorhist_vector *pchv)
|
||
|
{
|
||
|
pixval newmaxval=0;
|
||
|
int16 row=0;
|
||
|
colorhist_vector chv=NULL;
|
||
|
colorhist_vector colormap=NULL;
|
||
|
pixel *pP=NULL;
|
||
|
pixval maxval=(pixval)(MAXCOLORS-1);
|
||
|
colorhash_table cht=NULL;
|
||
|
int32* thisrerr=NULL;
|
||
|
int32* nextrerr=NULL;
|
||
|
int32* thisgerr=NULL;
|
||
|
int32* nextgerr=NULL;
|
||
|
int32* thisberr=NULL;
|
||
|
int32* nextberr=NULL;
|
||
|
int32* temperr=NULL;
|
||
|
int16 usehash,floyd=FLOYD_DEFAULT;
|
||
|
int16 fs_direction;
|
||
|
register int16 col, limitcol;
|
||
|
register int32 sr, sg, sb, err;
|
||
|
register int16 ind;
|
||
|
pixel** copy = 0;
|
||
|
|
||
|
*pchv=NULL;
|
||
|
XP_ASSERT(colors);
|
||
|
XP_ASSERT(maxcolorvalue);
|
||
|
/*
|
||
|
** Step 1: attempt to make a histogram of the colors, unclustered.
|
||
|
** If at first we don't succeed, lower maxval to increase color
|
||
|
** coherence and try again. This will eventually terminate, with
|
||
|
** maxval at worst 15, since 32^3 is approximately MAXCOLORS.
|
||
|
*/
|
||
|
/* Make a copy of the image pixels */
|
||
|
copy = XP_ALLOC(sizeof(pixel*) * imageheight);
|
||
|
{
|
||
|
int i;
|
||
|
pixel* pixelRow;
|
||
|
pixel* sourceRow;
|
||
|
for(i = 0; i < imageheight; i++){
|
||
|
pixelRow = XP_ALLOC(imagewidth*sizeof(pixel));
|
||
|
copy[i] = pixelRow;
|
||
|
sourceRow = (pixel*) imagearray[i];
|
||
|
/* XP_MEMCPY(pixelRow, sourceRow, sizeof(pixel)*imagewidth); */
|
||
|
{
|
||
|
int j;
|
||
|
for(j = 0; j < imagewidth; j++){
|
||
|
pixelRow[j] = sourceRow[j];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for ( ; ; )
|
||
|
{
|
||
|
chv = ppm_computecolorhist(
|
||
|
(pixel **)copy, imagewidth, imageheight, MAXCOLORS, colors );
|
||
|
if ( chv != (colorhist_vector) 0 )
|
||
|
break;
|
||
|
newmaxval = maxval / 2;
|
||
|
for ( row = 0; row < imageheight; ++row )
|
||
|
for ( col = 0, pP = (pixel *)copy[row]; col < imagewidth; ++col, ++pP ){
|
||
|
PPM_DEPTH( *pP, *pP, maxval, newmaxval );
|
||
|
}
|
||
|
maxval=newmaxval;
|
||
|
}
|
||
|
/* Free copy of image */
|
||
|
{
|
||
|
int i;
|
||
|
for(i = 0; i < imageheight; i++){
|
||
|
XP_FREE(copy[i]);
|
||
|
}
|
||
|
XP_FREE(copy);
|
||
|
}
|
||
|
|
||
|
/* we now have the maximum # of colors*/
|
||
|
/*
|
||
|
** Step 2: apply median-cut to histogram, making the new colormap.
|
||
|
*/
|
||
|
colormap = mediancut( chv, (int16)*colors, (int32)(imageheight * imagewidth), (pixval)maxval, (int16)*maxcolorvalue );
|
||
|
free( chv );
|
||
|
{ /* Crank up colors in color map */
|
||
|
int i;
|
||
|
for(i = 0; i < *colors; i++ ) {
|
||
|
PPM_DEPTH( colormap[i].color, colormap[i].color, maxval, MAXCOLORS-1 );
|
||
|
}
|
||
|
maxval = MAXCOLORS-1;
|
||
|
}
|
||
|
/*
|
||
|
** Step 3: map the colors in the image to their closest match in the
|
||
|
** new colormap, and write 'em out.
|
||
|
*/
|
||
|
cht = ppm_alloccolorhash( );
|
||
|
usehash = 1;
|
||
|
if ( floyd )
|
||
|
{
|
||
|
/* Initialize Floyd-Steinberg error vectors. */
|
||
|
thisrerr = (int32*) malloc( (imagewidth + 2)* sizeof(int32) );
|
||
|
nextrerr = (int32*) malloc( (imagewidth + 2)* sizeof(int32) );
|
||
|
thisgerr = (int32*) malloc( (imagewidth + 2)* sizeof(int32) );
|
||
|
nextgerr = (int32*) malloc( (imagewidth + 2)* sizeof(int32) );
|
||
|
thisberr = (int32*) malloc( (imagewidth + 2)* sizeof(int32) );
|
||
|
nextberr = (int32*) malloc( (imagewidth + 2)* sizeof(int32) );
|
||
|
if (!nextberr||!thisberr||!nextgerr||!thisgerr||!nextrerr||!thisrerr)
|
||
|
return CONVERR_OUTOFMEMORY; /*memory allocation failed*/
|
||
|
srandom( (int16) ( time( 0 ) ^ getpid( ) ) );
|
||
|
for ( col = 0; col < imagewidth + 2; ++col )
|
||
|
{
|
||
|
thisrerr[col] = random( ) % ( FS_SCALE * 2 ) - FS_SCALE;
|
||
|
thisgerr[col] = random( ) % ( FS_SCALE * 2 ) - FS_SCALE;
|
||
|
thisberr[col] = random( ) % ( FS_SCALE * 2 ) - FS_SCALE;
|
||
|
/* (random errors in [-1 .. 1]) */
|
||
|
}
|
||
|
fs_direction = 1;
|
||
|
}
|
||
|
for ( row = 0; row < imageheight; ++row )
|
||
|
{
|
||
|
if ( floyd )
|
||
|
for ( col = 0; col < imagewidth + 2; ++col )
|
||
|
nextrerr[col] = nextgerr[col] = nextberr[col] = 0;
|
||
|
if ( ( ! floyd ) || fs_direction )
|
||
|
{
|
||
|
col = 0;
|
||
|
limitcol = imagewidth;
|
||
|
pP = (pixel *)imagearray[row];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
col = imagewidth - 1;
|
||
|
limitcol = -1;
|
||
|
pP = &((pixel **)imagearray)[row][col]; /*(pixel *)&(imagearray[row][col*3]); times 3 because a CONV_IMAGE_ARRAY IS A char ** not a pixel ** */
|
||
|
}
|
||
|
do
|
||
|
{
|
||
|
if ( floyd )
|
||
|
{
|
||
|
/* Use Floyd-Steinberg errors to adjust actual color. */
|
||
|
sr = PPM_GETR(*pP) + thisrerr[col + 1] / FS_SCALE;
|
||
|
sg = PPM_GETG(*pP) + thisgerr[col + 1] / FS_SCALE;
|
||
|
sb = PPM_GETB(*pP) + thisberr[col + 1] / FS_SCALE;
|
||
|
if ( sr < 0 ) sr = 0;
|
||
|
else if ( sr > maxval ) sr = maxval;
|
||
|
if ( sg < 0 ) sg = 0;
|
||
|
else if ( sg > maxval ) sg = maxval;
|
||
|
if ( sb < 0 ) sb = 0;
|
||
|
else if ( sb > maxval ) sb = maxval;
|
||
|
PPM_ASSIGN( *pP, (pixval)sr, (pixval)sg, (pixval)sb );
|
||
|
}
|
||
|
|
||
|
/* Check hash table to see if we have already matched this color. */
|
||
|
ind = ppm_lookupcolor( cht, pP );
|
||
|
if ( ind == -1 )
|
||
|
{ /* No; search colormap for closest match. */
|
||
|
register int16 i, r1, g1, b1, r2, g2, b2;
|
||
|
register int32 dist, newdist;
|
||
|
r1 = PPM_GETR( *pP );
|
||
|
g1 = PPM_GETG( *pP );
|
||
|
b1 = PPM_GETB( *pP );
|
||
|
dist = 2000000000;
|
||
|
for ( i = 0; i < *maxcolorvalue; ++i )
|
||
|
{
|
||
|
r2 = PPM_GETR( colormap[i].color );
|
||
|
g2 = PPM_GETG( colormap[i].color );
|
||
|
b2 = PPM_GETB( colormap[i].color );
|
||
|
newdist = ( r1 - r2 ) * ( r1 - r2 ) +
|
||
|
( g1 - g2 ) * ( g1 - g2 ) +
|
||
|
( b1 - b2 ) * ( b1 - b2 );
|
||
|
if ( newdist < dist )
|
||
|
{
|
||
|
ind = i;
|
||
|
dist = newdist;
|
||
|
}
|
||
|
}
|
||
|
if ( usehash )
|
||
|
{
|
||
|
if ( ppm_addtocolorhash( cht, pP, ind ) < 0 )
|
||
|
{
|
||
|
usehash = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( floyd )
|
||
|
{
|
||
|
/* Propagate Floyd-Steinberg error terms. */
|
||
|
if ( fs_direction )
|
||
|
{
|
||
|
err = ( sr - (int32) PPM_GETR( colormap[ind].color ) ) * FS_SCALE;
|
||
|
thisrerr[col + 2] += ( err * 7 ) / 16;
|
||
|
nextrerr[col ] += ( err * 3 ) / 16;
|
||
|
nextrerr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextrerr[col + 2] += ( err ) / 16;
|
||
|
err = ( sg - (int32) PPM_GETG( colormap[ind].color ) ) * FS_SCALE;
|
||
|
thisgerr[col + 2] += ( err * 7 ) / 16;
|
||
|
nextgerr[col ] += ( err * 3 ) / 16;
|
||
|
nextgerr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextgerr[col + 2] += ( err ) / 16;
|
||
|
err = ( sb - (int32) PPM_GETB( colormap[ind].color ) ) * FS_SCALE;
|
||
|
thisberr[col + 2] += ( err * 7 ) / 16;
|
||
|
nextberr[col ] += ( err * 3 ) / 16;
|
||
|
nextberr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextberr[col + 2] += ( err ) / 16;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
err = ( sr - (int32) PPM_GETR( colormap[ind].color ) ) * FS_SCALE;
|
||
|
thisrerr[col ] += ( err * 7 ) / 16;
|
||
|
nextrerr[col + 2] += ( err * 3 ) / 16;
|
||
|
nextrerr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextrerr[col ] += ( err ) / 16;
|
||
|
err = ( sg - (int32) PPM_GETG( colormap[ind].color ) ) * FS_SCALE;
|
||
|
thisgerr[col ] += ( err * 7 ) / 16;
|
||
|
nextgerr[col + 2] += ( err * 3 ) / 16;
|
||
|
nextgerr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextgerr[col ] += ( err ) / 16;
|
||
|
err = ( sb - (int32) PPM_GETB( colormap[ind].color ) ) * FS_SCALE;
|
||
|
thisberr[col ] += ( err * 7 ) / 16;
|
||
|
nextberr[col + 2] += ( err * 3 ) / 16;
|
||
|
nextberr[col + 1] += ( err * 5 ) / 16;
|
||
|
nextberr[col ] += ( err ) / 16;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*pP = colormap[ind].color;
|
||
|
|
||
|
if ( ( ! floyd ) || fs_direction )
|
||
|
{
|
||
|
++col;
|
||
|
++pP;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
--col;
|
||
|
--pP;
|
||
|
}
|
||
|
}
|
||
|
while ( col != limitcol );
|
||
|
|
||
|
if ( floyd )
|
||
|
{
|
||
|
temperr = thisrerr;
|
||
|
thisrerr = nextrerr;
|
||
|
nextrerr = temperr;
|
||
|
temperr = thisgerr;
|
||
|
thisgerr = nextgerr;
|
||
|
nextgerr = temperr;
|
||
|
temperr = thisberr;
|
||
|
thisberr = nextberr;
|
||
|
nextberr = temperr;
|
||
|
fs_direction = ! fs_direction;
|
||
|
}
|
||
|
|
||
|
/* ppm_writeppmrow( stdout, (pixel *)imagearray[row], imagewidth, maxval, 0 );*/
|
||
|
}
|
||
|
ppm_freecolorhash(cht);
|
||
|
free(thisrerr);
|
||
|
free(nextrerr);
|
||
|
free(thisgerr);
|
||
|
free(nextgerr);
|
||
|
free(thisberr);
|
||
|
free(nextberr);
|
||
|
(*maxcolorvalue)=maxval;
|
||
|
(*pchv)=colormap;
|
||
|
return CONV_OK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Here is the fun part, the median-cut colormap generator. This is based
|
||
|
** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer
|
||
|
** Display", SIGGRAPH '82 Proceedings, page 297.
|
||
|
*/
|
||
|
|
||
|
static colorhist_vector
|
||
|
mediancut( colorhist_vector chv, int16 colors, int32 sum, pixval maxval, int16 newcolors )
|
||
|
{
|
||
|
colorhist_vector colormap;
|
||
|
box_vector bv;
|
||
|
register int16 bi, i;
|
||
|
int16 boxes;
|
||
|
|
||
|
bv = (box_vector) malloc( sizeof(struct box) * newcolors );
|
||
|
colormap = (colorhist_vector) malloc( sizeof(struct colorhist_item) * newcolors );
|
||
|
if ( bv == (box_vector) 0 || colormap == (colorhist_vector) 0 )
|
||
|
return NULL;
|
||
|
for ( i = 0; i < newcolors; ++i )
|
||
|
PPM_ASSIGN( colormap[i].color, 0, 0, 0 );
|
||
|
|
||
|
/*
|
||
|
** Set up the initial box.
|
||
|
*/
|
||
|
bv[0].ind = 0;
|
||
|
bv[0].colors = colors;
|
||
|
bv[0].sum = sum;
|
||
|
boxes = 1;
|
||
|
|
||
|
/*
|
||
|
** Main loop: split boxes until we have enough.
|
||
|
*/
|
||
|
while ( boxes < newcolors )
|
||
|
{
|
||
|
register int32 indx, clrs;
|
||
|
int32 sm;
|
||
|
register int32 minr, maxr, ming, maxg, minb, maxb, v;
|
||
|
int32 halfsum, lowersum;
|
||
|
|
||
|
/*
|
||
|
** Find the first splittable box.
|
||
|
*/
|
||
|
for ( bi = 0; bi < boxes; ++bi )
|
||
|
if ( bv[bi].colors >= 2 )
|
||
|
break;
|
||
|
if ( bi == boxes )
|
||
|
break; /* ran out of colors! */
|
||
|
indx = bv[bi].ind;
|
||
|
clrs = bv[bi].colors;
|
||
|
sm = bv[bi].sum;
|
||
|
|
||
|
/*
|
||
|
** Go through the box finding the minimum and maximum of each
|
||
|
** component - the boundaries of the box.
|
||
|
*/
|
||
|
minr = maxr = PPM_GETR( chv[indx].color );
|
||
|
ming = maxg = PPM_GETG( chv[indx].color );
|
||
|
minb = maxb = PPM_GETB( chv[indx].color );
|
||
|
for ( i = 1; i < clrs; ++i )
|
||
|
{
|
||
|
v = PPM_GETR( chv[indx + i].color );
|
||
|
if ( v < minr ) minr = v;
|
||
|
if ( v > maxr ) maxr = v;
|
||
|
v = PPM_GETG( chv[indx + i].color );
|
||
|
if ( v < ming ) ming = v;
|
||
|
if ( v > maxg ) maxg = v;
|
||
|
v = PPM_GETB( chv[indx + i].color );
|
||
|
if ( v < minb ) minb = v;
|
||
|
if ( v > maxb ) maxb = v;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Find the largest dimension, and sort by that component. I have
|
||
|
** included two methods for determining the "largest" dimension;
|
||
|
** first by simply comparing the range in RGB space, and second
|
||
|
** by transforming into luminosities before the comparison. You
|
||
|
** can switch which method is used by switching the commenting on
|
||
|
** the LARGE_ defines at the beginning of this source file.
|
||
|
*/
|
||
|
#ifdef LARGE_NORM
|
||
|
if ( maxr - minr >= maxg - ming && maxr - minr >= maxb - minb )
|
||
|
XP_QSORT(
|
||
|
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
|
||
|
(qsortprototype)redcompare );
|
||
|
else if ( maxg - ming >= maxb - minb )
|
||
|
XP_QSORT(
|
||
|
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
|
||
|
(qsortprototype)greencompare );
|
||
|
else
|
||
|
XP_QSORT(
|
||
|
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
|
||
|
(qsortprototype)bluecompare );
|
||
|
#endif /*LARGE_NORM*/
|
||
|
#ifdef LARGE_LUM
|
||
|
{
|
||
|
pixel p;
|
||
|
float rl, gl, bl;
|
||
|
|
||
|
PPM_ASSIGN(p, maxr - minr, 0, 0);
|
||
|
rl = PPM_LUMIN(p);
|
||
|
PPM_ASSIGN(p, 0, maxg - ming, 0);
|
||
|
gl = PPM_LUMIN(p);
|
||
|
PPM_ASSIGN(p, 0, 0, maxb - minb);
|
||
|
bl = PPM_LUMIN(p);
|
||
|
|
||
|
if ( rl >= gl && rl >= bl )
|
||
|
XP_QSORT(
|
||
|
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
|
||
|
redcompare );
|
||
|
else if ( gl >= bl )
|
||
|
XP_QSORT(
|
||
|
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
|
||
|
greencompare );
|
||
|
else
|
||
|
XP_QSORT(
|
||
|
(char*) &(chv[indx]), clrs, sizeof(struct colorhist_item),
|
||
|
bluecompare );
|
||
|
}
|
||
|
#endif /*LARGE_LUM*/
|
||
|
|
||
|
/*
|
||
|
** Now find the median based on the counts, so that about half the
|
||
|
** pixels (not colors, pixels) are in each subdivision.
|
||
|
*/
|
||
|
lowersum = chv[indx].value;
|
||
|
halfsum = sm / 2;
|
||
|
for ( i = 1; i < clrs - 1; ++i )
|
||
|
{
|
||
|
if ( lowersum >= halfsum )
|
||
|
break;
|
||
|
lowersum += chv[indx + i].value;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Split the box, and sort to bring the biggest boxes to the top.
|
||
|
*/
|
||
|
bv[bi].colors = i;
|
||
|
bv[bi].sum = lowersum;
|
||
|
bv[boxes].ind = indx + i;
|
||
|
bv[boxes].colors = clrs - i;
|
||
|
bv[boxes].sum = sm - lowersum;
|
||
|
++boxes;
|
||
|
XP_QSORT( (char*) bv, boxes, sizeof(struct box), (qsortprototype)sumcompare );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Ok, we've got enough boxes. Now choose a representative color for
|
||
|
** each box. There are a number of possible ways to make this choice.
|
||
|
** One would be to choose the center of the box; this ignores any structure
|
||
|
** within the boxes. Another method would be to average all the colors in
|
||
|
** the box - this is the method specified in Heckbert's paper. A third
|
||
|
** method is to average all the pixels in the box. You can switch which
|
||
|
** method is used by switching the commenting on the REP_ defines at
|
||
|
** the beginning of this source file.
|
||
|
*/
|
||
|
for ( bi = 0; bi < boxes; ++bi )
|
||
|
{
|
||
|
#ifdef REP_CENTER_BOX
|
||
|
register int16 indx = bv[bi].ind;
|
||
|
register int16 clrs = bv[bi].colors;
|
||
|
register int16 minr, maxr, ming, maxg, minb, maxb, v;
|
||
|
|
||
|
minr = maxr = PPM_GETR( chv[indx].color );
|
||
|
ming = maxg = PPM_GETG( chv[indx].color );
|
||
|
minb = maxb = PPM_GETB( chv[indx].color );
|
||
|
for ( i = 1; i < clrs; ++i )
|
||
|
{
|
||
|
v = PPM_GETR( chv[indx + i].color );
|
||
|
minr = min( minr, v );
|
||
|
maxr = max( maxr, v );
|
||
|
v = PPM_GETG( chv[indx + i].color );
|
||
|
ming = min( ming, v );
|
||
|
maxg = max( maxg, v );
|
||
|
v = PPM_GETB( chv[indx + i].color );
|
||
|
minb = min( minb, v );
|
||
|
maxb = max( maxb, v );
|
||
|
}
|
||
|
PPM_ASSIGN(
|
||
|
colormap[bi].color, ( minr + maxr ) / 2, ( ming + maxg ) / 2,
|
||
|
( minb + maxb ) / 2 );
|
||
|
#endif /*REP_CENTER_BOX*/
|
||
|
#ifdef REP_AVERAGE_COLORS
|
||
|
register int16 indx = bv[bi].ind;
|
||
|
register int16 clrs = bv[bi].colors;
|
||
|
register int32 r = 0, g = 0, b = 0;
|
||
|
|
||
|
for ( i = 0; i < clrs; ++i )
|
||
|
{
|
||
|
r += PPM_GETR( chv[indx + i].color );
|
||
|
g += PPM_GETG( chv[indx + i].color );
|
||
|
b += PPM_GETB( chv[indx + i].color );
|
||
|
}
|
||
|
r = r / clrs;
|
||
|
g = g / clrs;
|
||
|
b = b / clrs;
|
||
|
PPM_ASSIGN( colormap[bi].color, (pixval)r, (pixval)g, (pixval)b );
|
||
|
#endif /*REP_AVERAGE_COLORS*/
|
||
|
#ifdef REP_AVERAGE_PIXELS
|
||
|
register int16 indx = bv[bi].ind;
|
||
|
register int16 clrs = bv[bi].colors;
|
||
|
register int32 r = 0, g = 0, b = 0, sum = 0;
|
||
|
|
||
|
for ( i = 0; i < clrs; ++i )
|
||
|
{
|
||
|
r += PPM_GETR( chv[indx + i].color ) * chv[indx + i].value;
|
||
|
g += PPM_GETG( chv[indx + i].color ) * chv[indx + i].value;
|
||
|
b += PPM_GETB( chv[indx + i].color ) * chv[indx + i].value;
|
||
|
sum += chv[indx + i].value;
|
||
|
}
|
||
|
r = r / sum;
|
||
|
if ( r > maxval ) r = maxval; /* avoid math errors */
|
||
|
g = g / sum;
|
||
|
if ( g > maxval ) g = maxval;
|
||
|
b = b / sum;
|
||
|
if ( b > maxval ) b = maxval;
|
||
|
PPM_ASSIGN( colormap[bi].color, r, g, b );
|
||
|
#endif /*REP_AVERAGE_PIXELS*/
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** All done.
|
||
|
*/
|
||
|
free (bv);
|
||
|
return colormap;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
redcompare( ch1, ch2 )
|
||
|
const colorhist_vector ch1, ch2;
|
||
|
{
|
||
|
return (int16) PPM_GETR( ch1->color ) - (int16) PPM_GETR( ch2->color );
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
greencompare( ch1, ch2 )
|
||
|
const colorhist_vector ch1, ch2;
|
||
|
{
|
||
|
return (int16) PPM_GETG( ch1->color ) - (int16) PPM_GETG( ch2->color );
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
bluecompare( ch1, ch2 )
|
||
|
const colorhist_vector ch1, ch2;
|
||
|
{
|
||
|
return (int16) PPM_GETB( ch1->color ) - (int16) PPM_GETB( ch2->color );
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
sumcompare( b1, b2 )
|
||
|
const box_vector b1, b2;
|
||
|
{
|
||
|
return b2->sum - b1->sum;
|
||
|
}
|