Sign in Join
Wiki copytemporary

Developing with Graphics/ru: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Developing with Graphics/ru

OldNewLine
1+Эта страница описывает основные классы и технологии рисования графики в Lazarus. Какие-то специфические вещи ищите в других статьях.
2+
3+## Графические библиотеки
4+
5+[[Graphics libraries|Графические библиотеки]] - здесь вы можете посмотреть, какие есть основные графические библиотеки.
6+
7+## Другие статьи по графике
8+
9+### 2D-рисование
10+
11+- [[ZenGL/ru|ZenGL]] - кроссплатформенная библиотека для разработки игр на основе OpenGL.
12+- [[BGRABitmap/ru|BGRABitmap]] - Рисование фигур, прозрачных изображений, прямой доступ к пикселям и др.
13+- [[LazRGBGraphics]] - Пакет для быстрой обработки изображения в памяти и работы с пикселями (например, scan line).
14+- [[fpvectorial]] - Предоставляет возможность работы с векторной графикой.
15+- [[Double Gradient]] - Рисуйте легко растровые изображения 'double gradient' и 'n-gradient'.
16+- [[Gradient Filler]] - TGradientFiller - лучший способ создания пользовательских n-градиентов в Lazarus.
17+- [[PascalMagick]] - простой в использовании API для взаимодействия с [ImageMagick](http://www.imagemagick.org), многоплатформенным пакетом бесплатного программного обеспечения для создания, редактирования и создания растровых изображений.
18+- [[Sample Graphics]] - графическая галерея, созданная с помощью Lazarus и инструментов для рисования.
19+- [[Fast direct pixel access]] - сравнение скорости некоторых методов для прямого доступа к растровому пикселю.
20+- [AggPas](http://www.crossgl.com/aggpas/) - это нативный порт Object Pascal библиотеки Anti-Grain Geometry. Он быстр и очень мощен \[при работе\] со сглаженным рисунком и субпиксельной точностью. Вы можете думать об AggPas как о механизме рендеринга, который создает пиксельные изображения в памяти из некоторых векторных данных.
21+
22+### 3D-рисование
23+
24+- [[GLScene]] - Порт 3D визуальной графической библиотеки [GLScene OpenGL](http://www.glscene.org)
25+
26+### Диаграммы
27+
28+- [[TAChart]] - компонента для рисования диаграмм в Lazarus
29+- [[PlotPanel]] - Создание и черчение анимированных графиков
30+- [[Perlin Noise]] - статья об использовании Perlin Noise в LCL приложениях.
31+
32+## Введение в графическую модель LCL
33+
34+LCL предоставляет два вида классов рисования: нативные \[(собственные)\] классы и не-нативные \[(сторонние)\] классы. Нативные графические классы являются наиболее традиционным способом рисования графики в LCL, а также являются наиболее важными, в то время как не-нативные классы являются дополнительными, но также очень важными. Собственные классы в основном расположены в модуле **Graphics** LCL и являются хорошо известными классами: TBitmap, TCanvas, TFont, TBrush, TPen, TPortableNetworkGraphic и т.д.
35+
36+TCanvas - это класс, способный выполнять рисование. Он не может существовать один и должен быть либо прикреплен к чему-то видимому (или, по крайней мере, который может быть видимым), например, к визуальному элементу управления, происходящему из TControl, либо должен быть присоединен к внеэкранному буферу потомка от TRasterImage (TBitmap является наиболее часто использующимся). TFont, TBrush и TPen описывают, как рисование различных операций будет выполняться в Canvas.
37+
38+TRasterImage (обычно используется через его потомка TBitmap) - это область памяти, зарезервированная для рисования графики, но она создана для максимальной совместимости с собственным Canvas и, следовательно, в LCL-Gtk2 в X11 она расположена на сервере X11, который обеспечивает попиксельный доступ через свойство Pixels чрезвычайно медленно. В Windows это очень быстро, потому что Windows позволяет создавать локально выделенное изображение, которое может получать рисунки из Windows Canvas.
39+
40+Помимо них существуют также не-нативные классы рисования, расположенные в графическом типе модулей (TRawImage), intfgraphics (TLazIntfImage) и lazcanvas (TLazCanvas, этот существует в Lazarus \[с версии\] 0.9.31+). TRawImage - это хранилище и описание области памяти, которая содержит изображение. TLazIntfImage - это изображение, которое присоединяется к TRawImage и обеспечивает преобразование между TFPColor и форматом настоящего пикселя TRawImage. TLazCanvas - это не нативный Canvas, который может рисовать изображение в TLazIntfImage.
41+
42+Основное различие между нативными и не-нативными классами состоит в том, что нативные классы не выполняются одинаково на всех платформах, потому что рисование выполняется самой базовой платформой. Скорость, а также точный конечный результат рисования изображения могут иметь различия. Не-нативные классы гарантированно выполняют одинаковое рисование на всех платформах с точностью до пикселя, и все они работают достаточно быстро на всех платформах.
43+
44+В наборе виджетов LCL-CustomDrawn нативные классы реализованы с использованием не-нативных классов.
45+
46+Все эти классы будут лучше описаны в разделах ниже.
47+
48+## Работа с TCanvas
49+
50+### Рисование прямоугольника
51+
52+Многие элементы управления, например TForm, TPanel или TPaintbox, отображают свой холст как общедоступное свойство или событие OnPaint. Давайте используем TForm в качестве примера, чтобы продемонстрировать, как рисовать на холсте.
53+
54+Предположим, мы хотим нарисовать красный прямоугольник с синей рамкой толщиной 5 пикселей в центре формы; размер прямоугольника должен составлять половину размера формы. Для этого мы должны добавить код в событие OnPaint формы. Не рисуйте в обработчике OnClick, потому что это рисование не является постоянным и будет стираться всякий раз, когда операционная система запрашивает перерисовку, всегда рисуйте в событии OnPaint!
55+
56+Метод TCanvas для рисования прямоугольника вызывается именно так: `Rectangle()`. Он получает координаты краев прямоугольника либо отдельно, либо в виде записи `TRect`. Цвет заливки определяется цветом кисти Холста, а цвет границы задается цветом пера холста:
57+
58+``` pascal
59+procedure TForm1.FormPaint(Sender: TObject);
60+var
61+ w, h: Integer; // Ширина и высота прямоугольника
62+ cx, cy: Integer; // центр формы
63+ R: TRect; // запись, содержащая координаты левого, верхнего, правого, нижнего углов прямоугольника
64+begin
65+ // Высчитываем центр формы
66+ cx := Width div 2;
67+ cy := Height div 2;
68+
69+ // Рассчитываем размер прямоугольника
70+ w := Width div 2;
71+ h := Height div 2;
72+
73+ // Рассчитываем углы прямоугольника
74+ R.Left := cx - w div 2;
75+ R.Top := cy - h div 2;
76+ R.Right := cx + w div 2;
77+ R.Bottom := cy + h div 2;
78+
79+ // Устанавливаем цвет заливки
80+ Canvas.Brush.Color := clRed;
81+ Canvas.Brush.Style := bsSolid;
82+
83+ // Устанавливаем цвет границы
84+ Canvas.Pen.Color := clBlue;
85+ Canvas.Pen.Width := 5;
86+ Canvas.Pen.Style := psSolid;
87+
88+ // Рисуем прямоугольник
89+ Canvas.Rectangle(R);
90+end;
91+```
92+
93+### Использование шрифта GUI по умолчанию
94+
95+Это можно сделать с помощью этого простого кода:
96+
97+``` pascal
98+SelectObject(Canvas.Handle, GetStockObject(DEFAULT_GUI_FONT));
99+```
100+
101+### Рисование ограниченного по ширине текста
102+
103+Используйте процедуру DrawText, сначала с DT_CALCRECT, а затем без него.
104+
105+``` pascal
106+// Сначала рассчитываем размер текста, затем рисуем его
107+TextBox := Rect(0, currentPos.Y, Width, High(Integer));
108+DrawText(ACanvas.Handle, PChar(Text), Length(Text), TextBox, DT_WORDBREAK or DT_INTERNAL or DT_CALCRECT);
109+
110+DrawText(ACanvas.Handle, PChar(Text), Length(Text), TextBox, DT_WORDBREAK or DT_INTERNAL);
111+```
112+
113+### Рисование текста с резкими краями (без сглаживания)
114+
115+Некоторые виджеты поддерживают это через
116+
117+``` pascal
118+Canvas.Font.Quality := fqNonAntialiased;
119+```
120+
121+Некоторые виджеты, такие как gtk2, не поддерживают это и всегда рисуют сглаживание. Вот простая процедура рисования текста с резкими краями под gtk2. Она не предусматривает все случаи, но должна дать представление:
122+
123+``` pascal
124+procedure PaintAliased(Canvas: TCanvas; x,y: integer; const TheText: string);
125+var
126+ w,h: integer;
127+ IntfImg: TLazIntfImage;
128+ Img: TBitmap;
129+ dy: Integer;
130+ dx: Integer;
131+ col: TFPColor;
132+ FontColor: TColor;
133+ c: TColor;
134+begin
135+ w:=0;
136+ h:=0;
137+ Canvas.GetTextSize(TheText,w,h);
138+ if (w<=0) or (h<=0) then exit;
139+ Img:=TBitmap.Create;
140+ IntfImg:=nil;
141+ try
142+ // рисуем текст в растровое изображение
143+ Img.Masked:=true;
144+ Img.SetSize(w,h);
145+ Img.Canvas.Brush.Style:=bsSolid;
146+ Img.Canvas.Brush.Color:=clWhite;
147+ Img.Canvas.FillRect(0,0,w,h);
148+ Img.Canvas.Font:=Canvas.Font;
149+ Img.Canvas.TextOut(0,0,TheText);
150+ // получаем изображение в памяти
151+ IntfImg:=Img.CreateIntfImage;
152+ // заменяем серые пиксели
153+ FontColor:=ColorToRGB(Canvas.Font.Color);
154+ for dy:=0 to h-1 do begin
155+ for dx:=0 to w-1 do begin
156+ col:=IntfImg.Colors[dx,dy];
157+ c:=FPColorToTColor(col);
158+ if c<>FontColor then
159+ IntfImg.Colors[dx,dy]:=colTransparent;
160+ end;
161+ end;
162+ // создаем растровое изображение
163+ Img.LoadFromIntfImage(IntfImg);
164+ // рисуем
165+ Canvas.Draw(x,y,Img);
166+ finally
167+ IntfImg.Free;
168+ Img.Free;
169+ end;
170+end;
171+```
172+
173+## Работа с TBitmap и другими потомками TGraphic
174+
175+Объект TBitmap хранит растровое изображение, где вы можете рисовать, прежде чем показывать его на экране. Когда вы создаете растровое изображение, вы должны указать высоту и ширину, иначе это будет ноль, и ничто не будет нарисовано. И вообще, все остальные потомки TRasterImage предоставляют те же возможности. Следует использовать тот, который соответствует формату, необходимому для вывода/ввода с диска или TBitmap, в случае, если операции с дисками не будут выполняться, а также для формата Windows Bitmap (*.bmp).
176+
177+### Загрузка/Сохранение изображения из/на диск
178+
179+Чтобы загрузить изображение с диска, используйте [TGraphic.LoadFromFile](http://lazarus-ccr.sourceforge.net/docs/lcl/graphics/tgraphic.loadfromfile.html) и для сохранения его на другом диске, используйте [TGraphic.SaveToFile](http://lazarus-ccr.sourceforge.net/docs/lcl/graphics/tgraphic.savetofile.html). Используйте соответствующий потомок TGraphic, который соответствует ожидаемому формату. См. [[Developing with Graphics/ru|форматы изображений]] для \[просмотра\] списка доступных классов формата изображения.
180+
181+``` pascal
182+var
183+ MyBitmap: TBitmap;
184+begin
185+ MyBitmap := TBitmap.Create;
186+ try
187+ // Загрузка с диска
188+ MyBitmap.LoadFromFile(MyEdit.Text);
189+
190+ // Здесь вы можете использовать MyBitmap.Canvas для чтения/записи в/из изображения
191+
192+ // Записываем обратно на другой диск
193+ MyBitmap.SaveToFile(MyEdit2.Text);
194+ finally
195+ MyBitmap.Free;
196+ end;
197+end;
198+```
199+
200+При использовании любого другого формата процесс полностью идентичен, просто используйте соответствующий класс. Например, для изображений PNG:
201+
202+``` pascal
203+var
204+ MyPNG: TPortableNetworkGraphic;
205+begin
206+ MyPNG := TPortableNetworkGraphic.Create;
207+ try
208+ // Загрузка с диска
209+ MyPNG.LoadFromFile(MyEdit.Text);
210+
211+ // Здесь вы можете использовать MyPNG.Canvas для чтения/записи в/из изображения
212+
213+ // Записываем обратно на другой диск
214+ MyPNG.SaveToFile(MyEdit2.Text);
215+ finally
216+ MyPNG.Free;
217+ end;
218+end;
219+```
220+
221+Если вы заранее не знаете формат изображения, используйте TPicture, который определит формат на основе расширения файла. Обратите внимание, что TPicture не поддерживает все форматы, поддерживаемые Lazarus, с Lazarus 0.9.31 он поддерживает BMP, PNG, JPEG, Pixmap и PNM, в то время как Lazarus также поддерживает ICNS и другие форматы:
222+
223+``` pascal
224+var
225+ MyPicture: TPicture;
226+begin
227+ MyPicture := TPicture.Create;
228+ try
229+ // Загрузка с диска
230+ MyPicture.LoadFromFile(MyEdit.Text);
231+
232+ // Здесь вы можете использовать MyPicture.Graphic.Canvas для чтения/записи в/из изображения
233+
234+ // Записываем обратно на другой диск
235+ MyPicture.SaveToFile(MyEdit2.Text);
236+ finally
237+ MyPicture.Free;
238+ end;
239+end;
240+```
241+
242+### Дополнительные форматы файлов для TImage
243+
244+Вы можете добавить дополнительную поддержку форматов файлов, добавив модули [[fcl-image]] fpread* и/или fpwrite* в вашу секцию uses. Таким образом, вы можете, например, добавить поддержку TIFF для TImage
245+
246+### Прямой доступ к пикселям
247+
248+Для непосредственного доступа к пикселям растровых изображений можно использовать внешние библиотеки, такие как [[BGRABitmap]], [[LazRGBGraphics]] и [[Current conversion projects/ru|Graphics32]], или использовать нативный Lazarus'овский TLazIntfImage. Сравнение методов доступа к пикселям см. [[Fast direct pixel access|fast direct pixel access]].
249+
250+В некоторых наборах виджетов Lazarus (в частности, LCL-Gtk2) данные растрового изображения не сохраняются в памяти, к которой может обращаться приложение, и в общем случае собственные интерфейсы LCL рисуются только через собственные процедуры Canvas, поэтому каждая операция SetPixel / GetPixel включает медленный вызов родного Canvas API. В LCL-CustomDrawn это не так, поскольку растровое изображение локально сохраняется для всех бэкэндов, а SetPixel / GetPixel работает быстро. Для получения решения, которое работает во всех наборах виджетов, следует использовать TLazIntfImage. Поскольку Lazarus должен быть независимым от платформы и работать в gtk2, класс TBitmap не предоставляет такое свойство, как Scanline. Существует функция GetDataLineStart, эквивалентная Scanline, но доступная только для образов памяти, таких как [[Developing with Graphics/ru|TLazIntfImage]], которая внутренне использует TRawImage.
251+
252+Подводя итог, можно сказать, что со стандартным TBitmap вы можете только косвенно изменять пиксели, используя TCanvas.Pixels. Вызов собственного API для рисования / чтения отдельного пикселя, конечно, медленнее, чем прямой доступ к пикселям, особенно в LCL-gtk2 и LCL-Carbon.
253+
254+------------------------------------------------------------------------
255+
256+Прим.перев.: Есть весьма [неплохая статья](https://stackoverflow.com/questions/13583451/how-to-use-scanline-property-for-24-bit-bitmaps) (правда, на английском, чуть позже сделаю перевод и выложу на какой-нибудь ресурс), объясняющая суть работы свойства Scanline.
257+
258+------------------------------------------------------------------------
259+
260+### Рисование растровых изображений прозрачным цветом
261+
262+Новая функция, реализованная в Lazarus 0.9.11, - это растровые изображения с прозрачными цветами. В растровых файлах (*.BMP) не может храниться информация о прозрачности, но они могут работать так же, как если бы вы выбрали цвет для представления прозрачной области. Это распространенный прием, используемый в приложениях Win32.
263+
264+В следующем примере загружается растровое изображение из ресурса Windows, выбирается прозрачный цвет (clFuchsia) и затем он рисуется на холсте.
265+
266+``` pascal
267+procedure MyForm.MyButtonOnClick(Sender: TObject);
268+var
269+ buffer: THandle;
270+ bmp: TBitmap;
271+ memstream: TMemoryStream;
272+begin
273+ bmp := TBitmap.Create;
274+
275+ buffer := Windows.LoadBitmap(hInstance, MAKEINTRESOURCE(ResourceID));
276+
277+ if (buffer = 0) then exit; // Ошибка загрузки растрового изображения
278+
279+ bmp.Handle := buffer;
280+ memstream := TMemoryStream.create;
281+ try
282+ bmp.SaveToStream(memstream);
283+ memstream.position := 0;
284+ bmp.LoadFromStream(memstream);
285+ finally
286+ memstream.free;
287+ end;
288+
289+ bmp.Transparent := True;
290+ bmp.TransparentColor := clFuchsia;
291+
292+ MyCanvas.Draw(0, 0, bmp);
293+
294+ bmp.Free; // Освобождаем выделенный ресурс
295+end;
296+```
297+
298+Обратите внимание на операции с памятью, выполненные с помощью [[doc:rtl/classes/tmemorystream.html|TMemoryStream]]. Они необходимы для обеспечения правильной загрузки изображения.
299+
300+### Получение скриншота экрана
301+
302+Начиная с Lazarus 0.9.16, вы можете использовать LCL, чтобы делать скриншоты экрана кросс-платформенным способом. Следующий пример кода делает это (работает на gtk2 и win32, но не на gtk1 в настоящее время):
303+
304+``` pascal
305+uses Graphics, LCLIntf, LCLType;
306+ ...
307+var
308+ MyBitmap: TBitmap;
309+ ScreenDC: HDC;
310+begin
311+ MyBitmap := TBitmap.Create;
312+ ScreenDC := GetDC(0);
313+ MyBitmap.LoadFromDevice(ScreenDC);
314+ ReleaseDC(0,ScreenDC);
315+ ...
316+```
317+
318+## Работа с TLazIntfImage, TRawImage и TLazCanvas
319+
320+TLazIntfImage не является нативным эквивалентом TRasterImage (чаще используется в форме потомка TBitmap). Первое, что нужно знать об этом классе, это то, что в отличие от TBitmap, он не будет автоматически выделять область памяти для растрового изображения, сначала нужно инициализировать область памяти, а затем передать ее в TLazIntfImage. Сразу после создания TLazIntfImage нужно либо подключить его к TRawImage, либо загрузить из TBitmap.
321+
322+TRawImage относится к типу объект и поэтому не нуждается ни в создании, ни в освобождении. Он может либо выделить память для самого изображения при вызове TRawImage.CreateData, либо передать блок памяти, выделенный, например, сторонней библиотекой, такой как Windows API Cocoa Framework из Mac OS X, и передать информацию об изображении в TRawImage.Description, TRawImage.Data и TRawImage.DataSize. Вместо того, чтобы прикреплять его к RawImage, можно также загрузить его из TBitmap, который будет копировать данные из TBitmap и впоследствии не будет синхронизироваться с ним. TLazCanvas не может существовать один и всегда должен быть присоединен к TLazIntfImage.
323+
324+В приведенном ниже примере показано, как выбрать формат для данных и попросить TRawImage создать его для нас, а затем мы присоединим его к TLazIntfImage и потом присоединим к нему TLazCanvas:
325+
326+``` pascal
327+uses graphtype, intfgraphics, lazcanvas;
328+
329+var
330+ AImage: TLazIntfImage;
331+ ACanvas: TLazCanvas;
332+ lRawImage: TRawImage;
333+begin
334+ lRawImage.Init;
335+ lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(AWidth, AHeight);
336+ lRawImage.CreateData(True);
337+ AImage := TLazIntfImage.Create(0,0);
338+ AImage.SetRawImage(lRawImage);
339+ ACanvas := TLazCanvas.Create(AImage);
340+```
341+
342+### Инициализация TLazIntfImage
343+
344+Нельзя просто создать экземпляр TLazIntfImage и начать его использовать. Нужно добавить к нему хранилище. Есть 3 способа сделать это:
345+
346+1\. Прикрепить его к TRawImage
347+
348+2\. Загрузить его из TBitmap. Обратите внимание, что он скопирует память TBitmap, чтобы \[экземпляр TLazIntfImage\] не оставался подключенным к нему:
349+
350+``` pascal
351+ SrcIntfImg:=TLazIntfImage.Create(0,0);
352+ SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
353+```
354+
355+3\. Загрузить его из описания необработанного изображения, например так:
356+
357+``` pascal
358+ IntfImg := TLazIntfImage.Create(0,0);
359+ IntfImg.DataDescription:=GetDescriptionFromDevice(0);
360+ IntfImg.SetSize(10,10);
361+```
362+
363+Устройство 0 в **GetDescriptionFromDevice(0)** использует текущий формат экрана.
364+
365+### TLazIntfImage.LoadFromFile
366+
367+Вот пример того, как загрузить изображение непосредственно в TLazIntfImage. Он инициализирует TLazIntfImage в 32-битном формате RGBA. Имейте в виду, что это, вероятно, не собственный формат вашего экрана.
368+
369+``` pascal
370+uses LazLogger, Graphics, IntfGraphics, GraphType;
371+procedure TForm1.FormCreate(Sender: TObject);
372+var
373+ AImage: TLazIntfImage;
374+ lRawImage: TRawImage;
375+begin
376+ // создаем TLazIntfImage с 32 битами на пиксель, альфа 8 бит, красный 8 бит, зеленый 8 бит, синий 8 бит,
377+  // порядок битов: бит 0 - это пиксель 0, сверху вниз: строка 0 - это верх
378+ lRawImage.Init;
379+ lRawImage.Description.Init_BPP32_A8R8G8B8_BIO_TTB(0,0);
380+ lRawImage.CreateData(false);
381+ AImage := TLazIntfImage.Create(0,0);
382+ try
383+ AImage.SetRawImage(lRawImage);
384+ // Загружаем изображение с диска.
385+ // Используется расширение файла для правильного выбора изображения зарегистрированным ридером.
386+ // AImage будет масштабирован до ширины и высоты загруженного изображения.
387+ AImage.LoadFromFile('lazarus/examples/openglcontrol/data/texture1.png');
388+ debugln(['TForm1.FormCreate ',AImage.Width,' ',AImage.Height]);
389+ finally
390+ AImage.Free;
391+ end;
392+end;
393+```
394+
395+### Загрузка TLazIntfImage в TImage
396+
397+Пиксельные данные для **TImage** - это свойство **TImage.Picture**, которое имеет тип *TPicture*. **TPicture** - это мультиформатный контейнер, содержащий один из нескольких распространенных форматов изображений, таких как Bitmap, Icon, Jpeg или PNG. Обычно вы будете использовать *TPicture.Bitmap* для загрузки **TLazIntfImage**:
398+
399+``` pascal
400+ Image1.Picture.Bitmap.LoadFromIntfImage(IntfImg);
401+```
402+
403+**Примечание:**
404+
405+- Чтобы загрузить **прозрачный** TLazIntfImage, вы должны установить для **Image1.Transparent** значение true.
406+- TImage использует формат экрана. Если TLazIntfImage имеет другой формат, то пиксели будут преобразованы.
407+
408+Подсказка: вы можете использовать **IntfImg.DataDescription:=GetDescriptionFromDevice(0);** для инициализации TLazIntfImage с форматом экрана.
409+
410+### Пример обесцвечивания
411+
412+Пример обесцвечивания с \[использованием\] TLazIntfImage
413+
414+``` pascal
415+{ Этот код был взят из $LazarusPath/examples/lazintfimage/fadein1.lpi project. }
416+uses LCLType, // HBitmap type
417+ IntfGraphics, // TLazIntfImage type
418+ fpImage; // TFPColor type
419+...
420+ procedure TForm1.FadeIn(ABitMap: TBitMap);
421+ var
422+ SrcIntfImg, TempIntfImg: TLazIntfImage;
423+ ImgHandle,ImgMaskHandle: HBitmap;
424+ FadeStep: Integer;
425+ px, py: Integer;
426+ CurColor: TFPColor;
427+ TempBitmap: TBitmap;
428+ begin
429+ SrcIntfImg:=TLazIntfImage.Create(0,0);
430+ SrcIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
431+ TempIntfImg:=TLazIntfImage.Create(0,0);
432+ TempIntfImg.LoadFromBitmap(ABitmap.Handle,ABitmap.MaskHandle);
433+ TempBitmap:=TBitmap.Create;
434+ for FadeStep:=1 to 32 do begin
435+ for py:=0 to SrcIntfImg.Height-1 do begin
436+ for px:=0 to SrcIntfImg.Width-1 do begin
437+ CurColor:=SrcIntfImg.Colors[px,py];
438+ CurColor.Red:=(CurColor.Red*FadeStep) shr 5;
439+ CurColor.Green:=(CurColor.Green*FadeStep) shr 5;
440+ CurColor.Blue:=(CurColor.Blue*FadeStep) shr 5;
441+ TempIntfImg.Colors[px,py]:=CurColor;
442+ end;
443+ end;
444+ TempIntfImg.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
445+ TempBitmap.Handle:=ImgHandle;
446+ TempBitmap.MaskHandle:=ImgMaskHandle;
447+ Canvas.Draw(0,0,TempBitmap);
448+ end;
449+ SrcIntfImg.Free;
450+ TempIntfImg.Free;
451+ TempBitmap.Free;
452+ end;
453+```
454+
455+### Пример конкретного формата изображения
456+
457+Если вы знаете, что TBitmap использует синий 8-битный, зеленый 8-битный, красный 8-битный \[каналы\], вы можете напрямую получить доступ к байтам, что несколько быстрее:
458+
459+``` pascal
460+uses LCLType, // HBitmap type
461+ IntfGraphics, // TLazIntfImage type
462+ fpImage; // TFPColor type
463+...
464+type
465+ TRGBTripleArray = array[0..32767] of TRGBTriple;
466+ PRGBTripleArray = ^TRGBTripleArray;
467+
468+procedure TForm1.FadeIn2(aBitMap: TBitMap);
469+ var
470+ IntfImg1, IntfImg2: TLazIntfImage;
471+ ImgHandle,ImgMaskHandle: HBitmap;
472+ FadeStep: Integer;
473+ px, py: Integer;
474+ CurColor: TFPColor;
475+ TempBitmap: TBitmap;
476+ Row1, Row2: PRGBTripleArray;
477+ begin
478+ IntfImg1:=TLazIntfImage.Create(0,0);
479+ IntfImg1.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);
480+
481+ IntfImg2:=TLazIntfImage.Create(0,0);
482+ IntfImg2.LoadFromBitmap(aBitmap.Handle,aBitmap.MaskHandle);
483+
484+ TempBitmap:=TBitmap.Create;
485+
486+ //со Scanline-подобным [свойством]
487+ for FadeStep:=1 to 32 do begin
488+ for py:=0 to IntfImg1.Height-1 do begin
489+ Row1 := IntfImg1.GetDataLineStart(py); //как Delphi TBitMap.ScanLine
490+ Row2 := IntfImg2.GetDataLineStart(py); //как Delphi TBitMap.ScanLine
491+ for px:=0 to IntfImg1.Width-1 do begin
492+ Row2^[px].rgbtRed:= (FadeStep * Row1^[px].rgbtRed) shr 5;
493+ Row2^[px].rgbtGreen := (FadeStep * Row1^[px].rgbtGreen) shr 5; // Исчезновение
494+ Row2^[px].rgbtBlue := (FadeStep * Row1^[px].rgbtBlue) shr 5;
495+ end;
496+ end;
497+ IntfImg2.CreateBitmaps(ImgHandle,ImgMaskHandle,false);
498+
499+ TempBitmap.Handle:=ImgHandle;
500+ TempBitmap.MaskHandle:=ImgMaskHandle;
501+ Canvas.Draw(0,0,TempBitmap);
502+ end;
503+
504+ IntfImg1.Free;
505+ IntfImg2.Free;
506+ TempBitmap.Free;
507+ end;
508+```
509+
510+### Преобразование между TLazIntfImage и TBitmap
511+
512+Поскольку Lazarus не имеет свойства TBitmap.ScanLines, лучший способ быстрого доступа к пикселям изображения для чтения и записи - использование TLazIntfImage. TBitmap можно преобразовать в TLazIntfImage с помощью TBitmap.CreateIntfImage (), а после изменения пикселей он может быть преобразован обратно в TBitmap с помощью TBitmap.LoadFromIntfImage (); Вот пример того, как создать TLazIntfImage из TBitmap, изменить его и затем вернуться к TBitmap.
513+
514+``` pascal
515+uses
516+ ...GraphType, IntfGraphics, LCLType, LCLProc, LCLIntf ...
517+
518+procedure TForm1.Button4Click(Sender: TObject);
519+var
520+ b: TBitmap;
521+ t: TLazIntfImage;
522+begin
523+ b := TBitmap.Create;
524+ try
525+ b.LoadFromFile('test.bmp');
526+ t := b.CreateIntfImage;
527+
528+ // Читаем и/или записываем в пиксели
529+ t.Colors[10,20] := colGreen;
530+
531+ b.LoadFromIntfImage(t);
532+ finally
533+ t.Free;
534+ b.Free;
535+ end;
536+end;
537+```
538+
539+### Использование не-нативного StretchDraw из LazCanvas
540+
541+Как и в случае с TCanvas.StretchDraw, существует TLazCanvas.StretchDraw, но вам нужно указать интерполяцию, которую вы хотите использовать. Интерполяция, которая обеспечивает Windows-подобный StretchDraw с очень резким результатом (противоположным сглаживанию), может быть добавлена с помощью: `TLazCanvas.Interpolation: = TFPSharpInterpolation.Create;`
542+
543+В модуле fpcanvas доступны другие \[способы\] интерполяции.
544+
545+``` pascal
546+uses intfgraphics, lazcanvas;
547+
548+procedure TForm1.StretchDrawBitmapToBitmap(SourceBitmap, DestBitmap: TBitmap; DestWidth, DestHeight: integer);
549+var
550+ DestIntfImage, SourceIntfImage: TLazIntfImage;
551+ DestCanvas: TLazCanvas;
552+begin
553+ // Подготовка получателя
554+
555+ DestIntfImage := TLazIntfImage.Create(0, 0);
556+ DestIntfImage.LoadFromBitmap(DestBitmap.Handle, 0);
557+
558+ DestCanvas := TLazCanvas.Create(DestIntfImage);
559+
560+ //Подготовка источника
561+ SourceIntfImage := TLazIntfImage.Create(0, 0);
562+ SourceIntfImage.LoadFromBitmap(SourceBitmap.Handle, 0);
563+
564+ // Выполнение процесса растяжения [рисунка] с помощью TFPSharpInterpolation
565+ DestCanvas.Interpolation := TFPSharpInterpolation.Create;
566+ DestCanvas.StretchDraw(0, 0, DestWidth, DestHeight, SourceIntfImage);
567+
568+ // Перезагрузка изображения в TBitmap
569+ DestBitmap.LoadFromIntfImage(DestIntfImage);
570+
571+ SourceIntfImage.Free;
572+ DestCanvas.Interpolation.Free;
573+ DestCanvas.Free;
574+ DestIntfImage.Free;
575+end;
576+
577+procedure TForm1.FormPaint(Sender: TObject);
578+var
579+ Bmp, DestBitmap: TBitmap;
580+begin
581+ // Подготовка получателя
582+ DestBitmap := TBitmap.Create;
583+ DestBitmap.Width := 100;
584+ DestBitmap.Height := 100;
585+
586+ Bmp := TBitmap.Create;
587+ Bmp.Width := 10;
588+ Bmp.Height := 10;
589+ Bmp.Canvas.Pen.Color := clYellow;
590+ Bmp.Canvas.Brush.Color := clYellow;
591+ Bmp.Canvas.Rectangle(0, 0, 10, 10);
592+ StretchDrawBitmapToBitmap(Bmp, DestBitmap, 100, 100);
593+ Canvas.Draw(0, 0, Bmp);
594+ Canvas.Draw(100, 100, DestBitmap);
595+end;
596+```
597+
598+## Графика движения - как избежать мерцания
599+
600+Многие программы выводят свои данные в графический интерфейс в виде 2D-графики. Если эту графику нужно быстро менять, вы скоро столкнетесь с проблемой: быстро меняющаяся графика часто мерцает на экране. Это происходит, когда пользователи иногда видят целые изображения, а иногда только когда они частично прорисованы. Это происходит потому, что процесс отрисовки требует времени.
601+
602+Но как я могу избежать мерцания и получить лучшую скорость рисования? Конечно, вы можете работать с аппаратным ускорением, используя OpenGL, но этот подход довольно тяжел для небольших программ или старых компьютеров. Этот урок будет сосредоточен на рисовании в TCanvas. Если вам нужна помощь с OpenGL, взгляните на пример, который поставляется с Lazarus. Вы также можете использовать игровой пакет A.J. Venter'а, который предоставляет холст с двойной буферизацией и спрайт-компонент.
603+
604+Краткую и очень полезную статью по предотвращению мерцания можно найти [здесь](http://delphi.about.com/library/bluc/text/uc052102g.htm). Хотя эти методы написаны для Delphi, они хорошо работают \[и\] с Lazarus.
605+
606+Теперь мы рассмотрим варианты рисования на холсте:
607+
608+- [[Developing with Graphics/ru|Рисование на TImage]]
609+- [[Developing with Graphics/ru|Рисование в событии OnPaint формы, TPaintBox или другого элемента управления]]
610+- [[Developing with Graphics/ru|Создание пользовательского элемента управления с самостоятельной отрисовкой]]
611+
612+### Рисование на TImage
613+
614+TImage состоит из 2 частей: TGraphic, обычно TBitmap, в котором хранится постоянное изображение и визуальная область, которые перекрашиваются на каждом OnPaint. Изменение размера TImage **не** изменяет размер растрового изображения. Графическое изображение (или растровое изображение) доступно через Image1.Picture.Graphic (или Image1.Picture.Bitmap). Холст - это Image1.Picture.Bitmap.Canvas. Холст визуальной области TImage доступен только во время \[наступления события\] Image1.OnPaint через Image1.Canvas.
615+
616+**Важно**: Никогда не используйте событие OnPaint \[компонента\] Image1 для рисования в графическом/растровом изображении TImage. Графическое изображение TImage буферизуется, поэтому все, что вам нужно сделать, это нарисовать его из любого места, и изменения будут там навсегда. Однако, если вы постоянно перерисовываете, изображение будет мерцать. В этом случае вы можете попробовать другие варианты. Рисование в TImage считается медленнее, чем другие подходы.
617+
618+#### Изменение размера растрового изображения TImage
619+
620+> **Note:** Не используйте это в событии OnPaint.
621+
622+``` pascal
623+with Image1.Picture.Bitmap do begin
624+ Width:=100;
625+ Height:=120;
626+end;
627+```
628+
629+То же самое в один присест:
630+
631+``` pascal
632+with Image1.Picture.Bitmap do begin
633+ SetSize(100, 120);
634+end;
635+```
636+
637+#### Рисование на растровом изображении TImage
638+
639+> **Note:** Не используйте это в событии OnPaint.
640+
641+``` pascal
642+with Image1.Picture.Bitmap.Canvas do begin
643+ // заполняем все растровое изображение красным
644+ Brush.Color := clRed;
645+ FillRect(0, 0, Width, Height);
646+end;
647+```
648+
649+> **Note:** Внутри Image1.OnPaint'а Image1.Canvas указывает на изменчивую видимую область. За пределами Image1.OnPaint'а объект Image1.Canvas указывает на Image1.Picture.Bitmap.Canvas.
650+
651+Другой пример:
652+
653+``` pascal
654+procedure TForm1.BitBtn1Click(Sender: TObject);
655+var
656+ x, y: Integer;
657+begin
658+ // Рисуем фон
659+ MyImage.Canvas.Pen.Color := clWhite;
660+ MyImage.Canvas.Rectangle(0, 0, Image.Width, Image.Height);
661+
662+ // Рисуем квадраты
663+ MyImage.Canvas.Pen.Color := clBlack;
664+ for x := 1 to 8 do
665+ for y := 1 to 8 do
666+ MyImage.Canvas.Rectangle(Round((x - 1) * Image.Width / 8), Round((y - 1) * Image.Height / 8),
667+ Round(x * Image.Width / 8), Round(y * Image.Height / 8));
668+end;
669+```
670+
671+#### Рисование на изменчивой визуальной области TImage
672+
673+Вы можете рисовать только в этой области в событии OnPaint. \[Событие\] OnPaint в конечном итоге автоматически вызывается \[библиотекой\] LCL, когда область перерисовывается. Вы можете перерисовать область вручную с помощью Image1.Invalidate. Это вызовет OnPaint не сразу, и вы можете вызывать Invalidate столько раз, сколько захотите.
674+
675+``` pascal
676+procedure TForm.Image1Paint(Sender: TObject);
677+begin
678+ // рисуем линию
679+ Canvas.Pen.Color := clRed;
680+ Canvas.Line(0, 0, Width, Height);
681+end;
682+```
683+
684+### Рисование в событии OnPaint
685+
686+В этом случае все рисование должно выполняться по событию OnPaint формы или другого элемента управления. Рисунок не буферизуется, как в TImage, и его необходимо полностью перерисовывать при каждом вызове обработчика события OnPaint.
687+
688+``` pascal
689+procedure TForm.Form1Paint(Sender: TObject);
690+begin
691+ // рисуем линию
692+ Canvas.Pen.Color := clRed;
693+ Canvas.Line(0, 0, Width, Height);
694+end;
695+```
696+
697+### Создание пользовательского элемента управления с самостоятельной отрисовкой
698+
699+Создание пользовательского элемента управления имеет преимущество структурирования вашего кода, и вы можете использовать его повторно. Этот подход очень быстрый, но он все равно может вызывать мерцание, если вы сначала не рисуете на TBitmap'е, а лишь затем \[начнете\] рисовать на холсте. В этом случае нет необходимости использовать событие OnPaint элемента управления.
700+
701+Вот пример пользовательского элемента управления:
702+
703+``` pascal
704+uses
705+ Classes, SysUtils, Controls, Graphics, LCLType;
706+
707+type
708+ TMyDrawingControl = class(TCustomControl)
709+ public
710+ procedure EraseBackground(DC: HDC); override;
711+ procedure Paint; override;
712+ end;
713+
714+implementation
715+
716+procedure TMyDrawingControl.EraseBackground(DC: HDC);
717+begin
718+ // Раскомментируйте это, чтобы включить удаление фона по умолчанию
719+ //inherited EraseBackground(DC);
720+end;
721+
722+procedure TMyDrawingControl.Paint;
723+var
724+ x, y: Integer;
725+ Bitmap: TBitmap;
726+begin
727+ Bitmap := TBitmap.Create;
728+ try
729+ // Инициализируем размер растрового изображения
730+ Bitmap.Height := Height;
731+ Bitmap.Width := Width;
732+
733+ // Рисуем фон
734+ Bitmap.Canvas.Pen.Color := clWhite;
735+ Bitmap.Canvas.Rectangle(0, 0, Width, Height);
736+
737+ // Рисует квадраты
738+ Bitmap.Canvas.Pen.Color := clBlack;
739+ for x := 1 to 8 do
740+ for y := 1 to 8 do
741+ Bitmap.Canvas.Rectangle(Round((x - 1) * Width / 8), Round((y - 1) * Height / 8),
742+ Round(x * Width / 8), Round(y * Height / 8));
743+
744+ Canvas.Draw(0, 0, Bitmap);
745+ finally
746+ Bitmap.Free;
747+ end;
748+
749+ inherited Paint;
750+end;
751+```
752+
753+и как мы создаем это на форме:
754+
755+``` pascal
756+procedure TMyForm.FormCreate(Sender: TObject);
757+begin
758+ MyDrawingControl := TMyDrawingControl.Create(Self);
759+ MyDrawingControl.Height := 400;
760+ MyDrawingControl.Width := 500;
761+ MyDrawingControl.Top := 0;
762+ MyDrawingControl.Left := 0;
763+ MyDrawingControl.Parent := Self;
764+ MyDrawingControl.DoubleBuffered := True;
765+end;
766+```
767+
768+Он уничтожается автоматически, потому что мы используем Self в качестве владельца.
769+
770+Установка Top и Left на ноль не обязательна, так как это стандартная позиция, но это делается для того, чтобы усилить то место, где будет помещен элемент управления.
771+
772+"`MyDrawingControl.Parent := Self;`" - очень важная \[строка кода\], и вы не увидите свой элемент управления, если вы этого не сделаете.
773+
774+"`MyDrawingControl.DoubleBuffered := True;`" требуется, чтобы избежать мерцания в Windows. Это не \[оказывает никакого\] влияения на GTK.
775+
776+## Форматы изображений
777+
778+Вот таблица с правильным классом для каждого формата изображения.
779+
780+| Format | Image class | Unit |
781+|--------------------------------|-------------------------|----------|
782+| Cursor (cur) | TCursor | Graphics |
783+| Bitmap (bmp) | TBitmap | Graphics |
784+| Windows icon (ico) | TIcon | Graphics |
785+| Mac OS X icon (icns) | TicnsIcon | Graphics |
786+| Pixmap (xpm) | TPixmap | Graphics |
787+| Portable Network Graphic (png) | TPortableNetworkGraphic | Graphics |
788+| JPEG (jpg, jpeg) | TJpegImage | Graphics |
789+| PNM (pnm) | TPortableAnyMapGraphic | Graphics |
790+| Tiff (tif) | TTiffImage | Graphics |
791+
792+См. также список [[fcl-image|fcl-image supported formats]].
793+
794+### Преобразование форматов
795+
796+Иногда необходимо преобразовать один графический тип в другой. Одним из способов является преобразование графического объекта в промежуточный формат, а затем преобразование его в TBitmap. Большинство форматов могут создавать изображения из TBitmap.
797+
798+Преобразование растрового изображения в PNG и сохранение его в файл:
799+
800+``` pascal
801+procedure SaveToPng(const bmp: TBitmap; PngFileName: String);
802+var
803+ png : TPortableNetworkGraphic;
804+begin
805+ png := TPortableNetworkGraphic.Create;
806+ try
807+ png.Assign(bmp);
808+ png.SaveToFile(PngFileName);
809+ finally
810+ png.Free;
811+ end;
812+end;
813+```
814+
815+## Пиксельные форматы
816+
817+### TColor
818+
819+Внутренний пиксельный формат для TColor в LCL - это формат XXBBGGRR, который соответствует собственному формату Windows и противоположен большинству других библиотек, которые используют AARRGGBB. Часть XX используется для определения того, является ли цвет фиксированным цветом, в этом случае XX должен быть 00 или это индекс системного цвета. Нет места для альфа-канала.
820+
821+Для преобразования из отдельных каналов RGB в TColor используйте:
822+
823+``` pascal
824+RGBToColor(RedVal, GreenVal, BlueVal);
825+```
826+
827+Чтобы получить каждый канал переменной TColor, используйте функции Red, Green и Blue:
828+
829+``` pascal
830+RedVal := Red(MyColor);
831+GreenVal := Green(MyColor);
832+BlueVal := Blue(MyColor);
833+```
834+
835+### TFPColor
836+
837+TFPColor использует формат AARRGGBB, общий для большинства библиотек, но он использует 16-битную глубину каждого цветового канала, что составляет 64 бита на пиксель, что является необычным. Однако это не обязательно означает, что изображения будут занимать столько памяти. Изображения, созданные с помощью TRawImage + TLazIntfImage, могут иметь любой внутренний формат хранения, а затем при операциях рисования TFPColor преобразуется в этот внутренний формат.
838+
839+Модуль Graphics предоставляет процедуры для преобразования между TColor и TFPColor:
840+
841+``` pascal
842+function FPColorToTColorRef(const FPColor: TFPColor): TColorRef;
843+function FPColorToTColor(const FPColor: TFPColor): TColor;
844+function TColorToFPColor(const c: TColorRef): TFPColor; overload;
845+function TColorToFPColor(const c: TColor): TFPColor; overload; // не работает для системного цвета
846+```
847+
848+## Рисование с помощью fcl-image
849+
850+Вы можете рисовать изображения, которые не будут отображаться на экране без LCL, просто используя fcl-image напрямую. Например, программа, работающая на веб-сервере без X11, может выиграть от отсутствия визуальной библиотеки в качестве зависимости. FPImage (псевдоним fcl-image) - это очень общая библиотека изображений и графики, полностью написанная на паскале. Фактически, LCL использует FPImage также для всех загрузок и сохранений из/в файлы и реализует функцию рисования через вызовы наборов виджетов (winapi, gtk, carbon, ...). Fcl-изображение с другой стороны также имеет процедуры рисования.
851+
852+Для получения дополнительной информации, пожалуйста, прочитайте статью о [[fcl-image]].
853+
854+## Распространенная ошибка \[при использовании\] OnPaint
855+
856+Распространенной ошибкой, которая вызывает много ложных сообщений об багах, является вызов события Onpaint для одного объекта из другого объекта. При использовании LCL это может работать в GTK2 и Windows, но, вероятно, не удастся с Qt, Carbon и Cocoa. Обычно не нужно вызывать `Invalidate`. Тем не менее, иногда это может быть необходимо в процедуре Button1Click,
857+
858+вот так - плохо:
859+
860+``` pascal
861+procedure TForm1.Button1Click(Sender: TObject);
862+begin
863+ Shape1Paint(Self); // Вызов события Shape1Onpaint
864+ Shape1.Invalidate; // Вызываем фактическую перерисовку
865+
866+ ... больше кода для Button1 ...
867+end;
868+```
869+
870+Вот так - хорошо:
871+
872+``` pascal
873+procedure TForm1.Button1Click(Sender: TObject);
874+begin
875+ ... код для Button1 ...
876+ <Устанавливаем некоторые условия>;
877+ // Shape1.Invalidate; // Может быть необходимо в некоторых случаях
878+end;
879+
880+// Shape1Paint должен быть прописан в событии OnPaint объекта shape!
881+procedure TForm1.Shape1Paint(Sender: TObject);
882+var
883+ Myrect: TRect;
884+begin
885+ if <некоторое условие> then
886+ with Shape1.Canvas do
887+ begin
888+ ... куча кода ...
889+ end;
890+end;
891+```
892+
893+## Несколько полезных примеров
894+
895+### Пример 1: Рисование на загруженном JPEG с \[помощью\] TImage
896+
897+Добавьте процедуру LoadAndDraw в секцию public вашей формы и вставьте следующий код в раздел implementation:
898+
899+``` pascal
900+procedure TForm1.LoadAndDraw(const sFileName: String);
901+var
902+ jpg: TPicture;
903+begin
904+ jpg := TPicture.Create;
905+ try
906+ jpg.LoadFromFile(sFileName);
907+
908+ Image.Picture.Bitmap.SetSize(jpg.Width, jpg.Height);
909+ Image.Picture.Bitmap.Canvas.Draw(0, 0, jpg.Bitmap);
910+
911+ Image.Picture.Bitmap.Canvas.Pen.Color := clRed;
912+ Image.Picture.Bitmap.Canvas.Line(0, 0, 140, 140);
913+ finally
914+ jpg.Free;
915+ end;
916+end;
917+```
918+
919+### Пример 2: Рисование на элементах управления формы
920+
921+1\) Создайте новый проект New project -\> Application, добавьте в раздел uses следующие модули, если необходимо: Types, Controls, Graphics.
922+
923+2\) Положите на форму Button1, GroupBox1 и RadioGroup1
924+
925+3\) Положите на GroupBox1 еще одну кнопку - Button2
926+
927+4\) Ваша TForm1.Create должна выглядеть так:
928+
929+``` pascal
930+procedure TForm1.FormCreate(Sender: TObject);
931+var
932+ i: Integer;
933+begin
934+ for i := 0 to Self.ControlCount - 1 do
935+ RadioGroup1.Items.AddObject(Controls[i].Name, Controls[i]);
936+
937+ RadioGroup1.Items.AddObject(Button2.Name,Button2);
938+end;
939+```
940+
941+5\) Для RadioGroup1 создайте обработчик события OnSelectionChanged:
942+
943+``` pascal
944+procedure TForm1.RadioGroup1SelectionChanged(Sender: TObject);
945+begin
946+ Self.Repaint;
947+end;
948+```
949+
950+6\) Добавьте в секцию public вашей формы процедуру HighlightControl:
951+
952+``` pascal
953+procedure TForm1.HighlightControl(AControl: TControl);
954+var
955+ R: Types.TRect;
956+ aCC: TControlCanvas;
957+begin
958+ R := AControl.BoundsRect;
959+ InflateRect(R, 2, 2); // сделайте прямоугольник немного больше, чем элемент управления
960+ aCC := TControlCanvas.Create;
961+ aCC.Control := AControl.Parent;
962+ aCC.Pen.Color := clGreen;
963+ aCC.Pen.Width := 5;
964+ aCC.Pen.Style := psSolid;
965+ aCC.Brush.Style := bsClear;
966+ aCC.Rectangle(R);
967+ aCC.free;
968+end;
969+```
970+
971+## См. также
972+
973+- [[Fast direct pixel access]]
974+
975+---
976+*From the Free Pascal wiki, page "Developing with Graphics/ru", as it stood on 2020-02-13 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history