Графика GDI+

71

Интерфейс GDI+ - это модель рисования общего назначения для приложений .NET. В среде .NET интерфейс GDI+ используется в нескольких местах, в том числе при отправке документов на принтер, отображения графики в Windows-приложениях и визуализации графических элементов на веб-странице.

Применение кода GDI+ для прорисовки графики - это более медленный процесс, чем использование файла статического изображения. Однако этот метод обеспечивает значительно большую свободу и предоставляет несколько возможностей, которые были недоступны (или являлись недопустимо сложными) в предшествующих платформах разработки веб-приложений, таких как классическая ASP. Например, можно генерировать графические элементы, которые используют специфичную для пользователя информацию, и "на лету" визуализировать диаграммы и графики в соответствии с записями базы данных.

Ядром программирования GDI+ является класс System.Drawing.Graphics. Класс Graphics инкапсулирует поверхность рисования GDI+, будь то окно, печатный документ или хранящееся в памяти растровое изображение. Разработчикам ASP.NET редко приходится рисовать окна или печатные документы, поэтому в веб-приложениях наибольшее применение находит последняя из названных возможностей.

Чтобы использовать GDI+ в среде ASP.NET, необходимо выполнить следующие четыре действия:

  1. Создать хранящееся в памяти растровое изображение, в котором будет выполняться все рисование.

  2. Создать графический контекст GDI+ для изображения. Выполнение этого действия предоставит экземпляр объекта System.Drawing.Graphics.

  3. Выполнить рисование с помощью методов экземпляра объекта Graphics. Эти методы позволяют рисовать и закрашивать линии и формы, а также копировать содержимое растрового изображения из существующих файлов.

  4. Записать изображение в браузер с помощью свойства Response.OutputStream.

В последующих разделах приводится несколько примеров веб-страниц, в которых используется GDI+. Прежде чем продолжить, удостоверьтесь в том, что импортированы следующие пространства имен:

using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

В пространстве имен System.Drawing определены многие из основополагающих ингредиентов рисования, в том числе перья, кисти и растровые изображения. Пространство имен System.Drawing.Drawing2D добавляет другие полезные нюансы, такие как весьма гибкий класс GraphicsPath, а пространство имен System.Drawing.Imaging включает в себя пространство имен ImageFormat, позволяющее выбирать формат графики, в котором растровое изображение будет визуализировано при отправке клиенту.

Простое рисование

В следующем примере демонстрируется простейшая страница GDI+. Вся необходимая работа выполняется в обработчике события Page.Load.

Прежде всего, нужно построить хранящееся в памяти растровое изображение, создав экземпляр класса System.Drawing.Bitmap. При создании этого объекта в аргументах конструктора потребуется указать высоту и ширину изображения в пикселях. Размер изображения должен быть как можно меньшим. Крупные растровые изображения будут не только потреблять больше дополнительной памяти сервера во время выполнения, но возрастет также и размер визуализированного содержимого, отправляемого клиенту, что замедлит передачу данных.

protected void Page_Load(object sender, EventArgs e)
{
        // Создать хранящееся в памяти растровое изображение,
        // в котором будет выполняться рисование.
        // Ширина этого растрового изображения - 300 пикселей, а высота - 50 пикселей
        Bitmap image = new Bitmap (300, 50);
}

Затем нужно создать графический контекст GDI+ изображения, представленный объектом System.Drawing.Graphics. Методы этого объекта позволяют выполнять рисование в хранящемся в памяти растровом изображении. Чтобы создать объект Graphics из объекта Bitmap, используйте статический метод Graphics.FromImage().

Теперь начинается самое интересное. С помощью методов класса Graphics в растровом изображении можно нарисовать текст, формы и изображения. В этом примере код рисования чрезвычайно прост. Он заполняет графический элемент сплошным белым фоном, используя метод FillRectangle() объекта Graphics. (Первоначально в новом растровом изображении каждый пиксель устанавливается в черный цвет.)

Graphics g = Graphics.FromImage(image);

// Рисование сплошного белого прямоугольника.
// Рисование начинается с точки с координатами (1, 1).
// Ширина этого растрового изображения - 298 пикселей, а высота - 48 пикселей
g.FillRectangle(Brushes.White, 1, 1, 298, 48);

Метод FillRectangle() требует нескольких аргументов. Первый аргумент устанавливает цвет, следующие два - начальную точку, а последние два - ширину и высоту. Точка (0, 0) определяет в пикселях верхний левый угол изображения в координатах (X, Y). Значение координаты X увеличивается слева направо, а координаты Y - сверху вниз. В этом примере изображение имеет ширину 300 пикселей и высоту 50 пикселей - т.е. нижним правым утлом является точка с координатами (299, 49).

Рамка толщиной в 1 пиксель оставлена незаполненной. В результате отображается тонкая рамка исходного черного цвета. Следующий фрагмент кода рисования визуализирует сообщение статической метки. Для этого создается объект System.Drawing.Font, представляющий используемый шрифт. Этот объект не следует путать с объектом FontInfo, который применяется с элементами управления ASP.NET для указания запрошенного кода веб-страницы. В отличие от FontInfo, объект Font представляет один конкретный шрифт (включая его гарнитуру, размер и стиль), установленный на текущем компьютере. При создании объекта Font нужно указать имя шрифта, его размер в пунктах и стиль, например:

Font font = new Font("Vivaldi", 20, FontStyle.Regular);

Поскольку это изображение генерируется на сервере, при его создании можно использовать любой шрифт, установленный на сервере. Клиенту не обязательно иметь этот же шрифт, поскольку он получает текст в виде визуализированного изображения.

Для визуализации текста служит метод DrawString() объекта Graphics. Как и в случае объекта FillRectangle, понадобится указать координаты точки, с которой должно начинаться рисование. Эта точка представляет верхний левый угол текстового блока. В данном случае используется точка (10, 5), что ведет к смещению текста на 10 пикселей от левого края и на 5 пикселей от верхнего края экрана:

g.DrawString("Wuck forld!", font, Brushes.Blue, 10, 5);

Как только изображение готово, его можно оправить браузеру методом Bitmap.Save(). Изображение сохраняется в потоке ответа браузера. Оно отправляется клиенту и отображается в браузере. При записи непосредственно в поток ответа, как в данном примере, изображение заменяет любые другие данные веб-страниц и обходит модель веб-элемента управления:

// Визуализация изображения в выходной поток
image.Save(Response.OutputStream,
    System.Drawing.Imaging.ImageFormat.Gif);

Изображение можно сохранить в любой допустимый поток, включая FileStream. Эта технология позволяет сохранять динамически генерируемые изображения на диске, чтобы впоследствии их можно было использовать в других веб-страницах.

И, наконец, необходимо вызвать метод Dispose() для изображения и контекста графики, как показано ниже. И изображение, и контекст графики опираются на неуправляемые ресурсы, которые не могут быть освобождены немедленно, и при генерации большого объема изображений загруженность ресурсов может влиять на производительность сервера:

g.Dispose();
image.Dispose();

Чтобы сэкономить время можно использовать операторы using для классов реализующих IDisposable. Ниже показано сохранение графики в файл и отображение ее в элементе Image:

protected void Page_Load(object sender, EventArgs e)
{
        using (Bitmap image = new Bitmap(300, 80))
        {
            using (Graphics graphic = Graphics.FromImage(image))
            {
                graphic.FillRectangle(Brushes.White, 1, 1, 298, 78);
                Font font = new Font("Vivaldi", 32, FontStyle.Regular);
                graphic.DrawString("Wuck forld!", font, Brushes.Blue, 10, 5);

                image.Save(Server.MapPath("bitmap.gif"),
                    System.Drawing.Imaging.ImageFormat.Gif);
            }
        }

        Image1.ImageUrl = "bitmap.gif";
}
Графическая метка

Формат и качество изображения

При сохранении изображения можно выбрать желаемый формат. Формат JPEG обеспечивает наилучшую поддержку цвета и графики, хотя он использует сжатие, которое может привести к утрате деталей и размытию текста. Часто для графических изображений, содержащих текст, больше подходит формат GIF, но он предлагает не слишком хорошую поддержку цвета. В среде .NET каждое GIF-изображение применяет фиксированную палитру, состоящую из 256 обобщенных цветов. При использовании цвета, который не совпадает с одним из этих заранее установленных цветов, он будет размыт, приводя к неоптимальному результату.

Еще одним возможный выбором является формат PNG, который сочетает в себе лучшие черты форматов JPEG и GIF. Однако этот формат не может работать непосредственно в веб-странице - его придется вставить внутрь дескриптора <img>.

Качество определяется не только форматом изображения. Оно зависит также от способа визуализации исходного растрового изображения. GDI+ позволяет выбирать между оптимизацией кода рисования для улучшения внешнего вида или для повышения скорости. При выборе оптимизации для получения наилучшего внешнего вида .NET использует технологии визуализации, такие как сглаживание, для увеличения качества рисунка.

Сглаживание устраняет контурные неровности форм и текста. Оно действует за счет добавления текстурирования по границе края. Например, текстурирование полутонами может добавляться к краю черной кривой, придавая углу более гладкий вид.

Чтобы использовать сглаживание в приложениях, потребуется установить свойство SmoothingMode объекта Graphics. На выбор доступны значения None (по умолчанию), Highspeed, AntiAlias и HighQuality (которое аналогично AntiAlias, но использует другой, более медленный алгоритм оптимизации, предназначенный для повышения качества изображения на жидкокристаллических экранах). Свойство Graphics.SmoothingMode - одно из немногих зависящих от состояния членов класса Graphics. Это означает, что его устанавливают перед началом рисования, и оно применяется к любому рисуемому тексту или формам до тех пор, пока объект Graphics не будет удален:

graphic.SmoothingMode = SmoothingMode.AntiAlias;

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

Применение сглаживания к эллипсу

Сглаживание можно применять также к шрифтам, чтобы сгладить ступенчатость краев текстовых элементов. Чтобы гарантированно получить оптимизированный текст, можно установить свойство Graphics.TextRenderingHint. Для выбора доступны значения SingleBitPerPixelGridFit (наивысшая производительность и самое низкое качество), AntiAlias (хорошее качество благодаря сглаживанию), AntiAliasGridFit (более высокое качество благодаря сглаживанию и выводу подсказок, но более низкая производительность) и ClearTypeGridFit (наивысшее качество на жидкокристаллическом дисплее).

Или же можно использовать значение SystemDefault, чтобы применять любые настройки сглаживания шрифта, сконфигурированные пользователем. Настройка SystemDefault является настройкой по умолчанию, а системные настройки по умолчанию большинства компьютеров включают функцию сглаживания текста. Даже если эта настройка не установлена, как правило, динамически визуализированный текст будет рисоваться в высоком качестве. Однако, поскольку управление системными настройками веб-сервера возможно не всегда, если нужно рисовать текст в изображении, рекомендуется указывать эту настройку явно.

Класс Graphics

Класс Graphics также предоставляет методы для рисования определенных видов форм, изображений и текста. Эти методы кратко описаны в таблице ниже:

Методы рисования класса Graphics
Метод Описание
DrawArc()

Рисует дугу, представляющую часть эллипса, который определен парой координат - шириной и высотой

DrawBezier() и DrawBeziers()

Рисует известную кривую Безье, которая определяется четырьмя опорными точками

DrawClosedCurve()

Рисует кривую, а затем замыкает ее, соединяя конечные точки

DrawCurve()

Рисует кривую (которая формально представляет собой фундаментальный сплайн)

DrawEllipse()

Рисует эллипс, определенный ограничительным прямоугольником, который указан парой координат - высотой и шириной

DrawIcon() и DrawIconUnstreched()

Рисует значок, представленный объектом Icon, и (необязательно) растягивает его до размеров данного прямоугольника

DrawImage

Рисует изображение, представленное полученным из изображения объектом (например, Bitmap, который был загружен из файла), и растягивает его, чтобы оно соответствовало прямоугольной области

DrawImageUnscaled() и DrawImageUnscaledAndClipped()

Рисует изображение, представленное полученным из изображения объектом, без масштабирования, и (необязательно) усекает его, чтобы оно соответствовало указанной прямоугольной области

DrawLine() и DrawLines()

Рисует одну или более линий. Каждая линия соединяет две точки, указанные парами координат

DrawPath()

Рисует объект GraphicsPath, который может представлять комбинацию кривых и форм

DrawPie()

Рисует сектор, который определен эллипсом, указанным парой координат - шириной и высотой - и двумя радиальными линиями

DrawPolygon()

Рисует многоугольник, определенный массивом точек

DrawRectangle() и DrawRectangles()

Рисует один или более прямоугольников. Каждый прямоугольник определяется парой координат начальной точки, шириной и высотой

DrawString()

Рисует строку текста данным шрифтом

FillClosedCurve()

Рисует кривую, замыкает ее, соединяя конечные точки, и заливает результирующую фигуру

FillEllipse()

Заливает внутреннюю часть эллипса

FillPath()

Заливает форму, представленную объектом GraphicsPath

FillPie()

Заливает внутреннюю часть сектора

FillPolygon()

Заливает внутреннюю часть многоугольника

FillRectangle() и FillRectangles()

Заливает внутреннюю часть одного или более прямоугольников

Методы Draw...() рисуют контуры (например, грани прямоугольника). Методы Fill...() закрашивают сплошные области (например, фактическую поверхность внутри границы прямоугольника). Единственные исключения из этого правила - метод DrawString(), который рисует залитый текст, используя указанный шрифт, и методы DrawIcon() и DrawImage(), которые копируют растровые изображения на поверхность рисования.

Если требуется создать форму, обладающую контуром одного цвета и заливкой другого цвета, методы рисования и заливки должны использоваться совместно. Следующий пример вначале закрашивает белый прямоугольник, затем добавляет окружающую его зеленую границу:

graphic.FillRectangle(Brushes.White, 0, 0, 300, 50);
graphic.DrawRectangle(Pens.Green, 0, 0, 299, 49);

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

Обратите внимание, что при использовании метода заливки придется указать объект кисти Brush. При использовании метода рисования нужно указать объект пера Pen. В приведенном примере код использует предварительно созданные объекты Pen и Brush, которые можно получить соответственно из классов Pens и Brushes. Кисти, полученные подобным образом, всегда соответствуют сплошным цветам, а перья всегда имеют ширину равную 1 пикселю.

Используя описанные методы, легко создать простую веб-страницу, которая рисует более сложное изображение GDI+. В следующем примере класс Graphics применяется для рисования эллипса, текстового сообщения и изображения, полученного из файла. Необходимый для этого код имеет следующий вид:

protected void Page_Load(object sender, EventArgs e)
{
        using (Bitmap image = new Bitmap(450, 100))
        {
            using (Graphics graphic = Graphics.FromImage(image))
            {
                // Обеспечить рисование кривых в высоком качестве
                graphic.SmoothingMode = SmoothingMode.AntiAlias;

                // Закрасить фон
                graphic.FillRectangle(Brushes.White, 0, 0, 450, 100);
                
                // Добавить эллипс
                graphic.FillEllipse(Brushes.PaleGoldenrod, 120, 13, 300, 50);
                graphic.DrawEllipse(Pens.Green, 120, 13, 299, 49);

                // Нарисовать текст с помощью причудливого шрифта
                Font font = new Font("Harrington", 15, FontStyle.Bold);
                graphic.DrawString("Вкусный апельсин!", font, Brushes.DarkOrange, 150, 20);
                
                
                // Добавить графическое изображение из файла
                System.Drawing.Image orangeImage = 
                    System.Drawing.Image.FromFile(Server.MapPath("oranges.gif"));
                
                graphic.DrawImageUnscaled(orangeImage, 0, 0);
                
                // Сохранить изображение в файл
                image.Save(Server.MapPath("bitmap.gif"),
                    System.Drawing.Imaging.ImageFormat.Gif);
            }
        }

        Image1.ImageUrl = "bitmap.gif";
}

Результирующая веб-страница показана на рисунке ниже:

Использование множества элементов

Использование класса GraphicsPath

Пока что остались без внимания два интересных метода - DrawPath() и FillPath() - которые работают с классом GraphicsPath из пространства имен System.Drawing.Drawing2D.

Класс GraphicsPath инкапсулирует последовательность связанных линий, кривых и текста. Чтобы построить объект GraphicsPath, необходимо создать новый экземпляр этого класса и с помощью перечисленных в таблице ниже методов добавить все необходимые элементы:

GraphicsPath path = new GraphicsPath();
path.AddEllipse(0, 0, 100, 50);
path.AddRectangle (new Rectangle(100, 50, 100, 50));
graphic.DrawPath(Pens.Black, path);

Как только объект GraphicsPath создан, метод Graphics.DrawPath() можно использовать для рисования его контура, а метод Graphics.FillPath() - для закрашивания его области заливки.

Методы класса GraphicsPath
Метод Описание
AddArc()

Рисует дугу, представляющую часть эллипса, который определен парой координат - шириной и высотой

AddBezier() и AddBeziers()

Рисует известную кривую Безье, определенную четырьмя опорными точками

AddClosedCurve()

Рисует кривую, а затем замыкает ее, соединяя конечные точки

AddCurve()

Рисует кривую (которая формально представляет собой фундаментальный сплайн)

AddEllipse()

Рисует эллипс, определенный ограничительным прямоугольником, который указан парой координат - высотой и шириной

AddLine() и AddLines()

Рисует линию, которая соединяет две точки, указанные парами координат

AddPath()

Добавляет другой объект GraphicsPath к данному объекту GraphicsPath

AddPie()

Рисует сектор, который определен эллипсом, указанным парой координат - шириной и высотой, и двумя радиальными линиями

AddPolygon()

Рисует многоугольник, определенный массивом точек

AddRectangle() и AddRectangles()

Рисует обычный прямоугольник, определенный парой координат начальной точки, шириной и высотой

AddString()

Рисует строку текста данным шрифтом

Transform(), Warp() и Widen()

Применяют, соответственно, матричную трансформацию, деформирующую трансформацию (определенную прямоугольником и параллелограммом) и расширение

StartFigure() и CloseFigure()

Метод StartFigure() определяет начало новой замкнутой фигуры. При использовании метода CloseFigure() начальная точка будет соединена с конечной точкой дополнительной линией

Кроме того, можно также создать сплошную, залитую фигуру из отдельных линейных сегментов. Для этого вначале нужно вызвать метод StartFigure(). Затем с использованием соответствующих методов понадобится добавить необходимые кривые и линии. По окончании этого процесса должен быть вызван метод CloseFigure(), чтобы замкнуть форму линией, нарисованной от конечной точки до начальной точки. Эти методы можно использовать многократно, чтобы добавить несколько замкнутых фигур в один объект GraphicsPath.

Перья (Pen)

При использовании методов Draw...() из класса Graphics рисование границы формы или кривой выполняется предоставленным объектом Pen. Стандартное перо можно получить, используя одно из статических свойств класса System.Drawing.Pens. Все эти перья имеют ширину в 1 пиксель и различаются только цветом. Чтобы сконфигурировать все свойства, описанные в таблице ниже, можно создать специальный объект Pen. Например:

Pen myPen = new Pen(Color.Red);
myPen.DashCap = DashCap.Triangle;
myPen.DashStyle = DashStyle.DashDotDot;
myPen.Width = 5;
graphic.DrawLine(myPen, 10, 10, 150, 10);
Свойства класса Pen
Свойство Описание
DashPattern

Определяет стиль пунктира для прерывистых линий, используя массив чисел и пробелов

DashStyle

Определяет стиль пунктира для прерывистых линий, используя перечисление DashStyle

LineJoin

Определяет способ присоединения соединяющих линий в форме

PenType

Определяет тип заливки, которая будет использоваться для линии. Обычно им будет SolidColor, но можно использовать также градиентную заливку, растровую текстуру или штриховой узор, предоставляя соответствующий объект кисти при создании пера. Однако с помощью этого свойства нельзя установить тип PenType, поскольку оно доступно только для чтения

StartCap и EndCap

Определяет способ визуализации начала и конца линии. Можно также определить специальное окончание линии, создавая объект CustomLineCap (обычно посредством использования GraphicsPath), а затем присваивая его свойству CustomStartCap или CustomEndCap

Width

Определяет ширину (в пикселях) линий, рисуемых этим пером

Простейший способ понять различные свойства LineCap и DashStyle заключается в создании тестовой страницы, которая перечисляет различные варианты. Следующий код веб-страницы создает именно такой рисунок:

protected void Page_Load(object sender, EventArgs e)
{
        using (Bitmap image = new Bitmap(500, 400))
        {
            using (Graphics graphic = Graphics.FromImage(image))
            {
                graphic.FillRectangle(Brushes.White, 0, 0, 500, 400);

                // Создать перо, которое будет использовано во всех примерах
                Pen myPen = new Pen(Color.LimeGreen, 10);
                
                // Переменная y отслеживает текущую позицию (вверх/вниз) в изображении
                int y = 60;
                
                // Нарисовать пример каждого стиля LineCap в первом (левом) столбце
                graphic.DrawString("Типы LineCap", 
                    new Font("Tahoma", 15, FontStyle.Bold), Brushes.Blue, 0, 10);
                
                foreach (LineCap cap in System.Enum.GetValues(typeof(LineCap))) 
                {
                    myPen.StartCap = cap; 
                    myPen.EndCap = cap; 
                    graphic.DrawLine(myPen, 20, y, 100, y);
                    graphic.DrawString(cap.ToString(), new Font("Tahoma", 8),
                        Brushes.Black, 120, y - 10); 
                    y += 30;
                }

                // Нарисовать пример каждого стиля DashStyle во втором (правом) столбце
                y = 60;
                graphic.DrawString("Типы DashStyle", 
                    new Font("Tahoma", 15, FontStyle.Bold), Brushes.Blue, 200, 10);

                foreach (DashStyle dash in System.Enum.GetValues(typeof(DashStyle))) 
                {
                    // Сконфигурировать перо
                    myPen.DashStyle = dash;
                    
                    // Нарисовать короткий линейный сегмент
                    graphic.DrawLine(myPen, 220, y, 300, y);
                    
                    // Добавить текстовую метку
                    graphic.DrawString(dash.ToString(), 
                        new Font("Tahoma", 8), Brushes.Black, 320, y - 10);
                    
                    // Переместиться на одну строку вниз
                    y += 30;
                }
                
                // Сохранить изображение в файл
                image.Save(Server.MapPath("bitmap.gif"),
                    System.Drawing.Imaging.ImageFormat.Gif);
            }
        }

        Image1.ImageUrl = "bitmap.gif";
}
Различные варианты перьев Pen

Кисти (Brush)

Кисти служат для заливки пространства между линиями. Кисти используются при рисовании текста или при вызове любого из методов Fill...() класса Graphics для закрашивания внутренней части форма. Заранее определенную стандартную сплошную кисть можно быстро получить через статическое свойство класса Brushes. Можно также создавать специальные кисти. Простые сплошные кисти создаются из класса SolidBrush, а более сложные кисти доступны посредством других классов:

HatchBrush

Кисть HatchBrush имеет цвет переднего плана, цвет фона и стиль штриховки, который определяет способ объединения этих цветов. Как правило, цвета вкрапляются с помощью полос, сеток или точек, но можно выбрать и необычные стили узора, такие как кирпичи, конфетти, плетение и черепица.

LinearGradientBrush

Кисть LinearGradientBrush позволяет смешивать два цвета, создавая градиентный узор. Можно выбрать любые два цвета (как и при использовании штриховой кисти), а затем смешать их по горизонтали (слева направо), по вертикали (сверху вниз), по диагонали (от верхнего левого угла до нижнего правого угла) или по диагонали в обратном направлении (от верхнего правого угла до нижнего левого угла). Можно указать также начальную точку для любой стороны градиентной заливки.

TextureBrush

Кисть TextureBrush присоединяет растровое изображение к кисти. Изображение размещается в виде плитки в закрашенной части кисти, будь то текст или простой прямоугольник.

Ниже приведен пример логики рисования для тестирования всех стилей кисти LinearGradientBrush:

protected void Page_Load(object sender, EventArgs e)
{
        using (Bitmap image = new Bitmap(300, 300))
        {
            using (Graphics graphic = Graphics.FromImage(image))
            {
                graphic.FillRectangle(Brushes.White, 0, 0, 300, 300);
                
                // Отобразить прямоугольник с каждым типом градиентной заливки
                LinearGradientBrush myBrush; 
                int y = 20;
                
                foreach (LinearGradientMode gradientStyle in System.Enum.GetValues(typeof(LinearGradientMode)))
                {
                    // Сконфигурировать кисть
                    myBrush = new LinearGradientBrush(new Rectangle(20, y, 100, 60), 
                        Color.Violet, Color.White, gradientStyle);
                    
                    // Нарисовать маленький прямоугольник и добавить текстовую метку
                    graphic.FillRectangle(myBrush, 20, y, 100, 60);
                    graphic.DrawString(gradientStyle.ToString(), 
                        new Font("Tahoma", 8), Brushes.Black, 130, y + 20);
                    
                    // Переместиться на следующую строку
                    y += 70;
                }
                
                // Сохранить изображение в файл
                image.Save(Server.MapPath("bitmap.gif"),
                    System.Drawing.Imaging.ImageFormat.Gif);
            }
        }

        Image1.ImageUrl = "bitmap.gif";
}

Результат показан на рисунке ниже:

Тестирование стилей градиентной кисти

Можно также создать перо, которое выполняет рисование с использованием стиля заливки кисти. Это позволит рисовать линии, которые залиты градиентами и текстурами. Для этого начните с создания соответствующей кисти, а затем создайте новое перо. Один из перегруженных методов конструктора пера принимает ссылку на кисть - ту, которую нужно использовать для пера на основе кисти.

Пройди тесты
Лучший чат для C# программистов