1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program Tester Core 3 * ---------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief Image IO. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "tcuImageIO.hpp" 25 #include "tcuResource.hpp" 26 #include "tcuSurface.hpp" 27 #include "tcuCompressedTexture.hpp" 28 #include "deFilePath.hpp" 29 #include "deUniquePtr.hpp" 30 31 #include <string> 32 #include <vector> 33 #include <cstdio> 34 35 #include "png.h" 36 37 namespace tcu 38 { 39 namespace ImageIO 40 { 41 42 using std::string; 43 using std::vector; 44 45 /*--------------------------------------------------------------------*//*! 46 * \brief Load image from resource 47 * 48 * TextureLevel storage is set to match image data. Only PNG format is 49 * currently supported. 50 * 51 * \param dst Destination pixel container 52 * \param archive Resource archive 53 * \param fileName Resource file name 54 *//*--------------------------------------------------------------------*/ 55 void loadImage (TextureLevel& dst, const tcu::Archive& archive, const char* fileName) 56 { 57 string ext = de::FilePath(fileName).getFileExtension(); 58 59 if (ext == "png" || ext == "PNG") 60 loadPNG(dst, archive, fileName); 61 else 62 throw InternalError("Unrecognized image file extension", fileName, __FILE__, __LINE__); 63 } 64 65 DE_BEGIN_EXTERN_C 66 static void pngReadResource (png_structp png_ptr, png_bytep data, png_size_t length) 67 { 68 tcu::Resource* resource = (tcu::Resource*)png_get_io_ptr(png_ptr); 69 resource->read(data, (int)length); 70 } 71 DE_END_EXTERN_C 72 73 /*--------------------------------------------------------------------*//*! 74 * \brief Load PNG image from resource 75 * 76 * TextureLevel storage is set to match image data. 77 * 78 * \param dst Destination pixel container 79 * \param archive Resource archive 80 * \param fileName Resource file name 81 *//*--------------------------------------------------------------------*/ 82 void loadPNG (TextureLevel& dst, const tcu::Archive& archive, const char* fileName) 83 { 84 de::UniquePtr<Resource> resource(archive.getResource(fileName)); 85 86 // Verify header. 87 deUint8 header[8]; 88 resource->read(header, sizeof(header)); 89 TCU_CHECK(png_sig_cmp((png_bytep)&header[0], 0, DE_LENGTH_OF_ARRAY(header)) == 0); 90 91 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, DE_NULL, DE_NULL, DE_NULL); 92 TCU_CHECK(png_ptr); 93 94 png_infop info_ptr = png_create_info_struct(png_ptr); 95 TCU_CHECK(info_ptr); 96 97 if (setjmp(png_jmpbuf(png_ptr))) 98 throw InternalError("An error occured when loading PNG", fileName, __FILE__, __LINE__); 99 100 png_set_read_fn(png_ptr, resource.get(), pngReadResource); 101 png_set_sig_bytes(png_ptr, 8); 102 103 png_read_info(png_ptr, info_ptr); 104 105 const deUint32 width = (deUint32)png_get_image_width(png_ptr, info_ptr); 106 const deUint32 height = (deUint32)png_get_image_height(png_ptr, info_ptr); 107 TextureFormat textureFormat; 108 109 { 110 const png_byte colorType = png_get_color_type(png_ptr, info_ptr); 111 const png_byte bitDepth = png_get_bit_depth(png_ptr, info_ptr); 112 113 if (colorType == PNG_COLOR_TYPE_RGB && bitDepth == 8) 114 textureFormat = TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8); 115 else if (colorType == PNG_COLOR_TYPE_RGBA && bitDepth == 8) 116 textureFormat = TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8); 117 else 118 throw InternalError("Unsupported PNG depth or color type", fileName, __FILE__, __LINE__); 119 } 120 121 // Resize destination texture. 122 dst.setStorage(textureFormat, width, height); 123 124 std::vector<png_bytep> row_pointers; 125 row_pointers.resize(height); 126 for (deUint32 y = 0; y < height; y++) 127 row_pointers[y] = (deUint8*)dst.getAccess().getDataPtr() + y*dst.getAccess().getRowPitch(); 128 129 png_read_image(png_ptr, &row_pointers[0]); 130 131 png_destroy_info_struct(png_ptr, &info_ptr); 132 png_destroy_read_struct(&png_ptr, DE_NULL, DE_NULL); 133 } 134 135 static int textureFormatToPNGFormat (const TextureFormat& format) 136 { 137 if (format == TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8)) 138 return PNG_COLOR_TYPE_RGB; 139 else if (format == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8)) 140 return PNG_COLOR_TYPE_RGBA; 141 else 142 throw InternalError("Unsupported texture format", DE_NULL, __FILE__, __LINE__); 143 } 144 145 /*--------------------------------------------------------------------*//*! 146 * \brief Write image to file in PNG format 147 * 148 * This is provided for debugging and development purposes. Test code must 149 * not write to any files except the test log by default. 150 * 151 * \note Only RGB/RGBA, UNORM_INT8 formats are supported 152 * \param src Source pixel data 153 * \param fileName File name 154 *//*--------------------------------------------------------------------*/ 155 void savePNG (const ConstPixelBufferAccess& src, const char* fileName) 156 { 157 FILE* fp = fopen(fileName, "wb"); 158 TCU_CHECK(fp); 159 160 png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 161 162 if (!pngPtr) 163 { 164 fclose(fp); 165 TCU_CHECK(pngPtr); 166 } 167 168 png_infop infoPtr = png_create_info_struct(pngPtr); 169 if (!infoPtr) 170 { 171 png_destroy_write_struct(&pngPtr, NULL); 172 TCU_CHECK(infoPtr); 173 } 174 175 if (setjmp(png_jmpbuf(pngPtr))) 176 { 177 png_destroy_write_struct(&pngPtr, &infoPtr); 178 fclose(fp); 179 throw tcu::InternalError("PNG compression failed"); 180 } 181 else 182 { 183 int pngFormat = textureFormatToPNGFormat(src.getFormat()); 184 185 png_init_io(pngPtr, fp); 186 187 // Header 188 png_set_IHDR(pngPtr, infoPtr, src.getWidth(), src.getHeight(), 8, 189 pngFormat, PNG_INTERLACE_NONE, 190 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 191 png_write_info(pngPtr, infoPtr); 192 193 std::vector<png_bytep> rowPointers(src.getHeight()); 194 for (int y = 0; y < src.getHeight(); y++) 195 rowPointers[y] = (deUint8*)src.getDataPtr() + y*src.getRowPitch(); 196 197 png_write_image(pngPtr, &rowPointers[0]); 198 png_write_end(pngPtr, NULL); 199 200 png_destroy_write_struct(&pngPtr, &infoPtr); 201 fclose(fp); 202 } 203 } 204 205 enum PkmImageFormat 206 { 207 ETC1_RGB_NO_MIPMAPS = 0, 208 ETC1_RGBA_NO_MIPMAPS = 1, 209 ETC1_RGB_MIPMAPS = 2, 210 ETC1_RGBA_MIPMAPS = 3 211 }; 212 213 static inline deUint16 readBigEndianShort (tcu::Resource* resource) 214 { 215 deUint16 val; 216 resource->read((deUint8*)&val, sizeof(val)); 217 return (deUint16)(((val >> 8) & 0xFF) | ((val << 8) & 0xFF00)); 218 } 219 220 /*--------------------------------------------------------------------*//*! 221 * \brief Load compressed image data from PKM file 222 * 223 * \note Only ETC1_RGB8_NO_MIPMAPS format is supported 224 * \param dst Destination pixel container 225 * \param archive Resource archive 226 * \param fileName Resource file name 227 *//*--------------------------------------------------------------------*/ 228 void loadPKM (CompressedTexture& dst, const tcu::Archive& archive, const char* fileName) 229 { 230 de::UniquePtr<Resource> resource(archive.getResource(fileName)); 231 232 // Check magic and version. 233 deUint8 refMagic[] = {'P', 'K', 'M', ' ', '1', '0'}; 234 deUint8 magic[6]; 235 resource->read(magic, DE_LENGTH_OF_ARRAY(magic)); 236 237 if (memcmp(refMagic, magic, sizeof(magic)) != 0) 238 throw InternalError("Signature doesn't match PKM signature", resource->getName().c_str(), __FILE__, __LINE__); 239 240 deUint16 type = readBigEndianShort(resource.get()); 241 if (type != ETC1_RGB_NO_MIPMAPS) 242 throw InternalError("Unsupported PKM type", resource->getName().c_str(), __FILE__, __LINE__); 243 244 deUint16 width = readBigEndianShort(resource.get()); 245 deUint16 height = readBigEndianShort(resource.get()); 246 deUint16 activeWidth = readBigEndianShort(resource.get()); 247 deUint16 activeHeight = readBigEndianShort(resource.get()); 248 249 DE_UNREF(width && height); 250 251 dst.setStorage(COMPRESSEDTEXFORMAT_ETC1_RGB8, (int)activeWidth, (int)activeHeight); 252 resource->read((deUint8*)dst.getData(), dst.getDataSize()); 253 } 254 255 } // ImageIO 256 } // tcu 257