/* libcmap2.c - support routines for color rows
**
** Copyright (C) 1994 Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
**
** 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 "ppmcmap2.h"


colorhash_table
ppm_colorrowtocolorhash(colorrow, ncolors)
    pixel *colorrow;
    int ncolors;
{
    colorhash_table cht;
    int i;

    cht = ppm_alloccolorhash();
    for( i = 0; i < ncolors; i++ ) {
        if( ppm_lookupcolor(cht, &colorrow[i]) < 0 ) {
            if( ppm_addtocolorhash(cht, &colorrow[i], i) < 0 )
                pm_error("out of memory adding to hash table");
        }
    }
    return cht;
}


pixel *
ppm_computecolorrow(pixels, cols, rows, maxcolors, ncolorsP)
    pixel **pixels;
    int cols, rows, maxcolors;
    int *ncolorsP;
{
    int ncolors, row, col;
    colorhash_table cht;
    pixel *pixrow;

    pixrow = ppm_allocrow(maxcolors);
    cht = ppm_alloccolorhash(); ncolors = 0;
    for( row = 0; row < rows; row++ ) {
        for( col = 0; col < cols; col++ ) {
            if( ppm_lookupcolor(cht, &pixels[row][col]) < 0 ) {
                if( ncolors >= maxcolors ) {
                    ppm_freerow(pixrow);
                    pixrow = (pixel *)0;
                    ncolors = -1;
                    goto fail;
                }
                if( ppm_addtocolorhash(cht, &pixels[row][col], ncolors) < 0 )
                    pm_error("out of memory adding to hash table");
                pixrow[ncolors] = pixels[row][col];
                ++ncolors;
            }
        }
    }
fail:
    ppm_freecolorhash(cht);

    *ncolorsP = ncolors;
    return pixrow;
}


pixel *
ppm_mapfiletocolorrow(file, maxcolors, ncolorsP, maxvalP)
    FILE *file;
    int maxcolors;
    int *ncolorsP;
    pixval *maxvalP;
{
    int cols, rows, format, row, col, ncolors;
    pixel *pixrow, *temprow;
    colorhash_table cht;

    pixrow = ppm_allocrow(maxcolors);

    ppm_readppminit(file, &cols, &rows, maxvalP, &format);
    temprow = ppm_allocrow(cols);
    cht = ppm_alloccolorhash(); ncolors = 0;
    for( row = 0; row < rows; row++ ) {
        ppm_readppmrow(file, temprow, cols, *maxvalP, format);
        for( col = 0; col < cols; col++ ) {
            if( ppm_lookupcolor(cht, &temprow[col]) < 0 ) {
                if( ncolors >= maxcolors ) {
                    ppm_freerow(pixrow);
                    pixrow = (pixel *)0;
                    ncolors = -1;
                    goto fail;
                }
                if( ppm_addtocolorhash(cht, &temprow[col], ncolors) < 0 )
                    pm_error("out of memory adding to hash table");
                pixrow[ncolors] = temprow[col];
                ++ncolors;
            }
        }
    }
fail:
    ppm_freecolorhash(cht);
    ppm_freerow(temprow);

    *ncolorsP = ncolors;
    return pixrow;
}


static int
pixel_cmp(a, b)
    void *a, *b;
{
    pixel *p1 = (pixel *)a, *p2 = (pixel *)b;
    int diff;

    diff = PPM_GETR(*p1) - PPM_GETR(*p2);
    if( diff == 0 ) {
        diff = PPM_GETG(*p1) - PPM_GETG(*p2);
        if( diff == 0 )
            diff = PPM_GETB(*p1) - PPM_GETB(*p2);
    }
    return diff;
}

static int (*custom_cmp) ARGS((pixel *, pixel *));

static int
custom_stub(a, b)
    void *a, *b;
{
    return (*custom_cmp)((pixel *)a, (pixel *)b);
}


void
ppm_sortcolorrow(colorrow, ncolors, cmpfunc)
    pixel *colorrow;
    int ncolors;
    int (*cmpfunc) ARGS((pixel *, pixel *));
{
    if( cmpfunc ) {
        custom_cmp = cmpfunc;
        qsort((void *)colorrow, ncolors, sizeof(pixel), custom_stub);
    }
    else
        qsort((void *)colorrow, ncolors, sizeof(pixel), pixel_cmp);
}



int
ppm_addtocolorrow(colorrow, ncolorsP, maxcolors, pixelP)
    pixel *colorrow;
    int *ncolorsP;
    int maxcolors;
    pixel *pixelP;
{
    int i;
    pixval r, g, b;

    r = PPM_GETR(*pixelP);
    g = PPM_GETG(*pixelP);
    b = PPM_GETB(*pixelP);

    for( i = 0; i < *ncolorsP; i++ ) {
        if( PPM_GETR(colorrow[i]) == r &&
            PPM_GETG(colorrow[i]) == g &&
            PPM_GETB(colorrow[i]) == b )
                return i;
    }

    i = *ncolorsP;
    if( i >= maxcolors )
        return -1;
    colorrow[i] = *pixelP;
    ++*ncolorsP;
    return i;
}


int
ppm_findclosestcolor(colormap, ncolors, pP)
    pixel *colormap;
    int ncolors;
    pixel *pP;
{
    /* Search colormap for closest match.       */
    /* algorithm taken from ppmquant.c   -IUW   */
    register int i, r1, g1, b1;
    int ind;
    long dist;

    r1 = PPM_GETR(*pP);
    g1 = PPM_GETG(*pP);
    b1 = PPM_GETB(*pP);
    dist = 2000000000;
    for( i = 0; i < ncolors; i++ ) {
        register int r2, g2, b2;
        long newdist;

        r2 = PPM_GETR(colormap[i]);
        g2 = PPM_GETG(colormap[i]);
        b2 = PPM_GETB(colormap[i]);
        newdist = ( r1 - r2 ) * ( r1 - r2 ) +
                  ( g1 - g2 ) * ( g1 - g2 ) +
                  ( b1 - b2 ) * ( b1 - b2 );

        if( newdist < dist ) {
            ind = i;
            dist = newdist;
        }
    }
    return ind;
}


void
#if __STDC__
ppm_colorrowtomapfile(FILE *ofp, pixel *colormap, int ncolors, pixval maxval)
#else
ppm_colorrowtomapfile(ofp, colormap, ncolors, maxval)
    FILE *ofp;
    pixel *colormap;
    int ncolors;
    pixval maxval;
#endif
{
    int i;

    ppm_writeppminit(ofp, ncolors, 1, maxval, 1);
    for( i = 0; i < ncolors; i++ )
        ppm_writeppmrow(ofp, &colormap[i], 1, maxval, 1);
}

