forked from dolphin-emu/dolphin
		
	enable newline normalization get revision number via `hg svn info` for svnrev.h ignore incremental/generated binary files (windows/VS at least) leave a comment if some files need native eol set in svnprops git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5637 8ced0084-cf51-0410-be5f-012b33b47a6e
		
			
				
	
	
		
			436 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			436 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
    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;
 | 
						|
}
 |