osgEarth 2.1.1
|
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) |
Definition at line 44 of file ImageUtils.
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; }
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;
}
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); }
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; }
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; }
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 ); }
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 ); }
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; }
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; }
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(); }
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.
src_minx | The minimum x coordinate of the input image. |
src_miny | The minimum y coordinate of the input image. |
src_maxx | The maximum x coordinate of the input image. |
src_maxy | The maximum y coordinate of the input image. |
dst_minx | The desired minimum x coordinate of the cropped image. |
dst_miny | The desired minimum y coordinate of the cropped image. |
dst_maxx | The desired maximum x coordinate of the cropped image. |
dst_maxy | The 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; }
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 );
}
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; } }
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));
}
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; }
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 ); } }
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; }
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; }