Pull to refresh

Добавляем watermark к изображению

Reading time 4 min
Views 6.9K
Привет, Хабрахабр!
Вчера, прочитав статью SergeyVoyteshonok, посвященную отрисовке логотипа сайта или компании (проще говоря, «водяного знака») на загружаемых пользователями изображениях, я был удивлен некоторой тяжеловесностью предложенного автором решения.
Тогда я пообещал немного поэкспериментировать и предложить более рациональный вариант.


В моей версии метод отрисовки выглядит так:

public void DrawWatermark(Image original, Bitmap watermark,
    WatermarkPosition position, Color transparentColor, float opacity)
{
    if (original == null)
        throw new ArgumentNullException("original");
    if (watermark == null)
        throw new ArgumentNullException("watermark");
    if (opacity < 0 || opacity > 1)
        throw new ArgumentOutOfRangeException("Watermark opacity value is out of range");

    Rectangle dest = new Rectangle(
        GetDestination(original.Size, watermark.Size, position), watermark.Size);

    using (Graphics g = Graphics.FromImage(original))
    {
        ImageAttributes attr = new ImageAttributes();
        ColorMatrix matrix = new ColorMatrix(new float[][] {
            new float[] { opacity, 0f, 0f, 0f, 0f },
            new float[] { 0f, opacity, 0f, 0f, 0f },
            new float[] { 0f, 0f, opacity, 0f, 0f },
            new float[] { 0f, 0f, 0f, opacity, 0f },
            new float[] { 0f, 0f, 0f, 0f, opacity } });
        attr.SetColorMatrix(matrix);
        watermark.MakeTransparent(transparentColor);

        g.DrawImage(watermark, dest, 0, 0, watermark.Width, watermark.Height,
            GraphicsUnit.Pixel, attr, null, IntPtr.Zero);
        g.Save();
    }
}


* This source code was highlighted with Source Code Highlighter.

Дополнительно, мы используем также две вещи:
public enum WatermarkPosition
{
    TopLeft = 0,
    TopRight,
    BottomLeft,
    BottomRight,
    Middle
}


* This source code was highlighted with Source Code Highlighter.

для указания точки привязки, и метод, возвращающий нам конкретную точку расположения водяного знака в зависимости от размеров изображений и точки привязки:
private static Point GetDestination(Size originalSize, Size watermarkSize, WatermarkPosition position)
{
    Point destination = new Point(0, 0);
    switch (position)
    {
        case WatermarkPosition.TopRight:
            destination.X = originalSize.Width - watermarkSize.Width;
            break;
        case WatermarkPosition.BottomLeft:
            destination.Y = originalSize.Height - watermarkSize.Height;
            break;
        case WatermarkPosition.BottomRight:
            destination.X = originalSize.Width - watermarkSize.Width;
            destination.Y = originalSize.Height - watermarkSize.Height;
            break;
        case WatermarkPosition.Middle:
            destination.X = (originalSize.Width - watermarkSize.Width) / 2;
            destination.Y = (originalSize.Height - watermarkSize.Height) / 2;
            break;
    }
    return destination;
}

* This source code was highlighted with Source Code Highlighter.

На мой взгляд, все достаточно прозрачно. Собственно отрисовка логотипа заняла всего 15 строк, учитывая все навороты форматирования, а без них — и того меньше! Такого результата позволило достичь применение для решения задачи возможностей .NET-класса ColorMatrix.
Вообще, класс ColorMatrix обладает очень широкими возможностями манипуляции настройками изображения. С помощью него можно не только задавать прозрачность, но и изменять контрастность, насыщенность картинки, делать из нее негатив и многое другое.

В заключение нужно отметить две вещи. Первая — логотип, используемый в качестве водяного знака, лучше сохранять в PNG. GIF-формат для простых логотипов также допустим. В этом случае мы можем не задавать transparentColor (точнее, вообще убрать строчку watermark.MakeTransparent(transparentColor);) и расширить тип watermark до Image.

Вторая, несколько более важная — мой метод выполняет отрисовку прямо на переданной ему картинке, не создавая нового экземпляра. Поэтому, если вам необходимо сохранять и оригинал, нужно будет немного переделать метод для отрисовки с тем, чтобы он возвращал измененное изображение, не затрагивая исходного.

UPD: Если вы загружали исходное изображение из файла и хотите сохранить его под тем же именем, вам также нужно будет создать копию и сохранить ее вне блока using.

UPD2: По просьбе Sergeyev добавил демонстрацию результата (слева — исходная картинка, справа — она же с известным всем логотипом. Не фотошоп! :)
 

Ну вот, собственно, и все!

P.S.: Вот так, совершенно незаметно, появился мой первый топик на Хабре :)

Tags:
Hubs:
+39
Comments 27
Comments Comments 27

Articles