home *** CD-ROM | disk | FTP | other *** search
- /************************************************************************
- * *
- * colours.c *
- * ========= *
- * *
- * RISCOS sprite style bitmap processing library *
- * Uses spr_info generic bitmap interface from sprite.c library *
- * *
- * Functions to return closest value to desired RGB from sprites *
- * palette or colour range. A pix_str is used so value <0 & >255 *
- * Palette types can be identified and a fast routine attached to *
- * the sprite structure. *
- * *
- * Version 1.01 (05-May-1994) split from process.c *
- * *
- * (C) 1994 DEEJ Technology PLC *
- * *
- ************************************************************************/
-
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include "sprite.h"
- #include "colours.h"
-
- static pix_str *closest_rgb_core(pix_str *rgb, uint*, int);
- static pix_str *closest_rgb_gen(spr_info_str *s, pix_str *rgb);
- static pix_str *closest_rgb_24(spr_info_str *s, pix_str *rgb);
- static pix_str *closest_rgb_15(spr_info_str *s, pix_str *rgb);
- static pix_str *closest_rgb_1(spr_info_str *s, pix_str *rgb);
- static pix_str *closest_rgb_grey(spr_info_str *s, pix_str *rgb);
- static pix_str *closest_rgb_grey8(spr_info_str *s, pix_str *rgb);
- static pix_str *closest_rgb_desk8(spr_info_str *s, pix_str *rgb);
- static pix_str *closest_rgb_cube(spr_info_str *s, pix_str *rgb);
-
- /* global data */
-
- static pix_str cache[CACHE_SIZE];
-
- #ifdef SQR_TABLE
- static int squares[1024];
- #endif
-
- #ifdef CACHE_STAT
- int cache_calls;
- int cache_hits;
- int cache_misses;
- int cache_used;
- #endif
-
- /****************************************************************************/
-
- /*
- * checks for value in cache
- * returns valid pix_str for hit
- * or 0 and index = cache placement position for a miss
- * s used to check for initial call and sprite changed
- */
-
- static pix_str *check_cache(spr_info_str *s, pix_str *rgb, int *index)
- {
- static spr_info_str *last_spr = 0;
- int i, hash;
-
- if(s != last_spr)
- {
- #ifdef SQR_TABLE
- if(last_spr == 0)
- {
- for(i=-512; i<512; i++)
- {
- squares[512+i] = i*i;
- }
- }
- #endif
- /* initialise cache */
- #ifdef CACHE_STAT
- cache_calls = 0;
- cache_hits = 0;
- cache_misses = 0;
- cache_used = 0;
- #endif
- for(i=0; i<CACHE_SIZE; i++)
- {
- cache[i].value = -1;
- }
- last_spr = s;
- }
-
- /* hash RGB value and check for cache hit */
-
- hash = HASH(rgb->red, rgb->green, rgb->blue);
-
- #ifdef CACHE_STAT
- cache_calls++;
- #endif
- if(cache[hash].value != -1 &&
- cache[hash].red == rgb->red &&
- cache[hash].green == rgb->green &&
- cache[hash].blue == rgb->blue)
- {
- #ifdef CACHE_STAT
- cache_hits++;
- #endif
- return(&cache[hash]);
- }
-
- #ifdef CACHE_STAT
- if(cache[hash].value == -1)
- cache_used++;
- else
- cache_misses++;
- #endif
-
- *index = hash;
- return(0);
- }
-
-
- /*
- * general function
- * handles any palette and colour depth
- */
-
- pix_str *closest_rgb(spr_info_str *s, pix_str *rgb)
- {
- static spr_info_str *last_spr = 0;
- static pix_str close_rgb;
- REGISTER int i;
- REGISTER int r, g, b, r2, g2, b2;
- REGISTER int rd, gd, bd;
- REGISTER int close_diff, diff;
- int hash;
-
- if(s->bpp == 24) /* take care of 24 bpp case */
- {
- RANGE_RGB(rgb->red);
- RANGE_RGB(rgb->green);
- RANGE_RGB(rgb->blue);
-
- close_rgb.red = rgb->red;
- close_rgb.green = rgb->green;
- close_rgb.blue = rgb->blue;
- close_rgb.value = (rgb->blue << 24) |
- (rgb->green << 16) |
- (rgb->red << 8);
-
- return(&close_rgb);
- }
-
-
- /* initialise cache */
-
- if(s != last_spr)
- {
- #ifdef SQR_TABLE
- if(last_spr == 0)
- {
- for(i=-512; i<512; i++)
- {
- squares[512+i] = i*i;
- }
- }
- #endif
- #ifdef CACHE_STAT
- cache_calls = 0;
- cache_hits = 0;
- cache_misses = 0;
- cache_used = 0;
- #endif
- for(i=0; i<CACHE_SIZE; i++)
- {
- cache[i].value = -1;
- }
- last_spr = s;
- }
-
- if(s->bpp > 8)
- {
- RANGE_RGB(rgb->red);
- RANGE_RGB(rgb->green);
- RANGE_RGB(rgb->blue);
- }
-
- /* hash RGB value and check for cache hit */
-
- hash = HASH(rgb->red, rgb->green, rgb->blue);
-
- #ifdef CACHE_STAT
- cache_calls++;
- #endif
- if(cache[hash].value != -1 &&
- cache[hash].red == rgb->red &&
- cache[hash].green == rgb->green &&
- cache[hash].blue == rgb->blue)
- {
- #ifdef CACHE_STAT
- cache_hits++;
- #endif
- return(&cache[hash]);
- }
- #ifdef CACHE_STAT
- if(cache[hash].value == -1)
- cache_used++;
- else
- cache_misses++;
- #endif
-
- close_diff = MAX_DIFF;
-
- r = rgb->red;
- g = rgb->green;
- b = rgb->blue;
-
- for(i=0; i<s->cols; i++)
- {
- if(s->bpp == 15)
- {
- /*
- * special case - use 8 closest values
- * from corners of 15 bit colour cube
- * using max span alg for lower 3 bits
- */
-
- r2 = (r & 0xF8) | (r >> 5);
- g2 = (g & 0xF8) | (g >> 5);
- b2 = (b & 0xF8) | (b >> 5);
-
- if(r >= r2) rd = 0x11; else rd=-0x11;
- if(g >= g2) gd = 0x11; else gd=-0x11;
- if(b >= b2) bd = 0x11; else bd=-0x11;
-
- if((i & 1) > 0) r2 += rd;
- if((i & 2) > 0) g2 += gd;
- if((i & 4) > 0) b2 += bd;
-
- RANGE_RGB(r2);
- RANGE_RGB(g2);
- RANGE_RGB(b2);
-
- /* only do the 8 calcualtions */
- if(i==7) i=s->cols;
- }
- else
- {
- /*
- * do palette lookup
- * assume all sprites now use full 8 bit palette
- * so no need to copy upper to lower nibbles
- */
-
- r2 = (s->palette[i] >> 8) & 0xFF;
- g2 = (s->palette[i] >> 16) & 0xFF;
- b2 = (s->palette[i] >> 24) & 0xFF;
- }
-
- /* difference calculation */
-
- rd = r - r2;
- gd = g - g2;
- bd = b - b2;
-
- /*
- * diff^2 * weights
- * full 16 bit wieghts are to big, so shift by 3
- */
-
- /*
- diff = squares[512+rd] * (RED_WEIGHT >> WEIGHT_SHIFT) +
- squares[512+gd] * (GREEN_WEIGHT >> WEIGHT_SHIFT) +
- squares[512+bd] * (BLUE_WEIGHT >> WEIGHT_SHIFT);
- */
- #ifdef SQR_TABLE
- diff = squares[512+rd] * 3 +
- squares[512+gd] * 6 +
- squares[512+bd];
- #else
- diff = rd*rd * 3 +
- gd*gd * 6 +
- bd*bd;
- #endif
-
- /* check for better match */
-
- if(diff < close_diff)
- {
- close_diff = diff;
- close_rgb.value = i;
- close_rgb.red = r2;
- close_rgb.green = g2;
- close_rgb.blue = b2;
-
- /* short cut for extact match */
-
- if(diff == 0) i=s->cols;
- }
- }
-
- /* fixup value for 15 bit special case */
-
- if(s->bpp == 15)
- {
- close_rgb.value = (close_rgb.red >> 3) |
- ((close_rgb.green & 0xF8) << 2) |
- ((close_rgb.blue & 0xF8) << 7);
- }
-
- /* cache value and return closest colour index & rgb info */
-
- cache[hash] = close_rgb;
-
- return(&close_rgb);
- }
-
- static pix_str *closest_rgb_core(pix_str *rgb, uint *palette, int cols)
- {
- REGISTER int i;
- REGISTER int r, g, b, r2, g2, b2;
- REGISTER int rd, gd, bd;
- REGISTER int close_diff, diff;
- static pix_str close_rgb;
-
- close_diff = MAX_DIFF;
-
- r = rgb->red;
- g = rgb->green;
- b = rgb->blue;
-
- for(i=0; i<cols; i++)
- {
- /*
- * do palette lookup
- * assume all sprites now use full 8 bit palette
- * so no need to copy upper to lower nibbles
- */
-
- r2 = (palette[i] >> 8) & 0xFF;
- g2 = (palette[i] >> 16) & 0xFF;
- b2 = (palette[i] >> 24) & 0xFF;
-
- /* difference calculation */
-
- rd = r - r2;
- gd = g - g2;
- bd = b - b2;
-
- /*
- * diff^2 * weights
- * full 16 bit wieghts are to big, so shift by 3
- */
-
- /*
- diff = squares[512+rd] * (RED_WEIGHT >> WEIGHT_SHIFT) +
- squares[512+gd] * (GREEN_WEIGHT >> WEIGHT_SHIFT) +
- squares[512+bd] * (BLUE_WEIGHT >> WEIGHT_SHIFT);
- */
- #ifdef SQR_TABLE
- diff = squares[512+rd] * 3 +
- squares[512+gd] * 6 +
- squares[512+bd];
- #else
- diff = rd*rd * 3 +
- gd*gd * 6 +
- bd*bd;
- #endif
-
- /* check for better match */
-
- if(diff < close_diff)
- {
- close_diff = diff;
- close_rgb.value = i;
- close_rgb.red = r2;
- close_rgb.green = g2;
- close_rgb.blue = b2;
-
- /* short cut for extact match */
-
- if(diff == 0) return(&close_rgb);
- }
- }
- return(&close_rgb);
- }
-
- /*
- * general function for <=8 bits
- */
-
- static pix_str *closest_rgb_gen(spr_info_str *s, pix_str *rgb)
- {
- pix_str *close_rgb;
- int index;
-
- /* check for value in the cache */
-
- if((close_rgb = check_cache(s, rgb, &index))!=0)
- return(close_rgb);
-
- /* #### could use s->has_palette instead of s->colours */
-
- close_rgb = closest_rgb_core(rgb, s->palette, s->cols);
-
- /* cache value and return closest colour index & rgb info */
-
- cache[index] = *close_rgb;
-
- return(close_rgb);
- }
-
- /*
- * 24bpp - direct return
- */
-
- static pix_str *closest_rgb_24(spr_info_str *s, pix_str *rgb)
- {
- static pix_str close_rgb;
-
- RANGE_RGB(rgb->red);
- RANGE_RGB(rgb->green);
- RANGE_RGB(rgb->blue);
-
- close_rgb.red = rgb->red;
- close_rgb.green = rgb->green;
- close_rgb.blue = rgb->blue;
- close_rgb.value = (rgb->blue << 24) |
- (rgb->green << 16) |
- (rgb->red << 8);
-
- return(&close_rgb);
- }
-
- /*
- * 15bpp - finds best match from corners of 5x5x5 bit colour cube
- */
-
- static pix_str *closest_rgb_15(spr_info_str *s, pix_str *rgb)
- {
- uint palette[8];
- REGISTER int i;
- REGISTER int r,g,b;
- REGISTER int r2,g2,b2;
- REGISTER int rd,gd,bd;
- int index;
- pix_str *close_rgb;
-
- RANGE_RGB(rgb->red);
- RANGE_RGB(rgb->green);
- RANGE_RGB(rgb->blue);
-
- /* check for value in the cache */
-
- if((close_rgb = check_cache(s, rgb, &index))!=0)
- return(close_rgb);
-
- r = (rgb->red & 0xF8) | (rgb->red >> 5);
- g = (rgb->green & 0xF8) | (rgb->green >> 5);
- b = (rgb->blue & 0xF8) | (rgb->blue >> 5);
-
- if((rgb->red >= r) && (r < 255)) rd = 0x11; else rd=-0x11;
- if((rgb->green >= g) && (g < 255)) gd = 0x11; else gd=-0x11;
- if((rgb->blue >= b) && (b < 255)) bd = 0x11; else bd=-0x11;
-
- for(i=0; i<8; i++)
- {
- if((i & 1) > 0) r2 = r + rd; else r2 = r;
- if((i & 2) > 0) g2 = g + gd; else g2 = g;
- if((i & 4) > 0) b2 = b + bd; else b2 = b;
-
- palette[i] = (b2 << 24) | (g2 << 16) | (r2 << 8);
- }
-
- /* call core routine */
-
- close_rgb = closest_rgb_core(rgb, palette, 8);
-
- /* fix up value field */
-
- close_rgb->value = (close_rgb->red >> 3) |
- ((close_rgb->green & 0xF8) << 2) |
- ((close_rgb->blue & 0xF8) << 7);
-
- /* cache value and return closest colour index & rgb info */
-
- cache[index] = *close_rgb;
-
- return(close_rgb);
- }
-
- /*
- * 1 bpp black & white
- */
-
- static pix_str *closest_rgb_1(spr_info_str *s, pix_str *rgb)
- {
- static pix_str close_rgb;
- int grey,index;
- BOOL inverted = ((s->palette[0] & 0xFFFFFF00)!=0);
-
- grey = rgb->red * RED_WEIGHT +
- rgb->green * GREEN_WEIGHT +
- rgb->blue * BLUE_WEIGHT;
-
- #ifdef MULT
- index = (grey >= (0x80 * GREY_WEIGHT)) ? 1:0;
- #else
- index = (grey >= (0x80 << GREY_SHIFT)) ? 1:0;
- #endif
-
- close_rgb.value = inverted ? (1-index):index;
- close_rgb.red = index ? 255:0;
- close_rgb.green = close_rgb.red;
- close_rgb.blue = close_rgb.red;
-
- return(&close_rgb);
- }
-
- /*
- * arbitary grey palette
- */
-
- static pix_str *closest_rgb_grey(spr_info_str *s, pix_str *rgb)
- {
- REGISTER int i;
- REGISTER int grey, grey2;
- REGISTER int close_diff, diff;
- static pix_str close_rgb;
- pix_str rgb2;
- pix_str *cache_rgb;
- int index;
-
- /* check for value in the cache */
-
- grey = rgb->red * RED_WEIGHT +
- rgb->green * GREEN_WEIGHT +
- rgb->blue * BLUE_WEIGHT;
-
- rgb2.red = grey >> GREY_SHIFT;
- rgb2.green = rgb2.red;
- rgb2.blue = rgb2.red;
-
- if((cache_rgb = check_cache(s, &rgb2, &index))!=0)
- return(cache_rgb);
-
- close_diff = MAX_DIFF;
-
- for(i=0; i<s->cols; i++)
- {
- /* do palette lookup */
-
- grey2 = ((s->palette[i] >> 16) & 0xFF) * GREY_WEIGHT;
-
- /* difference calculation */
-
- diff = grey - grey2;
- if(diff<0) diff = -diff;
-
- /* check for better match */
-
- if(diff < close_diff)
- {
- close_diff = diff;
- close_rgb.value = i;
- close_rgb.red = (s->palette[i] >> 16) & 0xFF;
- close_rgb.green = close_rgb.red;
- close_rgb.blue = close_rgb.red;
-
- /* short cut for extact match */
-
- if(diff == 0) i=s->cols;
- }
- }
-
- /* cache value and return closest colour index & rgb info */
-
- cache[index] = close_rgb;
-
- return(&close_rgb);
- }
-
- /*
- * linear 8 bit grey or inverted linear 8 bit grey
- */
-
- static pix_str *closest_rgb_grey8(spr_info_str *s, pix_str *rgb)
- {
- static pix_str close_rgb;
- REGISTER int r,g,b;
- int index;
-
- r = rgb->red;
- g = rgb->green;
- b = rgb->blue;
-
- RANGE_RGB(r);
- RANGE_RGB(g);
- RANGE_RGB(b);
-
- index = (r * RED_WEIGHT +
- g * GREEN_WEIGHT +
- b * BLUE_WEIGHT) >> GREY_SHIFT;
-
- if((s->palette[0] & 0xFFFFFF00) != 0)
- index = 255-index;
-
- close_rgb.value = index;
- close_rgb.red = (s->palette[index] >> 16) & 0xFF;
- close_rgb.green = close_rgb.red;
- close_rgb.blue = close_rgb.red;
-
- return(&close_rgb);
- }
-
- /*
- * 8 bit standard desktop palette
- * finds best match from nearest +/- r,g,b and tint combinations
- */
-
- static pix_str *closest_rgb_desk8(spr_info_str *s, pix_str *rgb)
- {
- uint palette[3*3*3*4];
- uint value[3*3*3*4];
- int index;
- REGISTER int cols = 0;
- REGISTER int r,g,b;
- REGISTER int r2,g2,b2,t;
- pix_str rgb2 = *rgb;
- pix_str *close_rgb;
-
- /* check for value in the cache */
-
- RANGE_RGB(rgb2.red);
- RANGE_RGB(rgb2.green);
- RANGE_RGB(rgb2.blue);
-
- if((close_rgb = check_cache(s, &rgb2, &index))!=0)
- return(close_rgb);
-
- r = rgb2.red & 0xC0;
- g = rgb2.green & 0xC0;
- b = rgb2.blue & 0xC0;
-
- for(b2=b-0x40; b2<=b+0x40; b2+=0x40)
- {
- if(b2>=0 && b2<0x100)
- for(g2=g-0x40; g2<=g+0x40; g2+=0x40)
- {
- if(g2>=0 && g2<0x100)
- for(r2=r-0x40; r2<=r+0x40; r2+=0x40)
- {
- if(r2>=0 && r2<0x100)
- for(t=0; t<4; t++)
- {
- palette[cols] = ((b2+(t<<4)) << 24) |
- ((g2+(t<<4)) << 16) |
- ((r2+(t<<4)) << 8);
- palette[cols] |= (palette[cols] >> 4);
- value[cols++] = (b2 & 0x80) |
- ((g2 >> 1) & 0x60) |
- ((r2 >> 3) & 0x10) |
- ((b2 >> 3) & 0x08) |
- ((r2 >> 4) & 0x04) |
- t;;
- }
- }
- }
- }
-
- /* call core routine */
-
- close_rgb = closest_rgb_core(rgb, palette, cols);
-
- /* map returned value to calculated colour number */
-
- close_rgb->value = value[close_rgb->value];
-
- /* cache value and return closest colour index & rgb info */
-
- cache[index] = *close_rgb;
-
- return(close_rgb);
- }
-
- /*
- * palette that fully span colour cube
- */
-
- static pix_str *closest_rgb_cube(spr_info_str *s, pix_str *rgb)
- {
- uint palette[256];
- uint value[256];
- int index;
- REGISTER int i;
- REGISTER int cols = 0;
- REGISTER int r,g,b;
- pix_str rgb2 = *rgb;
- pix_str *close_rgb;
-
- /* check for value in the cache */
-
- RANGE_RGB(rgb2.red);
- RANGE_RGB(rgb2.green);
- RANGE_RGB(rgb2.blue);
-
- if((close_rgb = check_cache(s, &rgb2, &index))!=0)
- return(close_rgb);
-
- for(i=0; i<s->cols; i++)
- {
- SPLIT_RGB(s->palette[i]);
-
- if((r-rgb2.red)<0x80 && (r-rgb2.red)>-0x80 &&
- (g-rgb2.green)<0x80 && (g-rgb2.green)>-0x80 &&
- (b-rgb2.blue)<0x80 && (b-rgb2.blue)>-0x80)
- {
- palette[cols] = s->palette[i];
- value[cols++] = i;
- }
- }
-
- /* call core routine */
-
- close_rgb = closest_rgb_core(rgb, palette, cols);
-
- /* map returned value to calculated colour number */
-
- close_rgb->value = value[close_rgb->value];
-
- /* cache value and return closest colour index & rgb info */
-
- cache[index] = *close_rgb;
-
- return(close_rgb);
- }
-
- /*
- * work out palette type and select best closest_rgb function
- */
-
- void closest_rgb_func(spr_info_str *s)
- {
- BOOL grey, grey8, igrey8;
- uint rgb, r,g,b;
- uint span;
- int i;
-
- if(s->bpp == 24)
- {
- s->closest_rgb = closest_rgb_24;
- return;
- }
- if(s->bpp == 15)
- {
- s->closest_rgb = closest_rgb_15;
- return;
- }
-
- if(s->has_palette == 0)
- {
- fprintf(stderr,"sprite must have a palette for closest_rgb\n");
- exit(1);
- }
-
- if(s->bpp==1 && (((s->palette[0] & 0xFFFFFF00)==0x00000000 &&
- (s->palette[1] & 0xFFFFFF00)==0xFFFFFF00) ||
- ((s->palette[0] & 0xFFFFFF00)==0xFFFFFF00 &&
- (s->palette[1] & 0xFFFFFF00)==0x00000000)))
- {
- s->closest_rgb = closest_rgb_1;
- return;
- }
-
- grey = grey8 = igrey8 = TRUE;
- span = 0;
-
- for(i=0; i<s->cols; i++)
- {
- rgb = s->palette[i] & 0xFFFFFF00;
-
- if(grey)
- {
- SPLIT_RGB(rgb);
-
- if(r!=g || g!=b)
- {
- grey = FALSE;
- grey8 = FALSE;
- igrey8 = FALSE;
- }
- else
- {
- if(r != i) grey8 = FALSE;
- if(r != (255-i)) igrey8 = FALSE;
- }
- }
-
- if(rgb == 0x00000000) span |= 0x01;
- if(rgb == 0x0000FF00) span |= 0x02;
- if(rgb == 0x00FF0000) span |= 0x04;
- if(rgb == 0x00FFFF00) span |= 0x08;
- if(rgb == 0xFF000000) span |= 0x10;
- if(rgb == 0xFF00FF00) span |= 0x20;
- if(rgb == 0xFFFF0000) span |= 0x40;
- if(rgb == 0xFFFFFF00) span |= 0x80;
- }
-
- if(grey8 || igrey8)
- {
- s->closest_rgb = closest_rgb_grey8;
- return;
- }
- if(grey)
- {
- s->closest_rgb = closest_rgb_grey;
- return;
- }
-
- if(s->bpp == 8)
- {
- uint palette[256];
- palette256(palette);
- if(memcmp(s->palette, palette, 4*256)==0)
- {
- s->closest_rgb = closest_rgb_desk8;
- return;
- }
- }
-
- if(span == 0xFF)
- {
- s->closest_rgb = closest_rgb_cube;
- return;
- }
-
- /* use general routine */
-
- s->closest_rgb = closest_rgb_gen;
- }
-