--- /dev/null
+/*
+ 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