Расширение графических возможностей
Visual FoxPro

Библиотека GDIPLUS.DLL, поставляемая в составе Windows XP, а так же с Visual FoxPro восьмой и последующих версий, содержит большое количество функций, позволяющих осуществить эффективную поддержку графики в Ваших приложениях. К сожалению, группа сопровождения VFP из Microsoft не предоставляет доступ к этим возможностям разработчикам приложений.

Описанные ниже классы GdiPlus и GdiPlusDraw позволяют использовать функциональность GDI+ в приложениях, создаваемых на Visual FoxPro. Классы написаны на Visual FoxPro и находятся в файле GdiPlus.prg.

Классы могут быть использованы в  VFP8 и следующих версиях. Для правильной идентификации кодеков графических форматов в системе должен быть установлен Internet Explorer V5 и выше.
При работе  в Win98/WinME требуется скопировать библиотеку GdiPlus.DLL в папку Windows\System.

Класс GdiPlus

Класс предназначен для работы с растровыми изображениями. Для включения класса в приложение используется команда

SET PROCEDURE TO GdiPlus.prg ADDITIVE

Для работы с графикой необходимо создать объект на основе этого класса:

oGP = CREATEOBJECT('GdiPlus')

Класс реализует следующие функции:

Класс GdiPlus предоставляет следующие методы:

Все методы (за исключением ImageWidth, ImageHeight и GetErrorMessage) при успешном выполнении возвращают нуль. В случае ошибки возвращается положительное значение; текст ошибки можно получить при помощи метода GetErrorMessage, например:

oGP = CREATEOBJECT('GdiPlus')
IF oGP.LoarFromFile('MyPicture.jpg') > 0
    = MESSAGEBOX(oGP.GetErrorMessage())
ENDIF

Ниже приведено описание методов класса GdiPlus.

Метод LoadFromFile

Cоздаёт образ изображения на основе файла формата BMP, JPEG, GIF, TIFF и PNG.
Вызов:

    object.LoadFromFile(FileName)

Метод CopyFormToImage

Создаёт образ изображения из рабочей области формы VFP (без заголовка формы и рамки)
Вызов:

    object.CopyFormToImage(thisform)

Аргументы:

    thisform  - ссылка на форму VFP.

Метод CreateCanvas

Создаёт холст для рисования. Рисование на холсте выполняется при помощи методов объекта, созданного на основе класса GdiPlusDraw.
Цвет создаваемого холста - чёрный. Палитра - 24К цветов. Используя методы из GdiPlusDraw, можно залить холст любым цветом или текстурой на основе растрового файла.
Вызов:

    object.CreateCanvas(width, height)

Аргументы:

    width    - ширина холста
    height   - высота холста

Методы ImageWidth и ImageHeight

Возвращают соответственно значения для ширины b высоты изображения (в пикселях). Методы не имеют аргументов.
Вызов:

    object.ImageWidth()
    object.ImageHeight()

Метод ResizeImage

Изменяет размеры изображения. Изображение может быть увеличено, уменьшено, растянуто или сжато.
Вызов:

     object.ResizeImage(width, height, Mode)

Аргументы:

    width   - новое значение ширины изображения
    height 
- новое значение высоты изображения
    Mode   
- режим интерполяции (необязательный)

Примечания:

Если высота и ширина изображения не указаны или равны нулю, изменение размеров не выполняется.
Если высота изображения не указана (пропущена) или равна нулю, а ширина отлична от нуля, то в качестве высоты используется высота исходного изображения. То же справедливо и для ширины.
Допускается указывать режим интерполяции. Возможны следующие режимы:

По умолчанию используется InterpolationModeDefault.

Метод RotateImage

Поворачивает изображение на угол, кратный 90 градусам.
Вызов:

    object.RotateImage(Corner)

Аргумент Corner может принимать одно из следующих значений: 90, 180 и 270.

Метод CropRect

Вырезает прямоугольный фрагмент изображения и замещает им исходное изображение.
Вызов:

    object.CropRect(x, y, width, height)

Аргументы:

    x, y - координаты левого верхнего угла вырезаемого прямоугольника
    width, height
- ширина и высота вырезаемого прямоугольника

Примечания:

Если аргументы x, y  пропущены, то они полагаются равными нулю.
Аргументы width  и height  так же могут быть пропущены. Метод производит необходимые вычисления для корректировки аргументов. Например, пусть образ изображения имеет размеры 1000 (ширина) на 800 (высота) пикселей. Следующий код

    oGP.CropRect(100,,,400)

вырежет из исходного изображения прямоугольную область со следующими координатами:

левый верхний угол: x = 100, y = 0
правый нижний угол: x = 1000, y = 400

Вырезанная область будет иметь размеры 900 (ширина) на 400 (высота) пикселей.

 Метод СopyToClipBoard

Копирует изображение в буфер обмена Windows. Метод не имеет аргументов.
Вызов:

    object.CopyToClipBoard()

Метод DrawImageToForm

Рисует изображение в указанной прямоугольной области формы.
Вызов:

    object.DrawImageToForm(hWnd, x, y, width, height)

Аргументы:

    hWnd  - свойство hWnd формы (дескриптор окна)
    x, y
 
- координаты левого верхнего угла области рисования
    width, height
- ширина и высота области рисования

Все аргументы являются обязательными.

Примечания:

Нарисованное на форме изображение можно стереть, вызвав метод Cls формы. При перемещении над нарисованным изображением окон изображение затирается. Избежать этого можно, включив вызов метода DrawImageToForm в метод Pain формы. Это так же решит проблему стирания изображения при изменении мышью размеров формы.
Если под областью рисования находятся управляющие элементы (Controls), то при выводе изображения они будут "скрыты". Чтобы сделать их видимыми, достаточно в цикле установить их свойство Visible в .T.

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

Метод CopyToFile

Копирует изображение в файл (форматы BMP, JPEG, GIF) с возможностью изменения размеров и (для JPEG) качества. Исходное изображение остаётся неизменным.

Вызов:

    object.CopyToFile(FileName, Quality, Width, Height, Mode)

Аргументы:

    FileName - имя файла. Если тип не указан, то по умолчанию используется JPG.
    Quality
-
(необязательный) - качество изображения для формата JPEG. Для остальных форматов игнорируется. Допустимые значения - от 0 (минимальное качество, максимальное сжатие) до 100 (максимальное качество).
    Width, Height
- (необязательные) - новые значения ширины и высоты изображения. Если не указаны или равны нулю, то изменение размеров не выполняется.
    Mode
- (необязательный) - режим интерполяции при изменении размеров изображения.

Метод CopyRectToFile

Копирует в файл прямоугольный фрагмент изображения. Исходное изображение остаётся неизменным.
Вызов:

    object.CopyRectToFile(FileName, x, y, width, height)

Аргументы:

    FileName - имя файла. Если тип не указан, то по умолчанию используется JPG.
    x, y -
координаты левого верхнего угла вырезаемого прямоугольника
    width, height
- ширина и высота вырезаемого прямоугольника

Обработка аргументов x, y, width, height  аналогична описанной в методе CropRect.

Метод GetErrorMessage

Возвращает строку с сообщением об ошибке.
Ниже приведён список ошибок, определяемый классом GdiPlus.

1. Невозможно создать образ изображения
2. Недопустимый тип аргумента
3. Недопустимый тип файла
4. Файл не найден
5. Недопустимое число аргументов
6. Нет дескриптора изображения
7. Не указано имя файла для копирования
8. Недопустимый тип параметра качества изображения для JPEG
9. Недопустимое значение аргумента
10. Недопустимое значение параметра качества изображения для JPEG
11. Недопустимое имя файла
12. Недопустимый тип значения угла поворота изображения
13. Недопустимое значение угла поворота изображения
14. Ошибка при изменении размеров рисунка
15. Ошибка при
повороте изображения
16. Ошибка при выделении памяти
17. Ошибка при создании файла
18. Фрагмент выходит за область рисунка
19. Не назначен принтер по умолчанию
20. Ошибка при вырезании прямоугольного фрагмента из исходного изображения
21. Ошибка при рисовании изображения на форме
22. Ошибка записи в буфер обмена Windows
23. Нет доступа к буферу обмена
24. Недопустимый размер холста (минимум 16 пикселей ширина и 16 пикселей высота)
 

Класс GdiPlusDraw

Класс предназначен для рисования графических примитивов и текста на области изображения.
Объект на основе этого класса может быть создан только в том случае, если существует объект GdiPlus, и должен быть уничтожен до уничтожения объекта GdiPlus. Если такая ситуация всё же возникнет, то будет выведено сообщение "Объект GdiPlus уничтожен до возникновения события Destroy объекта GdiPlusDraw" и, возможно, произойдёт утечка памяти.

Для создания объекта на базе класса GdiPlusDraw используется команда:

oGPDraw = CREATEOBJECT('GdiPlusDraw', oGP)

где oGP - ссылка на объект, созданный на базе класса GdiPlus.

В процессе работы объекта GdiPlusDraw GDI+ создаёт собственные объекты:

Класс отслеживает время жизни этих объектов. При создании нового объекта предшествующий уничтожается.
Объект Font использует для рисования объект SolidBrush (текст рисуется определённым цветом) или Texture. Если при создании объекта Font кисть не существует, то возвращается ошибка и объект не создаётся.

При изменении параметров изображения в объекте GdiPlus (например, повороте) Вы должны создать объект GdiPlusDraw заново, иначе попытки рисования могут привести к непредсказуемым результатам, вплоть до исключения (фатальной ошибки VFP).

Класс GdiPlusDraw предоставляет следующие методы:

Все методы (за исключением GetErrorMessage) при успешном выполнении возвращают нуль. В случае ошибки возвращается положительное значение; текст ошибки можно получить при помощи метода GetErrorMessage, например:

oGPDraw = CREATEOBJECT('GdiPlusDraw', oGP)
.............
IF oGPDraw.DrawText("Привет, Мир!", 100, 220) > 0
    = MESSAGEBOX(oGPDraw.GetErrorMessage())
ENDIF

Ниже приведено описание методов класса GdiPlusDraw.

Метод CreateBrush

Создаёт кисть SolidBrush.
Вызов:

    object.CreateBrush(Color, Alpha)

Аргументы:

    Color - цвет кисти. Например, может быть определён как результат выполнения функции RGB().
    Alpha
- прозрачность. Допустимые значения: от 0 (непрозрачная) до 255 (полностью прозрачная).

Примечание:

В каждый момент может существовать только одна кисть. При создании новой кисти ранее существующая уничтожается.

Метод CreateTextureBrush

Создаёт кисть Texture.
Вызов:

    object.CreateTextureBrush(FileName)

Аргумент:

    FileName - имя растрового файла форматов BMP, JPEG, GIF, TIFF или PNG.

Примечание:

В каждый момент может существовать только одна кисть. При создании новой кисти ранее существующая уничтожается.

Метод DeleteBrush

Уничтожает существующую кисть. Метод не имеет аргументов.
Вызов:

    object.DeleteBrush()
 

Метод CreatePen

Создаёт карандаш.
Вызов:

    object.CreatePen(width, style, color, Alpha)

Аргументы:

    width - толщина линии (в пикселях)
    style
- стиль линии (0 - сплошная; 1 - пунктирная; 2 - штрих-пунктирная и т.д.)
    color
- цвет линии. Например, может быть определён как результат выполнения функции RGB().
      Alpha - прозрачность. Допустимые значения: от 0 (непрозрачный) до 255 (полностью прозрачный).

Примечание:

В каждый момент может существовать только один карандаш. При создании нового карандаша ранее существующий уничтожается.

Метод SetStylePen

Устанавливает стиль линии. Перед вызовом метода карандаш должен существовать.
Вызов:

    object.SetStylePen(style)

Аргумент:

    style - стиль линии (0 - сплошная; 1 - пунктирная; 2 - штрих-пунктирная и т.д.)
 

Метод SetWidthPen

Устанавливает толщину линии. Перед вызовом метода карандаш должен существовать.
Вызов:

    object.SetWidthPen(width)

Аргумент:

    width - толщина линии (в пикселях)

Метод SetColorPen

Устанавливает цвет линии. Перед вызовом метода карандаш должен существовать.
Вызов:

    object.SetColorPen(color, Alpha)

Аргументы:

    Color - цвет линии. Например, может быть определён как результат выполнения функции RGB().
    Alpha
-
прозрачность. Допустимые значения: от 0 (непрозрачная) до 255 (полностью прозрачная).

Метод DeletePen

Удаляет карандаш. Метод не имеет аргументов.
Вызов:

    object.DeletePen()
 

Метод CreateFont

Создаёт объект шрифта. Символы шрифта могут быть нарисованы как кистью типа SolidBrush (одноцветной), так и текстурированной кистью.
Вызов:

    object.CreateFont(FontName, FontSize, FontStyle)

Аргументы:

    FontName - наименование шрифта, например, "Times New Roman"
    FontSize
-
высота шрифта в пикселях
    FontStyle
- стиль шрифта (0-нормальный; 1 - полужирный; 2 - курсив; 3 - полужирный курсив)

Метод DeleteFont

Уничтожает объект шрифта. Метод не имеет аргументов.
Вызов:

    object.DeleteFont()
 

Метод BackGround

Заливает область изображения указанным цветом или текстурой (рисунком из растрового файла формата BMP, JPEG, GIF, TIFF или PNG)
Вызов:

    object.BackGround(Value)

Аргумент:

    Value - цвет заливки (RGB) или имя растрового файла

Метод DrawLine

Рисует линию. Перед вызовом метода карандаш должен существовать.
Вызов:

    object.DrawLine(x1, y1, x2, y2)

Аргументы:

    x1, y1 - координаты начальной точки линии (в пикселях)
    x2, y2
- координаты конечной точки линии (в пикселях)

Метод DrawRect

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

    object.DrawRect(x, y, width, height)

Аргументы:

    x, y - координаты левого верхнего угла прямоугольника (в пикселях)
    width, height - ширина и высота прямоугольника (в пикселях)

Метод DrawFillRect

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

    object.DrawFillRect(x, y, width, height)

Аргументы:

    x, y - координаты левого верхнего угла прямоугольника (в пикселях)
    width, height
- ширина и высота прямоугольника (в пикселях)

Метод DrawEllipse

Рисует эллипс (окружность). Для рисования используется карандаш, который должен быть создан перед вызовом метода.
Вызов:

    object.DrawEllipse(x, y, width, height)

Аргументы:

    x, y - координаты центра эллипса (окружности)  (в пикселях)
    width, height - диагонали эллипса  (в пикселях). При их равенстве рисуется окружность.

Метод DrawFillEllipse

Рисует залитый эллипс (окружность). Для рисования используется кисть, которая должна быть создана перед вызовом метода.
Вызов:

    object.DrawFillEllipse(x, y, width, height)

Аргументы:

    x, y - координаты центра эллипса (окружности)  (в пикселях)
    width, height - диагонали эллипса  (в пикселях). При их равенстве рисуется окружность.

Метод DrawPie

Рисует сектор. Для рисования используется карандаш, который должен быть создан перед вызовом метода.
Вызов:

    object.DrawPie(x, y, width, height, start, sweep)

Аргументы:

    x, y - координаты центра эллипса (окружности)  (в пикселях)
    width, height - диагонали эллипса  (в пикселях). При их равенстве рисуется окружность.
    start - угол начала сектора, в градусах, отсчитывается от оси X вниз.
    sweep
- ширина сектора в градусах.

Примечание:

Сектора рисуются по часовой стрелке. Для рисования в обратном порядке значения start  и sweep  должны быть отрицательными.

Метод DrawFillPie

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

    object.DrawPie(x, y, width, height, start, sweep)

Аргументы и примечания: см. метод DrawPie.

Метод DrawPolygon

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

    object.DrawPolygon(@PointArray)

Аргументы:

    PointArray - двумерный массив с координатами точек.

Примечание.

Массив должен передаваться по значению. Элементы массива должны заполняться следующим образом:

PointArray[1,1] = координата "X" первой точки
PointArray[1,
2] = координата "Y" первой точки
PointArray[
2,1] = координата "X" второй точки
PointArray[
2,2] = координата "Y" второй точки

и т.д.

Метод DrawFillPolygon

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

    object.DrawFillPolygon(@PointArray)

Аргументы:

    PointArray - двумерный массив с координатами точек.

Примечание.

См. выше.

Метод DrawText

Рисует текстовую строку. Для рисования используются объекты кисть и Font, которые должны быть созданы перед вызовом метода.
Вызов:

    object.DrawText(Text, x, y, width, height)

Аргументы:

    Text - текстовая строка, которую нужно нарисовать
    x, y
- координаты начальной точки строки. Строка рисуется вправо и вниз от начальной точки.
    width, height
- ширина и высота прямоугольника, в котором выводится текст (необязательные)

Примечание.

Если аргументы width  и height не заданы или равны нулю, то весь текст рисуется в одной строке. При заданных аргументах текст выводится внутри указанного прямоугольника с переносом по словам и выравниванием по левому краю (сколько поместится).

Метод GetErrorMessage

Возвращает строку с сообщением об ошибке.
Ниже приведён список ошибок, определяемый классом GdiPlusDraw.

1. Недопустимый тип аргумента
2. Недопустимое значение аргумента
3. Невозможно создать объект
4. Невозможно создать образ изображения для текстуры
5. Нет файла текстуры
6. Ошибка при удалении объекта
7. Объект не существует
8. Ошибка при установке стиля линии
9. Ошибка при установке цвета линии
10. Ошибка при установке цвета линии
11. Ошибка рисования
12. Переданный аргумент не является массивом
13. Передаваемый массив должен быть двумерным
14. Ошибка при создании фонта
15. Длина текстовой строки равна нулю
 

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

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

Холст так же создаёт BitMap, поэтому не злоупотребляйте его размерами.

Заключение

При написании классов GdiPlus и GdiPlusDraw имели место определённые трудности, вызванные отсутствием документации по функциям, включенным в GDIPLUS.DLL. Поэтому для желающих расширить реализованные возможности рекомендую следующую методику:

В то же время мне оказалась чрезвычайно полезной информация, найденная мною на сайте www.universalthread.com/wconnect/wc.dll?FournierTransformation~2,54,33,18584 - свободно распространяемый класс, созданный Александром Головлёвым, а так же примеры, опубликованные на сайте www.news2news.com/vfp/index.php.

Тем не менее не устранён один недостаток - явно указаны ClsID кодеков графических форматов, что может привести к неработоспособности класса (методов сохранения в файл) в новых разработках дяди Билла.

С уважением: Вячеслав Клепинин
Санкт-Петербург