From 2a34ba01ca72c870f9acaad0279f2c9936f5e574 Mon Sep 17 00:00:00 2001 From: Unbewohnte Date: Sun, 27 Feb 2022 14:49:30 +0300 Subject: [PATCH] [PPM] Reading functionality; Merged reader and writer in one class; Improved testing --- .gitignore | 4 +- src/pnm.cpp | 124 ++++++++++++++++++++++++++++++++----------------- tests/test.cpp | 64 ++++++++++++++++++++----- 3 files changed, 138 insertions(+), 54 deletions(-) diff --git a/.gitignore b/.gitignore index 2af2251..c5bc687 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -image.ppm +result_image.ppm +img.ppm +test_img512x512.ppm test \ No newline at end of file diff --git a/src/pnm.cpp b/src/pnm.cpp index a2db39f..af81d6a 100644 --- a/src/pnm.cpp +++ b/src/pnm.cpp @@ -10,9 +10,16 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + #ifndef RWPNM #define RWPNM +/* +RWPNM v0.1.0 + +A drop-in library to work with PNM images +*/ + #include #include @@ -36,6 +43,15 @@ public: uint8_t R; uint8_t G; uint8_t B; + + + bool operator==(const RGB& other) { + if (R != other.R || G != other.G || B != other.B) { + return false; + } + + return true; + } }; class Grayscale : public Color { @@ -49,7 +65,7 @@ public: }; -// PPM file format reader/writer base class +// PPM image file format reader/writer class PPM { protected: // PPM colored image magic number @@ -71,37 +87,86 @@ protected: } public: - PPM() {} + PPM() { + // set defaults (these can and will be changed when reading/coloring the image) + m_width = 0; + m_height = 0; + m_max_color = 255; + } ~PPM() {} // Color a pixel at coordinates (x, y) void put_pixel(uint32_t x, uint32_t y, RGB color) { uint64_t i = index_at(x, y); + if (i >= m_pixel_data.size()) { + // the image is not big enough ! + m_pixel_data.resize(i + 1); - if (i >= (m_width * m_height)) { - throw std::runtime_error("Pixel coordinates are out of bounds"); + if (x > m_width) { + m_width = x + 1; + } + + if (y > m_height) { + m_height = y + 1; + } } + m_pixel_data.at(i) = color; } -}; -// PPM file format image writer class -class PPM_writer : public PPM { -public: - PPM_writer(uint32_t width, uint32_t height, uint16_t maxcolor = 255) { - m_width = width; - m_height = height; - m_max_color = maxcolor; - m_pixel_data.resize(width*height); + // Read PPM image from disk + void read(std::string path) { + // try to open file + std::ifstream ppm_image_file; + ppm_image_file.open(path, std::ios::in); + if (!ppm_image_file.is_open()) { + throw std::runtime_error("Could not open an image"); + } + + // check for magic number + std::string entity; + ppm_image_file >> entity; + if (entity != "P6") { + throw std::runtime_error("Does not have a magic number"); + } + + // width, height, max color + ppm_image_file >> entity; + m_width = std::stoi(entity); + + ppm_image_file >> entity; + m_height = std::stoi(entity); + + ppm_image_file >> entity; + m_max_color = std::stoi(entity); + + m_pixel_data.resize(m_width * m_height); + + // skip one byte + char one[1]; + ppm_image_file.read(one, 1); + + // retrieve pixels + char pixel_color[3]; + for (uint64_t i = 0; i < m_width * m_height; i++) { + ppm_image_file.read(pixel_color, 3); + RGB color(pixel_color[0], pixel_color[1], pixel_color[2]); + if (color == RGB(255, 0, 0)) { + break; + } + m_pixel_data.at(i) = color; + } + + + ppm_image_file.close(); } - ~PPM_writer() {} // Write PPM image to disk void save(std::string path) { - // check if there are less/more pixels than needed - if (m_pixel_data.size() > (m_width * m_height) || m_pixel_data.size() < (m_width * m_height)) { - throw std::runtime_error("Invalid amount of pixels. There must be width*height pixels"); + // check if there are pixels at all + if (m_pixel_data.size() == 0) { + throw std::runtime_error("No pixels were assigned"); } // try to create file @@ -130,31 +195,6 @@ public: } }; - -class PPM_reader : public PPM { -public: - PPM_reader() {} - ~PPM_reader() {} - - // Read PPM image from disk - void read(std::string path) { - // try to open file - std::ifstream ppm_image_file; - ppm_image_file.open(path, std::ios::in); - if (!ppm_image_file.is_open()) { - throw std::runtime_error("Could not open an image"); - } - - while(true) { - std::string data; - ppm_image_file >> data; - if (data.length() == 0) { - break; - } - } - } -}; - } #endif \ No newline at end of file diff --git a/tests/test.cpp b/tests/test.cpp index e8409f2..215bae3 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -1,29 +1,71 @@ #include #include "../src/pnm.cpp" -using namespace pnm; -int main() { +void make_test_img() { try { - PPM_writer ppm_image(800, 800); + pnm::PPM image; - uint8_t cc = 0; - for (uint32_t y = 0; y < 800; y++) { - for (uint32_t x = 0; x < 800; x++) { - ppm_image.put_pixel(x, y, RGB(cc, cc, cc)); + const uint32_t width = 512; + const uint32_t height = 512; + + for (uint32_t y = 0; y < height; y++) { + for (uint32_t x = 0; x < width; x++) { + image.put_pixel(x, y, pnm::RGB(x / 2, y / 2, x / 2 + y / 2)); + } + } - cc++; + image.save("test_img512x512.ppm"); + } catch(const std::exception& e) { + std::string error_message("make_test_img: ", e.what()); + throw std::runtime_error(error_message); + } +} + +void green_rectangle_on_top_of_existing_image() { + try { + pnm::PPM image; + image.read("test_img512x512.ppm"); + // image.read("img.ppm"); + + uint32_t rec_width = 100; + uint32_t rec_height = 100; + + for (uint32_t y = 0; y < rec_height; y++) { + for (uint32_t x = 0; x < rec_width; x++) { + image.put_pixel(x, y, pnm::RGB(0, 255, 0)); } - cc--; } - ppm_image.save("image.ppm"); + + image.save("result_image.ppm"); + } catch(const std::exception& e) { + std::string error_message("green_rectangle_on_top_of_existing_image: ", e.what()); + throw std::runtime_error(error_message); + } +} + +void no_pixel_assign() { + try { + pnm::PPM image; + image.save("result_image.ppm"); + + std::string error_message("no_pixel_assign: should`ve gotten an error, but there weren`t any thrown"); + throw std::runtime_error(error_message); + } catch(const std::exception& e) {} +} + +int main() { + try { + make_test_img(); + green_rectangle_on_top_of_existing_image(); + no_pixel_assign(); } catch(const std::exception& e) { std::cout << "[ERROR] " << e.what() << "\n"; return 1; } - std::cout << "[SUCCESS]\n"; + std::cout << "[ALL TESTS WERE COMPLETED SUCCESSFULLY]\n"; return 0; }