Основные понятия В Windows приложения не имеют непосредственного доступа к устройствам отображения, таким как монитор или принтер. Вместо этого они обращаются к интерфейсу графического устройства Windows, а он, в свою очередь, транслирует эти обращения к драйверам устройств отображения, обеспечивая аппаратную независимость приложений. Интерфейс графического устройства (Graphics Device Interface, GDI) – часть Windows API, которая представляет собой библиотеку функций, обеспечивающих графический вывод на различные устройства отображения. С выходом Windows XP появилась библиотека GDI+, призванная заменить устаревшую библиотеку GDI. Библиотека GDI+ содержит в себе все возможности GDI предшественника и предоставляет множество новых. В новых версиях Windows приложения, использующие GDI, выполняются, но при создании приложений рекомендуется использовать GDI+. В библиотеке GDI+ используется объектно-ориентированный подход к работе с графическими объектами. Библиотека GDI+ содержит около 40 классов. Документация по классам GDI+ в Platform Software Development Kit (Platform SDK).Обработка ошибок в GDI+Большинство функций и методов классов GDI+ после выполнения возвращают одно из значений перечисляемого типа Status, определенного в заголовочном файле gdiplustypes.h. В случае успешного выполнения возвращается значение Ok. Остальные значения типа Status указывают на причину возникновения ошибки. Подробное описание значений перечисляемого типа Status в документации Platform SDK. Большинство классов GDI+ содержат метод GetLastStatus, который возвращает значение, указывающее причину возникновения ошибки (или ее отсутствие) в последнем вызове одного из методов этих классов. При создании объектов одного из классов GDI+ можно вызвать метод GetLastStatus этого объекта, для того чтобы определить успешно или нет был создан объект. В случае успешного создания метод GetLastStatus возвращает значение Ok. При создании объектов некоторых классов GDI+ в случае возникновения ошибки метод GetLastStatus независимо от ее причины может вернуть значение OutOfMemory, указывающее на нехватку памяти для создания объекта. Цвет в GDI+ 31...2423...1615...0807...00альфа-фактор (alpha-value)красный (red)зеленый (green)синий (blue)Параметры цвета могут принимать 28=256 значений, от 255 (максимальная яркость) до 0 (отсутствие цвета). Параметр прозрачности от 255 (отсутствие прозрачности) до 0 (полная прозрачность) Методы класса Color МетодОписаниеBYTE GetAlpha, BYTE GetAВозвращает значение альфа-фактораBYTE GetBlue, BYTE GetBВозвращает значение синего компонента цветаBYTE GetGreen, BYTE GetGВозвращает значение зеленого компонента цветаBYTE GetRed, BYTE GetRВозвращает значение красного компонента цветаARGB GetValueВозвращает значение цвета в формате ARGBMakeARGB(BYTE a, BYTE r, BYTE g, BYTE b)Собирает значения альфа-фактора и компонентов цвета в одно значение цвета в формате ARGBSetFromCOLORREF(COLORREF rgb)Задает значение цвета в формате COLORREFSetValue(ARGB argb)Задает значение цвета в формате ARGBКлассы GDI+ для работы с координатами и размерами КлассПоляОписаниеPointX, YПара целых чисел, представляющих собой координаты точкиPointFX, YПара вещественных чисел, представляющих собой координаты точкиSizeWidth, HeightПара целых чисел, представляющих собой ширину и высотуSizeFWidth, HeightПара вещественных чисел, представляющих собой ширину и высотуRectX, Y, Width, HeightНабор из четырех целых чисел, определяющих расположение и размер прямоугольникаRectFX, Y, Width, HeightНабор из четырех вещественных чисел, определяющих расположение и размер прямоугольникаPoint и PointF Классы Point и PointF используются для представления координат точек в двумерной прямоугольной системе координат. В классе Point у полей X и Y тип int, в классе PointF – float. В остальном классы Point и PointF идентичны. Size и SizeF Классы Size и SizeF используются для представления пары чисел, которые определяют ширину и высоту. В классе Size у полей Width и Height тип int, в классе SizeF – float. В остальном классы Size и SizeF идентичны. Rect и RectF Классы Rect и RectF используются для представления прямоугольников. Классы Rect и RectF в основном идентичны, в классе Rect поля X, Y, Width и Height имеют тип int, в классе RectF – float. Методы класса Rect МетодОписаниеRect* CloneКлонирует текущий объект класса Rect. Возвращает указатель на созданный объект класса Rect, который должен быть удален вызовом оператора delete после того как станет не нуженBOOL Contains(Point &pt) BOOL Contains(INT x, INT y)Возвращает TRUE, если прямоугольник, представленный текущим объектом класса Rect, включает указанную точку, и FALSE – в противном случаеBOOL Contains(Rect &rect)Возвращает TRUE, если прямоугольник, представленный текущим объектом класса Rect, включает прямоугольник, указанный параметром rect, и FALSE – в противном случаеBOOL Equals(Rect &rect)Возвращает TRUE, если прямоугольник, представленный текущим объектом класса Rect, равен прямоугольнику, указанному параметром rect, и FALSE – в противном случаеINT GetBottom()Возвращает координату по оси Y нижней стороны прямоугольникаvoid GetBounds(Rect *rect)Возвращает прямоугольник, представленный текущим объектом класса Rect. Результат сохраняется в объекте класса Rect, на который указывает параметр rectINT GetLeft()Возвращает координату по оси X левой стороны прямоугольникаvoid GetLocation(Point *point)Возвращает расположение прямоугольника, представленного текущим объектом класса Rect. Результат сохраняется в объекте класса Point, на который указывает параметр pointINT GetRight()Возвращает координату по оси X правой стороны прямоугольникаvoid GetSize(Size *size)Возвращает размер прямоугольника, представленного текущим объектом класса Rect. Результат сохраняется в объекте класса Size, на который указывает параметр sizevoid Inflate(INT dx, INT dy)Расширяет прямоугольник, представленный текущим объектом класса Rect, на значение dx c левой и правой стороны и dy с верхней и нижней стороныvoid Inflate(Point &point)Расширяет прямоугольник, представленный текущим объектом класса Rect, на значение point.X c левой и правой стороны и point.Y с верхней и нижней стороныBOOL Intersect(Rect &c, Rect &a, Rect &b)Находит пересечение прямоугольников, указанных параметрами a и b. Результат сохраняется в объекте класса Rect, на который указывает параметр c. Возвращаемое значение равно TRUE, если пересечение найдено, и FALSE – в противном случаеBOOL Intersect(Rect &rect)Находит пересечение прямоугольника, представленного текущим объектом класса Rect, и прямоугольника, указанного параметром rect. Результат сохраняется в текущем объекте класса Rect. Возвращаемое значение равно TRUE, если пересечение найдено, и FALSE – в противном случаеBOOL IntersectsWith(Rect &rect)Возвращает TRUE, если прямоугольник, представленный текущим объектом класса Rect, пересекается с прямоугольником, указанным параметром rect, и FALSE – в противном случаеBOOL IsEmptyArea()Возвращает TRUE, если поля Width и Height текущего объекта класса Rect меньше или равны нулю, и FALSE – в противном случаеvoid Offset(INT dx, INT dy)Перемещает прямоугольник, представленный текущим объектом класса Rect, на значение dx по горизонтали и dy по вертикалиvoid Offset(Point &point)Перемещает прямоугольник, представленный текущим объектом класса Rect, на значение point.X по горизонтали и point.Y по вертикалиBOOL Union(Rect &c, Rect &a, Rect &b)Находит объединение прямоугольников, указанных параметрами a и b. Результат сохраняется в объекте класса Rect, на который указывает параметр c. Возвращаемое значение равно TRUE, если объединение найдено, и FALSE – в противном случае
Графические объекты GDI+Библиотека GDI+ содержит набор графических объектов, обеспечивающих выполнение различных графических операций. К таким объектам относятся кисти, перья, шрифты и изображения.КистиЗамкнутая фигура (например, такая как окружность или многоугольник) закрашивается с помощью кисти (brush). В GDI+ имеется несколько классов для работы кистями, каждый из которых является производным от класса Brush. Большинство классов GDI+ для работы кистями (включая класс Brush) определено в заголовочном файле gdiplusbrush.h.Сплошные кистиСплошная кисть используется для заполнения замкнутой фигуры однородным цветом. Класс SolidBrush имеет следующий конструктор: SolidBrush(Color &color); Конструктор создает объект класса SolidBrush на основе объекта класса Color, который задает цвет сплошной кисти. В классе SolidBrush определены два метода – GetColor и SetColor, которые работают с цветом кисти. Эти методы имеют следующие прототипы: GetColor(Color *color) SetColor(Color &color); Метод GetColor сохраняет текущее значение цвета кисти в объект класса Color, на который указывает параметр color, метод SetColor задает новое значение цвета кисти. Цвет кисти можно задать как в конструкторе класса SolidBrush, так и позднее, для уже созданного объекта класса SolidBrushШтриховые кистиШтриховая кисть (класс HatchBrush) позволяет заполнять замкнутую фигуру с использованием определенного узора. Класс HatchBrush имеет следующий конструктор: HatchBrush(HatchStyle hatchStyle, Color &foreColor, Color &backColor = Color()); Этот конструктор создает объект класса HatchBrush на основе предопределенного стиля штриховой кисти, который задает его первый параметр – hatchStyle. Этот параметр должен принимать одно из значений перечисляемого типа HatchStyle, приведенных в таблице. Второй параметр, foreColor, задает цвет, который используется при отображении линий узора штриховой кисти, третий параметр, backColor, задает цвет фона штриховой кистиЗначения параметра hatchStyle ЗначениеhexУзорHatchStyle05Percent6HatchStyle10Percent7HatchStyle20Percent8HatchStyle25Percent9HatchStyle30PercentAHatchStyle40PercentBHatchStyle50PercentCHatchStyle60PercentDHatchStyle70PercentEHatchStyle75PercentFHatchStyle80Percent10HatchStyle90Percent11HatchStyleBackwardDiagonal3HatchStyleCross4HatchStyleDarkDownwardDiagonal5HatchStyleDarkHorizontal1DHatchStyleDarkUpwardDiagonal15HatchStyleDarkVertical1CHatchStyleDashedDownwardDiagonalHatchStyleDashedHorizontalHatchStyleDashedUpwardDiagonalHatchStyleDashedVerticalHatchStyleDiagonalBrickHatchStyleDiagonalCrossHatchStyleDivotHatchStyleDottedDiamondHatchStyleDottedGridHatchStyleForwardDiagonalHatchStyleHorizontalHatchStyleHorizontalBrickHatchStyleLargeCheckerBoardHatchStyleLargeConfettiHatchStyleLightDownwardDiagonalHatchStyleLightHorizontalHatchStyleLightUpwardDiagonalHatchStyleLightVerticalHatchStyleNarrowHorizontalHatchStyleNarrowVerticalHatchStyleOutlinedDiamondHatchStylePlaidHatchStyleShingleHatchStyleSmallCheckerBoardHatchStyleSmallConfettiHatchStyleSmallGridHatchStyleSolidDiamondHatchStyleSphereHatchStyleTrellisHatchStyleVerticalHatchStyleWaveHatchStyleWeaveHatchStyleWideDownwardDiagonalHatchStyleWideUpwardDiagonalHatchStyleZigZagМетоды класса HatchBrushВ таблице перечислены методы класса HatchBrush, которые могут быть полезны при работе с объектами этого класса. МетодОписаниеGetBackgroundColor( Color *color)Возвращает цвет, который используется при отображении узора штриховой кисти. Результат сохраняется в объект класса Color, на который указывает параметр colorGetForegroundColor( Color *color)Возвращает цвет фона штриховой кисти. Результат сохраняется в объект класса Color, на который указывает параметр colorHatchStyle GetHatchStyle()Возвращает стиль штриховой кистиТекстурные кистиТекстурная кисть (TextureBrush) позволяет заполнять замкнутую фигуру с использованием изображения. Класс TextureBrush имеет следующие конструкторы: TextureBrush(Image *image, WrapMode wrapMode = WrapModeTile); TextureBrush(Image *image, WrapMode wrapMode, RectF &dstRect); TextureBrush(Image *image, WrapMode wrapMode, RectF &dstRect, ImageAttributes *imageAttributes = NULL); TextureBrush(Image *image, WrapMode wrapMode, Rect &dstRect, ImageAttributes *imageAttributes = NULL); TextureBrush(Image *image, WrapMode wrapMode, Rect &dstRect); TextureBrush(Image *image, WrapMode wrapMode, REAL dstX, REAL dstY, REAL dstWidth, REAL dstHeight); TextureBrush(Image *image, WrapMode wrapMode, INT dstX, INT dstY, INT dstWidth, INT dstHeight); Все эти конструкторы создают объект класса TextureBrush на основе объекта класса Image, на который указывает параметр image. Объект класса Image, который в GDI+ используется для работы с изображениями, содержит изображение, используемое для создания текстурной кисти. Параметр wrapMode задает режим заполнения, когда изображение меньше заполняемой области. Этот параметр должен принимать одно из значений перечисляемого типа WrapMode: WrapModehexОписаниеWrapModeTile0мозаичное заполнение WrapModeTileFlipX1мозаичное заполнение с зеркальным отображением изображения по горизонтали WrapModeTileFlipY2мозаичное заполнение с зеркальным отображением изображения по вертикали WrapModeTileFlipXY3мозаичное заполнение с зеркальным отображением изображения по горизонтали и вертикали WrapModeClamp4заполнение не производитсяПараметр dstRect, а также параметры dstX, dstY, dstWidth и dstHeight, определяют часть изображения, которая будет использоваться для создания кисти. Параметр imageAttributes, указывает на объект класса ImageAttributes, задающий свойства изображения. Этот параметр может быть установлен в NULL. В классе TextureBrush определено несколько методов, которые могут быть полезны при работе с объектами этого класса. Основные методы этого класса перечислены в таблицеМетоды класса TextureBrush МетодОписаниеImage* GetImage()Возвращает указатель на объект класса Image, который использовался при создании текстурной кистиWrapMode GetWrapMode()Возвращает режим заполненияSetWrapMode( WrapMode wrapMode)Задает режим заполненияПолный перечень методов класса TextureBrush, а также их описание, можно найти в документации Platform SDK.Кисти линейного градиентаКисть линейного градиента (класс LinearGradientBrush) используется для заполнения замкнутой фигуры цветовым градиентом, в котором цвет меняется параллельно некоторой линии. Класс LinearGradientBrush имеет следующие конструкторы: LinearGradientBrush(PointF &point1, PointF &point2, Color &color1, Color &color2); LinearGradientBrush(Point &point1, Point &point2, Color &color1, Color &color2); LinearGradientBrush(RectF &rect, Color &color1, Color &color2, LinearGradientMode mode); LinearGradientBrush(Rect &rect, Color &color1, Color &color2, LinearGradientMode mode); LinearGradientBrush(RectF &rect, Color &color1, Color &color2, REAL angle, BOOL isAngleScalable = FALSE); LinearGradientBrush(Rect &rect, Color &color1, Color &color2, REAL angle, BOOL isAngleScalable = FALSE); Все эти конструкторы создают объект класса LinearGradientBrush на основе двух объектов класса Color, которые задают начальный и конечный цвет в линейном градиенте (соответственно параметры color1 и color2). В первом и втором конструкторах класса LinearGradientBrush параметры point1 и point2 задают, соответственно, начальную и конечную точку линейного градиента, которые образуют вектор. Как видно на рисунке, длина этого вектора определяет плавность градиента, а его направление – направление градиента В третьем и четвертом конструкторах класса LinearGradientBrush параметр rect задает прямоугольник, в котором определены начальная и конечная точки линейного градиента. При этом на определение этих точек влияет параметр mode, который должен принимать одно из значений перечисляемого типа LinearGradientMode: LinearGradientModehexLinearGradientModeHorizontal0начальная точка линейного градиента расположена в левом верхнем углу заданного прямоугольника, а конечная – в правом верхнем LinearGradientModeVertical1начальная точка линейного градиента расположена в левом верхнем углу заданного прямоугольника, а конечная – в левом нижнем LinearGradientModeForwardDiagonal2начальная точка линейного градиента расположена в левом верхнем углу заданного прямоугольника, а конечная – в правом нижнем LinearGradientModeBackwardDiagonal3начальная точка линейного градиента расположена в правом верхнем углу заданного прямоугольника, а конечная – в левом нижнем На рисунке показаны возможные варианты расположения начальной и конечной точки линейного градиента, определяемого прямоугольником. В пятом и шестом конструкторах класса LinearGradientBrush параметр rect задает прямоугольник, в верхнем левом углу которого находится начальная точка линейного градиента, а в правом нижнем углу – конечная точка. Параметр angle задает угол (в градусах) по часовой стрелке от верхней границы заданного прямоугольника. При этом если параметр isAngleScalable принимает значение равное FALSE, параметр angle задает угол, определяющий направление градиента. Если же параметр isAngleScalable принимает значение равное TRUE, параметр angle задает базовый угол, из которого рассчитывается угол, определяющий направление градиента, по следующей формуле: [math]\beta =\operatorname{arctg}\left (\frac{\displaystyle width}{\displaystyle height}\times\operatorname{tg}(\alpha)\right )[/math] где [math]\beta[/math]– угол, определяющий направление градиента; [math]width[/math] и [math]heigth[/math] – ширина и высота прямоугольника; [math]\alpha[/math] – базовый угол. Формула справедлива если [math]\alpha<90^{o}[/math].
Кисть линейного градиента, определяемая прямоугольником и углом, который задается (а) или вычисляется из базового угла (б) Для изменения начального и конечного цвета линейного градиента в классе LinearGradientBrush определен метод SetLinearColors: SetLinearColors(Color &color1, Color &color2); Параметры color1 и color2 задают начальный и конечный цвет линейного градиента. В классе LinearGradientBrush имеется метод GetLinear.Colors, который возвращает начальный и конечный цвет градиента: GetLinearColors(Color *colors) Параметр colors указывает на массив из двух объектов класса Color, в которые будут сохранены значения начального и конечного цвета линейного градиента. цвет в линейном градиенте может меняться не только между двумя цветами. В классе LinearGradientBrush есть метод SetInterpolationColors, позволяющий задать несколько цветов интерполяции, между которыми будет изменяться цвет в линейном градиенте. SetInterpolationColors(Color *presetColors, REAL *blendPositions, INT count); Первый параметр, presetColors, указывает на массив объектов класса Color, каждый из которых хранит значение цветов интерполяции в линейном градиенте. Второй параметр, blendPositions, указывает на массив значений типа float, которые задают положение цвета в градиенте. Значение каждого элемента этого массива находится в диапазоне от 0 до 1.0, 0 указывает начало градиента, 1.0 – окончание градиента. В массиве, на который указывает параметр blendPositions, должно быть, по крайней мере, два элемента: начало градиента и его окончание. Третий параметр, count, задает количество элементов в массиве presetColors. Количество элементов в массиве presetColors должно быть таким же, как и в массиве blendPositions. Создание градиентной кисти, определяемой тремя цветами Код (C): // создаем кисть линейного градиента LinearGradientBrush linGrBrush(Rect(0, 0, 200, 200), Color::White, Color::Black, 45.f); // изменяем параметры кисти линейного градиента... Color colors[3] = { Color(255, 255, 0, 0), // красный цвет Color(255, 0, 0, 255), // синий цвет Color(255, 0, 255, 0)}; // зеленый цвет float pos[3] = {0.0f, // начало градиента 0.3f, // 30% от начала градиента до его конца 1.0f}; // конец градиента linGrBrush.SetInterpolationColors(colors, pos, 3); Линейный градиент, определяемый тремя цветамиОпределить количество установленных цветов интерполяции в линейном градиенте можно с помощью метода GetInterpolationColorCount, а получить значения этих цветов и их позиции в градиенте можно с помощью метода GetInterpolationColors. INT GetInterpolationColorCount() GetInterpolationColors(Color *presetColors, REAL *blendPositions, INT count) Первый параметр, presetColors, указывает на массив объектов класса Color, в который будут сохранены значения цветов интерполяции в линейном градиенте. Второй параметр, blendPositions, указывает на массив типа float, в который будут сохранены позиции цветов в градиенте. Третий параметр, count, задает количество элементов в массиве presetColors. При этом количество элементов в массиве presetColors должно быть таким же, как и в массиве blendPositions. По умолчанию цвет в линейном градиенте меняется однородным образом. Однако можно настроить линейный градиент так, чтобы изменение цвета осуществлялось неоднородным образом. В классе LinearGradientBrush есть метод SetBlend, который позволяет настраивать способ градиентного изменения цвета при движении от начала до конца градиента. SetBlend(REAL *blendFactors, REAL *blendPositions, INT count); Первый параметр, blendFactors, указывает на массив значений типа float, которые задают факторы наложения. Значение каждого элемента в этом массиве определяет процент от конечного цвета линейного градиента и должно задаваться в пределах от 0 до 1.0. Второй параметр, blendPositions, указывает на массив значений типа float, которые задают положение фактора наложения в градиенте. Значение каждого элемента этого массива должно находиться в диапазоне от 0 до 1.0, где 0 указывает начало градиента, а 1.0 – окончание градиента. Третий параметр, count, задает количество элементов в массиве blendFactors. При этом количество элементов в массиве blendFactors должно быть таким же, как и в массиве blendPositions. Пример демонстрирует создание кисти линейного градиента, изображенного на рисунке б. Создание градиентной кисти с неоднородным изменением цвета Код (C): // создаем кисть линейного градиента LinearGradientBrush linGrBrush( Point(0, 0), Point(0, 200), Color::Red, // красный цвет Color::Blue); // синий цвет // изменяем параметры кисти линейного градиента... float factors[4] = { 0.0f, // 0% синего цвета, соответственно 100% красного 0.4f, // 40% синего цвета, соответственно 60% красного 0.6f, // 60% синего цвета, соответственно 40% красного 1.0f}; // 100% синего цвета, соответственно 0% красного float pos[4] = { 0.0f, // начало градиента 0.2f, // 20% от начала градиента до его конца 0.8f, // 80% от начала градиента до его конца 1.0f}; // конец градиента linGrBrush.SetBlend(factors, pos, 4); Цветовые градиенты с однородным (а) и неоднородным (б) изменением цветаОпределить количество установленных факторов наложения в линейном градиенте можно с помощью метода GetBlendCount, а получить значения этих факторов и их позиции в градиенте можно с помощью метода GetBlend. Прототипы этих методов имеют следующий вид: INT GetBlendCount() GetBlend(REAL *blendFactors, REAL *blendPositions, INT count) Первый параметр, blendFactors, указывает на массив типа float, в который будут сохранены значения факторов наложения в линейном градиенте. Второй параметр, blendPositions, указывает на массив значений типа float, в который будут сохранены позиции факторов наложения в градиенте. Третий параметр, count, задает количество элементов в массиве blendFactors. При этом количество элементов в массиве blendFactors должно быть таким же, как в массиве blendPositions. Чтобы интенсивность цветов в линейном градиенте была распределена более равномерно, можно включить гамма-коррекцию. Для этого в классе LinearGradientBrush определен метод SetGammaCorrection: SetGammaCorrection(BOOL useGammaCorrection); Параметр useGammaCorrection определяет, будет ли включена гамма-коррекция или нет. Если значение этого параметра равно TRUE, гамма-коррекция будет включена. По умолчанию гамма-коррекция отключена. На рисунке два линейных градиента: в одном гамма-коррекция отключена (а), в другом – включена (б). Линейный градиент: а – без гамма-коррекции; б – с гамма-коррекциейОпределить включена или нет гамма-коррекция можно с помощью метода GetGammaCorrection() Если гамма-коррекция включена, метод возвращает значение TRUE, в противном случае – FALSE . Класс LinearGradientBrush (подобно классу TextureBrush) позволяет определить режим заполнения, когда размер линейного градиента меньше заполняемой области. Для этого у него есть два метода – GetWrapMode и SetWrapMode, которые, соответственно, возвращает и задает режим заполнения. Методы имеют следующие прототипы: GetWrapMode() SetWrapMode(WrapMode wrapMode); На рисунке а показан пример мозаичного заполнения прямоугольника линейным градиентом, представленным на рисунке б. Мозаичное заполнение прямоугольника (а) линейным градиентом (б)Также класс LinearGradientBrush позволяет определить границы линейного градиента с помощью метода GetRectangle: GetRectangle(Rect *rect); GetRectangle(RectF *rect); Параметр rect указывает на объект класса Rect (или RectF), в который будет сохранен прямоугольник, определяющий границы линейного градиента. Кисти градиента контура Кисть градиента контура (класс PathGradientBrush) используется для заполнения замкнутой фигуры цветовым градиентом, в котором цвет меняется от центральной точки наружу до границы, определяемой замкнутым контуром. Класс PathGradientBrush определен в заголовочном файле gdipluspath.h и имеет следующие конструкторы: PathGradientBrush(GraphicsPath *path); PathGradientBrush(Point *points, INT count, WrapMode wrapMode = WrapModeClamp); PathGradientBrush(Point *points, INT count, WrapMode wrapMode = WrapModeClamp); Первый конструктор создает объект класса PathGradientBrush на основе объекта класса GraphicsPath, который в GDI+ используется для работы с контурами. Второй и третий конструкторы создают объект класса PathGradientBrush на основе массива точек, на который указывает параметр points, а параметр count задает количество элементов в этом массиве. Параметр wrapMode задает режим заполнения мозаичного заполнения. По умолчанию заполнение не производится. В следующем примере продемонстрировано создание кисти градиента контура на основе контура треугольной формы. Пример создания кисти градиента контура Код (C): // вершины треугольника Point points[3] = { Point(100, 0), Point(200, 200), Point(0, 200)}; // создаем кисть градиента контура PathGradientBrush pthGrBrush(points, 3); Рассмотрим на основе этого примера некоторые методы класса PathGradientBrush, которые могут пригодиться при работе кистями градиента контура. Полный перечень и подробное описание методов класса PathGradientBrush в документации Platform SDK. Класс PathGradientBrush позволяет определять отдельные цвета для центра, границы и даже для каждой точки на границе контура. Для работы с цветом центра контура в классе PathGradientBrush определены два метода – GetCenterColor и SetCenterColor: GetCenterColor(Color *color); SetCenterColor(Color &color); Метод GetCenterColor сохраняет текущее значение цвета центра контура в объект класса Color, на который указывает параметр color, а метод SetCenterColor задает новое значение цвета для центра контура. Для задания цвета границы контура в классе PathGradientBrush определен метод SetSurroundColors: SetSurroundColors(Color *colors, INT *count); Первый параметр, colors, указывает на массив объектов класса Color, каждый из которых хранит значение цветов. Второй параметр, count, указывает на переменную типа int, которая должна содержать количество задаваемых цветов. Если метод SetSurroundColors будет выполнен успешно, в переменную, на которую указывает этот параметр, будет сохранено количество заданных цветов. В листинге продемонстрирован пример задания отдельных цветов для центра и точек на границе контура для кисти, созданной в предыдущем примере. Полученный результат показан на рисунке а. Пример задания цветов для кисти градиента контура Код (C): // задаем цвет центра контура pthGrBrush.SetCenterColor(Color::Red); // задаем цвета для каждой точки на границе контура... Color colors[3] = { Color::DarkGreen, Color::Aqua, Color::Blue}; int n = 3; pthGrBrush.SetSurroundColors(colors, &n); Следующий пример демонстрирует задание цветов для центра и для всей границы контура для кисти из примера в листинге. Результат представлен на рисунке б. Еще один пример задания цветов для кисти градиента контура Код (C): // задаем цвет центра контура pthGrBrush.SetCenterColor(Color::Aqua); // задаем цвет границы контура... Color color = Color::Blue; int n = 1; pthGrBrush.SetSurroundColors(&color, &n); Различные варианты градиента контура: а – каждая точка на границе имеет свой цвет; б – общий цвет для всей границы Еще один пример задания цветов для кисти градиента контура Код (C): // задаем цвет центра контура pthGrBrush.SetCenterColor(Color::Aqua); // задаем цвет границы контура... Color color = Color::Blue; int n = 1; pthGrBrush.SetSurroundColors(&color, &n); Различные варианты градиента контура: а – каждая точка на границе имеет свой цвет; б – общий цвет для всей границыОпределить количество установленных цветов в градиенте контура можно с помощью метода GetSurroundColorCount, а получить значения этих цветов можно с помощью метода GetSurroundColors. Прототипы этих методов записываются следующим образом: INT GetSurroundColorCount(); GetSurroundColors(Color *colors, INT *count); Первый параметр, colors, указывает на массив объектов класса Color, в который будут сохранены значения цветов. Второй параметр, count, указывает на переменную типа int, которая должна содержать количество запрашиваемых цветов. Если метод GetSurroundColors будет выполнен успешно, в переменную, на которую указывает этот параметр, будет сохранено количество полученных цветов. Цвет центра используется не только в центральной точке, но и везде в пределах внутреннего контура. Класс PathGradientBrush позволяет настраивать внутренний контур, увеличивая размер фокуса с помощью метода SetFocusScales: SetFocusScales(REAL xScale, REAL yScale); Параметры xScale и yScale задают коэффициенты масштабирования, соответственно по шкале x и y. На рисунке показан эффект применения коэффициентов масштабирования к фокусу кисти градиента контура, который можно получить, если добавить в предыдущий пример следующий программный код: Код (C): // применим коэффициенты масштабирования pthGrBrush.SetFocusScales(0.3f, 0.7f); Масштабирование фокуса кисти градиента контураЧтобы получить установленные коэффициенты масштабирования фокуса нужно использовать метод GetFocusScales: GetFocusScales(REAL *xScale, REAL *yScale); Параметры xScale и yScale указывают на переменные типа float, в которые будут записаны коэффициенты масштабирования, соответственно по шкале x и y. По умолчанию центральной точкой кисти градиента контура является центр контура, используемого при создании этой кисти. Положение центральной точки можно изменить с помощью метода SetCenter.Point класса PathGradientBrush: SetCenterPoint(Point &point); SetCenterPoint(PointF &point); Параметр point ссылается на объект класса Point (или PointF), в котором представлены координаты новой центральной точки. На рисунке а показан результат изменения положения центральной точки, который можно получить, если добавить в пример из листинга следующий программный код: Код (C): // изменяем положение центральной точки pthGrBrush.SetCenterPoint( Point(80,120) ); В качестве центральной точки кисти градиента контура можно установить точку, которая лежит вне контура. На рисунке б показан результат задания центральной точки вне контура, представленного в следующем фрагменте программного кода: Код (C): // изменяем положение центральной точки pthGrBrush.SetCenterPoint( Point(-10,210) ); Изменение положения центральной точки кисти градиента контура: а – точка внутри контура; б – точка вне контураТекущее положение центральной точки определяют с помощью метода GetCenterPoint: GetCenterPoint(Point *point); GetCenterPoint(PointF *point); Параметр point указывает на объект класса Point (или PointF), в который будут сохранены координаты центральной точки. Нужно отметить, что в классе PathGradientBrush также имеются методы аналогичные тем, что были рассмотрены в классе LinearGradientBrush. Вот эти методы: GetBlend; GetBlendCount; GetGammaCorrection; GetInterpolationColorCount; GetInterpolationColors; GetRectangle; GetWrapMode; SetBlend; SetGammaCorrection; SetInterpolationColors; SetWrapMode. Перечисленные методы класса PathGradientBrush дают несколько другой эффект чем аналогичные методы класса LinearGradientBrush. В листинге показан пример использования метода SetInterpolationColors для того, чтобы изменить созданную кисть градиента контура, как показано на рисунке. Градиент контура, определяемый тремя цветамиСоздание кисти с неоднородным изменением цвета Код (C): // изменяем параметры кисти градиента контура... Color colors[3] = { Color::DarkGreen, Color::Aqua, Color::Blue}; float pos[3] = { 0.0f, // начало градиента 0.4f, // 40% от начала градиента до его конца 1.0f}; // конец градиента pthGrBrush.SetInterpolationColors(colors, pos, 3); Класс Brush Класс Brush является родительским для классов SolidBrush, HatchBrush, TextureBrush, LinearGradientBrush и PathGradientBrush. Однако в отличие от своих потомков сам по себе класс Brush практически бесполезен, так как его нельзя использовать непосредственно. Различные классы GDI+ в своих методах, выполняющих заполнение замкнутой фигуры, требуют указывать в качестве аргумента именно объект класса Brush. Это сделано только для того, чтобы такие методы могли работать идентично вне зависимости от кисти, используемой для заполнения. Дело в том, что все классы для работы кистями, являясь производными от класса Brush, могут использоваться в качестве аргумента в методах, которые требуют объект класса Brush. Следует также отметить, что класс Brush имеет несколько методов, которые наследуют все потомки этого класса. Среди этих методов есть уже рассмотренный ранее метод GetLastStatus. В классе также есть метод Clone, который клонирует текущий объект класса производного от класса Brush. Метод Clone имеет следующий прототип: Brush* Clone() ; В случае успеха метод возвращает указатель на объект класса Brush, который должен быть удален вызовом оператора delete после того как станет не нужен. В случае ошибки – NULL. В листинге пример использования метода Clone для клонирования объекта класса SolidBrush. При этом если аргумент brush указывает не на объект класса SolidBrush, клонирование не выполняется.
Создание кисти с неоднородным изменением цвета Код (C): // изменяем параметры кисти градиента контура... Color colors[3] = { Color::DarkGreen, Color::Aqua, Color::Blue}; float pos[3] = { 0.0f, // начало градиента 0.4f, // 40% от начала градиента до его конца 1.0f}; // конец градиента pthGrBrush.SetInterpolationColors(colors, pos, 3); Класс Brush Класс Brush является родительским для классов SolidBrush, HatchBrush, TextureBrush, LinearGradientBrush и PathGradientBrush. В отличие от своих потомков сам по себе класс Brush практически бесполезен, так как его нельзя использовать непосредственно. Различные классы GDI+ в своих методах, выполняющих заполнение замкнутой фигуры, требуют указывать в качестве аргумента объект класса Brush. Это сделано только для того, чтобы такие методы могли работать идентично вне зависимости от кисти, используемой для заполнения. Все классы для работы кистями, являясь производными от класса Brush, могут использоваться в качестве аргумента в методах, которые требуют объект класса Brush. Класс Brush имеет несколько методов, которые наследуют все потомки этого класса. Среди этих методов GetLastStatus. В классе есть метод Clone, который клонирует текущий объект класса производного от класса Brush. Метод Clone имеет следующий прототип: Brush* Clone(); В случае успеха метод возвращает указатель на объект класса Brush, который должен быть удален вызовом оператора delete после того как станет не нужен. В случае ошибки – NULL. В листинге продемонстрирован пример использования метода Clone для клонирования объекта класса SolidBrush. Если аргумент brush указывает не на объект класса SolidBrush, клонирование не выполняется. Функция клонирования объекта класса SolidBrush Код (C): SolidBrush* CloneSolidBrush(Brush *brush) { SolidBrush *result = dynamic_cast<SolidBrush *>(brush); if (NULL != result) result = (SolidBrush *)result->Clone(); return result; } // CloneSolidBrush Функцию из предыдущего примера можно использовать следующим образом: Код (C): SolidBrush *cloneBrush = NULL; // создаем сплошную кисть SolidBrush solidBrush(Color::Red); // создаем штриховую кисть HatchBrush hatchBrush(HatchStyleCross, Color::Blue); // клонируем объект solidBrush // результат - указатель новый экземпляр класса SolidBrush cloneBrush = CloneSolidBrush(&solidBrush); // удаляем клон delete cloneBrush, cloneBrush = NULL; // клонируем объект hatchBrush // результат - NULL cloneBrush = CloneSolidBrush(&hatchBrush); Определить тип кисти можно и без использования оператора dynamic_cast. В классе Brush есть метод GetType, который возвращает тип кисти. Прототип метода GetType имеет следующий вид: BrushType GetType(); Метод возвращает одно из следующих значений перечисляемого типа BrushType: BrushTypeSolidColor – сплошная кисть; BrushTypeHatchFill – штриховая кисть; BrushTypeTextureFill – текстурная кисть; BrushTypePathGradient – кисть градиента контура; BrushTypeLinearGradient – кисть линейного градиента. В следующем примере продемонстрирована видоизмененная функция, которая клонирует объект класса SolidBrush: Функция клонирования объекта класса SolidBrush Код (C): SolidBrush* CloneSolidBrush(Brush *brush) { BrushType type = brush->GetType(); if (type == BrushTypeSolidColor) return (SolidBrush *)brush->Clone(); return NULL; } // CloneSolidBrush ПерьяДля рисования линий, кривых и границ (контуров) различных фигур используется перо (pen), которое определяет графические атрибуты такие, как цвет, толщина, стиль штриха и т.п. Работа с перьями в GDI+ осуществляется с помощью класса Pen, который определен в заголовочном файле gdipluspen.h. Класс Pen имеет следующие конструкторы: Pen(Color &color, REAL width = 1.0f); Pen(Brush *brush, REAL width = 1.0f); Первый конструктор создает объект класса Pen на основе объекта класса Color. Второй конструктор создает объект класса Pen на основе объекта класса Brush. Параметр width задает толщину пера в логических единицах. В следующем примере показаны два способа создания пера красного цвета и толщиной 4. Код (C): // способ первый: // просто создаем объект класса Pen Pen pen1(Color(255, 0, 0), 4.f); // способ второй: // сперва создаем сплошную кисть SolidBrush sBrush(Color::Red); // красный цвет // а затем создаем объект класса Pen на основе объекта sBrush Pen pen2(&sBrush, 4.f); Прежде чем перейти к изучению основных методов класса Pen, следует отметить, что в этом классе (как и в классе Brush) есть методы GetLastStatus и Clone. Метод Clone имеет следующий прототип: Pen* Clone(); В случае успеха метод Clone возвращает указатель на объект класса Pen, который должен быть удален вызовом оператора delete после того как станет не нужен. В случае ошибки – NULL.Цвет пераЦвет пера определяет цвет рисуемых линий. В классе Pen определены два метода – GetColor и SetColor, которые работают с цветом пера. Эти методы имеют следующие прототипы: GetColor(Color *color); SetColor(Color &color); Метод GetColor сохраняет текущее значение цвета пера в объект класса Color, на который указывает параметр color, а метод SetColor задает новое значение цвета пера.Толщина и выравнивание пераТолщину пера можно указать в качестве одного из параметров конструктора класса Pen при создании объекта это класса. Можно также изменять толщину пера с помощью метода SetWidth: SetWidth(REAL width); Параметр width задает новую толщину пера. Определить текущую толщину пера можно с помощью метода GetWidth класса Pen: REAL GetWidth(); Если толщина пера больше 1.0, при рисовании замкнутого контура можно указать положение такого пера относительно рисуемого контура. Для этого используется метод SetAlignment класса Pen: SetAlignment(PenAlignment penAlignment); Параметр penAlignment задает положение пера. Этот параметр может принимать одно из значений перечисляемого типа PenAlignment: PenAlignmenthexPenAlignmentCenter0перо располагается по центру контура рисуемой фигуры PenAlignmentInset1перо располагается с внутренней стороны контура рисуемой фигуры На рисунке показаны различные варианты положения пера при рисовании окружности. Создание кисти с неоднородным изменением цвета Код (C): // изменяем параметры кисти градиента контура... Color colors[3] = { Color::DarkGreen, Color::Aqua, Color::Blue}; float pos[3] = { 0, // начало градиента 0.4, // 40% от начала градиента до его конца 1.0}; // конец градиента pthGrBrush.SetInterpolationColors(colors, pos, 3); Класс Brush Как уже было сказано ранее класс Brush является родительским для классов SolidBrush, HatchBrush, TextureBrush, LinearGradientBrush и PathGradientBrush. Однако в отличие от своих потомков сам по себе класс Brush практически бесполезен, так как его нельзя использовать непосредственно. Несмотря на это, различные классы GDI+ в своих методах, выполняющих заполнение замкнутой фигуры, требуют указывать в качестве аргумента именно объект класса Brush. Это сделано только для того, чтобы такие методы могли работать идентично вне зависимости от кисти, используемой для заполнения. Дело в том, что все классы для работы кистями, являясь производными от класса Brush, могут использоваться в качестве аргумента в методах, которые требуют объект класса Brush. Следует также отметить, что класс Brush имеет несколько методов, которые наследуют все потомки этого класса. Среди этих методов есть уже рассмотренный ранее метод GetLastStatus. В классе есть метод Clone, который клонирует текущий объект класса производного от класса Brush. Метод Clone имеет следующий прототип: Brush* Clone(); В случае успеха этот метод возвращает указатель на объект класса Brush, который должен быть удален вызовом оператора delete после того как станет не нужен. В случае ошибки – NULL. В листинге продемонстрирован пример использования метода Clone для клонирования объекта класса SolidBrush. При этом если аргумент brush указывает не на объект класса SolidBrush, клонирование не выполняется. Функция клонирования объекта класса SolidBrush Код (C): SolidBrush* CloneSolidBrush(Brush *brush) { SolidBrush *result = dynamic_cast<SolidBrush *>(brush); if (NULL != result) result = (SolidBrush *)result->Clone(); return result; } // CloneSolidBrush Функцию из предыдущего примера можно использовать следующим образом: Код (C): SolidBrush *cloneBrush = NULL; // создаем сплошную кисть SolidBrush solidBrush(Color::Red); // создаем штриховую кисть HatchBrush hatchBrush(HatchStyleCross, Color::Blue); // клонируем объект solidBrush // результат - указатель новый экземпляр класса SolidBrush cloneBrush = CloneSolidBrush(&solidBrush); // удаляем клон delete cloneBrush, cloneBrush = NULL; // клонируем объект hatchBrush // результат - NULL cloneBrush = CloneSolidBrush(&hatchBrush); Определить тип кисти можно и без использования оператора dynamic_cast. В классе Brush есть метод GetType, который возвращает тип кисти. Прототип метода GetType имеет следующий вид: BrushType GetType(); Этот метод возвращает одно из следующих значений перечисляемого типа BrushType: BrushTypeSolidColor – сплошная кисть; BrushTypeHatchFill – штриховая кисть; BrushTypeTextureFill – текстурная кисть; BrushTypePathGradient – кисть градиента контура; BrushTypeLinearGradient – кисть линейного градиента. В следующем примере продемонстрирована видоизмененная функция, которая клонирует объект класса SolidBrush: Функция клонирования объекта класса SolidBrush Код (C): SolidBrush* CloneSolidBrush(Brush *brush) { BrushType type = brush->GetType(); if (type == BrushTypeSolidColor) return (SolidBrush *)brush->Clone(); return NULL; } // CloneSolidBrush ПерьяДля рисования линий, кривых и границ (контуров) различных фигур используется перо (pen), которое определяет графические атрибуты такие, как цвет, толщина, стиль штриха и т.п. Работа с перьями в GDI+ осуществляется с помощью класса Pen, который определен в заголовочном файле gdipluspen.h. Класс Pen имеет следующие конструкторы: Pen(Color &color, REAL width = 1.0); Pen(Brush *brush, REAL width = 1.0); Первый конструктор создает объект класса Pen на основе объекта класса Color. Второй конструктор создает объект класса Pen на основе объекта класса Brush. Параметр width задает толщину пера в логических единицах. В следующем примере показаны два способа создания пера красного цвета и толщиной 4. Код (C): // способ первый: просто создаем объект класса Pen Pen pen1(Color(255, 0, 0), 4.0); // способ второй: сперва создаем сплошную кисть SolidBrush sBrush(Color::Red); // красный цвет // а затем создаем объект класса Pen на основе объекта sBrush Pen pen2(&sBrush, 4.0); Прежде чем перейти к изучению основных методов класса Pen, следует отметить, что в этом классе (как и в классе Brush) есть методы GetLastStatus и Clone. Метод Clone имеет следующий прототип: Pen* Clone(); В случае успеха метод Clone возвращает указатель на объект класса Pen, который должен быть удален вызовом оператора delete после того как станет не нужен. В случае ошибки – NULL.Цвет пераЦвет пера определяет цвет рисуемых линий. В классе Pen определены два метода – GetColor и SetColor, которые работают с цветом пера. Эти методы имеют следующие прототипы: GetColor(Color *color); SetColor(Color &color); Метод GetColor сохраняет текущее значение цвета пера в объект класса Color, на который указывает параметр color, а метод SetColor задает новое значение цвета пера.Толщина и выравнивание пераТолщину пера можно указать в качестве одного из параметров конструктора класса Pen при создании объекта это класса. Можно также изменять толщину пера с помощью метода SetWidth: SetWidth(REAL width); Параметр width задает новую толщину пера. Определить текущую толщину пера можно с помощью метода GetWidth класса Pen: REAL GetWidth(); Если толщина пера больше 1.0, при рисовании замкнутого контура можно указать положение такого пера относительно рисуемого контура. Для этого используется метод SetAlignment класса Pen: SetAlignment(PenAlignment penAlignment); Параметр penAlignment задает положение пера. Этот параметр может принимать одно из значений перечисляемого типа PenAlignment: PenAlignmentCenter – перо располагается по центру контура рисуемой фигуры; PenAlignmentInset – перо располагается с внутренней стороны контура рисуемой фигуры. По умолчанию перо имеет положение по центру контура. Определить текущее положение пера относительно контура можно с помощью метода GetAlignment класса Pen: PenAlignment GetAlignment();Стиль штрихаСтиль штриха определяет, каким образом будут рисоваться различные линии и контуры. Например, они могут рисоваться непрерывно или пунктиром, или еще как-нибудь. В классе Pen есть метод SetDashStyle, который задает стиль штриха. Этот метод имеет следующий прототип: SetDashStyle(DashStyle dashStyle); Параметр dashStyle определяет новый стиль штриха. Этот параметр должен принимать одно из значений перечисляемого типа DashStyle: DashStyleSolid – сплошная линия; DashStyleDash – обычный штрих; DashStyleDot – точки; DashStyleDashDot – штрих-пунктир; DashStyleDashDotDot – штрих-две точки. На рисунке представлены варианты линий, нарисованных с различными стилями штриха.Положение пера при рисовании окружностиПо умолчанию перо имеет положение по центру контура. Определить текущее положение пера относительно контура можно с помощью метода GetAlignment класса Pen: PenAlignment GetAlignment();Стиль штрихаСтиль штриха определяет, каким образом будут рисоваться различные линии и контуры. Например, они могут рисоваться непрерывно или пунктиром, или еще как-нибудь. В классе Pen есть метод SetDashStyle, который задает стиль штриха. Этот метод имеет следующий прототип: SetDashStyle(DashStyle dashStyle); Параметр dashStyle определяет новый стиль штриха. Этот параметр должен принимать одно из значений перечисляемого типа DashStyle: DashStylehexDashStyleSolid0сплошная линия DashStyleDash1обычный штрих DashStyleDot2точки DashStyleDashDot3штрих-пунктир DashStyleDashDotDot4штрих-две точки По умолчанию перо рисует сплошную линию. Определить текущий стиль штриха можно с помощью метода GetDashStyle класса Pen: DashStyle GetDashStyle(); Если стандартные стили штрихов не подходят, можно использовать собственный шаблон штриха. В классе Pen есть метод SetDashPattern, который задает шаблон штриха. Этот метод имеет следующий прототип: SetDashPattern(REAL *dashArray, INT count); dashArray, указывает на массив значений типа float, каждое из которых определяет длину штрихов и пробелов между ними. Все значения в этом массиве должны быть больше нуля. count, определяет количество элементов в массиве, на который указывает параметр dashArray. В следующем примере показано, как можно задавать шаблон штриха с помощью метода SetDashPattern класса Pen. Пример задания шаблона штриха Код (C): // массив штрихов и пробелов float dash[8] = { 6.0, // штрих длиной 6 3.0, // пробел длиной 3 1.0, // штрих длиной 1 2.0, // пробел длиной 2 1.0, // штрих длиной 1 2.0, // пробел длиной 2 1.0, // штрих длиной 1 3.0}; // пробел длиной 3 // создаем перо черного цвета и толщиной 2 Pen pen(Color::Black, 2.0); // задаем шаблон штриха pen.SetDashPattern(dash, 8); В приведенном примере шаблон штриха создается на основе массива {6, 3, 1, 2, 1, 2, 1, 3}. Умножив элементы этого массива на толщину пера, равную 2, получим массив {12, 6, 2, 4, 2, 4, 2, 6}. Получившиеся штрихи будут иметь длину 12 и 2, а длина промежутков между ними будет равна 6 или 4. Нарисованная с помощью такого пера линия показана на рисунке. Пример штриховой линииЕсли для пера установлен шаблон штриха, определить количество элементов в массиве, задающем длину штрихов и пробелов, можно с помощью метода GetDashPatternCount класса Pen, получить значения этого массива с помощью метода GetDashPattern. INT GetDashPatternCount(); GetDashPattern(REAL *dashArray, INT count); dashArray, указывает на массив типа float, в который будут сохранены длины штрихов и пробелов в установленном шаблоне штриха. count, задает количество элементов в массиве, на который указывает параметр dashArray. Стиль штриха определяет не только длину штрихов, но и форму их концов. В классе Pen есть метод SetDashCap, который указывает форму обоих концов каждого штриха. Метод SetDashCap имеет следующий прототип: SetDashCap(DashCap dashCap); Параметр dashCap задает форму концов штриха. Этот параметр должен принимать одно из значений перечисляемого типа DashCap: На рисунке представлены линии, нарисованные с различными концами штриха. DashCaphexDashCapFlat0прямоугольная форма DashCapRound2круглая форма DashCapTriangle3треугольная форма Штрихованные линии с различными формами концов штриха По умолчанию перо использует прямоугольную форму для обоих концов штриха. Определить текущую форму конца штриха можно с помощью метода GetDashCap класса Pen: GetDashCap(); При рисовании линий также можно указать расстояние от начала линии до начала штриха. На рисунке, показана линия, для которой расстояние от начала линии до начала штриха равно 0, а на рисунке б – линия, для которой такое расстояние равно 3. При этом в обоих случаях используется шаблон штриха из примера в листинге. абПример штриховой линии, в которой штрих начинается: а – с начала; б – с небольшим смещениемВ классе Pen есть два метода – GetDashOffset и SetDashOffset. Метод GetDashOffset возвращает текущее расстояние от начала линии до начала штриха, а метод SetDashOffset изменяет это расстояние. REAL GetDashOffset(); SetDashOffset(REAL dashOffset);Соединение и концы линийСоединение линий – это область, образуемая двумя линиями с соприкасающимися концами. Стиль соединения линий является атрибутом. После задания стиля соединения линий для объекта класса Pen этот стиль будет применяться ко всем соединенным линиям, для рисования которых используется данный объект. Для задания стиля соединения линий в классе Pen служит метод SetLineJoin: SetLineJoin(LineJoin lineJoin); Параметр lineJoin задает новый стиль соединения линий. Этот параметр должен принимать одно из значений перечисляемого типа LineJoin: LineJoinhexLineJoinMiter0угловое соединение со скосом в 45°. Получается острый (рис. а) или обрезанный угол (рис. б) в зависимости от того, превышает ли длина среза ограничение по срезуLineJoinBevel1скошенное соединение. Получается угол при диагоналиLineJoinRound2круговое соединение. Получается ровная круговая дуга между двумя линиямиLineJoinMiterClipped3угловое соединение со скосом в 45°. Получается острый (рис. а) или срезанный угол (рис. б) в зависимости от того, превышает ли длина среза ограничение по срезу абУгловое соединение, в котором длина среза: а – не превышает ограничение; б – превышает ограничениеПо умолчанию перо использует угловое соединение. Определить текущий стиль соединения линий можно с помощью метода GetLineJoin класса Pen: GetLineJoin(); Для того чтобы установить ограничение по срезу следует использовать метод SetMiterLimit класса Pen: SetMiterLimit(REAL miterLimit); Параметр miterLimit задает новое ограничение по срезу. Если значение параметра miterLimit меньше 1, оно будет заменено на 1. По умолчанию ограничение по срезу имеет значение 10. Определить текущее значение ограничения по срезу можно с помощью метода GetMiterLimit класса Pen: REAL GetMiterLimit(); Еще одним атрибутом является форма концов линии. Для указания формы концов линий в классе Pen есть два метода – SetStartCap и SetEndCap. Метод SetStartCap задает форму начала линии, а метод SetEndCap – форму окончания линии. Прототипы этих методов имеют следующий вид: SetStartCap(LineCap startCap); SetEndCap(LineCap endCap); Параметр startCap (endCap) определяет форму начала (окончания) линии. Этот параметр должен принимать одно из значений перечисляемого типа LineCap: ▪ LineCapFlat – прямоугольная форма; ▪ LineCapSquare – квадратная форма; ▪ LineCapRound – круглая форма; ▪ LineCapTriangle – треугольная форма; ▪ LineCapNoAnchor – без маркера; ▪ LineCapSquareAnchor – квадратный маркер; ▪ LineCapRoundAnchor – круглый маркер; ▪ LineCapDiamondAnchor – маркер в форме ромба; ▪ LineCapArrowAnchor – маркер в форме стрелки. По умолчанию на обоих концах перо использует плоское завершение. Определить текущую форму можно с помощью двух методов класса Pen – GetStartCap и GetEndCap. Метод GetStartCap определяет форму начала линии, а метод GetEndCap – форму окончания линии. GetStartCap(); GetEndCap(); На рисунке показаны различные стили соединений в сочетании с разными формами начала и окончания ломаной линии. LineJoinMiterLineJoinBevelLineJoinRoundLineCapFlat LineCapSquare LineCapRound LineCapTriangle Ломаные линии с различными стилями соединений в сочетании с разными формами начала и окончания Обратите внимание на рисунок, на котором представлены варианты линий, на обоих концах которых нарисованы специальные фигуры, называемые маркерами. hexLineCapSquareAnchor11 LineCapRoundAnchor12 LineCapDiamondAnchor13 LineCapArrowAnchor14 Линии с различными типами маркеров Нужно отметить, что в классе Pen имеется метод SetLineCap, который позволяет сразу задать форму для обоих концов линии и форму для концов штриха. Метод имеет SetLineCap следующий прототип: SetLineCap(LineCap startCap, LineCap endCap, DashCap dashCap); Параметры startCap и endCap определяют форму, соответственно, начала и окончания линии. Эти параметры должны принимать одно из значений перечисляемого типа LineCap. Последний параметр, dashCap, определяет форму обоих концов каждого штриха. Этот параметр должен принимать одно из значений перечисляемого типа DashCap.
Составные перьяСоставное перо рисует составную (сложную) линию, состоящую из параллельных линий и разделяющих их промежутков. Класс Pen позволяет делать из обычного пера составное перо. Для этого в классе Pen есть метод SetCompoundArray: SetCompoundArray(REAL *compoundArray, INT count); compoundArray, указывает на массив значений типа float, определяющих составное перо. Значения в массиве должны быть в порядке возрастания и находиться в диапазоне от 0 до 1.0. count, определяет количество элементов в массиве, на который указывает параметр compoundArray. В листинге приведен пример создания составного пера, которым нарисованы контуры для фигур, изображенных на рисунке. Пример создания составного пера Код (C): // создаем перо черного цвета и толщиной 10 Gdiplus::Pen pen(Color::Black, 10.f); // массив, определяющий составное перо float comp[6] = { 0.0f, 0.2f, // 1-я линия: от 0% до 20% от толщины пера 0.3f, 0.7f, // 2-я линия: от 30% до 70% от толщины пера 0.8f, 1.0f}; // 3-я линия: от 80% до 100% от толщины пера pen.SetCompoundArray(comp, 6); // делаем из пера составное перо Рисование контуров составным перомЕсли перо является составным, определить количество элементов в массиве, определяющем это составное перо, можно с помощью метода GetCompoundArrayCount класса Pen, получить значения этого массива с помощью метода GetCompoundArray. INT GetCompoundArrayCount(); GetCompoundArray(REAL *compoundArray, INT count); compoundArray, указывает на массив типа float, в который будут сохранены значения массив, определяющего это составного перо. count, задает количество элементов в массиве, на который указывает параметр compoundArray. Перья с текстурным заполнениемВместо рисования линий сплошным цветом можно нарисовать их с текстурной заливкой, например, как на рисунке. Для рисования линий с текстурной заливкой необходимо создать объект класса TextureBrush, определяющего текстурную кисть, и передать этот объект конструктору класса Pen при создании пера. Рисование пером с текстурным наполнениемШрифтыДля отображения текста используется шрифт (font), который определяет форму букв отображаемого текста. Работа с шрифтами в GDI+ осуществляется с помощью класса Font, который определен в заголовочном файле gdiplusheaders.h. Класс Font имеет следующие конструкторы: Font(HDC hdc); Font(HDC hdc, LOGFONTA *logfont); Font(HDC hdc, LOGFONTW *logfont); Font(HDC hdc, HFONT hfont); Font(FontFamily *family, REAL emSize, INT style = FontStyleRegular, Unit unit = UnitPoint); Font(WCHAR *familyName, REAL emSize, INT style = FontStyleRegular, Unit unit = UnitPoint, FontCollection *fontCollection = NULL); Первый конструктор создает объект класса Font на основе дескриптора контекста устройства, на который указывает параметр hdc. Второй и третий конструкторы создают объект класса Font на основе дескриптора контекста устройства и структуры LOGFONT, на которую указывает параметр logfont. Структура LOGFONT используется в библиотеке GDI для создания шрифтов. Четвертый конструктор создает объект класса Font на основе дескриптора контекста устройства и дескриптора шрифта, на который указывает параметр hfont. Дескриптор шрифта возвращают функции библиотеки GDI, которые создают шрифт, – CreateFont и CreateFontIndirect. Пятый и шестой конструкторы создают объект класса Font на основе имени гарнитуры шрифта, одними из известных гарнитур являются Arial, Times New Roman и Courier New. В пятом конструкторе имя гарнитуры определяется объектом класса FontFamily, на который указывает параметр family, а в шестом – строкой в формате Unicode, на которую указывает familyName. Параметр emSize задает размер шрифта в единицах измерения, задаваемых параметром unit. Параметр unit должен принимать одно из значений перечисляемого типа Unit. Параметр style задет стиль шрифта. Этот параметр должен принимать одно (или комбинацию) из значений перечисляемого типа FontStyle: FontStylehexFontStyleRegular0обычный текстFontStyleBold1полужирный текстFontStyleItalic2курсивный текстFontStyleBoldItalic3полужирный и курсивный текстFontStyleUnderline4подчеркнутый текстFontStyleStrikeout8перечеркнутый текстПараметр fontCollection указывает на объект класса FontCollection. Класс FontCollection используется в GDI+ для представления набора шрифтов, из которого будет выбираться шрифт, подходящий под задаваемые параметры, при создании объекта класса Font. Если этот параметр установлен в NULL, шрифт будет выбираться из набора шрифтов, установленных в операционной системе. В следующем примере показаны два способа создания шрифта Times New Roman размером 14 пт., который соответствует курсивному и перечеркнутому тексту. Пример создания шрифта Код (C): // способ первый: просто создаем объект класса Font Font font1(L"Times New Roman", 14.0, FontStyleItalic|FontStyleUnderline); // способ второй: сперва объект класса FontFamily, FontFamily family(L"Times New Roman"); // а затем объект класса Font Font font2(&family, 14.0, FontStyleItalic|FontStyleUnderline); На рисунке показаны различные варианты гарнитур Arial, Times New Roman и Courier New в сочетании с различными стилями. FontStyleUnderlineFontStyleUnderlineFontStyleStrikeoutFontStyleRegular FontStyleBold FontStyleItalic FontStyleBoldItalich Варианты шрифтов с различными стилями В классе Font определено несколько методов, которые могут быть полезны при работе с объектами этого класса. Основные методы этого класса перечислены в таблице. Подробное описание методов класса Font можно найти в документации Platform SDK. Методы класса Font МетодОписаниеFont* Clone()Клонирует текущий объект класса Font. Возвращает указатель на созданный объект класса Font, который должен быть удален вызовом оператора delete после того как станет не нуженGetFamily(FontFamily *family)Возвращает имя гарнитуры шрифта. Результат сохраняется в объекте класса FontFamily, на который указывает параметр familyREAL GetHeight(REAL dpi); REAL GetHeight(Graphics *graphics);Возвращает значение междустрочного интервала шрифтаGetLastStatus()Возвращает значение, указывающее на причину возникновения ошибки (или ее отсутствие) в последнем вызове одного из методов класса FontGetLogFontA(Graphics *g, LOGFONTA *logfontA); GetLogFontW(Graphics *g, LOGFONTW *logfontW)Возвращает информацию о шрифте. Результат сохраняется в структуру LOGFONTA (LOGFONTW), на которую указывает параметр logfontA (logfontW)REAL GetSize()Возвращает размер шрифтаINT GetStyle()Возвращает стиль шрифтаUnit GetUnit()Возвращает единицы изменения, в которых задается размер шрифтаBOOL IsAvailable()Возвращает TRUE, если объект класса Font успешно создан, в противном случае – FALSEИзображенияДля работы с изображениями в GDI+ имеется класс Image и два его потомка Metafile и Bitmap. Все эти классы определены в заголовочном файле gdiplusheaders.h.Векторные изображенияДля работы с векторным изображением в GDI+ используется класс Metafile, который является потомком класса Image. Конструктор класса Metafile: Metafile(WCHAR *filename); Конструктор создает объект класса Metafile на основе метафайла (metafile). Параметр filename указывает на Unicode-строку, содержащую имя метафайла. В примере показано, как создать объект класса Metafile из метафайла Sample.emf. Код (C): // загрузка из файла Sample.emf Metafile mf(L"Sample.emf"); Кроме загрузки векторных изображений из файла, объект класса Metafile можно создавать с помощью других конструкторов: Metafile(HMETAFILE hWmf, WmfPlaceableFileHeader *wmfPlaceableFileHeader, BOOL deleteWmf = FALSE); Metafile(HENHMETAFILE hEmf, BOOL deleteEmf = FALSE); Metafile(WCHAR *filename, WmfPlaceableFileHeader *wmfPlaceableFileHeader); Metafile(IStream *stream); Metafile(HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, WCHAR *description = NULL); Metafile(HDC referenceHdc, RectF &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, WCHAR *description = NULL); Metafile(HDC referenceHdc, Rect &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, WCHAR *description = NULL); Metafile(WCHAR *fileName, HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, WCHAR *description = NULL); Metafile(WCHAR *fileName, HDC referenceHdc, RectF &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, WCHAR *description = NULL); Metafile(WCHAR *fileName, HDC referenceHdc, Rect &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, WCHAR *description = NULL); Metafile(IStream *stream, HDC referenceHdc, EmfType type = EmfTypeEmfPlusDual, WCHAR *description = NULL); Metafile(IStream *stream, HDC referenceHdc, RectF &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, WCHAR *description = NULL); Metafile(IStream *stream, HDC referenceHdc, Rect &frameRect, MetafileFrameUnit frameUnit = MetafileFrameUnitGdi, EmfType type = EmfTypeEmfPlusDual, WCHAR *description = NULL); Подробное описание этих конструкторов, а также всех методов класса Metafile можно найти в документации Platform SDK.
Растровые изображенияДля работы с растровым изображением в GDI+ используется класс Bitmap, который является потомком класса Image. Класс Bitmap имеет следующие конструкторы: Bitmap( WCHAR *filename, BOOL useIcm = FALSE); Bitmap(INT width, INT height, PixelFormat format = PixelFormat32bppARGB); Первый конструктор создает объект класса Bitmap на основе растрового файла. Параметр filename указывает на Unicode-строку, содержащую имя файла, а параметр useIcm указывает, применяется ли цветокоррекция согласно информации об управлении цветом в файле с изображением. Если параметр useIcm имеет значение FALSE, цветокоррекция не применяется. Второй конструктор создает объект класса Bitmap на основе параметров width и height, которые определяют, соответственно ширину и высоту растрового изображений. Параметр format задает формат кодирования цвета в новом растровом изображении. Этот параметр должен принимать одно следующих значений: PixelFormat1bppIndexed – индексированные цвета, которые кодируются 1 битом (палитра содержит 21=2 цвета); PixelFormat4bppIndexed – индексированные цвета, которые кодируются 4 битами (палитра содержит 24=16 цветов); PixelFormat8bppIndexed – индексированные цвета, которые кодируются 8 битами (палитра содержит 28=256 цветов); PixelFormat16bppGrayScale – оттенки серого, которые кодируются 16 битами (216=65536 оттенков серого); PixelFormat16bppRGB555 – цвет кодируется 5+5+5+1=16 битами: по 5 бит на красный, зеленый и синий компоненты модели RGB. Оставшийся бит не используется; PixelFormat16bppRGB565 – цвет кодируется 5+5+6=16 битами: по 5 бит на красный и синий, и по 6 бит на зеленый компоненты модели RGB; PixelFormat16bppARGB1555 – цвет кодируется 1+5+5+5=16 битами: по 5 бит на красный, зеленый и синий компоненты модели RGB. Оставшийся бит используется на альфа-фактор; PixelFormat24bppRGB – цвет кодируется 8+8+8=24 битами: по 8 бит на красный, зеленый и синий компоненты модели RGB; PixelFormat32bppARGB – цвет кодируется 8+8+8+8=32 битами: по 8 бит на красный, зеленый и синий компоненты модели RGB. Оставшиеся биты используются на альфа-фактор; PixelFormat32bppRGB – цвет кодируется 8+8+8+8=32 битами: по 8 бит на красный, зеленый и синий компоненты модели RGB. Оставшиеся биты не используются; PixelFormat48bppRGB – цвет кодируется 16+16+16+16=64 битами: по 16 бит на красный, зеленый и синий компоненты модели RGB. Оставшиеся биты не используются; PixelFormat64bppARGB – цвет кодируется 16+16+16+16=64 битами: по 16 бит на красный, зеленый и синий компоненты модели RGB. Оставшиеся биты используются на альфа-фактор. В следующем примере показано, как можно создать объект класса Bitmap из файла Sample.png. Код (C): // загрузка из файла Sample.png Bitmap bmp(L"Sample.png"); Кроме уже рассмотренных конструкторов класс Bitmap имеет еще и следующие конструкторы: Bitmap(IStream *stream, BOOL useEmbeddedColorManagement = FALSE); Bitmap(INT width, INT height, INT stride, PixelFormat format, BYTE *scan0); Bitmap(INT width, INT height, Graphics *target); Bitmap(IDirectDrawSurface7 *surface); Bitmap(BITMAPINFO *gdiBitmapInfo, VOID *gdiBitmapData); Bitmap(HBITMAP hbm, HPALETTE hpal); Bitmap(HICON hicon); Bitmap(HINSTANCE hInstance, WCHAR *bitmapName); Для получения и установки цвета определенного пиксела в растровом изображении класс предоставляет методы GetPixel и SetPixel: GetPixel(INT x, INT y, Color *color); SetPixel(INT x, INT y, Color &color); Метод GetPixel сохраняет текущее значение цвета пикселя в объект класса Color, на который указывает параметр color, а метод SetPixel задает новое значение цвета пикселя. Параметры x и y задают координаты указанного пикселя.Класс ImageКласс Image позволяет работать, как с векторным изображением, так и растровым. Класс Image имеет следующие конструкторы: Image(WCHAR *filename, BOOL useEmbeddedColorManagement = FALSE); Image(IStream *stream, BOOL useEmbeddedColorManagement = FALSE); Первый конструктор создает объект класса Image на основе файла (векторного или растрового). Параметр filename указывает на Unicode-строку, содержащую имя файла. Второй конструктор создает объект класса Image на основе интерфейса IStream COM-объекта, на который указывает параметр stream. Параметр useEmbeddedColorManagement указывает, применяется ли цветокоррекция согласно информации об управлении цветом в файле с изображением. Если параметр useEmbeddedColorManagement имеет значение FALSE, цветокоррекция не применяется. Рассмотрим некоторые методы класса Image, которые наследуют все потомки этого класса. Среди методов класса Image есть методы GetLastStatus и Clone. Метод Clone имеет следующий прототип: Image* Clone(); В случае успеха метод Clone возвращает указатель на объект класса Image, который должен быть удален вызовом оператора delete после того как станет не нужен. В случае ошибки – NULL. Определить тип изображения можно с помощью метода GetType класса Image. Этот метод имеет следующий прототип: ImageType GetType(); Метод возвращает одно из следующих значений перечисляемого типа ImageType: ImageTypehexImageTypeUnknown0неизвестный тип изображенияImageTypeBitmap1растровое изображениеImageTypeMetafile2векторное изображениеДля определения формата кодирования цвета в изображении в классе Image есть метод GetPixelFormat(); Метод возвращает одно из значений перечисляемого типа PixelFormat. Ширину и высоту изображения можно определить, соответственно, с помощью методов GetWidth и GetHeight класса Image: UINT GetWidth(); UINT GetHeight(); В классе Image есть два статических метода FromFile и FromStream, которые создают объект класса Image аналогично его конструкторам: static Image* FromFile(WCHAR *filename, BOOL useEmbeddedColorManagement = FALSE); static Image* FromStream(IStream *stream, BOOL useEmbeddedColorManagement = FALSE); В случае успеха методы FromFile и FromStream возвращают указатель на объект класса Image, который должен быть удален вызовом оператора delete после того как станет не нужен. В случае ошибки – NULL. Следующий небольшой пример демонстрирует загрузку растрового изображения из файла с помощью метода FromFile класса Image: Код (C): // загрузка из файла Sample.png Image *image = Image::FromFile(L"Sample.png"); if (NULL != image) { /* файл успешно загружен */ } // if Метод FromStream класса Image можно, например, использовать для загрузки изображений из ресурсов приложения Windows, как показано в примере из листинга. Функция загрузки изображения из ресурсов приложения Код (C): Image* ImageFromResource(HINSTANCE hInstance, LPCTSTR szResName, LPCTSTR szResType) { // поиск ресурса указанного типа HRSRC hRsrc = FindResource(hInstance, szResName, szResType); if (NULL == hRsrc) return NULL; // загрузка ресурса HGLOBAL hResource = LoadResource(hInstance, hRsrc); if (NULL == hResource) return NULL; // определяем размера ресурса DWORD cbImage = SizeofResource(hInstance, hRsrc); // выделение памяти для буфера HGLOBAL hData = GlobalAlloc(GMEM_FIXED, cbImage); if (NULL != hData) { // копируем изображение в буфер CopyMemory(GlobalLock(hData), LockResource(hResource), cbImage); GlobalUnlock(hData); UnlockResource(hResource); } // if FreeResource(hResource); // освобождение ресурса ////////////////// if (NULL != hData) { IStream *pStream = NULL; // создание COM-объекта HRESULT hr = CreateStreamOnHGlobal(hData, TRUE, &pStream); if (SUCCEEDED(hr) && NULL != pStream) { Image *image = Image::FromStream(pStream); // загрузка изображения pStream->Release(); return image; } // if GlobalFree(hData); // освобождение памяти } // if return NULL; } // ImageFromResource Класс Image предоставляет очень удобный механизм создания эскизов изображений, которые содержат уменьшенную копию изображения. Поскольку эскизы имеют очень малые размеры, они очень быстро отображаются. Для создания эскизов в классе Image определен метод GetThumbnailImage: Image* GetThumbnailImage(UINT thumbWidth, UINT thumbHeight, GetThumbnailImageAbort callback = NULL, VOID *callbackData = NULL); Первые два параметра, thumbWidth и thumbHeight, указывают требуемые размеры эскиза: соответственно, ширину и высоту. Параметры callback и callbackData могут использоваться, если нужна возможность прерывания процесса создания эскиза. Эти параметры могут быть установлены в NULL. В случае успеха метод GetThumbnailImage возвращает указатель на объект класса Image, который должен быть удален вызовом оператора delete после того как станет не нужен. В случае ошибки – NULL. Следующий пример демонстрирует создание эскиза размером 320×240 из файла растрового изображения: Код (C): // загрузка изображения 320x240 из файла Sample.png Image *image = Image(L"Sample.png").GetThumbnailImage(320, 240); if (NULL != image) { /* Изображение успешно загружено */ } // if
Вывод графики с помощью GDI+Все операции графического вывода в GDI+ выполняются с помощью методов класса Graphics, который определен в заголовочном файле gdiplusgraphics.h. Класс Graphics имеет следующие конструкторы: Graphics(HDC hdc); Graphics(HDC hdc, HANDLE hdevice); Graphics(HWND hwnd, BOOL icm = FALSE); Graphics(Image *image); Первый и второй конструкторы создают объект класса Graphics на основе дескриптора контекста устройства, на который указывает параметр hdc. Второй конструктор для создания объекта класса Graphics также использует дескриптор устройства отображения (параметр hdevice). Третий конструктор создает объект класса Graphics на основе дескриптора окна hwnd. Параметр icm определяет, будет ли применяться настройка цвета. Если параметр icm имеет значение FALSE, настройка цвета не применяется. Четвертый конструктор создает объект класса Graphics на основе объекта класса Image, на который указывает параметр image. В приведенном далее примере показана функция отображения, в которой на основе контекста устройства отображения создается объект класса Graphics. Пример функции отображения Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); } // Display ОчисткаРисование на экране компьютера отличается от рисования на бумаге, так как бумага изначально является белой и все, что нужно сделать – это нарисовать на ней изображение. Однако при рисовании на экране компьютера прежде необходимо удалить старое уже нарисованное изображение, хранящееся в памяти компьютера. В классе Graphics есть метод Clear, который очищает область отображения некоторым цветом фона. Метод Clear имеет следующий прототип: Clear(Color &color); Параметр color – объект класса Color, в котором представлен цвет фона, используемый для очистки области отображения.Рисование фигур и линийВ классе Graphics все методы рисования имеют две версии: методы с префиксом Draw для рисования линий, контуров и т.п. и c префиксом Fill для заливки замкнутых фигур. Методам с префиксом Draw в качестве параметра требуется указатель на объект класса Pen, методам с префиксом Fill передается указатель на объект класса, порожденного от класса Brush. Рисование линийДля рисования различных линий используется метод DrawLine класса Graphics. Метод имеет следующие прототипы: DrawLine(Pen *pen, INT x1, INT y1, INT x2, INT y2); DrawLine(Pen *pen, REAL x1, REAL y1, REAL x2, REAL y2); DrawLine(Pen *pen, Point &pt1, Point &pt2); DrawLine(Pen *pen, PointF &pt1, PointF &pt2); Параметр pt1 (pt2), а также параметры x1 (x2) и y1 (y2) определяют координаты первой (второй) точки рисуемой линии. В листинге представлен пример, в котором демонстрируется рисование двух линий пером черного цвета. Пример рисования линий Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем перо черного цвета Pen blackPen(Color::Black); // рисуем линию с координатами (100,100) и (200,80) g.DrawLine(&blackPen, 100, 100, 200, 80); Point pt1(120, 20); // точка (120,20) Point pt2(40, 60); // точка (40,60) // рисуем линию с координатами (120,20) и (40,60) g.DrawLine(&blackPen, pt1, pt2); } // Display Для рисования ломаных линий используется метод DrawLines класса Graphics. Этот метод имеет следующие прототипы: DrawLines(Pen *pen, Point *points, INT count); DrawLines(Pen *pen, PointF *points, INT count); Параметр points указывает на массив объектов класса Point (PointF), которые содержат координаты вершин рисуемой ломаной линии, параметр count задает количество элементов в массиве, на который указывает параметр points. В следующем примере демонстрируется рисование ломаной линии из четырех точек с помощью пера черного цвета. Пример рисования ломаной линии Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем перо черного цвета Pen blackPen(Color::Black); Point points[4] = { Point(100, 100), // точка (100,100) Point(200, 80), // точка (200,80) Point(120, 20), // точка (120,20) Point(40, 60) // точка (40,60) }; // рисуем ломаную линию g.DrawLines(&blackPen, points, 4); } // Display Рисование прямоугольниковДля рисования прямоугольников используется метод DrawRectangle класса Graphics, а для закрашивания прямоугольников используется метод FillRectangle этого класса. Методы DrawRectangle и FillRectangle имеет следующие прототипы: DrawRectangle(Pen *pen, INT x, INT y, INT width, INT height); DrawRectangle(Pen *pen, REAL x, REAL y, REAL width, REAL height); DrawRectangle(Pen *pen, Rect &rect); DrawRectangle(Pen *pen, RectF &rect); FillRectangle(Brush *brush, INT x, INT y, INT width, INT height); FillRectangle(Brush *brush, REAL x, REAL y, REAL width, REAL height); FillRectangle(Brush *brush, Rect &rect); FillRectangle(Brush *brush, RectF &rect); Параметр rect, а также параметры x, y, width и height, определяют расположение и размер прямоугольника. Как уже было сказано ранее, в классе Graphics методы закрашивания замкнутых фигур отделены от методов рисования контуров таких фигур. Поэтому если необходимо нарисовать закрашенный прямоугольник с контуром, то первым делом необходимо нарисовать закрашенный прямоугольник с помощью метода FillRectangle, а затем контур этого прямоугольника с помощью метода DrawRectangle. В листинге представлен пример демонстрирующий рисование двух прямоугольников, изображенных на рисунке. Пример рисования прямоугольников Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем перо зеленого цвета Pen pen(Color(0, 128, 0)); // создаем сплошную кисть SolidBrush solidBrush(Color::Yellow); // желтый цвет // рисуем закрашенный прямоугольник g.FillRectangle(&solidBrush, 100, 70, 120, 50); // рисуем контур прямоугольника g.DrawRectangle(&pen, 100, 70, 120, 50); Rect rect(10, 10, 100, 50); // рисуем закрашенный прямоугольник g.FillRectangle(&solidBrush, rect); // рисуем контур прямоугольника g.DrawRectangle(&pen, rect); } // Display Пример нарисованных прямоугольниковДля рисования и закрашивания сразу нескольких прямоугольников используются, соответственно, методы DrawRectangles и FillRectangles класса Graphics. Эти методы имеет следующие прототипы: DrawRectangles(Pen *pen, Rect *rects, INT count); DrawRectangles( Pen *pen, RectF *rects, INT count); FillRectangles(Brush *brush, Rect *rects, INT count); FillRectangles(Brush *brush, RectF *rects, INT count); Параметр rects указывает на массив объектов класса Rect (RectF), которые определяют расположение и размеры прямоугольников, а параметр count задает количество элементов в массиве, на который указывает параметр rects. В листинге представлен немного видоизменный пример. В этом примере для рисования сразу двух прямоугольников используются методы FillRectangles и DrawRectangles. Пример рисования сразу нескольких прямоугольников Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем перо зеленого цвета Pen pen(Color(0, 128, 0)); // создаем сплошную кисть SolidBrush solidBrush(Color::Yellow); // желтый цвет // массив прямоугольников Rect rects[2] = { Rect(100, 70, 120, 50), Rect(10, 10, 100, 50) }; // рисуем закрашенные прямоугольники g.FillRectangles(&solidBrush, rects, 2); // рисуем контуры прямоугольников g.DrawRectangles(&pen, rects, 2); } // Display Рисование многоугольников Для рисования и закрашивания полигонов (многоугольников) используются, соответственно, методы DrawPolygon и FillPolygon класса Graphics. Эти методы имеют следующие прототипы: DrawPolygon(Pen *pen, Point *points, INT count); DrawPolygon(Pen *pen, PointF *points, INT count); FillPolygon(Brush *brush, Point *points, INT count); FillPolygon(Brush *brush, PointF *points, INT count); FillPolygon(Brush *brush, Point *points, INT count, FillMode fillMode); FillPolygon(Brush *brush, PointF *points, INT count, FillMode fillMode); Параметр points указывает на массив объектов класса Point (PointF), которые содержат координаты вершин рисуемого полигона, а параметр count задает количество элементов в массиве, на который указывает параметр points. Параметр fillMode определяет, как будет производиться заполнение для внутренней части полигона. Этот параметр должен принимать одно из значений FillMode. При заполнении полигонов необходимо определить область, которая должна быть заполнена. По умолчанию используется режим заполнения с чередованием. Для того чтобы определить область заполнения в режиме заполнения с чередованием, строится луч из произвольной точки внутри полигона к некоторой точке, которая явно расположена вне полигона. Если луч пересекает нечетное число ребер полигона, выбранная точка расположена в заполняемой области. Четное число пересечений означает, что точка не находится в области заполнения. В режиме заполнения с поворотом, так же строится луч, а затем находятся пересечения этого луча с ребрами полигона. Каждому такому пересечению присваивается +1 или –1, в зависимости от того, как ребро пересекает луч – по часовой (слева направо) или против часовой стрелки (справа налево). Если сумма этих значений будет отлична от нуля, точка считается расположенной внутри области заполнения. Если же сумма равна нулю, точка находится вне области заполнения. На рисунке показана пятиконечная звезда, нарисованная с различными режимами заполнения. Примеры заполнения полигонов (с указанием вершин) FillModehexFillModeAlternate0режим заполнения с чередованием FillModeWinding1режим заполнения с поворотом В следующем примере демонстрируется рисование закрашенного треугольника с контуром черного цвета. Пример рисования треугольника Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем перо черного цвета Pen blackPen(Color::Black); // создаем штриховую кисть HatchBrush hatchBrush( HatchStyleDiagonalBrick, Color::Black, Color::LightGray); // вершины треугольника Point points[3] = { Point(100, 100), // точка (100,100) Point(200, 130), // точка (200,130) Point(110, 200) // точка (110,200) }; // рисуем закрашенный треугольник g.FillPolygon(&hatchBrush, points, 3); // рисуем контур треугольника g.DrawPolygon(&blackPen, points, 3); } // Display Рисование эллипсов, окружностей, дуг и секторовДля рисования и закрашивания эллипсов используются, соответственно, методы DrawEllipse и FillEllipse класса Graphics. Эти методы имеют следующие прототипы: DrawEllipse(Pen *pen, INT x, INT y, INT width, INT height); DrawEllipse(Pen *pen, REAL x, REAL y, REAL width, REAL height); DrawEllipse(Pen *pen, Rect &rect); DrawEllipse(Pen *pen, RectF &rect); FillEllipse(Brush *brush, INT x, INT y, INT width, INT height); FillEllipse(Brush *brush, REAL x, REAL y, REAL width, REAL height); FillEllipse(Brush *brush, Rect &rect); FillEllipse(Brush *brush, RectF &rect); Параметр rect, а также параметры x, y, width и height, определяют расположение и размер эллипса, как показано на рисунке. Расположение и размер эллипсаСледующий пример демонстрирует рисование закрашенного эллипса, круга и окружности, изображенных на рисунке. Пример рисования эллипса, круга и окружности Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем перо черного цвета Pen blackPen(Color::Black); // создаем две сплошные кисти SolidBrush solidBrush1(Color::Yellow); SolidBrush solidBrush2(Color::Aqua); // рисуем закрашенный эллипс g.FillEllipse(&solidBrush1, 10, 10, 160, 80); // рисуем контур эллипса g.DrawEllipse(&blackPen, 10, 10, 160, 80); Rect rect(180, 50, 70, 70); // рисуем круг g.FillEllipse(&solidBrush2, rect); // рисуем окружность g.DrawEllipse(&blackPen, rect); } // Display Как видно из приведенного примера, перечисленные методы класса Graphics для рисования и закрашивания эллипсов также можно использовать, соответственно, для рисования окружности и круга. Пример эллипса, круга и окружностиРисование дуги похоже на рисование эллипса. Для рисования дуг использует метод DrawArc класса Graphics. Этот метод имеет следующие прототипы: DrawArc(Pen *pen, INT x, INT y, INT width, INT height, REAL startAngle, REAL sweepAngle); DrawArc(Pen *pen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle); DrawArc(Pen *pen, Rect &rect, REAL startAngle, REAL sweepAngle); DrawArc(Pen *pen, RectF &rect, REAL startAngle, REAL sweepAngle); Параметр rect, а также параметры x, y, width и height, определяют расположение и размер прямоугольника, в который вписывается рисуемая дуга. Параметр startAngle задает угол (в градусах) между осью х и начальной точкой дуги. Параметр sweepAngle задает угол (в градусах) между начальной и конечной точкой дуги. На рисунке показано, как рисуется дуга по заданным параметрам. Рисование дугиВ листинге представлен пример демонстрирующий рисование двух дуг пером красного цвета и толщиной 2. Пример рисования дуг Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона (x,y)heightwidthx // создаем перо красного цвета и толщиной 2 Pen redPen(Color::Red, 2.f); // рисуем дугу от -10 градусов до 130 градусов g.DrawArc(&redPen, 100, 70, 120, 50, -10.f, 140.f); Rect rect(10, 10, 100, 100); // рисуем дугу от 90 градусов до 300 градусов g.DrawArc(&redPen, rect, 90.f, 210.f); } // Display Для того чтобы нарисовать фигуру «сектор» в классе Graphics есть метод DrawPie и есть метод FillPie, который закрашивает эту фигуру. Пример рисования секторовМетоды DrawPie и FillPie класса Graphics имеют следующие прототипы: DrawPie(Pen *pen, INT x, INT y, INT width, INT height, REAL startAngle, REAL sweepAngle); DrawPie(Pen *pen, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle); DrawPie(Pen *pen, Rect &rect, REAL startAngle, REAL sweepAngle); DrawPie(Pen *pen, RectF &rect, REAL startAngle, REAL sweepAngle); FillPie(Brush *brush, INT x, INT y, INT width, INT height, REAL startAngle, REAL sweepAngle); FillPie(Brush *brush, REAL x, REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle); FillPie(Brush *brush, Rect &rect, REAL startAngle, REAL sweepAngle); FillPie(Brush *brush, RectF &rect, REAL startAngle, REAL sweepAngle); Параметр rect, а также параметры x, y, width и height, определяют расположение и размер прямоугольника, в который вписывается сектор. Параметр startAngle задает угол (в градусах) между осью х и начальной точкой сектора. Параметр sweepAngle задает угол (в градусах) между начальной и конечной точкой сектора. В следующем примере продемонстрировано рисование диаграммы, состоящей из четырех секторов. Пример рисования секторной диаграммы Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем перо черного цвета Pen blackPen(Color::Black); // создаем сплошную кисть SolidBrush solidBrush(Color::Red); // красный цвет // рисуем первый сектор g.FillPie(&solidBrush, -5, 30, 300, 300, 110.f, 30.f); // рисуем контур первого сектора g.DrawPie(&blackPen, -5, 30, 300, 300, 110.f, 30.f); // рисуем еще три сектора... Color colors[3] = { Color(255, 192, 0), Color(192, 0, 0), Color(0, 176, 80)}; float sweepAngle[3] = { 100.f, 115.f, 115.f}; float angle = 140.f; // начальный угол Rect rect(10, 10, 300, 300); for (int i = 0; i < 3; ++i) { solidBrush.SetColor(colors[I])[/I]; // изменяем цвет кисти // рисуем сектор g.FillPie(&solidBrush, rect, angle, sweepAngle); // рисуем контур сектора g.DrawPie(&blackPen, rect, angle, sweepAngle); angle += sweepAngle; // увеличиваем угол } // for } // Display
Рисование сплайновВ дополнение к рисованию линий и дуг GDI+ поддерживает рисование сплайнов (splines), которые являются очень удобным инструментом для рисования кривых в компьютерной графике. В классе Graphics есть несколько методов, которые позволяют рисовать кубические сплайны (cubic splines) и сплайны Безье (splines Bezier). Для рисования кубических сплайнов необходимо использовать методы DrawCurve и DrawClosedCurve. Метод DrawCurve рисует обычный кубический сплайн, а метод DrawClosedCurve – замкнутый кубический сплайн. Эти методы имеют следующие прототипы: DrawCurve(Pen *pen, Point *points, INT count); DrawCurve(Pen *pen, PointF *points, INT count); DrawCurve(Pen *pen, Point *points, INT count, REAL tension); DrawCurve(Pen *pen, PointF *points, INT count, REAL tension); DrawCurve(Pen *pen, Point *points, INT count, INT offset, INT numberOfSegments, REAL tension = 0.5f); DrawCurve(Pen *pen, PointF *points, INT count, INT offset, INT numberOfSegments, REAL tension = 0.5f); DrawClosedCurve(Pen *pen, Point *points, INT count); DrawClosedCurve(Pen *pen, PointF *points, INT count); DrawClosedCurve(Pen *pen, Point *points, INT count, REAL tension); DrawClosedCurve(Pen *pen, PointF *points, INT count, REAL tension); Параметр points указывает на массив объектов класса Point (PointF), определяющий набор точек, через которые проходит сплайн, а параметр count задает количество элементов в массиве, на который указывает параметр points. Параметр tension задает напряжение сплайна. По умолчанию напряжение сплайна равно 0.5. Параметр offset указывает индекс (начиная с 0) точки в массиве, с которой начинается рисование сплайна, а параметр numberOfSegments указывает их количество. Важно отметить, что все «невидимые» точки из массива, на который указывает параметр points, все равно будут использованы для расчетов. Кубический сплайн, заданный четырьмя точкамиВ классе Graphics есть метод FillClosedCurve для закрашивания замкнутых кубических сплайнов. FillClosedCurve(Brush *brush, Point *points, INT count); FillClosedCurve(Brush *brush, PointF *points, INT count); FillClosedCurve(Brush *brush, Point *points, INT count, FillMode fillMode, REAL tension = 0.5f); FillClosedCurve(Brush *brush, PointF *points, INT count, FillMode fillMode, REAL tension = 0.5f); Параметры points, count и tension аналогичны одноименным параметрам метода DrawClosedCurve. Параметр fillMode определяет, как будет производиться заполнение области, образуемой замкнутым сплайном. Этот параметр должен принимать одно из значений перечисляемого типа FillMode. В листинге показан пример рисования замкнутого кубического сплайна, проходящего через семь точек. Пример рисования замкнутого кубического сплайна Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем перо зеленого цвета Pen pen(Color::Green); // создаем сплошную кисть SolidBrush solidBrush(Color::Yellow); // желтый цвет // массив точек, через кот. Проходит сплайн Point points[7] = { Point(50, 50), // точка (50,50) Point(100, 25), // точка (100,25) Point(200, 5), // точка (200,5) Point(250, 50), // точка (250,50) Point(300, 100), // точка (300,100) Point(350, 200), // точка (350,200) Point(250, 250) // точка (250,250) }; // закрашиваем замкнутый сплайн g.FillClosedCurve(&solidBrush, points, 7); // рисуем замкнутый сплайн g.DrawClosedCurve(&pen, points, 7); } // Display Если нужно нарисовать сплайн Безье следует использовать метод DrawBezier или DrawBeziers. Метод DrawBezier рисует обычный сплайн Безье, а метод DrawBeziers – последовательность соединенных сплайнов Безье. Метод DrawBezier имеет следующие прототипы: DrawBezier(Pen *pen, INT x1, INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4); DrawBezier(Pen *pen, REAL x1, REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4); DrawBezier(Pen *pen, Point &pt1, Point &pt2, Point &pt3, Point &pt4); DrawBezier(Pen *pen, PointF &pt1, PointF &pt2, PointF &pt3, PointF &pt4); Параметры pt1, pt2, pt3 и pt4, а также параметры x1, y1, x2, y2, x3, y3, x4 и y4 задают координаты точек, определяющих положение и кривизну сплайна Безье. В следующем примере демонстрируется рисование двух различных сплайнов Безье с помощью метода DrawBezier класса Graphics. Пример рисования сплайнов Безье Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем два пера Pen pen1(Color::Green); // зеленый цвет Pen pen2(Color::Red); // красный цвет // рисуем сплайн Безье с начальной точкой (100,100) // и конечной точкой (500,100) g.DrawBezier(&pen1, 100, 100, 200, 10, 350, 50, 500, 100); Point pt1(100, 200); // точка (100,200) Point pt2(200, 150); // точка (200,150) Point pt3(400, 110); // точка (400,110) Point pt4(500, 200); // точка (500,200) // рисуем сплайн Безье с начальной точкой (100,200) и конечной точкой (500,200) g.DrawBezier(&pen2, pt1, pt2, pt3, pt4); } // Display Метод DrawBeziers имеет следующие прототипы: DrawBeziers(Pen *pen, Point *points, INT count); DrawBeziers(Pen *pen, PointF *points, INT count); Параметр points указывает на массив объектов класса Point (PointF), определяющий набор контрольных точек сплайнов Безье, а параметр count задает количество элементов в массиве, на который указывает параметр points. При этом точки в массиве должны располагаться так, чтобы последняя точка одного сплайна Безье, являлась начальной точкой следующего сплайна Безье. Следующий пример демонстрирует рисование кривой, состоящей сразу из двух сплайнов Безье. Пример рисования кривой, состоящей из сплайнов Безье Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем перо синего цвета Pen pen(Color::Blue); Point points[7] = { Point(100, 100), // начальная точка 1-го сплайна Point(200, 50), Point(400, 10), Point(500, 100), // конечная точка 1-го сплайна Point(600, 200), Point(500, 400), Point(800, 350)}; // конечная точка 2-го сплайна // рисуем кривую из сплайнов Безье g.DrawBeziers(&pen, points, 7); } // Display
Отображение текстаДля отображения текста в классе Graphics определен метод DrawString, который имеет следующие прототипы: DrawString(WCHAR *string, INT length, Font *font, PointF &origin, Brush *brush); DrawString( WCHAR *string, INT length, Font *font, PointF &origin, StringFormat *stringFormat, Brush *brush); DrawString(WCHAR *string, INT length, Font *font, RectF &layoutRect, StringFormat *stringFormat, Brush *brush); Первый параметр, string, указывает на Unicode-строку, содержащую отображаемый текст, а второй параметр, length, задает длину этой строки. Параметр length может принимать значение -1, если строка завершается нуль-символом. Третий параметр, font, указывает на объект класса Font, который представляет шрифт для отображения текста. Параметр origin – объект класса PointF, который содержит координаты точки расположения текста, а параметр layoutRect определяет прямоугольник, в котором будет отображаться текст. Параметр stringFormat указывает на объект класса StringFormat, который управляет форматированием текста. Последний параметр, brush, указывает на объект класса Brush, который представляет кисть для отображения текста. В следующем примере показано, как с помощью метода DrawString класса Graphics вывести текст «Привет мир!» в виде одной горизонтальной строки. Пример простого вывода текста Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем шрифт Times New Roman Font font(L"Times New Roman", 20.f, FontStyleBold); // создаем сплошную кисть SolidBrush blackBrush(Color::Black); // черный цвет // выводим текст, который начинается в точке (10,10) g.DrawString(L"Привет мир!", -1, &font, PointF(10.f, 10.f), &blackBrush); } // Display При необходимости форматированного вывода текста необходимо использовать объект класса StringFormat, который определен в заголовочном файле gdiplusstringformat.h. Класс StringFormat имеет следующие конструкторы: StringFormat(INT formatFlags = 0, LANGID language = LANG_NEUTRAL); StringFormat(StringFormat *format); Первый конструктор создает объект класса StringFormat на основе флагов форматирования, которые задает параметр formatFlags, и идентификатора язык, который задается параметром language. Параметр formatFlags может принимать одно (или комбинацию) из перечисляемого типа StringFormatFlags, а значение параметра language можно задавать с помощью макроса MAKELANGID, следующим образом: Код (C): LANGID language = MAKELANGID(LANG_RUSSIAN, SUBLANG_RUSSIAN_RUSSIA); Второй конструктор класса StringFormat создает копию объекта этого же класса, на который указывает параметр format. В листинге представлен пример форматированного вывода текста, результат которого показан на рисунке. Пример форматированного вывода текста Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем шрифт Times New Roman Font font(L"Times New Roman", 20.f, FontStyleBold); // создаем сплошную кисть SolidBrush blackBrush(Color::Black); // черный цвет // создаем перо красного цвета Pen pen(Color::Red); // создаем объект класса StringFormat для форматированного вывода текста StringFormat sf; // ограничивающий прямоугольник RectF rect(10.f, 10.f, 300.f, 80.f); // выводим текст g.DrawString(L"Съешь ещё этих мягких французских булок, да выпей чаю.", -1, &font, rect, &sf, &blackBrush); // рисуем прямоугольник (только для наглядности) g.DrawRectangle(&pen, rect); } // Display Форматированный вывод текстаПрежде чем перейти к изучению методов класса StringFormat, следует отметить, что в этом классе (как и во многих других классах GDI+) есть методы GetLastStatus и Clone. Метод Clone имеет следующий прототип: StringFormat* Clone(); В случае успеха метод Clone возвращает указатель на объект класса StringFormat, который должен быть удален вызовом оператора delete после того как станет не нужен. В случае ошибки – NULL.Флаги форматированияФлаги форматирования можно задавать в качестве одного из параметров конструктора класса StringFormat при создании объекта это класса. Можно также задать флаги форматирования с помощью метода SetFormatFlags: SetFormatFlags(INT flags); Параметр flags может принимать одно (или комбинацию) из следующих значений перечисляемого типа StringFormatFlags: ▪ StringFormatFlagsDirectionRightToLeft – текст будет отображаться справа на лево; ▪ StringFormatFlagsDirectionVertical – текст будет отображаться вертикально; ▪ StringFormatFlagsNoFitBlackBox – текст будет отображаться без дополнительного пустого пространства между внутренним контуром прямоугольной зоны форматирования и прямоугольником, ограничивающего отображаемый текст; ▪ StringFormatFlagsDisplayFormatControl – разрешает отображение управляющих символов, например, таких как метка слева направо (LefttoRight Embedding, LRE); ▪ StringFormatFlagsNoFontFallback – запрещается обращение к альтернативным шрифтам при отображении символов, не поддерживаемых в указанном шрифте. Такие символы, как правило, будут отображаться с помощью пустого квадрата; ▪ StringFormatFlagsMeasureTrailingSpaces – запрещается исключать пробелы в конце каждой строки при определении размеров ограничивающего прямоугольника; ▪ StringFormatFlagsNoWrap – запрещается автоматический перенос текста на новую строку при форматировании в прямоугольнике; ▪ StringFormatFlagsLineLimit – при форматировании в прямоугольнике будут отображаться только целые строки; ▪ StringFormatFlagsNoClip – разрешается отображать текст, выходящий за пределы прямоугольной зоны форматирования. Определить установленные флаги форматирования можно с помощью метода GetFormatFlags класса StringFormat: INT GetFormatFlags(); На рисунке показаны результаты использования некоторых флагов форматирования текста.Выравнивание текстаПри отображении текста может потребоваться выровнять этот текст. Чтобы выровнять текст необходимо в метод DrawString класса Graphics передать объект класса StringFormat, в котором задать параметры выравнивания текста с помощью методов SetAlignment и SetLineAlignment. Метод SetAlignment задает выравнивание текста по горизонтали, а метод SetLineAlignment – по вертикали. Эти методы имеют следующие прототипы: Status SetAlignment(StringAlignment align); Status SetLineAlignment(StringAlignment align);Применение флагов форматирования текста hex StringFormatFlagsNoFitBlackBox4 StringFormatFlagsNoWrap1000 StringFormatFlagsLineLimit2000 StringFormatFlagsNoClip4000Параметр align задает выравнивание текста относительно точки расположения текста или относительно границ прямоугольника, в котором будет отображаться текст. Этот параметр должен принимать одно из следующих значений перечисляемого типа StringAlignment: ▪ StringAlignmentNear – выравнивание по ближнему краю; ▪ StringAlignmentCenter – выравнивание по центру; ▪ StringAlignmentFar – выравнивание по дальнему краю. На рисунках показаны различные варианты выравнивания текста относительно точки и границ прямоугольника. Варианты выравнивания текста относительно точки StringAlignmentNearStringAlignmentCenterStringAlignmentFarStringAlignmentNear StringAlignmentCenter StringAlignmentFar Варианты выравнивания текста относительно границ прямоугольника StringAlignmentNearStringAlignmentCenterStringAlignmentFarStringAlignmentNear StringAlignmentCenter StringAlignmentFar Для горизонтального выравнивания ближним краем будет левый, а дальним краем – правый. Однако, при отображений текста справа на лево ближним краем будет правый, а дальним краем – левый. Для вертикального выравнивания ближним краем будет верхний, а дальним краем – нижний. Определить текущее горизонтальное и вертикальное выравнивание можно, соответственно, с помощью методов GetAlignment и GetLineAlignment класса StringFormat. Эти методы имеют следующие прототипы: StringAlignment GetAlignment(); StringAlignment GetLineAlignment();Вывод текста с табуляциейДля отображаемого текста можно устанавливать позиции табуляции. Для этого в классе StringFormat есть метод SetTabStops. Этот метод имеет следующий прототип: SetTabStops(REAL firstTabOffset, INT count, REAL *tabStops); Первый параметр, firstTabOffset, задает первое смещение позиции табуляции. Второй параметр, count, определяет количество элементов в массиве табуляции, на который указывает третий параметр – tabStops. Параметр tabStops указывает на массив значений типа float, которые задают смешение позиции табуляции. Каждое смещение позиции табуляции, кроме первого, выполняется относительно предыдущей позиции. Первое смещение позиции табуляции выполняется относительно первоначальной позиции смещения. Например, если начальная позиция смещения равна 5 и первое смещение позиции табуляции равно 40, первое смещение позиции табуляции расположено в позиции 45. Если начальная позиция смещения равна нулю, первое смещение позиции табуляции определяется относительно позиции 0, начала строки. Следующий пример демонстрирует форматированный вывода текста с табуляцией. Результат показан на рисунке. Пример задания позиций табуляции для выводимого текста Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем шрифт Arial Font font(L"Arial", 11.f, FontStyleRegular); // создаем сплошную кисть SolidBrush blackBrush(Color::Black); // черный цвет // создаем перо серого цвета Pen pen(Color::Gray); // создаем объект класса StringFormat для форматированного вывода текста StringFormat sf; // задаем выравнивание по левому краю sf.SetAlignment(StringAlignmentNear); // задаем выравнивание по верхнему краю sf.SetLineAlignment(StringAlignmentNear); float tabs[4] = {50.f, 100.f, 100.f, 100.f}; // устанавливаем позиции табуляции sf.SetTabStops(0.0, 4, tabs); // ограничивающий прямоугольник RectF rect(10.0, 10.0, 350.0, 90.0); wchar_t text[] = L"№\tФамилия\tИмя\tОтчество\n\ 1.\tАндреев\tАлександр\tДмитриевич\n\ 2.\tВасильев\tЮрий\tГеннадиевич\n\ 3.\tГромова\tИнна\tВикторовна\n\ 4.\tМартынов\tПавел\tБорисович"; // выводим текст g.DrawString(text, -1, &font, rect, &sf, &blackBrush); // рисуем прямоугольник g.DrawRectangle(&pen, rect); } // Display Форматированный вывод текста с табуляциейОпределить количество установленных позиций табуляции можно с помощью метода GetTabStopCount, а получить значения этих позиций можно с помощью метода GetTabStops. Прототипы этих методов записываются следующим образом: INT GetTabStopCount(); GetTabStops(INT count, REAL *firstTabOffset, REAL *tabStops); Первый параметр, count, указывает количество элементов в массиве, на который указывает параметр tabStops. Второй параметр, firstTabOffset, указывает на переменную типа float, в которую будет записано значение первого смещения позиции табуляции. Третий параметр, tabStops, указывает на массив типа float, в который будут сохранены значения установленных позиций табуляции.Вывод «горячих» клавишСимвол «&» (амперсанд) имеет специальное назначение в тексте, используемом в меню, кнопках и других элементах управления – он означает, что следующий за ним знак должен быть подчеркнут и будет использоваться для быстрого доступа к элементу управления. В классе StringFormat есть метод SetHotkeyPrefix, который определяет, как при отображении интерпретируются амперсанды. Метод SetHotkeyPrefix имеет следующий прототип: SetHotkeyPrefix(HotkeyPrefix hotkeyPrefix); Параметр hotkeyPrefix определяет, как будут интерпретироваться амперсанды. Этот параметр должен принимать одно из следующих значений перечисляемого типа HotkeyPrefix: HotkeyPrefixhexHotkeyPrefixNone0префикс «горячих» клавиш отсутствует HotkeyPrefixShow1отображать префикс «горячих» клавиш HotkeyPrefixHide2не отображать префикс «горячих» клавиш По умолчанию префикс «горячих» клавиш отсутствует. Определить, как интерпретируются амперсанды можно с помощью метода GetHotkeyPrefix класса StringFormat. Этот метод имеет следующий прототип: HotkeyPrefix GetHotkeyPrefix(); В таблице показаны варианты отображения текста «&Файл» с различными режимами интерпретации амперсанда.Обрезание текстаВ классе StringFormat есть метод SetTrimming, который определяет, как должны отображаться символы, если текст не полностью помещается в ограничивающий прямоугольник. Метод SetTrimming имеет следующий прототип: SetTrimming(StringTrimming trimming); Параметр trimming определяет формат обрезания текста. Этот параметр должен принимать одно из следующих значений перечисляемого типа StringTrimming: StringTrimminghexStringTrimmingNone0текст не обрезается StringTrimmingCharacter1текст обрезается по ближайшему символу StringTrimmingWord2текст обрезается по ближайшему слову StringTrimmingEllipsisCharacter3текст обрезается по ближайшему символу и в конце обрезанной строки вставляется троеточие StringTrimmingEllipsisWord4текст обрезается по ближайшему слову и в конце обрезанной строки вставляется троеточие StringTrimmingEllipsisPath5если текст не помещается в ограничивающий прямоугольник из него удаляется центральная часть и заменяется троеточием По умолчанию текст обрезается по ближайшему символу. Определить текущий формат обрезания текста можно с помощью метода GetTrimming класса StringFormat. Этот метод имеет следующий формат: StringTrimming GetTrimming(); На рисунке показаны различные варианты обрезания символов, если текст не помещается в ограничивающий прямоугольник. Определить ограничивающий прямоугольник для отображения текста с заданным шрифтом можно с помощью метода MeasureString класса Graphics. Этот метод имеет следующие прототипы: MeasureString(WCHAR *string, INT length, Font *font, PointF &origin, RectF *boundingBox); MeasureString(WCHAR *string, INT length, Font *font, RectF &layoutRect, RectF *boundingBox); MeasureString(WCHAR *string, INT length, Font *font, PointF &origin, StringFormat *stringFormat, RectF *boundingBox); MeasureString(WCHAR *string, INT length, Font *font, RectF &layoutRect, StringFormat *stringFormat, RectF *boundingBox, INT *codepointsFitted = 0, INT *linesFilled = 0); MeasureString(WCHAR *string, INT length, Font *font, RectF &layoutRect, StringFormat *stringFormat, SizeF *size, INT *codepointsFitted = 0, INT *linesFilled = 0); Параметры string, length, font, origin, layoutRect и stringFormat аналогичны одноименным параметрам метода DrawString класса Graphics. Параметр boundingBox указывает на объект класса RectF, в который будут сохранены расположение и размер ограничивающего прямоугольника. Параметр size указывает на объект класса SizeF, в который будет сохранен размер ограничивающего прямоугольника. Параметр codepointsFitted указывает на переменную типа INT, в которую записывается количество символов в тексте, размещенном в ограничивающем прямоугольнике. Параметр linesFilled указывает на переменную типа INT, в которую записывается количество строк в тексте, размещенном в ограничивающем прямоугольнике. Предопределенные форматы В классе StringFormat есть два статических метода GenericDefault и GenericTypographic, которые возвращают указатель на предопределенный объект класса StringFormat: StringFormat* GenericDefault(); StringFormat* GenericTypographic(); Метод GenericDefault возвращает указатель на объект класса StringFormat, который представляет следующими параметрами форматирования: ▪ флагов форматирования нет; ▪ горизонтальное выравнивание по ближнему краю; ▪ вертикальное выравнивание по ближнему краю; ▪ позиции табуляции не установлены; ▪ префикс «горячих» клавиш отсутствует; ▪ текст обрезается по ближайшему символу. Метод GenericTypographic возвращает указатель на объект класса StringFormat, который обладает следующими параметрами форматирования: ▪ заданы флаги форматирования StringFormatFlagsLineLimit, StringFormatFlagsNoClip и StringFormatFlagsNoFitBlackBox; ▪ горизонтальное выравнивание по ближнему краю; ▪ вертикальное выравнивание по ближнему краю; ▪ позиции табуляции не установлены; ▪ префикс «горячих» клавиш отсутствует; ▪ текст не обрезается. Важно отметить, что не нужно удалять объект, указатель на который возвращают методы метода GenericDefault и GenericTypographic, после того, как необходимость в нем отпадет. В следующем примере демонстрируется использование предопределенных объектов класса StringFormat для вывода текста. Пример форматированного вывода текста Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем шрифт Arial Font font(L"Arial", 14.f, FontStyleRegular); // создаем сплошную кисть SolidBrush blackBrush(Color::Black); // черный цвет // выводим текст, который начинается в точке (10,50) g.DrawString(L"Привет мир!", -1, &font, PointF(10.f, 50.f), StringFormat::GenericDefault(), &blackBrush); // создаем объект класса StringFormat на основе предопределенного объекта StringFormat sf(StringFormat::GenericTypographic()); // выводим текст, который начинается в точке (10,10) g.DrawString(L"Привет мир!", -1, &font, PointF(10.f, 10.f), &sf, &blackBrush); } // Display
Вывод графических изображенийДля отображения содержимого растрового или векторного изображения в классе Graphics есть метод DrawImage, который имеет огромное количество перегруженных вариантов. Наиболее употребительные варианты имеют следующие прототипы: DrawImage(Image *image, INT x, INT y); DrawImage(Image *image, REAL x, REAL y); DrawImage(Image *image, INT x, INT y, INT width, INT height); DrawImage(Image *image, REAL x, REAL y, REAL width, REAL height); DrawImage(Image *image, Point &point); DrawImage(Image *image, PointF &point); DrawImage(Image *image, Rect &rect); DrawImage(Image *image, RectF &rect); Первый параметр, image, указывает на объект класса Image или класса, производного от класса Image. Этот объект представляет изображение, содержимое которого необходимо отобразить. Параметр point, а также параметры x и y, определяют расположение выводимого изображения. Параметр rect, а также параметры x, y, width и height, определяют расположение и размер выводимого изображения. В следующем примере демонстрируется использование метода DrawImage класса Graphics для вывода изображения. Пример вывода изображения Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // загружаем изображение Sample.png Image image(L"Sample.png"); // вывод загруженного изображения g.DrawImage(&image, 10, 10); } // Display Важно отметить, что чтение изображения из файла может происходить достаточно медленно. Поэтому разумнее всего было бы вынести загрузку выводимых изображений из функции отображения, а не так как это сделано в приведенном примере.Пропорциональное масштабирование изображенияКак показано на рисунке в процессе вывода изображения может возникнуть искажение из-за того, что при масштабировании форматное соотношение прямоугольника, который определяет расположение и размер выводимого изображения, не совпадает с форматным соотношением этого изображения.Искажение изображения Форматное соотношение – это отношение ширины и высоты, которое находится по следующей формуле: [math]ratio=\frac{\displaystyle width}{\displaystyle height}[/math](1.2)где [math]ratio[/math] – форматное соотношение, а [math]width[/math] и [math]height[/math] – ширина и высота. Для того чтобы при масштабировании изображения не происходило его искажения необходимо найти для него такие ширину и высоту, для которых бы сохранялось их изначальное отношение и которые бы полностью влезали бы в заданный прямоугольник. Рассмотрим случай, когда форматное соотношение изображения больше чем форматное соотношение заданного прямоугольника. В этом случае ширина и высота изображения находятся по формуле: [math]Image.width=Rectangle.width[/math], [math]Image.height=\frac{\displaystyle 1}{\displaystyle ratio}\cdot Rectangle.width[/math] (1.3)где [math]Image.width[/math] и [math]Image.height[/math] – новые ширина и высота изображения; [math]Rectangle.width[/math] – ширина прямоугольника; [math]ratio[/math] – форматное соотношение изображения. В другом случае, когда изображение имеет меньшее форматное соотношение, чем у заданного прямоугольника, новые ширина и высота изображения находятся по другой формуле: [math]Image.width=ratio\cdot Rectangle.heigth[/math], [math]Image.heigth=Rectangle.heigth[/math] (1.4)где [math]Image.width[/math] и [math]Image.heigth[/math] – новые ширина и высота изображения; [math]Rectangle.heigth[/math] – высота прямоугольника; [math]ratio[/math] – форматное соотношение изображения. В листинге представлена функция, определяющей ширину и высоту изображения, для которых сохраняется их изначальное отношение, и которые будут полностью влезать в прямоугольник. Функция определяющая положение и размер изображения Код (C): MeasureImage(Image *image, RectF &layout, StringAlignment align, RectF *result) { if (NULL == image || layout.IsEmptyArea() || NULL == result) { return InvalidParameter; } // if RectF rect; // форматное соотношение изображения float fRatio = (float)image->GetWidth() / (float)image->GetHeight(); if (fRatio > (layout.Width / layout.Height)) { rect.Width = layout.Width; rect.Height = layout.Width / fRatio; rect.X = layout.GetLeft(); // выравнивание по вертикали switch (align) { case StringAlignmentNear: // по ближнему краю rect.Y = layout.GetTop(); break; case StringAlignmentCenter: // по центру rect.Y = layout.Y + (layout.Heightrect.Height)/2.f; break; case StringAlignmentFar: // по дальнему краю rect.Y = layout.GetBottom() - rect.Height; break; } // switch } // if else { rect.Width = fRatio * layout.Height; rect.Height = layout.Height; rect.Y = layout.GetTop(); // выравнивание по горизонтали switch (align) { case StringAlignmentNear: // по ближнему краю rect.X = layout.GetLeft(); break; case StringAlignmentCenter: // по центру rect.X = layout.X + (layout.Width-rect.Width)/2.f; break; case StringAlignmentFar: // по дальнему краю rect.X = layout.GetRight() - rect.Width; break; } // switch } // else rect.GetBounds(result); // возвращаем результат return Ok; } // MeasureImage Функция, приведенная в этом примере, определяет также положение изображения в задаваемом прямоугольнике в зависимости от параметров выравнивания, т.к. в результате пропорционального масштабирования может оставаться неиспользованное пространство. На рисунке представлены различные варианты выравнивания изображения для двух рассмотренных случаев соотношения изображения и прямоугольника.КонтурыКонтур (path) представляет собой последовательность графических примитивов (линий, прямоугольников, кривых, текста и т. п.), которые обрабатываются и отображаются как один объект. Контур можно разделить на отдельные фигуры, которые являются либо незамкнутыми, либо замкнутыми. В GDI+ создание контуров выполняется с помощью методов класса GraphicsPath, определенного в заголовочном файле gdipluspath.h. hexStringAlignmentNear StringAlignmentCenter StringAlignmentFar Варианты выравнивания изображения относительно границ прямоугольника Класс GraphicsPath имеет следующие конструкторы: GraphicsPath(FillMode fillMode = FillModeAlternate); GraphicsPath(Point *points, BYTE *types, INT count, FillMode fillMode = FillModeAlternate); GraphicsPath(PointF *points, BYTE *types, INT count, FillMode fillMode = FillModeAlternate); Первый конструктор создает объект класса GraphicsPath на основе режима заполнения, который задает параметр fillMode. Второй и третий конструкторы создают объект класса Graphics-Path на основе массива точек, на который указывает параметр points, а параметр count задает количество элементов в этом массиве. Пара-метр types указывает на массив типа INT, элементы которого определяют тип точек, указанных в массиве. Параметр fillMode задает режим заполнения. Тип точек определяется одним (или комбинацией) из значений перечисляемого типа PathPointType. Для построения контура в классе GraphicsPath есть множество методов схожих по названию с методами класса Graphics, предназначенных для рисования таких же фигур. В таблице перечислены схожие методы класса GraphicsPath и класса Graphics. Таблица Сопоставление методов классов Graphics и GraphicsPath Метод класса GraphicsPathАналог в классе GraphicsОписаниеAddArcDrawArcДобавляет в контур дугуAddBezierDrawBezierДобавляет в контур сплайн БезьеAddBeziersDrawBeziersДобавляет в контур кривую, состоящую из сплайнов БезьеAddClosedCurveDrawClosedCurveДобавляет в контур замкнутый кубический сплайнAddCurveDrawCurveДобавляет в контур кубический сплайнAddEllipseDrawEllipseДобавляет в контур эллипсAddLineDrawLineДобавляет в контур линиюAddLinesDrawLinesДобавляет в контур ломаную линиюAddPathDrawPathДобавляет в контур другой контурAddPieDrawPieДобавляет в контур секторAddPolygonDrawPolygonДобавляет в контур полигонAddRectangleDrawRectangleДобавляет в контур прямоугольникAddRectanglesDrawRectanglesДобавляет в контур несколько прямоугольниковAddStringDrawStringДобавляет в контур строкуКаждый метод класса GraphicsPath, имеет несколько прототипов схожих с аналогичными методами класса Graphics (подробнее см. в документации Platform SDK). Следует отметить, что в классе GraphicsPath, как и в других классах GDI+, есть методы GetLastStatus и Clone. Метод Clone имеет следующий прототип: GraphicsPath* Clone(); В случае успеха метод Clone возвращает указатель на объект класса GraphicsPath, который должен быть удален вызовом оператора delete после того как станет не нужен. В случае ошибки – NULL.Рисование контуровДля рисования контуров используется метод DrawPath класса Graphics, а для закрашивания контуров используется метод FillPath этого класса. Эти методы имеют следующие прототипы: DrawPath(Pen *pen, GraphicsPath *path); FillPath(Brush *brush, GraphicsPath *path); Параметр path указывает на объект класса GraphicsPath, который представляет отображаемый контур. В листинге представлен пример демонстрирующий рисование контура, изображенного на рисунке. Пример рисования контура Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона // создаем перо черного цвета Pen blackPen(Color::Black); // создаем сплошную кисть SolidBrush solidBrush(Color::Yellow); // желтый цвет Rect rect(70, 70, 100, 100); // создаем объект класса GraphicsPath для построения контура GraphicsPath path; // добавляем в контур прямоугольник path.AddRectangle(rect); rect.Inflate(50, 50); // добавляем в контур круг path.AddEllipse(rect); // рисуем закрашенный контур g.FillPath(&solidBrush, &path); // рисуем контур g.DrawPath(&blackPen, &path); } // Display Пример контура
Замкнутые контурыДля создания замкнутого контура в классе GraphicsPath есть методы StartFigure, CloseFigure и CloseAllFigures. Методы StartFigure, CloseFigure и CloseAllFigures имеют следующие прототипы: StartFigure(); CloseFigure(); CloseAllFigures(); Метод StartFigure открывает новую фигуру в контуре, не замыкая при этом текущую фигуру. Все последующие примитивы, добавляемые к контуру, добавляются к этой новой фигуре. Метод CloseFigure замыкает текущую фигуру и открывает новую фигуру. Если текущая фигура содержит последовательность соединенных линий, метод CloseFigure замыкает ее путем соединения начальной и конечной точек этих линий. Метод CloseAllFigures замыкает все незамкнутые фигуры в контуре и открывает новую фигуру. В листинге приведен пример создания замкнутого контура прямоугольника с закругленными углами. Прямоугольник с закругленными краямиФункция, создающая замкнутый контур Код (C): GraphicsPath* CreateRoundRectangle(Rect &rect, int Radius) { // создаем объект класса GraphicsPath для построения контура GraphicsPath path; // добавляем в контур линию path.AddLine(rect.GetLeft()+Radius, rect.GetTop(), rect.GetRight()-Radius, rect.GetTop()); // добавляем в контур дугу path.AddArc(rect.GetRight()-Radius, rect.GetTop(), Radius, Radius, 270.f, 90.f); // добавляем в контур линию path.AddLine(rect.GetRight(), rect.GetTop()+Radius, rect.GetRight(), rect.GetBottom()-Radius); // добавляем в контур дугу path.AddArc(rect.GetRight()-Radius, rect.GetBottom()-Radius, Radius, Radius, 0.f, 90.f); // добавляем в контур линию path.AddLine(rect.GetRight()-Radius, rect.GetBottom(), rect.GetLeft()+Radius, rect.GetBottom()); // добавляем в контур дугу path.AddArc(rect.GetLeft(), rect.GetBottom() - Radius, Radius, Radius, 90.f, 90.f); // добавляем в контур линию path.AddLine(rect.GetLeft(), rect.GetBottom()-Radius, rect.GetLeft(), rect.GetTop()+Radius); // добавляем в контур дугу path.AddArc(rect.GetLeft(), rect.GetTop(), Radius, Radius, 180.f, 90.f); // добавляем закрываем фигуру в контуре path.CloseFigure(); return path.Clone(); // клонируем объект класса GraphicsPath } // CreateRoundRectangle Функцию из приведенного примера можно использовать следующим образом: Код (C): Rect rect(30, 30, 200, 100); // создаем контур GraphicsPath *path = CreateRoundRectangle(rect, 20); if (NULL != path) { /* контур успешно создан */ } // if Когда объект класса GraphicsPath, на который указывает переменная path из приведенного примера, более станет не нужен, он должен быть удален с помощью оператора delete.
Поддержка полупрозрачностиВ GDI+ можно создавать, как непрозрачные перья и кисти, так и полупрозрачные перья и кисти. При рисовании полупрозрачным пером или кистью выполняется альфа-смешивание, которое представляет собой смешение фонового цвета и цвета пера или кисти. Каждый из трех компонентов (красный, зеленый, синий) фонового цвета смешивается с соответствующим компонентом цвета пера или кисти, согласно следующей формуле: [math]Color=SrcColor\cdot\frac{\displaystyle alpha}{\displaystyle 255}+BlndColor\cdot\frac{\displaystyle 255−alpha}{\displaystyle 255}[/math] (1.5)где [math]Color[/math]– отображаем цвет; [math]BlndColor[/math] – фоновый цвет; [math]SrcColor[/math] – цвет пера или кисти, а [math]alpha[/math] – альфа-фактор цвета пера или кисти. Для создания непрозрачного пера или кисти следует установить для цвета альфа-фактор равным 255. Чтобы создать полупрозрачное перо или кисть, нужно задать альфа-фактору любое значение из диапазона от 1 до 254. В листинге приведен пример альфа-смешивания при рисовании изображения, показанного на рисунке. Пример альфа-смешиванияПример рисования полупрозрачными графическими объектами Код (C): void Display(HDC hdc) { // создаем объект класса Graphics для рисования Graphics g(hdc); g.SetSmoothingMode(SmoothingModeHighQuality); // выполняем очистку перед рисованием g.Clear(Color::White); // белый цвет фона Rect rect(10, 20, 200, 100); // создаем эллиптический контур GraphicsPath path; path.AddEllipse(rect); // создаем полупрозрачное перо Pen pen1(Color(128, 0, 128, 0), 20.f); // создаем непрозрачное перо Pen pen2(Color(255, 0, 128, 0), 20.f); // создаем штриховую кисть с полупрозрачным фоном HatchBrush hatchBrush(HatchStyleCross, Color::Red, Color(100, 255, 0, 0)); // создаем непрозрачную кисть градиента контура PathGradientBrush pthGrBrush(&path); // задаем цвет центра контура pthGrBrush.SetCenterColor(Color::Aqua); // задаем цвет границы контура... Color color = Color::Blue; int n = 1; pthGrBrush.SetSurroundColors(&color, &n); // рисуем непрозрачный контур g.FillPath(&pthGrBrush, &path); // рисуем полупрозрачный прямоугольник g.FillRectangle(&hatchBrush, 30, 10, 50, 120); // рисуем полупрозрачную линию g.DrawLine(&pen1, 150, 10, 150, 130); // рисуем непрозрачную линию g.DrawLine(&pen2, 180, 10, 180, 130); } // Display Настройка графического выводаКласс Graphics содержит также методы, которые способны влиять на качество отображаемых объектов.Сглаживание линий и текстаПри отображении различных линий, кривых и текста могут появляться контурные неровности. Для устранения контурных неровностей применяется сглаживание (smoothing). Обычно отображение более качественных сглаженных линии, кривых и текста требует больших затрат вычислительных ресурсов, чем менее качественных. В классе Graphics есть метод SetSmoothingMode, который задает режим сглаживания линий. Этот метод имеет следующий прототип: SetSmoothingMode(SmoothingMode smoothingMode); Параметр smoothingMode задает режим сглаживания линий. Этот параметр может принимать одно из следующих значений перечисляемого типа SmoothingMode: SmoothingModehexSmoothingModeDefaultотображение без сглаживания линийSmoothingModeHighSpeedотображение без сглаживания линийSmoothingModeHighQualityотображение со сглаживанием линийSmoothingModeNoneотображение без сглаживания линий SmoothingModeAntiAliasотображение со сглаживанием линий На рисунке изображен сплайн в двух вариантах без применения и с применением сглаживания линий. По умолчанию сглаживание линий не применяется. Определить текущий режим сглаживания линий можно с помощью метода GetSmoothingMode класса Graphics: SmoothingMode GetSmoothingMode(); Режим сглаживания линий никак не влияет на качество отображения текста. Чтобы задать режим сглаживания текста, используется метод SetTextRenderingHint класса Graphics. Этот метод имеет следующий прототип: SetTextRenderingHint(TextRenderingHint newMode); Параметр newMode задает режим сглаживания текста. Этот параметр может принимать одно из следующих значений перечисляемого типа TextRenderingHint: TextRenderingHinthexTextRenderingHintSystemDefault0отображение текста с параметрами сглаживания, указанными для операционной системы TextRenderingHintSingleBitPerPixelGridFit1отображение текста без сглаживания и с хинтованием TextRenderingHintSingleBitPerPixel2отображение текста без сглаживания и без хинтования TextRenderingHintAntiAliasGridFit3отображение текста со сглаживанием и хинтованием TextRenderingHintAntiAlias4отображение текста со сглаживанием и без хинтования TextRenderingHintClearTypeGridFit5отображение текста со сглаживанием текста по технологии ClearType, применяемой для ЖК-мониторов В таблице приведены различные варианты сглаживания текста. Определить текущий режим сглаживания текста можно с помощью метода GetTextRenderingHint класса Graphics. Прототип метода: TextRenderingHint GetTextRenderingHint();
Качество растровых изображенийПри масштабировании растрового изображения происходит потеря качества этого изображения. В GDI+ реализованы различные алгоритмы интерполяции, позволяющие добиться улучшения качества масштабируемого изображения. В классе Graphics определен метод SetInterpolationMode, который позволяет указать, какой алгоритм интерполяции будет использоваться при выводе растровых изображений с изменением его размеров. Метод SetInterpolationMode имеет следующий прототип: SetInterpolationMode( InterpolationMode interpolationMode); Параметр interpolationMode задает режим интерполяции изображения. Этот параметр должен принимать одно из следующих значений перечисляемого типа InterpolationMode: Изображение для масштабированияНа рисунке представлены различные варианты интерполяции увеличенного в три раза изображения, показанного на рисунке. InterpolationModehexInterpolationModeDefault0интерполяция по умолчаниюInterpolationModeLowQuality1низкокачественная интерполяция InterpolationModeHighQuality2высококачественная интерполяция InterpolationModeBilinear3билинейная интерполяция (не используется для сжатия изображения до размера менее 50% от его исходного размера) InterpolationModeBicubic4бикубическая интерполяция (не используется для сжатия изображения до размера менее 25% от его исходного размера) InterpolationModeNearestNeighbor5интерполяция по ближайшим соседним значениямInterpolationModeHighQualityBilinear6высококачественная билинейная интерполяция InterpolationModeHighQualityBicubic7высококачественная бикубическая интерполяция Определить текущий режим интерполяции изображения можно с помощью метода GetInterpolationMode класса Graphics. Этот метод имеет следующий формат: InterpolationMode GetInterpolationMode()Логические единицыКласс Graphics позволяет абстрагироваться от физических характеристик устройства, позволяя указать, в какой логической системе координат будет происходить графический вывод. Для установки логической системы координат в классе Graphics имеется метод SetPageUnit. Этот метод имеет следующий прототип: SetPageUnit(Unit unit); Параметр unit указывает логические единицы устройства отображения. Этот параметр должен принимать одно из следующих перечисляемого типа Unit: UnithexUnitWorld0мировые (не физические) единицыUnitDisplay1дисплейные единицы (пиксели)UnitPixel2пикселиUnitPoint31 единица равна 1/72 дюймаUnitInch4дюймыUnitDocument51 единица равна 1/300 дюймаUnitMillimeter6миллиметрыОпределить, какие используются логические единицы можно с помощью метода GetPageUnit класса Graphics: Unit GetPageUnit(); Кроме того, можно задать коэффициент масштабирования логических единиц. Для этого в классе Graphics существует метод SetPageScale. Этот метод имеет следующий прототип: SetPageScale(REAL scale); Параметр scale определяет коэффициент масштабирования. В следующем небольшом примере продемонстрированно, как задать в качестве логической единицы 1 см. Код (C): Graphics g(hdc); // выбираем миллиметры в качестве логических единиц g.SetPageUnit(UnitMillimeter); // устанавливаем масштабирование логических единиц g.SetPageScale(10.f); Определить текущий коэффициент масштабирования логических единиц можно с помощью метода GetPageScale класса Graphics: REAL GetPageScale();
Использование GDI+ в приложениях Win64Библиотека GDI+ полностью реализована в DLL-библиотеке GdiPlus.dll, для получения доступа к которой следует подключить к программе библиотеку импорта GdiPlus.lib и заголовочный файл gdiplus.h. Библиотека GDI+ прибывает в собственном пространстве имен Gdiplus. Чтобы открыть прямой доступ к функциям и классам, определенным внутри GDI+, необходимо использовать директиву: using namespace Gdiplus;Инициализация и завершение работы GDI+Перед использованием классов и функций GDI+ эту библиотеку инициализируют с помощью функции GdiplusStartup, которая должна быть первой из функций GDI+, вызываемых в программе. Функция GdiplusStartup имеет прототип: GdiplusStartup(ULONG_PTR *token, GdiplusStartupInput *input, GdiplusStartupOutput *output); Первый параметр, token, указывает на переменную типа ULONG_PTR, в которую сохраняется маркер, необходимый для завершения работы с GDI+. Второй параметр, input, указывает на структуру GdiplusStartupInput, поля которой управляют различными аспектами использования библиотеки GDI+. Третий параметр, output, указывает на структуру GdiplusStartupOutput. Это поле может быть установлено в NULL. После выполнения функция GdiplusStartup возвращает одно из значений перечисляемого типа Status, определенного в заголовочном файле GdiPlusTypes.h. В случае успешного выполнения эта функция возвращается значение Ok. Описание структуры GdiplusStartupInput: Код (C): struct GdiplusStartupInput { UINT32 GdiplusVersion; // версия DebugEventProc DebugEventCallback; // функция отладки BOOL SuppressBackgroundThread; // флаг, разрешающий // отложенную инициализацию BOOL SuppressExternalCodecs; // флаг, разрешающий // использование внешних // кодеков изображений }; Первое поле, GdiplusVersion, задает версию GDI+. Это поле принимает значение 1, которое соответствует версии 1.0. Второе поле, DebugEventCallback, указывает на функцию, которая будет вызываться при возникновении ошибки в различных функциях GDI+. Поле может быть установлено в NULL. Третье поле, SuppressBackgroundThread, определяет, необходимо ли создавать фоновый поток (например, когда начинаются манипуляции с изображениями) для окончательной инициализации GDI+. При этом инициализируется только часть библиотеки GDI+ (такой механизм отложенной инициализации довольно распространен в библиотеках Microsoft). Если поле SuppressBackgroundThread принимает значение TRUE, то функция GdiplusStartup возвращает (в параметре output) указатель на специальные функции, которые должны быть вызваны в том же потоке, что и функция GdiplusStartup. Четвертое поле, SuppressExternalCodecs, определяет, необходимо ли использовать внешние кодеки изображений. Так как GDI+ версии 1.0 не поддерживает внешние кодеки изображений, поэтому значения этого поля игнорируются. Конструктор структуры GdiplusStartupInput по умолчанию выполняет инициализацию ее полей, достаточную для большинства программ. По умолчанию поле GdiplusVersion равно 1, поле DebugEventCallback = NULL, поля SuppressBackgroundThread и SuppressExternalCodecs равны FALSE. Функция, на которую указывает поле DebugEventCallback, может называться как угодно, но должна имеет следующую сигнатуру: Код (C): void WINAPI DebugEventProc(DebugEventLevel level, CHAR *message); Первый параметр, level, определяет уровень сообщения отладки и может принимать одно из следующих значений: ▪ DebugEventLevelFatal – критическая ошибка; ▪ DebugEventLevelWarning – предупреждение. Второй параметр, message, указывает на строку, в которой содержится текст сообщения отладки. Структура GdiplusStartupOutput имеет следующее описание: Код (C): struct GdiplusStartupOutput { NotificationHookProc NotificationHook; NotificationUnhookProc NotificationUnhook; }; Поля этой структуры, NotificationHook и NotificationUnhook, указывают на функции, которые нужно вызвать, соответственно, до и после цикла обработки оконных сообщений в приложении Win64. Функция, на которую указывает поле NotificationHook, имеет следующую сигнатуру: WINAPI NotificationHookProc(ULONG_PTR *token); Параметр token указывает на переменную типа ULONG_PTR, в которую будет сохранен маркер, необходимый для вызова функции, на которую указывает поле NotificationUnhook. Функция, на которую указывает поле NotificationUnhook, имеет следующую сигнатуру: void WINAPI NotificationUnhookProc(ULONG_PTR token); Когда больше нет необходимости в использовании функций и классов GDI+, следует вызвать функцию GdiplusShutdown: void GdiplusShutdown(ULONG_PTR token); В качестве параметра необходимо передать маркер, который вернула функция GdiplusStartup через параметр token. Следующий пример демонстрирует фрагмент программного кода инициализации и завершения работы с GDI+. Пример инициализации и завершения работы с GDI+ Код (C): ULONG_PTR gdiplusToken; GdiplusStartupInput gdiplusStartupInput; GdiplusStartupOutput gdiplusStartupOutput; // указываем функцию отладки gdiplusStartupInput.DebugEventCallback = GdiplusDebugProc; // разрешаем отложенную инициализацию gdiplusStartupInput.SuppressBackgroundThread = TRUE; // инициализация GDI+ Status stRet = GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, &gdiplusStartupOutput); if (Ok == stRet) { ULONG_PTR token; gdiplusStartupOutput.NotificationHook(&token); /* цикл обработки оконных сообщений */ gdiplusStartupOutput.NotificationUnhook(token); GdiplusShutdown(gdiplusToken); // завершение работы GDI+ } // if В примере используется отложенная инициализация GDI+ при дальнейшей отладке программы используется функция обратного вызова GdiplusDebugProc, которая может быть такой: Код (C): void WINAPI GdiplusDebugProc(DebugEventLevel level, CHAR *message) { switch (level) { case DebugEventLevelFatal: MessageBoxA(NULL, message, "Критическая ошибка", MB_OK | MB_ICONERROR | MB_TASKMODAL); break; case DebugEventLevelWarning: MessageBoxA(NULL, message, "Предупреждение", MB_OK | MB_ICONWARNING | MB_TASKMODAL); break; } // switch } // GdiplusDebugProc В большинстве программ инициализация GDI+ выполняется проще. Следующий пример демонстрирует фрагмент программного кода более простой инициализации и завершения работы GDI+: Пример простой инициализации и завершения работы с GDI+ Код (C): ULONG_PTR gdiplusToken; GdiplusStartupInput gdiplusStartupInput; // инициализация GDI+ Status stRet = GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); if (Ok == stRet) { /* цикл обработки оконных сообщений */ GdiplusShutdown(gdiplusToken); // завершение работы GDI+ } // if
Вместо эпилогаСтатья "Введение в GDI+" написана вчерне. Впереди перевод на masm64, добавление иллюстраций, удаление ошибок