/*
 *   surf - visualizing algebraic curves and algebraic surfaces
 *   Copyright (C) 1996-1997 Friedrich-Alexander-Universitaet
 *                           Erlangen-Nuernberg
 *                 1997-2000 Johannes Gutenberg-Universitaet Mainz
 *   Authors: Stephan Endrass, Hans Huelf, Ruediger Oertel,
 *            Kai Schneider, Ralf Schmitt, Johannes Beigel
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */




// ===========================================================================
// File      compfn.cc
// Author:   Stephan Endrass
// Address:  endrass@mi.uni-erlangen.de
// Date:     3.2.95
// ============================================================================


#include <limits.h>
#include <stdio.h>
#include <stdlib.h>

#include "def.h"
#include "compfn.h"





// ----------------------------------------------------------------------------
//  Dispersed dot ordered dither patterns
// ----------------------------------------------------------------------------

static  int     dispersed_dither_4_x_4_data[4][4] =
{
    {   1,  9,  3, 11 },
    {  13,  5, 15,  7 },
    {   4, 12,  2, 10 },
    {  16,  8, 14,  6 }
};

static  int     dispersed_dither_8_x_8_data[8][8] =
{
    {   1, 33,  9, 41,  3, 35, 11, 43 },
    {  49, 17, 57, 25, 51, 19, 59, 27 },
    {  13, 45,  5, 37, 15, 47,  7, 39 },
    {  61, 29, 53, 21, 63, 31, 55, 23 },
    {   4, 36, 12, 44,  2, 34, 10, 42 },
    {  52, 20, 60, 28, 50, 18, 58, 26 },
    {  16, 48,  8, 40, 14, 46,  6, 38 },
    {  64, 32, 56, 24, 62, 30, 54, 22 }
};

static  int     dispersed_dither_16_x_16_data[16][16] =
{
    {   1,129, 33,161,  9,137, 41,169,  3,131, 35,163, 11,139, 43,171 },
    { 193, 65,225, 97,201, 73,233,105,195, 67,227, 99,203, 75,235,107 },
    {  49,177, 17,145, 57,185, 25,153, 51,179, 19,147, 59,187, 27,155 },
    { 241,113,209, 81,249,121,217, 89,243,115,211, 83,251,123,219, 91 },
    {  13,141, 45,173,  5,133, 37,165, 15,143, 47,175,  7,135, 39,167 },
    { 205, 77,237,109,197, 69,229,101,207, 79,239,111,199, 71,231,103 },
    {  61,189, 29,157, 53,181, 21,149, 63,191, 31,159, 55,183, 23,151 },
    { 253,125,221, 93,245,117,213, 85,255,127,223, 95,247,119,215, 87 },
    {   4,132, 36,164, 12,140, 44,172,  2,130, 34,162, 10,138, 42,170 },
    { 196, 68,228,100,204, 76,236,108,194, 66,226, 98,202, 74,234,106 },
    {  52,180, 20,148, 60,188, 28,156, 50,178, 18,146, 58,186, 26,154 },
    { 244,116,212, 84,252,124,220, 92,242,114,210, 82,250,122,218, 90 },
    {  16,144, 48,176,  8,136, 40,168, 14,142, 46,174,  6,134, 38,166 },
    { 208, 80,240,112,200, 72,232,104,206, 78,238,110,198, 70,230,102 },
    {  64,192, 32,160, 56,184, 24,152, 62,190, 30,158, 54,182, 22,150 },
    { 256,128,224, 96,248,120,216, 88,254,126,222, 94,246,118,214, 86 }
};

static  float   dispersed_dither_4_x_4_compare  [ 4][ 4];
static  float   dispersed_dither_8_x_8_compare  [ 8][ 8];
static  float   dispersed_dither_16_x_16_compare[16][16];

// ----------------------------------------------------------------------------
//  Clustered dot ordered dither pattern
// ----------------------------------------------------------------------------

static  int     clustered_dither_4_x_4_data[4][4] =
{
    {   9, 11,  4,  2 },
    {  13, 15,  8,  6 },
    {   3,  1, 10, 12 },
    {   7, 5 , 14, 16 }
};

static  int     clustered_dither_8_x_8_data[8][8] =
{
    {  33, 41, 43, 35, 28, 12, 10, 26 },
    {  55, 57, 59, 45, 14,  4,  2, 24 },
    {  53, 61, 63, 47, 16,  6,  8, 22 },
    {  39, 51, 49, 37, 30, 18, 20, 32 },
    {  27, 11,  9, 25, 34, 42, 44, 36 },
    {  13,  3,  1, 23, 56, 58, 60, 46 },
    {  15,  5,  7, 21, 54, 62, 64, 48 },
    {  29, 17, 19, 31, 40, 52, 50, 38 }
};

static  int     clustered_dither_16_x_16_data[16][16] =
{
    { 129,139,157,185,187,159,141,131,124,110, 88, 56, 54, 86,108,122 },
    { 137,155,183,215,217,189,161,143,112, 90, 58, 30, 28, 52, 84,106 },
    { 153,181,213,237,239,219,191,163, 92, 60, 32, 12, 10, 26, 50, 82 },
    { 179,211,235,249,251,241,221,193, 62, 34, 14,  4,  2, 24, 48, 80 },
    { 177,209,233,253,255,243,223,195, 64, 36, 16,  6,  8, 22, 46, 78 },
    { 175,207,231,247,245,225,197,165, 94, 66, 38, 18, 20, 44, 76,104 },
    { 151,173,205,229,227,199,167,145,114, 96, 68, 40, 42, 74,102,120 },
    { 135,149,171,203,201,169,147,133,126,116, 98, 70, 72,100,118,128 },
    { 123,109, 87, 55, 53, 85,107,121,130,140,158,186,188,160,142,132 },
    { 111, 89, 57, 29, 27, 51, 83,105,138,156,184,216,218,190,162,144 },
    {  91, 59, 31, 11,  9, 25, 49, 81,154,182,214,238,240,220,192,164 },
    {  61, 33, 13,  3,  1, 23, 47, 79,180,212,236,250,252,242,222,194 },
    {  63, 35, 15,  5,  7, 21, 45, 77,178,210,234,254,256,244,224,196 },
    {  93, 65, 37, 17, 19, 43, 75,103,176,208,232,248,246,226,198,166 },
    { 113, 95, 67, 39, 41, 73,101,119,152,174,206,230,228,200,168,146 },
    { 125,115, 97, 69, 71, 99,117,127,136,150,172,204,202,170,148,134 }
};

static  float   clustered_dither_4_x_4_compare  [ 4][ 4];
static  float   clustered_dither_8_x_8_compare  [ 8][ 8];
static  float   clustered_dither_16_x_16_compare[16][16];

// ----------------------------------------------------------------------------
//  Matrix for dot-diffusion
// ----------------------------------------------------------------------------

// -----------------------
//  Matrix with one baron
// -----------------------

static  int     d_d_1_b_m[8][8] =
{
    { 25, 21, 13, 39, 47, 57, 53, 45 },
    { 48, 32, 29, 43, 55, 63, 61, 56 },
    { 40, 30, 35, 51, 59, 62, 60, 52 },
    { 36, 14, 22, 26, 46, 54, 58, 44 },
    { 16,  6, 10, 18, 38, 42, 50, 24 },
    {  8,  0,  2,  7, 15, 31, 34, 20 },
    {  4,  1,  3, 11, 23, 33, 28, 12 },
    { 17,  9,  5, 19, 27, 49, 41, 37 }
};

// ------------------------
//  Matrix with two barons
// ------------------------

static  int     d_d_2_b_m[8][8] =
{
    { 34, 48, 40, 32, 29, 15, 23, 31 },
    { 42, 58, 56, 53, 21,  5,  7, 10 },
    { 50, 62, 61, 45, 13,  1,  2, 18 },
    { 38, 46, 54, 37, 25, 17,  9, 26 },
    { 28, 14, 22, 30, 35, 49, 41, 33 },
    { 20,  4,  6, 11, 43, 59, 57, 52 },
    { 12,  0,  3, 19, 51, 63, 60, 44 },
    { 24, 16,  8, 27, 39, 47, 55, 36 }
};

static  int     (*d_d_m[2])[8][8] =
{
    &d_d_1_b_m,
    &d_d_2_b_m
};

// ----------------------------------------------------------------------------

// --------------------------------
//  Neighbour offsets x-coordinate
// --------------------------------

int     d_d_n_x[8] = 
{
    -1, 0, 1,
    -1,    1,
    -1, 0, 1
};

// --------------------------------
//  Neighbour offsets y-coordinate
// --------------------------------

int     d_d_n_y[8] =
{
    -1,-1,-1,
     0,    0,
     1, 1, 1
};

// -----------------------------
//  Corresponding error weights
// -----------------------------

int     d_d_w[8] =
{
     1, 2, 1,
     2,    2,
     1, 2, 1
};

// ----------------------------------------
//  Nearest neighbour offsets x-coordinate
// ----------------------------------------

int     d_d_n_n_x[9] =
{
    -1, 0, 1,
    -1, 0, 1,
    -1, 0, 1
};

// ----------------------------------------
//  Nearest neighbour offsets y-coordinate
// ----------------------------------------

int     d_d_n_n_y[9] =
{
    -1,-1,-1,
     0, 0, 0,
     1, 1, 1
};

// ---------------------------------
//  Information about dot diffusion
// ---------------------------------

static  dot_diffusion_struct d_d_1_b_s[64];
static  dot_diffusion_struct d_d_2_b_s[64];

typedef dot_diffusion_struct dot_diffusion_struct_arr[64];
typedef dot_diffusion_struct_arr *dot_diffusion_struct_arr_ptr;

dot_diffusion_struct_arr_ptr d_d_s[2] =
{
    &d_d_1_b_s,
    &d_d_2_b_s
};

// ----------------------------------------
//  Information about smooth dot diffusion
// ----------------------------------------

static  smooth_dot_diffusion_struct  s_d_d_1_b_s[64];
static  smooth_dot_diffusion_struct  s_d_d_2_b_s[64];

typedef smooth_dot_diffusion_struct smooth_dot_diffusion_struct_arr[64];
typedef smooth_dot_diffusion_struct_arr *smooth_dot_diffusion_struct_arr_ptr;

smooth_dot_diffusion_struct_arr_ptr s_d_d_s[2] =
{
    &s_d_d_1_b_s,
    &s_d_d_2_b_s,
};

// ----------------------------------------------------------------------------
//  Get the entry of a dot diffusion matrix
// ----------------------------------------------------------------------------

int     dot_diffusion_matrix( int i,int px,int py )
{
    px = ( px < 0 ? 8 - (-px)%8 : px%8 );
    py = ( py < 0 ? 8 - (-py)%8 : py%8 );

    return  (*d_d_m[i])[py][px];
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

void    nearest_neighbour_of_class( int i,int x,int y,int *rx,int *ry,int k )
{
    int     x_d, x_d_min = 0;
    int     y_d, y_d_min = 0;
    int     dist_min = 1000;
    int     dist;
    int     j;

    for( j = 0; j < 9; j++ )
    {
        x_d  = (*d_d_s[i])[k].x + 8*d_d_n_n_x[j] - x;
        y_d  = (*d_d_s[i])[k].y + 8*d_d_n_n_y[j] - y;
        dist = x_d*x_d + y_d*y_d;

        if( dist < dist_min )
        {
            dist_min = dist;
            x_d_min = x_d;
            y_d_min = y_d;
        }
    }

    *rx = x_d_min;
    *ry = y_d_min;
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

static  void    dot_diffusion_init_struct( void )
{
    int     i,j,k;
    int     x;
    int     y = 0;
    int     found;
    int     num_err;

    for( i = 0; i < 2; i++ )
    {
        for( k = 0; k < 64; k++ )
        {
            // -------------------------------
            //  look for number  k  in matrix
            // -------------------------------

            found = FALSE;
            x = -1;
            while( x < 7 && !found )
            {
                x++;
                y = -1;

                while( y < 7 && !found )
                {
                    y++;
                    found = ( (*d_d_m[i])[y][x] == k );
                }
            }

            if( !found )
            {
                fprintf( stderr,"Error while initializing dot diffusion\n" );
                exit( 1 );
            }

            // ---------------------------------
            //  Store position of  k  in struct
            // ---------------------------------

            (*d_d_s[i])[k].x = x;
            (*d_d_s[i])[k].y = y;

            // ------------------------------
            //  Calculate error distribution
            // ------------------------------

            num_err = 0;

            for( j = 0; j < 8; j++ )
            {
                if( dot_diffusion_matrix( i,x + d_d_n_x[j],
                                            y + d_d_n_y[j] ) > k )
                {
                    (*d_d_s[i])[k].err[j] = (double)d_d_w[j];
                    num_err += d_d_w[j];
                }
                else
                {
                    (*d_d_s[i])[k].err[j] = 0.0;
                }
            }

            if( num_err > 0 )
            {
                for( j = 0; j < 8; j++ )
                {
                    (*d_d_s[i])[k].err[j] /= num_err;
                }
            }
        }
    }
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

static  void    smooth_dot_diffusion_init_struct( void )
{
    int     i,j,k,l;

    // -----------------------------------------------
    //  Assume graceously that dot_diffusion_init has
    //  been called before
    // -----------------------------------------------

    for( i = 0; i < 2; i++ )
    {
        for( k = 0; k <= 63; k++ )
        {
            (*s_d_d_s[i])[k].x = (*d_d_s[i])[k].x;
            (*s_d_d_s[i])[k].y = (*d_d_s[i])[k].y;
            (*s_d_d_s[i])[k].n = 63 - k;
    
            if( k < 63 )
            {
                (*s_d_d_s[i])[k].err_x = new    int[(*s_d_d_s[i])[k].n];
                (*s_d_d_s[i])[k].err_y = new    int[(*s_d_d_s[i])[k].n];
                (*s_d_d_s[i])[k].err   = new double[(*s_d_d_s[i])[k].n];

                for( j = 0, l = k + 1; j < (*s_d_d_s[i])[k].n; j++,l++ )
                {
                    nearest_neighbour_of_class( i,
                           (*s_d_d_s[i])[k].x,
                           (*s_d_d_s[i])[k].y,
                           &(*s_d_d_s[i])[k].err_x[j],
                           &(*s_d_d_s[i])[k].err_y[j],
                           l ); 
                    (*s_d_d_s[i])[k].err[j] = 1.0/( 63 - k );
                }
            }
            else
            {
                (*s_d_d_s[i])[k].err_x = (int*)NULL;
                (*s_d_d_s[i])[k].err_y = (int*)NULL;
                (*s_d_d_s[i])[k].err   = (double*)NULL;
            }
        }
    }
}


void deinit_dot_diffusion()
{
	int     i,j,k,l;


	for( i = 0; i < 2; i++ ) {
		for( k = 0; k <= 63; k++ ) {
			(*s_d_d_s[i])[k].x = (*d_d_s[i])[k].x;
			(*s_d_d_s[i])[k].y = (*d_d_s[i])[k].y;
			(*s_d_d_s[i])[k].n = 63 - k;
    
			if( k < 63 ) {
				delete [] (*s_d_d_s[i])[k].err_x;
				delete [] (*s_d_d_s[i])[k].err_y;
				delete [] (*s_d_d_s[i])[k].err;
			}
		}
	}
}
// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

void    init_dot_diffusion( void )
{
    dot_diffusion_init_struct( );
    smooth_dot_diffusion_init_struct( );
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

void    init_pattern( void )
{
    int     i;
    int     j;

    for( i = 0; i < 4; i++ )
    {
        for( j = 0; j < 4; j++ )
        {
            clustered_dither_4_x_4_compare[i][j] =
                    (float)( 2*clustered_dither_4_x_4_data[i][j] - 1)/32;
            dispersed_dither_4_x_4_compare[i][j] =
                    (float)( 2*dispersed_dither_4_x_4_data[i][j] - 1)/32;
        }
    }

    for( i = 0; i < 8; i++ )
    {
        for( j = 0; j < 8; j++ )
        {
            clustered_dither_8_x_8_compare[i][j] =
                    (float)( 2*clustered_dither_8_x_8_data[i][j] - 1)/128;
            dispersed_dither_8_x_8_compare[i][j] =
                    (float)( 2*dispersed_dither_8_x_8_data[i][j] - 1)/128;
        }
    }

    for( i = 0; i < 16; i++ )
    {
        for( j = 0; j < 16; j++ )
        {
            clustered_dither_16_x_16_compare[i][j] =
                    (float)( 2*clustered_dither_16_x_16_data[i][j] - 1)/512;
            dispersed_dither_16_x_16_compare[i][j] =
                    (float)( 2*dispersed_dither_16_x_16_data[i][j] - 1)/512;
        }
    }
}

// ----------------------------------------------------------------------------
// Compare functions
// ----------------------------------------------------------------------------

static  float   compare_clustered_dither_4_x_4( int x,int y )
{
    return  clustered_dither_4_x_4_compare[y%4][x%4];
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

static  float   compare_clustered_dither_8_x_8( int x,int y )
{
    return  clustered_dither_8_x_8_compare[y%8][x%8];
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

static  float   compare_clustered_dither_16_x_16( int x,int y )
{
    return  clustered_dither_16_x_16_compare[y%16][x%16];
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

static  float   compare_dispersed_dither_4_x_4( int x,int y )
{
    return  dispersed_dither_4_x_4_compare[y%4][x%4];
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

static  float   compare_dispersed_dither_8_x_8( int x,int y )
{
    return  dispersed_dither_8_x_8_compare[y%8][x%8];
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

static  float   compare_dispersed_dither_16_x_16( int x,int y )
{
    return  dispersed_dither_16_x_16_compare[y%16][x%16];
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------

float   (*compare_function[2][3])( int,int ) =
{
	{
		compare_clustered_dither_4_x_4,
		compare_clustered_dither_8_x_8,
		compare_clustered_dither_16_x_16
	},
	{
		compare_dispersed_dither_4_x_4,
		compare_dispersed_dither_8_x_8,
		compare_dispersed_dither_16_x_16
	}
};





// ============================================================================
// End of compfn.cc
// ============================================================================


syntax highlighted by Code2HTML, v. 0.9.1