Move image.cc fns to same order as in image.h
This commit is contained in:
parent
a2535ce630
commit
6f0a0219b3
1 changed files with 451 additions and 451 deletions
902
src/image.cc
902
src/image.cc
|
@ -211,457 +211,6 @@ bool Image::SaveAsPPM(const char *filename, bool overwrite, bool packed) {
|
||||||
return SaveAsPPM(std::string(filename), overwrite, packed);
|
return SaveAsPPM(std::string(filename), overwrite, packed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::DecodePNG(const std::string &filename) {
|
|
||||||
FILE *file = std::fopen(filename.c_str(), "rb");
|
|
||||||
|
|
||||||
// Check header of file to check if it is actually a png file.
|
|
||||||
{
|
|
||||||
std::array<unsigned char, 8> buf;
|
|
||||||
if (std::fread(buf.data(), 1, 8, file) != 8) {
|
|
||||||
std::fclose(file);
|
|
||||||
std::cout << "ERROR: File \"" << filename << "\" is smaller than 8 bytes"
|
|
||||||
<< std::endl;
|
|
||||||
return;
|
|
||||||
} else if (png_sig_cmp(reinterpret_cast<png_const_bytep>(buf.data()), 0,
|
|
||||||
8) != 0) {
|
|
||||||
// not png file, do nothing
|
|
||||||
std::fclose(file);
|
|
||||||
std::cout << "ERROR: File \"" << filename << "\" is not a png file"
|
|
||||||
<< std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// seek to head of file
|
|
||||||
std::rewind(file);
|
|
||||||
|
|
||||||
// init required structs for png decoding
|
|
||||||
png_structp png_ptr =
|
|
||||||
png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
|
||||||
if (!png_ptr) {
|
|
||||||
std::cout << "ERROR: Failed to initialize libpng (png_ptr) for decoding "
|
|
||||||
"PNG file \""
|
|
||||||
<< filename << '"' << std::endl;
|
|
||||||
std::fclose(file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
png_infop png_info_ptr = png_create_info_struct(png_ptr);
|
|
||||||
if (!png_info_ptr) {
|
|
||||||
std::cout << "ERROR: Failed to initialize libpng (png_infop) for decoding "
|
|
||||||
"PNG file \""
|
|
||||||
<< filename << '"' << std::endl;
|
|
||||||
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
|
||||||
std::fclose(file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
png_infop png_end_info_ptr = png_create_info_struct(png_ptr);
|
|
||||||
if (!png_end_info_ptr) {
|
|
||||||
std::cout << "ERROR: Failed to initialize libpng (end png_infop) for "
|
|
||||||
"decoding PNG file \""
|
|
||||||
<< filename << '"' << std::endl;
|
|
||||||
png_destroy_read_struct(&png_ptr, &png_info_ptr, nullptr);
|
|
||||||
std::fclose(file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// required to handle libpng errors
|
|
||||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
|
||||||
png_destroy_read_struct(&png_ptr, &png_info_ptr, &png_end_info_ptr);
|
|
||||||
std::fclose(file);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pass the FILE pointer to libpng
|
|
||||||
png_init_io(png_ptr, file);
|
|
||||||
|
|
||||||
// have libpng process the png data
|
|
||||||
png_read_png(png_ptr, png_info_ptr, PNG_TRANSFORM_IDENTITY, nullptr);
|
|
||||||
|
|
||||||
// get image width (in pixels)
|
|
||||||
width_ = png_get_image_width(png_ptr, png_info_ptr);
|
|
||||||
|
|
||||||
// get image height (in pixels)
|
|
||||||
height_ = png_get_image_height(png_ptr, png_info_ptr);
|
|
||||||
|
|
||||||
// get channel count of image
|
|
||||||
unsigned int channels = png_get_channels(png_ptr, png_info_ptr);
|
|
||||||
if (channels == 1) {
|
|
||||||
is_grayscale_ = true;
|
|
||||||
} else {
|
|
||||||
is_grayscale_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (height_ > PNG_UINT_32_MAX) {
|
|
||||||
png_error(png_ptr, "Image is too tall to process in memory");
|
|
||||||
} else if (width_ > PNG_UINT_32_MAX) {
|
|
||||||
png_error(png_ptr, "Image is too wide to process in memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
png_byte **row_pointers = png_get_rows(png_ptr, png_info_ptr);
|
|
||||||
|
|
||||||
data_.clear();
|
|
||||||
if (channels == 3 || channels == 4) {
|
|
||||||
data_.reserve(width_ * 4 * height_);
|
|
||||||
} else if (channels == 1) {
|
|
||||||
data_.reserve(width_ * height_);
|
|
||||||
} else {
|
|
||||||
std::cout << "ERROR: PNG has invalid channel count == " << channels
|
|
||||||
<< std::endl;
|
|
||||||
png_destroy_read_struct(&png_ptr, &png_info_ptr, &png_end_info_ptr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (unsigned int y = 0; y < height_; ++y) {
|
|
||||||
for (unsigned int x = 0; x < width_; ++x) {
|
|
||||||
if (is_grayscale_) {
|
|
||||||
data_.push_back(row_pointers[y][x]);
|
|
||||||
} else if (channels == 3) {
|
|
||||||
for (unsigned int c = 0; c < channels; ++c) {
|
|
||||||
data_.push_back(row_pointers[y][x * channels + c]);
|
|
||||||
}
|
|
||||||
data_.push_back(255);
|
|
||||||
} else /* if (channels == 4) */ {
|
|
||||||
for (unsigned int c = 0; c < channels; ++c) {
|
|
||||||
data_.push_back(row_pointers[y][x * channels + c]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup
|
|
||||||
png_destroy_read_struct(&png_ptr, &png_info_ptr, &png_end_info_ptr);
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
// verify
|
|
||||||
if (is_grayscale_) {
|
|
||||||
if (data_.size() != width_ * height_) {
|
|
||||||
std::cout << "WARNING: data_.size() doesn't match width_ * height_"
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (data_.size() != 4 * width_ * height_) {
|
|
||||||
std::cout << "WARNING: data_.size() doesn't match 4 * width_ * height_"
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Image::DecodePGM(const std::string &filename) {
|
|
||||||
is_grayscale_ = true;
|
|
||||||
|
|
||||||
std::ifstream ifs(filename);
|
|
||||||
if (!ifs.is_open()) {
|
|
||||||
std::cout << "ERROR: Failed to open file \"" << filename << '"'
|
|
||||||
<< std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string str_input;
|
|
||||||
int int_input;
|
|
||||||
ifs >> str_input;
|
|
||||||
if (!ifs.good()) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PGM first identifier) \""
|
|
||||||
<< filename << '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str_input.compare("P2") == 0) {
|
|
||||||
// data stored in ascii format
|
|
||||||
|
|
||||||
// get width
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PGM width) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
width_ = int_input;
|
|
||||||
|
|
||||||
// get height
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PGM height) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
height_ = int_input;
|
|
||||||
|
|
||||||
// get max_value
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PGM max) \"" << filename << '"'
|
|
||||||
<< std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
float max_value = int_input;
|
|
||||||
|
|
||||||
// parse data
|
|
||||||
data_.clear();
|
|
||||||
data_.reserve(width_ * height_);
|
|
||||||
float value;
|
|
||||||
for (unsigned int i = 0; i < width_ * height_; ++i) {
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good()) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PGM data) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value = static_cast<float>(int_input) / max_value;
|
|
||||||
data_.push_back(std::round(value * 255.0F));
|
|
||||||
}
|
|
||||||
} else if (str_input.compare("P5") == 0) {
|
|
||||||
// data stored in raw format
|
|
||||||
|
|
||||||
// get width
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PGM width) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
width_ = int_input;
|
|
||||||
|
|
||||||
// get height
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PGM height) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
height_ = int_input;
|
|
||||||
|
|
||||||
// get max_value
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PGM max) \"" << filename << '"'
|
|
||||||
<< std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int max_value_int = int_input;
|
|
||||||
float max_value = int_input;
|
|
||||||
|
|
||||||
// validate max_value
|
|
||||||
if (max_value_int != 255 && max_value_int != 65535) {
|
|
||||||
std::cout << "ERROR: Invalid max value for PGM (should be 255 or 65535) "
|
|
||||||
"(filename \""
|
|
||||||
<< filename << "\")" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract whitespace before data
|
|
||||||
{
|
|
||||||
int c = ifs.get();
|
|
||||||
if (c != '\n' && c != ' ') {
|
|
||||||
std::cout << "WARNING: File data after PGM max is not whitespace "
|
|
||||||
"(filename \""
|
|
||||||
<< filename << "\")"
|
|
||||||
<< " value is " << c << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ifs.good()) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PGM after whitespace) \""
|
|
||||||
<< filename << '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse raw data
|
|
||||||
data_.clear();
|
|
||||||
data_.reserve(width_ * height_);
|
|
||||||
float value;
|
|
||||||
for (unsigned int i = 0; i < width_ * height_; ++i) {
|
|
||||||
if (max_value_int == 255) {
|
|
||||||
value = ifs.get() / max_value;
|
|
||||||
data_.push_back(std::round(value * 255.0F));
|
|
||||||
if (!ifs.good()) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PGM data) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else /* if (max_value_int == 65535) */ {
|
|
||||||
value = (ifs.get() & 0xFF) | ((ifs.get() << 8) & 0xFF00);
|
|
||||||
value /= max_value;
|
|
||||||
data_.push_back(std::round(value * 255.0F));
|
|
||||||
if (!ifs.good()) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PGM data 16-bit) \""
|
|
||||||
<< filename << '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ifs.get() != decltype(ifs)::traits_type::eof()) {
|
|
||||||
std::cout << "WARNING: Trailing data in PGM file \"" << filename << '"'
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cout << "ERROR: Invalid \"magic number\" in header of file \""
|
|
||||||
<< filename << '"' << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Image::DecodePPM(const std::string &filename) {
|
|
||||||
is_grayscale_ = false;
|
|
||||||
std::ifstream ifs(filename);
|
|
||||||
if (!ifs.is_open()) {
|
|
||||||
std::cout << "ERROR: Failed to open file \"" << filename << '"'
|
|
||||||
<< std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string str_input;
|
|
||||||
int int_input;
|
|
||||||
ifs >> str_input;
|
|
||||||
if (!ifs.good()) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PPM first identifier) \""
|
|
||||||
<< filename << '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (str_input.compare("P3") == 0) {
|
|
||||||
// data stored in ascii format
|
|
||||||
|
|
||||||
// get width
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PPM width) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
width_ = int_input;
|
|
||||||
|
|
||||||
// get height
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PPM height) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
height_ = int_input;
|
|
||||||
|
|
||||||
// get max_value
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PPM max) \"" << filename << '"'
|
|
||||||
<< std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
float max_value = int_input;
|
|
||||||
|
|
||||||
// parse data
|
|
||||||
data_.clear();
|
|
||||||
data_.reserve(width_ * height_ * 4);
|
|
||||||
float value;
|
|
||||||
for (unsigned int i = 0; i < width_ * height_ * 3; ++i) {
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good()) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PPM data) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
value = static_cast<float>(int_input) / max_value;
|
|
||||||
data_.push_back(std::round(value * 255.0F));
|
|
||||||
if (i % 3 == 2) {
|
|
||||||
// PPM is RGB but Image stores as RGBA
|
|
||||||
data_.push_back(255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (str_input.compare("P6") == 0) {
|
|
||||||
// data stored in raw format
|
|
||||||
|
|
||||||
// get width
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PPM width) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
width_ = int_input;
|
|
||||||
|
|
||||||
// get height
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PPM height) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
height_ = int_input;
|
|
||||||
|
|
||||||
// get max_value
|
|
||||||
ifs >> int_input;
|
|
||||||
if (!ifs.good() || int_input <= 0) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PPM max) \"" << filename << '"'
|
|
||||||
<< std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int max_value_int = int_input;
|
|
||||||
float max_value = int_input;
|
|
||||||
|
|
||||||
// validate max_value
|
|
||||||
if (max_value_int != 255 && max_value_int != 65535) {
|
|
||||||
std::cout << "ERROR: Invalid max value for PPM (should be 255 or 65535) "
|
|
||||||
"(filename \""
|
|
||||||
<< filename << "\")" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// extract whitespace before data
|
|
||||||
{
|
|
||||||
int c = ifs.get();
|
|
||||||
if (c != '\n' && c != ' ') {
|
|
||||||
std::cout
|
|
||||||
<< "WARNING: File data after PPM max is not whitespace (filename \""
|
|
||||||
<< filename << "\") value is " << c << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ifs.good()) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PPM after whitespace) \""
|
|
||||||
<< filename << '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse raw data
|
|
||||||
data_.clear();
|
|
||||||
data_.reserve(width_ * height_ * 4);
|
|
||||||
float value;
|
|
||||||
for (unsigned int i = 0; i < width_ * height_ * 3; ++i) {
|
|
||||||
if (max_value_int == 255) {
|
|
||||||
value = ifs.get() / max_value;
|
|
||||||
data_.push_back(std::round(value * 255.0F));
|
|
||||||
if (!ifs.good()) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PPM data) \"" << filename
|
|
||||||
<< '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else /* if (max_value_int == 65535) */ {
|
|
||||||
value = (ifs.get() & 0xFF) | ((ifs.get() << 8) & 0xFF00);
|
|
||||||
value /= max_value;
|
|
||||||
data_.push_back(std::round(value * 255.0F));
|
|
||||||
if (!ifs.good()) {
|
|
||||||
std::cout << "ERROR: Failed to parse file (PPM data 16-bit) \""
|
|
||||||
<< filename << '"' << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i % 3 == 2) {
|
|
||||||
// PPM is RGB but Image stores as RGBA
|
|
||||||
data_.push_back(255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ifs.get() != decltype(ifs)::traits_type::eof()) {
|
|
||||||
std::cout << "WARNING: Trailing data in PPM file \"" << filename << '"'
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cout << "ERROR: Invalid \"magic number\" in header of file \""
|
|
||||||
<< filename << '"' << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t Image::ColorToGray(uint8_t red, uint8_t green, uint8_t blue) {
|
uint8_t Image::ColorToGray(uint8_t red, uint8_t green, uint8_t blue) {
|
||||||
// values taken from Wikipedia article about conversion of color to grayscale
|
// values taken from Wikipedia article about conversion of color to grayscale
|
||||||
double y_linear = 0.2126 * (red / 255.0) + 0.7152 * (green / 255.0) +
|
double y_linear = 0.2126 * (red / 255.0) + 0.7152 * (green / 255.0) +
|
||||||
|
@ -1152,3 +701,454 @@ OpenCLHandle::Ptr Image::GetOpenCLHandle() {
|
||||||
|
|
||||||
return opencl_handle_;
|
return opencl_handle_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Image::DecodePNG(const std::string &filename) {
|
||||||
|
FILE *file = std::fopen(filename.c_str(), "rb");
|
||||||
|
|
||||||
|
// Check header of file to check if it is actually a png file.
|
||||||
|
{
|
||||||
|
std::array<unsigned char, 8> buf;
|
||||||
|
if (std::fread(buf.data(), 1, 8, file) != 8) {
|
||||||
|
std::fclose(file);
|
||||||
|
std::cout << "ERROR: File \"" << filename << "\" is smaller than 8 bytes"
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
} else if (png_sig_cmp(reinterpret_cast<png_const_bytep>(buf.data()), 0,
|
||||||
|
8) != 0) {
|
||||||
|
// not png file, do nothing
|
||||||
|
std::fclose(file);
|
||||||
|
std::cout << "ERROR: File \"" << filename << "\" is not a png file"
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// seek to head of file
|
||||||
|
std::rewind(file);
|
||||||
|
|
||||||
|
// init required structs for png decoding
|
||||||
|
png_structp png_ptr =
|
||||||
|
png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||||
|
if (!png_ptr) {
|
||||||
|
std::cout << "ERROR: Failed to initialize libpng (png_ptr) for decoding "
|
||||||
|
"PNG file \""
|
||||||
|
<< filename << '"' << std::endl;
|
||||||
|
std::fclose(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_infop png_info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
if (!png_info_ptr) {
|
||||||
|
std::cout << "ERROR: Failed to initialize libpng (png_infop) for decoding "
|
||||||
|
"PNG file \""
|
||||||
|
<< filename << '"' << std::endl;
|
||||||
|
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
||||||
|
std::fclose(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_infop png_end_info_ptr = png_create_info_struct(png_ptr);
|
||||||
|
if (!png_end_info_ptr) {
|
||||||
|
std::cout << "ERROR: Failed to initialize libpng (end png_infop) for "
|
||||||
|
"decoding PNG file \""
|
||||||
|
<< filename << '"' << std::endl;
|
||||||
|
png_destroy_read_struct(&png_ptr, &png_info_ptr, nullptr);
|
||||||
|
std::fclose(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// required to handle libpng errors
|
||||||
|
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||||
|
png_destroy_read_struct(&png_ptr, &png_info_ptr, &png_end_info_ptr);
|
||||||
|
std::fclose(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass the FILE pointer to libpng
|
||||||
|
png_init_io(png_ptr, file);
|
||||||
|
|
||||||
|
// have libpng process the png data
|
||||||
|
png_read_png(png_ptr, png_info_ptr, PNG_TRANSFORM_IDENTITY, nullptr);
|
||||||
|
|
||||||
|
// get image width (in pixels)
|
||||||
|
width_ = png_get_image_width(png_ptr, png_info_ptr);
|
||||||
|
|
||||||
|
// get image height (in pixels)
|
||||||
|
height_ = png_get_image_height(png_ptr, png_info_ptr);
|
||||||
|
|
||||||
|
// get channel count of image
|
||||||
|
unsigned int channels = png_get_channels(png_ptr, png_info_ptr);
|
||||||
|
if (channels == 1) {
|
||||||
|
is_grayscale_ = true;
|
||||||
|
} else {
|
||||||
|
is_grayscale_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (height_ > PNG_UINT_32_MAX) {
|
||||||
|
png_error(png_ptr, "Image is too tall to process in memory");
|
||||||
|
} else if (width_ > PNG_UINT_32_MAX) {
|
||||||
|
png_error(png_ptr, "Image is too wide to process in memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
png_byte **row_pointers = png_get_rows(png_ptr, png_info_ptr);
|
||||||
|
|
||||||
|
data_.clear();
|
||||||
|
if (channels == 3 || channels == 4) {
|
||||||
|
data_.reserve(width_ * 4 * height_);
|
||||||
|
} else if (channels == 1) {
|
||||||
|
data_.reserve(width_ * height_);
|
||||||
|
} else {
|
||||||
|
std::cout << "ERROR: PNG has invalid channel count == " << channels
|
||||||
|
<< std::endl;
|
||||||
|
png_destroy_read_struct(&png_ptr, &png_info_ptr, &png_end_info_ptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (unsigned int y = 0; y < height_; ++y) {
|
||||||
|
for (unsigned int x = 0; x < width_; ++x) {
|
||||||
|
if (is_grayscale_) {
|
||||||
|
data_.push_back(row_pointers[y][x]);
|
||||||
|
} else if (channels == 3) {
|
||||||
|
for (unsigned int c = 0; c < channels; ++c) {
|
||||||
|
data_.push_back(row_pointers[y][x * channels + c]);
|
||||||
|
}
|
||||||
|
data_.push_back(255);
|
||||||
|
} else /* if (channels == 4) */ {
|
||||||
|
for (unsigned int c = 0; c < channels; ++c) {
|
||||||
|
data_.push_back(row_pointers[y][x * channels + c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
png_destroy_read_struct(&png_ptr, &png_info_ptr, &png_end_info_ptr);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
// verify
|
||||||
|
if (is_grayscale_) {
|
||||||
|
if (data_.size() != width_ * height_) {
|
||||||
|
std::cout << "WARNING: data_.size() doesn't match width_ * height_"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (data_.size() != 4 * width_ * height_) {
|
||||||
|
std::cout << "WARNING: data_.size() doesn't match 4 * width_ * height_"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::DecodePGM(const std::string &filename) {
|
||||||
|
is_grayscale_ = true;
|
||||||
|
|
||||||
|
std::ifstream ifs(filename);
|
||||||
|
if (!ifs.is_open()) {
|
||||||
|
std::cout << "ERROR: Failed to open file \"" << filename << '"'
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string str_input;
|
||||||
|
int int_input;
|
||||||
|
ifs >> str_input;
|
||||||
|
if (!ifs.good()) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PGM first identifier) \""
|
||||||
|
<< filename << '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_input.compare("P2") == 0) {
|
||||||
|
// data stored in ascii format
|
||||||
|
|
||||||
|
// get width
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PGM width) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
width_ = int_input;
|
||||||
|
|
||||||
|
// get height
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PGM height) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
height_ = int_input;
|
||||||
|
|
||||||
|
// get max_value
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PGM max) \"" << filename << '"'
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float max_value = int_input;
|
||||||
|
|
||||||
|
// parse data
|
||||||
|
data_.clear();
|
||||||
|
data_.reserve(width_ * height_);
|
||||||
|
float value;
|
||||||
|
for (unsigned int i = 0; i < width_ * height_; ++i) {
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good()) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PGM data) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
value = static_cast<float>(int_input) / max_value;
|
||||||
|
data_.push_back(std::round(value * 255.0F));
|
||||||
|
}
|
||||||
|
} else if (str_input.compare("P5") == 0) {
|
||||||
|
// data stored in raw format
|
||||||
|
|
||||||
|
// get width
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PGM width) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
width_ = int_input;
|
||||||
|
|
||||||
|
// get height
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PGM height) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
height_ = int_input;
|
||||||
|
|
||||||
|
// get max_value
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PGM max) \"" << filename << '"'
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int max_value_int = int_input;
|
||||||
|
float max_value = int_input;
|
||||||
|
|
||||||
|
// validate max_value
|
||||||
|
if (max_value_int != 255 && max_value_int != 65535) {
|
||||||
|
std::cout << "ERROR: Invalid max value for PGM (should be 255 or 65535) "
|
||||||
|
"(filename \""
|
||||||
|
<< filename << "\")" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract whitespace before data
|
||||||
|
{
|
||||||
|
int c = ifs.get();
|
||||||
|
if (c != '\n' && c != ' ') {
|
||||||
|
std::cout << "WARNING: File data after PGM max is not whitespace "
|
||||||
|
"(filename \""
|
||||||
|
<< filename << "\")"
|
||||||
|
<< " value is " << c << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ifs.good()) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PGM after whitespace) \""
|
||||||
|
<< filename << '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse raw data
|
||||||
|
data_.clear();
|
||||||
|
data_.reserve(width_ * height_);
|
||||||
|
float value;
|
||||||
|
for (unsigned int i = 0; i < width_ * height_; ++i) {
|
||||||
|
if (max_value_int == 255) {
|
||||||
|
value = ifs.get() / max_value;
|
||||||
|
data_.push_back(std::round(value * 255.0F));
|
||||||
|
if (!ifs.good()) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PGM data) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else /* if (max_value_int == 65535) */ {
|
||||||
|
value = (ifs.get() & 0xFF) | ((ifs.get() << 8) & 0xFF00);
|
||||||
|
value /= max_value;
|
||||||
|
data_.push_back(std::round(value * 255.0F));
|
||||||
|
if (!ifs.good()) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PGM data 16-bit) \""
|
||||||
|
<< filename << '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifs.get() != decltype(ifs)::traits_type::eof()) {
|
||||||
|
std::cout << "WARNING: Trailing data in PGM file \"" << filename << '"'
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cout << "ERROR: Invalid \"magic number\" in header of file \""
|
||||||
|
<< filename << '"' << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Image::DecodePPM(const std::string &filename) {
|
||||||
|
is_grayscale_ = false;
|
||||||
|
std::ifstream ifs(filename);
|
||||||
|
if (!ifs.is_open()) {
|
||||||
|
std::cout << "ERROR: Failed to open file \"" << filename << '"'
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string str_input;
|
||||||
|
int int_input;
|
||||||
|
ifs >> str_input;
|
||||||
|
if (!ifs.good()) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PPM first identifier) \""
|
||||||
|
<< filename << '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_input.compare("P3") == 0) {
|
||||||
|
// data stored in ascii format
|
||||||
|
|
||||||
|
// get width
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PPM width) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
width_ = int_input;
|
||||||
|
|
||||||
|
// get height
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PPM height) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
height_ = int_input;
|
||||||
|
|
||||||
|
// get max_value
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PPM max) \"" << filename << '"'
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
float max_value = int_input;
|
||||||
|
|
||||||
|
// parse data
|
||||||
|
data_.clear();
|
||||||
|
data_.reserve(width_ * height_ * 4);
|
||||||
|
float value;
|
||||||
|
for (unsigned int i = 0; i < width_ * height_ * 3; ++i) {
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good()) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PPM data) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
value = static_cast<float>(int_input) / max_value;
|
||||||
|
data_.push_back(std::round(value * 255.0F));
|
||||||
|
if (i % 3 == 2) {
|
||||||
|
// PPM is RGB but Image stores as RGBA
|
||||||
|
data_.push_back(255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (str_input.compare("P6") == 0) {
|
||||||
|
// data stored in raw format
|
||||||
|
|
||||||
|
// get width
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PPM width) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
width_ = int_input;
|
||||||
|
|
||||||
|
// get height
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PPM height) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
height_ = int_input;
|
||||||
|
|
||||||
|
// get max_value
|
||||||
|
ifs >> int_input;
|
||||||
|
if (!ifs.good() || int_input <= 0) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PPM max) \"" << filename << '"'
|
||||||
|
<< std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int max_value_int = int_input;
|
||||||
|
float max_value = int_input;
|
||||||
|
|
||||||
|
// validate max_value
|
||||||
|
if (max_value_int != 255 && max_value_int != 65535) {
|
||||||
|
std::cout << "ERROR: Invalid max value for PPM (should be 255 or 65535) "
|
||||||
|
"(filename \""
|
||||||
|
<< filename << "\")" << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract whitespace before data
|
||||||
|
{
|
||||||
|
int c = ifs.get();
|
||||||
|
if (c != '\n' && c != ' ') {
|
||||||
|
std::cout
|
||||||
|
<< "WARNING: File data after PPM max is not whitespace (filename \""
|
||||||
|
<< filename << "\") value is " << c << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ifs.good()) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PPM after whitespace) \""
|
||||||
|
<< filename << '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse raw data
|
||||||
|
data_.clear();
|
||||||
|
data_.reserve(width_ * height_ * 4);
|
||||||
|
float value;
|
||||||
|
for (unsigned int i = 0; i < width_ * height_ * 3; ++i) {
|
||||||
|
if (max_value_int == 255) {
|
||||||
|
value = ifs.get() / max_value;
|
||||||
|
data_.push_back(std::round(value * 255.0F));
|
||||||
|
if (!ifs.good()) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PPM data) \"" << filename
|
||||||
|
<< '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else /* if (max_value_int == 65535) */ {
|
||||||
|
value = (ifs.get() & 0xFF) | ((ifs.get() << 8) & 0xFF00);
|
||||||
|
value /= max_value;
|
||||||
|
data_.push_back(std::round(value * 255.0F));
|
||||||
|
if (!ifs.good()) {
|
||||||
|
std::cout << "ERROR: Failed to parse file (PPM data 16-bit) \""
|
||||||
|
<< filename << '"' << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i % 3 == 2) {
|
||||||
|
// PPM is RGB but Image stores as RGBA
|
||||||
|
data_.push_back(255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifs.get() != decltype(ifs)::traits_type::eof()) {
|
||||||
|
std::cout << "WARNING: Trailing data in PPM file \"" << filename << '"'
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cout << "ERROR: Invalid \"magic number\" in header of file \""
|
||||||
|
<< filename << '"' << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue