An Hoa
An Hoa

Reputation: 1267

Direct 2D SVG Rendering in UWP

I am trying to adapt the Direct 2D SVG sample at https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/D2DSvgImage to draw SVG string instead of from file. All I did was replacing the code to open a stream from an Assets file. Here is the relevant excerpt:

void D2DSvgImageRenderer::CreateDeviceDependentResources()
{
    auto d2dContext = m_deviceResources->GetD2DDeviceContext();

    StorageFolder^ packageFolder = Windows::ApplicationModel::Package::Current->InstalledLocation;

    // Retrieve the SVG file from the app package.
    // ORIGINAL create_task(packageFolder->GetFileAsync("Assets\\drawing.svg")).then([=](StorageFile^ file)
    create_task([]()
    {
        // Open the SVG file for reading.
        // ORIGINAL return file->OpenAsync(FileAccessMode::Read);

        char *svg = "<svg><circle r=\"300\" cy=\"509\" cx=\"370\" style=\"fill:#ffff00;stroke:#000000;stroke-width:5\"/></svg>";
        auto stream = ref new Windows::Storage::Streams::InMemoryRandomAccessStream();
        auto writer = ref new Windows::Storage::Streams::DataWriter(stream);
        auto p = svg;
        while (*p != '\0')
        {
            writer->WriteByte((unsigned char)*p);
            p++;
        }
        create_task(writer->StoreAsync()).get();
        create_task(writer->FlushAsync()).get();
        return stream;
    }).then([=](IRandomAccessStream^ stream)
    {
        // Wrap the WinRT stream with a COM stream.
        ComPtr<IStream> iStream;
        DX::ThrowIfFailed(
            CreateStreamOverRandomAccessStream(
                stream,
                IID_PPV_ARGS(&iStream)
                )
            );

        // Parse the file stream into an SVG document.
        DX::ThrowIfFailed(
            d2dContext->CreateSvgDocument(
                iStream.Get(),
                D2D1::SizeF(sc_svgSize, sc_svgSize), // Create the document at a size of 500x500 DIPs.
                &m_svgDocument
                )
            );
    });
}

But I am always getting the inexplicable The parameter is incorrect exception and there is no way to debug as to why it is the case. (DirectX does NOT come with any way to debug or provide any hint why and which parameter is incorrect!?)

My educated guess is that the API never tell us the hidden fact that using InMemoryRandomAccessStream in CreateSvgDocument is strictly prohibited. How can I fix this?

Upvotes: 0

Views: 1205

Answers (1)

user7860670
user7860670

Reputation: 37488

Before throwing exception this code emits the following diagnostic:

D2D DEBUG ERROR - An error was encountered while parsing an SVG document.

When I tried to read stream content I've figured out that it is closed (by calling stream->Position; in the next lambda), that is the content is actually not there. Then I've looked into docs. From DataWriter::Close method reference:

Remarks

DataWriter takes ownership of the stream that is passed to its constructor. Calling this method also calls on the associated stream. After calling this method, calls to most other DataWriter methods will fail. If you do not want the associated stream to be closed when the reader closes, call DataWriter.DetachStream before calling this method.

So in your example writer will Close stream when it goes out of scope and SVG parser won't be able to read anything. So your code should look like this:

auto stream{ref new Windows::Storage::Streams::InMemoryRandomAccessStream{}};
auto writer{ref new Windows::Storage::Streams::DataWriter{stream}}; // takes ownership of stream
const auto & sz_svg{"<svg><circle r=\"300\" cy=\"509\" cx=\"370\" style=\"fill:#ffff00;stroke:#000000;stroke-width:5\"/></svg>"};
for(const char * p{sz_svg}; '\0' != *p; ++p)
{
    writer->WriteByte(static_cast< unsigned char >(*p));
}
create_task(writer->StoreAsync()).get(); // mandatory
//create_task(writer->FlushAsync()).get(); // not necessary since underlaying memory stream does not require flushing
writer->DetachStream(); // release ownership of stream, the value returned is not used since we already have stream variable

PS They should've written that remark for the constructor...

Upvotes: 1

Related Questions