1369 lines
57 KiB
C
1369 lines
57 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/* $XConsortium: ilpipe.c /main/6 1996/06/19 12:21:13 ageorge $ */
|
|
/**---------------------------------------------------------------------
|
|
***
|
|
*** (c)Copyright 1991 Hewlett-Packard Co.
|
|
***
|
|
*** RESTRICTED RIGHTS LEGEND
|
|
*** Use, duplication, or disclosure by the U.S. Government is subject to
|
|
*** restrictions as set forth in sub-paragraph (c)(1)(ii) of the Rights in
|
|
*** Technical Data and Computer Software clause in DFARS 252.227-7013.
|
|
*** Hewlett-Packard Company
|
|
*** 3000 Hanover Street
|
|
*** Palo Alto, CA 94304 U.S.A.
|
|
*** Rights for non-DOD U.S. Government Departments and Agencies are as set
|
|
*** forth in FAR 52.227-19(c)(1,2).
|
|
***
|
|
***-------------------------------------------------------------------*/
|
|
|
|
/* /ilc/ilpipe.c : General pipe handling code.
|
|
Also contains ilCrop() as this function is very much tied to the
|
|
implementation of pipes.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "ilcodec.h"
|
|
#include "ilint.h"
|
|
#include "ilimage.h"
|
|
#include "ilpipelem.h"
|
|
#include "ilpipeint.h"
|
|
#include "ilerrors.h"
|
|
|
|
/* One element in the pipe, added by ilAddElement(). Notes:
|
|
|
|
pNext, pPrev forward/back ptrs to next/prev element in list. First element
|
|
in list is *ilPipePtr->elementHead.pNext; last is *pPrev.
|
|
|
|
elementType One of: IL_PRODUCER, IL_FILTER, IL_CONSUMER. Not needed or
|
|
used, but very handy for debugging.
|
|
|
|
flags flag values, e.g. IL_ADD_PIPE_NO_DST
|
|
|
|
exec par block to Execute() for this element: ptrs to src/dst image
|
|
src/dst lines to read.
|
|
|
|
Init()
|
|
Cleanup()
|
|
Destroy()
|
|
Execute() functions passed to ilAddPipeElement(); see spec for details
|
|
|
|
clientPrivate start of client's private area; MUST BE LAST
|
|
*/
|
|
typedef struct _ilElementRec {
|
|
struct _ilElementRec *pNext, *pPrev;
|
|
unsigned int elementType;
|
|
unsigned long flags;
|
|
ilExecuteData exec;
|
|
ilError (*Init)();
|
|
ilError (*Cleanup)();
|
|
ilError (*Destroy)();
|
|
ilError (*ExecuteThree)();
|
|
ilError (*ExecuteFour)(ilExecuteData *,
|
|
long,
|
|
long *,
|
|
float);
|
|
unsigned long clientPrivate; /* client private: MUST BE LAST */
|
|
} ilElementRec, *ilElementPtr;
|
|
|
|
|
|
/* Actual contents of a ilPipe. Notes:
|
|
|
|
o std object header: MUST BE FIRST
|
|
|
|
state current state of the pipe as returned by ilGetPipeInfo(); one
|
|
of:
|
|
|
|
IL_PIPE_INVALID an error occurred in forming the pipe, and the pipe
|
|
cannot be used until ilEmptyPipe() is done. No more
|
|
elements should be added. The pipe is in fact empty,
|
|
only the state is different, to prevent other elements
|
|
from being added. This state is set by calling
|
|
ilDeclarePipeInvalid().
|
|
|
|
IL_PIPE_EMPTY pipe is empty; ready to add a producer (read)
|
|
|
|
IL_PIPE_FORMING pipe has a producer and zero or more filters; ready to
|
|
add filters or a consumer (write)
|
|
|
|
IL_PIPE_COMPLETE pipe has a consumer (write); ilExecutePipe() can now
|
|
be called on it (executable, from caller's view).
|
|
Error if an attempt to add an element.
|
|
|
|
IL_PIPE_EXECUTING one or more, but not all, strips have been processed.
|
|
|
|
producerCode code that defines producer; one of:
|
|
|
|
IL_PIPE_NOT_IMAGE the producer was not an image
|
|
IL_PIPE_IMAGE producer was an ilReadImage() call
|
|
IL_PIPE_FEED_IMAGE producer was an ilFeedFromImage() call
|
|
|
|
feedStartLine
|
|
feedNLines
|
|
feedCompOffset
|
|
feedCompNBytes
|
|
feedDone used only if producerCode == IL_PIPE_FEED_IMAGE: values
|
|
passed to / used by ilSetFeedPipeData()
|
|
|
|
elementHead head/tail of list of ilElementRec, the elements in the pipe
|
|
|
|
hookHead head/tail of list of IL_HOOK ilElementRec, the pseudo-elements
|
|
in the pipe of type "hook" - not in elementHead list.
|
|
|
|
stackIndex index of top of "execStack"; see below.
|
|
|
|
execStack stack of ptrs to elements that returned IL_ERROR_ELEMENT_AGAIN
|
|
"stackIndex" points to the top of stack. [0] contains the
|
|
first element in the list.
|
|
|
|
nDestroyObjects # of elements in "destroyObjects" array; see below.
|
|
|
|
destroyObjects an array of "nDestroyObjects" objects which must be destroyed
|
|
(by calling ilDestroyObject()) when the pipe is emptied. This
|
|
array is added to by calling ilAddPipeDestroyObject().
|
|
|
|
lastStrip inited to false when pipe started, set true when "last strip"
|
|
error returned from an element. Pipe execution is complete
|
|
when lastStrip && stackIndex <= 0 (> 0 => last strip has still
|
|
not made it all the way through the pipe).
|
|
|
|
needProducerThrottle true iff ilAddProducerImage() just called: pipe began
|
|
with an ilReadImage() and no filters added yet.
|
|
|
|
copyToConsumerImage true iff a copy filter must be inserted if the next
|
|
element is a consumer, specifying a consumerImage, e.g.
|
|
is ilWriteImage(). This would be true right after
|
|
ilAddProducerImage() is called, or right after any
|
|
IL_ADD_PIPE_NO_DST filter.
|
|
|
|
imageHead head/tail of list of ilImageRec, the temporary images
|
|
associated with this pipe. The images object prev/next ptrs
|
|
are used to maintain this list. Note that the images in this
|
|
list are not official objects; they must be deleted when then
|
|
the pipe is emptied. The images have buffers only when state
|
|
is IL_PIPE_EXECUTING; the list is null when state is IL_PIPE
|
|
INVALID or EMPTY.
|
|
|
|
pSrcPipeImage if non-null, points the source (input) pipe image to the next
|
|
element to be added. If null, must allocate a src image for
|
|
the next element unless it is a ilWriteImage().
|
|
|
|
image describes the current pipe image. The values in this struct
|
|
meaningful only if pipe forming (state == IL_PIPE_FORMING).
|
|
The fields are as returned by ilGetPipeInfo().
|
|
*/
|
|
|
|
#define IL_MAX_EXEC_STACK 100
|
|
#define IL_MAX_DESTROY_OBJECTS 10
|
|
|
|
typedef struct {
|
|
ilObjectRec o; /* std header: MUST BE FIRST */
|
|
unsigned int state;
|
|
unsigned int producerCode;
|
|
long feedStartLine, feedNLines, feedCompOffset, feedCompNBytes;
|
|
ilBool feedDone;
|
|
ilElementRec elementHead;
|
|
ilElementRec hookHead;
|
|
int stackIndex;
|
|
ilElementPtr execStack [IL_MAX_EXEC_STACK];
|
|
int nDestroyObjects;
|
|
ilObject destroyObjects [IL_MAX_DESTROY_OBJECTS];
|
|
ilBool lastStrip;
|
|
ilBool needProducerThrottle;
|
|
ilBool copyToConsumerImage;
|
|
ilObjectRec imageHead;
|
|
ilImageInfo *pSrcPipeImage;
|
|
struct {
|
|
ilPipeInfo info;
|
|
ilImageDes des;
|
|
ilImageFormat format;
|
|
} image;
|
|
} ilPipeRec, *ilPipePtr;
|
|
|
|
|
|
/* Default optimal strip size, in bytes. Used by ilRecommendedStripHeight().
|
|
NOTE: currently the same as TIFF strip size, "IL_WRITE_TIFF_STRIP_SIZE" in
|
|
/ilc/iltiffwrite.c . The two are not directly coupled, but making them different
|
|
may adversely affect performance, e.g. cause de/recompression!
|
|
*/
|
|
static long ilDefaultStripSize = (16 * 1024);
|
|
|
|
/* ------------------------ _ilSetDefaultStripSize ------------------------ */
|
|
/* "Back door" call to set ilDefaultStripSize. The IL test suite uses this
|
|
call to ensure that small images are stripped, and so that strip
|
|
size is consistent between test baselevels.
|
|
*/
|
|
void _ilSetDefaultStripSize (
|
|
long stripSize
|
|
)
|
|
{
|
|
ilDefaultStripSize = stripSize;
|
|
}
|
|
|
|
|
|
/* ------------------------ ilFreeTempImageBuffers ------------------------ */
|
|
/* Free the buffers associated with the temp images of this pipe. The image
|
|
structures are not freed; just the pixels.
|
|
*/
|
|
static void ilFreeTempImageBuffers (
|
|
ilPipePtr pPipe
|
|
)
|
|
{
|
|
ilImagePtr pImage;
|
|
|
|
pImage = (ilImagePtr)pPipe->imageHead.pNext;
|
|
while (pImage != (ilImagePtr)&pPipe->imageHead) {
|
|
_ilFreeImagePixels (pImage);
|
|
pImage = (ilImagePtr)pImage->o.pNext;
|
|
}
|
|
}
|
|
|
|
/* -------------------------- ilAllocTempImage ----------------------- */
|
|
/* Allocates a temporary (buffer) image, inits it and returns a ptr to it or
|
|
null if failure. The buffer for the pixels is not allocated. The
|
|
height of the image is not set; the caller must do that.
|
|
*/
|
|
static ilImagePtr ilAllocTempImage (
|
|
ilPipePtr pPipe,
|
|
ilPipeInfo *pInfo,
|
|
ilImageDes *pDes,
|
|
ilImageFormat *pFormat
|
|
)
|
|
{
|
|
ilImagePtr pImage;
|
|
|
|
pImage = (ilImagePtr)IL_MALLOC (sizeof (ilImageRec));
|
|
if (!pImage)
|
|
return (ilImagePtr)NULL; /* EXIT */
|
|
|
|
pImage->des = *pDes;
|
|
pImage->format = *pFormat;
|
|
pImage->pStripOffsets = (long *)NULL; /* freed if non-null */
|
|
|
|
pImage->i.pDes = &pImage->des;
|
|
pImage->i.pFormat = &pImage->format;
|
|
pImage->i.width = pInfo->width;
|
|
pImage->i.height = 0;
|
|
|
|
/* Set clientPixels/Palette/CompData true so they are *not* deallocated */
|
|
pImage->i.clientPixels = TRUE;
|
|
pImage->i.clientPalette = TRUE;
|
|
pImage->i.clientCompData = TRUE;
|
|
pImage->i.pPalette = pInfo->pPalette;
|
|
pImage->i.pCompData = pInfo->pCompData;
|
|
|
|
/* Link image into list of temp images, using object pPrev/pNext ptrs.
|
|
*/
|
|
pImage->o.pNext = pPipe->imageHead.pNext;
|
|
pImage->o.pPrev = (ilPtr)&pPipe->imageHead;
|
|
((ilImagePtr)pPipe->imageHead.pNext)->o.pPrev = (ilPtr)pImage;
|
|
pPipe->imageHead.pNext = (ilPtr)pImage;
|
|
|
|
return pImage;
|
|
}
|
|
|
|
|
|
/* ------------------------ ilCleanupRunningPipe ----------------------------- */
|
|
/* Cleanup a pipe which had been running, i.e. its state was IL_PIPE_EXECUTING.
|
|
"aborting" should be TRUE if the pipe is being aborted (was not finished).
|
|
The pipe state is set to IL_PIPE_COMPLETE.
|
|
*/
|
|
static ilError ilCleanupRunningPipe (
|
|
ilPipePtr pPipe,
|
|
ilBool aborting
|
|
)
|
|
{
|
|
ilElementPtr pElement, pElementHead;
|
|
ilError error, prevError;
|
|
|
|
/* Run thru the list of elements and call Cleanup function if non-null, then
|
|
do same for "hook" list. Pass given aborting flag to Cleanup().
|
|
If an error return, switch to "aborting" mode and return first error returned.
|
|
*/
|
|
prevError = IL_OK;
|
|
pElementHead = &pPipe->elementHead;
|
|
while (TRUE) {
|
|
pElement = pElementHead->pNext;
|
|
while (pElement != pElementHead) {
|
|
if (pElement->Cleanup) {
|
|
error = (*pElement->Cleanup) ((ilPtr)pElement->exec.pPrivate, aborting);
|
|
if (error) {
|
|
if (prevError == IL_OK)
|
|
prevError = error;
|
|
aborting = TRUE; /* error; now considered aborted */
|
|
}
|
|
}
|
|
pElement = pElement->pNext;
|
|
}
|
|
if (pElementHead == &pPipe->hookHead)
|
|
break; /* both lists scanned; done */
|
|
pElementHead = &pPipe->hookHead; /* run thru hook list */
|
|
}
|
|
|
|
ilFreeTempImageBuffers (pPipe);
|
|
pPipe->stackIndex = 0;
|
|
pPipe->state = IL_PIPE_COMPLETE;
|
|
return prevError;
|
|
}
|
|
|
|
|
|
/* ------------------------ ilEmptyPipe -------------------------------- */
|
|
/* Public function: see spec.
|
|
Also called locally, for example when aborting a pipe.
|
|
*/
|
|
ilBool ilEmptyPipe (
|
|
ilPipe pipe
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
ilElementPtr pElement, pNextElement, pElementHead;
|
|
ilImagePtr pImage, pNextImage;
|
|
int i;
|
|
ilError error;
|
|
|
|
pPipe = (ilPipePtr)pipe;
|
|
if (pPipe->o.p.objectType != IL_PIPE) {
|
|
pPipe->o.p.context->error = IL_ERROR_OBJECT_TYPE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* Action to empty a pipe depends on current state. Transition from running
|
|
(abort) to complete (empty element list) to invalid (mark as empty).
|
|
FALL THRU (no break) from each state.
|
|
*/
|
|
error = IL_OK; /* assume no error; log error from Cleanup() */
|
|
switch (pPipe->state) {
|
|
case IL_PIPE_EXECUTING: /* executing: must abort, fall thru to complete */
|
|
error = ilCleanupRunningPipe (pPipe, TRUE);
|
|
|
|
case IL_PIPE_COMPLETE: /* elements but no buffers allocated */
|
|
case IL_PIPE_FORMING: /* forming is same as complete */
|
|
|
|
/* Run thru list of elements and free them. Call optional Destroy
|
|
function if non-null. Make list head empty.
|
|
*/
|
|
pElementHead = &pPipe->elementHead;
|
|
while (TRUE) {
|
|
pElement = pElementHead->pNext;
|
|
while (pElement != pElementHead) {
|
|
if (pElement->Destroy)
|
|
(*pElement->Destroy) ((ilPtr)pElement->exec.pPrivate);
|
|
pNextElement = pElement->pNext;
|
|
IL_FREE (pElement);
|
|
pElement = pNextElement;
|
|
}
|
|
if (pElementHead == &pPipe->hookHead)
|
|
break; /* both lists scanned; done */
|
|
pElementHead = &pPipe->hookHead; /* run thru hook list */
|
|
}
|
|
pPipe->elementHead.pNext = pPipe->elementHead.pPrev = &pPipe->elementHead;
|
|
pPipe->hookHead.pNext = pPipe->hookHead.pPrev = &pPipe->hookHead;
|
|
|
|
/* Destroy all temp images associated with this pipe. The buffers are
|
|
already gone, because they only exist when state is IL_PIPE_EXECUTING,
|
|
and ilCleanupRunningPipe() would be called in that case.
|
|
*/
|
|
pImage = (ilImagePtr)pPipe->imageHead.pNext;
|
|
while (pImage != (ilImagePtr)&pPipe->imageHead) {
|
|
pNextImage = (ilImagePtr)pImage->o.pNext;
|
|
IL_FREE (pImage);
|
|
pImage = pNextImage;
|
|
}
|
|
pPipe->imageHead.pNext = pPipe->imageHead.pPrev = (ilPtr)&pPipe->imageHead;
|
|
|
|
/* Destroy all objects in destroyObjects array. */
|
|
for (i = 0; i < pPipe->nDestroyObjects; i++)
|
|
ilDestroyObject (pPipe->destroyObjects[i]);
|
|
pPipe->nDestroyObjects = 0;
|
|
|
|
case IL_PIPE_INVALID: /* pipe already empty; change state */
|
|
pPipe->state = IL_PIPE_EMPTY;
|
|
|
|
case IL_PIPE_EMPTY: /* pipe in desired state */
|
|
break;
|
|
}
|
|
|
|
pPipe->o.p.context->error = error;
|
|
return (error == IL_OK);
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------ ilCreatePipe -------------------------------- */
|
|
/* Public function: see spec.
|
|
*/
|
|
|
|
ilPipe ilCreatePipe (
|
|
ilContext context,
|
|
unsigned long mustBeZero
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
|
|
if (mustBeZero != 0) {
|
|
context->error = IL_ERROR_PAR_NOT_ZERO;
|
|
return (ilPipe)NULL;
|
|
}
|
|
|
|
pPipe = (ilPipePtr)_ilCreateObject (context, IL_PIPE, ((void (*)())ilEmptyPipe),
|
|
sizeof (ilPipeRec));
|
|
if (!pPipe)
|
|
return (ilPipe)NULL;
|
|
|
|
/* Declare pipe empty, set element and temp image lists to empty state.
|
|
The rest of the pipe state must be set by ilAddPipeElement() on each element.
|
|
Init some of elementHead, which itself is an element, and will be
|
|
referenced as the current element in a pipe with no elements.
|
|
*/
|
|
pPipe->state = IL_PIPE_EMPTY;
|
|
pPipe->elementHead.pNext = pPipe->elementHead.pPrev = &pPipe->elementHead;
|
|
pPipe->hookHead.pNext = pPipe->hookHead.pPrev = &pPipe->hookHead;
|
|
pPipe->imageHead.pNext = pPipe->imageHead.pPrev = (ilPtr)&pPipe->imageHead;
|
|
pPipe->elementHead.flags = 0;
|
|
pPipe->stackIndex = 0;
|
|
pPipe->nDestroyObjects = 0;
|
|
pPipe->image.info.stripHeight = 0;
|
|
|
|
context->error = IL_OK;
|
|
return (ilPipe)pPipe;
|
|
}
|
|
|
|
|
|
/* ------------------------ ilDeclarePipeInvalid ----------------------------- */
|
|
/* Public function; see spec.
|
|
Empties the pipe, sets state to invalid and posts given error.
|
|
FALSE is always returned, to allow the following:
|
|
if (<something wrong while forming pipe>)
|
|
return ilDeclarePipeInvalid (pipe, errorCode);
|
|
*/
|
|
ilBool ilDeclarePipeInvalid (
|
|
ilPipe pipe,
|
|
ilError error
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
|
|
pPipe = (ilPipePtr)pipe;
|
|
ilEmptyPipe ((ilPipe)pPipe);
|
|
if (pPipe->o.p.objectType == IL_PIPE)
|
|
pPipe->state = IL_PIPE_INVALID;
|
|
pPipe->o.p.context->error = error;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* ------------------------ ilQueryPipe ----------------------------- */
|
|
/* Public function; see spec.
|
|
Return description of current image, from pipe header, if pipe forming.
|
|
*/
|
|
unsigned int ilQueryPipe (
|
|
ilPipe pipe,
|
|
long *pWidth, /* RETURNED */
|
|
long *pHeight, /* RETURNED */
|
|
ilImageDes *pDes /* RETURNED */
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
|
|
pPipe = (ilPipePtr)pipe;
|
|
if (pPipe->o.p.objectType != IL_PIPE) {
|
|
pPipe->o.p.context->error = IL_ERROR_OBJECT_TYPE;
|
|
return IL_PIPE_INVALID; /* return phony state because of error */
|
|
}
|
|
|
|
if (pPipe->state == IL_PIPE_FORMING) {
|
|
if (pWidth)
|
|
*pWidth = pPipe->image.info.width;
|
|
if (pHeight)
|
|
*pHeight = pPipe->image.info.height;
|
|
if (pDes)
|
|
*pDes = pPipe->image.des;
|
|
}
|
|
|
|
pPipe->o.p.context->error = IL_OK;
|
|
return pPipe->state;
|
|
}
|
|
|
|
|
|
/* ------------------------ ilGetPipeInfo ----------------------------- */
|
|
/* Public function; see spec.
|
|
If pipe state is "forming": if "forceDecompress" and image is compressed, make
|
|
it uncompressed, then return description of current pipe state.
|
|
*/
|
|
unsigned int ilGetPipeInfo (
|
|
ilPipe pipe,
|
|
ilBool forceDecompress,
|
|
ilPipeInfo *pInfo,
|
|
ilImageDes *pDes,
|
|
ilImageFormat *pFormat
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
|
|
pPipe = (ilPipePtr)pipe;
|
|
if (pPipe->o.p.objectType != IL_PIPE) {
|
|
pPipe->o.p.context->error = IL_ERROR_OBJECT_TYPE;
|
|
return IL_PIPE_INVALID; /* EXIT w/ pseudo-error */
|
|
}
|
|
if (pPipe->state == IL_PIPE_FORMING) {
|
|
|
|
/* Decompress image if it is compressed and caller asked for decompressed.
|
|
*/
|
|
if (forceDecompress && (pPipe->image.des.compression != IL_UNCOMPRESSED)) {
|
|
_ilDecompress ((ilPipe)pPipe);
|
|
if (pPipe->o.p.context->error)
|
|
return IL_PIPE_INVALID; /* EXIT w/ pseudo-error */
|
|
}
|
|
|
|
if (pInfo)
|
|
*pInfo = pPipe->image.info;
|
|
if (pDes)
|
|
*pDes = pPipe->image.des;
|
|
if (pFormat)
|
|
*pFormat = pPipe->image.format;
|
|
}
|
|
|
|
pPipe->o.p.context->error = IL_OK;
|
|
return pPipe->state;
|
|
}
|
|
|
|
|
|
/* ----------------------- ilRecommendedStripHeight ------------------------ */
|
|
/* Public function; see spec.
|
|
*/
|
|
long ilRecommendedStripHeight (
|
|
const ilImageDes *pDes,
|
|
const ilImageFormat *pFormat,
|
|
long width,
|
|
long height
|
|
)
|
|
{
|
|
long bytesPerRow [IL_MAX_SAMPLES];
|
|
long stripHeight, nBytes;
|
|
int i;
|
|
|
|
/* Get the bytes/row, per plane. If pixel format, all bytes are in plane 0,
|
|
else in pDes->nSamplesPerPixel planes.
|
|
*/
|
|
if (height <= 0)
|
|
stripHeight = 0;
|
|
else {
|
|
ilGetBytesPerRow (pDes, pFormat, width, bytesPerRow);
|
|
if (pFormat->sampleOrder == IL_SAMPLE_PIXELS)
|
|
nBytes = bytesPerRow[0]; /* all bytes in one plane */
|
|
else {
|
|
nBytes = 0;
|
|
for (i = 0; i < pDes->nSamplesPerPixel; i++)
|
|
nBytes += bytesPerRow[i];
|
|
}
|
|
if (nBytes <= 0)
|
|
stripHeight = 1;
|
|
else {
|
|
stripHeight = ilDefaultStripSize / nBytes;
|
|
if (stripHeight > height)
|
|
stripHeight = height;
|
|
if (stripHeight <= 0)
|
|
stripHeight = 1;
|
|
}
|
|
}
|
|
return stripHeight;
|
|
}
|
|
|
|
|
|
/* ----------------------- ilChangeStripHeight ------------------------ */
|
|
/* Calcs the recommended strip height, in ilPipeInfo for the pipe.
|
|
Should be called whenever the pipe stripHeight is changed, after the
|
|
pipe height is changed (limits stripHeight to image height).
|
|
*/
|
|
static void ilChangeStripHeight (
|
|
ilPipePtr pPipe
|
|
)
|
|
{
|
|
long stripHeight;
|
|
|
|
/* Limit stripHeight to pipe height. */
|
|
stripHeight = pPipe->image.info.stripHeight;
|
|
if (stripHeight > pPipe->image.info.height)
|
|
pPipe->image.info.stripHeight = stripHeight = pPipe->image.info.height;
|
|
else if (stripHeight <= 0)
|
|
pPipe->image.info.stripHeight = stripHeight = 1;
|
|
|
|
/* If uncompressed image, calc recommendedStripHeight else current stripHeight */
|
|
if (pPipe->image.des.compression == IL_UNCOMPRESSED) {
|
|
long i;
|
|
i = ilRecommendedStripHeight (&pPipe->image.des, &pPipe->image.format,
|
|
pPipe->image.info.width, pPipe->image.info.height);
|
|
pPipe->image.info.recommendedStripHeight = (i > stripHeight) ? stripHeight : i;
|
|
}
|
|
else pPipe->image.info.recommendedStripHeight = stripHeight;
|
|
}
|
|
|
|
|
|
/* ------------------------ ilAddPipeDestroyObject ------------------------ */
|
|
/* IL internal function, declared in /ilc/ilpipeint.h .
|
|
Add the given object to a list of objects to be destroyed when this pipe
|
|
is emptied. The object's refCount should be inc'd before this call, so that
|
|
the object cannot be freed until this pipe is destroyed.
|
|
The pipe must be in the forming or complete state.
|
|
NOTE: a hard limit exists on the # of objects which can be added; not intended
|
|
for unlimited use, e.g. by filters!
|
|
Sets context->error to error code, returns true if success.
|
|
*/
|
|
IL_PRIVATE ilBool _ilAddPipeDestroyObject (
|
|
ilPipe pipe,
|
|
ilObject object
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
|
|
pPipe = (ilPipePtr)pipe;
|
|
if (pPipe->nDestroyObjects >= IL_MAX_DESTROY_OBJECTS) {
|
|
pipe->context->error = IL_ERROR_MALLOC; /* should not happen, return phony */
|
|
return FALSE;
|
|
}
|
|
pPipe->destroyObjects[pPipe->nDestroyObjects] = object;
|
|
pPipe->nDestroyObjects++;
|
|
pipe->context->error = IL_OK;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* ---------------------------- ilAddProducerImage ------------------------ */
|
|
/* IL internal function, declared in /ilc/ilpipeint.h .
|
|
Called by ilReadImage() to set *pImage as the producer to this pipe.
|
|
The pipe MUST be in the empty state. "needProducerThrottle" is true iff a
|
|
throttle must be inserted before next element added by ilAddPipeElement().
|
|
*/
|
|
IL_PRIVATE ilBool _ilAddProducerImage (
|
|
ilPipe pipe,
|
|
ilImagePtr pImage,
|
|
unsigned int producerCode,
|
|
long height,
|
|
long stripHeight,
|
|
ilBool constantStrip,
|
|
ilBool needProducerThrottle
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
|
|
/* Copy image data into pipe info, point to given image as producer. */
|
|
pPipe = (ilPipePtr)pipe;
|
|
pPipe->producerCode = producerCode;
|
|
pPipe->image.des = pImage->des;
|
|
pPipe->image.format = pImage->format;
|
|
pPipe->image.info.producerObject = (ilObject)pImage;
|
|
pPipe->image.info.tempImage = FALSE;
|
|
pPipe->image.info.width = pImage->i.width;
|
|
pPipe->image.info.height = height;
|
|
pPipe->image.info.constantStrip = constantStrip;
|
|
pPipe->image.info.stripHeight = stripHeight;
|
|
ilChangeStripHeight (pPipe);
|
|
|
|
pPipe->image.info.producerObject = (ilObject)pImage;
|
|
pPipe->image.info.pPalette = pImage->i.pPalette; /* point to image palette */
|
|
pPipe->image.info.pCompData = pImage->i.pCompData; /* point to comp data */
|
|
pPipe->pSrcPipeImage = &pImage->i;
|
|
pPipe->state = IL_PIPE_FORMING;
|
|
pPipe->feedDone = FALSE; /* ilSetFeedPipeData not done yet. */
|
|
|
|
/* Signal ilAddPipeElement() to add a throttle before next filter is added,
|
|
and that a copy filter must be added if ilWriteImage() is called next.
|
|
*/
|
|
pPipe->needProducerThrottle = needProducerThrottle;
|
|
pPipe->copyToConsumerImage = TRUE;
|
|
|
|
/* Inc pImage->refCount, and add as destroyObject for when pipe destroyed. */
|
|
pImage->o.refCount++;
|
|
return _ilAddPipeDestroyObject ((ilPipe)pPipe, (ilObject)pImage);
|
|
}
|
|
|
|
|
|
/* ------------------------ ilSetFeedPipeData ------------------------------- */
|
|
/* Called by ilFeedPipe() to set info for the first element in the given pipe,
|
|
which will be ilFeedProducerThrottleExecute(). If feeding compressed data:
|
|
"start" is the byte offset into the compressed data, and "nCompBytes" is
|
|
the number of bytes at that offset. If uncompressed data: "start" is the
|
|
starting line, and "nCompBytes" is ignored. Returns FALSE if the pipe was
|
|
not started with a ilFeedFromImage() producer.
|
|
*/
|
|
IL_PRIVATE ilBool _ilSetFeedPipeData (
|
|
ilPipe pipe,
|
|
long start,
|
|
long nLines,
|
|
long nCompBytes
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
ilImagePtr pImage;
|
|
|
|
pPipe = (ilPipePtr)pipe;
|
|
if (pPipe->producerCode != IL_PIPE_FEED_IMAGE)
|
|
return FALSE;
|
|
|
|
/* If uncompressed image, starting line plus # lines must be <= height, and
|
|
"start" is starting line; if compressed "start" is starting byte offset.
|
|
*/
|
|
pImage = (ilImagePtr)pPipe->image.info.producerObject;
|
|
if (pImage->des.compression == IL_UNCOMPRESSED) {
|
|
if ((start + nLines) > pImage->i.height)
|
|
return FALSE;
|
|
pPipe->feedStartLine = start;
|
|
pPipe->feedCompNBytes = 0;
|
|
pPipe->feedCompOffset = 0;
|
|
}
|
|
else {
|
|
pPipe->feedStartLine = 0;
|
|
pPipe->feedCompOffset = start;
|
|
pPipe->feedCompNBytes = nCompBytes;
|
|
}
|
|
pPipe->feedNLines = nLines;
|
|
pPipe->feedDone = TRUE; /* pipe now fed */
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* ------------------------ ilSetPipeDesFormat ------------------------------- */
|
|
/* IL internal function, declared in /ilc/ilpipeint.h .
|
|
Called by ilConvert() to "typecast" the pipe image to the given des and/or
|
|
format (ignored if pDes/pFormat null). No validation is done on the result!
|
|
*/
|
|
IL_PRIVATE void _ilSetPipeDesFormat (
|
|
ilPipe pipe,
|
|
ilImageDes *pDes,
|
|
ilImageFormat *pFormat
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
|
|
pPipe = (ilPipePtr)pipe;
|
|
if (pDes)
|
|
pPipe->image.des = *pDes;
|
|
if (pFormat)
|
|
pPipe->image.format = *pFormat;
|
|
}
|
|
|
|
|
|
/* ------------------------ ilAddPipeElement ------------------------------- */
|
|
/* Public function: see spec.
|
|
Add a pipe element if correct state.
|
|
*/
|
|
ilPtr ilAddPipeElement (
|
|
ilPipe pipe,
|
|
int elementType,
|
|
/* Use portable type for sizeof() operator, bug report OSF_QAR# 32082 */
|
|
size_t nBytesPrivate,
|
|
unsigned long flags,
|
|
ilSrcElementData *pSrcData,
|
|
ilDstElementData *pDstData,
|
|
ilError (*Init)(),
|
|
ilError (*Cleanup)(),
|
|
ilError (*Destroy)(),
|
|
/*
|
|
** Added another execute function for passing in a fourth
|
|
** parameter which is a floating pointing.
|
|
*/
|
|
ilError (*ExecuteThree)(),
|
|
ilError (*ExecuteFour)(ilExecuteData *,
|
|
long,
|
|
long *,
|
|
float),
|
|
unsigned long mustBeZero
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
unsigned int newState; /* pipe state after this element added */
|
|
ilElementPtr pElement; /* ptr to new element to add to list */
|
|
ilElementPtr pPrevElement;
|
|
ilError error;
|
|
long stripHeight, srcBufferHeight;
|
|
ilBool constantStrip, haveConsumerImage, insertCopyFilter;
|
|
|
|
stripHeight = 0; /* initializing the stripHeight variable */
|
|
pPipe = (ilPipePtr)pipe;
|
|
if (pPipe->o.p.objectType != IL_PIPE) {
|
|
pPipe->o.p.context->error = IL_ERROR_OBJECT_TYPE;
|
|
return (ilPtr)NULL; /* EXIT */
|
|
}
|
|
if (mustBeZero != 0) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_PAR_NOT_ZERO);
|
|
return (ilPtr)NULL;
|
|
}
|
|
|
|
/* Validate elementType and pipe state: "empty" for producer, else "forming".
|
|
Set newState to state pipe becomes after this element is added.
|
|
If a "hook" element, add hook element and EXIT.
|
|
For filters and consumers: set stripHeight to source (input) strip height
|
|
required by this element; set constantStrip true if every strip (except last)
|
|
must be stripHeight lines high. Default if !pSrcData or stripHeight == 0:
|
|
!constantStrip; filters: recommended strip height; consumers: whole strip.
|
|
Set some flags, explained when used below.
|
|
*/
|
|
haveConsumerImage = FALSE;
|
|
insertCopyFilter = FALSE;
|
|
|
|
switch (elementType) {
|
|
|
|
case IL_PRODUCER:
|
|
if (pPipe->state != IL_PIPE_EMPTY) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_PIPE_STATE);
|
|
return (ilPtr)NULL;
|
|
}
|
|
newState = IL_PIPE_FORMING;
|
|
break;
|
|
|
|
case IL_FILTER:
|
|
if (pPipe->state != IL_PIPE_FORMING) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_PIPE_STATE);
|
|
return (ilPtr)NULL;
|
|
}
|
|
newState = IL_PIPE_FORMING;
|
|
if (pSrcData && (pSrcData->minBufferHeight > 0))
|
|
insertCopyFilter = pPipe->copyToConsumerImage;
|
|
if (!pSrcData || !pSrcData->stripHeight) {
|
|
stripHeight = pPipe->image.info.recommendedStripHeight;
|
|
constantStrip = FALSE;
|
|
}
|
|
else {
|
|
stripHeight = pSrcData->stripHeight;
|
|
constantStrip = pSrcData->constantStrip;
|
|
}
|
|
break;
|
|
|
|
case IL_CONSUMER:
|
|
if (pPipe->state != IL_PIPE_FORMING) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_PIPE_STATE);
|
|
return (ilPtr)NULL;
|
|
}
|
|
newState = IL_PIPE_COMPLETE;
|
|
if (pSrcData) {
|
|
if (pSrcData->consumerImage) {
|
|
haveConsumerImage = TRUE;
|
|
insertCopyFilter = pPipe->copyToConsumerImage;
|
|
}
|
|
else if (pSrcData->minBufferHeight > 0)
|
|
insertCopyFilter = pPipe->copyToConsumerImage;
|
|
}
|
|
if (!pSrcData || !pSrcData->stripHeight) {
|
|
stripHeight = pPipe->image.info.stripHeight; /* accept strip height */
|
|
constantStrip = FALSE;
|
|
}
|
|
else {
|
|
stripHeight = pSrcData->stripHeight;
|
|
constantStrip = pSrcData->constantStrip;
|
|
}
|
|
break;
|
|
|
|
/* A "hook" element: invalid if pipe executing or invalid, else add element
|
|
to hook list with Init/Cleanup/Destroy functions copied.
|
|
*/
|
|
case IL_HOOK:
|
|
if ((pPipe->state == IL_PIPE_INVALID) || (pPipe->state == IL_PIPE_EXECUTING)) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_PIPE_STATE);
|
|
return (ilPtr)NULL;
|
|
}
|
|
if (ExecuteThree || ExecuteFour) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_PAR_NOT_ZERO);
|
|
return (ilPtr)NULL;
|
|
}
|
|
pElement = (ilElementPtr)IL_MALLOC_ZERO (sizeof(ilElementRec) + nBytesPrivate);
|
|
if (!pElement) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_MALLOC);
|
|
return (ilPtr)NULL; /* EXIT */
|
|
}
|
|
pElement->pPrev = pPipe->hookHead.pPrev;
|
|
pElement->pNext = &pPipe->hookHead;
|
|
pPipe->hookHead.pPrev->pNext = pElement;
|
|
pPipe->hookHead.pPrev = pElement;
|
|
pElement->elementType = IL_HOOK;
|
|
pElement->exec.pPrivate = (ilPtr)&pElement->clientPrivate;
|
|
pElement->exec.pSrcImage = pElement->exec.pDstImage = (ilImageInfo *)NULL;
|
|
pElement->Init = Init;
|
|
pElement->Cleanup = Cleanup;
|
|
pElement->Destroy = Destroy;
|
|
|
|
pPipe->o.p.context->error = IL_OK;
|
|
return (ilPtr)pElement->exec.pPrivate; /* done; EXIT */
|
|
break;
|
|
|
|
default:
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_ELEMENT_TYPE);
|
|
return (ilPtr)NULL;
|
|
break;
|
|
} /* END switch elementType */
|
|
|
|
|
|
/* Force stripHeight to be in range 1..image height */
|
|
if (stripHeight <= 0)
|
|
stripHeight = 1;
|
|
else if (stripHeight > pPipe->image.info.height)
|
|
stripHeight = pPipe->image.info.height;
|
|
|
|
/* If pipe begins with an ilReadImage() and this is first filter, add
|
|
a throttle to entire strip height - this throttle knows to read whole strip
|
|
and return IL_ERROR_LAST_STRIP - otherwise ilExecutePipe() would not stop.
|
|
Note: ilAddThrottlePipeElement() calls this function: recursion!
|
|
Also, note that no pipe state has been changed up to this point. BUT,
|
|
set needProducerThrottle to false or below check causes infinite recursion.
|
|
*/
|
|
if ((elementType != IL_PRODUCER) && pPipe->needProducerThrottle) {
|
|
pPipe->needProducerThrottle = FALSE; /* prevent recursion */
|
|
if (!_ilAddThrottlePipeElement ((ilPipe)pPipe, stripHeight,
|
|
constantStrip, pPipe->producerCode,
|
|
&pPipe->image.info.stripHeight, &pPipe->image.info.constantStrip)) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, pPipe->o.p.context->error);
|
|
return (ilPtr)NULL;
|
|
}
|
|
}
|
|
|
|
/* If writing consumerImage (e.g. ilWriteImage()) or inserting a throttle
|
|
requiring minBufferHeight, insert copy filter if flagged: e.g. previous
|
|
element was "no dst" filter or ilReadImage(); else possibly add throttle:
|
|
If not a producer and pipe image is not compressed, check "stripHeight",
|
|
the desired strip height, set above.
|
|
If != current strip height, or caller requires a constant strip
|
|
and current is not constant, add a filter which makes it so.
|
|
If throttle added successfully, set image info to what was asked for, but
|
|
first save stripHeight - it is the size of the src buffer to create, if any.
|
|
*/
|
|
srcBufferHeight = pPipe->image.info.stripHeight;
|
|
if (insertCopyFilter) {
|
|
if (pPipe->image.des.compression == IL_UNCOMPRESSED) {
|
|
if (!_ilInsertCopyFilter ((ilPipe)pPipe, pPipe->image.info.stripHeight))
|
|
return (ilPtr)NULL;
|
|
}
|
|
else if (!_ilInsertCompressedCopyFilter ((ilPipe)pPipe))
|
|
return (ilPtr)NULL;
|
|
}
|
|
else if ((elementType != IL_PRODUCER)
|
|
&& (pPipe->image.des.compression == IL_UNCOMPRESSED)
|
|
&& ((stripHeight != pPipe->image.info.stripHeight)
|
|
|| (constantStrip && !pPipe->image.info.constantStrip)) ) {
|
|
if (!_ilAddThrottlePipeElement ((ilPipe)pPipe, stripHeight,
|
|
constantStrip, IL_PIPE_NOT_IMAGE,
|
|
&pPipe->image.info.stripHeight, &pPipe->image.info.constantStrip)) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, pPipe->o.p.context->error);
|
|
return (ilPtr)NULL;
|
|
}
|
|
}
|
|
|
|
/* Allocate and zero element struct and add to end of list; exit if error. */
|
|
pElement = (ilElementPtr)IL_MALLOC_ZERO (sizeof(ilElementRec) + nBytesPrivate);
|
|
if (!pElement) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_MALLOC);
|
|
return (ilPtr)NULL; /* EXIT */
|
|
}
|
|
pElement->pPrev = pPipe->elementHead.pPrev;
|
|
pElement->pNext = &pPipe->elementHead;
|
|
pPipe->elementHead.pPrev->pNext = pElement;
|
|
pPipe->elementHead.pPrev = pElement;
|
|
|
|
/* Copy passed data into the element struct and init element.
|
|
Each element's pNextSrcLine points to the next one's srcLine, and compressed.
|
|
pOffset/pNBytesWritten point to next one's srcOffset/nBytesToRead.
|
|
Set "Destroy" to null for now, so it will not be called if we fail here.
|
|
Set it to passed function only when sure of success.
|
|
*/
|
|
pElement->elementType = elementType;
|
|
pElement->flags = flags;
|
|
pElement->exec.pPrivate = (ilPtr)&pElement->clientPrivate;
|
|
pPrevElement = pElement->pPrev;
|
|
pPrevElement->exec.pNextSrcLine = &pElement->exec.srcLine;
|
|
pPrevElement->exec.compressed.pDstOffset = &pElement->exec.compressed.srcOffset;
|
|
pPrevElement->exec.compressed.pNBytesWritten = &pElement->exec.compressed.nBytesToRead;
|
|
pElement->pNext->exec.srcLine = 0;
|
|
pElement->Init = Init;
|
|
pElement->Cleanup = Cleanup;
|
|
pElement->Destroy = IL_NPF; /* set when successful */
|
|
pElement->ExecuteThree = ExecuteThree;
|
|
pElement->ExecuteFour = ExecuteFour;
|
|
|
|
/* If a producer, must have valid dst data and pDes/Format, or error.
|
|
Skip allocation of src image for producers - they get src on their own;
|
|
set pSrcPipeImage to NULL. Init # src lines to read to strip height.
|
|
Set ilPipeInfo.pPalette to callers if palette image, else to null
|
|
*/
|
|
if (elementType == IL_PRODUCER) {
|
|
if (!pDstData || !pDstData->pDes || !pDstData->pFormat) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_PRODUCER_DATA);
|
|
return (ilPtr)NULL; /* EXIT */
|
|
}
|
|
if ((pDstData->width <= 0) || (pDstData->height <= 0)) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_ZERO_SIZE_IMAGE);
|
|
return (ilPtr)NULL; /* EXIT */
|
|
}
|
|
error = _ilValidateDesFormat (TRUE, pDstData->pDes, pDstData->pFormat);
|
|
if (error) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, error);
|
|
return (ilPtr)NULL; /* EXIT */
|
|
}
|
|
|
|
pPipe->producerCode = IL_PIPE_NOT_IMAGE; /* not a read from a (feed) image */
|
|
pPipe->pSrcPipeImage = (ilImageInfo *)NULL;
|
|
|
|
pPipe->image.des = *pDstData->pDes;
|
|
pPipe->image.format = *pDstData->pFormat;
|
|
pPipe->image.info.producerObject = pDstData->producerObject;
|
|
pPipe->image.info.tempImage = TRUE;
|
|
|
|
pPipe->image.info.width = pDstData->width;
|
|
pPipe->image.info.height = pDstData->height;
|
|
pPipe->image.info.constantStrip = pDstData->constantStrip;
|
|
pPipe->image.info.stripHeight = pDstData->stripHeight;
|
|
ilChangeStripHeight (pPipe);
|
|
|
|
pPipe->image.info.pPalette = (pPipe->image.des.type == IL_PALETTE) ?
|
|
pDstData->pPalette : (unsigned short *)NULL;
|
|
pPipe->image.info.pCompData = pDstData->pCompData;
|
|
pPipe->needProducerThrottle = FALSE; /* producer not an image */
|
|
pPipe->copyToConsumerImage = FALSE;
|
|
|
|
pElement->exec.pSrcImage = (ilImageInfo *)NULL;
|
|
}
|
|
else {
|
|
/* Not a producer. Create src image to this element, unless a consumer which
|
|
specifies its source image (e.g. ilWriteImage()), or src image specified
|
|
as output from last element = a producer (e.g. ilReadImage()): pSrcImage.
|
|
*/
|
|
if (pPipe->pSrcPipeImage) {
|
|
pElement->exec.pSrcImage = pPipe->pSrcPipeImage;
|
|
pPipe->pSrcPipeImage = (ilImageInfo *)NULL;
|
|
}
|
|
else if (haveConsumerImage) {
|
|
pElement->exec.pSrcImage = &((ilImagePtr)pSrcData->consumerImage)->i;
|
|
}
|
|
else {
|
|
/* Src image not provided; must create a temp and add to list of temps.
|
|
Set image height = max (minHeight if present, srcBufferHeight).
|
|
*/
|
|
ilImagePtr pImage;
|
|
pImage = ilAllocTempImage (pPipe, &pPipe->image.info, &pPipe->image.des,
|
|
&pPipe->image.format);
|
|
if (!pImage) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_MALLOC);
|
|
return (ilPtr)NULL; /* EXIT */
|
|
}
|
|
pElement->exec.pSrcImage = &pImage->i;
|
|
if (pSrcData && (pSrcData->minBufferHeight > srcBufferHeight))
|
|
srcBufferHeight = pSrcData->minBufferHeight;
|
|
pImage->i.height = srcBufferHeight;
|
|
}
|
|
|
|
/* Src image to this element is dst image of previous element.
|
|
If no dst from this element, then src to this element is src to next,
|
|
and must copy if next element is ilWriteImage(); otherwise, src to next
|
|
is necessarily a temp image, and don't need to copy.
|
|
*/
|
|
pElement->pPrev->exec.pDstImage = pElement->exec.pSrcImage;
|
|
if (flags & IL_ADD_PIPE_NO_DST) {
|
|
pPipe->pSrcPipeImage = pElement->exec.pSrcImage;
|
|
pPipe->copyToConsumerImage = TRUE;
|
|
}
|
|
else {
|
|
pPipe->image.info.tempImage = TRUE;
|
|
pPipe->copyToConsumerImage = FALSE;
|
|
}
|
|
|
|
/* If not a consumer and have pDstData: if non-null des/format ptr, copy
|
|
data from it into the pipe image area - element changes des or format.
|
|
Set rest of data from pDstData: stripHeight if not zero.
|
|
Set ilPipeInfo.pPalette to null if not a palette image, or to callers
|
|
value if it is not null.
|
|
Validate pDes and pFormat together if either non-null (else no change).
|
|
*/
|
|
if (pDstData && (elementType != IL_CONSUMER)) {
|
|
const ilImageDes *pDes;
|
|
if ((pDstData->width <= 0) || (pDstData->height <= 0)) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, IL_ERROR_ZERO_SIZE_IMAGE);
|
|
return (ilPtr)NULL; /* EXIT */
|
|
}
|
|
pPipe->image.info.width = pDstData->width;
|
|
pPipe->image.info.height = pDstData->height;
|
|
if (pDstData->stripHeight != 0) {
|
|
pPipe->image.info.constantStrip = pDstData->constantStrip;
|
|
pPipe->image.info.stripHeight = pDstData->stripHeight;
|
|
}
|
|
if ((pDes = pDstData->pDes)) {
|
|
if (pDes->type == IL_PALETTE) {
|
|
if (pDstData->pPalette)
|
|
pPipe->image.info.pPalette = pDstData->pPalette;
|
|
}
|
|
else pPipe->image.info.pPalette = (unsigned short *)NULL;
|
|
pPipe->image.des = *pDes;
|
|
}
|
|
pPipe->image.info.pCompData = pDstData->pCompData;
|
|
if (pDstData->pFormat)
|
|
pPipe->image.format = *pDstData->pFormat;
|
|
if (pDes || pDstData->pFormat) {
|
|
error = _ilValidateDesFormat (TRUE /* allow private types */,
|
|
&pPipe->image.des, &pPipe->image.format);
|
|
if (error) {
|
|
ilDeclarePipeInvalid ((ilPipe)pPipe, error);
|
|
return (ilPtr)NULL; /* EXIT */
|
|
}
|
|
}
|
|
ilChangeStripHeight (pPipe); /* reset (recommended) strip height */
|
|
} /* END have pDstData, not consumer */
|
|
} /* END not a producer */
|
|
|
|
/* Success: set pipe to new state; set pElement->Destroy, return pPriv. */
|
|
pPipe->state = newState;
|
|
pElement->Destroy = Destroy;
|
|
|
|
pPipe->o.p.context->error = IL_OK;
|
|
return (ilPtr)pElement->exec.pPrivate;
|
|
}
|
|
|
|
|
|
/* ------------------------ ilAbortPipe -------------------------------- */
|
|
/* Public function: see spec.
|
|
Aborts the given pipe; if not running, a noop.
|
|
*/
|
|
ilBool ilAbortPipe (
|
|
ilPipe pipe
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
|
|
pPipe = (ilPipePtr)pipe;
|
|
if (pPipe->o.p.objectType != IL_PIPE) {
|
|
pPipe->o.p.context->error = IL_ERROR_OBJECT_TYPE;
|
|
return FALSE;
|
|
}
|
|
|
|
if (pPipe->state == IL_PIPE_EXECUTING)
|
|
return ((pPipe->o.p.context->error = ilCleanupRunningPipe (pPipe, TRUE)) == IL_OK);
|
|
else {
|
|
pPipe->o.p.context->error = IL_OK;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* ------------------------ ilExecutePipe -------------------------------- */
|
|
/* Public function: see spec.
|
|
*/
|
|
int ilExecutePipe (
|
|
ilPipe pipe,
|
|
long nStrips,
|
|
float ratio
|
|
)
|
|
{
|
|
ilPipePtr pPipe;
|
|
ilElementPtr pElement, pExecHead, pElementHead;
|
|
ilImagePtr pImage;
|
|
long nLines, initNLines;
|
|
ilError error;
|
|
|
|
/* Validate zero par and that pipe is one.
|
|
*/
|
|
pPipe = (ilPipePtr)pipe;
|
|
if (pPipe->o.p.objectType != IL_PIPE) {
|
|
pPipe->o.p.context->error = IL_ERROR_OBJECT_TYPE;
|
|
return IL_EXECUTE_ERROR; /* EXIT */
|
|
}
|
|
|
|
/* Execute based on pipe state. Error if pipe invalid, empty or forming.
|
|
Otherwise: abort if requested, cleanup if in progress. Compile pipe
|
|
if complete, or executable and producer changed. If in progress and
|
|
producer changed, cleanup and return error.
|
|
*/
|
|
pExecHead = &pPipe->elementHead; /* head of "executable" (non-hook) list */
|
|
switch (pPipe->state) {
|
|
case IL_PIPE_INVALID:
|
|
case IL_PIPE_EMPTY:
|
|
case IL_PIPE_FORMING:
|
|
pPipe->o.p.context->error = IL_ERROR_PIPE_STATE;
|
|
return IL_EXECUTE_ERROR; /* EXIT */
|
|
|
|
/* Pipe complete: do setup to become executable.
|
|
Allocate pixel buffers for all temp images of this pipe.
|
|
If error, don't cleanup, as init not done yet.
|
|
*/
|
|
case IL_PIPE_COMPLETE:
|
|
pImage = (ilImagePtr)pPipe->imageHead.pNext;
|
|
while (pImage != (ilImagePtr)&pPipe->imageHead) {
|
|
ilError error = _ilMallocImagePixels (pImage);
|
|
if (error) {
|
|
ilFreeTempImageBuffers (pPipe);
|
|
pPipe->o.p.context->error = error;
|
|
return IL_EXECUTE_ERROR; /* EXIT */
|
|
}
|
|
pImage = (ilImagePtr)pImage->o.pNext;
|
|
}
|
|
|
|
/* Call elements' Init() functions if non-null. If Init() returns error:
|
|
call Cleanup() of previous elements, whose Init()s were already called,
|
|
and free image buffers allocated above, and return error in errorInfo.
|
|
Init all srcLines and srcOffsets to 0; always start at line/byte 0.
|
|
Do above first for "hook" list, then for regular element list.
|
|
*/
|
|
pElementHead = &pPipe->hookHead;
|
|
while (TRUE) {
|
|
pElement = pElementHead->pNext;
|
|
while (pElement != pElementHead) {
|
|
pElement->exec.srcLine = 0;
|
|
pElement->exec.compressed.srcOffset = 0;
|
|
if (pElement->Init) {
|
|
ilError error = (*pElement->Init) ((ilPtr)pElement->exec.pPrivate,
|
|
pElement->exec.pSrcImage, pElement->exec.pDstImage);
|
|
if (error) {
|
|
while (TRUE) {
|
|
while (pElement->pPrev != pElementHead) {
|
|
pElement = pElement->pPrev;
|
|
if (pElement->Cleanup)
|
|
(*pElement->Cleanup) (pElement->exec.pPrivate, TRUE);
|
|
}
|
|
if (pElementHead == &pPipe->hookHead)
|
|
break; /* both lists done */
|
|
pElementHead = &pPipe->hookHead;
|
|
pElement = pElementHead;
|
|
}
|
|
ilFreeTempImageBuffers (pPipe);
|
|
pPipe->o.p.context->errorInfo = error;
|
|
pPipe->o.p.context->error = (error < 0) ?
|
|
IL_ERROR_USER_PIPE_ELEMENT : IL_ERROR_PIPE_ELEMENT;
|
|
return IL_EXECUTE_ERROR; /* EXIT */
|
|
}
|
|
}
|
|
pElement = pElement->pNext;
|
|
} /* END while, one element list */
|
|
if (pElementHead == pExecHead)
|
|
break; /* both lists scanned; done */
|
|
pElementHead = pExecHead; /* run thru executable list */
|
|
} /* END while TRUE, all element lists */
|
|
|
|
/* Now executing; FALL THRU to execute one pass. Point top of execStack to
|
|
first element; it changes to top of execStack on a push.
|
|
*/
|
|
pPipe->stackIndex = 0;
|
|
pPipe->execStack[0] = pExecHead->pNext;
|
|
pPipe->state = IL_PIPE_EXECUTING;
|
|
pPipe->lastStrip = FALSE;
|
|
|
|
/* Pipe being executed, possibly first time. Execute nStrips strips (or all
|
|
if !nStrips). dstLine for each element is "srcLine" of next element.
|
|
Start with top element of "execStack".
|
|
"nLines" on entry to Execute() = # of src lines to read;
|
|
on exit = # of dst lines written (== 0, skip rest of pipe).
|
|
If error: check for pseudo-error, else abort. Pseudo-errors:
|
|
IL_ERROR_LAST_STRIP: finish processing this strip, then done
|
|
IL_ERROR_ELEMENT_AGAIN: element states that it needs to be recalled
|
|
(e.g. a throttle with more left in its input buffer). Push
|
|
pElement onto execStack; exit if overflow.
|
|
IL_ERROR_ELEMENT_COMPLETE: element states that it previously issued
|
|
"AGAIN" and is now done. Pop stack; exit if underflow.
|
|
*/
|
|
case IL_PIPE_EXECUTING:
|
|
if (pPipe->producerCode == IL_PIPE_FEED_IMAGE) {
|
|
ilElementPtr pFirstElement;
|
|
|
|
if (!pPipe->feedDone) { /* pipe executed directly instead of fed */
|
|
ilCleanupRunningPipe (pPipe, TRUE);
|
|
pPipe->o.p.context->error = IL_ERROR_PIPE_NOT_FED;
|
|
return IL_EXECUTE_ERROR;
|
|
}
|
|
pPipe->feedDone = FALSE;
|
|
initNLines = pPipe->feedNLines;
|
|
pFirstElement = pExecHead->pNext;
|
|
pFirstElement->exec.srcLine = pPipe->feedStartLine;
|
|
pFirstElement->exec.compressed.srcOffset = pPipe->feedCompOffset;
|
|
pFirstElement->exec.compressed.nBytesToRead = pPipe->feedCompNBytes;
|
|
}
|
|
else initNLines = pPipe->image.info.height; /* first element reads whole image */
|
|
|
|
while (TRUE) { /* until all or nStrips done */
|
|
nLines = initNLines;
|
|
pElement = pPipe->execStack [pPipe->stackIndex];
|
|
while (pElement != pExecHead) {
|
|
/*
|
|
** Added another execute function for passing in a fourth
|
|
** parameter which is a floating pointing. So check to see
|
|
** which execute function is not null and call that one.
|
|
*/
|
|
if(pElement->ExecuteThree)
|
|
{
|
|
error = (*pElement->ExecuteThree) (&pElement->exec, pElement->pNext->exec.srcLine,
|
|
&nLines);
|
|
}
|
|
else
|
|
{
|
|
error = (*pElement->ExecuteFour) (&pElement->exec,
|
|
pElement->pNext->exec.srcLine,
|
|
&nLines, ratio);
|
|
}
|
|
if (error) {
|
|
if (error == IL_ERROR_ELEMENT_AGAIN) {
|
|
if (++pPipe->stackIndex >= IL_MAX_EXEC_STACK) {
|
|
ilCleanupRunningPipe (pPipe, TRUE);
|
|
pPipe->o.p.context->error = IL_ERROR_EXECUTE_STACK_OVERFLOW;
|
|
return IL_EXECUTE_ERROR; /* EXIT */
|
|
}
|
|
pPipe->execStack [pPipe->stackIndex] = pElement;
|
|
}
|
|
else if (error == IL_ERROR_ELEMENT_COMPLETE) {
|
|
if (--pPipe->stackIndex < 0) {
|
|
ilCleanupRunningPipe (pPipe, TRUE);
|
|
pPipe->o.p.context->error = IL_ERROR_EXECUTE_STACK_UNDERFLOW;
|
|
return IL_EXECUTE_ERROR; /* EXIT */
|
|
}
|
|
}
|
|
else if (error == IL_ERROR_LAST_STRIP)
|
|
pPipe->lastStrip = TRUE;
|
|
else {
|
|
ilCleanupRunningPipe (pPipe, TRUE);
|
|
pPipe->o.p.context->errorInfo = error;
|
|
pPipe->o.p.context->error = (error < 0) ?
|
|
IL_ERROR_USER_PIPE_ELEMENT : IL_ERROR_PIPE_ELEMENT;
|
|
return IL_EXECUTE_ERROR; /* EXIT */
|
|
}
|
|
}
|
|
if (nLines <= 0) /* no lines written; skip rest of pipe */
|
|
break;
|
|
pElement = pElement->pNext;
|
|
}
|
|
|
|
/* If stack is empty (if not empty, still pushing the current strip
|
|
thru the pipe): if last strip, do cleanup and exit, else downcount
|
|
nStrips (if != 0 => all strips) and return if 0.
|
|
*/
|
|
if (pPipe->stackIndex <= 0) {
|
|
if (pPipe->lastStrip) {
|
|
ilError error = ilCleanupRunningPipe (pPipe, FALSE);
|
|
if (error == IL_OK)
|
|
return IL_EXECUTE_COMPLETE;
|
|
else {
|
|
pPipe->o.p.context->errorInfo = error;
|
|
pPipe->o.p.context->error = (error < 0) ?
|
|
IL_ERROR_USER_PIPE_ELEMENT : IL_ERROR_PIPE_ELEMENT;
|
|
return IL_EXECUTE_ERROR;
|
|
}
|
|
}
|
|
if ((nStrips != 0) && (--nStrips <= 0))
|
|
return IL_EXECUTE_AGAIN;
|
|
}
|
|
|
|
} /* END while true: execute strips */
|
|
} /* END switch pipe state */
|
|
return IL_EXECUTE_AGAIN;
|
|
}
|