/***************************************************************************** * * (C) COPYRIGHT MICROSOFT CORPORATION, 1999-2000 * * TITLE: StillPrc.cpp * * VERSION: 1.0 * * AUTHOR: OrenR * * DATE: 2000/10/27 * * DESCRIPTION: Implements Still Image Processing. * *****************************************************************************/ #include #pragma hdrstop #include 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; }