123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- /*
- Jonathan Dummer
- image helper functions
- MIT license
- */
- #include "image_helper.h"
- #include <stdlib.h>
- #include <math.h>
- /* Upscaling the image uses simple bilinear interpolation */
- int
- up_scale_image
- (
- const unsigned char* const orig,
- int width, int height, int channels,
- unsigned char* resampled,
- int resampled_width, int resampled_height
- )
- {
- float dx, dy;
- int x, y, c;
- /* error(s) check */
- if ( (width < 1) || (height < 1) ||
- (resampled_width < 2) || (resampled_height < 2) ||
- (channels < 1) ||
- (NULL == orig) || (NULL == resampled) )
- {
- /* signify badness */
- return 0;
- }
- /*
- for each given pixel in the new map, find the exact location
- from the original map which would contribute to this guy
- */
- dx = (width - 1.0f) / (resampled_width - 1.0f);
- dy = (height - 1.0f) / (resampled_height - 1.0f);
- for ( y = 0; y < resampled_height; ++y )
- {
- /* find the base y index and fractional offset from that */
- float sampley = y * dy;
- int inty = (int)sampley;
- /* if( inty < 0 ) { inty = 0; } else */
- if( inty > height - 2 ) { inty = height - 2; }
- sampley -= inty;
- for ( x = 0; x < resampled_width; ++x )
- {
- float samplex = x * dx;
- int intx = (int)samplex;
- int base_index;
- /* find the base x index and fractional offset from that */
- /* if( intx < 0 ) { intx = 0; } else */
- if( intx > width - 2 ) { intx = width - 2; }
- samplex -= intx;
- /* base index into the original image */
- base_index = (inty * width + intx) * channels;
- for ( c = 0; c < channels; ++c )
- {
- /* do the sampling */
- float value = 0.5f;
- value += orig[base_index]
- *(1.0f-samplex)*(1.0f-sampley);
- value += orig[base_index+channels]
- *(samplex)*(1.0f-sampley);
- value += orig[base_index+width*channels]
- *(1.0f-samplex)*(sampley);
- value += orig[base_index+width*channels+channels]
- *(samplex)*(sampley);
- /* move to the next channel */
- ++base_index;
- /* save the new value */
- resampled[y*resampled_width*channels+x*channels+c] =
- (unsigned char)(value);
- }
- }
- }
- /* done */
- return 1;
- }
- int
- mipmap_image
- (
- const unsigned char* const orig,
- int width, int height, int channels,
- unsigned char* resampled,
- int block_size_x, int block_size_y
- )
- {
- int mip_width, mip_height;
- int i, j, c;
- /* error check */
- if( (width < 1) || (height < 1) ||
- (channels < 1) || (orig == NULL) ||
- (resampled == NULL) ||
- (block_size_x < 1) || (block_size_y < 1) )
- {
- /* nothing to do */
- return 0;
- }
- mip_width = width / block_size_x;
- mip_height = height / block_size_y;
- if( mip_width < 1 )
- {
- mip_width = 1;
- }
- if( mip_height < 1 )
- {
- mip_height = 1;
- }
- for( j = 0; j < mip_height; ++j )
- {
- for( i = 0; i < mip_width; ++i )
- {
- for( c = 0; c < channels; ++c )
- {
- const int index = (j*block_size_y)*width*channels + (i*block_size_x)*channels + c;
- int sum_value;
- int u,v;
- int u_block = block_size_x;
- int v_block = block_size_y;
- int block_area;
- /* do a bit of checking so we don't over-run the boundaries
- (necessary for non-square textures!) */
- if( block_size_x * (i+1) > width )
- {
- u_block = width - i*block_size_y;
- }
- if( block_size_y * (j+1) > height )
- {
- v_block = height - j*block_size_y;
- }
- block_area = u_block*v_block;
- /* for this pixel, see what the average
- of all the values in the block are.
- note: start the sum at the rounding value, not at 0 */
- sum_value = block_area >> 1;
- for( v = 0; v < v_block; ++v )
- for( u = 0; u < u_block; ++u )
- {
- sum_value += orig[index + v*width*channels + u*channels];
- }
- resampled[j*mip_width*channels + i*channels + c] = sum_value / block_area;
- }
- }
- }
- return 1;
- }
- int
- scale_image_RGB_to_NTSC_safe
- (
- unsigned char* orig,
- int width, int height, int channels
- )
- {
- const float scale_lo = 16.0f - 0.499f;
- const float scale_hi = 235.0f + 0.499f;
- int i, j;
- int nc = channels;
- unsigned char scale_LUT[256];
- /* error check */
- if( (width < 1) || (height < 1) ||
- (channels < 1) || (orig == NULL) )
- {
- /* nothing to do */
- return 0;
- }
- /* set up the scaling Look Up Table */
- for( i = 0; i < 256; ++i )
- {
- scale_LUT[i] = (unsigned char)((scale_hi - scale_lo) * i / 255.0f + scale_lo);
- }
- /* for channels = 2 or 4, ignore the alpha component */
- nc -= 1 - (channels & 1);
- /* OK, go through the image and scale any non-alpha components */
- for( i = 0; i < width*height*channels; i += channels )
- {
- for( j = 0; j < nc; ++j )
- {
- orig[i+j] = scale_LUT[orig[i+j]];
- }
- }
- return 1;
- }
- unsigned char clamp_byte( int x ) { return ( (x) < 0 ? (0) : ( (x) > 255 ? 255 : (x) ) ); }
- /*
- This function takes the RGB components of the image
- and converts them into YCoCg. 3 components will be
- re-ordered to CoYCg (for optimum DXT1 compression),
- while 4 components will be ordered CoCgAY (for DXT5
- compression).
- */
- int
- convert_RGB_to_YCoCg
- (
- unsigned char* orig,
- int width, int height, int channels
- )
- {
- int i;
- /* error check */
- if( (width < 1) || (height < 1) ||
- (channels < 3) || (channels > 4) ||
- (orig == NULL) )
- {
- /* nothing to do */
- return -1;
- }
- /* do the conversion */
- if( channels == 3 )
- {
- for( i = 0; i < width*height*3; i += 3 )
- {
- int r = orig[i+0];
- int g = (orig[i+1] + 1) >> 1;
- int b = orig[i+2];
- int tmp = (2 + r + b) >> 2;
- /* Co */
- orig[i+0] = clamp_byte( 128 + ((r - b + 1) >> 1) );
- /* Y */
- orig[i+1] = clamp_byte( g + tmp );
- /* Cg */
- orig[i+2] = clamp_byte( 128 + g - tmp );
- }
- } else
- {
- for( i = 0; i < width*height*4; i += 4 )
- {
- int r = orig[i+0];
- int g = (orig[i+1] + 1) >> 1;
- int b = orig[i+2];
- unsigned char a = orig[i+3];
- int tmp = (2 + r + b) >> 2;
- /* Co */
- orig[i+0] = clamp_byte( 128 + ((r - b + 1) >> 1) );
- /* Cg */
- orig[i+1] = clamp_byte( 128 + g - tmp );
- /* Alpha */
- orig[i+2] = a;
- /* Y */
- orig[i+3] = clamp_byte( g + tmp );
- }
- }
- /* done */
- return 0;
- }
- /*
- This function takes the YCoCg components of the image
- and converts them into RGB. See above.
- */
- int
- convert_YCoCg_to_RGB
- (
- unsigned char* orig,
- int width, int height, int channels
- )
- {
- int i;
- /* error check */
- if( (width < 1) || (height < 1) ||
- (channels < 3) || (channels > 4) ||
- (orig == NULL) )
- {
- /* nothing to do */
- return -1;
- }
- /* do the conversion */
- if( channels == 3 )
- {
- for( i = 0; i < width*height*3; i += 3 )
- {
- int co = orig[i+0] - 128;
- int y = orig[i+1];
- int cg = orig[i+2] - 128;
- /* R */
- orig[i+0] = clamp_byte( y + co - cg );
- /* G */
- orig[i+1] = clamp_byte( y + cg );
- /* B */
- orig[i+2] = clamp_byte( y - co - cg );
- }
- } else
- {
- for( i = 0; i < width*height*4; i += 4 )
- {
- int co = orig[i+0] - 128;
- int cg = orig[i+1] - 128;
- unsigned char a = orig[i+2];
- int y = orig[i+3];
- /* R */
- orig[i+0] = clamp_byte( y + co - cg );
- /* G */
- orig[i+1] = clamp_byte( y + cg );
- /* B */
- orig[i+2] = clamp_byte( y - co - cg );
- /* A */
- orig[i+3] = a;
- }
- }
- /* done */
- return 0;
- }
- float
- find_max_RGBE
- (
- unsigned char *image,
- int width, int height
- )
- {
- float max_val = 0.0f;
- unsigned char *img = image;
- int i, j;
- for( i = width * height; i > 0; --i )
- {
- /* float scale = powf( 2.0f, img[3] - 128.0f ) / 255.0f; */
- float scale = ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 );
- for( j = 0; j < 3; ++j )
- {
- if( img[j] * scale > max_val )
- {
- max_val = img[j] * scale;
- }
- }
- /* next pixel */
- img += 4;
- }
- return max_val;
- }
- int
- RGBE_to_RGBdivA
- (
- unsigned char *image,
- int width, int height,
- int rescale_to_max
- )
- {
- /* local variables */
- int i, iv;
- unsigned char *img = image;
- float scale = 1.0f;
- /* error check */
- if( (!image) || (width < 1) || (height < 1) )
- {
- return 0;
- }
- /* convert (note: no negative numbers, but 0.0 is possible) */
- if( rescale_to_max )
- {
- scale = 255.0f / find_max_RGBE( image, width, height );
- }
- for( i = width * height; i > 0; --i )
- {
- /* decode this pixel, and find the max */
- float r,g,b,e, m;
- /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */
- e = scale * ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 );
- r = e * img[0];
- g = e * img[1];
- b = e * img[2];
- m = (r > g) ? r : g;
- m = (b > m) ? b : m;
- /* and encode it into RGBdivA */
- iv = (m != 0.0f) ? (int)(255.0f / m) : 1.0f;
- iv = (iv < 1) ? 1 : iv;
- img[3] = (iv > 255) ? 255 : iv;
- iv = (int)(img[3] * r + 0.5f);
- img[0] = (iv > 255) ? 255 : iv;
- iv = (int)(img[3] * g + 0.5f);
- img[1] = (iv > 255) ? 255 : iv;
- iv = (int)(img[3] * b + 0.5f);
- img[2] = (iv > 255) ? 255 : iv;
- /* and on to the next pixel */
- img += 4;
- }
- return 1;
- }
- int
- RGBE_to_RGBdivA2
- (
- unsigned char *image,
- int width, int height,
- int rescale_to_max
- )
- {
- /* local variables */
- int i, iv;
- unsigned char *img = image;
- float scale = 1.0f;
- /* error check */
- if( (!image) || (width < 1) || (height < 1) )
- {
- return 0;
- }
- /* convert (note: no negative numbers, but 0.0 is possible) */
- if( rescale_to_max )
- {
- scale = 255.0f * 255.0f / find_max_RGBE( image, width, height );
- }
- for( i = width * height; i > 0; --i )
- {
- /* decode this pixel, and find the max */
- float r,g,b,e, m;
- /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */
- e = scale * ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 );
- r = e * img[0];
- g = e * img[1];
- b = e * img[2];
- m = (r > g) ? r : g;
- m = (b > m) ? b : m;
- /* and encode it into RGBdivA */
- iv = (m != 0.0f) ? (int)sqrtf( 255.0f * 255.0f / m ) : 1.0f;
- iv = (iv < 1) ? 1 : iv;
- img[3] = (iv > 255) ? 255 : iv;
- iv = (int)(img[3] * img[3] * r / 255.0f + 0.5f);
- img[0] = (iv > 255) ? 255 : iv;
- iv = (int)(img[3] * img[3] * g / 255.0f + 0.5f);
- img[1] = (iv > 255) ? 255 : iv;
- iv = (int)(img[3] * img[3] * b / 255.0f + 0.5f);
- img[2] = (iv > 255) ? 255 : iv;
- /* and on to the next pixel */
- img += 4;
- }
- return 1;
- }
|