700 lines
27 KiB
C
700 lines
27 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: ilcompressjpeg.c /main/4 1996/01/08 12:14:02 lehors $ */
|
|
/**---------------------------------------------------------------------
|
|
***
|
|
*** (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).
|
|
***
|
|
***-------------------------------------------------------------------*/
|
|
|
|
#include "ilint.h"
|
|
#include "ilpipelem.h"
|
|
#include "ilcompress.h"
|
|
#include "ilcodec.h"
|
|
#include "ilerrors.h"
|
|
#include "ilutiljpeg.h"
|
|
#include "iljpgencode.h"
|
|
|
|
|
|
/* ============================= JIF to Raw =================================== */
|
|
/* This body of code supports converting JIF format JPEG to "raw" (stripped).
|
|
*/
|
|
|
|
/* Private data for ilJIFToRawExecute() */
|
|
typedef struct {
|
|
ilJPEGData *pCompData; /* ptr to IL JPEG data */
|
|
} ilJIFToRawPrivRec, *ilJIFToRawPrivPtr;
|
|
|
|
|
|
/* ----------------------- ilJIFToRawDestroy ------------------------------ */
|
|
/* Destroy() function for converting JIF format JPEG to "raw" JPEG.
|
|
*/
|
|
static ilError ilJIFToRawDestroy (
|
|
ilJIFToRawPrivPtr pPriv
|
|
)
|
|
{
|
|
/* Free pCompData including associated tables */
|
|
if (pPriv->pCompData) {
|
|
_ilJPEGFreeTables (pPriv->pCompData);
|
|
IL_FREE (pPriv->pCompData);
|
|
}
|
|
return IL_OK;
|
|
}
|
|
|
|
/* ----------------------- ilJIFToRawExecute ------------------------------ */
|
|
/* Execute() function for converting JIF format JPEG to "raw" JPEG.
|
|
*/
|
|
static ilError ilJIFToRawExecute (
|
|
ilExecuteData *pData,
|
|
unsigned long dstLine,
|
|
unsigned long *pNLines
|
|
)
|
|
{
|
|
ilJPEGDecodeStream streamRec;
|
|
iljpgDataPtr pJPEGData;
|
|
ilError error;
|
|
int i;
|
|
ilJPEGData *pCompData;
|
|
ilImagePlaneInfo *pDstPlane;
|
|
long dstOffset, nBytes;
|
|
|
|
/* Parse the src data, which is a JIF data stream */
|
|
streamRec.pData = pData->pSrcImage->plane[0].pPixels;
|
|
streamRec.nBytesLeft = pData->compressed.nBytesToRead;
|
|
if (error = iljpgDecodeJIF (&streamRec, &pJPEGData))
|
|
return error;
|
|
|
|
/* Point pCompData to tables decoded from JIF, and copy table indices. */
|
|
pCompData = ((ilJIFToRawPrivPtr)pData->pPrivate)->pCompData;
|
|
for (i = 0; i < 4; i++) {
|
|
pCompData->QTables[i] = pJPEGData->QTables[i];
|
|
pCompData->DCTables[i] = pJPEGData->DCTables[i];
|
|
pCompData->ACTables[i] = pJPEGData->ACTables[i];
|
|
}
|
|
for (i = 0; i < ILJPG_MAX_COMPS; i++) {
|
|
pCompData->sample[i].QTableIndex = pJPEGData->comp[i].QTableIndex;
|
|
pCompData->sample[i].DCTableIndex = pJPEGData->comp[i].DCTableIndex;
|
|
pCompData->sample[i].ACTableIndex = pJPEGData->comp[i].ACTableIndex;
|
|
}
|
|
|
|
/* Copy restartInterval from JIF info; it will be zero (0) if no DRI marker,
|
|
implying no restart markers.
|
|
*/
|
|
pCompData->restartInterval = pJPEGData->restartInterval;
|
|
|
|
/* Free decode data but not tables; they will be freed by ilJIFToRawDestroy() */
|
|
IL_FREE (pJPEGData);
|
|
|
|
/* Write rest of data (actual JPEG data, after tables etc.) to next filter.
|
|
Exclude the EOI marker if present (which it really should be).
|
|
*/
|
|
nBytes = streamRec.nBytesLeft;
|
|
if ((nBytes >= 2)
|
|
&& (streamRec.pData[nBytes-2] == ILJPGM_FIRST_BYTE)
|
|
&& (streamRec.pData[nBytes-1] == ILJPGM_EOI))
|
|
nBytes -= 2; /* remove EOI marker */
|
|
|
|
dstOffset = *pData->compressed.pDstOffset;
|
|
pDstPlane = pData->pDstImage->plane;
|
|
if (pDstPlane->bufferSize < (nBytes + dstOffset))
|
|
if (!_ilReallocCompressedBuffer (pData->pDstImage, 0, (nBytes + dstOffset)))
|
|
return IL_ERROR_MALLOC;
|
|
bcopy ((char *)streamRec.pData,
|
|
(char *)(pDstPlane->pPixels + dstOffset), nBytes);
|
|
*pData->compressed.pNBytesWritten = nBytes;
|
|
|
|
return IL_OK;
|
|
}
|
|
|
|
|
|
/* ----------------------- ilJIFToRaw ------------------------------ */
|
|
/* Called by _ilCompressJPEG() when the pipe image is JIF format, to
|
|
convert to "raw" JPEG format.
|
|
*/
|
|
static ilBool ilJIFToRaw (
|
|
ilPipe pipe,
|
|
ilPipeInfo *pInfo,
|
|
ilImageDes *pDes,
|
|
ilImageFormat *pFormat
|
|
)
|
|
{
|
|
ilJPEGData *pCompData;
|
|
ilJIFToRawPrivPtr pPriv;
|
|
ilDstElementData dstData;
|
|
|
|
/* Allocate an IL raw JPEG data block; point to it for pipe element */
|
|
if (!(pCompData = (ilJPEGData *)IL_MALLOC_ZERO (sizeof (ilJPEGData))))
|
|
return ilDeclarePipeInvalid (pipe, IL_ERROR_MALLOC);
|
|
|
|
dstData.producerObject = (ilObject)NULL;
|
|
pDes->compInfo.JPEG.reserved |= IL_JPEGM_RAW; /* now writing raw JPEG */
|
|
dstData.pDes = pDes;
|
|
dstData.pFormat = pFormat;
|
|
dstData.width = pInfo->width;
|
|
dstData.height = pInfo->height;
|
|
dstData.stripHeight = pInfo->height; /* write one strip = whole image */
|
|
dstData.constantStrip = TRUE;
|
|
dstData.pPalette = (unsigned short *)NULL;
|
|
dstData.pCompData = (ilPtr)pCompData;
|
|
|
|
pPriv = (ilJIFToRawPrivPtr)ilAddPipeElement (pipe, IL_FILTER,
|
|
sizeof (ilJIFToRawPrivRec), 0, (ilSrcElementData *)NULL, &dstData,
|
|
IL_NPF, IL_NPF, ilJIFToRawDestroy, ilJIFToRawExecute, 0);
|
|
if (!pPriv)
|
|
return FALSE;
|
|
|
|
pPriv->pCompData = pCompData;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* ============================= Raw to JIF =================================== */
|
|
/* This body of code supports converting "raw" (stripped) format JPEG to JIF.
|
|
*/
|
|
|
|
/* Private data for ilRawToJIFExecute() */
|
|
typedef struct {
|
|
ilJPEGData *pCompData; /* ptr to IL JPEG data */
|
|
long width; /* width of image */
|
|
long height; /* total # lines in image */
|
|
long stripHeight; /* # lines per strip */
|
|
ilBool nStripsWritten; /* inited to 0 by Init() */
|
|
long nLinesWritten; /* inited to 0 by Init() */
|
|
long dstOffset; /* inited to 0 by Init() */
|
|
} ilRawToJIFPrivRec, *ilRawToJIFPrivPtr;
|
|
|
|
|
|
/* ----------------------- ilRawToJIFInit ------------------------------ */
|
|
/* Init() function for converting JIF format pipe image to raw format.
|
|
*/
|
|
static ilError ilRawToJIFInit (
|
|
ilRawToJIFPrivPtr pPriv,
|
|
ilImageInfo *pSrcImage,
|
|
ilImageInfo *pDstImage
|
|
)
|
|
{
|
|
pPriv->nStripsWritten = 0;
|
|
pPriv->nLinesWritten = 0;
|
|
pPriv->dstOffset = 0;
|
|
return IL_OK;
|
|
}
|
|
|
|
|
|
/* ----------------------- ilRawToJIFExecute ------------------------------ */
|
|
/* Execute() function for converting "raw" JPEG to JIF format.
|
|
*/
|
|
static ilError ilRawToJIFExecute (
|
|
ilExecuteData *pData,
|
|
unsigned long dstLine,
|
|
unsigned long *pNLines
|
|
)
|
|
{
|
|
ilJPEGEncodeStream streamRec;
|
|
ilRawToJIFPrivPtr pPriv;
|
|
iljpgDataRec data;
|
|
ilImageInfo *pImage;
|
|
ilError error;
|
|
long nBytes, startOffset;
|
|
ilBool lastStrip;
|
|
|
|
|
|
pPriv = (ilRawToJIFPrivPtr)pData->pPrivate;
|
|
|
|
/* Setup stream to point to dst image buffer; set size, offset also.
|
|
"dstOffset" is offset into dst buffer; it is incremented by the # of
|
|
bytes written on each strip. When done, it is size of JIF image written.
|
|
*/
|
|
pImage = pData->pDstImage;
|
|
streamRec.pBuffer = pImage->plane[0].pPixels;
|
|
streamRec.pDst = streamRec.pBuffer + pPriv->dstOffset;
|
|
streamRec.pPastEndBuffer = streamRec.pBuffer + pImage->plane[0].bufferSize;
|
|
startOffset = ILJPG_ENCODE_OFFSET(&streamRec); /* stream offset before any writes */
|
|
pImage = pData->pSrcImage; /* point to src (raw) image */
|
|
|
|
/* If first strip write out the JIF header */
|
|
if (pPriv->nStripsWritten == 0) {
|
|
|
|
/* Setup iljpg data block: general info and tables from IL */
|
|
_ilJPEGDataIn (pImage->pDes, pPriv->width, pPriv->height, &data);
|
|
_ilJPEGTablesIn (pPriv->pCompData, &data);
|
|
|
|
/* Set restart interval: if a single strip, it is the restart interval
|
|
in the raw data (already set from above). If not a single strip, then
|
|
the raw data must not have a restart interval or an error - can't code
|
|
both of them! Otherwise restart interval is determined by strip size.
|
|
*/
|
|
if (pPriv->stripHeight != pPriv->height) { /* not single strip */
|
|
long mcuWidth, mcuHeight;
|
|
if (data.restartInterval)
|
|
return IL_ERROR_COMPRESSED_DATA; /* have restartInterval; error */
|
|
mcuWidth = 8 * data.maxHoriFactor;
|
|
mcuHeight = 8 * data.maxVertFactor;
|
|
data.restartInterval = /* # of mcus across * # down */
|
|
((data.width + mcuWidth - 1) / mcuWidth) *
|
|
((pPriv->stripHeight + mcuHeight - 1) / mcuHeight);
|
|
}
|
|
|
|
/* Write JIF to output stream, from IL's comp data */
|
|
if (error = iljpgEncodeJIF (&streamRec, &data, (iljpgJIFOffsetsPtr)NULL))
|
|
return error;
|
|
}
|
|
|
|
/* Write data from this strip to output buffer, reallocating if necessary.
|
|
Allocate 2 extra bytes for EOI or RSTn marker.
|
|
*/
|
|
nBytes = pData->compressed.nBytesToRead;
|
|
if ((streamRec.pDst + nBytes + 2) >= streamRec.pPastEndBuffer) {
|
|
if (error = _ilReallocJPEGEncode (&streamRec, nBytes + 2))
|
|
return error;
|
|
}
|
|
bcopy ((char *)(pImage->plane[0].pPixels + pData->compressed.srcOffset),
|
|
(char *)streamRec.pDst, nBytes);
|
|
streamRec.pDst += nBytes; /* bump past data just copied */
|
|
|
|
/* If last strip, write 2 byte EOI marker else write 2 byte restart marker. */
|
|
pPriv->nLinesWritten += *pNLines;
|
|
lastStrip = (pPriv->nLinesWritten >= pPriv->height);
|
|
if (lastStrip) {
|
|
*streamRec.pDst++ = ILJPGM_FIRST_BYTE; /* last strip; write EOI marker */
|
|
*streamRec.pDst++ = ILJPGM_EOI;
|
|
}
|
|
else {
|
|
/* Not last strip; write RST marker, with modulo 8 strip count */
|
|
*streamRec.pDst++ = ILJPGM_FIRST_BYTE;
|
|
*streamRec.pDst++ = ILJPGM_RST0 | (pPriv->nStripsWritten & 7);
|
|
}
|
|
pPriv->nStripsWritten += 1;
|
|
|
|
/* Store stream data into dst image; bump dst offset by # bytes written */
|
|
pImage = pData->pDstImage;
|
|
pImage->plane[0].pPixels = streamRec.pBuffer;
|
|
pImage->plane[0].bufferSize = streamRec.pPastEndBuffer - streamRec.pBuffer;
|
|
pPriv->dstOffset += ILJPG_ENCODE_OFFSET(&streamRec) - startOffset;
|
|
|
|
/* If last strip, pass # bytes written to next filter (= current dst offset;
|
|
note that dst offset set to 0 on first strip), and return # lines =
|
|
height of image, as JIF is one big strip. If not last strip, return
|
|
# lines as zero and next filter will not be called.
|
|
*/
|
|
if (lastStrip) {
|
|
*pNLines = pPriv->height;
|
|
*pData->compressed.pNBytesWritten = pPriv->dstOffset;
|
|
}
|
|
else *pNLines = 0;
|
|
|
|
return IL_OK;
|
|
}
|
|
|
|
|
|
/* ----------------------- ilRawToJIF ------------------------------ */
|
|
/* Called by _ilCompressJPEG() when the pipe image is "raw" JPEG format,
|
|
to convert to JIF format.
|
|
*/
|
|
static ilBool ilRawToJIF (
|
|
ilPipe pipe,
|
|
ilPipeInfo *pInfo,
|
|
ilImageDes *pDes,
|
|
ilImageFormat *pFormat
|
|
)
|
|
{
|
|
ilRawToJIFPrivPtr pPriv;
|
|
ilDstElementData dstData;
|
|
|
|
dstData.producerObject = (ilObject)NULL;
|
|
pDes->compInfo.JPEG.reserved &= ~IL_JPEGM_RAW; /* now writing JIF, not raw JPEG */
|
|
dstData.pDes = pDes;
|
|
dstData.pFormat = pFormat;
|
|
dstData.width = pInfo->width;
|
|
dstData.height = pInfo->height;
|
|
dstData.stripHeight = pInfo->height; /* write one strip = whole image */
|
|
dstData.constantStrip = TRUE;
|
|
dstData.pPalette = (unsigned short *)NULL;
|
|
dstData.pCompData = (ilPtr)NULL;
|
|
|
|
/* Add the filter. "Hold dst" flag set because filter will write to
|
|
output buffer, returning # lines = 0 until last strip written, after which
|
|
whole output buffer will be passed to next filter, # lines = height.
|
|
*/
|
|
pPriv = (ilRawToJIFPrivPtr)ilAddPipeElement (pipe, IL_FILTER,
|
|
sizeof (ilRawToJIFPrivRec), IL_ADD_PIPE_HOLD_DST, (ilSrcElementData *)NULL,
|
|
&dstData, ilRawToJIFInit, IL_NPF, IL_NPF, ilRawToJIFExecute, 0);
|
|
if (!pPriv)
|
|
return FALSE;
|
|
|
|
pPriv->pCompData = (ilJPEGData *)pInfo->pCompData;
|
|
pPriv->width = pInfo->width;
|
|
pPriv->height = pInfo->height;
|
|
pPriv->stripHeight = pInfo->stripHeight; /* input strip height */
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* ============================= Compress Raw =================================== */
|
|
/* This body of code supports compressing to JPEG "raw" format.
|
|
*/
|
|
|
|
/* Private data for ilCompressJPEGExecute() */
|
|
typedef struct {
|
|
ilJPEGData *pCompData; /* ptr to IL's view of JPEG data */
|
|
iljpgDataRec jpgData; /* data for iljpg functions */
|
|
iljpgPtr jpgPriv; /* pointer to iljpg private data */
|
|
} ilCompressJPEGPrivRec, *ilCompressJPEGPrivPtr;
|
|
|
|
|
|
/* -------------------- ilCompressJPEGInit -------------------------- */
|
|
/* Init() function for compressing to "raw" JPEG.
|
|
*/
|
|
static ilError ilCompressJPEGInit (
|
|
ilCompressJPEGPrivPtr pPriv,
|
|
ilImageInfo *pSrcImage,
|
|
ilImageInfo *pDstImage
|
|
)
|
|
{
|
|
ilError error;
|
|
|
|
/* Init iljpg package for compression */
|
|
if (error = iljpgEncodeInit (&pPriv->jpgData, &pPriv->jpgPriv))
|
|
return error;
|
|
return IL_OK;
|
|
}
|
|
|
|
|
|
/* -------------------- ilCompressJPEGCleanup -------------------------- */
|
|
/* Cleanup() function for compressing to "raw" JPEG.
|
|
*/
|
|
static ilError ilCompressJPEGCleanup (
|
|
ilCompressJPEGPrivPtr pPriv,
|
|
ilBool aborting
|
|
)
|
|
{
|
|
ilError error;
|
|
|
|
/* Call iljpg package cleanup function */
|
|
return iljpgEncodeCleanup (pPriv->jpgPriv);
|
|
}
|
|
|
|
|
|
/* ----------------------- ilCompressJPEGDestroy ------------------------------ */
|
|
/* Destroy() function for compressing to "raw" JPEG.
|
|
*/
|
|
static ilError ilCompressJPEGDestroy (
|
|
ilCompressJPEGPrivPtr pPriv
|
|
)
|
|
{
|
|
int i;
|
|
ilPtr pTable;
|
|
ilJPEGData *pCompData;
|
|
|
|
/* Free pCompData and any non-default Q tables */
|
|
if (pCompData = pPriv->pCompData) {
|
|
for (i = 0; i < 4; i++) {
|
|
if ((pTable = pCompData->QTables[i])
|
|
&& (pTable != iljpgLuminanceQTable)
|
|
&& (pTable != iljpgChrominanceQTable))
|
|
IL_FREE (pTable);
|
|
}
|
|
IL_FREE (pCompData);
|
|
}
|
|
return IL_OK;
|
|
}
|
|
|
|
|
|
/* ----------------------- ilCompressJPEGExecute ------------------------------ */
|
|
/* Execute() function for compressing to "raw" JPEG.
|
|
*/
|
|
static ilError ilCompressJPEGExecute (
|
|
ilExecuteData *pData,
|
|
unsigned long dstLine,
|
|
unsigned long *pNLines
|
|
)
|
|
{
|
|
ilCompressJPEGPrivPtr pPriv;
|
|
ilJPEGEncodeStream streamRec;
|
|
ilImageInfo *pImage;
|
|
ilImagePlaneInfo *pSrcPlane;
|
|
long nBytes, startOffset;
|
|
ilError error;
|
|
int i;
|
|
iljpgPtr pSrcPixels[IL_MAX_SAMPLES];
|
|
long nSrcBytesPerRow[IL_MAX_SAMPLES];
|
|
|
|
pPriv = (ilCompressJPEGPrivPtr)pData->pPrivate;
|
|
|
|
/* Setup stream to point to dst image buffer; set size, offset also */
|
|
pImage = pData->pDstImage;
|
|
streamRec.pBuffer = pImage->plane[0].pPixels;
|
|
streamRec.pDst = streamRec.pBuffer + *pData->compressed.pDstOffset;
|
|
streamRec.pPastEndBuffer = streamRec.pBuffer + pImage->plane[0].bufferSize;
|
|
startOffset = ILJPG_ENCODE_OFFSET(&streamRec); /* stream offset before any writes */
|
|
|
|
/* Encode one strip, writing output to dst buffer (pointed to by streamRec).
|
|
Input is from src buffer, starting at srcLine, divided by subsample factor
|
|
(which is "inverse" of JPEG factors; = max factor / component factor).
|
|
*/
|
|
pSrcPlane = pData->pSrcImage->plane;
|
|
for (i = 0; i < pPriv->jpgData.nComps; i++, pSrcPlane++) {
|
|
pSrcPixels[i] = pSrcPlane->pPixels + (pData->srcLine *
|
|
pPriv->jpgData.comp[i].vertFactor / pPriv->jpgData.maxVertFactor) *
|
|
pSrcPlane->nBytesPerRow;
|
|
nSrcBytesPerRow[i] = pSrcPlane->nBytesPerRow;
|
|
}
|
|
if (error = iljpgEncodeExecute (pPriv->jpgPriv, &streamRec, *pNLines,
|
|
pSrcPixels, nSrcBytesPerRow))
|
|
return error;
|
|
|
|
/* Set stream data back into dst image structure. Inform next filter of
|
|
the # bytes written = current stream offset - start offset.
|
|
*/
|
|
pImage->plane[0].pPixels = streamRec.pBuffer;
|
|
pImage->plane[0].bufferSize = streamRec.pPastEndBuffer - streamRec.pBuffer;
|
|
*pData->compressed.pNBytesWritten = ILJPG_ENCODE_OFFSET(&streamRec) - startOffset;
|
|
|
|
return IL_OK;
|
|
}
|
|
|
|
|
|
/* ------------------------- _ilScaleQTable -------------------------------- */
|
|
/* Malloc a new 64 byte table, copy given src Q table scaling by given Q factor
|
|
and return ptr to new table, or null if error.
|
|
*/
|
|
static ilPtr _ilScaleQTable (
|
|
ilPtr pSrc, /* ptr to 64 byte Q table to scale */
|
|
int Q /* IL Q factor: != 0, or no scale needed */
|
|
)
|
|
{
|
|
ilPtr pTable;
|
|
ilPtr pDst;
|
|
int i, value;
|
|
|
|
if (!(pTable = (ilPtr)IL_MALLOC (64)))
|
|
return (ilPtr)NULL;
|
|
|
|
/* Scale src to dst (pTable) by: dst = src * Qfactor / 50.
|
|
I don't know where this came from, but it is how the old code worked.
|
|
The old code however only allowed a max Q of 255; larger values became 255.
|
|
Here we will allow the maximum value of a short.
|
|
*/
|
|
for (i = 0, pDst = pTable; i < 64; i++) {
|
|
value = (*pSrc++ * Q) / 50;
|
|
if (value > 255)
|
|
value = 255;
|
|
else if (value < 1)
|
|
value = 1;
|
|
*pDst++ = value;
|
|
}
|
|
return pTable;
|
|
}
|
|
|
|
|
|
/* ----------------------- _ilCompressJPEG ------------------------------ */
|
|
/* Called by ilCompress() when desired compression is JPEG. The image
|
|
may already be JPEG-compressed, but possibly in the wrong format (JIF/raw).
|
|
This function handles conversions between those cases, or does
|
|
nothing if image is already in correct format.
|
|
On entry, the image must be either JPEG-compressed or uncompressed.
|
|
*/
|
|
IL_PRIVATE ilBool _ilCompressJPEG (
|
|
ilPipe pipe,
|
|
ilPipeInfo *pInfo,
|
|
ilImageDes *pDes,
|
|
ilImageFormat *pFormat,
|
|
ilSrcElementData *pSrcData,
|
|
ilPtr pEncodeData
|
|
)
|
|
{
|
|
ilCompressJPEGPrivPtr pPriv;
|
|
ilJPEGData *pCompData;
|
|
ilDstElementData dstData;
|
|
ilJPEGEncodeControl *pEncode;
|
|
int i, tableIndex, luminanceMask;
|
|
ilPtr pTable;
|
|
ilBool mustConvert;
|
|
long stripHeight, maxVert;
|
|
static ilJPEGEncodeControl ilDefaultJpegEncodeControl = {
|
|
0, /* short mustbezero; */
|
|
0 /* short Q; */
|
|
};
|
|
|
|
/* Check given encode data; default to JIF format, Q of 0 if null.
|
|
Unused part of mustbezero must be zero or error.
|
|
*/
|
|
if (!pEncodeData)
|
|
pEncode = &ilDefaultJpegEncodeControl;
|
|
else pEncode = (ilJPEGEncodeControl *)pEncodeData;
|
|
if (pEncode->mustbezero & ~IL_JPEGM_RAW)
|
|
return ilDeclarePipeInvalid (pipe, IL_ERROR_PAR_NOT_ZERO);
|
|
|
|
/* If already JPEG-compressed cvt to requested format (JIF/raw) if necessary */
|
|
if (pDes->compression == IL_JPEG) {
|
|
if (pDes->compInfo.JPEG.reserved & IL_JPEGM_RAW) /* src is raw */
|
|
if (pEncode->mustbezero & IL_JPEGM_RAW) { /* raw->raw */
|
|
pipe->context->error = IL_OK;
|
|
return TRUE;
|
|
}
|
|
else { /* raw->JIF */
|
|
return ilRawToJIF (pipe, pInfo, pDes, pFormat);
|
|
}
|
|
else /* src is JIF */
|
|
if (pEncode->mustbezero & IL_JPEGM_RAW) { /* JIF->raw */
|
|
return ilJIFToRaw (pipe, pInfo, pDes, pFormat);
|
|
}
|
|
else { /* JIF->JIF */
|
|
pipe->context->error = IL_OK;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/* Image is not JPEG; must be uncompressed. Check for supported image type.
|
|
Set maxVert to max vert subsampling; luminanceMask to sample #.
|
|
*/
|
|
switch (pDes->type) {
|
|
case IL_GRAY:
|
|
maxVert = 1;
|
|
luminanceMask = 1; /* gray is luminance */
|
|
break;
|
|
case IL_RGB:
|
|
maxVert = 1; /* no subsampling of RGB */
|
|
luminanceMask = ~0; /* use luminance tables for all components */
|
|
break;
|
|
case IL_YCBCR:
|
|
maxVert = 1;
|
|
if (pDes->typeInfo.YCbCr.sample[1].subsampleVert > maxVert)
|
|
maxVert = pDes->typeInfo.YCbCr.sample[1].subsampleVert;
|
|
if (pDes->typeInfo.YCbCr.sample[2].subsampleVert > maxVert)
|
|
maxVert = pDes->typeInfo.YCbCr.sample[2].subsampleVert;
|
|
luminanceMask = 1; /* Y is luminance */
|
|
break;
|
|
default:
|
|
return ilDeclarePipeInvalid (pipe, IL_ERROR_IMAGE_TYPE);
|
|
}
|
|
|
|
/* Force all samples to be 8 bit, 256 levels / sample */
|
|
for (i = 0, mustConvert = FALSE; i < pDes->nSamplesPerPixel; i++) {
|
|
if (pDes->nLevelsPerSample[i] != 256) {
|
|
pDes->nLevelsPerSample[i] = 256;
|
|
mustConvert = TRUE;
|
|
}
|
|
if (pFormat->nBitsPerSample[i] != 8) {
|
|
pFormat->nBitsPerSample[i] = 8;
|
|
mustConvert = TRUE;
|
|
}
|
|
}
|
|
if (mustConvert)
|
|
if (!ilConvert (pipe, pDes, pFormat, 0, (ilPtr)NULL))
|
|
return FALSE;
|
|
|
|
/* Convert to planar format if more than 1 sample/pixel. */
|
|
if ((pDes->nSamplesPerPixel > 1) && (pFormat->sampleOrder != IL_SAMPLE_PLANES)) {
|
|
pFormat->sampleOrder = IL_SAMPLE_PLANES;
|
|
if (!ilConvert (pipe, (ilImageDes *)NULL, pFormat, 0, (ilPtr)NULL))
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check pSrcData->stripHeight: make it a multiple of MCU. */
|
|
maxVert *= 8; /* now height of an MCU */
|
|
stripHeight = ((pSrcData->stripHeight + maxVert - 1) / maxVert) * maxVert;
|
|
if (stripHeight <= 0)
|
|
stripHeight = maxVert;
|
|
if (stripHeight > pInfo->height)
|
|
stripHeight = pInfo->height;
|
|
pSrcData->stripHeight = stripHeight;
|
|
|
|
/* Allocate an IL raw JPEG data block; point to it for pipe element */
|
|
if (!(pCompData = (ilJPEGData *)IL_MALLOC_ZERO (sizeof (ilJPEGData))))
|
|
return ilDeclarePipeInvalid (pipe, IL_ERROR_MALLOC);
|
|
|
|
/* Add the pipe element */
|
|
dstData.producerObject = (ilObject)NULL;
|
|
pDes->compInfo.JPEG.reserved |= IL_JPEGM_RAW; /* now writing raw JPEG */
|
|
pDes->compression = IL_JPEG;
|
|
dstData.pDes = pDes;
|
|
pFormat->sampleOrder = IL_SAMPLE_PIXELS; /* JPEG interleaved order */
|
|
dstData.pFormat = pFormat;
|
|
dstData.width = pInfo->width;
|
|
dstData.height = pInfo->height;
|
|
dstData.stripHeight = stripHeight;
|
|
dstData.constantStrip = TRUE;
|
|
dstData.pPalette = (unsigned short *)NULL;
|
|
dstData.pCompData = (ilPtr)pCompData;
|
|
|
|
pPriv = (ilCompressJPEGPrivPtr)ilAddPipeElement (pipe, IL_FILTER,
|
|
sizeof (ilCompressJPEGPrivRec), 0, pSrcData, &dstData,
|
|
ilCompressJPEGInit, ilCompressJPEGCleanup, ilCompressJPEGDestroy,
|
|
ilCompressJPEGExecute, 0);
|
|
if (!pPriv)
|
|
return FALSE;
|
|
|
|
/* Init tables to use: default luminance (index 0) or chrominance (index 1).
|
|
If "Q" not zero, scale up Q tables; must destroy them in Destroy().
|
|
*/
|
|
pPriv->pCompData = pCompData;
|
|
pCompData->restartInterval = 0; /* no restart markers added */
|
|
|
|
for (i = 0; i < pDes->nSamplesPerPixel; i++) {
|
|
if ((1 << i) & luminanceMask) {
|
|
pCompData->DCTables[0] = iljpgLuminanceDCTable;
|
|
pCompData->ACTables[0] = iljpgLuminanceACTable;
|
|
tableIndex = 0;
|
|
pTable = iljpgLuminanceQTable;
|
|
}
|
|
else {
|
|
pCompData->DCTables[1] = iljpgChrominanceDCTable;
|
|
pCompData->ACTables[1] = iljpgChrominanceACTable;
|
|
tableIndex = 1;
|
|
pTable = iljpgChrominanceQTable;
|
|
}
|
|
if (!pCompData->QTables[tableIndex]) { /* no Q table here yet; make one */
|
|
if (pEncode->Q) /* Q != 0; scale copy of Q table */
|
|
if (!(pTable = _ilScaleQTable (pTable, pEncode->Q)))
|
|
return ilDeclarePipeInvalid (pipe, IL_ERROR_MALLOC);
|
|
pCompData->QTables[tableIndex] = pTable;
|
|
}
|
|
pCompData->sample[i].QTableIndex = pCompData->sample[i].DCTableIndex =
|
|
pCompData->sample[i].ACTableIndex = tableIndex;
|
|
}
|
|
|
|
/* Init iljpg package data in private, based on IL view of JPEG comp data */
|
|
_ilJPEGDataIn (pDes, pInfo->width, pInfo->height, &pPriv->jpgData);
|
|
_ilJPEGTablesIn (pCompData, &pPriv->jpgData);
|
|
|
|
/* Data is now compressed to "raw" format. Convert to JIF if requested */
|
|
if (!(pEncode->mustbezero & IL_JPEGM_RAW)) {
|
|
ilGetPipeInfo (pipe, FALSE, pInfo, pDes, pFormat); /* update pipe info */
|
|
return ilRawToJIF (pipe, pInfo, pDes, pFormat);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|