Home > Back-end >  Using WPF and GifBitmapEncoder to create GIF with 1-bit transparent background Has anyone done this?
Using WPF and GifBitmapEncoder to create GIF with 1-bit transparent background Has anyone done this?

Time:05-24

Ive been able to use WPF and GifBitmapEncoder to create GIF files in C# but they are without transparency. I can remove the black background in code using alpha techniques and display transparent background images on an Image in my MainWindow, but GIF doesnt support alpha transparency, so the GIF files end up with non transparent white backgrounds. I know GIF supports "1-bit transparent" backgrounds but I have no clue how to use WPF and GiBitmapEncoder to set this up. I searched StackOverflow, Bing, Google, DuckDuck etc. Seems my only conclusion is that Microsoft has ignored this important feature in GifBitmapEncoder. Am I Wrong? There are tons of online services that will do this conversion for free but that wont help me. If there is a way to get 1 bit transparency by dinking with the palettes, ColorProfiles or MetaData or Codec it sure is well hidden information.

For what its worth heres what I tried: (Im reading in a gif with black background into a list of BitmapSources, then rendering those back out to a another GIF after performing useless alpha transparency.)

    GifBitmapDecoder GifDC = new GifBitmapDecoder(imageStreamSource, BitmapCreateOptions.DelayCreation,                     BitmapCacheOption.Default);
    List<BitmapSource> listBmpSources = new List<BitmapSource>();

    foreach (var thisBF in GifDC.Frames)
            {
                //Extract pixels for this BitmapFrame from dropped file
                byte[] thesePixels = new byte[thisBF.PixelHeight * thisBF.PixelWidth];
                thisBF.CopyPixels(thesePixels, thisBF.PixelWidth, 0);

                //Construct new BitmapSource list with Black set to Transparent
                List<System.Windows.Media.Color> thisListColors = new List<System.Windows.Media.Color>();
                SetBlackAsTransparent(thisBF, thisListColors);
                BitmapPalette thisBmpPalette = new BitmapPalette(thisListColors);
                BitmapSource thisBitmapSourceImage = BitmapSource.Create(thisBF.PixelWidth, thisBF.PixelHeight, thisBF.DpiX, 
                    thisBF.DpiY, thisBF.Format, thisBmpPalette, thesePixels, thisBF.PixelWidth);

                //Store bmp sources for GIFEncoder
                listBmpSources.Add(thisBitmapSourceImage);
            }

//Useless alpha transparency that doesnt survive rendering out to a Gif file:
    private void SetBlackAsTransparent(BitmapSource thisBF, List<System.Windows.Media.Color> listSWMC)
        {
            foreach (var thisColor in thisBF.Palette.Colors)
            {
                {
                    if (thisColor == Colors.Black)
                    {
                        listSWMC.Add(Colors.Transparent);
                        continue;
                    }

                    if (Feather(5, thisColor, listSWMC)) continue;
                    if (Feather(10, thisColor, listSWMC)) continue;
                    if (Feather(15, thisColor, listSWMC)) continue;
                    if (Feather(25, thisColor, listSWMC)) continue;
                    if (Feather(50, thisColor, listSWMC)) continue;
                    listSWMC.Add(thisColor);
                }
            }
        }

  void EncodeGifFile(List<BitmapSource> pListBmpSources, string pSaveAs)
        {
            GifBitmapEncoder encoder = new GifBitmapEncoder();
            
            foreach (var thisBmpSource in pListBmpSources)
            {   
                var thisBmpFrame = BitmapFrame.Create(thisBmpSource);
                encoder.Palette = thisBmpSource.Palette;
                encoder.Frames.Add(thisBmpFrame);

            }
            var stream = new FileStream(pSaveAs, FileMode.CreateNew);
            encoder.Save(stream);
            stream.Close();
        }

CodePudding user response:

Here is a short example that shows how to write a GIF file with transparency from a paletted BitmapSource with a transparent Color entry.

var colors = new Color[] { Colors.Transparent, Colors.Red, };

var bitmap = new WriteableBitmap(200, 200, 96, 96, PixelFormats.Indexed8,
                                 new BitmapPalette(colors));

var redPixels = Enumerable.Range(0, 10000).Select(i => (byte)1).ToArray();

bitmap.WritePixels(new Int32Rect(50, 50, 100, 100), redPixels, 100, 0);

var encoder = new GifBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));

using (var stream = File.Create("test.gif"))
{
    encoder.Save(stream); // save
}

image.Source = BitmapFrame.Create(new Uri("test.gif", UriKind.Relative)); // reload

In a test application with this XAML

<Grid>
    <Border HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Background="AliceBlue">
        <Image x:Name="image" Stretch="None"/>
    </Border>
</Grid>

the output is this:

enter image description here

CodePudding user response:

I made decent progress based on Clemens result but finally gave up on WPF's GifBitmapEncoder and used the MagickImage library and within a tenth of the time had a perfect solution. I used the MagickImageCollection to collect png files and output an animated GIF with transparency. I could set the Animation delay and clear the background using simple properties on each image added. Magick.Net has helpful examples and excellent documentation. And the Visual Studio NuGet package manager makes it super easy to install. I did see though someone posted on Stack OVF that you are advised not to use System.Drawing.

  • Related