From f206ad4c77f3b4adb3e8afb7d239a699f51b93ee Mon Sep 17 00:00:00 2001 From: Joshua Moerman Date: Wed, 6 Nov 2013 22:21:01 +0100 Subject: [PATCH] Adds png support --- include/basics.hpp | 115 +++++++++++++++++++++++++++ include/png.hpp | 191 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 306 insertions(+) create mode 100644 include/basics.hpp create mode 100644 include/png.hpp diff --git a/include/basics.hpp b/include/basics.hpp new file mode 100644 index 0000000..dbca2ba --- /dev/null +++ b/include/basics.hpp @@ -0,0 +1,115 @@ +// +// basics.hpp +// ImageStreams +// +// Created by Joshua Moerman on 9/7/12. +// Copyright (c) 2012 Vadovas. All rights reserved. +// + +#ifndef ImageStreams_basics_hpp +#define ImageStreams_basics_hpp + +/* + A PixelFormat p should have the following constructors (depending on number + of colors): + p(int r, int g, int b, int a) + p(double r, double g, double b, double a) + + p(int v) + p(double v) + + The order in the ctor is rgb even though the internal structure may be + different + + It should be trivially copyable (for memcpy). + + Furthermore it should have a static const size_t num_colors indicating the + number of colors and a static const size_t bit_per_color indicating the + number of bits per color (TODO: what to do with encodings like 565 ?) + */ + +#include +#include + +namespace pixel_formats { + inline uint8_t clamp(int n){ + return std::min(255, std::max(0, n)); + } + + struct gray { + static const size_t num_colors = 1; + static const size_t bits_per_color = 8; + + gray() : value(0) {} + gray(double intensity) : value(clamp(255*intensity)) {} + gray(int intensity) : value(clamp(intensity)) {} + + private: + uint8_t value; + }; + + struct rgb { + static const size_t num_colors = 3; + static const size_t bits_per_color = 8; + + rgb() + : red(0) + , green(0) + , blue(0) + {} + + rgb(double red, double green, double blue) + : red(clamp(255*red)) + , green(clamp(255*green)) + , blue(clamp(255*blue)) + {} + + rgb(int red, int green, int blue) + : red(clamp(red)) + , green(clamp(green)) + , blue(clamp(blue)) + {} + + private: + uint8_t red; + uint8_t green; + uint8_t blue; + }; + + struct bgr{ + static const size_t num_colors = 3; + static const size_t bits_per_color = 8; + + bgr() + : blue(0) + , green(0) + , red(0) + {} + + bgr(double red, double green, double blue) + : blue(clamp(255*blue)) + , green(clamp(255*green)) + , red(clamp(255*red)) + {} + + bgr(int red, int green, int blue) + : blue(clamp(blue)) + , green(clamp(green)) + , red(clamp(red)) + {} + + private: + uint8_t blue; + uint8_t green; + uint8_t red; + }; + + template + struct traits { + static const size_t num_colors = PixelType::num_colors; + static const size_t bits_per_color = PixelType::bits_per_color; + static const size_t bits_per_pixel = num_colors * bits_per_color; + }; +} + +#endif diff --git a/include/png.hpp b/include/png.hpp new file mode 100644 index 0000000..d7e60c9 --- /dev/null +++ b/include/png.hpp @@ -0,0 +1,191 @@ +// +// png.hpp +// ImageStreams +// +// Created by Joshua Moerman on 9/7/12. +// Copyright (c) 2012 Vadovas. All rights reserved. +// + +#ifndef ImageStreams_png_hpp +#define ImageStreams_png_hpp + +#include +#include +#include +#include +#include "basics.hpp" + +namespace png{ + template + int color_type(){ + int num_colors = pixel_formats::traits

::num_colors; + switch (num_colors) { + case 1: return PNG_COLOR_TYPE_GRAY; + case 2: return PNG_COLOR_TYPE_GRAY_ALPHA; + case 3: return PNG_COLOR_TYPE_RGB; + case 4: return PNG_COLOR_TYPE_RGB_ALPHA; + default: return PNG_COLOR_TYPE_RGB; + } + } + + template + struct ostream{ + typedef P pixel; + + ostream() = delete; + ostream(ostream const &) = delete; + ostream & operator=(ostream const &) = delete; + + ostream(uint32_t width, uint32_t height, std::string filename) + : fp(0) + , png_ptr(0) + , info_ptr(0) + , row(width) + , x(0) + { + fp = fopen(filename.c_str(), "wb"); + if(!fp) throw std::runtime_error("Could not open file"); + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if(!png_ptr) throw std::runtime_error("PNG structure could not be allocated"); + + info_ptr = png_create_info_struct(png_ptr); + if(!info_ptr) throw std::runtime_error("PNG information structure could not be allocated"); + + png_init_io(png_ptr, fp); + + png_set_IHDR(png_ptr, info_ptr, width, height, pixel_formats::traits::bits_per_color, color_type(), PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + png_set_compression_level(png_ptr, 9); + + png_write_info(png_ptr, info_ptr); + } + + ~ostream(){ + // NOTE: the pnglib already checks for us whether all pixels are written + png_write_end(png_ptr, info_ptr); + png_destroy_info_struct(png_ptr, &info_ptr); + fclose(fp); + } + + ostream& operator<<(pixel const & p){ + row[x] = p; + ++x; + if(x >= row.size()){ + png_write_row(png_ptr, reinterpret_cast(row.data())); + x = 0; + } + + return *this; + } + + private: + FILE* fp; + png_structp png_ptr; + png_infop info_ptr; + + std::vector row; + uint32_t x; + }; + + typedef ostream<> colored_ostream; + typedef ostream gray_ostream; + + struct istream{ + //typedef pixel pixel; + + istream(std::string filename) + : fp(0) + , png_ptr(0) + , info_ptr(0) + , valid(true) + , row() + , stride(0) + , x(0) + , y(0) + , height(0) + { + fp = fopen(filename.c_str(), "rb"); + if(!fp) throw std::runtime_error(filename + " could not be opened"); + + png_byte header[8]; + fread(header, 1, 8, fp); + + if(png_sig_cmp(header, 0, 8) != 0) throw std::runtime_error("File is not a PNG file."); + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if(!png_ptr) throw std::runtime_error("PNG structure could not be allocated"); + + info_ptr = png_create_info_struct(png_ptr); + if(!info_ptr) throw std::runtime_error("PNG information structure could not be allocated"); + + png_init_io(png_ptr, fp); + + png_set_sig_bytes(png_ptr, 8); + + png_read_info(png_ptr, info_ptr); + + if(png_get_interlace_type(png_ptr, info_ptr) != PNG_INTERLACE_NONE) + throw std::runtime_error("Interalced PNG's are not supported"); + + height = png_get_image_height(png_ptr, info_ptr); + auto width = png_get_image_width(png_ptr, info_ptr); + auto bit_depth = png_get_bit_depth(png_ptr, info_ptr); + auto channels = png_get_channels(png_ptr, info_ptr); + + // number of bytes in one row + stride = bit_depth * channels / 8; + size_t row_size = width * stride; + + if(bit_depth < 8) throw std::runtime_error("Bitdepths lower than 8 are not supported (yet)"); + + row.resize(row_size); + png_read_row(png_ptr, (png_bytep)row.data(), 0); + } + + ~istream(){ + png_read_end(png_ptr, 0); + png_destroy_read_struct(&png_ptr, &info_ptr, 0); + fclose(fp); + } + + template + istream& operator>>(Pixel & p){ + if( y >= height){ + valid = false; + return *this; + } + p = row[x]; + + if(++x >= get_width()){ + x = 0; + ++y; + if(y < height) + png_read_row(png_ptr, (png_bytep)row.data(), 0); + } + + return *this; + } + + operator bool() const { + return valid; + } + + uint32_t get_width() const { return row.size(); } + uint32_t get_height() const { return height; } + + private: + FILE* fp; + png_structp png_ptr; + png_infop info_ptr; + bool valid; + + std::vector row; + uint32_t stride; + uint32_t x; + uint32_t y; + uint32_t height; + }; +} + +#endif