Browse Source

Made the design more consistent, made it work and did already some documentation

master
Joshua Moerman 12 years ago
parent
commit
7de30322a9
  1. 14
      ImageStreams.xcodeproj/project.pbxproj
  2. 109
      ImageStreams/basics.hpp
  3. 150
      ImageStreams/bmp.hpp
  4. 73
      ImageStreams/main.cpp
  5. 92
      ImageStreams/png.hpp

14
ImageStreams.xcodeproj/project.pbxproj

@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
4263431015FA676F00977AF9 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4263430F15FA676F00977AF9 /* main.cpp */; };
4263431215FA676F00977AF9 /* ImageStreams.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4263431115FA676F00977AF9 /* ImageStreams.1 */; };
4263431C15FA6A3200977AF9 /* libpng.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4263431B15FA6A3200977AF9 /* libpng.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@ -28,6 +29,10 @@
4263430B15FA676F00977AF9 /* ImageStreams */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ImageStreams; sourceTree = BUILT_PRODUCTS_DIR; };
4263430F15FA676F00977AF9 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
4263431115FA676F00977AF9 /* ImageStreams.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ImageStreams.1; sourceTree = "<group>"; };
4263431815FA679600977AF9 /* bmp.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = bmp.hpp; sourceTree = "<group>"; };
4263431915FA67D500977AF9 /* png.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = png.hpp; sourceTree = "<group>"; };
4263431A15FA686300977AF9 /* basics.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = basics.hpp; sourceTree = "<group>"; };
4263431B15FA6A3200977AF9 /* libpng.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = libpng.framework; path = Library/Frameworks/libpng.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -35,6 +40,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4263431C15FA6A3200977AF9 /* libpng.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -44,6 +50,7 @@
4263430015FA676F00977AF9 = {
isa = PBXGroup;
children = (
4263431B15FA6A3200977AF9 /* libpng.framework */,
4263430E15FA676F00977AF9 /* ImageStreams */,
4263430C15FA676F00977AF9 /* Products */,
);
@ -62,6 +69,9 @@
children = (
4263430F15FA676F00977AF9 /* main.cpp */,
4263431115FA676F00977AF9 /* ImageStreams.1 */,
4263431A15FA686300977AF9 /* basics.hpp */,
4263431815FA679600977AF9 /* bmp.hpp */,
4263431915FA67D500977AF9 /* png.hpp */,
);
path = ImageStreams;
sourceTree = "<group>";
@ -129,6 +139,8 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
@ -155,6 +167,8 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;

109
ImageStreams/basics.hpp

@ -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

150
ImageStreams/bmp.hpp

@ -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

73
ImageStreams/main.cpp

@ -8,11 +8,74 @@
#include <iostream>
int main(int argc, const char * argv[])
{
#include "png.hpp"
#include "bmp.hpp"
#include "basics.hpp"
// insert code here...
std::cout << "Hello, World!\n";
return 0;
#include <cmath>
#include <vector>
#include <algorithm>
template <typename ImageType>
void test(std::string filename){
size_t size = 512;
ImageType image(size, size, filename);
for(int y = 0; y < size; ++y){
for(int x = 0; x < size; ++x){
image << typename ImageType::pixel(x ^ y, (2 * x) ^ y, x ^ (2 * y));
}
}
}
template <typename ImageType>
void test2(std::string filename){
size_t size = 256;
ImageType image(size, size, filename);
for(int y = 0; y < size; ++y){
for(int x = 0; x < size; ++x){
if(x < size/2)
image << typename ImageType::pixel(x ^ y);
else
image << typename ImageType::pixel(y);
}
}
}
// because lambdas dont work yet...
inline double nice_rand(){
return rand() / double(RAND_MAX);
}
template <typename ImageType>
void test3(std::string filename){
size_t size = 256;
std::vector<double> arr(size*size);
std::generate_n(arr.begin(), size*size, &nice_rand);
std::vector<double> arr2(size*size, 0);
for(int i = 0; i < size*size; ++i){
for (int j = i - 5; j <= i + 5; ++j) {
if(j < 0 || j >= size*size) continue;
arr2[i] += arr[j] * 0.1;
}
}
ImageType image(size, size, filename);
for(auto x : arr2)
image << typename ImageType::pixel(x);
}
int main(int argc, const char * argv[]){
test<png::png_stream<>>("test.png");
test<bmp::bitmap_stream<>>("test.bmp");
test2<png::png_stream<pixel_formats::Gray>>("test_gray.png");
test2<bmp::bitmap_stream<pixel_formats::Gray>>("test_gray.bmp");
test3<png::png_stream<pixel_formats::Gray>>("test_3.png");
test3<bmp::bitmap_stream<pixel_formats::Gray>>("test_3.bmp");
}

92
ImageStreams/png.hpp

@ -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