Wenn man die Farben binär kodiert und eine jeweils dunklere Bildebene immer jeweils doppelt so lange anzeigt, dann klappt es...
Also (am Beispiel): ------------------- Bild #0: 01010101 Bild #1: 00110011 Bild #2: 00001111Wie oft muß ein Bild pro Sequenz angezeigt werden?
1xBild #0 2xBild #1 4xBild #2
Wie wählt man am besten die Bildreihenfolge?
Z.B.: 0,2,1,2,1,2,2
(Es läßt sich nicht vermeiden, daß das Bild #2 zweimal hintereinander gezeigt werden muß...)
Was zeigt das Display dann?
Was sind das für Bits in den Bildern?
Man kann diese Verfahren (solange das Umschalten schnell genug ist), soweit betreiben wie man will: 4 Graustufen, 8 Graustufen, 16 Graustufen, 32, 64, 128, 256, 512, 1024, ...., 1048576 Graustufen... 16,7 Millionen Graustufen...
Nur: Das umschalten geht halt nicht SO schnell... (weil das Bild ja auch mindestens einmal vom Display angezeigt werden muß und dieses 70 Bilder pro Sekunde zeigt)
Benutzen des Timer-Interrupts für die GrayLib:
Leider haben die Graustufen den Nachteil, daß man jetzt vollkommen auf sich alleingestellt ist, also die Grafikroutinen komplett selbst schreiben muß... Und das ist Bit-Fummel-Arbeit, da jedes Pixel im RAM nur 1 Bit belegt und dadurch je 8 Pixel in einem Byte gespeichert werden, was bedeutet, daß man das Byte erst laden dann das "richtige" Bit modifizieren und dann das Byte wieder schreiben muß, um einen einfachen Pixel zu setzten!!!
Folgendes Problem: Da erst ich nicht wußte, daß es so knapp mit dem Arbeitsspeicher wird, hab ich das ganze auf
Geschwindigkeit und nicht auf Speicherbenutzung optimiert... Dadurch belegt die GrayLib wohl etwa 17KB RAM und
zwar des "near" Speichers!!! (von dem eigentlich nur 20KB da sind...)
Ich werde das aber demnächst reduzieren auf: ca. 14KB, indem ich den "Bildzwischenspeicher" des Betriebssystems
mitbenutzen werde... Außerdem werde ich auch versuchen den belegten Speicher in den "far" Bereich zu verschieben...
Z.B. indem das Hauptprogramm den Speicher "liefert"
Außerdem benutzt auch die GrayLib "DoubleBuffering", daß heißt: Es wird ein "Zwischenbild" erstmal im normalen
Speicher erzeugt und erst wenn es fertig ist, angezeigt... Dann sieht man den Bildaufbau nicht und es Flackert viel
weniger!
Aber: Es braucht auch mehr Speicher... (Im Moment 6400 Bytes, nach "Mitbenutzung" des "Bildzwischenspeichers"
des Betriebssystems nur noch 3200 Bytes)
Okay, was hat die GrayLib zu bieten?
#include "GrayLib.c"Und die Dateien GrayLib.c und intproc2.a86 in das Verzeichnis "C" des Projektes zu kopieren.
Folgende Zeile ist so abzuändern:
Von dieser Zeile: ----------------- APLOBJS = $(ODIR).obj In diese Zeilen: ---------------- APLOBJS = $(ODIR).obj $(ODIR)intproc2.objWobei statt , der jeweilige Name der *.c - Datei ist, die das Hauptprogramm enthält...
setintvec(); / FastIntOn();
FastIntOff(); / resetintvec();
clrscr();
manualput();
putscreen();
putspr(int x, int y, byte far *Sprite);
Spriteaufbau:
Die Sprites werden in einzelne waagerechte "Linien" (Blöcke) zerlegt, welche eine Länge >0 und jeweils eine
relative x- und y-Koordinate für zu dem "Punkt" haben, der dann später beim Aufruf von putspr angegeben wird
(das kann also beispielsweise der Mittelpunkt einer Figur oder aber der Punkt ganz links oben der Grafik sein)...
Es werden erst alle "Grauanteile" in Linien und dann alle "Schwarz-/Weißanteile" gespeichert...
1 Word: Anzahl Blöcke
Blöcke:
1 Word - Relative X-Position (Integer)
1 Word - Relative Y-Position (Integer)
1 Byte - Anzahl der "vollständigen" Bytes im Block
1 Byte - Byte mit den "restlichen" Bits
1 Byte - Anzahl der "benutzen" Bits im letzten Byte
Transparenz wird dadurch erreicht, daß eine Zeile eines Sprites einfach aus mehreren Blöcken (oder gar keinem Block) besteht, und dadurch dieser Bereich beim Zeichnen ausgelassen wird...
Anzahl der "vollständigen" Bytes im Block???
Byte mit den "restlichen" Bits???
Anzahl der "benutzen" Bits im letzten Byte???
Da immer 8 Pixel in einem Byte sind, wird hier erstmal gespeichert werden, muß genau festgehalten werden,
wieviele Bytes zu verarbeiten sind, und wieviele Pixel im Letzten Byte wirklich drin sind...
Wem das zu kompliziert ist, der sollte einfach den Konverter verwenden, den ich demnächst mit veröffentlichen
werde, der aus 8Bit-Graustufen (256 Graustufen) Bildern oder sogar farbigen Bildern 2Bit Graustufen (4 Graustufen)
Bilder macht, die putspr(..); dann versteht... Außerdem konvertiert das Programm komplette Animationen, was sehr
zeitsparend sein kann... man muß die Bilder nur durchnummerieren...
putpix(int x, int y, int c);
hline(int x,int y,int l,int c); UND
vline(int x,int y,int l,int c);
putplate(int,int,byte far *);
char pic1[5120]; und char pic2[5120];
unsigned char counter;
unsigned int counter2;
In der Variable "counter2" zählt das Programm von 0 bis 65535 hoch und fängt dann bei 0 wieder an... Diese Variable kann genutzt werden, wenn man das Timing im Programm regulieren will. Z.B.: Eine kurze Pause einbauen will oder die Zeit für ein Spiel messen will (wenn der Spieler meinetwegen 30 Sekunden Zeit hat) etc... Diese Variable kann ruhig gesetzt werden... Es wird dann ab diesem Wert weitergezählt... Das "weiterzählen" erfolgt alle 1/40 Sekunden (40 mal pro Sekunde)
DER PROFILER DER GRAYLIB:
char proc;
unsigned int ptime[32];
ACHTUNG: Der Interrupt (und damit das automatische "Graustufenbild-umschalten") muß aktiviert sein...
Wie benutzt man den Profiler?
=> am Ende des Programms wieder ausschalten:
FastIntOff();
resetintvec();
LibJumpMenu();
ptime[] enthält die Zeit, in 1/40 sekunden, die ein Programmteil verwendet, der die entsprechende "ID-Nummer" zugewiesen bekommen hat...
Nehmen wir an, wir haben 2 Teile, die in einer Funktion sind:
void testfunction() {
proc=1;
/* Hier ist der Programmteil der unter ID 1 gemessen werden soll... */
.
.
.
proc=2;
/* Hier ist der Programmteil der unter ID 2 gemessen werden soll... */
.
.
.
proc=0;
/* Die restliche Zeit soll unter ID 0 gemessen werden... */
.
.
.
}
|
Nehmen wir an, wir rufen eine Funktion aus verschiedenen anderen zu messenden Programmteilen auf... und wollen diesen Programmteil "gesondert" messen...
void test3() {
.
.
.
}
void test1() {
proc:=1;
/* Hier macht test1(); irgendwas, das unter ID 1 zu messen ist... */
.
.
.
test3(); /* Jetzt wird test3(); aufgerufen, das unter ID 3 zu messen sein soll... */
.
.
.
/* Hier macht test1(); irgendwas, das unter ID 1 zu messen ist... */
proc:=0;
}
void test2() {
proc:=2;
/* Hier macht test2(); irgendwas, das unter ID 2 zu messen ist... */
.
.
.
test3(); /* Jetzt wird test3(); aufgerufen, das unter ID 3 zu messen sein soll... */
.
.
.
/* Hier macht test2(); irgendwas, das unter ID 2 zu messen ist... */
proc:=0;
}
void main() {
setintvec();
FastIntOn();
test1();
test2();
test3();
FastIntOff();
resetintvec();
}
|
test3();
steht,
proc:=3;
test3();
proc:=;
hinschreiben... Und dazu müßte man erstmal suchen, wo denn test3(); überall aufgerufen wird...
Einfacher ist es test3(); entsprechen umzuschreiben:
void test3() {
int lproc;
lproc=proc;
.
.
.
proc=lproc;
}
|
/* event mask defines */
#define EVENT_TCH 1
#define EVENT_CRADLE 4
#define EVENT_BLD1 8
void PollEvent(TCHSTS far* tsts, byte event_mask)
/*----------------------------------------------------------------------------
description : poll on events
thanx to Wittawat Yamwong
parameters : tsts - pointer to touch status struct
event_mask - specifies events to poll
return : %
----------------------------------------------------------------------------*/
{
union REGS reg;
reg.x.ax = 0x0200 | event_mask;
reg.x.di = FP_OFF(tsts);
reg.x.es = FP_SEG(tsts);
int86(0x50,®,®);
}
|
Wie man sieht ist sie von "Wittawat Yamwong" und sie fragt das Touchscreen und andere "Events" ab...
PollEvent funktioniert genauso (soweit ich weiß...), wie die Routine LibTchWait(...); mit dem kleinem aber feinen
Unterschied, daß diese Routine nicht Ewigkeiten wartet, und erst nach 500ms das Programm weiterlaufen läßt...
Man kann theoretisch LibTchInit(); etc. verwenden, aber VORSICHT!!!, wenn ihr die HardICONS ("Menu" "Scheduler" und so weiter) aktiviert, dann paßt auf, daß Ihr den Interrupt wieder ausschaltet und zurücksetzt (FastIntOff(); resetintvec() Bevor (!!!) das Menu, der Scheduler etc. aufgerufen werden!!!
Ich bevorzuge aber, PollEvent(...); einfach so zu verwenden, und dann nur die Werte:
tsts.x und tsts.y
zu verwenden, die die Stiftposition zurückliefern... Oder (-1,-1) (funktioniert jedenfalls in PVWar so) wenn der Stift nicht auf dem Touchscreen ist...
Die Verwendung:
TCHSTS tsts;
Und rufen dann PollEvent etwa so auf:
PollEvent(&tsts, (EVENT_TCH | EVENT_CRADLE));
Für Ergänzungen wenden Sie sich bitte an:
Jürgen Wagner