Android Texture Compression [PDF 345KB]
Applying an image, or texture, to a 2D or 3D model to enhance graphical detail is a very common technique in the field of computer graphics. Android allows the usage of a variety of texture compression file formats, each of which has its own set of advantages and disadvantages. The Android Texture Compression sample allows developers to easily compare textures of four different texture compression file formats: Portable Network Graphics (PNG), Ericsson Texture Compression (ETC), PowerVR* Texture Compression (PVRTC), and S3 Texture Compression (S3TC), which is also known as DirectX* Texture Compression (DXTC). This sample demonstrates how to load and use these different formats with OpenGL ES* on Android. All supported texture formats are shown side-by-side so the relative size and quality can be observed. Choosing the right compression allows the developer to balance app size, visual quality, and performance.
The sample loads each texture format, determines the mapping coordinates of the texture, and displays a portion of each of the textures. The final composition will display the full image/texture, but as four separate, format-specific textures. They are individually labeled at the top of the screen and the file sizes are provided on a small bar at the bottom of the screen.
Background on Principles/Related Terms and Texture Formats
Texture mapping is a method by which an image is applied to the surface of a shape or polygon. A helpful analogy to keep in mind is picturing the texture as wrapping paper and the 3D model as a gift box to be wrapped. This is why this process is also called “texture wrapping.”
Figure 1 - 1 is solely the polygon/shape tank model and 2 is the texture-mapped model
Mipmaps are an optimized group of images that are generated with the primary texture. They are typically created for the purpose of improving rendering speed and reducing aliasing artifacts. Each mip (bitmap image of the collection of mipmap) is a lower resolution version of the primary texture, utilized when viewing the original texture from a distance or a downsized version of it. The creation and implementation of mipmaps come from the basic concept that we cannot detect as much detail in an object when it is located far away from us or when the object is miniscule. Based on this idea, different mips can be used to represent different parts of the texture/image based on the size of the objects. This increases rendering speed because the simplified mips have a much lower texel (overall number of texture pixels) count—resulting in fewer pixels to be processed. Additionally, since mipmaps are essentially anti-aliased, the number of noticeable artifacts is also greatly reduced. The support for mipmaps in PNG, ETC (KTX), PVRTC, and S3TC are included in the sample.
Figure 2 – Example of a mipmap chain
Portable Network Graphics (PNG)
PNG is a bitmapped image format primarily noted for its lossless data file compression. The image format is equipped with support for palette-based images (24-bit RGB or 32-bit RGBA) or grayscale images (with and without an alpha channel).
- Has a lossless compression scheme and high visual quality
- Handles both 8-bit and 16-bit transparency
- Large file size; this will increase app size and memory bandwidth requirements
- Highest GPU cost (i.e., worst performance)
Ericsson Texture Compression (ETC)
Ericsson Texture Compression is a texture compression format that was made standard by Khronos for OpenGL ES 2.0. Therefore, this texture compression format is available on nearly all Android devices. The texture compression scheme that is referred to here is also known as ETC1 (ETC2, an updated version, is still undergoing review/development). ETC1 supports compression of 24-bit RGB data, but it does not support the compression of any images/textures containing alpha components. In addition, there are two different file formats that both fall under the category of ETC texture compression: KTX and PKM. KTX is the standard Khronos Group compression format, and it provides a container for multiple images/textures. When mipmaps are generated with KTX, only one KTX file is created. On the other hand, PKM is a much simpler file format used mainly to contain single compressed images. Generating mipmaps in this case would create multiple PKM files instead of a single file, and so as a consequence, this format is not recommended for that purpose.
- File size is considerably smaller in comparison to the PNG texture compression format
- GPU hardware acceleration supported on nearly all Android devices
- Quality is not as high as PNG texture compression (ETC is a lossy compression format)
- Does not support alpha channels/components
Example of Tools Used For Compression:
PowerVR Texture Compression (PVRTC)
PowerVR Texture Compression is a lossy, fixed-rate texture compression format utilized primarily in Imagination Technologies’ PowerVR MBX, SGX, and Rogue technologies. It is currently being employed in all iPhone*, iPod*, and iPad* devices as the standard compression format. Unlike ETC and S3TC, PVRTC is not block-based. Instead it involves the bilinear upscaling and low-precision blending of two low-resolution images. In addition to the unique process of compression by the PVRTC format, it also supports RGBA (alpha channel supported) for both the 2-bpp (bits per pixel) and 4-bpp options.
- Supports alpha channels/components
- Supports RGBA data for both 2-bpp and 4-bpp modes
- File size is much smaller than one produced by PNG texture compression
- GPU hardware acceleration on PowerVR GPUs
- Quality is not as high as PNG texture compression (PVRTC is a lossy compression format)
- PVRTC is only supported on PowerVR hardware
- Only square (power-of-two) dimension textures are determined to work consistently, although in some cases, rectangular support is provided for the compressed texture
- Compressing textures into this format can be slow
Tool Used For Compression:
S3 Texture Compression (S3TC) or DirectX Texture Compression (DXTC)
S3 Texture Compression is a lossy, fixed-rate, texture compression format. This style of compression makes S3TC an ideal texture compression format for textures used in hardware-accelerated 3D computer graphics. Following the integration of S3TC with Microsoft’s DirectX 6.0 and OpenGL* 1.3, the compression format became much more widespread. There are at least 5 different variations of the S3TC format (including DXT1 through DXT5). The sample supports the commonly used variations (DXT1, DXT3, and DXT5).
DXT1: DXT1 is the smallest mode of S3TC compression; it converts each block of 16 pixels into 64 bits. Additionally, it is composed of two different 16-bit RGB 5:8:5 color values and a 4x4 2-bit lookup table. DXT1 does not support alpha channels.
DXT3: DXT3 converts each block of 16 pixels into 128 bits and is composed of 64 bits of alpha channel data and 64 bits of color data. DXT3 is a good format choice for images or textures with sharp alpha transitions (opaque versus translucent).
DXT5: DXT5 converts each block of 16 pixels into 128 bits and is composed of 64 bits of alpha channel data and 64 bits of color data. DXT5 is a good format choice for images or textures with gradient alpha transitions.
- File size is considerably smaller in comparison to the PNG texture compression format.
- Decent quality, low banding (artifacts not too visible)
- Good speed for compression/decompression
- GPU hardware acceleration for a wide range of video chip parts: support is almost universal on desktop, but is still growing on Android devices
- Quality is not as high as PNG texture compression (S3TC is a lossy compression format)
- Not supported on all Android devices
Tool Used for Compression:
- DirectX Texture Tool from DirectX (included in the DX SDK)
Accessing the Texture Data
Most texture compression file formats have a header that is placed before the actual texture data. The header usually contains data regarding the name of the texture compression format; the width, height, and depth of the texture; the size of the data; the internal format; and other specific properties of the file.
Our goal is to load and map texture data from each of the different texture compression files to a 2D model for comparison of quality and file size. The header that comes before the texture data is not to be included as part of the texture to be mapped, as doing so would distort the image/texture representation. The file headers all vary depending on the file compression format that is being considered, and as such, each texture compression file format needs individual support in order to load the textures and map them properly.
The PVRTC header is packed due to the presence of the 64-bit pixel format data member (mPixelFormat in the sample). ARM attempts to align the header by padding it with 4 additional bytes, making the header a total of 56 bytes instead of the raw 52 bytes. This in turn causes the image to be distorted when displayed on an ARM device. Intel devices do not pad the header, and so this isn’t an issue. Packing the header solves the ARM padding issue, and the texture displays correctly on both ARM and Intel devices.
Figure 3 – Image of ARM Padding Issue with PVRTC
Loading and Supporting the Texture Formats
Mipmaps are taken care of in PNG by a simple function called glGenerateMipmap, a predefined function from Khronos OpenGL designed for this specific purpose. Sean Barrett’s public domain stb_image.c was utilized in the reading and loading of the PNG files (as well as locating and pinpointing the texture data to be processed). The following is a piece of code that initializes the texture and provides mipmap support for the PNG compression format.
// Initialize the texture
glTexImage2D( GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, pData );
// Mipmap support
glGenerateMipmap( GL_TEXTURE_2D );
As mentioned earlier, ETC is composed of two different format types—KTX and PKM. KTX is the standard compression format, used as a container for multiple images/textures, and is ideal for mipmapping. PKM, on the other hand, is designed for simple single texture compression, and so generating mipmaps gives rise to multiple PKM files, which is inefficient. For this reason, mipmap support for ETC texture compression in the sample app is restricted to KTX file compression only. Khronos provides an open source C library (libktx) in which KTX texture loading with mipmaps is supported. We took advantage of this tool and implemented the code in a texture loading function called LoadTextureETC_KTX. The function used to actually load the KTX texture compression format file is ktxLoadTextureM (loads the desired texture from data in memory). This function (ktxLoadTextureM) was provided in the library (libktx) and is documented on the Khronos site (see “Resources” below).
The following is a piece of code that initializes the texture and provides mipmap support for the ETC (KTX) compression format:
// Generate handle & Load Texture
GLuint handle = 0;
KTX_error_code result = ktxLoadTextureM( pData, fileSize, &handle, &target, NULL, &mipmapped, NULL, NULL, NULL );
if( result != KTX_SUCCESS )
LOGI( "KTXLib couldn't load texture %s. Error: %d", TextureFileName, result );
// Bind the texture
glBindTexture( target, handle );
Providing mipmap support for PVRTC textures was a bit trickier. After reading through the header, the offset is defined as the size of the header plus the metadata size (metadata follows the header and is also not part of the actual texture data). For each of the mips generated, pixels are grouped into blocks (different depending on if it is 4 bpp or 2 bpp—both valid PVRTC formats). Next, clamping occurs, and so height and width of the blocks are limited to certain boundaries. Then, the function glCompressedTexImage() is called to identify a two-dimensional image in the PVRTC compressed format. Following that, the pixel data size is calculated and then is added to the offset in order to group the set of pixels in the next mip. This process is repeated until there are no more mips to operate on.
// Initialize the texture
unsigned int offset = sizeof(PVRHeaderV3) + pHeader->mMetaDataSize;
unsigned int mipWidth = pHeader->mWidth;
unsigned int mipHeight = pHeader->mHeight;
unsigned int mip = 0;
// Determine size (width * height * bbp/8), min size is 32
unsigned int pixelDataSize = ( mipWidth * mipHeight * bitsPerPixel ) >> 3;
pixelDataSize = (pixelDataSize < 32) ? 32 : pixelDataSize;
// Upload texture data for this mip
glCompressedTexImage2D(GL_TEXTURE_2D, mip, format, mipWidth, mipHeight, 0, pixelDataSize, pData + offset);
// Next mips is half the size (divide by 2) with a min of 1
mipWidth = ( mipWidth >> 1 == 0 ) ? 1 : mipWidth >> 1;
mipHeight = ( mipHeight >> 1 == 0 ) ? 1 : mipHeight >> 1;
// Move to next mip
offset += pixelDataSize;
} while(mip < pHeader->mMipmapCount);
After loading an S3TC texture file, determining the format, and reading past the header, mipmap support takes place. Each mip is looped through, and pixels are grouped into blocks. Then, the function glCompressedTexImage is called to identify a two-dimensional image in the S3TC compressed format. The aggregate data size of the blocks is then added to the offset in order to move to the next mip and perform the same actions. The process repeats until there are no more mips to operate on. The following is a piece of code that initializes the texture and provides mipmap support for the S3TC compression format.
// Initialize the texture
unsigned int offset = 0;
unsigned int width = pHeader->mWidth;
unsigned int height = pHeader->mHeight;
unsigned int mip = 0;
// Determine size
// As defined in extension: size = ceil(<w>/4) * ceil(<h>/4) * blockSize
unsigned int Size = ((width + 3) >> 2) * ((height + 3) >> 2) * blockSize;
glCompressedTexImage2D( GL_TEXTURE_2D, mip, format, width, height, 0, Size, (pData + sizeof(DDSHeader)) + offset );
checkGlError( "glCompressedTexImage2D" );
offset += Size;
if( ( width <<= 1 ) == 0) width = 1;
if( ( height <<= 1 ) == 0) height = 1;
} while( mip < pHeader->mMipMapCount );
Depending on the situation it is used in, proper texture compression may improve visual quality, decrease the size of an app considerably, and greatly enhance performance. Optimal texture compression provides substantial advantages to developers and their applications. The Android Texture Compression sample app demonstrates how to load and access the most popular texture formats that can be found on Android. Go download the source code and incorporate the best texture compression in your next project.
About the Author
William Guo created this sample while he was an intern with Intel’s Personal Form Factor team while working on Intel phones and tablets. He is currently attending the University of California, Berkeley as a sophomore with an expected graduation date of May 2015. He intends to major in Electrical Engineering and Computer Science with a possible minor in psychology.
- Texture Mapping Figure 1:
- Mipmapping Image Figure 2:
- PNG Info:
- ETC (KTX and PKM) Info:
- PVRTC Info
- S3TC Info:
INFORMATION IN THIS DOCUMENT IS PROVIDED IN CONNECTION WITH INTEL PRODUCTS. NO LICENSE, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, TO ANY INTELLECTUAL PROPERTY RIGHTS IS GRANTED BY THIS DOCUMENT. EXCEPT AS PROVIDED IN INTEL'S TERMS AND CONDITIONS OF SALE FOR SUCH PRODUCTS, INTEL ASSUMES NO LIABILITY WHATSOEVER AND INTEL DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY, RELATING TO SALE AND/OR USE OF INTEL PRODUCTS INCLUDING LIABILITY OR WARRANTIES RELATING TO FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR INFRINGEMENT OF ANY PATENT, COPYRIGHT OR OTHER INTELLECTUAL PROPERTY RIGHT.
UNLESS OTHERWISE AGREED IN WRITING BY INTEL, THE INTEL PRODUCTS ARE NOT DESIGNED NOR INTENDED FOR ANY APPLICATION IN WHICH THE FAILURE OF THE INTEL PRODUCT COULD CREATE A SITUATION WHERE PERSONAL INJURY OR DEATH MAY OCCUR.
Intel may make changes to specifications and product descriptions at any time, without notice. Designers must not rely on the absence or characteristics of any features or instructions marked "reserved" or "undefined." Intel reserves these for future definition and shall have no responsibility whatsoever for conflicts or incompatibilities arising from future changes to them. The information here is subject to change without notice. Do not finalize a design with this information.
The products described in this document may contain design defects or errors known as errata which may cause the product to deviate from published specifications. Current characterized errata are available on request.
Contact your local Intel sales office or your distributor to obtain the latest specifications and before placing your product order.
Copies of documents which have an order number and are referenced in this document, or other Intel literature, may be obtained by calling 1-800-548-4725, or go to: http://www.intel.com/design/literature.htm
Software and workloads used in performance tests may have been optimized for performance only on Intel microprocessors. Performance tests, such as SYSmark and MobileMark, are measured using specific computer systems, components, software, operations, and functions. Any change to any of those factors may cause the results to vary. You should consult other information and performance tests to assist you in fully evaluating your contemplated purchases, including the performance of that product when combined with other products.
Any software source code reprinted in this document is furnished under a software license and may only be used or copied in accordance with the terms of that license.
Intel and the Intel logo are trademarks of Intel Corporation in the US and/or other countries.
OpenGL is a registered trademark of Silicon Graphics, Inc. in the United States and/or other countries worldwide.
Copyright © 2012 Intel Corporation. All rights reserved.
*Other names and brands may be claimed as the property of others.