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. --- libdimension-png/dimension-png.h | 5 ++++ libdimension-png/png.c | 53 ++++++++++++++++++++++++++++----- libdimension/canvas.c | 32 ++++++++++++++++++-- libdimension/color.c | 20 +++++++++++++ libdimension/dimension.h | 4 +-- libdimension/dimension/canvas.h | 8 ++--- libdimension/dimension/color.h | 13 ++++---- libdimension/dimension/error.h | 10 +++++-- libdimension/dimension/geometry.h | 5 ++-- libdimension/error.c | 13 +++++++- libdimension/geometry.c | 8 +++++ 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 +++++++++++- 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 #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 #include +/* 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 /* Must be first included header */ -#include /* For malloc(), free() */ +#include +#include /* 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 -#include - #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 @@ * . * *************************************************************************/ -/* - * A canvas which is rendered to. - */ - #ifndef DIMENSION_CANVAS_H #define DIMENSION_CANVAS_H #include +/* + * 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 @@ * . * *************************************************************************/ +#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 +// 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