/*-
* Copyright (c) 1997-2002 The Protein Laboratory, University of Copenhagen
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id$
*/
/* Created by Dmitry Karasik <dk@plab.ku.dk> */
#include "img_conv.h"
#ifdef __cplusplus
extern "C" {
#endif
#define map_RGB_gray ((Byte*)std256gray_palette)
Byte map_stdcolorref [ 256];
Byte div51 [ 256];
Byte div17 [ 256];
Byte mod51 [ 256];
Byte mod17mul3 [ 256];
RGBColor cubic_palette [ 256];
RGBColor cubic_palette8 [ 8];
RGBColor cubic_palette16 [ 16] =
{
{0,0,0}, {0,128,128}, {128,0,128}, {0,0,255},
{128,128,0}, {0,255,0}, {255,0,0}, {85,85,85},
{170,170,170},{0,255,255},{255,0,255},{128,128,255},
{255,255,0},{128,255,128},{255,128,128},{255,255,255}
};
RGBColor stdmono_palette [ 2] = {{ 0, 0, 0}, { 0xFF, 0xFF, 0xFF}};
RGBColor std16gray_palette [ 16];
RGBColor std256gray_palette [ 256];
Byte map_halftone8x8_51 [ 64] = {
0, 38, 9, 47, 2, 40, 11, 50,
25, 12, 35, 22, 27, 15, 37, 24,
6, 44, 3, 41, 8, 47, 5, 43,
31, 19, 28, 15, 34, 21, 31, 18,
1, 39, 11, 49, 0, 39, 10, 48,
27, 14, 36, 23, 26, 13, 35, 23,
7, 46, 4, 43, 7, 45, 3, 42,
33, 20, 30, 17, 32, 19, 29, 16
};
Byte map_halftone8x8_64 [ 64] = {
0, 47, 12, 59, 3, 50, 15, 62,
32, 16, 43, 28, 34, 19, 46, 31,
8, 55, 4, 51, 11, 58, 7, 54,
39, 24, 35, 20, 42, 27, 38, 23,
2, 49, 14, 61, 1, 48, 13, 60,
33, 18, 45, 30, 32, 17, 44, 29,
10, 57, 6, 53, 9, 56, 5, 52,
41, 26, 37, 22, 40, 25, 36, 21
};
void
cm_init_colormap( void)
{
int i;
for ( i = 0; i < 256; i++)
{
map_RGB_gray[ i * 3] = map_RGB_gray[ i * 3 + 1] = map_RGB_gray[ i * 3 + 2] = i;
map_stdcolorref[ i] = i;
div51[ i] = i / 51;
div17[ i] = i / 17;
mod51[ i] = i % 51;
mod17mul3[ i] = ( i % 17) * 3;
}
for ( i = 0; i < 16; i++)
std16gray_palette[ i]. r = std16gray_palette[ i]. g = std16gray_palette[ i]. b = i * 17;
{
int b, g, r;
for ( b = 0; b < 6; b++) for ( g = 0; g < 6; g++) for ( r = 0; r < 6; r++)
{
/* cubic_palette[ b + g * 6 + r * 36] =( RGBColor) { b * 51, g * 51, r *
* 51 }; */
int idx = b + g * 6 + r * 36;
cubic_palette[ idx]. b = b * 51;
cubic_palette[ idx]. g = g * 51;
cubic_palette[ idx]. r = r * 51;
}
}
{
int b, g, r;
for ( b = 0; b < 2; b++) for ( g = 0; g < 2; g++) for ( r = 0; r < 2; r++)
{
/* cubic_palette8[ b + g * 2 + r * 4] = ( RGBColor) { b * 255, g * 255,
* r * 255 }; */
int idx = b + g * 2 + r * 4;
cubic_palette8[ idx]. b = b * 255;
cubic_palette8[ idx]. g = g * 255;
cubic_palette8[ idx]. r = r * 255;
}
}
}
void
cm_reverse_palette( PRGBColor source, PRGBColor dest, int colors)
{
while( colors--)
{
register Byte r = source[0].r;
register Byte b = source[0].b;
register Byte g = source[0].g;
dest[0].r = b;
dest[0].b = r;
dest[0].g = g;
source++;
dest++;
}
}
void
cm_squeeze_palette( PRGBColor source, int srcColors, PRGBColor dest, int destColors)
{
if (( srcColors == 0) || ( destColors == 0)) return;
if ( srcColors <= destColors)
memcpy( dest, source, srcColors * sizeof( RGBColor));
else
{
int tolerance = 0;
int colors = srcColors;
PRGBColor buf = allocn( RGBColor, srcColors);
if (!buf) return;
memcpy( buf, source, srcColors * sizeof( RGBColor));
while (1)
{
int i;
int tt2 = tolerance*tolerance;
for ( i = 0; i < colors - 1; i++)
{
register int r = buf[i]. r;
register int g = buf[i]. g;
register int b = buf[i]. b;
int j;
register PRGBColor next = buf + i + 1;
for ( j = i + 1; j < colors; j++)
{
if (( ( next-> r - r)*( next-> r - r) +
( next-> g - g)*( next-> g - g) +
( next-> b - b)*( next-> b - b)) <= tt2)
{
buf[ j] = buf[ --colors];
if ( colors <= destColors) goto Enough;
}
next++;
}
}
tolerance += 2;
}
Enough:
memcpy( dest, buf, destColors * sizeof( RGBColor));
free( buf);
}
}
Byte
cm_nearest_color( RGBColor color, int palSize, PRGBColor palette)
{
int diff = INT_MAX, cdiff = 0;
Byte ret = 0;
while( palSize--)
{
int dr=abs( (int)color. r - (int)palette[ palSize]. r),
dg=abs( (int)color. g - (int)palette[ palSize]. g),
db=abs( (int)color. b - (int)palette[ palSize]. b);
cdiff=dr*dr+dg*dg+db*db;
if ( cdiff < diff)
{
ret = palSize;
diff = cdiff;
if ( cdiff == 0) break;
}
}
return ret;
}
void
cm_fill_colorref( PRGBColor fromPalette, int fromColorCount, PRGBColor toPalette, int toColorCount, Byte * colorref)
{
while( fromColorCount--)
colorref[ fromColorCount] =
cm_nearest_color( fromPalette[ fromColorCount], toColorCount, toPalette);
}
U16 *
cm_study_palette( RGBColor * palette, int pal_size)
{
RGBColor * org_palette = palette;
int i, pal2count = 1, pal2size = 64;
int sz = CELL_SIZE * pal2size;
U16 * p = malloc( sz * sizeof( U16));
if ( !p) return nil;
for ( i = 0; i < sz; i++) p[i] = PAL_FREE;
for ( i = 0; i < pal_size; i++, palette++) {
int table = 0, index =
((palette-> r >> 6) << 4) +
((palette-> g >> 6) << 2) +
(palette-> b >> 6);
int shift = 4;
while ( 1) {
if ( p[table + index] & PAL_FREE) {
p[table + index] = i;
break;
} else if ( p[table + index] & PAL_REF) {
table = (p[table + index] & ~PAL_REF) * CELL_SIZE;
index =
(((palette-> r >> shift) & 3) << 4) +
(((palette-> g >> shift) & 3) << 2) +
((palette-> b >> shift) & 3);
shift -= 2;
} else {
U16 old = p[table + index];
int sub_index, old_sub_index, new_table;
REPEAT:
if ( pal2count == pal2size) {
int j, newsz;
U16 * n;
newsz = ( pal2size += 64 ) * CELL_SIZE;
if ( !(n = malloc( newsz * sizeof( U16)))) {
free( p);
return nil;
}
memcpy( n, p, sizeof(U16) * sz);
for ( j = sz; j < newsz; j++) n[j] = PAL_FREE;
free( p);
p = n;
sz = newsz;
}
sub_index =
(((palette-> r >> shift) & 3) << 4) +
(((palette-> g >> shift) & 3) << 2) +
((palette-> b >> shift) & 3);
old_sub_index =
(((org_palette[old].r >> shift) & 3) << 4) +
(((org_palette[old].g >> shift) & 3) << 2) +
((org_palette[old].b >> shift) & 3);
new_table = pal2count * CELL_SIZE;
p[table + index] = (pal2count++) | PAL_REF;
if ( sub_index != old_sub_index) {
p[ new_table + sub_index] = i;
p[ new_table + old_sub_index] = old;
break;
} else {
if ( shift > 1) {
shift -= 2;
table = new_table;
index = sub_index;
goto REPEAT;
}
p[ table + index] = i;
break;
}
}
}
}
{
struct {
int i;
int table;
int r;
int g;
int b;
} stack[4]; /* max depth */
int sp = 0;
memset( stack, 0, sizeof(stack));
for ( ; stack[sp].i < 64; stack[sp].i++) {
if ( p[stack[sp].table + stack[sp].i] & PAL_FREE) {
RGBColor cell;
int shift = 6 - sp * 2, delta = 32 >> sp * 2;
cell. r = stack[sp]. r + delta + ((( stack[sp].i >> 4) & 3) << shift);
cell. g = stack[sp]. g + delta + ((( stack[sp].i >> 2) & 3) << shift);
cell. b = stack[sp]. b + delta + (( stack[sp].i & 3) << shift);
p[stack[sp].table + stack[sp].i] = cm_nearest_color( cell, pal_size, org_palette);
} else if ( p[stack[sp].table + stack[sp].i] & PAL_REF) {
int shift = 6 - sp * 2;
stack[sp + 1].r = stack[sp]. r + ((( stack[sp].i >> 4) & 3) << shift);
stack[sp + 1].g = stack[sp]. g + ((( stack[sp].i >> 2) & 3) << shift);
stack[sp + 1].b = stack[sp]. b + (( stack[sp].i & 3) << shift);
stack[sp + 1].table = (p[stack[sp].table + stack[sp].i] & ~PAL_REF) * CELL_SIZE;
stack[++sp].i = -1;
}
while ( stack[sp].i == 63 && sp > 0) sp--;
}
}
return p;
}
static int
sort_palette( const void * a, const void * b)
{
register unsigned int A =
((PRGBColor)a)->r +
((PRGBColor)a)->g +
((PRGBColor)a)->b
;
register unsigned int B =
((PRGBColor)b)->r +
((PRGBColor)b)->g +
((PRGBColor)b)->b
;
if ( A < B)
return -1;
else if ( A > B)
return 1;
else
return 0;
}
void
cm_sort_palette( RGBColor * palette, int size)
{
qsort( palette, size, sizeof(RGBColor), sort_palette);
}
#define MAP1_SIDE 32
#define MAP1_SHIFT 3
#define MAP1_SIDE3 (MAP1_SIDE*MAP1_SIDE*MAP1_SIDE)
#define MAP2_SIDE 8
#define MAP2_SHIFT 3
#define MAP2_MASK 7
#define MAP2_ITEM_SIZE 64
#define MAP2_ITEM_SHIFT 6
Bool
cm_optimized_palette( Byte * data, int lineSize, int width, int height, RGBColor * palette, int * max_pal_size)
{
int i, j, sz, count, side = MAP1_SIDE, shift = MAP1_SHIFT, force_squeeze = 0, map0index = 0;
int countB, countL, map2scale = 0;
Byte * map, * map2;
RGBColor * big_pal;
if ( !( map = malloc( MAP1_SIDE3))) return false;
REPEAT_CALC:
count = 0;
memset( map, 0, MAP1_SIDE3);
/* calculate colors with resolution 1 / 512 */
for ( i = 0; i < height; i++) {
RGBColor * p = ( RGBColor*)( data + i * lineSize );
for ( j = 0; j < width; j++, p++) {
int index = (p-> r >> shift) * side * side +
(p-> g >> shift) * side +
(p-> b >> shift);
if ( map[index] == 0) {
map[index] = 1;
count++;
}
}
}
j = 0;
sz = side * side * side;
/* if too many colors, extract only max_pal_size and return */
if ( count > *max_pal_size) {
if (( count > 512) && ( side > 8) && !force_squeeze) {
side >>= 1;
shift++;
goto REPEAT_CALC;
}
NO_MEMORY:
{
int delta = (1 << shift) - 1;
RGBColor * big_pal = malloc( count * sizeof(RGBColor));
if ( !big_pal) {
free( map);
return false;
}
for ( i = 0; i < sz; i++)
if ( map[i]) {
big_pal[j]. r = (i / ( side * side)) << shift;
big_pal[j]. g = ((i / side) % side ) << shift;
big_pal[j]. b = (i % side) << shift;
if ( big_pal[j]. r > 127) big_pal[j]. r += delta;
if ( big_pal[j]. g > 127) big_pal[j]. g += delta;
if ( big_pal[j]. b > 127) big_pal[j]. b += delta;
j++;
}
cm_squeeze_palette( big_pal, j, palette, *max_pal_size);
cm_sort_palette( palette, *max_pal_size);
free( big_pal);
free( map);
return true;
}
}
/* scale was lowered due to many colors, but now it is too few colors... */
if ( side != MAP1_SIDE) {
force_squeeze = 1;
side <<= 1;
shift--;
goto REPEAT_CALC;
}
/* stage 2 - full color calc */
if (!( map2 = malloc( MAP2_ITEM_SIZE * count))) goto NO_MEMORY;
memset( map2, 0, MAP2_ITEM_SIZE * count);
/* calculate colors with full resolution */
sz = MAP1_SIDE * MAP1_SIDE * MAP1_SIDE;
count = 0;
for ( i = 0; i < sz; i++)
if ( map[i]) {
if ( count == 0) map0index = i;
map[i] = count++;
}
count = 0;
for ( i = 0; i < height; i++) {
RGBColor * p = ( RGBColor*)( data + i * lineSize );
for ( j = 0; j < width; j++, p++) {
int index1 = (p-> r >> MAP1_SHIFT) * MAP1_SIDE * MAP1_SIDE +
(p-> g >> MAP1_SHIFT) * MAP1_SIDE +
(p-> b >> MAP1_SHIFT);
int index2 = (p-> r & MAP2_MASK) * MAP2_SIDE * MAP2_SIDE +
(p-> g & MAP2_MASK) * MAP2_SIDE +
(p-> b & MAP2_MASK);
index1 = (map[index1] << MAP2_ITEM_SHIFT) + (index2 >> 3);
if (( map2[index1] & (1 << (index2 & 7))) == 0) {
map2[index1] |= (1 << (index2 & 7));
count++;
}
}
}
/* Too many colors - if indeed too many, resample with /8 and /64 scale.
Even /64 scale though can result in up to 4K colors */
countB = countL = 0;
if ( count > *max_pal_size) {
if ( count > *max_pal_size * 2 && map2scale == 0) {
for ( i = 0; i < sz; i++)
if ( i == map0index || map[i] != 0) {
Byte * k = map2 + map[i] * MAP2_ITEM_SIZE;
U32 * l = ( U32 *) k;
for ( j = 0; j < MAP2_ITEM_SIZE / 4; j+=2)
if ( l[j] || l[j+1]) countL++;
for ( j = 0; j < MAP2_ITEM_SIZE; j++)
if ( k[j]) countB++;
}
if ( countB > *max_pal_size * 2) {
count = countL;
map2scale = 2;
} else {
count = countB;
map2scale = 1;
}
}
}
/* collect final palette */
if ( count > *max_pal_size) {
if ( !( big_pal = malloc( count * sizeof(RGBColor)))) {
free( map);
free( map2);
return false;
}
} else
big_pal = palette;
count = 0;
for ( i = 0; i < sz; i++)
if ( i == map0index || map[i] != 0) {
int r = (i / ( MAP1_SIDE * MAP1_SIDE)) << MAP1_SHIFT;
int g = ((i / MAP1_SIDE) % MAP1_SIDE ) << MAP1_SHIFT;
int b = (i % MAP1_SIDE) << MAP1_SHIFT;
Byte * k = map2 + map[i] * MAP2_ITEM_SIZE;
switch ( map2scale) {
case 0: /* 1/1 */
for ( j = 0; j < 512; j++) {
if ( k[j >> 3] & ( 1 << ( j & 7))) {
big_pal[count]. r = r + j / ( MAP2_SIDE * MAP2_SIDE);
big_pal[count]. g = g + (j / MAP2_SIDE) % MAP2_SIDE;
big_pal[count]. b = b + j % MAP2_SIDE;
count++;
}
}
break;
case 1: /* 1/8 */
for ( j = 0; j < 64; j++) {
if ( k[j]) {
big_pal[count]. r = r + ((j / ( 4 * 4)) << 1);
big_pal[count]. g = g + (((j / 4) % 4) << 1);
big_pal[count]. b = b + ((j % 4) << 1);
if ( big_pal[count]. r > 127) big_pal[count]. r ++;
if ( big_pal[count]. g > 127) big_pal[count]. g ++;
if ( big_pal[count]. b > 127) big_pal[count]. b ++;
count++;
}
}
break;
case 2: /* 1/64 */
for ( j = 0; j < 8; j++) {
if ( *(( U32*) k) || *((( U32*) k) + 1)) {
big_pal[count]. r = r + ((j / ( 2 * 2)) << 2);
big_pal[count]. g = g + (((j / 2) % 2) << 2);
big_pal[count]. b = b + ((j % 2) << 2);
if ( big_pal[count]. r > 127) big_pal[count]. r += 3;
if ( big_pal[count]. g > 127) big_pal[count]. g += 3;
if ( big_pal[count]. b > 127) big_pal[count]. b += 3;
count++;
}
k += 7;
}
break;
}
}
if ( big_pal != palette) {
cm_squeeze_palette( big_pal, count, palette, *max_pal_size);
count = *max_pal_size;
free( big_pal);
}
free( map);
free( map2);
*max_pal_size = count;
cm_sort_palette( palette, count);
return true;
}
#ifdef __cplusplus
}
#endif