locked
Creating a WIC bitmap

    Question

  • I have picked a file from file picker, and now I have StorageFile^.

    I know that I can not call CreateDecoderFromFilename because of sandbox restrictions.

    But I am not able to figure out how do I call CreateDecoderFromStream using StorageFile ^ i.e. how do I obtain IStream* using StorageFile^.

    Could someone please help? Thanks in advance.

    Wednesday, April 24, 2013 2:16 PM

Answers

  • Step 1: Create a Blank Windows Store C++ App named App4

    Step 2: Open MainPage.xaml and drag a Button control and an Image control on to it; they should not overlap.

    Step 3: In the XAML, assign the Button the attribute x:Name="FilePickerButton" .

    Step 4: In the XAML, assign the Image the attribute x:Name="DisplayImage" .

    Step 5: Double click on the FilePickerButton Button in the designer so that it creates a Click event handler named FilePickerButton_Click .

    Step 6: Replace the Main.xaml.cpp contents with the following:

    // Licensed under the Microsoft Limited Public License (Ms-LPL).
    //
    // MainPage.xaml.cpp
    // Implementation of the MainPage class.
    //
    
    #include "pch.h"
    #include "MainPage.xaml.h"
    #include <ppltasks.h>
    #include <concrt.h>
    #include <wincodec.h>
    #include <wrl.h>
    #include <robuffer.h>
    
    using namespace App4;
    
    using namespace concurrency;
    using namespace Microsoft::WRL;
    using namespace Windows::Storage;
    using namespace Windows::Storage::Streams;
    
    using namespace Platform;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Controls::Primitives;
    using namespace Windows::UI::Xaml::Data;
    using namespace Windows::UI::Xaml::Input;
    using namespace Windows::UI::Xaml::Media;
    using namespace Windows::UI::Xaml::Navigation;
    
    // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
    
    MainPage::MainPage()
    {
    	InitializeComponent();
    }
    
    /// <summary>
    /// Invoked when this page is about to be displayed in a Frame.
    /// </summary>
    /// <param name="e">Event data that describes how this page was reached.  The Parameter
    /// property is typically used to configure the page.</param>
    void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
    {
    	(void) e;	// Unused parameter
    }
    
    
    void MainPage::FilePickerButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
    	auto filePicker = ref new Windows::Storage::Pickers::FileOpenPicker();
    	filePicker->FileTypeFilter->Append(L".png");
    	filePicker->CommitButtonText = L"Open File";
    
    	create_task(filePicker->PickSingleFileAsync()).then([this] (StorageFile^ file)
    	{
    		return file->OpenReadAsync();
    	}).then([this] (IRandomAccessStreamWithContentType^ inputStream)
    	{
    		auto size = static_cast<uint32>(inputStream->Size);
    		auto buffer = ref new Buffer(size);
    		return inputStream->ReadAsync(buffer, size, InputStreamOptions::None);
    	}).then([this] (IBuffer^ buffer)
    	{
    		auto dataReader = DataReader::FromBuffer(buffer);
    		auto byteArray = ref new Array<byte>(buffer->Length);
    		dataReader->ReadBytes(byteArray);
    
    		ComPtr<IWICImagingFactory> wicFactory;
    
    		HRESULT hr = CoCreateInstance( 
    			CLSID_WICImagingFactory, 
    			nullptr, 
    			CLSCTX_INPROC_SERVER, 
    			IID_PPV_ARGS(&wicFactory) 
    			);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		ComPtr<IWICStream> stream; 
    		hr = wicFactory->CreateStream(&stream); 
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		hr = stream->InitializeFromMemory( 
    			byteArray->Data, 
    			byteArray->Length 
    			);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		ComPtr<IWICBitmapDecoder> bitmapDecoder;
    
    		hr = wicFactory->CreateDecoderFromStream(
    			stream.Get(),
    			nullptr,
    			WICDecodeOptions::WICDecodeMetadataCacheOnDemand,
    			&bitmapDecoder
    			);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		// From here on the code is just something to demonstrate that it worked.
    		ComPtr<IWICBitmapFrameDecode> frameDecode;
    		hr = bitmapDecoder->GetFrame(0, &frameDecode);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		uint32 width, height;
    
    		hr = frameDecode->GetSize(&width, &height);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		auto writeableBitmap = ref new Windows::UI::Xaml::Media::Imaging::WriteableBitmap(width, height);
    
    		ComPtr<IBufferByteAccess> bufferByteAccess;
    
    		hr = (reinterpret_cast<IUnknown*>(writeableBitmap->PixelBuffer))->QueryInterface<IBufferByteAccess>(&bufferByteAccess);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		// Should not be freed; it doesn't belong to us.
    		byte* pixelData;
    
    		hr = bufferByteAccess->Buffer(&pixelData);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		frameDecode->CopyPixels(nullptr, width * 4, width * height * 4, pixelData);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		this->DisplayImage->Source = writeableBitmap;
    	});
    }
    

    Note that this is just an example and is not intended to be production grade code. It doesn't handle different BPPs nor different pixel formats, for example. It loaded every image I tried from multiple locations (specifically my Documents library and Pictures library) but did not display all of them correctly (some showed up just fine, others ended up in a gray scale with visual artifacts (it looked like a picture that was taken of something on an interlaced TV but it was a picture of a person in real life that looks normal outside of the example above).


    Visual C++ MVP | Website | Blog | @mikebmcl | Windows Store DirectX Game Template

    Thursday, April 25, 2013 9:07 AM

All replies

  • Have you tried using CreateDecoderFromFilename? It should work just fine.

    That said, the BasicLoader class and BasicReaderWriter class in the Marble Maze sample contain code showing how to load the data into a stream in order to use CreateDecoderFromStream - http://code.msdn.microsoft.com/windowsapps/DirectX-Marble-Maze-Game-e4806345 . Look at BasicLoader::LoadTextureAsync and then follow the various code that it calls.


    Visual C++ MVP | Website | Blog | @mikebmcl | Windows Store DirectX Game Template

    Wednesday, April 24, 2013 4:30 PM
  • CreateDecoderFromFilename has the same problem. If the file exists in your app package, it works otherwise it will not due to sandboxing restrictions. I have picked a file through file picker, which is outside the app's sandbox, therefore it does not work either.
    Wednesday, April 24, 2013 5:30 PM
  • Hi,

    How about copy image into localFolder, and use ms-appdata:///local/ to access them.

    Best regards,
    Jesse


    Jesse Jiang
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, April 25, 2013 2:03 AM
  • Copying the image is an option, but it does not seem to be a good idea. It is a headache for an application to copy and then delete the file from local folder just because APIs are not good enough.

    I don't understand this. Logically, when I picked a file through file picker, I have got access to it by user. So, why I can not access it through APIs like CreateDecoderFromFilename? How come it violates the sandboxing rules? Its just because APIs are faulty. Like Apple, it would be better if you also work on URI everywhere instead of paths and allow us to create URIs from file paths.

    Thursday, April 25, 2013 5:55 AM
  • Step 1: Create a Blank Windows Store C++ App named App4

    Step 2: Open MainPage.xaml and drag a Button control and an Image control on to it; they should not overlap.

    Step 3: In the XAML, assign the Button the attribute x:Name="FilePickerButton" .

    Step 4: In the XAML, assign the Image the attribute x:Name="DisplayImage" .

    Step 5: Double click on the FilePickerButton Button in the designer so that it creates a Click event handler named FilePickerButton_Click .

    Step 6: Replace the Main.xaml.cpp contents with the following:

    // Licensed under the Microsoft Limited Public License (Ms-LPL).
    //
    // MainPage.xaml.cpp
    // Implementation of the MainPage class.
    //
    
    #include "pch.h"
    #include "MainPage.xaml.h"
    #include <ppltasks.h>
    #include <concrt.h>
    #include <wincodec.h>
    #include <wrl.h>
    #include <robuffer.h>
    
    using namespace App4;
    
    using namespace concurrency;
    using namespace Microsoft::WRL;
    using namespace Windows::Storage;
    using namespace Windows::Storage::Streams;
    
    using namespace Platform;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Controls::Primitives;
    using namespace Windows::UI::Xaml::Data;
    using namespace Windows::UI::Xaml::Input;
    using namespace Windows::UI::Xaml::Media;
    using namespace Windows::UI::Xaml::Navigation;
    
    // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
    
    MainPage::MainPage()
    {
    	InitializeComponent();
    }
    
    /// <summary>
    /// Invoked when this page is about to be displayed in a Frame.
    /// </summary>
    /// <param name="e">Event data that describes how this page was reached.  The Parameter
    /// property is typically used to configure the page.</param>
    void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
    {
    	(void) e;	// Unused parameter
    }
    
    
    void MainPage::FilePickerButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
    	auto filePicker = ref new Windows::Storage::Pickers::FileOpenPicker();
    	filePicker->FileTypeFilter->Append(L".png");
    	filePicker->CommitButtonText = L"Open File";
    
    	create_task(filePicker->PickSingleFileAsync()).then([this] (StorageFile^ file)
    	{
    		return file->OpenReadAsync();
    	}).then([this] (IRandomAccessStreamWithContentType^ inputStream)
    	{
    		auto size = static_cast<uint32>(inputStream->Size);
    		auto buffer = ref new Buffer(size);
    		return inputStream->ReadAsync(buffer, size, InputStreamOptions::None);
    	}).then([this] (IBuffer^ buffer)
    	{
    		auto dataReader = DataReader::FromBuffer(buffer);
    		auto byteArray = ref new Array<byte>(buffer->Length);
    		dataReader->ReadBytes(byteArray);
    
    		ComPtr<IWICImagingFactory> wicFactory;
    
    		HRESULT hr = CoCreateInstance( 
    			CLSID_WICImagingFactory, 
    			nullptr, 
    			CLSCTX_INPROC_SERVER, 
    			IID_PPV_ARGS(&wicFactory) 
    			);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		ComPtr<IWICStream> stream; 
    		hr = wicFactory->CreateStream(&stream); 
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		hr = stream->InitializeFromMemory( 
    			byteArray->Data, 
    			byteArray->Length 
    			);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		ComPtr<IWICBitmapDecoder> bitmapDecoder;
    
    		hr = wicFactory->CreateDecoderFromStream(
    			stream.Get(),
    			nullptr,
    			WICDecodeOptions::WICDecodeMetadataCacheOnDemand,
    			&bitmapDecoder
    			);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		// From here on the code is just something to demonstrate that it worked.
    		ComPtr<IWICBitmapFrameDecode> frameDecode;
    		hr = bitmapDecoder->GetFrame(0, &frameDecode);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		uint32 width, height;
    
    		hr = frameDecode->GetSize(&width, &height);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		auto writeableBitmap = ref new Windows::UI::Xaml::Media::Imaging::WriteableBitmap(width, height);
    
    		ComPtr<IBufferByteAccess> bufferByteAccess;
    
    		hr = (reinterpret_cast<IUnknown*>(writeableBitmap->PixelBuffer))->QueryInterface<IBufferByteAccess>(&bufferByteAccess);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		// Should not be freed; it doesn't belong to us.
    		byte* pixelData;
    
    		hr = bufferByteAccess->Buffer(&pixelData);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		frameDecode->CopyPixels(nullptr, width * 4, width * height * 4, pixelData);
    
    		if (FAILED(hr))
    		{
    			throw Exception::CreateException(hr);
    		}
    
    		this->DisplayImage->Source = writeableBitmap;
    	});
    }
    

    Note that this is just an example and is not intended to be production grade code. It doesn't handle different BPPs nor different pixel formats, for example. It loaded every image I tried from multiple locations (specifically my Documents library and Pictures library) but did not display all of them correctly (some showed up just fine, others ended up in a gray scale with visual artifacts (it looked like a picture that was taken of something on an interlaced TV but it was a picture of a person in real life that looks normal outside of the example above).


    Visual C++ MVP | Website | Blog | @mikebmcl | Windows Store DirectX Game Template

    Thursday, April 25, 2013 9:07 AM