📄 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 "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))
{
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;
inputXPOS = 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;
}
/*****************************************************************************
*
* 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(1), m_cBytesWritten(0),
m_lBrightness(0), m_lContrast(0), m_lRotation(0), m_lDeskewX(0), m_lDeskewY(0)
{
}
///
/// 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)
{
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);
//
// For large images it is important that we make a good estimate of the image size,
// otherwise memory re-allocations may become very expensive.
//
if (SUCCEEDED(hr))
{
ULARGE_INTEGER ullSize = {0};
LARGE_INTEGER ullPos = {0};
ullSize.QuadPart = GetUpperLimitSize(lXExtent, lYExtent, lBitDepth);
hr = m_pCachingStream->SetSize(ullSize);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -