用于在应用程序的任何窗口上绘制图片的 C++ OCX





3.00/5 (5投票s)
2002 年 11 月 7 日
2分钟阅读

107067

2893
该控件覆盖窗口事件处理程序以接管绘制事件。
引言
这个 OCX 让你可以在(至少)你的应用程序的任何窗口上缩放任何你喜欢的图片。 主要分为三个部分,即加载所需的图片,重定向 Windows 事件处理程序以及绘制图片。
Windows NT、Windows 2000 和 Windows XP 只允许应用程序覆盖自己窗口的 Windows 事件处理程序。 该示例或多或少是不言自明的。 图片使用 OleLoadPicture()
加载。 图片将使用 Windows 标准图形 API 函数 strechblt()
绘制。
为了取代 Windows 事件处理程序,使用了 SetWindowLong()
。
详细说明
绘制例程
主要的绘制是在 ondraw
方法中完成的。 如果 OCX 应该在其他窗口上绘制,它将直接调用 ondraw
。
由于 OCX 是使用 MFC 生成的,因此方法头是标准的并且是自动生成的
void CPictureZoomCtrl::OnDraw( CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
PictureZoom
使用一个标志来标识是否已加载图片
if (m_bNoPicture) return;
第一步是创建一个兼容的 DC,它需要保存图片
CDC mdc; // memory DC
mdc.CreateCompatibleDC(pdc);
CBitmap* pOld = mdc.SelectObject(&m_Picture);
为了防止在绘制拉伸或收缩的图片时出现干扰颜色,请正确设置颜色模式
pdc->SetStretchBltMode(COLORONCOLOR); mdc.SetStretchBltMode(COLORONCOLOR);
为了处理图片,我们需要它的尺寸
m_Picture.UnrealizeObject(); BITMAP BM; m_Picture.GetBitmap(&BM);
接下来的部分有点棘手,因为我们需要区分三种显示图片的方式。 第一种是在不保持原始图片的宽高比的情况下绘制它
if (!m_bAspectRatio) pdc->StretchBlt(0, 0, rcBounds.Width(), rcBounds.Height(),&mdc, 0, 0, BM.bmWidth, BM.bmHeight, SRCCOPY);
下一个块区分其他两种情况
else { double Strech = double(rcBounds.Width()) / double(BM.bmWidth); int iHeight = Strech * BM.bmHeight; int iWidth = Strech * BM.bmWidth; int yMove = ((iHeight - rcBounds.Height()) / 2) / Strech;
这些其他两种情况中的第一种情况是,图片需要在顶部和底部进行裁剪,以便以放大的方式在整个给定窗口上绘制。 第一步是计算源(未缩放!)位图上的左上角
if (yMove >= 0) { // Strech for width int xp = -m_xAdd; int yp = yMove - m_yAdd; if (xp < 0) xp = 0; if (yp < 0) yp = 0; if (xp > iWidth - rcBounds.Width()) xp = iWidth - rcBounds.Width(); if (yp > (iHeight - rcBounds.Height()) / Strech) yp = (iHeight - rcBounds.Height()) / Strech;
现在,由于我对仅居中显示图片有点不满意,因此我决定允许一些对齐设置
if (m_Align == TOP) yp = 0; else if (m_Align == BOTTOM) yp = (iHeight - rcBounds.Height()) / Strech; m_xAdd = -xp; m_yAdd = yMove - yp;
情况一的最后一步是使用 strechblt()
简单地将源矩形绘制到目标窗口上
// Blit only on the rectangle that is invalid
CRect SourceRect((rcInvalid.left * BM.bmWidth) /
rcBounds.Width() + xp,
(rcInvalid.top * (rcBounds.Height() / Strech)) /
rcBounds.Height() + yp,
(rcInvalid.right * BM.bmWidth) / rcBounds.Width()
+ xp, (rcInvalid.bottom * (rcBounds.Height() /
Strech)) / rcBounds.Height() + yp);
pdc->StretchBlt(rcInvalid.left, rcInvalid.top,
rcInvalid.Width(), rcInvalid.Height(),&mdc,
SourceRect.left, SourceRect.top,
SourceRect.right - SourceRect.left,
SourceRect.bottom - SourceRect.top, SRCCOPY);
}
第二种情况或多或少与之前的步骤相同。 唯一的区别是,图片现在将在左右两侧被截断。 发生这种情况是因为要绘制的窗口的比例小于 1(相对于宽度和高度而言)。
else { Strech = double(rcBounds.Height()) / double(BM.bmHeight); int iHeight = Strech * BM.bmHeight; int iWidth = Strech * BM.bmWidth; int xMove = ((iWidth - rcBounds.Width()) / 2) / Strech; int xp = xMove - m_xAdd; int yp = -m_yAdd; if (xp < 0) xp = 0; if (yp < 0) yp = 0; if (xp > (iWidth - rcBounds.Width()) / Strech) xp = (iWidth - rcBounds.Width()) / Strech; if (yp > iHeight - rcBounds.Height()) yp = iHeight - rcBounds.Height();
同样,我决定允许一些对齐方式(到窗口的左侧或右侧边框)
if (m_Align == LEFT) xp = 0; else if (m_Align == RIGHT) xp = (iWidth - rcBounds.Width()) / Strech; m_xAdd = xMove - xp; m_yAdd = -yp; // Blit only on the rectangle that is invalid CRect SourceRect((rcInvalid.left * rcBounds.Width()/Strech) / rcBounds.Width() + xp, (rcInvalid.top * BM.bmHeight) / rcBounds.Height() + yp, (rcInvalid.right * rcBounds.Width()/Strech) / rcBounds.Width() + xp, (rcInvalid.bottom * BM.bmHeight) / rcBounds.Height() + yp); pdc->StretchBlt(rcInvalid.left, rcInvalid.top, rcInvalid.Width(), rcInvalid.Height(),&mdc, SourceRect.left, SourceRect.top, SourceRect.right - SourceRect.left, SourceRect.bottom - SourceRect.top, SRCCOPY); } }
绘制的最后一步是释放所有已使用的资源
mdc.SelectObject(pOld); mdc.DeleteDC(); }
本文将在未来几天内继续更新,一旦工作和学习之间有更多时间。