Тема 8. Проектирование графических приложений

1 Построение графических изображений - компонент Image

2. Канва - холст для рисования

3. Режимы рисования

4. События OnPaint

Презентация к лекции

 

1 Построение графических изображений - компонент Image

 

Часто возникает потребность вставить в приложение графические объекты. Это может быть графическая заставка, являющаяся логотипом приложения или графические объекты при разработке приложения, работающего с базой данных. В первом случае можно использовать компонент Image (Additional), во втором -DBImage, связанный с данными (Data Controls).

Свойства компонента Image.

Его свойство, которое может содержать картинку - Picture

Свойство AutoSize позволяет подгонять компонент под размер рисунка. Если установить в true, то размер компонента Image будет автоматически подгоняться под размер помещенной в него картинки, а если false, то изображение может не поместиться в компонент или, наоборот, площадь компонента может оказаться много больше площади изображения.

Свойство - Stretch позволяет подгонять рисунок под размер компонента. Для этого нужно установить AutoSize = false, растянуть или сжать размер компонента Image и установить Stretch в true. Видим, что рисунок займет всю площадь компонента, но поскольку вряд ли реально установить размеры Image точно пропорциональными размеру рисунка, то изображение исказится. Устанавливать Stretch в true может иметь смысл только для каких-то узоров, но не для картинок. Свойство Stretch не действует на изображения пиктограмм, которые не могут изменять своих размеров.

Свойство - Center, установленное в true, центрирует изображение на площади Image, если размер компонента больше размера рисунка.

Свойство - Transparent (прозрачность) = true, то изображение в Image становится прозрачным. Это можно использовать для наложения изображений друг на друга. Поместите на форму второй компонент Image и загрузите в него какую-нибудь мало заполненную, контурную картинку. Передвиньте Image так, чтобы они перекрывали друг друга, и в верхнем компоненте установите Transparent равным true. Вы увидите, что верхняя картинка перестала заслонять нижнюю. Одно из возможных применений этого свойства - наложение на картинку надписей, выполненных в виде битовой матрицы.

Свойство Transparent действует только на битовые матрицы.

Рассмотрим пример. Поместим на форму компонент Image.

Нажмите на кнопку с многоточием свойства Picture или сделайте двойной щелчок на Image, откроется окно Picture Editor, позволяющее загрузить в свойство Picture графический файл (кнопка Load), а также сохранить открытый файл под новым именем или в новой папке. Щелкните на Load, чтобы загрузить графический файл. Откроется окно Load Picture. По мере перемещения курсора в списке по графическим файлам в правом окне отображаются содержащиеся в них изображения. В окне загрузки графического файла можно не только просмотреть изображение, хранящееся в выбираемом файле, но и увидеть размер изображения.

После загрузки файла щелкните на ОК и в компоненте Image отобразится выбранный объект. Можно запустить приложение.

Когда в процессе проектирования загрузили картинку из файла в компонент Image, он не просто отображает ее, но и сохраняет в приложении. Это дает возможность поставлять приложение без отдельного графического файла. В Image можно загружать и внешние графические файлы в процессе выполнения приложения.

Теперь изменим приложение так, чтобы пользователь смог просматривать и загружать любые графические файлы. Для этого поместим на форму компонент ОреnPictureDialog и кнопку Button и в обработчике щелчка на кнопке введем код:

if (OpenPictureDialog1->Execute())

Image1->Picture->LoadFromFile(OpenPictureDialog1->FileName);

Этот оператор загружает в свойство Picture компонента Image1 файл, выбранный в диалоге пользователем. Щелкая на кнопке можете выбрать любой графический файл и загрузить его в компонент Image1.

В таком приложении есть один недостаток - изображения могут быть разных размеров и их положение на форме или будет несимметричным, или они не будут помещаться в окне. Это легко изменить, заставив форму автоматически настраиваться на размеры изображения. Для этого надо установить в компоненте Image1 свойство AutoSize равным true, а приведенный ранее оператор изменить следующим образом:

if (OpenPictureDialog1->Execute () )

{

Image1->Picture->LoadFromFile(OpenPictureDialog1->FileName);

Form1->ClientHeight = Image1->Height + 10;

Image1->Top=Form1->ClientRect.Top + (Form1->ClientHeight - Image1->Height)/2;

Form1->ClientWidth = Image1->Width + 10;

Image1->Left=Form1->ClientRect.Left + (Form1->ClientWidth - Image1->Width)/2;

}

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

Запустите приложение, и увидите, что при различных размерах изображения приложение выглядит отлично.

 

2. Канва - холст для рисования

2.1 Канва и пикселы

Многие компоненты в C++Builder (формы, графические компоненты Image, PaintBox, Bitmap и др.) имеют свойство Canvas (канва, холст), Канва – это область компонента, на которой можно рисовать или отображать готовые изображения. Канва содержит свойства и методы, существенно упрощающие графику C++Builder. Все сложные взаимодействия с системой спрятаны для пользователя.

Каждая точка канвы имеет координаты X и Y. Система координат канвы имеет началом левый верхний угол канвы. Координата X возрастает при перемещении слева направо, а координата Y - при перемещении сверху вниз.

Координаты измеряются в пикселах. Пиксел - это наименьший элемент поверхности рисунка, с которым можно манипулировать. Важнейшее свойство пиксела - его цвет. Для описания цвета используется тип TCoIor.

Для графики иногда этих предопределенных констант не хватает, т.е. могут понадобиться такие оттенки, которых нет в стандартных палитрах. В этом случае можно задавать цвет 4-байтовым шестнадцатеричным числом. Например, значение $00FF0000 соответствует чистому синему цвету, $0000FF00 - чистому зеленому, $000000FF - чистому красному. $00000000 - черный цвет, $00FFFFFF - белый.

 

2.2 Рисование по пикселам

Рисовать на канве можно разными способами.

Рассмотрим 1 первый вариант - рисование по пикселам. Для этого используется свойство канвы Pixels - представляет собой двумерный массив Canvas->Pixels[int X][int Y], который отвечает за цвета канвы. Например, Canvas->Pixels[10][20] соответствует цвету пиксела, 10-го слева и 20-го сверху. С массивом пикселов можно обращаться как с любым свойством: изменять цвет, задавая пикселу новое значение, или определять его цвет по хранящемуся в нем значению. Например, Canvas->Pixels[10][20] = clBlack - это задание пикселу черного цвета.

Попробуем нарисовать график некоторой функции F(X) на канве компонента Image1, если известен диапазон ее изменения Ymax и Ymin и диапазон изменения аргумента Xmin и Хmах. Это можно сделать такой процедурой:

float x,y;      // координаты функции

int Px, Py;    // координаты пикселов

for (Рx = 0; Рx <= Image1->Width; Px++)

{ //х - координата, соответствующая пикселу с координатой Рх

x = Xmin + Рx * (Xmax - Xmin)/ Image1->Width;

y = F(x) ;

//Pу - координата пиксела, соответствующая координате у

Py = Image1->Height - (y - Ymin)*Image1->Height/(Ymax-Ymin); //Устанавливается черный цвет выбранного пиксела

Image1->Canvas->Pixels[Px] [Py] = clBlack;

}

В этом коде вводятся переменные x и y, являющиеся значениями аргумента и функции, а также переменные Рx и Py, являющиеся координатами пикселов, соответствующими x и y. Сама процедура состоит из цикла по всем значениям горизонтальной координаты пикселов Рx компонента Image1. Сначала выбранное значение Рx пересчитывается в соответствующее значение x. Затем производится вызов функции и определяется ее значение y. Это значение пересчитывается в вертикальную координату пиксела Py. И в заключение цвет пиксела с координатами (Рx, Py) устанавливается черным.

Рассмотрим пример приложения построения графика функции sin(x), для которой Xmin=0, Хmах=4π, Ymin=-1, Ymax=1.

Поместим на форму компонент Image и кнопку с надписью «Нарисовать», в обработчик события OnClick запишем следующий код:

const double Pi =3.14159

float x, y;                      // координаты функции sin(x)

int Px, Py;                                          // координаты пикселов

for (Рx = 0; Рx <- Image1->Width; Px++)

{ //x - координата, соответствующая пикселу с координатой Рx

x = Рx * 4 * Pi / Image1->Width;

y = sin(x) ; //Py - координата пиксела, соответствующая координате y

Py = Image1->Height - (y+1) * Image1->Height / 2;

Image1->Canvas->Pixels[Px][Py] = clBlack;

}

 

2.3 Рисование с помощью пера Реn

Рассмотрим второй вариант рисования на канве. У канвы есть свойство Реn - перо. Это объект, в свою очередь имеет ряд свойств. Свойство Color - цвет, которым наносится рисунок.

Свойство - Width (ширина линии) - задается в пикселах. По умолчанию равна 1.

Свойство Style определяет вид линии, может принимать следующие значения:

 

psSolid

Сплошная линия.

psDash

Штриховая линия.

psDot

Пунктирная линия.

psDashDot

Штрихпунктирная линия.

psDashDotDot

Линия, чередующая штрих и два пунктира.

psClear

Отсутствие линии.

psInsideFrame

Сплошная линия, но при Width > 1 допускающая цвета, отличные от палитры Windows.

 

Все стили со штрихами и пунктирами доступны только при Width=1. В противном случае линии этих стилей рисуются как сплошные.

Стиль psInsideFrame - единственный, который допускает произвольные цвета. Цвет линии при остальных стилях округляется до ближайшего из палитры Windows.

Свойство канвы PenPos определяет в координатах канвы текущую позицию пера.

Перемещение пера без прорисовки линии, т.е. изменение PenPos, производится методом канвы MoveTo(x,y) (где (x,y) - координаты точки, в которую перемещается перо).

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

Рассмотрим рисование пером графика синуса. Добавим на форму еще один компонент Image. Размеры обоих компонентов Image должны быть абсолютно одинаковы. Сделать размеры компонентов абсолютно одинаковыми легко, выделив их оба и воспользовавшись командой всплывающего меню Size.

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

В итоге, код должен иметь вид:

const double Pi = 3.14159;

float x, y;              // координаты функции sin(x)

int Px, Py;                              // координаты пикселов

Image2->Canvas->MoveTo(0,Image2->Height / 2) ;

for (Рx = 0; Рx < Image1->Width; Px++)

{

x = Рx * 4 * Pi / Image1->Width;

y = sin(x) ;

Py = Image1->Height - (y+l) * Imagel->Height /2;

Image1->Canvas->Pixels[Px][Py] = clBlack;

Image2->Canvas->LineTo(Px,Py);     // Проводится линия на втором графике

}

Для простоты воспользовались тем, что оба графика у нас абсолютно одинакового размера и, следовательно, пересчет координат достаточно провести для одного из них, а потом воспользоваться этими координатами для рисования обоих графиков.

Легко видеть, что качество двух одинаковых графиков сильно различается. В левом на крутых участках сплошной линии нет - она распадается на - пикселы. А правый график весь сплошной. Это показывает, что при прочих равных условиях рисовать лучше пером.

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

Y = 2 * sin(x) ;

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

Перо может рисовать не только прямые линии, но и фигуры. Ниже перечислены некоторые из методов канвы, использующие перо для рисования фигур:

Arc

Рисует дугу окружности или эллипса.

Chord

Рисует замкнутую фигуру, ограниченную дугой окружности или эллипса и хордой.

Ellipse

Рисует окружность или эллипс.

Pie

Рисует сектор окружности или эллипса.

Polygon

Рисует замкнутую фигуру с кусочно-линейной границей.

Polyline

Рисует кусочно-линейную кривую.

Rectangle

Рисует прямоугольник.

RoundRect

Рисует прямоугольник со скругленными углами.

 

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

Image1->Canvas->Font->Style << fsBold;

Image1->Canvas->Arc(10,10,90,90, 90, 50,10,50) ;

Image1->Canvas->TextOut (40, 60, "Arc") ;

Image1->Canvas->Chord(110,10,190,90,190,50,110, 50) ;

Image1->Canvas->TextOut(135,60,"Chord");

Image1->Canvas->Ellipse(210,10,290, 50) ;

Image1->Canvas->TextOut(230,60,"Ellipse") ;

Image1->Canvas->Pie(310,10,390,90,390,30,310,30);

Image1->Canvas->TextOut(340,60,"Pie") ;

TPoint points[5];

points[0] = Point(30,150) ;

points[1] = Point(40,130);

points[2] = Point(50,140);

points[3] = Point(60,130) ;

points[4] = Point(70,150) ;

Image1->Canvas->Polygon(points,4);

Image1->Canvas->TextOut(30,170,"Polygon");

points[0].x += 100;

points[1].x += 100;

points[2].x += 100;

points[3].x += 100;

points[4].x += 100;

Image1->Canvas->Polyline(points, 4) ;

Image1->Canvas->TextOut(130,170,"Polyline");

Image1->Canvas->Rectangle(230,120,280,160) ;

Image1->Canvas->TextOut(230,170,"Rectangle") ;

Image1->Canvas->RoundRect(330,120,380,160,20,20) ;

Image1->Canvas->TextOut(325,170,"RoundRect") ;

Для вывода текста на канву использован метод TextOut, синтаксис которого:

void _fastcall TextOut(int X, int Y, AnsiString Text);

 

Свойство канвы Brush - кисть определяет фон и заполнение замкнутых фигур на канве. Brush - это объект, имеющий в свою очередь ряд свойств.

Свойство Color определяет цвет заполнения.

Свойство Style определяет шаблон заполнения (штриховку).

Свойство Bitmap является указателем на объект типа TBitmap и определяющее нестандартное заполнение заданным шаблоном. Шаблон задается битовой матрицей размером 8x8. Если для кисти задан шаблон Bitmap, то заполнение производится именно этим шаблоном, независимо от значения свойства Style.

Шаблон Bitmap может создаваться в процессе выполнения приложения или, например, загружаться из файла.

Метод канвы FillRect (TRect SRect) заполняет заданным стилем или шаблоном прямоугольную область, заданную параметром Rect. Для его задания проще всего использовать функцию Rect(x1,y1,x2,y2), возвращающую структуру Rect с координатами углов, заданных параметрами (x1, y1) и (x2, y2).

Функцию FillRect удобно, в частности, использовать для стирания изображения. Например, оператор

Image1->Canvas->FillRect(Rect(0,0, Image1->Width,Image1->Height)); очищает всю площадь канвы компонента Image1.

Кисть участвует в заполнении фигур не только с помощью этой функции. Все перечисленные ранее методы рисования замкнутых фигур тоже заполняют их с помощью кисти. Это относится к методам Chord, Ellipse, Pie, Polygon и др.

Следующий метод кисти - это метод FloodFill, который заполняет замкнутую область на канве. Он определен следующим образом:

FloodFill(int x, int y, TColor Color, TFillstyle FillStyle);

Точка с координатами x и y является произвольной внутренней точкой заполняемой области, которая может иметь произвольную форму. Граница этой области определяется сочетанием параметров Color и FillStyle. Параметр Color указывает цвет, который используется при определении границы заполняемой области, а параметр FillStyle определяет, как именно по этому цвету определяется граница. Параметр FillStyle может принимать одно из двух следующих значений: fsSurface или fsBorder. Если FillStyle = fsSurface, то заполняется область, окрашенная цветом Color, а на других цветах метод останавливается. Если FillStyle = fsBorder, то наоборот, заполняется область окрашенная любыми цветами, не равными Color, a на цвете Color метод останавливается.

Для определения области закрашивания можно использовать координаты и цвет одного из пикселов, расположенных внутри области (если FillStyle = fsSurface) или снаружи ее (если FillStyle = fsBorder).

Следующий метод канвы, связанный с кистью -  FrameRect. Он рисует на канве текущей кистью прямоугольную рамку, не закрашивая ее. Синтаксис метода FrameRect:

FrameRect(TRect &Rect);

Параметр Rect определяет позицию и размеры прямоугольной рамки. Толщина рамки - 1 пиксел. Область внутри рамки кистью не заполняется. Метод FrameRect отличается от рассмотренного ранее метода Rectangle тем, что рамка рисуется цветом кисти (в методе Rectangle - цветом пера) и область не закрашивается (в методе Rectangle закрашивается).

 

3. Режимы рисования

3.1 Режимы пера

У пера Реn имеется еще одно свойство - Mode (режим). По умолчанию значение Mode = pmCopy. Это означает, что линии проводятся цветом, заданным в свойстве Color. Но возможны и другие режимы, в которых учитывается не только цвет Color, но и цвет соответствующих пикселов фона. Наиболее интересным из этих режимов является режим pmNotХоr - сложение с фоном по инверсному исключающему ИЛИ. Операция инверсного исключающего ИЛИ анализирует по битам два своих операнда. Результирующий бит равен "0", если соответствующие биты двух операндов не равны друг другу, а при равенстве битов операндов результирующий бит равен "1".

Как известно, каждый пиксел хранит цвет как набор битов. Пусть, например, фоновый пиксел имеет значение 0110011, а цвет пера установлен в 1111000. Применение, операции pmNotXor к этим двум числам даст цвет со значением 0110100. Этот цвет перо задаст данному пикселу. А теперь посмотрим, что получится, если перо повторно пройдет по тому же пикселу. В этом случае опять будет выполнена операция pmNotXor по отношению к цвету пера 1111000 и текущему цвету пиксела, который стал равен 0110100. Применение pmNotXor к этим числам даст в результате 0110011, т.е. первоначальный цвет пиксела.

Это значит, что если нарисовать на фоне какую-то фигуру один раз, а затем нарисовать ту же фигуру повторно, то нарисованная фигура исчезнет и каждый пиксел вернется к своему первоначальному цвету. Эту особенность режима pmNotXor, свойственную также режиму ртХог - сложение с фоном по исключающему ИЛИ, можно использовать для создания простенькой анимации. Достаточно нарисовать нечто, затем стереть нарисованное, перерисовать немного измененным - и рисунок будет представляться ожившим.

3.2 Режимы копирования и рисования канвы

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

CopyRect(TRect &Dest, TCanvas * Canvas, TRect &Source);

Параметр Dest определяет прямоугольную область канвы, в которую производится копирование. Параметр Canvas указывает источник, из которого копируется изображение. Это может быть канва любого компонента: типа TImage, типа TBitmap и др. В частном случае источником может быть и канва того же компонента, в который производится копирование.

Параметр Source определяет прямоугольную область в источнике изображения, которая копируется в область Dest. Обе прямоугольные области и в источнике, и в приемнике имеют тип TRect.

Копирование - это не просто перенос изображения, а сложное взаимодействие копируемого изображения и того, которое было до этого в области, куда производится копирование. Характер этого взаимодействия определяется параметром CopyMode (режим копирования) той канвы, в которую производится копирование. По умолчанию значение CopyMode равно cmSrcCopy. Это единственный режим, при котором производится действительное копирование: изображение в Dest стирается и заменяется скопированным.

Есть два значения - cmWhiteness и cmBlackness, при которых собственно никакое копирование не производится: просто область закрашивается соответственно белым или черным цветом. А все остальные режимы определяют сложное взаимодействие копируемого изображения с тем, которое было в Dest.

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

Интересен также режим cmSrcAnd. Если копируемое изображение представляет собой контурный черный рисунок на белом фоне, то этот рисунок наложится на прежнее изображение канвы, а белый фон будет прозрачным, так что под ним будет видно первоначальное изображение.

В режиме cmSrcPaint аналогичный эффект будет, если копируемое изображение представляет собой белый контурный рисунок на черном фоне.

Приведем примеры копирования в различных режимах. Операторы

Image1->Canvas->CopyMode = cmSrcCopy;

Image1->Canvas->CopyRect(Rect(0,0,200,200),Image2->Canvas, Rect (0,0,200,200));

обеспечивают копирование изображения фрагмента канвы компонента Image2 в указанную область канвы компонента Image1. Изображение, которое ранее было на канве компонента Image1 в пределах области с координатами углов (0, 0) и (200, 200), просто заменяется новым. Операторы

Image1->Canvas->CopyMode = cmSrcInvert;

Image1->Canvas->CopyRect(Rect(0,0,200,200),Image2->Canvas, Rect(0,0,200,200)) ;

Image1->Canvas->CopyRect (Rect (0, 0, 200, 200) ,lmage2->Canvas,Rect (0,0,200,200));

обеспечивают копирование изображения фрагмента канвы компонента Image2 в указанную область канвы компонента Image1 в режиме cmSrcInvert. После выполнения функции CopyRect в первый раз изображения в компонентах Image1 и Image2 налагаются друг на друга, а в результате выполнения функции CopyRect во второй раз исходное изображение на канве компонента Image1 восстанавливается. Операторы

Image1->Canvas->CopyMode = cmWhiteness;

Image1->Canvas->CopyRect(Rect(0,0,200,200),Image2->Canvas, Rect(0,0,200,200)) ;

просто очищают указанную область канвы компонента Image1, закрашивая ее белым цветом. При этом изображение в компоненте Image2 никак не участвует в операциях копирования.

Помимо методов копирования свойство CopyMode используется также методом рисования на канве Draw. Его описание:

void _fastcall Draw(int x, int y, TGraphic* Graphic);

Метод Draw рисует изображение, содержащееся в объекте, указанном параметром Graphic, сохраняя исходный размер изображения в его источнике и перенося изображение в область канвы объекта, верхний левый угол которой определяется параметрами x и y. Источник изображения может быть битовой матрицей, пиктограммой или метафайлом. Если источник - объект типа TBitmap, то перенос изображения производится в режиме, установленном свойством канвы CopyMode.

Например, оператор

Image1->Canvas->Draw(10,10,Bitmap1);

рисует на канве компонента Image1 изображение из компонента Bitmap1 в область с координатами левого верхнего угла (10, 10).

Метод рисования – DrawFocusRect - рисует изображение прямоугольника в виде, используемом для отображения рамки фокуса, операцией XOR. Функция DrawFocusRect объявлена следующим образом:

void      _fastcall DrawFocusRect(const Windows::TRect *Rect);

где Rect - прямоугольная область. Поскольку при рисовании используется операция XOR, то повторный вызов этого метода с тем же значением Rect удаляет изображение прямоугольника.

 

4. События OnPaint

Канву имеют многие компоненты, например, формы. Все, что ранее рисовали на канве компонента Image, можно рисовать и на форме. Кроме того, имеется специальный компонент PaintBox, имеющий канву и позволяющий рисовать на ней.

При рисовании на канве формы или PaintBox надо учитывать некоторые особенности. Сначала выясним, о чем идет речь.

Откройте новое приложение, перенесите на него диалог OpenPictureDialog, кнопку и в обработчик щелчка на ней вставьте операторы:

void _fastcall TForm1::ButtonlClick(TObject *Sender)

{

if (OpenPictureDialog1->Execute ())

 {

 Graphics::TBitmap *Bitmap = new Graphics::TBitmap;

try

{

 Bitmap->LoadFromFile(OpenPictureDialog1->FileName);

Form1->Canvas->Draw(0,0,Bitmap);

}

    finally

{

Bitmap->Free();

    }      }     }

Эти операторы обеспечивают загрузку выбранного пользователем графического файла и отображение изображения непосредственно на канве формы. Запустите приложение, выберите файл. А теперь, не закрывая приложение, вернитесь в C++Builder и, ничего там не делая, опять перейдите в приложение. Если окно Редактора Кода, выступившее на первый план при переходе в C++Builder, целиком перекрыло окно приложения, то, вернувшись в него увидите, что картинка в окне исчезла. Если же опять загрузите в него картинку и сдвинете окно приложения так, чтобы окно Редактора Кода не могло целиком его закрыть, то, повторив эксперимент с переходом в C++Builder и обратно увидите результат.

Если окно какого-то другого приложения перекрывает на время окно приложения, то изображение, нарисованное на канве формы, портится. В компоненте Image этого не происходило, поскольку в классе TImage уже предусмотрены все необходимые действия, осуществляющие перерисовку испорченного изображения. А при рисовании на канве формы или других оконных компонентов эти меры должен принимать разработчик приложения.

Если окно было перекрыто и изображение испортилось, операционная система сообщает приложению, что в окружении что-то изменилось и что приложение должно предпринять соответствующие действия. Как только требуется обновление окна, для него генерируется событие OnPaint. В обработчике этого события нужно перерисовать изображение.

Перерисовка может производиться разными способами в зависимости от приложения. В примере можно было бы вынести объявление указателя Bitmap за пределы приведенной выше процедуры, т.е. сделать эту переменную глобальной:

Graphics::TBitmap *Bitmap;

Тогда приведенная выше процедура загрузки файла примет вид:

if (OpenPictureDialog1->Execute())

{

Bitmap = new Graphics::TBitmap;

Bitmap->LoadFromFile(OpenPictureDialog1->FileName);

Canvas->Draw(0,0,Bitmap);

}

Оператор Bitmap->Free() нужно перенести в обработчик события формы OnDestroy. Тогда в течение всего времени выполнения приложения будете иметь копию картинки в компоненте Bitmap и достаточно ввести в обработчик события OnPaint формы всего один оператор:

if (Bitmap != NULL)

Canvas->Draw(0,0,Bitmap);

Оператор if используется, чтобы избежать ошибочного обращения к Bitmap, пока графический файл еще не загружался и объект Bitmap не создан.

Теперь изображение на форме не портится при любых перекрытиях окон.

Написанный обработчик перерисовывает все изображение. При больших изображениях это может существенно замедлять перерисовку и вызывать неприятные зрительные эффекты. Перерисовку можно существенно ускорить, если перерисовывать только испорченную область канвы. У канвы есть свойство ClipRect типа TRect, которое в момент обработки события OnPaint указывает область, которая подлежит перерисовке. Поэтому более экономным будет обработчик:

if (Bitmap != NULL)

Canvas->CopyRect (Canvas->ClipRect, Bitmap->Canvas, Canvas->ClipRect);

Он перерисовывает только область ClipRect, которая испорчена.