---
title: "Add a watermark to an image in C#"
url: https://recursiveai.org/oneimage/cookbook/add-watermark/
description: "Add a watermark to an image in C#: a logo with opacity in a corner, or a tiled, diagonal text watermark across the photo. Runnable code with OneImage, on any OS."
version: "1.10.4"
---

# Add a watermark to an image in C#

Load the photo and the watermark, size the watermark from the photo, and lay it on top with `Composite`, giving the position and an opacity. The program below puts a logo in the bottom-right corner of NASA’s “Blue Marble” photograph (public domain).

```shell
dotnet new console -o Watermark && cd Watermark
dotnet add package RecursiveAI.OneImage --version 1.10.4
curl -L -o photo.jpg \
  https://images-assets.nasa.gov/image/as17-148-22727/as17-148-22727~orig.jpg
curl -L -o logo.png https://recursiveai.org/assets/brand/icon-512.png
```

OneImage needs the .NET 10 SDK. Put this in `Program.cs` and run `dotnet run`:

```csharp
using OneImage;

Image photo = Image.Load("photo.jpg");

// The logo at a sixth of the photo's width, so it stays in proportion on any image.
Image logo = Image.Load("logo.png").Resize(width: photo.Width / 6);

// Bottom-right, with a margin of 3% of the width, at 60% opacity.
int margin = photo.Width * 3 / 100;
int x = photo.Width - logo.Width - margin;
int y = photo.Height - logo.Height - margin;
photo.Composite(logo, x, y, opacity: 0.6f).Save("photo-logo.jpg");

Console.WriteLine($"photo-logo.jpg: {logo.Width}x{logo.Height} logo at ({x}, {y})");
```

```text
photo-logo.jpg: 763x763 logo at (3679, 3679)
```

The logo’s own transparency is respected, so only its shape shows. `(margin, margin)` is the top-left corner, and `((photo.Width - logo.Width) / 2, (photo.Height - logo.Height) / 2)` centers it. The overlay is converted to the photo’s color profile, and photo and logo are decoded, blended and encoded in one pass at `Save`. Phone photos are loaded upright, so the corner is the corner people see.

## A tiled, diagonal text watermark

Proofs repeat a translucent word across the whole picture, so cropping can’t remove it. Text needs the `RecursiveAI.OneImage.Text` package and a font file:

```shell
dotnet add package RecursiveAI.OneImage.Text --version 1.10.4
fonts=https://github.com/notofonts/notofonts.github.io/raw/main/fonts
curl -L -o NotoSans-Regular.ttf $fonts/NotoSans/hinted/ttf/NotoSans-Regular.ttf
```

```csharp
using System.Numerics;
using OneImage;
using OneImage.Drawing;
using OneImage.Text;
using Path = OneImage.Drawing.Path;

Image photo = Image.Load("photo.jpg").Resize(width: 1500);

FontFamily noto = new FontCollection().Add("NotoSans-Regular.ttf");
Font font = noto.CreateFont(photo.Width / 14f);
var options = new TextOptions(font)
{
    HorizontalAlignment = HorizontalAlignment.Center,   // centered on (0, 0)
    VerticalAlignment = VerticalAlignment.Center,
};
Path text = TextBuilder.GeneratePath("PREVIEW", options);   // laid out once

// Fill the outline on a grid, turned 30 degrees counterclockwise.
Brush white = Brush.Solid(new Rgba32(255, 255, 255, 80));
Matrix3x2 turn = Matrix3x2.CreateRotation(-MathF.PI / 6);
int step = photo.Width / 3, count = 0;
for (int y = step / 2; y < photo.Height; y += step)
{
    for (int x = step / 2; x < photo.Width; x += step, count++)
    {
        Matrix3x2 place = turn * Matrix3x2.CreateTranslation(x, y);
        photo = photo.Fill(white, text, transform: place);
    }
}
photo.Save("photo-tiled.jpg");
Console.WriteLine($"photo-tiled.jpg: {count} copies of PREVIEW");
```

```text
photo-tiled.jpg: 9 copies of PREVIEW
```

Change `step` for a denser grid. For a single line of text, such as a copyright notice in a corner, see [Draw text on an image](https://recursiveai.org/oneimage/cookbook/draw-text-on-image/).

## Limits

- **A visible watermark discourages reuse, it doesn’t prevent it.** A corner logo can be cropped off; a tiled mark is harder to remove.
- **No fonts are bundled.** Text needs a font file you ship, or an installed one through `SystemFonts`.
- **Animations:** `Composite` applies to every frame, so an animated GIF stays animated with the mark on each frame.

To watermark images as they are served, leaving the originals unmarked, use the `OnBeforeSaveAsync` hook of [OneImage.Web](https://recursiveai.org/oneimage/docs/web/) ([Resize images on the fly in ASP.NET Core](https://recursiveai.org/oneimage/cookbook/resize-images-aspnet-core/)). Blend modes and compositing are in the [guide](https://recursiveai.org/oneimage/docs/guide/#operations).
