summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@gmail.com>2009-06-22 21:56:19 +0000
committerTavian Barnes <tavianator@gmail.com>2009-06-22 21:56:19 +0000
commitefe600537740bb572f4a062ab6b9df12623e0c24 (patch)
tree0f7903c7a53fb068f87dc1a62d676ba00eb7f65e
parent9a72562eca9eb76e11439fb892a7b41bfc2c0a3e (diff)
downloaddimension-efe600537740bb572f4a062ab6b9df12623e0c24.tar.xz
Finish asynchronous PNG interface, and test it in png test.
-rw-r--r--libdimension/dimension/progress.h11
-rw-r--r--libdimension/png.c124
-rw-r--r--libdimension/progress.c84
-rw-r--r--tests/png.c89
4 files changed, 254 insertions, 54 deletions
diff --git a/libdimension/dimension/progress.h b/libdimension/dimension/progress.h
index d436b5d..194ae83 100644
--- a/libdimension/dimension/progress.h
+++ b/libdimension/dimension/progress.h
@@ -36,6 +36,10 @@ typedef struct {
/* The worker thread */
pthread_t thread;
+
+ /* Condition variable for waiting for a particular amount of progress */
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
} dmnsn_progress;
dmnsn_progress *dmnsn_new_progress();
@@ -47,7 +51,10 @@ int dmnsn_finish_progress(dmnsn_progress *progress);
double dmnsn_get_progress(const dmnsn_progress* progress);
-void dmnsn_new_progress_element(dmnsn_progress* progress, unsigned int total);
-void dmnsn_increment_progress(dmnsn_progress* progress);
+void dmnsn_new_progress_element(dmnsn_progress *progress, unsigned int total);
+void dmnsn_increment_progress(dmnsn_progress *progress);
+void dmnsn_progress_done(dmnsn_progress *progress);
+
+void dmnsn_wait_progress(dmnsn_progress *progress, double prog);
#endif /* DIMENSION_PROGRESS_H */
diff --git a/libdimension/png.c b/libdimension/png.c
index 551bac8..b8cfabc 100644
--- a/libdimension/png.c
+++ b/libdimension/png.c
@@ -38,10 +38,7 @@ typedef struct {
} dmnsn_png_read_payload;
static void *dmnsn_png_write_canvas_thread(void *ptr);
-static int dmnsn_png_write_canvas_impl(const dmnsn_canvas *canvas, FILE *file);
-
static void *dmnsn_png_read_canvas_thread(void *ptr);
-static dmnsn_canvas *dmnsn_png_read_canvas_impl(FILE *file);
/* Write a canvas to a png file, using libpng. Return 0 on success, nonzero on
failure. */
@@ -75,6 +72,8 @@ dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, FILE *file)
!= 0) {
dmnsn_error(DMNSN_SEVERITY_MEDIUM,
"Creating png writing worker thread failed.");
+ dmnsn_delete_progress(progress);
+ return NULL;
}
}
@@ -114,25 +113,48 @@ dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, FILE *file)
!= 0) {
dmnsn_error(DMNSN_SEVERITY_MEDIUM,
"Creating png writing worker thread failed.");
+ dmnsn_delete_progress(progress);
+ return NULL;
}
}
return progress;
}
+static int dmnsn_png_write_canvas_impl(dmnsn_progress *progress,
+ const dmnsn_canvas *canvas, FILE *file);
+static dmnsn_canvas *dmnsn_png_read_canvas_impl(dmnsn_progress *progress,
+ FILE *file);
static void *
dmnsn_png_write_canvas_thread(void *ptr)
{
dmnsn_png_write_payload *payload = ptr;
int *retval = malloc(sizeof(int));
if (retval) {
- *retval = dmnsn_png_write_canvas_impl(payload->canvas, payload->file);
+ *retval = dmnsn_png_write_canvas_impl(payload->progress,
+ payload->canvas, payload->file);
+ }
+ dmnsn_progress_done(payload->progress);
+ return retval;
+}
+
+static void *
+dmnsn_png_read_canvas_thread(void *ptr)
+{
+ dmnsn_png_read_payload *payload = ptr;
+ int *retval = malloc(sizeof(int));
+ if (retval) {
+ *payload->canvas = dmnsn_png_read_canvas_impl(payload->progress,
+ payload->file);
+ *retval = payload->canvas ? 0 : 1;
}
+ dmnsn_progress_done(payload->progress);
return retval;
}
static int
-dmnsn_png_write_canvas_impl(const dmnsn_canvas *canvas, FILE *file)
+dmnsn_png_write_canvas_impl(dmnsn_progress *progress,
+ const dmnsn_canvas *canvas, FILE *file)
{
png_structp png_ptr;
png_infop info_ptr;
@@ -147,6 +169,8 @@ dmnsn_png_write_canvas_impl(const dmnsn_canvas *canvas, FILE *file)
width = canvas->x;
height = canvas->y;
+ dmnsn_new_progress_element(progress, height);
+
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) return 1; /* Couldn't create libpng write struct */
@@ -231,6 +255,7 @@ dmnsn_png_write_canvas_impl(const dmnsn_canvas *canvas, FILE *file)
/* Write the row */
png_write_row(png_ptr, (png_bytep)row);
+ dmnsn_increment_progress(progress);
}
/* Finish the PNG file */
@@ -241,27 +266,25 @@ dmnsn_png_write_canvas_impl(const dmnsn_canvas *canvas, FILE *file)
return 0;
}
-static void *
-dmnsn_png_read_canvas_thread(void *ptr)
-{
- dmnsn_png_read_payload *payload = ptr;
- int *retval = malloc(sizeof(int));
- if (retval) {
- *payload->canvas = dmnsn_png_read_canvas_impl(payload->file);
- *retval = payload->canvas ? 1 : 0;
- }
- return retval;
-}
+/* Thread-specific pointer to the appropriate dmnsn_progress* for
+ dmnsn_png_read_row_callback */
+static pthread_key_t progress_key;
+static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int progress_key_init = 0;
+
+static void dmnsn_png_read_row_callback(png_structp png_ptr, png_uint_32 row,
+ int pass);
static dmnsn_canvas *
-dmnsn_png_read_canvas_impl(FILE *file)
+dmnsn_png_read_canvas_impl(dmnsn_progress *progress, FILE *file)
{
dmnsn_canvas *canvas;
png_byte header[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
png_structp png_ptr;
png_infop info_ptr;
png_uint_32 width, height, rowbytes;
- int bit_depth, color_type, interlace_type, compression_type, filter_method;
+ int bit_depth, color_type, interlace_type, compression_type, filter_method,
+ number_of_passes;
png_bytep image = NULL;
png_bytep *row_pointers = NULL;
unsigned int x, y;
@@ -269,10 +292,38 @@ dmnsn_png_read_canvas_impl(FILE *file)
dmnsn_sRGB sRGB;
png_bytep png_pixel;
+ /* Initialize/set progress_key */
+
+ if (pthread_mutex_lock(&progress_mutex) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_MEDIUM,
+ "Couldn't lock thread-specific pointer mutex.");
+ }
+
+ if (progress_key_init == 0) {
+ if (pthread_key_create(&progress_key, NULL) != 0) {
+ /* High severity because dmnsn_png_read_row_callback will surely segfault
+ if it can't get the dmnsn_progress* from the key */
+ dmnsn_error(DMNSN_SEVERITY_HIGH,
+ "Couldn't create thread-specific pointer.");
+ }
+
+ progress_key_init = 1;
+ }
+
+ if (pthread_setspecific(progress_key, progress) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_HIGH, "Couldn't set thread-specific pointer.");
+ }
+
+ if (pthread_mutex_unlock(&progress_mutex) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_MEDIUM,
+ "Couldn't unlock thread-specific pointer mutex.");
+ }
+
if (!file) return NULL; /* file was NULL */
fread(header, 1, 8, file);
- if (png_sig_cmp(header, 0, 8)) return NULL; /* file is not a PNG file */
+ if (png_sig_cmp(header, 0, 8)) return NULL; /* file is not a PNG file, or the
+ read failed */
/* Create the libpng read struct */
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
@@ -305,6 +356,10 @@ dmnsn_png_read_canvas_impl(FILE *file)
/* 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);
+ number_of_passes = png_set_interlace_handling(png_ptr);
+
+ dmnsn_new_progress_element(progress, (number_of_passes + 1)*height);
+ png_set_read_status_fn(png_ptr, &dmnsn_png_read_row_callback);
/*
* - Convert paletted images to RGB.
@@ -350,8 +405,8 @@ dmnsn_png_read_canvas_impl(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. */
+ /* Read the image to memory 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 */
@@ -370,8 +425,8 @@ dmnsn_png_read_canvas_impl(FILE *file)
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) {
- for (y = 0; y < height; ++y) {
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
png_pixel = image + 8*(y*width + x);
sRGB.R = ((double)((png_pixel[0] << UINT16_C(8)) + png_pixel[1]))
@@ -386,10 +441,11 @@ dmnsn_png_read_canvas_impl(FILE *file)
+ png_pixel[7]))/UINT16_MAX;
dmnsn_set_pixel(canvas, x, height - y - 1, color);
}
+ dmnsn_increment_progress(progress);
}
} else {
- for (x = 0; x < width; ++x) {
- for (y = 0; y < height; ++y) {
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
png_pixel = image + 6*(y*width + x);
sRGB.R = ((double)((png_pixel[0] << UINT16_C(8)) + png_pixel[1]))
@@ -402,13 +458,14 @@ dmnsn_png_read_canvas_impl(FILE *file)
color = dmnsn_color_from_sRGB(sRGB);
dmnsn_set_pixel(canvas, x, height - y - 1, color);
}
+ dmnsn_increment_progress(progress);
}
}
} else {
/* Bit depth is 8 */
if (color_type & PNG_COLOR_MASK_ALPHA) {
- for (x = 0; x < width; ++x) {
- for (y = 0; y < height; ++y) {
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
png_pixel = image + 4*(y*width + x);
sRGB.R = ((double)png_pixel[0])/UINT8_MAX;
@@ -419,10 +476,11 @@ dmnsn_png_read_canvas_impl(FILE *file)
color.trans = ((double)png_pixel[3])/UINT8_MAX;
dmnsn_set_pixel(canvas, x, height - y - 1, color);
}
+ dmnsn_increment_progress(progress);
}
} else {
- for (x = 0; x < width; ++x) {
- for (y = 0; y < height; ++y) {
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
png_pixel = image + 3*(y*width + x);
sRGB.R = ((double)png_pixel[0])/UINT8_MAX;
@@ -432,6 +490,7 @@ dmnsn_png_read_canvas_impl(FILE *file)
color = dmnsn_color_from_sRGB(sRGB);
dmnsn_set_pixel(canvas, x, height - y - 1, color);
}
+ dmnsn_increment_progress(progress);
}
}
}
@@ -442,3 +501,10 @@ dmnsn_png_read_canvas_impl(FILE *file)
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return canvas;
}
+
+static void
+dmnsn_png_read_row_callback(png_structp png_ptr, png_uint_32 row, int pass)
+{
+ dmnsn_progress *progress = pthread_getspecific(progress_key);
+ dmnsn_increment_progress(progress);
+} \ No newline at end of file
diff --git a/libdimension/progress.c b/libdimension/progress.c
index e072f5d..aa6891c 100644
--- a/libdimension/progress.c
+++ b/libdimension/progress.c
@@ -25,9 +25,19 @@
dmnsn_progress *
dmnsn_new_progress()
{
+ dmnsn_progress_element element = { .progress = 0, .total = 1 };
dmnsn_progress *progress = malloc(sizeof(dmnsn_progress));
if (progress) {
progress->elements = dmnsn_new_array(sizeof(dmnsn_progress_element));
+ dmnsn_array_push(progress->elements, &element);
+
+ if (pthread_cond_init(&progress->cond, NULL) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_MEDIUM,
+ "Couldn't initialize condition variable.");
+ }
+ if (pthread_mutex_init(&progress->mutex, NULL) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't initialize mutex.");
+ }
}
return progress;
}
@@ -36,6 +46,13 @@ void
dmnsn_delete_progress(dmnsn_progress *progress)
{
if (progress) {
+ if (pthread_mutex_destroy(&progress->mutex) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_LOW, "Leaking mutex.");
+ }
+ if (pthread_cond_destroy(&progress->cond) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_LOW, "Leaking condition variable.");
+ }
+
dmnsn_delete_array(progress->elements);
free(progress);
}
@@ -47,6 +64,10 @@ int dmnsn_finish_progress(dmnsn_progress *progress)
int retval = 1;
if (progress) {
+ if (pthread_cond_broadcast(&progress->cond) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't signal condition variable.");
+ }
+
if (pthread_join(progress->thread, &ptr) != 0) {
/* Medium severity because an unjoined thread likely means that the thread
is incomplete or invalid */
@@ -62,7 +83,7 @@ int dmnsn_finish_progress(dmnsn_progress *progress)
}
double
-dmnsn_get_progress(const dmnsn_progress* progress)
+dmnsn_get_progress(const dmnsn_progress *progress)
{
dmnsn_progress_element *element;
double prog = 0.0;
@@ -81,23 +102,68 @@ dmnsn_get_progress(const dmnsn_progress* progress)
}
void
-dmnsn_new_progress_element(dmnsn_progress* progress, unsigned int total)
+dmnsn_new_progress_element(dmnsn_progress *progress, unsigned int total)
{
dmnsn_progress_element element = { .progress = 0, .total = total };
dmnsn_array_push(progress->elements, &element);
}
void
-dmnsn_increment_progress(dmnsn_progress* progress)
+dmnsn_increment_progress(dmnsn_progress *progress)
{
dmnsn_progress_element *element;
dmnsn_array_wrlock(progress->elements);
- element = dmnsn_array_at(progress->elements, progress->elements->length - 1);
- ++element->progress;
- if (element->progress >= element->total) {
- dmnsn_array_resize_unlocked(progress->elements,
- progress->elements->length - 1);
- }
+ element = dmnsn_array_at(progress->elements, progress->elements->length - 1);
+ ++element->progress;
+
+ while (element->progress >= element->total
+ && progress->elements->length > 1) {
+ dmnsn_array_resize_unlocked(progress->elements,
+ progress->elements->length - 1);
+ element = dmnsn_array_at(progress->elements, progress->elements->length - 1);
+ ++element->progress;
+ }
+
+ if (pthread_cond_broadcast(&progress->cond) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't signal condition variable.");
+ }
dmnsn_array_unlock(progress->elements);
}
+
+/* Immediately set to 100% completion */
+void
+dmnsn_progress_done(dmnsn_progress *progress)
+{
+ dmnsn_progress_element *element;
+
+ dmnsn_array_wrlock(progress->elements);
+ dmnsn_array_resize_unlocked(progress->elements, 1);
+ element = dmnsn_array_at(progress->elements, progress->elements->length - 1);
+ element->progress = element->total;
+
+ if (pthread_cond_broadcast(&progress->cond) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't signal condition variable.");
+ }
+ dmnsn_array_unlock(progress->elements);
+}
+
+/* Wait until dmnsn_get_progress(progress) >= prog */
+void
+dmnsn_wait_progress(dmnsn_progress *progress, double prog)
+{
+ if (pthread_mutex_lock(&progress->mutex) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't lock condition mutex.");
+ }
+
+ while (dmnsn_get_progress(progress) < prog) {
+ if (pthread_cond_wait(&progress->cond, &progress->mutex) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_MEDIUM,
+ "Couldn't wait on condition variable.");
+ }
+ }
+
+ if (pthread_mutex_unlock(&progress->mutex) != 0) {
+ dmnsn_error(DMNSN_SEVERITY_MEDIUM, "Couldn't unlock condition mutex.");
+ }
+}
diff --git a/tests/png.c b/tests/png.c
index f8c81b8..213e176 100644
--- a/tests/png.c
+++ b/tests/png.c
@@ -23,8 +23,11 @@
#include <stdio.h>
#include <stdint.h>
-int main() {
+int
+main() {
dmnsn_canvas *canvas;
+ dmnsn_progress *progress;
+ double prog;
dmnsn_color color;
dmnsn_CIE_xyY xyY;
dmnsn_CIE_Lab Lab;
@@ -42,12 +45,6 @@ int main() {
return EXIT_FAILURE;
}
- ofile = fopen("dimension1.png", "wb");
- if (!ofile) {
- fprintf(stderr, "--- Opening 'dimension1.png' for writing failed! ---\n");
- return EXIT_FAILURE;
- }
-
for (i = 0; i < x; ++i) {
for (j = 0; j < y; ++j) {
/*
@@ -106,37 +103,101 @@ int main() {
}
}
- if (dmnsn_png_write_canvas(canvas, ofile)) {
- fprintf(stderr, "--- Writing canvas failed! ---\n");
+ ofile = fopen("dimension1.png", "wb");
+ if (!ofile) {
+ fprintf(stderr, "--- Opening 'dimension1.png' for writing failed! ---\n");
+ dmnsn_delete_canvas(canvas);
+ return EXIT_FAILURE;
+ }
+
+ progress = dmnsn_png_write_canvas_async(canvas, ofile);
+ if (!progress) {
+ fprintf(stderr, "--- Creating PNG writing worker thread failed! ---\n");
+ fclose(ofile);
+ dmnsn_delete_canvas(canvas);
return EXIT_FAILURE;
}
- dmnsn_delete_canvas(canvas);
+ /* Give an ellipsis progress indication */
+ prog = 0.0;
+ while ((prog += 1.0/10.0) < 1.0) {
+ dmnsn_wait_progress(progress, prog);
+ printf(".");
+ fflush(stdout);
+ }
+ printf("\n");
+
+ if (dmnsn_finish_progress(progress) != 0) {
+ fprintf(stderr, "--- Writing canvas to PNG failed! ---\n");
+ fclose(ofile);
+ dmnsn_delete_canvas(canvas);
+ return EXIT_FAILURE;
+ }
fclose(ofile);
+ dmnsn_delete_canvas(canvas);
+
ifile = fopen("dimension1.png", "rb");
if (!ifile) {
fprintf(stderr, "--- Opening 'dimension1.png' for reading failed! ---\n");
return EXIT_FAILURE;
}
- if (!(canvas = dmnsn_png_read_canvas(ifile))) {
- fprintf(stderr, "--- Reading canvas failed! ---\n");
+ progress = dmnsn_png_read_canvas_async(&canvas, ifile);
+ if (!progress) {
+ fprintf(stderr, "--- Creating PNG reading worker thread failed! ---\n");
+ fclose(ifile);
+ return EXIT_FAILURE;
+ }
+
+ /* Give an ellipsis progress indication */
+ prog = 0.0;
+ while ((prog += 1.0/10.0) < 1.0) {
+ dmnsn_wait_progress(progress, prog);
+ printf(".");
+ fflush(stdout);
+ }
+ printf("\n");
+
+ if (dmnsn_finish_progress(progress) != 0) {
+ fprintf(stderr, "--- Reading canvas from PNG failed! ---\n");
+ fclose(ifile);
return EXIT_FAILURE;
}
fclose(ifile);
+
ofile = fopen("dimension2.png", "wb");
if (!ofile) {
fprintf(stderr, "--- Opening 'dimension2.png' for writing failed! ---\n");
return EXIT_FAILURE;
}
- if (dmnsn_png_write_canvas(canvas, ofile)) {
- fprintf(stderr, "--- Writing canvas failed! ---\n");
+ progress = dmnsn_png_write_canvas_async(canvas, ofile);
+ if (!progress) {
+ fprintf(stderr, "--- Creating PNG writing worker thread failed! ---\n");
+ fclose(ofile);
+ dmnsn_delete_canvas(canvas);
return EXIT_FAILURE;
}
+ /* Give an ellipsis progress indication */
+ prog = 0.0;
+ while ((prog += 1.0/10.0) < 1.0) {
+ dmnsn_wait_progress(progress, prog);
+ printf(".");
+ fflush(stdout);
+ }
+ printf("\n");
+
+ if (dmnsn_finish_progress(progress) != 0) {
+ fprintf(stderr, "--- Writing canvas to PNG failed! ---\n");
+ fclose(ofile);
+ dmnsn_delete_canvas(canvas);
+ return EXIT_FAILURE;
+ }
+
+ fclose(ofile);
dmnsn_delete_canvas(canvas);
return EXIT_SUCCESS;
}