Implement support for mipmap generation

TRAC #11338

Signed-off-by: Nicolas Capens
Signed-off-by: Daniel Koch

Author:    Andrew Lewycky

git-svn-id: https://angleproject.googlecode.com/svn/trunk@161 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/libGLESv2/Texture.cpp b/src/libGLESv2/Texture.cpp
index 309298b..f530307 100644
--- a/src/libGLESv2/Texture.cpp
+++ b/src/libGLESv2/Texture.cpp
@@ -549,9 +549,9 @@
     {
         RECT sourceRect;
         sourceRect.left = x;
-        sourceRect.top = y + height;
         sourceRect.right = x + width;
-        sourceRect.bottom = y;
+        sourceRect.top = source->getHeight() - (y + height);
+        sourceRect.bottom = source->getHeight() - y;
 
         IDirect3DSurface9 *dest;
         HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
@@ -584,9 +584,9 @@
 
     RECT sourceRect;
     sourceRect.left = x;
-    sourceRect.top = y + height;
     sourceRect.right = x + width;
-    sourceRect.bottom = y;
+    sourceRect.top = source->getHeight() - (y + height);
+    sourceRect.bottom = source->getHeight() - y;
 
     IDirect3DSurface9 *dest;
     HRESULT hr = mTexture->GetSurfaceLevel(level, &dest);
@@ -796,6 +796,50 @@
     return false;
 }
 
+void Texture2D::generateMipmaps()
+{
+    if (!isPow2(mImageArray[0].width) || !isPow2(mImageArray[0].height))
+    {
+        return error(GL_INVALID_OPERATION);
+    }
+
+    // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
+    unsigned int q = log2(std::max(mWidth, mHeight));
+    for (unsigned int i = 1; i <= q; i++)
+    {
+        if (mImageArray[i].surface != NULL)
+        {
+            mImageArray[i].surface->Release();
+            mImageArray[i].surface = NULL;
+        }
+
+        mImageArray[i].dirty = false;
+
+        mImageArray[i].format = mImageArray[0].format;
+        mImageArray[i].width = std::max(mImageArray[0].width >> i, 1);
+        mImageArray[i].height = std::max(mImageArray[0].height >> i, 1);
+    }
+
+    getRenderTarget();
+
+    for (unsigned int i = 1; i <= q; i++)
+    {
+        IDirect3DSurface9 *upper = NULL;
+        IDirect3DSurface9 *lower = NULL;
+
+        mTexture->GetSurfaceLevel(i-1, &upper);
+        mTexture->GetSurfaceLevel(i, &lower);
+
+        if (upper != NULL && lower != NULL)
+        {
+            getBlitter()->boxFilter(upper, lower);
+        }
+
+        if (upper != NULL) upper->Release();
+        if (lower != NULL) lower->Release();
+    }
+}
+
 TextureCubeMap::TextureCubeMap(Context *context) : Texture(context)
 {
     mTexture = NULL;
@@ -1184,9 +1228,9 @@
     {
         RECT sourceRect;
         sourceRect.left = x;
-        sourceRect.top = y + height;
         sourceRect.right = x + width;
-        sourceRect.bottom = y;
+        sourceRect.top = source->getHeight() - (y + height);
+        sourceRect.bottom = source->getHeight() - y;
 
         IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
 
@@ -1251,9 +1295,9 @@
 
     RECT sourceRect;
     sourceRect.left = x;
-    sourceRect.top = y + height;
     sourceRect.right = x + width;
-    sourceRect.bottom = y;
+    sourceRect.top = source->getHeight() - (y + height);
+    sourceRect.bottom = source->getHeight() - y;
 
     IDirect3DSurface9 *dest = getCubeMapSurface(face, level);
 
@@ -1261,4 +1305,70 @@
     dest->Release();
 }
 
+bool TextureCubeMap::isCubeComplete() const
+{
+    if (mImageArray[0][0].width == 0)
+    {
+        return false;
+    }
+
+    for (unsigned int f = 1; f < 6; f++)
+    {
+        if (mImageArray[f][0].width != mImageArray[0][0].width
+            || mImageArray[f][0].format != mImageArray[0][0].format)
+        {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void TextureCubeMap::generateMipmaps()
+{
+    if (!isPow2(mImageArray[0][0].width) || !isCubeComplete())
+    {
+        return error(GL_INVALID_OPERATION);
+    }
+
+    // Purge array levels 1 through q and reset them to represent the generated mipmap levels.
+    unsigned int q = log2(mImageArray[0][0].width);
+    for (unsigned int f = 0; f < 6; f++)
+    {
+        for (unsigned int i = 1; i <= q; i++)
+        {
+            if (mImageArray[f][i].surface != NULL)
+            {
+                mImageArray[f][i].surface->Release();
+                mImageArray[f][i].surface = NULL;
+            }
+
+            mImageArray[f][i].dirty = false;
+
+            mImageArray[f][i].format = mImageArray[f][0].format;
+            mImageArray[f][i].width = std::max(mImageArray[f][0].width >> i, 1);
+            mImageArray[f][i].height = mImageArray[f][i].width;
+        }
+    }
+
+    getRenderTarget();
+
+    for (unsigned int f = 0; f < 6; f++)
+    {
+        for (unsigned int i = 1; i <= q; i++)
+        {
+            IDirect3DSurface9 *upper = getCubeMapSurface(f, i-1);
+            IDirect3DSurface9 *lower = getCubeMapSurface(f, i);
+
+            if (upper != NULL && lower != NULL)
+            {
+                getBlitter()->boxFilter(upper, lower);
+            }
+
+            if (upper != NULL) upper->Release();
+            if (lower != NULL) lower->Release();
+        }
+    }
+}
+
 }