729 lines
28 KiB
C++
729 lines
28 KiB
C++
/* -*-c++-*- */
|
|
/* osgEarth - Geospatial SDK for OpenSceneGraph
|
|
* Copyright 2020 Pelican Mapping
|
|
* http://osgearth.org
|
|
*
|
|
* osgEarth is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
*/
|
|
|
|
#ifndef OSGEARTH_IMAGEUTILS_H
|
|
#define OSGEARTH_IMAGEUTILS_H
|
|
|
|
#include <osgEarth/Common>
|
|
#include <osgEarth/Bounds>
|
|
#include <osgEarth/optional>
|
|
#include <osg/Image>
|
|
#include <osg/Texture>
|
|
#include <osg/Texture2DArray>
|
|
#include <osg/GL>
|
|
#include <osg/NodeVisitor>
|
|
#include <osgDB/ReaderWriter>
|
|
#include <vector>
|
|
#include <functional>
|
|
|
|
//These formats were not added to OSG until after 2.8.3 so we need to define them to use them.
|
|
#ifndef GL_EXT_texture_compression_rgtc
|
|
#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB
|
|
#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC
|
|
#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD
|
|
#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE
|
|
#endif
|
|
|
|
#ifndef GL_IMG_texture_compression_pvrtc
|
|
#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
|
|
#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
|
|
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
|
|
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
|
|
#endif
|
|
|
|
namespace osgEarth { namespace Util
|
|
{
|
|
class OSGEARTH_EXPORT ImageUtils
|
|
{
|
|
public:
|
|
/**
|
|
* 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.
|
|
*/
|
|
static osg::Image* cloneImage( const osg::Image* image );
|
|
|
|
/**
|
|
* Copys a portion of one image into another.
|
|
*/
|
|
static bool copyAsSubImage(
|
|
const osg::Image* src,
|
|
osg::Image* dst,
|
|
int dst_start_col, int dst_start_row);
|
|
|
|
/**
|
|
* Resizes an image. 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.
|
|
*/
|
|
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, bool bilinear=true );
|
|
|
|
/**
|
|
* 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.
|
|
*@param src_minx
|
|
* The minimum x coordinate of the input image.
|
|
*@param src_miny
|
|
* The minimum y coordinate of the input image.
|
|
*@param src_maxx
|
|
* The maximum x coordinate of the input image.
|
|
*@param src_maxy
|
|
* The maximum y coordinate of the input image.
|
|
*@param dst_minx
|
|
* The desired minimum x coordinate of the cropped image.
|
|
*@param dst_miny
|
|
* The desired minimum y coordinate of the cropped image.
|
|
*@param dst_maxx
|
|
* The desired maximum x coordinate of the cropped image.
|
|
*@param dst_maxy
|
|
* The desired maximum y coordinate of the cropped image.
|
|
*/
|
|
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);
|
|
|
|
/**
|
|
* Crops the input image using the input dimension
|
|
* Returns a new image, leaving the input image unaltered.
|
|
*@param x
|
|
* The cropping x coordinate of the input image.
|
|
*@param y
|
|
* The cropping y coordinate of the input image.
|
|
*@param width
|
|
* The width in pixels of the crop.
|
|
*@param height
|
|
* The height in pixels of the crop.
|
|
*/
|
|
static osg::Image* cropImage(osg::Image* image, unsigned int x, unsigned int y, unsigned int width, unsigned int height);
|
|
|
|
/**
|
|
* Blends the "src" image into the "dest" image, based on the "a" value.
|
|
* The two images must be the same.
|
|
*/
|
|
static bool mix( osg::Image* dest, const osg::Image* src, float a );
|
|
|
|
/**
|
|
* Creates and returns a copy of the input image after applying a
|
|
* sharpening filter. Returns a new image, leaving the input image unaltered.
|
|
*/
|
|
static osg::Image* createSharpenedImage( const osg::Image* image );
|
|
|
|
/**
|
|
* For each "layer" in the input image (each bitmap in the "r" dimension),
|
|
* create a new, separate image with r=1.
|
|
* Returns true upon sucess, false upon failure or if r < 2
|
|
*/
|
|
static bool flattenImage(
|
|
const osg::Image* image,
|
|
std::vector<osg::ref_ptr<osg::Image> >& output);
|
|
|
|
/**
|
|
* Gets whether the input image's dimensions are powers of 2.
|
|
*/
|
|
static bool isPowerOfTwo(const osg::Image* image);
|
|
|
|
/**
|
|
* Gets a transparent, single pixel image used for a placeholder
|
|
*/
|
|
static osg::Image* createEmptyImage();
|
|
|
|
/**
|
|
* Gets a transparent image used for a placeholder with the specified dimensions
|
|
*/
|
|
static osg::Image* createEmptyImage(unsigned int s, unsigned int t, unsigned int r = 1);
|
|
|
|
/**
|
|
* Creates a one-pixel image.
|
|
*/
|
|
static osg::Image* createOnePixelImage(const osg::Vec4& color);
|
|
|
|
/**
|
|
* Tests an image to see whether it's "empty", i.e. completely transparent,
|
|
* within an alpha threshold.
|
|
*/
|
|
static bool isEmptyImage(const osg::Image* image, float alphaThreshold =0.01);
|
|
|
|
/**
|
|
* Tests an image to see whether it's "single color", i.e. completely filled with a single color,
|
|
* within an threshold (threshold is tested on each channel).
|
|
*/
|
|
static bool isSingleColorImage(const osg::Image* image, float threshold =0.01);
|
|
|
|
/**
|
|
* Returns true if it is possible to convert the image to the specified
|
|
* format/datatype specification.
|
|
*/
|
|
static bool canConvert(const osg::Image* image, GLenum pixelFormat, GLenum dataType);
|
|
|
|
/**
|
|
* Converts an image to the specified format.
|
|
*/
|
|
static osg::Image* convert(const osg::Image* image, GLenum pixelFormat, GLenum dataType);
|
|
|
|
/**
|
|
*Converts the given image to RGB8
|
|
*/
|
|
static osg::Image* convertToRGB8(const osg::Image* image);
|
|
|
|
/**
|
|
*Converts the given image to RGBA8
|
|
*/
|
|
static osg::Image* convertToRGBA8(const osg::Image* image);
|
|
|
|
/**
|
|
* True if the two images are of the same format (pixel format, data type, etc.)
|
|
* though not necessarily the same size, depth, etc.
|
|
*/
|
|
static bool sameFormat(const osg::Image* lhs, const osg::Image* rhs);
|
|
|
|
/**
|
|
* True if the two images have the same format AND size, and can therefore
|
|
* be used together in a texture array.
|
|
*/
|
|
static bool textureArrayCompatible(const osg::Image* lhs, const osg::Image* rhs);
|
|
|
|
/**
|
|
*Compares the image data of two images and determines if they are equivalent
|
|
*/
|
|
static bool areEquivalent(const osg::Image *lhs, const osg::Image *rhs);
|
|
|
|
/**
|
|
* Whether two colors are roughly equivalent.
|
|
*/
|
|
static bool areRGBEquivalent( const osg::Vec4& lhs, const osg::Vec4& rhs, float epsilon =0.01f ) {
|
|
return
|
|
fabs(lhs.r() - rhs.r()) < epsilon &&
|
|
fabs(lhs.g() - rhs.g()) < epsilon &&
|
|
fabs(lhs.b() - rhs.b()) < epsilon;
|
|
}
|
|
|
|
/**
|
|
* Checks whether the image has an alpha component
|
|
*/
|
|
static bool hasAlphaChannel( const osg::Image* image );
|
|
|
|
/**
|
|
* Checks whether an image has transparency; i.e. whether
|
|
* there are any pixels with an alpha component whole value
|
|
* falls below the specified threshold.
|
|
*/
|
|
static bool hasTransparency(const osg::Image* image, float alphaThreshold =1.0f);
|
|
|
|
/**
|
|
* Converts an image (in place) to premultiplied-alpha format.
|
|
* Returns False is the conversion fails, e.g., if there is no reader
|
|
* or writer for the image format.
|
|
*/
|
|
static bool convertToPremultipliedAlpha(osg::Image* image);
|
|
|
|
/**
|
|
* Checks whether the given image is compressed
|
|
*/
|
|
static bool isCompressed( const osg::Image* image );
|
|
|
|
/**
|
|
* Generated a bump map image for the input image
|
|
*/
|
|
static osg::Image* createBumpMap( const osg::Image* input );
|
|
|
|
/**
|
|
* Is it a floating-point texture format?
|
|
*/
|
|
static bool isFloatingPointInternalFormat( GLint internalFormat );
|
|
|
|
/**
|
|
* Compute a texture compression format suitable for the image.
|
|
*/
|
|
static bool computeTextureCompressionMode(
|
|
const osg::Image* image,
|
|
osg::Texture::InternalFormatMode& out_mode);
|
|
|
|
/**
|
|
* Replaces "no data" values in the target image with the corresponding
|
|
* value found in the "reference" image. The images much be GL_LUMINANCE
|
|
* data types.
|
|
*/
|
|
static bool replaceNoDataValues(
|
|
osg::Image* target,
|
|
const Bounds& targetBounds,
|
|
const osg::Image* reference,
|
|
const Bounds& referenceBounds);
|
|
|
|
/**
|
|
* Bicubic upsampling in a quadrant. Target image is already allocated.
|
|
*/
|
|
static bool bicubicUpsample(
|
|
const osg::Image* source,
|
|
osg::Image* target,
|
|
unsigned quadrant,
|
|
unsigned stride);
|
|
|
|
/**
|
|
* Activates mipmapping for a texture image if the correct filters exist.
|
|
*
|
|
* If OSG has an ImageProcessor service installed, this method will use that
|
|
* to generate mipmaps. If not osgEarth will generate them if possible.
|
|
|
|
* Returns true if mipmaps were added
|
|
*/
|
|
//static bool generateMipmaps(osg::Texture* texture);
|
|
|
|
/**
|
|
* Adds mipmaps to an image.
|
|
* Returns false if the image already has mipmaps, or if we cannot
|
|
* generate mipmaps for some reason.
|
|
*/
|
|
//static bool generateMipmaps(osg::Image* image);
|
|
|
|
/**
|
|
* Creates a copy of the input image with added mipmaps.
|
|
* @param image Input image to generate mipmaps for
|
|
* @param minLevelSize The smallest mipmap level size to generate
|
|
* @return image with mipmaps. If the input already had mipmaps,
|
|
* just returns the input pointer (that is why it's const)
|
|
*/
|
|
static const osg::Image* mipmapImage(
|
|
const osg::Image* image,
|
|
int minLevelSize = 16);
|
|
|
|
/**
|
|
* Adds mipmaps to an existing image if neccessary
|
|
* @param image Input image to generate mipmaps for
|
|
*/
|
|
static void mipmapImageInPlace(
|
|
osg::Image* image);
|
|
|
|
//! Returns a compressed copy of the input image.
|
|
//! @param image Image to compress
|
|
//! @param method Compression method to use; see ImageLayer::getCompressionMethod
|
|
static const osg::Image* compressImage(
|
|
const osg::Image* image,
|
|
const std::string& method ="cpu");
|
|
|
|
//! Compressed an exsting image.
|
|
//! @param image Image to compress
|
|
//! @param method Compression method to use; see ImageLayer::getCompressionMethod
|
|
static void compressImageInPlace(
|
|
osg::Image* image,
|
|
const std::string& method ="auto");
|
|
|
|
//! Compresses and mipmaps all textures in the given subgraph
|
|
//! @param node The node to process
|
|
static void compressAndMipmapTextures(osg::Node* node);
|
|
|
|
/**
|
|
* Gets an osgDB::ReaderWriter for the given input stream.
|
|
* Returns NULL if no ReaderWriter can be found.
|
|
*/
|
|
static osgDB::ReaderWriter* getReaderWriterForStream(std::istream& stream);
|
|
|
|
/**
|
|
* Reads an osg::Image from the given input stream.
|
|
* Returns NULL if the image could not be read.
|
|
*/
|
|
static osg::Image* readStream(std::istream& stream, const osgDB::Options* options);
|
|
|
|
//! Make an empty 2D texture array
|
|
static osg::Texture2DArray* makeTexture2DArray(osg::Image* image);
|
|
|
|
//! If a max texture size is stipulated in the options, and it
|
|
//! the image supports it, return the size.
|
|
static optional<int> getMaxTextureSize(const osg::Image*, const osgDB::Options*);
|
|
|
|
//! Convert u,v [0..1] into s,t [pixels] with nearest neighbor sampling.
|
|
template<typename NDCTYPE, typename INDEXTYPE>
|
|
static void nnUVtoST(NDCTYPE u, NDCTYPE v, INDEXTYPE& s, INDEXTYPE& t, unsigned width, unsigned height) {
|
|
const NDCTYPE umin = 1.0 / (2.0 * width);
|
|
const NDCTYPE vmin = 1.0 / (2.0 * height);
|
|
s = u<umin ? 0 : u>(1.0 - umin) ? width - 1 : (int)floor(u*(double)width);
|
|
t = v<vmin ? 0 : v>(1.0 - vmin) ? height - 1 : (int)floor(v*(double)height);
|
|
}
|
|
|
|
/**
|
|
* Convenience object to iterate over an image with a lamdba function
|
|
*/
|
|
struct ImageIterator
|
|
{
|
|
public:
|
|
ImageIterator(const osg::Image* image) :
|
|
_image(image) { }
|
|
|
|
//ImageIterator(PixelReader& reader) :
|
|
// _image(reader._image) { }
|
|
|
|
//ImageIterator(PixelWriter& writer) :
|
|
// _image(writer._image) { }
|
|
|
|
inline int r() const { return _r; }
|
|
inline int s() const { return _s; }
|
|
inline int t() const { return _t; }
|
|
inline int m() const { return 0; }
|
|
inline double u() const { return _u; }
|
|
inline double v() const { return _v; }
|
|
inline void quit() { _break = true; }
|
|
|
|
template<typename CALLABLE>
|
|
inline void forEachPixel(CALLABLE&& func)
|
|
{
|
|
_break = false;
|
|
int tt = _image->t(), ss = _image->s(), rr = _image->r();
|
|
for (_t = 0; _t < tt && !_break; ++_t)
|
|
{
|
|
_v = (double)_t / (double)(_image->t() - 1);
|
|
|
|
for (_s = 0; _s < ss && !_break; ++_s)
|
|
{
|
|
_u = (double)_s / (double)(_image->s() - 1);
|
|
|
|
for (_r = 0; _r < rr && !_break; ++_r)
|
|
{
|
|
func(*this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
const osg::Image* _image;
|
|
int _r, _s, _t;
|
|
double _u, _v;
|
|
bool _break = false;
|
|
};
|
|
|
|
/**
|
|
* Reads color data out of an image, regardles of its internal pixel format.
|
|
*/
|
|
class OSGEARTH_EXPORT PixelReader
|
|
{
|
|
public:
|
|
//! Empty pixel reader. Must call setImage before use.
|
|
PixelReader();
|
|
|
|
/**
|
|
* Constructs a pixel reader. "Normalized" means that the values in the source
|
|
* image have been scaled to [0..1] and should be denormalized upon reading.
|
|
*/
|
|
PixelReader(const osg::Image* image);
|
|
|
|
/** Sets an image to read. */
|
|
void setImage(const osg::Image* image);
|
|
|
|
//! Sets a texture whose first image to read (along with texture params)
|
|
void setTexture(const osg::Texture* tex);
|
|
|
|
//! Whether to denormalize data upon reading or to leave it as-is
|
|
void setDenormalize(bool value) { _normalized = value; }
|
|
|
|
/** Whether to use bilinear interpolation when reading with u,v coords (default=true) */
|
|
void setBilinear(bool value) { _bilinear = value; }
|
|
|
|
//! Whether to sample the image like a texture (GLSL)
|
|
void setSampleAsTexture(bool value) { _sampleAsTexture = value; }
|
|
|
|
//! Whether to sample with texture-wrapping
|
|
void setSampleAsRepeatingTexture(bool value) { _sampleAsRepeatingTexture = value; }
|
|
|
|
/** Whether PixelReader supports a given format/datatype combiniation. */
|
|
static bool supports( GLenum pixelFormat, GLenum dataType );
|
|
|
|
/** Whether PixelReader can read from the specified image. */
|
|
static bool supports( const osg::Image* image ) {
|
|
return image && supports(image->getPixelFormat(), image->getDataType() );
|
|
}
|
|
|
|
//! Whether the image is valid and usable
|
|
bool valid() const {
|
|
return _image != nullptr && _read != nullptr;
|
|
}
|
|
|
|
inline int s() const { return _image->s(); }
|
|
inline int t() const { return _image->t(); }
|
|
inline int r() const { return _image->r(); }
|
|
|
|
//! Returns an iterator into this image
|
|
ImageIterator iterator() const {
|
|
return ImageIterator(_image);
|
|
}
|
|
|
|
//! Iterator over this image with the user function CALLABLE
|
|
//! with the signature void CALLABLE(ImageIterator&)
|
|
template<typename CALLABLE>
|
|
void forEachPixel(CALLABLE&& func) const {
|
|
ImageIterator(_image).forEachPixel(func);
|
|
}
|
|
|
|
//! Returns a color from an image at pixel (s,t,r,m)
|
|
inline osg::Vec4f operator()(int s, int t, int r=0, int m=0) const {
|
|
osg::Vec4f temp;
|
|
_read(this, temp, s, t, r, m);
|
|
return temp;
|
|
}
|
|
inline void operator()(osg::Vec4f& output, int s, int t, int r=0, int m=0) const {
|
|
_read(this, output, s, t, r, m);
|
|
}
|
|
|
|
//! Returns a color from an image at pixel (s,t,r,m)
|
|
inline osg::Vec4f operator()(unsigned s, unsigned t, unsigned r=0, int m=0) const {
|
|
osg::Vec4f temp;
|
|
_read(this, temp, s, t, r, m);
|
|
return temp;
|
|
}
|
|
inline void operator()(osg::Vec4f& output, unsigned s, unsigned t, int r=0, int m=0) const {
|
|
_read(this, output, s, t, r, m);
|
|
}
|
|
|
|
//! composite version of pixel read operator
|
|
template<typename T>
|
|
inline osg::Vec4f operator()(const T& composite) const {
|
|
return (*this)(composite.s(), composite.t(), composite.r());
|
|
}
|
|
template<typename T>
|
|
inline void operator()(osg::Vec4f& output, const T& composite) const {
|
|
_read(this, output, composite.s(), composite.t(), composite.r(), 0);
|
|
}
|
|
|
|
/** Reads a color from the image by unit coords [0..1] */
|
|
osg::Vec4f operator()(float u, float v, int r=0, int m=0) const;
|
|
void operator()(osg::Vec4f& output, float u, float v, int r=0, int m=0) const;
|
|
osg::Vec4f operator()(double u, double v, int r=0, int m=0) const;
|
|
void operator()(osg::Vec4f& output, double u, double v, int t=0, int m=0) const;
|
|
|
|
// internals:
|
|
const unsigned char* data(int s=0, int t=0, int r=0, int m=0) const {
|
|
return m == 0 ?
|
|
_image->data() + s*_colBytes + t*_rowBytes + r*_imageBytes :
|
|
_image->getMipmapData(m-1) + (s>>m)*_colBytes + (t>>m)*(_rowBytes>>m) + r*(_imageBytes>>m);
|
|
}
|
|
|
|
typedef void (*ReaderFunc)(const PixelReader* ia, osg::Vec4f& output, int s, int t, int r, int m);
|
|
|
|
ReaderFunc _read;
|
|
const osg::Image* _image;
|
|
unsigned _colBytes;
|
|
unsigned _rowBytes;
|
|
unsigned _imageBytes;
|
|
bool _normalized;
|
|
bool _bilinear;
|
|
bool _sampleAsTexture;
|
|
bool _sampleAsRepeatingTexture;
|
|
};
|
|
|
|
/**
|
|
* Writes color data to an image, regardles of its internal pixel format.
|
|
*/
|
|
class OSGEARTH_EXPORT PixelWriter
|
|
{
|
|
public:
|
|
/**
|
|
* Constructs a pixel writer. "Normalized" means the values are scaled to [0..1]
|
|
* before writing.
|
|
*/
|
|
PixelWriter(osg::Image* image);
|
|
|
|
//! Whether data should be normalized to [0..1] or left as-is
|
|
void setNormalize(bool value) { _normalized = value; }
|
|
|
|
/** Whether PixelWriter can write to an image with the given format/datatype combo. */
|
|
static bool supports( GLenum pixelFormat, GLenum dataType );
|
|
|
|
/** Whether PixelWriter can write to non-const version of an image. */
|
|
static bool supports( const osg::Image* image ) {
|
|
return image && supports(image->getPixelFormat(), image->getDataType() );
|
|
}
|
|
|
|
inline int s() const { return _image->s(); }
|
|
inline int t() const { return _image->t(); }
|
|
inline int r() const { return _image->r(); }
|
|
|
|
//! Initialize to all one value
|
|
void assign(const osg::Vec4& c);
|
|
|
|
//! Initialize to all one value (one layer)
|
|
void assign(const osg::Vec4& c, int r);
|
|
|
|
/** Writes a color to a pixel. */
|
|
inline void operator()(const osg::Vec4& c, int s, int t, int r=0, int m=0) {
|
|
(*_writer)(this, c, s, t, r, m );
|
|
}
|
|
|
|
inline void f(const osg::Vec4& c, float s, float t, int r=0, int m=0) {
|
|
this->operator()( c,
|
|
(int)(s * (float)(_image->s()-1)),
|
|
(int)(t * (float)(_image->t()-1)),
|
|
r, m);
|
|
}
|
|
|
|
//! composite version of pixel write operator
|
|
template<typename T>
|
|
inline void operator()(const osg::Vec4& c, const T& composite) const {
|
|
(*_writer)(this, c, composite.s(), composite.t(), composite.r(), composite.m());
|
|
}
|
|
|
|
//! Iterator over this image with the user function CALLABLE
|
|
//! with the signature void CALLABLE(ImageIterator&)
|
|
template<typename CALLABLE>
|
|
void forEachPixel(CALLABLE&& func) const {
|
|
ImageIterator(_image).forEachPixel(func);
|
|
}
|
|
|
|
// internals:
|
|
osg::Image* _image;
|
|
unsigned _colBytes;
|
|
unsigned _rowBytes;
|
|
unsigned _imageBytes;
|
|
bool _normalized;
|
|
|
|
unsigned char* data(int s=0, int t=0, int r=0, int m=0) const;
|
|
|
|
typedef void (*WriterFunc)(const PixelWriter* iw, const osg::Vec4& c, int s, int t, int r, int m);
|
|
WriterFunc _writer;
|
|
};
|
|
|
|
/**
|
|
* Convenience object to iterate over an image, with or without
|
|
* a geospatial extent, with a lambda function
|
|
*/
|
|
template<typename EXTENT>
|
|
struct ImageIteratorWithExtent
|
|
{
|
|
public:
|
|
ImageIteratorWithExtent(const osg::Image* image, const EXTENT& extent) :
|
|
_image(image), _extent(extent) { }
|
|
|
|
ImageIteratorWithExtent(PixelReader& reader, const EXTENT& extent) :
|
|
_image(reader._image), _extent(extent) { }
|
|
|
|
ImageIteratorWithExtent(PixelWriter& writer, const EXTENT& extent) :
|
|
_image(writer._image), _extent(extent) { }
|
|
|
|
inline int s() const { return _s; }
|
|
inline int t() const { return _t; }
|
|
inline int r() const { return _r; }
|
|
inline int m() const { return 0; }
|
|
inline double u() const { return _u; }
|
|
inline double v() const { return _v; }
|
|
inline double x() const { return _x; }
|
|
inline double y() const { return _y; }
|
|
|
|
template<typename CALLABLE>
|
|
inline void forEachPixel(CALLABLE&& func)
|
|
{
|
|
forEachPixelOnCenter(func);
|
|
}
|
|
|
|
template<typename CALLABLE>
|
|
inline void forEachPixelOnCenter(CALLABLE&& func)
|
|
{
|
|
int tt = _image->t(), ss = _image->s(), rr = _image->r();
|
|
double _bu = 0.5 / (double)ss;
|
|
double _bv = 0.5 / (double)tt;
|
|
|
|
for (_t = 0; _t < tt; ++_t)
|
|
{
|
|
_v = _bv + ((double)_t * 2.0 * _bv);
|
|
_y = _extent.yMin() + _extent.height() * _v;
|
|
|
|
for (_s = 0; _s < ss; ++_s)
|
|
{
|
|
_u = _bu + ((double)_s * 2.0 * _bu);
|
|
_x = _extent.xMin() + _extent.width() * _u;
|
|
|
|
for (_r = 0; _r < rr; ++_r)
|
|
{
|
|
func();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
const osg::Image* _image;
|
|
const EXTENT _extent;
|
|
int _s, _t, _r;
|
|
double _u, _v;
|
|
double _x, _y;
|
|
};
|
|
|
|
template<typename EXTENT>
|
|
class PixelReaderWithExtent : public PixelReader
|
|
{
|
|
public:
|
|
PixelReaderWithExtent(const osg::Image* image, const EXTENT& extent) :
|
|
PixelReader(image), _extent(extent) { }
|
|
|
|
void readCoord(osg::Vec4f& value, double x, double y, int r = 0, int m = 0) const {
|
|
PixelReader::operator()(
|
|
value,
|
|
(double)(x - _extent.xMin()) / _extent.width(),
|
|
(double)(y - _extent.yMin()) / _extent.height(),
|
|
r, m);
|
|
}
|
|
|
|
bool readCoordWithoutClamping(osg::Vec4f& value, double x, double y, int r = 0, int m = 0) const {
|
|
double u = (x - _extent.xMin()) / _extent.width();
|
|
double v = (y - _extent.yMin()) / _extent.height();
|
|
if (u < 0.0 || u > 1.0 || v < 0.0 || v > 1.0) return false;
|
|
PixelReader::operator()(value, u, v, r, m);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
const EXTENT _extent;
|
|
};
|
|
};
|
|
|
|
/** Visitor that finds and operates on textures and images */
|
|
class OSGEARTH_EXPORT TextureAndImageVisitor : public osg::NodeVisitor
|
|
{
|
|
public:
|
|
TextureAndImageVisitor();
|
|
virtual ~TextureAndImageVisitor() { }
|
|
|
|
public:
|
|
/** Visits a texture and, by default, all its components images */
|
|
virtual void apply(osg::Texture& texture);
|
|
|
|
/** Visits an image inside a texture */
|
|
virtual void apply(osg::Image& image) { }
|
|
|
|
public: // osg::NodeVisitor
|
|
virtual void apply(osg::Node& node);
|
|
virtual void apply(osg::StateSet& stateSet);
|
|
};
|
|
} }
|
|
|
|
#endif //OSGEARTH_IMAGEUTILS_H
|