Home | History | Annotate | Download | only in common
      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