project64/Source/Project64/UserInterface/Debugger/ScriptAPI/N64Image_PNG.cpp

202 lines
5.7 KiB
C++

#include <stdafx.h>
#include <3rdParty/png/png.h>
#include "N64Image.h"
#pragma warning (disable:4611) // disable setjmp/c++ deconstruction warning
struct PNGReadState {
uint8_t* pngData;
size_t pngSize;
png_size_t offset;
};
static void PNGReadCallback(png_structp png_ptr, png_bytep data, png_size_t length);
static void PNGWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length);
static void PNGFlushCallback(png_structp png_ptr);
static bool ParsePNGRow(png_byte* row, png_size_t rowSize, int bitDepth, int colorType, std::vector<uint8_t>& outRGBA32);
int CN64Image::ReadPNG(uint8_t* pngData, size_t pngSize, size_t* outWidth, size_t* outHeight, std::vector<uint8_t>& outRGBA32)
{
if (!png_check_sig(pngData, 8))
{
return N64IMG_PNG_HEADER_MISSING;
}
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!png_ptr)
{
return N64IMG_PNG_OUT_OF_MEMORY;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
return N64IMG_PNG_OUT_OF_MEMORY;
}
if (setjmp(png_jmpbuf(png_ptr)))
{
g_Notify->BreakPoint(__FILE__, __LINE__);
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
return N64IMG_PNG_EXCEPTION;
}
PNGReadState readState;
readState.pngData = pngData;
readState.pngSize = pngSize;
readState.offset = 8;
png_set_read_fn(png_ptr, &readState, PNGReadCallback);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
png_uint_32 width, height;
int bitDepth, colorType;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitDepth,
&colorType, nullptr, nullptr, nullptr);
png_size_t rowSize = png_get_rowbytes(png_ptr, info_ptr);
std::vector<png_bytep> rowPointers(height);
std::vector<png_byte> imageData(height * rowSize);
for (size_t nRow = 0; nRow < height; nRow++)
{
rowPointers[nRow] = &imageData[nRow * rowSize];
}
png_read_image(png_ptr, &rowPointers[0]);
for (size_t nRow = 0; nRow < height; nRow++)
{
if (!ParsePNGRow(rowPointers[nRow], rowSize, bitDepth, colorType, outRGBA32))
{
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
return N64IMG_PNG_PARSER_FAILED;
}
}
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
*outWidth = width;
*outHeight = height;
return N64IMG_OK;
}
void CN64Image::WritePNG(uint8_t* rgba32, size_t width, size_t height, std::vector<uint8_t>& buffer)
{
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!png_ptr)
{
png_destroy_write_struct(&png_ptr, nullptr);
return;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
{
png_destroy_write_struct(&png_ptr, nullptr);
return;
}
if (setjmp(png_jmpbuf(png_ptr)))
{
png_destroy_write_struct(&png_ptr, &info_ptr);
return;
}
png_set_IHDR(png_ptr, info_ptr, width, height,
8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_set_write_fn(png_ptr, &buffer, PNGWriteCallback, PNGFlushCallback);
png_write_info(png_ptr, info_ptr);
size_t rowSize = width * 4;
std::vector<png_bytep> rowPointers(height);
for (size_t nRow = 0; nRow < height; nRow++)
{
rowPointers[nRow] = &rgba32[nRow * rowSize];
}
png_write_image(png_ptr, &rowPointers[0]);
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
}
static void PNGReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
PNGReadState* state = (PNGReadState*)png_get_io_ptr(png_ptr);
if (state->offset + length > state->pngSize)
{
return;
}
memcpy(data, &state->pngData[state->offset], length);
state->offset += length;
}
static void PNGWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
std::vector<uint8_t>* buffer = (std::vector<uint8_t>*)png_get_io_ptr(png_ptr);
buffer->insert(buffer->end(), &data[0], &data[length]);
}
static void PNGFlushCallback(png_structp /*png_ptr*/)
{
}
static bool ParsePNGRow(png_byte* row, png_size_t rowSize, int bitDepth, int colorType, std::vector<uint8_t>& outRGBA32)
{
if (colorType == PNG_COLOR_TYPE_RGBA)
{
if (bitDepth == 8)
{
outRGBA32.insert(outRGBA32.end(), &row[0], &row[rowSize]);
return true;
}
if (bitDepth == 16)
{
for (png_size_t i = 0; i < rowSize; i += 8)
{
outRGBA32.push_back(png_get_uint_16(&row[i + 0]) >> 8);
outRGBA32.push_back(png_get_uint_16(&row[i + 2]) >> 8);
outRGBA32.push_back(png_get_uint_16(&row[i + 4]) >> 8);
outRGBA32.push_back(png_get_uint_16(&row[i + 6]) >> 8);
}
return true;
}
}
if (colorType == PNG_COLOR_TYPE_RGB)
{
if (bitDepth == 8)
{
for (png_size_t i = 0; i < rowSize; i += 3)
{
outRGBA32.insert(outRGBA32.end(), &row[i], &row[i + 3]);
outRGBA32.push_back(255);
}
return true;
}
if (bitDepth == 16)
{
for (png_size_t i = 0; i < rowSize; i += 6)
{
outRGBA32.push_back(png_get_uint_16(&row[i + 0]) >> 8);
outRGBA32.push_back(png_get_uint_16(&row[i + 2]) >> 8);
outRGBA32.push_back(png_get_uint_16(&row[i + 4]) >> 8);
outRGBA32.push_back(255);
}
return true;
}
}
return false;
}