
/* 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.
 */
#pragma warning(disable:4005)

#include "windows.h"

// Workaround for redefinition of INT32
#define   XMD_H  1

#include "jpegapi.h"

#include "jmemsys.h"


// #define INVERTBGR   0

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

/* 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;

    try
    {
        jpeg_compress_struct *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;
    }
    catch( THROWN thrownHR )
    {
        hr = thrownHR.Hr();
    }

    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;

    try
    {
        jpeg_decompress_struct * 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, TRUE);

        spjds->out_color_space = JCS_RGBA;

        *phJpegD = (HANDLE) spjds;
    }
    catch( THROWN thrownHR )
    {
        hr = thrownHR.Hr();
    }

    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;

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

    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;

    try
    {
        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;
    }
    catch( THROWN thrownHR )
    {
        hr = thrownHR.Hr();
    }

    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;

    try
    {
        struct jpeg_decompress_struct *pjds;
        jpeg_decompress_struct * spjds = new jpeg_decompress_struct;
        struct jpeg_error_mgr *jem;
        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.
            jem = spjem;
        }
        else if ( hJpegD == NULL )
        {
            // This should never happen.  The decompression header was not set up return an
            // error indication by setting the return value to kpvNil.
            return E_FAIL;
        }
        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_YCbCrA;
                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;
    }
    catch( THROWN thrownHR )
    {
        hr = thrownHR.Hr();
    }

    return hr;
}

HRESULT
GetJPEGHeaderFields(
    HANDLE  hJpegD,
    UINT    *pWidth,
    UINT    *pHeight,
    INT     *pNumComponents,
    J_COLOR_SPACE   *pColorSpace
    )
{
    struct jpeg_decompress_struct *pjds;

    pjds = (struct jpeg_decompress_struct *)hJpegD;

    if (hJpegD) {

        *pWidth = pjds->image_width;
        *pHeight = pjds->image_height;
        *pNumComponents = pjds->num_components;
        *pColorSpace = pjds->jpeg_color_space;

    }

    return S_OK;

}

BOOL
WINAPI
Win32DIBFromJPEG(
    BYTE        *prgbJPEG,
    ULONG       ulBufferSize,
    LPBITMAPINFO pbmi,
    HBITMAP     *phBitmap,
    PVOID       *ppvBits
    )
{

    BITMAPINFO  bmi;

    struct jpeg_decompress_struct *pjds;
    jpeg_decompress_struct * spjds = new jpeg_decompress_struct;
    struct jpeg_error_mgr *jem;
    jpeg_error_mgr * spjem = new jpeg_error_mgr;

    // Only 24bpp for now
    const UINT    uiReturnedNumChannels  = 3;

    //
    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.
    jem = spjem;

    JSAMPROW rgrow[1];

    // Set the various image parameters.

    try {

        jpeg_buf_src(pjds, prgbJPEG, ulBufferSize); // Init the "source manager"
        jpeg_read_header(pjds, TRUE);

    } catch (...) {

        //
        // for some reason we crashed, so return the exception code
        //
        #ifdef DEBUG
        OutputDebugString("JPEG DIB crashed");
        #endif
        return FALSE;
    }


    // Set parameter for decompression
    // Defaults are OK for this occasssion
    // Specify the JCS_BGR output colorspace

    pjds->out_color_space = JCS_BGR;
    // pjds->out_color_space = JCS_RGB;

    //
    // Create DIB bits
    //

    pbmi->bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);

    pbmi->bmiHeader.biWidth           = pjds->image_width;
    pbmi->bmiHeader.biHeight          = -((int)pjds->image_height); // Top Down bitmap
    pbmi->bmiHeader.biPlanes          = 1;
    pbmi->bmiHeader.biBitCount        = 24; // BUGBUG careful
    pbmi->bmiHeader.biCompression     = BI_RGB;
    pbmi->bmiHeader.biSizeImage       = 0;
    pbmi->bmiHeader.biXPelsPerMeter   = 0;
    pbmi->bmiHeader.biYPelsPerMeter   = 0;
    pbmi->bmiHeader.biClrUsed         = 0;
    pbmi->bmiHeader.biClrImportant    = 0;

    *phBitmap = ::CreateDIBSection (NULL,
                                  pbmi,
                                  DIB_RGB_COLORS,
                                  ppvBits,
                                  NULL,
                                  0);

//
// 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)*ppvBits;

    while (pjds->output_scanline < pjds->output_height)
    {
        jpeg_read_scanlines(pjds, rgrow, 1);

        // Invert BGR --> RGB
        #ifdef INVERTBGR

        RGBTRIPLE *pTriplet;

        pTriplet = (RGBTRIPLE *) rgrow[0];

        for (int iPixel = 0;
            iPixel < (int)pjds->output_width;
            iPixel++,pTriplet++) {

            BYTE    bTemp;

            bTemp = pTriplet->rgbtBlue;
            pTriplet->rgbtBlue = pTriplet->rgbtRed;
            pTriplet->rgbtRed = bTemp;

        }
        #endif

        rgrow[0] = (JSAMPROW) ALIGNIT(rgrow[0] + pjds->output_width * uiReturnedNumChannels,DWORD);
    }

    jpeg_finish_decompress(pjds);   // Finish up decompressing

    jpeg_destroy_decompress(pjds);  //Destroy the decompression object
    delete spjem;
    delete spjds;

    return TRUE;

}


BOOL
WINAPI
JPEGFromWin32DIB(
    HBITMAP     hBitmap,


    BYTE        *prgbJPEG,
    ULONG       ulBufferSize,
    LPBITMAPINFO pbmi,
    PVOID       *ppvBits
    )
{


    return TRUE;

}


