//----------------------------------------------------------------------- void Image::scale(const PixelBox &src, const PixelBox &scaled, Filter filter) { assert(PixelUtil::isAccessible(src.format)); assert(PixelUtil::isAccessible(scaled.format)); MemoryDataStreamPtr buf; // For auto-delete PixelBox temp; switch (filter) { default: case FILTER_NEAREST: if(src.format == scaled.format) { // No intermediate buffer needed temp = scaled; } else { // Allocate temporary buffer of destination size in source format temp = PixelBox(scaled.getWidth(), scaled.getHeight(), scaled.getDepth(), src.format); buf.bind(OGRE_NEW MemoryDataStream(temp.getConsecutiveSize())); temp.data = buf->getPtr(); } // super-optimized: no conversion switch (PixelUtil::getNumElemBytes(src.format)) { case 1: NearestResampler<1>::scale(src, temp); break; case 2: NearestResampler<2>::scale(src, temp); break; case 3: NearestResampler<3>::scale(src, temp); break; case 4: NearestResampler<4>::scale(src, temp); break; case 6: NearestResampler<6>::scale(src, temp); break; case 8: NearestResampler<8>::scale(src, temp); break; case 12: NearestResampler<12>::scale(src, temp); break; case 16: NearestResampler<16>::scale(src, temp); break; default: // never reached assert(false); } if(temp.data != scaled.data) { // Blit temp buffer PixelUtil::bulkPixelConversion(temp, scaled); } break; case FILTER_LINEAR: case FILTER_BILINEAR: switch (src.format) { case PF_L8: case PF_A8: case PF_BYTE_LA: case PF_R8G8B8: case PF_B8G8R8: case PF_R8G8B8A8: case PF_B8G8R8A8: case PF_A8B8G8R8: case PF_A8R8G8B8: case PF_X8B8G8R8: case PF_X8R8G8B8: if(src.format == scaled.format) { // No intermediate buffer needed temp = scaled; } else { // Allocate temp buffer of destination size in source format temp = PixelBox(scaled.getWidth(), scaled.getHeight(), scaled.getDepth(), src.format); buf.bind(OGRE_NEW MemoryDataStream(temp.getConsecutiveSize())); temp.data = buf->getPtr(); } // super-optimized: byte-oriented math, no conversion switch (PixelUtil::getNumElemBytes(src.format)) { case 1: LinearResampler_Byte<1>::scale(src, temp); break; case 2: LinearResampler_Byte<2>::scale(src, temp); break; case 3: LinearResampler_Byte<3>::scale(src, temp); break; case 4: LinearResampler_Byte<4>::scale(src, temp); break; default: // never reached assert(false); } if(temp.data != scaled.data) { // Blit temp buffer PixelUtil::bulkPixelConversion(temp, scaled); } break; case PF_FLOAT32_RGB: case PF_FLOAT32_RGBA: if (scaled.format == PF_FLOAT32_RGB || scaled.format == PF_FLOAT32_RGBA) { // float32 to float32, avoid unpack/repack overhead LinearResampler_Float32::scale(src, scaled); break; } // else, fall through default: // non-optimized: floating-point math, performs conversion but always works LinearResampler::scale(src, scaled); } break; } }
void GLESTextureBuffer::upload(const PixelBox &data, const Image::Box &dest) { glBindTexture(mTarget, mTextureID); GL_CHECK_ERROR; if (PixelUtil::isCompressed(data.format)) { if(data.format != mFormat || !data.isConsecutive()) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Compressed images must be consecutive, in the source format", "GLESTextureBuffer::upload"); GLenum format = GLESPixelUtil::getClosestGLInternalFormat(mFormat); // Data must be consecutive and at beginning of buffer as PixelStorei not allowed // for compressed formats if (dest.left == 0 && dest.top == 0) { glCompressedTexImage2D(mFaceTarget, mLevel, format, dest.getWidth(), dest.getHeight(), 0, data.getConsecutiveSize(), data.data); GL_CHECK_ERROR; } else { glCompressedTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), format, data.getConsecutiveSize(), data.data); GL_CHECK_ERROR; } } else if (mSoftwareMipmap) { if (data.getWidth() != data.rowPitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLESTextureBuffer::upload"); } if (data.getHeight() * data.getWidth() != data.slicePitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLESTextureBuffer::upload"); } glPixelStorei(GL_UNPACK_ALIGNMENT, 1); GL_CHECK_ERROR; buildMipmaps(data); } else { if(data.getWidth() != data.rowPitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLESTextureBuffer::upload"); } if(data.getHeight()*data.getWidth() != data.slicePitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLESTextureBuffer::upload"); } if ((data.getWidth() * PixelUtil::getNumElemBytes(data.format)) & 3) { // Standard alignment of 4 is not right glPixelStorei(GL_UNPACK_ALIGNMENT, 1); GL_CHECK_ERROR; } // LogManager::getSingleton().logMessage("GLESTextureBuffer::upload - ID: " + StringConverter::toString(mTextureID) + // " Format: " + PixelUtil::getFormatName(data.format) + // " Origin format: " + StringConverter::toString(GLESPixelUtil::getGLOriginFormat(data.format, 0, ' ', std::ios::hex)) + // " Data type: " + StringConverter::toString(GLESPixelUtil::getGLOriginDataType(data.format, 0, ' ', std::ios::hex)) // ); glTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), GLESPixelUtil::getGLOriginFormat(data.format), GLESPixelUtil::getGLOriginDataType(data.format), data.data); GL_CHECK_ERROR; } glPixelStorei(GL_UNPACK_ALIGNMENT, 4); GL_CHECK_ERROR; }
//-------------------------------------------------------------------------- void Texture::_loadImages( const ConstImagePtrList& images ) { if(images.size() < 1) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Cannot load empty vector of images", "Texture::loadImages"); // Set desired texture size and properties from images[0] mSrcWidth = mWidth = images[0]->getWidth(); mSrcHeight = mHeight = images[0]->getHeight(); mSrcDepth = mDepth = images[0]->getDepth(); // Get source image format and adjust if required mSrcFormat = images[0]->getFormat(); if (mTreatLuminanceAsAlpha && mSrcFormat == PF_L8) { mSrcFormat = PF_A8; } if (mDesiredFormat != PF_UNKNOWN) { // If have desired format, use it mFormat = mDesiredFormat; } else { // Get the format according with desired bit depth mFormat = PixelUtil::getFormatForBitDepths(mSrcFormat, mDesiredIntegerBitDepth, mDesiredFloatBitDepth); } // The custom mipmaps in the image have priority over everything size_t imageMips = images[0]->getNumMipmaps(); if(imageMips > 0) { mNumMipmaps = mNumRequestedMipmaps = images[0]->getNumMipmaps(); // Disable flag for auto mip generation mUsage &= ~TU_AUTOMIPMAP; } // Create the texture createInternalResources(); // Check if we're loading one image with multiple faces // or a vector of images representing the faces size_t faces; bool multiImage; // Load from multiple images? if(images.size() > 1) { faces = images.size(); multiImage = true; } else { faces = images[0]->getNumFaces(); multiImage = false; } // Check wether number of faces in images exceeds number of faces // in this texture. If so, clamp it. if(faces > getNumFaces()) faces = getNumFaces(); if (TextureManager::getSingleton().getVerbose()) { // Say what we're doing StringUtil::StrStreamType str; str << "Texture: " << mName << ": Loading " << faces << " faces" << "(" << PixelUtil::getFormatName(images[0]->getFormat()) << "," << images[0]->getWidth() << "x" << images[0]->getHeight() << "x" << images[0]->getDepth() << ") with "; if (!(mMipmapsHardwareGenerated && mNumMipmaps == 0)) str << mNumMipmaps; if(mUsage & TU_AUTOMIPMAP) { if (mMipmapsHardwareGenerated) str << " hardware"; str << " generated mipmaps"; } else { str << " custom mipmaps"; } if(multiImage) str << " from multiple Images."; else str << " from Image."; // Scoped { // Print data about first destination surface HardwarePixelBufferSharedPtr buf = getBuffer(0, 0); str << " Internal format is " << PixelUtil::getFormatName(buf->getFormat()) << "," << buf->getWidth() << "x" << buf->getHeight() << "x" << buf->getDepth() << "."; } LogManager::getSingleton().logMessage( LML_NORMAL, str.str()); } // Main loading loop // imageMips == 0 if the image has no custom mipmaps, otherwise contains the number of custom mips for(size_t mip = 0; mip<=imageMips; ++mip) { for(size_t i = 0; i < faces; ++i) { PixelBox src; if(multiImage) { // Load from multiple images src = images[i]->getPixelBox(0, mip); } else { // Load from faces of images[0] src = images[0]->getPixelBox(i, mip); } // Sets to treated format in case is difference src.format = mSrcFormat; if(mGamma != 1.0f) { // Apply gamma correction // Do not overwrite original image but do gamma correction in temporary buffer MemoryDataStreamPtr buf; // for scoped deletion of conversion buffer buf.bind(OGRE_NEW MemoryDataStream( PixelUtil::getMemorySize( src.getWidth(), src.getHeight(), src.getDepth(), src.format))); PixelBox corrected = PixelBox(src.getWidth(), src.getHeight(), src.getDepth(), src.format, buf->getPtr()); PixelUtil::bulkPixelConversion(src, corrected); Image::applyGamma(static_cast<uint8*>(corrected.data), mGamma, corrected.getConsecutiveSize(), static_cast<uchar>(PixelUtil::getNumElemBits(src.format))); // Destination: entire texture. blitFromMemory does the scaling to // a power of two for us when needed getBuffer(i, mip)->blitFromMemory(corrected); } else { // Destination: entire texture. blitFromMemory does the scaling to // a power of two for us when needed getBuffer(i, mip)->blitFromMemory(src); } } } // Update size (the final size, not including temp space) mSize = getNumFaces() * PixelUtil::getMemorySize(mWidth, mHeight, mDepth, mFormat); }
//----------------------------------------------------------------------- void Image::scale(const PixelBox &src, const PixelBox &scaled, Filter filter) { assert(PixelUtil::isAccessible(src.format)); assert(PixelUtil::isAccessible(scaled.format)); #ifdef NEWSCALING MemoryDataStreamPtr buf; // For auto-delete PixelBox temp; switch (filter) { case FILTER_NEAREST: if(src.format == scaled.format) { // No intermediate buffer needed temp = scaled; } else { // Allocate temporary buffer of destination size in source format temp = PixelBox(scaled.getWidth(), scaled.getHeight(), scaled.getDepth(), src.format); buf.bind(new MemoryDataStream(temp.getConsecutiveSize())); temp.data = buf->getPtr(); } // super-optimized: no conversion switch (PixelUtil::getNumElemBytes(src.format)) { case 1: NearestResampler<1>::scale(src, temp); break; case 2: NearestResampler<2>::scale(src, temp); break; case 3: NearestResampler<3>::scale(src, temp); break; case 4: NearestResampler<4>::scale(src, temp); break; case 6: NearestResampler<6>::scale(src, temp); break; case 8: NearestResampler<8>::scale(src, temp); break; case 12: NearestResampler<12>::scale(src, temp); break; case 16: NearestResampler<16>::scale(src, temp); break; default: // never reached assert(false); } if(temp.data != scaled.data) { // Blit temp buffer PixelUtil::bulkPixelConversion(temp, scaled); } break; case FILTER_LINEAR: case FILTER_BILINEAR: switch (src.format) { case PF_L8: case PF_A8: case PF_BYTE_LA: case PF_R8G8B8: case PF_B8G8R8: case PF_R8G8B8A8: case PF_B8G8R8A8: case PF_A8B8G8R8: case PF_A8R8G8B8: case PF_X8B8G8R8: case PF_X8R8G8B8: if(src.format == scaled.format) { // No intermediate buffer needed temp = scaled; } else { // Allocate temp buffer of destination size in source format temp = PixelBox(scaled.getWidth(), scaled.getHeight(), scaled.getDepth(), src.format); buf.bind(new MemoryDataStream(temp.getConsecutiveSize())); temp.data = buf->getPtr(); } // super-optimized: byte-oriented math, no conversion switch (PixelUtil::getNumElemBytes(src.format)) { case 1: LinearResampler_Byte<1>::scale(src, temp); break; case 2: LinearResampler_Byte<2>::scale(src, temp); break; case 3: LinearResampler_Byte<3>::scale(src, temp); break; case 4: LinearResampler_Byte<4>::scale(src, temp); break; default: // never reached assert(false); } if(temp.data != scaled.data) { // Blit temp buffer PixelUtil::bulkPixelConversion(temp, scaled); } break; case PF_FLOAT32_RGB: case PF_FLOAT32_RGBA: if (scaled.format == PF_FLOAT32_RGB || scaled.format == PF_FLOAT32_RGBA) { // float32 to float32, avoid unpack/repack overhead LinearResampler_Float32::scale(src, scaled); break; } // else, fall through default: // non-optimized: floating-point math, performs conversion but always works LinearResampler::scale(src, scaled); } break; default: // fall back to old, slow, wildly incorrect DevIL code #endif #if OGRE_NO_DEVIL == 0 ILuint ImageName; ilGenImages( 1, &ImageName ); ilBindImage( ImageName ); // Convert image from OGRE to current IL image ILUtil::fromOgre(src); // set filter iluImageParameter(ILU_FILTER, getILFilter(filter)); // do the scaling if(!iluScale(scaled.getWidth(), scaled.getHeight(), scaled.getDepth())) { OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, iluErrorString(ilGetError()), "Image::scale" ) ; } ILUtil::toOgre(scaled); ilDeleteImages(1, &ImageName); // return to default filter iluImageParameter(ILU_FILTER, ILU_NEAREST); #else OGRE_EXCEPT( Exception::UNIMPLEMENTED_FEATURE, "Scaling algorithm not implemented without DevIL", "Image::scale" ) ; #endif #ifdef NEWSCALING } #endif }
void GLES2TextureBuffer::upload(const PixelBox &data, const Image::Box &dest) { OGRE_CHECK_GL_ERROR(glBindTexture(mTarget, mTextureID)); if (PixelUtil::isCompressed(data.format)) { if(data.format != mFormat || !data.isConsecutive()) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Compressed images must be consecutive, in the source format", "GLES2TextureBuffer::upload"); GLenum format = GLES2PixelUtil::getClosestGLInternalFormat(mFormat); // Data must be consecutive and at beginning of buffer as PixelStorei not allowed // for compressed formats if (dest.left == 0 && dest.top == 0) { OGRE_CHECK_GL_ERROR(glCompressedTexImage2D(mFaceTarget, mLevel, format, dest.getWidth(), dest.getHeight(), 0, data.getConsecutiveSize(), data.data)); } else { OGRE_CHECK_GL_ERROR(glCompressedTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), format, data.getConsecutiveSize(), data.data)); } } else if (mSoftwareMipmap) { if (data.getWidth() != data.rowPitch) { #if OGRE_NO_GLES3_SUPPORT == 0 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, data.rowPitch)); #else OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); #endif } if (data.getHeight() * data.getWidth() != data.slicePitch) { #if OGRE_NO_GLES3_SUPPORT == 0 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, (data.slicePitch/data.getWidth()))); #else OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); #endif } #if OGRE_NO_GLES3_SUPPORT == 0 if(data.left > 0 || data.top > 0 || data.front > 0) OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_SKIP_PIXELS, data.left + data.rowPitch * data.top + data.slicePitch * data.front)); #endif OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); buildMipmaps(data); } else { if (data.getWidth() != data.rowPitch) { #if OGRE_NO_GLES3_SUPPORT == 0 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, data.rowPitch)); #else OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); #endif } if (data.getHeight() * data.getWidth() != data.slicePitch) { #if OGRE_NO_GLES3_SUPPORT == 0 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, (data.slicePitch/data.getWidth()))); #else OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); #endif } #if OGRE_NO_GLES3_SUPPORT == 0 if(data.left > 0 || data.top > 0 || data.front > 0) OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_SKIP_PIXELS, data.left + data.rowPitch * data.top + data.slicePitch * data.front)); #endif if ((data.getWidth() * PixelUtil::getNumElemBytes(data.format)) & 3) { // Standard alignment of 4 is not right OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); } // LogManager::getSingleton().logMessage("GLES2TextureBuffer::upload - ID: " + StringConverter::toString(mTextureID) + // " Format: " + PixelUtil::getFormatName(data.format) + // " Origin format: " + StringConverter::toString(GLES2PixelUtil::getGLOriginFormat(data.format), 0, std::ios::hex) + // " Data type: " + StringConverter::toString(GLES2PixelUtil::getGLOriginDataType(data.format), 0, ' ', std::ios::hex)); OGRE_CHECK_GL_ERROR(glTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), GLES2PixelUtil::getGLOriginFormat(data.format), GLES2PixelUtil::getGLOriginDataType(data.format), data.data)); } #if OGRE_NO_GLES3_SUPPORT == 0 OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0)); OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0)); #endif OGRE_CHECK_GL_ERROR(glPixelStorei(GL_UNPACK_ALIGNMENT, 4)); }
void GLES2TextureBuffer::upload(const PixelBox &data, const Image::Box &dest) { glBindTexture(mTarget, mTextureID); GL_CHECK_ERROR; if (PixelUtil::isCompressed(data.format)) { if(data.format != mFormat || !data.isConsecutive()) OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Compressed images must be consecutive, in the source format", "GLES2TextureBuffer::upload"); GLenum format = GLES2PixelUtil::getClosestGLInternalFormat(mFormat); // Data must be consecutive and at beginning of buffer as PixelStorei not allowed // for compressed formats if (dest.left == 0 && dest.top == 0) { glCompressedTexImage2D(mFaceTarget, mLevel, format, dest.getWidth(), dest.getHeight(), 0, data.getConsecutiveSize(), data.data); GL_CHECK_ERROR; } else { glCompressedTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), format, data.getConsecutiveSize(), data.data); GL_CHECK_ERROR; } } else if (mSoftwareMipmap) { if (data.getWidth() != data.rowPitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); } if (data.getHeight() * data.getWidth() != data.slicePitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); } glPixelStorei(GL_UNPACK_ALIGNMENT, 1); GL_CHECK_ERROR; buildMipmaps(data); } else { if(data.getWidth() != data.rowPitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); } if(data.getHeight()*data.getWidth() != data.slicePitch) { // TODO OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Unsupported texture format", "GLES2TextureBuffer::upload"); } if ((data.getWidth() * PixelUtil::getNumElemBytes(data.format)) & 3) { // Standard alignment of 4 is not right glPixelStorei(GL_UNPACK_ALIGNMENT, 1); GL_CHECK_ERROR; } glTexSubImage2D(mFaceTarget, mLevel, dest.left, dest.top, dest.getWidth(), dest.getHeight(), GLES2PixelUtil::getGLOriginFormat(data.format), GLES2PixelUtil::getGLOriginDataType(data.format), data.data); } if ((mUsage & TU_AUTOMIPMAP) && !mSoftwareMipmap && (mLevel == 0)) { glGenerateMipmap(mFaceTarget); GL_CHECK_ERROR; } glPixelStorei(GL_UNPACK_ALIGNMENT, 4); GL_CHECK_ERROR; }