a half-working render_slices. Render_multi is currently semantically broken/buggy, but it's time to go to sleep
authorTero Marttila <terom@fixme.fi>
Mon, 09 Jun 2008 03:15:34 +0300
changeset 17 8e8b56b0e0f5
parent 16 50995bbe442a
child 18 86f2e5b7191b
a half-working render_slices. Render_multi is currently semantically broken/buggy, but it's time to go to sleep

committer: Tero Marttila <terom@fixme.fi>
Makefile
render_internal.h
render_local.c
render_multi.c
render_png.c
render_png.h
render_png_struct.h
render_raw.c
render_raw.h
render_raw_struct.h
render_slices.c
render_slices.h
render_slices_struct.h
render_struct.h
--- a/Makefile	Sun Jun 08 23:10:36 2008 +0300
+++ b/Makefile	Mon Jun 09 03:15:34 2008 +0300
@@ -16,6 +16,7 @@
 render_local.o: render_local.c render_local.h
 render_multi.o: render_multi.c render_multi.h render_remote.h
 render_mandelbrot.o: render_mandelbrot.c render_mandelbrot.h
+render_slices.o: render_slices.c render_slices.h
 
 file_main.o: file_main.c
 node_main.o: node_main.c
@@ -23,7 +24,7 @@
 
 file_main: file_main.o common.o render.o render_raw.o render_png.o render_local.o render_mandelbrot.o
 node_main: node_main.o common.o render.o render_raw.o render_png.o render_local.o render_mandelbrot.o
-web_main: web_main.o common.o http.o render.o render_png.o remote_node.o remote_pool.o render_remote.o render_multi.o
+web_main: web_main.o common.o http.o render.o render_png.o remote_node.o remote_pool.o render_remote.o render_multi.o render_slices.o
 
 clean :
 	rm *.o ${EXECS}
--- a/render_internal.h	Sun Jun 08 23:10:36 2008 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-#ifdef RENDER_H
-#error render_internal.h must be included before render.h
-#endif
-
-#ifndef RENDER_INTERNAL_H
-#define RENDER_INTERNAL_H
-
-#include "render.h"
-
-struct render {
-/* render mode */
-    int mode;
-
-/* image size */
-    u_int32_t img_w;
-    u_int32_t img_h;
-
-/* mandelbrot region */
-    double      x1;
-    double      y1;
-    double      x2;
-    double      y2;
-
-/* local rendering */
-    unsigned char **local_rowbuf_addr;
-    render_ctx_row_cb local_row_fn;
-
-/* local I/O parameters */
-    // if this is non-NULL, the image data is simply fwrite():en to this stream
-    FILE *io_stream;
-
-    // called to handle the output data
-    render_ctx_write_cb io_write_fn;
-
-    // called when the output data should be flushed - can be safely ignored if not needed
-    render_ctx_flush_cb io_flush_fn;
-
-    // the callback argument for all of the above *_fn
-    void *cb_arg;
-};
-
-#endif /* RENDER_INTERNAL_H */
-
--- a/render_local.c	Sun Jun 08 23:10:36 2008 +0300
+++ b/render_local.c	Mon Jun 09 03:15:34 2008 +0300
@@ -3,21 +3,27 @@
 #include <assert.h>
 
 #include "common.h"
-#include "render_internal.h"
+#include "render_struct.h"
 #include "render_local.h"
+#include "render_png_struct.h"
 #include "render_png.h"
+#include "render_raw_struct.h"
 #include "render_raw.h"
 #include "render_mandelbrot.h"
 
 int render_local (struct render *render, double *duration) {
     unsigned char *rowbuf = NULL;
-    struct render_png *png_ctx = NULL;
-    struct render_raw *raw_ctx = NULL;
+    struct render_png png_ctx;
+    struct render_raw raw_ctx;
     clock_t t1, t2;
     
     if (duration)
         *duration = -1;
 
+    // ugh
+    memset(&png_ctx, 0, sizeof(png_ctx));
+    memset(&raw_ctx, 0, sizeof(raw_ctx));
+
     // alloc the memory buffer
     if (!(rowbuf = malloc(render->img_w)))
         ERROR("malloc");
@@ -26,22 +32,22 @@
     switch (render->mode) {
         case RENDER_PNG :
             // the render_png stuff
-            if (!(png_ctx = render_png_init(render)))
+            if (render_png_init(&png_ctx, render))
                 ERROR("render_png_init");
                 
             //  set render_* to use the render_png
-            if (render_local_mem(render, &rowbuf, (int(*)(void *arg, unsigned char *)) &render_png_row, png_ctx))
+            if (render_local_mem(render, &rowbuf, (int(*)(void *arg, unsigned char *)) &render_png_row, &png_ctx))
                 ERROR("render_local_mem");
 
             break;
 
         case RENDER_RAW :
             // the render_raw stuff
-            if (!(raw_ctx = render_raw_init(render)))
+            if (render_raw_init(&raw_ctx, render))
                 ERROR("render_raw_init");
 
             //  set render_* to use the render_raw
-            if (render_local_mem(render, &rowbuf, (int(*)(void *arg, unsigned char *)) &render_raw_row, raw_ctx))
+            if (render_local_mem(render, &rowbuf, (int(*)(void *arg, unsigned char *)) &render_raw_row, &raw_ctx))
                 ERROR("render_local_mem");
 
             break;
@@ -68,11 +74,8 @@
 error:
     free(rowbuf);
 
-    if (png_ctx)
-        render_png_abort(png_ctx);
-
-    if (raw_ctx)
-        render_raw_abort(raw_ctx);
+    render_png_deinit(&png_ctx);
+    render_raw_deinit(&raw_ctx);
 
     return -1;
 }
--- a/render_multi.c	Sun Jun 08 23:10:36 2008 +0300
+++ b/render_multi.c	Mon Jun 09 03:15:34 2008 +0300
@@ -4,11 +4,13 @@
 #include <assert.h>
 
 #include "common.h"
-#include "render_internal.h"
+#include "render_struct.h"
 #include "render_multi.h"
 #include "render_remote.h"
 #include "remote_node.h"
-#include "render_png.h"
+#include "remote_pool.h"
+#include "render_slices_struct.h"
+#include "render_slices.h"
 
 /*
 // the states we can go through
@@ -21,8 +23,8 @@
     STATE_FAILED,               // we failed
 };
 
-enum render_multi_sub_state {
-    STATE_INIT,                 // the render_multi_sub is in the process of being initialized
+enum render_multi_node_state {
+    STATE_INIT,                 // the render_multi_node is in the process of being initialized
     STATE_SENDING,              // we're waiting for the requests to be sent
     STATE_FILL_ROW,             // we're filling the row with data
     STATE_ROW_FULL,             // our row is full
@@ -35,22 +37,16 @@
 
 struct render_multi {
     // these are used as arguments to render_remote
-    struct render_multi_sub {
-        // our offset in the list
-        int index;
+    struct render_multi_node {
+        // the slice info
+        struct render_slice_info *info;
 
         // the render_remote_ctx
         struct render_remote *render_remote;
         
-        // a pointer to ourself
-        struct render_multi *self;
-
         // _render_multi_sent called for this?
         int render_sent;
 
-        // our offset into the row, static
-        size_t row_offset;
-
         // how wide our slice is, static
         size_t slice_width;
 
@@ -59,11 +55,14 @@
 
         // _render_multi_done called for this?
         int render_done;
+        
+        // a pointer to ourself
+        struct render_multi *self;
 
-    } remote_renders[RENDER_MULTI_NODES_MAX];
+    } nodes[RENDER_SLICES_MAX];
     
-    // how many remote_renders we have
-    int remote_render_count;
+    // how many nodes we have
+    int node_count;
 
     // is this still alive?
     int alive;
@@ -77,15 +76,12 @@
     // have we called cb_sent?
     int have_sent;
 
-    // the png thing
-    struct render_png *png_info;
+    // the render_slices thing
+    struct render_slices slices;
 
     // has render_png_done returned?
     int png_done;
 
-    // our pixel data row
-    unsigned char *rowbuf;
-
     // buffer render_png output in this
     struct evbuffer *out_buf;
 
@@ -115,10 +111,7 @@
 static void _render_multi_do_free (struct render_multi *ctx) {
     assert(ctx && ctx->alive == 0);
     
-    if (ctx->rowbuf) {
-        free(ctx->rowbuf);
-        ctx->rowbuf = NULL;
-    }
+    render_slices_deinit(&ctx->slices);
 
     if (ctx->out_buf) {
         evbuffer_free(ctx->out_buf);
@@ -132,9 +125,9 @@
 static void _render_multi_do_sent (struct render_multi *ctx) {
     int i;
 
-    // check that all the remote_renders have indeed been sent
-    for (i = 0; i < ctx->remote_render_count; i++) {
-        assert(ctx->remote_renders[i].render_sent);
+    // check that all the nodes have indeed been sent
+    for (i = 0; i < ctx->node_count; i++) {
+        assert(ctx->nodes[i].render_sent);
     }
 
     // assert the callbacks are still valid
@@ -186,37 +179,30 @@
 // the request completed normally, flush the png data and return
 // _render_multi_png_done takes care of calling cb_done, not us!
 static void _render_multi_do_done (struct render_multi *ctx) {
-    assert(ctx->alive && ctx->png_info);
+    assert(ctx->alive);
 
     int i;
     
     // check that all the remote_renders are indeed complete
-    for (i = 0; i < ctx->remote_render_count; i++) {
-        assert(ctx->remote_renders[i].render_remote == NULL);
-        assert(ctx->remote_renders[i].col == 0);
+    for (i = 0; i < ctx->node_count; i++) {
+        assert(ctx->nodes[i].render_remote == NULL);
+        assert(ctx->nodes[i].col == 0);
     }
+
+    // and that the PNG stuff is complete
+    assert(ctx->png_done);
     
-    // we need to make sure this render_png_done call only happens once (render_multi_do_fail, state bugs)
-    if (render_png_done(ctx->png_info)) {
-        ctx->png_info = NULL;
-
-        ERROR("render_png_done");
-    }
-    
-    ctx->png_info = NULL;
-
-    // mark the png as done now
-    ctx->png_done = 1;
-
     // if that all the data handled now, we're done
     _render_multi_do_png_data(ctx);
 
     // don't free ourself, our user does that (probably already did, via render_png_done)
     return;
 
+/*
 error:    
-    /* render_png_done failed, probably because we didn't have enough data */
+    / * render_png_done failed, probably because we didn't have enough data * /
     _render_multi_do_fail(ctx, FAIL_PARTIAL);
+*/    
 }
 
 // the request completed abnormally. Flags:
@@ -232,17 +218,13 @@
     ctx->alive = 0;
 
     // cancel any in-progress remote renders
-    for (i = 0; i < ctx->remote_render_count; i++)
-        if (ctx->remote_renders[i].render_remote) {
-            render_remote_cancel(ctx->remote_renders[i].render_remote);
-            ctx->remote_renders[i].render_remote = NULL;
+    for (i = 0; i < ctx->node_count; i++)
+        if (ctx->nodes[i].render_remote) {
+            render_remote_cancel(ctx->nodes[i].render_remote);
+            ctx->nodes[i].render_remote = NULL;
         }
     
-    if (!(flags & FAIL_PARTIAL) || ctx->png_info) {
-        // abort the render_png
-        render_png_abort(ctx->png_info);
-        ctx->png_info = NULL;
-    }
+    // XXX: does render_slices need an abort?
     
     // check that both callbacks are still valid
     assert(ctx->cb_fail && ctx->cb_done);
@@ -263,14 +245,14 @@
  * Once all of these commands have been sent, invoke our cb_sent.
  */
 static void _render_multi_sent (void *arg) {
-    struct render_multi_sub *ctx = arg;
+    struct render_multi_node *ctx = arg;
     
     // mark these as sent
     ctx->render_sent = 1;
     ctx->self->renders_sent++;
 
     // have all render_sub_ctxs been sent?
-    if (ctx->self->renders_sent == ctx->self->remote_render_count) {
+    if (ctx->self->renders_sent == ctx->self->node_count) {
         // tell our user
         _render_multi_do_sent(ctx->self);
     }
@@ -280,7 +262,7 @@
  * One render node failed, abort the whole thing
  */
 static void _render_multi_fail (void *arg) {
-    struct render_multi_sub *ctx = arg;
+    struct render_multi_node *ctx = arg;
 
     // free this ctx's remote render
     render_remote_free(ctx->render_remote);
@@ -294,7 +276,7 @@
  * Got new data for some remote render
  */
 static void _render_multi_data_raw (int fd, short event, void *arg) {
-    struct render_multi_sub *ctx = arg;
+    struct render_multi_node *ctx = arg;
     int ret;
 
     assert(ctx->col <= ctx->slice_width);   // check it isn't out of range
@@ -305,8 +287,8 @@
 
     // read new data into our slice
     ret = read(fd, 
-        ctx->self->rowbuf + ctx->row_offset + ctx->col, // our fixed offset + partial row offset
-        ctx->slice_width - ctx->col                     // how many bytes left in the window
+        ctx->info->render_buf + ctx->col,               // our segment buffer + partial segment offset
+        ctx->slice_width - ctx->col                     // how many bytes left in the segment
     );
     
     // errors/EOF?
@@ -327,12 +309,12 @@
         render_remote_done(ctx->render_remote);
         ctx->render_remote = NULL;
 
-        // is the data incomplete?
-        if (!(ctx->col == ctx->slice_width || ctx->col == 0))
-            ERROR("incomplete data for slice %d: %zu/%zu bytes", ctx->index, ctx->col, ctx->slice_width);
+        // we should only receive an EOF when ctx->col is zero or full
+        if (ctx->col != 0 || ctx->col != ctx->slice_width)
+            ERROR("incomplete data for slice %zu: %zu/%zu bytes", ctx->info->index, ctx->col, ctx->slice_width);
 
         // are all of them done?
-        if (ctx->self->renders_done == ctx->self->remote_render_count) {
+        if (ctx->self->renders_done == ctx->self->node_count) {
             // finish it off
             _render_multi_do_done(ctx->self);
 
@@ -341,30 +323,45 @@
         // do *NOT* reschedule ourself, ctx->render_remote is invalid anyways (as is ctx!)
         return;
     }
+
+    // check if we were expecting
     
     // ok, we received some data normally
     ctx->col += ret;
 
     // is our slice full now?
     if (ctx->col == ctx->slice_width) {
-        // is the row complete now?
-        int i;
-        for (i = 0; i < ctx->self->remote_render_count; i++) {
-            if (ctx->self->remote_renders[i].col < ctx->self->remote_renders[i].slice_width)
-                break;
-        }
+        // yes!
+        int status;
         
-        if (i == ctx->self->remote_render_count) {
-            // pass the data to render_png, this results in calls to _render_multi_png_data
-            if (render_png_row(ctx->self->png_info, ctx->self->rowbuf))
-                ERROR("render_png_row");
+        if ((status = render_slices_segment_done(&ctx->self->slices, ctx->info->index)) == -1)
+            ERROR("render_slices_segment_done");
+        
+        // row done?
+        if (status & SLICE_PROCESS_ROW) {
+            // XXX: ignore SLICE_PROCESS_ROW from render_slices_process_row
+            status = render_slices_process_row(&ctx->self->slices);
 
-            // clear the col values and reschedule the reads in case they were paused
-            for (i = 0; i < ctx->self->remote_render_count; i++) {
-                ctx->self->remote_renders[i].col = 0;
-                render_remote_reschedule(ctx->self->remote_renders[i].render_remote);
+            // we should have SLICE_CONTINUE by now, unless the PNG is done
+            if (status & SLICE_CONTINUE) {
+                // clear the col values and reschedule the reads in case they were ~SLICE_CONTINUE
+                int i;
+                for (i = 0; i < ctx->self->node_count; i++) {
+                    ctx->self->nodes[i].col = 0;
+                    render_remote_reschedule(ctx->self->nodes[i].render_remote);
+                }
+            } else {
+                // XXX: assume render_slices_process_row doesn't return a SLICE_PROCESS_ROW alone
+                assert(status == 0);
+
+                ctx->self->png_done = 1;
+
+                // and wait for the EOF...
             }
         }
+
+        // if we got SLICE_CONTINUE here, I don't care, don't try and get another row now
+        // just fall through and reschedule
     }
 
     // ok, reschedule ourselves
@@ -402,11 +399,6 @@
     return -1;
 }
 
-#define ROUND_DOWN(dividend, divisor) ((dividend) / (divisor))
-#define ROUND_UP(dividend, divisor) (((dividend) / (divisor)) + ((dividend) % (divisor)))
-
-#define HALF(a, b) (( a + b) / 2)
-
 struct render_multi *render_multi (
         struct render *render,               // what to render
         struct remote_pool *pool_info,    // what render pool to use
@@ -417,7 +409,6 @@
         void *cb_arg
 ) {
     struct render_multi *ctx = NULL;
-    struct render r_left, r_right;
     
     // alloc the render_multi
     ctx = calloc(1, sizeof(struct render_multi));
@@ -425,32 +416,6 @@
     if (!ctx)
         ERROR("calloc");
 
-    // init the remote_render
-    // for now, just split it in half into two render_ts
-    ctx->remote_renders[0].index = 0;
-    ctx->remote_renders[0].self = ctx;
-    ctx->remote_renders[0].slice_width = render->img_w / 2;
-    ctx->remote_renders[0].row_offset = 0;
-
-    ctx->remote_renders[1].index = 1;
-    ctx->remote_renders[1].self = ctx;
-    ctx->remote_renders[1].slice_width = render->img_w / 2 + render->img_w % 2;
-    ctx->remote_renders[1].row_offset = render->img_w / 2;
-
-    ctx->remote_render_count = 2;
-
-    assert(RENDER_MULTI_NODES_MAX >= 2);
-
-    if (
-            render_init(&r_left, RENDER_RAW)
-         || render_init(&r_right, RENDER_RAW)
-         || render_set_size(&r_left, ctx->remote_renders[0].slice_width, render->img_h)
-         || render_set_size(&r_right, ctx->remote_renders[1].slice_width, render->img_h)
-         || render_region_raw(&r_left, render->x1, render->y1, HALF(render->x1, render->x2), render->y2)
-         || render_region_raw(&r_right, HALF(render->x1, render->x2), render->y1, render->x2, render->y2)
-    )
-        ERROR("render_{init,set_size,set_region_raw}");
-            
     // store the provided callback functions
     ctx->cb_sent = cb_sent;
     ctx->cb_data = cb_data;
@@ -458,10 +423,6 @@
     ctx->cb_fail = cb_fail;
     ctx->cb_arg = cb_arg;
 
-    // our rowbuf
-    if (!(ctx->rowbuf = malloc(render->img_w)))
-        ERROR("malloc");
-
     // store our render_png callbacks, must be before png_info
     if (render_io_custom(render, &_render_multi_png_data, NULL, ctx))
         ERROR("render_io_custom");
@@ -470,28 +431,37 @@
     if (!(ctx->out_buf = evbuffer_new()))
         ERROR("evbuffer_new");
 
-    // png info
-    if (!(ctx->png_info = render_png_init(render)))
-        ERROR("render_png_init");
-
-    // pull two nodes from the pool
-    struct remote_node *node_left, *node_right;
+    // then initialize the render_slices
+    if (render_slices_init(&ctx->slices, render))
+        ERROR("render_slices_init");
+    
+    // how many nodes?
+    ctx->node_count = render_slices_get_count(&ctx->slices);
 
-    if (
-            !(node_left = remote_pool_get(pool_info))
-         || !(node_right = remote_pool_get(pool_info))
-    )
-        ERROR("remote_pool_get");
+    // load them
+    int i;
+
+    for (i = 0; i < ctx->node_count; i++) {
+        struct remote_node *node_info;
+        
+        // store the info struct
+        ctx->nodes[i].info = render_slices_get_slice_info(&ctx->slices, i);
+
+        // some simple attributes
+        ctx->nodes[i].slice_width = ctx->nodes[i].info->render_info->img_w;
+        ctx->nodes[i].self = ctx;
+
+        // get a node from the pool
+        if (!(node_info = remote_pool_get(pool_info)))
+            ERROR("remote_pool_get");
+        
+        // the render_remote
+        if (!(ctx->nodes[i].render_remote = render_remote_rawio(ctx->nodes[i].info->render_info, node_info, 
+                &_render_multi_sent, &_render_multi_fail, &_render_multi_data_raw, &ctx->nodes[i]))
+        )
+            ERROR("render_remote_rawio");
+    }
     
-    // the two render_remote calls
-    if (
-            !(ctx->remote_renders[0].render_remote = render_remote_rawio(&r_left, node_left, 
-                &_render_multi_sent, &_render_multi_fail, &_render_multi_data_raw, &ctx->remote_renders[0]))
-         || !(ctx->remote_renders[1].render_remote = render_remote_rawio(&r_right, node_right,
-                &_render_multi_sent, &_render_multi_fail, &_render_multi_data_raw, &ctx->remote_renders[1]))
-    )
-        ERROR("render_remote");
-
     // we are now alive
     ctx->alive = 1;
     
--- a/render_png.c	Sun Jun 08 23:10:36 2008 +0300
+++ b/render_png.c	Mon Jun 09 03:15:34 2008 +0300
@@ -3,19 +3,10 @@
 #include <png.h>
 
 #include "common.h"
-#include "render_internal.h"
+#include "render_struct.h"
+#include "render_png_struct.h"
 #include "render_png.h"
 
-struct render_png {
-    // libpng handles
-    png_structp png_ptr;
-    png_infop info_ptr;
-
-    // some info that we need to keep from the struct render
-    render_ctx_write_cb io_write_fn;
-    render_ctx_flush_cb io_flush_fn;
-    void *cb_arg;
-};
 
 static void _render_png_write(png_structp png_ptr, png_bytep data, png_size_t length) {
     struct render_png *ctx = png_get_io_ptr(png_ptr);
@@ -38,21 +29,37 @@
 }
 
 static void _render_png_free (struct render_png *ctx) {
-    if (ctx)
-        png_destroy_write_struct(&ctx->png_ptr, &ctx->info_ptr);
-
-    free(ctx);
+    render_png_deinit(ctx);
+    
+    if (ctx->owned_by_me)
+        free(ctx);
 }
 
-
-struct render_png *render_png_init (struct render *render) {
+int render_png_deinit (struct render_png *ctx) {
+    // not initialized? Just return a positive nonzero value
+    if (!ctx->png_ptr)
+        return 1;
 
-    struct render_png *ctx = NULL;
+    // libpng error handling
+    if (setjmp(png_jmpbuf(ctx->png_ptr)))
+        ERROR("libpng");
+    
+    png_destroy_write_struct(&ctx->png_ptr, &ctx->info_ptr);
 
-    // calloc the render_png
-    if (!(ctx = calloc(1, sizeof(struct render_png))))
-        ERROR("calloc");
+    // success
+    return 0;
 
+error:
+    free(ctx);
+    return -1;
+}
+
+/*
+ * Note that it's vitally important not to call any libpng functions directly,
+ * the error-handling setjmp will be invalid.
+ */
+
+int render_png_init (struct render_png *ctx, struct render *render) {
     // store some info from the struct render
     ctx->io_write_fn = render->io_write_fn;
     ctx->io_flush_fn = render->io_flush_fn;
@@ -83,6 +90,28 @@
 
     // write out the PNG header
     png_write_info(ctx->png_ptr, ctx->info_ptr);
+    
+    // success
+    return 0;
+
+error:
+    render_png_deinit(ctx);
+    return -1;
+}
+
+struct render_png *render_png_alloc (struct render *render) {
+    struct render_png *ctx = NULL;
+
+    // calloc the render_png
+    if (!(ctx = calloc(1, sizeof(struct render_png))))
+        ERROR("calloc");
+    
+    // I need to free it
+    ctx->owned_by_me = 1;
+
+    // init it
+    if (render_png_init(ctx, render))
+        goto error;
 
     // success
     return ctx;
--- a/render_png.h	Sun Jun 08 23:10:36 2008 +0300
+++ b/render_png.h	Mon Jun 09 03:15:34 2008 +0300
@@ -19,7 +19,7 @@
  *
  * returns NULL on failure.
  */
-struct render_png *render_png_init (struct render *render);
+struct render_png *render_png_alloc (struct render *render);
 
 /*
  * Feed a full row of raw pixel data into the PNG image.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render_png_struct.h	Mon Jun 09 03:15:34 2008 +0300
@@ -0,0 +1,26 @@
+#ifndef RENDER_PNG_INTERNAL_H
+#define RENDER_PNG_INTERNAL_H
+
+#include <png.h>
+
+#include "render.h"
+#include "render_png.h"
+
+struct render_png {
+    // do I need to free it?
+    int owned_by_me;
+
+    // libpng handles
+    png_structp png_ptr;
+    png_infop info_ptr;
+
+    // some info that we need to keep from the struct render
+    render_ctx_write_cb io_write_fn;
+    render_ctx_flush_cb io_flush_fn;
+    void *cb_arg;
+};
+
+int render_png_init (struct render_png *ctx, struct render *render);
+int render_png_deinit (struct render_png *ctx);
+
+#endif /* RENDER_PNG_INTERNAL_H */
--- a/render_raw.c	Sun Jun 08 23:10:36 2008 +0300
+++ b/render_raw.c	Mon Jun 09 03:15:34 2008 +0300
@@ -1,31 +1,25 @@
 #include <stdlib.h>
 
 #include "common.h"
-#include "render_internal.h"
+#include "render_struct.h"
+#include "render.h"
+#include "render_raw_struct.h"
 #include "render_raw.h"
 
-struct render_raw {
-    // some info that we need to keep from the struct render
-    u_int32_t img_w;
 
-    // cb or stream?
-    FILE *io_stream;
-    render_ctx_write_cb io_write_fn;
-    render_ctx_flush_cb io_flush_fn;
-    void *cb_arg;
-};
 
-static void _render_raw_free (struct render_raw *ctx) {
-    free(ctx);
+int render_raw_deinit (struct render_raw *ctx) {
+    return 0;
 }
 
-struct render_raw *render_raw_init (struct render *render) {
-    struct render_raw *ctx = NULL;
+void render_raw_free (struct render_raw *ctx) {
+    render_raw_deinit(ctx);
+    
+    if (ctx->owned_by_me)
+        free(ctx);
+}
 
-    // calloc the render_png
-    if (!(ctx = calloc(1, sizeof(struct render_raw))))
-        ERROR("calloc");
-
+int render_raw_init (struct render_raw *ctx, struct render *render) {
     // store some info from the struct render
     ctx->img_w = render->img_w;
     ctx->io_stream = render->io_stream;
@@ -34,10 +28,33 @@
     ctx->cb_arg = render->cb_arg;
 
     // success
+    return 0;
+
+/*
+error:
+    _render_raw_deinit(ctx);
+    return -1;
+*/    
+}
+
+struct render_raw *render_raw_alloc (struct render *render) {
+    struct render_raw *ctx = NULL;
+
+    // calloc the render_png
+    if (!(ctx = calloc(1, sizeof(struct render_raw))))
+        ERROR("calloc");
+    
+    // mark it to be freed
+    ctx->owned_by_me = 1;
+
+    // initialize
+    if (render_raw_init(ctx, render))
+        goto error;
+
     return ctx;
 
 error:
-    _render_raw_free(ctx);
+    render_raw_free(ctx);
     return NULL;
 }
 
@@ -78,19 +95,19 @@
     }
 
     // free everything
-    _render_raw_free(ctx);
+    render_raw_free(ctx);
 
     // success
     return 0;
 
 error:
-    _render_raw_free(ctx);
+    render_raw_free(ctx);
     return -1;
 }
 
 int render_raw_abort (struct render_raw *ctx) {
     // just free it
-    _render_raw_free(ctx);
+    render_raw_free(ctx);
 
     // success
     return 0;
--- a/render_raw.h	Sun Jun 08 23:10:36 2008 +0300
+++ b/render_raw.h	Mon Jun 09 03:15:34 2008 +0300
@@ -19,7 +19,7 @@
  *
  * returns NULL on failure.
  */
-struct render_raw *render_raw_init (struct render *render);
+struct render_raw *render_raw_alloc (struct render *render);
 
 /*
  * Feed a full row of raw pixel data to be handled
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render_raw_struct.h	Mon Jun 09 03:15:34 2008 +0300
@@ -0,0 +1,25 @@
+#ifndef RENDER_RAW_STRUCT_H
+#define RENDER_RAW_STRUCT_H
+
+#include <stdio.h>
+
+#include "render_raw.h"
+
+struct render_raw {
+    // do I have to free it?
+    int owned_by_me;
+
+    // some info that we need to keep from the struct render
+    u_int32_t img_w;
+
+    // cb or stream?
+    FILE *io_stream;
+    render_ctx_write_cb io_write_fn;
+    render_ctx_flush_cb io_flush_fn;
+    void *cb_arg;
+};
+
+int render_raw_init (struct render_raw *ctx, struct render *render);
+int render_raw_deinit (struct render_raw *ctx);
+
+#endif /* RENDER_RAW_STRUCT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render_slices.c	Mon Jun 09 03:15:34 2008 +0300
@@ -0,0 +1,171 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#include "common.h"
+#include "render_struct.h"
+#include "render_slices_struct.h"
+#include "render_slices.h"
+
+void render_slices_free (struct render_slices *ctx) {
+    render_slices_deinit(ctx);
+
+    if (ctx->owned_by_me)
+        free(ctx);
+}
+
+int render_slices_deinit (struct render_slices *ctx) {
+    // if it's not initialized, just ignore
+    if (!ctx->rowbuf)
+        return 1;
+
+    free(ctx->rowbuf);
+    
+    render_png_deinit(&ctx->png_info);
+
+    return 0;
+}
+
+#define HALF(a, b) (( a + b) / 2)
+
+int render_slices_init (struct render_slices *ctx, struct render *render) {
+    // initialize the slices
+    // for now, just split it in half into two render_ts
+    ctx->slices[0].info.index = 0;
+    ctx->slices[0].info.render_info = &ctx->slices[0].render_info;
+    ctx->slices[0].self = ctx;
+    ctx->slices[0].row_offset = 0;
+
+    ctx->slices[1].info.index = 1;
+    ctx->slices[1].info.render_info = &ctx->slices[1].render_info;
+    ctx->slices[1].self = ctx;
+    ctx->slices[1].row_offset = render->img_w / 2;
+
+    ctx->num_slices = 2;
+    ctx->render_row = 0;
+    ctx->done_row = -1;
+
+    assert(RENDER_SLICES_MAX >= 2);
+
+    if (
+            render_init         (&ctx->slices[0].render_info,   RENDER_RAW)
+         || render_init         (&ctx->slices[1].render_info,   RENDER_RAW)
+
+         || render_set_size     (&ctx->slices[0].render_info,   render->img_w / 2,                      render->img_h)
+         || render_set_size     (&ctx->slices[1].render_info,   render->img_w / 2 + render->img_w % 2,  render->img_h)
+
+         || render_region_raw   (&ctx->slices[0].render_info,   render->x1,                     render->y1, HALF(render->x1, render->x2),   render->y2)
+         || render_region_raw   (&ctx->slices[1].render_info,   HALF(render->x1, render->x2),   render->y1, render->x2,                     render->y2)
+    )
+        ERROR("render_{init,set_size,set_region_raw}");
+ 
+    // allocate the rowbuf
+    if (!(ctx->rowbuf = malloc(render->img_w * 2)))
+        ERROR("malloc");
+
+    // update the slice_buf pointers
+    ctx->rows[0] = ctx->rowbuf;
+    ctx->rows[1] = ctx->rowbuf + render->img_w;
+    ctx->slices[0].info.render_buf = ctx->rows[ctx->render_row] + ctx->slices[0].row_offset;
+    ctx->slices[1].info.render_buf = ctx->rows[ctx->render_row] + ctx->slices[1].row_offset;
+    
+    // png info
+    if (render_png_init(&ctx->png_info, render))
+        ERROR("render_png_init");
+
+    // great success!
+    return 0;
+
+error:
+    render_slices_deinit(ctx);
+    return -1;
+}
+
+struct render_slices *render_slices_alloc (struct render *render) {
+    struct render_slices *ctx = NULL;
+    
+    // allocate it
+    if (!(ctx = calloc(1, sizeof(*ctx))))
+        ERROR("calloc");
+    
+    // mark it for free()ing
+    ctx->owned_by_me = 1;
+    
+    // initialize it
+    if (render_slices_init(ctx, render))
+        goto error;
+
+    return ctx;
+
+error :
+    render_slices_free(ctx);
+    return NULL;
+}
+
+int render_slices_get_count (struct render_slices *ctx) {
+    return ctx->num_slices;
+}
+
+struct render_slice_info *render_slices_get_slice_info (struct render_slices *ctx, int index) {
+    assert(index >= 0 && index < ctx->num_slices);
+
+    return &ctx->slices[index].info;
+}
+
+int render_slices_segment_done (struct render_slices *ctx, int index) {
+    assert(index >= 0 && index < ctx->num_slices);
+    assert(ctx->slices_done < ctx->num_slices);
+    
+    // keep track of the number of full slices
+    ctx->slices_done++;
+    
+    // is the row complete?
+    if (ctx->slices_done == ctx->num_slices) {
+        // the row is complete
+        ctx->slices_done = 0;
+        
+        // is the other row waiting for it to get processed?
+        if (ctx->done_row < 0) {
+            // no, point the done_row at this row, point the render buffers to the unused row
+            ctx->done_row = ctx->render_row;
+            ctx->render_row = (ctx->render_row == 0 ? 1 : 0);
+            
+            int i ;
+            for (i = 0; i < ctx->num_slices; i++) {
+                ctx->slices[i].info.render_buf = ctx->rows[ctx->render_row] + ctx->slices[i].row_offset;
+            }
+            
+            // can now continue rendering as well as call process_row
+            return SLICE_PROCESS_ROW & SLICE_CONTINUE;
+
+        } else {
+            // cannot continue rendering, need to have process_row complete first
+            return SLICE_PROCESS_ROW;
+
+        }
+
+    } else {
+        // the row is not complete, do not render the next segment, do not call process_row
+        return 0;
+
+    }
+}
+
+int render_slices_process_row (struct render_slices *ctx) {
+    assert(ctx->done_row >= 0);
+
+    // pass the data to render_png, this results in calls to _render_multi_png_data
+    if (render_png_row(&ctx->png_info, ctx->rows[ctx->done_row]))
+        ERROR("render_png_row");
+    
+    // mark the row as processed
+    ctx->done_row = -1;
+
+    // ok, but don't call me again until I tell you to.
+    return SLICE_CONTINUE;
+
+error:
+    // user needs to deinit/free us
+    return -1;
+}   
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render_slices.h	Mon Jun 09 03:15:34 2008 +0300
@@ -0,0 +1,113 @@
+#ifndef RENDER_SLICES_H
+#define RENDER_SLICES_H
+
+#include "render.h"
+
+#define RENDER_SLICES_MAX 2
+
+struct render_slices;
+
+struct render_slice_info {
+    // our index
+    size_t index;
+
+    // the render info
+    struct render *render_info;
+
+    // where to put the pixel data
+    unsigned char *render_buf;
+};
+
+/*
+ * Split the given render operation into multiple slices that can be rendered
+ * induvidually.
+ *
+ * A segment is a single row of a slice. The raw pixel data for each segment
+ * should be stored into the render_buf item in that slice's render_slice_info.
+ * When a full segment has been stored into the render_buf, you should call
+ * render_slices_segment_done with that slice's index as an argument, and then
+ * proceed based on its return code.
+ *
+ * Returns a struct render_slices handle which can be used to operate on the
+ * slices, or NULL on failure.
+ */
+struct render_slices *render_slices_alloc (struct render *render);
+
+/*
+ * Get the number of slices that need to be rendered, which is also the valid
+ * range of index values for render_slices_get_slice_info.
+ */
+int render_slices_get_count (struct render_slices *ctx);
+
+/*
+ * Get the struct render_slice_info corresponding to the given index. This
+ * the information required to render the segment. The index is used as an
+ * argument to render_slices_segment_done.
+ */
+struct render_slice_info *render_slices_get_slice_info (struct render_slices *ctx, int index);
+
+/*
+ * You may call render_slices_segment_done again for any slice now, including
+ * those which returned ~SLICE_CONTINUE earlier.
+ */
+#define SLICE_CONTINUE 0x01
+
+/*
+ * You may call render_slices_process_row next.
+ */
+#define SLICE_PROCESS_ROW 0x02
+
+/*
+ * The render_buf for the render_slice_info with the given index value contains
+ * the full information for the next row of the render_info render. Returns a
+ * bitwise-AND of the SLICE_* flags, or -1 for an error:
+ *
+ * Valid return values:
+ *  zero
+ *      Do not call segment_done for this index, or process_row right now.
+ *      Information for some slice needed to complete the next row is missing,
+ *      and the other segments must call segment_done first.
+ * 
+ *  SLICE_PROCESS_ROW
+ *      Do not call segment_done for this index or any others, but do call
+ *      process_row as soon as possible, and then continue once it returns
+ *      (see render_slices_process_row).
+ *  
+ *  SLICE_CONTINUE & SLICE_PROCESS_ROW
+ *      You should call process_row next, but you can also call segment_done
+ *      for all segments that returned ~SLICE_CONTINUE earlier.
+ *
+ * -1   - An error occured. Rendering must be aborted.
+ *
+ */
+int render_slices_segment_done (struct render_slices *ctx, int index);
+
+/*
+ * Process a completed row and generate output in the PNG format (how that is
+ * handled depends on the render given to render_slices_alloc/init). Returns a
+ * bitwise-AND of the SLICE_* flags, or -1 for an error:
+ *
+ * Valid return values :
+ *  zero
+ *      The PNG is done - you don't need to call segment_done or process_row
+ *      anymore, doing so will result in an error. You don't need to deinit
+ *      ctx, but you may need to free it.
+ *
+ *  SLICE_CONTINUE
+ *      You may call render_slices_segment_done again for those slices that
+ *      returned ~SLICE_CONTINUE earlier.
+ *
+ *  SLICE_CONTINUE & SLICE_PROCESS_ROW
+ *      In addition to calling render_slices_segment_done again, you may also
+ *      call render_slices_process_row again.
+ *
+ * -1   - An error occured. Rendering must be aborted.
+ */
+int render_slices_process_row (struct render_slices *ctx);
+
+/*
+ * Free a render_slices. The rendering may or may not be complete.
+ */
+void render_slices_free (struct render_slices *ctx);
+
+#endif /* RENDER_SLICES_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render_slices_struct.h	Mon Jun 09 03:15:34 2008 +0300
@@ -0,0 +1,46 @@
+
+#include "render_struct.h"
+#include "render_png_struct.h"
+#include "render_slices.h"
+
+struct render_slices {
+    // do I have to free it?
+    int owned_by_me;
+
+    // the induvidual slices
+    struct render_slice {
+        // the public info
+        struct render_slice_info info;
+
+        // the render info
+        struct render render_info;
+        
+        // our offset into rowbuf
+        size_t row_offset;
+        
+        // pointer back to parent
+        struct render_slices *self;
+    } slices[RENDER_SLICES_MAX];
+    
+    // how many slices are in use
+    size_t num_slices;
+
+    // how many slices have reported the current row as done
+    size_t slices_done;
+
+    // the a pointer to the raw memory buffer
+    unsigned char *rowbuf;
+
+    // the two rows of memory area used to a) render b) build the png
+    unsigned char *rows[2];
+    
+    // which of the two rows is is used for png-building (default = 0, 1)
+    int render_row, done_row;
+    
+    // the png struct
+    struct render_png png_info;
+};
+
+int render_slices_init (struct render_slices *ctx, struct render *render);
+int render_slices_deinit (struct render_slices *ctx);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render_struct.h	Mon Jun 09 03:15:34 2008 +0300
@@ -0,0 +1,39 @@
+#ifndef RENDER_STRUCT_H
+#define RENDER_STRUCT_H
+
+#include "render.h"
+
+struct render {
+/* render mode */
+    int mode;
+
+/* image size */
+    u_int32_t img_w;
+    u_int32_t img_h;
+
+/* mandelbrot region */
+    double      x1;
+    double      y1;
+    double      x2;
+    double      y2;
+
+/* local rendering */
+    unsigned char **local_rowbuf_addr;
+    render_ctx_row_cb local_row_fn;
+
+/* local I/O parameters */
+    // if this is non-NULL, the image data is simply fwrite():en to this stream
+    FILE *io_stream;
+
+    // called to handle the output data
+    render_ctx_write_cb io_write_fn;
+
+    // called when the output data should be flushed - can be safely ignored if not needed
+    render_ctx_flush_cb io_flush_fn;
+
+    // the callback argument for all of the above *_fn
+    void *cb_arg;
+};
+
+#endif /* RENDER_STRUCT_H */
+