From b9e19e076662ae5743b9c81eb238fe11204f6dbd Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 12 Apr 2009 19:06:50 +0000 Subject: Add some comments. --- libdimensionxx/canvas.cpp | 51 +++++++++++++++++++++++++++++++++ libdimensionxx/color.cpp | 1 + libdimensionxx/cookie.cpp | 50 ++++++++++++++++++++++++-------- libdimensionxx/dimensionxx/canvas.hpp | 32 ++++++++++++++------- libdimensionxx/dimensionxx/color.hpp | 13 +++++++++ libdimensionxx/dimensionxx/cookie.hpp | 3 ++ libdimensionxx/dimensionxx/error.hpp | 8 ++++++ libdimensionxx/dimensionxx/geometry.hpp | 15 +++++++++- libdimensionxx/dimensionxx/png.hpp | 20 +++++++++++++ libdimensionxx/error.cpp | 3 ++ libdimensionxx/png.cpp | 19 +++++++++++- 11 files changed, 190 insertions(+), 25 deletions(-) (limited to 'libdimensionxx') diff --git a/libdimensionxx/canvas.cpp b/libdimensionxx/canvas.cpp index 2340066..411d4e8 100644 --- a/libdimensionxx/canvas.cpp +++ b/libdimensionxx/canvas.cpp @@ -22,8 +22,59 @@ namespace Dimension { + // Allocate the canvas with dmnsn_new_canvas() + Canvas::Canvas(unsigned int width, unsigned int height) + : m_canvas(dmnsn_new_canvas(width, height)) { } + + // Virtual destructor: delete the canvas with dmnsn_delete_canvas(). Canvas::~Canvas() { dmnsn_delete_canvas(m_canvas); } + + // Get the width + unsigned int + Canvas::width() const + { + return m_canvas->x; + } + + // Get the height + unsigned int + Canvas::height() const + { + return m_canvas->y; + } + + // Get a particular pixel, thread-safely, with dmnsn_get_pixel(). + Color + Canvas::pixel(unsigned int x, unsigned int y) const + { + return Color(dmnsn_get_pixel(m_canvas, x, y)); + } + + // Set a particular pixel, thread-safely, with dmnsn_set_pixel(). + void + Canvas::pixel(unsigned int x, unsigned int y, const Color& c) + { + dmnsn_set_pixel(m_canvas, x, y, c.dmnsn()); + } + + // Return the wrapped canvas + dmnsn_canvas* + Canvas::dmnsn() + { + return m_canvas; + } + + // Return a const version of the wrapped canvas + const dmnsn_canvas* + Canvas::dmnsn() const + { + return m_canvas; + } + + // Protected default constructor: set m_canvas to NULL. + Canvas::Canvas() + : m_canvas(0) { } } diff --git a/libdimensionxx/color.cpp b/libdimensionxx/color.cpp index c7f768a..ffbc808 100644 --- a/libdimensionxx/color.cpp +++ b/libdimensionxx/color.cpp @@ -22,5 +22,6 @@ namespace Dimension { + // Definition of whitepoint const CIE_XYZ whitepoint(dmnsn_whitepoint); } diff --git a/libdimensionxx/cookie.cpp b/libdimensionxx/cookie.cpp index dd8a06d..c3b1541 100644 --- a/libdimensionxx/cookie.cpp +++ b/libdimensionxx/cookie.cpp @@ -21,10 +21,20 @@ #include "dimensionxx.hpp" #include +// The conundrum: libdimension and libdimension-* use C I/O, with FILE*'s. +// We want to use C++ I/O with std::i/ostreams. Unfortunately, there's no +// standard way to map between them, so we use the nonportable GNU stdio +// extension fopencookie(), which creates a FILE* with custom read/write/seek +// functions. BSD also has a similar function, funopen(), which we should +// use too. Failing in all that, we should fall back on a tmpfile() buffer, +// but that would require an fclosecookie() function as well, to copy the +// buffer to the stream potentially. + namespace Dimension { namespace { + // Internal cookie type to hold the C++ streams. struct Cookie { public: @@ -32,43 +42,50 @@ namespace Dimension std::ostream* ostr; }; + // Cookie read function ssize_t cookie_read(void* cookie, char* buf, size_t size) { Cookie* streams = reinterpret_cast(cookie); + // Do the unformatted read streams->istr->read(buf, size); - if (streams->istr->eof()) { - return streams->istr->gcount(); - } else if (!streams->istr->good()) { - return -1; + if (streams->istr->eof() || streams->istr->good()) { + return streams->istr->gcount(); // This returns 0 on an immediate EOF + // for us. } else { - return streams->istr->gcount(); + // Some non-EOF error + return -1; } } + // Cookie write function ssize_t cookie_write(void* cookie, const char* buf, size_t size) { Cookie* streams = reinterpret_cast(cookie); + // Do the unformatted write streams->ostr->write(buf, size); - if (!streams->ostr->good()) { - return -1; - } else { + if (streams->ostr->good()) { + // Write operation succeeded, so we must've written size bytes return size; + } else { + // Write operation failed + return -1; } } + // Cookie seek function int cookie_seek(void* cookie, off64_t* offset, int whence) { Cookie* streams = reinterpret_cast(cookie); - bool success = true; if (streams->istr) { + // If we have an input stream, seek it switch (whence) { case SEEK_SET: streams->istr->seekg(*offset, std::ios::beg); @@ -82,11 +99,13 @@ namespace Dimension } if (!streams->istr->good()) { - success = false; + // Seek failed + return 1; } } if (streams->ostr) { + // If we have an output stream, seek it too switch (whence) { case SEEK_SET: streams->ostr->seekp(*offset, std::ios::beg); @@ -99,20 +118,25 @@ namespace Dimension } if (!streams->ostr->good()) { - success = false; + // Seek failed + return 1; } } - return !success; + // Seek succeeded + return 0; } + // Cookie delete function int cookie_close(void* cookie) { + // Free the cookie delete reinterpret_cast(cookie); } } + // Make an input FILE* std::FILE* fcookie(std::istream& istr) { @@ -129,6 +153,7 @@ namespace Dimension return fopencookie(reinterpret_cast(cookie), "r", io_funcs); } + // Make an output FILE* std::FILE* fcookie(std::ostream& ostr) { @@ -145,6 +170,7 @@ namespace Dimension return fopencookie(reinterpret_cast(cookie), "w", io_funcs); } + // Make an I/O FILE* std::FILE* fcookie(std::iostream& iostr) { diff --git a/libdimensionxx/dimensionxx/canvas.hpp b/libdimensionxx/dimensionxx/canvas.hpp index c501413..eda9ad3 100644 --- a/libdimensionxx/dimensionxx/canvas.hpp +++ b/libdimensionxx/dimensionxx/canvas.hpp @@ -21,29 +21,39 @@ #ifndef DIMENSIONXX_CANVAS_HPP #define DIMENSIONXX_CANVAS_HPP +// dmnsn_canvas* wrapper. + namespace Dimension { + // Base canvas class. Wraps a dmnsn_canvas*. class Canvas { public: - Canvas(unsigned int width, unsigned int height) - : m_canvas(dmnsn_new_canvas(width, height)) { } + // Allocate a dmnsn_canvas specified width and height. + Canvas(unsigned int width, unsigned int height); + + // Wrap an existing canvas. explicit Canvas(dmnsn_canvas* canvas) : m_canvas(canvas) { } + + // Delete the canvas. (dmnsn_delete_canvas(m_canvas).) virtual ~Canvas(); - unsigned int width() const { return m_canvas->x; } - unsigned int height() const { return m_canvas->y; } + // Get the width and height. + unsigned int width() const; + unsigned int height() const; - Color pixel(unsigned int x, unsigned int y) const - { return Color(dmnsn_get_pixel(m_canvas, x, y)); } - void pixel(unsigned int x, unsigned int y, const Color& c) - { dmnsn_set_pixel(m_canvas, x, y, c.dmnsn()); } + // Get and set a pixel, thread-safely. + Color pixel(unsigned int x, unsigned int y) const; + void pixel(unsigned int x, unsigned int y, const Color& c); - dmnsn_canvas* dmnsn() { return m_canvas; } - const dmnsn_canvas* dmnsn() const { return m_canvas; } + // Access the wrapped C object. + dmnsn_canvas* dmnsn(); + const dmnsn_canvas* dmnsn() const; protected: - Canvas() : m_canvas(0) { } + // Derived classes may want to set m_canvas later. Set it to NULL now, so + // that the destructor can still dmnsn_delete_canvas it. + Canvas(); dmnsn_canvas* m_canvas; diff --git a/libdimensionxx/dimensionxx/color.hpp b/libdimensionxx/dimensionxx/color.hpp index afd7ea5..a7087a7 100644 --- a/libdimensionxx/dimensionxx/color.hpp +++ b/libdimensionxx/dimensionxx/color.hpp @@ -21,16 +21,21 @@ #ifndef DIMENSIONXX_COLOR_HPP #define DIMENSIONXX_COLOR_HPP +// Wrappers for libdimension colors. + namespace Dimension { + // Forward declarations class CIE_XYZ; class CIE_xyY; class CIE_Lab; class CIE_Luv; class sRGB; + // Default whitepoint (D50) extern const CIE_XYZ whitepoint; + // Wrapper for dmnsn_color class Color { public: @@ -44,6 +49,7 @@ namespace Dimension // Color(const Color& c); // ~Color(); + // Get and set filtered and unfiltered transparancy double filter() const { return m_color.filter; } double trans() const { return m_color.trans; } @@ -51,15 +57,20 @@ namespace Dimension double trans(double t) { m_color.trans = t; } // Color& operator=(const Color& c); + + // Add a color to this one in a perceptually correct manner Color& operator+=(const Color& c) { m_color = dmnsn_color_add(m_color, c.m_color); return *this; } + // Access the wrapped color dmnsn_color dmnsn() const { return m_color; } private: dmnsn_color m_color; }; + // Wrappers for all libdimension color types + class CIE_XYZ { public: @@ -206,6 +217,7 @@ namespace Dimension // Color operators + // Perceptually correct color combination inline Color operator+(const Color& lhs, const Color& rhs) { @@ -214,6 +226,7 @@ namespace Dimension return temp; } + // Perceptual color difference inline double operator-(const Color& lhs, const Color& rhs) { diff --git a/libdimensionxx/dimensionxx/cookie.hpp b/libdimensionxx/dimensionxx/cookie.hpp index acb783d..639fbfe 100644 --- a/libdimensionxx/dimensionxx/cookie.hpp +++ b/libdimensionxx/dimensionxx/cookie.hpp @@ -21,6 +21,9 @@ #ifndef DIMENSIONXX_COOKIE_HPP #define DIMENSIONXX_COOKIE_HPP +// Some internal magic to use C FILE* I/O with C++ streams. Currently this ties +// us to Linux and glibc, but in the future, this will be portable. + #include #include #include diff --git a/libdimensionxx/dimensionxx/error.hpp b/libdimensionxx/dimensionxx/error.hpp index 8919895..99670cb 100644 --- a/libdimensionxx/dimensionxx/error.hpp +++ b/libdimensionxx/dimensionxx/error.hpp @@ -21,21 +21,29 @@ #ifndef DIMENSIONXX_ERROR_HPP #define DIMENSIONXX_ERROR_HPP +// Wrappers for libdimension error handling, and an exception class. +// dmnsn_error is still used by libdimensionxx whenever an exception shouldn't +// be thrown, like in destructors, and whenever libdimension or libdimension-* +// use it internally. Exceptions are thrown otherwise to report errors. + #include #include #include namespace Dimension { + // Wrapper for dmnsn_severity enum Severity { SEVERITY_LOW = DMNSN_SEVERITY_LOW, SEVERITY_MEDIUM = DMNSN_SEVERITY_MEDIUM, SEVERITY_HIGH = DMNSN_SEVERITY_HIGH }; + // Get or set the resilience, thread-safely Severity resilience(); void resilience(Severity resilience); + // Generic exception class, derives from std::runtime_error class Dimension_Error : public std::runtime_error { public: diff --git a/libdimensionxx/dimensionxx/geometry.hpp b/libdimensionxx/dimensionxx/geometry.hpp index 4295c05..78b9b49 100644 --- a/libdimensionxx/dimensionxx/geometry.hpp +++ b/libdimensionxx/dimensionxx/geometry.hpp @@ -21,12 +21,15 @@ #ifndef DIMENSIONXX_GEOMETRY_HPP #define DIMENSIONXX_GEOMETRY_HPP +// Wrappers for geometric types (Scalars, Vectors, Lines (rays)). + #include namespace Dimension { - typedef dmnsn_scalar Scalar; + typedef dmnsn_scalar Scalar; // This is really `double' + // Wrapper for dmnsn_vector class Vector { public: @@ -37,10 +40,13 @@ namespace Dimension // Vector(const Vector& v); // ~Vector(); + // Get the x, y, and z components. Scalar x() const { return m_vector.x; } Scalar y() const { return m_vector.y; } Scalar z() const { return m_vector.z; } + // Vector arithmetic + // Vector& operator=(const Vector& rhs); Vector& operator+=(const Vector& rhs) { m_vector = dmnsn_vector_add(m_vector, rhs.m_vector); return *this; } @@ -51,12 +57,14 @@ namespace Dimension Vector& operator/=(Scalar rhs) { m_vector = dmnsn_vector_div(m_vector, rhs); return *this; } + // Get the wrapped vector dmnsn_vector dmnsn() const { return m_vector; } private: dmnsn_vector m_vector; }; + // Wrapper for dmnsn_line class line { public: @@ -70,8 +78,11 @@ namespace Dimension Vector n() const { return Vector(m_line.n); } // line& operator=(const line& l); + + // Get the point `t' on the line (x0 + t*n) Vector operator()(Scalar t) { return Vector(dmnsn_line_point(m_line, t)); } + // Get the wrapped line dmnsn_line dmnsn() const { return m_line; } private: @@ -120,12 +131,14 @@ namespace Dimension return r; } + // Dot product inline Scalar dot(const Vector& lhs, const Vector& rhs) { return dmnsn_vector_dot(lhs.dmnsn(), rhs.dmnsn()); } + // Cross product inline Vector cross(const Vector& lhs, const Vector& rhs) { diff --git a/libdimensionxx/dimensionxx/png.hpp b/libdimensionxx/dimensionxx/png.hpp index 96ab2ca..c14b25f 100644 --- a/libdimensionxx/dimensionxx/png.hpp +++ b/libdimensionxx/dimensionxx/png.hpp @@ -21,25 +21,43 @@ #ifndef DIMENSIONXX_PNG_HPP #define DIMENSIONXX_PNG_HPP +// C++ wrapper for libdimension-png. PNG_Canvas derives from Canvas. + #include #include namespace Dimension { + // PNG_Canvas handles reading a Canvas from a PNG file, writing one to a PNG + // file, or both, depending on what type of stream(s) are given to the + // constructor. class PNG_Canvas : public Canvas { public: + // Input PNG_Canvas; read the Canvas from istr now explicit PNG_Canvas(std::istream& istr) : Canvas(), m_istr(&istr), m_ostr(0), m_written(false) { read(); } + + // Output PNG_Canvas; write the Canvas to ostr at destruction, or when + // write() is called. PNG_Canvas(unsigned int x, unsigned int y, std::ostream& ostr) : Canvas(x, y), m_istr(0), m_ostr(&ostr), m_written(false) { } + + // I/O PNG_Canvas; read the Canvas from istr now, and write to ostr at + // destruction or then write() is called. PNG_Canvas(std::istream& istr, std::ostream& ostr) : Canvas(), m_istr(&istr), m_ostr(&ostr), m_written(false) { read(); } + + // Call write() if we're an output PNG_Canvas, but trap any exceptions and + // report a dmnsn_error() instead. virtual ~PNG_Canvas(); + // Write the Canvas to the output stream, throwing a Dimension_Error on + // error. void write(); protected: + // In case a derived class needs to set m_canvas after we're constructed PNG_Canvas(std::ostream* ostr) : Canvas(), m_istr(0), m_ostr(ostr), m_written(false) { } @@ -48,6 +66,8 @@ namespace Dimension std::ostream* m_ostr; bool m_written; + // Read the Canvas from a PNG file, and throw a Dimension_Error upon + // failure. void read(); // Copying prohibited diff --git a/libdimensionxx/error.cpp b/libdimensionxx/error.cpp index 948a8c0..3d7dfa0 100644 --- a/libdimensionxx/error.cpp +++ b/libdimensionxx/error.cpp @@ -24,18 +24,21 @@ namespace Dimension { + // Get the resilience, thread-safely, with dmnsn_get_resilience(). Severity resilience() { return static_cast(dmnsn_get_resilience()); } + // Set the resilience, thread-safely, with dmnsn_set_resilience(). void resilience(Severity resilience) { dmnsn_set_resilience(static_cast(resilience)); } + // Dimension_Error constructor Dimension_Error::Dimension_Error(const std::string& str) : std::runtime_error(str) { } } diff --git a/libdimensionxx/png.cpp b/libdimensionxx/png.cpp index 1ca3047..348b7a7 100644 --- a/libdimensionxx/png.cpp +++ b/libdimensionxx/png.cpp @@ -25,6 +25,9 @@ namespace Dimension { + // PNG_Canvas destructor. Call write() to write the PNG file if not already + // written, but catch any exceptions and instead report the error with + // dmnsn_error() to avoid throwing from a destructor. PNG_Canvas::~PNG_Canvas() { if (m_ostr && !m_written) { @@ -37,46 +40,60 @@ namespace Dimension } } + // Write the PNG file. Uses the fcookie() interface to make a FILE* + // corresponding to an std::ostream (including std::ostringstream, etc). void PNG_Canvas::write() { if (m_written) { + // Does writing a PNG file twice make sense? throw Dimension_Error("Attempt to write canvas to PNG twice."); } if (!m_ostr) { + // Don't call write() if we're not an output PNG_Canvas... throw Dimension_Error("Attempt to write canvas to PNG without an output" " stream."); } FILE* file = fcookie(*m_ostr); if (!file) { + // fcookie() shouldn't fail, really throw Dimension_Error("Couldn't create C++/C IO interface when writing" " canvas to PNG."); } + // Write the PNG file if (dmnsn_png_write_canvas(m_canvas, file)) { + // The actual write operation failed, for some reason. std::fclose(file); throw Dimension_Error("Writing canvas to PNG failed."); } std::fclose(file); - m_written = true; + m_written = true; // We've written the file now, don't do it again } + // Read a canvas from a PNG file. Uses the fcookie() interface to make a + // FILE* corresponding to an std::istream (including std::istringstream, etc). void PNG_Canvas::read() { if (!m_istr) { + // read() is private, and only called from the appropriate constructors, + // so this REALLY shouldn't happen. throw Dimension_Error("Attempt to read canvas from PNG without an input" " stream."); } FILE* file = fcookie(*m_istr); if (!file) { + // fcookie() shouldn't fail, really throw Dimension_Error("Couldn't create C++/C IO interface when reading" " canvas from PNG."); } + // Read the canvas from a PNG file if (!(m_canvas = dmnsn_png_read_canvas(file))) { + // The read operation failed std::fclose(file); throw Dimension_Error("Reading canvas from PNG failed."); } -- cgit v1.2.3