使用 Direct2d 绘制和创建 non-rectangular 位图

Drawing and creating non-rectangular bitmaps using Direct2d

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

谢谢

Edit

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

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

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

经过一百万次尝试和失败之后,我找到了答案。 Microsoft 关于如何在 here and here 中创建位图不透明蒙版的说明非常混乱,听起来好像您需要黑白图像作为蒙版,但这与事实相去甚远。我们真正需要的是一张只有 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);
    }
}

为简单起见,我省略了大部分错误检查。
当函数 returns 时,最后一个参数应该有一个有效的位图掩码(如果一切正常)以供 ID2D1RenderTargetFillOpacityMask 函数使用。
现在我们需要将 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.
的指针 如果你有 HBITMAP 或者 C++ BuilderTBitmap,你可以找到转换指令ID2D1Bitmap here.
的位图 现在每次你想画你的window,你可以简单地这样做:

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

希望这对您有所帮助。
山姆