Программирование графики
--------------------------------------------------------------------------------
Содержание
Вступление
Немного об эволюции компьютерной графики
О видео картах
Вывод точки
Поумневшая точка
Очистка экрана
Буферизация экрана
Окна (View port)
Работа с палитрой
--------------------------------------------------------------------------------
Речь в этой статье пойдет об основах компьютерной графики. На сегодняшний день (2000 год) компьютерная графика достигла столь высокого уровня, что начинающему программисту сложно разобраться в происходящем, и это зачастую отбивает всю охоту программировать компьютерную графику. Ко всему этому добавляется нехватка хоть кокой ни будь информации по этому вопросу не изобилующей сложными терминами и невнятными описаниями самого процесса программирования, к тому же героические фанаты MS WINDOWS дают совсем на мой взгляд бессмысленные рекомендации о использовании готовых библиотек для полноценного использования которых требуется не меньшее знание программирования чем для самостоятельно программирования видео адаптера. Поэтому я решил написать сие творение, дабы новички, да и не только смогли понять принципы программирования графики на IBM PC.
--------------------------------------------------------------------------------
Немного об эволюции компьютерной графики
С начала не было
ни чего, и все выводилось на перфоленту.
Затем что-то появилось, но я думаю, это что-то не заслуживает внимания
Потом появилась MDA (Монохромный дисплейный адаптер) который позволял выводить
черно-белый текст.
Но текста оказалось не достаточно, и появился CGA (Цветной графический адаптер),
этот девайс уже позволял выводить графику по точкам, правда в четырех цветах.
Затем изобрели HERCULES (или до), о его характеристиках я точно не знаю, но
в общем-то тоже лажа.
Но и четырех цветной CGA графики народу оказалось маловато, и тогда стараниями
фирмы IBM собрали EGA адаптер, который назвали (Расширенный графический адаптер)
и он кроме поддержки всего что было раньше позволял выводить графику в расширенных
режимах с скажем так высоким разрешением 640*400, и выдавал 16 цветов из палитры
в 64 цвета.
Дальше понеслось, для компьютеров PS/2 создали MCGA адаптер, который позволял
выводить графику в 256 цветах, правда было маловато оттенков.
Я точно не знаю, но похоже MCGA создала не IBM, и по этому этот гигант решил
перехватить рынок, создав VGA адаптер (Видео графическая матрица), который на
корню похоронил MCGA, и стал основным стандартом. Основными его характеристиками
стали возможность выбора цвета из 262143 оттенков для всех режимов, вывод на
экран сразу 256 различных цветов, ну и главной фишкой оказался режим 320*200
256 цветов. (А еще если его юзать с адаптером HERCULES можно было достичь разрешения
1024*768)
На основе VGA адаптера был создан SVGA адаптер (Супер видео графическая матрица)
который на сегодняшний день (2000г.) остается одним из основных, и позволяет
выводить графику в огромных разрешениях (По сравнению с EGA) и с глубиной цвета
до 16777215 оттенков, при этом без ограничения 256 цветами.
Вот так примерно выглядит эволюция графики на PC. Говоря о бедующем, увеличения
количества цветов нам не грозит, и сейчас основным направлением развития видео
адаптеров стало увеличения скорости обработки данных, увеличение видео памяти
и акселерация основных видео функций и 3D графики.
--------------------------------------------------------------------------------
На сегодняшний день (2000г.) на рынке появилось много графических адаптеров, которые так или иначе пытаются нам нахваливать и давать советы о том что именно его мы должны купить. При этом используются страшные заклинания как PCI, AGP, 2D/3D Акселерация и т.д. и т.п. Если вы профи, то да, это вам скажет о многом, но если нет, как разобраться что все это значит и почему GeForce с 64мб DDRDRAM круче S3 3d/2X с 8Мб, чем AGP лучше PCI и т.д. и т.п. Начнем по немножку, AGP, PCI, ISA, это слоты на материнской плате, в которые втыкаются всякие платы (Звук, Видео, Модем ...). Главное различие этих слотов, это скорость обмена данными между устройством и процессором, по этому чем лучше у вас слот, тем быстрее работает ваша видео карта. ISA - Этот разъем использовался еще на 80286 машинах, а может и раньше. Где-то в 90х годах его понемножку начал вытеснять разъем PCI (Бедующие компьютерной техники (см. кино ХАКЕРЫ)) который работал на порядок быстрее, но в погоне за выгодой, да и в целях еще большего разгона видео карт INTEL выпускает в свет AGP стандарт ориентированный на графические карты который позволяет проводить обмен данными между (видео карта) - (процессор) в обход системной шины, что увеличивает производительность еще в несколько раз. Так что для видео карт на данный момент (2000г.) AGP - стандарт является одним из самых наилучших, хотя и PCI еще не потерял свою актуальность.
Видео память - многие считаю что это показатель крутости видео адаптера, но они заблуждаются. Если они используют карту без 3D ускорения, то огромное количество видео памяти им и не потребуется, по этому я не понимаю смысла покупки простой видео карты с 32Мб видео памяти, когда за такие же деньги можно взять VOODOO с 16 Мб. Для того, чтобы понять сколько видео памяти вам нужно, вы должны разобраться в принципе ее распределения в видео адаптере. В 32 битно графике (максимальное количество цветов) на каждую точку (Пиксель) на экране отводится 4 байта, в режиме 1600*1280 мы имеем 2048000 точек, перемножив это на 4 мы получим число нужных нам байт для хранения такого кол-ва информации 8192000байт, далее переведем все это в мегабайты (разделим на 1024 два раза) получим примерно 8 Мб, для работы в WINDOWS нужно чтобы этот объем покрывался 2 раза, то есть нам нужно 16 Мб видео память и 20 дюймовый монитор, но самое интересное то, что вы не такое разрешение использовать не когда не будете, т.к. оно вам и не надо, стандартное разрешение экрана где-то 1024*768 24бит (True Color) в Windows и 800*600 в играх, исходя из этого посчитаем сикока видео памяти нам нужно для удовлетворения этих потребностей
1024*768*3*2 = 4.5 Мб
(В 24 битной графике на 1 пиксель уходит 3 байта)
Видео карта с 6Мб это изврат, но можно сделать вывод, что 8Мб нам хватит выше крыши.
Но сегодня мало карт с просто 2D ускорением, а карты с большим количеством памяти, ну не могут не претендовать на 3D ускорение, и вопят что такое кол-во памяти им нужно для хранения текстур. Тут конечно сложно спорить, все, что у нас остается от вывода графики, программа использует под текстуры, но кто мне объяснит, какое количество и каких текстур нужно загнать в 3D ускоритель, со скажем 32Мб видео памяти, в котором при режиме 800*600 24бита остается свободными более 28Мб.
Еще одним показателем качества видео памяти является ее тип, в простых видео картах используется памяти VRAM, но с ускорением карты, требуется более быстрая память, поэтому в современных картах используется SDRAM, которая гораздо быстрее чем VRAM, ну и SDRAM не предел, сегодня появился стандарт DDRDRAM, который используется в высокопроизводительных 3D ускорителях типа GeForce2. Скорость памяти, это один из ее основных показателей, потому что при работе в режиме 320*200 вам хватит и VRAM, а в режимах скажем 1024*768 карта должна работать с 10 кратным ускорением для того чтобы достичь подобных результатов, и это для режимов с 256 цветами.
Ну ладно, хватит о преимуществах и недостатках, в конце приведем классификацию карт.
Для бедных - 4/2/1 Мб видео памяти VRAM, что-то типа TRIDENT, CIRUS LOGICK или что-то подобное.
Для офиса - 8Мб карта с памятью SDRAM, с 2D ускорением, S3 Virge или Matrox
Игровая - RivaTNT2, VooDoo 3000 или выше. Лучший вариант объединяющий 2D и 3D ускорение.
Рулез - GeForce 2, 32(64)Mb DDRDRAM
--------------------------------------------------------------------------------
Если вы хоть немного разберитесь в программировании, то вы наверняка использовали стандартные модули для вывода графики как GRAPH. Его прототип есть в Ц и в Паскале. Он дает много возможностей, но не блещет скоростью вывода и мобильностью, требует BGI файл, и в стандартном варианте позволяет работать только с графикой 16-256 цветов и меньше. Ко всему этому, когда смотришь какую-нибудь ДЕМУ, там творят такое, что с модулем граф не когда не сделать.
Первое что нам нужно сделать, это установить нужный нам видео режим. Делается это путем использования прерывания процессора Int 10h (16), где в регистр AH заносим 0, а в регистр AL-номер видео режима.
Program
SetMode320*200_256;
Uses Dos;
Var Rp:Registers; {Создаем переменную регистров}
Begin
Rp.Ah:=0;
Rp.Al:=$13; {Номер видео режима, 320*200 256 цветов}
Intr($10,Rp); {вызываем прерывание}
End.
Через это прерывание можно установить все VGA видео режимы, но самый полезный
из них это режим $13. С ним просто работать и в нашем распоряжении 256 цветов.
Вернуть экран обратно в текстовый режим можно путем вызова того-же прерывания
но со значением $3 в регистре Al
Program
SetTextMode;
Uses Dos;
Var Rp:Registers; {Создаем переменную регистров}
Begin
Rp.Ah:=0;
Rp.Al:=$3; {Номер стандартного текстового режима, 80*25}
Intr($10,Rp); {вызываем прерывание}
End.
Установив режим мы теперь должны что-то вывести на экран, делается это просто. Информация о содержимом экрана храниться в памяти по адресу $A000 для графики, и $B800 для текста. В Паскале для этого есть две переменные SegA000 и SegB800. Желательно использовать именно их, по тому что в защищенном режиме памяти меняется адресация, и в этих переменных храниться нужное нам значение сегмента. Вот пример вывода чяго ни будь на экран.
Program
Demo1;
Uses Dos,Crt;
Var Rp:Registers;
I:Integer;
Begin
Rp.Ah:=0;
Rp.Al:=$3; {Номер стандартного текстового режима, 80*25}
Intr($10,Rp); {вызываем прерывание}
{Выведем что-нибудь на экран}
For i:=0 to 64000 doMem[SegA000,i]:=i;
Readkey;
End.
Экран заполниться разноцветными пикселями. Что же произошло. В сегменте SegA000 память отведена для работы с видео адаптером, и когда мы изменяем там значение, оно сразу же изменяется в видео памяти. Данные там храниться построчно, т.е. для режима 320*200 256 цветов первые 320 байт соответствуют первой строке на экране, следующие 320 байт - это вторая строка, и т.д. и т.п. до 200 строки. Теперь не сложно посчитать положение точки на экране, Mem[Y*320+X]:=Col, и если мы по этому смещению установим нужный нам байтик в значение нужного нам цвета, то на экране сразу отобразиться точка, ну и получить точку можно также, но только не установив байт, а прочитав его Col:=Mem[Y*320+X];
Program
Demo2;
{Сделаем программу более мобильной, сделаем все в виде процедур}
Uses Dos,Crt;
{Установим
видео режим}
Procedure SetMode(N:Byte)
Var Rp:Registers;
Begin
Rp.Ax:=N; {Занесем номер режима}
Intr($10,Rp); {Вызовем прерывание}
End;
{Рисуем
точку на экран}
Procedure Pix(X,Y:Integer;Col:Byte);
Begin
Mem[SegA000:Y*320+X]:=Col;
End;
{Получаем
точку с экран}
Function GetPix(X,Y:Integer):Byte;
Begin
GetPIx:=Mem[SegA000:Y*320+X];
End;
Begin
SetMode($13); {Установим режим 320*200 256 цветов}
Pix(100,100,15); {Поставим точку на экране по координатам 100,100 цветом 15}
Readkey;
SetMode($3);
End.
--------------------------------------------------------------------------------
Отлично, точку мы поставили, но теперь проведем эксперимент, поставим точку по координатам (321,10), по идеи точка должна попасть за экран, но у нас она выскакивает с другой стороны экрана, как же этого можно избежать. А очень просто, нужно проверять координаты точки перед ее выводом, а делается это так.
Program
Demo3;
Uses Dos,Crt;
{Установим
видео режим}
Procedure SetMode(N:Byte)
Var Rp:Registers;
Begin
Rp.Ax:=N;
Intr($10,Rp);
End;
{Рисем
точку на экран}
Procedure Pix(X,Y:Integer;Col:Byte);
Begin
{Первое берем X и Y как Word, если вдруг придет отрицательное число, то
у нас получиться нечто более 32768, этим мы и воспользуемся}
If (Word(X)<320) And (Word(Y)<200) Then Mem[SegA000:Y*320+X]:=Col;
End;
{Получаем
точку с экран}
Function GetPix(X,Y:Integer):Byte;
Begin
If (Word(X)<320) And (Word(Y)<200) Then GetPIx:=Mem[SegA000:Y*320+X];
End;
Begin
SetMode($13); {Установим режим 320*200 256 цветов}
Pix(100,100,15); {Поставим точку на экране по координатам 100,100 цветом 15}
Pix(321,100,15); {Поставим точку на экране по координатам 100,100 цветом 15}
Readkey;
SetMode($3);
End.
Теперь точка не выскакивает за экран, а значит мы можем смело рисовать по любым координатам.
--------------------------------------------------------------------------------
Теперь решим следующую задачу, как очистить экран. Первое что приходит в голову, в цикле поставить везде точки одного цвета, но для полного экрана это не очень рационально, т.к. мы делаем 64000 сравнения координат точки, а нам просто надо установить все байты в сегменте $A000, в нужное нам значение. Все это решается очень просто, в Паскале есть функция заполнения данных под названием FillChar, которая эквивалентна STOSB на ассемблере. Делается это так;
Program
Demo4;
Uses Dos,Crt;
{Установим
видео режим}
Procedure SetMode(N:Byte)
Var Rp:Registers;
Begin
Rp.Ax:=N;
Intr($10,Rp);
End;
Procedure
Cls(Col:Byte);
Begin
{Устанавливаем 64000 байт в сегменте $A000 по смещению 0 байт в значение Col}
FillChar(Mem[SegA000:0],64000,Col);
End;
Begin
SetMode($13); {Установим режим 320*200 256 цветов}
RepeatCls(Random($FF));
Until Keypressed;
Readkey;
SetMode($3);
End.
--------------------------------------------------------------------------------
Хоть вывод графики у нас и быстрый, но все равно, если у нас будет что-то с полноэкранной прорисовкой, то все это, при прямом выводе на экран будет заметно. Для того, чтобы этого избежать нам нужно использовать буфер в памяти, делается это примерно так, резервируем 64Kb памяти, и выводим не в сегмент $A000, а в сегмент нашего буфера, на который будет указывать специальная переменная, скажем VideoSEG. Заодно решаем, что нам нужно.
1.Переделаем все выше приведенные исподники в модуль
2.Для работы с буфером нам нужно
Переменную указатель
на вывод
Процедуру создания буфера
Процедуру вывода буфера
Процедура удаления буфера
Процедура копирования буфер
Процедуры установки вывода в буфер и на экран
3.Немного переписать процедуру вывода точки
Сказано, сделано ...
Unit
MYVGA;
Interface
Const
ScreenXres = 320; {Ширина экрана в пикселях}
ScreenYres = 200; {Высота экрана в пикселях}
Var VideoSeg:Word; {Сегмент вывода}
Procedure SetMode(N:Byte); {Установка видео режима}
Procedure Pix(X,Y:Integer;Col:Byte); {Вывести точку}
Procedure GetPix(X,Y:Integer):Byte; {Получить точку}
Procedure Cls(Col:Byte); {Очистка экрана}Procedure NewB(P:Pointer); {Создать новый буфер в памяти}
Procedure DelB(P:Pointer); {Удалить буфер из памяти}
Procedure SetB(P:Pointer); {Установить вывод в буфер}
Procedure SetS; {Установить вывод на экран}
Procedure OutB(P:Pointer); {Вывести буфер на экран}
Procedure GetB(P:Pointer); {Копировать экран в буфер}
Procedure CopyBB(Src,Dest:Pointer); {Копировать буфер в буфер}
Implementation
{Создаем
новый буфер}
Procedure NewB(P:Pointer);
Begin
GetMem(P,$FFFF); {Берем $FFFF байт под буфер в динамической памяти}
End;
{Удаляем
буфер из памяти}
Procedure DelB(P:Pointer);
Begin
FreeMem(P,64000); {Освобождаем 64000 байт динамической памяти}
End;
{Установить
вывод в буфер}
Procedure SetB(P:Pointer);
Begin
VideoSeg:=Seg(P^); {Видео сегмент равен сегменту нашего буфера}
End;
{Установить
вывод на экран}
Procedure SetS;
Begin
VideoSeg:=SegA000; {Видео сегмент равен сегменту экрана}
End;
{Копируем
буфер на экран}
Procedure OutB(P:Pointer);
Begin
Move(P^,Mem[SegA000:0],64000); {Копируем 64000 байт из памяти по адресу буфера, в видео сегмент}
End;
{Копируем
экран в буфер}
Procedure GetB(P:Pointer);
Begin
Move(Mem[SegA000:0],P,64000); {Тоже что и OutB, но на оборот}
End;
{Копируем
буфер в буфер}
Procedure CopyBB(Src,Dest:Pointer);
Begin
Move(Src^,Dest^,64000); {Копируем буфер в буфер}
End;
{Установка
видео режима}
Procedure SetMode(N:Byte)
Var Rp:Registers;
Begin
Rp.Ax:=N; {Занесем номер режима}
Intr($10,Rp); {Вызовем прерывание}
End;
{Очистка
экрана}
Procedure Cls(Col:Byte);
Begin
{Устанавливаем 64000 байт в сегменте $A000 по смещению 0 байт в значение Col}
FillChar(Mem[VideoSeg:0],64000,Col);
End;
{Рисем
точку на экран}
Procedure Pix(X,Y:Integer;Col:Byte);
Begin
{Первое берем X и Y как Word, если вдруг придет отрицательное число, то у нас получиться нечто более 32768, этим мы и воспользуемся}
If (Word(X)<320) And (Word(Y)<200) Then Mem[VideoSeg:Y*320+X]:=Col;
End;
{Получаем
точку с экран}
Function GetPix(X,Y:Integer):Byte;
Begin
If (Word(X)<320) And (Word(Y)<200) Then GetPIx:=Mem[VideSeg:Y*320+X];
End;
Begin
VideoSeg:=SegA000; {Устанавливаем вывод на экран}
End.
*********************************************************************************
Program
Demo5;
Uses MyVGA,CRT;
Var
Bufer:Pointer; {Переменная буфера}
I,J,K:Integer;
Begin
Writeln('Вывод графики с использованием буфера');
Readkey;
SetMode($13);
NewB(Bufer); {Создаем буфер в динамической памяти}
SetB(Bufer); {Вывод в буфер}
RepeatFor j:=0 to 199 do
For i:=0 to 319 doPix(i,j,i+j+k);
Inc(K);
OutB(Bufer); {Выводим буфер на экран}Until Keypressed;
DelB(Bufer); {Удаляем буфер из динамической памяти}
SetS;
Readkey
SetMode($3);Writeln('Вывод графики с без использования буфера');
Readkey;
SetMode($13);
RepeatFor j:=0 to 199 do
For i:=0 to 319 doPix(i,j,i+j+k);
Inc(K);
Until Keypressed;
Readkey;
SetMode($3);
End.
P.S. Так как мы все делаем в модуле, я буду писать отдельные процедуры и ф-ции для модуля, дабы сократить размер текста.
--------------------------------------------------------------------------------
Еще одна штука, которая нам может пригодиться, это вывод не на весь экран, а в окно (окно - прямоугольная область экрана). При этом начало координат экрана переноситься на начало окна, а вывод за его приделы не как не отражается на экране. Для вывода в окно, нам нужно создать переменные задающие координаты окна, и дающие размер окна, а также переменную флаг, которая будет говорить нам о том, что вывод производится в окно. Еще нам придется немного дополнить процедуры PIX, GETPIX и CLS.
Unit
MYVGA;
Interface
Const
.... Предыдущие константы ....
Var
.... Предыдущие переменные ....
WLX,WLY:Integer; {Координаты левого верхнего угла окна}
WHX,WHY:Integer; {Координаты правого нижнего угла окна}
WXRES,WYRES:Integer; {Ширина и высота окна}
WEnabled:Boolean; {Если True то вывод в окно}
.... Процедуры описанные ранее ...
Procedure Pix(X,Y:Integer;Col:Byte); {Вывести точку}
Procedure GetPix(X,Y:Integer):Byte; {Получить точку}
Procedure Cls(Col:Byte); {Очистка экрана}
Procedure SetWindow(X,Y,X1,Y1:Integer); {Установить вывод в окно}
Procedure SetScreen; {Установить вывод на весь экран}
Function MaxX:Integer; {Ширина окна или экрана}
Function MaxY:Integer; {Высота окна или экрана}
Implementation
{Установить
вывод в окно}
Procedure SetWindow(X,Y,X1,Y1:Integer);
Begin
If X>X1 Then Begin WLX:=X1;WHX:=X End Else Begin WLX:=X;WHX:=X1 End;
If Y>Y1 Then Begin WLY:=Y1;WHY:=Y End Else Begin WLY:=Y;WHY:=Y1 End;
WXres:=WHX-WLX+1;
WYres:=WHY-WLY+1;
WEnabled:=True;
End;
{Установить
вывод на весь экран}
Procedure SetScreen;
Begin
WEnabled:=False;
End;
{Ширина
окна или экрана}
Function MaxX:Integer;
Begin
If Enabled Then MaxX:=WXres Else MaxX;=ScreenXres;
End;
{Высота
окна или экрана}
Function MaxY:Integer;
Begin
If Enabled Then MaxY:=WYres Else MaxY;=ScreenYres;
End;
{Очистка
экрана}
Procedure Cls(Col:Byte);
Begin
If WEnabled Then Begin
WEnabled:=False;{Этот маленький фокус поможет нам выиграть пару тактов у процессора}
{Конечно грубо, но эффективно}
For j:=WYL to WYH do
For i:=WXL to WXH do
Pix(i,j,Col);
WEnabled:=True;End Else FillChar(Mem[VideoSeg:0],64000,Col);
End;
{Рисуем
точку на экран}
Procedure Pix(X,Y:Integer;Col:Byte);
Begin
{Проверяем, а выводим ли мы в окно}
if WEnabeld Then{Далее смотри, а не выходим ли мы за рамки окна}
If (Word(X)<WXres) And (Word(Y)<WYres) Then Begin{Если нет, то пересчитываем координаты для экрана}
Inc(X,WLX);
Inc(Y,WLY);End Else Exit; {Иначе выходим из процедуры}
If (Word(X)<ScreenXres) And (Word(Y)<ScreenYres) Then
Mem[VideoSeg:Y*320+X]:=Col;
End;
{Получаем
точку с экран}
Function GetPix(X,Y:Integer):Byte;
Begin
if WEnabeld Then
If (Word(X)<WXres) And (Word(Y)<WYres) Then BeginInc(X,WLX);
Inc(Y,WLY);End Else Exit;
If (Word(X)<ScreenXres) And (Word(Y)<ScreenYres) Then
GetPIx:=Mem[VideSeg:Y*320+X];
End;
.... Процедуры описанные ранее в которые не были внесены изменения ...
Procedure SetMode(N:Byte); {Установка видео режима}
Procedure NewB(P:Pointer); {Создать новый буфер в памяти}
Procedure DelB(P:Pointer); {Удалить буфер из памяти}
Procedure SetB(P:Pointer); {Установить вывод в буфер}
Procedure SetS; {Установить вывод на экран}
Procedure OutB(P:Pointer); {Вывести буфер на экран}
Procedure GetB(P:Pointer); {Копировать экран в буфер}
Procedure CopyBB(Src,Dest:Pointer); {Копировать буфер в буфер}
Begin
VideoSeg:=SegA000; {Устанавливаем вывод на экран}
End.
*********************************************************************************
Program
Demo6;
Uses MyVGA,CRT;
Var
Bufer:Pointer; {Переменная буфера}
I,J,K:Integer;
Begin
Writeln('Вывод графики в окно');
Readkey;
SetMode($13);
SetWindow(10,10,100,100);
NewB(Bufer); {Создаем буфер в динамической памяти}
SetB(Bufer); {Вывод в буфер}
RepeatFor j:=0 to MaxY-1 do
For i:=0 to MaxX-1 doPix(i+Trunc(Sin((j+k)*0.05)*20),j,i+j+k);
Inc(K);
OutB(Bufer); {Выводим буфер на экран}Until Keypressed;
DelB(Bufer); {Удаляем буфер из динамической памяти}
SetS;
Readkey;
SetMode($3);
End.
--------------------------------------------------------------------------------
При работе с 256 цветовыми режимами или как это иногда называется 8 битной графикой, в нашем распоряжении 256 цветов. При этом все это вместе называется палитрой. Оттенок каждого цвета состоит из трех компонент RGB (Красный, Зеленый и Голубой) в пределе от 0 до 63. Задать его можно двумя способами, через БИОС и через порты ($3C7, $3C8, $3C9). Так как, через БИОС все работает почему-то тормазнуто, лучший сделать все это через порты, пример чего я привожу ниже.
Желательно эти процедуры вставить в модуль MyVGA
{Процедура
установки RGB}
Procedure SetRGB(N,R,G,B:Byte);
Begin
Port[$3C8]:=N; {В порт $3C8 посылаем номер цвета в палитре}
Port[$3C9]:=R; {В порт $3C9 последовательно посылаем компоненты R, G, B}
Port[$3C9]:=G;
Port[$3C9]:=B;
End;
{Получение
значений RGB для цвета N}
Procedure SetRGB(N:Byte;Var R,G,B:Byte);
Begin
Port[$3C7]:=N; {В порт $3C7 посылаем номер цвета в палитре}
R:=Port[$3C9]; {Из порта $3C9 последовательно считываем компоненты R, G, B}
G:=Port[$3C9];
B:=Port[$3C9];
End;
*********************************************************************************
Program
Demo7;
Uses MyVGA,CRT;
{В MyVGA должны быть прописаны процедуры GetRGB и SetRGB}
Var
I,J,K:Integer;
Begin
Writeln('Пример работы с палитрой');
Readkey;
SetMode($13);
For j:=0 to MaxY-1 do
For i:=0 to MaxX-1 doPix(i+Trunc(Sin((j+k)*0.05)*20),j,i+j+k);
Readkey;
{Изменяем значения палитры}
For i:=0 to 255 doSetRGB(i,i,i,0);
Readkey;
SetMode($3);
End.