Subversion Repositories gelsvn

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
    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;
}