【问题标题】:Drawing and creating non-rectangular bitmaps using Direct2d使用 Direct2d 绘制和创建非矩形位图
【发布时间】:2016-09-14 02:36:41
【问题描述】:

我需要使用 Direct2D 绘制位图的一部分(不是矩形),如下图所示。就像在 Photoshop 中用户如何根据所选区域 (HRGN) 创建一个新图层并移动它一样。我能找到的最接近的例子是here,但我无法让它工作。有人知道怎么做吗?

谢谢

编辑

看起来人们误解了我想要实现的目标。这个新的动画 GIF 应该能更好地解释它。

【问题讨论】:

  • 必须抗锯齿吗?
  • 如果可以的话那就太好了,如果不行,我很乐意在没有抗锯齿的情况下显示它。
  • 你是如何在你的窗口中按原样显示它的?
  • 此时我正在使用非常慢的 GDI,当用户移动所选区域时它会闪烁。我正在尝试将其更新为 Direct2D。
  • 另外,您的目标是让它变得透明吗?有一个 API 函数可以根据左上角像素颜色(在您的情况下为深蓝色)使图像透明。

标签: c++ windows direct2d


【解决方案1】:

在经历了一百万次尝试和失败之后,我找到了答案。微软在herehere 中关于如何创建位图不透明蒙版的说明非常令人困惑,听起来您需要黑白图像作为蒙版,但这远非事实。我们真正需要的是一张只有 alpha 值信息的图像。我也找不到任何将HRGN 转换为ID2D1Bitmap 的指令。所以我必须想出我自己的,我将在这里分享给任何可能遇到同样问题的人。

void HRGN_to_D2Bitmap(const DWORD bmpWidth, const DWORD bmpHeight, HRGN hShape, ID2D1RenderTarget* pRT, ID2D1Bitmap** ppBmpMask)
{
    boost::scoped_ptr<unsigned char> pBmpBuff(new unsigned char[bmpWidth * bmpHeight]);
    DWORD dwCount;
    LPRGNDATA pRgnData;

    memset(pBmpBuff.get(), 0, bmpWidth * bmpHeight);
    dwCount = ::GetRegionData(hShape, 0, NULL);
    boost::scoped_ptr<unsigned char> pRgnBuff(new unsigned char[dwCount]);
    pRgnData = reinterpret_cast<LPRGNDATA>(pRgnBuff.get());
    if (dwCount == ::GetRegionData(hShape, dwCount, pRgnData))
    {
        DWORD i, y;
        RECT* pRect;
        D2D1_BITMAP_PROPERTIES bmpPorp;
        D2D1_SIZE_U bmpSize = D2D1::SizeU(bmpWidth, bmpHeight);

        pRect = (RECT*)pRgnData->Buffer;
        for (i = 0; i < pRgnData->rdh.nCount; i++)
        {
            for (y = (DWORD)pRect->top; ((y < (DWORD)pRect->bottom) && (y < bmpHeight)); y++)
                memset(&pBmpBuff.get()[(y * bmpWidth) + pRect->left], 0xFFFFFFFF, pRect->right - pRect->left);
            pRect++;
        }
        bmpPorp.dpiX = 0.0f;
        bmpPorp.dpiY = 0.0f;
        bmpPorp.pixelFormat.format = DXGI_FORMAT_A8_UNORM;
        bmpPorp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_STRAIGHT;
        pRT->CreateBitmap(bmpSize, pBmpBuff.get(), bmpWidth, bmpPorp, ppBmpMask);
    }
}

为简单起见,我省略了大部分错误检查。
当函数返回时,最后一个参数应该有一个有效的位图掩码(如果一切正常的话)供FillOpacityMaskID2D1RenderTarget 函数使用。
现在我们需要将我们的ID2D1Bitmap 变成ID2D1BitmapBrush

D2D1_BITMAP_BRUSH_PROPERTIES propertiesXClampYClamp = D2D1::BitmapBrushProperties(
                    D2D1_EXTEND_MODE_CLAMP,
                    D2D1_EXTEND_MODE_CLAMP,
                    D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
                    );

pRT->CreateBitmapBrush(pOrigBmp, propertiesXClampYClamp, &bkBrush);

pOrigBmp 是我们想要移动的图像,bkBrush 是指向ID2D1BitmapBrush 的指针。
如果您有 HBITMAPC++ Builder 人是 TBitmap,您可以找到将位图转换为 ID2D1Bitmap here 的说明。
现在每次你想画你的窗口,你可以简单地这样做:

pRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
pRT->FillOpacityMask(pBmpMask, bkBrush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS);
pRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);

为了移动图像,我们必须使用SetTransformpBmpMask 是使用 HRGN_to_D2Bitmap 创建的 ID2D1Bitmap。即:

ID2D1Bitmap* pBmpMask;

HRGN_to_D2Bitmap(bmpWidth, bmpHeight, hShape, pRT, &pBmpMask);

希望这会有所帮助。
山姆

【讨论】:

    【解决方案2】:

    好的,我明白了。你想让我把项目源发邮件给你吗?它是 C++ Builder 6,所以你的版本应该能够转换它。关于 TPanel 需要将 FullRepaint 设置为 false,我是对的。

    基本思路是在TPanel上放一个TImage,然后根据你要使用的图像的透明色“剪掉”panel的hRGN。此外,请务必将 TPanel 的“FullRepaint”属性设置为 false,否则您会看到您注意到的闪烁。这就是我为我制作的几款游戏制作移动动画的方式。

    【讨论】:

    • 另外,我把你的 PNG 做成了 BMP
    • 我也可以发布说明,但通过电子邮件发送可能对您来说更容易。
    • 感谢您的努力。对此,我真的非常感激。我已经更改了原始帖子以更好地解释问题。我已经有一个使用 GDI (TImage) 的工作版本,但是我正在使用的图像非常大,并且在 GDI 中执行它会产生闪烁问题。我希望将其升级到 Direct2D 以使浮动部分的运动更平滑且无闪烁。再次感谢您的努力。顺便说一句,Stackoverflow 没有私人消息系统,发布电子邮件地址只是自找麻烦。
    猜你喜欢
    • 2016-06-11
    • 2017-06-20
    • 2020-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    • 1970-01-01
    • 2014-07-15
    相关资源
    最近更新 更多