/* readyuv.c - read an Abekas A66 style image file.   Steve Simon, 2003 */
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <ctype.h>
#include "imagefile.h"

/*
 * ITU/CCIR Rec601 states:
 *
 * R = y + 1.402 * Cr
 * B = Y + 1.77305 * Cb
 * G = Y - 0.72414 * Cr - 0.34414 * Cb
 *
 *	using 8 bit traffic
 * Y = 16 + 219 * Y
 * Cr = 128 + 224 * Cr
 * Cb = 128 + 224 * Cb
 * 	or, if 10bit is used
 * Y = 64 + 876 * Y
 * Cr = 512 + 896 * Cr
 * Cb = 512 + 896 * Cb
 */

enum {
	PAL = 576, NTSC = 486 };


static int lsbtab[] = { 6, 4, 2, 0};

static int 
clip(int x)
{
	x >>= 18;

	if (x > 255)
		return 0xff;
	if (x <= 0)
		return 0;
	return x;
}


Rawimage**
Breadyuv(Biobuf *bp, int colourspace)
{
	Dir * d;
	Rawimage * a, **array;
	char	*e, ebuf[128];
	ushort * mux, *end, *frm;
	uchar buf[720 * 2], *r, *g, *b;
	int	y1, y2, cb, cr, sz, c, l, w, base, bits, lines;

	frm = 0;
	if (colourspace != CYCbCr) {
		errstr(ebuf, sizeof ebuf);	/* throw it away */
		werrstr("ReadYUV: unknown colour space %d", colourspace);
		return nil;
	}

	if ((a = calloc(sizeof(Rawimage), 1)) == nil)
		sysfatal("no memory");

	if ((array = calloc(sizeof(Rawimage * ), 2)) == nil)
		sysfatal("no memory");
	array[0] = a;
	array[1] = nil;

	if ((d = dirfstat(Bfildes(bp))) != nil) {
		sz = d->length;
		free(d);
	} else {
		fprint(2, "cannot stat input, assuming 720x576x10bit\n");
		sz = 720 * PAL * 2L + (720 * PAL / 2L);
	}

	switch (sz) {
	case 720 * PAL * 2:				/* 625 x 8bit */
		bits = 8;
		lines = PAL;
		break;
	case 720 * NTSC * 2:				/* 525 x 8bit */
		bits = 8;
		lines = NTSC;
		break;
	case 720 * PAL * 2 + (720 * PAL / 2) :		/* 625 x 10bit */
			bits = 10;
		lines = PAL;
		break;
	case 720 * NTSC * 2 + (720 * NTSC / 2) :	/* 525 x 10bit */
			bits = 10;
		lines = NTSC;
		break;
	default:
		e = "unknown file size";
		goto Error;
	}

	/*	print("bits=%d pixels=%d lines=%d\n", bits, 720, lines); */
	/* */
	a->nchans = 3;
	a->chandesc = CRGB;
	a->chanlen = 720 * lines;
	a->r = Rect(0, 0, 720, lines);

	e = "no memory";
	if ((frm = malloc(720 * 2 * lines * sizeof(ushort))) == nil)
		goto Error;

	for (c = 0; c  < 3; c++)
		if ((a->chans[c] = malloc(720 * lines)) == nil)
			goto Error;

	e = "read file";
	for (l = 0; l < lines; l++) {
		if (Bread(bp, buf, 720 * 2) == -1)
			goto Error;

		base = l * 720 * 2;
		for (w = 0; w < 720 * 2; w++)
			frm[base + w] = ((ushort)buf[w]) << 2;
	}


	if (bits == 10)
		for (l = 0; l < lines; l++) {
			if (Bread(bp, buf, 720 / 2) == -1)
				goto Error;


			base = l * 720 * 2;
			for (w = 0; w < 720 * 2; w++)
				frm[base + w] |= buf[w / 4] >> lsbtab[w % 4];
		}

	mux = frm;
	end = frm + 720 * lines * 2;
	r = a->chans[0];
	g = a->chans[1];
	b = a->chans[2];

	while (mux < end) {
		cb = *mux++ - 512;
		y1 = (*mux++ - 64) * 76310;
		cr = *mux++ - 512;
		y2 = (*mux++ - 64) * 76310;

		*r++ = clip((104635 * cr) + y1);
		*g++ = clip((-25690 * cb + -53294 * cr) + y1);
		*b++ = clip((132278 * cb) + y1);

		*r++ = clip((104635 * cr) + y2);
		*g++ = clip((-25690 * cb + -53294 * cr) + y2);
		*b++ = clip((132278 * cb) + y2);
	}
	free(frm);
	return array;

Error:

	errstr(ebuf, sizeof ebuf);
	if (ebuf[0] == 0)
		strcpy(ebuf, e);
	errstr(ebuf, sizeof ebuf);

	for (c = 0; c < 3; c++)
		free(a->chans[c]);
	free(a->cmap);
	free(array[0]);
	free(array);
	free(frm);
	return nil;
}


Rawimage**
readyuv(int fd, int colorspace)
{
	Rawimage * *a;
	Biobuf b;

	if (Binit(&b, fd, OREAD) < 0)
		return nil;
	a = Breadyuv(&b, colorspace);
	Bterm(&b);
	return a;
}


