
/*
 * Z buffer: 16 bits Z / 16 bits color
 * 
 */

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "TinyGL/gl_zbuffer.h"


void ZB_update( ZBuffer *zb, void *frame_buffer, int xsize, int ysize, int linesize ){
	zb->pbuf     = (PIXEL*)frame_buffer;
	zb->xsize    = xsize;
	zb->ysize    = ysize;
	zb->linesize = linesize;
}

ZBuffer *ZB_open(
	int            xsize,
	int            ysize,
	int            mode,
	int            nb_colors,
	unsigned char *color_indexes,
	int           *color_table,
	void          *frame_buffer
){
    ZBuffer *zb    = (ZBuffer*)malloc( sizeof(ZBuffer) );
    int      size;

    if(zb == NULL){
		return NULL;
	}

    zb->xsize    = xsize;
    zb->ysize    = ysize;
    zb->mode     = mode;
    zb->linesize = (xsize * PSZB + 3) & ~3;

    switch( mode ) {
#ifdef TGL_FEATURE_8_BITS
    case ZB_MODE_INDEX:
		ZB_initDither( zb, nb_colors, color_indexes, color_table );
		break;
#endif

#ifdef TGL_FEATURE_32_BITS
    case ZB_MODE_RGBA:
#endif
#ifdef TGL_FEATURE_24_BITS
    case ZB_MODE_RGB24:
#endif
    case ZB_MODE_5R6G5B:
		zb->nb_colors = 0;
		break;
    default:
		goto error;
    }

    size = zb->xsize * zb->ysize * sizeof( unsigned short );

    zb->zbuf = (unsigned short int*)malloc( size );
    if( zb->zbuf == NULL ){
		goto error;
	}

    if( frame_buffer == NULL ){
		zb->pbuf = (PIXEL*)malloc(zb->ysize * zb->linesize);
		if (zb->pbuf == NULL) {
			free(zb->zbuf);
			goto error;
		}
		zb->frame_buffer_allocated = 1;
    }else{
		zb->frame_buffer_allocated = 0;
		zb->pbuf = (PIXEL*)frame_buffer;
    }

    zb->current_texture = NULL;
    return zb;
error:
	free( zb );
    return NULL;
}

void ZB_close( ZBuffer * zb ){
#ifdef TGL_FEATURE_8_BITS
    if( zb->mode == ZB_MODE_INDEX ){
		ZB_closeDither( zb );
	}
#endif

    if( zb->frame_buffer_allocated ){
		free( zb->pbuf );
	}

    free( zb->zbuf );
    free( zb );
}

void ZB_resize( ZBuffer * zb, void *frame_buffer, int xsize, int ysize ){
    int size;

    /* xsize must be a multiple of 4 */
    xsize = xsize & ~3;

    zb->xsize    = xsize;
    zb->ysize    = ysize;
    zb->linesize = (xsize * PSZB + 3) & ~3;

    size = zb->xsize * zb->ysize * sizeof(unsigned short);

    free( zb->zbuf );
    zb->zbuf = (unsigned short int *)malloc( size );

    if( zb->frame_buffer_allocated ){
		free( zb->pbuf );
	}

    if (frame_buffer == NULL) {
		zb->pbuf                   = (PIXEL *)malloc( zb->ysize * zb->linesize );
		zb->frame_buffer_allocated = 1;
    } else {
		zb->pbuf                   = (PIXEL *)frame_buffer;
		zb->frame_buffer_allocated = 0;
    }
}

#if TGL_FEATURE_RENDER_BITS == 16

/* 16 bpp copy */

void ZB_copyFrameBuffer5R6G5B( ZBuffer *zb, void *buf, int linesize ){
    unsigned short *p1 = (short unsigned int *)buf;
	unsigned short *q  = zb->pbuf;
	int             n  = zb->xsize * PSZB;
    int             y;

    for( y=0; y<zb->ysize; y++) {
		memcpy( p1, q, n );
		p1 += linesize;
		q = (PIXEL *) ((char *) q + zb->linesize);
    }
}

/* 32 bpp copy */

#ifdef TGL_FEATURE_32_BITS

#define RGB16_TO_RGB32(p0,p1,v)\
{\
    unsigned int g,b,gb;\
    g = (v & 0x07E007E0) << 5;\
    b = (v & 0x001F001F) << 3;\
    gb = g | b;\
    p0 = (gb & 0x0000FFFF) | ((v & 0x0000F800) << 8);\
    p1 = (gb >> 16) | ((v & 0xF8000000) >> 8);\
}

void ZB_copyFrameBufferRGB32( ZBuffer *zb, void *buf, int linesize ){
    unsigned short *q;
    unsigned int   *p, *p1, v, w0, w1;
    int             y, n;

    q =  zb->pbuf;
    p1 = (unsigned int *) buf;

    for (y = 0; y < zb->ysize; y++) {
	p = p1;
	n = zb->xsize >> 2;
	do {
	    v = *(unsigned int *) q;
#if BYTE_ORDER == BIG_ENDIAN
	    RGB16_TO_RGB32(w1, w0, v);
#else
	    RGB16_TO_RGB32(w0, w1, v);
#endif
	    p[0] = w0;
	    p[1] = w1;

	    v = *(unsigned int *) (q + 2);
#if BYTE_ORDER == BIG_ENDIAN
	    RGB16_TO_RGB32(w1, w0, v);
#else
	    RGB16_TO_RGB32(w0, w1, v);
#endif
	    p[2] = w0;
	    p[3] = w1;

	    q += 4;
	    p += 4;
	} while (--n > 0);

	p1 += linesize;
    }
}

#endif

/* 24 bit packed pixel handling */

#ifdef TGL_FEATURE_24_BITS

/* order: RGBR GBRG BRGB */

/* XXX: packed pixel 24 bit support not tested */
/* XXX: big endian case not optimised */

#if BYTE_ORDER == BIG_ENDIAN

#define RGB16_TO_RGB24(p0,p1,p2,v1,v2)\
{\
    unsigned int r1,g1,b1,gb1,g2,b2,gb2;\
    v1 = (v1 << 16) | (v1 >> 16);\
    v2 = (v2 << 16) | (v2 >> 16);\
    r1 = (v1 & 0xF800F800);\
    g1 = (v1 & 0x07E007E0) << 5;\
    b1 = (v1 & 0x001F001F) << 3;\
    gb1 = g1 | b1;\
    p0 = ((gb1 & 0x0000FFFF) << 8) | (r1 << 16) | (r1 >> 24);\
    g2 = (v2 & 0x07E007E0) << 5;\
    b2 = (v2 & 0x001F001F) << 3;\
    gb2 = g2 | b2;\
    p1 = (gb1 & 0xFFFF0000) | (v2 & 0xF800) | ((gb2 >> 8) & 0xff);\
    p2 = (gb2 << 24) | ((v2 & 0xF8000000) >> 8) | (gb2 >> 16);\
}

#else

#define RGB16_TO_RGB24(p0,p1,p2,v1,v2)\
{\
    unsigned int r1,g1,b1,gb1,g2,b2,gb2;\
    r1  = (v1 & 0xF800F800);\
    g1  = (v1 & 0x07E007E0) << 5;\
    b1  = (v1 & 0x001F001F) << 3;\
    gb1 = g1 | b1;\
    p0  = ((gb1 & 0x0000FFFF) << 8) | (r1 << 16) | (r1 >> 24);\
    g2  = (v2 & 0x07E007E0) << 5;\
    b2  = (v2 & 0x001F001F) << 3;\
    gb2 = g2 | b2;\
    p1  = (gb1 & 0xFFFF0000) | (v2 & 0xF800) | ((gb2 >> 8) & 0xff);\
    p2  = (gb2 << 24) | ((v2 & 0xF8000000) >> 8) | (gb2 >> 16);\
}

#endif

void ZB_copyFrameBufferRGB24( ZBuffer *zb, void *buf, int linesize ){
    unsigned short *q;
    unsigned int   *p, *p1, w0, w1, w2, v0, v1;
    int             y, n;

    q        = zb->pbuf;
    p1       = (unsigned int *) buf;
    linesize = linesize * 3;

    for( y=0; y<zb->ysize; y++ ){
	p = p1;
	n = zb->xsize >> 2;
	do {
	    v0 = *(unsigned int *) q;
	    v1 = *(unsigned int *) (q + 2);
	    RGB16_TO_RGB24(w0, w1, w2, v0, v1);
	    p[0] = w0;
	    p[1] = w1;
	    p[2] = w2;

	    q += 4;
	    p += 3;
	} while (--n > 0);

	(char *) p1 += linesize;
    }
}

#endif

#endif /* TGL_FEATURE_RENDER_BITS == 16 */

void ZB_copyFrameBuffer( ZBuffer *zb, void *buf, int linesize ){
    switch (zb->mode) {
#ifdef TGL_FEATURE_8_BITS
    case ZB_MODE_INDEX:
	ZB_ditherFrameBuffer(zb, (unsigned char *)buf, linesize);
	break;
#endif
    case ZB_MODE_5R6G5B:
	ZB_copyFrameBuffer5R6G5B(zb, buf, linesize);
	break;
#ifdef TGL_FEATURE_32_BITS
    case ZB_MODE_RGBA:
	ZB_copyFrameBufferRGB32(zb, buf, linesize);
	break;
#endif
#ifdef TGL_FEATURE_24_BITS
    case ZB_MODE_RGB24:
	ZB_copyFrameBufferRGB32(zb, buf, linesize);
	break;
#endif
    default:
#if defined(_WIN32)
	assert(0);
#endif
	break;
    }
}

/*
 * adr must be aligned on an 'int'
 */
void memset_s( void *adr, int val, int count ){
	int             i;
	int             n = count >> 3;
	int             v = val | (val << 16);
    unsigned int   *p = (unsigned int *)adr;
    unsigned short *q;

    
    for (i = 0; i < n; i++) {
		p[0] = v;
		p[1] = v;
		p[2] = v;
		p[3] = v;
		p += 4;
    }

    q = (unsigned short *) p;
    n = count & 7;
    for( i=0; i<n; i++ ){
		*q++ = val;
	}
}

void memset_l( void *adr, int val, int count ){
	int           i;
	int           n = count >> 2;
	int           v = val;
	unsigned int *p = (unsigned int *)adr;

    for( i=0; i<n; i++ ){
		p[0] = v;
		p[1] = v;
		p[2] = v;
		p[3] = v;
		p += 4;
    }

    n = count & 3;
    for( i=0; i<n; i++ ){
		*p++ = val;
	}
}

void ZB_clear( ZBuffer *zb, int clear_z, int z, int clear_color, int r, int g, int b ){
    int    color, y;
    PIXEL *pp;

    if( clear_z ){
		memset_s(zb->zbuf, z, zb->xsize * zb->ysize);
    }
    if (clear_color) {
	color = RGB_TO_PIXEL(r, g, b);
	pp = zb->pbuf;
	for (y = 0; y < zb->ysize; y++) {
#if TGL_FEATURE_RENDER_BITS == 15 || TGL_FEATURE_RENDER_BITS == 16
	    memset_s(pp, color, zb->xsize);
#elif TGL_FEATURE_RENDER_BITS == 32
	    memset_l(pp, color, zb->xsize);
#else
#error TODO
#endif
	    pp = (PIXEL *) ((char *) pp + zb->linesize);
	}
    }
}

