C
C#4mo 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);
}
}
/// <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);
}
}
1 Reply
JBSP
JBSP4mo ago
You don’t need to apply methods per frame. Simply mutate the image adding the watermark and the library will do it all for you.
Want results from more Discord servers?
Add your server