/*++ Copyright (C) 1999- Microsoft Corporation Module Name: minidrv.cpp Abstract: This module implements main part of CWiaMiniDriver class Author: William Hsieh (williamh) created Revision History: --*/ #include "pch.h" const WORD TIFFTAG_IMAGELENGTH = 257; const WORD TIFFTAG_IMAGEWIDTH = 256; const WORD TIFFTAG_RESOLUTIONUNIT = 296; const WORD TIFFTAG_PHOTOMETRIC = 262; const WORD TIFFTAG_COMPRESSION = 259; const WORD TIFFTAG_XRESOLUTION = 282; const WORD TIFFTAG_YRESOLUTION = 283; const WORD TIFFTAG_ROWSPERSTRIP = 278; const WORD TIFFTAG_STRIPOFFSETS = 273; const WORD TIFFTAG_STRIPBYTECOUNTS = 279; const WORD TIFFTAG_COLORMAP = 320; const WORD TIFFTAG_BITSPERSAMPLE = 258; const WORD TIFFTAG_SAMPLESPERPIXEL = 277; const WORD TIFFTAG_ARTIST = 315; const WORD TIFFTAG_COPYRIGHT = 33432; const WORD TIFFTAG_DATETIME = 306; const WORD TIFFTAG_MAKE = 271; const WORD TIFFTAG_IMAGEDESCRIPTION = 270; const WORD TIFFTAG_MAXSAMPLEVALUE = 281; const WORD TIFFTAG_MINSAMPLEVALUE = 280; const WORD TIFFTAG_MODEL = 272; const WORD TIFFTAG_NEWSUBFILETYPE = 254; const WORD TIFFTAG_ORIENTATION = 274; const WORD TIFFTAG_PLANARCONFIGURATION = 284; const char LITTLE_ENDIAN_MARKER = 'I'; const char BIG_ENDIAN_MARKER = 'M'; const WORD TIFF_SIGNATURE_I = 0x002A; const WORD TIFF_SIGNATURE_M = 0x2A00; const WORD TIFF_PHOTOMETRIC_WHITE = 0; const WORD TIFF_PHOTOMETRIC_BLACK = 1; const WORD TIFF_PHOTOMETRIC_RGB = 2; const WORD TIFF_PHOTOMETRIC_PALETTE = 3; const WORD TIFF_COMPRESSION_NONE = 1; const WORD TIFF_TYPE_BYTE = 1; const WORD TIFF_TYPE_ASCII = 2; const WORD TIFF_TYPE_SHORT = 3; const WORD TIFF_TYPE_LONG = 4; const WORD TIFF_TYPE_RATIONAL = 5; const WORD TIFF_TYPE_SBYTE = 6; const WORD TIFF_TYPE_UNDEFINED = 7; const WORD TIFF_TYPE_SSHORT = 8; const WORD TIFF_TYPE_SLONG = 9; const WORD TIFF_TYPE_SRATIONAL = 10; const WORD TIFF_TYPE_FLOAT = 11; const WORD TIFF_TYPE_DOUBLE = 12; typedef struct tagTiffHeader { char ByteOrder_1; char ByteOrder_2; WORD Signature; DWORD IFDOffset; }TIFF_HEADER, *PTIFF_HEADER; typedef struct tagTiffTag { WORD TagId; // tag id WORD Type; // tag data type DWORD Count; // how many items DWORD ValOffset; // offset to the data items }TIFF_TAG, *PTIFF_TAG; typedef struct tagTiffImageInfo { DWORD ImageHeight; DWORD ImageWidth; DWORD BitsPerSample; DWORD SamplesPerPixel; DWORD PhotoMetric; DWORD Compression; DWORD RowsPerStrip; DWORD NumStrips; DWORD *pStripOffsets; DWORD *pStripByteCounts; }TIFF_IMAGEINFO, *PTIFF_IMAGEINFO; WORD ByteSwapWord(WORD w) { return((w &0xFF00) >> 8 | (w & 0xFF) << 8); } DWORD ByteSwapDword(DWORD dw) { return((DWORD)(ByteSwapWord((WORD)((dw &0xFFFF0000) >> 16))) | (DWORD)(ByteSwapWord((WORD)(dw & 0xFFFF))) << 16); } DWORD GetDIBLineSize( DWORD Width, DWORD BitsCount ) { return(Width * (BitsCount / 8) + 3) & ~3; } DWORD GetDIBSize( BITMAPINFO *pbmi ) { return GetDIBBitsOffset(pbmi) + GetDIBLineSize(pbmi->bmiHeader.biWidth, pbmi->bmiHeader.biBitCount) * abs(pbmi->bmiHeader.biHeight); } DWORD GetDIBBitsOffset( BITMAPINFO *pbmi ) { DWORD Offset = (DWORD)-1; if (pbmi && pbmi->bmiHeader.biSize >= sizeof(BITMAPINFOHEADER)) { Offset = pbmi->bmiHeader.biSize; if (pbmi->bmiHeader.biBitCount <= 8) { if (pbmi->bmiHeader.biClrUsed) { Offset += pbmi->bmiHeader.biClrUsed * sizeof(RGBQUAD); } else { Offset += ((DWORD) 1 << pbmi->bmiHeader.biBitCount) * sizeof(RGBQUAD); } } if (BI_BITFIELDS == pbmi->bmiHeader.biCompression) { Offset += 3 * sizeof(DWORD); } } return Offset; } HRESULT WINAPI GetTiffDimensions( BYTE *pTiff, UINT TiffSize, UINT *pWidth, UINT *pHeight, UINT *pBitDepth ) { if (!pTiff || !TiffSize || !pWidth || !pHeight || !pBitDepth) return E_INVALIDARG; DWORD CurOffset; WORD TagCounts; BOOL bByteSwap; TIFF_TAG *pTiffTags; HRESULT hr; DWORD BitsPerSample; DWORD SamplesPerPixel; if (BIG_ENDIAN_MARKER == *((CHAR *)pTiff) && BIG_ENDIAN_MARKER == *((CHAR *)pTiff + 1)) { if (TIFF_SIGNATURE_M != *((WORD *)pTiff + 1)) return E_INVALIDARG; bByteSwap = TRUE; CurOffset = ByteSwapDword(*((DWORD *)(pTiff + 4))); TagCounts = ByteSwapWord(*((WORD *)(pTiff + CurOffset))); } else { if (TIFF_SIGNATURE_I != *((WORD *)pTiff + 1)) return E_INVALIDARG; bByteSwap = FALSE; CurOffset = *((DWORD *)(pTiff + 4)); TagCounts = *((WORD *)(pTiff + CurOffset)); } pTiffTags = (TIFF_TAG *)(pTiff + CurOffset + sizeof(WORD)); hr = S_OK; *pWidth = 0; *pHeight = 0; *pBitDepth = 0; // // Assuming it is 24bits color // BitsPerSample = 8; SamplesPerPixel = 3; while (TagCounts && S_OK == hr) { WORD TagId; WORD Type; DWORD Count; DWORD ValOffset; WORD i; DWORD *pdwOffset; WORD *pwOffset; if (bByteSwap) { TagId = ByteSwapWord(pTiffTags->TagId); Type = ByteSwapWord(pTiffTags->Type); Count = ByteSwapDword(pTiffTags->Count); ValOffset = ByteSwapDword(pTiffTags->ValOffset); } else { TagId = pTiffTags->TagId; Type = pTiffTags->Type; Count = pTiffTags->Count; ValOffset = pTiffTags->ValOffset; } switch (TagId) { case TIFFTAG_IMAGELENGTH: if (TIFF_TYPE_SHORT == Type) *pHeight = (WORD)ValOffset; else *pHeight = ValOffset; break; case TIFFTAG_IMAGEWIDTH: if (TIFF_TYPE_SHORT == Type) *pWidth = (WORD)ValOffset; else *pWidth = ValOffset; break; case TIFFTAG_PHOTOMETRIC: if (TIFF_PHOTOMETRIC_RGB != (WORD)ValOffset) { // // bi-level or grayscale or palette. // SamplesPerPixel = 1; } else { SamplesPerPixel = 3; } break; case TIFFTAG_BITSPERSAMPLE: BitsPerSample = (WORD)ValOffset; break; case TIFFTAG_SAMPLESPERPIXEL: SamplesPerPixel = (WORD)ValOffset; break; default: break; } pTiffTags++; TagCounts--; } *pBitDepth = SamplesPerPixel * BitsPerSample; return S_OK; } // // This function converts a TIFF file in memory to DIB bitmap // Input: // pTiff -- Tiff file in memory. TIFF, TIFF/EP, TIFF/IT are supported // TiffSize -- the TIFF file size // DIBBmpSize -- DIB bitmap buffer size // pDIBBmp -- DIB bitmap buffer // LineSize -- destination scanline size in bytes // MaxLines -- maximum scanline can be delivered per callback // 0 if we decide it. // pProgressCB -- optional callback // pCBContext -- context for the callback. // If no callback is provided, the given dib // bitmap buffer must be big enough to // receive the entire bitmap. // Output: // HRESULT -- S_FALSE if the client aborted the transfer // HRESULT WINAPI Tiff2DIBBitmap( BYTE *pTiff, UINT TiffSize, BYTE *pDIBBmp, UINT DIBBmpSize, UINT LineSize, UINT MaxLines ) { if (!pTiff || !TiffSize || !pDIBBmp || !DIBBmpSize || !LineSize) return E_INVALIDARG; HRESULT hr; DWORD CurOffset; WORD TagCounts; BOOL bByteSwap; TIFF_TAG *pTiffTags; TIFF_IMAGEINFO TiffImageInfo; ZeroMemory(&TiffImageInfo, sizeof(TiffImageInfo)); // // Set some default values // TiffImageInfo.PhotoMetric = TIFF_PHOTOMETRIC_RGB; TiffImageInfo.SamplesPerPixel = 3; TiffImageInfo.BitsPerSample = 8; TiffImageInfo.Compression = TIFF_COMPRESSION_NONE; if (BIG_ENDIAN_MARKER == *((CHAR *)pTiff) && BIG_ENDIAN_MARKER == *((CHAR *)pTiff + 1)) { if (TIFF_SIGNATURE_M != *((WORD *)pTiff + 1)) return E_INVALIDARG; bByteSwap = TRUE; CurOffset = ByteSwapDword(*((DWORD *)(pTiff + 4))); TagCounts = ByteSwapWord(*((WORD *)(pTiff + CurOffset))); } else { if (TIFF_SIGNATURE_I != *((WORD *)pTiff + 1)) return E_INVALIDARG; bByteSwap = FALSE; CurOffset = *((DWORD *)(pTiff + 4)); TagCounts = *((WORD *)(pTiff + CurOffset)); } pTiffTags = (TIFF_TAG *)(pTiff + CurOffset + sizeof(WORD)); hr = S_OK; while (TagCounts && SUCCEEDED(hr)) { WORD TagId; WORD Type; DWORD Count; DWORD ValOffset; WORD i; DWORD *pdwOffset; WORD *pwOffset; if (bByteSwap) { TagId = ByteSwapWord(pTiffTags->TagId); Type = ByteSwapWord(pTiffTags->Type); Count = ByteSwapDword(pTiffTags->Count); ValOffset = ByteSwapDword(pTiffTags->ValOffset); } else { TagId = pTiffTags->TagId; Type = pTiffTags->Type; Count = pTiffTags->Count; ValOffset = pTiffTags->ValOffset; } switch (TagId) { case TIFFTAG_IMAGELENGTH: if (TIFF_TYPE_SHORT == Type) TiffImageInfo.ImageHeight = (WORD)ValOffset; else TiffImageInfo.ImageHeight = ValOffset; break; case TIFFTAG_IMAGEWIDTH: if (TIFF_TYPE_SHORT == Type) TiffImageInfo.ImageWidth = (WORD)ValOffset; else TiffImageInfo.ImageWidth = ValOffset; break; case TIFFTAG_PHOTOMETRIC: TiffImageInfo.PhotoMetric = (WORD)ValOffset; if (TIFF_PHOTOMETRIC_RGB != (WORD)ValOffset) { // // bi-level or grayscale or palette. // TiffImageInfo.SamplesPerPixel = 1; } else { TiffImageInfo.SamplesPerPixel = 3; } break; case TIFFTAG_COMPRESSION: TiffImageInfo.Compression = ValOffset; break; case TIFFTAG_ROWSPERSTRIP: if (TIFF_TYPE_SHORT == Type) TiffImageInfo.RowsPerStrip = (WORD)ValOffset; else TiffImageInfo.RowsPerStrip = ValOffset; break; case TIFFTAG_STRIPOFFSETS: TiffImageInfo.pStripOffsets = new DWORD[Count]; TiffImageInfo.NumStrips = Count; if (!TiffImageInfo.pStripOffsets) { hr = E_OUTOFMEMORY; break; } for (i = 0; i < Count ; i++) { if (TIFF_TYPE_SHORT == Type) { pwOffset = (WORD *)(pTiff + ValOffset); if (bByteSwap) { TiffImageInfo.pStripOffsets[i] = ByteSwapWord(*pwOffset); } else { TiffImageInfo.pStripOffsets[i] = *pwOffset; } } else if (TIFF_TYPE_LONG == Type) { pdwOffset = (DWORD *)(pTiff + ValOffset); if (bByteSwap) { TiffImageInfo.pStripOffsets[i] = ByteSwapDword(*pdwOffset); } else { TiffImageInfo.pStripOffsets[i] = *pdwOffset; } } } break; case TIFFTAG_STRIPBYTECOUNTS: TiffImageInfo.pStripByteCounts = new DWORD[Count]; TiffImageInfo.NumStrips = Count; if (!TiffImageInfo.pStripByteCounts) { hr = E_OUTOFMEMORY; break; } for (i = 0; i < Count ; i++) { if (TIFF_TYPE_SHORT == Type) { pwOffset = (WORD *)(pTiff + ValOffset); if (bByteSwap) { TiffImageInfo.pStripByteCounts[i] = ByteSwapWord(*pwOffset); } else { TiffImageInfo.pStripByteCounts[i] = *pwOffset; } } else if (TIFF_TYPE_LONG == Type) { pdwOffset = (DWORD *)(pTiff + ValOffset); if (bByteSwap) { TiffImageInfo.pStripByteCounts[i] = ByteSwapDword(*pdwOffset); } else { TiffImageInfo.pStripByteCounts[i] = *pdwOffset; } } } break; case TIFFTAG_BITSPERSAMPLE: TiffImageInfo.BitsPerSample = (WORD)ValOffset; break; case TIFFTAG_SAMPLESPERPIXEL: TiffImageInfo.SamplesPerPixel = (WORD)ValOffset; break; case TIFFTAG_XRESOLUTION: case TIFFTAG_YRESOLUTION: case TIFFTAG_RESOLUTIONUNIT: // do this later break; default: break; } pTiffTags++; TagCounts--; } if (!SUCCEEDED(hr)) { // // If something wrong happen along the way, free // any memory we have allocated. // if (TiffImageInfo.pStripOffsets) delete [] TiffImageInfo.pStripOffsets; if (TiffImageInfo.pStripByteCounts) delete [] TiffImageInfo.pStripByteCounts; return hr; } // // Support RGB full color for now. // Also, we do not support any compression. // if (TIFF_PHOTOMETRIC_RGB != TiffImageInfo.PhotoMetric || TIFF_COMPRESSION_NONE != TiffImageInfo.Compression || DIBBmpSize < LineSize * TiffImageInfo.ImageHeight) { delete [] TiffImageInfo.pStripOffsets; delete [] TiffImageInfo.pStripByteCounts; return E_INVALIDARG; } if (1 == TiffImageInfo.NumStrips) { // // With single strip, the writer may write a // 2**31 -1(infinity) which would confuses our // code below. Here, we set it to the right value // TiffImageInfo.RowsPerStrip = TiffImageInfo.ImageHeight; } // // DIB scanlines are DWORD aligned while TIFF scanlines // are BYTE aligned(when the compression value is 1 which // is the case we enforce). Because of this, we copy the bitmap // scanline by scanline // DWORD NumStrips; DWORD *pStripOffsets; DWORD *pStripByteCounts; DWORD TiffLineSize; // // Tiff scanlines with compression 1 are byte aligned. // TiffLineSize = TiffImageInfo.ImageWidth * TiffImageInfo. BitsPerSample * TiffImageInfo.SamplesPerPixel / 8; // // For convenience // pStripOffsets = TiffImageInfo.pStripOffsets; pStripByteCounts = TiffImageInfo.pStripByteCounts; NumStrips = TiffImageInfo.NumStrips; for (hr = S_OK, NumStrips = TiffImageInfo.NumStrips; NumStrips; NumStrips--) { DWORD Lines; BYTE *pTiffBits; // // how many lines to copy in this strip. Ignore any remaining bytes // Lines = *pStripByteCounts / TiffLineSize; // // The bits // pTiffBits = pTiff + *pStripOffsets; for (hr = S_OK; Lines, S_OK == hr; Lines--) { if (DIBBmpSize >= LineSize) { memcpy(pDIBBmp, pTiffBits, TiffLineSize); pDIBBmp -= LineSize; DIBBmpSize -= LineSize; } else { hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); } pTiffBits += TiffLineSize; } pStripOffsets++; pStripByteCounts++; } delete [] TiffImageInfo.pStripOffsets; delete [] TiffImageInfo.pStripByteCounts; return hr; } ////////////////////////////// GDI+ dynamic linking, image geometry ////////////////////////////// retrieval & decompression #include #include #include HINSTANCE g_hGdiPlus = NULL; ULONG_PTR g_GdiPlusToken = 0; GUID g_guidCodecBmp; Gdiplus::GdiplusStartupInput g_GdiPlusStartupInput; Gdiplus::GpStatus (WINAPI *pGdipLoadImageFromStream)(IStream *pStream, Gdiplus::GpImage **pImage) = NULL; Gdiplus::GpStatus (WINAPI *pGdipSaveImageToStream)(Gdiplus::GpImage *image, IStream* stream, CLSID* clsidEncoder, Gdiplus::EncoderParameters* encoderParams) = NULL; Gdiplus::GpStatus (WINAPI *pGdipSaveImageToFile)(Gdiplus::GpImage *image, WCHAR * stream, CLSID* clsidEncoder, Gdiplus::EncoderParameters* encoderParams) = NULL; Gdiplus::GpStatus (WINAPI *pGdipGetImageWidth)(Gdiplus::GpImage *pImage, UINT *pWidth) = NULL; Gdiplus::GpStatus (WINAPI *pGdipGetImageHeight)(Gdiplus::GpImage *pImage, UINT *pWidth) = NULL; Gdiplus::GpStatus (WINAPI *pGdipGetImagePixelFormat)(Gdiplus::GpImage *pImage, Gdiplus::PixelFormat *pFormat) = NULL; Gdiplus::GpStatus (WINAPI *pGdipDisposeImage)(Gdiplus::GpImage *pImage) = NULL; Gdiplus::GpStatus (WINAPI *pGdiplusStartup)(ULONG_PTR *token, const Gdiplus::GdiplusStartupInput *input, Gdiplus::GdiplusStartupOutput *output) = NULL; Gdiplus::GpStatus (WINAPI *pGdipGetImageEncodersSize)(UINT *numEncoders, UINT *size) = NULL; Gdiplus::GpStatus (WINAPI *pGdipGetImageEncoders)(UINT numEncoders, UINT size, Gdiplus::ImageCodecInfo *encoders) = NULL; VOID (WINAPI *pGdiplusShutdown)(ULONG_PTR token) = NULL; HRESULT InitializeGDIPlus(void) { HRESULT hr = E_FAIL; Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL; g_hGdiPlus = LoadLibraryA("gdiplus.dll"); if(!g_hGdiPlus) { wiauDbgError("InitializeGDIPlus", "Failed to load gdiplus.dll"); return HRESULT_FROM_WIN32(GetLastError()); } *((FARPROC*)&pGdipLoadImageFromStream) = GetProcAddress(g_hGdiPlus, "GdipLoadImageFromStream"); *((FARPROC*)&pGdipSaveImageToStream) = GetProcAddress(g_hGdiPlus, "GdipSaveImageToStream"); *((FARPROC*)&pGdipSaveImageToFile) = GetProcAddress(g_hGdiPlus, "GdipSaveImageToFile"); *((FARPROC*)&pGdipGetImageWidth) = GetProcAddress(g_hGdiPlus, "GdipGetImageWidth"); *((FARPROC*)&pGdipGetImageHeight) = GetProcAddress(g_hGdiPlus, "GdipGetImageHeight"); *((FARPROC*)&pGdipGetImagePixelFormat) = GetProcAddress(g_hGdiPlus, "GdipGetImagePixelFormat"); *((FARPROC*)&pGdipDisposeImage) = GetProcAddress(g_hGdiPlus, "GdipDisposeImage"); *((FARPROC*)&pGdiplusStartup) = GetProcAddress(g_hGdiPlus, "GdiplusStartup"); *((FARPROC*)&pGdipGetImageEncodersSize) = GetProcAddress(g_hGdiPlus, "GdipGetImageEncodersSize"); *((FARPROC*)&pGdipGetImageEncoders) = GetProcAddress(g_hGdiPlus, "GdipGetImageEncoders"); *((FARPROC*)&pGdiplusShutdown) = GetProcAddress(g_hGdiPlus, "GdiplusShutdown"); if(!pGdipLoadImageFromStream || !pGdipSaveImageToStream || !pGdipGetImageWidth || !pGdipGetImageHeight || !pGdipGetImagePixelFormat || !pGdipDisposeImage || !pGdiplusStartup || !pGdipGetImageEncodersSize || !pGdipGetImageEncoders || !pGdiplusShutdown) { wiauDbgError("InitializeGDIPlus", "Failed to retrieve all the entry points from GDIPLUS.DLL"); hr = E_FAIL; goto Cleanup; } if(Gdiplus::Ok != pGdiplusStartup(&g_GdiPlusToken, &g_GdiPlusStartupInput, NULL)) { wiauDbgError("InitializeGDIPlus", "GdiPlusStartup() failed"); hr = E_FAIL; goto Cleanup; } UINT num = 0; // number of image encoders UINT size = 0; // size of the image encoder array in bytes pGdipGetImageEncodersSize(&num, &size); if(size == 0) { wiauDbgError("InitializeGDIPlus", "GetImageEncodersSize() failed"); hr = E_FAIL; goto Cleanup; } pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size)); if(pImageCodecInfo == NULL) { wiauDbgError("InitializeGDIPlus", "failed to allocate encoders data"); hr = E_OUTOFMEMORY; goto Cleanup; } if(Gdiplus::Ok != pGdipGetImageEncoders(num, size, pImageCodecInfo)) { wiauDbgError("InitializeGDIPlus", "failed to retrieve encoders data"); hr = E_FAIL; goto Cleanup; } for(UINT j = 0; j < num; ++j) { if( pImageCodecInfo[j].FormatID == WiaImgFmt_BMP) { g_guidCodecBmp = pImageCodecInfo[j].Clsid; hr = S_OK; break; } } // for Cleanup: if(pImageCodecInfo) free(pImageCodecInfo); return hr; } void UnInitializeGDIPlus(void) { if(!pGdipLoadImageFromStream) return; if(pGdiplusShutdown) pGdiplusShutdown(g_GdiPlusToken); FreeLibrary(g_hGdiPlus); pGdipLoadImageFromStream = 0; } HRESULT LoadImageFromMemory(BYTE *pData, UINT CompressedDataSize, Gdiplus::GpImage **ppImage) { HRESULT hr = S_OK; if(pData == NULL || CompressedDataSize == 0 || ppImage == NULL) { return E_INVALIDARG; } if(!pGdipLoadImageFromStream) { hr = InitializeGDIPlus(); if(FAILED(hr)) { wiauDbgError("LoadImageFromMemory", "Failed to initialize GDI+"); return hr; } } CImageStream *pStream = new CImageStream(); if(!pStream) { wiauDbgError("LoadImageFromMemory", "Failed to create Image Stream"); return E_OUTOFMEMORY; } hr = pStream->SetBuffer(pData, CompressedDataSize); if(FAILED(hr)) { wiauDbgError("LoadImageFromMemory", "Failed to create Image Stream"); goto Cleanup; } if(Gdiplus::Ok == pGdipLoadImageFromStream(pStream, ppImage)) { hr = S_OK; } else { wiauDbgError("LoadImageFromMemory", "GDI+ failed to load image"); hr = E_FAIL; } Cleanup: if(pStream) pStream->Release(); return hr; } HRESULT DisposeImage(Gdiplus::GpImage **ppImage) { if(ppImage == NULL || *ppImage == NULL) { return E_INVALIDARG; } if(pGdipDisposeImage) { pGdipDisposeImage(*ppImage); } *ppImage = NULL; return S_OK; } HRESULT SaveImageToBitmap(Gdiplus::GpImage *pImage, BYTE *pBuffer, UINT BufferSize) { HRESULT hr = S_OK; CImageStream *pOutStream = new CImageStream; if(!pOutStream) { wiauDbgError("SaveImageToBitmap", "failed to allocate CImageStream"); hr = E_OUTOFMEMORY; goto Cleanup; } hr = pOutStream->SetBuffer(pBuffer, BufferSize, SKIP_OFF); if(FAILED(hr)) { wiauDbgError("SaveImageToBitmap", "failed to set output buffer"); goto Cleanup; } if(Gdiplus::Ok != pGdipSaveImageToStream(pImage, pOutStream, &g_guidCodecBmp, NULL)) { wiauDbgError("SaveImageToBitmap", "GDI+ save failed"); hr = E_FAIL; goto Cleanup; } Cleanup: if(pOutStream) { pOutStream->Release(); } return hr; } HRESULT WINAPI GetImageDimensions( UINT ptpFormatCode, BYTE *pCompressedData, UINT CompressedDataSize, UINT *pWidth, UINT *pHeight, UINT *pBitDepth ) { HRESULT hr = S_OK; if(pWidth) *pWidth = 0; if(pHeight) *pHeight = 0; if(pBitDepth) *pBitDepth = 0; // locate GUID for this particular format FORMAT_INFO *pFormatInfo = FormatCodeToFormatInfo((WORD) ptpFormatCode); if(pFormatInfo == NULL || pFormatInfo->FormatGuid == NULL || IsEqualGUID(WiaImgFmt_UNDEFINED, *pFormatInfo->FormatGuid)) { wiauDbgError("GetImageDimensions", "unrecoginzed PTP format code"); return E_INVALIDARG; } Gdiplus::GpImage *pImage = NULL; hr = LoadImageFromMemory(pCompressedData, CompressedDataSize, &pImage); if(FAILED(hr) || !pImage) { wiauDbgError("GetImageDimensions", "failed to create GDI+ image from supplied data."); return hr; } if(pWidth) pGdipGetImageWidth(pImage, pWidth); if(pHeight) pGdipGetImageHeight(pImage, pHeight); if(pBitDepth) { Gdiplus::PixelFormat pf = 0; pGdipGetImagePixelFormat(pImage, &pf); *pBitDepth = Gdiplus::GetPixelFormatSize(pf); } DisposeImage(&pImage); return hr; } HRESULT WINAPI ConvertAnyImageToBmp(BYTE *pCompressedImage, UINT CompressedSize, UINT *pWidth, UINT *pHeight, UINT *pBitDepth, BYTE **pDIBBmp, UINT *pImageSize, UINT *pHeaderSize ) { HRESULT hr = S_OK; Gdiplus::GpImage *pImage = NULL; Gdiplus::PixelFormat pf = 0; UINT headersize; UNALIGNED BITMAPINFOHEADER *pbi; UNALIGNED BITMAPFILEHEADER *pbf; if(!pCompressedImage || !CompressedSize || !pWidth || !pHeight || !pBitDepth || !pDIBBmp || !pImageSize || !pHeaderSize) { hr = E_INVALIDARG; goto Cleanup; } hr = LoadImageFromMemory(pCompressedImage, CompressedSize, &pImage); if(FAILED(hr) || !pImage) { wiauDbgError("ConvertAnyImageToBmp", "failed to create GDI+ image from supplied data."); goto Cleanup; } pGdipGetImageWidth(pImage, pWidth); pGdipGetImageHeight(pImage, pHeight); pGdipGetImagePixelFormat(pImage, &pf); *pBitDepth = Gdiplus::GetPixelFormatSize(pf); *pImageSize = ((*pWidth) * (*pBitDepth) / 8L) * *pHeight; headersize = 8192; // big enough to hold any bitmap header *pDIBBmp = new BYTE[*pImageSize + headersize]; if(!*pDIBBmp) { wiauDbgError("ConvertAnyImageToBmp", "failed to convert GDI+ image to bitmap."); hr = E_OUTOFMEMORY; goto Cleanup; } hr = SaveImageToBitmap(pImage, *pDIBBmp, *pImageSize + headersize); if(FAILED(hr)) { wiauDbgError("ConvertAnyImageToBmp", "failed to convert GDI+ image to bitmap."); goto Cleanup; } // find out real header size pbf = (BITMAPFILEHEADER *)*pDIBBmp; pbi = (BITMAPINFOHEADER *)(*pDIBBmp + sizeof(BITMAPFILEHEADER)); if(*pBitDepth == 8 && pbi->biClrUsed == 2) { // expand color table for bilevel images // (TWAIN apps don't understand 2 entry colortable (0,0,0)(1,1,1) UNALIGNED RGBQUAD *pRgb = (RGBQUAD *)((BYTE *)pbi + pbi->biSize); BYTE *src = (BYTE *)(pRgb + 2); BYTE *dst = (BYTE *)(pRgb + 256); int i; // negate and move image for(i = *pImageSize - 1; i >= 0; i--) { dst[i] = src[i] ? 255 : 0; } pbi->biClrUsed = 256; pbi->biClrImportant = 256; pRgb[0].rgbBlue = pRgb[0].rgbRed = pRgb[0].rgbGreen = 0; pRgb[0].rgbReserved = 0; for(i = 1; i < 256; i++) { pRgb[i].rgbReserved = 0; pRgb[i].rgbBlue = pRgb[i].rgbRed = pRgb[i].rgbGreen = 255; } pbf->bfOffBits = sizeof(BITMAPFILEHEADER) + pbi->biSize + sizeof(RGBQUAD) * 256; pbf->bfSize = pbf->bfOffBits + *pImageSize; } *pHeaderSize = pbf->bfOffBits; Cleanup: if(FAILED(hr)) { delete [] *pDIBBmp; *pDIBBmp = NULL; } DisposeImage(&pImage); return hr; }