summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libdimension-png/dimension-png.h5
-rw-r--r--libdimension-png/png.c53
-rw-r--r--libdimension/canvas.c32
-rw-r--r--libdimension/color.c20
-rw-r--r--libdimension/dimension.h4
-rw-r--r--libdimension/dimension/canvas.h8
-rw-r--r--libdimension/dimension/color.h13
-rw-r--r--libdimension/dimension/error.h10
-rw-r--r--libdimension/dimension/geometry.h5
-rw-r--r--libdimension/error.c13
-rw-r--r--libdimension/geometry.c8
-rw-r--r--libdimensionxx/canvas.cpp51
-rw-r--r--libdimensionxx/color.cpp1
-rw-r--r--libdimensionxx/cookie.cpp50
-rw-r--r--libdimensionxx/dimensionxx/canvas.hpp32
-rw-r--r--libdimensionxx/dimensionxx/color.hpp13
-rw-r--r--libdimensionxx/dimensionxx/cookie.hpp3
-rw-r--r--libdimensionxx/dimensionxx/error.hpp8
-rw-r--r--libdimensionxx/dimensionxx/geometry.hpp15
-rw-r--r--libdimensionxx/dimensionxx/png.hpp20
-rw-r--r--libdimensionxx/error.cpp3
-rw-r--r--libdimensionxx/png.cpp19
22 files changed, 330 insertions, 56 deletions
diff --git a/libdimension-png/dimension-png.h b/libdimension-png/dimension-png.h
index 46ce82d..9ee55c3 100644
--- a/libdimension-png/dimension-png.h
+++ b/libdimension-png/dimension-png.h
@@ -25,10 +25,15 @@
#include <stdio.h>
#ifdef __cplusplus
+/* We've been included from a C++ file; mark everything here as extern "C" */
extern "C" {
#endif
+/* Write canvas to file in PNG format. Returns 0 on success, nonzero on
+ failure */
int dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file);
+
+/* Read a canvas from a PNG file. Returns NULL on failure. */
dmnsn_canvas *dmnsn_png_read_canvas(FILE *file);
#ifdef __cplusplus
diff --git a/libdimension-png/png.c b/libdimension-png/png.c
index b639900..7e9036f 100644
--- a/libdimension-png/png.c
+++ b/libdimension-png/png.c
@@ -24,6 +24,8 @@
#include <arpa/inet.h>
#include <stdlib.h>
+/* Write a canvas to a png file, using libpng. Return 0 on success, nonzero on
+ failure. */
int
dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file)
{
@@ -35,54 +37,65 @@ dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file)
dmnsn_color color;
dmnsn_sRGB sRGB;
+ if (!file) return 1; /* file was NULL */
+
width = canvas->x;
height = canvas->y;
- if (!file) return 1;
-
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if (!png_ptr) return 1;
+ if (!png_ptr) return 1; /* Couldn't create libpng write struct */
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
+ /* Couldn't create libpng info struct */
png_destroy_write_struct(&png_ptr, NULL);
return 1;
}
+ /* libpng will longjmp here if it encounters an error from here on */
if (setjmp(png_jmpbuf(png_ptr))) {
+ /* libpng error */
if (row) free(row);
png_destroy_write_struct(&png_ptr, &info_ptr);
return 1;
}
+ /* Associate file with the libpng write struct */
png_init_io(png_ptr, file);
+ /* Set header correctly for 16-bit sRGB image */
png_set_IHDR(png_ptr, info_ptr, width, height, 16,
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
-
png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
+ /* We think of transparency in the opposite way that PNG does */
png_set_invert_alpha(png_ptr);
+
+ /* Write the info struct */
png_write_info(png_ptr, info_ptr);
if (htonl(1) != 1) {
- /* We are little-endian */
+ /* We are little-endian; swap the byte order of the pixels */
png_set_swap(png_ptr);
}
+ /* Allocate the temporary row of RGBA values */
row = malloc(4*sizeof(uint16_t)*width);
if (!row) {
png_destroy_write_struct(&png_ptr, &info_ptr);
return 1;
}
+ /* Write the pixels */
for (y = 0; y < height; ++y) {
for (x = 0; x < width; ++x) {
/* Invert the rows. PNG coordinates are fourth quadrant. */
color = dmnsn_get_pixel(canvas, x, height - y - 1);
sRGB = dmnsn_sRGB_from_color(color);
+ /* Saturate R, G, and B to [0, UINT16_MAX] */
+
if (sRGB.R <= 0.0) {
row[4*x] = 0;
} else if (sRGB.R >= 1.0) {
@@ -107,11 +120,15 @@ dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file)
row[4*x + 2] = sRGB.B*UINT16_MAX;
}
+ /* color.filter + color.trans is in [0.0, 1.0] by definition */
row[4*x + 3] = (color.filter + color.trans)*UINT16_MAX;
}
+
+ /* Write the row */
png_write_row(png_ptr, (png_bytep)row);
}
+ /* Finish the PNG file */
png_write_end(png_ptr, info_ptr);
free(row);
@@ -119,6 +136,7 @@ dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file)
return 0;
}
+/* Read a canvas from the PNG file `file'. Return NULL on error. */
dmnsn_canvas *
dmnsn_png_read_canvas(FILE *file)
{
@@ -135,32 +153,40 @@ dmnsn_png_read_canvas(FILE *file)
dmnsn_sRGB sRGB;
png_bytep png_pixel;
- if (!file) return NULL;
+ if (!file) return NULL; /* file was NULL */
fread(header, 1, 8, file);
- if (png_sig_cmp(header, 0, 8)) return NULL;
+ if (png_sig_cmp(header, 0, 8)) return NULL; /* file is not a PNG file */
+ /* Create the libpng read struct */
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) return NULL;
+ /* Create the libpng info struct */
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_read_struct(&png_ptr, NULL, NULL);
return NULL;
}
+ /* libpng will longjmp here if it encounters an error from here on */
if (setjmp(png_jmpbuf(png_ptr))) {
+ /* libpng error */
if (row_pointers) free(row_pointers);
if (image) free(image);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return NULL;
}
+ /* Associate the read struct with the file, and tell it we've already checked
+ 8 bytes of signature */
png_init_io(png_ptr, file);
png_set_sig_bytes(png_ptr, 8);
+ /* Read the PNG header into info struct */
png_read_info(png_ptr, info_ptr);
+ /* Get useful information from the info struct */
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
&interlace_type, &compression_type, &filter_method);
@@ -182,15 +208,21 @@ dmnsn_png_read_canvas(FILE *file)
}
png_set_invert_alpha(png_ptr);
+ /* Update the info struct */
png_read_update_info(png_ptr, info_ptr);
+
+ /* Get bytes/image row */
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+ /* Allocate the temporary image buffer */
image = malloc(rowbytes*height);
if (!image) {
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return NULL;
}
+ /* Allocate and set an array of pointers to rows in image */
+
row_pointers = malloc(sizeof(png_bytep)*height);
if (!row_pointers) {
free(image);
@@ -202,8 +234,11 @@ dmnsn_png_read_canvas(FILE *file)
row_pointers[y] = image + y*rowbytes;
}
+ /* Read the image to image all at once. At the expense of greater memory use,
+ this handles interlacing for us. */
png_read_image(png_ptr, row_pointers);
+ /* Allocate the canvas */
canvas = dmnsn_new_canvas(width, height);
if (!canvas) {
free(row_pointers);
@@ -213,6 +248,10 @@ dmnsn_png_read_canvas(FILE *file)
return NULL;
}
+ /* Now we convert the image to our canvas format. This depends on the image
+ bit depth (which has been scaled up to at least 8 or 16), and the presence
+ of an alpha channel. For performance reasons, the tests are outside the
+ loops, although that doesn't really matter for a decent compiler. */
if (bit_depth == 16) {
if (color_type & PNG_COLOR_MASK_ALPHA) {
for (x = 0; x < width; ++x) {
diff --git a/libdimension/canvas.c b/libdimension/canvas.c
index 49096fc..516c978 100644
--- a/libdimension/canvas.c
+++ b/libdimension/canvas.c
@@ -19,24 +19,33 @@
*************************************************************************/
#include "dimension.h"
-#include <pthread.h> /* Must be first included header */
-#include <stdlib.h> /* For malloc(), free() */
+#include <pthread.h>
+#include <stdlib.h> /* For malloc(), free() */
+/* Allocate a new canvas, of width x and height y. If any intermediary step
+ fails, free all acquired memory to avoid leaks. */
dmnsn_canvas *
dmnsn_new_canvas(unsigned int x, unsigned int y)
{
unsigned int i, j, k, l;
+ /* Allocate the dmnsn_canvas struct */
dmnsn_canvas *canvas = malloc(sizeof(dmnsn_canvas));
if (canvas) {
+ /* *canvas exists */
+
+ /* Set the width and height */
canvas->x = x;
canvas->y = y;
+
+ /* Allocate the pixels */
canvas->pixels = malloc(sizeof(dmnsn_color)*x*y);
if (!canvas->pixels) {
free(canvas);
return NULL;
}
+ /* Allocate the rwlocks */
canvas->rwlocks = malloc(sizeof(pthread_rwlock_t)*x*y);
if (!canvas->rwlocks) {
free(canvas->pixels);
@@ -44,6 +53,7 @@ dmnsn_new_canvas(unsigned int x, unsigned int y)
return NULL;
}
+ /* Initialize the rwlocks */
for (i = 0; i < x; ++i) {
for (j = 0; j < y; ++j) {
if (pthread_rwlock_init(&canvas->rwlocks[j*x + i], NULL) != 0) {
@@ -53,7 +63,7 @@ dmnsn_new_canvas(unsigned int x, unsigned int y)
for (l = 0; l < j; ++l) {
for (k = 0; k < x; ++k) {
if (pthread_rwlock_destroy(&canvas->rwlocks[l*x + k]) != 0) {
- /* Low severity, because leaked memory won't actually hurt us */
+ /* Low severity, because leaked locks won't actually hurt us */
dmnsn_error(DMNSN_SEVERITY_LOW,
"Leaking rwlocks in failed allocation.");
}
@@ -79,12 +89,16 @@ dmnsn_new_canvas(unsigned int x, unsigned int y)
return canvas;
}
+/* Delete a dmnsn_canvas allocated with dmnsn_new_canvas */
void
dmnsn_delete_canvas(dmnsn_canvas *canvas)
{
unsigned int i, j;
if (canvas) {
+ /* *canvas exists */
+
+ /* Destroy the rwlocks */
for (i = 0; i < canvas->x; ++i) {
for (j = 0; j < canvas->y; ++j) {
if (pthread_rwlock_destroy(&canvas->rwlocks[j*canvas->x + i]) != 0) {
@@ -94,12 +108,14 @@ dmnsn_delete_canvas(dmnsn_canvas *canvas)
}
}
+ /* Free the locks, pixels, and canvas */
free(canvas->rwlocks);
free(canvas->pixels);
free(canvas);
}
}
+/* Get a pixel at (x,y) thread-safely, using dmnsn_rdlock_pixel. */
dmnsn_color
dmnsn_get_pixel(const dmnsn_canvas *canvas, unsigned int x, unsigned int y)
{
@@ -110,6 +126,7 @@ dmnsn_get_pixel(const dmnsn_canvas *canvas, unsigned int x, unsigned int y)
return color;
}
+/* Set a pixel at (x,y) thread-safely, using dmnsn_wrlock_pixel. */
void
dmnsn_set_pixel(dmnsn_canvas *canvas,
unsigned int x, unsigned int y, dmnsn_color color)
@@ -119,28 +136,37 @@ dmnsn_set_pixel(dmnsn_canvas *canvas,
dmnsn_unlock_pixel(canvas, x, y);
}
+/* Acquire a read-lock for a pixel */
void
dmnsn_rdlock_pixel(const dmnsn_canvas *canvas, unsigned int x, unsigned int y)
{
if (pthread_rwlock_rdlock(&canvas->rwlocks[y*canvas->x + x]) != 0) {
+ /* Medium severity, because undefined behaviour is pretty likely if our
+ reads and writes aren't synced */
dmnsn_error(DMNSN_SEVERITY_MEDIUM,
"Couldn't acquire read-lock for pixel.");
}
}
+/* Acquire a write-lock for a pixel */
void
dmnsn_wrlock_pixel(dmnsn_canvas *canvas, unsigned int x, unsigned int y)
{
if (pthread_rwlock_wrlock(&canvas->rwlocks[y*canvas->x + x]) != 0) {
+ /* Medium severity, because undefined behaviour is pretty likely if our
+ reads and writes aren't synced */
dmnsn_error(DMNSN_SEVERITY_MEDIUM,
"Couldn't acquire write-lock for pixel.");
}
}
+/* Unlock a pixel */
void
dmnsn_unlock_pixel(const dmnsn_canvas *canvas, unsigned int x, unsigned int y)
{
if (pthread_rwlock_unlock(&canvas->rwlocks[y*canvas->x + x]) != 0) {
+ /* Medium severity, because if the pixel is locked, we're likely to hang
+ the next time we try to read or write it */
dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't unlock pixel.");
}
}
diff --git a/libdimension/color.c b/libdimension/color.c
index e2a4df9..5a685c1 100644
--- a/libdimension/color.c
+++ b/libdimension/color.c
@@ -26,6 +26,7 @@ const dmnsn_CIE_XYZ dmnsn_whitepoint = { .X = 0.9504060171449392,
.Y = 0.9999085943425312,
.Z = 1.089062231497274 };
+/* Convert a CIE XYZ color to a dmnsn_color (actually a no-op) */
dmnsn_color
dmnsn_color_from_XYZ(dmnsn_CIE_XYZ XYZ)
{
@@ -34,6 +35,7 @@ dmnsn_color_from_XYZ(dmnsn_CIE_XYZ XYZ)
return ret;
}
+/* Convert a CIE xyY color to a dmnsn_color */
dmnsn_color
dmnsn_color_from_xyY(dmnsn_CIE_xyY xyY)
{
@@ -44,6 +46,7 @@ dmnsn_color_from_xyY(dmnsn_CIE_xyY xyY)
return ret;
}
+/* Inverse function of CIE L*a*b*'s `f' function, for the reverse conversion */
static double dmnsn_Lab_finv(double t) {
if (t > 6.0/29.0) {
return t*t*t;
@@ -52,6 +55,8 @@ static double dmnsn_Lab_finv(double t) {
}
}
+/* Convert a CIE L*a*b* color to a dmnsn_color, relative to the given
+ whitepoint. */
dmnsn_color
dmnsn_color_from_Lab(dmnsn_CIE_Lab Lab, dmnsn_CIE_XYZ white)
{
@@ -71,6 +76,8 @@ dmnsn_color_from_Lab(dmnsn_CIE_Lab Lab, dmnsn_CIE_XYZ white)
return ret;
}
+/* Convert a CIE L*u*v* color to a dmnsn_color, relative to the given
+ whitepoint. */
dmnsn_color
dmnsn_color_from_Luv(dmnsn_CIE_Luv Luv, dmnsn_CIE_XYZ white)
{
@@ -94,6 +101,7 @@ dmnsn_color_from_Luv(dmnsn_CIE_Luv Luv, dmnsn_CIE_XYZ white)
return ret;
}
+/* Inverse function of sRGB's `C' function, for the reverse conversion */
static double dmnsn_sRGB_Cinv(double CsRGB) {
/*
* If C represents R, G, and B, then the Clinear values are now found as
@@ -110,6 +118,7 @@ static double dmnsn_sRGB_Cinv(double CsRGB) {
}
}
+/* Convert an sRGB value to a dmnsn_color */
dmnsn_color
dmnsn_color_from_sRGB(dmnsn_sRGB sRGB)
{
@@ -140,6 +149,7 @@ dmnsn_color_from_sRGB(dmnsn_sRGB sRGB)
return ret;
}
+/* Convert a dmnsn_color to a CIE XYZ color (actually a no-op) */
dmnsn_CIE_XYZ
dmnsn_XYZ_from_color(dmnsn_color color)
{
@@ -147,6 +157,7 @@ dmnsn_XYZ_from_color(dmnsn_color color)
return ret;
}
+/* Convert a dmnsn_color to a CIE xyY color */
dmnsn_CIE_xyY
dmnsn_xyY_from_color(dmnsn_color color)
{
@@ -156,6 +167,7 @@ dmnsn_xyY_from_color(dmnsn_color color)
return ret;
}
+/* CIE L*a*b*'s `f' function */
static double dmnsn_Lab_f(double t) {
if (t > 216.0/24389.0) {
return pow(t, 1.0/3.0);
@@ -164,6 +176,8 @@ static double dmnsn_Lab_f(double t) {
}
}
+/* Convert a dmnsn_color to a CIE L*a*b* color, relative to the given
+ whitepoint */
dmnsn_CIE_Lab
dmnsn_Lab_from_color(dmnsn_color color, dmnsn_CIE_XYZ white)
{
@@ -176,6 +190,8 @@ dmnsn_Lab_from_color(dmnsn_color color, dmnsn_CIE_XYZ white)
return ret;
}
+/* Convert a dmnsn_color to a CIE L*u*v* color, relative to the given
+ whitepoint */
dmnsn_CIE_Luv
dmnsn_Luv_from_color(dmnsn_color color, dmnsn_CIE_XYZ white)
{
@@ -194,6 +210,7 @@ dmnsn_Luv_from_color(dmnsn_color color, dmnsn_CIE_XYZ white)
return ret;
}
+/* sRGB's `C' function */
static double dmnsn_sRGB_C(double Clinear) {
/*
* If C represents R, G, and B, then the sRGB values are now found as follows:
@@ -209,6 +226,7 @@ static double dmnsn_sRGB_C(double Clinear) {
}
}
+/* Convert a dmnsn_color to an sRGB color */
dmnsn_sRGB
dmnsn_sRGB_from_color(dmnsn_color color)
{
@@ -234,6 +252,7 @@ dmnsn_sRGB_from_color(dmnsn_color color)
return ret;
}
+/* Add two colors in a perceptually correct manner, using CIE L*a*b*. */
dmnsn_color
dmnsn_color_add(dmnsn_color color1, dmnsn_color color2)
{
@@ -254,6 +273,7 @@ dmnsn_color_add(dmnsn_color color1, dmnsn_color color2)
return ret;
}
+/* Find the perceptual difference between two colors, using CIE L*a*b*. */
double
dmnsn_color_difference(dmnsn_color color1, dmnsn_color color2)
{
diff --git a/libdimension/dimension.h b/libdimension/dimension.h
index 97f07a5..735e1a0 100644
--- a/libdimension/dimension.h
+++ b/libdimension/dimension.h
@@ -21,10 +21,8 @@
#ifndef DIMENSION_H
#define DIMENSION_H
-#include <stdbool.h>
-#include <stdint.h>
-
#ifdef __cplusplus
+/* We've been included from a C++ file; mark everything here as extern "C" */
extern "C" {
#endif
diff --git a/libdimension/dimension/canvas.h b/libdimension/dimension/canvas.h
index 4449403..f05264b 100644
--- a/libdimension/dimension/canvas.h
+++ b/libdimension/dimension/canvas.h
@@ -18,15 +18,14 @@
* <http://www.gnu.org/licenses/>. *
*************************************************************************/
-/*
- * A canvas which is rendered to.
- */
-
#ifndef DIMENSION_CANVAS_H
#define DIMENSION_CANVAS_H
#include <pthread.h>
+/*
+ * A canvas which is rendered to.
+ */
typedef struct {
unsigned int x, y;
@@ -40,6 +39,7 @@ typedef struct {
pthread_rwlock_t *rwlocks;
} dmnsn_canvas;
+/* Allocate and free a canvas */
dmnsn_canvas *dmnsn_new_canvas(unsigned int x, unsigned int y);
void dmnsn_delete_canvas(dmnsn_canvas *canvas);
diff --git a/libdimension/dimension/color.h b/libdimension/dimension/color.h
index 0e64875..f1964c3 100644
--- a/libdimension/dimension/color.h
+++ b/libdimension/dimension/color.h
@@ -25,10 +25,6 @@
#ifndef DIMENSION_COLOR_H
#define DIMENSION_COLOR_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/* Internally, we use CIE 1931 XYZ color. */
typedef struct {
double X, Y, Z;
@@ -67,6 +63,8 @@ typedef struct {
/* Standard whitepoint, determined by the conversion of sRGB white to XYZ */
extern const dmnsn_CIE_XYZ dmnsn_whitepoint;
+/* Color conversions */
+
dmnsn_color dmnsn_color_from_XYZ(dmnsn_CIE_XYZ XYZ);
dmnsn_color dmnsn_color_from_xyY(dmnsn_CIE_xyY xyY);
dmnsn_color dmnsn_color_from_Lab(dmnsn_CIE_Lab Lab, dmnsn_CIE_XYZ white);
@@ -79,11 +77,10 @@ dmnsn_CIE_Lab dmnsn_Lab_from_color(dmnsn_color color, dmnsn_CIE_XYZ white);
dmnsn_CIE_Luv dmnsn_Luv_from_color(dmnsn_color color, dmnsn_CIE_XYZ white);
dmnsn_sRGB dmnsn_sRGB_from_color(dmnsn_color color);
+/* Perceptually correct color combination */
dmnsn_color dmnsn_color_add(dmnsn_color color1, dmnsn_color color2);
-double dmnsn_color_difference(dmnsn_color color1, dmnsn_color color2);
-#ifdef __cplusplus
-}
-#endif
+/* Perceptual color difference */
+double dmnsn_color_difference(dmnsn_color color1, dmnsn_color color2);
#endif /* DIMENSION_COLOR_H */
diff --git a/libdimension/dimension/error.h b/libdimension/dimension/error.h
index 2c00a4a..858ff96 100644
--- a/libdimension/dimension/error.h
+++ b/libdimension/dimension/error.h
@@ -18,24 +18,28 @@
* <http://www.gnu.org/licenses/>. *
*************************************************************************/
+#ifndef DIMENSION_ERROR_H
+#define DIMENSION_ERROR_H
+
/*
* Error handling.
*/
-#ifndef DIMENSION_ERROR_H
-#define DIMENSION_ERROR_H
-
typedef enum {
DMNSN_SEVERITY_LOW, /* Only die on low resilience */
DMNSN_SEVERITY_MEDIUM, /* Die on low or medium resilience */
DMNSN_SEVERITY_HIGH /* Always die */
} dmnsn_severity;
+/* Use this to report an error */
#define dmnsn_error(severity, str) \
dmnsn_report_error(severity, __PRETTY_FUNCTION__, __LINE__, str)
+/* Called by dmnsn_error() - don't call directly */
void dmnsn_report_error(dmnsn_severity severity,
const char *func, unsigned int line, const char *str);
+
+/* Get and set the library resilience, thread-safely */
dmnsn_severity dmnsn_get_resilience();
void dmnsn_set_resilience(dmnsn_severity resilience);
diff --git a/libdimension/dimension/geometry.h b/libdimension/dimension/geometry.h
index 26705ca..e1d1497 100644
--- a/libdimension/dimension/geometry.h
+++ b/libdimension/dimension/geometry.h
@@ -29,12 +29,13 @@
typedef double dmnsn_scalar;
typedef struct { dmnsn_scalar x, y, z; } dmnsn_vector;
-/* Vector arithmetic */
-
+/* Shorthand for vector construction */
dmnsn_vector dmnsn_vector_construct(dmnsn_scalar x,
dmnsn_scalar y,
dmnsn_scalar z);
+/* Vector arithmetic */
+
dmnsn_vector dmnsn_vector_add(dmnsn_vector lhs, dmnsn_vector rhs);
dmnsn_vector dmnsn_vector_sub(dmnsn_vector lhs, dmnsn_vector rhs);
dmnsn_vector dmnsn_vector_mul(dmnsn_scalar lhs, dmnsn_vector rhs);
diff --git a/libdimension/error.c b/libdimension/error.c
index 728652b..38ba40b 100644
--- a/libdimension/error.c
+++ b/libdimension/error.c
@@ -26,29 +26,36 @@
static dmnsn_severity dmnsn_resilience = DMNSN_SEVERITY_MEDIUM;
static pthread_mutex_t dmnsn_resilience_mutex = PTHREAD_MUTEX_INITIALIZER;
+// Called by dmnsn_error macro (don't call directly).
void
dmnsn_report_error(dmnsn_severity severity, const char *func, unsigned int line,
const char *str)
{
if (severity >= dmnsn_get_resilience()) {
+ // An error more severe than our resilience happened, bail out
fprintf(stderr, "Dimension ERROR: %s, line %u: %s\n", func, line, str);
exit(EXIT_FAILURE);
} else {
+ // A trivial error happened, warn and continue
fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n", func, line, str);
}
}
+/* Return the current resilience, thread-safely. */
dmnsn_severity
dmnsn_get_resilience()
{
dmnsn_severity resilience;
if (pthread_mutex_lock(&dmnsn_resilience_mutex) != 0) {
+ // Couldn't lock the mutex, so warn and continue.
fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n",
__PRETTY_FUNCTION__, __LINE__,
"Couldn't lock resilience mutex.");
}
- resilience = dmnsn_resilience;
+ resilience = dmnsn_resilience; // Copy the static variable to a local
if (pthread_mutex_unlock(&dmnsn_resilience_mutex) != 0) {
+ // Couldn't unlock the mutex, so warn and continue. If the mutex was locked
+ // earlier, the next dmnsn_get/set_resilience is likely to hang.
fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n",
__PRETTY_FUNCTION__, __LINE__,
"Couldn't unlock resilience mutex.");
@@ -60,6 +67,7 @@ void
dmnsn_set_resilience(dmnsn_severity resilience)
{
if (resilience > DMNSN_SEVERITY_HIGH) {
+ // Tried to set an illegal resilience, bail out
fprintf(stderr, "Dimension ERROR: %s, line %u: %s\n",
__PRETTY_FUNCTION__, __LINE__,
"Resilience has wrong value.");
@@ -67,12 +75,15 @@ dmnsn_set_resilience(dmnsn_severity resilience)
}
if (pthread_mutex_lock(&dmnsn_resilience_mutex) != 0) {
+ // Couldn't lock the mutex, so warn and continue.
fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n",
__PRETTY_FUNCTION__, __LINE__,
"Couldn't lock resilience mutex.");
}
dmnsn_resilience = resilience;
if (pthread_mutex_unlock(&dmnsn_resilience_mutex) != 0) {
+ // Couldn't unlock the mutex, so warn and continue. If the mutex was locked
+ // earlier, the next dmnsn_get/set_resilience is likely to hang.
fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n",
__PRETTY_FUNCTION__, __LINE__,
"Couldn't unlock resilience mutex.");
diff --git a/libdimension/geometry.c b/libdimension/geometry.c
index 5786f57..0eab10b 100644
--- a/libdimension/geometry.c
+++ b/libdimension/geometry.c
@@ -20,6 +20,7 @@
#include "dimension.h"
+/* Construct a vector from x, y, and z. Just for convienence. */
dmnsn_vector
dmnsn_vector_construct(dmnsn_scalar x, dmnsn_scalar y, dmnsn_scalar z)
{
@@ -27,6 +28,7 @@ dmnsn_vector_construct(dmnsn_scalar x, dmnsn_scalar y, dmnsn_scalar z)
return v;
}
+/* Add two vectors */
dmnsn_vector
dmnsn_vector_add(dmnsn_vector lhs, dmnsn_vector rhs)
{
@@ -36,6 +38,7 @@ dmnsn_vector_add(dmnsn_vector lhs, dmnsn_vector rhs)
return v;
}
+/* Subtract two vectors */
dmnsn_vector
dmnsn_vector_sub(dmnsn_vector lhs, dmnsn_vector rhs)
{
@@ -45,6 +48,7 @@ dmnsn_vector_sub(dmnsn_vector lhs, dmnsn_vector rhs)
return v;
}
+/* Multiply a vector by a scalar */
dmnsn_vector
dmnsn_vector_mul(dmnsn_scalar lhs, dmnsn_vector rhs)
{
@@ -52,6 +56,7 @@ dmnsn_vector_mul(dmnsn_scalar lhs, dmnsn_vector rhs)
return v;
}
+/* Divide a vector by a scalar */
dmnsn_vector
dmnsn_vector_div(dmnsn_vector lhs, dmnsn_scalar rhs)
{
@@ -59,12 +64,14 @@ dmnsn_vector_div(dmnsn_vector lhs, dmnsn_scalar rhs)
return v;
}
+/* Dot product */
dmnsn_scalar
dmnsn_vector_dot(dmnsn_vector lhs, dmnsn_vector rhs)
{
return lhs.x*rhs.x + lhs.y*rhs.y + lhs.z*rhs.z;
}
+/* Cross product */
dmnsn_vector
dmnsn_vector_cross(dmnsn_vector lhs, dmnsn_vector rhs)
{
@@ -74,6 +81,7 @@ dmnsn_vector_cross(dmnsn_vector lhs, dmnsn_vector rhs)
return v;
}
+/* A point on a line, l. Returns l.x0 + t*l.n */
dmnsn_vector
dmnsn_line_point(dmnsn_line l, dmnsn_scalar t)
{
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 <stdio.h>
+// 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*>(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*>(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*>(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*>(cookie);
}
}
+ // Make an input FILE*
std::FILE*
fcookie(std::istream& istr)
{
@@ -129,6 +153,7 @@ namespace Dimension
return fopencookie(reinterpret_cast<void*>(cookie), "r", io_funcs);
}
+ // Make an output FILE*
std::FILE*
fcookie(std::ostream& ostr)
{
@@ -145,6 +170,7 @@ namespace Dimension
return fopencookie(reinterpret_cast<void*>(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 <istream>
#include <ostream>
#include <cstdio>
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 <dimension.h>
#include <stdexcept>
#include <string>
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 <dimension.h>
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 <istream>
#include <ostream>
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<Severity>(dmnsn_get_resilience());
}
+ // Set the resilience, thread-safely, with dmnsn_set_resilience().
void
resilience(Severity resilience)
{
dmnsn_set_resilience(static_cast<dmnsn_severity>(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.");
}