문제

The question is very similar to this, but that one didn't get answered yet. My question is, I have a D2D DXGI RenderTarget from d2dfactory->CreateDxgiSurfaceRenderTarget(), and I want to save its content to an image file using WIC. I was just reading this and this, so it looks to me that I can not just create a ID2D1Bitmap on a WIC render target and use ID2D1Bitmap::CopyFromRenderTarget() to copy from the input render target I want to save, because they are using different resources. So here is what I came up with using ID2D1RenderTarget::CreateSharedBitmap():

HRESULT SaveRenderTargetToFile(
    ID2D1RenderTarget* pRTSrc,
    LPCWSTR uri
    )
{
    HRESULT hr = S_OK;

    ComPtr<IWICBitmap> spWICBitmap;
    ComPtr<ID2D1RenderTarget> spRT;
    ComPtr<IWICBitmapEncoder> spEncoder;
    ComPtr<IWICBitmapFrameEncode> spFrameEncode;
    ComPtr<IWICStream> spStream;

    //
    // Create WIC bitmap to save and associated render target
    //

    UINT bitmapWidth = static_cast<UINT>(pRTSrc->GetSize().width + .5f);
    UINT bitmapHeight = static_cast<UINT>(pRTSrc->GetSize().height + .5f);

    HR(m_spWICFactory->CreateBitmap(
        bitmapWidth,
        bitmapHeight,
        GUID_WICPixelFormat32bppPBGRA,
        WICBitmapCacheOnLoad,
        &spWICBitmap
        ));

    D2D1_RENDER_TARGET_PROPERTIES prop = D2D1::RenderTargetProperties();
    prop.pixelFormat = D2D1::PixelFormat(
        DXGI_FORMAT_B8G8R8A8_UNORM,
        D2D1_ALPHA_MODE_PREMULTIPLIED
        );
    prop.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
    prop.usage = D2D1_RENDER_TARGET_USAGE_NONE;
    HR(m_spD2D1Factory->CreateWicBitmapRenderTarget(
        spWICBitmap,
        prop,
        &spRT
        ));

    //
    // Create a shared bitmap from this RenderTarget
    //
    ComPtr<ID2D1Bitmap> spBitmap;
    D2D1_BITMAP_PROPERTIES bp = D2D1::BitmapProperties();
    bp.pixelFormat = prop.pixelFormat;

    HR(spRT->CreateSharedBitmap(
        __uuidof(IWICBitmap),
        static_cast<void*>(spWICBitmap.GetRawPointer()),
        &bp,
        &spBitmap
        ));    // <------------------------- This fails with E_INVALIDARG

    //
    // Copy the source RenderTarget to this bitmap
    //
    HR(spBitmap->CopyFromRenderTarget(nullptr, pRTSrc, nullptr));

    //
    // Draw this bitmap to the output render target
    //

    spRT->BeginDraw();
    spRT->Clear(D2D1::ColorF(D2D1::ColorF::GreenYellow));
    spRT->DrawBitmap(spBitmap);
    HR(spRT->EndDraw());

    //
    // Save image to file
    //

    HR(m_spWICFactory->CreateStream(&spStream));
    WICPixelFormatGUID format = GUID_WICPixelFormat32bppPBGRA;
    HR(spStream->InitializeFromFilename(uri, GENERIC_WRITE));

    HR(m_spWICFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, &spEncoder));
    HR(spEncoder->Initialize(spStream, WICBitmapEncoderNoCache));
    HR(spEncoder->CreateNewFrame(&spFrameEncode, nullptr));
    HR(spFrameEncode->Initialize(nullptr));
    HR(spFrameEncode->SetSize(bitmapWidth, bitmapHeight));
    HR(spFrameEncode->SetPixelFormat(&format));
    HR(spFrameEncode->WriteSource(spWICBitmap, nullptr));
    HR(spFrameEncode->Commit());
    HR(spEncoder->Commit());
    HR(spStream->Commit(STGC_DEFAULT));

done:
    return hr;
}

Anything wrong with this code? (I'm sure there's a lot :)) Somewhere on MSDN it says that WIC render target only supports software mode, while DXGI render target only supports hardware mode. Is this the reason why the above call to CreateSharedBitmap() fails? How should I save a DXGI surface content to an image file with D2D then?

도움이 되었습니까?

해결책

  1. With some limitations, you can use D3DX11SaveTextureToFile. Use QI on your surface to get the ID3D11Resource.

  2. On the same page they are recommending DirectXTex library as a replacement, CaptureTexture then SaveToXXXFile (where XXX is WIC, DDS, or TGA). So that's another option.

  3. Also, if your surface has been created as GDI compatible, you can use IDXGISurface1::GetDC. (Use QI on your IDXGISurface to get the IDXGISurface1). Saving DC to a file is left as an exercise to the reader.

Remember to use the Debug Layer for help with cryptic return codes like E_INVALIDARG.

다른 팁

You could try this (I haven't):

  • Make your old DXGISurface.
  • Make an auxiliary ID2D1DeviceContext render target.
  • Use ID2D1DeviceContext::CreateBitmapFromDxgiSurface to create an ID2D1Bitmap1 associated to the DXGI surface.
  • Draw on your DXGISurface. You should get the same on the ID2D1Bitmap1.
  • Use ID2D1Bitmap1::Map to get a memory pointer to the pixeldata.
  • Copy the pixeldata to file, or to a wicbitmap for encoding (jpeg, tiff, etc.)

Perhaps this:(succeed running)

D2DFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperies(D2D1_RENDER_TARGET_TYPE_SOFTWARE,D2D1::Pixel Format(DXGI_FORMAT_B8G8R8A8_UNORM,D2D1_ALPHA_MODE_PREMULTIPLIED)), ……)

your RenderTarget should be set the static of SOFTWARE the same as WICRenderTarget.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top