C#C
C#2y ago
kalebbroo

How to add a watermark to a GIF?

Hello, I'm a beginner and I'm working on a Discord bot. I want to add an image watermark on a GIF I have the bot post. I am using ImageSharp and I cant find a clean way to just add the watermark. I assumed I would have to loop through each frame adding the watermark but it looks like I cant use Mutate on frames. I then thought maybe I had to make each frame an Image first them apply the watermark then recreate the gif but results are very poor.

I am not sure there is a good way to do this or im missing it. Please help? I would also take any advice on handling the watermark process.

/// <summary>Adds a watermark to every frame of an animated GIF and returns the modified image as a memory stream.</summary>
/// <param name="imagePath">Path to the original GIF image.</param>
/// <returns>A memory stream containing the watermarked GIF.</returns>
public static async Task<MemoryStream> AddWatermarkToGifAsync(string imagePath)
{
    MemoryStream ms = new();
    using Image<Rgba32> originalImage = await Image.LoadAsync<Rgba32>(imagePath);
    Image<Rgba32> watermarkImage = await LoadWatermarkImageAsync();
    // Create a list to store the modified frames
    List<Image<Rgba32>> modifiedFrames = [];
    foreach (ImageFrame<Rgba32> frame in originalImage.Frames)
    {
        // Create a new image from the frame data
        Image<Rgba32> frameImage = new(frame.Width, frame.Height);
        // Copy pixel data from the original frame to the new image
        frameImage.Frames[0].DangerousTryGetSinglePixelMemory(out Memory<Rgba32> memory);
        frame.DangerousTryGetSinglePixelMemory(out Memory<Rgba32> frameMemory);
        frameMemory.CopyTo(memory);
        ApplyWatermarkDirectlyToImage(frameImage, watermarkImage);
        // Add the processed frame to the list
        modifiedFrames.Add(frameImage);
    }
    // Create a new image to hold the frames with watermarks
    using var newImage = new Image<Rgba32>(originalImage.Width, originalImage.Height);
    foreach (var modifiedFrame in modifiedFrames)
    {
        newImage.Frames.AddFrame(modifiedFrame.Frames[0]);
    }
    // Save the new animated GIF to the memory stream
    newImage.SaveAsGif(ms);
    ms.Position = 0;
    return ms;
}

/// <summary>Applies a watermark directly to an image.</summary>
/// <param name="image">The image to apply the watermark to.</param>
/// <param name="watermarkImage">The watermark image.</param>
private static void ApplyWatermarkDirectlyToImage(Image<Rgba32> image, Image<Rgba32> watermarkImage)
{
    int watermarkWidth = image.Width / 5;
    int watermarkHeight = watermarkImage.Height * watermarkWidth / watermarkImage.Width;
    watermarkImage.Mutate(x => x.Resize(watermarkWidth, watermarkHeight));
    int xPosition = image.Width - watermarkWidth - 10;
    int yPosition = image.Height - watermarkHeight - 10;
    image.Mutate(ctx => ctx.DrawImage(watermarkImage, new Point(xPosition, yPosition), 0.5f)); // Apply 50% opacity
}

/// <summary>Loads the watermark image from a file or URL.</summary>
/// <returns>The loaded watermark image.</returns>
private static async Task<Image<Rgba32>> LoadWatermarkImageAsync()
{
    if (File.Exists(watermarkPath))
    {
        return await Image.LoadAsync<Rgba32>(watermarkPath);
    }
    else
    {
        using HttpClient httpClient = new();
        Stream watermarkStream = await httpClient.GetStreamAsync(watermarkUrl);
        return await Image.LoadAsync<Rgba32>(watermarkStream);
    }
}
Was this page helpful?