//
// loadimag.cpp
//
// implementation of loading a file from disk via an installed graphic filter
//

#include "stdafx.h"
#include "pbrush.h"
#include "imgwnd.h"
#include "imgsuprt.h"
#include "loadimag.h"
#include "bmpstrm.h"
#include "imaging.h"

#include <atlbase.h>

// must define one of the following:
//#define _USE_FLT_API
#ifdef _X86_
#define _USE_IFL_API
#endif

#ifdef _USE_FLT_API
#include "filtapi.h"
#endif

#ifdef _USE_IFL_API
#include "image.h"
#include "interlac.h"
#define MAX_PAL_SIZE 256

#ifdef PNG_SUPPORT // for Portable Network Graphics. As of 12/10/1996 the support was broken

//----------------------------------------------------------------------------
//    Places a line of image data from an ADAM 7 interlaced file (i.e., currently
//    a PNG file) into its correct position in a memory buffer: this memory
//    buffer is essentially an array of pointers to the rows of the image in
//    which the pixel data is to be set.
//----------------------------------------------------------------------------
 IFLERROR ReadADAM7InterlacedImage(LPBYTE apbImageBuffer[], IFLHANDLE pfpbFROM,
                                         int ImageHeight, int ImageWidth, int cbPixelSize,
                                         IFLCLASS ImageClass)
{


        int cRasterLines = iflGetRasterLineCount(pfpbFROM);

        ADAM7_STRUCT stAdam7;
        stAdam7.iImageHeight = ImageHeight;
        stAdam7.iImageWidth = ImageWidth;
        stAdam7.Class = ImageClass;
        stAdam7.cbPixelSize = iflGetBitsPerPixel (pfpbFROM)/8;//cbPixelSize;
        stAdam7.iPassLine = 0;
        LPBYTE pbScanLine = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, ImageWidth * stAdam7.cbPixelSize);
        wsprintf (buf, TEXT("Pixel size: %d, Size of a scan line: %d\n"), stAdam7.cbPixelSize,
                          ImageWidth*stAdam7.cbPixelSize);

        if (pbScanLine == NULL)
                return IFLERR_MEMORY;

        int cTotalScanLines = iADAM7CalculateNumberOfScanLines(&stAdam7);

        int iLine;
        IFLERROR idErr;
        BOOL fEmptyLine;

        for (iLine = 0, idErr = IFLERR_NONE, fEmptyLine = FALSE;
                idErr == IFLERR_NONE && iLine < (int)cRasterLines;
                iLine++)
        {
                if (!fEmptyLine)
                        idErr = iflRead(pfpbFROM, pbScanLine, 1);

                stAdam7.iScanLine = iLine;
                fEmptyLine = ADAM7AddRowToImageBuffer(apbImageBuffer, pbScanLine, &stAdam7);
        }

        HeapFree(GetProcessHeap(), 0, pbScanLine);
        return idErr;
}

//----------------------------------------------------------------------------
//    Deallocates the image space allocated in the function AllocateImageSpace()
//----------------------------------------------------------------------------
 LPBYTE *FreeImageSpace(HANDLE hHeap, LPBYTE ppImageSpace[], int height)
{
        if (ppImageSpace != NULL)
        {
                for (int i = 0; i < height; i++)
                {
                        if (ppImageSpace[i] != NULL)
                        {
                                HeapFree(hHeap, 0, ppImageSpace[i]);
                                ppImageSpace[i] = NULL;
                        }
                }

                HeapFree(hHeap, 0, ppImageSpace);
                ppImageSpace = NULL;
        }

        return ppImageSpace;
}

//----------------------------------------------------------------------------
//    Allocate some image space: this function will create a dynamic array
//    of "height" pointers which each point to an allocated row of bytes of
//    size "width".
//----------------------------------------------------------------------------
 LPBYTE *AllocateImageSpace(HANDLE hHeap, int height, int width, int cbSize)
{
        LPBYTE *ppImageSpace = (LPBYTE *)HeapAlloc(hHeap, 0, height * sizeof(void *));
        TCHAR buf[200];
        wsprintf (buf, TEXT("Size of image line: %d\n"), width*cbSize);


        if (ppImageSpace != NULL)
        {
                // Init the pointers to NULL: this makes error recovery easier
                for (int i = 0; i < height; i++)
                        ppImageSpace[i] = NULL;

                // NOW allocate the pointer space for the image
                for (i = 0; i < height; i++)
                {
                        ppImageSpace[i] = (LPBYTE)HeapAlloc(hHeap, 0, width * cbSize);
                        if (ppImageSpace[i] == NULL)
                                break;
                }

                if (i < height) // We weren't able to allocate the required space
                        ppImageSpace = FreeImageSpace(hHeap, ppImageSpace, height);
        }

        return ppImageSpace;
}
#endif // PNG_SUPPORT

#endif // _USE_IFL_API



// returns a pointer to the extension of a file.
//
// in:
//      qualified or unqualfied file name
//
// returns:
//      pointer to the extension of this file.  if there is no extension
//      as in "foo" we return a pointer to the NULL at the end
//      of the file
//
//      foo.txt     ==> ".txt"
//      foo         ==> ""
//      foo.        ==> "."
//

LPCTSTR FindExtension(LPCTSTR pszPath)
{
        for (LPCTSTR pszDot = NULL; *pszPath; pszPath = CharNext(pszPath))
        {
                switch (*pszPath)
                {
                        case TEXT('.'):
                                pszDot = pszPath;       // remember the last dot
                                break;
                        case TEXT('\\'):
                        case TEXT(' '):                               // extensions can't have spaces
                                pszDot = NULL;          // forget last dot, it was in a directory
                                break;
                }
        }

        // if we found the extension, return ptr to the dot, else
        // ptr to end of the string (NULL extension)
        return pszDot ? pszDot : pszPath;
}

//
// GetFilterInfo
//
//  32-bit import filters are listed in the registry...
//
//  HKLM\SOFTWARE\Microsoft\Shared Tools\Graphics Filters\Import\XXX
//      Path        = filename
//      Name        = friendly name
//      Extenstions = file extenstion list
//
#pragma data_seg(".text")
static const TCHAR c_szImpHandlerKey[] = TEXT("SOFTWARE\\Microsoft\\Shared Tools\\Graphics Filters\\Import");
static const TCHAR c_szExpHandlerKey[] = TEXT("SOFTWARE\\Microsoft\\Shared Tools\\Graphics Filters\\Export");
static const TCHAR c_szName[] = TEXT("Name");
static const TCHAR c_szPath[] = TEXT("Path");
static const TCHAR c_szExts[] = TEXT("Extensions");
static const TCHAR c_szImageAPI[] = TEXT("Image API Enabled Filters");
#pragma data_seg()

BOOL GetInstalledFilters(BOOL bOpenFileDialog, int i, LPTSTR szName, UINT cbName,
                LPTSTR szExt, UINT cbExt, LPTSTR szHandler, UINT cbHandler, BOOL& bImageAPI)
{
        HKEY hkey;
        HKEY hkeyT;
        TCHAR ach[80];
        BOOL rc = FALSE;        // return code

        bImageAPI = FALSE;

        if (RegOpenKey(HKEY_LOCAL_MACHINE,
                bOpenFileDialog ? c_szImpHandlerKey : c_szExpHandlerKey, &hkey) == 0)
        {
                if (RegEnumKey(hkey, i, ach, sizeof(ach)/sizeof(ach[0]))==0)
                {
                        if (RegOpenKey(hkey, ach, &hkeyT) == 0)
                        {
                                if (szName)
                                {
                                        szName[0] = 0;
                                        RegQueryValueEx(hkeyT, c_szName, NULL, NULL,
                                                (LPBYTE)szName, (LPDWORD)&cbName);
                                }
                                if (szExt)
                                {
                                        szExt[0] = 0;
                                        RegQueryValueEx(hkeyT, c_szExts, NULL, NULL,
                                                (LPBYTE)szExt, (LPDWORD)&cbExt);
                                }
                                if (szHandler)
                                {
                                        szHandler[0] = 0;
                                        RegQueryValueEx(hkeyT, c_szPath, NULL, NULL,
                                                (LPBYTE)szHandler, (LPDWORD)&cbHandler);
                                }

                                RegCloseKey(hkeyT);
                                rc = TRUE;
                        }

                        TCHAR szEnabledFilters[1024];
                        DWORD dwEnabledFiltersSize = sizeof(szEnabledFilters);

                        // Does the filter support Image Library Files API ?

                        if (RegQueryValueEx(hkey, c_szImageAPI, NULL, NULL,
                                (LPBYTE)szEnabledFilters, &dwEnabledFiltersSize) == 0)
                        {
                            for (
                                LPCTSTR pExt = _tcstok(szEnabledFilters, _T(" "));
                                pExt != NULL && bImageAPI != TRUE;
                                pExt = _tcstok(NULL, _T(" "))) 
                            {
                                if (_tcsicmp(pExt, ach) == 0) 
                                {
                                    bImageAPI = TRUE;
                                }
                            }
                        }
                }
                RegCloseKey(hkey);
        }

        return rc;
}

#ifdef _USE_FLT_API
//
//  GetHandlerForFile
//
//  find an import/export filter for the given file.
//
BOOL GetHandlerForFile(BOOL bImport, LPCTSTR szFile, LPTSTR szHandler, UINT cb)
{
        TCHAR    buf[40];
        BOOL    rc = FALSE;     // return code

        *szHandler = 0;

        if (szFile == NULL)
                return FALSE;

        // find the extension
        LPCTSTR ext = FindExtension(szFile);

        BOOL bImageAPI;

        for (int i = 0;
                GetInstalledFilters(bImport, i, NULL, 0, buf, sizeof(buf), szHandler, cb, bImageAPI);
                i++)
        {
                if (lstrcmpi(ext+1, buf) == 0)
                        break;
                else
                        *szHandler = 0;
        }

        // make sure the handler file does exist
        if (*szHandler && GetFileAttributes(szHandler) != -1)
                rc = TRUE;

        return rc;
}

//
// FindBitmapInfo
//
// find the DIB bitmap in a memory meta file...
//
LPBITMAPINFOHEADER FindBitmapInfo(LPMETAHEADER pmh)
{
        for (LPMETARECORD pmr = (LPMETARECORD)((LPBYTE)pmh + pmh->mtHeaderSize*2);
                pmr < (LPMETARECORD)((LPBYTE)pmh + pmh->mtSize*2);
                pmr = (LPMETARECORD)((LPBYTE)pmr + pmr->rdSize*2))
        {
                switch (pmr->rdFunction)
                {
                        case META_DIBBITBLT:
                                return (LPBITMAPINFOHEADER)&(pmr->rdParm[8]);

                        case META_DIBSTRETCHBLT:
                                return (LPBITMAPINFOHEADER)&(pmr->rdParm[10]);

                        case META_STRETCHDIB:
                                return (LPBITMAPINFOHEADER)&(pmr->rdParm[11]);

                        case META_SETDIBTODEV:
                                return (LPBITMAPINFOHEADER)&(pmr->rdParm[9]);
                }
        }

        return NULL;
}

#endif // _USE_FLT_API

#ifdef _USE_IFL_API
  IFLERROR ReadGIFInterlacedImage(BYTE *ppbImageBuffer,
                                      IFLHANDLE pfpbFROM,
                                      int ImageHeight, DWORD dwWidthInBytes)
{
    int          iLine, iPass, iIntLine, iTempLine;
    IFLERROR   idErr;


    WORD       InterlaceMultiplier[] = { 8, 8, 4, 2 };
    WORD       InterlaceOffset[]     = { 0, 4, 2, 1 };

    idErr = IFLERR_NONE;

    iPass = 0;
    iIntLine = InterlaceOffset[iPass];
    iLine = 0;
    while (idErr == IFLERR_NONE && iLine < ImageHeight)
    {
       iTempLine = InterlaceMultiplier[iPass] * iIntLine + InterlaceOffset[iPass];
       if (iTempLine >= ImageHeight)
       {
           iPass++;
           iIntLine = 0;
           iTempLine = InterlaceOffset[iPass];
       }

       if (iTempLine < ImageHeight)
       {
           idErr = iflRead(pfpbFROM,
                     (LPBYTE)ppbImageBuffer+((ImageHeight-iTempLine-1)*dwWidthInBytes),
                           1);
           iLine++;
       }
       iIntLine++;
    }

    return idErr;
}
#endif // _USE_IFL_API

//
//  LoadDIBFromFile
//
//  load a image file using a image import filter. The filters use ANSI strings.
//

HGLOBAL LoadDIBFromFileA(LPCSTR szFileName, GUID *pguidFltTypeUsed)
{
#ifdef _USE_IFL_API

        IFLTYPE iflType;

        iflImageType((LPSTR)szFileName, &iflType);

        // make sure the image is of a type we know how to import
        if (iflType == IFLT_PNG)
        {
           return NULL;
        }

        IFLHANDLE iflHandle = iflCreateReadHandle(iflType);
        if (!iflHandle)
        {
           //
           // No filter installed for this type
           //
           return NULL;
        }

        LPBYTE lpStart = 0;

    __try 
    {

        IFLERROR iflErr = iflOpen(iflHandle, (LPSTR)szFileName, IFLM_READ);
        if (iflErr != IFLERR_NONE)
        {
                iflFreeHandle(iflHandle);
                return NULL;
        }

        ASSERT(pguidFltTypeUsed);

        switch (iflType)
        {
            case IFLT_GIF:  *pguidFltTypeUsed = WiaImgFmt_GIF;       break;
            case IFLT_BMP:  *pguidFltTypeUsed = WiaImgFmt_BMP;       break;
            case IFLT_JPEG: *pguidFltTypeUsed = WiaImgFmt_JPEG;      break;
            case IFLT_TIFF: *pguidFltTypeUsed = WiaImgFmt_TIFF;      break;
            case IFLT_PNG:  *pguidFltTypeUsed = WiaImgFmt_PNG;       break;
            case IFLT_PCD:  *pguidFltTypeUsed = WiaImgFmt_PHOTOCD;   break;
            default:        *pguidFltTypeUsed = WiaImgFmt_UNDEFINED; break;
        }

        IFLCLASS        iflClass = iflGetClass(iflHandle);
        IFLSEQUENCE     iflSequence = iflGetSequence(iflHandle);
        IFLCOMPRESSION  iflCompression = iflGetCompression(iflHandle);
        WORD            iBPS = (WORD) iflGetBitsPerChannel(iflHandle);

        if (iflClass != IFLCL_RGB && iflClass != IFLCL_PALETTE &&
            iflClass != IFLCL_GRAY && iflClass != IFLCL_BILEVEL)
        {
#ifdef _DEBUG
           TRACE(TEXT("LoadDIBFromFile: Not a RGB/PALETTE/GRAY/BW image.\n"));
           MessageBox (NULL, TEXT("Not a RGB/PALETTE/GRAY/BW image."),
                             TEXT("Loadimag.cpp"), MB_OK);
#endif
           iflClose(iflHandle);
           iflFreeHandle(iflHandle);
           return NULL;
        }

        // get the transparent color
        if (iflClass == IFLCL_RGB)
        {
                IFLCOLOR iflTransColor;
                g_bUseTrans = (IFLERR_NONE ==
                        iflControl(iflHandle, IFLCMD_TRANS_RGB, 0, 0, &iflTransColor));
                if (g_bUseTrans)
                        crTrans = RGB(iflTransColor.wRed,
                                                  iflTransColor.wGreen,
                                                  iflTransColor.wBlue);
        }
        else // must be IFLCL_PALETTE or IFLCL_GRAY or IFLCL_BILEVEL
        {
                BYTE byTransIdx;
                g_bUseTrans = (IFLERR_NONE ==
                        iflControl(iflHandle, IFLCMD_TRANS_IDX, 0, 0, &byTransIdx));
                if (g_bUseTrans)
                        crTrans = byTransIdx; // need to convert to COLORREF below
        }

        BITMAPINFOHEADER bi;
        memset(&bi, 0, sizeof(BITMAPINFOHEADER));

        bi.biSize = sizeof(BITMAPINFOHEADER); // should be 0x28 or 40 decimal
        bi.biWidth = iflGetWidth(iflHandle);
        bi.biHeight = iflGetHeight(iflHandle);
        bi.biPlanes = 1;




        if (iflClass == IFLCL_RGB)
        {
#ifdef PNG_SUPPORT
           if (iflType == IFLT_PNG)
           {
              bi.biBitCount = iBPS*3;
           }
           else
#endif // PNG_SUPPORT
           {
              bi.biBitCount = (WORD) iflGetBitsPerPixel (iflHandle);
           }
        }
        else // must be IFLCL_PALETTE or IFLCL_GRAY or IFLCL_BILEVEL
        {
           bi.biBitCount = 8;
        }



        bi.biCompression = 0;
        // convert width in pixels to bytes after rounding it up first
        DWORD dwWidthInBytes = ((bi.biWidth * bi.biBitCount + 31) & ~31)/8;
        bi.biSizeImage = abs(bi.biHeight) * dwWidthInBytes;
//      bi.biXPelsPerMeter = 0;
//      bi.biYPelsPerMeter = 0;
        if (iflClass == IFLCL_PALETTE || iflClass == IFLCL_GRAY
             || iflClass == IFLCL_BILEVEL)
                bi.biClrUsed = MAX_PAL_SIZE;
//      bi.biClrImportant = 0;

        LPBYTE lpBMP;

        if ((lpBMP = lpStart = (LPBYTE) GlobalAlloc(GMEM_FIXED, 
                bi.biSize + bi.biClrUsed*sizeof(RGBQUAD) + bi.biSizeImage)) == NULL)
                goto exit;

        memcpy(lpBMP, &bi, bi.biSize);
        lpBMP += bi.biSize;

        BYTE    byTemp;
        int             i, j;

        switch (iflSequence)
        {
           case IFLSEQ_TOPDOWN:
              switch (iflClass)
              {
                 case IFLCL_RGB:

                    lpBMP += bi.biClrUsed*sizeof(RGBQUAD) + bi.biSizeImage -
                    dwWidthInBytes;
                    for (i = 0; i < abs(bi.biHeight); lpBMP-=dwWidthInBytes, i++)
                    {
                       // read in one line at a time
                       iflRead(iflHandle, (LPBYTE)lpBMP, 1);
                       // need to swap RED with BLUE for internal DIB display
                       for (j = 0; j < bi.biWidth*3; j+=3)
                       {
                          byTemp = *(lpBMP+j);
                          *(lpBMP+j) = *(lpBMP+j+2);
                          *(lpBMP+j+2) = byTemp;
                       }
                    }
                    break;

                 case IFLCL_PALETTE:

                    // get palette info first...
                    RGBTRIPLE Pal3[MAX_PAL_SIZE];
                    RGBQUAD   Pal4[MAX_PAL_SIZE];
                    ZeroMemory (Pal3, MAX_PAL_SIZE*(sizeof(RGBTRIPLE)));
                    iflErr = iflControl(iflHandle, IFLCMD_PALETTE, 0, 0, &Pal3);

                    for (i = 0; i < MAX_PAL_SIZE; i++)
                    {
                       Pal4[i].rgbBlue     = Pal3[i].rgbtRed;
                       Pal4[i].rgbGreen    = Pal3[i].rgbtGreen;
                       Pal4[i].rgbRed      = Pal3[i].rgbtBlue;
                       Pal4[i].rgbReserved = 0;
                    }
                    memcpy(lpBMP, Pal4, sizeof(Pal4));

                    if (g_bUseTrans)
                    // convert the transparent color index to COLORREF
                       crTrans = RGB(Pal4[crTrans].rgbRed,Pal4[crTrans].rgbGreen,
                                             Pal4[crTrans].rgbBlue);

                    lpBMP += sizeof(Pal4) + bi.biSizeImage - dwWidthInBytes;

                    for (i = 0;i < abs(bi.biHeight);lpBMP-=dwWidthInBytes, i++)
                    {
                       // read in one line at a time
                       iflRead(iflHandle, (LPBYTE)lpBMP, 1);
                    }

                    break;

                 case IFLCL_GRAY:

                    // get palette info first...
                    //BYTE PalGray[MAX_PAL_SIZE];
                    //iflErr = iflControl(iflHandle, IFLCMD_PALETTE, 0, 0, &PalGray);

                    for (i = 0; i < MAX_PAL_SIZE; i++)
                    {
                       Pal4[i].rgbBlue     = (BYTE) i;//PalGray[i];
                       Pal4[i].rgbGreen    = (BYTE) i;//PalGray[i];
                       Pal4[i].rgbRed      = (BYTE) i;//PalGray[i];
                       Pal4[i].rgbReserved = 0;
                    }
                    memcpy(lpBMP, Pal4, sizeof(Pal4));

                    if (g_bUseTrans)
                    // convert the transparent color index to COLORREF
                       crTrans = RGB(Pal4[crTrans].rgbRed, Pal4[crTrans].rgbGreen,
                                                   Pal4[crTrans].rgbBlue);

                    lpBMP += sizeof(Pal4) + bi.biSizeImage - dwWidthInBytes;

                    for (i = 0;i < abs(bi.biHeight);lpBMP-=dwWidthInBytes, i++)
                    {
                       // read in one line at a time
                       iflRead(iflHandle, (LPBYTE)lpBMP, 1);
                    }

                    break;

                 case IFLCL_BILEVEL:

                    // set color Black
                    Pal4[0].rgbBlue     = 0;
                    Pal4[0].rgbGreen    = 0;
                    Pal4[0].rgbRed      = 0;
                    Pal4[0].rgbReserved = 0;

                    // set color White
                    Pal4[1].rgbBlue     = 255;
                    Pal4[1].rgbGreen    = 255;
                    Pal4[1].rgbRed      = 255;
                    Pal4[1].rgbReserved = 0;

                    memcpy(lpBMP, Pal4, sizeof(Pal4));

                    if (g_bUseTrans)
                       // convert the transparent color index to COLORREF
                       crTrans = RGB(Pal4[crTrans].rgbRed,
                                     Pal4[crTrans].rgbGreen,
                                     Pal4[crTrans].rgbBlue);

                    lpBMP += sizeof(Pal4) + bi.biSizeImage - dwWidthInBytes;

                    for (i = 0;i < abs(bi.biHeight);lpBMP-=dwWidthInBytes, i++)
                    {
                       // read in one line at a time
                       iflRead(iflHandle, (LPBYTE)lpBMP, 1);
                    }
                    break;

                 default:
                 // currently not supported
                    break;
              }
              break;

           case IFLSEQ_BOTTOMUP:

              lpBMP += bi.biClrUsed*sizeof(RGBQUAD) + bi.biSizeImage - dwWidthInBytes;

              for (i = 0;i < abs(bi.biHeight);lpBMP-=dwWidthInBytes, i++)
              {
                 // read in one line at a time
                 iflRead(iflHandle, (LPBYTE)lpBMP, 1);

                 // need to swap RED with BLUE for internal DIB display
                 for (j = 0; j < bi.biWidth*3; j+=3)
                 {
                    byTemp = *(lpBMP+j);
                    *(lpBMP+j) = *(lpBMP+j+2);
                    *(lpBMP+j+2) = byTemp;
                 }
              }
              break;

           case IFLSEQ_GIF_INTERLACED:
           {

              // get color palette info first...
              RGBTRIPLE Pal3[MAX_PAL_SIZE];
              RGBQUAD   Pal4[MAX_PAL_SIZE];
              iflErr = iflControl(iflHandle, IFLCMD_PALETTE, 0, 0, &Pal3);

              for (i = 0; i < MAX_PAL_SIZE; i++)
                 {
                    Pal4[i].rgbBlue     = Pal3[i].rgbtRed;
                    Pal4[i].rgbGreen    = Pal3[i].rgbtGreen;
                    Pal4[i].rgbRed      = Pal3[i].rgbtBlue;
                    Pal4[i].rgbReserved = 0;
                 }
                 memcpy(lpBMP, Pal4, sizeof(Pal4));

                 if (g_bUseTrans)
                 // convert the transparent color index to COLORREF
                    crTrans = RGB(Pal4[crTrans].rgbRed,Pal4[crTrans].rgbGreen,
                                                       Pal4[crTrans].rgbBlue);

              LPBYTE lpTemp = lpBMP + sizeof(Pal4);
              ReadGIFInterlacedImage (lpTemp, iflHandle, bi.biHeight, dwWidthInBytes);

           }
           break;
/*         case 1010101:
              {

                 int IM[] = { 8, 8, 4, 2 }; // interlace multiplier
                 //int IO[] = { 1, 5, 3 ,2 }; // interface offset
                 int IO[] = { 0, 4, 2,1 };

                 for (j = 0; j < 4; j++)
                 {
                    lpBMP = lpTemp + bi.biSizeImage - dwWidthInBytes*IO[j];
                    for (i = 0; i < abs(bi.biHeight) && lpBMP >= lpTemp;
                                 lpBMP-=dwWidthInBytes*IM[j], i+=8)
                    {
                       // read in one line at a time
                       iflRead(iflHandle, (LPBYTE)lpBMP, 1);
                    }
                 }

                 break;
              }*/
#ifdef PNG_SUPPORT
           case IFLSEQ_ADAM7_INTERLACED:
           {

              // get color palette info first...
              RGBTRIPLE Pal3[MAX_PAL_SIZE];
              RGBQUAD   Pal4[MAX_PAL_SIZE];
              iflErr = iflControl(iflHandle, IFLCMD_PALETTE, 0, 0, &Pal3);

              for (i = 0; i < MAX_PAL_SIZE; i++)
              {
                 Pal4[i].rgbBlue     = Pal3[i].rgbtRed;
                 Pal4[i].rgbGreen    = Pal3[i].rgbtGreen;
                 Pal4[i].rgbRed      = Pal3[i].rgbtBlue;
                 Pal4[i].rgbReserved = 0;
              }
              memcpy(lpBMP, Pal4, sizeof(Pal4));

              if (g_bUseTrans)
                 // convert the transparent color index to COLORREF
                 crTrans = RGB(Pal4[crTrans].rgbRed,
                               Pal4[crTrans].rgbGreen,
                               Pal4[crTrans].rgbBlue);
/////////////////////////////
                HANDLE hHeap = GetProcessHeap();
                LPBYTE *ppbRGBRowPtrs =(LPBYTE *)AllocateImageSpace(hHeap,
                                      bi.biHeight, dwWidthInBytes, /*bi.biWidth, */sizeof(BYTE));

                if (ppbRGBRowPtrs != NULL)
                {
                // First get the image. This function will de-interlace the image
                // AND any alpha channel information: it will also resize the alpha
                // channel data structure to the image height from the number of
                // raster lines, if necessary.
                   iflErr = ReadADAM7InterlacedImage(ppbRGBRowPtrs, iflHandle,
                                                    bi.biHeight, bi.biWidth,
                                                    sizeof(BYTE)*3, iflClass);
/////////////////////////////

                   if (iflErr == IFLERR_NONE)
                   {
                      lpBMP += bi.biClrUsed*sizeof(RGBQUAD) + bi.biSizeImage -
                                             dwWidthInBytes;
                      for (i = 0;i < abs(bi.biHeight);lpBMP-=dwWidthInBytes, i++)
                      {
                         // read in one line at a time
                         memcpy((LPBYTE)lpBMP, ppbRGBRowPtrs[i], dwWidthInBytes);

                         // need to swap RED with BLUE for internal DIB display
                         for (j = 0; j < bi.biWidth*3; j+=3)
                         {
                            byTemp = *(lpBMP+j);
                            *(lpBMP+j) = *(lpBMP+j+2);
                            *(lpBMP+j+2) = byTemp;
                         }
                     }
                  }

                  ppbRGBRowPtrs = (LPBYTE *)FreeImageSpace(hHeap,
                                                           ppbRGBRowPtrs,
                                                           bi.biHeight);
                }
                break;

             }
#endif // PNG_SUPPORT
             default:
                break;
        }

    } 
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    }

exit:
        iflClose(iflHandle);
        iflFreeHandle(iflHandle);

        return (HGLOBAL)lpStart;

#endif // _USE_IFL_API

//////////////////////////////////////////////////////////////////////////////

#ifdef _USE_FLT_API

        HINSTANCE           hLib = NULL;
        FILESPEC            fileSpec;               // file to load
        GRPI                            pict;
        UINT                rc;                     // return code
        HANDLE              hPrefMem = NULL;        // filter-supplied preferences
        UINT                wFilterType;            // 2 = graphics filter
        char                szHandler[128];
        HGLOBAL             hDib = NULL;

        PFNGetFilterInfo lpfnGetFilterInfo;
        PFNImportGr lpfnImportGr;

        if (!GetHandlerForFile(TRUE, szFileName, szHandler, sizeof(szHandler)))
        return FALSE;

    if (szHandler[0] == 0)
        return FALSE;

    if ((hLib = LoadLibrary(szHandler)) == NULL)
        goto exit;

    // get a pointer to the ImportGR function
    lpfnGetFilterInfo = (PFNGetFilterInfo)GetProcAddress(hLib, "GetFilterInfo");
    lpfnImportGr = (PFNImportGr)GetProcAddress(hLib, "ImportGr");

    if (lpfnGetFilterInfo == NULL)
        lpfnGetFilterInfo = (PFNGetFilterInfo)GetProcAddress(hLib, "GetFilterInfo@16");

    if (lpfnImportGr == NULL)
        lpfnImportGr = (PFNImportGr)GetProcAddress(hLib, "ImportGr@16");

    if (lpfnImportGr == NULL)
        goto exit;

    if (lpfnGetFilterInfo != NULL)
    {
        wFilterType = (*lpfnGetFilterInfo)
            ((short) 2,                 // interface version no.
            (char *)NULL,               // unused
            (HANDLE *) &hPrefMem,       // fill in: preferences
            (DWORD) 0x00020000);        // unused in Windows

        // the return value is the type of filter: 0=error,
        // 1=text-filter, 2=graphics-filter
        if (wFilterType != 2)
            goto exit;
    }

    fileSpec.slippery = FALSE;      // TRUE if file may disappear
    fileSpec.write = FALSE;         // TRUE if open for write
    fileSpec.unnamed = FALSE;       // TRUE if unnamed
    fileSpec.linked = FALSE;        // Linked to an FS FCB
    fileSpec.mark = FALSE;          // Generic mark bit
    fileSpec.dcbFile = 0L;
    //the converters need a pathname without spaces...

    GetShortPathName(szFileName, fileSpec.szName, sizeof(fileSpec.szName));

    pict.hmf = NULL;

    rc = (*lpfnImportGr)
        (NULL,                      // "the target DC" (printer?)
        (FILESPEC *) &fileSpec,     // file to read
        (GRPI *) &pict,             // fill in: result metafile
        (HANDLE) hPrefMem);         // preferences memory

    if (pict.hmf != NULL)
    {
        if (rc == 0)
        {
            // find the BITMAPINFO in the returned metafile

            LPMETAHEADER lpMetaHeader = (LPMETAHEADER) GlobalLock(pict.hmf);

            LPBITMAPINFOHEADER lpbi = FindBitmapInfo(lpMetaHeader);

            if (lpbi != NULL)
            {
                // copy the DIB

                SIZE_T nSize = FindDibSize(lpbi);

                hDib = GlobalAlloc(GMEM_FIXED, nSize);

                CopyMemory(hDib, lpbi, nSize);
            }

            GlobalUnlock(pict.hmf);
        }

        GlobalFree(pict.hmf);
    }

exit:
    if (hPrefMem != NULL)
        GlobalFree(hPrefMem);

    if (hLib)
        FreeLibrary(hLib);

    return hDib;

#endif // _USE_FLT_API
   return NULL;
}

CGdiplusInit::CGdiplusInit(
    Gdiplus::DebugEventProc debugEventCallback       /*= 0*/,
    BOOL                    suppressBackgroundThread /*= FALSE*/,
    BOOL                    suppressExternalCodecs   /*= FALSE*/
)
{
    Gdiplus::GdiplusStartupInput StartupInput(
        debugEventCallback,
        suppressBackgroundThread,
        suppressExternalCodecs
    );

    StartupStatus = GdiplusSafeStartup(&Token, &StartupInput, this);
}

CGdiplusInit::~CGdiplusInit()
{
    if (StartupStatus == Gdiplus::Ok)
    {
        Gdiplus::GdiplusShutdown(Token);
    }
}

Gdiplus::Status
CGdiplusInit::GdiplusSafeStartup(
    ULONG_PTR                          *token,
    const Gdiplus::GdiplusStartupInput *input,
    Gdiplus::GdiplusStartupOutput      *output
)
{
    __try
    {
        return Gdiplus::GdiplusStartup(token, input, output);
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return Gdiplus::GdiplusNotInitialized;
    }
}

BOOL GetGdiplusDecoders(UINT *pnCodecs, Gdiplus::ImageCodecInfo **ppCodecs)
{
    ASSERT(pnCodecs);
    ASSERT(ppCodecs);

    *ppCodecs = 0;
    *pnCodecs = 0;

    if (theApp.GdiplusInit.StartupStatus == Gdiplus::Ok)
    {
        __try
        {
            UINT cbCodecs;

            if (Gdiplus::GetImageDecodersSize(pnCodecs, &cbCodecs) == Gdiplus::Ok)
            {
                if (*pnCodecs > 0)
                {
                    *ppCodecs = (Gdiplus::ImageCodecInfo *) LocalAlloc(LMEM_FIXED, cbCodecs);

                    if (*ppCodecs != 0)
                    {
                        if (Gdiplus::GetImageDecoders(*pnCodecs, cbCodecs, *ppCodecs) == Gdiplus::Ok)
                        {
                            return TRUE;
                        }
                    }
                }
            }
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
        }

        LocalFree(*ppCodecs);

        *ppCodecs = 0;
        *pnCodecs = 0;
    }

    return FALSE;
}

BOOL GetGdiplusEncoders(UINT *pnCodecs, Gdiplus::ImageCodecInfo **ppCodecs)
{
    ASSERT(pnCodecs);
    ASSERT(ppCodecs);

    *ppCodecs = 0;
    *pnCodecs = 0;

    if (theApp.GdiplusInit.StartupStatus == Gdiplus::Ok)
    {
        __try
        {
            UINT cbCodecs;

            if (Gdiplus::GetImageEncodersSize(pnCodecs, &cbCodecs) == Gdiplus::Ok)
            {
                if (*pnCodecs > 0)
                {
                    *ppCodecs = (Gdiplus::ImageCodecInfo *) LocalAlloc(LMEM_FIXED, cbCodecs);

                    if (*ppCodecs != 0)
                    {
                        if (Gdiplus::GetImageEncoders(*pnCodecs, cbCodecs, *ppCodecs) == Gdiplus::Ok)
                        {
                            return TRUE;
                        }
                    }
                }
            }
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
        }

        LocalFree(*ppCodecs);

        *ppCodecs = 0;
        *pnCodecs = 0;
    }

    return FALSE;
}

BOOL GetClsidOfEncoder(REFGUID guidFormatID, CLSID *pClsid)
{
    Gdiplus::ImageCodecInfo *pCodecs = 0;
    UINT                     nCodecs = 0;

    GetGdiplusEncoders(&nCodecs, &pCodecs);

    for (UINT i = 0; i < nCodecs; ++i)
    {
        if (pCodecs[i].FormatID == guidFormatID)
        {
            if (pClsid)
            {
                *pClsid = pCodecs[i].Clsid;
            }

            LocalFree(pCodecs);

            return TRUE;
        }
    }

    LocalFree(pCodecs);

    return FALSE;
}

HGLOBAL LoadDIBGdiplus(LPCTSTR szFileName, GUID *pguidFltTypeUsed)
{
    // check that the BMP encoder exists

    CLSID ClsidBmpEncoder;

    if (GetClsidOfEncoder(WiaImgFmt_BMP, &ClsidBmpEncoder))
    {
         // let GDI+ import the file

        USES_CONVERSION;

        Gdiplus::Bitmap image(T2CW(szFileName));

        if (image.GetLastStatus() == Gdiplus::Ok)
        {
            // read the image type

            ASSERT(pguidFltTypeUsed);

            image.GetRawFormat(pguidFltTypeUsed);

            // create a stream that emulates a bmp file

            CComPtr<CBmpStream> pStream;
            
            if (CBmpStream::Create(&pStream) == S_OK)
            {
                // convert the image into a BMP

                if (image.Save(pStream, &ClsidBmpEncoder, 0) == Gdiplus::Ok)
                {
                    return pStream->GetBuffer();
                }

                pStream->FreeBuffer();
            }
        }
    }

    return 0;
}

HGLOBAL LoadDIBFromFile(LPCTSTR szFileName, GUID *pguidFltTypeUsed)
{
    // Try GDI+ filters first. If it fails to convert the image or 
    // if it's not available, try the old method

    HGLOBAL hResult = 0;

    if (theApp.GdiplusInit.StartupStatus == Gdiplus::Ok)
    {
        __try
        {
            hResult = LoadDIBGdiplus(szFileName, pguidFltTypeUsed);
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
        }
    }

    if (!hResult)
    {
        USES_CONVERSION;

        hResult = LoadDIBFromFileA(T2CA(szFileName), pguidFltTypeUsed);
    }

    return hResult;
}