Add a background colour to a MediaComposition
Asked Answered
A

1

10

I'm trying to create a MediaComposition. I have succeeded in combining multiple png images into a single video; however, the files that's created has a black background. At first I thought this might be because the files were png files, but the same bevaviour occurs for jpgs. The following is how I'm saving the image:

public async Task<bool> Save(InkCanvas canvas, StorageFile file)
{

    if (canvas != null && canvas.InkPresenter.StrokeContainer.GetStrokes().Count > 0)
    {
        if (file != null)
        {
            using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
            {
                await canvas.InkPresenter.StrokeContainer.SaveAsync(stream);
            }
        }
        Clear(canvas);
        return true;
    }
    return false;
}

It saves the image fine, but the background is alpha. What this means is that when I try and chain these together into a media composition, there is no background, and it renders as black. I have tried using overlays when creating the MediaComposition to correct this:

MediaClip overlayVideoClip = MediaClip.CreateFromColor(Colors.White, new TimeSpan(0, 1, 0));
MediaOverlay mo = new MediaOverlay(overlayVideoClip);
MediaOverlayLayer mol = new MediaOverlayLayer();
mol.Overlays.Add(mo);

composition.OverlayLayers.Add(mol);

But to no avail. My suspicion is that I'm misunderstanding the meaning of the term overlay in this case. So, my questions are: is it possible to overlay the video at composition time and, if so, how? Alternatively, if this needs to be done in the image itself, how can I save the image with a background?

EDIT:

I've made progress (?) with this; the following compiles and runs, but creates a solid black image:

    public async Task TestSave(InkCanvas canvas, StorageFile file)
    {
        RenderTargetBitmap rtb = 
           new RenderTargetBitmap();
        PixelFormats.Pbgra32);
        await rtb.RenderAsync(canvas);
        var pixelBuffer = await rtb.GetPixelsAsync();

        using (IRandomAccessStream stream = 
             await file.OpenAsync(FileAccessMode.ReadWrite))
        {                
            BitmapEncoder encoder = 
                 await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);

            encoder.SetPixelData(BitmapPixelFormat.Bgra8,
                BitmapAlphaMode.Straight,
                (uint)rtb.PixelWidth,
                (uint)rtb.PixelHeight,
                96d, 96d,
                pixelBuffer.ToArray());

            await encoder.FlushAsync();                
        }
    }

EDIT:

I found this answer, which sort of solves the problem by using the Win2D library; although it doesn't address my actual issue, it lets me let around it. Hopefully there is a better solution out there.

Abradant answered 23/11, 2015 at 18:13 Comment(2)
Not clear what you are trying to do. Just save the image with no alpha background?Infatuate
Either save the image with a background, or layer the background in at the time of creating the video file (currently using MediaComposition)Abradant
I
5

The only thing I understand about this is you are trying to save an image with a background. My suggestion to solve this is to save the transparent image you have to something like

<StackPanel x:Name="AreaWhichWillBeSavedToImage" Background=*Some Color*>
  <Image x:Name="theAlphaImage">
</StackPanel>

Now if you don't want the image to display on your GUI just set it to Hidden.

Then you can save the file with the color background of your choice.

var bitmap = await SaveToFileAsync(AreaWhichWillBeSavedToImage, await StorageFile.GetFileFromPathAsync(Windows.ApplicationModel.Package.Current.InstalledLocation.Path + @"someimage.jpg"));

  async Task<RenderTargetBitmap> SaveToFileAsync(FrameworkElement uielement, StorageFile file)
    {
        if (file != null)
        {
            CachedFileManager.DeferUpdates(file);

            Guid encoderId = GetBitmapEncoder(file.FileType);

            try
            {
                using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
                {
                    return await CaptureToStreamAsync(uielement, stream, encoderId);
                }
            }
            catch (Exception ex)
            {
                //DisplayMessage(ex.Message);
            }

            var status = await CachedFileManager.CompleteUpdatesAsync(file);
        }

        return null;
    }

    async Task<RenderTargetBitmap> CaptureToStreamAsync(FrameworkElement uielement, IRandomAccessStream stream, Guid encoderId)
    {
        try
        {
            var renderTargetBitmap = new RenderTargetBitmap();
            await renderTargetBitmap.RenderAsync(uielement);

            var pixels = await renderTargetBitmap.GetPixelsAsync();

            var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
            var encoder = await BitmapEncoder.CreateAsync(encoderId, stream);
            encoder.SetPixelData(
                BitmapPixelFormat.Bgra8,
                BitmapAlphaMode.Ignore,
                (uint)renderTargetBitmap.PixelWidth,
                (uint)renderTargetBitmap.PixelHeight,
                logicalDpi,
                logicalDpi,
                pixels.ToArray());

            await encoder.FlushAsync();

            return renderTargetBitmap;
        }
        catch (Exception ex)
        {
            //DisplayMessage(ex.Message);
        }

        return null;
    }

    Guid GetBitmapEncoder(string fileType)
    {
        Guid encoderId = BitmapEncoder.JpegEncoderId;
        switch (fileType)
        {
            case ".bmp":
                encoderId = BitmapEncoder.BmpEncoderId;
                break;
            case ".gif":
                encoderId = BitmapEncoder.GifEncoderId;
                break;
            case ".png":
                encoderId = BitmapEncoder.PngEncoderId;
                break;
            case ".tif":
                encoderId = BitmapEncoder.TiffEncoderId;
                break;
        }

        return encoderId;
    }
Infatuate answered 1/12, 2015 at 18:5 Comment(4)
Save InkCanvas Image as theAlphaImage's ImageSource, then it worksInfatuate
If you mean like this: <Image x:Name="theAlphaImage" Source="{Binding ElementName=myInkCanvas}"> then it doesn't seem to workAbradant
You are saving your inkCanvas to a IRandomAccessStream. That stream is easily converted into a Render Bitmap or WritableBitmap etc. Once you convert it to a type of bitmap, you can set the image source to the bitmapInfatuate
Whilst this does work (many thanks), the image only saves when it is visible in the visual tree. Otherwise, it just saves as blank.Abradant

© 2022 - 2024 — McMap. All rights reserved.