You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 16:15:04