Skip to content

Instantly share code, notes, and snippets.

@julienr
Last active August 29, 2015 14:27
Show Gist options
  • Save julienr/01fea8c04ceddea65119 to your computer and use it in GitHub Desktop.
Save julienr/01fea8c04ceddea65119 to your computer and use it in GitHub Desktop.
libpng-android loading from assets

This is a code dump (i.e: non-compiling example) from one project that shows how to use libpng on Android to load a PNG from an asset.

The FromAssetPNGFile function returns an ImageData struct that contains the image pixels and can be used to create an OpenGL texture or whatever else suits you.

#include "android_png.h"
#include <android/asset_manager.h>
#include <png.h>
struct PNGImageData : public ImageData {
PNGImageData(png_byte* pixels, int width, int height) {
this->img_width = width;
this->img_height = height;
this->pixels = (uint8_t*)pixels;
}
virtual ~PNGImageData() {
delete[] pixels;
}
};
static AAsset* asset;
void png_asset_read(png_structp png_ptr, png_bytep data, png_size_t length) {
CHECK_GT(AAsset_read(asset, data, length), 0) << "Reading past end-of-file";
}
ImageData* FromAssetPNGFile(AAssetManager* mgr, const string& fname) {
asset = AAssetManager_open(mgr, fname.c_str(), AASSET_MODE_STREAMING);
if (!asset) {
LOG(ERROR) << "Error opening " << fname;
return NULL;
}
//header for testing if it is a png
png_byte header[8];
//read the header
AAsset_read(asset, header, 8);
//test if png
int is_png = !png_sig_cmp(header, 0, 8);
if (!is_png) {
AAsset_close(asset);
LOG(ERROR) << "Not a png file : " << fname;
return NULL;
}
//create png struct
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
NULL, NULL);
if (!png_ptr) {
AAsset_close(asset);
LOG(ERROR) << "Unable to create png struct : " << fname;
return NULL;
}
//create png info struct
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
LOG(ERROR) << "Unable to create png info : " << fname;
AAsset_close(asset);
return NULL;
}
//create png end info struct
png_infop end_info = png_create_info_struct(png_ptr);
if (!end_info) {
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
LOG(ERROR) << "Unable to create png end info : " << fname;
AAsset_close(asset);
return NULL;
}
//png error stuff, not sure libpng man suggests this.
if (setjmp(png_jmpbuf(png_ptr))) {
AAsset_close(asset);
LOG(ERROR) << "Error during setjmp : " << fname;
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
return NULL;
}
//init png reading
//png_init_io(png_ptr, fp);
png_set_read_fn(png_ptr, NULL, png_asset_read);
//let libpng know you already read the first 8 bytes
png_set_sig_bytes(png_ptr, 8);
// read all the info up to the image data
png_read_info(png_ptr, info_ptr);
//variables to pass to get info
int bit_depth, color_type;
png_uint_32 width, height;
// get info about png
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
NULL, NULL, NULL);
if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) {
LOG(ERROR) << "Non-power of two (" << width << "x" << height
<< ") texture are not supported : " << fname;
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
AAsset_close(asset);
return NULL;
}
// Update the png info struct.
png_read_update_info(png_ptr, info_ptr);
// Row size in bytes.
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
// Allocate the image_data as a big block
png_byte *image_data = new png_byte[rowbytes * height];
if (!image_data) {
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
LOG(ERROR) << "Unable to allocate image_data while loading " << fname;
AAsset_close(asset);
return NULL;
}
//row_pointers is for pointing to image_data for reading the png with libpng
png_bytep *row_pointers = new png_bytep[height];
if (!row_pointers) {
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
delete[] image_data;
LOG(ERROR) << "Unable to allocate row_pointer while loading " << fname;
AAsset_close(asset);
return NULL;
}
// set the individual row_pointers to point at the correct offsets of image_data
for (int i = 0; i < height; ++i) {
//row_pointers[height - 1 - i] = image_data + i * rowbytes;
row_pointers[i] = image_data + i * rowbytes;
}
//read the png into image_data through row_pointers
png_read_image(png_ptr, row_pointers);
ImageData* img = new PNGImageData(image_data, width, height);
//clean up memory and close stuff
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
delete[] row_pointers;
AAsset_close(asset);
return img;
}
#ifndef _ANDROID_PNG_H_
#define _ANDROID_PNG_H_
#include "common.h"
class AAssetManager;
struct ImageData {
int img_width, img_height;
uint8_t* pixels;
virtual ~ImageData();
};
inline
bool IsPowerOfTwo(int n) {
// http://www.exploringbinary.com/ten-ways-to-check-if-an-integer-is-a-power-of-two-in-c/
return (n & (n - 1)) == 0;
}
inline
int NextPowerOfTwo(int n) {
// Explanation : http://stackoverflow.com/questions/1322510/given-an-integer-how-do-i-find-the-next-largest-power-of-two-using-bit-twiddlin
if (n < 0) {
return 0;
}
n--;
n |= n >> 1; // Divide by 2^k for consecutive doublings of k up to 32,
n |= n >> 2; // and then or the results.
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n++; // The result is a number of 1 bits equal to the number
// of bits in the original number, plus 1. That's the
// next highest power of 2.
return n;
}
// Loads a PNG image from the asset directory
// Note that this REQUIRES that fname has a power-of-two size
ImageData* FromAssetPNGFile(AAssetManager* mgr, const string& fname);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment