diff --git a/ImageStreams.xcodeproj/project.pbxproj b/ImageStreams.xcodeproj/project.pbxproj index 9d9df10..89c7323 100644 --- a/ImageStreams.xcodeproj/project.pbxproj +++ b/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 = ""; }; 4263431115FA676F00977AF9 /* ImageStreams.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ImageStreams.1; sourceTree = ""; }; + 4263431815FA679600977AF9 /* bmp.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = bmp.hpp; sourceTree = ""; }; + 4263431915FA67D500977AF9 /* png.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = png.hpp; sourceTree = ""; }; + 4263431A15FA686300977AF9 /* basics.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = basics.hpp; sourceTree = ""; }; + 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 = ""; @@ -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; diff --git a/ImageStreams/basics.hpp b/ImageStreams/basics.hpp new file mode 100644 index 0000000..c3de42f --- /dev/null +++ b/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 + 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 diff --git a/ImageStreams/bmp.hpp b/ImageStreams/bmp.hpp new file mode 100644 index 0000000..eea68e8 --- /dev/null +++ b/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 +#include +#include +#include +#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 + 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(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(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 + 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 + struct default_color_table : public std::conditional::bits_per_pixel <= 8, gray_color_table::bits_per_pixel>, no_color_table>::type + {}; + + template > + 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 + 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

::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 diff --git a/ImageStreams/main.cpp b/ImageStreams/main.cpp index b9540d5..a4b7f1b 100644 --- a/ImageStreams/main.cpp +++ b/ImageStreams/main.cpp @@ -8,11 +8,74 @@ #include -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 +#include +#include + +template +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 +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 +void test3(std::string filename){ + size_t size = 256; + std::vector arr(size*size); + std::generate_n(arr.begin(), size*size, &nice_rand); + std::vector 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>("test.png"); + test>("test.bmp"); + + test2>("test_gray.png"); + test2>("test_gray.bmp"); + + test3>("test_3.png"); + test3>("test_3.bmp"); } diff --git a/ImageStreams/png.hpp b/ImageStreams/png.hpp new file mode 100644 index 0000000..b1ff23a --- /dev/null +++ b/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 +#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 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::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); + } + + ~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(row.data())); + x = 0; + } + + return *this; + } + + private: + FILE* fp; + png_structp png_ptr; + png_infop info_ptr; + + std::vector row; + uint32_t x; + }; +} + +#endif