Algoritmy rovinné grafiky

     Obrázek vytvářený na obrazovce se skládá z různých
elementárních objektů: bodů, úseček, kruhů, elips atd.
Zobrazení těchto objektů je různě složité. Bodu odpovídá
jeden bit obrazové paměti, zde není žádný problém.
U složitějších objektů (čára, kruh) je situace poněkud
složitější. Z čistě matematického hlediska jsou tyto objekty
složeny z nekonečného počtu bodů. Obrazovka samozřejmě
nemůže zobrazit tak velký počet nekonečně malých bodů a tak
musí být provedena určitá aproximace, kdy se vykreslí pixely
ležící nejblíže skutečnému umístění křivky. Algoritmy pro
rovinou grafiku mají za úkol, co nejrychleji
a nejjednodušeji, určit polohu aproximovaných pixelů.
Z těchto algoritmů je nejjednodušší algoritmus pro kreslení
úseček. Rozebereme ho zde tedy poněkud podrobněji.

Bresenhamův algoritmus pro kresbu úsečky
     Z matematiky si ještě možná pamatujete, že rovnici
přímky lze zapsat ve tvaru:

     y = mx + b,                         ( 1 )

kde m je směrnice a b posun na ose y. Je-li úsečka určena
počátečním a koncovým bodem (souřadnicmi bodů [x1,y1] a
[x2,y2]), můžeme směrnici a posunutí vyjádřit následujícími
vztahy:
           y2-y1     ëy
     m =  ------- = ----
           x2-x1     ëx
                                         ( 2 )
          x2y1-x1y2     x2y1-x1y2
     b = ----------- = -----------.
            x2-x1           ëx

Algoritmy pro zobrazení úsečky vycházejí z rovnic 1 a 2.
Na nějakém intervalu ëx se souřadnice y změní o hodnotu ëy
podle následujícího vztahu:

     ëy = m ëx.

     J.E.Bresenham vymyslel algoritmus, který při generování
úsečky vystačí s celočíselnou aritmetikou. Na obrázku 10 je
nakreslena část obrazovky, na které mají být zobrazeny
pixely úsečky. Po nakreslení levého koncového bodu úsečky
máme rozhodnout, zda následující bod bude vykreslen na
pozici se souřadnicí y stejnou jako předchozí bod nebo o
jedna větší. Z těchto dvou možností umístění následujícího
pixelu vybereme tu, která leží blíže skutečné poloze bodu
úsečky.
     Popíšeme si zde způsob, jak pomocí Bresenhamova
algoritmu vykreslit úsečku s kladnou směrnicí menší než 1.
Ostatní směry jsou pouze obměnou dále uvdedného postupu. V
případě, že úsečka má kladnou směrnici menší než jedna je
řídící osou osa x. Znamená to, že postupně pro všechny
souřadnice x měněné o jedničku, určíme jim odpovídající
souřadnice y. Předpokokládejme, že pixel o souřadnicích
[xi,yi] již byl nakreslen a my máme rozhodnou o poloze
následujícího. Na výběr máme ze dvou možností [xi+1,yi] a
[xi+1,yi+1]. Vzdálenost těchto dvou bodů od skutečného bodu
úsečky je na obraázku 10 vyznačena jako d1 a d2. Pro
skutečnou souřadnci y platí:

     y = m (xi + 1) + b.

Pak platí:

     d1 = y - yi       = m (xi + 1) + b - yi

     d2 = yi + 1 - y   = yi + 1 - m (xi + 1) - b

Rozdíl těchto dvou vzdáleností je:

     ëd = d1 - d2 = 2m (xi + 1) - 2yi + 2b - 1    ( 3 )

Podle proměné ëd můžeme určit, který ze dvou pixelů leží
blíže skutečnému umístění usečky. Kladná hodnota ëd, znamená
že d1 > d2 a tudíž bližší je pixel o souřadnicích
[xi+1,yi+1]. Naopak záporná hodnota ëd (d1 < d2) vybere
pixel o souřadnicích [xi+1,yi]. Není tedy důležitá hodnota
ëd, ale její znaménko. Bohužel ëd není díky m celočíslená.
Celou rovnici 3 však můžeme vynásobit ëx a dostáváme:

     pi = ëd ëx = 2 ëy xi - 2 ëx yi + 2 ëy + ëx (2b - 1),  ( 4 )

kde 2 ëy + ëx (2b - 1) je konstanta, která bude při
následujících úpravách vyloučena. Hodnotu p, podle jejíhož
znaménka určujeme polohu následujících pixelů nazveme
predikcí. Predikci následujcího bodu pi+1 můžeme zapsat
jako:

     pi+1 = 2 ëy xi+1 - 2 ëx yi+1 + konstanta        ( 5 )

Rovnice 4 a 5 můžeme odečíst, vyjádříme tak pi+1 pomocí pi:

     pi+1 = pi + 2 ëy - 2 ëx (yi+1 - yi),            ( 6 )

za předpokladu, že xi+1 = xi + 1.
     Následující vztahy vyjadřují závislost hodnoty pi+1 na
hodnotě pi:
   /---------------------------------------------\
   | pi < 0      =>    pi+1 = pi + 2 ëy          |
   |---------------------------------------------|
   | pi ň 0      =>    pi+1 = pi + 2 ëy - 2 ëx   |
   \---------------------------------------------/
První hodnota predikce p1 se získá dosazením počátečního
bodu úsečky do rovnice 4. Dostaneme p1 = 2 ëy - ëx.
     Predikce tvoří základní kritérium pro výběr pixelů
tvořících rastrový obraz úsečky. Hodnotu pi pro každý pixel
postupně aktualizujeme podle jednoduchých vztahů v tabulce.
Znaménko predikce určuje polohu následujícího pixelu. Pokud
je predikce záporná, y souřadnice následujícího pixelu se
nemění. Pokud je kladná, zvětší se o jedna.
     V případě, kdy je směrnice větší než jedna zaměníme
souřadnice x a y a algoritmus zůstane stejný. Pokud je
směrnice úsečky záporná, jedna ze souřadnic se zmenšuje a
zbytek postupu je shodný.

                     Příklad na závěr

     Některé zde uvedené poznatky shrneme do ukázkového
programu. Je jím jednoduchá grafická knihovna, která dovede
pouze kreslit čáry různými barvami a různým způsobem je
kombinovat s již nakreslenými objekty. Knihovna je ve tvaru
unity pro Turbo Pascal, což je pro většinu z vás asi
nejdostupnější a nejsrozumitelnější programovací jazyk. Pro
dosažení vysoké rychlosti jsou její klíčové části napsáné
v assembleru.

Unit SGraph;

interface

Const
  { Konstanty pro typ kreslení }
  NormalPut = 0;              { Přepisování }
  ANDPut    = 8;              { Logické AND }
  ORPut     = 16;             { Logické OR }
  XORPut    = 24;             { Logické XOR }

{ Hlavičky exportovaných procedur }
procedure Line( X1, Y1, X2, Y2: word);  { Kreslení čáry }
procedure InitGraph;                    { Inicializace grafiky }
procedure CloseGraph;                   { Obnovení původního videomódu }
procedure SetColor( Color: byte);       { Nastavení barvy pro kreslení čar }
procedure SetWriteMode( Mode: byte);    { Volba druhu kombinace kreslených a
                                          již zobrazených dat }

implementation

Const
  ActColor: byte = 15;          { Proměná sloužící k uložení aktuální barvy
                                  Standardně je nastavena bílá barva }
  WriteMode: byte = 0;          { Zapisovací režim 0 = přepisování
                                                   8 = AND
                                                  16 = OR
                                                  24 = XOR          }
  BytesPerRow = 80;             { Počet byte v jedné řádce (640/8 = 80) }
  IsGraphicsMode: boolean
                  = false;      { Indikace zda jsme v grafice }
  PixelsPerByte      = 3;            { Počet bodů v jednom bite udaný
                                       v bitových posuvech }
  ModMask            = 7;            { Bitová maska pro získání MOD 8 (zbytku
                                       po dělení osmi }
  BMask              = 128;          { Bitová maska pro kreslení bodu }
  VRAMSegment        = $a000;        { Segment počátku obrazové paměti }
  GraphicsAR         = $3ce;         { Port Graphics 1 and 2 Address Register }
  ModeRegister       = 5;            { Index registru zapisovacího módu }
  DataRotate         = 3;            { Index registru pro rotaci a kombinaci dat }
  BitMask            = 8;            { Index registru bitové masky }

var
   OldVMode: byte;                   { Proměná sloužící pro uchování čísla
                                       zobrazovacího režimu }
   delta: word;                      { Pomocné proměné pro uchování 2dx nebo 2dy}

procedure Line( X1, Y1, X2, Y2: word); assembler;
{ Kreslí čáru z X1, Y1 do X2, Y2 }
const
     neg_dx     = 2;            { Znaménko od delta x }
     neg_dy     = 4;            { Znaménko od delta y }

asm
        push    BP              { Uchování registrů BP }
        push    DS              {                 a DS }

        mov     AX, X1          { Načtení souřadnic do registrů procesoru }
        mov     BX, Y1
        mov     CX, X2
        mov     DX, Y2

        xor     BP, BP          { Smazání registru BP }
        mov     DI, CX          { Spočítání hodnoty deltax }
        sub     DI, AX
        jg      @p_dx           { Je deltax kladné ? }
        or      BP, neg_dx      { ne => ulož znaménko do BP }
        neg     DI              {       absolutní hodnota DI tj. deltax }

  @p_dx:
        mov     SI, DX          { Určení hodnoty deltay }
        sub     SI, BX
        jg      @p_dy           { Je deltay kladné ? }
        or      BP, neg_dy      { ne => ulož znaménko do BP }
        neg     SI              {       absolutní hodnota delaty }

  @p_dy:
        cmp     DI, SI          { Porovnání deltax a deltay }
        jb      @vertical_dir   { deltax > deltay <=> absolutní hodnota směrnice > 1
                                                          => vertikální směr
                                  deltax <= deltay <=> absolutní hodnota směrnice <= 1
                                                          => horizontální směr          }

  @horizontal_dir:
        test    BP, neg_dx      { Porovnání X1 a X2 }
        jz      @hor_init       { Je směr zprava doleva ? }
        xchg    AX, CX          { ne => vyměn souřadnice počátku a konce čáry }
        xchg    BX, DX
        xor     BP, neg_dy      { uprav znaménko deltay }

  @hor_init:                    { Výpočet adresy prvního bodu }
        mov     CX, AX          { Do CX X1 }
        xchg    AX, BX          { BX = X1, AX = X2 }
        mov     DX, BytesPerRow        { DX = Počet byte na jedné řádce (80) }
        mul     DX                     { AX:DX = Y1 * 80 }
        shr     BX, PixelsPerByte      { BX = BX div 8 }
        add     BX, AX                 { V BX je offset prvního bodu čáry }

        mov     AX, DS                 { Do ES přesuneme DS }
        mov     ES, AX

        mov     AX, VRAMSegment        { Segment obrazové paměti do DS }
        mov     DS, AX
        mov     DX, GraphicsAR            { Port adresového registru grafického kontroleru }
        mov     AX, ModeRegister + $0200  { Nastaví zapisovací mód 2 }
        out     DX, AX

        mov     AL, DataRotate            { Nastaví způsob kombinace dat z CPU }
        mov     AH, ES: WriteMode         { s latch-registry }
        out     DX, AX

        and     CX, ModMask             { CX = X1 and 7 }
        mov     AH, BMask               { AH = 80h, tj. nastaven je levý pixel }
        shr     AH, CL                  { Rotace AH. Po rotaci je v AH bitová maska bodu }

        mov     DX, BP                  { Uchová znaménka deltax a deltay v DX }

        mov     CX, DI                  { CX = deltax }
        inc     CX                      { CX = deltax - 1, tj. počet bodů na čáře }
                                        { Výpočet predikce do BP: }
        shl     SI, 1                   { SI = 2*deltay }
        mov     BP, SI                  { BP = 2*deltay }
        sub     BP, DI                  { BP = 2*deltax - deltay }
        shl     DI, 1                   { DI = 2*deltax }

        mov     ES: delta, DI           { Uložení hodnoty 2*deltax }

        mov     DI, BX                  { Do DI offset adresy 1. bodu }

        test    DX, neg_dy              { Určení směru }
        pushf                           { Uchování flagů }
        mov     DX, GraphicsAR          { Nastaví index graf. kontroleru na Bit mask reg. }
        mov     AL, BitMask
        out     DX, AL
        inc     DX                      { DX = datový port graf. kontroleru }
        popf                            { Obnoví flagy s výsledkem testu směru }
        jnz     @hor_neg_dy_init        { Rozskok, podle znaménka směrnice }

  @hor_pos_dy_init:                     { Kladná směrnice, y souřadnice je zvyšována }
        mov     BL, ES: ActColor        { BL = Barva čáry }
        mov     BH, BL                  { BH = Barva čáry }
        mov     AL, AH                  { AL = Bitová maska prvního bodu }

  @hor_pos_dy:
        cmp      BP, 0                  { Test predikce }
        jng      @hor_pos_dy_L1
        out      DX, AL                 { predikce >= 0 }
        mov      BL, BH                 { BL = Barva bodu }
        xchg     [DI], BL               { Nakresli bod }
        xor      AL, AL                 { Vynuluje střádanou masku }
        sub      BP, ES: delta          { Upraví predikaci P = P - 2*deltax }
        add      DI, BytesPerRow        { Zvětšení y souřadnice }
  @hor_pos_dy_L1:
        add      BP, SI                 { Úprava predikce P = P + 2*deltay }
        ror      AH, 1                  { Zvětšení x souřadnice }
        jc       @hor_pos_dy_L2         { Přesáhli jsme jeden byte ? }
        or       AL, AH                 { Uprav masku byte }
        loop     @hor_pos_dy            { Další bod }
        jmp      @hor_pos_dy_lastbyte   { Došli jsme do koncového bodu }
  @hor_pos_dy_L2:
        out      DX, AL                 { Nastaví novou bitovou masku }
        mov      BL, BH
        xchg     [DI], BL               { Nakreslí body jednoho byte }
        mov      AL, AH                 { Inicializuje bitovou masku }
        inc      DI                     { Zvětší x souřadnici }
        loop     @hor_pos_dy            { Byl to poslední bod ? }
        jmp      @l_done                {        => konec kreslení čáry }
  @hor_pos_dy_lastbyte:
        xor      AL, AH                 { Poslední bit je neplatný, odstranit }
        out      DX, AL                 { Nastavení bitové masky }
        mov      BL, BH                 { BL = Barva čáry }
        xchg     [DI], BL               { Nakreslení bodu }
        jmp      @l_done                { Ukončení kreslení čáry }

  @hor_neg_dy_init:                     { Záporná směrnice, y souřadnice je zmenšována }
        mov     BL, ES: ActColor        { BL = Barva čáry }
        mov     BH, BL                  { BH = Barva čáry }
        mov     AL, AH                  { AL = Bitová maska prvního bodu }

  @hor_neg_dy:
        cmp      BP, 0                  { Test predikce }
        jng      @hor_neg_dy_L1
        out      DX, AL                 { predikce >= 0 }
        mov      BL, BH                 { BL = Barva bodu }
        xchg     [DI], BL               { Nakresli bod }
        xor      AL, AL                 { Vynuluje střádanou masku }
        sub      BP, ES: delta          { Upraví predikaci P = P - 2*deltax }
        sub      DI, BytesPerRow        { Zmenšení y souřadnice }
  @hor_neg_dy_L1:
        add      BP, SI                 { Úprava predikce P = P + 2*deltay }
        ror      AH, 1                  { Zvětšení x souřadnice }
        jc       @hor_neg_dy_L2         { Přesáhli jsme jeden byte ? }
        or       AL, AH                 { Uprav masku byte }
        loop     @hor_neg_dy            { Další bod }
        jmp      @hor_neg_dy_lastbyte   { Došli jsme do koncového bodu }
  @hor_neg_dy_L2:
        out      DX, AL                 { Nastaví novou bitovou masku }
        mov      BL, BH
        xchg     [DI], BL               { Nakreslí body jednoho byte }
        mov      AL, AH                 { Inicializuje bitovou masku }
        inc      DI                     { Zvětší x souřadnici }
        loop     @hor_neg_dy            { Byl to poslední bod ? }
        jmp      @l_done                {        => konec kreslení čáry }
  @hor_neg_dy_lastbyte:
        xor      AL, AH                 { Poslední bit je neplatný, odstranit }
        out      DX, AL                 { Nastavení bitové masky }
        mov      BL, BH                 { BL = Barva čáry }
        xchg     [DI], BL               { Nakreslení bodu }
        jmp      @l_done                { Ukončení kreslení čáry }


  @vertical_dir:                        { Výpočet adresy 1. bodu }
        test    BP, neg_dy              { Kreslíme shora dolů ? }
        jz      @vert_init
        xchg    AX, CX                  { Prohození souřadnic }
        xchg    BX, DX
        xor     BP, neg_dx              { Úprava znaménka }

  @vert_init:
        mov     CX, AX                  { CX = X1 }
        xchg    AX, BX                  { AX = Y1, BX = X1 }
        mov     DX, BytesPerRow         { DX = Počet byte na řádku (80) }
        mul     DX                      { AX:DX = Y1 * 80 }
        shr     BX, PixelsPerByte       { BX = X1 div 8 }
        add     BX, AX                  { BX = Offset adresy 1. bodu }

        mov     AX, DS                  { Uloží datový segment do ES }
        mov     ES, AX

        mov     AX, VRAMSegment         { DS = Segment obrazové paměti }
        mov     DS, AX
        mov     DX, GraphicsAR             { Port adresového registru graf. kontroleru }
        mov     AX, ModeRegister + $0200   { Nastaví zapisovací mód 2 }
        out     DX, AX

        mov     AL, DataRotate          { Nastavení způsobu kombinování dat z CPU }
        mov     AH, ES: WriteMode       { s latch-registry                        }
        out     DX, AX

        and     CX, ModMask             { CX = X1 and 7 }
        mov     AH, BMask               { Bitová maska pro levý bod (80h) }
        shr     AH, CL                  { Rotací vytvoří správnou masku }

        mov     DX, BP                  { Uloží znaménka do DX }

        mov     CX, SI                  { CX = Délka čáry }
        inc     CX
                                        { Určení predikace }
        shl     DI, 1                   { DI = 2*deltax }
        add     BP, DI                  { BP = 2*deltax }
        sub     BP, SI                  { BP = 2*deltax - deltay }
        shl     SI, 1                   { SI = 2*deltay }

        mov     ES: delta, SI           { uloží 2*deltay }
        mov     SI, DI                  { SI = 2*deltax }

        mov     DI, BX                  { DI = Offset adresy 1. bodu }

        test    DX, neg_dx              { Zjištění směru }
        mov     DX, GraphicsAR          { DX = adresový port graf. kontroleru }
        jnz     @vert_neg_dx_init       { Rozskok podle znaménka směrnice }

  @vert_pos_dx_init:                    { Kladná směrnice }
        mov     BL, ES: ActColor        { BL = Barva čáry }
        mov     BH, BL                  { BH = Barva čáry }
        mov     AL, BitMask             { Index Bit mask registru }
        out     DX, AX
        inc     DX                      { DX = datový registr graf. kontroleru }
        mov     AL, AH                  { AL = Bitová maska }

  @vert_pos_dx:
        mov     BL, BH                  { BL = Barva čáry }
        xchg    [DI], BL                { Nakreslí bod }
        cmp     BP, 0                   { Test predikce }
        jng     @vert_pos_dx_L1
        sub     BP, ES: delta           { P >= 0, P = P - 2*deltay }
        ror     AL, 1                   { Zvyš souřadnici x }
        adc     DI, 0                   { Přesun do dalšího byte }
        out     DX, AL                  { Nastav novou bitovou masku }

  @vert_pos_dx_L1:
        add     BP, SI                  { P = P + 2*deltax }
        add     DI, BytesPerRow         { Zvyš souřadnici y }
        loop    @vert_pos_dx            { Další bod }
        jmp     @l_done                 { Konec čáry }

  @vert_neg_dx_init:                    { Záporná směrnice }
        mov     BL, ES: ActColor        { BL = Barva čáry }
        mov     BH, BL                  { BH = Barva čáry }
        mov     AL, BitMask             { Index Bit mask registru }
        out     DX, AX
        inc     DX                      { DX = datový registr graf. kontroleru }
        mov     AL, AH                  { AL = Bitová maska }

  @vert_neg_dx:
        mov     BL, BH                  { BL = Barva čáry }
        xchg    [DI], BL                { Nakreslí bod }
        cmp     BP, 0                   { Test predikce }
        jng     @vert_neg_dx_L1
        sub     BP, ES: delta           { P >= 0, P = P - 2*deltay }
        rol     AL, 1                   { Zmenši souřadnici x }
        sbb     DI, 0                   { Přesun do dalšího byte }
        out     DX, AL                  { Nastav novou bitovou masku }

  @vert_neg_dx_L1:
        add     BP, SI                  { P = P + 2*deltax }
        add     DI, BytesPerRow         { Zvyš souřadnici y }
        loop    @vert_neg_dx            { Další bod }
        jmp     @l_done                 { Konec čáry }

  @l_done:
        mov     DX, GraphicsAR          { Smazání bitové masky }
        mov     AX, BitMask
        out     DX, AX
        mov     AX, ModeRegister
        out     DX, AX                  { Nastaví zapisovací mód 0 }
        mov     AX, DataRotate
        out     DX, AX                  { Vypne rotyci dat, standardní kombinování dat }

  @exit:
        pop      DS                     { Obnoví obsah registrů DS }
        pop      BP                     {                       BP }
end;

procedure SetMode( Mode: byte); assembler;
{ Nastaví aktuální zobrazovací režim pomocí služby 00h BIOS }
asm
        mov     AH, 00h
        mov     AL, Mode
        int     10h
end;
function GetMode: byte; assembler;
{ Službou BIOS zjistí zobrazovací režim }
asm
        mov     AH, 0fh
        int     10h
end;

procedure InitGraph;
{ Inicializace grafiky }
begin
  if not IsGraphicsMode then
    OldVMode := GetMode;        { Uloží číslo aktivního zobrazovacího režimu }
  SetMode( $12);                { Nastaví zobrazovací režim 640 x 480, 16 barev }
  IsGraphicsMode := True;       { Nastaví příznak přepnutí do grafiky }
end;

procedure CloseGraph;
{ Ukončí grafický režim }
begin
  if IsGraphicsMode then SetMode( OldVMode);    { Obnoví původní zobr. režim }
  IsGraphicsMode := False;
end;

procedure SetColor( Color: byte);
{ Nastaví barvu použitou pro kreslení }
begin
  ActColor := Color;
end;

procedure SetWriteMode( Mode: byte);
{ Nastaví zapisovací režim pro kreslení }
begin
  WriteMode := Mode;
end;

begin
end.


Použití grafické knihovny demonstruje následující krátký
program, který navíc využívá některé z registrů karty VGA
k snadné implementaci vertikálního scrolování dvěma směry
zároveň. Všimněte si, že scrolování bude stejně rychlé na
všech počítačích, bez ohledu na jejich rychlost. Je to
způsobeno tím, že jednotlivé animační kroky jsou odděleny
čekáním na vertikální zpětný chod, který je v tomto
zobrazovacím režimu generován s frekvencí přibližně 60 Hz.
Program pro svojí správnou činnost potřebuje kartu VGA.


Uses
    Sgraph,                     { Použvá jednotku SGraph }
    Crt;                        {               a Crt    }

procedure SetStartAdr( Adr: word); assembler;
{ Procedura nastavující registry CRTC počáteční adresa index 0ch a 0dh }
asm
        mov     DX, 3dah         { Vstupní stavový registr 1 }
  @wait_retrace:
        in      AL, DX
        and     AL, 8
        jz      @wait_retrace    { Čekání na vertikální zpětný chod }
  @wait_display:
        in      AL, DX
        and     AL, 8
        jnz     @wait_display    { Čekání na aktivní zobrazovací signál }

        mov     cx, Adr
        mov     dx, 03d4h       { CRTC adresový registr }
        mov     al, 0ch         { index počáteční adresa - vyšší byte }
        mov     ah, ch
        out     dx, ax
        inc     al              { index počáteční adresa - nižší byte }
        mov     ah, cl          { nižší byte adresy }
        out     dx, ax
end;

procedure SetLineComp( Line: word); assembler;
{ Nastaví registr CRTC porovnání řádky včetně 9. a 10. bitu }
asm
        mov     DX, 03d4h       { CRTC adresový registr }
        mov     AL, 18h         { index registru porovnání řádky }
        out     DX, AL
        mov     CX, Line
        mov     AL, CL
        inc     DX
        out     DX, AL          { zápis 8 nižších byte čísla řádky }
        dec     DX
        mov     AL, 07h         { index registru přetečení }
        out     DX, AL
        inc     DX
        in      AL, DX          { čte nastavení registru přetečení }
        mov     CL, CH
        and     CL, 1
        shl     CL, 4
        and     AL, 11101111b   { smaže 9. bit čísla řádky pro porovnání }
        or      AL, CL          { nastaví 9. bit }
        out     DX, AL          { zapíše registr }
        dec     DX
        mov     AL, 09h         { registr počet řádek na znak - obsahuje 10. bit }
        out     DX, AL
        inc     DX
        in      AL, DX
        shr     CH, 1
        and     CH, 1
        shl     CH, 6
        and     AL, 10111111b   { smazání staré hodnoty 10. bitu }
        or      AL, CH          { nová hodnota 10. bitu }
        out     DX, AL          { zapsání registru }
end;

const
     MaxX = 639;                { Maximální hodnota souřadnice x }
     MaxY = 479;                { Maximální hodnota souřadnice y }

var
   i: word;                     { Pomocné proměnné }
   x: word;

begin
  InitGraph;                    { Inicializace grafiky }
  SetWriteMode( XORPut);        { Nastavení zapisovacího způsobu }

  { Nakreslení jednoduchého obrazce přes celou obrazovku }
  for x := 0 to MaxX do
    Line( MaxX div 2, MaxY div 2, x, 0);
  for x := 0 to MaxY do
    Line( MaxX div 2, MaxY div 2, MaxX, x);
  for x := MaxX downto 0 do
    Line( MaxX div 2, MaxY div 2, x, MaxY);
  for x := MaxY downto 0 do
    Line( MaxX div 2, MaxY div 2, 0, x);

  { Jednoduchá animace }
  for i := 0 to MaxY div 2 do
  begin
    SetLineComp( i);                    { Scrolování dolů pomocí porovnání řádky }
    SetStartAdr( i* 80);                { Zároveň scrolování nahorů pomocí počáteční adresy }
  end;

  { Obrácený směr animace }
  for i := MaxY div 2 downto 0 do
  begin
    SetLineComp( i);
    SetStartAdr( i* 80);
  end;

  Delay( 300);                  { Malá pauza nakonec }
  SetLineComp( 1023);           { Nastavení standardní hodnoty }
  CloseGraph;                   { Ukončení grafiky, předchozí zobr. režim }
end.


Literatura:

[1]  Kliewer, B.D. - EGA/VGA A programmer's reference guide
     371 stran, McGraw-Hill Publishing Company, 1990

[2]  Žára, J. - Počítačová grafika - principy a algoritmy
     472 stran, Grada a.s., 1992

[3]  Brown, R. - Interrupt List - freewarová el. příručka
     1993

[Obsah]


Copyright © Jiří Kosek