/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: ijglwrap.cpp,v 1.1.26.1 2004/07/09 01:53:22 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

// system
#include <stdio.h>
#include <setjmp.h>

// include
#include "hxtypes.h"
#include "hxcom.h"
#include "hxresult.h"
#include "ihxpckts.h"

// pnmisc
#include "netbyte.h"
#include "baseobj.h"

// pncont
#include "hxbuffer.h"

// pxcomlib
#include "glist.h"
#include "gstring.h"
#include "pxtransp.h"

// jpeglib
#include "ijglwrap.h"
extern "C"
{
#include "jbuffsrc.h"
#include "jbuffdst.h"
}

// pndebug
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static char HX_THIS_FILE[] = __FILE__;
#endif

CIJGLibraryWrapper::CIJGLibraryWrapper()
{
    // Set the state
    m_lRefCount           = 0;
    m_ulState             = kStateNotCreated;
    m_pImageBuffer        = NULL;
    m_ulImageBufferSize   = 0;
    m_ulPadWidth          = 0;
    m_bRowsInverted       = FALSE;
    m_bSuspended          = FALSE;
    m_lSessionHandle      = 0;
    m_bNeedPacket         = TRUE;
    m_bValid              = TRUE;
    m_ulLastSeqNum        = 0;
    m_bBigEndian          = FALSE;
    m_pLastOpaque         = NULL;
    m_ulOpacity           = 255;
    m_bChromaKeySpecified = FALSE;
    m_ulChromaKey         = 0;
    m_ulChromaKeyTol      = 0;
    m_ulChromaKeyOpacity  = 0;
}

CIJGLibraryWrapper::~CIJGLibraryWrapper()
{
    Terminate();
}

STDMETHODIMP CIJGLibraryWrapper::QueryInterface(REFIID riid, void** ppvObj)
{
    HX_RESULT retVal = HXR_OK;

    if (IsEqualIID(riid, IID_IUnknown))
    {
        AddRef();
        *ppvObj = (IUnknown *) this;
    }
    else
    {
        *ppvObj = NULL;
        retVal  = HXR_NOINTERFACE;
    }

    return retVal;
}

STDMETHODIMP_(UINT32) CIJGLibraryWrapper::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(UINT32) CIJGLibraryWrapper::Release()
{
    
    if (InterlockedDecrement(&m_lRefCount) > 0)
        return m_lRefCount;

    delete this;

    return 0;
}

HX_RESULT CIJGLibraryWrapper::Initialize(UINT32 ulBufferOffset)
{
    /* Check the state */
    if (m_ulState != kStateNotCreated)
    {
        return HXR_UNEXPECTED;
    }

    /* Initialize the error manager */
    m_cDecompress.err = (struct jpeg_error_mgr *) &m_cErrMgr;

    /* Set up the default methods we won't override */
    jpeg_std_error(&m_cErrMgr.m_cPubErrMgr);

    /* Set the fields in the public manager we want to override */
    m_cErrMgr.m_cPubErrMgr.error_exit     = ErrorExit;
    m_cErrMgr.m_cPubErrMgr.output_message = OutputMessage;

    /* Give the string enough to hold the biggest error string */
    if (m_cErrMgr.m_cErrStr.reserve(JMSG_LENGTH_MAX))
    {
        return HXR_OUTOFMEMORY;
    }

    /* Create the decompression object */
    jpeg_create_decompress(&m_cDecompress);

    /* Set the state */
    m_ulState = kStateCreated;

    /* Now we initialize the source manager */
    m_cDecompress.src = (struct jpeg_source_mgr *) &m_cSrcMgr;

    /* Set all the values in the source manager */
    m_cSrcMgr.m_cPubSrcMgr.init_source       = InitSource;
    m_cSrcMgr.m_cPubSrcMgr.fill_input_buffer = FillInputBuffer;
    m_cSrcMgr.m_cPubSrcMgr.skip_input_data   = SkipInputData;
    m_cSrcMgr.m_cPubSrcMgr.resync_to_restart = jpeg_resync_to_restart;
    m_cSrcMgr.m_cPubSrcMgr.term_source       = TermSource;
    m_cSrcMgr.m_cPubSrcMgr.bytes_in_buffer   = 0;
    m_cSrcMgr.m_cPubSrcMgr.next_input_byte   = NULL;

    /* Reset the list and the current index */
    m_cSrcMgr.m_cBufferList.EraseAll();
    m_cSrcMgr.m_lCurrentIndex  = -1;
    m_cSrcMgr.m_bFirstFill     = TRUE;
    m_cSrcMgr.m_ulBufferOffset = ulBufferOffset;

    /* Set the flags */
    m_bSuspended    = FALSE;

    // Check the endian-ness
    m_bBigEndian = TestBigEndian(); // FALSE if little-endian (intel)

    return HXR_OK;
}

void CIJGLibraryWrapper::Terminate()
{
    /* Release the IHXBuffer objects remaining in our list */
    for (GListIterator itr  = m_cSrcMgr.m_cBufferList.Begin();
                       itr != m_cSrcMgr.m_cBufferList.End(); itr++)
    {
        /* Get the pointer from the iterator */
        IHXBuffer *pBuffer = (IHXBuffer *) *itr;
        HX_RELEASE(pBuffer);
    }

    /* Now erase the list */
    m_cSrcMgr.m_cBufferList.EraseAll();

    /* Destroy the decompression object */
    jpeg_destroy_decompress(&m_cDecompress);

    // Release the last opaque buffer
    HX_RELEASE(m_pLastOpaque);

    /* Set the state */
    m_ulState = kStateNotCreated;
}

void CIJGLibraryWrapper::AppendBuffer(IHXBuffer *pBuffer)
{
    /* Check for input error conditions */
    if (pBuffer == NULL)
    {
        return;
    }

    /* Append the buffer to the list */
    m_cSrcMgr.m_cBufferList.PushBack((void *) pBuffer);

    /* Add a reference to this object */
    pBuffer->AddRef();

    // If we were suspended, then clear the suspended flag
    m_bSuspended = FALSE;
}

HX_RESULT CIJGLibraryWrapper::ReadHeader(IHXValues *pValues)
{
    /* Check for input error conditions */
    if (pValues == NULL)
    {
        return HXR_INVALID_PARAMETER;
    }

    /* Check the state */
    if (m_ulState != kStateCreated)
    {
        return HXR_UNEXPECTED;
    }

    /*
     * Set our longjmp() parameters. This is where we will
     * come in the case of a fatal error.
     */
    if (setjmp(m_cErrMgr.m_cSetJmpBuf))
    {
        /* If we get here, the JPEG code has signaled a fatal error. */
        jpeg_destroy_decompress(&m_cDecompress);
        m_ulState = kStateFatalError;
        return HXR_FAILED;
    }

    /* Read the header */
    boolean bRet = jpeg_read_header(&m_cDecompress, TRUE);
    if (bRet == FALSE)
    {
        m_bSuspended = TRUE;
        return HXR_OK;
    }

    /* Set the parameters */
    pValues->SetPropertyULONG32("InputImageWidth",  (UINT32) m_cDecompress.image_width);
    pValues->SetPropertyULONG32("InputImageHeight", (UINT32) m_cDecompress.image_height);
    pValues->SetPropertyULONG32("NumComponents",    (UINT32) m_cDecompress.num_components);

    /* Set the state */
    m_ulState = kStateHeaderRead;

    return HXR_OK;
}

HX_RESULT CIJGLibraryWrapper::ReadHeader()
{
    HX_RESULT retVal = HXR_OK;

    if (m_ulState == kStateCreated)
    {
        // Set our longjmp() parameters. This is where we will
        // come in the case of a fatal error.
        if (setjmp(m_cErrMgr.m_cSetJmpBuf))
        {
            // If we get here, the JPEG code has signaled a fatal error.
            jpeg_destroy_decompress(&m_cDecompress);
            m_ulState = kStateFatalError;
            return HXR_FAILED;
        }

        // Read the header
        boolean bRet = jpeg_read_header(&m_cDecompress, TRUE);
        if (bRet)
        {
            // Set the state
            m_ulState = kStateHeaderRead;
        }
        else
        {
            m_bSuspended = TRUE;
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

HX_RESULT CIJGLibraryWrapper::SetOutputParameters(BYTE  *pImageBuffer,
                                                  UINT32 ulImageBufferSize,
                                                  UINT32 ulPadWidth,
                                                  BOOL   bRowsInverted)
{
    /* Check for input error conditions */
    if (pImageBuffer      == NULL ||
        ulImageBufferSize == 0    ||
        ulPadWidth        == 0)
    {
        return HXR_INVALID_PARAMETER;
    }

    /* Check the state */
    if (m_ulState != kStateHeaderRead)
    {
        return HXR_UNEXPECTED;
    }

    /* Set the output member variables */
    m_pImageBuffer      = pImageBuffer;
    m_ulImageBufferSize = ulImageBufferSize;
    m_ulPadWidth        = ulPadWidth;
    m_bRowsInverted     = bRowsInverted;

    return HXR_OK;
}

void CIJGLibraryWrapper::GetLastPacketBuffer(IHXBuffer **ppBuffer)
{
    if (m_cSrcMgr.m_cBufferList.Size() > 0)
    {
        *ppBuffer = (IHXBuffer *) m_cSrcMgr.m_cBufferList.Last();
    }
    else
    {
        *ppBuffer = NULL;
    }
}

HX_RESULT CIJGLibraryWrapper::Decompress()
{
    if (m_ulState != kStateHeaderRead       &&
        m_ulState != kStateStarted          &&
        m_ulState != kStateReadingScanlines &&
        m_ulState != kStateReadingCompleted)
    {
        return HXR_UNEXPECTED;
    }

    /*
     * Set our longjmp() parameters. This is where we will
     * come in the case of a fatal error.
     */
    if (setjmp(m_cErrMgr.m_cSetJmpBuf))
    {
        /* If we get here, the JPEG code has signaled a fatal error. */
        jpeg_destroy_decompress(&m_cDecompress);
        return HXR_FAILED;
    }

    /* State loop */
    boolean bRet;
    do
    {
        if (m_ulState == kStateHeaderRead)
        {
            /* Start the decompression */
            bRet = jpeg_start_decompress(&m_cDecompress);
            if (bRet == TRUE)
            {
                /* We had enough data */
                m_bSuspended = FALSE;
                /* Set the new state */
                m_ulState    = kStateStarted;
            }
            else
            {
                /* We suspended */
                m_bSuspended = TRUE;
            }
        }
        else if (m_ulState == kStateStarted)
        {
            /* Decompress the image */
            while (m_cDecompress.output_scanline < m_cDecompress.output_height)
            {
                /* Compute the point in the buffer to write into */
                BYTE* pRowBuf = NULL;
                if (m_bRowsInverted)
                {
                    pRowBuf = m_pImageBuffer +
                              (m_cDecompress.output_height - 1 - m_cDecompress.output_scanline) * m_ulPadWidth;
                }
                else
                {
                    pRowBuf = m_pImageBuffer + m_cDecompress.output_scanline * m_ulPadWidth;
                }
                /* Read a scanline */
                UINT32 ulNumScanLines = jpeg_read_scanlines(&m_cDecompress, &pRowBuf, 1);
                if (ulNumScanLines == 0)
                {
                    m_bSuspended = TRUE;
                    break;
                }
                if (m_cDecompress.out_color_space == JCS_GRAYSCALE)
                {
                    ExpandGrayToRGB(pRowBuf, (UINT32) m_cDecompress.output_width, m_bBigEndian);
                }
                else
                {
                    if (m_bBigEndian)
                    {
                        SwapDWordBytes((UINT32*) pRowBuf, m_cDecompress.output_width);
                    }
                }
                if (m_ulOpacity < 255 || m_bChromaKeySpecified)
                {
                    ProcessOpacityAndChromaKey(pRowBuf,
                                               (UINT32) m_cDecompress.output_width,
                                               m_ulOpacity,
                                               m_bChromaKeySpecified,
                                               m_ulChromaKey,
                                               m_ulChromaKeyTol,
                                               m_ulChromaKeyOpacity);
                }
            }
            if (m_cDecompress.output_scanline >= m_cDecompress.output_height)
            {
                m_ulState = kStateReadingCompleted;
            }
        }
        else if (m_ulState == kStateReadingCompleted)
        {
            /* Finish the decompression */
            bRet = jpeg_finish_decompress(&m_cDecompress);
            if (bRet == TRUE)
            {
                /* We had enough data */
                m_bSuspended = FALSE;
                /* Set the new state */
                m_ulState = kStateFinished;
            }
            else
            {
                /* We suspended */
                m_bSuspended = TRUE;
            }
        }
    }
    while (m_bSuspended == FALSE && m_ulState != kStateFinished);

    return HXR_OK;
}

void /*_cdecl*/ CIJGLibraryWrapper::ErrorExit(j_common_ptr cinfo)
{
    /* Check for input error conditions */
    if (cinfo->err == NULL)
    {
        return;
    }
    if (cinfo->err->output_message == NULL)
    {
        return;
    }

    /* Call our output message function */
    (*cinfo->err->output_message)(cinfo);

    /* Get a pointer to a WrapperErrorMgr */
    WrapperErrorMgr *pMgr = (WrapperErrorMgr *) cinfo->err;

    /* Return control to the setjmp point */
    longjmp(pMgr->m_cSetJmpBuf, 1);
};

void /*_cdecl*/ CIJGLibraryWrapper::OutputMessage(j_common_ptr cinfo)
{
    /* Check for input error conditions */
    if (cinfo->err == NULL)
    {
        return;
    }
    if (cinfo->err->format_message == NULL)
    {
        return;
    }

    /* Get a pointer to a WrapperErrorMgr */
    WrapperErrorMgr *pMgr = (WrapperErrorMgr *) cinfo->err;

    /* Create the message */
    (*cinfo->err->format_message)(cinfo, (char *) pMgr->m_cErrStr.c_str());
}

UINT32 CIJGLibraryWrapper::GetMemorySum()
{
    /* Run through our current buffer list and add up the sizes */
    UINT32 ulSum = 0;
    for (GListIterator itr  = m_cSrcMgr.m_cBufferList.Begin();
                       itr != m_cSrcMgr.m_cBufferList.End(); itr++)
    {
        /* Get the pointer from the iterator */
        IHXBuffer *pBuffer = (IHXBuffer *) *itr;

        /* Add the buffer size */
        ulSum += pBuffer->GetSize();
    }

    return ulSum;
}

void /*_cdecl*/ CIJGLibraryWrapper::InitSource(j_decompress_ptr cinfo)
{
    /* No work to do here */
}

boolean /*_cdecl*/ CIJGLibraryWrapper::FillInputBuffer(j_decompress_ptr cinfo)
{
    /* Check for input error conditions */
    if (cinfo->src == NULL)
    {
        ERREXIT(cinfo, JERR_INPUT_EMPTY);
    }

    /* Get a pointer to a BufferListSrcMgr */
    BufferListSrcMgr *pMgr = (BufferListSrcMgr *) cinfo->src;

    /* Check to see if there are more buffers in the list */
    if (pMgr->m_lCurrentIndex + 1 >= (INT32) pMgr->m_cBufferList.Size())
    {
        /* We don't have any more buffers in our list, so suspend. */
        return FALSE;
    }

    /* We do have more buffers, so increment the index and get the MyBuffer pointer */
    pMgr->m_lCurrentIndex++;
    IHXBuffer *pBuffer = (IHXBuffer *) pMgr->m_cBufferList[pMgr->m_lCurrentIndex];
    if (pBuffer == NULL)
    {
        ERREXIT(cinfo, JERR_INPUT_EMPTY);
    }

    /* Save the current public source manager state variables */
    UINT32 ulOldBytesInBuffer = pMgr->m_cPubSrcMgr.bytes_in_buffer;
    BYTE  *pOldNextInputByte  = (BYTE *) pMgr->m_cPubSrcMgr.next_input_byte;

    // There might be some header bytes at the beginning of each buffer,
    // so we must offset by this amount
    pMgr->m_cPubSrcMgr.bytes_in_buffer = pBuffer->GetSize()   - pMgr->m_ulBufferOffset;
    pMgr->m_cPubSrcMgr.next_input_byte = pBuffer->GetBuffer() + pMgr->m_ulBufferOffset;

    /*
     * If this is the first successful fill we've done, then
     * don't try to remove any buffers from the list.
     */
    if (pMgr->m_bFirstFill == TRUE)
    {
        pMgr->m_bFirstFill = FALSE;
        return TRUE;
    }
    
    /*
     * Determine which buffers we can release. The restart point is
     * defined by the next_input_byte pointer. All buffers which are
     * before the buffer which contains the restart point can be deleted.
     */
    while (pMgr->m_cBufferList.Size() > 0)
    {
        /* Get the buffer at the front of the list */
        IHXBuffer *pListBuffer = (IHXBuffer *) pMgr->m_cBufferList.First();

        /* Check to see if the old restart point is within this buffer */
        if (pOldNextInputByte >= pListBuffer->GetBuffer() &&
            pOldNextInputByte <= pListBuffer->GetBuffer() + pListBuffer->GetSize())
        {
            /*
             * The old restart marker is within this buffer, so
             * we can't delete it. This is because the jpeg library
             * may have to back up into this buffer
             */
            break;
        }

        /* Remove this buffer from the front of the list */
        pMgr->m_cBufferList.PopFront();

        /*
         * Decrement the current index since there's now one
         * less buffer at the beginning of the list
         */
        pMgr->m_lCurrentIndex--;

        /* Release the IHXBuffer object */
        HX_RELEASE(pListBuffer);
    }

    return TRUE;
}

void /*_cdecl*/ CIJGLibraryWrapper::SkipInputData(j_decompress_ptr cinfo, long num_bytes)
{
    /* Check for input error conditions */
    if (cinfo->src == NULL)
    {
        ERREXIT(cinfo, JERR_INPUT_EMPTY);
    }

    /* Get a pointer to a BufferListSrcMgr */
    BufferListSrcMgr *pMgr = (BufferListSrcMgr *) cinfo->src;

    /* If we don't currently have any data, then we're finished */
    if (pMgr->m_cBufferList.Size() == 0)
    {
        return;
    }

    /*
     * All we're going to do for now is skip if we can within the
     * data we have in the current buffer.
     */
    INT32 lLeftOverBytes = pMgr->m_cPubSrcMgr.bytes_in_buffer - num_bytes;
    if (lLeftOverBytes >= 0)
    {
        /* We've got enough in the current buffer */
        pMgr->m_cPubSrcMgr.bytes_in_buffer -= num_bytes;
        pMgr->m_cPubSrcMgr.next_input_byte += num_bytes;
    }
}

void /*_cdecl*/ CIJGLibraryWrapper::TermSource(j_decompress_ptr cinfo)
{
    /* No work to do here */
}

HX_RESULT CIJGLibraryWrapper::TranscodeToRestartInterval(IHXBuffer *      pInputImage,
                                                         UINT32            ulRestartInterval,
                                                         REF(IHXBuffer *) rpOutputImage)
{
    // Initialize the decompression error manager
    WrapperErrorMgr               cErrMgr;
    struct jpeg_decompress_struct cSrcInfo;
    cSrcInfo.err = (struct jpeg_error_mgr *) &cErrMgr;

    // Set up the default methods we won't override
    jpeg_std_error(&cErrMgr.m_cPubErrMgr);

    // Set the fields in the public manager we want to override
    cErrMgr.m_cPubErrMgr.error_exit     = ErrorExit;
    cErrMgr.m_cPubErrMgr.output_message = OutputMessage;

    // Give the string enough to hold the biggest error string
    if (cErrMgr.m_cErrStr.reserve(JMSG_LENGTH_MAX))
    {
        return HXR_OUTOFMEMORY;
    }

    // Forward declare some local variables
    struct jpeg_compress_struct cDstInfo;
    BYTE *                      pDstBuf = NULL;

    // Here's the error return point
    if (setjmp(cErrMgr.m_cSetJmpBuf))
    {
        // If we get here, the JPEG library has signaled a fatal error.
        jpeg_destroy_compress(&cDstInfo);
        jpeg_destroy_decompress(&cSrcInfo);
        HX_VECTOR_DELETE(pDstBuf);
        return HXR_FAIL;
    }

    // Initialize the JPEG decompression object with our error handling
    jpeg_create_decompress(&cSrcInfo);

    // Specify data source for decompression
    jpeg_buffer_src(&cSrcInfo, pInputImage->GetBuffer(), pInputImage->GetSize());

    // Read input file header
    (void) jpeg_read_header(&cSrcInfo, TRUE);

    // Read the DCT coefficients out of the input file
    jvirt_barray_ptr *src_coef_arrays = jpeg_read_coefficients(&cSrcInfo);

    // Create an output buffer the size of the uncompressed image - that
    // will surely be enough
    UINT32 ulDstBufSize = cSrcInfo.image_width * cSrcInfo.image_height * cSrcInfo.num_components;
    pDstBuf             = new BYTE [ulDstBufSize];
    if (!pDstBuf)
    {
        jpeg_destroy_compress(&cDstInfo);
        jpeg_destroy_decompress(&cSrcInfo);
        HX_VECTOR_DELETE(pDstBuf);
        return HXR_OUTOFMEMORY;
    }

    // Initialize the compression error manager
    cDstInfo.err = (struct jpeg_error_mgr *) &cErrMgr;

    // Initialize the JPEG compression object with our error handling.
    jpeg_create_compress(&cDstInfo);

    // Initialize destination compression parameters from source values
    jpeg_copy_critical_parameters(&cSrcInfo, &cDstInfo);

    // Output DCT arrays will be same as input DCT arrays
    jvirt_barray_ptr *dst_coef_arrays = src_coef_arrays;

    // Specify data destination for compression
    jpeg_buffer_dst(&cDstInfo, pDstBuf, ulDstBufSize);

    // Set restart interval
    cDstInfo.restart_interval = ulRestartInterval;
    cDstInfo.restart_in_rows  = 0;

    // Start compressor (note no image data is actually written here)
    jpeg_write_coefficients(&cDstInfo, dst_coef_arrays);

    // Finish compression
    jpeg_finish_compress(&cDstInfo);

    // Compute the output file size
    UINT32 ulOutSize = ulDstBufSize - cDstInfo.dest->free_in_buffer;

    // Destroy the compression object
    jpeg_destroy_compress(&cDstInfo);

    // Finish decompression
    (void) jpeg_finish_decompress(&cSrcInfo);

    // Destroy the decompression object
    jpeg_destroy_decompress(&cSrcInfo);

    // Create an output IHXBuffer
    IHXBuffer *pBuffer = new CHXBuffer();
    if (!pBuffer)
    {
        HX_VECTOR_DELETE(pDstBuf);
        return HXR_OUTOFMEMORY;
    }
    pBuffer->AddRef();

    // Set the output IHXBuffer to the output data
    HX_RESULT retVal = pBuffer->Set(pDstBuf, ulOutSize);
    if (retVal != HXR_OK)
    {
        HX_VECTOR_DELETE(pDstBuf);
        HX_RELEASE(pBuffer);
        return retVal;
    }

    // Now we're done with the intermediate buffer
    HX_VECTOR_DELETE(pDstBuf);

    // Set the outgoing buffer
    rpOutputImage = pBuffer;

    return HXR_OK;
}

HX_RESULT CIJGLibraryWrapper::GetLastOpaqueBuffer(REF(IHXBuffer*) rpOpaque)
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_pLastOpaque)
    {
        HX_RELEASE(rpOpaque);
        rpOpaque = m_pLastOpaque;
        rpOpaque->AddRef();
        retVal   = HXR_OK;
    }

    return retVal;
}

void CIJGLibraryWrapper::SetLastOpaqueBuffer(IHXBuffer* pOpaque)
{
    if (pOpaque)
    {
        HX_RELEASE(m_pLastOpaque);
        m_pLastOpaque = pOpaque;
        m_pLastOpaque->AddRef();
    }
}

void CIJGLibraryWrapper::SetChromaKeyInfo(UINT32 ulKey, UINT32 ulTol, UINT32 ulOp)
{
    m_ulChromaKey         = ulKey;
    m_ulChromaKeyTol      = ulTol;
    m_ulChromaKeyOpacity  = ulOp;
    m_bChromaKeySpecified = TRUE;
}

void CIJGLibraryWrapper::ExpandGrayToRGB(BYTE*  pBuf,
                                         UINT32 ulWidth,
                                         BOOL   bBigEndian)
{
    if (pBuf)
    {
        BYTE*   pGray = pBuf + ulWidth - 1;
        UINT32* pRGB  = ((UINT32*) pBuf) + ulWidth - 1;
        while (ulWidth--)
        {
            *pRGB = ((*pGray) << 16) | ((*pGray) << 8) | (*pGray);
            pRGB--;
            pGray--;
        }
    }
}

void CIJGLibraryWrapper::ProcessOpacityAndChromaKey(BYTE*  pBuf,
                                                    UINT32 ulWidth,
                                                    UINT32 ulOpacity,
                                                    BOOL   bChromaKey,
                                                    UINT32 ulChromaKey,
                                                    UINT32 ulChromaKeyTol,
                                                    UINT32 ulChromaKeyOpacity)
{
    if (pBuf)
    {
        // Clip the opacities
        if (ulOpacity > 255) ulOpacity = 255;
        if (ulChromaKeyOpacity > 255) ulChromaKeyOpacity = 255;
        // Compute alphas (go ahead and pre-shift)
        UINT32 ulAlpha       = (255 - ulOpacity) << 24;
        UINT32 ulChromaAlpha = (255 - ulChromaKeyOpacity) << 24;
        // Determine whether we need to process opacity,
        // chroma key, or both
        if (ulOpacity < 255 && !bChromaKey)
        {
            // Process opacity but not chroma key
            UINT32* pPix = (UINT32*) pBuf;
            while (ulWidth--)
            {
                *pPix = (*pPix & 0x00FFFFFF) | ulAlpha;
                pPix++;
            }
        }
        else if (ulOpacity >= 255 && bChromaKey)
        {
            // Process chroma key but not opacity
            UINT32* pPix = (UINT32*) pBuf;
            while (ulWidth--)
            {
                if (DoesChromaKeyMatch(*pPix, ulChromaKey, ulChromaKeyTol))
                {
                    *pPix = (*pPix & 0x00FFFFFF) | ulChromaAlpha;
                }
                pPix++;
            }
        }
        else if (ulOpacity < 255 && bChromaKey)
        {
            // Compute new chroma alpha
            UINT32 ulNewChromaKeyOpacity = ulChromaKeyOpacity * ulOpacity / 255;
            UINT32 ulNewChromaAlpha      = (255 - ulNewChromaKeyOpacity) << 24;
            // Process both opacity and chroma key
            UINT32* pPix = (UINT32*) pBuf;
            while (ulWidth--)
            {
                if (DoesChromaKeyMatch(*pPix, ulChromaKey, ulChromaKeyTol))
                {
                    *pPix = (*pPix & 0x00FFFFFF) | ulNewChromaAlpha;
                }
                else
                {
                    *pPix = (*pPix & 0x00FFFFFF) | ulAlpha;
                }
                pPix++;
            }
        }
    }
}
