osgEarth 2.1.1
Classes | Static Public Member Functions

osgEarth::ImageUtils Class Reference

List of all members.

Classes

struct  CopyImage
class  PixelReader
struct  PixelVisitor
class  PixelWriter

Static Public Member Functions

static osg::Image * cloneImage (const osg::Image *image)
static void normalizeImage (osg::Image *image)
static bool copyAsSubImage (const osg::Image *src, osg::Image *dst, int dst_start_col, int dst_start_row, int dst_start_img=0)
static bool resizeImage (const osg::Image *input, unsigned int new_s, unsigned int new_t, osg::ref_ptr< osg::Image > &output, unsigned int mipmapLevel=0)
static osg::Image * cropImage (const osg::Image *image, double src_minx, double src_miny, double src_maxx, double src_maxy, double &dst_minx, double &dst_miny, double &dst_maxx, double &dst_maxy)
static osg::Image * createMipmapBlendedImage (const osg::Image *primary, const osg::Image *secondary)
static bool mix (osg::Image *dest, const osg::Image *src, float a)
static osg::Image * sharpenImage (const osg::Image *image)
static bool isPowerOfTwo (const osg::Image *image)
static osg::Image * createEmptyImage ()
static bool canConvert (const osg::Image *image, GLenum pixelFormat, GLenum dataType)
static osg::Image * convert (const osg::Image *image, GLenum pixelFormat, GLenum dataType)
static osg::Image * convertToRGB8 (const osg::Image *image)
static osg::Image * convertToRGBA8 (const osg::Image *image)
static bool areEquivalent (const osg::Image *lhs, const osg::Image *rhs)
static bool areRGBEquivalent (const osg::Vec4 &lhs, const osg::Vec4 &rhs, float epsilon=0.01f)
static bool hasAlphaChannel (const osg::Image *image)
static bool isCompressed (const osg::Image *image)

Detailed Description

Definition at line 44 of file ImageUtils.


Member Function Documentation

bool ImageUtils::areEquivalent ( const osg::Image *  lhs,
const osg::Image *  rhs 
) [static]

Compares the image data of two images and determines if they are equivalent

Definition at line 441 of file ImageUtils.cpp.

{
        if (lhs == rhs) return true;

        if ((lhs->s() == rhs->s()) &&
                (lhs->t() == rhs->t()) &&
                (lhs->getInternalTextureFormat() == rhs->getInternalTextureFormat()) &&
                (lhs->getPixelFormat() == rhs->getPixelFormat()) &&
                (lhs->getDataType() == rhs->getDataType()) &&
                (lhs->getPacking() == rhs->getPacking()) &&
                (lhs->getImageSizeInBytes() == rhs->getImageSizeInBytes()))
        {
                unsigned int size = lhs->getImageSizeInBytes();
        const unsigned char* ptr1 = lhs->data();
        const unsigned char* ptr2 = rhs->data();
                for (unsigned int i = 0; i < size; ++i)
                {
            if ( *ptr1++ != *ptr2++ )
                return false;
                }

        return true;
        }

        return false;
}

Here is the caller graph for this function:

static bool osgEarth::ImageUtils::areRGBEquivalent ( const osg::Vec4 &  lhs,
const osg::Vec4 &  rhs,
float  epsilon = 0.01f 
) [inline, static]

Whether two colors are roughly equivalent.

Definition at line 182 of file ImageUtils.

                                                                                                     {
            return
                fabs(lhs.r() - rhs.r()) < epsilon &&
                fabs(lhs.g() - rhs.g()) < epsilon &&
                fabs(lhs.b() - rhs.b()) < epsilon;
        }

Here is the caller graph for this function:

bool ImageUtils::canConvert ( const osg::Image *  image,
GLenum  pixelFormat,
GLenum  dataType 
) [static]

Returns true if it is possible to convert the image to the specified format/datatype specification.

Definition at line 390 of file ImageUtils.cpp.

{
    if ( !image ) return false;
    return PixelReader::supports( image ) && PixelWriter::supports(pixelFormat, dataType);
}

Here is the call graph for this function:

Here is the caller graph for this function:

osg::Image * ImageUtils::cloneImage ( const osg::Image *  image) [static]

Clones an image.

Use this instead of the osg::Image copy construtor, which keeps the referenced to its underlying BufferObject around. Calling dirty() on the new clone appears to help, but just call this method instead to be sure.

Definition at line 34 of file ImageUtils.cpp.

{
    // Why not just call image->clone()? Because, the osg::Image copy constructor does not
    // clear out the underlying BufferData/BufferObject's GL handles. This can cause 
    // exepected results if you are cloning an image that has already been used in GL.
    // Calling clone->dirty() might work, but we are not sure.

    if ( !input ) return 0L;
    
    osg::Image* clone = osg::clone( input, osg::CopyOp::DEEP_COPY_ALL );
    clone->dirty();
    return clone;
}

Here is the caller graph for this function:

osg::Image * ImageUtils::convert ( const osg::Image *  image,
GLenum  pixelFormat,
GLenum  dataType 
) [static]

Converts an image to the specified format.

Definition at line 397 of file ImageUtils.cpp.

{
    if ( !image )
        return 0L;

    if ( image->getPixelFormat() == pixelFormat && image->getDataType() == dataType)
    {
        GLenum texFormat = image->getInternalTextureFormat();
        if (dataType != GL_UNSIGNED_BYTE
            || (pixelFormat == GL_RGB && texFormat == GL_RGB8)
            || (pixelFormat == GL_RGBA && texFormat == GL_RGBA8))
        return cloneImage(image);
    }
    if ( !canConvert(image, pixelFormat, dataType) )
        return 0L;

    osg::Image* result = new osg::Image();
    result->allocateImage(image->s(), image->t(), image->r(), pixelFormat, dataType);

    if ( pixelFormat == GL_RGB && dataType == GL_UNSIGNED_BYTE )
        result->setInternalTextureFormat( GL_RGB8 );
    else if ( pixelFormat == GL_RGBA && dataType == GL_UNSIGNED_BYTE )
        result->setInternalTextureFormat( GL_RGBA8 );
    else
        result->setInternalTextureFormat( pixelFormat );

    PixelVisitor<CopyImage>().accept( image, result );

    return result;
}

Here is the call graph for this function:

Here is the caller graph for this function:

osg::Image * ImageUtils::convertToRGB8 ( const osg::Image *  image) [static]

Converts the given image to RGB8

Definition at line 429 of file ImageUtils.cpp.

{
    return convert( image, GL_RGB, GL_UNSIGNED_BYTE );
}

Here is the call graph for this function:

Here is the caller graph for this function:

osg::Image * ImageUtils::convertToRGBA8 ( const osg::Image *  image) [static]

Converts the given image to RGBA8

Definition at line 435 of file ImageUtils.cpp.

{
    return convert( image, GL_RGBA, GL_UNSIGNED_BYTE );
}

Here is the call graph for this function:

Here is the caller graph for this function:

bool ImageUtils::copyAsSubImage ( const osg::Image *  src,
osg::Image *  dst,
int  dst_start_col,
int  dst_start_row,
int  dst_start_img = 0 
) [static]

Copys a portion of one image into another.

Definition at line 64 of file ImageUtils.cpp.

{
    if (!src || !dst ||
        dst_start_col + src->s() > dst->s() ||
        dst_start_row + src->t() > dst->t() )
    {
        return false;
    }

    // check for fast bytewise copy:
    if (src->getPacking() == dst->getPacking() &&
        src->getDataType() == dst->getDataType() &&
        src->getPixelFormat() == dst->getPixelFormat() &&
        src->getInternalTextureFormat() == dst->getInternalTextureFormat() )
    {
        for( int src_row=0, dst_row=dst_start_row; src_row < src->t(); src_row++, dst_row++ )
        {
            const void* src_data = src->data( 0, src_row, 0 );
            void* dst_data = dst->data( dst_start_col, dst_row, dst_img );
            memcpy( dst_data, src_data, src->getRowSizeInBytes() );
        }
    }

    // otherwise loop through an convert pixel-by-pixel.
    else
    {
        PixelReader read(src);
        PixelWriter write(dst);

        for( int src_t=0, dst_t=dst_start_row; src_t < src->t(); src_t++, dst_t++ )
        {
            for( int src_s=0, dst_s=dst_start_col; src_s < src->s(); src_s++, dst_s++ )
            {           
                write( read(src_s, src_t), dst_s, dst_t );
            }
        }
    }

    return true;
}  

Here is the caller graph for this function:

osg::Image * ImageUtils::createEmptyImage ( ) [static]

Gets a transparent, single pixel image used for a placeholder

Definition at line 377 of file ImageUtils.cpp.

{
    //TODO: Make this a static or store it in the registry to avoid creating it
    // each time.
    osg::Image* image = new osg::Image;
    image->allocateImage(1,1,1, GL_RGBA, GL_UNSIGNED_BYTE);
    image->setInternalTextureFormat( GL_RGBA8 );
    unsigned char *data = image->data(0,0);
    memset(data, 0, 4);
    return image;
}

Here is the caller graph for this function:

osg::Image * ImageUtils::createMipmapBlendedImage ( const osg::Image *  primary,
const osg::Image *  secondary 
) [static]

Creates an Image that "blends" two images into a new image in which "primary" occupies mipmap level 0, and "secondary" occupies all the other mipmap levels.

WARNING: this method assumes that primary and seconday are the same exact size and the same exact format.

Definition at line 190 of file ImageUtils.cpp.

{
    // ASSUMPTION: primary and secondary are the same size, same format.

    // first, build the image that will hold all the mipmap levels.
    int numMipmapLevels = osg::Image::computeNumberOfMipmapLevels( primary->s(), primary->t() );
    int pixelSizeBytes  = osg::Image::computeRowWidthInBytes( primary->s(), primary->getPixelFormat(), primary->getDataType(), primary->getPacking() ) / primary->s();
    int totalSizeBytes  = 0;
    std::vector< unsigned int > mipmapDataOffsets;

    mipmapDataOffsets.reserve( numMipmapLevels-1 );

    for( int i=0; i<numMipmapLevels; ++i )
    {
        if ( i > 0 )
            mipmapDataOffsets.push_back( totalSizeBytes );

        int level_s = primary->s() >> i;
        int level_t = primary->t() >> i;
        int levelSizeBytes = level_s * level_t * pixelSizeBytes;

        totalSizeBytes += levelSizeBytes;
    }

    unsigned char* data = new unsigned char[totalSizeBytes];

    osg::ref_ptr<osg::Image> result = new osg::Image();
    result->setImage(
        primary->s(), primary->t(), 1,
        primary->getInternalTextureFormat(), 
        primary->getPixelFormat(), 
        primary->getDataType(), 
        data, osg::Image::USE_NEW_DELETE );

    result->setMipmapLevels( mipmapDataOffsets );

    // now, populate the image levels.
    int level_s = primary->s();
    int level_t = primary->t();

    for( int level=0; level<numMipmapLevels; ++level )
    {
        if ( secondary && level > 0 )
            ImageUtils::resizeImage( secondary, level_s, level_t, result, level );
        else
            ImageUtils::resizeImage( primary, level_s, level_t, result, level );

        level_s >>= 1;
        level_t >>= 1;
    }

    return result.release();
}

Here is the call graph for this function:

osg::Image * ImageUtils::cropImage ( const osg::Image *  image,
double  src_minx,
double  src_miny,
double  src_maxx,
double  src_maxy,
double &  dst_minx,
double &  dst_miny,
double &  dst_maxx,
double &  dst_maxy 
) [static]

Crops the input image to the dimensions provided and returns a new image. Returns a new image, leaving the input image unaltered. Note: The input destination bounds are modified to reflect the bounds of the actual output image. Due to the fact that you cannot crop in the middle of a pixel The specified destination extents and the output extents may vary slightly.

Parameters:
src_minxThe minimum x coordinate of the input image.
src_minyThe minimum y coordinate of the input image.
src_maxxThe maximum x coordinate of the input image.
src_maxyThe maximum y coordinate of the input image.
dst_minxThe desired minimum x coordinate of the cropped image.
dst_minyThe desired minimum y coordinate of the cropped image.
dst_maxxThe desired maximum x coordinate of the cropped image.
dst_maxyThe desired maximum y coordinate of the cropped image.

Definition at line 282 of file ImageUtils.cpp.

{
    //Compute the desired cropping rectangle
    int windowX        = osg::clampBetween( (int)floor( (dst_minx - src_minx) / (src_maxx - src_minx) * (double)image->s()), 0, image->s()-1);
    int windowY        = osg::clampBetween( (int)floor( (dst_miny - src_miny) / (src_maxy - src_miny) * (double)image->t()), 0, image->t()-1);
    int windowWidth    = osg::clampBetween( (int)ceil(  (dst_maxx - src_minx) / (src_maxx - src_minx) * (double)image->s()) - windowX, 0, image->s());
    int windowHeight   = osg::clampBetween( (int)ceil(  (dst_maxy - src_miny) / (src_maxy - src_miny) * (double)image->t()) - windowY, 0, image->t());    

    if (windowX + windowWidth > image->s())
    {
        windowWidth = image->s() - windowX;        
    }

    if (windowY + windowHeight > image->t())
    {
        windowHeight = image->t() - windowY;
    }
    
    if ((windowWidth * windowHeight) == 0)
    {
        return NULL;
    }

    //Compute the actual bounds of the area we are computing
    double res_s = (src_maxx - src_minx) / (double)image->s();
    double res_t = (src_maxy - src_miny) / (double)image->t();

    dst_minx = src_minx + (double)windowX * res_s;
    dst_miny = src_miny + (double)windowY * res_t;
    dst_maxx = dst_minx + (double)windowWidth * res_s;
    dst_maxy = dst_miny + (double)windowHeight * res_t;

    //OE_NOTICE << "Copying from " << windowX << ", " << windowY << ", " << windowWidth << ", " << windowHeight << std::endl;

    //Allocate the croppped image
    osg::Image* cropped = new osg::Image;
    cropped->allocateImage(windowWidth, windowHeight, 1, image->getPixelFormat(), image->getDataType());
    cropped->setInternalTextureFormat( image->getInternalTextureFormat() );
    
    
    for (int src_row = windowY, dst_row=0; dst_row < windowHeight; src_row++, dst_row++)
    {
        if (src_row > image->t()-1) OE_NOTICE << "HeightBroke" << std::endl;
        const void* src_data = image->data(windowX, src_row, 0);
        void* dst_data = cropped->data(0, dst_row, 0);
        memcpy( dst_data, src_data, cropped->getRowSizeInBytes());
    }

    return cropped;
}

Here is the caller graph for this function:

bool ImageUtils::hasAlphaChannel ( const osg::Image *  image) [static]

Checks whether the image has an alpha component

Definition at line 469 of file ImageUtils.cpp.

{
    return image && (
        image->getPixelFormat() == GL_RGBA ||
        image->getPixelFormat() == GL_BGRA ||
        image->getPixelFormat() == GL_LUMINANCE_ALPHA );
}

Here is the caller graph for this function:

bool ImageUtils::isCompressed ( const osg::Image *  image) [static]

Checks whether the given image is compressed

Definition at line 478 of file ImageUtils.cpp.

{
    //Later versions of OSG have an Image::isCompressed function but earlier versions like 2.8.3 do not.  This is a workaround so that 
    //we can tell if an image is compressed on all versions of OSG.
    switch(image->getPixelFormat())
    {
        case(GL_COMPRESSED_ALPHA_ARB):
        case(GL_COMPRESSED_INTENSITY_ARB):
        case(GL_COMPRESSED_LUMINANCE_ALPHA_ARB):
        case(GL_COMPRESSED_LUMINANCE_ARB):
        case(GL_COMPRESSED_RGBA_ARB):
        case(GL_COMPRESSED_RGB_ARB):
        case(GL_COMPRESSED_RGB_S3TC_DXT1_EXT):
        case(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT):
        case(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT):
        case(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT):
        case(GL_COMPRESSED_SIGNED_RED_RGTC1_EXT):
        case(GL_COMPRESSED_RED_RGTC1_EXT):
        case(GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT):
        case(GL_COMPRESSED_RED_GREEN_RGTC2_EXT):
        case(GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG): 
        case(GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG):
        case(GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG):
        case(GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG):
            return true;
        default:
            return false;
    }
}

Here is the caller graph for this function:

bool ImageUtils::isPowerOfTwo ( const osg::Image *  image) [static]

Gets whether the input image's dimensions are powers of 2.

Definition at line 336 of file ImageUtils.cpp.

{
    return (((image->s() & (image->s()-1))==0) &&
            ((image->t() & (image->t()-1))==0));
}

Here is the caller graph for this function:

bool ImageUtils::mix ( osg::Image *  dest,
const osg::Image *  src,
float  a 
) [static]

Blends the "src" image into the "dest" image, based on the "a" value. The two images must be the same.

Definition at line 266 of file ImageUtils.cpp.

{
    if (!dest || !src || dest->s() != src->s() || dest->t() != src->t() )
        return false;
    
    PixelVisitor<MixImage> mixer;
    mixer._a = osg::clampBetween( a, 0.0f, 1.0f );
    mixer._srcHasAlpha = src->getPixelSizeInBits() == 32;
    mixer._destHasAlpha = src->getPixelSizeInBits() == 32;    

    mixer.accept( src, dest );  

    return true;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void ImageUtils::normalizeImage ( osg::Image *  image) [static]

Tweaks an image for consistency. OpenGL allows enums like "GL_RGBA" et.al. to be used in the internal texture format, when really "GL_RGBA8" is the proper things to use. This method accounts for that. Some parts of osgEarth (like the texture- array compositor) rely on the internal texture format being correct. (http://http.download.nvidia.com/developer/Papers/2005/Fast_Texture_Transfers/Fast_Texture_Transfers.pdf)

Definition at line 49 of file ImageUtils.cpp.

{
    // OpenGL is lax about internal texture formats, and e.g. allows GL_RGBA to be used
    // instead of the proper GL_RGBA8, etc. Correct that here, since some of our compositors
    // rely on having a proper internal texture format.
    if ( image->getDataType() == GL_UNSIGNED_BYTE )
    {
        if ( image->getPixelFormat() == GL_RGB )
            image->setInternalTextureFormat( GL_RGB8 );
        else if ( image->getPixelFormat() == GL_RGBA )
            image->setInternalTextureFormat( GL_RGBA8 );
    }
}

Here is the caller graph for this function:

bool ImageUtils::resizeImage ( const osg::Image *  input,
unsigned int  new_s,
unsigned int  new_t,
osg::ref_ptr< osg::Image > &  output,
unsigned int  mipmapLevel = 0 
) [static]

Resizes an image using nearest-neighbor resampling. Returns a new image, leaving the input image unaltered.

Note. If the output parameter is NULL, this method will allocate a new image and resize into that new image. If the output parameter is non-NULL, this method will assume that the output image is already allocated to the proper size, and will do a resize+copy into that image. In the latter case, it is your responsibility to make sure the output image is allocated to the proper size.

If the output parameter is non-NULL, then the mipmapLevel is also considered. This lets you resize directly into a particular mipmap level of the output image.

Definition at line 106 of file ImageUtils.cpp.

{
    if ( !input && out_s == 0 && out_t == 0 )
        return false;

    if ( !PixelReader::supports(input) )
    {
        OE_WARN << LC << "resizeImage: unsupported format" << std::endl;
        return false;
    }

    if ( output.valid() && !PixelWriter::supports(output.get()) )
    {
        OE_WARN << LC << "resizeImage: pre-allocated output image is in an unsupported format" << std::endl;
        return false;
    }

    unsigned int in_s = input->s();
    unsigned int in_t = input->t();

    if ( !output.valid() )
    {
        output = new osg::Image();

        if ( PixelWriter::supports(input) )
        {
            output->allocateImage( out_s, out_t, 1, input->getPixelFormat(), input->getDataType(), input->getPacking() );
            output->setInternalTextureFormat( input->getInternalTextureFormat() );
        }
        else
        {
            // for unsupported write formats, convert to RGBA8 automatically.
            output->allocateImage( out_s, out_t, 1, GL_RGBA, GL_UNSIGNED_BYTE );
            output->setInternalTextureFormat( GL_RGBA8 );
        }
    }
    else
    {
        // make sure they match up
        output->setInternalTextureFormat( input->getInternalTextureFormat() );
    }

    if ( in_s == out_s && in_t == out_t && mipmapLevel == 0 && input->getInternalTextureFormat() == output->getInternalTextureFormat() )
    {
        memcpy( output->data(), input->data(), input->getTotalSizeInBytes() );
    }
    else
    {       
        PixelReader read( input );
        PixelWriter write( output.get() );

        unsigned int pixel_size_bytes = input->getRowSizeInBytes() / in_s;

        unsigned char* dataOffset = output->getMipmapData(mipmapLevel);
        unsigned int   dataRowSizeBytes = output->getRowSizeInBytes() >> mipmapLevel;

        for( unsigned int output_row=0; output_row < out_t; output_row++ )
        {
            // get an appropriate input row
            float output_row_ratio = (float)output_row/(float)out_t;
            int input_row = (unsigned int)( output_row_ratio * (float)in_t );
            if ( input_row >= input->t() ) input_row = in_t-1;
            else if ( input_row < 0 ) input_row = 0;

            for( unsigned int output_col = 0; output_col < out_s; output_col++ )
            {
                float output_col_ratio = (float)output_col/(float)out_s;
                int input_col = (unsigned int)( output_col_ratio * (float)in_s );
                if ( input_col >= (int)in_s ) input_col = in_s-1;
                else if ( input_row < 0 ) input_row = 0;

                osg::Vec4 color = read( input_col, input_row ); // read pixel from mip level 0
                write( color, output_col, output_row, 0, mipmapLevel ); // write to target mip level
            }
        }
    }

    return true;
}

Here is the call graph for this function:

Here is the caller graph for this function:

osg::Image * ImageUtils::sharpenImage ( const osg::Image *  image) [static]

Creates and returns a copy of the input image after applying a sharpening filter. Returns a new image, leaving the input image unaltered.

Definition at line 344 of file ImageUtils.cpp.

{
    int filter[9] = { 0, -1, 0, -1, 5, -1, 0, -1, 0 };
    osg::Image* output = ImageUtils::cloneImage(input);
    for( int t=1; t<input->t()-1; t++ )
    {
        for( int s=1; s<input->s()-1; s++ )
        {
            int pixels[9] = {
                *(int*)input->data(s-1,t-1), *(int*)input->data(s,t-1), *(int*)input->data(s+1,t-1),
                *(int*)input->data(s-1,t  ), *(int*)input->data(s,t  ), *(int*)input->data(s+1,t  ),
                *(int*)input->data(s-1,t+1), *(int*)input->data(s,t+1), *(int*)input->data(s+1,t+1) };

            int shifts[4] = { 0, 8, 16, 32 };

            for( int c=0; c<4; c++ ) // components
            {
                int mask = 0xff << shifts[c];
                int sum = 0;
                for( int i=0; i<9; i++ )
                {
                    sum += ((pixels[i] & mask) >> shifts[c]) * filter[i];
                }
                sum = sum > 255? 255 : sum < 0? 0 : sum;
                output->data(s,t)[c] = sum;
            }
        }
    }
    return output;
}

Here is the call graph for this function:


The documentation for this class was generated from the following files:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines