mandelbrot.c
author Tero Marttila <terom@fixme.fi>
Sun, 01 Jun 2008 05:03:53 +0300
changeset 4 49edbdf9ebe7
parent 3 675be0a45157
child 11 082bfaf38cf0
permissions -rw-r--r--
updated code to use libevent svn (with custom modifications, need to create a git repo for that)

committer: Tero Marttila <terom@fixme.fi>
#include <stdlib.h>
#include <time.h>

#include <png.h>

#include "render.h"
#include "mandelbrot.h"
#include "common.h"

#define DETAIL 255

#define absdelta(a, b) (a>b ? a-b : b-a)

void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
    struct render_ctx *ctx = (struct render_ctx *) png_get_io_ptr(png_ptr);

    if (ctx->io_write_fn)
        switch (ctx->io_write_fn(data, length, ctx->io_cb_arg)) {
            case RENDER_CB_ERR :
                ctx->io_error = 1;
                break;

            case RENDER_CB_OK :
                // great!
                break;
        }
}

void user_flush_data(png_structp png_ptr) {
    struct render_ctx *ctx = (struct render_ctx *) png_get_io_ptr(png_ptr);

    if (ctx->io_flush_fn)
        switch (ctx->io_flush_fn(ctx->io_cb_arg)) {
            case RENDER_CB_ERR :
                ctx->io_error = 1;
                break;

            case RENDER_CB_OK :
                // great!
                break;
        }
}

int mandelbrot_render (render_t *ctx) {
    // libpng handles
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;

    // render algorithm vars
    u_int32_t img_x, img_y;
    double x0, y0, x, y, _x, _y, w_scale, h_scale;
    u_int8_t iter;
    u_int8_t *row;

    // clear out any potential error in ctx
    ctx->io_error = 0;
    
    // calcluate the scale factors
    w_scale = ctx->img_w/absdelta(ctx->x1, ctx->x2);
    h_scale = ctx->img_h/absdelta(ctx->y1, ctx->y2);
    
    // malloc the memory used to render each row
    row = (u_int8_t *) malloc(ctx->img_w);

    if (!row)
        goto error;
    
    // PNG or not?
    if (ctx->mode == RENDER_PNG) {
        // libpng initialization
        png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

        if (!png_ptr)
            goto error;

        info_ptr = png_create_info_struct(png_ptr);

        if (!info_ptr)
            goto error;

        // libpng error handling
        if (setjmp(png_jmpbuf(png_ptr))) {
            goto error;
        }
        
        if (ctx->io_stream) {
            // use normal libpng I/O
            png_init_io(png_ptr, ctx->io_stream);
        } else {
            // setup our custom I/O callbacks
            png_set_write_fn(png_ptr, ctx, &user_write_data, &user_flush_data);
        }

        // some PNG metadata
        png_set_IHDR(png_ptr, info_ptr, ctx->img_w, ctx->img_h, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

        // write out the PNG header
        png_write_info(png_ptr, info_ptr);

        // possible error return
        if (ctx->io_error)
            goto error;
    }

    // start rendering!
    for (img_y=0; img_y < ctx->img_h; img_y++) {
        // render the current row
        for (img_x=0; img_x < ctx->img_w; img_x++) {
            x = 0;
            y = 0;
            x0 = img_x/w_scale + ctx->x1;
            y0 = img_y/h_scale + ctx->y1;
            iter = DETAIL; 

            while (x*x + y*y < (2*2) && iter > 0) {
                _x = x*x - y*y + x0;
                _y = 2*x*y + y0;

                x = _x;
                y = _y;

                iter--;
            }
            
            row[img_x] = iter;
        }
        
        if (ctx->mode == RENDER_PNG) {
            // write the raw pixels to libpng
            png_write_row(png_ptr, row);
            
            // check for user errors return
            if (ctx->io_error)
                goto error;
        } else {
            // pass on the pixels to the io callback
            if (ctx->io_write_fn(row, ctx->img_w, ctx->io_cb_arg) == RENDER_CB_ERR) {
                ctx->io_error = 1;
                goto error;
            }
        }
    }
   
    if (ctx->mode == RENDER_PNG) {
        // finished writing
        png_write_end(png_ptr, info_ptr);
    }
    
    // clean up
    png_destroy_write_struct(&png_ptr, &info_ptr);
    free(row); row = NULL;

    // check for user errors return
    if (ctx->io_error)
        goto error;
    
    // return succesfully
    return MANDELBROT_OK;

error:
    if (png_ptr || info_ptr)
        png_destroy_write_struct(&png_ptr, &info_ptr);

    if (row) 
        free(row);
    
    row = NULL;

    return MANDELBROT_ERR;
}

int mandelbrot_render_timed (render_t *ctx, double *duration) {
    clock_t t1 = clock();
    int ret = mandelbrot_render(ctx);
    clock_t t2 = clock();

    *duration = ((double)(t2 - t1))/CLOCKS_PER_SEC;

    return ret;
}