Skip to content

Instantly share code, notes, and snippets.

@vegardsjo
Created December 30, 2013 00:09
Show Gist options
  • Select an option

  • Save vegardsjo/8176262 to your computer and use it in GitHub Desktop.

Select an option

Save vegardsjo/8176262 to your computer and use it in GitHub Desktop.
D readpng wrapper
import std.stdio;
import std.stdint;
import std.string;
import std.conv;
import core.memory;
extern (C) Png.png_image_t read_png(ubyte* in_data, size_t in_size, void* function(uint32_t) alloc);
class Png
{
struct png_image_t
{
ubyte* data;
size_t size;
uint32_t width, height;
int32_t bit_depth, color_type;
int32_t components;
int32_t alpha;
byte error;
char* error_message;
}
private png_image_t image;
@property ubyte* data() { return image.data; }
@property size_t size() { return image.size; }
@property uint32_t width() { return image.width; }
@property uint32_t height() { return image.height; }
@property int32_t bitDepth() { return image.bit_depth; }
@property int32_t colorType() { return image.color_type; }
@property int32_t alpha() { return image.alpha; }
private extern (C) void* alloc(uint32_t size) {
//writefln("Trying to alloc %s bytes", size);
return GC.malloc(size);
}
static Png readData(ubyte[] data)
{
auto self = new Png;
self.image = read_png(data.ptr, data.length, &alloc);
if (self.image.error)
throw new Exception("Could not read image: %s".format(self.image.error_message.to!(string)));
return self;
}
static Png readFile(string filename)
{
auto fp = File(filename, "rb");
fp.seek(0, SEEK_END);
size_t size = fp.tell();
fp.seek(0);
auto buffer = new ubyte[size];
buffer = fp.rawRead(buffer);
return readData(buffer);
}
private this() {};
}
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#include <stdint.h>
#include <stdbool.h>
#include "readpng.h"
typedef struct user_read_struct
{
unsigned char *in_data;
size_t size;
size_t pos;
} user_read_struct_t;
void read_fn(png_structp png_ptr, png_bytep out_data, png_size_t length)
{
user_read_struct_t *read_struct = (user_read_struct_t*)png_get_io_ptr(png_ptr);
if (read_struct->pos + length > read_struct->size) {
png_error(png_ptr, "trying to read past end of image data");
return;
}
//printf("Reading %d bytes from data\n", length);
memcpy(out_data, read_struct->in_data + read_struct->pos, length);
read_struct->pos += length;
}
//png_image read_png(unsigned char *in_data, size_t size, unsigned char *out_data, size_t out_size)
png_image_t read_png(unsigned char *in_data, size_t size, void *(*alloc)(uint32_t))
{
png_image_t image;
printf("COLOR_TYPE_RGBA: %d\n", PNG_COLOR_TYPE_RGBA);
if (size < 8 || png_sig_cmp(in_data, 0, 8)) {
image.error_message = "file is not a png image";
goto read_png_error;
}
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
image.error_message = "could not allocate png struct.";
goto read_png_error;
}
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
image.error_message = "could not allocate png info struct.";
png_destroy_read_struct(&png_ptr, NULL, NULL);
goto read_png_error;
}
png_infop end_info_ptr = png_create_info_struct(png_ptr);
if (!end_info_ptr) {
image.error_message = "could not allocate png end info struct.";
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
goto read_png_error;
}
// Error handling
if (setjmp(png_jmpbuf(png_ptr))) {
image.error_message = "error reading image."; // TODO: get detailed error from png
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr);
goto read_png_error;
}
// Initialize io
//png_init_io(png_ptr, fp);
user_read_struct_t user_read_struct = { in_data, size, 0 };
png_set_read_fn(png_ptr, &user_read_struct, read_fn);
//png_set_sig_bytes(png_ptr, 8); // Necessary when not reading from file?
// Callbacks
//png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, read_chunk_callback);
//png_set_read_status_fn(png_ptr, read_row_callback);
// Unusued chunks
png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER, NULL, 0); // ignore all custom chunks
//png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER, "TODO", 1);
// Custom chunks
//png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_IF_SAFE, "CAKE", 1);
// Set sane image resolution limits
png_uint_32 width_max = 4096, height_max = 4096; // TODO
png_set_user_limits(png_ptr, width_max, height_max);
// Read image header
png_read_info(png_ptr, info_ptr);
png_get_IHDR(png_ptr, info_ptr, &image.width, &image.height, &image.bit_depth, &image.color_type, NULL, NULL, NULL);
// Do input transformations
// Convert paletted image to rgb
if (image.color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png_ptr);
// Convert sub 8bit grayscale to 8bit
else if (image.color_type == PNG_COLOR_TYPE_GRAY && image.bit_depth < 8)
png_set_expand_gray_1_2_4_to_8(png_ptr);
// TODO: document
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png_ptr);
// Set the alpha channel to mean level of transparency instead of opacity
//png_set_invert_alpha(png_ptr);
// Unpack packed pixels
if (image.bit_depth < 8)
png_set_packing(png_ptr);
// Convert 16 bit pixels to 8
else if (image.bit_depth == 16)
png_set_strip_16(png_ptr);
// Convert grayscale to rgb
if (image.color_type == PNG_COLOR_TYPE_GRAY || image.color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png_ptr);
// Convert RGB to RGBA
png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
image.color_type = PNG_COLOR_TYPE_RGBA;
// TODO: something to with interlacing; document
//int number_of_passes = png_set_interlace_handling(png_ptr);
// Update the info struct
png_read_update_info(png_ptr, info_ptr);
// Will probably be useful later
image.alpha = image.color_type == PNG_COLOR_TYPE_RGBA;
image.components = image.alpha ? 4 : 3;
// Allocate space for image
int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
image.size = sizeof(png_byte) * rowbytes * image.height;
image.data = (png_bytep)alloc(image.size);
// Setup row pointer and read image
//png_bytepp image_rowp = png_get_rows(png_ptr, info_ptr);
png_bytepp image_rowp = malloc(sizeof(png_bytep) * image.height);
for (png_uint_32 i = 0; i < image.height; i++)
//image_rowp[image.height - i - 1] = image.data + rowbytes * i;
image_rowp[i] = image.data + rowbytes * i;
png_read_image(png_ptr, image_rowp);
printf("[read_png] width: %u, height: %u, bit_depth: %d, components: %d, alpha: %s\n", image.width, image.height, image.bit_depth, image.components, image.alpha ? "true" : "false");
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr);
free(image_rowp);
read_png_ok:
image.error = false;
return image;
read_png_error:
image.error = true;
return image;
}
#if 0
void read_row_callback(png_structp png_ptr, png_uint_32 row, int pass)
{
}
#endif
#ifndef READPNG_H
#define READPNG_H
#include <stdlib.h>
#include <stdint.h>
#include <png.h>
typedef struct png_image
{
png_bytep data;
size_t size;
png_uint_32 width, height;
png_int_32 bit_depth, color_type;
png_int_32 components;
png_int_32 alpha;
int8_t error;
char* error_message;
} png_image_t;
png_image_t read_png(unsigned char *in_data, size_t size, void *(*alloc)(uint32_t));
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment