Graues wird noch grauer & Buntes noch bunter: ‘Echte’ Zwischenfarben per Software - ohne Hertz-Flimmern

Sie glauben, Ihr s/w-Monitor sollte Grau nicht nur als Muster „Pfeffer & Salz“ darstellen? Sie wünschen sich Ihren Farbmonitor etwas bunter - aber doch bei gewohnter Auflösung? Bitte sehr, können Sie haben. Der Lötkolben bleibt dabei kalt, nur etwas Programmierarbeit ist angesagt: Anbei ein Beispiel aus der Serie vollständiger Demo-Programme zum Zwecke der moralischen Erbauung.

Gleich vorweg möchte ich aber die zwei Voraussetzungen nennen, die Ihr System erfüllen muß. damit Sie keine Enttäuschung erleben.

  1. Die Bildwechselfrequenz muß mindestens 60 Hz (besser 70 Hz) betragen
  2. Das Betriebssystem (& die Hardware!) muß Screen-Switching unterstützen

Mit weniger als 60 Hz werden Sie nämlich sonst auch nnt der hier vertretenen Methode das Flimmern nicht in den Griff bekommen, und ohne eine SetScreen-Funktion werden Sie zumindest das bei hegende Modula-Programm nicht anpassen können. Es läuft auf dem ATARI ST (in „hoher" Auflösung), aber prinzipiell sollte auch auf einem PC dergleichen möglich sein.

Am Anfang der Welt gab's nur Licht und Finsternis [1]. Vielleicht ist das der Grund dafür, daß das bekannte Desktop-Grau mit der Lupe besehen ein Schachbrettmuster aus Schwarz und Weiß ist („Pfeffer & Salz“). Im Gegensatz dazu soll es hier um echtes Grau gehen. Mit dem Wort „echt" meine ich - und das ist das Besondere an der hier vorgestellten Methode (die ich mal „verschränktes doppeltes Interleaving" nennen möchte), daß keine Einbuße an Auflösung eintritt - anders als beim altbekannten Dithering. Allerdings ist es nur so nur gelungen, eine einzige Graustufe ohne Flimmern zu erzeugen. Vielleicht hat ja noch jemand eine zusätzliche Idee, um dies auszubauen. Bei vier Grundfarben bekommt man 6 weitere Zwischenfarben, bei 16 Grundfarben sind es schon 104 usw. Wem dies nicht reicht, der kann ja Dithering noch zusätzlich anwenden ... Das Demo-Programm beschränkt sich allerdings auf das eine echte Grau und eine 1x2-Matrix für zwei weite re Graustufen.

Die erste Idee, die ich zu diesem Thema hatte, war zugegebenermaßen etwas naiv: Da der ATARI-Monitor in der s/w-Ausführung SM 124 eine Bildwechselfrequenz von ca. 72 Hz besitzt, die sog. Filmfrequenz von 24 Hz aber nur ein Drittel davon betrugt, mußte es doch möglich sein, durch Überlagerung von zwei oder mehr Bildern echte Grauwerte zu erzeugen, indem man mit Hilfe der XBIOS-Funktion SetScreen physikalische Bildschirme im VBI umswitcht...

Machen wir aber den 1. Schirm ganz einfarbig (z.B. schwarz) und den 2. Schirm in einer anderen Farbe ebenfalls ganz uni (z.B. weiß), können wir uns an einem herrlichen 36-Hz-Flackern erfreuen. Ohne uns davon beeindrucken zu lassen, nehmen wir als nächstes eine Anleihe beim TV: benutzen wir die sog. Zeilenverschränkung (Interleaving); belegen wir also beide Bildschirme mit je einer waagerechten Schraffur in den beiden gewählten Farben, die aber um die halbe Schrittweite gegeneinander versetzt sind (so daß die Linien immer, auf Lücke' treffen). Das Ergebnis ist, wie wohl jeder erwartet, zwar grau -aber eben die altbekannte ,Flimmerkiste' (Test 1 im Programm, links), und damit sind ATARIaner schon lange nicht mehr zufrieden.

Zwingt Grau rein

Sie ahnen bestimmt schon den nächsten Schritt: Was wir in der Vertikalen gerade angewendet haben, wollen wir jetzt auch noch horizontal zusätzlich draufsetzen: Doppelt gestreift heißt bekanntlich kariert, also kommt je ein Pfeffer & Salz-Muster (das Desktop-Grau) in beide Schirme, aber ,auf Lücke' plaziert: Und siehe da, das neue, leckere Grau erscheint! Etwas nachregeln an den Knöpfen für Kontrast und Helligkeit beseitigt noch den letzten Flimmer.

Mit den vier Farben Yellow, Magenta, Cyan & Weiß bekämen Sie nach dem gleichen Prinzip zusätzlich: Hellgelb, Rosa und Helltürkis sowie Rot, Grün und Blau. Bei 16 Grundfarben müssen Sie etwas bei der Auswahl aufpassen, damit nicht Mischfarben doppelt Vorkommen - Ergebnis: 120 Farben insgesamt. Bei 256 Farben als Basis (,TT low') resultieren bis zu maximal 32384 Mischungen! Und alles noch in der gewohnten Auflösung!

Weil aber nun (im Fall s/w) eine einzige Graustufe doch etwas mickrig ist, nehmen wir noch eine kleine Matrix hinzu. Bekanntlich kann man auf jedem Monitor, der zu nur 2 ,Farben' fähig ist, dazwischen liegende Farbstufen durch Vergröberung der Auflösung erzeugen. Besteht ein Bild-Pixel aus einer 2x2-Matrix von Schirm-Pixeln, so erhält man 5 Stufen:

OO *O O* O* **OO OO *O ** **. 

Eine 1x2-Matrix brächte nur 3 Stufen:

OO O* **.

Füllt man aber 3 Schattierungen ein, gibt’s ebenfalls 5 Stufen:

OO O+ ++ +* **.

Die Auflösung ist also gegenüber der 2x2-Matrix bei gleicher Stufenzahl doppelt so hoch! Die mittlere ST-Auflösung kann damit lässig dargestellt werden, und auch für ein graues Preview der 5 Grundfarben eines 4-Farbdruckers reicht’s! Hat da gerade jemand ,ich weiß' gesagt?

Leider führt die 4x4-Matrix nur zu 9 Graustufen. Das wäre anders, könnte man das echte Grau ,unmittig' wählen. Die naheliegende Idee, nicht in jedem VBI zwischen den Schirmen umzuschalten, sondern ,humpelnd' (also 112112112...), hat bei meinen Versuchen aber zwei Graustufen (nämlich bei 1/3 und 2/3 der max. Helligkeit) ergeben, die eher greulich als grau waren, mit einem unerträglichen Flackern (und die 4x4-Matrix brächte doch nur 12 Stufen). Vielleicht habe ich die richtige Verschränkung noch nicht gefunden? Jedenfalls scheint die entscheidende physiologische Größe das Produkt aus Frequenz und Ortsfrequenz zu sein - bei 100 Hz Bildfrequenz wär’s kein Problem mehr, und bei entsprechend kleinerem Pitch wohl auch nicht.

Ganz verwegene TT-Besitzer können ja mal eben demonstrieren, daß ihr Gerät zu ,True Color' fähig ist: Die obigen 32640 Farben in Kombination mit einer 1x2-Matrix dürften dafür auf dem normalen Farbmonitor ausreichen (Resultat: 320x 240 Pixel in einer Auswahl aus bis zu 10,6 Mio. Farben, also bereits unsinnig viel -denn es gehen davon ja nur maximal 76800 auf den Schirm). Ich gebe allerdings zu, daß die Verwaltung dieser Farbfülle durchaus nicht trivial ist.

Programmdetaiis

Das Rahmenprogramm ist etwas, quick & dirty' gestrickt - aber für einen Test treibt man nun mal nicht so viel Aufwand (und gedruckt soll das Listing ja auch nicht zu lang werden). Die übliche GEM-Eröffnung (ApplInit, Open Virtual Workstation etc.pp) habe ich mir schlicht gespart; man muß darum das Programm vom Desktop aus starten, damit es dessen AES-Variable mitbenutzen kann (das sollten Sie aber für Ihre eigenen Programme auf gar keinen Fall zur Regel machen). Auch die Prozedur FillWords ist nicht gerade vom feinen Stil, nun ja.

Dafür läßt sich das lokale Modul Gray sehr leicht auslagern und zu einem Bibliotheksmodul ausbauen. Es besteht selbst aus drei zu ihm lokalen Modulen: Das erste dient mehr dem komfortablen Umgang mit CopyRasterOpaque. Im zweiten werden MFDBs für die Bildschirme definiert, der nötige Speicher alloziert und Umschalter bereitgestellt; das zugrundeliegende Konzept ist das eines ,aktiven Bildschirms' sowie eines ,Scraps‘ im Hintergrund. Besonders beachten muß man lediglich, daß der Shifter im ST den Anfang des Video-RAMs auf einer ,Blattgrenze' des Speichers erwartet.

Das dritte lokale Modul geht etwas mehr ans Eingemachte: Die systemnahen Eigenschaften von Modula werden ausgenutzt (schließlich wurde die Sprache ja ursprünglich mal konzipiert, um Betriebssysteme zu schreiben - es ist mir völlig unverständlich, warum dafür immer noch so viel Assembler benutzt wird). Dennoch glaube ich, daß eine Übersetzung nach C oder sogar nach BASIC problemlos möglich ist. Man sieht sehr gut, wie Hochsprache und Maschinencode gemischt werden können (das geht nur bei fleißiger Benutzung eines Disassemblers - beim guten, alten TDI-Modula das Utility ,DECLNK‘). Anstatt sich in die Queue des Vertical Blank Interrupt Handlers einzuhängen, also ,hinten anzustellen' (wie von den TOS-Entwicklern normalerweise vorgesehen), drängelt sich das Programm aber vor: Der Vektor des Handlers wird verbogen (mit guten Konsequenzen für die Cursor-Darstellung; die Methode ist in diesem Fall durchaus legitim). Dazu wird das XBRA-Protokoll eingesetzt (vorgeschlagen von Braner/Reschke; nach dem neuesten Stand der Diskussion müssen Sie aber die XBRA-Strukturen ins DATA-Segment verlagern [2]). Nachdem das Programm die eigenen Belange erledigt hat, wird anschließend der alte Handler (mitsamt der Queue) aufgerufen. Die Prozeduren zum Ein- und Ausklinken sind allerdings noch verbesserungsfähig - lohnt es sich, die Prozedur GrayCode ganz in Inline-Code auszuführen? (Vielleicht könnte man 50 Takte sparen.)

Das Hauptprogramm geht bieder geradeaus. Natürlich hätte man VDI-Füllmuster verwenden können... Test 1 läßt eine Palette von Grautönen erscheinen, von denen insbesondere drei interessant sind: links außen das ,Fernseh-Grau‘, rechts außen meine flimmerfreie Lösung, in der Mitte u.a. der flackernde Einstieg. In Test 2 toggeln die gleichen Schirme, aber im Zeitverhältnis 1:2. Test 3 zeigt noch einmal die drei flimmerfreien Stufen plus zwei Pfeffer-&-Salz-Zusätze, die bei etwas zurückgeregeltem Kontrast flimmerfrei werden. Damit sind fünf Graustufen erreicht, obwohl die Auflösung nur um die Hälfte reduziert wurde, also doppelt so hoch ist wie beim 2x2 Dithering.

Literatur:

[1] Genesis 1,3 [2] ST-Computer 12/91, S.156

MODULE GrayTest;
(* by ROLF SCHRADER, (c) MAXON Computer 1993 *)

FROM   SYSTEM IMPORT ADDRESS;
IMPORT SYSTEM, BIOS, XBIOS, AESGraphics, GEMVDIbase, VDIRasters;

MODULE Gray;
(* Dieser Modul stellt 2 Bildschirme #0, #1 und dazugehörige Prozeduren zu*)
(* deren Manipulation zur Verfügung. Der Standardschirm {norm. GEM-Video- *)
(* RAM) wird Scrap (normal #0). SetScreen & FlipScreen ermöglichen, in die*)
(* Schirme mit gew. GEM-Fktnen. zu schreiben. AS ist der aktuelle, SS der *)
(* Scrap-Schirm; 8ung: memPtr nur zum Bitpopeln.*) 
(* Nach Benutzung dieses Moduls muß man bei Prg.-Ende EndScreen aufrufen! *)

FROM    SYSTEM      IMPORT ADR, ADDRESS, CODE;
FROM    XBIOS       IMPORT ScreenPhysicalBase, ScreenLogicalBase, SetScreenBase, SuperExec;
FROM    AESGraphics IMPORT GrafHandle;
FROM    GEMVDIbase  IMPORT BigPxyArrayType;
FROM    VDIRasters  IMPORT CopyRasterOpaque;

EXPORT  GraySwitch, ModBox, FlipActS, InvScreen, SetScreen, EndScreen,
        ASmemPtr, SSmemPtr, BPZ;

CONST   WMAX = 640; HMAX = 400;
        XMAX = 639; YMAX = 399;
        BPZ  = WMAX DIV 8; (* Bytes pro Zeile *)
        SCREENSIZE = HMAX * (WMAX DIV 8);
CONST   SPTRADR = 45EH; VBLHADR = 70H;  (* System-Adressen *)

VAR     ASadr, SSadr: ADDRESS;  (* -> MFDBs für CopyRas. *) 
        ASmemPtr, SSmemPtr: POINTER TO ADDRESS; (* akt. Schirm & Scrap. *)

MODULE  Manisch;
IMPORT  GrafHandle, BigPxyArrayType,
        CopyRasterOpaque, ASadr, SSadr,
        FlipVideo, XMAX, YMAX;
EXPORT  ClrScrap, ClrScreen, SaveScreen, ReScreen, 
        InvScreen, ModBox;

VAR     ja:     INTEGER; (* Ja-Sager *)
        GDev:   INTEGER; (* VDI-Handle f. Graph. Dev.*) 
        Ganz:   BigPxyArrayType;
        REKoo:  BigPxyArrayType;

PROCEDURE SetPoints (ax,ay,bx,by,Ax,Ay,Bx,By:INTEGER;VAR P:BigPxyArrayType); 
BEGIN 
    P[0] := ax; P[1] := ay; P[2] := bx; P[3] := by;
    P[4] := Ax; P[5] := Ay; P[6] := Bx;
    P[7] := By; END SetPoints;

PROCEDURE ClrScrap;
BEGIN CopyRasterOpaque (GDev, 0, Ganz, SSadr, SSadr) END ClrScrap;
PROCEDURE ClrScreen;
BEGIN CopyRasterOpaque (GDev, 0, Ganz, ASadr, ASadr) END ClrScreen; 
PROCEDURE SaveScreen; (* Rettet den Bildschirm in den Scrapbildschirm.*) 
BEGIN CopyRasterOpaque (GDev, 3, Ganz, ASadr, SSadr) END SaveScreen; 
PROCEDURE ReScreen; (* Schreibt Scrapbildschirm zuruck in den Bildschirm*) 
BEGIN CopyRasterOpaque (GDev, 3, Ganz, SSadr, ASadr) END ReScreen;
PROCEDURE InvScreen;
BEGIN CopyRasterOpaque (GDev,12, Ganz, ASadr, SSadr);
    FlipVideo; ReScreen; FlipVideo; END 
    InvScreen; (* Ohne Flimmern! *)

PROCEDURE ModBox (ax,ay, bx,by, wrMode: INTEGER); (* wrMode -> CopyRast.O.*)
    (* M=0: AS weiß; M=-15: SS schwarz; M=3: SS->AS; M=-6: ASxorSS->SS ...*)
BEGIN SetPoints (ax,ay, bx,by, ax,ay, bx,by, REKoo);
    IF (wrMode >15) OR (wrMode < -16) THEN RETURN END;
    IF (wrMode < 0) THEN IF (wrMode = -16) THEN wrMode := -0 END;
    CopyRasterOpaque (GDev,-wrMode, REKoo, ASadr, SSadz); RETURN END (*I*); 
    CopyRasterOpaque (GDev, wrMode, REKoo, SSadr, ASadr); END ModBox;

BEGIN (* Init.*) GDev := GrafHandle (ja,ja,ja,ja); 
    SetPoints (0,0,XMAX,YMAX, 0,0,XMAX,YMAX, Ganz); END (*M*) ManiSch;


MODULE  Screens;
IMPORT  ADR, ADDRESS, WMAX, HMAX, SCREENSIZE, 
        ClrScreen, ClrScrap,
        ScreenPhysicalBase, ScreenLogicalBase, SetScreenBase,
        ASadr,ASmemPtr, SSadr,SSmemPtr;
EXPORT  SetScreen, FlipVideo, FlipScreen,
        InitScreen, EndScreen, FlipActS;
CONST   A1 = ADDRESS (-1);
TYPE    MFDBType = RECORD adr: ADDRESS; w,h,ww,koo, p1,r1,r2,r3: INTEGER; END; 
VAR     Memory: ARRAY [0..SCREENSIZE +256] OF CHAR; (* für S.*)
        BsMemAdr: ARRAY [0..1] OF ADDRESS; (* Scrap + Bildschirm.*)
        ASMB,SSMB: MFDBType;    (* MemFormDefs für AS,SS.*)

PROCEDURE SetScreen (Nr: INTEGER);  (* Setzt den aktuellen Bildschirm.*) 
BEGIN ASmemPtr^ := BsMemAdr [Nr]; END SetScreen; 
PROCEDURE FlipVideo; (* Wechselt den Video-Schirm, aber nicht die Ausgabe.*) 
BEGIN IF (ScreenPhysicalBase () = ASmemPtr^)
      THEN
            SetScreenBase (A1, SSmemPtr^, -1);
      ELSE  SetScreenBase (A1, ASmemPtr^, -1);
      END END FlipVideo;
PROCEDURE FlipScreen; (* Wechselt den Ausgabeschirm, aber nicht das Video.*) 
BEGIN IF (ScreenLogicalBase () = ASmemPtr^)
      THEN
            SetScreenBase (SSmemPtr^, A1, -1);
      ELSE  SetScreenBase (ASmemPtr^, A1, -1);
      END END FlipScreen;
PROCEDURE InitScreen; (* Setzt neuen Speicherbereich für den Bildschirm.*) 
BEGIN   SetScreenBase (BsMemAdr[1], A1, -1); ClrScreen;
        SetScreenBase (A1, BsMemAdr[1], -1); END InitScreen;
PROCEDURE EndScreen; (* Setzt wieder den Standard-Bildschirm.*) 
BEGIN   SetScreenBase (BsMemAdr[0], A1, -1); ClrScrap;
        SetScreenBase (A1, BsMemAdr[0], -1); END EndScreen;
PROCEDURE FlipActS; (* Vertauscht akt. Schirm & Scrap (Ziel-Rtg.-Umkehr) *)
VAR a: ADDRESS;
BEGIN a := ASmemPtr^; ASmemPtr^ := SSmemPtr^; 
           SSmemPtr^ := a; END FlipActS;

PROCEDURE Blattgrenze (MemAdr: ADDRESS): ADDRESS; (* Erford. f. Video-Ram!*)
BEGIN RETURN MemAdr + (256 - (MemAdr MOD 256))
END Blattgrenze;

BEGIN (* Altes Video-RAM wird Scrapschirm, wahres Bild liegt im 'Memory'. *)
    BsMemAdr[0] := ScreenLogicalBase(); (* sic! nicht PhysB.! *) 
    BsMemAdr[1] ;= Blattgrenze (ADR (Memory));
    WITH ASMB DO    adr := BsMemAdr[1];
         w := WMAX; h := HMAX; ww := W DIV 16; 
         koo := 1; pl := 1; END (*W*);
    ASadr := ADP (ASMB);  ASmemPtr := ADR (ASMB.adr);
    SSMB  := ASMB;        SSMB.adr := BsMemAdr[0];
    SSadr := ADR (SSMB);  SSmemPtr := ADR (SSMB.adr);
    SetScreen (1); (* Zuerst ist #1 der aktive Bildschirm. *)
    InitScreen; (* Bei Programmende mit EndScreen zurucksetzen! *)
END (*M*) Screens;

MODULE Dorian;
IMPORT ADDRESS, CODE, SuperExec, SPTRADR, VBLHADR, 
       ScreenPhysicalBase, SetScreenBase;
EXPORT GraySwitch;

VAR    VBHandler[VBLHADR]:  ADDRESS;
       ScreenPtr[SPTRADR]:  ADDRESS;
       Sadr0, Sadr1, OldS:  ADDRESS;
       GrayHandler:         ADDRESS;
       Count, GrauGrenze:   CARDINAL;
       OldVecPtr:           POINTER TO ADDRESS;
       xbraP, grayP:        POINTER TO LONGCARD;
       ProtPtr:             POINTER TO ARRAY [-3..-1] OF LONGCARD; 
CONST  RTS = 04E75H;   A1 = ADDRESS (-1);

PROCEDURE GraySwitch (OnOff: CARDINAL; Plane0, Plane1: ADDRESS);
(* Verbiegt den Vektor ‘VBI-List-Handler', hin und zurück! *)
BEGIN GrauGrenze := OnOff+1;
      OldS := ScreenPhysicalBase ();
 IF     (OnOff # 0) THEN Sadr0 := Plane0;
                         Sadr1 := Plane1;
        SuperExec (PROC (InstallGray));
 ELSE   SuperExec (PROC ( BleachGray));
        SetScreenBase (A1, OldS, -1); END (*E*);
        END GraySwitch;

(*$P-*) PROCEDURE InstallGray;
(* P- -> Komm. zu XBIOS.SuperExec.*)
BEGIN ProtPtr := ADDRESS (-12) + VBHandler;
 IF (ProtPtr^[-3] # xbraP^) OR 
    (ProtPtr^[-2] # grayP^) THEN 
    OldVecPtr^  := VBHandler;
    VBHandler   := GrayHandler; END (*I*);
 CODE (RTS); (* 'raus wie C *) END InstallGray; 
 (*$P=*)

(*$P-*) PROCEDURE BleachGray;
(* P- -> Komm. zu XBIOS.SuperExec.*)
BEGIN ProtPtr :=    ADDRESS (-12) + VBHandler; 
      ScreenPtr := 0 (*!!*);
  IF (ProtPtr^[-3] = xbraP^) &
     (ProtPtr^[-2] = grayP^) THEN VBHandler := OldVecPtr^; END (*I*);
 CODE (RTS); (* 'raus wie C *) END BleachGray; 
 (*$P=*)

(*$P-*) PROCEDURE GrayCode;
(* Wird von MakeXBRAgray modifiziert! *)
 BEGIN CODE (0,0,0,0,0,0); (* Platz f. XBRA-Protokoll nach Braner/Reschke.*)
    (* Einsprung: *) (* Zuerst den alten VBL-Handler auf den Stack: *) 
    CODE (02F3AH, 0FFFAH); (* move.l d(pc),-(sp);-6 (lt. XBRA genau da!) *)
    IF      (Count = 0) THEN ScreenPtr := Sadr0;
            Count:= GrauGrenze;
    ELSIF   (Count = 1) THEN ScreenPtr := Sadr1;
    ELSE                     ScreenPtr := 0; END;
    DEC     (Count); (* mod GrauGrenze *)
    CODE    (RTS) (* mit‘m alten Handler weiter! *)
    END     GrayCode; (*$P=*)

PROCEDURE MakeXBRAgray; (* Modifiziert GrayCode gemäß XBRA-Protokoll! *) 
VAR     Ch4Ptr: POINTER TO ARRAY [0..3] OF CHAR; 
BEGIN   GrayHandler := ADDRESS (GrayCode) +12; (* Einsprung-Adresse. *)
        OldVecPtr := GrayHandler -4;
        Ch4Ptr := GrayHandler -8;
        Ch4Ptr^ := 'GRAY';
        grayP := GrayHandler -8;
        Ch4Ptr := GrayHandler -12;
        Ch4Ptr^ := 'XBRA';
        xbraP := GrayHandler -12;
END MakeXBRAgray;

BEGIN (* Init.*) MakeXBRAgray; Count := 0; END 
      (* M*) Dorian;

END (*M*) Gray;

PROCEDURE Mon;
BEGIN AESGraphics.GrafMouse (257, NIL) END Mon; 
PROCEDURE Moff;
BEGIN AESGraphics.GrafMouse (256, NIL) END Moff; 
PROCEDURE Fillwords (adr: ADDRESS; W,H, fw: CARDINAL);
VAR w: CARDINAL; ptr: POINTER TO CARDINAL;
BEGIN WHILE (H > 0) DO ptr := adr;
        INC (adr, BPZ); DEC (H);
        FOR w := 1 TO W DO ptr^ := fw; (*$T-*) 
        INC (ptr, 2) (*$T=*) END;
        END (*H*); END Fillwords;
PROCEDURE Pause; BEGIN IF (0 = BIOS.BConIn (BIOS.CON)) THEN END END Pause;
PROCEDURE wCh (ch: CHAR); BEGIN BIOS.BConOut (BIOS.CON, ch) END wCh;
PROCEDURE wSt (VAR s: ARRAY OF CHAR); VAR k: CARDINAL;
 BEGIN k:=0; WHILE (k<=HIGH(s))&(s[k]#0C) DO wCh (s[k]); INC(k) END END wSt;

PROCEDURE PaintGray (K, s0,a0, W,H, F1,F2,F3, F4: CARDINAL);
 BEGIN FOR k := 0 TO K DO
        Fillwords (SSmemPtr^ +ADDRESS (s0+ (k*2)*BPZ), W, H, F1); 
        Fillwords (SSmemPtr^ +ADDRESS (s0+ (1+k*2)*BPZ), W,H, F2); 
        Fillwords (ASmemPtr^ +ADDRESS (a0+ (k*2)*BPZ), W,H, F3); 
        Fillwords (ASmemPtr^ +ADDRESS (a0+ (1+k*2)*BPZ), W,H, F4);
        END (*k*); END PaintGray;

VAR k, f1,f2; CARDINAL;
BEGIN Moff;     (* TESTPROGRAMM: *)
    FlipActS;   ModBox (160, 50,480,150, 0);
    InvScreen;  ModBox (160,250,480,350, 0); FlipActS;
                ModBox (240,100,320,300, 0);
                ModBox (320,100,400,300, 15);

(* Interleaving a la Fernsehen *)
    PaintGray (124, 10+50*BPZ, 10+100*BPZ, 10,1, 0, 0FFFFH,0FFFFH,0); 
    f1 := 1 +4 +16 +64; f1 := f1 *256 +f1; 
    f2 := f1 *2;
(* das neue, leckere Grau ! *)
    PaintGray (124, 50+50*BPZ, 50+100*BPZ, 10,1, f1, f2, f2, f1) ;

    Mon;
(* TEST 1: *)
    GraySwitch (1, ASmemPtr^,SSmemPtr^); Pause; 
    GraySwitch (0, 0,0);
(* TEST 2: *)
    GraySwitch (2, ASmemPtr^,SSmemPtr^); Pause; 
    GraySwitch (0, 0,0);
   Moff;

    PaintGray (150, 10+ 50*BPZ,10+ 50*BPZ,30,1, f1, f2, f2,f1); (* grau *)
    PaintGray (50, 40+200*BPZ,40+200*BPZ, 5,1, 0FFFFH,0FFFFH,0FFFFH,0FFFFH); 
    PaintGray (50, 30+200*BPZ,30+200*BPZ, 5,1, 0, 0,0,0); (* s+w *)
    PaintGray (16, 352*BPZ, 352*BPZ,40,1,0,0,0,0); (* +2 weitere Grau6tufen - bei halber Auflösung *) 
    PaintGray (50, 30+100*BPZ,30+100*BPZ, 5,1, f1, 0FFFFH,0FFFFH,f2);
    PaintGray (50, 40+100*BPZ,40+100*BPZ, 5,1, f1, 0,0,f2);

(* wCh (33C); wCh ("Y"); wCh (CHR (32 +8)); wCh (CHR (32+32)); *)
   wCh (33C); wCh ("Y"); wCh (CHR (32+22)); wCh (CHR (32 +10));
   wSt (' 2 weitere Graustufen mit Pfeffer & Salz! ');
   wCh (33C); wCh ("Y"); wCh (CHR (32+23)); wCh (CHR (32 +10));
   wSt ( ' Flimmerts noch? Dann Kontrast oder Helligkeit etwas verringern.');
   Mon;
(* TEST 3: *)
    GraySwitch (1, ASmemPtr^,SSmemPtr^); Pause; 
   Moff; GraySwitch (0, 0,0); EndScreen;
END GrayTest.

Rolf Schrader
Links

Copyright-Bestimmungen: siehe Über diese Seite