Skip to content

Instantly share code, notes, and snippets.

@mamontov-cpp
Created April 24, 2024 07:58
Show Gist options
  • Save mamontov-cpp/41bfcf00411efa44f9c64df2cf1f9410 to your computer and use it in GitHub Desktop.
Save mamontov-cpp/41bfcf00411efa44f9c64df2cf1f9410 to your computer and use it in GitHub Desktop.
PNG Loader via libPNG
#include "imageformats/pngloader.h"
#define TAR7Z_SADDY
#include "3rdparty/tar7z/include/tar.h"
#define STBI_ONLY_PNG
#define STB_IMAGE_IMPLEMENTATION
#include "3rdparty/stb/stb_image.h"
#include "png.h"
bool sad::imageformats::PNGLoader::load(FILE * file, sad::Texture * texture)
{
if (file == nullptr || texture == nullptr)
return false;
int width, height;
png_byte color_type;
png_byte bit_depth;
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (!png) return false;
png_infop info = png_create_info_struct(png);
if (!info) return false;
if (setjmp(png_jmpbuf(png))) return false;
png_init_io(png, file);
png_read_info(png, info);
width = png_get_image_width(png, info);
height = png_get_image_height(png, info);
color_type = png_get_color_type(png, info);
bit_depth = png_get_bit_depth(png, info);
if (height == 0)
{
return false;
}
// Read any color_type into 8bit depth, RGBA format.
// See http://www.libpng.org/pub/png/libpng-manual.txt
if (bit_depth == 16)
png_set_strip_16(png);
if (color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
// PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth.
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png);
if (png_get_valid(png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png);
// These color_type don't have an alpha channel then fill it with 0xff.
if (color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_PALETTE)
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
png_read_update_info(png, info);
std::vector<size_t> pointer_offsets;
pointer_offsets.reserve(height);
unsigned int current_offset = 0;
for (int y = 0; y < height; y++) {
pointer_offsets.push_back(current_offset);
current_offset += png_get_rowbytes(png, info);
}
const unsigned int total_bytes = current_offset;
unsigned char* data = new unsigned char[total_bytes];
std::vector<unsigned char*> row_pointers;
row_pointers.reserve(pointer_offsets.size());
for (size_t pointer_offset : pointer_offsets)
{
row_pointers.push_back(data + pointer_offset);
}
png_read_image(png, &(row_pointers[0]));
png_destroy_read_struct(&png, &info, nullptr);
texture->width() = static_cast<unsigned int>(width);
texture->height() = static_cast<unsigned int>(height);
texture->bpp() = 32;
texture->Format = sad::Texture::InternalFormat::SFT_R8_G8_B8_A8;
delete texture->Buffer;
texture->Buffer = new sad::Texture::PointerBuffer(data);
return true;
}
bool sad::imageformats::PNGLoader::load(tar7z::Entry* entry, sad::Texture* texture)
{
if (entry == nullptr || texture == nullptr)
return false;
int components = 4;
int width = 0, height = 0;
unsigned char* data = stbi_load_from_memory(reinterpret_cast<unsigned char*>(entry->contents()), entry->Size, &width, &height, &components, 4);
bool ok = data != nullptr;
if (ok)
{
texture->width() = static_cast<unsigned int>(width);
texture->height() = static_cast<unsigned int>(height);
texture->bpp() = 32;
texture->Format = sad::Texture::InternalFormat::SFT_R8_G8_B8_A8;
delete texture->Buffer;
texture->Buffer = new sad::Texture::PointerBuffer(data);
}
return ok;
}
sad::imageformats::PNGLoader::~PNGLoader()
{
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment