使用 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 时,最后一个参数应该有一个有效的位图掩码(如果一切正常)以供 ID2D1RenderTarget
的 FillOpacityMask
函数使用。
现在我们需要将 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++ Builder 人 TBitmap
,你可以找到转换指令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);
为了移动图像,我们必须使用 SetTransform
。 pBmpMask
是使用 HRGN_to_D2Bitmap
创建的 ID2D1Bitmap
。即:
ID2D1Bitmap* pBmpMask;
HRGN_to_D2Bitmap(bmpWidth, bmpHeight, hShape, pRT, &pBmpMask);
希望这对您有所帮助。
山姆
我需要使用 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 时,最后一个参数应该有一个有效的位图掩码(如果一切正常)以供 ID2D1RenderTarget
的 FillOpacityMask
函数使用。
现在我们需要将 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++ Builder 人 TBitmap
,你可以找到转换指令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);
为了移动图像,我们必须使用 SetTransform
。 pBmpMask
是使用 HRGN_to_D2Bitmap
创建的 ID2D1Bitmap
。即:
ID2D1Bitmap* pBmpMask;
HRGN_to_D2Bitmap(bmpWidth, bmpHeight, hShape, pRT, &pBmpMask);
希望这对您有所帮助。
山姆