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 -