summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@gmail.com>2009-06-24 21:28:51 +0000
committerTavian Barnes <tavianator@gmail.com>2009-06-24 21:28:51 +0000
commit603712c38a127e297eddd23975fb950499a0c10c (patch)
tree1ec4a3f870f57ab72b0003399657654d91bf247b
parent706acbb46a91adef8ed2ec1d255802e93c59b65a (diff)
downloaddimension-603712c38a127e297eddd23975fb950499a0c10c.tar.xz
New asynchronous PNG_Canvas interface.
-rw-r--r--libdimensionxx/dimensionxx/png.hpp29
-rw-r--r--libdimensionxx/dimensionxx/progress.hpp73
-rw-r--r--libdimensionxx/png.cpp111
-rw-r--r--libdimensionxx/progress.cpp25
-rw-r--r--tests/pngxx.cpp33
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;
}