/*
 * 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: ildecompjpeg.c /main/5 1996/06/19 12:23:41 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).
***
***-------------------------------------------------------------------*/

#include "ilint.h"
#include "ilpipelem.h"
#include "ilerrors.h"
#include "ildecomp.h"
#include "iljpgdecode.h"
#include "ilutiljpeg.h"

    /*  Private data when decompressing JPEG data */
typedef struct {
    iljpgDataRec        jpgData;        /* data for iljpg?() functions */
    ilBool              firstStrip;     /* set true by Init(): this is first strip */
    long                nLinesWritten;  /* inited to 0 by Init() */
    long                stripHeight;    /* height of strips being written */
    iljpgPtr            pJPEGPriv;      /* iljpg private data */
    iljpgDataPtr        pJPEGData;      /* JIF only: ptr to JPEG data */
    ilJPEGDecodeStream  streamRec;      /* JIF only: src decoding stream */
    } ilJPEGPrivRec, *ilJPEGPrivPtr;


    /*  -------------------- ilDecompInit -------------------------- */
    /*  Init() function for decompressing JPEG.
    */
static ilError ilDecompInit (
    ilJPEGPrivPtr       pPriv,
    ilImageInfo        *pSrcImage,
    ilImageInfo        *pDstImage
    )
{
    pPriv->firstStrip = TRUE;
    pPriv->nLinesWritten = 0;
    pPriv->pJPEGData = (iljpgDataPtr)NULL;
    pPriv->pJPEGPriv = (iljpgPtr)NULL;
    return IL_OK;
}


    /*  -------------------- ilDecompCleanup -------------------------- */
    /*  Cleanup() function for decompressing JPEG.
    */
static ilError ilDecompCleanup (
    ilJPEGPrivPtr       pPriv,
    ilBool              aborting
    )
{
    ilError         error;

        /*  Cleanup from JPEG decode, but only if firstStrip setup done */
    if (!pPriv->firstStrip) {
        if (pPriv->pJPEGPriv) 
            if ((error = iljpgDecodeCleanup (pPriv->pJPEGPriv)))
                return error;
        if (pPriv->pJPEGData)
            return iljpgFreeData (pPriv->pJPEGData);
        }
    return IL_OK;
}


    /*  -------------------- ilDecompRawExecute -------------------------- */
    /*  Execute() function for decompressing "raw" (non-JIF) JPEG: handle strips.
    */
static ilError ilDecompRawExecute (
    ilExecuteData      *pData,
    unsigned long       dstLine,
    unsigned long      *pNLines
    )
{
    ilJPEGPrivPtr pPriv;
    ilJPEGDecodeStream  streamRec;
    ilError             error;
    iljpgPtr            pPixels[ILJPG_MAX_COMPS];
    long                nBytesPerRow[ILJPG_MAX_COMPS];
    int                 i;
    ilImagePlaneInfo   *pPlane;
    ilImageInfo *pSrcImage, *pDstImage;

    pPriv = (ilJPEGPrivPtr)pData->pPrivate;
    pSrcImage = pData->pSrcImage;
    pDstImage = pData->pDstImage;

        /*  If first strip, setup iljpg data and call iljpg Init() function */
    if (pPriv->firstStrip) {
        _ilJPEGTablesIn ((ilJPEGData *)pSrcImage->pCompData, &pPriv->jpgData);
        if ((error = iljpgDecodeInit (&pPriv->jpgData, &pPriv->pJPEGPriv)))
            return error;
        pPriv->firstStrip = FALSE;
        }

        /*  Decode one strip into dst pixels */
    streamRec.pData = pSrcImage->plane[0].pPixels + pData->compressed.srcOffset;
    streamRec.nBytesLeft = pData->compressed.nBytesToRead;

    for (i = 0, pPlane = pData->pDstImage->plane; 
      i < pData->pDstImage->pDes->nSamplesPerPixel; i++, pPlane++) {
        pPixels[i] = pPlane->pPixels + 
            (dstLine * pPriv->jpgData.comp[i].vertFactor / pPriv->jpgData.maxVertFactor) *
                                            pPlane->nBytesPerRow;
        nBytesPerRow[i] = pPlane->nBytesPerRow;
        }

    return iljpgDecodeExecute (pPriv->pJPEGPriv, &streamRec, TRUE, *pNLines, 
                               pPixels, nBytesPerRow);
}


    /*  -------------------- ilDecompJIFExecute -------------------------- */
    /*  Execute() function for decompressing JIF: a single strip.
    */
static ilError ilDecompJIFExecute (
    ilExecuteData      *pData,
    unsigned long       dstLine,
    unsigned long      *pNLines
    )
{
    ilJPEGPrivPtr pPriv;
    ilError             error;
    int                 i;
    iljpgPtr            pPixels[ILJPG_MAX_COMPS];
    long                nBytesPerRow[ILJPG_MAX_COMPS];
    ilImagePlaneInfo   *pPlane;
    ilBool              firstStrip;

    pPriv = (ilJPEGPrivPtr)pData->pPrivate;

        /*  If firstStrip decode header, determine # of strips and init for decode */
    firstStrip = pPriv->firstStrip;
    pPriv->firstStrip = FALSE;
    if (firstStrip) {
        pPriv->streamRec.pData = pData->pSrcImage->plane[0].pPixels;
        pPriv->streamRec.nBytesLeft = pData->compressed.nBytesToRead;
        if ((error = iljpgDecodeJIF (&pPriv->streamRec, &pPriv->pJPEGData)))
            return error;
        if ((error = iljpgDecodeInit (pPriv->pJPEGData, &pPriv->pJPEGPriv)))
            return error;
        }

        /*  Decode one strip into dst pixels, or remaining lines if last strip */
    for (i = 0, pPlane = pData->pDstImage->plane; 
      i < pData->pDstImage->pDes->nSamplesPerPixel; i++, pPlane++) {
        pPixels[i] = pPlane->pPixels + 
            (dstLine * pPriv->jpgData.comp[i].vertFactor / pPriv->jpgData.maxVertFactor) *
                                            pPlane->nBytesPerRow;
        nBytesPerRow[i] = pPlane->nBytesPerRow;
        }
    if ((pPriv->nLinesWritten + pPriv->stripHeight) > pPriv->jpgData.height)
         *pNLines = pPriv->jpgData.height - pPriv->nLinesWritten;
    else *pNLines = pPriv->stripHeight;
    if ((error = iljpgDecodeExecute (pPriv->pJPEGPriv, &pPriv->streamRec, FALSE, *pNLines,
                                    pPixels, nBytesPerRow)))
        return error;

        /*  Handle based on whether firstStrip and/or last strip:
                first?  last?
                YES     YES     normal return; image is one strip, no call backs needed.
                YES     NO      return "call back" error code: want this filter to
                                be called again until another return cancels it
                NO      YES     cancel call back; don't want to be called again
                NO      NO      normal return; more strips to decompress
        */
    pPriv->nLinesWritten += pPriv->stripHeight;
    if (firstStrip)
        if (pPriv->nLinesWritten >= pPriv->jpgData.height)      /* last strip */
             return IL_OK;
        else return IL_ERROR_ELEMENT_AGAIN;
    else
        if (pPriv->nLinesWritten >= pPriv->jpgData.height)
             return IL_ERROR_ELEMENT_COMPLETE;
        else return IL_OK;
}


    /*  -------------------- _ilDecompJPEG -------------------------- */
    /*  Called by ilDecompress() when pipe image is JPEG compressed.  Add a filter to
        decompress the pipe image.
    */
ilBool _ilDecompJPEG (
    ilPipe              pipe,
    ilPipeInfo         *pInfo,                              
    ilImageDes         *pDes
    )
{
    ilDstElementData    dstData;
    ilImageDes          des;
    ilImageFormat       format;
    ilJPEGPrivPtr       pPriv;
    iljpgDataRec        jpgData;


    memset(&jpgData, 0, sizeof(jpgData));

        /*  Define decompressed output from this filter: planar format unless
            a single component image (e.g. gray).
        */
    dstData.producerObject = (ilObject)NULL;
    des = *pDes;
    des.compression = IL_UNCOMPRESSED;
    dstData.pDes = &des;
    if (pDes->nSamplesPerPixel == 1)
        dstData.pFormat = IL_FORMAT_BYTE;
    else {
        IL_INIT_IMAGE_FORMAT (&format);
        format.sampleOrder = IL_SAMPLE_PLANES;
        format.byteOrder = IL_MSB_FIRST;
        format.rowBitAlign = 8;
        format.nBitsPerSample[0] =
            format.nBitsPerSample[1] =
            format.nBitsPerSample[2] = 8;
        dstData.pFormat = &format;
        }
    dstData.width = pInfo->width;
    dstData.height = pInfo->height;
    dstData.pPalette = (unsigned short *)NULL;
    dstData.pCompData = (ilPtr)NULL;

        /*  Init iljpg package data */
    _ilJPEGDataIn (&des, pInfo->width, pInfo->height, &jpgData);

        /*  If "raw" JPEG, decode in strips, the size of those presented to this filter */
    if (pDes->compInfo.JPEG.reserved & IL_JPEGM_RAW) {
        dstData.stripHeight = pInfo->stripHeight;   /* decode strips as they come in */
        dstData.constantStrip = pInfo->constantStrip;
        pPriv = (ilJPEGPrivPtr)ilAddPipeElement(pipe, IL_FILTER, 
                sizeof(ilJPEGPrivRec), 0, (ilSrcElementData *)NULL, &dstData, 
                ilDecompInit, ilDecompCleanup, IL_NPF, ilDecompRawExecute, NULL, 0);
        }
    else {
            /*  JIF: a single data stream (strip) with tables in the stream: decompress
                in IL's default strip size for this image, made a multiple of MCUs.
            */
        long        stripHeight, mcuHeight;
        stripHeight = ilRecommendedStripHeight (dstData.pDes, dstData.pFormat,
                                                dstData.width, dstData.height);
        mcuHeight = 8 * jpgData.maxVertFactor;
        if (mcuHeight == 0)
            mcuHeight = 32;                         /* ? error, default to max possible */
        stripHeight = ((stripHeight + mcuHeight - 1) / mcuHeight) * mcuHeight;
        if (stripHeight > dstData.height)
            stripHeight = dstData.height;
        dstData.stripHeight = stripHeight;
        dstData.constantStrip = TRUE;
        pPriv = (ilJPEGPrivPtr)ilAddPipeElement(pipe, IL_FILTER, 
                sizeof(ilJPEGPrivRec), 0, (ilSrcElementData *)NULL, &dstData, 
                ilDecompInit, ilDecompCleanup, IL_NPF, ilDecompJIFExecute, NULL, 0);
        }

        /*  Fill in data into private; copy jpgData */
    if (!pPriv) 
        return FALSE;
    pPriv->jpgData = jpgData;
    pPriv->stripHeight = dstData.stripHeight;

    return TRUE;
}



