65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconemptyStarIconemptyStarIcon

3.00/5 (5投票s)

2002 年 11 月 7 日

2分钟阅读

viewsIcon

107067

downloadIcon

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();
}

本文将在未来几天内继续更新,一旦工作和学习之间有更多时间。

© . All rights reserved.