📄 winutil.cpp
字号:
{
#ifdef WINUTIL_STUB
STUBRET(GDI_ERROR);
#else
HBITMAP hbm = NULL;
HBITMAP hbmOld;
int iStride;
HDC hdcSrc;
int iRet;
PLOGPALETTE pPal;
HPALETTE hpal = NULL;
HPALETTE hpalOld;
// Check input pointers
if ((lpBitsInfo == NULL) || (lpBits == NULL))
return GDI_ERROR;
// Stride is bitmap width in bytes, DWORD aligned
iStride = ((lpBitsInfo->bmiHeader.biWidth * lpBitsInfo->bmiHeader.biBitCount / 8) + 3) & ~3;
// Make a source DC to blt from
hdcSrc = CreateCompatibleDC(hdc);
if (hdcSrc == NULL)
{
iRet = GDI_ERROR;
goto cleanup;
}
// Wrap a GDI BITMAP object around these bitmap bits, using "secret" MGDI function
hbm = CreateBitmapFromPointer(lpBitsInfo, iStride, (PVOID)lpBits);
if (hbm == NULL)
{
iRet = GDI_ERROR;
goto cleanup;
}
hbmOld = (HBITMAP)SelectObject(hdcSrc, hbm);
if (hbmOld == NULL)
{
iRet = GDI_ERROR;
goto cleanup;
}
// Do we need a palette?
if ((lpBitsInfo->bmiHeader.biCompression == BI_RGB) && (iUsage == DIB_RGB_COLORS) && (lpBitsInfo->bmiHeader.biBitCount <= 8))
{
DWORD cColors = lpBitsInfo->bmiHeader.biClrUsed;
if (cColors == 0)
{
cColors = 1 << lpBitsInfo->bmiHeader.biBitCount;
}
pPal = (PLOGPALETTE)LocalAlloc(LMEM_FIXED, sizeof(LOGPALETTE) +
sizeof(PALETTEENTRY) * cColors);
if (pPal == NULL)
{
iRet = GDI_ERROR;
goto cleanup;
}
pPal->palVersion = 0x300;
pPal->palNumEntries = (WORD)cColors;
for (WORD idx = 0; idx < cColors; idx++)
{
pPal->palPalEntry[idx].peRed = lpBitsInfo->bmiColors[idx].rgbRed;
pPal->palPalEntry[idx].peGreen = lpBitsInfo->bmiColors[idx].rgbGreen;
pPal->palPalEntry[idx].peBlue = lpBitsInfo->bmiColors[idx].rgbBlue;
pPal->palPalEntry[idx].peFlags = 0;
}
hpal = CreatePalette(pPal);
if (hpal == NULL)
{
LocalFree(pPal);
iRet = GDI_ERROR;
goto cleanup;
}
hpalOld = SelectPalette(hdcSrc, hpal, FALSE);
LocalFree(pPal);
}
// Do the Blt
if ( StretchBlt(hdc, XDest, YDest, nDestWidth, nDestHeight,
hdcSrc, XSrc, YSrc, nSrcWidth, nSrcHeight, dwRop))
{
// REVIEW martsh: doesn't take into account clipping. Should this be nDestHeight?
iRet = nSrcHeight > 0 ? nSrcHeight : -nSrcHeight;
}
else
{
iRet = GDI_ERROR;
}
cleanup:
if (hbm)
{
SelectObject(hdcSrc, hbmOld);
DeleteObject(hbm);
}
// Did we use a palette?
if (hpal)
{
SelectPalette(hdcSrc, hpalOld, FALSE);
DeleteObject(hpal);
}
if (hdcSrc)
{
DeleteDC(hdcSrc);
}
return iRet;
#endif // WINUTIL_STUB
}
#endif // UNDER_CE
// This is called when there is a sample ready to be drawn, unfortunately the
// output pin was being rotten and didn't choose our super excellent shared
// memory DIB allocator so we have to do this slow render using boring old GDI
// SetDIBitsToDevice and StretchDIBits. The down side of using these GDI
// functions is that the image data has to be copied across from our address
// space into theirs before going to the screen (although in reality the cost
// is small because all they do is to map the buffer into their address space)
void CDrawImage::SlowRender(IMediaSample *pMediaSample)
{
#ifdef WINUTIL_STUB
STUBNORET;
#else
// Get the BITMAPINFOHEADER for the connection
ASSERT(m_pMediaType);
BITMAPINFOHEADER *pbmi = GetBitmapInfoHeader (m_pMediaType);
BYTE *pImage;
// Get the image data buffer
HRESULT hr = pMediaSample->GetPointer(&pImage);
if (FAILED(hr)) {
return;
}
// This allows derived classes to change the source rectangle that we do
// the drawing with. For example a renderer may ask a codec to stretch
// the video from 320x240 to 640x480, in which case the source we see in
// here will still be 320x240, although the source we want to draw with
// should be scaled up to 640x480. The base class implementation of this
// method does nothing but return the same rectangle as we are passed in
RECT SourceRect = ScaleSourceRect(&m_SourceRect);
LONG lAdjustedSourceTop = SourceRect.top;
// if the origin of bitmap is bottom-left, adjust soruce_rect_top
// to be the bottom-left corner instead of the top-left.
if (pbmi->biHeight > 0) {
lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom;
}
// Is the window the same size as the video
#ifndef UNDER_CE
if (m_bStretch == FALSE) {
// Put the image straight into the window
SetDIBitsToDevice(
(HDC) m_hdc, // Target device HDC
m_TargetRect.left, // X sink position
m_TargetRect.top, // Y sink position
m_TargetRect.right - m_TargetRect.left, // Destination width
m_TargetRect.bottom - m_TargetRect.top, // Destination height
SourceRect.left, // X source position
lAdjustedSourceTop, // Adjusted Y source position
(UINT) 0, // Start scan line
pbmi->biHeight, // Scan lines present
pImage, // Image data
(BITMAPINFO *) pbmi, // DIB header
DIB_RGB_COLORS); // Type of palette
} else {
// Stretch the image when copying to the window
StretchDIBits(
(HDC) m_hdc, // Target device HDC
m_TargetRect.left, // X sink position
m_TargetRect.top, // Y sink position
m_TargetRect.right - m_TargetRect.left, // Destination width
m_TargetRect.bottom - m_TargetRect.top, // Destination height
SourceRect.left, // X source position
lAdjustedSourceTop, // Adjusted Y source position
SourceRect.right - SourceRect.left, // Source width
SourceRect.bottom - SourceRect.top, // Source height
pImage, // Image data
(BITMAPINFO *) pbmi, // DIB header
DIB_RGB_COLORS, // Type of palette
SRCCOPY); // Simple image copy
}
#else // !UNDER_CE
CeStretchDIBits(
(HDC) m_hdc, // Target device HDC
m_TargetRect.left, // X sink position
m_TargetRect.top, // Y sink position
m_TargetRect.right - m_TargetRect.left, // Destination width
m_TargetRect.bottom - m_TargetRect.top, // Destination height
SourceRect.left, // X source position
lAdjustedSourceTop, // Adjusted Y source position
SourceRect.right - SourceRect.left, // Source width
SourceRect.bottom - SourceRect.top, // Source height
pImage, // Image data
(BITMAPINFO *) pbmi, // DIB header
DIB_RGB_COLORS, // Type of palette
SRCCOPY); // Simple image copy
#endif // !UNDER_CE
// This shows the sample reference times over the top of the image which
// looks a little flickery. I tried using GdiSetBatchLimit and GdiFlush to
// control the screen updates but it doesn't quite work as expected and
// only partially reduces the flicker. I also tried using a memory context
// and combining the two in that before doing a final BitBlt operation to
// the screen, unfortunately this has considerable performance penalties
// and also means that this code is not executed when compiled retail
#ifdef DEBUG
DisplaySampleTimes(pMediaSample);
#endif
#endif // WINUTIL_STUB
}
// This is called with an IMediaSample interface on the image to be drawn. We
// decide on the drawing mechanism based on who's allocator we are using. We
// may be called when the window wants an image painted by WM_PAINT messages
// We can't realise the palette here because we have the renderer lock, any
// call to realise may cause an interthread send message to the window thread
// which may in turn be waiting to get the renderer lock before servicing it
BOOL CDrawImage::DrawImage(IMediaSample *pMediaSample)
{
#ifdef WINUTIL_STUB
STUBRET(FALSE);
#else
SetDrawContext();
ASSERT(m_hdc);
ASSERT(m_MemoryDC);
NotifyStartDraw();
// If the output pin used our allocator then the samples passed are in
// fact CVideoSample objects that contain CreateDIBSection data that we
// use to do faster image rendering, they may optionally also contain a
// DirectDraw surface pointer in which case we do not do the drawing
if (m_bUsingImageAllocator == FALSE) {
SlowRender(pMediaSample);
#ifndef UNDER_CE
EXECUTE_ASSERT(GdiFlush());
#endif // UNDER_CE
NotifyEndDraw();
return TRUE;
}
// This is a DIBSECTION buffer
FastRender(pMediaSample);
#ifndef UNDER_CE
EXECUTE_ASSERT(GdiFlush());
#endif // UNDER_CE
NotifyEndDraw();
return TRUE;
#endif // WINUTIL_STUB
}
// This is called by the owning window object after it has created the window
// and it's drawing contexts. We are constructed with the base window we'll
// be drawing into so when given the notification we retrive the device HDCs
// to draw with. We cannot call these in our constructor as they are virtual
void CDrawImage::SetDrawContext()
{
#ifdef WINUTIL_STUB
STUBNORET;
#else
m_MemoryDC = m_pBaseWindow->GetMemoryHDC();
m_hdc = m_pBaseWindow->GetWindowHDC();
#endif // WINUTIL_STUB
}
// This is called to set the target rectangle in the video window, it will be
// called whenever a WM_SIZE message is retrieved from the message queue. We
// simply store the rectangle and use it later when we do the drawing calls
void CDrawImage::SetTargetRect(RECT *pTargetRect)
{
#ifdef WINUTIL_STUB
STUBNORET;
#else
ASSERT(pTargetRect);
m_TargetRect = *pTargetRect;
SetStretchMode();
#endif // WINUTIL_STUB
}
// Return the current target rectangle
void CDrawImage::GetTargetRect(RECT *pTargetRect)
{
#ifdef WINUTIL_STUB
STUBNORET;
#else
ASSERT(pTargetRect);
*pTargetRect = m_TargetRect;
#endif // WINUTIL_STUB
}
// This is called when we want to change the section of the image to draw. We
// use this information in the drawing operation calls later on. We must also
// see if the source and destination rectangles have the same dimensions. If
// not we must stretch during the drawing rather than a direct pixel copy
void CDrawImage::SetSourceRect(RECT *pSourceRect)
{
#ifdef WINUTIL_STUB
STUBNORET;
#else
ASSERT(pSourceRect);
m_SourceRect = *pSourceRect;
SetStretchMode();
#endif // WINUTIL_STUB
}
// Return the current source rectangle
void CDrawImage::GetSourceRect(RECT *pSourceRect)
{
#ifdef WINUTIL_STUB
STUBNORET;
#else
ASSERT(pSourceRect);
*pSourceRect = m_SourceRect;
#endif // WINUTIL_STUB
}
// This is called when either the source or destination rectanges change so we
// can update the stretch flag. If the rectangles don't match we stretch the
// video during the drawing otherwise we call the fast pixel copy functions
// NOTE the source and/or the destination rectangle may be completely empty
void CDrawImage::SetStretchMode()
{
#ifdef WINUTIL_STUB
STUBNORET;
#else
// Calculate the overall rectangle dimensions
LONG SourceWidth = m_SourceRect.right - m_SourceRect.left;
LONG SinkWidth = m_TargetRect.right - m_TargetRect.left;
LONG SourceHeight = m_SourceRect.bottom - m_SourceRect.top;
LONG SinkHeight = m_TargetRect.bottom - m_TargetRect.top;
m_bStretch = TRUE;
if (SourceWidth == SinkWidth) {
if (SourceHeight == SinkHeight) {
m_bStretch = FALSE;
}
}
#endif // WINUTIL_STUB
}
// Tell us whose allocator we are using. This should be called with TRUE if
// the filter agrees to use an allocator based around the CImageAllocator
// SDK base class - whose image buffers are made through CreateDIBSection.
// Otherwise this should be called with FALSE and we will draw the images
// using SetDIBitsToDevice and StretchDIBitsToDevice. None of these calls
// can handle buffers which have non zero strides (like DirectDraw uses)
void CDrawImage::NotifyAllocator(BOOL bUsingImageAllocator)
{
#ifdef WINUTIL_STUB
STUBNORET;
#else
m_bUsingImageAllocator = bUsingImageAllocator;
#endif // WINUTIL_STUB
}
// Are we using the image DIBSECTION allocator
BOOL CDrawImage::UsingImageAllocator()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -