Video

How to apply an “Opacity Mask to an image in Universal apps using Direct2D

Introduction

We use Direct2D to implement this image effect which we often use in Photoshop. This sample is based on James Dailey’s code sample. See More Information below.

Running the Sample

 Build the sample in Visual Studio 2013, and then run it. Click the “Open the Image” button to open the image which will be processed. Then select the mask image from “Select mask image here” Combobox. We offer you 3 mask images for testing. When finishing these operations, we can click “Draw Opacity Mask” to apply the mask to the image. The running application is shown below.

Using the Code

•  We add the D2DImageSourceWithOpacityMask project to our Xaml project solution and add reference to the Xaml project.

•  In our Xaml code behind, we call the methods from D2DImageSourceWithOpacityMask namespace in which we implement the opacity mask effect applied for image. We should first set the image source and mask image source:

C#C++
Edit|Remove
m_d2dImageSource.SetSource(stream); 
m_d2dImageSource.SetMask(stream1); 
 

•  Here we get the original image randomAccessStream from the file picker.

C#C++
Edit|Remove
private async void OpenImageBtn_Click(object sender, RoutedEventArgs e) 
        { 
            FileOpenPicker openPicker = new FileOpenPicker(); 
            openPicker.ViewMode = PickerViewMode.Thumbnail; 
            openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary; 
            openPicker.FileTypeFilter.Add(".jpg"); 
            openPicker.FileTypeFilter.Add(".png"); 
            openPicker.FileTypeFilter.Add(".jpeg"); 
            StorageFile file = await openPicker.PickSingleFileAsync(); 
            if(file != null) 
            {                 
                var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read); 
                m_d2dImageSource.SetSource(stream); 
                var bitmapImage = new Windows.UI.Xaml.Media.Imaging.BitmapImage(); 
                await bitmapImage.SetSourceAsync(stream); 
                OrignalImage.Source = bitmapImage; 
                ImageProperties imageProperties = await file.Properties.GetImagePropertiesAsync(); 
                OriImageSize.Text = "Image size: " + imageProperties.Width + " * " + imageProperties.Height; 
                _btnRender.IsEnabled = true; 
            } 
        } 
 

•  We get the mask image randomAccessStream from uri.

C#C++
Edit|Remove
private async void MaskComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) 
        { 
            StorageFile file; 
            var bitmapImage = new Windows.UI.Xaml.Media.Imaging.BitmapImage(); 
            switch(MaskComboBox.SelectedIndex) 
            { 
                case 0: 
                    file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/BitmapMask.png")); 
                    var stream1 = await file.OpenAsync(Windows.Storage.FileAccessMode.Read); 
                    m_d2dImageSource.SetMask(stream1); 
                    await bitmapImage.SetSourceAsync(stream1); 
                    MaskImage.Source = bitmapImage; 
                    ImageProperties imageProperties1 = await file.Properties.GetImagePropertiesAsync(); 
                    MaskImageSize.Text = "Image size: " + imageProperties1.Width + " * " + imageProperties1.Height; 
                    break; 
                case 1: 
                    file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Mask1.png")); 
                    var stream2 = await file.OpenAsync(Windows.Storage.FileAccessMode.Read); 
                    m_d2dImageSource.SetMask(stream2); 
                    await bitmapImage.SetSourceAsync(stream2); 
                    MaskImage.Source = bitmapImage; 
                    ImageProperties imageProperties2 = await file.Properties.GetImagePropertiesAsync(); 
                    MaskImageSize.Text = "Image size: " + imageProperties2.Width + " * " + imageProperties2.Height; 
                    break; 
                case 2: 
                    file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Mask2.png")); 
                    var stream3 = await file.OpenAsync(Windows.Storage.FileAccessMode.Read); 
                    m_d2dImageSource.SetMask(stream3); 
                    await bitmapImage.SetSourceAsync(stream3); 
                    MaskImage.Source = bitmapImage; 
                    ImageProperties imageProperties3 = await file.Properties.GetImagePropertiesAsync(); 
                    MaskImageSize.Text = "Image size: " + imageProperties3.Width + " * " + imageProperties3.Height; 
                    break; 
            } 
        } 
 

•  We should then draw the image with the “Draw Opacity Mask” button being clicked.

C#C++
Edit|Remove
private void _btnRender_Click(object sender, RoutedEventArgs e) 
        { 
            // Begin updating the SurfaceImageSource 
            m_d2dImageSource.BeginDraw(); 
            // Clear background 
            m_d2dImageSource.Clear(Colors.Transparent); 
            // Render the source and apply the mask 
            m_d2dImageSource.RenderBitmap(); 
            // Stop updating the SurfaceImageSource and draw its contents 
            m_d2dImageSource.EndDraw(); 
        } 

•  In the D2DImageSourceWithOpacityMask namespace, we use WIC api to build Direct2D image from the IRandomAccessStream object.

C++
Edit|Remove
void D2DImageSource::SetMask(Windows::Storage::Streams::IRandomAccessStream^ randomAccessStream)
{
    ComPtr<IWICBitmapDecoder> wicBitmapDecoder;
    ComPtr<IStream> stream;
    DX::ThrowIfFailed(
        CreateStreamOverRandomAccessStream(randomAccessStream, IID_PPV_ARGS(&stream))
        );
    DX::ThrowIfFailed(
        m_wicFactory->CreateDecoderFromStream(
        stream.Get(),
        nullptr,
        WICDecodeMetadataCacheOnDemand,
        &wicBitmapDecoder
        )
        );
    ComPtr<IWICBitmapFrameDecode> wicBitmapFrame;
    DX::ThrowIfFailed(
        wicBitmapDecoder->GetFrame(0, &wicBitmapFrame)
        );
    ComPtr<IWICFormatConverter> wicFormatConverter;
    DX::ThrowIfFailed(
        m_wicFactory->CreateFormatConverter(&wicFormatConverter)
        );
    DX::ThrowIfFailed(
        wicFormatConverter->Initialize(
        wicBitmapFrame.Get(),
        GUID_WICPixelFormat32bppPBGRA,
        WICBitmapDitherTypeNone,
        nullptr,
        0.0,
        WICBitmapPaletteTypeCustom  // the BGRA format has no palette so this value is ignored
        )
        );
    double dpiX = 96.0f;
    double dpiY = 96.0f;
    DX::ThrowIfFailed(
        wicFormatConverter->GetResolution(&dpiX, &dpiY)
        );
    DX::ThrowIfFailed(
        m_d2dContext->CreateBitmapFromWicBitmap(
        wicFormatConverter.Get(),
        nullptr,
        &m_Mask  //D2Dbitmap
        )
    );
}

•  We run the same steps for the image which will be processed. Then we get the two ID2D1Bitmap1 objects : m_Bitmap and m_Mask.

•  Finally, we use ID2D1DeviceContext::FillOpacityMask method to apply the mask to image.

C++
Edit|Remove
void D2DImageSource::RenderBitmap()
{
    if (m_Mask)
    {
        Microsoft::WRL::ComPtr<ID2D1BitmapBrush> bmpBrsh;
        m_d2dContext->CreateBitmapBrush(m_Bitmap.Get(), &bmpBrsh);
        m_d2dContext->FillOpacityMask(m_Mask.Get(), bmpBrsh.Get());
    }
    else
    {
        m_d2dContext->DrawBitmap(m_Bitmap.Get());
    }
}
 

More Information

• How to apply an “Opacity Mask” to an image by mixing XAML and Direct2D

• Direct2D Opacity Mask


Microsoft All-In-One Code Framework is a free, centralized code sample library driven by developers' real-world pains and needs. The goal is to provide customer-driven code samples for all Microsoft development technologies, and reduce developers' efforts in solving typical programming tasks. Our team listens to developers’ pains in the MSDN forums, social media and various DEV communities. We write code samples based on developers’ frequently asked programming tasks, and allow developers to download them with a short sample publishing cycle. Additionally, we offer a free code sample request service. It is a proactive way for our developer community to obtain code samples directly from Microsoft.