PixelBox GL3PlusHardwarePixelBuffer::lockImpl( const Image::Box &lockBox, LockOptions options )
    {
        //Allocate memory for the entire image, as the buffer
        //maynot be freed and be reused in subsequent calls.
        allocateBuffer( PixelUtil::getMemorySize( mWidth, mHeight, mDepth, mFormat ) );

        mBuffer = PixelBox( lockBox.getWidth(), lockBox.getHeight(),
                            lockBox.getDepth(), mFormat, mBuffer.data );
        mCurrentLock = mBuffer;
        mCurrentLock.left    = lockBox.left;
        mCurrentLock.right  += lockBox.left;
        mCurrentLock.top     = lockBox.top;
        mCurrentLock.bottom += lockBox.top;

        if(options != HardwareBuffer::HBL_DISCARD)
        {
            // Download the old contents of the texture
            download( mCurrentLock );
        }
        mCurrentLockOptions = options;
        mLockedBox = lockBox;
        mCurrentLock = mBuffer;

        return mBuffer;
    }
    void GLES2HardwarePixelBuffer::blitFromMemory(const PixelBox &src, const Image::Box &dstBox)
    {
        if (!mBuffer.contains(dstBox))
        {
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                        "Destination box out of range",
                        "GLES2HardwarePixelBuffer::blitFromMemory");
        }

        PixelBox scaled;

        if (src.getWidth() != dstBox.getWidth() ||
            src.getHeight() != dstBox.getHeight() ||
            src.getDepth() != dstBox.getDepth())
        {
            // Scale to destination size.
            // This also does pixel format conversion if needed
            allocateBuffer();
            scaled = mBuffer.getSubVolume(dstBox);
            Image::scale(src, scaled, Image::FILTER_BILINEAR);
        }
#if OGRE_PLATFORM != OGRE_PLATFORM_APPLE_IOS
        else if ((src.format != mFormat) ||                 
                 ((GLES2PixelUtil::getGLOriginFormat(src.format) == 0) && (src.format != PF_R8G8B8)))
#else
        else if (GLES2PixelUtil::getGLOriginFormat(src.format) == 0)
#endif
        {
            // Extents match, but format is not accepted as valid source format for GL
            // do conversion in temporary buffer
            allocateBuffer();
            scaled = mBuffer.getSubVolume(dstBox);
            PixelUtil::bulkPixelConversion(src, scaled);
        }
        else
        {
            allocateBuffer();
            // No scaling or conversion needed
            scaled = src;
            if (src.format == PF_R8G8B8)
            {
                scaled.format = PF_B8G8R8;
                PixelUtil::bulkPixelConversion(src, scaled);
            }
#if OGRE_PLATFORM == OGRE_PLATFORM_NACL
            if (src.format == PF_A8R8G8B8)
            {
                scaled.format = PF_A8B8G8R8;
                PixelUtil::bulkPixelConversion(src, scaled);
            }
#endif
        }

        upload(scaled, dstBox);
        freeBuffer();
    }
    void GLESHardwarePixelBuffer::blitToMemory(const Image::Box &srcBox, const PixelBox &dst)
    {
        if (!mBuffer.contains(srcBox))
        {
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                        "source box out of range",
                        "GLESHardwarePixelBuffer::blitToMemory");
        }

        if (srcBox.left == 0 && srcBox.right == getWidth() &&
            srcBox.top == 0 && srcBox.bottom == getHeight() &&
            srcBox.front == 0 && srcBox.back == getDepth() &&
            dst.getWidth() == getWidth() &&
            dst.getHeight() == getHeight() &&
            dst.getDepth() == getDepth() &&
            GLESPixelUtil::getGLOriginFormat(dst.format) != 0)
        {
            // The direct case: the user wants the entire texture in a format supported by GL
            // so we don't need an intermediate buffer
            download(dst);
        }
        else
        {
            // Use buffer for intermediate copy
            allocateBuffer();
            // Download entire buffer
            download(mBuffer);
            if(srcBox.getWidth() != dst.getWidth() ||
                srcBox.getHeight() != dst.getHeight() ||
                srcBox.getDepth() != dst.getDepth())
            {
                // We need scaling
                Image::scale(mBuffer.getSubVolume(srcBox), dst, Image::FILTER_BILINEAR);
            }
            else
            {
                // Just copy the bit that we need
                PixelUtil::bulkPixelConversion(mBuffer.getSubVolume(srcBox), dst);
            }
            freeBuffer();
        }
    }
    void GL3PlusHardwarePixelBuffer::blitFromMemory(const PixelBox &src, const Image::Box &dstBox)
    {
        if (!mBuffer.contains(dstBox))
        {
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
                        "Destination box out of range",
                        "GL3PlusHardwarePixelBuffer::blitFromMemory");
        }

        PixelBox scaled;

        if (src.getWidth() != dstBox.getWidth() ||
            src.getHeight() != dstBox.getHeight() ||
            src.getDepth() != dstBox.getDepth())
        {
            // Scale to destination size.
            // This also does pixel format conversion if needed.
            allocateBuffer( mSizeInBytes );
            scaled = mBuffer.getSubVolume(dstBox);
            Image::scale(src, scaled, Image::FILTER_BILINEAR);
        }
        else if (GL3PlusPixelUtil::getGLOriginFormat(src.format) == 0)
        {
            // Extents match, but format is not accepted as valid
            // source format for GL. Do conversion in temporary buffer.
            allocateBuffer( mSizeInBytes );
            scaled = mBuffer.getSubVolume(dstBox);
            PixelUtil::bulkPixelConversion(src, scaled);
        }
        else
        {
            allocateBuffer( mSizeInBytes );
            // No scaling or conversion needed.
            scaled = src;
        }

        upload(scaled, dstBox);
        freeBuffer();
    }
    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 PagingLandScapeTexture_InstantBaseTextureEdit::update()
{
    assert (!mMaterial.isNull() && "PagingLandScapeTexture_InstantBaseTextureEdit::update()");
    assert (!mTexture.isNull() && "PagingLandScapeTexture_InstantBaseTextureEdit::update()");
    assert (!mBuffer.isNull() && "PagingLandScapeTexture_InstantBaseTextureEdit::update()");

    // at least deformed once, so need to save texture if asked by user (option)
    mIsModified = true;


    Image::Box rect (0, 0, 0, 0, 0, 1);

    // computes deformation
    PagingLandScapeData2D *data;
    const bool isDeformed = mIsDeformRectModified;
    if (isDeformed)
    {
        rect = mDeformRect;
        data = PagingLandScapeData2DManager::getSingleton().getData2d(mDataX, mDataZ);
//            rect = data->getDeformationRectangle ();
        if (rect.getWidth() && rect.getHeight ())
        {
            rect.right += 1;
            rect.bottom += 1;

            // Do here some alpha map modification based on deformation
            computeInstantBase(data, rect);
        }
    }
    // try to upload only the smallest rectangle containing modification
    if (mIsPaintRectModified)
    {
        if (isDeformed)
        {
            rect.left = std::min (mPaintRect.left, rect.left);
            rect.right = std::max (mPaintRect.right, rect.right);
            rect.top =  std::min (mPaintRect.top, rect.top);
            rect.bottom = std::max (mPaintRect.bottom, rect.bottom);
        } // if (mNeedUpdate)
        else
        {
            rect = mPaintRect;
            rect.right += 1;
            rect.bottom += 1;
        }
    } // if (mIsRectModified)

    // Upload any changes (deformation or )
    if (rect.getWidth() && rect.getHeight ())
    {
        const PixelBox srcBox = mImage.getPixelBox().getSubVolume(rect);
        const PixelBox lock = mBuffer->lock(rect, HardwareBuffer::HBL_DISCARD);
        PixelUtil::bulkPixelConversion(srcBox, lock);
        mBuffer->unlock();
    } // if (rect.getWidth() && rect.getHeight ())

    if (isDeformed)
        data->resetDeformationRectangle ();

    PagingLandScapeTexture::updated ();
}
	//-----------------------------------------------------------------------------  
	PixelBox D3D10HardwarePixelBuffer::lockImpl(const Image::Box lockBox,  LockOptions options)
	{
		// Check for misuse
		if(mUsage & TU_RENDERTARGET)
			OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "DirectX does not allow locking of or directly writing to RenderTargets. Use blitFromMemory if you need the contents.",
			"D3D10HardwarePixelBuffer::lockImpl");	
		// Set extents and format
		// Note that we do not carry over the left/top/front here, since the returned
		// PixelBox will be re-based from the locking point onwards
		PixelBox rval(lockBox.getWidth(), lockBox.getHeight(), lockBox.getDepth(), mFormat);
		// Set locking flags according to options
		D3D10_MAP  flags = D3D10_MAP_WRITE_DISCARD ;
		switch(options)
		{
		case HBL_DISCARD:
			// D3D only likes D3DLOCK_DISCARD if you created the texture with D3DUSAGE_DYNAMIC
			// debug runtime flags this up, could cause problems on some drivers
			if (mUsage & HBU_DYNAMIC)
				flags = D3D10_MAP_WRITE_DISCARD;
			break;
		case HBL_READ_ONLY:
			flags = D3D10_MAP_READ;
			break;
		default: 
			break;
		};

		mDevice.clearStoredErrorMessages();

		// TODO - check return values here
		switch(mParentTexture->getTextureType()) {
		case TEX_TYPE_1D:
			{

				mParentTexture->GetTex1D()->Map(static_cast<UINT>(mSubresourceIndex), flags, 0, &rval.data);
				if (mDevice.isError())
				{
					String errorDescription = mDevice.getErrorDescription();
					OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, 
						"D3D10 device cannot map 1D texture\nError Description:" + errorDescription,
						"D3D10HardwarePixelBuffer::lockImpl");
				}
			}
			break;
		case TEX_TYPE_CUBE_MAP:
		case TEX_TYPE_2D:
			{
				D3D10_MAPPED_TEXTURE2D mappedTex2D;
				mParentTexture->GetTex2D()->Map(static_cast<UINT>(mSubresourceIndex), flags, 0, &mappedTex2D);
				rval.data = mappedTex2D.pData;
				if (mDevice.isError())
				{
					String errorDescription = mDevice.getErrorDescription();
					OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, 
						"D3D10 device cannot map 1D texture\nError Description:" + errorDescription,
						"D3D10HardwarePixelBuffer::lockImpl");
				}
			}
			break;
		case TEX_TYPE_3D:
			{
				D3D10_MAPPED_TEXTURE3D mappedTex3D;
				mParentTexture->GetTex3D()->Map(static_cast<UINT>(mSubresourceIndex), flags, 0, &mappedTex3D);
				rval.data = mappedTex3D.pData;
				if (mDevice.isError())
				{
					String errorDescription = mDevice.getErrorDescription();
					OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, 
						"D3D10 device cannot map 1D texture\nError Description:" + errorDescription,
						"D3D10HardwarePixelBuffer::lockImpl");
				}
			}
			break;
		}




		return rval;
	}
	//-----------------------------------------------------------------------------  
	void D3D10HardwarePixelBuffer::blitFromMemory(const PixelBox &src, const Image::Box &dstBox)
	{
		bool isDds = false;
		switch(mFormat)
		{
		case PF_DXT1:
		case PF_DXT2:
		case PF_DXT3:
		case PF_DXT4:
		case PF_DXT5:
			isDds = true;
			break;
		default:

			break;
		}

		if (isDds && (dstBox.getWidth() % 4 != 0 || dstBox.getHeight() % 4 != 0 ))
		{
			return;
		}


		// for scoped deletion of conversion buffer
		MemoryDataStreamPtr buf;
		PixelBox converted = src;

		D3D10_BOX dstBoxDx10 = OgreImageBoxToDx10Box(dstBox);

		// convert to pixelbuffer's native format if necessary
		if (src.format != mFormat)
		{
			buf.bind(new MemoryDataStream(
				PixelUtil::getMemorySize(src.getWidth(), src.getHeight(), src.getDepth(),
				mFormat)));
			converted = PixelBox(src.getWidth(), src.getHeight(), src.getDepth(), mFormat, buf->getPtr());
			PixelUtil::bulkPixelConversion(src, converted);
		}

		// In d3d10 the Row Pitch is defined as: "The size of one row of the source data" and not 
		// the same as the OGRE row pitch - meaning that we need to multiple the OGRE row pitch 
		// with the size in bytes of the element to get the d3d10 row pitch. 
		UINT d3dRowPitch = static_cast<UINT>(converted.rowPitch) * static_cast<UINT>(PixelUtil::getNumElemBytes(mFormat));


		switch(mParentTexture->getTextureType()) {
		case TEX_TYPE_1D:
			{

				mDevice->UpdateSubresource( 
					mParentTexture->GetTex1D(), 
					0,
					&dstBoxDx10,
					converted.data,
					0,
					0 );
				if (mDevice.isError())
				{
					String errorDescription = mDevice.getErrorDescription();
					OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, 
						"D3D10 device cannot update 1d subresource\nError Description:" + errorDescription,
						"D3D10HardwarePixelBuffer::blitFromMemory");
				}
			}
			break;
		case TEX_TYPE_CUBE_MAP:
		case TEX_TYPE_2D:
			{
				mDevice->UpdateSubresource( 
					mParentTexture->GetTex2D(), 
					static_cast<UINT>(mSubresourceIndex),
					&dstBoxDx10,
					converted.data,
					d3dRowPitch,
					mFace );
				if (mDevice.isError())
				{
					String errorDescription = mDevice.getErrorDescription();
					OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, 
						"D3D10 device cannot update 2d subresource\nError Description:" + errorDescription,
						"D3D10HardwarePixelBuffer::blitFromMemory");
				}
			}
			break;
		case TEX_TYPE_3D:
			{
				mDevice->UpdateSubresource( 
					mParentTexture->GetTex2D(), 
					static_cast<UINT>(mSubresourceIndex),
					&dstBoxDx10,
					converted.data,
					d3dRowPitch,
					static_cast<UINT>(converted.slicePitch)
					);
				if (mDevice.isError())
				{
					String errorDescription = mDevice.getErrorDescription();
					OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, 
						"D3D10 device cannot update 3d subresource\nError Description:" + errorDescription,
						"D3D10HardwarePixelBuffer::blitFromMemory");
				}
			}
			break;
		}


		if (!isDds)
		{
			_genMipmaps();
		}

	}
	void D3D10HardwarePixelBuffer::blit(const HardwarePixelBufferSharedPtr &rsrc, const Image::Box &srcBox, const Image::Box &dstBox)
	{
		if (
			(srcBox.getWidth() != dstBox.getWidth())
			|| (srcBox.getHeight() != dstBox.getHeight())
			|| (srcBox.getDepth() != dstBox.getDepth())
			)
		{
			OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, 
				"D3D10 device cannot copy a subresource - source and dest size are not the same and they have to be the same in DX10.",
				"D3D10HardwarePixelBuffer::blit");
		}

		D3D10_BOX srcBoxDx10 = OgreImageBoxToDx10Box(srcBox);


		D3D10HardwarePixelBuffer * rsrcDx10 = static_cast<D3D10HardwarePixelBuffer *>(rsrc.get());

		switch(mParentTexture->getTextureType()) {
		case TEX_TYPE_1D:
			{

				mDevice->CopySubresourceRegion(
					mParentTexture->GetTex1D(), 
					static_cast<UINT>(mSubresourceIndex),
					static_cast<UINT>(dstBox.left),
					0,
					0,
					rsrcDx10->mParentTexture->GetTex1D(),
					static_cast<UINT>(rsrcDx10->mSubresourceIndex),
					&srcBoxDx10);
				if (mDevice.isError())
				{
					String errorDescription = mDevice.getErrorDescription();
					OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, 
						"D3D10 device cannot copy 1d subresource Region\nError Description:" + errorDescription,
						"D3D10HardwarePixelBuffer::blit");
				}			
			}
			break;
		case TEX_TYPE_CUBE_MAP:
		case TEX_TYPE_2D:
			{
				mDevice->CopySubresourceRegion(
					mParentTexture->GetTex2D(), 
					static_cast<UINT>(mSubresourceIndex),
					static_cast<UINT>(dstBox.left),
					static_cast<UINT>(dstBox.top),
					mFace,
					rsrcDx10->mParentTexture->GetTex2D(),
					static_cast<UINT>(rsrcDx10->mSubresourceIndex),
					&srcBoxDx10);
				if (mDevice.isError())
				{
					String errorDescription = mDevice.getErrorDescription();
					OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, 
						"D3D10 device cannot copy 2d subresource Region\nError Description:" + errorDescription,
						"D3D10HardwarePixelBuffer::blit");
				}
			}
			break;
		case TEX_TYPE_3D:
			{
				mDevice->CopySubresourceRegion(
					mParentTexture->GetTex2D(), 
					static_cast<UINT>(mSubresourceIndex),
					static_cast<UINT>(dstBox.left),
					static_cast<UINT>(dstBox.top),
					static_cast<UINT>(dstBox.front),
					rsrcDx10->mParentTexture->GetTex2D(),
					static_cast<UINT>(rsrcDx10->mSubresourceIndex),
					&srcBoxDx10);
				if (mDevice.isError())
				{
					String errorDescription = mDevice.getErrorDescription();
					OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, 
						"D3D10 device cannot copy 3d subresource Region\nError Description:" + errorDescription,
						"D3D10HardwarePixelBuffer::blit");
				}
			}
			break;
		}


		_genMipmaps();

	}
void WriteToTexture(const String &str, TexturePtr destTexture, Image::Box destRectangle, Font* font, const ColourValue &color, char justify,  bool wordwrap)
{
    using namespace Ogre;

    if (destTexture->getHeight() < destRectangle.bottom)
        destRectangle.bottom = destTexture->getHeight();
    if (destTexture->getWidth() < destRectangle.right)
        destRectangle.right = destTexture->getWidth();

    if (!font->isLoaded())
        font->load();

    TexturePtr fontTexture = (TexturePtr) TextureManager::getSingleton().getByName(font->getMaterial()->getTechnique(0)->getPass(0)->getTextureUnitState(0)->getTextureName());

    HardwarePixelBufferSharedPtr fontBuffer = fontTexture->getBuffer();
    HardwarePixelBufferSharedPtr destBuffer = destTexture->getBuffer();

    PixelBox destPb = destBuffer->lock(destRectangle,HardwareBuffer::HBL_NORMAL);

    // The font texture buffer was created write only...so we cannot read it back :o). One solution is to copy the buffer  instead of locking it. (Maybe there is a way to create a font texture which is not write_only ?)

    // create a buffer
    size_t nBuffSize = fontBuffer->getSizeInBytes();
    unsigned char* buffer = (unsigned char*)calloc(nBuffSize, sizeof(unsigned char));

    // create pixel box using the copy of the buffer
    PixelBox fontPb(fontBuffer->getWidth(), fontBuffer->getHeight(),fontBuffer->getDepth(), fontBuffer->getFormat(), buffer);
    fontBuffer->blitToMemory(fontPb);

    unsigned char* fontData = static_cast<unsigned char*>( fontPb.data );
    unsigned char* destData = static_cast<unsigned char*>( destPb.data );

    const size_t fontPixelSize = PixelUtil::getNumElemBytes(fontPb.format);
    const size_t destPixelSize = PixelUtil::getNumElemBytes(destPb.format);

    const size_t fontRowPitchBytes = fontPb.rowPitch * fontPixelSize;
    const size_t destRowPitchBytes = destPb.rowPitch * destPixelSize;

    Box *GlyphTexCoords;
    GlyphTexCoords = new Box[str.size()];

    Font::UVRect glypheTexRect;
    size_t charheight = 0;
    size_t charwidth = 0;

    for (unsigned int i = 0; i < str.size(); i++)
    {
        if ((str[i] != '\t') && (str[i] != '\n') && (str[i] != ' '))
        {
            glypheTexRect = font->getGlyphTexCoords(str[i]);
            GlyphTexCoords[i].left = glypheTexRect.left * fontTexture->getSrcWidth();
            GlyphTexCoords[i].top = glypheTexRect.top * fontTexture->getSrcHeight();
            GlyphTexCoords[i].right = glypheTexRect.right * fontTexture->getSrcWidth();
            GlyphTexCoords[i].bottom = glypheTexRect.bottom * fontTexture->getSrcHeight();

            if (GlyphTexCoords[i].getHeight() > charheight)
                charheight = GlyphTexCoords[i].getHeight();
            if (GlyphTexCoords[i].getWidth() > charwidth)
                charwidth = GlyphTexCoords[i].getWidth();
        }

    }

    size_t cursorX = 0;
    size_t cursorY = 0;
    size_t lineend = destRectangle.getWidth();
    bool carriagreturn = true;
    for (unsigned int strindex = 0; strindex < str.size(); strindex++)
    {
        switch(str[strindex])
        {
        case ' ':
            cursorX += charwidth;
            break;
        case '\t':
            cursorX += charwidth * 3;
            break;
        case '\n':
            cursorY += charheight;
            carriagreturn = true;
            break;
        default:
        {
            //wrapping
            if ((cursorX + GlyphTexCoords[strindex].getWidth()> lineend) && !carriagreturn )
            {
                cursorY += charheight;
                carriagreturn = true;
            }

            //justify
            if (carriagreturn)
            {
                size_t l = strindex;
                size_t textwidth = 0;
                size_t wordwidth = 0;

                while( (l < str.size() ) && (str[l] != '\n'))
                {
                    wordwidth = 0;

                    switch (str[l])
                    {
                    case ' ':
                        wordwidth = charwidth;
                        ++l;
                        break;
                    case '\t':
                        wordwidth = charwidth *3;
                        ++l;
                        break;
                    case '\n':
                        l = str.size();
                    }

                    if (wordwrap)
                        while((l < str.size()) && (str[l] != ' ') && (str[l] != '\t') && (str[l] != '\n'))
                        {
                            wordwidth += GlyphTexCoords[l].getWidth();
                            ++l;
                        }
                    else
                    {
                        wordwidth += GlyphTexCoords[l].getWidth();
                        l++;
                    }

                    if ((textwidth + wordwidth) <= destRectangle.getWidth())
                        textwidth += (wordwidth);
                    else
                        break;
                }

                if ((textwidth == 0) && (wordwidth > destRectangle.getWidth()))
                    textwidth = destRectangle.getWidth();

                switch (justify)
                {
                case 'c':
                    cursorX = (destRectangle.getWidth() - textwidth)/2;
                    lineend = destRectangle.getWidth() - cursorX;
                    break;

                case 'r':
                    cursorX = (destRectangle.getWidth() - textwidth);
                    lineend = destRectangle.getWidth();
                    break;

                default:
                    cursorX = 0;
                    lineend = textwidth;
                    break;
                }

                carriagreturn = false;
            }

            //abort - net enough space to draw
            if ((cursorY + charheight) > destRectangle.getHeight())
                goto stop;

            //draw pixel by pixel
            for (size_t i = 0; i < GlyphTexCoords[strindex].getHeight(); i++ )
                for (size_t j = 0; j < GlyphTexCoords[strindex].getWidth(); j++)
                {
                    float alpha =  color.a * (fontData[(i + GlyphTexCoords[strindex].top) * fontRowPitchBytes + (j + GlyphTexCoords[strindex].left) * fontPixelSize +1 ] / 255.0);
                    float invalpha = 1.0 - alpha;
                    size_t offset = (i + cursorY) * destRowPitchBytes + (j + cursorX) * destPixelSize;
                    ColourValue pix;
                    PixelUtil::unpackColour(&pix,destPb.format,&destData[offset]);
                    pix = (pix * invalpha) + (color * alpha);
                    PixelUtil::packColour(pix,destPb.format,&destData[offset]);
                }

            cursorX += GlyphTexCoords[strindex].getWidth();
        }//default
        }//switch
    }//for

stop:
    delete[] GlyphTexCoords;

    destBuffer->unlock();

    // Free the memory allocated for the buffer
    free(buffer);
    buffer = 0;
}
    //-----------------------------------------------------------------------------  
    // Very fast texture-to-texture blitter and hardware bi/trilinear scaling implementation using FBO
    // Destination texture must be 1D, 2D, 3D, or Cube
    // Source texture must be 1D, 2D or 3D
    // Supports compressed formats as both source and destination format, it will use the hardware DXT compressor
    // if available.
    // @author W.J. van der Laan
    void GLES2TextureBuffer::blitFromTexture(GLES2TextureBuffer *src, const Image::Box &srcBox, const Image::Box &dstBox)
    {
		return; // todo - add a shader attach...
//        std::cerr << "GLES2TextureBuffer::blitFromTexture " <<
//        src->mTextureID << ":" << srcBox.left << "," << srcBox.top << "," << srcBox.right << "," << srcBox.bottom << " " << 
//        mTextureID << ":" << dstBox.left << "," << dstBox.top << "," << dstBox.right << "," << dstBox.bottom << std::endl;

        // Store reference to FBO manager
        GLES2FBOManager *fboMan = static_cast<GLES2FBOManager *>(GLES2RTTManager::getSingletonPtr());
        
        RenderSystem* rsys = Root::getSingleton().getRenderSystem();
        rsys->_disableTextureUnitsFrom(0);
		glActiveTexture(GL_TEXTURE0);

        // Disable alpha, depth and scissor testing, disable blending, 
        // and disable culling
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_SCISSOR_TEST);
        glDisable(GL_BLEND);
        glDisable(GL_CULL_FACE);

        // Set up source texture
        glBindTexture(src->mTarget, src->mTextureID);
        GL_CHECK_ERROR;
        
        // Set filtering modes depending on the dimensions and source
        if(srcBox.getWidth()==dstBox.getWidth() &&
           srcBox.getHeight()==dstBox.getHeight() &&
           srcBox.getDepth()==dstBox.getDepth())
        {
            // Dimensions match -- use nearest filtering (fastest and pixel correct)
            glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            GL_CHECK_ERROR;
            glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            GL_CHECK_ERROR;
        }
        else
        {
            // Dimensions don't match -- use bi or trilinear filtering depending on the
            // source texture.
            if(src->mUsage & TU_AUTOMIPMAP)
            {
                // Automatic mipmaps, we can safely use trilinear filter which
                // brings greatly improved quality for minimisation.
                glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
                GL_CHECK_ERROR;
                glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    
                GL_CHECK_ERROR;
            }
            else
            {
                // Manual mipmaps, stay safe with bilinear filtering so that no
                // intermipmap leakage occurs.
                glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                GL_CHECK_ERROR;
                glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                GL_CHECK_ERROR;
            }
        }
        // Clamp to edge (fastest)
        glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        GL_CHECK_ERROR;
        glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        GL_CHECK_ERROR;
        
        // Store old binding so it can be restored later
        GLint oldfb;
        glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
        GL_CHECK_ERROR;

        // Set up temporary FBO
        glBindFramebuffer(GL_FRAMEBUFFER, fboMan->getTemporaryFBO());
        GL_CHECK_ERROR;

        GLuint tempTex = 0;
        if(!fboMan->checkFormat(mFormat))
        {
            // If target format not directly supported, create intermediate texture
            GLenum tempFormat = GLES2PixelUtil::getClosestGLInternalFormat(fboMan->getSupportedAlternative(mFormat));
            glGenTextures(1, &tempTex);
            GL_CHECK_ERROR;
            glBindTexture(GL_TEXTURE_2D, tempTex);
            GL_CHECK_ERROR;
#if GL_APPLE_texture_max_level
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL_APPLE, 0);
            GL_CHECK_ERROR;
#endif
            // Allocate temporary texture of the size of the destination area
            glTexImage2D(GL_TEXTURE_2D, 0, tempFormat, 
                         GLES2PixelUtil::optionalPO2(dstBox.getWidth()), GLES2PixelUtil::optionalPO2(dstBox.getHeight()), 
                         0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
            GL_CHECK_ERROR;
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                      GL_TEXTURE_2D, tempTex, 0);
            GL_CHECK_ERROR;
            // Set viewport to size of destination slice
            glViewport(0, 0, dstBox.getWidth(), dstBox.getHeight());
            GL_CHECK_ERROR;
        }
        else
        {
            // We are going to bind directly, so set viewport to size and position of destination slice
            glViewport(dstBox.left, dstBox.top, dstBox.getWidth(), dstBox.getHeight());
            GL_CHECK_ERROR;
        }
        
        // Process each destination slice
        for(size_t slice=dstBox.front; slice<dstBox.back; ++slice)
        {
            if(!tempTex)
            {
                // Bind directly
                bindToFramebuffer(GL_COLOR_ATTACHMENT0, slice);
            }

            /// Calculate source texture coordinates
            float u1 = (float)srcBox.left / (float)src->mWidth;
            float v1 = (float)srcBox.top / (float)src->mHeight;
            float u2 = (float)srcBox.right / (float)src->mWidth;
            float v2 = (float)srcBox.bottom / (float)src->mHeight;
            /// Calculate source slice for this destination slice
            float w = (float)(slice - dstBox.front) / (float)dstBox.getDepth();
            /// Get slice # in source
            w = w * (float)srcBox.getDepth() + srcBox.front;
            /// Normalise to texture coordinate in 0.0 .. 1.0
            w = (w+0.5f) / (float)src->mDepth;
            
            /// Finally we're ready to rumble	
            glBindTexture(src->mTarget, src->mTextureID);
            GL_CHECK_ERROR;
            glEnable(src->mTarget);
            GL_CHECK_ERROR;

            GLfloat squareVertices[] = {
               -1.0f, -1.0f,
                1.0f, -1.0f,
               -1.0f,  1.0f,
                1.0f,  1.0f,
            };
            GLfloat texCoords[] = {
                u1, v1, w,
                u2, v1, w,
                u2, v2, w,
                u1, v2, w
            };

            GLuint posAttrIndex = 0;
            GLuint texAttrIndex = 0;
            if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_SEPARATE_SHADER_OBJECTS))
            {
                GLSLESProgramPipeline* programPipeline = GLSLESProgramPipelineManager::getSingleton().getActiveProgramPipeline();
                posAttrIndex = (GLuint)programPipeline->getAttributeIndex(VES_POSITION, 0);
                texAttrIndex = (GLuint)programPipeline->getAttributeIndex(VES_TEXTURE_COORDINATES, 0);
            }
            else
            {
                GLSLESLinkProgram* linkProgram = GLSLESLinkProgramManager::getSingleton().getActiveLinkProgram();
                posAttrIndex = (GLuint)linkProgram->getAttributeIndex(VES_POSITION, 0);
                texAttrIndex = (GLuint)linkProgram->getAttributeIndex(VES_TEXTURE_COORDINATES, 0);
            }

            // Draw the textured quad
            glVertexAttribPointer(posAttrIndex,
                                  2,
                                  GL_FLOAT,
                                  0,
                                  0,
                                  squareVertices);
            GL_CHECK_ERROR;
            glEnableVertexAttribArray(posAttrIndex);
            GL_CHECK_ERROR;
            glVertexAttribPointer(texAttrIndex,
                                  3,
                                  GL_FLOAT,
                                  0,
                                  0,
                                  texCoords);
            GL_CHECK_ERROR;
            glEnableVertexAttribArray(texAttrIndex);
            GL_CHECK_ERROR;

            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
            GL_CHECK_ERROR;

            glDisable(src->mTarget);
            GL_CHECK_ERROR;

            if(tempTex)
            {
                // Copy temporary texture
                glBindTexture(mTarget, mTextureID);
                GL_CHECK_ERROR;
                switch(mTarget)
                {
                    case GL_TEXTURE_2D:
                    case GL_TEXTURE_CUBE_MAP:
                        glCopyTexSubImage2D(mFaceTarget, mLevel, 
                                            dstBox.left, dstBox.top, 
                                            0, 0, dstBox.getWidth(), dstBox.getHeight());
                        GL_CHECK_ERROR;
                        break;
                }
            }
        }
        // Finish up 
        if(!tempTex)
        {
            // Generate mipmaps
            if(mUsage & TU_AUTOMIPMAP)
            {
                glBindTexture(mTarget, mTextureID);
                GL_CHECK_ERROR;
                glGenerateMipmap(mTarget);
                GL_CHECK_ERROR;
            }
        }
        
        // Reset source texture to sane state
        glBindTexture(src->mTarget, src->mTextureID);
        GL_CHECK_ERROR;
        
        // Detach texture from temporary framebuffer
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                     GL_RENDERBUFFER, 0);
        GL_CHECK_ERROR;
        // Restore old framebuffer
        glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
        GL_CHECK_ERROR;
        glDeleteTextures(1, &tempTex);
        GL_CHECK_ERROR;
    }
//-----------------------------------------------------------------------------
void D3D11HardwarePixelBuffer::blitFromMemory(const PixelBox &src, const Image::Box &dstBox)
{
    bool isDds = false;
    switch(mFormat)
    {
    case PF_DXT1:
    case PF_DXT2:
    case PF_DXT3:
    case PF_DXT4:
    case PF_DXT5:
        isDds = true;
        break;
    default:

        break;
    }

    if (isDds && (dstBox.getWidth() % 4 != 0 || dstBox.getHeight() % 4 != 0 ))
    {
        return;
    }


    // for scoped deletion of conversion buffer
    MemoryDataStreamPtr buf;
    PixelBox converted = src;

    D3D11_BOX dstBoxDx11 = OgreImageBoxToDx11Box(dstBox);
    dstBoxDx11.front = 0;
    dstBoxDx11.back = converted.getDepth();

    // convert to pixelbuffer's native format if necessary
    if (src.format != mFormat)
    {
        buf.bind(new MemoryDataStream(
                     PixelUtil::getMemorySize(src.getWidth(), src.getHeight(), src.getDepth(),
                                              mFormat)));
        converted = PixelBox(src.getWidth(), src.getHeight(), src.getDepth(), mFormat, buf->getPtr());
        PixelUtil::bulkPixelConversion(src, converted);
    }

    if (mUsage & HBU_DYNAMIC)
    {
        size_t sizeinbytes;
        if (PixelUtil::isCompressed(converted.format))
        {
            // D3D wants the width of one row of cells in bytes
            if (converted.format == PF_DXT1)
            {
                // 64 bits (8 bytes) per 4x4 block
                sizeinbytes = std::max<size_t>(1, converted.getWidth() / 4) * std::max<size_t>(1, converted.getHeight() / 4) * 8;
            }
            else
            {
                // 128 bits (16 bytes) per 4x4 block
                sizeinbytes = std::max<size_t>(1, converted.getWidth() / 4) * std::max<size_t>(1, converted.getHeight() / 4) * 16;
            }
        }
        else
        {
            sizeinbytes = converted.getHeight() * converted.getWidth() * PixelUtil::getNumElemBytes(converted.format);
        }

        const Ogre::PixelBox &locked = lock(dstBox, HBL_DISCARD);

        memcpy(locked.data, converted.data, sizeinbytes);

        unlock();
    }
    else
    {
        size_t rowWidth;
        if (PixelUtil::isCompressed(converted.format))
        {
            // D3D wants the width of one row of cells in bytes
            if (converted.format == PF_DXT1)
            {
                // 64 bits (8 bytes) per 4x4 block
                rowWidth = (converted.rowPitch / 4) * 8;
            }
            else
            {
                // 128 bits (16 bytes) per 4x4 block
                rowWidth = (converted.rowPitch / 4) * 16;
            }
        }
        else
        {
            rowWidth = converted.rowPitch * PixelUtil::getNumElemBytes(converted.format);
        }

        switch(mParentTexture->getTextureType()) {
        case TEX_TYPE_1D:
        {
            D3D11RenderSystem* rsys = reinterpret_cast<D3D11RenderSystem*>(Root::getSingleton().getRenderSystem());
            if (rsys->_getFeatureLevel() >= D3D_FEATURE_LEVEL_10_0)
            {
                mDevice.GetImmediateContext()->UpdateSubresource(
                    mParentTexture->GetTex1D(),
                    0,
                    &dstBoxDx11,
                    converted.data,
                    rowWidth,
                    0 );
                if (mDevice.isError())
                {
                    String errorDescription = mDevice.getErrorDescription();
                    OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
                                "D3D11 device cannot update 1d subresource\nError Description:" + errorDescription,
                                "D3D11HardwarePixelBuffer::blitFromMemory");
                }
                break; // For Feature levels that do not support 1D textures, revert to creating a 2D texture.
            }
        }
        case TEX_TYPE_CUBE_MAP:
        case TEX_TYPE_2D:
        {
            mDevice.GetImmediateContext()->UpdateSubresource(
                mParentTexture->GetTex2D(),
                D3D11CalcSubresource(static_cast<UINT>(mSubresourceIndex), mFace, mParentTexture->getNumMipmaps()+1),
                &dstBoxDx11,
                converted.data,
                rowWidth,
                0 );
            if (mDevice.isError())
            {
                String errorDescription = mDevice.getErrorDescription();
                OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
                            "D3D11 device cannot update 2d subresource\nError Description:" + errorDescription,
                            "D3D11HardwarePixelBuffer::blitFromMemory");
            }
        }
        break;
        case TEX_TYPE_2D_ARRAY:
        {
            mDevice.GetImmediateContext()->UpdateSubresource(
                mParentTexture->GetTex2D(),
                D3D11CalcSubresource(static_cast<UINT>(mSubresourceIndex), src.front, mParentTexture->getNumMipmaps()+1),
                &dstBoxDx11,
                converted.data,
                rowWidth,
                0 );
            if (mDevice.isError())
            {
                String errorDescription = mDevice.getErrorDescription();
                OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
                            "D3D11 device cannot update 2d array subresource\nError Description:" + errorDescription,
                            "D3D11HardwarePixelBuffer::blitFromMemory");
            }
        }
        break;
        case TEX_TYPE_3D:
        {
            // copied from dx9
            size_t sliceWidth;
            if (PixelUtil::isCompressed(converted.format))
            {
                // D3D wants the width of one slice of cells in bytes
                if (converted.format == PF_DXT1)
                {
                    // 64 bits (8 bytes) per 4x4 block
                    sliceWidth = (converted.slicePitch / 16) * 8;
                }
                else
                {
                    // 128 bits (16 bytes) per 4x4 block
                    sliceWidth = (converted.slicePitch / 16) * 16;
                }

            }
            else
            {
                sliceWidth = converted.slicePitch * PixelUtil::getNumElemBytes(converted.format);
            }

            mDevice.GetImmediateContext()->UpdateSubresource(
                mParentTexture->GetTex3D(),
                static_cast<UINT>(mSubresourceIndex),
                &dstBoxDx11,
                converted.data,
                rowWidth,
                sliceWidth
            );
            if (mDevice.isError())
            {
                String errorDescription = mDevice.getErrorDescription();
                OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR,
                            "D3D11 device cannot update 3d subresource\nError Description:" + errorDescription,
                            "D3D11HardwarePixelBuffer::blitFromMemory");
            }
        }
        break;
        }

        if (!isDds)
        {
            _genMipmaps();
        }
    }

}
//-----------------------------------------------------------------------------
PixelBox D3D11HardwarePixelBuffer::lockImpl(const Image::Box lockBox,  LockOptions options)
{
    // Check for misuse
    if(mUsage & TU_RENDERTARGET)
        OGRE_EXCEPT(Exception::ERR_RENDERINGAPI_ERROR, "DirectX does not allow locking of or directly writing to RenderTargets. Use blitFromMemory if you need the contents.",
                    "D3D11HardwarePixelBuffer::lockImpl");

    mLockBox = lockBox;

    // Set extents and format
    // Note that we do not carry over the left/top/front here, since the returned
    // PixelBox will be re-based from the locking point onwards
    PixelBox rval(lockBox.getWidth(), lockBox.getHeight(), lockBox.getDepth(), mFormat);
    // Set locking flags according to options
    D3D11_MAP  flags = D3D11_MAP_WRITE_DISCARD ;
    switch(options)
    {
    case HBL_NO_OVERWRITE:
        flags = D3D11_MAP_WRITE_NO_OVERWRITE;
        break;
    case HBL_NORMAL:
        flags = D3D11_MAP_READ_WRITE;
        break;
    case HBL_DISCARD:
        flags = D3D11_MAP_WRITE_DISCARD;
        break;
    case HBL_READ_ONLY:
        flags = D3D11_MAP_READ;
        break;
    case HBL_WRITE_ONLY:
        flags = D3D11_MAP_WRITE;
        break;
    default:
        break;
    };

    size_t offset = 0;

    if(mUsage == HBU_STATIC || mUsage & HBU_DYNAMIC)
    {
        if(mUsage == HBU_STATIC || options == HBL_READ_ONLY || options == HBL_NORMAL || options == HBL_WRITE_ONLY)
            rval.data = _mapstagingbuffer(flags);
        else
            _map(mParentTexture->getTextureResource(), flags, rval);

        // calculate the offset in bytes
        offset = D3D11Mappings::_getSizeInBytes(rval.format, rval.left, rval.front);
        // add the offset, so the right memory will be changed
        //rval.data = static_cast<int*>(rval.data) + offset;
    }
    else
    {
        size_t sizeOfImage = rval.getConsecutiveSize();
        mDataForStaticUsageLock = new int8[sizeOfImage];
        rval.data = mDataForStaticUsageLock;
    }
    // save without offset
    mCurrentLock = rval;
    mCurrentLockOptions = options;

    // add the offset, so the right memory will be changed
    rval.data = static_cast<int*>(rval.data) + offset;

    return rval;
}
    //-----------------------------------------------------------------------------  
    // blitFromMemory doing hardware trilinear scaling
    void GLES2TextureBuffer::blitFromMemory(const PixelBox &src_orig, const Image::Box &dstBox)
    {
        // Fall back to normal GLHardwarePixelBuffer::blitFromMemory in case 
        // - FBO is not supported
        // - Either source or target is luminance due doesn't looks like supported by hardware
        // - the source dimensions match the destination ones, in which case no scaling is needed
        // TODO: Check that extension is NOT available
        if(PixelUtil::isLuminance(src_orig.format) ||
           PixelUtil::isLuminance(mFormat) ||
           (src_orig.getWidth() == dstBox.getWidth() &&
            src_orig.getHeight() == dstBox.getHeight() &&
            src_orig.getDepth() == dstBox.getDepth()))
        {
            GLES2HardwarePixelBuffer::blitFromMemory(src_orig, dstBox);
            return;
        }
        if(!mBuffer.contains(dstBox))
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Destination box out of range",
                        "GLES2TextureBuffer::blitFromMemory");
        // For scoped deletion of conversion buffer
        MemoryDataStreamPtr buf;
        PixelBox src;
        
        // First, convert the srcbox to a OpenGL compatible pixel format
        if(GLES2PixelUtil::getGLOriginFormat(src_orig.format) == 0)
        {
            // Convert to buffer internal format
            buf.bind(OGRE_NEW MemoryDataStream(PixelUtil::getMemorySize(src_orig.getWidth(), src_orig.getHeight(),
                                                                        src_orig.getDepth(), mFormat)));
            src = PixelBox(src_orig.getWidth(), src_orig.getHeight(), src_orig.getDepth(), mFormat, buf->getPtr());
            PixelUtil::bulkPixelConversion(src_orig, src);
        }
        else
        {
            // No conversion needed
            src = src_orig;
        }
        
        // Create temporary texture to store source data
        GLuint id;
        GLenum target =
#if OGRE_NO_GLES3_SUPPORT == 0
        (src.getDepth() != 1) ? GL_TEXTURE_3D :
#endif
            GL_TEXTURE_2D;

        GLsizei width = GLES2PixelUtil::optionalPO2(src.getWidth());
        GLsizei height = GLES2PixelUtil::optionalPO2(src.getHeight());
        GLenum format = GLES2PixelUtil::getClosestGLInternalFormat(src.format);
        GLenum datatype = GLES2PixelUtil::getGLOriginDataType(src.format);

        // Generate texture name
        OGRE_CHECK_GL_ERROR(glGenTextures(1, &id));
        
        // Set texture type
        OGRE_CHECK_GL_ERROR(glBindTexture(target, id));

#if GL_APPLE_texture_max_level && OGRE_PLATFORM != OGRE_PLATFORM_NACL
        OGRE_CHECK_GL_ERROR(glTexParameteri(target, GL_TEXTURE_MAX_LEVEL_APPLE, 1000 ));
#elif OGRE_NO_GLES3_SUPPORT == 0
        OGRE_CHECK_GL_ERROR(glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 1000 ));
#endif

        // Allocate texture memory
#if OGRE_NO_GLES3_SUPPORT == 0
        if(target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY)
            glTexImage3D(target, 0, src.format, src.getWidth(), src.getHeight(), src.getDepth(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
        else
#endif
            OGRE_CHECK_GL_ERROR(glTexImage2D(target, 0, format, width, height, 0, format, datatype, 0));

        // GL texture buffer
        GLES2TextureBuffer tex(StringUtil::BLANK, target, id, width, height, format, src.format,
                              0, 0, (Usage)(TU_AUTOMIPMAP|HBU_STATIC_WRITE_ONLY), false, false, 0);
        
        // Upload data to 0,0,0 in temporary texture
        Image::Box tempTarget(0, 0, 0, src.getWidth(), src.getHeight(), src.getDepth());
        tex.upload(src, tempTarget);
        
        // Blit
        blitFromTexture(&tex, tempTarget, dstBox);
        
        // Delete temp texture
        OGRE_CHECK_GL_ERROR(glDeleteTextures(1, &id));
    }
    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));
    }
    //-----------------------------------------------------------------------------  
    // Very fast texture-to-texture blitter and hardware bi/trilinear scaling implementation using FBO
    // Destination texture must be 2D
    // Source texture must be 2D
    // Supports compressed formats as both source and destination format, it will use the hardware DXT compressor
    // if available.
    // @author W.J. van der Laan
    void GLESTextureBuffer::blitFromTexture(GLESTextureBuffer *src, const Image::Box &srcBox, const Image::Box &dstBox)
    {
        if(Root::getSingleton().getRenderSystem()->getCapabilities()->hasCapability(RSC_FBO) == false)
        {
            // the following code depends on FBO support, it crashes if FBO is not supported.
            // TODO - write PBUFFER version of this function or a version that doesn't require FBO
            return; // for now - do nothing.
        }

//        std::cerr << "GLESTextureBuffer::blitFromTexture " <<
//        src->mTextureID << ":" << srcBox.left << "," << srcBox.top << "," << srcBox.right << "," << srcBox.bottom << " " << 
//        mTextureID << ":" << dstBox.left << "," << dstBox.top << "," << dstBox.right << "," << dstBox.bottom << std::endl;

        // Store reference to FBO manager
        GLESFBOManager *fboMan = static_cast<GLESFBOManager *>(GLESRTTManager::getSingletonPtr());
        
        // Save and clear GL state for rendering
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |  GL_STENCIL_BUFFER_BIT);
        
        RenderSystem* rsys = Root::getSingleton().getRenderSystem();
        rsys->_disableTextureUnitsFrom(0);

        // Disable alpha, depth and scissor testing, disable blending, 
        // disable culling, disble lighting, disable fog and reset foreground
        // colour.
        glDisable(GL_ALPHA_TEST);
        glDisable(GL_DEPTH_TEST);
        glDisable(GL_SCISSOR_TEST);
        glDisable(GL_BLEND);
        glDisable(GL_CULL_FACE);
        glDisable(GL_LIGHTING);
        glDisable(GL_FOG);
        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
        GL_CHECK_ERROR;

        // Save and reset matrices
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();
        glMatrixMode(GL_PROJECTION);
        glPushMatrix();
        glLoadIdentity();
        glMatrixMode(GL_TEXTURE);
        glPushMatrix();
        glLoadIdentity();
        GL_CHECK_ERROR;
        
        // Set up source texture
        glBindTexture(src->mTarget, src->mTextureID);
        GL_CHECK_ERROR;
        
        // Set filtering modes depending on the dimensions and source
        if(srcBox.getWidth()==dstBox.getWidth() &&
           srcBox.getHeight()==dstBox.getHeight() &&
           srcBox.getDepth()==dstBox.getDepth())
        {
            // Dimensions match -- use nearest filtering (fastest and pixel correct)
            glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
            GL_CHECK_ERROR;
            glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            GL_CHECK_ERROR;
        }
        else
        {
            // Dimensions don't match -- use bi or trilinear filtering depending on the
            // source texture.
            if(src->mUsage & TU_AUTOMIPMAP)
            {
                // Automatic mipmaps, we can safely use trilinear filter which
                // brings greatly imporoved quality for minimisation.
                glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
                GL_CHECK_ERROR;
                glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    
                GL_CHECK_ERROR;
            }
            else
            {
                // Manual mipmaps, stay safe with bilinear filtering so that no
                // intermipmap leakage occurs.
                glTexParameteri(src->mTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
                GL_CHECK_ERROR;
                glTexParameteri(src->mTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
                GL_CHECK_ERROR;
            }
        }
        // Clamp to edge (fastest)
        glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        GL_CHECK_ERROR;
        glTexParameteri(src->mTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        GL_CHECK_ERROR;
        
        // Store old binding so it can be restored later
        GLint oldfb;
        glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &oldfb);
        GL_CHECK_ERROR;

        // Set up temporary FBO
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, fboMan->getTemporaryFBO());
        GL_CHECK_ERROR;

        GLuint tempTex = 0;
        if(!fboMan->checkFormat(mFormat))
        {
            // If target format not directly supported, create intermediate texture
            GLenum tempFormat = GLESPixelUtil::getClosestGLInternalFormat(fboMan->getSupportedAlternative(mFormat));
            glGenTextures(1, &tempTex);
            GL_CHECK_ERROR;
            glBindTexture(GL_TEXTURE_2D, tempTex);
            GL_CHECK_ERROR;
            // Allocate temporary texture of the size of the destination area
            glTexImage2D(GL_TEXTURE_2D, 0, tempFormat, 
                         GLESPixelUtil::optionalPO2(dstBox.getWidth()), GLESPixelUtil::optionalPO2(dstBox.getHeight()), 
                         0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
            GL_CHECK_ERROR;
            glFramebufferTexture2DOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES,
                                      GL_TEXTURE_2D, tempTex, 0);
            GL_CHECK_ERROR;
            // Set viewport to size of destination slice
            glViewport(0, 0, dstBox.getWidth(), dstBox.getHeight());
            GL_CHECK_ERROR;
        }
        else
        {
            // We are going to bind directly, so set viewport to size and position of destination slice
            glViewport(dstBox.left, dstBox.top, dstBox.getWidth(), dstBox.getHeight());
            GL_CHECK_ERROR;
        }
        
        // Process each destination slice
        for(size_t slice=dstBox.front; slice<dstBox.back; ++slice)
        {
            if(!tempTex)
            {
                /// Bind directly
                bindToFramebuffer(GL_COLOR_ATTACHMENT0_OES, slice);
            }

            if(tempTex)
            {
                // Copy temporary texture
                glBindTexture(mTarget, mTextureID);
                GL_CHECK_ERROR;
                switch(mTarget)
                {
                    case GL_TEXTURE_2D:
#if OGRE_PLATFORM == OGRE_PLATFORM_ANDROID
                    case GL_TEXTURE_CUBE_MAP_OES:
#endif
                        glCopyTexSubImage2D(mFaceTarget, mLevel, 
                                            dstBox.left, dstBox.top, 
                                            0, 0, dstBox.getWidth(), dstBox.getHeight());
                        GL_CHECK_ERROR;
                        break;
                }
            }
        }
        // Finish up 
        if(!tempTex)
        {
            // Generate mipmaps
            if(mUsage & TU_AUTOMIPMAP)
            {
                glBindTexture(mTarget, mTextureID);
                GL_CHECK_ERROR;
                glGenerateMipmapOES(mTarget);
                GL_CHECK_ERROR;
            }
        }
        
        // Reset source texture to sane state
        glBindTexture(src->mTarget, src->mTextureID);
        GL_CHECK_ERROR;
        
        // Detach texture from temporary framebuffer
        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES,
                                     GL_RENDERBUFFER_OES, 0);
        GL_CHECK_ERROR;
        // Restore old framebuffer
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, oldfb);
        GL_CHECK_ERROR;
        // Restore matrix stacks and render state
        glMatrixMode(GL_TEXTURE);
        glPopMatrix();
        glMatrixMode(GL_PROJECTION);
        glPopMatrix();
        glMatrixMode(GL_MODELVIEW);
        glPopMatrix();
        GL_CHECK_ERROR;
        glDeleteTextures(1, &tempTex);
        GL_CHECK_ERROR;
    }
    //-----------------------------------------------------------------------------  
    // blitFromMemory doing hardware trilinear scaling
    void GLESTextureBuffer::blitFromMemory(const PixelBox &src_orig, const Image::Box &dstBox)
    {
        // Fall back to normal GLHardwarePixelBuffer::blitFromMemory in case 
        // - FBO is not supported
        // - Either source or target is luminance due doesn't looks like supported by hardware
        // - the source dimensions match the destination ones, in which case no scaling is needed
        if(!GL_OES_framebuffer_object ||
           PixelUtil::isLuminance(src_orig.format) ||
           PixelUtil::isLuminance(mFormat) ||
           (src_orig.getWidth() == dstBox.getWidth() &&
            src_orig.getHeight() == dstBox.getHeight() &&
            src_orig.getDepth() == dstBox.getDepth()))
        {
            GLESHardwarePixelBuffer::blitFromMemory(src_orig, dstBox);
            return;
        }
        if(!mBuffer.contains(dstBox))
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Destination box out of range",
                        "GLESTextureBuffer::blitFromMemory");
        // For scoped deletion of conversion buffer
        MemoryDataStreamPtr buf;
        PixelBox src;
        
        // First, convert the srcbox to a OpenGL compatible pixel format
        if(GLESPixelUtil::getGLOriginFormat(src_orig.format) == 0)
        {
            // Convert to buffer internal format
            buf.bind(OGRE_NEW MemoryDataStream(PixelUtil::getMemorySize(src_orig.getWidth(), src_orig.getHeight(), src_orig.getDepth(),
                                                                   mFormat)));
            src = PixelBox(src_orig.getWidth(), src_orig.getHeight(), src_orig.getDepth(), mFormat, buf->getPtr());
            PixelUtil::bulkPixelConversion(src_orig, src);
        }
        else
        {
            // No conversion needed
            src = src_orig;
        }
        
        // Create temporary texture to store source data
        GLuint id;
        GLenum target = GL_TEXTURE_2D;
        GLsizei width = GLESPixelUtil::optionalPO2(src.getWidth());
        GLsizei height = GLESPixelUtil::optionalPO2(src.getHeight());
        GLenum format = GLESPixelUtil::getClosestGLInternalFormat(src.format);
        GLenum datatype = GLESPixelUtil::getGLOriginDataType(src.format);
        // Generate texture name
        glGenTextures(1, &id);
        GL_CHECK_ERROR;
        
        // Set texture type
        glBindTexture(target, id);
        GL_CHECK_ERROR;
        
        // Set automatic mipmap generation; nice for minimisation
        glTexParameteri(target, GL_GENERATE_MIPMAP, GL_TRUE );
        GL_CHECK_ERROR;

        // Allocate texture memory
        glTexImage2D(target, 0, format, width, height, 0, format, datatype, 0);
        GL_CHECK_ERROR;

        // GL texture buffer
        GLESTextureBuffer tex(BLANKSTRING, target, id, width, height, format, src.format,
                              0, 0, (Usage)(TU_AUTOMIPMAP|HBU_STATIC_WRITE_ONLY), false, false, 0);
        
        // Upload data to 0,0,0 in temporary texture
        Image::Box tempTarget(0, 0, 0, src.getWidth(), src.getHeight(), src.getDepth());
        tex.upload(src, tempTarget);
        
        // Blit
        blitFromTexture(&tex, tempTarget, dstBox);
        
        // Delete temp texture
        glDeleteTextures(1, &id);
        GL_CHECK_ERROR;
    }
    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;
    }