/*
 * 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: iljpgdecodejif.c /main/3 1995/10/23 15:55:08 rswiston $ */
/**---------------------------------------------------------------------
***	
***    (c)Copyright 1992 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 <stdlib.h>

#include "iljpgdecodeint.h"


    /*  -------------------- iljpgFreeData -------------------------- */
    /*  Free the given data block and associated tables, for example as 
        created by iljpgDecodeJIF().
    */
    ILJPG_PUBLIC_EXTERN
iljpgError iljpgFreeData (
    iljpgDataPtr        pData
    )
{
    int                 i;

    if (!pData)                                 /* null data ptr; exit */
        return 0;
    for (i = 0; i < 4; i++) {
        if (pData->QTables[i])
            ILJPG_FREE (pData->QTables[i]);
        if (pData->DCTables[i])
            ILJPG_FREE (pData->DCTables[i]);
        if (pData->ACTables[i])
            ILJPG_FREE (pData->ACTables[i]);
        }
    ILJPG_FREE (pData);
    return 0;
}

    /*  -------------------- iljpgGet2Bytes -------------------------- */
    /*  Get 2 bytes from the given stream and return (short) value or error.
    */
static iljpgError iljpgGet2Bytes (
    ILJPG_DECODE_STREAM stream,
    int                *pValue                  /* RETURNED */
    )
{
    int                 byte0, byte1;
    iljpgError          error;

    if (!ILJPG_DECODE_GET_BYTE (stream, byte0, error))
        return error;
    if (!ILJPG_DECODE_GET_BYTE (stream, byte1, error))
        return error;
    *pValue = (byte0 << 8) + byte1;
    return 0;
}

    /*  -------------------- iljpgSkipBytes -------------------------- */
    /*  Skip given # of bytes in JPEG stream
    */
static iljpgError iljpgSkipBytes (
    ILJPG_DECODE_STREAM stream,
    int                 nBytes
    )
{
    int                 byte;
    iljpgError          error;

    while (nBytes-- > 0) {
        if (!ILJPG_DECODE_GET_BYTE (stream, byte, error))
            return error;
        }
    return 0;
}


    /*  -------------------- iljpgDecodeJIF -------------------------- */
    /*  Parse the given stream which must point to the beginning of a
        JIF (JPEG Interchange Format) stream.  Return a ptr to a created
        iljpgData block, which can be freed by iljpgFreeData(), or null
        if an error occurs (non-zero error code returned).
    */
    ILJPG_PUBLIC_EXTERN
iljpgError iljpgDecodeJIF (
    ILJPG_DECODE_STREAM stream,
    iljpgDataPtr       *ppData                  /* RETURNED */
    )
{
    iljpgDataPtr pData;
    iljpgBool           firstMarker, SOSFound, SOF0Found;
    iljpgError          error;
    int                 value, nFrameComps = 0, length;
    iljpgPtr           *ppTable;
    iljpgCompDataPtr    pComp;
    int        i, index;
    iljpgPtr   pTable;
    struct {
        int             id, horiFactor, vertFactor, QTableIndex;
        }               frameComp[ILJPG_MAX_COMPS], *pFrame;


    pData = (iljpgDataPtr)ILJPG_MALLOC_ZERO (sizeof (iljpgDataRec));
    if (!pData)
        return ILJPG_ERROR_DECODE_MALLOC;

    firstMarker = TRUE;
    SOSFound = FALSE;
    SOF0Found = FALSE;

        /*  Parse input stream until SOS marker found.
            Note that the "length" value following most markers includes the length
            of length itself, and thus must be made -2 to get length of rest of data.
        */
    while (!SOSFound) {
        do {                        /* get a marker */
            do {
                if (!ILJPG_DECODE_GET_BYTE (stream, value, error))
                    goto JIFError;
                } while (value != ILJPGM_FIRST_BYTE);
            do {
                if (!ILJPG_DECODE_GET_BYTE (stream, value, error))
                    goto JIFError;
                } while (value == ILJPGM_FIRST_BYTE);
            } while (!value);          /* skip "0" after ff */
            
            /*  First marker must be SOI or error; else parse marker */
        if (firstMarker) {
            if (value != ILJPGM_SOI)
                goto BadJIFData;
            firstMarker = FALSE;
            }
        else switch (value) {
          case ILJPGM_TEM:
            break;              /* legal (?) "stand-alone" markers - no data to skip */
          case ILJPGM_SOI:
          case ILJPGM_RST0: case ILJPGM_RST1: case ILJPGM_RST2: case ILJPGM_RST3:  
          case ILJPGM_RST4: case ILJPGM_RST5: case ILJPGM_RST6: case ILJPGM_RST7:
          case ILJPGM_EOI:
            goto BadJIFData;
            break;              /* illegal markers - should not see these before SOS */

            /*  DRI: get value and save into restartInterval */
          case ILJPGM_DRI:
            if ((error = iljpgGet2Bytes (stream, &value)))        /* length */
                goto JIFError;
            if ((error = iljpgGet2Bytes (stream, &value)))        /* restart interval */
                goto JIFError;
            pData->restartInterval = value;
            break;

            /*  Start Of Frame 0 (SOF0): save Q table indices; note marker seen. */
          case ILJPGM_SOF0:
            SOF0Found = TRUE;
            if ((error = iljpgGet2Bytes (stream, &value)))        /* length */
                goto JIFError;
            if (!ILJPG_DECODE_GET_BYTE (stream, value, error))
                goto JIFError;
            if (value != 8)                     /* precision != 8; not supported */
                goto BadJIFData;
            if ((error = iljpgGet2Bytes (stream, &pData->height)))
                goto JIFError;
            if ((error = iljpgGet2Bytes (stream, &pData->width)))
                goto JIFError;
            if (!ILJPG_DECODE_GET_BYTE (stream, nFrameComps, error))
                goto JIFError;
            if (nFrameComps > ILJPG_MAX_COMPS)
                goto BadJIFData;

                /*  Get and store info for each frame component (Ci, Hi, Vi, Tqi) */
            for (i = 0, pFrame = frameComp; i < nFrameComps; i++, pFrame++) {
                if (!ILJPG_DECODE_GET_BYTE (stream, pFrame->id, error))
                    goto JIFError;
                if (!ILJPG_DECODE_GET_BYTE (stream, value, error))
                    goto JIFError;
                pFrame->horiFactor = value >> 4;                /* Hi in upper nibble */
                pFrame->vertFactor = value & 0xf;               /* Vi in lower nibble */
                if (!ILJPG_DECODE_GET_BYTE (stream, value, error))
                    goto JIFError;
                if (value > 3)                                  /* Tqi must be 0..3 */
                    goto BadJIFData;
                pFrame->QTableIndex = value;
                }
            break;
          
            /*  Define Quantization Table (DQT): malloc memory for table and set into 
                pData->QTables for this index, freeing table if already there.
                Multiple tables ("n") can be defined: n = (length-2)/65;
            */
          case ILJPGM_DQT:
            if ((error = iljpgGet2Bytes (stream, &length)))       /* length */
                goto JIFError;
            length -= 2;                                        /* includes itself */
            while (length > 0) {
                if ((length -= 65) < 0)                         /* at least 65 bytes */
                    goto JIFError;                              
                if (!ILJPG_DECODE_GET_BYTE (stream, value, error))  /* Pq (0), Tq */
                    goto JIFError;
                value &= 0xf;                                   /* table index (0..3) */
                if (value > (ILJPG_MAX_COMPS-1))
                    goto BadJIFData;

                    /*  Read bytes into malloc'd space, free old if non-null and store */
                if (!(pTable = (iljpgPtr)ILJPG_MALLOC (64))) {
                    error = ILJPG_ERROR_DECODE_MALLOC;
                    goto JIFError;
                    }
                if (pData->QTables[value])
                    ILJPG_FREE (pData->QTables[value]);
                pData->QTables[value] = pTable;
                for (i = 0; i < 64; i++) {
                    if (!ILJPG_DECODE_GET_BYTE (stream, value, error))
                        goto JIFError;
                    *pTable++ = value;
                    }
                }
            break;
          
            /*  Define Huffman Table (DHT): see comment for DQT. 
                Multiple tables can be defined in this marker; the "length" value must
                be used; = 2 * n * (17 + m), where "n" is the number of tables, and "m"
                is the sum of 16 length bytes (bytes 1..16, after Th).
                The table index (Th) is 0,1 for DC tables 0,1 or 16, 17 for AC tables 0,1.
                4 AC or DC tables are allowed,used by "extended" DCT mode.
            */
          case ILJPGM_DHT:
            if ((error = iljpgGet2Bytes (stream, &length)))
                goto JIFError;
            length -= 2;                            /* length includes itself */
            while (length > 0) {
                iljpgByte       L[16];
                int             sumL;

                if ((length -= 17) < 0)             /* sub Th, 16 bytes of Li */
                    goto JIFError;
                if (!ILJPG_DECODE_GET_BYTE (stream, value, error))  /* Th */
                    goto JIFError;
                i = value & 0xf;
                if (i > (ILJPG_MAX_COMPS-1))
                    goto BadJIFData;
                if (value >= 16)
                     ppTable = &pData->ACTables[i];
                else ppTable = &pData->DCTables[i];

                    /*  Read 16 bytes of Li; set sumL to their sum */
                for (i = 0, sumL = 0; i < 16; i++) {
                    if (!ILJPG_DECODE_GET_BYTE (stream, value, error))  /* Th */
                        goto JIFError;
                    sumL += value;
                    L[i] = value;
                    }

                    /*  Malloc space for table; copy 1st 16 bytes; read in rest */
                if ((length -= sumL) < 0)           /* sub # bytes of huff codes */
                    goto JIFError;
                if (!(pTable = (iljpgPtr)ILJPG_MALLOC (16 + sumL))) {
                    error = ILJPG_ERROR_DECODE_MALLOC;
                    goto JIFError;
                    }
                if (*ppTable)
                    ILJPG_FREE (*ppTable);
                *ppTable = pTable;
                for (i = 0; i < 16; i++)
                    *pTable++ = L[i];
                for (i = 0; i < sumL; i++) {
                    if (!ILJPG_DECODE_GET_BYTE (stream, value, error))
                        goto JIFError;
                    *pTable++ = value;
                    }
                }   /* END while length > 0 */
            break;

            /*  Start Of Scan (SOS): read the scan header and leave stream positioned
                right after header, i.e. pointing to the image data.
            */
          case ILJPGM_SOS:
            SOSFound = TRUE;                        /* terminate while loop */
            if (!SOF0Found)                         /* SOS before SOF0 is an error */
                goto BadJIFData;
            if ((error = iljpgGet2Bytes (stream, &value)))        /* length */
                goto JIFError;

            if (!ILJPG_DECODE_GET_BYTE (stream, value, error))  /* Ns (0..3) */
                goto JIFError;
            if (value > (ILJPG_MAX_COMPS-1))
                goto BadJIFData;
            pData->nComps = value;

                /*  For each scan component: get "id" and find component with same id
                    in frameComponents or error.  Copy component data from frame to 
                    pData->component, and get DC/AC table indices from stream.
                    Set pData->maxHori/VertFactor to largest component factor found.
                */
            for (i = 0, pComp = pData->comp; i < pData->nComps; i++, pComp++) {
                if (!ILJPG_DECODE_GET_BYTE (stream, value, error))  /* Csj (id) */
                    goto JIFError;
                pFrame = &frameComp[nFrameComps - 1];
                while (TRUE) {
                    if (pFrame->id == value)
                        break;
                    if (pFrame == frameComp)
                        goto BadJIFData;            /* frames searched; id not found */
                    pFrame--;                       /* seach back one */
                    }

                    /*  pFrame points to frame with given id; copy data to *pComp */
                pComp->horiFactor = pFrame->horiFactor;
                if (pComp->horiFactor > pData->maxHoriFactor)
                    pData->maxHoriFactor = pComp->horiFactor;
                pComp->vertFactor = pFrame->vertFactor;
                if (pComp->vertFactor > pData->maxVertFactor)
                    pData->maxVertFactor = pComp->vertFactor;

                    /*  Get DC/AC table index; error if that or Q table not defd yet. */
                if (!ILJPG_DECODE_GET_BYTE (stream, value, error))  /* Tdj/Taj */
                    goto JIFError;
                index = (value >> 4) & 3;               /* DC table index, 0..3 */
                if (!pData->DCTables[index])
                    goto BadJIFData;
                pComp->DCTableIndex = index;
                index = value & 3;                      /* AC table index, 0..3 */
                if (!pData->ACTables[index])
                    goto BadJIFData;
                pComp->ACTableIndex = index;
                pComp->QTableIndex = pFrame->QTableIndex;
                if (!pData->QTables[pComp->QTableIndex])
                    goto BadJIFData;
                }   /* END each scan header component */

            if ((error = iljpgSkipBytes (stream, 3)))     /* skip Ss, Se, Ah/Al */
                goto JIFError;
            break;

                /* All other markers have lengths: get length and skip length-2 bytes */
          default:
            if ((error = iljpgGet2Bytes (stream, &value)))
                goto JIFError;
            if ((error = iljpgSkipBytes (stream, value - 2))) /* "length" already skipped */
                goto JIFError;
            break;
            }   /* END switch marker */
        }       /* END while (!SOSFound) */

    *ppData = pData;                                /* return filled in data block */
    return 0;

    /*  Branch point if bad JPEG data */
BadJIFData:
    iljpgFreeData (pData);
    return ILJPG_ERROR_DECODE_JIF;

    /*  Branch point if other error; return "error" */
JIFError:
    iljpgFreeData (pData);
    return error;
}



