/* 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 . */ #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 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 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(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; }