Joshua Moerman
12 years ago
5 changed files with 433 additions and 5 deletions
@ -0,0 +1,109 @@ |
|||||
|
//
|
||||
|
// 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) |
||||
|
|
||||
|
It should be trivially writable with memcpy (TODO: check exact requirement) |
||||
|
|
||||
|
Furthermore it should have a static constexpr size_t num_colors indicating the |
||||
|
number of colors and a static constexpr size_t bit_per_color indicating the |
||||
|
number of bits per color (TODO: what to do with encodings like 565 ?) |
||||
|
*/ |
||||
|
|
||||
|
namespace pixel_formats { |
||||
|
inline uint8_t clamp(int n){ |
||||
|
return std::min(255, std::max(0, n)); |
||||
|
} |
||||
|
|
||||
|
struct Gray { |
||||
|
static constexpr size_t num_colors = 1; |
||||
|
static constexpr 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 constexpr size_t num_colors = 3; |
||||
|
static constexpr 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 constexpr size_t num_colors = 3; |
||||
|
static constexpr 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 <typename PixelType> |
||||
|
struct traits { |
||||
|
static constexpr size_t num_colors = PixelType::num_colors; |
||||
|
static constexpr size_t bits_per_color = PixelType::bits_per_color; |
||||
|
static constexpr size_t bits_per_pixel = num_colors * bits_per_color; |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,150 @@ |
|||||
|
//
|
||||
|
// bmp.hpp
|
||||
|
// ImageStreams
|
||||
|
//
|
||||
|
// Created by Joshua Moerman on 9/7/12.
|
||||
|
// Copyright (c) 2012 Vadovas. All rights reserved.
|
||||
|
//
|
||||
|
|
||||
|
#ifndef ImageStreams_bmp_hpp |
||||
|
#define ImageStreams_bmp_hpp |
||||
|
|
||||
|
#include <ostream> |
||||
|
#include <fstream> |
||||
|
#include <stdexcept> |
||||
|
#include <algorithm> |
||||
|
#include "basics.hpp" |
||||
|
|
||||
|
namespace bmp { |
||||
|
// file header
|
||||
|
struct bitmap_file_header { |
||||
|
uint32_t filesize; // the size of the BMP file in bytes
|
||||
|
uint16_t creator1; // reserved
|
||||
|
uint16_t creator2; // reserved
|
||||
|
uint32_t bmp_offset; // starting offset of image data
|
||||
|
|
||||
|
template <typename DIBT, typename CT> |
||||
|
bitmap_file_header(DIBT dib_header, CT color_table) |
||||
|
: filesize(dib_header.bytes() + dib_header.header_sz + color_table.size() + 12 + 2) |
||||
|
, creator1(0) |
||||
|
, creator2(0) |
||||
|
, bmp_offset(dib_header.header_sz + color_table.size() + 12 + 2) {} |
||||
|
|
||||
|
void write(std::ostream& out) const { |
||||
|
out << "BM"; |
||||
|
out.write(reinterpret_cast<const char*>(this), 12); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// the very basic bitmapcoreheader, no alpha supported
|
||||
|
struct bitmapcoreheader { |
||||
|
uint32_t header_sz; |
||||
|
uint16_t width; |
||||
|
uint16_t height; |
||||
|
uint16_t nplanes; |
||||
|
uint16_t bitspp; |
||||
|
|
||||
|
bitmapcoreheader(uint16_t width, uint16_t height, uint16_t bitspp) |
||||
|
: header_sz(sizeof(bitmapcoreheader)) |
||||
|
, width(width) |
||||
|
, height(height) |
||||
|
, nplanes(1) |
||||
|
, bitspp(bitspp) {} |
||||
|
|
||||
|
void write(std::ostream& out) const { |
||||
|
out.write(reinterpret_cast<const char*>(this), header_sz); |
||||
|
} |
||||
|
|
||||
|
uint32_t bytes() const { |
||||
|
return width*height*bitspp/8; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// empty color table
|
||||
|
struct no_color_table { |
||||
|
uint32_t size() const { |
||||
|
return 0; |
||||
|
}; |
||||
|
|
||||
|
void write(std::ostream& out) const {} |
||||
|
}; |
||||
|
|
||||
|
// gray color table without alpha channel (eg for bitmapcoreheader)
|
||||
|
template <size_t N> |
||||
|
struct gray_color_table { |
||||
|
uint32_t size() const { |
||||
|
// 2^N entries of 3 bytes
|
||||
|
return (2 << (N-1)) * 3; |
||||
|
} |
||||
|
|
||||
|
void write(std::ostream& out) const { |
||||
|
for(unsigned int i = 0; i < (2 << (N-1)); ++i){ |
||||
|
const char v = i; |
||||
|
// more or less b g r.
|
||||
|
const char arr[3] = {v, v, v}; |
||||
|
out.write(arr, 3); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// when bpp > 8 there should be no color table, else there should be
|
||||
|
template <typename P> |
||||
|
struct default_color_table : public std::conditional<pixel_formats::traits<P>::bits_per_pixel <= 8, gray_color_table<pixel_formats::traits<P>::bits_per_pixel>, no_color_table>::type |
||||
|
{}; |
||||
|
|
||||
|
template <typename P = pixel_formats::BGR, typename CT = default_color_table<P>> |
||||
|
struct bitmap_stream { |
||||
|
typedef P pixel; |
||||
|
typedef CT color_table; |
||||
|
|
||||
|
bitmap_stream() = delete; |
||||
|
bitmap_stream(bitmap_stream const &) = delete; |
||||
|
bitmap_stream & operator=(bitmap_stream const &) = delete; |
||||
|
|
||||
|
// TODO: make this template on class level?
|
||||
|
// You can't use them in a ctor without template argument deduction...
|
||||
|
template <typename DIBT = bitmapcoreheader> |
||||
|
bitmap_stream(uint16_t width, uint16_t height, std::string filename) |
||||
|
: file(filename.c_str()) |
||||
|
, width(width) |
||||
|
, height(height) |
||||
|
, x(0) |
||||
|
, y(0) |
||||
|
{ |
||||
|
if(!file) throw std::runtime_error("bitmap file could not be opened."); |
||||
|
|
||||
|
DIBT dib_header(width, height, pixel_formats::traits<P>::bits_per_pixel); |
||||
|
color_table ct; |
||||
|
bitmap_file_header header(dib_header, ct); |
||||
|
|
||||
|
header.write(file); |
||||
|
dib_header.write(file); |
||||
|
ct.write(file); |
||||
|
} |
||||
|
|
||||
|
bitmap_stream& operator<<(pixel const & p){ |
||||
|
if (y >= height) throw std::out_of_range("Writing BMP image out of bounds."); |
||||
|
|
||||
|
file.write((char const *)&p, sizeof(pixel)); |
||||
|
++x; |
||||
|
if (x >= width){ |
||||
|
x = 0; |
||||
|
++y; |
||||
|
} |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
// TODO: destructor with checking / padding?
|
||||
|
|
||||
|
private: |
||||
|
std::ofstream file; |
||||
|
|
||||
|
uint16_t width; |
||||
|
uint16_t height; |
||||
|
|
||||
|
uint16_t x; |
||||
|
uint16_t y; |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,92 @@ |
|||||
|
//
|
||||
|
// 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 <stdio.h> |
||||
|
#include <stdexcept> |
||||
|
#include <vector> |
||||
|
#include <png.h> |
||||
|
#include "basics.hpp" |
||||
|
|
||||
|
namespace png{ |
||||
|
template <typename P> |
||||
|
int color_type(){ |
||||
|
int num_colors = pixel_formats::traits<P>::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 <typename P = pixel_formats::RGB> |
||||
|
struct png_stream{ |
||||
|
typedef P pixel; |
||||
|
|
||||
|
png_stream() = delete; |
||||
|
png_stream(png_stream const &) = delete; |
||||
|
png_stream & operator=(png_stream const &) = delete; |
||||
|
|
||||
|
png_stream(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<pixel>::bits_per_color, color_type<pixel>(), 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); |
||||
|
} |
||||
|
|
||||
|
~png_stream(){ |
||||
|
// 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); |
||||
|
} |
||||
|
|
||||
|
png_stream& operator<<(pixel const & p){ |
||||
|
row[x] = p; |
||||
|
++x; |
||||
|
if(x >= row.size()){ |
||||
|
png_write_row(png_ptr, reinterpret_cast<unsigned char const*>(row.data())); |
||||
|
x = 0; |
||||
|
} |
||||
|
|
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
FILE* fp; |
||||
|
png_structp png_ptr; |
||||
|
png_infop info_ptr; |
||||
|
|
||||
|
std::vector<pixel> row; |
||||
|
uint32_t x; |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
#endif |
Reference in new issue