Оказывается, Delphi 7 по-прежнему популярна и любима многими. У меня практически все примеры написаны для XE с использованием «родного» GDI+, что делает невозможным их компиляцию в Delphi 7. А потребность, как подключить GDI+ для Delphi 7, есть.
Конечно, просто так подключить не получиться. Поэтому пришлось произвести эдакий «регрессионный рефакторинг» родных модулей из XE, чтобы они могли быть скомпилированы в Delphi 7.
Подключение
Есть библиотека GDI+ для Delphi / Lazarus. Там есть все. Даже эффекты. Но подход отличается от принятого для Delphi XE, и совместная компиляция ни к чему хорошему не приводит. А хочется иметь код, который компилируется и в Delphi7, и в Delphi XE.
Поэтому качаем блок из четырех модулей: GDIPlus.zip (130 Кб). Это стандартные для XE модули, переделанные для Delphi7.
Подключаем каталог в опциях проекта. Если каталог находится на одном уровне с каталогом проекта, то выглядеть будет так:
В предложении Uses пишем так:
1 2 3 4 5 6 7 8 |
implementation uses {$IF CompilerVersion > 25} // >= XE5 Winapi.GDIPAPI, Winapi.GDIPOBJ {$ELSE} GDIPAPI, GDIPOBJ {$IFEND} |
Это нужно, чтобы XE, начиная с пятой (не знаю, как обстоят дела в более древних версиях XE), использовал свои Winapi.GDIPAPI, Winapi.GDIPOBJ модули.
Все готово. Теперь все, что написано о GDI+ в контексте XE, можно использовать и для Delphi 7.
GDI+ для Delphi 7
Возьмем, например, код, написанный для Delphi 7, из статьи принадлежность точки отрезку, и модифицируем его для GDI+.
Было:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
procedure TFmMain.pbPaintGDI(Sender: TObject); var rct: Trect; bmp: TBitmap; p1, p2, p: TPoint; r : TxPoint; eps: Integer; begin p1 := FP1; p2 := GetP2(FP2); p := FMousePoint; r := FMouseRes; rct := pb.ClientRect; bmp := TBitmap.Create; bmp.Width := rct.Right - rct.Left; bmp.Height := rct.Bottom - rct.Top; eps := SpinEdit1.Value; try with bmp.Canvas do begin Font.Assign(pb.Font); rct := ClipRect; Brush.Color := clWHite; FillRect(rct); Pen.Color := clSkyBlue; Brush.Color := clSkyBlue; MoveTo(p1.X, p1.y); LineTo(p2.X, p2.y); Ellipse(p1.X-eps,p1.Y-eps,p1.X+eps,p1.Y+eps); Ellipse(p2.X-eps,p2.Y-eps,p2.X+eps,p2.Y+eps); if FMouseMode > 0 then begin Ellipse(p.X-eps,p.Y-eps,p.X+eps,p.Y+eps); Brush.Color := clMaroon; Ellipse(round(r.X-eps),round(r.Y-eps),round(r.X+eps),round(r.Y+eps)); end; DrawLabels(bmp.Canvas, xPoint(p1),xPoint(p2),xPoint(p),r, SpinEdit1.Value, CurrCalcMode, FMouseMode); Pen.Color := $00999999; Rectangle(ClipRect); end; finally pb.Canvas.Draw(0,0,bmp); FreeAndNil(bmp); end; end; |
Стало:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
procedure TFmMain.pbPaintGDIP(Sender: TObject); var rct: Trect; bmp: TBitmap; p1, p2, p, r: TxPoint; eps: Extended; gpg: TGPGraphics; pen: TGPPen; brush: TGPSolidBrush; begin p1 := xPoint(FP1); p2 := xPoint(GetP2(FP2)); p := xPoint(FMousePoint); r := FMouseRes; rct := pb.ClientRect; bmp := TBitmap.Create; bmp.Width := rct.Right - rct.Left; bmp.Height := rct.Bottom - rct.Top; eps := SpinEdit1.Value; gpg := TGPGraphics.Create(bmp.Canvas.Handle); pen := TGPPen.Create(MakeColor($A6,$CA,$F0)); brush := TGPSolidBrush.Create(MakeColor($A6,$CA,$F0)); try gpg.SetSmoothingMode(SmoothingModeAntiAlias8x4); with bmp.Canvas do begin Font.Assign(pb.Font); rct := ClipRect; Brush.Color := clWHite; FillRect(rct); end; gpg.DrawLine(pen, p1.X, p1.y, p2.X, p2.y); gpg.FillEllipse(brush, p1.X-eps,p1.Y-eps,2*eps,2*eps); gpg.FillEllipse(brush, p2.X-eps,p2.Y-eps,2*eps,2*eps); if FMouseMode > 0 then begin gpg.FillEllipse(brush, p.X-eps,p.Y-eps,2*eps,2*eps); Brush.SetColor(MakeColor($80,$00, $00)); gpg.FillEllipse(brush, r.X-eps,r.Y-eps,2*eps,2*eps); end else begin pen.SetColor(MakeColor($99,$99,$99)); gpg.DrawEllipse(pen, p.X-eps,p.Y-eps,2*eps,2*eps); gpg.DrawLine(pen, p.X, p.y - eps*1.3, p.X, p.y + eps*1.3); gpg.DrawLine(pen, p.X - eps*1.3, p.y, p.X + eps*1.3, p.y); end; DrawLabels(bmp.Canvas, p1,p2,p,r, SpinEdit1.Value, CurrCalcMode, FMouseMode); bmp.Canvas.Pen.Color := $00999999; bmp.Canvas.Rectangle(bmp.Canvas.ClipRect); finally pb.Canvas.Draw(0,0,bmp); FreeAndNil(brush); FreeAndNil(pen); FreeAndNil(gpg); FreeAndNil(bmp); end; end; |
Этот код без проблем скомпилируется в XE7, XE10 (возможно и в XE5, проверить сейчас не могу).
Модификация GDIPCanvas
Когда-то давно сделал наследника TCanvas, умеющего работать, и как стандартный TCanvas, так и с вызовами GDIPlus. Заточен был на работу в XE, т.к. в Delphi 7 нет родного GDI+. В связи с появлением у меня возможности использовать ровно те же самые вызовы для Delphi 7, что и для XE, немного переделал блок GDIPCanvas.
Скачать GDIPCanvas.zip (99.5 Кб)
Необходимо скачать, распаковать, почитать лицензию, порадоваться за себя, как-то ненавязчиво сказать мне — спасибо, или раскритиковать в дым.
Если каталог GDIPCanvas будет находиться на одном уровне с каталогом проекта и каталогом GDIPlus, то предложение Search path должно выглядеть так: ..\GDIPlus\;..\GDIPCanvas\
В предложение uses добавляем следующие модули:
1 2 3 4 |
uses xIPCanvas, xGDIPFont, ... ; |
Зачем вообще использовать GDIPCanvas. Во-первых, рисование в нем совершенно аналогичное стандартному TCanvas, во-вторых, он писался в свое время только для XE. Интересно же посмотреть, как он съест отрефакторенные модули GDIP для Delphi 7?
Для режима одновременно включенных GDI+ и GDIPCanvas не стал устанавливать шрифт, чтобы было видно, как GDIPCanvas отрабатывает разные фонты.
В целом, все чудесно подхватилось и работает, как ожидалось.
Нюансы GDIPCanvas для Delphi7 и XE
К сожалению, TCanvas в исполнении Delphi7, не предоставил виртуализации своих методов. Поэтому, для Delphi7, при отрисовке, надо жестко указывать тип холста.
Из-за этого пришлось писать дубль функции отрисовки текста.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
procedure DrawLabels(ACanvas: TCanvas; const p1,p2,p,r: TxPoint; const Epsilon: Extended; const AMode, AMouseMode: Integer); {$IF CompilerVersion < 26} overload; {$IFEND} var str, decimal, tmp: String; dx, dy, px, py: Extended; v: TxPoint; rct: TRect; begin {$IF CompilerVersion >= 26} decimal := FormatSettings.DecimalSeparator; {$ELSE} decimal := DecimalSeparator; {$IFEND} with ACanvas do begin Brush.Style := bsClear; ... end; end; {$IF CompilerVersion < 26} procedure DrawLabels(ACanvas: TxIPCanvas; const p1,p2,p,r: TxPoint; const Epsilon: Extended; const AMode, AMouseMode: Integer); overload; begin with ACanvas do begin Brush.Style := bsClear; // все то же самое, различие только в типе ACanvas ... end; end; {$IFEND} |
Как видим, для XE5, в случае обычного TCanvas или TxIPCanvas, будет работать единственная процедура DrawLabels без перегрузки.
Также, для Delphi7 и ХЕ, будут разные подходы к объявлению типа и использованию.
Например для pbPaintGDIP():
1 2 3 4 |
procedure TFmMain.pbPaintGDI(Sender: TObject); var ... cnv: {$if CompilerVersion > 25}TCanvas{$else}TxIPCanvas{$ifend}; |
Видим, что для Delphi7 тип холста cnv явно указан, как TxIPCanvas, в то время, как для XE это TCanvas. То есть может быть, как стандартным TCanvas, так и его наследником.
Соответственно, меняется и способ создания.
1 2 3 4 5 6 7 8 9 10 11 |
{$if CompilerVersion > 25} // требуется GDIPCanvas if CheckBox2.Checked then cnv := IPCanvas(bmp.Canvas, True) else cnv := bmp.Canvas; {$else} cnv := IPCanvas(bmp.Canvas, True); cnv.UseStandartGDI := not CheckBox2.Checked; {$ifend} |
Если имеем дело с XE, и если нужен GDIPCanvas, создаем его. Иначе, просто присваиваем холст битмапа, т.е. стандартный GDI-холст. В случае Delphi7, GDIPCanvas создается в любом случае, и если возможности GDI+ не требуются, устанавливаем UseStandartGDI в True. В этом случае все вызовы будут стандартными.
Использование также меняется.
1 2 3 4 5 6 7 8 9 10 11 |
{$if CompilerVersion > 25} DrawLabels(cnv, p1,p2,p,r, SpinEdit1.Value, CurrCalcMode, FMouseMode); {$else} if CheckBox2.Checked then DrawLabels(cnv, p1,p2,p,r, SpinEdit1.Value, CurrCalcMode, FMouseMode) else DrawLabels(bmp.Canvas, p1,p2,p,r, SpinEdit1.Value, CurrCalcMode, FMouseMode); {$ifend} |
Как видим, для XE используется одна и та же процедура, в то время, как для Delphi7 — две перегруженных.
Таким образом, чтобы рисовать в GDI+ нам уже не требуется отдельная процедура. Все можно сделать в одной. Конечно, директивы чуть скрадывают код, но, думаю, суть понятна. Для рисования либо в стандартном GDI, либо в GDI+ не надо писать два совершенно разных кода.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
procedure TFmMain.pbPaintGDI(Sender: TObject); var rct: Trect; bmp: TBitmap; p1, p2, p: TPoint; r : TxPoint; eps: Integer; cnv: {$if CompilerVersion > 25}TCanvas{$else}TxIPCanvas{$ifend}; begin p1 := FP1; p2 := GetP2(FP2); p := FMousePoint; r := FMouseRes; rct := pb.ClientRect; bmp := TBitmap.Create; bmp.Width := rct.Right - rct.Left; bmp.Height := rct.Bottom - rct.Top; eps := SpinEdit1.Value; {$if CompilerVersion > 25} if CheckBox2.Checked then cnv := IPCanvas(bmp.Canvas, True) else cnv := bmp.Canvas; {$else} cnv := IPCanvas(bmp.Canvas, True); cnv.UseStandartGDI := not CheckBox2.Checked; {$ifend} try with cnv do begin Font.Assign(pb.Font); rct := ClipRect; Brush.Color := clWhite; FillRect(rct); Pen.Color := clSkyBlue; Brush.Color := clSkyBlue; MoveTo(p1.X, p1.y); LineTo(p2.X, p2.y); Ellipse(p1.X-eps,p1.Y-eps,p1.X+eps,p1.Y+eps); Ellipse(p2.X-eps,p2.Y-eps,p2.X+eps,p2.Y+eps); if FMouseMode > 0 then begin Ellipse(p.X-eps,p.Y-eps,p.X+eps,p.Y+eps); Brush.Color := clMaroon; Ellipse(round(r.X-eps),round(r.Y-eps),round(r.X+eps),round(r.Y+eps)); end; DrawLabels(cnv, xPoint(p1),xPoint(p2),xPoint(p),r, SpinEdit1.Value, CurrCalcMode, FMouseMode); Pen.Color := $00999999; Rectangle(ClipRect); end; finally if cnv <> bmp.Canvas then FreeAndNil(cnv); pb.Canvas.Draw(0,0,bmp); FreeAndNil(bmp); end; end; |
Скачать
Друзья, спасибо за внимание!
Было бы очень здорово, если о результатах использования GDIP и GDIPCanvas вы поделились бы в комментариях. Думаю, это полезно всем. О всех изменениях и обновлениях буду сообщать в телегу Подписывайтесь, чтобы не пропустить!
Исходник с GDI+ (zip) 148 Кб.
Исполняемый файл c GDI+ (zip) 216 Кб.
Исходник с GDIPCanvas (zip) 241 Кб.
Исполняемый файл с GDIPCanvas (zip) 283 Кб.
Эти каталоги уже включены в исходники, для скачивания отдельно от них:
GDIPlus.zip 130 Кб.
GDIPCanvas.zip 99.5 Кб.
В статье про быстрый доступ к пикселям есть ссылка на скачивание версии для Delphi 7 без поддержки GDI+. Теперь можно скачать с поддержкой GDI+ и как следует откомментарить ))) Исправлена, кстати одна неприятность, так что работает лучше, чем было.
Исходники (zip) 3.09 Мб
Исполняемый файл (zip) 2.69 Мб
При компиляции в XE вначале надо сделать Build. После этого Compile (Ctrl+F9) будет работать без проблем.
Привет! Отличная, и, главное, очень полезная статья. Я, пока детально в код ещё не вникал, но подозреваю, что вполне вероятно использовать GDI+ не только в Delphi 7, но и в более древней Delphi 5 (да, есть люди, которые и в ней работают, как бы странно это не звучало).
Ну, и если можно покритиковать, то чутку покритикую — в статье всегда и не очень очевидно, про какую именно версию Делфи ХЕ идёт речь. Дело в том, что после выхода Delphi 2010 «бракоделы» выпустили ХЕ, потом уже была ХЕ2, ХЕ3…ХЕ8, а дальше уже Делфи 10, сиречь Сиэтл, когда они переключились на города в именовании.
Так вот, когда в статье встретил упоминание про Delphi XE, то сразу и подумал именно про XE (которая по идее должна быть Делфи 2011). И лишь потом осознал, что речь идёт про семейство в целом, причем как я понял, гарантированно лишь про ХЕ8, а все, что младше, оно как бы сказать…. Вроде тоже должно работать, но не обязано )))
Так моя мысля в том, чтобы не вносить сумятицу и неумышленно не вводить в заблуждение по поводу версий ) И только.
Что же до статьи — могу только ещё раз поблагодарить, и, раз уж волей случая автору самому понадобилось поработать в классике (т.е. в Делфи 7), то смею надеяться, что это не последняя статья, да и материал вообще, который можно применить в семерине.
Лично я бы не сказал, что часто прям мне нужно что-то делать в семёрке, но тем не менее, порой бывает и такое. А ещё уверен, что статья и код будут крайне полезны именно тем, у кого семёрка в ходу и по сей день.
И вот в финале своего лонгрида хотел бы спросить про вероятность того, что в принципе, насколько реально и трудоёмко научить Делфи 7 помимо GDI+ работать ещё и с Direct2D, с теми замечательными эффектами и примерами, цикл статей о которых есть тут, на сайте?
Насколько я заглядывал в модули про Директ2Д (а это было бегло), то мне видится, что задачка не самая простая. Хотя, возможно, я ошибаюсь и не все так страшно?
Ещё раз спасибо!!!!
Привет! Говорил в телеге, еще раз повторю, очень рад тебя видеть.
С критикой — согласен. Под XE я малодушно прячу свое незнание (нежелание ставить) всей линейки XE. Работал с XE, XE2 — ну горе, горе… Потом XE5 — хм, вполне. Потом XE7 — в которой до сих пор в основном и работаю, все устраивает. XE 10.0, 10.3, 10.4 — установлены, в них тестируются все исходники на сайте. Но любви особой нет. Поэтому под загадочным XE, по сути, прячу XE 5, XE 7 и XE 10. В них уверен. А когда там в линейке появилась поддержка GDIP — черт его знает, инет многолик и разноречив.
А вот XE 8 — в глаза не видел ))) Как и 9. Даже не знал, что существуют. Видимо застрял в XE 7, как в Delphi 7 в свое время.
Delphi 5 — там надо вообще все перелопачивать, CompilerVersion нет.
И, конечно, в планах сделать такой же Canvas для Direct2D, как и для GDI+.
GDIPCanvas — сейчас работаю над версией, включающей в себя, и поддержку Delphi 7, и эффекты. Помнишь, ты говорил про кривой блюр. Он кривой для Windows 8/10. При радиусе до 20 pix блюрит строго по горизонтали. В Windows 7 все работает изумительно. Но излечимо.
D2DCanvas — это просто надо сделать, потому что TDirect2DCanvas, предлагаемый Delphi XE (7,10 конечно) не состоятелен по причине 1) порожден от TCustomCanvas, что влечет за собой целый ворох проблем, 2) INTEGER КООРДИНАТЫ!!!! Бред какой-то, самое полезное, из-за чего стоит использовать подобные штуки, как GDI+ и Direct2D — это вещественные координаты.
Сделать D2D для D7 — возможно, уже смотрел, кое-что написал, знаю как буду действовать. Но, блин, время ))) Сейчас заканчиваю один и сразу в другой проект.
Снова — рад тебя видеть. Написать статью не хочешь? Мои двери открыты )))
Да ваще, приезжай в гости )))
В версии XE нет модуля UITypes.
Если верить информации по нижеприведённой ссылке, то искомый модуль появился с версии XE2
https://sourceforge.net/p/gexperts/bugs/99/
Видимо, это касается GDIPCanvas: xGDIPFont и xIPCanvas.
Можно поправить pro_param.inc:
{$IFNDEF VER200}
{$IFNDEF VER210}
{$IFNDEF VER220}
{$DEFINE VER_XE} { Delphi XE2 or higher }
{$IFNDEF VER230}
{$DEFINE VER_XE10} { Delphi XE3 or higher }
{$ENDIF}
{$ENDIF}
{$ENDIF}
{$ENDIF}