Sample Code
Windows Driver Samples/ Windows Image Acquisition (WIA) Driver Samples/ C++/ wiadriverex/ imgfilter/ imagefilter.cpp/
/***************************************************************************** * * imagefilter.cpp * * Copyright (c) 2003 Microsoft Corporation. All Rights Reserved. * * DESCRIPTION: * * Contains implementation of Image Processing Filer with "filtering stream". * The implementation uses GDI+ for cutting out images, for deskewing as well * as for implementing brightness and contrast. * *******************************************************************************/ #include "stdafx.h" #include <gdiplus.h> #include <math.h> #include <objidl.h> #include "imagefilter.h" #include "wiaitem.h" #include "gphelper.h" using namespace Gdiplus; /***************************************************************************** * * @func STDMETHODIMP | DoFiltering | Reads unfiltered data from input stream, cuts, deskews, rotates and filters the image data * and then writes fitlered data to output stream. * * @parm LONG | lBrightness | * The brightness set into the region we are filtering. Should be between -1000 and 1000 * * @parm LONG | lContrast | * The contrast set into the region we are filtering. Should be between -1000 and 1000 * * @parm LONG | regionRotate | * How much we should rotate the reion (note rotate happens after deskew!) * * @parm LONG | regionDeskewX | * WIA_IPS_DESKEW_X for region to deskew (note 0 means no deskew) * * @parm LONG | regionDeskewY | * WIA_IPS_DESKEW_Y for region to deskew (note 0 means no deskew) * * @parm IStream* | pInputStream | * Unfiltered image data, either directly from driver of from WIA Preview Component * * * @parm IStream* | pOutputStream | * Application stream where we write image data * * @parm LONG | inputXPOS | * X-position of upper left corner of region to "cut-out" from image in pInputStream. * Note that this parameter is relative to image in pInputStream which is not necessarily * its X-position on the flatbed. * * @parm LONG | inputYPOS | * Y-position of upper left corner of region to "cut-out" from image in pInputStream. * Note that this parameter is relative to image in pInputStream which is not necessarily * its Y-position on the flatbed. * * @parm LONG | boundingRegionWidth | * Width of bounding area to "cut-out" from pInputStream. A value of 0 means that we should not perform * any cutting, but instead filter the whole image. * boundingRegionWidth will be set to 0 when we receive the image data from the driver since the driver * will only send us the bounding rectangle of the selected region and not the entire flatbed. * Note: if there is not deskewing being performed this is the "actual" width of the region. * * @parm LONG | boundingRegionHeight | * Height of bounding area to "cut-out" from pInputStream. A value of 0 means that we should not perform * any cutting, but instead filter the whole image. * boundingRegionHeight will be set to 0 when we receive the image data from the driver since the driver * will only send us the bounding rectangle of the selected region and not the entire flatbed. * Note: if there is not deskewing being performed this is the "actual" height of the region. * * @comm * Note, our simple implementation of DoFiltering always write all the data * in one chunk to the application. An actual image processing filter should * be able to work on bands of data in the case where there is no rotation * or deskewing being performed. * This implementation also does not send callbacks (TransferCallback) messages * to the application indicating progress. A "real" implementation should do that! * * @rvalue S_OK | * The function succeeded. * @rvalue E_XXX | * The function failed * *****************************************************************************/ static HRESULT DoFiltering( LONG lBrightness, LONG lContrast, LONG regionRotate, LONG regionDeskewX, LONG regionDeskewY, _In_ IStream *pInputStream, _In_ IStream *pOutputStream, _Inout_ ULONG64 *pulBytesWrittenToOutputStream, LONG inputXPOS = 0, LONG inputYPOS = 0, LONG boundingRegionWidth = 0, LONG boundingRegionHeight = 0 ) { HRESULT hr = S_OK; Bitmap *pOriginalBitmap = NULL; Bitmap *pTargetBitmap = NULL; CLSID formatEncoder = {0}; GdiplusStartupInput gdiplusStartupInput; ULONG_PTR ulImageLibraryToken = 0; if (SUCCEEDED(hr)) { hr = GDISTATUS_TO_HRESULT(GdiplusStartup(&ulImageLibraryToken, &gdiplusStartupInput, NULL)); } // // Create a Bitmap object on the unfiltered input stream // if (SUCCEEDED(hr)) { #pragma prefast(suppress:__WARNING_ALIASED_MEMORY_LEAK_EXCEPTION, "Sample code only. Production code should handle exceptions.") pOriginalBitmap = new Bitmap(pInputStream, TRUE); if (pOriginalBitmap) { hr = GDISTATUS_TO_HRESULT(pOriginalBitmap->GetLastStatus()); } else { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { hr = GDISTATUS_TO_HRESULT(GetEncoderGUIDFromImage(pOriginalBitmap, &formatEncoder)); } } // // If boundingRegionWidth or boundingRegionHeight is 0, this means that we should not perform any // "cutting" but instead just filter the whole input image. // if (SUCCEEDED(hr)) { if ((0 == boundingRegionWidth) || (0 == boundingRegionHeight)) { inputXPOS = 0; inputYPOS = 0; boundingRegionWidth = pOriginalBitmap->GetWidth(); boundingRegionHeight = pOriginalBitmap->GetHeight(); } } // // Perform filtering. This is done in 3 steps: // 1. Create a new bitmap with the dimensions of the final, filtered image. // 2. "Cut-out" and deskew final image from full image. This is done by a translate // followed by a rotate transformtation. // 3. Apply color matrix to to perform brightness and contrast modifications. // if (SUCCEEDED(hr)) { PixelFormat originalPixelFormat = pOriginalBitmap->GetPixelFormat(); double dblDeskewAngle = 0.0; LONG lXdelta = 0; LONG lYdelta = 0; LONG lActualRegionWidth = 0; LONG lActualRegionHeight = 0; // // No deskew, just cut out a rectangular area! // if ((regionDeskewX) == 0 || (regionDeskewY == 0)) { lActualRegionWidth = boundingRegionWidth; lActualRegionHeight = boundingRegionHeight; dblDeskewAngle = 0.0; } else { if (regionDeskewX > regionDeskewY) { lYdelta = regionDeskewY; dblDeskewAngle = atan2((double)regionDeskewY, (double)regionDeskewX); lActualRegionWidth = (LONG) sqrt((double) (regionDeskewX * regionDeskewX + regionDeskewY * regionDeskewY)); lActualRegionHeight = (LONG) (((double) (boundingRegionHeight - regionDeskewY)) / cos(dblDeskewAngle)); } else { lXdelta = regionDeskewX; dblDeskewAngle = atan2((double)regionDeskewX, (double)regionDeskewY); lActualRegionWidth = (LONG) (((double) (boundingRegionWidth - regionDeskewX)) / cos(dblDeskewAngle)); lActualRegionHeight = (LONG) sqrt((double) (regionDeskewX * regionDeskewX + regionDeskewY * regionDeskewY)); dblDeskewAngle = -dblDeskewAngle; } } pTargetBitmap = new Bitmap(lActualRegionWidth, lActualRegionHeight, originalPixelFormat); if (pTargetBitmap) { hr = GDISTATUS_TO_HRESULT(pTargetBitmap->GetLastStatus()); } else { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { Graphics graphics(pTargetBitmap); ImageAttributes imageAttributes; hr = GDISTATUS_TO_HRESULT(graphics.GetLastStatus()); if (SUCCEEDED(hr)) { graphics.TranslateTransform((REAL)-(inputXPOS + lXdelta), (REAL)-(inputYPOS + lYdelta)); hr = GDISTATUS_TO_HRESULT(graphics.GetLastStatus()); } if (dblDeskewAngle != 0.0) { if (SUCCEEDED(hr)) { graphics.RotateTransform((REAL)(dblDeskewAngle * 180.0 / PI), MatrixOrderAppend); hr = GDISTATUS_TO_HRESULT(graphics.GetLastStatus()); } } if (SUCCEEDED(hr)) { // // Calculate the values needed for the matrix // REAL scale = 0.0; REAL trans = 0.0; // // Normalize brightness and contrast to 0 to 1000. // This assumes valid settings are - 1000 to 1000. // CalculateBrightnessAndContrastParams( (lBrightness + 1000) /2, (lContrast + 1000) / 2, &scale, &trans ); // // Prepare the matrix for brightness and contrast transforms // ColorMatrix brightnessAndContrast = {scale, 0, 0, 0, 0, 0, scale, 0, 0, 0, 0, 0, scale, 0, 0, 0, 0, 0, 1, 0, trans, trans, trans, 0, 1}; hr = imageAttributes.SetColorMatrix(&brightnessAndContrast); } if (SUCCEEDED(hr)) { UINT uWidth = pOriginalBitmap->GetWidth(); UINT uHeight = pOriginalBitmap->GetHeight(); Rect rect( 0, 0, uWidth, uHeight ); hr = GDISTATUS_TO_HRESULT(graphics.DrawImage(pOriginalBitmap,rect,0,0,uWidth, uHeight,UnitPixel,&imageAttributes)); } } } // // The last step for us to perform is rotating the region // if (SUCCEEDED(hr) && (regionRotate != PORTRAIT)) { RotateFlipType rotateFlipType; switch (regionRotate) { case LANSCAPE: rotateFlipType = Rotate270FlipNone; break; case ROT180: rotateFlipType = Rotate180FlipNone; break; case ROT270: rotateFlipType = Rotate90FlipNone; break; default: // // We should never get here! // rotateFlipType = RotateNoneFlipNone; } hr = GDISTATUS_TO_HRESULT(pTargetBitmap->RotateFlip(rotateFlipType)); } // // The GDI+ Bitmap::Save method does not work very well for images that // an application displays band by band since it results in a large number // of small Write calls. Instead we do a LockBits to read the bits from // the bitmap and then write them to the application's stream. // if (SUCCEEDED(hr)) { hr = WriteBitmapToStream(pTargetBitmap, pOutputStream, pulBytesWrittenToOutputStream); } if (pOriginalBitmap) { delete pOriginalBitmap; } if (pTargetBitmap) { delete pTargetBitmap; } if(ulImageLibraryToken) { GdiplusShutdown(ulImageLibraryToken); ulImageLibraryToken = 0; } return hr; } /******************************************************************************* Routine Name: ConvertBMPImageToRaw Routine Description: Converts an uncompressed 24-bpp RGB BMP image Stream to a RAW Stream Arguments: input Stream Return Value: Output Stream if conversion successful HRESULT (S_OK in case the operation succeeds) *******************************************************************************/ HRESULT ConvertBMPImageToRaw(IStream * pStreamIn, IStream *pStreamOut, ULONG64 * pcbWritten = NULL) { HRESULT hr = S_OK; ULONG ulRead = 0, ulWrite = 0; WIA_RAW_HEADER RawHeader = {0}; BITMAPFILEHEADER bmfh = {0}; BITMAPINFOHEADER bmih = {0}; if (!pStreamIn || !pStreamOut) { hr = E_POINTER; } // // Seek to the beginning of Input Stream // if (SUCCEEDED(hr)) { if (pcbWritten) { *pcbWritten = (ULONG64)0; } LARGE_INTEGER li = {0}; hr = pStreamIn->Seek(li, STREAM_SEEK_SET, NULL); } // // Attempt to read the Bitmap File Header // if (SUCCEEDED(hr)) { hr = pStreamIn->Read(&bmfh, sizeof(bmfh), &ulRead); if (SUCCEEDED(hr)) { if (ulRead != sizeof(bmfh)) { hr = E_FAIL; } } } // // Attempt to read the Bitmap File Header // if (SUCCEEDED(hr)) { hr = pStreamIn->Read(&bmih, sizeof(bmih), &ulRead); if (SUCCEEDED(hr)) { if (ulRead != sizeof(bmih)) { hr = E_FAIL; } } } // // todo: check the bitmap info header and bitmap file header for validity // if (SUCCEEDED(hr)) { // // The 'WRAW' 4 ASCII character signature is required at the begining of all WIA Raw transfers: // const char szSignature[] = "WRAW"; memcpy(&RawHeader.Tag, szSignature, sizeof(DWORD)); // // Fill in the fields describing version identity for this header: // RawHeader.Version = 0x00010000; RawHeader.HeaderSize = sizeof(WIA_RAW_HEADER); // // Fill in all the fields that we can retrieve directly from the current MINIDRV_TRANSFER_CONTEXT: // // // Resolution values must be converted to DPI (pixels per inch) from pixels per meter: // // (1" = 25.4 mm, 1 m ~ 39.37") // RawHeader.XRes = (LONG)((float)bmih.biXPelsPerMeter / 39.37f); RawHeader.YRes = (LONG)((float)bmih.biYPelsPerMeter / 39.37f); RawHeader.XExtent = bmih.biWidth; RawHeader.YExtent = bmih.biHeight; RawHeader.BytesPerLine = bmih.biWidth * 3; RawHeader.BitsPerPixel = bmih.biBitCount; RawHeader.ChannelsPerPixel = 3; RawHeader.DataType = WIA_DATA_RAW_RGB; ZeroMemory(RawHeader.BitsPerChannel, sizeof(RawHeader.BitsPerChannel)); RawHeader.BitsPerChannel[0] = 8; RawHeader.BitsPerChannel[1] = 8; RawHeader.BitsPerChannel[2] = 8; RawHeader.Compression = WIA_COMPRESSION_NONE; RawHeader.PhotometricInterp = bmih.biSizeImage; RawHeader.LineOrder = WIA_LINE_ORDER_BOTTOM_TO_TOP; // // Raw data: the offset is the size of the header (we don't have a color palette in this case): // RawHeader.RawDataOffset = RawHeader.HeaderSize; RawHeader.RawDataSize = bmih.biSizeImage; RawHeader.PaletteSize = 0; RawHeader.PaletteOffset = 0; } // // Save the RawHeader: Dont Seek // if (SUCCEEDED(hr)) { hr = pStreamOut->Write(&RawHeader, sizeof(RawHeader), &ulWrite); if (SUCCEEDED(hr)) { if (pcbWritten) { (*pcbWritten)+= (ULONG64)ulWrite; } if (ulWrite != sizeof(RawHeader)) { hr = E_FAIL; } } } // // Save the DIB data: Read from in stream and write to out stream // ULONG ulBufferSize = 100000; // approx 100 KB BYTE *pbImageData = NULL; if (SUCCEEDED(hr)) { pbImageData = (BYTE *)malloc(ulBufferSize); if (!pbImageData) { hr = E_OUTOFMEMORY; } // // memory allocated. now copy // while(S_OK == hr) { hr = pStreamIn->Read(pbImageData, ulBufferSize, &ulRead); if (SUCCEEDED(hr)) { hr = pStreamOut->Write(pbImageData, ulRead, &ulWrite); if (SUCCEEDED(hr)) { if (pcbWritten) { (*pcbWritten)+= (ULONG64)ulWrite; } if (ulRead != ulWrite) { hr = E_FAIL; } } } if (ulRead != ulBufferSize) { break; } } } // // Clean-up: // if (pbImageData) { free(pbImageData); pbImageData = NULL; } return hr; } /******************************************************************************* Routine Name: ConvertRawImageToBMP Routine Description: Converts an uncompressed 24-bpp RGB raw image Stream to a DIB Stream Arguments: input Stream Return Value: Output Stream if conversion successful HRESULT (S_OK in case the operation succeeds) *******************************************************************************/ HRESULT ConvertRawImageToBMP(IStream * pStreamIn, IStream **ppStreamOut, ULONG64 * pcbWritten = NULL) { HRESULT hr = S_OK; ULONG ulRead = 0, ulWrite = 0; IStream *pStreamOut = NULL; WIA_RAW_HEADER RawHeader = {0}; BITMAPFILEHEADER bmfh = {0}; BITMAPINFOHEADER bmih = {0}; if (!pStreamIn || !ppStreamOut) { hr = E_POINTER; } if (SUCCEEDED(hr)) { if(pcbWritten) { *pcbWritten = (ULONG64)0; } (*ppStreamOut) = NULL; // // Seek to the beginning of Input Stream // LARGE_INTEGER li = {0}; hr = pStreamIn->Seek(li, STREAM_SEEK_SET, NULL); } // // Attempt to read the WIA_RAW_HEADER: // if (SUCCEEDED(hr)) { hr = pStreamIn->Read(&RawHeader, sizeof(WIA_RAW_HEADER), &ulRead); if (SUCCEEDED(hr)) { if (ulRead != sizeof(WIA_RAW_HEADER)) { hr = E_FAIL; } } } // // Verify the WIA raw header signature: // if (SUCCEEDED(hr)) { const char szSignature[] = "WRAW"; if (memcmp(&RawHeader.Tag, szSignature, sizeof(DWORD))) { hr = E_FAIL; } } // // Verify the WIA raw header reported size and version number: // if (SUCCEEDED(hr)) { if ((0x00010000 != RawHeader.Version) || (sizeof(WIA_RAW_HEADER) != RawHeader.HeaderSize)) { hr = E_FAIL; } } // // Verify if the raw image format - this sample supports only uncompressed 24-bpp RGB data: // if (SUCCEEDED(hr)) { if ((WIA_COMPRESSION_NONE != RawHeader.Compression) || (24 != RawHeader.BitsPerPixel) || (3 != RawHeader.ChannelsPerPixel) || (RawHeader.PaletteSize) || (8 != RawHeader.BitsPerChannel[0]) || (8 != RawHeader.BitsPerChannel[1]) || (8 != RawHeader.BitsPerChannel[2])) { hr = E_FAIL; } } // // Build the BITMAPFILEHEADER and the BITMAPINFOHEADER structures needed // to convert the raw uncompressed 24-bpp RGB image to a DIB: // if (SUCCEEDED(hr)) { // // BITMAPFILEHEADER: // const char szBM[] = "BM"; memcpy(&bmfh.bfType, szBM, sizeof(WORD)); bmfh.bfSize = sizeof(BITMAPFILEHEADER); bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // // BITMAPINFOHEADER: // bmih.biSize = sizeof(BITMAPINFOHEADER); bmih.biWidth = RawHeader.XExtent; bmih.biHeight = (WIA_LINE_ORDER_BOTTOM_TO_TOP == RawHeader.LineOrder) ? ((LONG)RawHeader.YExtent) : (-(LONG)RawHeader.YExtent); bmih.biPlanes = 1; bmih.biBitCount = (WORD)RawHeader.BitsPerPixel; bmih.biCompression = BI_RGB; bmih.biSizeImage = RawHeader.RawDataSize; bmih.biClrUsed = 0; bmih.biClrImportant = 0; // // Resolution values must be converted from DPI (pixels per inch) to pixels per meter: // // (1" = 25.4 mm, 1 m ~ 39.37") // bmih.biXPelsPerMeter = (LONG)((float)RawHeader.XRes * 39.37f); bmih.biYPelsPerMeter = (LONG)((float)RawHeader.YRes * 39.37f); } if (SUCCEEDED(hr)) { hr = CreateStreamOnHGlobal(0, TRUE, &pStreamOut); } // // Save the BITMAPFILEHEADER: // if (SUCCEEDED(hr)) { hr = pStreamOut->Write(&bmfh, bmfh.bfSize, &ulWrite); if (SUCCEEDED(hr)) { if (pcbWritten) { (*pcbWritten)+= (ULONG64)ulWrite; } if (ulWrite != bmfh.bfSize) { hr = E_FAIL; } } } // // Save the BITMAPINFOHEADER: // if (SUCCEEDED(hr)) { hr = pStreamOut->Write(&bmih, bmih.biSize, &ulWrite); if (SUCCEEDED(hr)) { if (pcbWritten) { (*pcbWritten)+= (ULONG64)ulWrite; } if (ulWrite != bmih.biSize) { hr = E_FAIL; } } } // // Save the DIB data: Read from in stream and write to out stream // ULONG ulBufferSize = 100000; // approx 100 KB BYTE *pbImageData = NULL; if (SUCCEEDED(hr)) { pbImageData = (BYTE *)malloc(ulBufferSize); if (!pbImageData) { hr = E_OUTOFMEMORY; } // // memory allocated. now copy // while(S_OK == hr) { hr = pStreamIn->Read(pbImageData, ulBufferSize, &ulRead); if (SUCCEEDED(hr)) { hr = pStreamOut->Write(pbImageData, ulRead, &ulWrite); if (SUCCEEDED(hr)) { if (pcbWritten) { (*pcbWritten)+= (ULONG64)ulWrite; } if (ulRead != ulWrite) { hr = E_FAIL; } } } if (ulRead != ulBufferSize) { break; } } } // // Clean-up: // if (pbImageData) { free(pbImageData); pbImageData = NULL; } if (SUCCEEDED(hr)) { // // Seek to the beginning of Output Stream (We can do this since we created the stream) // LARGE_INTEGER li = {0}; hr = pStreamOut->Seek(li, STREAM_SEEK_SET, NULL); } if (pStreamOut) { if (SUCCEEDED(hr)) { *ppStreamOut = pStreamOut; } else { pStreamOut->Release(); } } return hr; } /***************************************************************************** * * CMyFilterStream is our implemetation of the filtering stream. * The only IStream method that it implements is Write(). * * The stream keeps a reference to the applications stream into which it writes * the filtered data (the header is not modified however). * *******************************************************************************/ /// /// Constructor - note sets reference count to 1 /// CMyFilterStream::CMyFilterStream( VOID) : m_pAppStream(NULL) , m_pCachingStream(NULL), m_nRefCount(0), m_cBytesWritten(0), m_lBrightness(0), m_lContrast(0), m_lRotation(0), m_lDeskewX(0), m_lDeskewY(0) { // // Note: Do not initialize refcount to 1 as it will break module locking, instead call AddRef() // AddRef(); } /// /// Destructor: /// CMyFilterStream::~CMyFilterStream( VOID) { if (m_pAppStream) { m_pAppStream->Release(); m_pAppStream = NULL; } if (m_pCachingStream) { m_pCachingStream->Release(); m_pCachingStream = NULL; } } /// /// Initilize stores a reference to the application's stream. It also creates /// its own stream with CreateStreamOnHGlobal into which it stores all the /// unfiltered image data before it performs its filtering (in Flush). /// HRESULT CMyFilterStream::Initialize( _In_ IStream *pAppStream, LONG lBrightness, LONG lContrast, LONG lRotation, LONG lDeskewX, LONG lDeskewY, LONG lXExtent, LONG lYExtent, LONG lBitDepth, GUID guidFormat) { UNREFERENCED_PARAMETER(lXExtent); UNREFERENCED_PARAMETER(lYExtent); UNREFERENCED_PARAMETER(lBitDepth); HRESULT hr = S_OK; hr = pAppStream ? S_OK : E_INVALIDARG; if (SUCCEEDED(hr)) { m_pAppStream = pAppStream; m_pAppStream->AddRef(); } if (SUCCEEDED(hr)) { hr = CreateStreamOnHGlobal(0, TRUE, &m_pCachingStream); } if (SUCCEEDED(hr)) { m_lBrightness = lBrightness; m_lContrast = lContrast; m_lRotation = lRotation; m_lDeskewX = lDeskewX; m_lDeskewY = lDeskewY; m_guidFormat = guidFormat; } return hr; } /***************************************************************************** * * @func STDMETHODIMP | CMyFilterStream::Flush | Reads unfiltered data, performs filtering and writes * data to application stream * * @comm * * Flush is called when the image processing filter receives a WIA_TRANSFER_MSG_END_OF_STREAM message. * Flush calls DoFiltering where the actual filtering is done. * * Note that this simple implementation performs all its filtering only after it has received all * unfiltered image data and stored it in m_pCachingStream. A "real" implementation should be able * to work on bands of data (at least if no deskew and rotation has to be performed). * * @rvalue S_OK | * The function succeeded. * @rvalue E_XXX | * The function failed * *****************************************************************************/ HRESULT CMyFilterStream::Flush( VOID) { HRESULT hr = S_OK; IStream * tempStream = NULL; IStream * tempOutStream = NULL; if(IsEqualGUID(m_guidFormat, WiaImgFmt_RAW)) { if (SUCCEEDED(hr)) { hr = ConvertRawImageToBMP(m_pCachingStream, &tempStream); if (SUCCEEDED(hr) && tempStream) { hr = CreateStreamOnHGlobal(0, TRUE, &tempOutStream); if (SUCCEEDED(hr) && tempOutStream) { ULONG64 ulDummy = 0; hr = DoFiltering(m_lBrightness, m_lContrast, m_lRotation, m_lDeskewX, m_lDeskewY, tempStream, tempOutStream, &ulDummy); if (SUCCEEDED(hr)) { hr = ConvertBMPImageToRaw(tempOutStream, m_pAppStream, &m_cBytesWritten); } tempOutStream->Release(); } tempStream->Release(); } } } else { hr = DoFiltering( m_lBrightness, m_lContrast, m_lRotation, m_lDeskewX, m_lDeskewY, m_pCachingStream, m_pAppStream, &m_cBytesWritten); } // // Note: m_pAppStream and m_pCachingStream are released by ReleaseStreams which must be always called after Flush // return hr; } /// /// Query Interface /// STDMETHODIMP CMyFilterStream::QueryInterface(_In_ const IID& iid_requested, _Out_ void** ppInterfaceOut) { HRESULT hr = S_OK; hr = ppInterfaceOut ? S_OK : E_POINTER; if (SUCCEEDED(hr)) { *ppInterfaceOut = NULL; } // // We support IID_IUnknown and IID_IStream // if (SUCCEEDED(hr)) { if (IID_IUnknown == iid_requested) { *ppInterfaceOut = static_cast<IUnknown*>(this); } else if (IID_IStream == iid_requested) { *ppInterfaceOut = static_cast<IStream*>(this); } else { hr = E_NOINTERFACE; } } if (SUCCEEDED(hr)) { reinterpret_cast<IUnknown*>(*ppInterfaceOut)->AddRef(); } return hr; } /// /// AddRef /// STDMETHODIMP_(ULONG) CMyFilterStream::AddRef(void) { if (m_nRefCount == 0) { LockModule(); } return InterlockedIncrement(&m_nRefCount); } /// /// Release /// STDMETHODIMP_(ULONG) CMyFilterStream::Release(void) { ULONG nRetval = InterlockedDecrement(&m_nRefCount); if (0 == nRetval) { delete this; UnlockModule(); } return nRetval; } STDMETHODIMP CMyFilterStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, _Out_ ULARGE_INTEGER *plibNewPosition) { return m_pCachingStream->Seek(dlibMove,dwOrigin,plibNewPosition); } STDMETHODIMP CMyFilterStream::SetSize(ULARGE_INTEGER libNewSize) { return m_pCachingStream->SetSize(libNewSize); } STDMETHODIMP CMyFilterStream::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return m_pCachingStream->LockRegion(libOffset,cb,dwLockType); } STDMETHODIMP CMyFilterStream::CopyTo(_In_ IStream *pstm, ULARGE_INTEGER cb, _Out_ ULARGE_INTEGER *pcbRead, _Out_ ULARGE_INTEGER *pcbWritten) { return m_pCachingStream->CopyTo(pstm,cb,pcbRead,pcbWritten); } STDMETHODIMP CMyFilterStream::Commit(DWORD grfCommitFlags) { return m_pCachingStream->Commit(grfCommitFlags); } STDMETHODIMP CMyFilterStream::Revert(void) { return m_pCachingStream->Revert(); } STDMETHODIMP CMyFilterStream::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { return m_pCachingStream->UnlockRegion(libOffset,cb,dwLockType); } STDMETHODIMP CMyFilterStream::Stat(_Out_ STATSTG *pstatstg, DWORD grfStatFlag) { return m_pCachingStream->Stat(pstatstg, grfStatFlag); } STDMETHODIMP CMyFilterStream::Clone(_Out_ IStream **ppstm) { return m_pCachingStream->Clone(ppstm); } STDMETHODIMP CMyFilterStream::Read(_Out_ void *pv, ULONG cb, _Out_ ULONG *pcbRead) { return m_pCachingStream->Read(pv,cb,pcbRead); } STDMETHODIMP CMyFilterStream::ReleaseStreams() { if (m_pAppStream) { m_pAppStream->Release(); m_pAppStream = NULL; } if (m_pCachingStream) { m_pCachingStream->Release(); m_pCachingStream = NULL; } return S_OK; } /***************************************************************************** * * @func STDMETHODIMP | CMyFilterStream::Write | Filtering streams implementation of Write * * @parm const void * | pv | * Pointer to the memory buffer. * * @parm ULONG | cb | * Specifies the number of bytes of data to write from the stream object. * * @parm ULONG | pcbWritten | * Pointer to a ULONG variable that receives the actual number of bytes written from the stream object. * * @comm * Write simply writes unfiltered data from the driver into its internal caching stream. * * @rvalue S_OK | * The function succeeded. * @rvalue E_XXX | * The function failed * *****************************************************************************/ STDMETHODIMP CMyFilterStream::Write(_In_ const void *pv, ULONG cb, _Out_ ULONG *pcbWritten) { return m_pCachingStream->Write(pv, cb, pcbWritten); } /// /// Constructor /// CImageFilter::CImageFilter( VOID) : m_pWiaItem(NULL), m_pAppWiaTransferCallback(NULL), m_nRefCount(0), m_pCurrentStream(NULL) { // // Nothing // } /// /// Destructor /// CImageFilter::~CImageFilter( VOID) { if (m_pWiaItem) { m_pWiaItem->Release(); m_pWiaItem = NULL; } if (m_pAppWiaTransferCallback) { m_pAppWiaTransferCallback->Release(); m_pAppWiaTransferCallback = NULL; } } /// /// QueryInterface /// STDMETHODIMP CImageFilter::QueryInterface(_In_ const IID& iid_requested, _Out_ void** ppInterfaceOut) { HRESULT hr = S_OK; hr = ppInterfaceOut ? S_OK : E_POINTER; if (SUCCEEDED(hr)) { *ppInterfaceOut = NULL; } // // We support IID_IUnknown, IID_IWiaImageFilter and IID_IWiaTransferCallback // if (SUCCEEDED(hr)) { if (IID_IUnknown == iid_requested) { *ppInterfaceOut = static_cast<IWiaImageFilter*>(this); } else if (IID_IWiaImageFilter == iid_requested) { *ppInterfaceOut = static_cast<IWiaImageFilter*>(this); } else if (IID_IWiaTransferCallback == iid_requested) { *ppInterfaceOut = static_cast<IWiaTransferCallback*>(this); } else { hr = E_NOINTERFACE; } } if (SUCCEEDED(hr)) { reinterpret_cast<IUnknown*>(*ppInterfaceOut)->AddRef(); } return hr; } /// /// AddRef /// STDMETHODIMP_(ULONG) CImageFilter::AddRef(void) { if (m_nRefCount == 0) { LockModule(); } return InterlockedIncrement(&m_nRefCount); } /// /// Release /// STDMETHODIMP_(ULONG) CImageFilter::Release(void) { ULONG nRetval = InterlockedDecrement(&m_nRefCount); if (0 == nRetval) { delete this; UnlockModule(); } return nRetval; } /***************************************************************************** * * @func STDMETHODIMP | CImageFilter::InitializeFilter | Initializes image processing filter * * @parm IWiaItem2 | pWiaItem | * The WIA item we are doing the download for. This will actually be the parent item * for some of the item we acquire the image for. See implementation of GetNextStream * for more details. * * @parm IWiaTransferCallback | pWiaTransferCallback | * Application's callback function * * @comm * Initializes image processing filter. Stores references to applications callback interface * and IWiaItem2 * * @rvalue S_OK | * The function succeeded. * @rvalue E_XXX | * The function failed * *****************************************************************************/ STDMETHODIMP CImageFilter::InitializeFilter( _In_ IN IWiaItem2 *pWiaItem, __callback IN IWiaTransferCallback *pWiaTransferCallback) { HRESULT hr = S_OK; m_bTransferCancelled = FALSE; hr = (pWiaItem && pWiaTransferCallback) ? S_OK : E_INVALIDARG; // // Image processing filters supplied with WIA drivers do not // support storage items. // if (SUCCEEDED(hr)) { GUID guidItemCategory = {0}; hr = pWiaItem->GetItemCategory(&guidItemCategory); if (SUCCEEDED(hr)) { if ((WIA_CATEGORY_FINISHED_FILE == guidItemCategory) || (WIA_CATEGORY_FOLDER == guidItemCategory) || (WIA_CATEGORY_ROOT == guidItemCategory)) { hr = E_NOTIMPL; } } } // // InitializeFilter should only be called once ... but we still Release // any resources we might reference // if (SUCCEEDED(hr)) { if (m_pWiaItem) { m_pWiaItem->Release(); m_pWiaItem = NULL; } if (m_pAppWiaTransferCallback) { m_pAppWiaTransferCallback->Release(); m_pAppWiaTransferCallback = NULL; } } if (SUCCEEDED(hr)) { m_pWiaItem = pWiaItem; m_pWiaItem->AddRef(); m_pAppWiaTransferCallback = pWiaTransferCallback; m_pAppWiaTransferCallback->AddRef(); } return hr; } /***************************************************************************** * * @func STDMETHODIMP | CImageFilter::SetNewCallback | Sets new callback for image processing filter to use * * @parm IWiaTransferCallback | pWiaTransferCallback | * The new application callback which the filter should use. * * @comm * Since an application can change the callback to use in the IWiaPreview::UpdatePreview call the image * processing filter must "get notified" of this. * Note, the image processing filter is always required to release its current callback even if it is * passed NULL for the callback. * * @rvalue S_OK | * The function succeeded. * @rvalue E_XXX | * The function failed * *****************************************************************************/ STDMETHODIMP CImageFilter::SetNewCallback( _In_opt_ __callback IN IWiaTransferCallback *pWiaTransferCallback) { if (m_pAppWiaTransferCallback) { m_pAppWiaTransferCallback->Release(); m_pAppWiaTransferCallback = NULL; } if (pWiaTransferCallback) { m_pAppWiaTransferCallback = pWiaTransferCallback; m_pAppWiaTransferCallback->AddRef(); } return S_OK; } /***************************************************************************** * * @func STDMETHODIMP | CImageFilter::FilterPreviewImage | FilterPreviewImage implementation * * @parm IWiaItem2 | pWiaChildItem | * pWiaChildItem2 is the item which the image process is to process. * This item must be a child item of the item, m_pWiaItem, that was passed into InitializeFilter. * * @parm RECT | InputImageExtents | * The coordinates (on the flatbed scanner) of the image that the preview component caches internally, * which is also the image that is passed into the pInputStream parameter. * We need this parameter since it is possible that the cached image (pInputStream) was not captured * with XPOS=YPOS=0. * * @parm IStream | pInputStream | * Unfiltered image that is stored by WIA Preview Component. * * @comm * FilterPreviewImage is called by the preview component, when an application calls UpdatePreview. * We simply read all the properties from pWiaChildItem that are required for us to do the filtering * and then retrieve the application stream. The actual filtering is then performed in DoFiltering. * * @rvalue S_OK | * The function succeeded. * @rvalue E_XXX | * The function failed * *****************************************************************************/ STDMETHODIMP CImageFilter::FilterPreviewImage( IN LONG lFlags, _In_ IN IWiaItem2 *pWiaChildItem, IN RECT InputImageExtents, _In_ IN IStream *pInputStream) { UNREFERENCED_PARAMETER(lFlags); IStream *pAppStream = NULL; BSTR bstrItemName = NULL; BSTR bstrFullItemName = NULL; GUID guidItemCategory = {0}; LONG xpos = 0, ypos = 0, width = 0, height = 0; LONG lBrightness = 0; LONG lContrast = 0; LONG lDeskewX = 0; LONG lDeskewY = 0; LONG lRotation = PORTRAIT; HRESULT hr = S_OK; IStream * tempStream = NULL; IStream * tempOutStream = NULL; BOOL bConvertedRawImage = FALSE; ULONG64 ulBytesWrittenToOutputStream = 0; // // Parameter validation // hr = (pWiaChildItem && pInputStream) ? S_OK : E_INVALIDARG; if (SUCCEEDED(hr)) { // // Check whether the image extents are correct. // Error if the right or bottom coordinate is zero. // Or Left >= Right or Top >= Bottom. // if ((0 == InputImageExtents.right) || (0 == InputImageExtents.bottom) || (InputImageExtents.left >= InputImageExtents.right) || (InputImageExtents.top >= InputImageExtents.bottom)) { hr = E_INVALIDARG; } } if (SUCCEEDED(hr)) { hr = m_pAppWiaTransferCallback ? S_OK : E_UNEXPECTED; } // // Read all properties we need // if (SUCCEEDED(hr)) { CWiaItem *pWiaItemWrapper = new CWiaItem(); hr = pWiaItemWrapper ? S_OK : E_OUTOFMEMORY; if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->SetIWiaItem(pWiaChildItem); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyGUID(WIA_IPA_ITEM_CATEGORY, &guidItemCategory); if (SUCCEEDED(hr) && ((guidItemCategory == WIA_CATEGORY_ROOT) || (guidItemCategory == WIA_CATEGORY_FINISHED_FILE) || (WIA_CATEGORY_FOLDER == guidItemCategory))) { // // We should never get here for storage items! // hr = E_INVALIDARG; } } // // Error if the following is not satisfied: // WIA_IPS_MIN_HORIZONTAL_SIZE <= right - left <= WIA_IPS_MAX_HORIZONTAL_SIZE // WIA_IPS_MIN_VERTICAL_SIZE <= bottom - top <= WIA_IPS_MAX_VERTICAL_SIZE // if (SUCCEEDED(hr)) { LONG lHorMin = 0, lHorMax = 0, lHorExtent = InputImageExtents.right - InputImageExtents.left; LONG lVerMin = 0, lVerMax = 0, lVerExtent = InputImageExtents.bottom - InputImageExtents.top; if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_MIN_HORIZONTAL_SIZE, &lHorMin); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_MAX_HORIZONTAL_SIZE, &lHorMax); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_MIN_VERTICAL_SIZE, &lVerMin); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_MAX_VERTICAL_SIZE, &lVerMax); } if (SUCCEEDED(hr)) { if ((lHorExtent < lHorMin) || (lHorExtent > lHorMax) || (lVerExtent < lVerMin) || (lVerExtent > lVerMax)) { hr = E_INVALIDARG; } } } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_XPOS, &xpos); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_YPOS, &ypos); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_XEXTENT, &width); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_YEXTENT, &height); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyBSTR(WIA_IPA_ITEM_NAME, &bstrItemName); if (SUCCEEDED(hr) && !bstrItemName) { hr = E_UNEXPECTED; } } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyBSTR(WIA_IPA_FULL_ITEM_NAME, &bstrFullItemName); if (SUCCEEDED(hr) && !bstrFullItemName) { hr = E_UNEXPECTED; } } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_BRIGHTNESS, &lBrightness); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_CONTRAST, &lContrast); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_ROTATION, &lRotation); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_DESKEW_X, &lDeskewX); } if (SUCCEEDED(hr)) { hr = pWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_DESKEW_Y, &lDeskewY); } if(SUCCEEDED(hr)) { GUID guidItemFormat = {0}; hr = pWiaItemWrapper->ReadRequiredPropertyGUID(WIA_IPA_FORMAT, &guidItemFormat); if((SUCCEEDED(hr)) && (IsEqualGUID(guidItemFormat, WiaImgFmt_RAW))) { hr = ConvertRawImageToBMP(pInputStream, &tempStream); if (SUCCEEDED(hr) && tempStream) { bConvertedRawImage = TRUE; pInputStream = tempStream; } } } if (pWiaItemWrapper) { delete pWiaItemWrapper; } } // // If the upper left corner of the passed image does not correspond to (0,0) // on the flatbed we have to adjust xpos and ypos accordingly in order for us // to "cut out" the correct region represented by pWiaChildItem // if (SUCCEEDED(hr)) { xpos = xpos - InputImageExtents.left; ypos = ypos - InputImageExtents.top; } // // Now get the application stream and write to it // if (SUCCEEDED(hr)) { hr = m_pAppWiaTransferCallback->GetNextStream(0, bstrItemName, bstrFullItemName, &pAppStream); if (SUCCEEDED(hr) && !pAppStream) { hr = E_UNEXPECTED; } } if (SUCCEEDED(hr)) { if (bConvertedRawImage) { hr = CreateStreamOnHGlobal(0, TRUE, &tempOutStream); if (SUCCEEDED(hr)) { ULONG64 ulDummy = 0; hr = DoFiltering(lBrightness, lContrast, lRotation, lDeskewX, lDeskewY, pInputStream, tempOutStream, &ulDummy, xpos, ypos, width, height ); } if (SUCCEEDED(hr)) { hr = ConvertBMPImageToRaw(tempOutStream, pAppStream,&ulBytesWrittenToOutputStream); } } else { hr = DoFiltering(lBrightness, lContrast, lRotation, lDeskewX, lDeskewY, pInputStream, pAppStream, &ulBytesWrittenToOutputStream, xpos, ypos, width, height ); } } if (pAppStream) { pAppStream->Release(); } if (tempStream) { tempStream->Release(); } if (tempOutStream) { tempOutStream->Release(); } return hr; } /***************************************************************************** * * @func STDMETHODIMP | CImageFilter::ApplyProperties | Apply properties after filtering. * * @parm IWiaPropertyStorage | pWiaPropertyStorage | * Pointer to property storage that the image processing filter can write properties to. * * @comm * ApplyProperties is called by the WIA service after the image processing filter has processed * the raw data. This method allows the image processing filter to write data back to the driver and device. * This may be necessary for filters that implement things such as auto-exposure. * Note, an image processing filter should only use the WriteMultiple method to write properties into * the provided storage. * * @rvalue S_OK | * The function succeeded. * @rvalue E_XXX | * The function failed * *****************************************************************************/ STDMETHODIMP CImageFilter::ApplyProperties( _Inout_ IN IWiaPropertyStorage *pWiaPropertyStorage) { HRESULT hr = S_OK; hr = pWiaPropertyStorage ? S_OK : E_INVALIDARG; // // This filter only writes the MY_TEST_FILTER_PROP property for // illustrational purposes. // In general if a filter does not need to write any properties it // should just return S_OK. // if (SUCCEEDED(hr)) { PROPSPEC PropSpec[1] = {0}; PROPVARIANT PropVariant[1] = {0}; PropVariantInit(PropVariant); PropSpec[0].ulKind = PRSPEC_PROPID; PropSpec[0].propid = MY_TEST_FILTER_PROP; PropVariant[0].vt = VT_I4; PropVariant[0].lVal = 1; // // Set the properties // hr = pWiaPropertyStorage->WriteMultiple( 1, PropSpec, PropVariant, WIA_IPA_FIRST ); } return hr; } /***************************************************************************** * * @func STDMETHODIMP | CImageFilter::TransferCallback | TransferCallback implementation * * @parm LONG | lFlags | * Flags * * @parm WiaTransferParams | pWiaTransferParams | * Contains transfer status * * @comm * TransferCallback delegates to the application's callback. It changes the * number of bytes written since we always cache all the data before writing to * the application's stream. We do however not change the percentage since this * represents percentage of total transfer time (a "real" implementation probably * would take the filtering into account here). * We do not write the data to the application's stream until when we receive * a WIA_TRANSFER_MSG_END_OF_STREAM message. * * @rvalue S_OK | * The function succeeded. * @rvalue E_XXX | * The function failed * *****************************************************************************/ STDMETHODIMP CImageFilter::TransferCallback( IN LONG lFlags, _In_ IN WiaTransferParams *pWiaTransferParams) { HRESULT hr = S_OK; if (!m_pAppWiaTransferCallback) { hr = E_UNEXPECTED; } if ((SUCCEEDED(hr)) && (!pWiaTransferParams)) { hr = E_INVALIDARG; } if (SUCCEEDED(hr)) { if (m_pCurrentStream) { pWiaTransferParams->ulTransferredBytes = m_pCurrentStream->m_cBytesWritten; } // // Note the percent reflects the amount of scanning the driver reports // whereas the "BytesWritten" member is the actual number of bytes // that we have sent to the application stream. // if (m_pCurrentStream && (pWiaTransferParams->lMessage == WIA_TRANSFER_MSG_END_OF_STREAM)) { if (!m_bTransferCancelled) { hr = m_pCurrentStream->Flush(); pWiaTransferParams->ulTransferredBytes = m_pCurrentStream->m_cBytesWritten; } m_pCurrentStream -> ReleaseStreams(); } // // Call this regardless of hr because applications need termination messages // HRESULT hrInner = m_pAppWiaTransferCallback->TransferCallback(lFlags, pWiaTransferParams); // // Don't overwrite the original error if there was one // if (SUCCEEDED(hr)) { hr = hrInner; } if (m_pCurrentStream && (pWiaTransferParams->lMessage == WIA_TRANSFER_MSG_END_OF_STREAM)) { m_pCurrentStream->Release(); m_pCurrentStream = NULL; } // // To indicate not to write to the stream later // if ( S_OK != hr) { m_bTransferCancelled = TRUE; } } return hr; } /***************************************************************************** * * @func STDMETHODIMP | CImageFilter::GetNextStream | Implementation of GetNextStream * * @parm LONG | lFlags | * Flags * * @parm BSTR | bstrItemName | * Name of item * * @parm BSTR | bstrFullItemName | * Full name of item * * @parm IStream | ppDestination | * Upon successful return this will contain the filtering stream * * @comm * GetNextStream creates a filtering stream. Since the item represented by * bstrFullItemName may be a child item of the item passed into InitializeFilter * we have to call FindItemByName to retrieve the actual item. * * @rvalue S_OK | * The function succeeded. * @rvalue E_XXXXXX | * Failure * *****************************************************************************/ STDMETHODIMP #pragma warning(suppress: 6101) CImageFilter::GetNextStream( LONG lFlags, _In_z_ BSTR bstrItemName, _In_z_ BSTR bstrFullItemName, _Outptr_result_maybenull_ _At_(*ppDestination, _When_(return == S_OK, _Post_notnull_)) IStream **ppDestination) { HRESULT hr; IStream *pAppStream = NULL; IWiaItem2 *pCurrentWiaItem = NULL; BOOL bStorageItem = FALSE; LONG lBrightness = 0; LONG lContrast = 0; LONG lDeskewX = 0; LONG lDeskewY = 0; LONG lRotation = PORTRAIT; LONG lXExtent = 0; LONG lYExtent = 0; LONG lBitDepth = 0; GUID guidItemFormat = {0}; hr = (bstrItemName && bstrFullItemName && ppDestination) ? S_OK : E_INVALIDARG; if (SUCCEEDED(hr)) { *ppDestination = NULL; hr = m_pAppWiaTransferCallback ? S_OK : E_UNEXPECTED; } if (m_pCurrentStream) { m_pCurrentStream->Release(); m_pCurrentStream = NULL; } if (SUCCEEDED(hr)) { hr = m_pAppWiaTransferCallback->GetNextStream(lFlags, bstrItemName, bstrFullItemName, &pAppStream); if (SUCCEEDED(hr) && !pAppStream) { hr = E_UNEXPECTED; } } // // Return immediately following cancellations or skips // if ((S_FALSE == hr) || (WIA_STATUS_SKIP_ITEM == hr)) { return hr; } if (SUCCEEDED(hr)) { hr = m_pWiaItem->FindItemByName(0, bstrFullItemName, &pCurrentWiaItem); } // // Here we read all properties from pCurrentWiaItem that we need in order to // do the the filtering - in this specific case only brightness. // if (SUCCEEDED(hr)) { CWiaItem *pIWiaItemWrapper = NULL; pIWiaItemWrapper = new CWiaItem(); hr = pIWiaItemWrapper ? S_OK : E_OUTOFMEMORY; if (SUCCEEDED(hr)) { hr = pIWiaItemWrapper->SetIWiaItem(pCurrentWiaItem); } if (SUCCEEDED(hr)) { GUID guidItemCategory = {0}; hr = pIWiaItemWrapper->ReadRequiredPropertyGUID(WIA_IPA_ITEM_CATEGORY,&guidItemCategory); bStorageItem = ((guidItemCategory == WIA_CATEGORY_FINISHED_FILE) || (WIA_CATEGORY_FOLDER == guidItemCategory)); } if(SUCCEEDED(hr)) { hr = pIWiaItemWrapper->ReadRequiredPropertyGUID(WIA_IPA_FORMAT, &guidItemFormat); } if (!bStorageItem) { if (SUCCEEDED(hr)) { hr = pIWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_BRIGHTNESS,&lBrightness); } if (SUCCEEDED(hr)) { hr = pIWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_CONTRAST,&lContrast); } if (SUCCEEDED(hr)) { hr = pIWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_ROTATION, &lRotation); } if (SUCCEEDED(hr)) { hr = pIWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_DESKEW_X, &lDeskewX); } if (SUCCEEDED(hr)) { hr = pIWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_DESKEW_Y, &lDeskewY); } if (SUCCEEDED(hr)) { hr = pIWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_XEXTENT, &lXExtent); } if (SUCCEEDED(hr)) { hr = pIWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPS_YEXTENT, &lYExtent); } if (SUCCEEDED(hr)) { hr = pIWiaItemWrapper->ReadRequiredPropertyLong(WIA_IPA_DEPTH, &lBitDepth); } } if (pIWiaItemWrapper) { delete pIWiaItemWrapper; } } if (SUCCEEDED(hr)) { if (!bStorageItem) { // // We could easily improve the performace by creating a separate filtering stream // which simply delegates all calls directly to the application's stream in case // Rotation, DeskewX, DeskewY, Brightness and Contrast are all set to 0. // m_pCurrentStream = new CMyFilterStream(); if (m_pCurrentStream) { hr = m_pCurrentStream->Initialize(pAppStream, lBrightness, lContrast, lRotation, lDeskewX, lDeskewY, lXExtent, lYExtent, lBitDepth, guidItemFormat); } else { hr = E_OUTOFMEMORY; } } else { (*ppDestination) = pAppStream; (*ppDestination)->AddRef(); } } if (SUCCEEDED(hr) && m_pCurrentStream) { hr = m_pCurrentStream->QueryInterface(IID_IStream, (void**)ppDestination); } if (pAppStream) { pAppStream->Release(); } if (pCurrentWiaItem) { pCurrentWiaItem->Release(); } return hr; } /***************************************************************************** * * Class Object * *******************************************************************************/ class CFilterClass : public IClassFactory { public: STDMETHODIMP QueryInterface(_In_ const IID& iid_requested, _Out_ void** ppInterfaceOut) { HRESULT hr = S_OK; hr = ppInterfaceOut ? S_OK : E_POINTER; if (SUCCEEDED(hr)) { *ppInterfaceOut = NULL; } // // We support IID_IUnknown and IID_IClassFactory // if (SUCCEEDED(hr)) { if (IID_IUnknown == iid_requested) { *ppInterfaceOut = static_cast<IUnknown*>(this); } else if (IID_IClassFactory == iid_requested) { *ppInterfaceOut = static_cast<IClassFactory*>(this); } else { hr = E_NOINTERFACE; } } if (SUCCEEDED(hr)) { reinterpret_cast<IUnknown*>(*ppInterfaceOut)->AddRef(); } return hr; } STDMETHODIMP_(ULONG) AddRef(void) { LockModule(); return 2; } STDMETHODIMP_(ULONG) Release(void) { UnlockModule(); return 1; } STDMETHODIMP CreateInstance(_In_ IUnknown *pUnkOuter, _In_ REFIID riid, _Out_ void **ppv) { CImageFilter *pImageFilter = NULL; HRESULT hr; hr = ppv ? S_OK : E_POINTER; if (SUCCEEDED(hr)) { *ppv = 0; } if (SUCCEEDED(hr)) { if (pUnkOuter) { hr = CLASS_E_NOAGGREGATION; } } if (SUCCEEDED(hr)) { pImageFilter = new CImageFilter(); hr = pImageFilter ? S_OK : E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { pImageFilter->AddRef(); hr = pImageFilter->QueryInterface(riid, ppv); pImageFilter->Release(); } return hr; } STDMETHODIMP LockServer(BOOL bLock) { if (bLock) { LockModule(); } else { UnlockModule(); } return S_OK; } }; STDAPI DllCanUnloadNow(void) { return (g_cLocks == 0) ? S_OK : S_FALSE; } STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ void **ppv) { static CFilterClass s_FilterClass; if (rclsid == CLSID_WiaImageFilter) { return s_FilterClass.QueryInterface(riid, ppv); } *ppv = 0; return CLASS_E_CLASSNOTAVAILABLE; } // // Registered in driver INF file - what about un-regestering? // STDAPI DllUnregisterServer() { return S_OK; } STDAPI DllRegisterServer() { return S_OK; }
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials