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
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;
|
|
}
|
|
|
|
|