render_slices.c
author Tero Marttila <terom@fixme.fi>
Wed, 27 Aug 2008 21:30:32 +0300
changeset 41 540737bf6bac
parent 23 31307efd7e78
permissions -rw-r--r--
sending requests, and partial support for receiving -- incomplete, not tested
#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);
}

void render_slices_deinit (struct render_slices *ctx) {
    if (ctx->rowbuf) {
        free(ctx->rowbuf);
        ctx->rowbuf = NULL;
    }
    
    render_png_deinit(&ctx->png_info);
}

#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);
    
    // should probably use memcpy instead...
    if (
            render_init         (&ctx->slices[0].render_info)
         || render_init         (&ctx->slices[1].render_info)

         || render_set_mode     (&ctx->slices[0].render_info,   RENDER_RAW)
         || render_set_mode     (&ctx->slices[1].render_info,   RENDER_RAW)

         || render_set_size     (&ctx->slices[0].render_info,   render->img_w,  render->img_h)
         || render_set_size     (&ctx->slices[1].render_info,   render->img_w,  render->img_h)

         || render_region_raw   (&ctx->slices[0].render_info,   render->x1, render->y1, render->x2, render->y2)
         || render_region_raw   (&ctx->slices[1].render_info,   render->x1, render->y1, render->x2, render->y2)
    )
        ERROR("render_{init,set_size,set_region_raw}");
    
    // set the offsets
    ctx->slices[0].render_info.img_left = 0;
    ctx->slices[0].render_info.img_right = render->img_w / 2;
    ctx->slices[1].render_info.img_left = render->img_w / 2;
    ctx->slices[1].render_info.img_right = render->img_w;

    // 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->render_row >= 0);
    assert(ctx->segments_done >= 0 && ctx->segments_done < ctx->num_slices);
    
    // our return status
    int i, status = 0;

    // is this row now complete?
    if (++ctx->segments_done == ctx->num_slices) {
        // the row is complete
        ctx->segments_done = 0;
    
        // once we fill up a row we can always call process_row
        status |= SLICE_PROCESS_ROW;
        
        // is the other row already rendered?
        if (ctx->done_row < 0) {
            // yes, 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);
            
            // can now continue rendering as well as call process_row
            status |= SLICE_CONTINUE;
            
            // update the remaining render buffers
            for (i = 0; i < ctx->num_slices; i++) {
                ctx->slices[i].info.render_buf = ctx->rows[ctx->render_row] + ctx->slices[i].row_offset;
            }

        } else {
            // cannot continue rendering, need to have process_row complete first
            
            ctx->render_row = -1;
        }


    } else {
        // the row is not complete, do not render the next segment, do not call process_row

    }

    return status;
}

int render_slices_process_row (struct render_slices *ctx) {
    assert(ctx->done_row >= 0);

    int i;

    // 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");
    
    // is the other row still in the process of being assembled?
    if (ctx->render_row == -1) {
        // no, both rows were full
        ctx->render_row = ctx->done_row;
        ctx->done_row = (ctx->done_row == 0 ? 1 : 0);
        
        // update the remaining render buffers
        for (i = 0; i < ctx->num_slices; i++) {
            ctx->slices[i].info.render_buf = ctx->rows[ctx->render_row] + ctx->slices[i].row_offset;
        }
        
        // you can call me again, but also segment_done
        return SLICE_PROCESS_ROW | SLICE_CONTINUE;

    } else {
        // yes, so that still needs to be finished
        ctx->done_row = -1;

        if (ctx->segments_done == 0) {
            // that row is empty, then we can continue
            return SLICE_CONTINUE;

        } else {
            // that row is partially built, so some segment is still on the way - don't resume the other slices until that's arrived!
            return 0;
        }
    }

error:
    // user needs to deinit/free us
    return -1;
}   

int render_slices_done (struct render_slices *ctx) {
    // finish off render_png
    if (render_png_done(&ctx->png_info))
        goto error;
    
    return 0;

error:
    render_slices_free(ctx);
    return -1;
}