|
| |||||||||||||||||
Паскаль для новичков (часть 36)Спрашивали? Отвечаю…
Работаем с графическим режимомАвтор: Владислав Демьянишин
![]() Но это меня не остановило. Уже на то время я был “тертый калач” со стажем работы на ассемблере, и это меня подвигло к написанию собственных модулей, позволяющих работать с графическими режимами высокой разрешающей и цветовой способностью.
При написании такого модуля оказалось, что можно убить сразу нескольких зайцев, кроликов, куропаток, и прочих охотничьих трофеев. ;О) А именно: модуль включал в себя все основные средства работы с адаптером SVGA, что позволяло компилировать самодостаточную программу, которой бы не требовался довесок в виде файла *.BGI; функциональность вложенных в него процедур и функций обеспечивала кроссрежимность модуля; использование режимов SVGA позволяло перешагнуть через цветовой барьер в 4 бита на цвет и достичь 8/16/24 битной цветности; барьер разрешающей способности в 640x480 тоже оказался преодолим, и стали доступны разрешения вплоть до 1280x1024.
Все это, конечно, предполагает наличия SVGA-совместимого видеоадаптера в конфигурации используемого компьютера. Доступность видеорежимов определяется объемом доступной видеопамяти, установленной на плате видеоадаптера. Формула вычисления объема необходимой памяти для того или иного видеорежима (видео-экрана) на редкость проста: память=ширина*высота*цветность, где ширина и высота указываются в пикселях (точках), а цветность в битах, деленных на 8. В итоге расчет выдаст необходимый объем видеопамяти для определенного видеорежима. Следовательно, если объем памяти на плате адаптера меньше, чем расчетный, то данный видеорежим будет недоступен.
Сегодня мы попытаемся составить базовый модуль для работы с SVGA режимами, основываясь на спецификации стандарта VESA, не забыв, при этом, старый добрый VGA.
Итак, приступим. Для начала, установим директивы и дадим название нашему модулю SGraph. Зачем скромничать, ведь мы хотим добиться от нашего модуля сверх возможностей, которые ранее были нам не доступны. Отсюда и название SGraph (Super Graph).
{$G+,N+}
Unit SGraph;
interface
type TColor = longint;
function IsVGA : boolean;
procedure WaitRetrace;
function GetVESAInfo: boolean;
function GetVESAModeInfo( Mode : word ): boolean;
function InitGraph( ModeIndex : word ): boolean;
procedure ExitGraph;
procedure SetWindow( MinX, MinY, MaxX, MaxY : word );
procedure DefaultWindow;
procedure GetPalette( var Palette );
procedure SetPalette( var Palette );
function SetMode( Index : word ): boolean;
procedure PutPixel( X, Y : integer; Color : TColor );
function GetPixel( X, Y : integer): TColor;
function RGBTo16Bits( Red, Green, Blue : byte ): TColor;
function RGBTo24Bits( Red, Green, Blue : byte ): TColor;
За перечнем заголовков публикуемых подпрограмм следует
объявление процедурных типов, обеспечивающих универсальность нашего модуля. Тип TRGB описывает структуру цвета точки, формируемого из компонент Red(R)/Green(G)/Blue(B) соответственно красный/зеленый/синий. Значения каждой компоненты колеблются в пределах 0..255. Тип TPalette объявляет структуру для формирования цветовой палитры для 256-цветных режимов. Тип TVESAGraphMode нужен для представления списка популярных режимов в виде массива констант Metrics. Тип TScreenMetrics описывает структуру для хранения характеристик текущего режима.
Для поддержки оконного изолирования графического вывода пригодится тип TWinRect и, соответственно, для хранения характеристики текущего окна. Для получения и расшифровки информации о видеоадаптере понадобиться структура TVESAInfo, на рассмотрении которой я останавливаться не буду, так как для подробного ознакомления с ней следует обратиться к спецификации VBE2.0, архив которой доступен на выше указанном сайте. В ней же можно найти подробное описание структуры TVESAModeInfo, позволяющей получить информацию о конкретном видеорежиме. В данной статье я буду опираться на укороченный вариант структуры TVESAModeInfo, где объявлены наиболее востребованные поля.
type
TPutPixelProc = procedure ( X, Y : word; Color : TColor );
TGetPixelProc = function ( X, Y : word ): TColor;
TRGBToColorProc = function ( Red, Green, Blue : byte ): TColor;
TRGB = record
Red, Green, Blue : byte;
end;
TPalette = array [0..255] of TRGB;
TVESAGraphMode = record
Mode, Width, Height : word;
end;
TScreenMetrics = record
Mode, Width, Height,
BytesPerScanline, BitsPerPixel : word;
end;
TWinRect = record
Left, Top, Right, Bottom, Width, Height : word;
end;
TVESAInfo = record
ID: array [0..3] of char; Ver: word;
OemStringPtr: pointer; Capabilities: longint;
ModeList: pointer; TotalMemory: word;
Reserved: array [0..221] of byte;
OemData: array [0..255] of byte;
end; {512 bytes}
TVESAModeInfo = record
ModeAttr, No_Use1, WinGranul: word;
No_Use2 : array [0..9] of byte;
BytesPerScanLine, Width, Height: word;
CharWidth, CharHeight, Planes, BitsPerPixel,
Banks, MemoryModel, BankSize, ImagePages: byte;
Reserved: array [0..226] of char;
end; {256 bytes}
Следует не забыть объявить константы индексов видеорежимов, адрес начала графического видеобуфера, и типизированные константы базовых цветов, которые могут быть изменены при переходе от одного режима к другому режиму. Некоторые константы пропущены, так как для более глубокого изучения все равно понадобится исходный код модуля SGRAPH, который можно взять на вышеуказанном ресурсе в Интернете.
И, наконец, таблица популярных режимов, способных удовлетворить большинство запросов.
const VESAText = 0;
{ 8 bits/color }
VESA320x200x256 = 1;
VESA640x400x256 = 2;
VESA640x480x256 = 3;
VESA800x600x256 = 4;
VESA1024x768x256 = 5;
VESA1280x1024x256 = 6;
{ 16 bits/color }
VESA320x200x64k = 7;
VESA640x480x64k = 8;
…
VESA1280x1024x64k = 11;
{ 24 bits/color }
VESA320x200x16M = 12;
…
VESA1280x1024x16M = 16;
SegA000 = $0A000;
{ Colors }
clBlack: longint = 0;
clBlue: longint = 1;
clGreen: longint = 2;
…
clYellow: longint = 14;
clWhite: longint = 15;
Metrics : array [0..16] of TVESAGraphMode =
((Mode:$003; Width:001; Height:001),
(Mode:$013; Width:320; Height:200),
(Mode:$100; Width:640; Height:400),
(Mode:$101; Width:640; Height:480),
(Mode:$103; Width:800; Height:600),
(Mode:$105; Width:1024; Height:768),
(Mode:$107; Width:1280; Height:1024),
(Mode:$10E; Width:320; Height:200),
(Mode:$111; Width:640; Height:480),
(Mode:$114; Width:800; Height:600),
(Mode:$117; Width:1024; Height:768),
(Mode:$11A; Width:1280; Height:1024),
(Mode:$10F; Width:320; Height:200),
(Mode:$112; Width:640; Height:480),
(Mode:$115; Width:800; Height:600),
(Mode:$118; Width:1024; Height:768),
(Mode:$11B; Width:1280; Height:1024));
Теперь объявим глобальные переменные, необходимые для работы модуля. Переменная LastPage предназначена для хранения текущей страницы видеопамяти, на которую установлен видеобуфер. Переменные Screen, WinRect, VESAInfo и ModeInfo в комментариях не нуждаются. В GranulShift будет храниться длина битового сдвига, соответствующего гранулярности текущего видеорежима. Процедурные переменные PutPixelProc, GetPixelProc и RGBToColor при установке видеорежима будут хранить адрес подпрограмм по рисованию точки на экран и чтению цвета точки с экрана в заданной позиции экрана, и преобразованию компонент R/G/B в цветовой тип соответственно. При инициализации модуля эти переменные устанавливаются на адреса подпрограмм-пустышек, дабы предотвратить последствия в случае нерадивых программистов.
var LastPage : word;
Screen : TScreenMetrics;
WinRect : TWinRect;
GranulShift : word;
VESAInfo : TVESAInfo;
ModeInfo : TVESAModeInfo;
PutPixelProc : TPutPixelProc;
GetPixelProc : TGetPixelProc;
RGBToColor : TRGBToColorProc;
Прежде, чем продолжить, следует ввести вас в курс архитектурных особенностей поддержки объявленных видеорежимов.
В данном модуле предполагается использование видеорежимов с 8/16/24 битной цветностью.
8-битная цветность предусматривает, что на каждую точку (пиксель) графического изображения в видеопамяти будет расходоваться один байт. При этом это байтное значение отнюдь не является цветом точки, а лишь индексом цвета, который описан в палитре, то есть в таблице цветов, имеющей структуру типа TPalette.
Каждый N-ый TRGB-элемент таблицы описывает параметры цвета в виде сочетания трех цветов: красного (R), зеленого (G) и синего (B).
Несмотря на то, что под каждую компоненту то ли R, то ли G то ли B отведен один байт, для корректного аппаратного отображения цветов значения величин должны быть в пределах 0..63, где ноль соответствует самой низкой яркости компоненты, а 63 – самой высокой яркости. То есть, например, при выводе изображения файла в формате BMP, где 8-битное изображение предусматривает для каждой компоненты допустимые величины 0..255, следует каждую компоненту палитры BMP-файла поделить на 4, чтобы получить величину в пределах 0..63.
При переходе в 8-битный видеорежим по умолчанию устанавливается палитра цветов, где первые 16 индексов 0..15 соответствуют стандартным EGA-цветам, грубо говоря, как в текстовом режиме. Остальные 16..255 представляют собой градации серого цвета и некоторых цветов радуги.
При работе в таком режиме часто возникает необходимость установить собственные (пользовательские) цвета. Если речь идет всего лишь о нескольких цветах из палитры, то достаточно прочитать текущую палитру процедурой GetPalette в переменную типа TPalette и откорректировать необходимые элементы массива. После чего, следует установить измененную палитру процедурой SetPalette, указав в качестве параметра откорректированный массив.
После выполнения последней процедуры изменения вступят в силу,
при этом, само изображение в видеобуфере изменять нет необходимости.
Такие видеорежимы имеют ряд преимуществ: работают довольно быстро, требуют не слишком много памяти, позволяют выполнять различные цветовые эффекты весьма быстро и позволяют выводить на экран изображение с вполне приемлемым качеством. Основным недостатком таких режимов, все же, является низкое цветовое разрешение.
16/24-битная цветность вместо использования палитры цветов предполагает установку для каждой конкретной точки изображения своего определенного цвета в виде тех же компонент R/G/B. При этом эти три компоненты должны быть упакованы определенным образом. Например, чтобы получить 16-битный цвет, следует компоненты упаковать в двухбайтное слово так: R(5 бит)/G(6 бит)/B(5 бит), где компонента R расположена в старших битах, а B в младших, ну и G посередке ;0) При этом, если компоненты имели значения по 8 бит, то их следует поделить для каждой компоненты так, чтобы значащих бит осталось столько, как указано выше. Этого можно добиться обыкновенным логическим сдвигом вправо, то есть формулы будут выглядеть так: Rnew:=R shr 3; Gnew:=G shr 2; Bnew:=B shr 3. Как видно из формул, такие преобразования повлекут за собой некоторые искажения выводимого изображения, но в целом это лучше, чем 8-бит на цвет.
Упаковка 24-битного цвета осуществляется в двойное слово (4 байта) и выглядит проще: 0(8 бит)/R(8 бит)/G(8 бит)/B(8 бит). Это позволяет осуществить полную передачу цветовой гаммы выводимого изображения без искажений. Заметьте, что старший байт цвета всегда будет равен нулю и практически никак не участвует в формировании цвета, а стало быть, расходуется напрасно. Этому есть объяснение, так как старший четвертый байт служит для выравнивания цвета точки по 32-х битной границе памяти. В такой упаковке есть смысл для процессоров i80386 и выше, так как пересылка 4 байт происходит гораздо быстрее, чем трех, как бы это странно не звучало, к тому же вычисление адреса, кратного четырем, реализуется гораздо проще, чем кратного трем.
Как раз это и учли инженеры, разрабатывавшие видеоадаптеры. Во всяком случае, меня поймут те, кто знаком с архитектурными особенностями 32-разрядных процессоров.
Выше я проиллюстрировал традиционный формат упаковки 16/24-битного цвета. Следует знать, что различные видеоадаптеры могут содержать подмножество таких режимов, где вместо 16 бит на цвет может на самом деле учитываться лишь 15 бит, то есть формат 0(1)/R(5)/G(5)/B(5) (например, модель адаптера Cirrus Logic GD-5436/46 VGA/PCI). Помимо этого, некоторые модели могут иметь несколько другой формат упаковки, например не R(?)/G(?)/B(?), а B(?)/G(?)/R(?). Чтобы подробнее узнать о подобных вольностях конструкторов видеоадаптеров следует использовать полный вариант структуры TVESAModeInfo (смотрите спецификацию VBE). Могу вас успокоить тем, что в большинстве случаев используется традиционный формат упаковки цвета.
Продолжение следует…
© Владислав Демьянишин
Литература3. Interrupt list by Ralf Brawn v.3.3. На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на MIDLetPascal, Turbo Pascal 6, Turbo Pascal 7, Borland Pascal, по программированию устройств Sound Blaster, Adlib, VESA BIOS, справочник Norton Guide и много другой полезной информации для программистов, включая примеры решения реальных задач по созданию резидентных программ. Журнал > Программирование > Паскаль для новичков (Turbo Pascal, Assembler) > Паскаль для новичков (часть 36): Работаем с графическим режимом
| ||||||||||||||||||
|
||||||||||||||||||