
/* jpegapi.cpp -- interface layer for painless JPEG compression of NIFty images.
 * Written by Ajai Sehgal 3/10/96
 * (c) Copyright Microsoft Corporation
 *
 *  08-27-1997 (kurtgeis) Pushed exception handling into library.  Changed
 *  all entry points to return HRESULTs.  Added width and heigth to parameters.
 *  04-20-2001 (bobday) Removed exception handling.  If we have problems, I
 *  say we fix the problems! (or check for the proper error conditions)
 */
#pragma warning(disable:4005)
#include "stdafx.h"
#include "jpegapi.h"
#include "jmemsys.h"

#define I_LIKE_CPP_EH   0           // turn this on if we ever find a reason for it

/********************************************************************************/

/* JPEGCompressHeader()
 *
 * Arguments:
 *      tuQuality   "Quality" of the resulting JPEG (0..100, 100=best)
 *
 * Returns:
 *      HRESULT
 */
HRESULT JPEGCompressHeader(BYTE *prgbJPEGHeaderBuf, UINT tuQuality, ULONG *pcbOut, HANDLE *phJpegC, J_COLOR_SPACE ColorSpace)
{
    HRESULT         hr = S_OK;
    jpeg_compress_struct *spjcs = NULL;

#if I_LIKE_CPP_EH
    try
    {
#endif
        spjcs = new jpeg_compress_struct;
        struct jpeg_error_mgr *jem = new jpeg_error_mgr;

        spjcs->err = jpeg_std_error(jem);   // Init the error handler
        jpeg_create_compress(spjcs);        // Init the compression object
        
        if (ColorSpace == JCS_GRAYSCALE)
        {
            spjcs->in_color_space = JCS_GRAYSCALE;
            spjcs->input_components = 1;
        }
        else
        {
            spjcs->in_color_space = JCS_RGBA;
            spjcs->input_components = 4;
        }
        jpeg_set_defaults(spjcs);       // Init the compression engine with the defaults
        
        jpeg_set_quality(spjcs, tuQuality, TRUE);
        
        jpeg_set_colorspace(spjcs,ColorSpace);

        jpeg_mem_dest(spjcs, prgbJPEGHeaderBuf);    // Init the "destination manager"
        
        spjcs->comps_in_scan = 0;
        
        spjcs->write_JFIF_header = FALSE;

        jpeg_write_tables(spjcs);

        jpeg_suppress_tables(spjcs, TRUE);

        *pcbOut = spjcs->bytes_in_buffer;

        *phJpegC = (HANDLE) spjcs;
#if I_LIKE_CPP_EH
    }
    catch( THROWN thrownHR )
    {
        hr = thrownHR.Hr();
    }
    catch( ... )
    {
        if ( spjcs ) delete spjcs;
        hr = E_OUTOFMEMORY;
    }
#endif

    return hr;
}

/* JPEGDecompressHeader()
 *
 * Arguments:
 *      *prgbJPEGBuf : pointer to the JPEG header data as read from file
 *      *phJpegD:   pointer to Handle of the JPEG decompression object returned
 *
 * Returns:
 *      HRESULT
 */
HRESULT JPEGDecompressHeader(BYTE *prgbJPEGHeaderBuf, HANDLE *phJpegD, ULONG ulBufferSize)
{
    HRESULT hr = S_OK;
    jpeg_decompress_struct * spjds = NULL;

#if I_LIKE_CPP_EH
    try
    {
#endif
        spjds = new jpeg_decompress_struct;
        struct jpeg_error_mgr *jem = new jpeg_error_mgr;

        spjds->err = jpeg_std_error(jem);   // Init the error handler
        
        jpeg_create_decompress(spjds);  // Init the decompression object
        

        // Now we need to "read" it into the decompression object...

        jpeg_mem_src(spjds, prgbJPEGHeaderBuf, ulBufferSize);
        
        jpeg_read_header(spjds, FALSE);

        spjds->out_color_space = JCS_RGBA;
        
        *phJpegD = (HANDLE) spjds;

#if I_LIKE_CPP_EH
    }
    catch( THROWN thrownHR )
    {
        hr = thrownHR.Hr();
    }
    catch( ... )
    {
        if ( spjds ) delete spjds;
        hr = E_OUTOFMEMORY;
    }
#endif

    return hr;
}

// DestroyJPEGCompress
//
// Release all the JPEG stuff from the handle we gave to the user
//
HRESULT DestroyJPEGCompressHeader(HANDLE hJpegC)
{
    HRESULT         hr = S_OK;

    try
    {
        struct jpeg_compress_struct *pjcs = (struct jpeg_compress_struct *)hJpegC;
        jpeg_destroy_compress(pjcs);
        delete pjcs->err;
        delete pjcs;
    }
    catch( THROWN thrownHR )
    {
        hr = thrownHR.Hr();
    }

    return hr;
}

// DestroyJPEGDecompressHeader
//
// Release all the JPEG stuff from the handle we gave to the user
//
HRESULT DestroyJPEGDecompressHeader(HANDLE hJpegD)
{
    HRESULT         hr = S_OK;

#if I_LIKE_CPP_EH
    try
    {
#endif
        struct jpeg_decompress_struct *pjds = (struct jpeg_decompress_struct *)hJpegD;
        jpeg_destroy_decompress(pjds);
        delete pjds->err;
        delete pjds;
#if I_LIKE_CPP_EH
    }
    catch( THROWN thrownHR )
    {
        hr = thrownHR.Hr();
    }
#endif

    return hr;
}

/* JPEGFromRGBA()
 *
 * Arguments:
 *      prgbImage   A raw image buffer (4 bytes/pixel, RGBA order)
 *      cpxlAcross  Width of the image, in pixels
 *      cpxlDown    Height of the image, in pixels
 *      tuQuality   "Quality" of the resulting JPEG (0..100, 100=best)
 *      A memory buffer containing the complete JPEG compressed version of the
 *      given image. NULL on error.
 *
 * Returns:
 *      HRESULT
 */

HRESULT JPEGFromRGBA(BYTE *prgbImage, BYTE *prgbJPEGBuf, UINT tuQuality, ULONG *pcbOut, HANDLE hJpegC, J_COLOR_SPACE ColorSpace, UINT nWidth, UINT nHeight )
{
    HRESULT         hr = S_OK;

#if I_LIKE_CPP_EH
    try
    {
#endif
        struct jpeg_compress_struct *pjcs = (jpeg_compress_struct *)hJpegC;

        JSAMPROW rgrow[1];
        

//
// On non X86 architectures use only C code
//
#if defined (_X86_)
        pjcs->dct_method = JDCT_ISLOW;
#else
        pjcs->dct_method = JDCT_FLOAT;
#endif      
                pjcs->image_width = nWidth;
        pjcs->image_height = nHeight;
        pjcs->data_precision = 8;       /* 8 bits / sample */
        pjcs->bytes_in_buffer = 0;
        pjcs->write_JFIF_header = FALSE;

        if (ColorSpace == JCS_GRAYSCALE)
        {
            pjcs->input_components = 1;
        }
        else
        {
            pjcs->input_components = 4;
        }

        jpeg_set_colorspace(pjcs,ColorSpace);
 
        jpeg_set_quality(pjcs, tuQuality, TRUE);

        jpeg_suppress_tables(pjcs, TRUE);

        jpeg_mem_dest(pjcs, prgbJPEGBuf);   // Init the "destination manager"
        
        jpeg_start_compress(pjcs, FALSE);

        rgrow[0] = (JSAMPROW)prgbImage;

        while (pjcs->next_scanline < nHeight )
        {
            jpeg_write_scanlines(pjcs, rgrow, 1);

            rgrow[0] += nWidth * pjcs->input_components; //input_components is the equivalent of # of bytes
        }

        jpeg_finish_compress(pjcs);     // Finish up compressing

        
        *pcbOut = pjcs->bytes_in_buffer;
#if I_LIKE_CPP_EH
    }
    catch( THROWN thrownHR )
    {
        hr = thrownHR.Hr();
    }
#endif

    return hr;
}

/* RGBAFromJPEG()
 *
 * Arguments:
 *      prgbJPEG: A JPEG data stream, as returned by JPEGFromRGBA()
 *      A memory buffer containing the reconstructed image in RGBA format.
 *      NULL on error.
 *
 * Returns:
 *      HRESULT
*/

HRESULT RGBAFromJPEG(BYTE *prgbJPEG, BYTE *prgbImage, HANDLE hJpegD, ULONG ulBufferSize, BYTE bJPEGConversionType, ULONG *pulReturnedNumChannels, UINT nWidth, UINT nHeight )
{
    HRESULT         hr = S_OK;
    jpeg_decompress_struct * spjds = NULL;

#if I_LIKE_CPP_EH
    try
    {
#endif
        struct jpeg_decompress_struct *pjds;
        spjds = new jpeg_decompress_struct;
        jpeg_error_mgr * spjem = new jpeg_error_mgr;
        
        if ( hJpegD == NULL )
        {
            spjds->err = jpeg_std_error(spjem); // Init the error handler
            jpeg_create_decompress(spjds);      // Init the decompression object in the case
            pjds = spjds;                       // that the headers are with the tiles.
        }
        else
        {
            pjds = (struct jpeg_decompress_struct *)hJpegD;
        }

        JSAMPROW rgrow[1];
            
        // Set the various image parameters.
        pjds->data_precision = 8;
        pjds->image_width = nWidth;
        pjds->image_height = nHeight;
        
        jpeg_mem_src(pjds, prgbJPEG, ulBufferSize); // Init the "source manager"

        jpeg_read_header(pjds, TRUE);
        
        switch (bJPEGConversionType)
        {
        case 1:
            pjds->out_color_space = JCS_RGBA;
            if (pjds->jpeg_color_space != JCS_RGBA)
            {
                if ( 4 == pjds->num_components) 
                    pjds->jpeg_color_space = JCS_YCbCrALegacy;
                else
                    pjds->jpeg_color_space = JCS_YCbCr;
            }
            *pulReturnedNumChannels = 4;
            break;
        case 2:
            pjds->out_color_space = JCS_RGBA;

            if ( 4 == pjds->num_components) 
                pjds->jpeg_color_space = JCS_YCbCrA;
            else
                pjds->jpeg_color_space = JCS_YCbCr;

            pjds->jpeg_color_space = JCS_YCbCrA;
            *pulReturnedNumChannels = 4;
            break;
        default:
            pjds->out_color_space = JCS_UNKNOWN; 
            pjds->jpeg_color_space = JCS_UNKNOWN;
            *pulReturnedNumChannels = pjds->num_components;
        }

//
// On non X86 architectures use only C code
//
#if defined (_X86_)
        pjds->dct_method = JDCT_ISLOW;
#else
            pjds->dct_method = JDCT_FLOAT;
#endif

        jpeg_start_decompress(pjds);

        rgrow[0] = (JSAMPROW)prgbImage;

        while (pjds->output_scanline < pjds->output_height)
        {
            jpeg_read_scanlines(pjds, rgrow, 1);
            rgrow[0] += pjds->output_width * *pulReturnedNumChannels;
        }
        
        jpeg_finish_decompress(pjds);   // Finish up decompressing

        if (hJpegD == NULL)
            jpeg_destroy_decompress(pjds);  //Destroy the decompression object if it
                                            //was locally allocated as in when the header
                                            //is part of the tile.
        delete spjem;
        delete spjds;
#if I_LIKE_CPP_EH
    }
    catch( THROWN thrownHR )
    {
        hr = thrownHR.Hr();
    }
    catch( ... )
    {
        if ( spjds ) delete spjds;
        hr = E_OUTOFMEMORY;
    }
#endif

    return hr;
}
