Implements compressed textures.
TRAC #12714
Signed-off-by: Daniel Koch

Author:    Shannon Woods

git-svn-id: https://angleproject.googlecode.com/svn/trunk@397 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/Texture.cpp b/src/libGLESv2/Texture.cpp
index 031cbbf..612d795 100644
--- a/src/libGLESv2/Texture.cpp
+++ b/src/libGLESv2/Texture.cpp
@@ -23,7 +23,7 @@
 {
 
 Texture::Image::Image()
-  : width(0), height(0), dirty(false), surface(NULL)
+  : width(0), height(0), dirty(false), surface(NULL), format(GL_NONE)
 {
 }
 
@@ -177,7 +177,15 @@
 // Selects an internal Direct3D 9 format for storing an Image
 D3DFORMAT Texture::selectFormat(GLenum format)
 {
-    return D3DFMT_A8R8G8B8;
+    if (format == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ||
+        format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
+    {
+        return D3DFMT_DXT1;
+    }
+    else
+    {
+        return D3DFMT_A8R8G8B8;
+    }
 }
 
 int Texture::imagePitch(const Image &img) const
@@ -305,14 +313,13 @@
     }
 }
 
-void Texture::setImage(GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *img)
+void Texture::createSurface(GLsizei width, GLsizei height, GLenum format, Image *img)
 {
     IDirect3DTexture9 *newTexture = NULL;
     IDirect3DSurface9 *newSurface = NULL;
 
     if (width != 0 && height != 0)
     {
-        //HRESULT result = getDevice()->CreateOffscreenPlainSurface(width, height, selectFormat(format), D3DPOOL_SYSTEMMEM, &newSurface, NULL);
         HRESULT result = getDevice()->CreateTexture(width, height, 1, NULL, selectFormat(format), D3DPOOL_SYSTEMMEM, &newTexture, NULL);
 
         if (FAILED(result))
@@ -331,18 +338,46 @@
     img->width = width;
     img->height = height;
     img->format = format;
+}
 
-    if (pixels != NULL && newSurface != NULL)
+void Texture::setImage(GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels, Image *img)
+{
+    createSurface(width, height, format, img);
+
+    if (pixels != NULL && img->surface != NULL)
     {
         D3DLOCKED_RECT locked;
-        HRESULT result = newSurface->LockRect(&locked, NULL, 0);
+        HRESULT result = img->surface->LockRect(&locked, NULL, 0);
 
         ASSERT(SUCCEEDED(result));
 
         if (SUCCEEDED(result))
         {
             loadImageData(0, 0, width, height, format, type, unpackAlignment, pixels, locked.Pitch, locked.pBits);
-            newSurface->UnlockRect();
+            img->surface->UnlockRect();
+        }
+
+        img->dirty = true;
+    }
+
+    mDirtyMetaData = true;
+}
+
+void Texture::setCompressedImage(GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *img)
+{
+    createSurface(width, height, format, img);
+
+    if (pixels != NULL && img->surface != NULL)
+    {
+        D3DLOCKED_RECT locked;
+        HRESULT result = img->surface->LockRect(&locked, NULL, 0);
+
+        ASSERT(SUCCEEDED(result));
+
+        if (SUCCEEDED(result))
+        {
+            memcpy(locked.pBits, pixels, imageSize);
+            img->surface->UnlockRect();
         }
 
         img->dirty = true;
@@ -374,6 +409,46 @@
     return true;
 }
 
+bool Texture::subImageCompressed(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels, Image *img)
+{
+    if (width + xoffset > img->width || height + yoffset > img->height)
+    {
+        error(GL_INVALID_VALUE);
+        return false;
+    }
+
+    if (format != getFormat())
+    {
+        error(GL_INVALID_OPERATION);
+        return false;
+    }
+
+    RECT updateRegion;
+    updateRegion.left = xoffset;
+    updateRegion.right = xoffset + width;
+    updateRegion.bottom = yoffset + height;
+    updateRegion.top = yoffset;
+
+    D3DLOCKED_RECT locked;
+    HRESULT result = img->surface->LockRect(&locked, &updateRegion, 0);
+
+    ASSERT(SUCCEEDED(result));
+
+    if (SUCCEEDED(result))
+    {
+        GLsizei inputPitch = ComputeCompressedPitch(width, format);
+        int rows = imageSize / inputPitch;
+        for (int i = 0; i < rows; ++i)
+        {
+            memcpy((void*)((BYTE*)locked.pBits + i * locked.Pitch), (void*)((BYTE*)pixels + i * inputPitch), inputPitch);
+        }
+        img->surface->UnlockRect();
+    }
+
+    img->dirty = true;
+    return true;
+}
+
 IDirect3DBaseTexture9 *Texture::getTexture()
 {
     if (!isComplete())
@@ -484,6 +559,11 @@
     return GL_TEXTURE_2D;
 }
 
+GLenum Texture2D::getFormat() const
+{
+    return mImageArray[0].format;
+}
+
 // While OpenGL doesn't check texture consistency until draw-time, D3D9 requires a complete texture
 // for render-to-texture (such as CopyTexImage). We have no way of keeping individual inconsistent levels.
 // Call this when a particular level of the texture must be defined with a specific format, width and height.
@@ -542,6 +622,13 @@
     Texture::setImage(width, height, format, type, unpackAlignment, pixels, &mImageArray[level]);
 }
 
+void Texture2D::setCompressedImage(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
+{
+    redefineTexture(level, internalFormat, width, height);
+
+    Texture::setCompressedImage(width, height, internalFormat, imageSize, pixels, &mImageArray[level]);
+}
+
 void Texture2D::commitRect(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
 {
     ASSERT(mImageArray[level].surface != NULL);
@@ -585,6 +672,14 @@
     }
 }
 
+void Texture2D::subImageCompressed(GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
+{
+    if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[level]))
+    {
+        commitRect(level, xoffset, yoffset, width, height);
+    }
+}
+
 void Texture2D::copyImage(GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, RenderbufferStorage *source)
 {
     if (redefineTexture(level, internalFormat, width, height))
@@ -715,6 +810,11 @@
     return true;
 }
 
+bool Texture2D::isCompressed() const
+{
+    return IsCompressed(getFormat());
+}
+
 // Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
 IDirect3DBaseTexture9 *Texture2D::createTexture()
 {
@@ -954,6 +1054,11 @@
     return GL_TEXTURE_CUBE_MAP;
 }
 
+GLenum TextureCubeMap::getFormat() const
+{
+    return mImageArray[0][0].format;
+}
+
 void TextureCubeMap::setImagePosX(GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, GLint unpackAlignment, const void *pixels)
 {
     setImage(0, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
@@ -984,6 +1089,13 @@
     setImage(5, level, internalFormat, width, height, format, type, unpackAlignment, pixels);
 }
 
+void TextureCubeMap::setCompressedImage(GLenum face, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei imageSize, const void *pixels)
+{
+    redefineTexture(level, internalFormat, width);
+
+    Texture::setCompressedImage(width, height, internalFormat, imageSize, pixels, &mImageArray[faceIndex(face)][level]);
+}
+
 void TextureCubeMap::commitRect(GLenum faceTarget, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
 {
     int face = faceIndex(faceTarget);
@@ -1027,6 +1139,14 @@
     }
 }
 
+void TextureCubeMap::subImageCompressed(GLenum face, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *pixels)
+{
+    if (Texture::subImageCompressed(xoffset, yoffset, width, height, format, imageSize, pixels, &mImageArray[faceIndex(face)][level]))
+    {
+        commitRect(face, level, xoffset, yoffset, width, height);
+    }
+}
+
 // Tests for GL texture object completeness. [OpenGL ES 2.0.24] section 3.7.10 page 81.
 bool TextureCubeMap::isComplete() const
 {
@@ -1093,6 +1213,11 @@
     return true;
 }
 
+bool TextureCubeMap::isCompressed() const
+{
+    return IsCompressed(getFormat());
+}
+
 // Constructs a Direct3D 9 texture resource from the texture images, or returns an existing one
 IDirect3DBaseTexture9 *TextureCubeMap::createTexture()
 {
@@ -1538,4 +1663,9 @@
     return mTexture->getHeight();
 }
 
+GLenum Texture::TextureColorbufferProxy::getFormat() const
+{
+    return mTexture->getFormat();
+}
+
 }