locked
Save rendered image to a disc file.

    Question

  • I have written a program to render an image on the screen in Metro and C++

    Now I would like to save the image to disc.

    Is there a way to capture the image and save it to a file ?


    n.Wright

    Friday, August 31, 2012 11:59 PM

Answers

  • I eventually worked out I needed shcore.lib added as a dependency.

    The linker was bombing out with an error previously.


    n.Wright

    Sunday, September 23, 2012 1:31 AM

All replies

  • It depends on what you mean by rendering an image on the screen.

    There is no way to capture a bitmap from a Xaml UIElement, but if you are rendering onto a DirectX surface or a Xaml WriteableBitmap then you can access the pixels directly, pass them to a BitmapEncoder, and then write that out to disk.

    --Rob

    Saturday, September 01, 2012 12:15 AM
    Owner
  • I am using direct2d to render to the screen.

    I found an example of writing the screen to a file but it keeps crashing out with an error on:

    Error 1 error LNK2001: unresolved external symbol _CreateStreamOverRandomAccessStream@12 C:\p200\Simple_PCBCAD\Simple_PCBCAD\Simple_PCBCAD\SimpleTextRenderer.obj Simple_PCBCAD

    I found I needed #include <shcore.h> but the linker has a problem finding the createstreamoverrandomaccessstream.

    		
    		void SimpleTextRenderer::writer()
    		{
    		
    
        
        Render();
        Present();
    
        // Prepare a file picker for customers to input image file name.
        Pickers::FileSavePicker^ savePicker = ref new Pickers::FileSavePicker();
        auto pngExtensions = ref new Platform::Collections::Vector<Platform::String^>();
        pngExtensions->Append(".png");
        savePicker->FileTypeChoices->Insert("PNG file", pngExtensions);
        auto jpgExtensions = ref new Platform::Collections::Vector<Platform::String^>();
        jpgExtensions->Append(".jpg");
        savePicker->FileTypeChoices->Insert("JPEG file", jpgExtensions);
        auto bmpExtensions = ref new Platform::Collections::Vector<Platform::String^>();
        bmpExtensions->Append(".bmp");
        savePicker->FileTypeChoices->Insert("BMP file", bmpExtensions);
        savePicker->DefaultFileExtension = ".png";
        savePicker->SuggestedFileName = "SaveScreen";
        savePicker->SuggestedStartLocation = Pickers::PickerLocationId::PicturesLibrary;
    
        std::shared_ptr<GUID> wicFormat = std::make_shared<GUID>(GUID_ContainerFormatPng);
    
        create_task(savePicker->PickSaveFileAsync()).then([=](StorageFile^ file)
        {
           
    
            Platform::String^ m_imageFileName = file->Name;
            if (file->FileType == ".bmp")
            {
                *wicFormat = GUID_ContainerFormatBmp;
            }
            else if (file->FileType == ".jpg")
            {
                *wicFormat = GUID_ContainerFormatJpeg;
            }
            return file->OpenAsync(FileAccessMode::ReadWrite);
    
        }).then([=](Streams::IRandomAccessStream^ randomAccessStream)
        {
            // Convert the RandomAccessStream to an IStream.
            ComPtr<IStream> stream;
            DX::ThrowIfFailed(
                CreateStreamOverRandomAccessStream(randomAccessStream, IID_PPV_ARGS(&stream))
                );
    
            // Render the screen contents to an off-screen bitmap and then save that bitmap to a file.
            ComPtr<ID2D1Bitmap1> targetBitmap;
            D2D1_SIZE_U pixelSize = m_d2dContext->GetPixelSize();
    
            DX::ThrowIfFailed(
                m_d2dContext->CreateBitmap(
                    pixelSize,
                    nullptr,
                    pixelSize.width * 4,    // pitch = width * size of pixel (4 bytes for B8G8R8A8)
                    D2D1::BitmapProperties1(
                        D2D1_BITMAP_OPTIONS_TARGET,
                        D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)
                        ),
                    &targetBitmap
                    )
                );
    
            m_d2dContext->SetTarget(targetBitmap.Get());
            Render();
            m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
    
            SaveBitmapToStream(targetBitmap, m_wicFactory, m_d2dContext, *wicFormat, stream.Get());
    
           
            Render();
            Present();
        });
    
    
    
    		}
    
    
    
    
    		
    // Save render target bitmap to a stream using WIC.
    void SaveBitmapToStream(
        _In_ ComPtr<ID2D1Bitmap1> d2dBitmap,
        _In_ ComPtr<IWICImagingFactory2> wicFactory2,
        _In_ ComPtr<ID2D1DeviceContext> d2dContext,
        _In_ REFGUID wicFormat,
        _In_ IStream* stream
        )
    {
        // Create and initialize WIC Bitmap Encoder.
        ComPtr<IWICBitmapEncoder> wicBitmapEncoder;
        DX::ThrowIfFailed(
            wicFactory2->CreateEncoder(
                wicFormat,
                nullptr,    // No preferred codec vendor.
                &wicBitmapEncoder
                )
            );
    
        DX::ThrowIfFailed(
            wicBitmapEncoder->Initialize(
                stream,
                WICBitmapEncoderNoCache
                )
            );
    
        // Create and initialize WIC Frame Encoder.
        ComPtr<IWICBitmapFrameEncode> wicFrameEncode;
        DX::ThrowIfFailed(
            wicBitmapEncoder->CreateNewFrame(
                &wicFrameEncode,
                nullptr     // No encoder options.
                )
            );
    
        DX::ThrowIfFailed(
            wicFrameEncode->Initialize(nullptr)
            );
    
        // Retrieve D2D Device.
        ComPtr<ID2D1Device> d2dDevice;
        d2dContext->GetDevice(&d2dDevice);
    
        // Create IWICImageEncoder.
        ComPtr<IWICImageEncoder> imageEncoder;
        DX::ThrowIfFailed(
            wicFactory2->CreateImageEncoder(
                d2dDevice.Get(),
                &imageEncoder
                )
            );
    
        DX::ThrowIfFailed(
            imageEncoder->WriteFrame(
                d2dBitmap.Get(),
                wicFrameEncode.Get(),
                nullptr     // Use default WICImageParameter options.
                )
            );
    
        DX::ThrowIfFailed(
            wicFrameEncode->Commit()
            );
    
        DX::ThrowIfFailed(
            wicBitmapEncoder->Commit()
            );
    
        // Flush all memory buffers to the next-level storage object.
        DX::ThrowIfFailed(
            stream->Commit(STGC_DEFAULT)
            );
    }
    


    n.Wright

    Saturday, September 01, 2012 2:08 AM
  • I eventually worked out I needed shcore.lib added as a dependency.

    The linker was bombing out with an error previously.


    n.Wright

    Sunday, September 23, 2012 1:31 AM