Привет, Хабрахабр!
Вчера, прочитав статью SergeyVoyteshonok, посвященную отрисовке логотипа сайта или компании (проще говоря, «водяного знака») на загружаемых пользователями изображениях, я был удивлен некоторой тяжеловесностью предложенного автором решения.
Тогда я пообещал немного поэкспериментировать и предложить более рациональный вариант.
В моей версии метод отрисовки выглядит так:
Дополнительно, мы используем также две вещи:
для указания точки привязки, и метод, возвращающий нам конкретную точку расположения водяного знака в зависимости от размеров изображений и точки привязки:
На мой взгляд, все достаточно прозрачно. Собственно отрисовка логотипа заняла всего 15 строк, учитывая все навороты форматирования, а без них — и того меньше! Такого результата позволило достичь применение для решения задачи возможностей .NET-класса ColorMatrix.
Вообще, класс ColorMatrix обладает очень широкими возможностями манипуляции настройками изображения. С помощью него можно не только задавать прозрачность, но и изменять контрастность, насыщенность картинки, делать из нее негатив и многое другое.
В заключение нужно отметить две вещи. Первая — логотип, используемый в качестве водяного знака, лучше сохранять в PNG. GIF-формат для простых логотипов также допустим. В этом случае мы можем не задавать transparentColor (точнее, вообще убрать строчку
Вторая, несколько более важная — мой метод выполняет отрисовку прямо на переданной ему картинке, не создавая нового экземпляра. Поэтому, если вам необходимо сохранять и оригинал, нужно будет немного переделать метод для отрисовки с тем, чтобы он возвращал измененное изображение, не затрагивая исходного.
UPD: Если вы загружали исходное изображение из файла и хотите сохранить его под тем же именем, вам также нужно будет создать копию и сохранить ее вне блока using.
UPD2: По просьбе Sergeyev добавил демонстрацию результата (слева — исходная картинка, справа — она же с известным всем логотипом. Не фотошоп! :)

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