Add an overload of `Image::ReadPPM` method

Make it able to load image data from a stream.
This commit is contained in:
RangerUFO 2024-10-12 21:15:05 +08:00
parent a4d6adbc43
commit a784b8459d
2 changed files with 32 additions and 21 deletions

View File

@ -55,14 +55,14 @@ float StretchToSigned(float value) {
bool IsLineBreak(int c) { return c == '\r' || c == '\n'; } bool IsLineBreak(int c) { return c == '\r' || c == '\n'; }
void SkipWhitespaceAndComments(std::ifstream& file) { void SkipWhitespaceAndComments(std::istream& in) {
int value = file.get(); int value = in.get();
while (std::isspace(value)) value = file.get(); while (std::isspace(value)) value = in.get();
while (value == '#') { // Skip comment lines. while (value == '#') { // Skip comment lines.
while (!IsLineBreak(value)) value = file.get(); while (!IsLineBreak(value)) value = in.get();
while (std::isspace(value)) value = file.get(); while (std::isspace(value)) value = in.get();
} }
file.unget(); // Rewind last byte. in.unget(); // Rewind last byte.
} }
} // namespace } // namespace
@ -72,25 +72,37 @@ bool Image::ReadPPM(const std::string& filename) {
std::cerr << "Failed to open " << filename << "\n"; std::cerr << "Failed to open " << filename << "\n";
return false; return false;
} }
if (!ReadPPM(file)) {
return false;
}
if (file.get() != EOF) {
std::cerr << "Extra data in file\n";
return false;
}
file.close();
return true;
}
bool Image::ReadPPM(std::istream& in) {
std::string format; std::string format;
file >> format; in >> format;
if (format != "P6") { if (format != "P6") {
std::cerr << "We only support binary PPM (P6) but got: " << format << "\n"; std::cerr << "We only support binary PPM (P6) but got: " << format << "\n";
return false; return false;
} }
int width, height, max_value; int width, height, max_value;
SkipWhitespaceAndComments(file); SkipWhitespaceAndComments(in);
file >> width; in >> width;
SkipWhitespaceAndComments(file); SkipWhitespaceAndComments(in);
file >> height; in >> height;
SkipWhitespaceAndComments(file); SkipWhitespaceAndComments(in);
file >> max_value; in >> max_value;
if (max_value <= 0 || max_value > 255) { if (max_value <= 0 || max_value > 255) {
std::cerr << "Unsupported max value " << max_value << "\n"; std::cerr << "Unsupported max value " << max_value << "\n";
return false; return false;
} }
// P6 requires exactly one whitespace character after the header. // P6 requires exactly one whitespace character after the header.
int value = file.get(); int value = in.get();
if (!std::isspace(value)) { if (!std::isspace(value)) {
std::cerr << "Missing whitespace after header\n"; std::cerr << "Missing whitespace after header\n";
return false; return false;
@ -100,19 +112,14 @@ bool Image::ReadPPM(const std::string& filename) {
int data_size = width * height * 3; int data_size = width * height * 3;
data_.resize(data_size); data_.resize(data_size);
std::vector<uint8_t> data_bytes(data_size); std::vector<uint8_t> data_bytes(data_size);
file.read(reinterpret_cast<char*>(data_bytes.data()), data_size); in.read(reinterpret_cast<char*>(data_bytes.data()), data_size);
if (file.gcount() != data_size) { if (in.gcount() != data_size) {
std::cerr << "Failed to read " << data_size << " bytes\n"; std::cerr << "Failed to read " << data_size << " bytes\n";
return false; return false;
} }
for (int i = 0; i < data_size; ++i) { for (int i = 0; i < data_size; ++i) {
data_[i] = StretchToSigned(static_cast<float>(data_bytes[i]) / max_value); data_[i] = StretchToSigned(static_cast<float>(data_bytes[i]) / max_value);
} }
if (file.get() != EOF) {
std::cerr << "Extra data in file\n";
return false;
}
file.close();
return true; return true;
} }

View File

@ -18,6 +18,7 @@
#include <cstddef> #include <cstddef>
#include <string> #include <string>
#include <istream>
#include <vector> #include <vector>
namespace gcpp { namespace gcpp {
@ -30,6 +31,9 @@ class Image {
// Reads a file in PPM format (P6, binary), normalizes to [-1, 1]. // Reads a file in PPM format (P6, binary), normalizes to [-1, 1].
// Returns true on success. // Returns true on success.
bool ReadPPM(const std::string& filename); bool ReadPPM(const std::string& filename);
// Reads PPM format (P6, binary) data from a stream, normalizes to [-1, 1].
// Returns true on success.
bool ReadPPM(std::istream& in);
// Resizes to 224x224 (nearest-neighbor for now, bilinear or antialias would // Resizes to 224x224 (nearest-neighbor for now, bilinear or antialias would
// be better). // be better).
void Resize(); void Resize();