📜 ⬆️ ⬇️

Compression of textures in Android: format comparison and code samples

What is the best format for compressing textures? Maybe it's PNG, ETC, PVRTC, S3TC, or some other? The question is not simple, but very important. The answer depends on the quality of visual design, speed and size of Android applications. The matter is complicated by the fact that a universal “best format” simply does not exist. It all depends on the needs of the developer.



The technology of applying textures to two-dimensional or three-dimensional models is widely used in computer graphics. This is done in order to improve the detail of objects that are represented by models. Android supports many texture compression formats, each with its own advantages and disadvantages.

Preliminary information about working with textures and their storage formats


Texture mapping is a method of “sticking” an image onto the surface of shapes or polygons. In order to make it clearer, the figure can be compared with a box, and the texture with a patterned wrapping paper in which this box is wrapped in order to put something good in it and give it to someone. Therefore, in the English-language literature, texture mapping is also called “texture wrapping,” which can be translated as “wrapping textures.”
')

The first tank is a polygonal model, and the second is the same model on which the textures are applied.

MIP maps (Mipmaps) are optimized groups of images that are generated for the main texture. Usually they are created in order to increase the speed of image rendering and to smooth images (anti-aliasing), that is, to get rid of the effect of “stepped” lines. Each level of the map (it is called “mip”, in fact, this is one of the bitmap images, of which the set of textures included in the MIP map consists of) is a version of the original texture with a lower resolution.

Such an image is used in cases where a textured object is visible from a distance, or when its size is reduced. The idea of ​​using MIP cards is based on the fact that we simply cannot distinguish between small details of an object that is far from us or has small dimensions. Based on this idea, different parts of the map can be used to represent different parts of the texture, based on the size of the object. This increases the rendering speed due to the fact that reduced versions of the main texture have much less texels (texture pixels), that is, the GPU has to process less data to display a textured model. In addition, since MIP cards are usually smoothed, the number of noticeable artifacts is seriously reduced. Here we look at MIP cards in PNG, ETC (KTX), ETC2 (KTX), PVRTC, and S3TC formats.


MIP card

Portable Network Graphics (PNG)


PNG is a raster image storage format, especially noticeable in that it uses an algorithm for compressing image data without loss of information. It supports color indexed images (24 bits RGB or 32 bits RGBA), full-color and halftone images, as well as an alpha channel.

Benefits



disadvantages



Ericsson Texture Compression (ETC)


Ericsson Texture Compression is a texture compression format that operates on blocks of 4x4 pixels. Initially, Khronos used ETC as the standard format for Open GL ES 2.0. (this version is also called ETC1). As a result, this format is available on almost all Android devices. With the release of OpenGL ES 3.0. As a standard, the ETC2 format is used - a revised version of ETC1. The main difference between these two standards lies in the algorithm that operates on pixel groups. Improvements in the algorithm led to a higher accuracy of displaying fine detail images. As a result, image quality has improved, but file size does not.

ETC1 and ETC2 support compression of 24-bit RGB data, but they do not support compression of images with the alpha channel. In addition, there are two different file formats related to the ETC algorithm: these are KTX and PKM.

KTX is the standard file format of the Khronos Group, it provides a container in which you can store many images. When a MIP card is created using KTX, a single KTX file is generated. The PKM file format is much simpler, such files are mainly used to store individual images. As a result, using PKM to create a MIP card will result in several PKM files instead of a single KTX. Therefore, it is not recommended to use the PKM format for storing MIP cards.

Benefits



disadvantages



You can use the Mali GPU Texture Compression Tool and the ETC-Pack Tool to compress images in ETC.

PowerVR Texture Compression (PVRTC)


PowerVR Texture Compression is a lossy image compression format with a fixed compression level, which is used mainly in devices of Imagination Technology PowerVR MBX, SGX and Rogue. It is used as a standard method of compressing images on the iPhone, iPod, iPad.

Unlike ETC and S3TC, the PVRTC algorithm does not work with fixed blocks of pixels. It uses bilinear zooming and low-resolution mixing of two low-resolution images. In addition to the unique compression process, PVRTC supports the RGBA format (with transparency) for both the 2-bpp option (2 bits per pixel) and the 4-bpp option (4 bits per pixel).

Benefits



disadvantages




PVRTexTool can be used for compression .

S3 Texture Compression (S3TC) or DirectX Texture Compression (DXTC)


S3 Texture Compression is a lossy image compression format with a fixed compression level. Its features make this format ideal for compressing textures used in 3D applications designed for the use of a graphics accelerator. The integration of S3TC with Microsoft DirectX 6.0 and OpenGL 1.3 contributed to its widespread adoption. There are at least 5 different S3TC format options (from DXT1 to DXT5). The sample application supports the most commonly used variants (DXT1, DXT3 and DXT5).

DXT1 provides the strongest compression. Each 16-pixel input block is converted into a 64-bit block consisting of two 16-bit RGB 5: 6: 5 color values ​​and a 2-bit lookup table of 4x4 size. Transparency support is limited to one color (1-bit transparency).

DXT3 converts each block of 16 pixels into 128 bits, 64 bits fall into alpha channel data, 64 - into color information. DXT3 is very well suited for images or textures with sharp transitions between transparent and opaque areas. However, if there are no gradations of transparency, and there are transparent areas in the image, it is worth considering using DXT1.

DXT5, like DXT3, converts each block of 16 pixels into 128 bits, 64 bits fall into alpha channel data, 64 into color information. However, unlike DXT3, DXT5 is suitable for images or textures with smooth transitions between transparent and opaque areas.

Benefits



disadvantages



You can use DirectX Texture Tool from DirectX (included in the DX SDK) to work with this format.

Access to texture data


Most file formats for storing compressed textures include a header located in front of the image data. Usually the header contains information about the name of the format of the compression of textures, the width and height of the texture, its color depth, the size of the data, the internal format and other information about the file.

Our goal is to load texture data from different files and superimpose them on a two-dimensional model to compare image quality and data size. The header, which is located in front of the graphic data, should not be processed as part of the texture, if we consider it a fragment of the image and superimpose it on the model, this will lead to distortions. File headers for different texture compression formats are different, so each format needs individual support, otherwise it will not work to load and apply texture correctly.

note


The PVRTC header is packed taking into account the presence of a data member of a 64-bit pixel format (mPixelFormat in the example). In the code compiled for ARM, the header is aligned with the addition of 4 additional bytes to it, with the result that, from the original 52-byte one, it becomes 56-byte. This leads to the fact that when outputting to ARM devices, the image is distorted. In the code compiled for Intel processors, this does not happen. Packing the header solves the alignment problem on ARM devices, as a result, the texture is displayed correctly on both ARM devices and Intel devices.


Here's what the image distortion on the ARM device looks like caused by alignment of the header.

About example application


An example of Android Texture Compression, fragments of which will be given below, allows everyone to quickly compare the quality of textures in five formats. Namely, it is Portable Network Graphics (PNG), Ericsson Texture Compression (ETC), Ericsson Texture Compression 2 (ETC2), PowerVR Texture Compression (PVRTC), and S3 Texture Compression (S3TC), which is sometimes called DirectX Texture Compression (DXTC) .

The example shows how to load and use textures of these formats using OpenGL ES in Android. Images stored in different formats are located next to each other, which allows you to compare their size and quality. Choosing the most suitable format for storing textures for a specific project allows the developer to find the right balance between application size, visual picture quality and performance.

In the example, the image stored in the file of each of the formats is loaded, the coordinates are determined to overlay it on the model, and a fragment of each texture is displayed. The result is a single image, divided into four textures of the appropriate format. The formats are shown at the top of the screen, the file size is shown below.

The example considered here is based on the code that William Guo created. Christiano Ferreira, an expert on graphics applications from Intel, has added an example using ETC2 texture compression. Download the code here .


Texture compression formats: size and quality

PNG download


You can work with PNG MIP maps using the simple glGenerateMipmap function from Khronos OpenGL, which was created specifically for this purpose. We, in order to read and download PNG files, used the code prepared by Sean Barret, stb_image.c, which is publicly available. Also, this code is used to find and select the area of ​​texture that needs to be processed.

//      glTexImage2D( GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, pData );    //  MIP-    glGenerateMipmap( GL_TEXTURE_2D ); 

ETC / ETC2 Download


As mentioned above, ETC textures can be stored in KTX and PKM files. KTX is a standard compression format used as a container for multiple images, it is ideal for creating MIP cards. In turn, PKM was created to store separate compressed images, so creating MIP cards based on it results in the need to generate multiple files, and this is inefficient. MTC card support for ETC in the example is limited to the KTX format.

Khronos provides an open source library written in C (libktx), which supports downloading MIP cards from KTX files. We used this library and implemented the code in the LoadTextureETC_KTX function responsible for loading textures. The function that directly loads KTX files is called ktxLoadTextureM. It allows you to load the desired texture from the data in memory. This function is part of the libktx library; documentation on it can be found on the Khronos website .

Here is a snippet of code that initializes the texture and provides MIP card support for the ETC (KTX) format.

 //   (handle)      GLuint handle = 0;   GLenum target;   GLboolean mipmapped;       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 );       return 0;   }   //     glBindTexture( target, handle ); 

PVRTC loading


MIP card support for PVRTC textures is a bit more complicated. After reading the header, an offset is determined that equals the sum of the sizes of the header and the metadata. Metadata follows the heading; they are not part of the image. For each generated map level, pixels are grouped into blocks (the differences depend on whether the encoding is 4 bits per pixel or 2 bits - both options are suitable for PVRTC). Further, there is a search for boundaries, fixed width and height of the blocks. Then the function glCompressedTexImage () is called, it identifies a two-dimensional image in the compressed format PVRTC. Further, the size of the pixel data is calculated and what is obtained is added to the previously found offset in order to group the set of pixels for the next map fragment. This process is repeated until all the textures that make up the map are processed.

 //     unsigned int offset = sizeof(PVRHeaderV3) + pHeader->mMetaDataSize;   unsigned int mipWidth = pHeader->mWidth;   unsigned int mipHeight = pHeader->mHeight;   unsigned int mip = 0;   do   {       //   ( *  * bbp/8),    32       unsigned int pixelDataSize = ( mipWidth * mipHeight * bitsPerPixel ) >> 3;       pixelDataSize = (pixelDataSize < 32) ? 32 : pixelDataSize;       //             glCompressedTexImage2D(GL_TEXTURE_2D, mip, format, mipWidth, mipHeight, 0, pixelDataSize, pData + offset);       checkGlError("glCompressedTexImage2D");       //        ,  – 1       mipWidth  = ( mipWidth >> 1 == 0 ) ? 1 : mipWidth >> 1;       mipHeight = ( mipHeight >> 1 == 0 ) ? 1 : mipHeight >> 1;       //           offset += pixelDataSize;       mip++;   } while(mip < pHeader->mMipmapCount); 

S3TC download


After downloading the file that stores the S3TC texture, its format is determined and the MIP card located behind the header is read. A fragment of the map is traversed, the pixels are grouped into blocks. Then, to identify the two-dimensional image in the compressed data, the function glCompressedTexImage () is called. The total block size is then added to the offset in order to be able to find the beginning of the next map fragment and perform the same actions. This is repeated until all levels of the map have been processed. Here is a code snippet that initializes the texture and provides MIP card support for the S3TC format.

 //     //      unsigned int offset = 0;   unsigned int width = pHeader->mWidth;   unsigned int height = pHeader->mHeight;   unsigned int mip = 0;   do   {       //         //   : 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;       mip++;   } while( mip < pHeader->mMipMapCount ); 

findings


Depending on the specific situation, choosing the most appropriate format for storing compressed textures can improve the appearance of images, seriously reduce the size of the application and significantly improve performance. Careful selection of the optimal texture compression method can give developers and their applications a serious competitive advantage. The example application Android Texture Compression shows how to work with textures of the most popular formats in Android. Download the code and add support for the most appropriate texture compression formats to your projects.

Source: https://habr.com/ru/post/276089/


All Articles