22.4. Работа с графическими образами на диске

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

Мы уже рассматривали, каким образом можно получить копию изображения с экрана на принтере (см. разд. 19.6.1). Теперь

- 521 -

попробуем записать изображение с экрана на диск. Казалось бы просто — достаточно немного изменить процедуру CopyToPrint. Однако при таком подходе место на диске будет расходоваться неэкономно: в каждом байте старшие четыре (как минимум) бита не используются. Исключение составляют все режимы с количеством цветов 256 и более (VGA, MCGA, IBM8514). Чтобы не заботиться об этом, можно воспользоваться функцией ImageSize (см. разд. 19.6.1) — она возвращает размер картинки в байтах, уже учтя все нюансы ее расположения в ОЗУВ. Воспользовавшись затем процедурой GetImage, можно сохранить изображение на диске (рис. 22.11).

| USES Graph;

| {$I initgraf.pas} {процедура инициализации (см. гл. 19) }

| { Процедура записи на диск картинки с экрана. }

| { Максимальный размер 640x200 при 16 цветах. }

| PROCEDURE SaveScreen( X1, Y1 { координаты картинки }

| X2, Y2 : Word;

| FileName : String ); { имя файла картинки }

| VAR

| PicFile : File; { бестиповый файл }

| size :Word; { размер файла }

| dataptr : Pointer; { указатель на буфер }

| BEGIN

| Size := ImageSize(X1,Y1, X2, Y2); { размер картинки }

| GetMem( dataptr, size ); { выделение памяти }

| GetImage(X1,Y1,X2,Y2,dataptr^); { картинку – в буфер }

| Assign( PicFile, FileName ); { Открытие файла для }

| Rewrite( PicFile, size ); { записи картинки. }

| BlockWrite(PicFile,dataptr^,1); { запись картинки }

| Close( PicFile ); { закрытие файла }

| FreeMem( dataptr, size ) { освобождение кучи }

| END;

| BEGIN { Пример вызова процедуры }

| GrInit;

| SetFillStyle(1,15); Bar( 0, 0, GetMaxX, GetMaxY );

| SetFillStyle(2, 2); Bar(40, 40, GetMaxX-40, GetMaxY-40 );

| SetFillStyle(3, 3); Bar(120, 120, GetMaxX-120, GetMaxY-120);

| SetFillStyle(4, 4);Bar(240, 180, GetMaxX-240, GetMaxY-180);

| ReadLn;

| SaveScreen(70,70, GetMaxX-70, GetMaxY-70, 'graph.scr');

| CloseGraph

| END.

Рис. 22.11

- 522 -

А с помощью процедуры PutImage можно восстановить это изображение (рис. 22.12).

| USES Graph;

| {$I initgraf.pas} {процедура инициализации (см. гл. 19) }

| {Процедура вывода на экран картинки, записанной на диск. }

| {Максимальный размер - экран в режиме 640x200, 16 цветов. }

| PROCEDURE LoadScreen(X,Y:Word; { координаты левого верх- }

| { него угла картинки }

| FileName : String; { имя файла картинки }

| Put : Word ); { режим вывода на экран }

| VAR

| PicFile :File; { бестиповый файл }

| size : Word; { размер файла в байтах }

| dataptr : Pointer; { указатель на начало }

| BEGIN

| Assign( PicFile, FileName ); { Открытие файла для }

| Reset( PicFile, 1 ); { чтения по одному байту. }

| size := FileSize( PicFile ); { размер файла в байтах }

| Reset( PicFile, size ); { смена буфера файла }

| GetMem( dataptr, size ); { выделение памяти }

| BlockRead(PicFile,dataptr^,1); { чтение картинки }

| PutImage(X,Y,dataptr^,put); { вывод картинки на экран }

| Close( PicFile ); { закрытие файла }

| FreeMem( dataptr, size ) { освобождение памяти }

| END;

| BEGIN { Пример вызовов процедуры }

| GrInit;

| LoadScreen( 0, 0, 'graph.scr', XORPut);

| LoadScreen(20, 20, 'graph.scr', XORPut);

| LoadScreen(40, 40, 'graph.scr', XORPut);

| LoadScreen(60, 60, 'graph.scr', XORPut } ;

| LoadScreen(80, 80, 'graph.scr', XORPut);

| ReadLn;

| CloseGraph

| END.

Рис. 22.12

При таком способе сохранения изображений существует ограничение на их размер. Это связано с тем, что функция ImageSize возвращает результат типа Word. Таким образом, картинка не должна занимать в памяти больше 65520 байт. Для адаптера EGA,

- 523 -

например, полностью весь экран можно записать на диск максимум в режиме 640x200, 16 цветов — это займет 64004 байта. Кроме того, если размеры картинки таковы, что она полностью не сможет поместиться на экран, то выводиться на него при чтении с диска она не будет.

У такого способа записи изображений есть одно удобное свойство: в каком бы графическом режиме картинка не была записана, она всегда сможет быть прочитана и показана в любом другом графическом режиме.

Другой способ сохранения и чтения картинок основан на знании устройства и работы графических адаптеров: каждая цветовая плоскость сохраняется в отдельном файле. При таком способе работы ограничений на размер картинки практически нет (рис. 22.13).

| USES Graph;

| {$I initgraf.pas} {процедура инициализации (см. гл. 19) }

| TYPE

| g_plan=Array [1..38400] of Byte; { массив-плоскость }

| {Процедура сохранения изображения всего экрана на диске.

| Для каждой битовой плоскости создается отдельный файл.

| Параметр file_name - имя файла без расширения. }

| PROCEDURE SaveBitPlanes( file name : String );

| VAR

| scr_buf:Pointer ; { указатель на буфер данных }

| scr_arr:g_plan absolute $A000:$0; { память адаптера }

| plen,mx,my:LongInt; { размер плоскости }

| { Вложенная процедура записи плоскости на диск }

| PROCEDURE WriteBlk( name : String );

| VAR

| image_file : File;

| BEGIN

| Assign( image_file, name );

| Rewrite( image_fite, plen );

| BlockWrite( image_file, scr_buf^, 1 );

| Close( image file )

| END;

| BEGIN

| mx := GetMaxX+1; my := GetMaxY+1; { размеры плоскости }

Рис. 22.13

- 524 -

| plen := mx*my div 8;

| { получение длины буфера }

| GetMem( scr_buf, plen );

| { память для буфера }

| Port[$3CE]:=4; Port[$3CF]:=0;

| { чтение плоскости Blue }

| Move(scr_arr,scr_buf^,plen); { копирование ее в буфер }

| WriteBlk(file_name+'.blu');

| { запись буфера на диск }

| Port[$3CE]:=4; Port[$3CF]:=1; { чтение плоскости Green }

| Move(scr_arr,scr_buf^,plen); { копирование ее в буфер }

| WriteBlk(file_name+'.grn'}; { запись буфера на диск }

| Port[$3CE]:=4; Port[$3CF]:=2; { чтение плоскости Red }

| Move(scr_arr,scr_buf^,plen); { копирование ее в буфер }

| WriteBlk(file name+'. red');

| { запись буфера на диск }

| Port[$3CE]:=4; Port[$3CF]:=3; { плоскость яркости }

| Move(scr_arr,scr_buf^,plen); { копирование ее в буфер }

| WriteBlk(file_name+'.int'); { запись буфера на диск }

| FreeMem( scr_buf, plen ); { освобождение памяти }

| Port[$3CE]:=4; Port[$3CF]:=0; { восстановление портов }

| END;

{Процедура чтения файлов изображения и вывода их на экран. Параметр file_name - hmz файла без расширения. }

| PROCEDURE LoadBitPlanes( file_name : String );

| VAR scr_buf:Pointer ; { указатель на буфер данных }

| scr_arr:g_plan absolute $A000:$0; { память адаптера }

| plen,mx,my:LongInt; { размер плоскости }

| { Вложенная процедура чтения плоскости с диска }

| PROCEDURE ReadBlk( name : String );

| VAR image file : File;

| BEGIN Assign( image.file, name );

| Reset( image_file, plen );

| BlockRead( image_file, scr_buf^, 1 );

| Close( image_file )

| END;

| BEGIN

| mx:=GetMaxX+1;

| my:=GetMaxY+1; {размеры плоскости }

| plen := mx*my div 8; {получение длины буфера }

| GetMem( scr_buf, plen ); {память для буфера }

| ReadBlk( file_name+'.blu' ); {чтение с диска в буфер }

| Port[$3C4]:=2; Port[$3C5]:=1; {Буфер копируется }

| Move(scr_buf^,scr_arr,plen); {в плоскость Blue. }

| ReadBlk(file_name+'.grn' ); {чтение с диска в буфер }

Рис. 22.13 (продолжение)

- 525 -

| Port[$3C4]:=2;

| Port[$3C5]:=2;

| { Буфер копируется }

| Move(scr_buf^,scr_arr,plen);

| { в плоскость Green. }

| ReadBlk( file_name+'.red' );

| { чтение с диска в буфер }

| Port[$3C4]:=2;

| Port[$3C5]:=4;

| { Буфер копируется }

| Move(scr_buf^,scr_arr,plen );

| { в плоскость Red. }

| ReadBlk( file_name+'.int' );

| { чтение с диска в буфер }

| Port[$3C4]:=2; Port[$3C5]:=8; { Буфер копируется }

| Move(scr_buf^,scr_arr,plen ); { в плоскость яркости. }

| FreeMem( scr_buf, plen ); { освобождение памяти }

| Port[$3C4]:=2;Port[$3C5]:=15; { восстановление портов }

| END;

| BEGIN

| { Пример вызовов процедур }

| GrInit;

| SetFillStyle(1,15);

| Bar( 0, 0, GetMaxX, GetMaxY );

| SetFillStyle(2, 2);

| Bar(40, 40,GetMaxX-40, GetMaxY-40 );

| SetFillStyle(3,3);

| Bar(120,120, GetMaxX-120, GetMaxY-120);

| SetFillStyle(4,4);

| Bar(240,180, GetMaxX-240, GetMaxY-180);

| ReadLn;

| SaveBitPlanes( 'plane' );

| ClearDevice;

| ReadLn;

| LoadBitPlanes( 'plane' );

| ReadLn;

| CloseGraph

| END.

Рис. 22.13 (окончание)

Эти процедуры работают для всех режимов, кроме CGA (так как в нем другой способ хранения данных). Поэтому для него приведем отдельные процедуры (рис. 22.14).

| USES Graph;

| TYPE

| g_plan=Array [1..16384] of Byte; { массив двух блоков }

| {Процедура сохранения изображения всего экрана на диске }

| { для адаптеров, работающих в режиме CGA. }

| PROCEDURE SaveCGAScr(file_name:String); {полное имя файла }

| VAR

| image_file : File;

| scr_buf : Pointer; { ссылка на буфер }

| scr_addr:g_plan absolute $B800:0; { память адаптера }

| plen, mx, my : LongInt; { размер плоскости }

Рис. 22.14

- 526 -

| BEGIN

| mx := GetMaxX+1;

| my := GetMaxY+1;

| { размеры плоскости }

| if GetGraphMode = CGAHi { размер буфера: }

| then

| plen := mx*my div 8 + 384 { один бит на точку }

| else { или }

| plen := mx*my div 4 + 384; { два бита на точку }

| GetMem( scr_buf, plen ); { выделение памяти }

| Assign( image_file, file_name ); { связь файлов }

| Rewrite( image_file, plen ); { открытие файла }

| Move( scr_addr, scr_buf^, plen ); { экран – в буфер }

| BlockWrite(image_file,scr_buf^,1); { запись его в файл }

| Close( image_file ); { закрытие файла }

| FreeMem( scr_buf, plen ) { удаление буфера }

| END;

| { Процедура чтения изображения с диска и вывода его на }

| { экран для адаптеров, работающих в режиме CGA }

| PROCEDURE LoadCGAScr(file_name:String );

| {полное имя файла }

| VAR

| image_file : File;

| scr_buf : Pointer; { ссылка на буфер }

| scr_addr:g_plan absolute $8800:0; { память адаптера }

| plen,mx,my: LongInt; { размер плоскости }

| BEGIN

| mx := GetMaxX+1;

| my := GetMaxY+1; { размеры плоскости }

| if GetGraphMode = CGAHi { размер буфера: }

| then

| plen := mx*my div 8 + 384 { один бит на точку }

| else { или }

| plen := mx*my div 4 + 384; { два бита на точку }

| GetMem( scr_buf, plen ); { отводится память }

| Assign( image_file, file_name ); { связь файлов }

| Reset( image_file, plen ); { открытие файла }

| BlockRead(image_file,scr_buf^,1); { чтение картинки }

| Close( image.file ); { закрытие файла }

| Move(scr_buf^,scr_addr, plen); { буфер – на экран }

| FreeMem( scr buf, plen ) { удаление буфера }

| END;

Рис. 22.14 (окончание)

Подобный способ хранения изображений используется в некоторых графических пакетах программ (PBrush, DrHALO). Однако формат записи, используемый в них, другой. Поэтому они не совместимы ни между собой, ни с другими графическими пакетами.

- 527 -