257 lines
8.1 KiB
C++
257 lines
8.1 KiB
C++
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program Tester Core
|
|
* ----------------------------------------
|
|
*
|
|
* Copyright 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief Image IO.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "tcuImageIO.hpp"
|
|
#include "tcuResource.hpp"
|
|
#include "tcuSurface.hpp"
|
|
#include "tcuCompressedTexture.hpp"
|
|
#include "deFilePath.hpp"
|
|
#include "deUniquePtr.hpp"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <cstdio>
|
|
|
|
#include "png.h"
|
|
|
|
namespace tcu
|
|
{
|
|
namespace ImageIO
|
|
{
|
|
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
/*--------------------------------------------------------------------*//*!
|
|
* \brief Load image from resource
|
|
*
|
|
* TextureLevel storage is set to match image data. Only PNG format is
|
|
* currently supported.
|
|
*
|
|
* \param dst Destination pixel container
|
|
* \param archive Resource archive
|
|
* \param fileName Resource file name
|
|
*//*--------------------------------------------------------------------*/
|
|
void loadImage (TextureLevel& dst, const tcu::Archive& archive, const char* fileName)
|
|
{
|
|
string ext = de::FilePath(fileName).getFileExtension();
|
|
|
|
if (ext == "png" || ext == "PNG")
|
|
loadPNG(dst, archive, fileName);
|
|
else
|
|
throw InternalError("Unrecognized image file extension", fileName, __FILE__, __LINE__);
|
|
}
|
|
|
|
DE_BEGIN_EXTERN_C
|
|
static void pngReadResource (png_structp png_ptr, png_bytep data, png_size_t length)
|
|
{
|
|
tcu::Resource* resource = (tcu::Resource*)png_get_io_ptr(png_ptr);
|
|
resource->read(data, (int)length);
|
|
}
|
|
DE_END_EXTERN_C
|
|
|
|
/*--------------------------------------------------------------------*//*!
|
|
* \brief Load PNG image from resource
|
|
*
|
|
* TextureLevel storage is set to match image data.
|
|
*
|
|
* \param dst Destination pixel container
|
|
* \param archive Resource archive
|
|
* \param fileName Resource file name
|
|
*//*--------------------------------------------------------------------*/
|
|
void loadPNG (TextureLevel& dst, const tcu::Archive& archive, const char* fileName)
|
|
{
|
|
de::UniquePtr<Resource> resource(archive.getResource(fileName));
|
|
|
|
// Verify header.
|
|
deUint8 header[8];
|
|
resource->read(header, sizeof(header));
|
|
TCU_CHECK(png_sig_cmp((png_bytep)&header[0], 0, DE_LENGTH_OF_ARRAY(header)) == 0);
|
|
|
|
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, DE_NULL, DE_NULL, DE_NULL);
|
|
TCU_CHECK(png_ptr);
|
|
|
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
|
TCU_CHECK(info_ptr);
|
|
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
|
throw InternalError("An error occured when loading PNG", fileName, __FILE__, __LINE__);
|
|
|
|
png_set_read_fn(png_ptr, resource.get(), pngReadResource);
|
|
png_set_sig_bytes(png_ptr, 8);
|
|
|
|
png_read_info(png_ptr, info_ptr);
|
|
|
|
const deUint32 width = (deUint32)png_get_image_width(png_ptr, info_ptr);
|
|
const deUint32 height = (deUint32)png_get_image_height(png_ptr, info_ptr);
|
|
TextureFormat textureFormat;
|
|
|
|
{
|
|
const png_byte colorType = png_get_color_type(png_ptr, info_ptr);
|
|
const png_byte bitDepth = png_get_bit_depth(png_ptr, info_ptr);
|
|
|
|
if (colorType == PNG_COLOR_TYPE_RGB && bitDepth == 8)
|
|
textureFormat = TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8);
|
|
else if (colorType == PNG_COLOR_TYPE_RGBA && bitDepth == 8)
|
|
textureFormat = TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8);
|
|
else
|
|
throw InternalError("Unsupported PNG depth or color type", fileName, __FILE__, __LINE__);
|
|
}
|
|
|
|
// Resize destination texture.
|
|
dst.setStorage(textureFormat, width, height);
|
|
|
|
std::vector<png_bytep> row_pointers;
|
|
row_pointers.resize(height);
|
|
for (deUint32 y = 0; y < height; y++)
|
|
row_pointers[y] = (deUint8*)dst.getAccess().getDataPtr() + y*dst.getAccess().getRowPitch();
|
|
|
|
png_read_image(png_ptr, &row_pointers[0]);
|
|
|
|
png_destroy_info_struct(png_ptr, &info_ptr);
|
|
png_destroy_read_struct(&png_ptr, DE_NULL, DE_NULL);
|
|
}
|
|
|
|
static int textureFormatToPNGFormat (const TextureFormat& format)
|
|
{
|
|
if (format == TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8))
|
|
return PNG_COLOR_TYPE_RGB;
|
|
else if (format == TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
|
|
return PNG_COLOR_TYPE_RGBA;
|
|
else
|
|
throw InternalError("Unsupported texture format", DE_NULL, __FILE__, __LINE__);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*//*!
|
|
* \brief Write image to file in PNG format
|
|
*
|
|
* This is provided for debugging and development purposes. Test code must
|
|
* not write to any files except the test log by default.
|
|
*
|
|
* \note Only RGB/RGBA, UNORM_INT8 formats are supported
|
|
* \param src Source pixel data
|
|
* \param fileName File name
|
|
*//*--------------------------------------------------------------------*/
|
|
void savePNG (const ConstPixelBufferAccess& src, const char* fileName)
|
|
{
|
|
FILE* fp = fopen(fileName, "wb");
|
|
TCU_CHECK(fp);
|
|
|
|
png_structp pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
|
|
if (!pngPtr)
|
|
{
|
|
fclose(fp);
|
|
TCU_CHECK(pngPtr);
|
|
}
|
|
|
|
png_infop infoPtr = png_create_info_struct(pngPtr);
|
|
if (!infoPtr)
|
|
{
|
|
png_destroy_write_struct(&pngPtr, NULL);
|
|
TCU_CHECK(infoPtr);
|
|
}
|
|
|
|
if (setjmp(png_jmpbuf(pngPtr)))
|
|
{
|
|
png_destroy_write_struct(&pngPtr, &infoPtr);
|
|
fclose(fp);
|
|
throw tcu::InternalError("PNG compression failed");
|
|
}
|
|
else
|
|
{
|
|
int pngFormat = textureFormatToPNGFormat(src.getFormat());
|
|
|
|
png_init_io(pngPtr, fp);
|
|
|
|
// Header
|
|
png_set_IHDR(pngPtr, infoPtr, src.getWidth(), src.getHeight(), 8,
|
|
pngFormat, PNG_INTERLACE_NONE,
|
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
|
png_write_info(pngPtr, infoPtr);
|
|
|
|
std::vector<png_bytep> rowPointers(src.getHeight());
|
|
for (int y = 0; y < src.getHeight(); y++)
|
|
rowPointers[y] = (deUint8*)src.getDataPtr() + y*src.getRowPitch();
|
|
|
|
png_write_image(pngPtr, &rowPointers[0]);
|
|
png_write_end(pngPtr, NULL);
|
|
|
|
png_destroy_write_struct(&pngPtr, &infoPtr);
|
|
fclose(fp);
|
|
}
|
|
}
|
|
|
|
enum PkmImageFormat
|
|
{
|
|
ETC1_RGB_NO_MIPMAPS = 0,
|
|
ETC1_RGBA_NO_MIPMAPS = 1,
|
|
ETC1_RGB_MIPMAPS = 2,
|
|
ETC1_RGBA_MIPMAPS = 3
|
|
};
|
|
|
|
static inline deUint16 readBigEndianShort (tcu::Resource* resource)
|
|
{
|
|
deUint16 val;
|
|
resource->read((deUint8*)&val, sizeof(val));
|
|
return (deUint16)(((val >> 8) & 0xFF) | ((val << 8) & 0xFF00));
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*//*!
|
|
* \brief Load compressed image data from PKM file
|
|
*
|
|
* \note Only ETC1_RGB8_NO_MIPMAPS format is supported
|
|
* \param dst Destination pixel container
|
|
* \param archive Resource archive
|
|
* \param fileName Resource file name
|
|
*//*--------------------------------------------------------------------*/
|
|
void loadPKM (CompressedTexture& dst, const tcu::Archive& archive, const char* fileName)
|
|
{
|
|
de::UniquePtr<Resource> resource(archive.getResource(fileName));
|
|
|
|
// Check magic and version.
|
|
deUint8 refMagic[] = {'P', 'K', 'M', ' ', '1', '0'};
|
|
deUint8 magic[6];
|
|
resource->read(magic, DE_LENGTH_OF_ARRAY(magic));
|
|
|
|
if (memcmp(refMagic, magic, sizeof(magic)) != 0)
|
|
throw InternalError("Signature doesn't match PKM signature", resource->getName().c_str(), __FILE__, __LINE__);
|
|
|
|
deUint16 type = readBigEndianShort(resource.get());
|
|
if (type != ETC1_RGB_NO_MIPMAPS)
|
|
throw InternalError("Unsupported PKM type", resource->getName().c_str(), __FILE__, __LINE__);
|
|
|
|
deUint16 width = readBigEndianShort(resource.get());
|
|
deUint16 height = readBigEndianShort(resource.get());
|
|
deUint16 activeWidth = readBigEndianShort(resource.get());
|
|
deUint16 activeHeight = readBigEndianShort(resource.get());
|
|
|
|
DE_UNREF(width && height);
|
|
|
|
dst.setStorage(COMPRESSEDTEXFORMAT_ETC1_RGB8, (int)activeWidth, (int)activeHeight);
|
|
resource->read((deUint8*)dst.getData(), dst.getDataSize());
|
|
}
|
|
|
|
} // ImageIO
|
|
} // tcu
|