Example #1
0
/* fut_free_otbl_p
		This function is passed a fut_otbl_t pointer
		and handle.	If the ref count is zero, the table and
		the fut_otbl_t is freed.	Otherwise the ref count is 
		decremented and the lock state of the fut_otbl_t
		is returned to it's state on entry.
*/
static void
	fut_free_otbl_p (	fut_otbl_p	otblPtr,
						KpHandle_t	otblHdl)
{
fut_otbl_p	otbl = otblPtr;

	if (otblHdl == NULL) {
		return;
	}

	if (otbl == NULL) {		/* otbl is unlocked on entry */
		otbl = lockBuffer(otblHdl);
	}

	if (IS_OTBL(otbl)) {
		if (otbl->ref == 0) {	/* last reference being freed */
			freeBuffer(otbl->tblHandle);
			otbl->magic = 0;
			freeBufferPtr ((KpGenericPtr_t)otbl);
		}
		else {
			if (otbl->ref > 0) {	/* still other references */
				otbl->ref--;	/* leave in original lock state */
				if (otblPtr == NULL) {
					unlockBuffer(otblHdl);
				}
			}
		}
	}
}
Example #2
0
/* fut_size_otbl returns the size in bytes of an output table */
static KpInt32_t
	fut_size_otbl (	fut_otbl_p	otbl)	
{
	if ( ! IS_OTBL (otbl)) return (0);

	return ((sizeof (KpInt32_t)*3) + (sizeof (fut_otbldat_t)*FUT_OUTTBL_ENT));
}
Example #3
0
/* fut_copy_otbl makes an exact copy of an existing fut_otbl_t
 */
fut_otbl_p
	fut_copy_otbl (fut_otbl_p otbl)
{
fut_otbl_p		new_otbl;
KpHandle_t		h;

	/* check for valid otbl */
	if ( ! IS_OTBL(otbl) ) {
		return (FUT_NULL_OTBL);
	}

	/* allocate the new otbl structure */
	new_otbl = fut_alloc_otbl ();
	if ( new_otbl == FUT_NULL_OTBL ) {
		DIAG("fut_copy_otbl: can't alloc output table struct.\n", 0);
		return (FUT_NULL_OTBL);
	}

	h = new_otbl->handle;	/* save handle before copying over old otbl */

	*new_otbl = *otbl;		/* copy entire struct except reference count */

	new_otbl->handle = h;
	new_otbl->ref = 0;		/* first reference */

	if (otbl->tbl != NULL) {	/* copy fixed otbl data */
		new_otbl->tbl = fut_alloc_otbldat (new_otbl);
		if ( new_otbl->tbl == NULL ) {
			DIAG("fut_copy_otbl: can't alloc output table array.\n", 0);
			goto ErrOut;
		}
		new_otbl->tblHandle = getHandleFromPtr((KpGenericPtr_t)new_otbl->tbl);

		/* copy the table entries */
		KpMemCpy (new_otbl->tbl, otbl->tbl, FUT_OUTTBL_ENT * sizeof (fut_otbldat_t));
	}

	if (otbl->refTbl != NULL) {	/* copy reference otbl data */
		new_otbl->refTbl = fut_alloc_omftdat (new_otbl, new_otbl->refTblEntries);
		if (new_otbl->refTbl == NULL ) {
			DIAG("fut_copy_otbl: can't alloc output table array.\n", 0);
			goto ErrOut;
		}

		/* copy the table entries */
		KpMemCpy (new_otbl->refTbl, otbl->refTbl, new_otbl->refTblEntries * sizeof (mf2_tbldat_t));
	}

	return (new_otbl);
	

ErrOut:
	fut_free_otbl (new_otbl);
	return (FUT_NULL_OTBL);
}
Example #4
0
void
	fut_free_otbldat (	fut_otbl_p		otbl,
						fut_freeMode_t	mode)
{
	if ( IS_OTBL(otbl) ) {
		if ((mode == freeTable) ||
			((mode == freeData) && (otbl->refTbl != NULL))) {

			freeBuffer (otbl->tblHandle);
			otbl->tbl = NULL;
			otbl->tblHandle = NULL;
		}
	}
}
Example #5
0
void
	fut_free_otbl (fut_otbl_p otbl)
{
	if ( ! IS_OTBL(otbl)) {		/* defined? */
		return;
	}

	if (otbl->ref != 0) {					/* last reference? */
		otbl->ref--;
	}
	else {
		fut_free_omftdat (otbl, freeTable);	/* free the data */
		fut_free_otbldat (otbl, freeTable);
		otbl->magic = 0;
		freeBufferPtr ((KpGenericPtr_t)otbl);
	}
}
Example #6
0
/* fut_calc_otbl computes the values of an output table from a user defined
 * function.  Ofun must be a pointer to a function accepting accepting a
 * double and returning a double, both in the range (0.0,1.0). (NULL is a
 * legal value - it just returns, leaving the table uninitialized).
 * fut_calc_otbl returns 0 (FALSE) if an error occurs (ofun returned
 * value out of range) and 1 (TRUE) otherwise.
 */
KpInt32_t
	fut_calc_otblEx (	fut_otbl_p		otbl,
						fut_ofunc_t		ofun,
						fut_calcData_p	data)
{
KpInt32_t		i, mftData;
mf2_tbldat_p	theOtbl;
double			val, indexNorm, indexInc, mftMaxData = MF2_TBL_MAXVAL;
fData_t			fDataL;
fut_calcData_p	fDataP;

	if ( ! IS_OTBL(otbl) ) {
		return (0);
	}

	if (ofun != NULL) {
		otbl->id = fut_unique_id();

		if (data == NULL) {
			fDataP = &fDataL.std;
			fDataL.scale = 1.0;
		}
		else {
			fDataP = data;
		}

		theOtbl = otbl->refTbl;
		indexInc = 1.0 / (double) (otbl->refTblEntries -1);

		for (i = 0, indexNorm = 0.0; i < otbl->refTblEntries; i++, indexNorm += indexInc) {
			val = (*ofun) (indexNorm, fDataP);

			MFT_QUANT(val, mftData)

			theOtbl[i] = (mf2_tbldat_t)mftData;
		}
	}

	return (1);
}
Example #7
0
/* check the input and output data class of a PT
 * if a color space is known and the data class is not known,
 * set the data class to correspond to the color space
 */
void
	checkDataClass (PTRefNum_t	PTRefNum)
{
KpInt32_t		i1;
KpHandle_t		PTData;
fut_p			fut;
fut_chan_p		chan;
fut_otbl_p		otbl;
PTDataClass_t	iDataClass, oDataClass;

	iDataClass = getPTDataClass (PTRefNum, KCM_IN_SPACE);
	oDataClass = getPTDataClass (PTRefNum, KCM_OUT_SPACE);

	PTData = getPTData (PTRefNum);
	fut = fut_lock_fut (PTData);
	if ( ! IS_FUT(fut)) return;		/* bummer */

	checkInDataClass (iDataClass, fut->itbl);	/* check the data class of each shared input table */

	for (i1 = 0; i1 < FUT_NOCHAN; i1++) {
		chan = fut->chan[i1];

		if (IS_CHAN(chan)) {
			checkInDataClass (iDataClass, chan->itbl);	/* check the data class of each input table */

			if (oDataClass != KCP_UNKNOWN) {	/* check the data class of each output table */
				otbl = chan->otbl;
				if ((IS_OTBL(otbl)) && (otbl->dataClass == KCP_UNKNOWN)) {
					otbl->dataClass = oDataClass;
				}
			}
		}
	}
	
	fut_unlock_fut (fut);
}
Example #8
0
/* fut_new_chan allocates and initializes a fut_chan_t data structure.
 * If a required input table is missing, a ramp of the proper grid size
 * will be created.	If a supplied itbl is not required, it will not be
 * inserted into the channel's private itbl list.	All tables which are
 * actually used are copied and so the caller is responsible for
 * freeing the passed tables if necessary.
 *
 * If VARARGS is used, the list of input tables may be relaced by a
 * single array of fut_itbl_t pointers.	This array must then be followed
 * by a fut_gtbl_p and a fut_otbl_p.
 */
fut_chan_p
	fut_new_chan (	KpInt32_t		iomask,
					fut_itbl_p FAR*	itbls,
					fut_gtbl_p		gtbl,
					fut_otbl_p		otbl)
{
fut_itbl_p	itbl[FUT_NCHAN];
fut_chan_p	chan;
KpInt32_t	imask, i, tIndex;

	/* get input mask */
	imask = (KpInt32_t)FUT_IMASK(iomask);

	/* get args specified by imask */
	for ( i=0, tIndex = 0; i<FUT_NCHAN; i++ ) {
		itbl[i] = ((imask & FUT_BIT(i)) && (itbls != NULL)) ? itbls[tIndex++] : NULL;
	}

				/* allocate and clear the fut_chan_t structure */
	chan = fut_alloc_chan ();
	if ( ! IS_CHAN(chan)) {
		return (NULL);
	}

				/* check for valid grid and output tables */
	if (( ! IS_GTBL(gtbl)) || ((otbl != NULL) && ( ! IS_OTBL(otbl))) ) {
		DIAG("fut_new_chan: invalid grid or output table.\n", 0);
		fut_free_chan (chan);
		return (NULL);
	}

	/* get required input channels from gtbl */
	chan->imask = fut_gtbl_imask(gtbl);

				/* insert the required input tables */
	for ( i=0; i<FUT_NICHAN; i++ ) {
		if ( (chan->imask & FUT_BIT(i)) == 0 ) continue;

		if ( itbl[i] == FUT_NULL_ITBL ) {
			chan->itbl[i] = fut_new_itblEx (KCP_REF_TABLES, KCP_FIXED_RANGE, gtbl->size[i], fut_irampEx, NULL);
			if ( chan->itbl[i] == NULL) {
				DIAG("fut_new_chan: can't create itbl.\n",0);
				fut_free_chan (chan);
				return (NULL);
			}

			chan->itblHandle[i] = chan->itbl[i]->handle;
		}
		else {
			if ( ! IS_ITBL (itbl[i])) {
				DIAG("fut_new_chan: invalid input table.\n", 0);
				fut_free_chan (chan);
				return (NULL);
			}
			else {
				if ( itbl[i]->size != gtbl->size[i] ) {
					DIAG("fut_new_chan: gtbl-itbl size mismatch.\n", 0);
					fut_free_chan (chan);
					return (NULL);
				}
				else {
					chan->itbl[i] = fut_share_itbl(itbl[i]);	/* share the input table */
					chan->itblHandle[i] = chan->itbl[i]->handle;
				}
			}
		}
	}

					/* insert grid and output tables */
	chan->gtbl = fut_share_gtbl (gtbl);
	chan->gtblHandle =	(IS_GTBL(chan->gtbl)) ? chan->gtbl->handle : FUT_NULL_HANDLE;
	
	if (IS_OTBL(otbl)) {
		chan->otbl = fut_share_otbl (otbl);
	}
	else {
		chan->otbl = fut_alloc_otbl();
	}

	chan->otblHandle = (IS_OTBL(chan->otbl)) ? chan->otbl->handle : FUT_NULL_HANDLE;

	return (chan);
}
Example #9
0
void
	evalTh1gen (	imagePtr_p	inp,
					KpInt32_p	inStride,
					KpUInt32_t	dataTypeI,
					imagePtr_p	outp,
					KpInt32_p	outStride,
					KpUInt32_t	dataTypeO,
					KpInt32_t	n,
					PTTable_p	PTTableP)
{
imagePtr_t		inData[FUT_NICHAN], outData[FUT_NOCHAN];
KpInt32_t		inStrideL[FUT_NICHAN], outStrideL[FUT_NOCHAN];
KpInt32_t		i1, separableFut, numInputs, numOutputs, gDimSize[FUT_NOCHAN], oTblEntries[FUT_NOCHAN];
division_t		iIndexFactor[FUT_NICHAN], gIndexFactor[FUT_NICHAN], oIndexFactor[FUT_NOCHAN];
KpInt32_t		oDataShift, oDataRound, oDataFactor, dataMax, oDataBits;
fut_p			fut;
fut_itbl_p		iTbl[FUT_NICHAN], theITbl;
fut_chan_p		chan[FUT_NOCHAN], theChan;
mf2_tbldat_p	gTbl[FUT_NOCHAN], oTbl[FUT_NOCHAN];
KpInt32_p		BoseSort[FUT_NICHAN] = {BoseSort1, BoseSort2, BoseSort3, BoseSort4, BoseSort5, BoseSort6, BoseSort7, BoseSort8};
mf2_tbldat_t	identityTable[2] = {0, MF2_TBL_MAXVAL};

	fut = PTTableP->dataP;

	separableFut = fut_is_separable (fut);		/* check for separable (linearization) fut */

	/* set up input table stuff */
	switch (dataTypeI) {
	case KCM_UBYTE:
		dataMax = (1 << 8) -1;
		break;

	case KCM_USHORT_12:
		dataMax = (1 << 12) -1;
		break;

	case KCM_USHORT:
		dataMax = (1 << 16) -1;
		break;
		
	default:
		dataMax = 1;
	}

	for (i1 = 0, numInputs = 0; i1 < FUT_NICHAN; i1++) {
		if (inp[i1].p8 != NULL) {
			inData[numInputs].p8 = inp[i1].p8;	/* copy addresses - do not change supplied lists! */
			inStrideL[numInputs] = inStride[i1];

			theITbl = fut->itbl[i1];
			if ( ! IS_ITBL(theITbl)) {
				return;
			}

			iTbl[numInputs] = theITbl;					/* collect the input tables */

			/* set up interpolation into input table */
			doDivide (theITbl->refTblEntries -1, dataMax, iIndexFactor[numInputs]);	/* set up interpolation into input table */

			/* set up interpolation into grid table */
			gDimSize[i1] = theITbl->size;	/* save in case of separable fut */
			doDivide (gDimSize[i1] -1, MF2_TBL_MAXVAL, gIndexFactor[numInputs]);	/* set up interpolation into input table */
			
			numInputs++;
		}
	}

	/* set up grid and output table stuff */
	for (i1 = 0, numOutputs = 0; i1 < FUT_NOCHAN; i1++) {
		if (outp[i1].p8 != NULL) {
			fut_otbl_p	theOTbl;

			outData[numOutputs].p8 = outp[i1].p8;	/* copy addresses - do not update supplied lists! */
			outStrideL[numOutputs] = outStride[i1];

			theChan = fut->chan[i1];
			if ( ! IS_CHAN(theChan)) {
				return;
			}

			chan[numOutputs] = theChan;

			gTbl[numOutputs] = theChan->gtbl->refTbl;	/* get the grid */

			theOTbl = theChan->otbl;		/* set up interpolation into output table */
			if ( ! IS_OTBL(theOTbl) || ((oTbl[numOutputs] = theOTbl->refTbl) == NULL)) {
				oTbl[numOutputs] = identityTable;
				oTblEntries[numOutputs] = 2;
			}
			else {
				oTblEntries[numOutputs] = theOTbl->refTblEntries;
			}

			doDivide (oTblEntries[numOutputs] -1, MF2_TBL_MAXVAL, oIndexFactor[numOutputs]);	/* set up interpolation into input table */

			numOutputs++;
		}
	}
	
	/* set up output data scaling */
	switch (dataTypeO) {
	case KCM_UBYTE:
		oDataBits = 8;
		break;

	case KCM_USHORT_12:
		oDataBits = 12;
		break;

	case KCM_USHORT:
		oDataBits = 16;
		break;
		
	default:
		dataMax = 1;
	}

	dataMax = (1 << oDataBits) -1;
	oDataShift = 32 -1 - oDataBits;
	oDataFactor = (dataMax << oDataShift) / MF2_TBL_MAXVAL;
	oDataRound = (1 << (oDataShift -1)) -1;


	/* all set up; evaluate each pixel */
	for (i1 = 0; i1 < n; i1++) {
		KpInt32_t	cell, i2, index, dimSize, numCompares, hVert[FUT_NICHAN];
		KpInt32_t	sPosition, iTableData[FUT_NICHAN], hFrac[FUT_NICHAN];
		KpInt32_p	BoseSortP;

		for (i2 = 0, cell = 0; i2 < numInputs; i2++) {
			KpInt32_t	srcData, interpData;

			if (dataTypeI == KCM_UBYTE) {
				srcData = (KpInt32_t) (*inData[i2].p8); 	/* get 8 bit input data */
			}
			else {
				srcData = (KpInt32_t) (*inData[i2].p16); 	/* get 12/16 bit input data */
			}
						
			inData[i2].p8 += inStrideL[i2];

			/* pass source image data through the input table */
			theITbl = iTbl[i2];
			interpData = interp1DTable (theITbl->refTbl, theITbl->refTblEntries, srcData, iIndexFactor[i2]);
			iTableData[i2] = interpData;	/* save in case of separable fut */

			doMultiply (interpData, gIndexFactor[i2], sPosition);	/* calculate the input table position */
			index = sPosition >> EVAL_FRACBITS;

			dimSize = theITbl->size;					/* size of this dimension */

			if (index < dimSize -1) {
				hFrac[i2] = sPosition & ((1 << EVAL_FRACBITS) -1);	/* get grid interpolant */
			}
			else {
				hFrac[i2] = (1 << EVAL_FRACBITS) -1;
				index--;
			}

			hVert[i2] = dimSize;		/* save for offset calcs */
			cell *= dimSize;			/* build cell index */
			cell += index;				/* add in this index */
		}

		/* build offsets for each dimension */
		index = 2;
		for (i2 = numInputs-1; i2 >= 0; i2--) {
			dimSize = hVert[i2];
			hVert[i2] = index;
			index *= dimSize;
		}

		/* find the hyperhedron in which the interpolation point is located */
		BoseSortP = BoseSort[numInputs -1];
		numCompares = *BoseSortP++;		/* first element is # of compares */
		
		for (i2 = 0; i2 < numCompares; i2++) {
			KpInt32_t	tmpI, index1, index2;

			index1 = *BoseSortP++;
			index2 = *BoseSortP++;
			
			/* sort into largest to smallest based upon interpolants */
			tmpI = hFrac[index1];
			if (tmpI < hFrac[index2]) {
				hFrac[index1] = hFrac[index2];	/* swap interpolants */
				hFrac[index2] = tmpI;

				tmpI = hVert[index1];			/* swap vertices */
				hVert[index1] = hVert[index2];
				hVert[index2] = tmpI;
			}
		}

		/* evaluate each output channel */
		for (i2 = 0; i2 < numOutputs; i2++) {
			KpInt32_t	i3, tResult, oTableData, previousVertex, thisVertex;
			KpUInt8_p	vertexP;

			if (separableFut == 1) {
				tResult = interp1DTable (gTbl[i2], gDimSize[i2], iTableData[i2], gIndexFactor[i2]);
			}
			else {		/* hyperhedral interpolation */		
				vertexP = (KpUInt8_p)(gTbl[i2] + cell);

				previousVertex = (KpInt32_t) *(mf2_tbldat_p)(vertexP);
				tResult = previousVertex << (EVAL_FRACBITS - EVAL_EXTENDED_BITS);

				for (i3 = 0; i3 < numInputs; i3++) {
					vertexP += hVert[i3];
					thisVertex = (KpInt32_t) *(mf2_tbldat_p)(vertexP);

					interpolateDelta (previousVertex, thisVertex, hFrac[i3], previousVertex)
					tResult += previousVertex;

					previousVertex = thisVertex;
				}

				tResult += ROUND_VALUE(EVAL_FRACBITS - EVAL_EXTENDED_BITS);
				KCP_SHIFT_RIGHT(tResult, tResult, EVAL_FRACBITS - EVAL_EXTENDED_BITS);
			}

			/* output table lookup */
			oTableData = interp1DTable (oTbl[i2], oTblEntries[i2], tResult, oIndexFactor[i2]);
			oTableData *= oDataFactor;		/* convert to dest size */
			oTableData += oDataRound;		/* round */
			oTableData >>= oDataShift;		/* remove fractional bits */

			if (dataTypeO == KCM_UBYTE) {
				*outData[i2].p8 = (KpUInt8_t) oTableData;
			}
			else {
				*outData[i2].p16 = (KpUInt16_t) oTableData;
			}
			
			outData[i2].p8 += outStrideL[i2];	/* next output data location */
		}
	}
}
Example #10
0
/* fut_comp_iotblMF composes an output table with an input table.  The composite table
 * has the same data width and format as an input table, but has the number of entries of
 * an output table.
 */
static KpInt32_t
	fut_comp_iotblMF (	fut_itbl_p	itbl,
						fut_otbl_p	otbl,
						fut_itbl_p	dstitbl)
{
mf2_tbldat_t	x, intDestData, theOTbl[MF2_MAX_TBL_ENT];
mf2_tbldat_t	identOTbl[2] = {0, MF2_TBL_MAXVAL};
mf2_tbldat_p	srcOTblP, theOTblP;
fut_otbldat_p	optr;
KpInt32_t	i, tableIndex, tableIndexNext, srcOTblEntries;
KpFloat32_t	index, tableFrac, srcData, srcData1, destData, indexRatio;

	if ( ! IS_ITBL(itbl) || ! IS_OTBL(otbl) || ! IS_ITBL(dstitbl)) {
		return 0;
	}

	if (otbl->refTblEntries > dstitbl->refTblEntries) {
		return 0;	/* screwed up somewhere */
	}

	if ((srcOTblP = otbl->refTbl) == NULL) {
		srcOTblP = identOTbl;	/* null means identity */
		srcOTblEntries = 2;
	}
	else {
		srcOTblEntries = otbl->refTblEntries;
	}
	
	if (otbl->refTblEntries == dstitbl->refTblEntries) {
		theOTblP = otbl->refTbl;
	}
	else {	/* expand current otbl to specified size */
		theOTblP = theOTbl;

		convert1DTable (srcOTblP, sizeof (mf2_tbldat_t), srcOTblEntries, MF2_TBL_MAXVAL,
						theOTblP, sizeof (mf2_tbldat_t), dstitbl->refTblEntries, MF2_TBL_MAXVAL,
						KCP_MAP_END_POINTS, KCP_MAP_END_POINTS);
	}

	/* compose output table into this input table */
	optr = dstitbl->refTbl;
	indexRatio = (KpFloat32_t) (itbl->refTblEntries -1) / (KpFloat32_t) MF2_TBL_MAXVAL;

	for (i = 0; i < dstitbl->refTblEntries; i++) {
		x = theOTblP [i];	/* interpolation value */

		index = (KpFloat32_t) x * indexRatio;			/* calculate the input table position */
		tableIndex = (KpInt32_t) index;					/* the input table index */
		tableFrac = index - (KpFloat32_t) tableIndex;	/* and the input table interpolant */

		if (tableIndex >= itbl->refTblEntries) {	/* make sure we're in range for interpolation */
			tableIndex = itbl->refTblEntries -1;	/* 1st source is past end */
			tableIndexNext = tableIndex;
		}
		else {
			tableIndexNext = tableIndex +1;

			if (tableIndexNext == itbl->refTblEntries) {
				tableIndexNext = tableIndex;		/* 1st source is at end */
			}
		}

		srcData = (KpFloat32_t) itbl->refTbl [tableIndex];
		srcData1 = (KpFloat32_t) itbl->refTbl [tableIndexNext];

		destData = srcData + (tableFrac * (srcData1 - srcData));	/* interpolate */

		/* round and convert to integer */
		intDestData = (mf2_tbldat_t)(destData + 0.5);		
		if (intDestData > (mf2_tbldat_t)MF2_TBL_MAXVAL) {
			intDestData = MF2_TBL_MAXVAL;
		}

		optr [i] = intDestData;
	}

	return 1;
}
Example #11
0
/*---------------------------------------------------------------------------
 *  makeInverseXformFromMatrix -- make a fut of given gridsize from given
 *	matrix data for inverse transform (XYZ -> RGB); return status code
 *---------------------------------------------------------------------------
 */
PTErr_t
	makeInverseXformFromMatrix (LPMATRIXDATA	mdata,
								KpUInt32_t		interpMode,
								KpInt32_p		dim,
								fut_p			theFut)
{
PTErr_t			PTErr = KCP_SUCCESS;
ResponseRecord_p	rrp;
KpInt32_t		i;
fut_chan_p		theChan;
fut_gtbl_p		theGtbl;
fut_otbl_p		theOtbl;
mf2_tbldat_p	gtblDat[3], otblDat, prevOtblDat;
KpUInt16_t		prevGamma = 0, thisGamma;
double			fwdgamma, one[3];
double			offset[3] = {1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0};
KpUInt16_t		*pCurveData = NULL;

	for (i = 0; i < 3; i++) {
		if (!IS_CHAN(theChan = theFut->chan[i])
			|| !IS_GTBL(theGtbl = theChan->gtbl)
			|| ((gtblDat[i] = theGtbl->refTbl) == NULL) 	/* Get grid tables */
			|| !IS_OTBL(theOtbl = theChan->otbl)
			|| ((otblDat = theOtbl->refTbl) == NULL)) {		/* Get output table */
		   return KCP_INCON_PT;
		}
		
		if (theOtbl->refTblEntries != FUT_OUTTBL_ENT) return KCP_INCON_PT;

		 /* Get ResponseRecord:  */
		rrp = mdata->outResponse[i];
		if (NULL == rrp) {
			break;				/* must only have output tables */
		}
		if (PARA_TYPE_SIG == rrp->TagSig)
		{
			pCurveData = (KpUInt16_p) allocBufferPtr (MFV_CURVE_TBL_ENT*sizeof(KpUInt16_t));	/* get memory for curve data */
			if (NULL == pCurveData) {
				return KCP_NO_MEMORY;
			}
			makeCurveFromPara (rrp->ParaFunction, rrp->ParaParams, pCurveData, MFV_CURVE_TBL_ENT);
			rrp->CurveCount = MFV_CURVE_TBL_ENT;
			rrp->CurveData = pCurveData;
		}
		if ((rrp->CurveCount > 0) && (rrp->CurveData == (KpUInt16_p)NULL)) {
			PTErr = KCP_INCON_PT;
			goto ErrOut;
		}

		 /* Recompute output table:  */
		switch (rrp->CurveCount) {
		case 0:	/* linear response, with clipping */
			calcOtbl0 (otblDat);
			break;
			
		case 1:	/* power law */
			thisGamma = rrp->CurveData[0];
			if (prevGamma == thisGamma) {	/* same gamma, just copy table */
				memcpy (otblDat, prevOtblDat, sizeof (*otblDat) * FUT_OUTTBL_ENT);
			}
			else {					
				prevGamma = thisGamma;
				prevOtblDat = otblDat;

				fwdgamma = (double)thisGamma / SCALEDOT8;
				if (fwdgamma <= 0.0) {
					PTErr = KCP_INCON_PT;
					goto ErrOut;
				}
				calcOtbl1 (otblDat, fwdgamma);
			}
			break;
			
		default:	/* look-up table of arbitrary length */
			makeInverseMonotonic (rrp->CurveCount, rrp->CurveData);

			if (rrp->CurveCount == theOtbl->refTblEntries) {	/* ready-to-use look-up table */
				memcpy (otblDat, rrp->CurveData, sizeof (*otblDat) * rrp->CurveCount);
			}
			else {
				PTErr = calcOtblN (otblDat, rrp, interpMode);
				if (PTErr != KCP_SUCCESS) {
					PTErr = KCP_INCON_PT;
					goto ErrOut;
				}
			}

			break;
		}
	}

	/* Compute inverse matrix (XYZ -> RGB):  */
	one[0] = one[1] = one[2] = 1.0;			/* arbitrary vector */

	 /* replaces matrix with inverse */
	if (solvemat (3, mdata->matrix, one) != 0) {
		PTErr = KCP_INCON_PT;
		goto ErrOut;
	}

	/* Rescale given matrix by factor of 3 for extended range:  */
	for (i = 0; i < 3; i++) {
		KpInt32_t	j;

		for (j = 0; j < 3; j++) {
			mdata->matrix[i][j] /= 3.0;
		}
	}

    /* Replace grid tables:  */
	calcGtbl3 (gtblDat, dim, mdata->matrix, offset);	/* with offset */

ErrOut:
	if (NULL != pCurveData) {
		freeBufferPtr (pCurveData);
	}
	return PTErr;
}