shithub: pdffs

ref: 75ceb243faad4aa529afef047473ece203113eff
dir: /f_flate.c/

View raw version
#include <u.h>
#include <libc.h>
#include <flate.h>
#include "pdf.h"

typedef struct FlateParms FlateParms;

struct FlateParms {
	int predictor;
	int columns;
};

static uchar 
paeth(uchar a, uchar b, uchar c)
{
	int p, pa, pb, pc;

	p = a + b - c;
	pa = abs(p - a);
	pb = abs(p - b);
	pc = abs(p - c);

	if(pa <= pb && pa <= pc)
		return a;
	return pb <= pc ? b : c;
}

static int
pngunpredict(int pred, uchar *buf, uchar *up, int len)
{
	int i;

	switch(pred){
	case 0: /* None */
		break;

	case 1: /* Sub */
		for(i = 1; i < len; ++i)
			buf[i] += buf[i-1];
		break;

	case 2: /* Up */
		for(i = 0; i < len; ++i)
			buf[i] += up[i];
		break;

	case 3: /* Average */
		buf[0] += up[0]/2;
		for(i = 1; i < len; ++i)
			buf[i] += (buf[i-1]+up[i])/2;
		break;

	case 4: /* Paeth */
		buf[0] += paeth(0, up[0], 0);
		for(i = 0; i < len; ++i)
			buf[i] += paeth(buf[i-1], up[i], up[i-1]);
		break;

	/* FIXME 5 optimum??? */

	default:
		werrstr("unsupported predictor %d", pred);
		return -1;
	}

	return 0;
}

static int
bw(void *aux, void *d, int n)
{
	return bufput(aux, d, n);
}

static int
bget(void *aux)
{
	uchar c;

	return bufget(aux, &c, 1) == 1 ? c : -1;
}

static int
flreadall(void *aux, Buffer *bi, Buffer *bo)
{
	int r, i, rows, n;
	FlateParms *fp;
	uchar *x, *y, *zero;

	fp = aux;

	do{
		r = inflatezlib(bo, bw, bi, bget);
	}while(r == FlateOk && !bufeof(bi));

	if(r != FlateOk && bufleft(bo) < 1){
		werrstr("%s", flateerr(r));
		return -1;
	}
	r = 0;

	/* 7.4.4.4 LZW and Flate predictor functions */
	if(fp->predictor >= 10 && fp->columns > 0){
		n = fp->columns + 1;
		rows = bo->sz/n;
		x = bo->b;
		y = bo->b;
		zero = mallocz(fp->columns, 1);
		for(i = r = 0; i < rows && r == 0; i++, x += n, y += n)
			r = pngunpredict(x[0], x+1, i < 1 ? zero : y+1-n, fp->columns);
		free(zero);

		x = bo->b;
		y = bo->b+1;
		for(i = 0; i < rows; i++, x += fp->columns, y += n)
			memmove(x, y, fp->columns);
		bo->sz -= rows;
	}

	return r;
}

static int
flopen(Filter *f, Object *o)
{
	Object *parms;
	FlateParms *fp;
	int predictor, columns;

	parms = dictget(o, "DecodeParms");
	predictor = dictint(parms, "Predictor");
	columns = dictint(parms, "Columns");
	if((predictor >= 2 && predictor < 10) || predictor >= 15){
		werrstr("unsupported flate predictor %d", predictor);
		return -1;
	}
	if(predictor >= 10 && predictor <= 15 && columns < 1){
		werrstr("invalid columns %d for predictor %d", columns, predictor);
		return -1;
	}

	if((fp = malloc(sizeof(FlateParms))) == nil)
		return -1;
	fp->predictor = predictor;
	fp->columns = columns;
	f->aux = fp;

	return 0;
}

static void
flclose(Filter *f)
{
	free(f->aux);
}

Filter filterFlate = {
	.name = "FlateDecode",
	.readall = flreadall,
	.open = flopen,
	.close = flclose,
};