Merge "Fix GLES2 format mismatch" into marshmallow-cts-dev
[platform/upstream/VK-GL-CTS.git] / framework / common / tcuImageIO.cpp
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