画像の簡易入出力とテストプログラムを追加
[cuda.git] / libutils / ImageIO.cpp
diff --git a/libutils/ImageIO.cpp b/libutils/ImageIO.cpp
new file mode 100644 (file)
index 0000000..309e8ea
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+       Copyright (C) 2012  fmaj7b5.info
+
+       This program is free software: you can redistribute it and/or modify
+       it under the terms of the GNU General Public License as published by
+       the Free Software Foundation, either version 2 of the License, or
+       (at your option) any later version.
+
+       This program is distributed in the hope that it will be useful,
+       but WITHOUT ANY WARRANTY; without even the implied warranty of
+       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+       GNU General Public License for more details.
+
+       You should have received a copy of the GNU General Public License
+       along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+#include "ImageIO.h"
+
+using namespace FM7b5;
+
+struct PbmHeader
+{
+       char magic[3];
+       int width;
+       int height;
+       int depth;
+
+       PbmHeader()
+               : width(0), height(0), depth(0)
+       {
+               magic[0] = magic[1] = magic[2] = '\0';
+       }
+};
+
+/* declarations for static functions */
+static bool read_header(FILE* fp, PbmHeader* header);
+static bool read_magic(FILE* fp, char magic[3]);
+static bool read_digit(FILE* fp, int* val);
+static bool read_whitespace(FILE* fp);
+static bool skip_comment(FILE* fp);
+static bool read_end_of_line(FILE* fp);
+
+
+Image<std::uint8_t>
+FM7b5::loadPGM(const std::string& filename)
+{
+       FILE* fp(nullptr);
+
+       if (fopen_s(&fp, filename.c_str(), "rb") != 0) {
+               throw std::runtime_error(__FUNCTION__ ": could not open a file.");
+       }
+
+       try {
+               PbmHeader header;
+               read_header(fp, &header);
+
+               printf("--- header ---\n");
+               printf("magic: %s\n", header.magic);
+               printf("size : %dx%d\n", header.width, header.height);
+               printf("depth: %d\n", header.depth);
+
+               /* check if the color format is supported */
+               if (header.magic[1] != '2' && header.magic[1] != '5') {
+                       throw std::runtime_error(__FUNCTION__ ": non support format.");
+               }
+               if (header.depth != 255) {
+                       throw std::runtime_error(__FUNCTION__ ": supported depth is 255 only.");
+               }
+
+               Image<std::uint8_t> img(header.width, header.height);
+
+               switch (header.magic[1]) {
+               case '2': // Gray ASCII format
+                       for (size_t h = 0; h < img.height(); ++h) {
+                               for (size_t w = 0; w < img.width(); ++w) {
+                                       while (read_whitespace(fp));
+
+                                       int val;
+                                       if (!read_digit(fp, &val)) {
+                                               throw std::runtime_error(__FUNCTION__ ": invalid data.");
+                                       }
+
+                                       img(w, h) = static_cast<std::uint8_t>(val);
+                               }
+                       }
+                       break;
+
+               case '5': // Gray binary format
+                       {
+                               const size_t size(header.width * header.height);
+                               size_t ret = fread(img.data(), sizeof(std::uint8_t), size, fp);
+                               if (ret != size) {
+                                       throw std::runtime_error(__FUNCTION__ ": insufficient data.");
+                               }
+                       }
+                       break;
+
+               default:
+                       throw std::runtime_error(__FUNCTION__ ": unexpected format was specified.");
+               }
+
+               printf("successfully read (^^\n");
+
+               return img;
+       }
+       catch (...)
+       {
+               fclose(fp);
+               throw;
+       }
+}
+
+
+bool
+read_header(FILE* fp, PbmHeader* header)
+{
+       if (!read_magic(fp, header->magic) || !read_whitespace(fp)) {
+               throw std::runtime_error(__FUNCTION__ ": not a netpbm file.");
+       }
+
+       while (read_whitespace(fp)); // skip comments and white spaces
+
+       if (!read_digit(fp, &header->width) || !read_whitespace(fp)) {
+               throw std::runtime_error(__FUNCTION__ ": invalid width.");
+       }
+
+       if (!read_digit(fp, &header->height) || !read_whitespace(fp)) {
+               throw std::runtime_error(__FUNCTION__ ": invalid height.");
+       }
+
+       if (!read_digit(fp, &header->depth)) {
+               throw std::runtime_error(__FUNCTION__ ": invalid depth.");
+       }
+
+       if (!read_whitespace(fp)) {
+               throw std::runtime_error(__FUNCTION__ ": just one white space is required right after the depth value.");
+       }
+
+       return true;
+}
+
+bool
+read_magic(FILE* fp, char magic[3])
+{
+       size_t pos(0);
+
+       magic[pos++] = fgetc(fp);
+       if (magic[0] != 'P') {
+               goto on_error;
+       }
+
+       magic[pos++] = fgetc(fp);
+       if (!isdigit(magic[1])) {
+               goto on_error;
+       }
+       magic[2] = '\0';
+
+       return true;
+
+on_error:
+       for (size_t i = 0; i < pos; ++i) {
+               ungetc(magic[i], fp);
+               magic[i] = '\0';
+       }
+       return false;
+}
+
+bool
+read_digit(FILE* fp, int* val)
+{
+       static const size_t buf_size(16);
+
+       char buf[buf_size];
+       size_t pos(0);
+       for (pos = 0; pos < buf_size; ++pos) {
+               buf[pos] = fgetc(fp);
+               if (!isdigit(buf[pos])) {
+                       break;
+               }
+       }
+       if (pos == 0) {
+               ungetc(buf[0], fp);
+               return false;
+       }
+       if (pos == buf_size) { // too long digits
+               for (size_t i = 0; i < buf_size; ++i) {
+                       ungetc(buf[i], fp);
+                       return false;
+               }
+       }
+
+       // push back the next char
+       ungetc(buf[pos], fp);
+
+       // read a number
+       buf[pos] = '\0';
+       *val = atoi(buf);
+
+       return true;
+}
+
+bool
+read_whitespace(FILE* fp)
+{
+       while (skip_comment(fp));
+
+       char ch = fgetc(fp);
+
+       if (!isspace(ch)) {
+               ungetc(ch, fp);
+               return false;
+       }
+
+       // check CR+LF
+       if (ch == '\r') {
+               ch = fgetc(fp);
+               if (ch != '\n') {
+                       ungetc(ch, fp);
+               }
+       }
+
+       return true;
+}
+
+bool
+skip_comment(FILE* fp)
+{
+       char ch = fgetc(fp);
+
+       if (ch != '#') {
+               ungetc(ch, fp);
+               return false;
+       }
+
+       // skip to eol
+       while (!read_end_of_line(fp)) {
+               ch = fgetc(fp);
+       }
+
+       return true;
+}
+
+bool
+read_end_of_line(FILE* fp)
+{
+       char ch = fgetc(fp);
+
+       if (ch == '\r') {
+               ch = fgetc(fp);
+               if (ch != '\n') {
+                       ungetc(ch, fp);
+               }
+       }
+       else if (ch != '\n') { // not an end_of_line
+               ungetc(ch, fp);
+               return false;
+       }
+
+       return true;
+}
\ No newline at end of file