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.
709 lines
18 KiB
709 lines
18 KiB
/*****************************************************************************
|
|
*
|
|
* (C) COPYRIGHT MICROSOFT CORPORATION, 1999-2000
|
|
*
|
|
* TITLE: StillPrc.cpp
|
|
*
|
|
* VERSION: 1.0
|
|
*
|
|
* AUTHOR: OrenR
|
|
*
|
|
* DATE: 2000/10/27
|
|
*
|
|
* DESCRIPTION: Implements Still Image Processing.
|
|
*
|
|
*****************************************************************************/
|
|
#include <precomp.h>
|
|
#pragma hdrstop
|
|
#include <gphelper.h>
|
|
|
|
using namespace Gdiplus;
|
|
|
|
///////////////////////////////
|
|
// CStillProcessor Constructor
|
|
//
|
|
CStillProcessor::CStillProcessor() :
|
|
m_bTakePicturePending(FALSE),
|
|
m_hSnapshotReadyEvent(NULL),
|
|
m_uiFileNumStartPoint(0)
|
|
{
|
|
DBG_FN("CStillProcessor::CStillProcessor");
|
|
|
|
//
|
|
// This event is used to wait for a picture to be returned to us from the
|
|
// still pin on the capture filter (if it exists)
|
|
//
|
|
m_hSnapshotReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
ASSERT(m_hSnapshotReadyEvent != NULL);
|
|
}
|
|
|
|
///////////////////////////////
|
|
// CStillProcessor Destructor
|
|
//
|
|
CStillProcessor::~CStillProcessor()
|
|
{
|
|
DBG_FN("CStillProcessor::~CStillProcessor");
|
|
|
|
if (m_hSnapshotReadyEvent)
|
|
{
|
|
CloseHandle(m_hSnapshotReadyEvent);
|
|
m_hSnapshotReadyEvent = NULL;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////
|
|
// Init
|
|
//
|
|
HRESULT CStillProcessor::Init(CPreviewGraph *pPreviewGraph)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_pPreviewGraph = pPreviewGraph;
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// Term
|
|
//
|
|
HRESULT CStillProcessor::Term()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_pPreviewGraph = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// SetTakePicturePending
|
|
//
|
|
HRESULT CStillProcessor::SetTakePicturePending(BOOL bTakePicturePending)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
m_bTakePicturePending = bTakePicturePending;
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// IsTakePicturePending
|
|
//
|
|
BOOL CStillProcessor::IsTakePicturePending()
|
|
{
|
|
return m_bTakePicturePending;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// CreateImageDir
|
|
//
|
|
HRESULT CStillProcessor::CreateImageDir(
|
|
const CSimpleString *pstrImagesDirectory)
|
|
{
|
|
DBG_FN("CStillProcessor::CreateImageDir");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
ASSERT(pstrImagesDirectory != NULL);
|
|
|
|
if (pstrImagesDirectory == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
CHECK_S_OK2(hr, ("CStillProcessor::CreateImage received a NULL "
|
|
"param"));
|
|
|
|
return hr;
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
m_strImageDir = *pstrImagesDirectory;
|
|
m_uiFileNumStartPoint = 0;
|
|
|
|
if (!RecursiveCreateDirectory(pstrImagesDirectory))
|
|
{
|
|
DBG_ERR(("ERROR: Failed to create directory '%ls', last "
|
|
"error = %d",
|
|
m_strImageDir.String(),
|
|
::GetLastError()));
|
|
}
|
|
else
|
|
{
|
|
DBG_TRC(("*** Images will be stored in '%ls' ***",
|
|
m_strImageDir.String()));
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// DoesDirectoryExist
|
|
//
|
|
// Checks to see whether the given
|
|
// fully qualified directory exists.
|
|
|
|
BOOL CStillProcessor::DoesDirectoryExist(LPCTSTR pszDirectoryName)
|
|
{
|
|
DBG_FN("CStillProcessor::DoesDirectoryExist");
|
|
|
|
BOOL bExists = FALSE;
|
|
|
|
if (pszDirectoryName)
|
|
{
|
|
//
|
|
// Try to determine if this directory exists
|
|
//
|
|
|
|
DWORD dwFileAttributes = GetFileAttributes(pszDirectoryName);
|
|
|
|
if ((dwFileAttributes == 0xFFFFFFFF) ||
|
|
!(dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
|
{
|
|
bExists = FALSE;
|
|
}
|
|
else
|
|
{
|
|
bExists = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bExists = FALSE;
|
|
}
|
|
|
|
return bExists;
|
|
}
|
|
|
|
|
|
///////////////////////////////
|
|
// RecursiveCreateDirectory
|
|
//
|
|
// Take a fully qualified path and
|
|
// create the directory in pieces as
|
|
// needed.
|
|
//
|
|
BOOL CStillProcessor::RecursiveCreateDirectory(
|
|
const CSimpleString *pstrDirectoryName)
|
|
{
|
|
DBG_FN("CStillProcessor::RecursiveCreateDirectory");
|
|
|
|
ASSERT(pstrDirectoryName != NULL);
|
|
|
|
//
|
|
// If this directory already exists, return true.
|
|
//
|
|
|
|
if (DoesDirectoryExist(*pstrDirectoryName))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Otherwise try to create it.
|
|
//
|
|
|
|
CreateDirectory(*pstrDirectoryName, NULL);
|
|
|
|
//
|
|
// If it now exists, return true
|
|
//
|
|
|
|
if (DoesDirectoryExist(*pstrDirectoryName))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Remove the last subdir and try again
|
|
//
|
|
|
|
int nFind = pstrDirectoryName->ReverseFind(TEXT('\\'));
|
|
if (nFind >= 0)
|
|
{
|
|
RecursiveCreateDirectory(&(pstrDirectoryName->Left(nFind)));
|
|
|
|
//
|
|
// Now try to create it.
|
|
//
|
|
|
|
CreateDirectory(*pstrDirectoryName, NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
//Does it exist now?
|
|
//
|
|
|
|
return DoesDirectoryExist(*pstrDirectoryName);
|
|
}
|
|
|
|
///////////////////////////////
|
|
// RegisterStillProcessor
|
|
//
|
|
HRESULT CStillProcessor::RegisterStillProcessor(
|
|
IStillSnapshot *pFilterOnCapturePin,
|
|
IStillSnapshot *pFilterOnStillPin)
|
|
{
|
|
DBG_FN("CStillProcessor::RegisterStillProcessor");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
m_CaptureCallbackParams.pStillProcessor = this;
|
|
m_StillCallbackParams.pStillProcessor = this;
|
|
|
|
if (pFilterOnCapturePin)
|
|
{
|
|
hr = pFilterOnCapturePin->RegisterSnapshotCallback(
|
|
CStillProcessor::SnapshotCallback,
|
|
(LPARAM) &m_CaptureCallbackParams);
|
|
|
|
CHECK_S_OK2(hr, ("Failed to register for callbacks with WIA filter "
|
|
" on capture pin"));
|
|
}
|
|
|
|
if (pFilterOnStillPin)
|
|
{
|
|
hr = pFilterOnStillPin->RegisterSnapshotCallback(
|
|
CStillProcessor::SnapshotCallback,
|
|
(LPARAM) &m_StillCallbackParams);
|
|
|
|
CHECK_S_OK2(hr, ("Failed to register for callbacks with WIA filter "
|
|
"on still pin"));
|
|
}
|
|
|
|
//
|
|
// Reset our file name starting point number
|
|
//
|
|
m_uiFileNumStartPoint = 0;
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// WaitForNewImage
|
|
//
|
|
HRESULT CStillProcessor::WaitForNewImage(UINT uiTimeout,
|
|
CSimpleString *pstrNewImageFullPath)
|
|
|
|
{
|
|
DBG_FN("CStillProcessor::WaitForCompletion");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Wait for callback function to return from Still Filter which will
|
|
// trigger this event.
|
|
//
|
|
|
|
if (SUCCEEDED(hr) && m_hSnapshotReadyEvent)
|
|
{
|
|
DWORD dwRes = 0;
|
|
|
|
//
|
|
// Wait for snapshot to complete for dwTimeout seconds.
|
|
//
|
|
dwRes = WaitForSingleObject(m_hSnapshotReadyEvent, uiTimeout );
|
|
|
|
if (dwRes == WAIT_OBJECT_0)
|
|
{
|
|
if (pstrNewImageFullPath)
|
|
{
|
|
pstrNewImageFullPath->Assign(m_strLastSavedFile);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
|
|
if (pstrNewImageFullPath)
|
|
{
|
|
pstrNewImageFullPath->Assign(TEXT(""));
|
|
}
|
|
|
|
if (dwRes == WAIT_TIMEOUT)
|
|
{
|
|
CHECK_S_OK2(hr, ("***Timed out waiting for "
|
|
"m_hSnapshotReadyEvent!***"));
|
|
}
|
|
else if (dwRes == WAIT_ABANDONED)
|
|
{
|
|
CHECK_S_OK2(hr, ("***WAIT_ABANDONED while waiting for "
|
|
"m_hSnapshotReadyEvent!***"));
|
|
}
|
|
else
|
|
{
|
|
CHECK_S_OK2(hr, ("***Unknown error (dwRes = %d) waiting "
|
|
"for m_hSnapshotReadyEvent***", dwRes));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// ProcessImage
|
|
//
|
|
HRESULT CStillProcessor::ProcessImage(HGLOBAL hDIB)
|
|
{
|
|
DBG_FN("CStillProcessor::ProcessImage");
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
ASSERT(hDIB != NULL);
|
|
|
|
if (hDIB == NULL)
|
|
{
|
|
hr = E_FAIL;
|
|
CHECK_S_OK2(hr, ("CStillProcessor::ProcessImage, received NULL "
|
|
"image data"));
|
|
return hr;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CSimpleString strJPEG;
|
|
CSimpleString strBMP;
|
|
|
|
hr = CreateFileName(&strJPEG, &strBMP);
|
|
|
|
//
|
|
// Save the new image to a file
|
|
//
|
|
hr = SaveToFile(hDIB, &strJPEG, &strBMP);
|
|
|
|
//
|
|
// Let people know the image is available...
|
|
//
|
|
|
|
if (IsTakePicturePending())
|
|
{
|
|
if (m_hSnapshotReadyEvent)
|
|
{
|
|
m_strLastSavedFile = strJPEG;
|
|
|
|
SetEvent(m_hSnapshotReadyEvent);
|
|
}
|
|
else
|
|
{
|
|
DBG_WRN(("CStillProcessor::ProcessImage, failed to Set "
|
|
"SnapshotReady event because it was NULL"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_pPreviewGraph)
|
|
{
|
|
hr = m_pPreviewGraph->ProcessAsyncImage(&strJPEG);
|
|
}
|
|
else
|
|
{
|
|
DBG_WRN(("CStillProcessor::ProcessImage failed to call "
|
|
"ProcessAsyncImage, m_pPreviewGraph is NULL"));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
///////////////////////////////
|
|
// SnapshotCallback
|
|
//
|
|
// Static Fn
|
|
//
|
|
// This function is called by the
|
|
// WIA StreamSnapshot Filter
|
|
// in wiasf.ax. It delivers to us
|
|
// the newly captured still image.
|
|
//
|
|
BOOL CStillProcessor::SnapshotCallback(HGLOBAL hDIB,
|
|
LPARAM lParam)
|
|
{
|
|
DBG_FN("CStillProcessor::SnapshotCallback");
|
|
|
|
BOOL bSuccess = TRUE;
|
|
|
|
SnapshotCallbackParam_t *pCallbackParam =
|
|
(SnapshotCallbackParam_t*) lParam;
|
|
|
|
if (pCallbackParam)
|
|
{
|
|
if (pCallbackParam->pStillProcessor)
|
|
{
|
|
pCallbackParam->pStillProcessor->ProcessImage(hDIB);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bSuccess = FALSE;
|
|
DBG_ERR(("CStillProcessor::SnapshotCallback, pCallbackParam is "
|
|
"NULL when it should contain the snapshot callback params"));
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// ConvertToJPEG
|
|
//
|
|
// Takes a .bmp file and converts
|
|
// it to a .jpg file
|
|
|
|
HRESULT CStillProcessor::ConvertToJPEG(LPCTSTR pszInputFilename,
|
|
LPCTSTR pszOutputFilename)
|
|
{
|
|
DBG_FN("CStillProcessor::ConvertToJPEG");
|
|
|
|
HRESULT hr = CGdiPlusHelper().Convert(
|
|
CSimpleStringConvert::WideString(
|
|
CSimpleString(pszInputFilename)).String(),
|
|
CSimpleStringConvert::WideString(
|
|
CSimpleString(pszOutputFilename)).String(),
|
|
ImageFormatJPEG);
|
|
|
|
CHECK_S_OK(hr);
|
|
return hr;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// CreateFileName
|
|
//
|
|
HRESULT CStillProcessor::CreateFileName(CSimpleString *pstrJPEG,
|
|
CSimpleString *pstrBMP)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
UINT uiNum = 0;
|
|
|
|
ASSERT(pstrJPEG != NULL);
|
|
ASSERT(pstrBMP != NULL);
|
|
|
|
if ((pstrJPEG == NULL) ||
|
|
(pstrBMP == NULL))
|
|
{
|
|
hr = E_POINTER;
|
|
CHECK_S_OK2(hr, ("CStillProcessor::CreateFileName received "
|
|
"NULL param"));
|
|
|
|
return hr;
|
|
}
|
|
|
|
TCHAR szJPG[MAX_PATH + 1] = {0};
|
|
|
|
CSimpleString strBaseName(IDS_SNAPSHOT, _Module.GetModuleInstance());
|
|
CSimpleString strNumberFormat(IDS_NUM_FORMAT, _Module.GetModuleInstance());
|
|
|
|
//
|
|
// Get the lowest number JPG file name we can find.
|
|
//
|
|
m_uiFileNumStartPoint = NumberedFileName::GenerateLowestAvailableNumberedFileName(0,
|
|
szJPG,
|
|
(sizeof(szJPG) / sizeof(szJPG[0])) - 1,
|
|
m_strImageDir,
|
|
strBaseName,
|
|
strNumberFormat,
|
|
TEXT("jpg"),
|
|
false,
|
|
m_uiFileNumStartPoint);
|
|
|
|
//
|
|
// Save the returned JPG file name.
|
|
//
|
|
*pstrJPEG = szJPG;
|
|
|
|
//
|
|
// Give the BMP file, which is a temp file, the same name as the JPG
|
|
// but strip off the JPG extension and attach the BMP extension instead.
|
|
//
|
|
pstrBMP->Assign(*pstrJPEG);
|
|
*pstrBMP = pstrBMP->Left(pstrBMP->ReverseFind(TEXT(".jpg")));
|
|
pstrBMP->Concat(TEXT(".bmp"));
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
///////////////////////////////
|
|
// SaveToFile
|
|
//
|
|
// This method is called when the
|
|
// DShow filter driver delivers us
|
|
// a new set of bits from a snapshot
|
|
// that was just taken. We write these
|
|
// bits out to a file.
|
|
//
|
|
HRESULT CStillProcessor::SaveToFile(HGLOBAL hDib,
|
|
const CSimpleString *pstrJPEG,
|
|
const CSimpleString *pstrBMP)
|
|
{
|
|
DBG_FN("CStillProcessor::SaveToFile");
|
|
|
|
ASSERT(hDib != NULL);
|
|
ASSERT(pstrJPEG != NULL);
|
|
ASSERT(pstrBMP != NULL);
|
|
|
|
HRESULT hr = S_OK;
|
|
BITMAPINFO * pbmi = NULL;
|
|
LPBYTE pImageBits = NULL;
|
|
LPBYTE pColorTable = NULL;
|
|
LPBYTE pFileBits = NULL;
|
|
UINT uNum = 1;
|
|
UINT uFileSize = 0;
|
|
UINT uDibSize = 0;
|
|
UINT uColorTableSize = 0;
|
|
|
|
if ((hDib == NULL) ||
|
|
(pstrJPEG == NULL) ||
|
|
(pstrBMP == NULL))
|
|
{
|
|
hr = E_POINTER;
|
|
CHECK_S_OK2(hr, ("CStillProcessor::SaveToFile, received NULL param"));
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// calculate where the bits are -- basically,
|
|
// right after BITMAPINFOHEADER + color table
|
|
//
|
|
|
|
pbmi = (BITMAPINFO *)GlobalLock(hDib);
|
|
|
|
if (pbmi)
|
|
{
|
|
//
|
|
// Find the image bits
|
|
//
|
|
|
|
pImageBits = (LPBYTE)pbmi + sizeof(BITMAPINFOHEADER);
|
|
if (pbmi->bmiHeader.biClrUsed)
|
|
{
|
|
pImageBits += pbmi->bmiHeader.biClrUsed * sizeof(RGBQUAD);
|
|
}
|
|
else if (pbmi->bmiHeader.biBitCount <= 8)
|
|
{
|
|
pImageBits += (1 << pbmi->bmiHeader.biBitCount) * sizeof(RGBQUAD);
|
|
}
|
|
else if (pbmi->bmiHeader.biCompression == BI_BITFIELDS)
|
|
{
|
|
pImageBits += (3 * sizeof(DWORD));
|
|
}
|
|
|
|
pColorTable = (LPBYTE)pbmi + pbmi->bmiHeader.biSize;
|
|
uColorTableSize = (DWORD)(pImageBits - pColorTable);
|
|
|
|
//
|
|
// calculate the size of the image bits & size of full file
|
|
//
|
|
|
|
UINT uiSrcScanLineWidth = 0;
|
|
UINT uiScanLineWidth = 0;
|
|
|
|
// Align scanline to ULONG boundary
|
|
uiSrcScanLineWidth = (pbmi->bmiHeader.biWidth *
|
|
pbmi->bmiHeader.biBitCount) / 8;
|
|
|
|
uiScanLineWidth = (uiSrcScanLineWidth + 3) & 0xfffffffc;
|
|
|
|
//
|
|
// Calculate DIB size and allocate memory for the DIB.
|
|
uDibSize = (pbmi->bmiHeader.biHeight > 0) ?
|
|
pbmi->bmiHeader.biHeight * uiScanLineWidth :
|
|
-(pbmi->bmiHeader.biHeight) * uiScanLineWidth;
|
|
|
|
uFileSize = sizeof(BITMAPFILEHEADER) +
|
|
pbmi->bmiHeader.biSize +
|
|
uColorTableSize +
|
|
uDibSize;
|
|
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
CHECK_S_OK2(hr, ("Unable to lock hDib"));
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Create a mapped view to the new file so we can start writing out
|
|
// the bits...
|
|
//
|
|
CMappedView cmv(pstrBMP->String(), uFileSize, OPEN_ALWAYS);
|
|
|
|
pFileBits = cmv.Bits();
|
|
|
|
if (!pFileBits)
|
|
{
|
|
hr = E_FAIL;
|
|
CHECK_S_OK2(hr, ("Filemapping failed"));
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Write out BITMAPFILEHEADER
|
|
//
|
|
|
|
BITMAPFILEHEADER bmfh;
|
|
|
|
bmfh.bfType = (WORD)'MB';
|
|
bmfh.bfSize = sizeof(BITMAPFILEHEADER);
|
|
bmfh.bfReserved1 = 0;
|
|
bmfh.bfReserved2 = 0;
|
|
|
|
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) +
|
|
(DWORD)(pImageBits - (LPBYTE)pbmi);
|
|
|
|
memcpy( pFileBits, &bmfh, sizeof(BITMAPFILEHEADER));
|
|
pFileBits += sizeof(BITMAPFILEHEADER);
|
|
|
|
//
|
|
// Write out BITMAPINFOHEADER
|
|
//
|
|
|
|
memcpy( pFileBits, pbmi, pbmi->bmiHeader.biSize );
|
|
pFileBits += pbmi->bmiHeader.biSize;
|
|
|
|
//
|
|
// If there's a color table or color mask, write it out
|
|
//
|
|
|
|
if (pImageBits > pColorTable)
|
|
{
|
|
memcpy( pFileBits, pColorTable, pImageBits - pColorTable );
|
|
pFileBits += (pImageBits - pColorTable);
|
|
}
|
|
|
|
//
|
|
// Write out the image bits
|
|
//
|
|
|
|
memcpy(pFileBits, pImageBits, uDibSize );
|
|
|
|
//
|
|
// We're done w/the image bits now & the file mapping
|
|
//
|
|
|
|
GlobalUnlock( hDib );
|
|
cmv.CloseAndRelease();
|
|
|
|
//
|
|
// Convert image to .jpg file
|
|
//
|
|
|
|
if (SUCCEEDED(ConvertToJPEG(*pstrBMP, *pstrJPEG )))
|
|
{
|
|
DeleteFile(*pstrBMP);
|
|
}
|
|
else
|
|
{
|
|
DBG_ERR(("CStillProcessor::SaveToFile, failed to create image file '%ls'",
|
|
pstrJPEG->String()));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|