715 lines
21 KiB
C
715 lines
21 KiB
C
|
|
/*
|
|
* render2d
|
|
*
|
|
* Threshold or Error diffusion screen pixel processing object.
|
|
* (Simplified from DPS code)
|
|
*
|
|
* Author: Graeme W. Gill
|
|
* Date: 8/9/2005
|
|
* Version: 1.00
|
|
*
|
|
* Copyright 2005, 2012 Graeme W. Gill
|
|
* All rights reserved.
|
|
* This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
|
|
* see the License.txt file for licencing details.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "aconfig.h"
|
|
#include "numlib.h"
|
|
//#include "icc.h"
|
|
#include "sort.h"
|
|
//#include "xcolorants.h"
|
|
#include "thscreen.h"
|
|
|
|
/* Configuration: */
|
|
#undef DEBUG
|
|
|
|
#undef CHECK_EXPECTED_ED_LEVELS /* Output expected quantized levels for checkking */
|
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
#ifdef DEBUG
|
|
# define DBG(text) printf text ; fflush(stdout);
|
|
#else
|
|
# define DBG(text)
|
|
#endif
|
|
|
|
/* ----------------------------------------------------------- */
|
|
/* Setup a set of screens */
|
|
/* Screen data is used that best matches the requested parameters. */
|
|
|
|
#include "screens.h" /* Pre-generated screen patterns */
|
|
|
|
/* Threshold screen lines of multiplane pixels */
|
|
void screen_thscreens(
|
|
thscreens *t, /* Screening object pointer */
|
|
int width, int height, /* Width and height to screen in pixels */
|
|
int xoff, int yoff, /* Offset into screening pattern */
|
|
unsigned char *out, /* Output pixel buffer */
|
|
unsigned long opitch, /* Increment between output lines in components */
|
|
unsigned char *in, /* Input pixel buffer */
|
|
unsigned long ipitch /* Increment between input lines in components */
|
|
) {
|
|
int i;
|
|
for (i = 0; i < t->np; i++)
|
|
t->sc[i]->screen(t->sc[i], width, height, xoff, yoff,
|
|
out + i, t->np, opitch,
|
|
in + 2 * i, t->np, ipitch);
|
|
}
|
|
|
|
/* Error diffusion screen lines of multiplane pixels */
|
|
void screen_edscreens(
|
|
thscreens *t, /* Screening object pointer */
|
|
int width, int height, /* Width and height to screen in pixels */
|
|
int xoff, int yoff, /* Offset into screening pattern, [xoff + width < mxwidth] */
|
|
unsigned char *out, /* Output pixel buffer */
|
|
unsigned long opitch, /* Increment between output lines in components */
|
|
unsigned char *_in, /* Input pixel buffer */
|
|
unsigned long ipitch /* Increment between input lines in components */
|
|
) {
|
|
unsigned short *in = (unsigned short *)_in; /* Pointer to input pixel sized values */
|
|
unsigned short *ein = in + height * ipitch; /* Vertical end pixel marker */
|
|
unsigned short *ein1; /* Horizontal end pixel markers */
|
|
int xo, yo; /* Threshold screen offset */
|
|
int x, j;
|
|
|
|
/* Limit width to mxwidth */
|
|
if ((xoff + width) > t->mxwidth) {
|
|
width = t->mxwidth - xoff;
|
|
if (width < 0)
|
|
return;
|
|
}
|
|
|
|
/* If not sequential, clear error buffer */
|
|
if (yoff != (t->lastyoff+1)) {
|
|
for (x = -1; x <= t->mxwidth; x++) {
|
|
for (j = 0; j < t->np; j++)
|
|
t->ebuf[j][x] = 0.0;
|
|
}
|
|
}
|
|
|
|
/* Clear "next to right" error */
|
|
for (j = 0; j < t->np; j++) {
|
|
t->ebuf[j][-2] = 0.0;
|
|
}
|
|
|
|
t->lastyoff = yoff;
|
|
|
|
/* For each line: */
|
|
for (; in < ein; in += ipitch, ein1 += ipitch, out += opitch, yoff++) {
|
|
unsigned short *ip; /* Horizontal input pointer */
|
|
unsigned char *op; /* Horizontal output pointer */
|
|
int xinc, pinc;
|
|
|
|
/* Do in serpentine order */
|
|
if (yoff & 1) {
|
|
xinc = -1;
|
|
x = xoff + width-1; /* x is index into error buffer */
|
|
pinc = -t->np;
|
|
ein1 = in + pinc;
|
|
ip = in + t->np * (width-1);
|
|
op = out + t->np * (width-1);
|
|
} else {
|
|
xinc = 1;
|
|
x = xoff;
|
|
pinc = t->np;
|
|
ein1 = in + t->np * width;
|
|
ip = in;
|
|
op = out;
|
|
}
|
|
|
|
/* For each pixel */
|
|
for (; ip != ein1; ip += pinc, op += pinc, x += xinc) {
|
|
double ov[THMXCH2D], tv[THMXCH2D], ev[THMXCH2D];
|
|
|
|
/* Limit error propogation if asked */
|
|
if (t->mxerr != 0.0) {
|
|
for (j = 0; j < t->np; j++) {
|
|
if (t->ebuf[j][x] < -t->mxerr)
|
|
t->ebuf[j][x] = -t->mxerr;
|
|
else if (t->ebuf[j][x] > t->mxerr)
|
|
t->ebuf[j][x] = t->mxerr;
|
|
}
|
|
}
|
|
|
|
/* For each plane */
|
|
for (j = 0; j < t->np; j++) {
|
|
tv[j] = t->luts[j][ip[j]] / 65535.0; /* 0.0 - 1.0 value */
|
|
|
|
/* Value + accumulated error */
|
|
ov[j] = tv[j] = tv[j] + t->ebuf[j][x];
|
|
|
|
/* Limit */
|
|
if (ov[j] > 1.0)
|
|
ov[j] = 1.0;
|
|
else if (ov[j] < 0.0)
|
|
ov[j] = 0.0;
|
|
|
|
/* Output encode */
|
|
op[j] = t->oevalues[(int)(ov[j] * (t->oelev-1.0) + 0.5)];
|
|
}
|
|
|
|
#ifdef CHECK_EXPECTED_ED_LEVELS
|
|
#pragma message("######### render/thscreen.c CHECK_EXPECTED_ED_LEVELS defined ! ##")
|
|
// Put expected values in output to check levels
|
|
t->quant(t->qcntx, ev, ov);
|
|
for (j = 0; j < t->np; j++)
|
|
op[j] = t->oevalues[(int)(ev[j] * (t->oelev-1.0) + 0.5)];
|
|
#endif
|
|
|
|
/* Quantize to values that it actually will be */
|
|
if (t->quant != NULL)
|
|
t->quant(t->qcntx, ov, ov);
|
|
else {
|
|
for (j = 0; j < t->np; j++)
|
|
ov[j] = floor(ov[j] * (t->oelev-1) + 0.5)/(t->oelev-1.0);
|
|
}
|
|
|
|
/* Compute the error to the target */
|
|
for (j = 0; j < t->np; j++) {
|
|
|
|
/* Error to target */
|
|
ev[j] = tv[j] - ov[j];
|
|
}
|
|
|
|
/* Distribute the error */
|
|
for (j = 0; j < t->np; j++) {
|
|
#ifdef NEVER
|
|
/* Classic error diffusion */
|
|
t->ebuf[j][x-xinc] += 0.1875 * ev[j]; /* Lower left */
|
|
t->ebuf[j][x] = t->ebuf[j][-2] + 0.3125 * ev[j]; /* Lower */
|
|
t->ebuf[j][-2] = 0.0625 * ev[j]; /* Lower right */
|
|
t->ebuf[j][x+xinc] += 0.4375 * ev[j]; /* Right */
|
|
#else
|
|
/* Using random placement error distribution */
|
|
double rav;
|
|
int ii;
|
|
t->so->next(t->so, &rav); /* For some order */
|
|
rav *= 4.0;
|
|
rav += d_rand(0.0, 2.5); /* For some randomness */
|
|
ii = (int)(rav);
|
|
if (ii > 3)
|
|
ii -= 4;
|
|
t->ebuf[j][x] = t->ebuf[j][-2];
|
|
t->ebuf[j][-2] = 0.0;
|
|
switch (ii) {
|
|
case 0:
|
|
t->ebuf[j][x-xinc] += ev[j]; /* Lower left */
|
|
break;
|
|
case 1:
|
|
t->ebuf[j][x] += ev[j]; /* Lower */
|
|
break;
|
|
case 2:
|
|
t->ebuf[j][-2] += ev[j]; /* Lower right */
|
|
break;
|
|
case 3:
|
|
t->ebuf[j][x+xinc] += ev[j]; /* Right */
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Delete a thscreens */
|
|
void del_thscreens(thscreens *t) {
|
|
int i;
|
|
|
|
if (t->sc != NULL) {
|
|
for (i = 0; i < t->np; i++) {
|
|
if (t->sc[i] != NULL)
|
|
t->sc[i]->del(t->sc[i]);
|
|
}
|
|
free(t->sc);
|
|
}
|
|
if (t->ebuf != NULL) {
|
|
free_fmatrix(t->ebuf, 0, t->np-1, -2, t->mxwidth);
|
|
}
|
|
|
|
if (t->luts != NULL) {
|
|
free_imatrix(t->luts, 0, t->np-1, 0, 65535);
|
|
}
|
|
|
|
if (t->so != NULL)
|
|
t->so->del(t->so);
|
|
|
|
free(t);
|
|
}
|
|
|
|
/* Create a new thscreens object matching the parameters */
|
|
thscreens *new_thscreens(
|
|
int exact, /* Return only exact matches */
|
|
int nplanes, /* Number of planes to screen */
|
|
double asp, /* Target aspect ratio (== dpiX/dpiY) */
|
|
int size, /* Target screen size */
|
|
sc_iencoding ie, /* Input encoding - must be scie_16 */
|
|
int oebpc, /* Output encoding bits per component - must be 8 */
|
|
int oelev, /* Output encoding levels. Must be <= 2 ^ oebpc */
|
|
int *oevalues, /* Optional output encoding values for each level */
|
|
/* Must be oelev entries. Default is 0 .. oelev-1 */
|
|
sc_oorder oo, /* Output bit ordering */
|
|
double overlap, /* Overlap between levels, 0 - 1.0 */
|
|
int mxwidth, /* max width in pixels of raster to be screened */
|
|
void **cntx, /* List of contexts for lookup table callback */
|
|
double (**lutfunc)(void *cntx, double in), /* List of callback functions, NULL if none */
|
|
int edif, /* nz if using error diffusion */
|
|
void (*quant)(void *qcntx, double *out, double *in), /* optional quantization func. for edif */
|
|
void *qcntx,
|
|
double mxerr /* If error diffusion anf != 0, max error to propogate */
|
|
) {
|
|
thscreens *t;
|
|
int i, bi = -1;
|
|
double bamatch; /* Best aspect match */
|
|
int bsize = 100000; /* Best size match */
|
|
int swap = 0; /* width and height will need swapping */
|
|
|
|
DBG(("thscreens: new called with:\n"));
|
|
DBG((" nplanes = 0x%x\n",nplanes));
|
|
DBG((" asp = %f\n",asp));
|
|
DBG((" ie = %d\n",ie));
|
|
DBG((" oebpc = %d\n",oebpc));
|
|
DBG((" oelev = %d\n",oelev));
|
|
DBG((" oo = %d\n",oo));
|
|
DBG((" overlap = %f\n",overlap));
|
|
|
|
if (asp < 1.0) { /* All screens[] have asp >= 1.0 */
|
|
asp = 1.0/asp;
|
|
swap = 1;
|
|
DBG(("thscreens: aspect swap needed\n"));
|
|
}
|
|
|
|
if ((t = (thscreens *)calloc(1, sizeof(thscreens))) == NULL) {
|
|
DBG(("thscreens: malloc of thscreens failed\n"));
|
|
return NULL;
|
|
}
|
|
|
|
t->np = nplanes; /* Number of planes */
|
|
t->edif = edif; /* Error diffusion */
|
|
t->quant = quant; /* Optional quantization function */
|
|
t->qcntx = qcntx;
|
|
|
|
t->mxwidth = mxwidth;
|
|
t->mxerr = mxerr;
|
|
t->lastyoff = -1;
|
|
|
|
/* Allocate and initialise a next line error buffer. */
|
|
/* we allow 2 extra locations for pixels to the left and right of the current one: */
|
|
/* [-1] for the one to the below left when we are at x = 0, */
|
|
/* [-2] for the one below right, before we use [x] */
|
|
if (t->edif)
|
|
t->ebuf = fmatrixz(0, t->np-1, -2, t->mxwidth);
|
|
|
|
t->oebpc = oebpc;
|
|
t->oelev = oelev;
|
|
if (oevalues != NULL) {
|
|
for (i = 0; i < t->oelev; i++) {
|
|
if (oevalues[i] >= (1 << t->oebpc)) {
|
|
DBG(("new_thscreens() oevalues[%d] value %d can't fit in %d bits\n",i,oevalues[i],t->oebpc));
|
|
free(t);
|
|
return NULL;
|
|
}
|
|
t->oevalues[i] = oevalues[i];
|
|
}
|
|
} else {
|
|
for (i = 0; i < t->oelev; i++)
|
|
t->oevalues[i] = i;
|
|
}
|
|
|
|
DBG(("thscreens no planes = %d\n",t->np));
|
|
|
|
t->del = del_thscreens;
|
|
|
|
DBG(("thscreens: searching amongst %d screens, exact = %d\n",NO_SCREENS,exact));
|
|
|
|
DBG(("thscreens: looking for non-exact match\n"));
|
|
|
|
/* Synthesise a set of screens from what's there */
|
|
/* (Don't bother with matching the colorspace) */
|
|
for (i = 0;i < NO_SCREENS; i++) {
|
|
double thamatch; /* This aspect match */
|
|
int thsize; /* This size match */
|
|
|
|
thamatch = asp/screens[i].asp;
|
|
if (thamatch < 1.0)
|
|
thamatch = 1.0/thamatch;
|
|
|
|
if (bi < 0 || (thamatch < bamatch)) { /* No best or new best */
|
|
bamatch = thamatch;
|
|
bi = i;
|
|
DBG(("thscreens: new best with aspmatch %f\n",bamatch));
|
|
continue; /* On to next */
|
|
}
|
|
if (thamatch > bamatch) /* Worse aspect match */
|
|
continue;
|
|
/* Same on aspect ratio. Check size */
|
|
thsize = size - screens[i].size;
|
|
if (thsize < 0)
|
|
thsize = -thsize;
|
|
if (thsize < bsize) { /* New better size match */
|
|
bsize = thsize;
|
|
bi = i;
|
|
DBG(("thscreens: new best with size %d\n",bsize));
|
|
}
|
|
}
|
|
|
|
if (bi < 0) /* Strange */
|
|
return NULL;
|
|
|
|
if (t->edif) {
|
|
int j;
|
|
int npix;
|
|
|
|
t->screen = screen_edscreens;
|
|
|
|
t->luts = imatrix(0, t->np-1, 0, 65535);
|
|
|
|
/* Create a suitable LUT from the given function */
|
|
/* Input is either 8 or 16 bits, output is always 16 bits */
|
|
for (j = 0; j < t->np; j++) {
|
|
for (i = 0; i < 65536; i++) {
|
|
if (lutfunc != NULL && lutfunc[j] != NULL) {
|
|
double v = i/65535.0;
|
|
v = lutfunc[j](cntx[j], v);
|
|
t->luts[j][i] = (int)(v * 65535.0 + 0.5);
|
|
} else
|
|
t->luts[j][i] = i;
|
|
}
|
|
}
|
|
|
|
if ((t->so = new_sobol(1)) == NULL) {
|
|
DBG(("thscreens: new_sobol() failed\n"));
|
|
return NULL;
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
t->screen = screen_thscreens;
|
|
|
|
if ((t->sc = malloc(sizeof(thscreen *) * t->np)) == NULL) {
|
|
free(t);
|
|
DBG(("thscreens: malloc of thscreens->sc[] failed\n"));
|
|
return NULL;
|
|
}
|
|
|
|
/* Create each screening object from one defined screen. */
|
|
/* Use the 0'th plane screen */
|
|
/* Stagger the screens with a round of 9 offset */
|
|
for (i = 0; i < t->np; i++) {
|
|
int xoff = ((i % 3) * screens[bi].width)/3;
|
|
int yoff = (((i/3) % 3) * screens[bi].height)/3;
|
|
void *cx = NULL;
|
|
double (*lf)(void *cntx, double in) = 0;
|
|
if (cntx != NULL)
|
|
cx = cntx[i];
|
|
if (lutfunc != NULL)
|
|
lf = lutfunc[i];
|
|
|
|
DBG(("thscreens: creating plane %d/%d thscreen, offset %d %d\n",i,t->np,xoff,yoff));
|
|
if ((t->sc[i] = new_thscreen(screens[bi].width, screens[bi].height, xoff, yoff,
|
|
screens[bi].asp, swap, screens[bi].list[0],
|
|
ie, oebpc, oelev, oevalues, oo, overlap,
|
|
cx, lf)) == NULL) {
|
|
for (--i; i >= 0; i--)
|
|
t->sc[i]->del(t->sc[i]);
|
|
free(t->sc);
|
|
free(t);
|
|
DBG(("thscreens: new_thscreen() failed\n"));
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
DBG(("thscreens: returning nonexact match\n"));
|
|
|
|
return t;
|
|
}
|
|
|
|
/* ----------------------------------------------------------- */
|
|
/* The kernel stocastic screening routine */
|
|
|
|
void thscreen16_8(
|
|
struct _thscreen *t, /* Screening object pointer */
|
|
int width, int height, /* Width and height to screen in pixels */
|
|
int xoff, int yoff, /* Offset into screening pattern (must be +ve) */
|
|
unsigned char *out, /* Output pixel buffer */
|
|
unsigned long opinc, /* Increment between output pixels in components */
|
|
unsigned long opitch, /* Increment between output lines in components */
|
|
unsigned char *_in, /* Input pixel buffer */
|
|
unsigned long ipinc, /* Increment between input pixels in components */
|
|
unsigned long ipitch /* Increment between input lines in components */
|
|
) {
|
|
unsigned short *in = (unsigned short *)_in; /* Pointer to input pixel sized values */
|
|
int *lut = t->lut; /* Copy of 8 or 16 -> 16 bit lookup table */
|
|
unsigned short *ein = in + height * ipitch; /* Vertical end pixel marker */
|
|
unsigned short *ein1; /* Horizontal end pixel markers */
|
|
unsigned char **oth, **eth; /* Current lines start, origin and end in screening table. */
|
|
int thtsize; /* Overall size of threshold table */
|
|
unsigned char **eeth; /* Very end of threshold table */
|
|
|
|
{
|
|
unsigned char **sth; /* Start point of line intable */
|
|
sth = t->thp + (yoff % t->sheight) * t->twidth;
|
|
oth = sth + (xoff % t->swidth); /* Orgin of pattern to start from */
|
|
eth = sth + t->swidth; /* Ending point to wrap back */
|
|
thtsize = t->twidth * t->theight;
|
|
eeth = t->thp + thtsize; /* very end of table */
|
|
}
|
|
|
|
ein1 = in + ipinc * width;
|
|
|
|
/* For each line: */
|
|
for (; in < ein; in += ipitch, ein1 += ipitch, out += opitch) {
|
|
unsigned char **th = oth; /* Threshold table origin */
|
|
unsigned short *ip = in; /* Horizontal input pointer */
|
|
unsigned char *op = out; /* Horizontal output pointer */
|
|
|
|
/* Do pixels one output byte at a time */
|
|
for (; ip < ein1; ip += ipinc, op += opinc) {
|
|
int tt = lut[*ip];
|
|
*op = (unsigned char)th[0][tt];
|
|
if (++th >= eth)
|
|
th -= t->swidth;
|
|
}
|
|
|
|
/* Advance screen table pointers with vertical wrap */
|
|
oth += t->twidth;
|
|
eth += t->twidth;
|
|
if (eth > eeth) {
|
|
oth -= thtsize;
|
|
eth -= thtsize;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
/* We're done with the screening object */
|
|
static void th_del(
|
|
thscreen *t
|
|
) {
|
|
if (t->lut != NULL)
|
|
free(t->lut);
|
|
if (t->thp != NULL)
|
|
free(t->thp);
|
|
free(t);
|
|
}
|
|
|
|
/* Create a new thscreen object */
|
|
/* Return NULL on error */
|
|
thscreen *new_thscreen(
|
|
int width, /* width in pixels of screen */
|
|
int height, /* Height in pixels of screen */
|
|
int xoff, int yoff, /* Pattern offsets into width & height */
|
|
double asp, /* Aspect ratio of screen (== dpiX/dpiY) */
|
|
int swap, /* Swap X & Y to invert aspect ratio & swap width/height */
|
|
ccoord *thli, /* Pointer to list of threshold coordinates */
|
|
sc_iencoding ie, /* Input encoding - must be scie_16 */
|
|
int oebpc, /* Output encoding bits per component - must be 8 */
|
|
int oelev, /* Output encoding levels. Must be <= 2 ^ oebpc */
|
|
int *oevalues, /* Optional output encoding values for each level */
|
|
/* Must be oelev entries. Default is 0 .. oelev-1 */
|
|
sc_oorder oo, /* Output bit ordering */
|
|
double olap, /* Overlap between levels, 0 - 1.0 */
|
|
void *cntx, /* Context for LUT table callback */
|
|
double (*lutfunc)(void *cntx, double in) /* Callback function, NULL if none */
|
|
) {
|
|
thscreen *t; /* Object being created */
|
|
int npix; /* Total pixels in screen */
|
|
double mrang; /* threshold modulation range */
|
|
double **fthr; /* Floating point threshold array */
|
|
int i, j;
|
|
|
|
DBG(("new_thscreen() called, oebpc = %d\n",oebpc));
|
|
DBG(("new_thscreen() called, oelev = %d\n",oelev));
|
|
|
|
/* Sanity check overlap */
|
|
if (olap < 0.0)
|
|
olap = 0.0;
|
|
else if (olap > 1.0)
|
|
olap = 1.0;
|
|
|
|
/* Sanity check parameters */
|
|
if (ie != scie_16) {
|
|
DBG(("new_thscreen() ie %d != scie_16\n",ie));
|
|
return NULL;
|
|
}
|
|
if (oebpc != 8) {
|
|
DBG(("new_thscreen() oebpc %d != 8\n",oebpc));
|
|
return NULL;
|
|
}
|
|
|
|
if (oelev < 2 || oelev > (1 << oebpc)) {
|
|
DBG(("new_thscreen() oelev %d > 2^%d = %d\n",oelev,1 << oebpc,oebpc));
|
|
return NULL;
|
|
}
|
|
|
|
if ((t = (thscreen *)calloc(1, sizeof(thscreen))) == NULL) {
|
|
DBG(("new_thscreen() calloc failed\n"));
|
|
return NULL;
|
|
}
|
|
|
|
/* Instantiation parameters */
|
|
t->ie = ie;
|
|
t->oebpc = oebpc;
|
|
t->oelev = oelev;
|
|
if (oevalues != NULL) {
|
|
for (i = 0; i < t->oelev; i++) {
|
|
if (oevalues[i] >= (1 << t->oebpc)) {
|
|
DBG(("new_thscreen() oevalues[%d] value %d can't fit in %d bits\n",i,oevalues[i],t->oebpc));
|
|
free(t);
|
|
return NULL;
|
|
}
|
|
t->oevalues[i] = oevalues[i];
|
|
}
|
|
} else {
|
|
for (i = 0; i < t->oelev; i++)
|
|
t->oevalues[i] = i;
|
|
}
|
|
|
|
t->oo = oo;
|
|
t->overlap = olap;
|
|
|
|
/* Create a suitable LUT from the given function */
|
|
/* Input is either 8 or 16 bits, output is always 16 bits */
|
|
DBG(("new_thscreen() about to create LUT\n"));
|
|
if ((t->lut = (int *)malloc(sizeof(int) * 65536)) == NULL) {
|
|
free(t);
|
|
DBG(("new_thscreen() malloc of 16 bit LUT failed\n"));
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < 65536; i++) {
|
|
if (lutfunc != NULL) {
|
|
double v = i/65535.0;
|
|
v = lutfunc(cntx, v);
|
|
t->lut[i] = (int)(v * 65535.0 + 0.5);
|
|
} else
|
|
t->lut[i] = i;
|
|
}
|
|
|
|
/* Screen definition parameters */
|
|
if (swap) {
|
|
t->asp = 1.0/asp;
|
|
t->swidth = height;
|
|
t->sheight = width;
|
|
} else {
|
|
t->asp = asp;
|
|
t->swidth = width;
|
|
t->sheight = height;
|
|
}
|
|
DBG(("new_thscreen() target width %d, height %d, asp %f\n",t->swidth,t->sheight,t->asp));
|
|
DBG(("new_thscreen() given width %d, height %d, asp %f\n",width,height,asp));
|
|
|
|
npix = t->swidth * t->sheight; /* Total pixels */
|
|
/* Allow for read of a words worth of pixels from within screen: */
|
|
DBG(("new_thscreen() tot pix %d, lev %d, bpp %d\n",npix,t->oelev,t->oebpc));
|
|
|
|
t->twidth = t->swidth + (8/t->oebpc) -1;
|
|
t->theight = t->sheight;
|
|
|
|
DBG(("new_thscreen() th table size = %d x %d\n",t->twidth,t->theight));
|
|
DBG(("new_thscreen() about to turn screen list into float threshold matrix\n"));
|
|
|
|
/* Convert the list of screen cells into a floating point threshold array */
|
|
fthr = dmatrix(0, t->sheight-1, 0, t->swidth-1); /* Temporary matrix */
|
|
if (swap) {
|
|
double tt = xoff; /* Swap offsets to align with orientation */
|
|
xoff = yoff;
|
|
yoff = tt;
|
|
for (i = 0; i < npix; i++)
|
|
fthr[thli[i].x][thli[i].y] = (double)(i/(npix - 1.0));
|
|
} else {
|
|
for (i = 0; i < npix; i++)
|
|
fthr[thli[i].y][thli[i].x] = (double)(i/(npix - 1.0));
|
|
}
|
|
|
|
/* The range that the screen has to modulate */
|
|
/* over to cross all the thresholds evenly. */
|
|
mrang = 65535.0/(t->oelev - 1.0);
|
|
DBG(("new_thscreen() raw modulation rande = %f\n",mrang));
|
|
|
|
/* Modify the modulation range to accommodate any level overlap */
|
|
if (olap > 0.0 && t->oelev > 2) {
|
|
mrang = ((t->oelev - 2.0) * olap * mrang + 65535.0)/(t->oelev - 1.0);
|
|
DBG(("new_thscreen() modulation adjusted for overlap = %f\n",mrang));
|
|
}
|
|
|
|
/* Init the threshold table. It holds the quantized, encoded output */
|
|
/* values, allowing an input value offset by the screen to be */
|
|
/* thresholded directly into the output value. We allow a guard band at */
|
|
/* each end for the effects of the screen modulating the input value. */
|
|
|
|
DBG(("new_thscreen() about to init threshold table\n"));
|
|
t->tht = &t->_tht[32768]; /* base allows for -ve & +ve range */
|
|
for (i = -32768; i < (2 * 65536) + 32768; i++) {
|
|
if (i < mrang) { /* Lower guard band */
|
|
t->tht[i] = t->oevalues[0];
|
|
} else if (i >= 65535) { /* Upper guard band */
|
|
t->tht[i] = t->oevalues[t->oelev-1];
|
|
} else { /* Middle range */
|
|
t->tht[i] = t->oevalues[1 + (int)((t->oelev - 2.0) * (i - mrang)/(65535.0 - mrang))];
|
|
}
|
|
}
|
|
|
|
/* Allocate the 2D table of pointers into the */
|
|
/* threshold table that encodes the screen offset. */
|
|
if ((t->thp = (unsigned char **)malloc(sizeof(unsigned char *)
|
|
* t->twidth * t->theight)) == NULL) {
|
|
free_dmatrix(fthr, 0, t->sheight-1, 0, t->swidth-1);
|
|
free(t->lut);
|
|
free(t);
|
|
DBG(("new_thscreen() malloc of threshold pointer matrix failed\n"));
|
|
return NULL;
|
|
}
|
|
|
|
/* Setup the threshold pointer array to point into the apropriate */
|
|
/* point into the threshold array itself. This implicitly adds */
|
|
/* the screen pattern offset value to the input before thresholding it. */
|
|
/* The input screen offsets are applied at this point too. */
|
|
DBG(("new_thscreen() about to init threshold pointer table\n"));
|
|
for (i = 0; i < t->twidth; i++) {
|
|
for (j = 0; j < t->theight; j++) {
|
|
double sov = fthr[(j+yoff) % t->sheight][(i+xoff) % t->swidth];
|
|
int tho = (int)((mrang - 1.0) * (1.0 - sov) + 0.5);
|
|
t->thp[j * t->twidth + i] = &t->tht[tho];
|
|
}
|
|
}
|
|
free_dmatrix(fthr, 0, t->sheight-1, 0, t->swidth-1);
|
|
|
|
DBG(("new_thscreen() about to setup method pointers\n"));
|
|
|
|
/* Methods */
|
|
t->screen = thscreen16_8;
|
|
t->del = th_del;
|
|
|
|
DBG(("new_thscreen() done\n"));
|
|
return t;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|