Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

967 lines
29 KiB

/*++
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 <gdiplus.h>
#include <gdiplusflat.h>
#include <private.h>
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;
}