/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library 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 * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dxtctool.h" using namespace osg; using namespace std; Image::Image() :Object(true) { setDataVariance(STATIC); _fileName = ""; _origin = BOTTOM_LEFT; _s = _t = _r = 0; _internalTextureFormat = 0; _pixelFormat = (unsigned int)0; _dataType = (unsigned int)0; _packing = 4; _allocationMode = USE_NEW_DELETE; _data = (unsigned char *)0L; _modifiedCount = 0; } Image::Image(const Image& image,const CopyOp& copyop): Object(image,copyop), _fileName(image._fileName), _origin(image._origin), _s(image._s), _t(image._t), _r(image._r), _internalTextureFormat(image._internalTextureFormat), _pixelFormat(image._pixelFormat), _dataType(image._dataType), _packing(image._packing), _data(0L), _modifiedCount(image._modifiedCount), _mipmapData(image._mipmapData) { if (image._data) { int size = image.getTotalSizeInBytesIncludingMipmaps(); setData(new unsigned char [size],USE_NEW_DELETE); memcpy(_data,image._data,size); } } Image::~Image() { deallocateData(); } void Image::deallocateData() { if (_data) { if (_allocationMode==USE_NEW_DELETE) delete [] _data; else if (_allocationMode==USE_MALLOC_FREE) ::free(_data); _data = 0; } } int Image::compare(const Image& rhs) const { // if at least one filename is empty, then need to test buffer // pointers because images could have been created on the fly // and therefore we can't rely on file names to get an accurate // comparison if (getFileName().empty() || rhs.getFileName().empty()) { if (_datarhs._data) return 1; } // need to test against image contents here... COMPARE_StateAttribute_Parameter(_s) COMPARE_StateAttribute_Parameter(_t) COMPARE_StateAttribute_Parameter(_internalTextureFormat) COMPARE_StateAttribute_Parameter(_pixelFormat) COMPARE_StateAttribute_Parameter(_dataType) COMPARE_StateAttribute_Parameter(_packing) COMPARE_StateAttribute_Parameter(_mipmapData) COMPARE_StateAttribute_Parameter(_modifiedCount) // same buffer + same parameters = same image if ((_data || rhs._data) && (_data == rhs._data)) return 0; // slowest comparison at the bottom! COMPARE_StateAttribute_Parameter(getFileName()) return 0; } void Image::setFileName(const std::string& fileName) { _fileName = fileName; } void Image::setData(unsigned char* data, AllocationMode mode) { deallocateData(); _data = data; _allocationMode = mode; } bool Image::isPackedType(GLenum type) { switch(type) { case(GL_UNSIGNED_BYTE_3_3_2): case(GL_UNSIGNED_BYTE_2_3_3_REV): case(GL_UNSIGNED_SHORT_5_6_5): case(GL_UNSIGNED_SHORT_5_6_5_REV): case(GL_UNSIGNED_SHORT_4_4_4_4): case(GL_UNSIGNED_SHORT_4_4_4_4_REV): case(GL_UNSIGNED_SHORT_5_5_5_1): case(GL_UNSIGNED_SHORT_1_5_5_5_REV): case(GL_UNSIGNED_INT_8_8_8_8): case(GL_UNSIGNED_INT_8_8_8_8_REV): case(GL_UNSIGNED_INT_10_10_10_2): case(GL_UNSIGNED_INT_2_10_10_10_REV): return true; default: return false; } } GLenum Image::computePixelFormat(GLenum format) { switch(format) { case(GL_ALPHA16F_ARB): case(GL_ALPHA32F_ARB): return GL_ALPHA; case(GL_LUMINANCE16F_ARB): case(GL_LUMINANCE32F_ARB): return GL_LUMINANCE; case(GL_INTENSITY16F_ARB): case(GL_INTENSITY32F_ARB): return GL_INTENSITY; case(GL_LUMINANCE_ALPHA16F_ARB): case(GL_LUMINANCE_ALPHA32F_ARB): return GL_LUMINANCE_ALPHA; case(GL_RGB32F_ARB): case(GL_RGB16F_ARB): return GL_RGB; case(GL_RGBA32F_ARB): case(GL_RGBA16F_ARB): return GL_RGBA; case(GL_ALPHA8I_EXT): case(GL_ALPHA16I_EXT): case(GL_ALPHA32I_EXT): case(GL_ALPHA8UI_EXT): case(GL_ALPHA16UI_EXT): case(GL_ALPHA32UI_EXT): return GL_ALPHA_INTEGER_EXT; case(GL_LUMINANCE8I_EXT): case(GL_LUMINANCE16I_EXT): case(GL_LUMINANCE32I_EXT): case(GL_LUMINANCE8UI_EXT): case(GL_LUMINANCE16UI_EXT): case(GL_LUMINANCE32UI_EXT): return GL_LUMINANCE_INTEGER_EXT; case(GL_INTENSITY8I_EXT): case(GL_INTENSITY16I_EXT): case(GL_INTENSITY32I_EXT): case(GL_INTENSITY8UI_EXT): case(GL_INTENSITY16UI_EXT): case(GL_INTENSITY32UI_EXT): notify(WARN)<<"Image::computePixelFormat("<isTexture2DArraySupported()) { glGetBooleanv(GL_TEXTURE_BINDING_2D_ARRAY_EXT, &binding2DArray); } else { binding2DArray = GL_FALSE; } GLenum textureMode = binding1D ? GL_TEXTURE_1D : binding2D ? GL_TEXTURE_2D : binding3D ? GL_TEXTURE_3D : binding2DArray ? GL_TEXTURE_2D_ARRAY_EXT : 0; if (textureMode==0) return; GLint internalformat; GLint width; GLint height; GLint depth; GLint packing; GLint numMipMaps = 0; if (copyMipMapsIfAvailable) { for(;numMipMaps<20;++numMipMaps) { glGetTexLevelParameteriv(textureMode, numMipMaps, GL_TEXTURE_WIDTH, &width); glGetTexLevelParameteriv(textureMode, numMipMaps, GL_TEXTURE_HEIGHT, &height); glGetTexLevelParameteriv(textureMode, numMipMaps, GL_TEXTURE_DEPTH, &depth); if (width==0 || height==0 || depth==0) break; } } else { numMipMaps = 1; } GLint compressed = 0; if (textureMode==GL_TEXTURE_2D) { if (extensions->isCompressedTexImage2DSupported()) { glGetTexLevelParameteriv(textureMode, 0, GL_TEXTURE_COMPRESSED_ARB,&compressed); } } else if (textureMode==GL_TEXTURE_3D) { if (extensions3D->isCompressedTexImage3DSupported()) { glGetTexLevelParameteriv(textureMode, 0, GL_TEXTURE_COMPRESSED_ARB,&compressed); } } else if (textureMode==GL_TEXTURE_2D_ARRAY_EXT) { if (extensions2DArray->isCompressedTexImage3DSupported()) { glGetTexLevelParameteriv(textureMode, 0, GL_TEXTURE_COMPRESSED_ARB,&compressed); } } /* if the compression has been successful */ if (compressed == GL_TRUE) { MipmapDataType mipMapData; unsigned int total_size = 0; GLint i; for(i=0;i0) mipMapData.push_back(total_size); GLint compressed_size; glGetTexLevelParameteriv(textureMode, i, GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, &compressed_size); total_size += compressed_size; } unsigned char* data = new unsigned char[total_size]; if (!data) { osg::notify(osg::WARN)<<"Warning: Image::readImageFromCurrentTexture(..) out of memory, now image read."<glGetCompressedTexImage(textureMode, i, getMipmapData(i)); } dirty(); } else { MipmapDataType mipMapData; // Get the internal texture format and packing value from OpenGL, // instead of using possibly outdated values from the class. glGetTexLevelParameteriv(textureMode, 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat); glGetIntegerv(GL_UNPACK_ALIGNMENT, &packing); glPixelStorei(GL_PACK_ALIGNMENT, packing); unsigned int total_size = 0; GLint i; for(i=0;i0) mipMapData.push_back(total_size); glGetTexLevelParameteriv(textureMode, i, GL_TEXTURE_WIDTH, &width); glGetTexLevelParameteriv(textureMode, i, GL_TEXTURE_HEIGHT, &height); glGetTexLevelParameteriv(textureMode, i, GL_TEXTURE_DEPTH, &depth); unsigned int level_size = computeRowWidthInBytes(width,internalformat,type,packing)*height*depth; total_size += level_size; } unsigned char* data = new unsigned char[total_size]; if (!data) { osg::notify(osg::WARN)<<"Warning: Image::readImageFromCurrentTexture(..) out of memory, now image read."<s(),t_offset+source->t(),r_offset+source->r(), source->getPixelFormat(),source->getDataType(), source->getPacking()); } if (s_offset>=_s || t_offset>=_t || r_offset>=_r) { notify(WARN)<<"Warning: offsets passed to Image::copySubImage(..) outside destination image, operation ignored."<getPixelFormat()) { notify(WARN)<<"Warning: image with an incompatible pixel formats passed to Image::copySubImage(..), operation ignored."<getPacking()); glPixelStorei(GL_PACK_ROW_LENGTH,_s); glPixelStorei(GL_UNPACK_ALIGNMENT,_packing); GLint status = gluScaleImage(_pixelFormat, source->s(), source->t(), source->getDataType(), source->data(), source->s(), source->t(), _dataType, data_destination); glPixelStorei(GL_PACK_ROW_LENGTH,0); if (status!=0) { notify(WARN) << "Error Image::scaleImage() do not succeed : errorString = "<1) { notify(WARN) << "Error Image::flipVertical() do not succeed : flipping of mipmap 3d textures not yet supported."<>= 1; t >>= 1; if (s==0) s=1; if (t==0) t=1; if (!dxtc_tool::VerticalFlip(s,t,_pixelFormat,_data+_mipmapData[i])) { // its not a compressed image, so implement flip oursleves. unsigned int rowSize = computeRowWidthInBytes(s,_pixelFormat,_dataType,_packing); unsigned char* top = _data+_mipmapData[i]; unsigned char* bottom = top + (t-1)*rowSize; flipImageVertical(top, bottom, rowSize); } } } dirty(); } void Image::ensureValidSizeForTexturing(GLint maxTextureSize) { int new_s = computeNearestPowerOfTwo(_s); int new_t = computeNearestPowerOfTwo(_t); if (new_s>maxTextureSize) new_s = maxTextureSize; if (new_t>maxTextureSize) new_t = maxTextureSize; if (new_s!=_s || new_t!=_t) { if (!_fileName.empty()) notify(NOTICE) << "Scaling image '"<<_fileName<<"' from ("<<_s<<","<<_t<<") to ("< bool _findLowerAlphaValueInRow(unsigned int num, T* data,T value, unsigned int delta) { for(unsigned int i=0;i bool _maskedFindLowerAlphaValueInRow(unsigned int num, T* data,T value, T mask, unsigned int delta) { for(unsigned int i=0;is(),image->t()); } Geode* osg::createGeodeForImage(osg::Image* image,float s,float t) { if (image) { if (s>0 && t>0) { float y = 1.0; float x = y*(s/t); // set up the texture. osg::Texture2D* texture = new osg::Texture2D; texture->setImage(image); // set up the drawstate. osg::StateSet* dstate = new osg::StateSet; dstate->setMode(GL_CULL_FACE,osg::StateAttribute::OFF); dstate->setMode(GL_LIGHTING,osg::StateAttribute::OFF); dstate->setTextureAttributeAndModes(0, texture,osg::StateAttribute::ON); // set up the geoset. Geometry* geom = new Geometry; geom->setStateSet(dstate); Vec3Array* coords = new Vec3Array(4); (*coords)[0].set(-x,0.0f,y); (*coords)[1].set(-x,0.0f,-y); (*coords)[2].set(x,0.0f,-y); (*coords)[3].set(x,0.0f,y); geom->setVertexArray(coords); Vec2Array* tcoords = new Vec2Array(4); (*tcoords)[0].set(0.0f,1.0f); (*tcoords)[1].set(0.0f,0.0f); (*tcoords)[2].set(1.0f,0.0f); (*tcoords)[3].set(1.0f,1.0f); geom->setTexCoordArray(0,tcoords); osg::Vec4Array* colours = new osg::Vec4Array(1); (*colours)[0].set(1.0f,1.0f,1.0,1.0f); geom->setColorArray(colours); geom->setColorBinding(Geometry::BIND_OVERALL); geom->addPrimitiveSet(new DrawArrays(PrimitiveSet::QUADS,0,4)); // set up the geode. osg::Geode* geode = new osg::Geode; geode->addDrawable(geom); return geode; } else { return NULL; } } else { return NULL; } } template Vec4 _readColor(GLenum pixelFormat, T* data,float scale) { switch(pixelFormat) { case(GL_LUMINANCE): { float l = float(*data++)*scale; return Vec4(l, l, l, 1.0f); } case(GL_ALPHA): { float a = float(*data++)*scale; return Vec4(1.0f, 1.0f, 1.0f, a); } case(GL_LUMINANCE_ALPHA): { float l = float(*data++)*scale; float a = float(*data++)*scale; return Vec4(l,l,l,a); } case(GL_RGB): { float r = float(*data++)*scale; float g = float(*data++)*scale; float b = float(*data++)*scale; return Vec4(r,g,b,1.0f); } case(GL_RGBA): { float r = float(*data++)*scale; float g = float(*data++)*scale; float b = float(*data++)*scale; float a = float(*data++)*scale; return Vec4(r,g,b,a); } case(GL_BGR): { float b = float(*data++)*scale; float g = float(*data++)*scale; float r = float(*data++)*scale; return Vec4(r,g,b,1.0f); } case(GL_BGRA): { float b = float(*data++)*scale; float g = float(*data++)*scale; float r = float(*data++)*scale; float a = float(*data++)*scale; return Vec4(r,g,b,a); } } return Vec4(1.0f,1.0f,1.0f,1.0f); } Vec4 Image::getColor(unsigned int s,unsigned t,unsigned r) const { const unsigned char* ptr = data(s,t,r); switch(_dataType) { case(GL_BYTE): return _readColor(_pixelFormat, (char*)ptr, 1.0f/128.0f); case(GL_UNSIGNED_BYTE): return _readColor(_pixelFormat, (unsigned char*)ptr, 1.0f/255.0f); case(GL_SHORT): return _readColor(_pixelFormat, (short*)ptr, 1.0f/32768.0f); case(GL_UNSIGNED_SHORT): return _readColor(_pixelFormat, (unsigned short*)ptr, 1.0f/65535.0f); case(GL_INT): return _readColor(_pixelFormat, (int*)ptr, 1.0f/2147483648.0f); case(GL_UNSIGNED_INT): return _readColor(_pixelFormat, (unsigned int*)ptr, 1.0f/4294967295.0f); case(GL_FLOAT): return _readColor(_pixelFormat, (float*)ptr, 1.0f); } return Vec4(1.0f,1.0f,1.0f,1.0f); } Vec4 Image::getColor(const Vec3& texcoord) const { int s = int(texcoord.x()*float(_s-1)) % _s; int t = int(texcoord.y()*float(_t-1)) % _t; int r = int(texcoord.z()*float(_r-1)) % _r; //osg::notify(osg::NOTICE)<<"getColor("<