summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJose Fonseca <jfonseca@vmware.com>2016-05-05 11:09:52 +0100
committerJose Fonseca <jfonseca@vmware.com>2016-05-05 11:09:52 +0100
commitbceafecaa6a3e606dfc31c0016cecb9e82d5cab8 (patch)
tree7dbe09eb4846dc76a1ac7c4dbf976fd30fa04da2 /lib
parentbe6cf883b322b52371f7a45bea4d0339ed2fc29d (diff)
image: Move into lib.
Diffstat (limited to 'lib')
-rw-r--r--lib/CMakeLists.txt1
-rw-r--r--lib/image/CMakeLists.txt18
-rw-r--r--lib/image/README.markdown2
-rw-r--r--lib/image/image.hpp156
-rw-r--r--lib/image/image_bmp.cpp142
-rw-r--r--lib/image/image_md5.cpp64
-rw-r--r--lib/image/image_png.cpp277
-rw-r--r--lib/image/image_pnm.cpp319
-rw-r--r--lib/image/image_raw.cpp66
9 files changed, 1045 insertions, 0 deletions
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 3ce90a6e..813e180a 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -1,2 +1,3 @@
add_subdirectory (guids)
add_subdirectory (highlight)
+add_subdirectory (image)
diff --git a/lib/image/CMakeLists.txt b/lib/image/CMakeLists.txt
new file mode 100644
index 00000000..1cc3c4ce
--- /dev/null
+++ b/lib/image/CMakeLists.txt
@@ -0,0 +1,18 @@
+include_directories (
+ ${PNG_INCLUDE_DIR}
+ ${MD5_INCLUDE_DIR}
+)
+
+add_library (image STATIC
+ image.hpp
+ image_bmp.cpp
+ image_png.cpp
+ image_pnm.cpp
+ image_raw.cpp
+ image_md5.cpp
+)
+
+target_link_libraries (image
+ ${PNG_LIBRARIES}
+ ${MD5_LIBRARIES}
+)
diff --git a/lib/image/README.markdown b/lib/image/README.markdown
new file mode 100644
index 00000000..b2909713
--- /dev/null
+++ b/lib/image/README.markdown
@@ -0,0 +1,2 @@
+This directory contains a class to represent and manipulate images, in memory
+or disk.
diff --git a/lib/image/image.hpp b/lib/image/image.hpp
new file mode 100644
index 00000000..e6f0d226
--- /dev/null
+++ b/lib/image/image.hpp
@@ -0,0 +1,156 @@
+/**************************************************************************
+ *
+ * Copyright 2008-2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+/*
+ * Image I/O.
+ */
+
+#pragma once
+
+
+#include <iostream>
+
+#include <string>
+
+
+namespace image {
+
+
+enum ChannelType {
+ TYPE_UNORM8 = 0,
+ TYPE_FLOAT
+};
+
+
+class Image {
+public:
+ unsigned width;
+ unsigned height;
+ unsigned channels;
+ ChannelType channelType;
+ unsigned bytesPerChannel;
+ unsigned bytesPerPixel;
+
+ // Flipped vertically or not
+ bool flipped;
+
+ // Pixels in RGBA format
+ unsigned char *pixels;
+
+ std::string label;
+
+ inline Image(unsigned w, unsigned h, unsigned c = 4, bool f = false, ChannelType t = TYPE_UNORM8) :
+ width(w),
+ height(h),
+ channels(c),
+ channelType(t),
+ bytesPerChannel(t == TYPE_FLOAT ? 4 : 1),
+ bytesPerPixel(channels * bytesPerChannel),
+ flipped(f),
+ pixels(new unsigned char[h*w*bytesPerPixel])
+ {}
+
+ inline ~Image() {
+ delete [] pixels;
+ }
+
+ // Absolute stride
+ inline unsigned
+ _stride() const {
+ return width*bytesPerPixel;
+ }
+
+ inline unsigned char *start(void) {
+ return flipped ? pixels + (height - 1)*_stride() : pixels;
+ }
+
+ inline const unsigned char *start(void) const {
+ return flipped ? pixels + (height - 1)*_stride() : pixels;
+ }
+
+ inline unsigned char *end(void) {
+ return flipped ? pixels - _stride() : pixels + height*_stride();
+ }
+
+ inline const unsigned char *end(void) const {
+ return flipped ? pixels - _stride() : pixels + height*_stride();
+ }
+
+ inline signed stride(void) const {
+ return flipped ? -(signed)_stride() : _stride();
+ }
+
+ bool
+ writeBMP(const char *filename) const;
+
+ void
+ writePNM(std::ostream &os, const char *comment = NULL) const;
+
+ bool
+ writePNM(const char *filename, const char *comment = NULL) const;
+
+ void
+ writeMD5(std::ostream &os) const;
+
+ bool
+ writePNG(std::ostream &os, bool strip_alpha = false) const;
+
+ bool
+ writePNG(const char *filename, bool strip_alpha = false) const;
+
+ void
+ writeRAW(std::ostream &os) const;
+
+ bool
+ writeRAW(const char *filename) const;
+};
+
+
+Image *
+readPNG(std::istream &is);
+
+Image *
+readPNG(const char *filename);
+
+
+struct PNMInfo
+{
+ unsigned width;
+ unsigned height;
+ unsigned channels;
+ ChannelType channelType;
+ int commentNumber;
+};
+
+const char *
+readPNMHeader(const char *buffer, size_t size, PNMInfo &info);
+
+Image *
+readPNM(const char *buffer, size_t bufferSize);
+
+
+} /* namespace image */
+
+
diff --git a/lib/image/image_bmp.cpp b/lib/image/image_bmp.cpp
new file mode 100644
index 00000000..d349be06
--- /dev/null
+++ b/lib/image/image_bmp.cpp
@@ -0,0 +1,142 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * Copyright 2008-2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <fstream>
+
+#include "image.hpp"
+
+
+namespace image {
+
+
+#pragma pack(push,2)
+struct FileHeader {
+ uint16_t bfType;
+ uint32_t bfSize;
+ uint16_t bfReserved1;
+ uint16_t bfReserved2;
+ uint32_t bfOffBits;
+};
+#pragma pack(pop)
+
+struct InfoHeader {
+ uint32_t biSize;
+ int32_t biWidth;
+ int32_t biHeight;
+ uint16_t biPlanes;
+ uint16_t biBitCount;
+ uint32_t biCompression;
+ uint32_t biSizeImage;
+ int32_t biXPelsPerMeter;
+ int32_t biYPelsPerMeter;
+ uint32_t biClrUsed;
+ uint32_t biClrImportant;
+};
+
+struct Pixel {
+ uint8_t rgbBlue;
+ uint8_t rgbGreen;
+ uint8_t rgbRed;
+ uint8_t rgbAlpha;
+};
+
+
+bool
+Image::writeBMP(const char *filename) const {
+ assert(channels == 4);
+ assert(channelType == TYPE_UNORM8);
+
+ struct FileHeader bmfh;
+ struct InfoHeader bmih;
+ unsigned x, y;
+
+ bmfh.bfType = 0x4d42;
+ bmfh.bfSize = 14 + 40 + height*width*4;
+ bmfh.bfReserved1 = 0;
+ bmfh.bfReserved2 = 0;
+ bmfh.bfOffBits = 14 + 40;
+
+ bmih.biSize = 40;
+ bmih.biWidth = width;
+ bmih.biHeight = height;
+ bmih.biPlanes = 1;
+ bmih.biBitCount = 32;
+ bmih.biCompression = 0;
+ bmih.biSizeImage = height*width*4;
+ bmih.biXPelsPerMeter = 0;
+ bmih.biYPelsPerMeter = 0;
+ bmih.biClrUsed = 0;
+ bmih.biClrImportant = 0;
+
+ std::ofstream stream(filename, std::ofstream::binary);
+
+ if (!stream) {
+ return false;
+ }
+
+ stream.write((const char *)&bmfh, 14);
+ stream.write((const char *)&bmih, 40);
+
+ unsigned stride = width*4;
+
+ if (flipped) {
+ for (y = 0; y < height; ++y) {
+ const unsigned char *ptr = pixels + y * stride;
+ for (x = 0; x < width; ++x) {
+ struct Pixel pixel;
+ pixel.rgbRed = ptr[x*4 + 0];
+ pixel.rgbGreen = ptr[x*4 + 1];
+ pixel.rgbBlue = ptr[x*4 + 2];
+ pixel.rgbAlpha = ptr[x*4 + 3];
+ stream.write((const char *)&pixel, 4);
+ }
+ }
+ } else {
+ y = height;
+ while (y--) {
+ const unsigned char *ptr = pixels + y * stride;
+ for (x = 0; x < width; ++x) {
+ struct Pixel pixel;
+ pixel.rgbRed = ptr[x*4 + 0];
+ pixel.rgbGreen = ptr[x*4 + 1];
+ pixel.rgbBlue = ptr[x*4 + 2];
+ pixel.rgbAlpha = ptr[x*4 + 3];
+ stream.write((const char *)&pixel, 4);
+ }
+ }
+ }
+
+ stream.close();
+
+ return true;
+}
+
+
+} /* namespace image */
diff --git a/lib/image/image_md5.cpp b/lib/image/image_md5.cpp
new file mode 100644
index 00000000..85141e18
--- /dev/null
+++ b/lib/image/image_md5.cpp
@@ -0,0 +1,64 @@
+/**************************************************************************
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reversed.
+ * Author: Meng Mengmeng <mengmeng.meng@intel.com>
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <fstream>
+#include "image.hpp"
+
+#include "md5.h"
+
+
+using namespace std;
+namespace image {
+
+
+void
+Image::writeMD5(std::ostream &os) const {
+ struct MD5Context md5c;
+ MD5Init(&md5c);
+ const unsigned char *row;
+ unsigned len = width*bytesPerPixel;
+ for (row = start(); row != end(); row += stride()) {
+ MD5Update(&md5c, (unsigned char *)row, len);
+ }
+ unsigned char signature[16];
+ MD5Final(signature, &md5c);
+
+ const char hex[] = "0123456789ABCDEF";
+ char csig[33];
+ for(int i = 0; i < sizeof signature; i++){
+ csig[2*i ] = hex[signature[i] >> 4];
+ csig[2*i + 1] = hex[signature[i] & 0xf];
+ }
+ csig[32] = '\0';
+
+ os << csig;
+ os << "\n";
+}
+
+
+} /* namespace image */
+
diff --git a/lib/image/image_png.cpp b/lib/image/image_png.cpp
new file mode 100644
index 00000000..72b9f9a8
--- /dev/null
+++ b/lib/image/image_png.cpp
@@ -0,0 +1,277 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * Copyright 2008-2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <zlib.h>
+#include <png.h>
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <fstream>
+
+#include "image.hpp"
+
+
+namespace image {
+
+
+static const int png_compression_level = Z_BEST_SPEED;
+
+
+static void
+pngWriteCallback(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ std::ostream *os = (std::ostream *) png_get_io_ptr(png_ptr);
+ os->write((const char *)data, length);
+}
+
+
+static inline uint8_t
+floatToUnorm8(float c)
+{
+ if (c <= 0.0f) {
+ return 0;
+ }
+ if (c >= 1.0f) {
+ return 255;
+ }
+ return c * 255.0f + 0.5f;
+}
+
+
+static inline uint8_t
+floatToSRGB(float c)
+{
+ if (c <= 0.0f) {
+ return 0;
+ }
+ if (c >= 1.0f) {
+ return 255;
+ }
+ if (c <= 0.0031308f) {
+ c *= 12.92f;
+ } else {
+ c = 1.055f * powf(c, 1.0f/2.4f) - 0.055f;
+ }
+ return c * 255.0f + 0.5f;
+}
+
+
+bool
+Image::writePNG(std::ostream &os, bool strip_alpha) const
+{
+ png_structp png_ptr;
+ png_infop info_ptr;
+ int color_type;
+
+ switch (channels) {
+ case 4:
+ color_type = strip_alpha ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+ break;
+ case 3:
+ color_type = PNG_COLOR_TYPE_RGB;
+ break;
+ case 2:
+ color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+ break;
+ case 1:
+ color_type = PNG_COLOR_TYPE_GRAY;
+ break;
+ default:
+ assert(0);
+ goto no_png;
+ }
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ goto no_png;
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, NULL);
+ goto no_png;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ goto no_png;
+ }
+
+ png_set_write_fn(png_ptr, &os, pngWriteCallback, NULL);
+
+ png_set_IHDR(png_ptr, info_ptr, width, height, 8,
+ color_type, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ png_set_compression_level(png_ptr, png_compression_level);
+
+ png_write_info(png_ptr, info_ptr);
+
+ if (channels == 4 && strip_alpha) {
+ png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
+ }
+
+ switch (channelType) {
+ case TYPE_UNORM8:
+ for (const unsigned char *row = start(); row != end(); row += stride()) {
+ png_write_rows(png_ptr, (png_bytepp) &row, 1);
+ }
+ break;
+ case TYPE_FLOAT:
+ png_bytep rowUnorm8 = new png_byte[width * channels];
+ for (const unsigned char *row = start(); row != end(); row += stride()) {
+ const float *rowFloat = (const float *)row;
+ for (unsigned x = 0, i = 0; x < width; ++x) {
+ for (unsigned channel = 0; channel < channels; ++channel, ++i) {
+ float c = rowFloat[i];
+ bool srgb = channels >= 3 && channel < 3;
+ rowUnorm8[i] = srgb ? floatToSRGB(c) : floatToUnorm8(c);
+ }
+ }
+ png_write_rows(png_ptr, (png_bytepp) &rowUnorm8, 1);
+ }
+ delete [] rowUnorm8;
+ break;
+ }
+
+ png_write_end(png_ptr, info_ptr);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+
+ return true;
+
+no_png:
+ return false;
+}
+
+
+bool
+Image::writePNG(const char *filename, bool strip_alpha) const
+{
+ std::ofstream os(filename, std::ofstream::binary);
+ if (!os) {
+ return false;
+ }
+ return writePNG(os, strip_alpha);
+}
+
+
+static void
+pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+ std::istream *os = (std::istream *) png_get_io_ptr(png_ptr);
+ os->read((char *)data, length);
+}
+
+
+Image *
+readPNG(std::istream &is)
+{
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_infop end_info;
+ unsigned channels;
+ Image *image;
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ goto no_png;
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ goto no_png;
+ }
+
+ end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ goto no_png;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ goto no_png;
+ }
+
+ png_set_read_fn(png_ptr, &is, pngReadCallback);
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type, compression_type, filter_method;
+
+ png_get_IHDR(png_ptr, info_ptr,
+ &width, &height,
+ &bit_depth, &color_type, &interlace_type,
+ &compression_type, &filter_method);
+
+ /* Convert to RGBA8 */
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb(png_ptr);
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(png_ptr);
+ if (bit_depth == 16)
+ png_set_strip_16(png_ptr);
+
+ channels = png_get_channels(png_ptr, info_ptr);
+ image = new Image(width, height, channels);
+ if (!image)
+ goto no_image;
+
+ assert(png_get_rowbytes(png_ptr, info_ptr) == width*channels);
+ for (unsigned y = 0; y < height; ++y) {
+ png_bytep row = (png_bytep)(image->pixels + y*width*channels);
+ png_read_row(png_ptr, row, NULL);
+ }
+
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+
+ return image;
+
+no_image:
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+no_png:
+ return NULL;
+}
+
+Image *
+readPNG(const char *filename)
+{
+ std::ifstream is(filename, std::ifstream::binary);
+ if (!is) {
+ return NULL;
+ }
+ return readPNG(filename);
+}
+
+
+} /* namespace image */
diff --git a/lib/image/image_pnm.cpp b/lib/image/image_pnm.cpp
new file mode 100644
index 00000000..14c1e558
--- /dev/null
+++ b/lib/image/image_pnm.cpp
@@ -0,0 +1,319 @@
+/**************************************************************************
+ *
+ * Copyright 2011 Jose Fonseca
+ * Copyright 2008-2010 VMware, Inc.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <algorithm>
+
+#include <fstream>
+
+#include "image.hpp"
+
+
+namespace image {
+
+/**
+ * http://en.wikipedia.org/wiki/Netpbm_format
+ * http://netpbm.sourceforge.net/doc/ppm.html
+ * http://netpbm.sourceforge.net/doc/pfm.html
+ */
+void
+Image::writePNM(std::ostream &os, const char *comment) const
+{
+ const char *identifier;
+ unsigned outChannels;
+
+ switch (channelType) {
+ case TYPE_UNORM8:
+ if (channels == 1) {
+ identifier = "P5";
+ outChannels = 1;
+ } else {
+ identifier = "P6";
+ outChannels = 3;
+ }
+ break;
+ case TYPE_FLOAT:
+ if (channels == 1) {
+ identifier = "Pf";
+ outChannels = 1;
+ } else if (channels <= 3) {
+ identifier = "PF";
+ outChannels = 3;
+ } else {
+ // Non-standard extension for 4 floats
+ identifier = "PX";
+ outChannels = 4;
+ }
+ break;
+ default:
+ assert(0);
+ return;
+ }
+
+ os << identifier << "\n";
+
+ if (comment) {
+ os << "#" << comment << "\n";
+ }
+ os << width << " " << height << "\n";
+
+ if (channelType == TYPE_UNORM8) {
+ os << "255" << "\n";
+ } else {
+ os << "1" << "\n";
+ }
+
+ const unsigned char *row;
+
+ if (channels == outChannels) {
+ /*
+ * Write whole pixel spans straight from the image buffer.
+ */
+
+ for (row = start(); row != end(); row += stride()) {
+ os.write((const char *)row, width*bytesPerPixel);
+ }
+ } else {
+ /*
+ * Need to add/remove channels, one pixel at a time.
+ */
+
+ unsigned char *tmp = new unsigned char[width*outChannels*bytesPerChannel];
+
+ if (channelType == TYPE_UNORM8) {
+ /*
+ * Optimized path for 8bit unorms.
+ */
+
+ if (channels == 4) {
+ for (row = start(); row != end(); row += stride()) {
+ const uint32_t *src = (const uint32_t *)row;
+ uint32_t *dst = (uint32_t *)tmp;
+ unsigned x;
+ for (x = 0; x + 4 <= width; x += 4) {
+ /*
+ * It's much faster to access dwords than bytes.
+ *
+ * FIXME: Big-endian version.
+ */
+
+ uint32_t rgba0 = *src++ & 0xffffff;
+ uint32_t rgba1 = *src++ & 0xffffff;
+ uint32_t rgba2 = *src++ & 0xffffff;
+ uint32_t rgba3 = *src++ & 0xffffff;
+ uint32_t rgb0 = rgba0
+ | (rgba1 << 24);
+ uint32_t rgb1 = (rgba1 >> 8)
+ | (rgba2 << 16);
+ uint32_t rgb2 = (rgba2 >> 16)
+ | (rgba3 << 8);
+ *dst++ = rgb0;
+ *dst++ = rgb1;
+ *dst++ = rgb2;
+ }
+ for (; x < width; ++x) {
+ tmp[x*3 + 0] = row[x*4 + 0];
+ tmp[x*3 + 1] = row[x*4 + 1];
+ tmp[x*3 + 2] = row[x*4 + 2];
+ }
+ os.write((const char *)tmp, width*3);
+ }
+ } else if (channels == 2) {
+ for (row = start(); row != end(); row += stride()) {
+ const unsigned char *src = row;
+ unsigned char *dst = tmp;
+ for (unsigned x = 0; x < width; ++x) {
+ *dst++ = *src++;
+ *dst++ = *src++;
+ *dst++ = 0;
+ }
+ os.write((const char *)tmp, width*3);
+ }
+ } else {
+ assert(0);
+ }
+ } else {
+ /*
+ * General path for float images.
+ */
+
+ unsigned copyChannels = std::min(channels, outChannels);
+
+ assert(channelType == TYPE_FLOAT);
+
+ for (row = start(); row != end(); row += stride()) {
+ const float *src = (const float *)row;
+ float *dst = (float *)tmp;
+ for (unsigned x = 0; x < width; ++x) {
+ unsigned channel = 0;
+ for (; channel < copyChannels; ++channel) {
+ *dst++ = *src++;
+ }
+ for (; channel < outChannels; ++channel) {
+ *dst++ = 0;
+ }
+ }
+ os.write((const char *)tmp, width*outChannels*bytesPerChannel);
+ }
+ }
+
+ delete [] tmp;
+ }
+}
+
+
+bool
+Image::writePNM(const char *filename, const char *comment) const
+{
+ std::ofstream os(filename, std::ofstream::binary);
+ if (!os) {
+ return false;
+ }
+ writePNM(os, comment);
+ return true;
+}
+
+
+/**
+ * Parse PNM header.
+ *
+ * Returns pointer to data start, or NULL on failure.
+ */
+const char *
+readPNMHeader(const char *buffer, size_t bufferSize, PNMInfo &info)
+{
+ info.channels = 0;
+ info.width = 0;
+ info.height = 0;
+ info.commentNumber = -1;
+
+ const char *currentBuffer = buffer;
+ const char *nextBuffer;
+
+ // parse number of channels
+ char c;
+ int scannedChannels = sscanf(currentBuffer, "P%c\n", &c);
+ if (scannedChannels != 1) { // validate scanning of channels
+ // invalid channel line
+ return NULL;
+ }
+ // convert channel token to number of channels
+ switch (c) {
+ case '5':
+ info.channels = 1;
+ info.channelType = TYPE_UNORM8;
+ break;
+ case '6':
+ info.channels = 3;
+ info.channelType = TYPE_UNORM8;
+ break;
+ case 'f':
+ info.channels = 1;
+ info.channelType = TYPE_FLOAT;
+ break;
+ case 'F':
+ info.channels = 3;
+ info.channelType = TYPE_FLOAT;
+ break;
+ case 'X':
+ info.channels = 4;
+ info.channelType = TYPE_FLOAT;
+ break;
+ default:
+ return NULL;
+ }
+
+ // advance past channel line
+ nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+ bufferSize -= nextBuffer - currentBuffer;
+ currentBuffer = nextBuffer;
+
+ // skip over optional comment
+ if (*currentBuffer == '#') {
+ // advance past '#'
+ currentBuffer += 1;
+ bufferSize += 1;
+
+ // actually try to read a number
+ sscanf(currentBuffer, "%d", &info.commentNumber);
+
+ // advance past comment line
+ nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+ bufferSize -= nextBuffer - currentBuffer;
+ currentBuffer = nextBuffer;
+ }
+
+ // parse dimensions of image
+ int scannedDimensions = sscanf(currentBuffer, "%u %u\n", &info.width, &info.height);
+ if (scannedDimensions != 2) { // validate scanning of dimensions
+ // invalid dimension line
+ return NULL;
+ }
+
+ // advance past dimension line
+ nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+ bufferSize -= nextBuffer - currentBuffer;
+ currentBuffer = nextBuffer;
+
+ // skip scale factor / endianness line
+ nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+
+ // return start of image data
+ return nextBuffer;
+}
+
+
+Image *
+readPNM(const char *buffer, size_t bufferSize)
+{
+ PNMInfo info;
+
+ const char *headerEnd = readPNMHeader(buffer, bufferSize, info);
+
+ // if invalid PNM header was encountered, ...
+ if (headerEnd == NULL) {
+ std::cerr << "error: invalid PNM header";
+ return NULL;
+ }
+
+ Image *image = new Image(info.width, info.height, info.channels, false, info.channelType);
+
+ size_t rowBytes = info.width * image->bytesPerPixel;
+ for (unsigned char *row = image->start(); row != image->end(); row += image->stride()) {
+ memcpy(row, headerEnd, rowBytes);
+ headerEnd += rowBytes;
+ }
+
+ return image;
+}
+
+
+
+} /* namespace image */
diff --git a/lib/image/image_raw.cpp b/lib/image/image_raw.cpp
new file mode 100644
index 00000000..4d8d363f
--- /dev/null
+++ b/lib/image/image_raw.cpp
@@ -0,0 +1,66 @@
+/**************************************************************************
+ *
+ * Copyright (C) 2013 Intel Corporation. All rights reversed.
+ * Author: Shuang He <shuang.he@intel.com>
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ **************************************************************************/
+
+
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <fstream>
+
+#include "image.hpp"
+
+
+namespace image {
+
+
+void
+Image::writeRAW(std::ostream &os) const
+{
+ assert(channelType == TYPE_UNORM8);
+
+ const unsigned char *row;
+
+ for (row = start(); row != end(); row += stride()) {
+ os.write((const char *)row, width*bytesPerPixel);
+ }
+}
+
+
+bool
+Image::writeRAW(const char *filename) const
+{
+ std::ofstream os(filename, std::ofstream::binary);
+ if (!os) {
+ return false;
+ }
+ writeRAW(os);
+ return true;
+}
+
+
+} /* namespace image */