C++将摄像头BYTE*数据转ID2D1Image*实现Direct2D交叉淡入淡出效果
解决方案
1. RGB32 BYTE数组转ID2D1Image*实现
由于ID2D1Bitmap接口继承自ID2D1Image,你只需要将BYTE数组的帧数据转换成ID2D1Bitmap对象,即可直接传入SetInput方法作为效果输入。
代码修改步骤:
首先修改CrossFade类定义,新增成员变量存储帧宽高和对应的D2D位图对象:
// CrossFade.h修改部分 class CrossFade { ID2D1Factory* pD2DFactory = nullptr; ID2D1HwndRenderTarget* pRT = nullptr; int m_width = 0; int m_height = 0; ID2D1Bitmap* m_pSrcBitmap = nullptr; ID2D1Bitmap* m_pDstBitmap = nullptr; public: BYTE * srcData = nullptr; BYTE * dstData = nullptr; CrossFade(HWND hwndPgm, int iWidth, int iHeight); ~CrossFade(); void Draw(float dWeight); // 新增帧数据更新方法,每次摄像头帧刷新时调用 void UpdateFrames(); };
然后实现CreateBitmap和数据拷贝逻辑:
// CrossFade.cpp修改部分 CrossFade::CrossFade(HWND hwndPgm, int iWidth, int iHeight) : m_width(iWidth), m_height(iHeight) { HRESULT hr = S_OK; hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory); if (FAILED(hr)) return; hr = pD2DFactory->CreateHwndRenderTarget( D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(hwndPgm, D2D1::SizeU(iWidth, iHeight)), &pRT ); if (FAILED(hr)) return; // 初始化两个空位图,像素格式对应RGB32(B8G8R8A8顺序,忽略Alpha通道) D2D1_BITMAP_PROPERTIES bitmapProps = D2D1::BitmapProperties( D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE) ); pRT->CreateBitmap(D2D1::SizeU(iWidth, iHeight), nullptr, 0, bitmapProps, &m_pSrcBitmap); pRT->CreateBitmap(D2D1::SizeU(iWidth, iHeight), nullptr, 0, bitmapProps, &m_pDstBitmap); } CrossFade::~CrossFade() { // 释放COM资源 if (m_pSrcBitmap) m_pSrcBitmap->Release(); if (m_pDstBitmap) m_pDstBitmap->Release(); if (pRT) pRT->Release(); if (pD2DFactory) pD2DFactory->Release(); } void CrossFade::UpdateFrames() { if (!srcData || !dstData || !m_pSrcBitmap || !m_pDstBitmap) return; // 每行字节数,RGB32格式每像素4字节,步长=宽度*4 UINT pitch = m_width * 4; m_pSrcBitmap->CopyFromMemory(nullptr, srcData, pitch); m_pDstBitmap->CopyFromMemory(nullptr, dstData, pitch); } void CrossFade::Draw(float dWeight) { if (!m_pSrcBitmap || !m_pDstBitmap) return; UpdateFrames(); CComQIPtr<ID2D1DeviceContext> spDeviceContext = pRT; if (!spDeviceContext) return; pRT->BeginDraw(); pRT->Clear(D2D1::ColorF(D2D1::ColorF::Black)); CComPtr<ID2D1Effect> spCrossFadeEffect; HRESULT hr = spDeviceContext->CreateEffect(CLSID_D2D1CrossFade, &spCrossFadeEffect); if (SUCCEEDED(hr)) { spCrossFadeEffect->SetInput(0, m_pSrcBitmap); spCrossFadeEffect->SetInput(1, m_pDstBitmap); spCrossFadeEffect->SetValue(D2D1_CROSSFADE_PROP_WEIGHT, dWeight); spDeviceContext->DrawImage(spCrossFadeEffect); } hr = pRT->EndDraw(); if (hr == D2DERR_RECREATE_TARGET) { // 渲染目标丢失时重建资源,这里可以补充重建逻辑 } }
注意:如果你的RGB32数据是RGBA顺序,将像素格式改为DXGI_FORMAT_R8G8B8A8_UNORM即可
2. 非MFC平滑过渡动画实现
你遇到的头文件报错是因为afxanimationcontroller.h是MFC专属组件,非MFC项目无需使用该头文件,可选用以下两种轻量方案实现动画:
- 方案1:自定义定时器插值实现(最简单,适配C++/CLI项目)
你可以使用C++/CLI的托管定时器System::Timers::Timer,设置间隔为16ms(对应60FPS刷新),用时间戳计算当前过渡权重:// 伪代码示例,可放在你的托管逻辑部分 System::Timers::Timer^ animTimer = gcnew System::Timers::Timer(16); float animDuration = 1.0f; // 过渡总时长1秒 ULONGLONG startTime = GetTickCount64(); animTimer->Elapsed += gcnew System::Timers::ElapsedEventHandler([&](Object^ sender, System::Timers::ElapsedEventArgs^ e) { float elapsed = (GetTickCount64() - startTime) / 1000.0f; float weight = min(1.0f, elapsed / animDuration); crossFadeInstance->Draw(weight); if (weight >= 1.0f) animTimer->Stop(); }); animTimer->Start(); - 方案2:使用原生Windows Animation Manager
这是微软官方的原生动画API,无需MFC依赖,引入头文件UIAnimation.h,链接uianimation.lib即可使用,可实现更复杂的缓动动画效果。
内容的提问来源于stack exchange,提问作者Travelinman
相关产品推荐
相关产品推荐

