Made the design more consistent, made it work and did already some documentation
This commit is contained in:
parent
faf9cf1aa1
commit
7de30322a9
5 changed files with 433 additions and 5 deletions
|
@ -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
Normal file
109
ImageStreams/basics.hpp
Normal file
|
@ -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
Normal file
150
ImageStreams/bmp.hpp
Normal file
|
@ -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
|
|
@ -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
Normal file
92
ImageStreams/png.hpp
Normal file
|
@ -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 a new issue