Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.opengl.cts;
     17 
     18 import android.content.res.Resources;
     19 import android.graphics.Bitmap;
     20 import android.opengl.ETC1;
     21 import android.opengl.ETC1Util;
     22 import android.opengl.GLES20;
     23 
     24 import java.io.InputStream;
     25 import java.nio.Buffer;
     26 import java.nio.ByteBuffer;
     27 import java.nio.ByteOrder;
     28 import java.util.HashMap;
     29 
     30 public class CompressedTextureLoader {
     31     public static final String TEXTURE_UNCOMPRESSED = "UNCOMPRESSED";
     32     public static final String TEXTURE_ETC1 = "ETC1";
     33     public static final String TEXTURE_S3TC = "S3TC";
     34     public static final String TEXTURE_ATC = "ATC";
     35     public static final String TEXTURE_PVRTC = "PVRTC";
     36 
     37     public static class Texture {
     38         public Texture(int width, int height, int internalformat, ByteBuffer data,
     39                        String formatName) {
     40             mWidth = width;
     41             mHeight = height;
     42             mInternalFormat = internalformat;
     43             mData = data;
     44             mFormatName = formatName;
     45         }
     46 
     47         /**
     48          * Get the width of the texture in pixels.
     49          * @return the width of the texture in pixels.
     50          */
     51         public int getWidth() { return mWidth; }
     52 
     53         /**
     54          * Get the height of the texture in pixels.
     55          * @return the width of the texture in pixels.
     56          */
     57         public int getHeight() { return mHeight; }
     58 
     59         /**
     60          * Get the compressed data of the texture.
     61          * @return the texture data.
     62          */
     63         public ByteBuffer getData() { return mData; }
     64 
     65         /**
     66          * Get the format of the texture.
     67          * @return the internal format.
     68          */
     69         public int getFormat() { return mInternalFormat; }
     70 
     71         /**
     72          * Get the format of the texture.
     73          * @return the internal format.
     74          */
     75         public boolean isSupported() { return isFormatSupported(mFormatName); }
     76 
     77         private int mWidth;
     78         private int mHeight;
     79         private int mInternalFormat;
     80         private ByteBuffer mData;
     81         private String mFormatName;
     82     }
     83 
     84     /*  .pvr header is described by the following c struct
     85         typedef struct PVR_TEXTURE_HEADER_TAG{
     86             unsigned int  dwHeaderSize;   // size of the structure
     87             unsigned int  dwHeight;    // height of surface to be created
     88             unsigned int  dwWidth;    // width of input surface
     89             unsigned int  dwMipMapCount;   // number of MIP-map levels requested
     90             unsigned int  dwpfFlags;   // pixel format flags
     91             unsigned int  dwDataSize;   // Size of the compress data
     92             unsigned int  dwBitCount;   // number of bits per pixel
     93             unsigned int  dwRBitMask;   // mask for red bit
     94             unsigned int  dwGBitMask;   // mask for green bits
     95             unsigned int  dwBBitMask;   // mask for blue bits
     96             unsigned int  dwAlphaBitMask;   // mask for alpha channel
     97             unsigned int  dwPVR;    // should be 'P' 'V' 'R' '!'
     98             unsigned int  dwNumSurfs;   //number of slices for volume textures or skyboxes
     99         } PVR_TEXTURE_HEADER;
    100     */
    101     static final int PVR_HEADER_SIZE = 13 * 4;
    102     static final int PVR_2BPP = 24;
    103     static final int PVR_4BPP = 25;
    104     static final int PVR_MAGIC_NUMBER = 559044176;
    105 
    106     static final int GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00;
    107     static final int GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01;
    108     static final int GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02;
    109     static final int GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03;
    110 
    111     static class PVRHeader {
    112         int mHeaderSize;   // size of the structure
    113         int mHeight;    // height of surface to be created
    114         int mWidth;    // width of input surface
    115         int mMipMapCount;   // number of MIP-map levels requested
    116         int mpfFlags;   // pixel format flags
    117         int mDataSize;   // Size of the compress data
    118         int mBitCount;   // number of bits per pixel
    119         int mRBitMask;   // mask for red bit
    120         int mGBitMask;   // mask for green bits
    121         int mBBitMask;   // mask for blue bits
    122         int mAlphaBitMask;   // mask for alpha channel
    123         int mPVR;    // should be 'P' 'V' 'R' '!'
    124         int mNumSurfs;   //number of slices for volume textures or skyboxes
    125     }
    126 
    127     protected static PVRHeader readPVRHeader(InputStream is) {
    128 
    129         byte[] headerData = new byte[PVR_HEADER_SIZE];
    130         try {
    131             is.read(headerData);
    132         } catch (Exception e) {
    133             throw new RuntimeException("Unable to read data");
    134         }
    135 
    136         ByteBuffer headerBuffer = ByteBuffer.allocateDirect(PVR_HEADER_SIZE)
    137                 .order(ByteOrder.nativeOrder());
    138         headerBuffer.put(headerData, 0, PVR_HEADER_SIZE).position(0);
    139 
    140         PVRHeader header = new PVRHeader();
    141 
    142         header.mHeaderSize = headerBuffer.getInt();
    143         header.mHeight = headerBuffer.getInt();
    144         header.mWidth = headerBuffer.getInt();
    145         header.mMipMapCount = headerBuffer.getInt();
    146         header.mpfFlags = headerBuffer.getInt();
    147         header.mDataSize = headerBuffer.getInt();
    148         header.mBitCount = headerBuffer.getInt();
    149         header.mRBitMask = headerBuffer.getInt();
    150         header.mGBitMask = headerBuffer.getInt();
    151         header.mBBitMask = headerBuffer.getInt();
    152         header.mAlphaBitMask = headerBuffer.getInt();
    153         header.mPVR = headerBuffer.getInt();
    154         header.mNumSurfs = headerBuffer.getInt();
    155 
    156         if (header.mHeaderSize != PVR_HEADER_SIZE ||
    157             header.mPVR != PVR_MAGIC_NUMBER) {
    158             throw new RuntimeException("Invalid header data");
    159         }
    160 
    161         return header;
    162     }
    163 
    164     public static Texture loadTextureATC(Resources res, int id) {
    165         Texture tex = new Texture(0, 0, 0, null, "Cts!");
    166         return tex;
    167     }
    168 
    169     private static ETC1Util.ETC1Texture compressTexture(Buffer input,
    170                                                         int width, int height,
    171                                                         int pixelSize, int stride){
    172         int encodedImageSize = ETC1.getEncodedDataSize(width, height);
    173         ByteBuffer compressedImage = ByteBuffer.allocateDirect(encodedImageSize).
    174             order(ByteOrder.nativeOrder());
    175         ETC1.encodeImage(input, width, height, pixelSize, stride, compressedImage);
    176         return new ETC1Util.ETC1Texture(width, height, compressedImage);
    177     }
    178 
    179     public static Texture createFromUncompressedETC1(Bitmap bitmap) {
    180         int dataSize = bitmap.getRowBytes() * bitmap.getHeight();
    181 
    182         ByteBuffer dataBuffer;
    183         dataBuffer = ByteBuffer.allocateDirect(dataSize).order(ByteOrder.nativeOrder());
    184         bitmap.copyPixelsToBuffer(dataBuffer);
    185         dataBuffer.position(0);
    186 
    187         int bytesPerPixel = bitmap.getRowBytes() / bitmap.getWidth();
    188         ETC1Util.ETC1Texture compressed = compressTexture(dataBuffer,
    189                                                           bitmap.getWidth(),
    190                                                           bitmap.getHeight(),
    191                                                           bytesPerPixel,
    192                                                           bitmap.getRowBytes());
    193 
    194         Texture tex = new Texture(compressed.getWidth(), compressed.getHeight(),
    195                                   ETC1.ETC1_RGB8_OES, compressed.getData(), TEXTURE_ETC1);
    196 
    197         return tex;
    198     }
    199 
    200     private static ByteBuffer read(InputStream is, int dataSize) {
    201         ByteBuffer dataBuffer;
    202         dataBuffer = ByteBuffer.allocateDirect(dataSize).order(ByteOrder.nativeOrder());
    203         byte[] ioBuffer = new byte[4096];
    204         for (int i = 0; i < dataSize; ) {
    205             int chunkSize = Math.min(ioBuffer.length, dataSize - i);
    206             try {
    207                 is.read(ioBuffer, 0, chunkSize);
    208             } catch (Exception e) {
    209                 throw new RuntimeException("Unable to read data");
    210             }
    211             dataBuffer.put(ioBuffer, 0, chunkSize);
    212             i += chunkSize;
    213         }
    214         dataBuffer.position(0);
    215         return dataBuffer;
    216     }
    217 
    218     public static Texture loadTexturePVRTC(Resources res, int id) {
    219         InputStream is = null;
    220         try {
    221             is = res.openRawResource(id);
    222         } catch (Exception e) {
    223             throw new RuntimeException("Unable to open resource " + id);
    224         }
    225 
    226         PVRHeader header = readPVRHeader(is);
    227 
    228         int format = header.mpfFlags & 0xFF;
    229         int internalFormat = 0;
    230         if (format == PVR_2BPP && header.mAlphaBitMask == 1) {
    231             internalFormat = GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
    232         } else if (format == PVR_2BPP && header.mAlphaBitMask == 0) {
    233             internalFormat = GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
    234         } else if (format == PVR_4BPP && header.mAlphaBitMask == 1) {
    235             internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
    236         } else if (format == PVR_4BPP && header.mAlphaBitMask == 0) {
    237             internalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
    238         }
    239 
    240         // only load the first mip level for now
    241         int dataSize = (header.mWidth * header.mHeight * header.mBitCount) >> 3;
    242         ByteBuffer dataBuffer = read(is, dataSize);
    243         Texture tex = new Texture(header.mWidth, header.mHeight,
    244                                   internalFormat, dataBuffer,
    245                                   TEXTURE_PVRTC);
    246         try {
    247             is.close();
    248         } catch (Exception e) {
    249             throw new RuntimeException("Unable to close resource stream " + id);
    250         }
    251         return tex;
    252     }
    253 
    254     /* DDS Header is described by the following structs
    255        typedef struct {
    256           DWORD           dwSize;
    257           DWORD           dwFlags;
    258           DWORD           dwHeight;
    259           DWORD           dwWidth;
    260           DWORD           dwPitchOrLinearSize;
    261           DWORD           dwDepth;
    262           DWORD           dwMipMapCount;
    263           DWORD           dwReserved1[11];
    264           DDS_PIXELFORMAT ddspf;
    265           DWORD           dwCaps;
    266           DWORD           dwCaps2;
    267           DWORD           dwCaps3;
    268           DWORD           dwCaps4;
    269           DWORD           dwReserved2;
    270         } DDS_HEADER;
    271 
    272         struct DDS_PIXELFORMAT {
    273           DWORD dwSize;
    274           DWORD dwFlags;
    275           DWORD dwFourCC;
    276           DWORD dwRGBBitCount;
    277           DWORD dwRBitMask;
    278           DWORD dwGBitMask;
    279           DWORD dwBBitMask;
    280           DWORD dwABitMask;
    281         };
    282 
    283         In the file it looks like this
    284         DWORD               dwMagic;
    285         DDS_HEADER          header;
    286         DDS_HEADER_DXT10    header10; // If the DDS_PIXELFORMAT dwFlags is set to DDPF_FOURCC
    287                                       // and dwFourCC is DX10
    288 
    289     */
    290 
    291     static final int DDS_HEADER_STRUCT_SIZE = 124;
    292     static final int DDS_PIXELFORMAT_STRUCT_SIZE = 32;
    293     static final int DDS_HEADER_SIZE = 128;
    294     static final int DDS_MAGIC_NUMBER = 0x20534444;
    295     static final int DDS_DDPF_FOURCC = 0x4;
    296     static final int DDS_DXT1 = 0x31545844;
    297     static final int DDS_DXT5 = 0x35545844;
    298 
    299     static final int COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0;
    300     static final int COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1;
    301     static final int COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3;
    302 
    303     static class DDSHeader {
    304         int mMagic;
    305         int mSize;
    306         int mFlags;
    307         int mHeight;
    308         int mWidth;
    309         int mPitchOrLinearSize;
    310         int mDepth;
    311         int mMipMapCount;
    312         int[] mReserved1;
    313         // struct DDS_PIXELFORMAT {
    314             int mPixelFormatSize;
    315             int mPixelFormatFlags;
    316             int mPixelFormatFourCC;
    317             int mPixelFormatRGBBitCount;
    318             int mPixelFormatRBitMask;
    319             int mPixelFormatGBitMask;
    320             int mPixelFormatBBitMask;
    321             int mPixelFormatABitMask;
    322         // };
    323         int mCaps;
    324         int mCaps2;
    325         int mCaps3;
    326         int mCaps4;
    327         int mReserved2;
    328 
    329         DDSHeader() {
    330             mReserved1 = new int[11];
    331         }
    332     }
    333 
    334     protected static DDSHeader readDDSHeader(InputStream is) {
    335 
    336         byte[] headerData = new byte[DDS_HEADER_SIZE];
    337         try {
    338             is.read(headerData);
    339         } catch (Exception e) {
    340             throw new RuntimeException("Unable to read data");
    341         }
    342 
    343         ByteBuffer headerBuffer = ByteBuffer.allocateDirect(DDS_HEADER_SIZE)
    344                 .order(ByteOrder.nativeOrder());
    345         headerBuffer.put(headerData, 0, DDS_HEADER_SIZE).position(0);
    346 
    347         DDSHeader header = new DDSHeader();
    348 
    349         header.mMagic = headerBuffer.getInt();
    350         header.mSize = headerBuffer.getInt();
    351         header.mFlags = headerBuffer.getInt();
    352         header.mHeight = headerBuffer.getInt();
    353         header.mWidth = headerBuffer.getInt();
    354         header.mPitchOrLinearSize = headerBuffer.getInt();
    355         header.mDepth = headerBuffer.getInt();
    356         header.mMipMapCount = headerBuffer.getInt();
    357         for (int i = 0; i < header.mReserved1.length; i ++) {
    358             header.mReserved1[i] = headerBuffer.getInt();
    359         }
    360         // struct DDS_PIXELFORMAT {
    361             header.mPixelFormatSize = headerBuffer.getInt();
    362             header.mPixelFormatFlags = headerBuffer.getInt();
    363             header.mPixelFormatFourCC = headerBuffer.getInt();
    364             header.mPixelFormatRGBBitCount = headerBuffer.getInt();
    365             header.mPixelFormatRBitMask = headerBuffer.getInt();
    366             header.mPixelFormatGBitMask = headerBuffer.getInt();
    367             header.mPixelFormatBBitMask = headerBuffer.getInt();
    368             header.mPixelFormatABitMask = headerBuffer.getInt();
    369         // };
    370         header.mCaps = headerBuffer.getInt();
    371         header.mCaps2 = headerBuffer.getInt();
    372         header.mCaps3 = headerBuffer.getInt();
    373         header.mCaps4 = headerBuffer.getInt();
    374         header.mReserved2 = headerBuffer.getInt();
    375 
    376         if (header.mSize != DDS_HEADER_STRUCT_SIZE ||
    377             header.mPixelFormatSize != DDS_PIXELFORMAT_STRUCT_SIZE ||
    378             header.mMagic != DDS_MAGIC_NUMBER) {
    379             throw new RuntimeException("Invalid header data");
    380         }
    381 
    382         return header;
    383     }
    384 
    385     // Very simple loader that only reads in the header and a DXT1 mip level 0
    386     public static Texture loadTextureDXT(Resources res, int id) {
    387         InputStream is = null;
    388         try {
    389             is = res.openRawResource(id);
    390         } catch (Exception e) {
    391             throw new RuntimeException("Unable to open resource " + id);
    392         }
    393 
    394         DDSHeader header = readDDSHeader(is);
    395 
    396         if (header.mPixelFormatFlags != DDS_DDPF_FOURCC) {
    397             throw new RuntimeException("Unsupported DXT data");
    398         }
    399 
    400         int internalFormat = 0;
    401         int bpp = 0;
    402         switch (header.mPixelFormatFourCC) {
    403         case DDS_DXT1:
    404             internalFormat = COMPRESSED_RGB_S3TC_DXT1_EXT;
    405             bpp = 4;
    406             break;
    407         case DDS_DXT5:
    408             internalFormat = COMPRESSED_RGBA_S3TC_DXT5_EXT;
    409             bpp = 8;
    410             break;
    411         default:
    412             throw new RuntimeException("Unsupported DXT data");
    413         }
    414 
    415         // only load the first mip level for now
    416         int dataSize = (header.mWidth * header.mHeight * bpp) >> 3;
    417         if (dataSize != header.mPitchOrLinearSize) {
    418             throw new RuntimeException("Expected data and header mismatch");
    419         }
    420         ByteBuffer dataBuffer = read(is, dataSize);
    421 
    422         Texture tex = new Texture(header.mWidth, header.mHeight, internalFormat,
    423                                   dataBuffer, TEXTURE_S3TC);
    424         return tex;
    425     }
    426 
    427     static HashMap<String, Boolean> sExtensionMap;
    428     static HashMap<String, Boolean> sFormatMap;
    429 
    430     private static synchronized void updateSupportedFormats() {
    431         if (sExtensionMap != null) {
    432             return;
    433         }
    434 
    435         sExtensionMap = new HashMap<String, Boolean>();
    436         sFormatMap = new HashMap<String, Boolean>();
    437         String extensionList = GLES20.glGetString(GLES20.GL_EXTENSIONS);
    438 
    439         for (String extension : extensionList.split(" ")) {
    440             sExtensionMap.put(extension, true);
    441         }
    442 
    443         // Check ETC1
    444         sFormatMap.put(TEXTURE_ETC1, ETC1Util.isETC1Supported());
    445         // Check ATC
    446         if (sExtensionMap.get("GL_AMD_compressed_ATC_texture") != null ||
    447             sExtensionMap.get("GL_ATI_compressed_texture_atitc") != null ||
    448             sExtensionMap.get("GL_ATI_texture_compression_atitc") != null) {
    449             sFormatMap.put(TEXTURE_ATC, true);
    450         }
    451         // Check DXT
    452         if (sExtensionMap.get("GL_EXT_texture_compression_dxt1") != null ||
    453             sExtensionMap.get("GL_EXT_texture_compression_s3tc") != null ||
    454             sExtensionMap.get("OES_texture_compression_S3TC") != null) {
    455             sFormatMap.put(TEXTURE_S3TC, true);
    456         }
    457         // Check DXT
    458         if (sExtensionMap.get("GL_IMG_texture_compression_pvrtc") != null) {
    459             sFormatMap.put(TEXTURE_PVRTC, true);
    460         }
    461 
    462         /*Log.i(TAG, "mIsSupportedETC1 " + sFormatMap.get(TEXTURE_ETC1));
    463         Log.i(TAG, "mIsSupportedATC " + sFormatMap.get(TEXTURE_ATC));
    464         Log.i(TAG, "mIsSupportedDXT " + sFormatMap.get(TEXTURE_S3TC));
    465         Log.i(TAG, "mIsSupportedPVRTC " + sFormatMap.get(TEXTURE_PVRTC));*/
    466     }
    467 
    468     private static boolean isFormatSupported(String format) {
    469         updateSupportedFormats();
    470         Boolean supported = sFormatMap.get(format);
    471         return supported != null ? supported : false;
    472     }
    473 }
    474