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

    Привет, Хабрахабр!
    Вчера, прочитав статью 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.: Вот так, совершенно незаметно, появился мой первый топик на Хабре :)

    Средняя зарплата в IT

    120 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 9 072 анкет, за 1-ое пол. 2021 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 27

      +3
      ImageMagic:
      composite -watermark 30 -gravity center watermark.png input.jpg output.jpg
        –3
        во-во!
          +1
          это же .net, тут не принят unix-way :))
            0
            Ну вообще-то надо еще написать код для вызова imagemagick + убедиться что он есть на сервере(
              –1
              это от силы 3-5 строк
                0
                Тут накладывает ватермарк тоже фактически одна строка — g.DrawImage(watermark, dest, 0, 0, watermark.Width, watermark.Height,
                GraphicsUnit.Pixel, attr, null, IntPtr.Zero);
                  –1
                  + прелюдия на 50 строк:)
                  ЗЫ на самом деле, я не против. нет и полностью нативной обработки, однако, я для себя выбираю то, что проще и быстрее
                    –1
                    >на самом деле, я не против. нет и полностью…
                    читать так: на самом деле, я не против .NET и полностью…
                      0
                      Я однажды пробовал использовать ImageMagick (он, к счастью, избавлен от некоторых глюков GDI+), но .NET-обертка для него мне не приглянулась…
                      А свой велосипед не может не нравиться :)
                    0
                    интересно, что сожрёт больше памяти и процессора: 1000 одновременных спавнов ImageMagic или 1000 вызовов этого метода? :)
                +5
                Другое дело! Тут уже прикопаться не к чему, значительно лучший вариант.
                PS: переносите уже в .net :)
                  +2
                  Спасибо! Перенес топик.
                • НЛО прилетело и опубликовало эту надпись здесь
                  +1
                  мне тоже вариант понравился, буду им иногда пользоваться )))
                    –1
                    Используя возможности класса встроенного в codeigniter задачу добавления водяного знака можно решить в 3 строки)
                    $config['wm_overlay_path'] = $logo;
                    $CI->image_lib->initialize($config);
                    $CI->image_lib->watermark();
                      +3
                      CodeIgniter для PHP. Здесь решение для .NET.
                      Зато потом собираете в сборку, подключаете и решаете все задачи в одну строку :)
                        0
                        А уже собранных классов разве нет? Я просто искренне удивлен обилию топиков посвященных этой проблеме.
                          +7
                          .NET только набирает популярность в качестве веб-платформы для некорпоративных проектов. В этой сфере существует масса наработок, которыми никто никогда не поделится ввиду их закрытости. Сейчас вокруг ASP.NET складывается довольно мощное комьюнити, которое помаленьку восполняет этот пробел.
                            +1
                            Понятно, извините что влез
                      0
                      Огромное спасибо!)
                        0
                        Любопытно, на каких-то сайтах используется добавление watermark только при просмотре картинки не на сайте? Ведь зачем прилеплять логотип, если он уже вверху висит. Чтобы при прямой ссылке на картинку watermark был.
                          0
                          Это вы сами спросили, сами и ответили? :)
                            0
                            не, меня ж заинтересовало где не просто запрет на картинки стоит а с эмблемой показывает
                          +2
                          Я извиняюсь, но у Вас небольшая потеря качества — если присмотреться.
                          Этого можно избежать указав Quality 100.
                            0
                            Спасибо за совет, я с качеством 85 результат сохранял.
                            В приведенном коде, однако, манипуляции с качеством не производятся, поэтому все зависит от того, кто будет код использовать.

                            В GDI+ вообще хватает багов… Попробуйте сохранить картинку в Photoshop и в .NET с одинаковым уровнем качества. Вы получите абсолютно разные результаты. PNG, например, вообще не сжимается почему-то :)
                              +2
                              Естественно — ни в каких стандартах/рекомендациях не указано как мерить Quality — оно везде в своих попугаях.

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое