#include "StdAfx.h"

//#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>
#include <stdlib.h>                     // MAX_ constants
#include "diblib.h"

/*--------------------------------------------------------------------
        READ TIFF
        Load the TIFF data from the file into memory.  Return
        a pointer to a valid DIB (or NULL for errors).
        Uses the TIFFRGBA interface to libtiff.lib to convert
        most file formats to a useable form.  We just keep the 32 bit
        form of the data to display, rather than optimizing for the
        display.

        Main entry points:

            int ChkTIFF ( LPCTSTR lpszPath )
            PVOID ReadTIFF ( LPCTSTR lpszPath )

        RETURN
            A valid DIB pointer for success; NULL for failure.

  --------------------------------------------------------------------*/

#include "TiffLib/tiff.h"
#include "TiffLib/tiffio.h"
#include <assert.h>
#include <stdio.h>


// piggyback some data on top of the RGBA Image
struct TIFFDibImage {
    TIFFRGBAImage tif;
    int  dibinstalled;
} ;


HANDLE LoadTIFFinDIB(LPCTSTR lpFileName);
HANDLE TIFFRGBA2DIB(TIFFDibImage* dib, uint32_t* raster)  ;

static void
MyWarningHandler(const char* module, const char* fmt, va_list ap)
{
    // ignore all warnings (unused tags, etc)
    return;
}

static void
MyErrorHandler(const char* module, const char* fmt, va_list ap)
{
    return;
}

//  Turn off the error and warning handlers to check if a valid file.
//  Necessary because of the way that the Doc loads images and restart files.
int ChkTIFF ( LPCTSTR lpszPath )
{
    int rtn = 0;

    TIFFErrorHandler  eh;
    TIFFErrorHandler  wh;

    eh = TIFFSetErrorHandler(NULL);
    wh = TIFFSetWarningHandler(NULL);

    TIFF* tif = TIFFOpen(lpszPath, "r");
    if (tif) {
        rtn = 1;
        TIFFClose(tif);
    }

    TIFFSetErrorHandler(eh);
    TIFFSetWarningHandler(wh);

    return rtn;
}

void DibInstallHack(TIFFDibImage* img) ;

PVOID ReadTIFF ( LPCTSTR lpszPath )
{
    void*             pDIB = 0;
    TIFFErrorHandler  wh;

    wh = TIFFSetWarningHandler(MyWarningHandler);

    if (ChkTIFF(lpszPath)) {
        TIFF* tif = TIFFOpen(lpszPath, "r");
        if (tif) {
            char emsg[1024];

            if (TIFFRGBAImageOK(tif, emsg)) {
                TIFFDibImage img;
                char emsg[1024];

                if (TIFFRGBAImageBegin(&img.tif, tif, -1, emsg)) {
                    size_t npixels;
                    uint32_t* raster;

                    DibInstallHack(&img);

                    npixels = img.tif.width * img.tif.height;
                    raster = (uint32_t*) _TIFFmalloc(npixels * sizeof (uint32_t));
                    if (raster != NULL) {
                        if (TIFFRGBAImageGet(&img.tif, raster, img.tif.width, img.tif.height)) {
                            pDIB = TIFFRGBA2DIB(&img, raster);
                        }
                    }
                    _TIFFfree(raster);
                }
                TIFFRGBAImageEnd(&img.tif);
            }
            else {
                TRACE("Unable to open image(%s): %s\n", lpszPath, emsg );
            }
            TIFFClose(tif);
        }
    }

    TIFFSetWarningHandler(wh);

    return pDIB;
}



HANDLE TIFFRGBA2DIB(TIFFDibImage* dib, uint32_t* raster)
{
    void*   pDIB = 0;
    TIFFRGBAImage* img = &dib->tif;

    uint32_t imageLength;
    uint32_t imageWidth;
    uint16_t BitsPerSample;
    uint16_t SamplePerPixel;
    uint32_t RowsPerStrip;
    uint16_t PhotometricInterpretation;

    BITMAPINFOHEADER   bi;
    int                dwDIBSize ;

    TIFFGetField(img->tif, TIFFTAG_IMAGEWIDTH, &imageWidth);
    TIFFGetField(img->tif, TIFFTAG_IMAGELENGTH, &imageLength);
    TIFFGetField(img->tif, TIFFTAG_BITSPERSAMPLE, &BitsPerSample);
    TIFFGetField(img->tif, TIFFTAG_ROWSPERSTRIP, &RowsPerStrip);
    TIFFGetField(img->tif, TIFFTAG_SAMPLESPERPIXEL, &SamplePerPixel);
    TIFFGetField(img->tif, TIFFTAG_PHOTOMETRIC, &PhotometricInterpretation);

    if ( BitsPerSample == 1 && SamplePerPixel == 1 && dib->dibinstalled ) {   // bilevel
        bi.biSize           = sizeof(BITMAPINFOHEADER);
        bi.biWidth          = imageWidth;
        bi.biHeight         = imageLength;
        bi.biPlanes         = 1;  // always
        bi.biBitCount       = 1;
        bi.biCompression    = BI_RGB;
        bi.biSizeImage      = WIDTHBYTES(bi.biWidth * bi.biBitCount) * bi.biHeight;
        bi.biXPelsPerMeter  = 0;
        bi.biYPelsPerMeter  = 0;
        bi.biClrUsed        = 0;  //  must be zero for RGB compression (none)
        bi.biClrImportant   = 0;  // always

        // Get the size of the DIB
        dwDIBSize = GetDIBSize( &bi );

        // Allocate for the BITMAPINFO structure and the color table.
        pDIB = GlobalAllocPtr( GHND, dwDIBSize );
        if (pDIB == 0) {
            return( NULL );
        }

        // Copy the header info
        *((BITMAPINFOHEADER*)pDIB) = bi;

        // Get a pointer to the color table
        RGBQUAD   *pRgbq = (RGBQUAD   *)((LPSTR)pDIB + sizeof(BITMAPINFOHEADER));

        pRgbq[0].rgbRed      = 0;
        pRgbq[0].rgbBlue     = 0;
        pRgbq[0].rgbGreen    = 0;
        pRgbq[0].rgbReserved = 0;
        pRgbq[1].rgbRed      = 255;
        pRgbq[1].rgbBlue     = 255;
        pRgbq[1].rgbGreen    = 255;
        pRgbq[1].rgbReserved = 255;

        // Pointers to the bits
        //PVOID pbiBits = (LPSTR)pRgbq + bi.biClrUsed * sizeof(RGBQUAD);
        //
        // In the BITMAPINFOHEADER documentation, it appears that
        // there should be no color table for 32 bit images, but
        // experience shows that the image is off by 3 words if it
        // is not included.  So here it is.
        PVOID pbiBits = GetDIBImagePtr((BITMAPINFOHEADER*)pDIB);  //(LPSTR)pRgbq + 3 * sizeof(RGBQUAD);

        int       sizeWords = bi.biSizeImage/4;
        RGBQUAD*  rgbDib = (RGBQUAD*)pbiBits;
        long*     rgbTif = (long*)raster;

        _TIFFmemcpy(pbiBits, raster, bi.biSizeImage);
    }

        //  For now just always default to the RGB 32 bit form.                                                       // save as 32 bit for simplicity
    else if ( true /*BitsPerSample == 8 && SamplePerPixel == 3*/ ) {   // 24 bit color

        bi.biSize           = sizeof(BITMAPINFOHEADER);
        bi.biWidth          = imageWidth;
        bi.biHeight         = imageLength;
        bi.biPlanes         = 1;  // always
        bi.biBitCount       = 32;
        bi.biCompression    = BI_RGB;
        bi.biSizeImage      = WIDTHBYTES(bi.biWidth * bi.biBitCount) * bi.biHeight;
        bi.biXPelsPerMeter  = 0;
        bi.biYPelsPerMeter  = 0;
        bi.biClrUsed        = 0;  //  must be zero for RGB compression (none)
        bi.biClrImportant   = 0;  // always

        // Get the size of the DIB
        dwDIBSize = GetDIBSize( &bi );

        // Allocate for the BITMAPINFO structure and the color table.
        pDIB = GlobalAllocPtr( GHND, dwDIBSize );
        if (pDIB == 0) {
            return( NULL );
        }

        // Copy the header info
        *((BITMAPINFOHEADER*)pDIB) = bi;

        // Get a pointer to the color table
        RGBQUAD   *pRgbq = (RGBQUAD   *)((LPSTR)pDIB + sizeof(BITMAPINFOHEADER));

        // Pointers to the bits
        //PVOID pbiBits = (LPSTR)pRgbq + bi.biClrUsed * sizeof(RGBQUAD);
        //
        // In the BITMAPINFOHEADER documentation, it appears that
        // there should be no color table for 32 bit images, but
        // experience shows that the image is off by 3 words if it
        // is not included.  So here it is.
        PVOID pbiBits = (LPSTR)pRgbq + 3 * sizeof(RGBQUAD);

        int       sizeWords = bi.biSizeImage/4;
        RGBQUAD*  rgbDib = (RGBQUAD*)pbiBits;
        long*     rgbTif = (long*)raster;

        // Swap the byte order while copying
        for ( int i = 0 ; i < sizeWords ; ++i )
        {
            rgbDib[i].rgbRed   = TIFFGetR(rgbTif[i]);
            rgbDib[i].rgbBlue  = TIFFGetB(rgbTif[i]);
            rgbDib[i].rgbGreen = TIFFGetG(rgbTif[i]);
            rgbDib[i].rgbReserved = 0;
        }
    }

    return pDIB;
}




///////////////////////////////////////////////////////////////
//
//  Hacked from tif_getimage.c in libtiff in v3.5.7
//
//
typedef unsigned char u_char;


#define DECLAREContigPutFunc(name) \
static void name(\
    TIFFRGBAImage* img, \
    uint32_t* cp, \
    uint32_t x, uint32_t y, \
    uint32_t w, uint32_t h, \
    int32_t fromskew, int32_t toskew, \
    u_char* pp \
)

#define DECLARESepPutFunc(name) \
static void name(\
    TIFFRGBAImage* img,\
    uint32_t* cp,\
    uint32_t x, uint32_t y, \
    uint32_t w, uint32_t h,\
    int32_t fromskew, int32_t toskew,\
    u_char* r, u_char* g, u_char* b, u_char* a\
)

DECLAREContigPutFunc(putContig1bitTile);
static int getStripContig1Bit(TIFFRGBAImage* img, uint32_t* uraster, uint32_t w, uint32_t h);

//typdef struct TIFFDibImage {
//    TIFFRGBAImage tif;
//    dibinstalled;
//} TIFFDibImage ;

void DibInstallHack(TIFFDibImage* dib) {
    TIFFRGBAImage* img = &dib->tif;
    dib->dibinstalled = false;
    switch (img->photometric) {
        case PHOTOMETRIC_MINISWHITE:
        case PHOTOMETRIC_MINISBLACK:
        switch (img->bitspersample) {
            case 1:
                img->put.contig = putContig1bitTile;
                img->get = getStripContig1Bit;
                dib->dibinstalled = true;
                break;
        }
        break;
    }
}

/*
 * 1-bit packed samples => 1-bit
 *
 *   Override to just copy the data
 */
DECLAREContigPutFunc(putContig1bitTile)
{
    int samplesperpixel = img->samplesperpixel;

    (void) y;
    fromskew *= samplesperpixel;
    int wb = WIDTHBYTES(w);
    u_char*  ucp = (u_char*)cp;

    /* Convert 'w' to bytes from pixels (rounded up) */
    w = (w+7)/8;

    while (h-- > 0) {
        _TIFFmemcpy(ucp, pp, w);
        /*
        for (x = wb; x-- > 0;) {
            *cp++ = rgbi(Map[pp[0]], Map[pp[1]], Map[pp[2]]);
            pp += samplesperpixel;
        }
        */
        ucp += (wb + toskew);
        pp += (w + fromskew);
    }
}

/*
 *  Hacked from the tif_getimage.c file.
 */
static uint32_t
setorientation(TIFFRGBAImage* img, uint32_t h)
{
    TIFF* tif = img->tif;
    uint32_t y;

    switch (img->orientation) {
    case ORIENTATION_BOTRIGHT:
    case ORIENTATION_RIGHTBOT:  /* XXX */
    case ORIENTATION_LEFTBOT:   /* XXX */
    TIFFWarning(TIFFFileName(tif), "using bottom-left orientation");
    img->orientation = ORIENTATION_BOTLEFT;
    /* fall through... */
    case ORIENTATION_BOTLEFT:
    y = 0;
    break;
    case ORIENTATION_TOPRIGHT:
    case ORIENTATION_RIGHTTOP:  /* XXX */
    case ORIENTATION_LEFTTOP:   /* XXX */
    default:
    TIFFWarning(TIFFFileName(tif), "using top-left orientation");
    img->orientation = ORIENTATION_TOPLEFT;
    /* fall through... */
    case ORIENTATION_TOPLEFT:
    y = h-1;
    break;
    }
    return (y);
}

/*
 * Get a strip-organized image that has
 *  PlanarConfiguration contiguous if SamplesPerPixel > 1
 * or
 *  SamplesPerPixel == 1
 *
 *  Hacked from the tif_getimage.c file.
 *
 *    This is set up to allow us to just copy the data to the raster
 *    for 1-bit bitmaps
 */
static int
getStripContig1Bit(TIFFRGBAImage* img, uint32_t* raster, uint32_t w, uint32_t h)
{
    TIFF* tif = img->tif;
    tileContigRoutine put = img->put.contig;
    uint16_t orientation;
    uint32_t row, y, nrow, rowstoread;
    uint32_t pos;
    u_char* buf;
    uint32_t rowsperstrip;
    uint32_t imagewidth = img->width;
    tsize_t scanline;
    int32_t fromskew, toskew;
    tstrip_t strip;
    tsize_t  stripsize;
    u_char* braster = (u_char*)raster; // byte wide raster
    uint32_t  wb = WIDTHBYTES(w);
    int ret = 1;

    buf = (u_char*) _TIFFmalloc(TIFFStripSize(tif));
    if (buf == 0) {
        TIFFErrorExt(tif->tif_clientdata, TIFFFileName(tif), "No space for strip buffer");
        return (0);
    }
    y = setorientation(img, h);
    orientation = img->orientation;
    toskew = -(int32_t) (orientation == ORIENTATION_TOPLEFT ? wb+wb : wb-wb);
    TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
    scanline = TIFFScanlineSize(tif);
    fromskew = (w < imagewidth ? imagewidth - w : 0)/8;
    for (row = 0; row < h; row += nrow)
    {
        rowstoread = rowsperstrip - (row + img->row_offset) % rowsperstrip;
        nrow = (row + rowstoread > h ? h - row : rowstoread);
        strip = TIFFComputeStrip(tif,row+img->row_offset, 0);
        stripsize = ((row + img->row_offset)%rowsperstrip + nrow) * scanline;
        if (TIFFReadEncodedStrip(tif, strip, buf, stripsize ) < 0
            && img->stoponerr)
        {
            ret = 0;
            break;
        }

        pos = ((row + img->row_offset) % rowsperstrip) * scanline;
        (*put)(img, (uint32_t*)(braster+y*wb), 0, y, w, nrow, fromskew, toskew, buf + pos);
        y += (orientation == ORIENTATION_TOPLEFT ?-(int32_t) nrow : (int32_t) nrow);
    }
    _TIFFfree(buf);
    return (ret);
}

/*
 * Local Variables:
 * mode: c++
 * c-basic-offset: 8
 * fill-column: 78
 * End:
 */
