/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: pxcjpgrn.cpp,v 1.1.26.1 2004/07/09 01:51:18 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 ***** */

// include
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcom.h"
#include "hxcomm.h"
#include "ihxpckts.h"
#include "hxplugn.h"
#include "hxfiles.h"
#include "hxauth.h"
#include "hxver.h"
#include "hxvsurf.h"

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

// pncont
#include "hxslist.h"
#include "hxstring.h"
#include "hxmap.h"

// pxcomlib
#include "rpcodec.h"
#include "gstring.h"
#include "glist.h"
#include "pxrndcod.h"
#include "pxutil.h"
#include "pxmapmgr.h"

// jpeglib
#include "ijglwrap.h"

// pxjpeg2
#include "pxcjpgrn.h"
#include "rpjpgdll.ver"

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

const char*  CRealPixJPEGRendererCodec::m_pszDescription       = "Helix RealPix JPEG Renderer Codec Plugin";
const char*  CRealPixJPEGRendererCodec::m_pszCopyright         = HXVER_COPYRIGHT;
const char*  CRealPixJPEGRendererCodec::m_pszMoreInfoURL       = HXVER_MOREINFO;
const char*  CRealPixJPEGRendererCodec::m_ppszFileMimeType[]   = {"image/vndr.rn-realpix.jpeg", NULL};
const UINT32 CRealPixJPEGRendererCodec::m_ulHighestSupportedContentVersion = HX_ENCODE_PROD_VERSION(0, 0, 0, 0);
const UINT32 CRealPixJPEGRendererCodec::m_ulHighestSupportedStreamVersion  = HX_ENCODE_PROD_VERSION(0, 0, 0, 0);

CRealPixJPEGRendererCodec::CRealPixJPEGRendererCodec()
{
    m_lRefCount           = 0;
    m_pContext            = NULL;
    m_pCommonClassFactory = NULL;
    m_pMapManager         = NULL;
}

CRealPixJPEGRendererCodec::~CRealPixJPEGRendererCodec()
{
    ReleaseAllWrappers();
    HX_RELEASE(m_pContext);
    HX_RELEASE(m_pCommonClassFactory);
    HX_RELEASE(m_pMapManager);
}

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

    if (ppvObj)
    {
        // Set default
        *ppvObj = NULL;
        // Check for IID type
        if (IsEqualIID(riid, IID_IUnknown))
        {
            AddRef();
            *ppvObj = (IUnknown*) (IHXPlugin*) this;
        }
        else if (IsEqualIID(riid, IID_IHXPlugin))
        {
            AddRef();
            *ppvObj = (IHXPlugin*) this;
        }
        else if (IsEqualIID(riid, IID_IHXRealPixRendererCodec))
        {
            AddRef();
            *ppvObj = (IHXRealPixRendererCodec*) this;
        }
        else
        {
            retVal = HXR_NOINTERFACE;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

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


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

    delete this;

    return 0;
}


STDMETHODIMP CRealPixJPEGRendererCodec::GetPluginInfo(REF(BOOL)        bMultipleLoad,
                                                      REF(const char*) pDescription,
                                                      REF(const char*) pCopyright,
                                                      REF(const char*) pMoreInfoURL,
                                                      REF(ULONG32)     ulVersionNumber)
{
    bMultipleLoad   = TRUE;
    pDescription    = m_pszDescription;
    pCopyright      = m_pszCopyright;
    pMoreInfoURL    = m_pszMoreInfoURL;
    ulVersionNumber = TARVER_ULONG32_VERSION;

    return HXR_OK;
}

STDMETHODIMP CRealPixJPEGRendererCodec::InitPlugin(IUnknown* pContext)
{
    HX_RESULT retVal = HXR_OK;

    if (pContext)
    {
        // Save a copy of the context
        HX_RELEASE(m_pContext);
        m_pContext = pContext;
        m_pContext->AddRef();

        HX_RELEASE(m_pCommonClassFactory);
        retVal = m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                                            (void**) &m_pCommonClassFactory);
        if (SUCCEEDED(retVal))
        {
            HX_RELEASE(m_pMapManager);
            m_pMapManager = new PXMapManager();
            if (m_pMapManager)
            {
                m_pMapManager->AddRef();
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

STDMETHODIMP CRealPixJPEGRendererCodec::GetRendererCodecInfo(REF(const char**) rppszFileMimeType,
                                                             REF(UINT32)       rulHighestSupportedContentVersion,
                                                             REF(UINT32)       rulHighestSupportedStreamVersion)
{
    rppszFileMimeType                 = m_ppszFileMimeType;
    rulHighestSupportedContentVersion = m_ulHighestSupportedContentVersion;
    rulHighestSupportedStreamVersion  = m_ulHighestSupportedStreamVersion;

    return HXR_OK;
}

STDMETHODIMP CRealPixJPEGRendererCodec::ValidInputData(IHXBuffer* pBuffer, REF(BOOL) rbValid)
{
    HX_RESULT retVal = HXR_OK;

    // Set default
    rbValid = FALSE;

    if (pBuffer)
    {
        if (pBuffer->GetSize() >= 11)
        {
            BYTE* pBuf = pBuffer->GetBuffer();
            if (pBuf)
            {
                // Check if this is a legal JFIF file
                if (pBuf[0] == 0xFF && pBuf[1] == 0xD8 && pBuf[2]  == 0xFF && pBuf[3] == 0xE0 &&
                    pBuf[4] == 0x00 && pBuf[5] == 0x10 && pBuf[6]  == 'J'  && pBuf[7] == 'F'  &&
                    pBuf[8] == 'I'  && pBuf[9] == 'F'  && pBuf[10] == '\0')
                {
                    rbValid = TRUE;
                }
                else if (pBuf[0] == 0xFF && pBuf[1] == 0xD8)
                {
                    // Usually, jpeg images coming out of editing programs
                    // have an SOI marker followed by an APP0 marker. The APP0
                    // marker is the "JFIF" marker which is the defacto standard.
                    // However, it is not a real standard, so in a very few cases,
                    // some devices such as a few digital cameras don't output an
                    // APP0 marker - they go have an SOI marker followed directly
                    // by one or more DQT markers. We decode these images just
                    // fine - it's just that they don't have the APP0 marker. 
                    // Therefore, we shouldn't fail on them.
                    rbValid = TRUE;
                }
            }
            else
            {
                retVal = HXR_INVALID_PARAMETER;
            }
        }
        else
        {
            retVal = HXR_INVALID_PARAMETER;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

STDMETHODIMP CRealPixJPEGRendererCodec::GetHeaderInfo(IHXBuffer*      pBuffer,
                                                      IHXBuffer*      pOpaqueImageData,
                                                      REF(HXxSize)     rImageDim,
                                                      REF(UINT32)      rulNumFrames,
                                                      REF(IHXValues*) rpImageInfo,
                                                      REF(UINT32)      rulSessionHandle)
{
    HX_RESULT retVal = HXR_OK;

    if (pBuffer && m_pMapManager)
    {
        // Set defaults
        rImageDim.cx = 0;
        rImageDim.cy = 0;
        rulNumFrames = 1;
        rpImageInfo  = NULL;

        // Create a wrapper object
        CIJGLibraryWrapper* pWrapper = new CIJGLibraryWrapper();
        if (pWrapper)
        {
            // Addref the wrapper object
            pWrapper->AddRef();
            // Initialize the object
            retVal = pWrapper->Initialize(0); // no offset within the buffer since we pass nested buffers
            if (SUCCEEDED(retVal))
            {
                BOOL bValid = FALSE;
                retVal      = ValidInputData(pBuffer, bValid);
                if (SUCCEEDED(retVal))
                {
                    if (bValid)
                    {
                        // Initialize the last sequence number
                        pWrapper->SetLastSequenceNumber(0);
                        // Set need packet flag
                        pWrapper->SetNeedPacket(FALSE);
                        // Append the buffer to the list
                        pWrapper->AppendBuffer(pBuffer);
                        // Read the header
                        retVal = pWrapper->ReadHeader();
                        if (SUCCEEDED(retVal))
                        {
                            // Set image dimensions
                            rImageDim.cx = pWrapper->GetImageWidth();
                            rImageDim.cy = pWrapper->GetImageHeight();
                            // Add it to the map
                            retVal = m_pMapManager->AddEntry((void*) pWrapper, rulSessionHandle);
                            if (SUCCEEDED(retVal))
                            {
                                // AddRef it since it's in the map now
                                pWrapper->AddRef();
                                // Set session handle
                                pWrapper->SetSessionHandle((INT32) rulSessionHandle);
                            }
                        }
                    }
                    else
                    {
                        retVal = HXR_FAIL;
                    }
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        HX_RELEASE(pWrapper);
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

STDMETHODIMP CRealPixJPEGRendererCodec::GetFrameInfo(UINT32           ulSessionHandle,
                                                     UINT32           ulFrameNum,
                                                     REF(HXxRect)     rFrameDim,
                                                     REF(IHXValues*) rpMoreInfo)
{
    HX_RESULT retVal = HXR_FAIL;

    if (ulSessionHandle && !ulFrameNum && m_pMapManager)
    {
        // Set defaults
        rFrameDim.left   = 0;
        rFrameDim.top    = 0;
        rFrameDim.right  = 0;
        rFrameDim.bottom = 0;
        rpMoreInfo       = NULL;

        CIJGLibraryWrapper* pWrapper = NULL;
        retVal                       = m_pMapManager->GetEntry(ulSessionHandle, (void**) &pWrapper);
        if (SUCCEEDED(retVal))
        {
            // Create an IHXValues
            IHXValues* pInfo = NULL;
            retVal            = m_pCommonClassFactory->CreateInstance(CLSID_IHXValues,
                                                                      (void**) &pInfo);
            if (SUCCEEDED(retVal))
            {
                // Tell the renderer we don't have transparency
                pInfo->SetPropertyULONG32("UsesAlphaChannel", 0);
                // Set the out parameters
                rFrameDim.right  = pWrapper->GetImageWidth();
                rFrameDim.bottom = pWrapper->GetImageHeight();
                rpMoreInfo       = pInfo;
                rpMoreInfo->AddRef();
            }
            HX_RELEASE(pInfo);
        }
    }

    return retVal;
}

STDMETHODIMP CRealPixJPEGRendererCodec::SetDecompressParam(UINT32      ulSessionHandle,
                                                           UINT32      ulFrameNum,
                                                           IHXBuffer* pOutputBuffer,
                                                           HXxSize     cFrameDim,
                                                           UINT32      ulRowStride,
                                                           UINT32      ulBitsPerPixel,
                                                           UINT32      ulColorFormat,
                                                           BOOL        bRowsInverted,
                                                           IHXValues* pMoreParam)
{
    HX_RESULT retVal = HXR_FAIL;

    if (ulFrameNum     == 0  && pOutputBuffer &&
        ulBitsPerPixel == 32 && ulColorFormat == HX_RGB &&
        m_pMapManager)
    {
        CIJGLibraryWrapper* pWrapper = NULL;
        retVal                       = m_pMapManager->GetEntry(ulSessionHandle, (void**) &pWrapper);
        if (SUCCEEDED(retVal))
        {
            // Make sure that we are decoding to 1/1
            if (cFrameDim.cx == pWrapper->GetImageWidth() &&
                cFrameDim.cy == pWrapper->GetImageHeight())
            {
                retVal = pWrapper->SetOutputParameters(pOutputBuffer->GetBuffer(),
                                                       pOutputBuffer->GetSize(),
                                                       ulRowStride,
                                                       bRowsInverted);
            }
            else
            {
                retVal = HXR_INVALID_PARAMETER;
            }
        }
    }

    return retVal;
}

HX_RESULT CRealPixJPEGRendererCodec::InsertDummyData(CIJGLibraryWrapper* pWrap,
                                                     IHXBuffer*         pCurrData,
                                                     IHXBuffer*         pCurrOpaque)
{
    HX_RESULT retVal = HXR_OK;

    // Get the last data buffer appended to the library's decompression buffer list
    IHXBuffer* pLastOpaque = NULL;
    retVal = pWrap->GetLastOpaqueBuffer(pLastOpaque);
    if (SUCCEEDED(retVal))
    {
        // Compute the number of lost packets
        BYTE*  pCurOpBuf      = pCurrOpaque->GetBuffer();
        BYTE*  pLstOpBuf      = pLastOpaque->GetBuffer();
        UINT32 ulCurrSeqNum = 0;
        UINT32 ulLastSeqNum = 0;
        UnPack32(pCurOpBuf, ulCurrSeqNum);
        UnPack32(pLstOpBuf, ulLastSeqNum);
        UINT32 ulNumLostPackets = ulCurrSeqNum - ulLastSeqNum - 1;

        // This image has restart markers. So we need to first find out the
        // value of the last restart marker. We know, because we parsed the image,
        // that the last two bytes of a packet will be 0xFF 0xDq where q = [0,7].
        // If the last two bytes are NOT that, then this is the first data packet.
        IHXBuffer* pLastData = NULL;
        pWrap->GetLastPacketBuffer(&pLastData);
        if (pLastData)
        {
            BYTE   *pData            = pLastData->GetBuffer();
            UINT32  ulLen            = pLastData->GetSize();
            UINT32  ulLastByte       = pData[ulLen - 1];
            UINT16  usLastStartBlock = 0;
            UINT16  usLastBlockCount = 0;
            pLstOpBuf                = pLastOpaque->GetBuffer() + 8;
            UnPack16(pLstOpBuf, usLastStartBlock);
            UnPack16(pLstOpBuf, usLastBlockCount);

            // Compute the next marker
            UINT32 ulNextMarker;
            if (ulLastSeqNum > 0)
            {
                // The last packet received was not the header packet. Therefore, we
                // should be able to look at the last two bytes of the last packet
                ulNextMarker = ulLastByte + 1;
                if (ulNextMarker == 0xD8)
                {
                    ulNextMarker = 0xD0;
                }
            }
            else
            {
                // The last packet received was the header packet. Therefore,
                // the first restart marker is 0xD0
                ulNextMarker = 0xD0;
            }

            // Get the current starting block index
            pCurOpBuf = pCurrOpaque->GetBuffer() + 8;
            UINT16 usCurStartBlock = 0;
            UnPack16(pCurOpBuf, usCurStartBlock);

            // Compute the number of blocks to fill in and the size of the dummy buffer
            UINT32 ulNumBlocksToFill = usCurStartBlock - usLastStartBlock - usLastBlockCount;
            if (ulNumBlocksToFill >= ulNumLostPackets)
            {
                // What we will do is put 1 block per lost packet up until the last lost packet,
                // where we will fill in the rest of the lost blocks
                for (UINT32 i = 0; i < ulNumLostPackets && SUCCEEDED(retVal); i++)
                {
                    UINT32 ulNumBlocksInThisPacket = 0;
                    if (i == ulNumLostPackets - 1)
                    {
                        // We're doing the last lost packet, so we fill
                        // in the rest of the lost blocks in this packet
                        ulNumBlocksInThisPacket = ulNumBlocksToFill - (ulNumLostPackets - 1);
                    }
                    else
                    {
                        // We fill in only 1 block in this packet
                        ulNumBlocksInThisPacket = 1;
                    }

                    // Compute the size of the opaque data of this packet
                    UINT32 ulDummySize = ulNumBlocksInThisPacket * 6;

                    // Create an IHXBuffer
                    IHXBuffer *pDummy = NULL;
                    retVal             = m_pCommonClassFactory->CreateInstance(CLSID_IHXBuffer,
                                                                               (void **) &pDummy);
                    if (SUCCEEDED(retVal))
                    {
                        // Set the size
                        retVal = pDummy->SetSize(ulDummySize);
                        if (SUCCEEDED(retVal))
                        {
                            // Loop through the blocks to fill in dummy data
                            pData = pDummy->GetBuffer();
                            for (UINT32 j = ulNumBlocksInThisPacket; j; j--)
                            {
                                // Set this block
                                pData[0] = (BYTE) 0;
                                pData[1] = (BYTE) 0;
                                pData[2] = (BYTE) 0;
                                pData[3] = (BYTE) 0;
                                pData[4] = (BYTE) 0xFF;
                                pData[5] = (BYTE) ulNextMarker;

                                // Increment the buffer pointer
                                pData   += 6;

                                // Modulo-8 increment the marker
                                ulNextMarker++;
                                if (ulNextMarker == 0xD8)
                                {
                                    ulNextMarker = 0xD0;
                                }
                            }
                            // Now append this to the buffer list
                            pWrap->AppendBuffer(pDummy);
                        }
                    }
                    // Now we can release the buffer, since the wrapper AddRef's it
                    HX_RELEASE(pDummy);
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }
    else
    {
        pWrap->SetValid(FALSE);
    }

    return HXR_OK;
}

STDMETHODIMP CRealPixJPEGRendererCodec::Decompress(UINT32      ulSessionHandle,
                                                   IHXBuffer* pBuffer,
                                                   IHXBuffer* pOpaquePacketData)
{
    HX_RESULT retVal = HXR_FAIL;

    if (ulSessionHandle && pBuffer && m_pMapManager)
    {
        CIJGLibraryWrapper* pWrapper = NULL;
        retVal                       = m_pMapManager->GetEntry(ulSessionHandle, (void**) &pWrapper);
        if (SUCCEEDED(retVal))
        {
            // If the wrapper has been labeled invalid, that means that we lost the
            // header packet - therefore we don't need to do any of this stuff.
            if (pWrapper->GetValid())
            {
                // Check to see if this is the header packet being sent again for this session
                if (pWrapper->GetNeedPacket())
                {
                    if (pOpaquePacketData)
                    {
                        BYTE* pBuf = pOpaquePacketData->GetBuffer();
                        if (pBuf)
                        {
// XXXMEH - seq num is now beginning of opaque
//                            // Advance to seq num
//                            pBuf += 8;
                            // Get seq num
                            UINT32 ulSeqNum = 0;
                            UnPack32(pBuf, ulSeqNum);
                            // Check seq num
                            if (ulSeqNum != pWrapper->GetLastSequenceNumber() + 1)
                            {
                                // We lost one or more packets in between
                                //
                                // Do we have restart markers in this image?
                                if (pWrapper->GetRestartInterval() == 0)
                                {
                                    // This image has no restart markers. Therefore, we set the image to invalid
                                    pWrapper->SetValid(FALSE);
                                }
                                else
                                {
                                    // Insert some fake data with proper restart markers
                                    retVal = InsertDummyData(pWrapper, pBuffer, pOpaquePacketData);
                                }
                            }
                            else
                            {
                                // Update the last sequence number
                                pWrapper->SetLastSequenceNumber(ulSeqNum);
                            }
                        }
                        else
                        {
                            retVal = HXR_FAIL;
                        }
                    }

                    if (SUCCEEDED(retVal) && pWrapper->GetValid())
                    {
                        // Append this buffer to the wrapper's list
                        pWrapper->AppendBuffer(pBuffer);
                        // Now call decompress for the wrapper
                        retVal = pWrapper->Decompress();
                        if (SUCCEEDED(retVal))
                        {
                            // Set the last opaque buffer in the wrapper
                            pWrapper->SetLastOpaqueBuffer(pOpaquePacketData);
                        }
                    }
                }
                else
                {
                    pWrapper->SetNeedPacket(TRUE);
                }
            }
        }
    }

    return retVal;
}

STDMETHODIMP CRealPixJPEGRendererCodec::GetDecompressStatus(UINT32     ulSessionHandle,
                                                            REF(INT32) rlStatus)
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_pMapManager)
    {
        CIJGLibraryWrapper* pWrapper = NULL;
        retVal                       = m_pMapManager->GetEntry(ulSessionHandle, (void**) &pWrapper);
        if (SUCCEEDED(retVal))
        {
            if (!pWrapper->GetValid())
            {
                rlStatus = DECOMPRESS_STATUS_ABORTED;
            }
            else if (pWrapper->IsFinished())
            {
                rlStatus = DECOMPRESS_STATUS_FINISHED;
            }
            else
            {
                rlStatus = DECOMPRESS_STATUS_INPROGRESS;
            }
        }
    }

    return retVal;
}

STDMETHODIMP CRealPixJPEGRendererCodec::FinishDecompress(UINT32 ulSessionHandle)
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_pMapManager)
    {
        CIJGLibraryWrapper* pWrapper = NULL;
        retVal                       = m_pMapManager->DeleteEntry(ulSessionHandle, (void**) &pWrapper);
        if (SUCCEEDED(retVal))
        {
            pWrapper->Terminate();
        }
        HX_RELEASE(pWrapper);
    }

    return retVal;
}

HX_RESULT STDAPICALLTYPE CRealPixJPEGRendererCodec::HXCreateInstance(IUnknown** ppIUnknown)
{
    HX_RESULT retVal = HXR_OK;

    if (ppIUnknown)
    {
        // Create renderer codec
        CRealPixJPEGRendererCodec* pCodec = new CRealPixJPEGRendererCodec();
        if (pCodec)
        {
            // QI for IUnknown
            retVal = pCodec->QueryInterface(IID_IUnknown, (void**) ppIUnknown);
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        if (FAILED(retVal))
        {
            HX_DELETE(pCodec);
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CRealPixJPEGRendererCodec::ComputeStatistics(BYTE *pBuf,            UINT32 ulLen,
                                                       UINT32 &rulNumPackets, UINT32 &rulMinSize,
                                                       UINT32 &rulMaxSize,    UINT32 &rulAvgSize)
{
    UINT32 ulNumPackets = 0;
    UINT32 ulMinSize    = 0xFFFFFFFF;
    UINT32 ulMaxSize    = 0;
    UINT32 ulPacketSize;

    // Parse the header
    UINT32 ulRestartInterval = 0;
    BOOL   bHeaderComplete   = FALSE;
    BYTE  *pCurByte          = pBuf;
    BYTE  *pBufLimit         = pBuf + ulLen;
    while (bHeaderComplete == FALSE && pCurByte < pBufLimit)
    {
        // Look for a 0xFF, which would signify a marker
        if (*pCurByte++ == 0xFF)
        {
            // Now take different actions depending on what kind of
            // marker this is - some markers have data associated with
            // them (such as DRI) and others don't (such as SOI and EOI)
            BYTE ucMarker = *pCurByte++;

            if (!(ucMarker == kMarkerSOI ||
                  ucMarker == kMarkerEOI ||
                  ucMarker == kMarkerTEM ||
                  (ucMarker >= kMarkerRST0 && ucMarker <= kMarkerRST7)))
            {
                UINT32 ulSegLen = (pCurByte[0] << 8) | pCurByte[1];

                if (ucMarker == kMarkerAPP0)
                {
                    // We look at this marker to verify this is a JFIF image file
                    if (pCurByte[0] != 0x00 || pCurByte[1] != 0x10 ||
                        pCurByte[2] != 'J'  || pCurByte[3] != 'F'  ||
                        pCurByte[4] != 'I'  || pCurByte[5] != 'F'  ||
                        pCurByte[6] != '\0')
                    {
                        return HXR_FAIL;
                    }
                }
                else if (ucMarker == kMarkerSOF0)
                {
                    // We look at this marker to extract the width and height
//                    m_ulImageHeight = (pCurByte[3] << 8) | pCurByte[4];
//                    m_ulImageWidth  = (pCurByte[5] << 8) | pCurByte[6];
                }
                else if (ucMarker == kMarkerDRI)
                {
                    // We look at this marker to extract the restart interval
                    ulRestartInterval = (pCurByte[2] << 8) | pCurByte[3];
                }
                else if (ucMarker == kMarkerSOS)
                {
                    // This marker is the last marker in the header
                    bHeaderComplete = TRUE;
                }

                // Advance the pointer
                pCurByte += ulSegLen;
            }
        }
    }

    // Did we get a valid header?
    if (!bHeaderComplete)
    {
        return HXR_FAIL;
    }

    // Update statistics
    ulPacketSize = pCurByte - pBuf;
    if (ulPacketSize < ulMinSize)
    {
        ulMinSize = ulPacketSize;
    }
    if (ulPacketSize > ulMaxSize)
    {
        ulMaxSize = ulPacketSize;
    }
    ulNumPackets++;

    // If the restart interval is zero, something's wrong
    if (ulRestartInterval == 0)
    {
        return HXR_FAIL;
    }

    BOOL bImageComplete = FALSE;
    while (pCurByte < pBufLimit && bImageComplete == FALSE)
    {
        BOOL   bPacketComplete = FALSE;
        BYTE  *pCurStart       = pCurByte;
        while (bPacketComplete == FALSE && pCurByte < pBufLimit)
        {
            if (*pCurByte++ == 0xFF)
            {
                BYTE ucMarker = *pCurByte++;
                if (ucMarker >= kMarkerRST0 && ucMarker <= kMarkerRST7)
                {
                    bPacketComplete = TRUE;
                }
                else if (ucMarker == kMarkerEOI)
                {
                    bPacketComplete = TRUE;
                    bImageComplete  = TRUE;
                }
            }
        }

        // Did we get a valid packet?
        if (bPacketComplete == FALSE)
        {
            return HXR_FAIL;
        }

        // Update statistics
        ulPacketSize = pCurByte - pCurStart;
        if (ulPacketSize < ulMinSize)
        {
            ulMinSize = ulPacketSize;
        }
        if (ulPacketSize > ulMaxSize)
        {
            ulMaxSize = ulPacketSize;
        }
        ulNumPackets++;
    }

    // Did we finish the image OK?
    if (bImageComplete == FALSE)
    {
        return HXR_FAIL;
    }

    // Assign statistics
    rulNumPackets = ulNumPackets;
    rulMinSize    = ulMinSize;
    rulMaxSize    = ulMaxSize;

    // Compute average
    rulAvgSize = (ulLen + (ulNumPackets >> 1)) / ulNumPackets;

    return HXR_OK;
}

void CRealPixJPEGRendererCodec::ReleaseAllWrappers()
{
    if (m_pMapManager)
    {
        UINT32              ulHandle = 0;
        CIJGLibraryWrapper* pWrapper = NULL;
        HX_RESULT           retVal   = m_pMapManager->GetFirstEntry(ulHandle, (void**) &pWrapper);
        while (SUCCEEDED(retVal))
        {
            HX_RELEASE(pWrapper);
            retVal = m_pMapManager->GetNextEntry(ulHandle, (void**) &pWrapper);
        }
        m_pMapManager->DeleteAllEntries();
    }
}

