diff options
author | Tavian Barnes <tavianator@gmail.com> | 2009-06-24 21:28:51 +0000 |
---|---|---|
committer | Tavian Barnes <tavianator@gmail.com> | 2009-06-24 21:28:51 +0000 |
commit | 603712c38a127e297eddd23975fb950499a0c10c (patch) | |
tree | 1ec4a3f870f57ab72b0003399657654d91bf247b | |
parent | 706acbb46a91adef8ed2ec1d255802e93c59b65a (diff) | |
download | dimension-603712c38a127e297eddd23975fb950499a0c10c.tar.xz |
New asynchronous PNG_Canvas interface.
-rw-r--r-- | libdimensionxx/dimensionxx/png.hpp | 29 | ||||
-rw-r--r-- | libdimensionxx/dimensionxx/progress.hpp | 73 | ||||
-rw-r--r-- | libdimensionxx/png.cpp | 111 | ||||
-rw-r--r-- | libdimensionxx/progress.cpp | 25 | ||||
-rw-r--r-- | tests/pngxx.cpp | 33 |
5 files changed, 254 insertions, 17 deletions
diff --git a/libdimensionxx/dimensionxx/png.hpp b/libdimensionxx/dimensionxx/png.hpp index 07d7c18..c2cdbd5 100644 --- a/libdimensionxx/dimensionxx/png.hpp +++ b/libdimensionxx/dimensionxx/png.hpp @@ -25,6 +25,7 @@ #include <istream> #include <ostream> +#include <memory> namespace Dimension { @@ -35,18 +36,15 @@ namespace Dimension { 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(); } + explicit PNG_Canvas(std::istream& istr); // 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) { } + PNG_Canvas(unsigned int x, unsigned int y, std::ostream& ostr); // 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(); } + PNG_Canvas(std::istream& istr, std::ostream& ostr); // Call write() if we're an output PNG_Canvas, but trap any exceptions and // report a dmnsn_error() instead. @@ -56,12 +54,23 @@ namespace Dimension // error. void write(); + // Write the Canvas to the output stream in the background + Progress write_async(); + + // Construct an input or I/O PNG_Canvas in the background + static Progress read_async(std::istream& istr); + explicit PNG_Canvas(Progress& progress); + explicit PNG_Canvas(Progress& progress, std::ostream& ostr); + 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) { } + explicit PNG_Canvas(std::ostream& ostr); private: + // Copying prohibited + PNG_Canvas(const PNG_Canvas&); + PNG_Canvas& operator=(const PNG_Canvas&); + std::istream* m_istr; std::ostream* m_ostr; bool m_written; @@ -69,10 +78,6 @@ namespace Dimension // Read the Canvas from a PNG file, and throw a Dimension_Error upon // failure. void read(); - - // Copying prohibited - PNG_Canvas(const PNG_Canvas&); - PNG_Canvas& operator=(const PNG_Canvas&); }; } diff --git a/libdimensionxx/dimensionxx/progress.hpp b/libdimensionxx/dimensionxx/progress.hpp index 72099c4..b9df870 100644 --- a/libdimensionxx/dimensionxx/progress.hpp +++ b/libdimensionxx/dimensionxx/progress.hpp @@ -22,16 +22,71 @@ #define DIMENSIONXX_PROGRESS_HPP #include <tr1/memory> // For tr1::shared_ptr +#include <list> // dmnsn_canvas* wrapper. namespace Dimension { + // Base class for persisting objects + class Persist_Base + { + public: + virtual ~Persist_Base() = 0; + + protected: + Persist_Base() { } + + private: + // Copying prohibited + Persist_Base(const Persist_Base&); + Persist_Base& operator=(const Persist_Base&); + }; + + // Class for persisting objects + template <typename T> + class Persist : public Persist_Base + { + public: + Persist(T* t) : m_t(t) { } + virtual ~Persist() { delete m_t; } + + T* persisted() const { return m_t; } + + private: + T* m_t; + }; + + // Class for persisting many objects + class Persister + { + public: + // Persister(); + // Persister(const Persister& persister); + // ~Persister(); + + // Persister& operator=(const Persister& persister); + + template <typename T> + void persist(T* t); + + // Access the first persisted element + template <typename T> + Persist<T>& first(); + + private: + // Copy-assignment prohibited + Persister& operator=(const Persister&); + + std::list<std::tr1::shared_ptr<Persist_Base> > m_persists; + }; + // dmnsn_progress* wrapper class to represent an asynchronous worker thread class Progress { public: explicit Progress(dmnsn_progress* progress); + Progress(dmnsn_progress* progress, const Persister& persister); // Progress(const Progress& progress); // Finishes the job without throwing @@ -47,6 +102,9 @@ namespace Dimension // Wait for job to finish, throwing if the job failed void finish(); + // Access the set of persisted objects + Persister& persister(); + // Access the wrapped C object. dmnsn_progress* dmnsn(); const dmnsn_progress* dmnsn() const; @@ -56,7 +114,22 @@ namespace Dimension Progress& operator=(const Progress&); std::tr1::shared_ptr<dmnsn_progress*> m_progress; + Persister m_persister; }; + + template <typename T> + void + Persister::persist(T* t) + { + m_persists.push_back(std::tr1::shared_ptr<Persist_Base>(new Persist<T>(t))); + } + + template <typename T> + Persist<T>& + Persister::first() + { + return dynamic_cast<Persist<T>&>(*m_persists.front()); + } } #endif /* DIMENSIONXX_PROGRESS_HPP */ diff --git a/libdimensionxx/png.cpp b/libdimensionxx/png.cpp index 029e431..41162e6 100644 --- a/libdimensionxx/png.cpp +++ b/libdimensionxx/png.cpp @@ -24,6 +24,22 @@ namespace Dimension { + PNG_Canvas::PNG_Canvas(std::istream& istr) + : Canvas(), m_istr(&istr), m_ostr(0), m_written(false) + { + read(); + } + + PNG_Canvas::PNG_Canvas(unsigned int x, unsigned int y, std::ostream& ostr) + : Canvas(x, y), m_istr(0), m_ostr(&ostr), m_written(false) + { } + + PNG_Canvas::PNG_Canvas(std::istream& istr, std::ostream& ostr) + : Canvas(), m_istr(&istr), m_ostr(&ostr), m_written(false) + { + read(); + } + // 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. @@ -52,20 +68,109 @@ namespace Dimension // 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."); - } + } // Make the C++/C I/O interface FILE_Cookie cookie(*m_ostr); // Write the PNG file if (dmnsn_png_write_canvas(m_canvas, cookie.file())) { - // The actual write operation failed, for some reason. + // The actual write operation failed, for some reason throw Dimension_Error("Writing canvas to PNG failed."); } m_written = true; // We've written the file now, don't do it again } + // Write a PNG file in the background + Progress + PNG_Canvas::write_async() + { + 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_async() if we're not an output PNG_Canvas... + throw Dimension_Error("Attempt to write canvas to PNG without an output" + " stream."); + } + + m_written = true; // We've written the file now, don't do it again + + // Object to persist local variables past function return + Persister persister; + + // Make the C++/C I/O interface + FILE_Cookie* cookie = new FILE_Cookie(*m_ostr); + persister.persist(cookie); + + // Return the Progress object + return Progress(dmnsn_png_write_canvas_async(m_canvas, cookie->file()), + persister); + } + + // Read a PNG file in the background + Progress + PNG_Canvas::read_async(std::istream& istr) + { + // Object to persist local variables past function return + Persister persister; + + // Store a pointer to a dmnsn_canvas* in the persister to later construct + // the PNG_Canvas + dmnsn_canvas** canvas = new dmnsn_canvas*; + persister.persist(canvas); + + // Make the C++/C I/O interface + FILE_Cookie* cookie = new FILE_Cookie(istr); + persister.persist(cookie); + + // Return the Progress object + return Progress(dmnsn_png_read_canvas_async(canvas, cookie->file()), + persister); + } + + // Construct an input PNG_Canvas from a background task + PNG_Canvas::PNG_Canvas(Progress& progress) + : Canvas(), m_istr(0), m_ostr(0), m_written(false) + { + dmnsn_canvas** canvas + = progress.persister().first<dmnsn_canvas*>().persisted(); + + try { + progress.finish(); + } catch (...) { + dmnsn_delete_canvas(*canvas); + throw; + } + + m_canvas = *canvas; + } + + // Construct an I/O PNG_Canvas from a background task + PNG_Canvas::PNG_Canvas(Progress& progress, std::ostream& ostr) + : Canvas(), m_istr(0), m_ostr(&ostr), m_written(false) + { + dmnsn_canvas** canvas + = progress.persister().first<dmnsn_canvas*>().persisted(); + + try { + progress.finish(); + } catch (...) { + dmnsn_delete_canvas(*canvas); + throw; + } + + m_canvas = *canvas; + } + + // Protected constructor which sets the canvas to NULL for now + PNG_Canvas::PNG_Canvas(std::ostream& ostr) + : Canvas(), m_istr(0), m_ostr(&ostr), m_written(false) + { } + // Read a canvas from a PNG file. Uses the FILE_Cookie() interface to make a // FILE* corresponding to an std::istream (including std::istringstream, etc). void PNG_Canvas::read() @@ -75,7 +180,7 @@ namespace Dimension // so this REALLY shouldn't happen. throw Dimension_Error("Attempt to read canvas from PNG without an input" " stream."); - } + } // Make the C++/C I/O interface FILE_Cookie cookie(*m_istr); diff --git a/libdimensionxx/progress.cpp b/libdimensionxx/progress.cpp index d09ac9b..ca74571 100644 --- a/libdimensionxx/progress.cpp +++ b/libdimensionxx/progress.cpp @@ -22,13 +22,20 @@ namespace Dimension { + Persist_Base::~Persist_Base() + { } + Progress::Progress(dmnsn_progress* progress) : m_progress(new dmnsn_progress*(progress)) { } + Progress::Progress(dmnsn_progress* progress, const Persister& persister) + : m_progress(new dmnsn_progress*(progress)), m_persister(persister) + { } + Progress::~Progress() { - if (m_progress.unique()) { + if (m_progress && m_progress.unique()) { try { dmnsn_finish_progress(dmnsn()); } catch (...) { @@ -73,20 +80,36 @@ namespace Dimension { if (m_progress.unique()) { dmnsn_finish_progress(dmnsn()); + m_progress.reset(); } else { throw Dimension_Error("Attempt to finish non-unique Progress."); } } + // Access the set of persisted objects + Persister& + Progress::persister() + { + return m_persister; + } + dmnsn_progress* Progress::dmnsn() { + if (!m_progress) { + throw Dimension_Error("Attempting to access finished array."); + } + return *m_progress; } const dmnsn_progress* Progress::dmnsn() const { + if (!m_progress) { + throw Dimension_Error("Attempting to access finished array."); + } + return *m_progress; } } diff --git a/tests/pngxx.cpp b/tests/pngxx.cpp index 8dc79b2..2bb9fdb 100644 --- a/tests/pngxx.cpp +++ b/tests/pngxx.cpp @@ -20,6 +20,7 @@ #include "../libdimensionxx/dimensionxx.hpp" #include <fstream> +#include <iostream> int main() @@ -92,11 +93,41 @@ main() ocanvas.pixel(x + 2*width, y, color); } } + + Dimension::Progress progress = ocanvas.write_async(); + + // Display ellipsis progress bar + double prog = 0.0; + while ((prog += 1.0/10.0) <= 1.0) { + progress.wait(prog); + std::cout << "." << std::flush; + } + std::cout << std::endl; } std::ifstream ifstr("dimensionxx1.png", std::ios::binary); std::ofstream ofstr("dimensionxx2.png", std::ios::binary); - Dimension::PNG_Canvas iocanvas(ifstr, ofstr); + + Dimension::Progress iprogress + = Dimension::PNG_Canvas::read_async(ifstr); + + double iprog = 0.0; + while ((iprog += 1.0/10.0) <= 1.0) { + iprogress.wait(iprog); + std::cout << "." << std::flush; + } + std::cout << std::endl; + + Dimension::PNG_Canvas iocanvas(iprogress, ofstr); + + Dimension::Progress oprogress = iocanvas.write_async(); + + double oprog = 0.0; + while ((oprog += 1.0/10.0) <= 1.0) { + oprogress.wait(oprog); + std::cout << "." << std::flush; + } + std::cout << std::endl; return 0; } |