Как уже говорилось, прямой доступ к памяти видеоадаптеров затруднен в связи с тем, что при этом необходимо обращаться к их специальным управляющим регистрам. Для тех, кому могут понадобиться манипуляции с изображением на экране, может быть полезна изложенная ниже информация.
Мы уже рассматривали, каким образом можно получить копию изображения с экрана на принтере (см. разд. 19.6.1). Теперь {520} попробуем записать изображение с экрана на диск. Казалось бы просто — достаточно немного изменить процедуру 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 {521}
А с помощью процедуры 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, {522} например, полностью весь экран можно записать на диск максимум в режиме 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 {523}
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' ); {чтение с диска в буфер }
Эти процедуры работают для всех режимов, кроме 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 {525}
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). Однако формат записи, используемый в них, другой. Поэтому они не совместимы ни между собой, ни с другими графическими пакетами. {526}