Add support for DXT3 and DXT5

TEST=none
BUG=none
Review URL: http://codereview.appspot.com/4809068

git-svn-id: https://angleproject.googlecode.com/svn/trunk@721 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/extensions/ANGLE_texture_compression_dxt.txt b/extensions/ANGLE_texture_compression_dxt.txt
new file mode 100644
index 0000000..d155730
--- /dev/null
+++ b/extensions/ANGLE_texture_compression_dxt.txt
@@ -0,0 +1,73 @@
+Name
+
+    ANGLE_texture_compression_dxt
+
+Name Strings
+
+    GL_ANGLE_texture_compression_dxt3
+    GL_ANGLE_texture_compression_dxt5
+
+Contributors
+
+    Gregg Tavares, Google Inc.
+    Daniel Koch, TransGaming Inc.
+    Al Patrick, Google Inc.
+
+Contacts
+
+    Gregg Tavares, Google Inc. (gman 'at' google 'dot' com)
+
+Status
+
+    Implemented in ANGLE ES2
+
+Version
+
+    Last Modified Date: Aug 2, 2011
+
+Number
+
+    OpenGL ES Extension #..
+
+Dependencies
+
+    Requires OpenGL ES 2.0.
+
+    The extension is written against the OpenGL ES 2.0 specification.
+
+Overview
+
+    These extensions are exactly the same as EXT_texture_compression_dxt1
+    except they expose the formats COMPRESSED_RGBA_S3TC_DXT3_ANGLE and
+    COMPRESSED_RGBA_S3TC_DXT5_ANGLE respectively.
+
+    See EXT_texture_compression_dxt1 for the full list of changes. Also
+    see EXT_texture_compression_s3tc for a description of the formats.
+
+New Procedures and Functions
+
+    None.
+
+New Types
+
+    None.
+
+New Tokens
+
+    Accepted by the <internalformat> parameter of CompressedTexImage2D
+    and the <format> parameter of CompressedTexSubImage2D:
+
+    COMPRESSED_RGBA_S3TC_DXT3_ANGLE                   0x83F2
+    COMPRESSED_RGBA_S3TC_DXT5_ANGLE                   0x83F3
+
+Errors
+
+    None.
+
+New State
+
+    None.
+
+Revision History
+
+
diff --git a/include/GLES2/gl2ext.h b/include/GLES2/gl2ext.h
index 4eae246..9371ad7 100644
--- a/include/GLES2/gl2ext.h
+++ b/include/GLES2/gl2ext.h
@@ -207,6 +207,16 @@
 #define GL_MAX_SAMPLES_ANGLE                                    0x8D57
 #endif
 
+/* GL_ANGLE_texture_compression_dxt3 */
+#ifndef GL_ANGLE_texture_compression_dxt3
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE                      0x83F2
+#endif
+
+/* GL_ANGLE_texture_compression_dxt5 */
+#ifndef GL_ANGLE_texture_compression_dxt5
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE                      0x83F3
+#endif
+
 /*------------------------------------------------------------------------*
  * APPLE extension tokens
  *------------------------------------------------------------------------*/
@@ -790,6 +800,16 @@
 typedef void (GL_APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEANGLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
 #endif
 
+/* GL_ANGLE_texture_compression_dxt3 */
+#ifndef GL_ANGLE_texture_compression_dxt3
+#define GL_ANGLE_texture_compression_dxt3 1
+#endif
+
+/* GL_ANGLE_texture_compression_dxt5 */
+#ifndef GL_ANGLE_texture_compression_dxt5
+#define GL_ANGLE_texture_compression_dxt5 1
+#endif
+
 /*------------------------------------------------------------------------*
  * APPLE extension functions
  *------------------------------------------------------------------------*/
diff --git a/src/common/version.h b/src/common/version.h
index 739791f..e4607fb 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -1,7 +1,7 @@
 #define MAJOR_VERSION 0
 #define MINOR_VERSION 0
 #define BUILD_VERSION 0
-#define BUILD_REVISION 720
+#define BUILD_REVISION 721
 
 #define STRINGIFY(x) #x
 #define MACRO_STRINGIFY(x) STRINGIFY(x)
diff --git a/src/libEGL/Display.cpp b/src/libEGL/Display.cpp
index ea5fcc3..791eca4 100644
--- a/src/libEGL/Display.cpp
+++ b/src/libEGL/Display.cpp
@@ -773,7 +773,7 @@
     }
 }
 
-bool Display::getCompressedTextureSupport()
+bool Display::getDXT1TextureSupport()
 {
     D3DDISPLAYMODE currentDisplayMode;
     mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
@@ -781,6 +781,22 @@
     return SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0, D3DRTYPE_TEXTURE, D3DFMT_DXT1));
 }
 
+bool Display::getDXT3TextureSupport()
+{
+    D3DDISPLAYMODE currentDisplayMode;
+    mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
+
+    return SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0, D3DRTYPE_TEXTURE, D3DFMT_DXT3));
+}
+
+bool Display::getDXT5TextureSupport()
+{
+    D3DDISPLAYMODE currentDisplayMode;
+    mD3d9->GetAdapterDisplayMode(mAdapter, &currentDisplayMode);
+
+    return SUCCEEDED(mD3d9->CheckDeviceFormat(mAdapter, mDeviceType, currentDisplayMode.Format, 0, D3DRTYPE_TEXTURE, D3DFMT_DXT5));
+}
+
 bool Display::getFloatTextureSupport(bool *filtering, bool *renderable)
 {
     D3DDISPLAYMODE currentDisplayMode;
diff --git a/src/libEGL/Display.h b/src/libEGL/Display.h
index 3c6ac88..f241670 100644
--- a/src/libEGL/Display.h
+++ b/src/libEGL/Display.h
@@ -63,7 +63,9 @@
     virtual D3DADAPTER_IDENTIFIER9 *getAdapterIdentifier();
     bool isDeviceLost();
     virtual void getMultiSampleSupport(D3DFORMAT format, bool *multiSampleArray);
-    virtual bool getCompressedTextureSupport();
+    virtual bool getDXT1TextureSupport();
+    virtual bool getDXT3TextureSupport();
+    virtual bool getDXT5TextureSupport();
     virtual bool getEventQuerySupport();
     virtual bool getFloatTextureSupport(bool *filtering, bool *renderable);
     virtual bool getHalfFloatTextureSupport(bool *filtering, bool *renderable);
diff --git a/src/libGLESv2/Context.cpp b/src/libGLESv2/Context.cpp
index f6dd3f5..77fff26 100644
--- a/src/libGLESv2/Context.cpp
+++ b/src/libGLESv2/Context.cpp
@@ -156,8 +156,11 @@
 
     mHasBeenCurrent = false;
 
-    mSupportsCompressedTextures = false;
+    mSupportsDXT1Textures = false;
+    mSupportsDXT3Textures = false;
+    mSupportsDXT5Textures = false;
     mSupportsEventQueries = false;
+    mNumCompressedTextureFormats = 0;
     mMaxSupportedSamples = 0;
     mMaskedClearSavedState = NULL;
     markAllStateDirty();
@@ -280,7 +283,9 @@
         mMaxSupportedSamples = max;
 
         mSupportsEventQueries = display->getEventQuerySupport();
-        mSupportsCompressedTextures = display->getCompressedTextureSupport();
+        mSupportsDXT1Textures = display->getDXT1TextureSupport();
+        mSupportsDXT3Textures = display->getDXT3TextureSupport();
+        mSupportsDXT5Textures = display->getDXT5TextureSupport();
         mSupportsFloatTextures = display->getFloatTextureSupport(&mSupportsFloatLinearFilter, &mSupportsFloatRenderableTextures);
         mSupportsHalfFloatTextures = display->getHalfFloatTextureSupport(&mSupportsHalfFloatLinearFilter, &mSupportsHalfFloatRenderableTextures);
         mSupportsLuminanceTextures = display->getLuminanceTextureSupport();
@@ -288,6 +293,20 @@
 
         mSupports32bitIndices = mDeviceCaps.MaxVertexIndex >= (1 << 16);
 
+        mNumCompressedTextureFormats = 0;
+        if (supportsDXT1Textures())
+        {
+            mNumCompressedTextureFormats += 2;
+        }
+        if (supportsDXT3Textures())
+        {
+            mNumCompressedTextureFormats += 1;
+        }
+        if (supportsDXT5Textures())
+        {
+            mNumCompressedTextureFormats += 1;
+        }
+
         initExtensionString();
 
         mState.viewportX = 0;
@@ -1240,18 +1259,7 @@
       case GL_MAX_TEXTURE_SIZE:                 *params = getMaximumTextureDimension();         break;
       case GL_MAX_CUBE_MAP_TEXTURE_SIZE:        *params = getMaximumCubeTextureDimension();     break;
       case GL_NUM_COMPRESSED_TEXTURE_FORMATS:   
-        {
-            if (supportsCompressedTextures())
-            {
-                // at current, only GL_COMPRESSED_RGB_S3TC_DXT1_EXT and 
-                // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT are supported
-                *params = 2;
-            }
-            else
-            {
-                *params = 0;
-            }
-        }
+        params[0] = mNumCompressedTextureFormats;
         break;
       case GL_MAX_SAMPLES_ANGLE:
         {
@@ -1307,10 +1315,18 @@
         break;
       case GL_COMPRESSED_TEXTURE_FORMATS:
         {
-            if (supportsCompressedTextures())
+            if (supportsDXT1Textures())
             {
-                params[0] = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
-                params[1] = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+                *params++ = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
+                *params++ = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
+            }
+            if (supportsDXT3Textures())
+            {
+                *params++ = GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE;
+            }
+            if (supportsDXT5Textures())
+            {
+                *params++ = GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE;
             }
         }
         break;
@@ -1422,7 +1438,12 @@
     // application.
     switch (pname)
     {
-      case GL_COMPRESSED_TEXTURE_FORMATS: /* no compressed texture formats are supported */ 
+      case GL_COMPRESSED_TEXTURE_FORMATS:
+        {
+            *type = GL_INT;
+            *numParams = mNumCompressedTextureFormats;
+        }
+        break;
       case GL_SHADER_BINARY_FORMATS:
         {
             *type = GL_INT;
@@ -3053,9 +3074,19 @@
     return mSupportsEventQueries;
 }
 
-bool Context::supportsCompressedTextures() const
+bool Context::supportsDXT1Textures() const
 {
-    return mSupportsCompressedTextures;
+    return mSupportsDXT1Textures;
+}
+
+bool Context::supportsDXT3Textures() const
+{
+    return mSupportsDXT3Textures;
+}
+
+bool Context::supportsDXT5Textures() const
+{
+    return mSupportsDXT5Textures;
 }
 
 bool Context::supportsFloatTextures() const
@@ -3330,11 +3361,21 @@
         mExtensionString += "GL_NV_fence ";
     }
 
-    if (supportsCompressedTextures())
+    if (supportsDXT1Textures())
     {
         mExtensionString += "GL_EXT_texture_compression_dxt1 ";
     }
 
+    if (supportsDXT3Textures())
+    {
+        mExtensionString += "GL_ANGLE_texture_compression_dxt3 ";
+    }
+
+    if (supportsDXT5Textures())
+    {
+        mExtensionString += "GL_ANGLE_texture_compression_dxt5 ";
+    }
+
     if (supportsFloatTextures())
     {
         mExtensionString += "GL_OES_texture_float ";
diff --git a/src/libGLESv2/Context.h b/src/libGLESv2/Context.h
index 3002654..77d9406 100644
--- a/src/libGLESv2/Context.h
+++ b/src/libGLESv2/Context.h
@@ -437,7 +437,9 @@
     int getNearestSupportedSamples(D3DFORMAT format, int requested) const;
     const char *getExtensionString() const;
     bool supportsEventQueries() const;
-    bool supportsCompressedTextures() const;
+    bool supportsDXT1Textures() const;
+    bool supportsDXT3Textures() const;
+    bool supportsDXT5Textures() const;
     bool supportsFloatTextures() const;
     bool supportsFloatLinearFilter() const;
     bool supportsFloatRenderableTextures() const;
@@ -532,7 +534,9 @@
     std::map<D3DFORMAT, bool *> mMultiSampleSupport;
     GLsizei mMaxSupportedSamples;
     bool mSupportsEventQueries;
-    bool mSupportsCompressedTextures;
+    bool mSupportsDXT1Textures;
+    bool mSupportsDXT3Textures;
+    bool mSupportsDXT5Textures;
     bool mSupportsFloatTextures;
     bool mSupportsFloatLinearFilter;
     bool mSupportsFloatRenderableTextures;
@@ -542,6 +546,7 @@
     bool mSupportsLuminanceTextures;
     bool mSupportsLuminanceAlphaTextures;
     bool mSupports32bitIndices;
+    int mNumCompressedTextureFormats;
 
     // state caching flags
     bool mClearStateDirty;
diff --git a/src/libGLESv2/Texture.cpp b/src/libGLESv2/Texture.cpp
index 0187bcf..e143535 100644
--- a/src/libGLESv2/Texture.cpp
+++ b/src/libGLESv2/Texture.cpp
@@ -49,6 +49,8 @@
       case D3DFMT_L8:
       case D3DFMT_A8L8:
       case D3DFMT_DXT1:
+      case D3DFMT_DXT3:
+      case D3DFMT_DXT5:
         return false;
       case D3DFMT_A8R8G8B8:
       case D3DFMT_X8R8G8B8:
@@ -69,6 +71,14 @@
     {
         return D3DFMT_DXT1;
     }
+    else if (type == GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE)
+    {
+        return D3DFMT_DXT3;
+    }
+    else if (type == GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE)
+    {
+        return D3DFMT_DXT5;
+    }
     else if (type == GL_FLOAT)
     {
         return D3DFMT_A32B32G32R32F;
@@ -767,6 +777,132 @@
 }
 
 void Texture::loadCompressedImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+                                      int inputPitch, const void *input, size_t outputPitch, void *output) const {
+    switch (getD3DFormat())
+    {
+        case D3DFMT_DXT1:
+          loadDXT1ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
+          break;
+        case D3DFMT_DXT3:
+          loadDXT3ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
+          break;
+        case D3DFMT_DXT5:
+          loadDXT5ImageData(xoffset, yoffset, width, height, inputPitch, input, outputPitch, output);
+          break;
+    }
+}
+
+static void FlipCopyDXT1BlockFull(const unsigned int* source, unsigned int* dest) {
+  // A DXT1 block layout is:
+  // [0-1] color0.
+  // [2-3] color1.
+  // [4-7] color bitmap, 2 bits per pixel.
+  // So each of the 4-7 bytes represents one line, flipping a block is just
+  // flipping those bytes.
+
+  // First 32-bits is two RGB565 colors shared by tile and does not need to be modified.
+  dest[0] = source[0];
+
+  // Second 32-bits contains 4 rows of 4 2-bit interpolants between the colors. All rows should be flipped.
+  dest[1] = (source[1] >> 24) |
+            ((source[1] << 8) & 0x00FF0000) |
+            ((source[1] >> 8) & 0x0000FF00) |
+            (source[1] << 24);
+}
+
+// Flips the first 2 lines of a DXT1 block in the y direction.
+static void FlipCopyDXT1BlockHalf(const unsigned int* source, unsigned int* dest) {
+  // See layout above.
+  dest[0] = source[0];
+  dest[1] = ((source[1] << 8) & 0x0000FF00) |
+            ((source[1] >> 8) & 0x000000FF);
+}
+
+// Flips a full DXT3 block in the y direction.
+static void FlipCopyDXT3BlockFull(const unsigned int* source, unsigned int* dest) {
+  // A DXT3 block layout is:
+  // [0-7]  alpha bitmap, 4 bits per pixel.
+  // [8-15] a DXT1 block.
+
+  // First and Second 32 bits are 4bit per pixel alpha and need to be flipped.
+  dest[0] = (source[1] >> 16) | (source[1] << 16);
+  dest[1] = (source[0] >> 16) | (source[0] << 16);
+
+  // And flip the DXT1 block using the above function.
+  FlipCopyDXT1BlockFull(source + 2, dest + 2);
+}
+
+// Flips the first 2 lines of a DXT3 block in the y direction.
+static void FlipCopyDXT3BlockHalf(const unsigned int* source, unsigned int* dest) {
+  // See layout above.
+  dest[0] = (source[1] >> 16) | (source[1] << 16);
+  FlipCopyDXT1BlockHalf(source + 2, dest + 2);
+}
+
+// Flips a full DXT5 block in the y direction.
+static void FlipCopyDXT5BlockFull(const unsigned int* source, unsigned int* dest) {
+  // A DXT5 block layout is:
+  // [0]    alpha0.
+  // [1]    alpha1.
+  // [2-7]  alpha bitmap, 3 bits per pixel.
+  // [8-15] a DXT1 block.
+
+  // The alpha bitmap doesn't easily map lines to bytes, so we have to
+  // interpret it correctly.  Extracted from
+  // http://www.opengl.org/registry/specs/EXT/texture_compression_s3tc.txt :
+  //
+  //   The 6 "bits" bytes of the block are decoded into one 48-bit integer:
+  //
+  //     bits = bits_0 + 256 * (bits_1 + 256 * (bits_2 + 256 * (bits_3 +
+  //                   256 * (bits_4 + 256 * bits_5))))
+  //
+  //   bits is a 48-bit unsigned integer, from which a three-bit control code
+  //   is extracted for a texel at location (x,y) in the block using:
+  //
+  //       code(x,y) = bits[3*(4*y+x)+1..3*(4*y+x)+0]
+  //
+  //   where bit 47 is the most significant and bit 0 is the least
+  //   significant bit.
+  const unsigned char* sourceBytes = static_cast<const unsigned char*>(static_cast<const void*>(source));
+  unsigned char* destBytes = static_cast<unsigned char*>(static_cast<void*>(dest));
+  unsigned int line_0_1 = sourceBytes[2] + 256 * (sourceBytes[3] + 256 * sourceBytes[4]);
+  unsigned int line_2_3 = sourceBytes[5] + 256 * (sourceBytes[6] + 256 * sourceBytes[7]);
+  // swap lines 0 and 1 in line_0_1.
+  unsigned int line_1_0 = ((line_0_1 & 0x000fff) << 12) |
+                          ((line_0_1 & 0xfff000) >> 12);
+  // swap lines 2 and 3 in line_2_3.
+  unsigned int line_3_2 = ((line_2_3 & 0x000fff) << 12) |
+                          ((line_2_3 & 0xfff000) >> 12);
+  destBytes[0] = sourceBytes[0];
+  destBytes[1] = sourceBytes[1];
+  destBytes[2] = line_3_2 & 0xff;
+  destBytes[3] = (line_3_2 & 0xff00) >> 8;
+  destBytes[4] = (line_3_2 & 0xff0000) >> 8;
+  destBytes[5] = line_1_0 & 0xff;
+  destBytes[6] = (line_1_0 & 0xff00) >> 8;
+  destBytes[7] = (line_1_0 & 0xff0000) >> 8;
+
+  // And flip the DXT1 block using the above function.
+  FlipCopyDXT1BlockFull(source + 2, dest + 2);
+}
+
+// Flips the first 2 lines of a DXT5 block in the y direction.
+static void FlipCopyDXT5BlockHalf(const unsigned int* source, unsigned int* dest) {
+  // See layout above.
+  const unsigned char* sourceBytes = static_cast<const unsigned char*>(static_cast<const void*>(source));
+  unsigned char* destBytes = static_cast<unsigned char*>(static_cast<void*>(dest));
+  unsigned int line_0_1 = sourceBytes[2] + 256 * (sourceBytes[3] + 256 * sourceBytes[4]);
+  unsigned int line_1_0 = ((line_0_1 & 0x000fff) << 12) |
+                          ((line_0_1 & 0xfff000) >> 12);
+  destBytes[0] = sourceBytes[0];
+  destBytes[1] = sourceBytes[1];
+  destBytes[2] = line_1_0 & 0xff;
+  destBytes[3] = (line_1_0 & 0xff00) >> 8;
+  destBytes[4] = (line_1_0 & 0xff0000) >> 8;
+  FlipCopyDXT1BlockHalf(source + 2, dest + 2);
+}
+
+void Texture::loadDXT1ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                       int inputPitch, const void *input, size_t outputPitch, void *output) const
 {
     ASSERT(xoffset % 4 == 0);
@@ -778,29 +914,24 @@
     const unsigned int *source = reinterpret_cast<const unsigned int*>(input);
     unsigned int *dest = reinterpret_cast<unsigned int*>(output);
 
+    // Round width up in case it is less than 4.
+    int blocksAcross = (width + 3) / 4;
+    int intsAcross = blocksAcross * 2;
+
     switch (height)
     {
         case 1:
-            // Round width up in case it is 1.
-            for (int x = 0; x < (width + 1) / 2; x += 2)
+            for (int x = 0; x < intsAcross; x += 2)
             {
-                // First 32-bits is two RGB565 colors shared by tile and does not need to be modified.
+                // just copy the block
                 dest[x] = source[x];
-
-                // Second 32-bits contains 4 rows of 4 2-bit interpolants between the colors, the last 3 rows being unused. No flipping should occur.
                 dest[x + 1] = source[x + 1];
             }
             break;
         case 2:
-            // Round width up in case it is 1.
-            for (int x = 0; x < (width + 1) / 2; x += 2)
+            for (int x = 0; x < intsAcross; x += 2)
             {
-                // First 32-bits is two RGB565 colors shared by tile and does not need to be modified.
-                dest[x] = source[x];
-
-                // Second 32-bits contains 4 rows of 4 2-bit interpolants between the colors, the last 2 rows being unused. Only the top 2 rows should be flipped.
-                dest[x + 1] = ((source[x + 1] << 8) & 0x0000FF00) |
-                              ((source[x + 1] >> 8) & 0x000000FF);       
+                FlipCopyDXT1BlockHalf(source + x, dest + x);
             }
             break;
         default:
@@ -810,17 +941,109 @@
                 const unsigned int *source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
                 unsigned int *dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 8);
 
-                // Round width up in case it is 1.
-                for (int x = 0; x < (width + 1) / 2; x += 2)
+                for (int x = 0; x < intsAcross; x += 2)
                 {
-                    // First 32-bits is two RGB565 colors shared by tile and does not need to be modified.
-                    dest[x] = source[x];
+                    FlipCopyDXT1BlockFull(source + x, dest + x);
+                }
+            }
+            break;
+    }
+}
 
-                    // Second 32-bits contains 4 rows of 4 2-bit interpolants between the colors. All rows should be flipped.
-                    dest[x + 1] = (source[x + 1] >> 24) | 
-                                  ((source[x + 1] << 8) & 0x00FF0000) |
-                                  ((source[x + 1] >> 8) & 0x0000FF00) |
-                                  (source[x + 1] << 24);                    
+void Texture::loadDXT3ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+                                int inputPitch, const void *input, size_t outputPitch, void *output) const
+{
+    ASSERT(xoffset % 4 == 0);
+    ASSERT(yoffset % 4 == 0);
+    ASSERT(width % 4 == 0 || width == 2 || width == 1);
+    ASSERT(inputPitch % 16 == 0);
+    ASSERT(outputPitch % 16 == 0);
+
+    const unsigned int *source = reinterpret_cast<const unsigned int*>(input);
+    unsigned int *dest = reinterpret_cast<unsigned int*>(output);
+
+    // Round width up in case it is less than 4.
+    int blocksAcross = (width + 3) / 4;
+    int intsAcross = blocksAcross * 4;
+
+    switch (height)
+    {
+        case 1:
+            for (int x = 0; x < intsAcross; x += 4)
+            {
+                // just copy the block
+                dest[x] = source[x];
+                dest[x + 1] = source[x + 1];
+                dest[x + 2] = source[x + 2];
+                dest[x + 3] = source[x + 3];
+            }
+            break;
+        case 2:
+            for (int x = 0; x < intsAcross; x += 4)
+            {
+                FlipCopyDXT3BlockHalf(source + x, dest + x);
+            }
+            break;
+        default:
+            ASSERT(height % 4 == 0);
+            for (int y = 0; y < height / 4; ++y)
+            {
+                const unsigned int *source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
+                unsigned int *dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
+
+                for (int x = 0; x < intsAcross; x += 4)
+                {
+                  FlipCopyDXT3BlockFull(source + x, dest + x);
+                }
+            }
+            break;
+    }
+}
+
+void Texture::loadDXT5ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+                                int inputPitch, const void *input, size_t outputPitch, void *output) const
+{
+    ASSERT(xoffset % 4 == 0);
+    ASSERT(yoffset % 4 == 0);
+    ASSERT(width % 4 == 0 || width == 2 || width == 1);
+    ASSERT(inputPitch % 16 == 0);
+    ASSERT(outputPitch % 16 == 0);
+
+    const unsigned int *source = reinterpret_cast<const unsigned int*>(input);
+    unsigned int *dest = reinterpret_cast<unsigned int*>(output);
+
+    // Round width up in case it is less than 4.
+    int blocksAcross = (width + 3) / 4;
+    int intsAcross = blocksAcross * 4;
+
+    switch (height)
+    {
+        case 1:
+            for (int x = 0; x < intsAcross; x += 4)
+            {
+                // just copy the block
+                dest[x] = source[x];
+                dest[x + 1] = source[x + 1];
+                dest[x + 2] = source[x + 2];
+                dest[x + 3] = source[x + 3];
+            }
+            break;
+        case 2:
+            for (int x = 0; x < intsAcross; x += 4)
+            {
+                FlipCopyDXT5BlockHalf(source + x, dest + x);
+            }
+            break;
+        default:
+            ASSERT(height % 4 == 0);
+            for (int y = 0; y < height; ++y)
+            {
+                const unsigned int *source = reinterpret_cast<const unsigned int*>(static_cast<const unsigned char*>(input) + y * inputPitch);
+                unsigned int *dest = reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(output) + (y + yoffset) * outputPitch + xoffset * 16);
+
+                for (int x = 0; x < intsAcross; x += 4)
+                {
+                    FlipCopyDXT5BlockFull(source + x, dest + x);
                 }
             }
             break;
diff --git a/src/libGLESv2/Texture.h b/src/libGLESv2/Texture.h
index 5bdb2fb..95045b8 100644
--- a/src/libGLESv2/Texture.h
+++ b/src/libGLESv2/Texture.h
@@ -185,6 +185,12 @@
                            int inputPitch, const void *input, size_t outputPitch, void *output) const;
     void loadCompressedImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
                                  int inputPitch, const void *input, size_t outputPitch, void *output) const;
+    void loadDXT1ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+                           int inputPitch, const void *input, size_t outputPitch, void *output) const;
+    void loadDXT3ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+                           int inputPitch, const void *input, size_t outputPitch, void *output) const;
+    void loadDXT5ImageData(GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
+                           int inputPitch, const void *input, size_t outputPitch, void *output) const;
 
     static unsigned int issueSerial();
 
diff --git a/src/libGLESv2/libGLESv2.cpp b/src/libGLESv2/libGLESv2.cpp
index 23957fe..26f2f23 100644
--- a/src/libGLESv2/libGLESv2.cpp
+++ b/src/libGLESv2/libGLESv2.cpp
@@ -772,6 +772,8 @@
         {
           case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
           case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+          case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+          case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
             break;
           default:
             return error(GL_INVALID_ENUM);
@@ -821,9 +823,27 @@
                 return error(GL_INVALID_ENUM);
             }
 
-            if (!context->supportsCompressedTextures())
-            {
-                return error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
+            switch (internalformat) {
+              case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+              case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+                if (!context->supportsDXT1Textures())
+                {
+                    return error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
+                }
+                break;
+              case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+                if (!context->supportsDXT3Textures())
+                {
+                    return error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
+                }
+                break;
+              case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+                if (!context->supportsDXT5Textures())
+                {
+                    return error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
+                }
+                break;
+              default: UNREACHABLE();
             }
 
             if (imageSize != gl::ComputeCompressedSize(width, height, internalformat))
@@ -897,6 +917,8 @@
         {
           case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
           case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+          case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+          case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
             break;
           default:
             return error(GL_INVALID_ENUM);
@@ -916,9 +938,27 @@
                 return error(GL_INVALID_VALUE);
             }
 
-            if (!context->supportsCompressedTextures())
-            {
-                return error(GL_INVALID_ENUM); // in this case, it's as though the format switch has failed.
+            switch (format) {
+              case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+              case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+                if (!context->supportsDXT1Textures())
+                {
+                    return error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
+                }
+                break;
+              case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+                if (!context->supportsDXT3Textures())
+                {
+                    return error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
+                }
+                break;
+              case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+                if (!context->supportsDXT5Textures())
+                {
+                    return error(GL_INVALID_ENUM); // in this case, it's as though the internal format switch failed
+                }
+                break;
+              default: UNREACHABLE();
             }
 
             if (imageSize != gl::ComputeCompressedSize(width, height, format))
@@ -929,7 +969,7 @@
             if (xoffset % 4 != 0 || yoffset % 4 != 0)
             {
                 return error(GL_INVALID_OPERATION); // we wait to check the offsets until this point, because the multiple-of-four restriction
-                                                    // does not exist unless DXT1 textures are supported.
+                                                    // does not exist unless DXT textures are supported.
             }
 
             if (target == GL_TEXTURE_2D)
@@ -1093,7 +1133,27 @@
                  break;
               case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
               case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-                if (context->supportsCompressedTextures())
+                if (context->supportsDXT1Textures())
+                {
+                    return error(GL_INVALID_OPERATION);
+                }
+                else
+                {
+                    return error(GL_INVALID_ENUM);
+                }
+                break;
+              case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+                if (context->supportsDXT3Textures())
+                {
+                    return error(GL_INVALID_OPERATION);
+                }
+                else
+                {
+                    return error(GL_INVALID_ENUM);
+                }
+                break;
+              case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+                if (context->supportsDXT5Textures())
                 {
                     return error(GL_INVALID_OPERATION);
                 }
@@ -1245,6 +1305,8 @@
                 break;
               case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
               case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+              case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+              case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
                 return error(GL_INVALID_OPERATION);
               default:
                 return error(GL_INVALID_OPERATION);
@@ -4467,6 +4529,8 @@
             break;
           case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:  // error cases for compressed textures are handled below
           case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+          case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+          case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
             break; 
           default:
             return error(GL_INVALID_VALUE);
@@ -4511,10 +4575,10 @@
                 return error(GL_INVALID_ENUM);
             }
 
-            if (format == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ||
-                format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
-            {
-                if (context->supportsCompressedTextures())
+            switch (format) {
+              case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+              case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+                if (context->supportsDXT1Textures())
                 {
                     return error(GL_INVALID_OPERATION);
                 }
@@ -4522,6 +4586,29 @@
                 {
                     return error(GL_INVALID_ENUM);
                 }
+                break;
+              case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+                if (context->supportsDXT3Textures())
+                {
+                    return error(GL_INVALID_OPERATION);
+                }
+                else
+                {
+                    return error(GL_INVALID_ENUM);
+                }
+                break;
+              case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+                if (context->supportsDXT5Textures())
+                {
+                    return error(GL_INVALID_OPERATION);
+                }
+                else
+                {
+                    return error(GL_INVALID_ENUM);
+                }
+                break;
+              default:
+                break;
             }
 
             if (type == GL_FLOAT)
diff --git a/src/libGLESv2/utilities.cpp b/src/libGLESv2/utilities.cpp
index ba23fd3..01cca3c 100644
--- a/src/libGLESv2/utilities.cpp
+++ b/src/libGLESv2/utilities.cpp
@@ -200,18 +200,23 @@
     {
       case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
       case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+        return 8 * (GLsizei)ceil((float)width / 4.0f) * (GLsizei)ceil((float)height / 4.0f);
         break;
+      case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
+      case GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE:
+        return 16 * (GLsizei)ceil((float)width / 4.0f) * (GLsizei)ceil((float)height / 4.0f);
       default:
         return 0;
     }
 
-    return 8 * (GLsizei)ceil((float)width / 4.0f) * (GLsizei)ceil((float)height / 4.0f);
 }
 
 bool IsCompressed(GLenum format)
 {
     if(format == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ||
-       format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
+       format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ||
+       format == GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE ||
+       format == GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE)
     {
         return true;
     }