programmieren

Programmieren mit dem picture.datatype V43

Der picture.datatype V43, nachfolgend PDT43 genannt, gestattet die Darstellung von Bildern mit bis zu 24 bit Farbtiefe. Sein Einsatz ist nur im Zusammenhang mit Grafikkarten möglich. Es gibt unterschiedliche Versionen des PDT43, die sich dem Vernehmen nach in ihrem Verhalten etwas unterscheiden. Die nachfolgend geschilderten Erfahrungen beziehen sich auf den PDT43 der Picasso96-Software. Diese arbeitet übrigens auch mit CyberVision-Grafikkarten zusammen.

Vor einigen Wochen kaufte ich mir eine CyberVision64-Karte und hatte damit erstmals die Möglichkeit, die besonderen Fähigkeiten des PDT43 auszutesten. Zunächst interessierte mich, ob meine alte Routine zur Bilddarstellung mittels DataTypes funktionierte. Sie tat es! Wie sich aber später herausstellte war auch etwas Glück dabei, denn nicht alle Methoden, die in der Dokumentation zum Standard-picture.datatype (Developer CD) empfohlen werden, eignen sich.

Ich setze voraus, dass das allgemeine Vorgehen, um ein Bild auf den Screen zu bringen, bekannt ist. Wem das alles neu ist, kann sich an Beispielproggrammen (mit Sourcecode) orientieren, die sich auf der Developer CD befinden (dto.c, dtpic.c). Außerdem gibt es im Archiv des png.datatype von Cloanto ein Beispielprogramm mit gut dokumentierten Sourcecode (ViewDT).

Der grobe Ablauf bis zum Erscheinen des Bildes ist folgender:

  1. Neues Datatype-Objekt erzeugen mit NewDTObjectA().
    PDTA_Remap muß auf FALSE gesetzt werden, denn wir wollen alle Farben haben. Wenn man vorher nicht mit ObtainDataTypA() den Dateityp geprüft hat, muss man außerdem DTA_GroupID, GID_PICTURE angeben.
    Nach erfolgreichem Aufruf sollte man DoMethodA() mit

         DTM_PROCLAYOUT
    GInfo = NULL
    Initial = 1

    aufrufen, was lt. Dokumentation das Layout/Remap auf dem eigenen Prozess stattfinden lässt und nicht auf Intuitions. Da nun Intuition gar kein Prozess ist, allenfalls ein Handler, bleibt mir der Sinn dieser Methode verborgen. Sie schadet jedenfalls nichts und ist in speziellen Fällen sogar besser.

  2. Attribute des Objektes holen mit GetDTAttrsA().
    Folgende Tags sollten angegeben werden:

         PDTA_BitMap
    PDTA_BitMapHeader
    PDTA_ModeID
    PDTA_CRegs
    PDTA_NumColors

    Wenn alles gut geht, gibt GetDTAttrs die Zahl der gelieferten Attribute zurück, in diesem Fall muss es also eine 5 sein. Wir bekommen einen Zeiger auf die BitMap des Bildes, die im Fall des PDT43 aber nicht planar sein muss. Aus der BitMapHeader-Struktur kann man Höhe, Breite und Tiefe des Bildes auslesen. Der Rest ist nach meine bisherigen Erfahrungen unwichtig. Die CRegs sind ein Array der Farbewerte in einem Format, das sich für LoadRGB32() eignet. Jede Farbe ist hier durch 3 LONGs representiert, je eins für Rot, Grün und Blau. Wenn NumColors beispielsweise 4 ist, enthält CRegs 4x3 LONGs.

  3. CustomScreen öffnen mit OpenScreenTagList(), wobei die Werte aus dem BitMapHeader verwendet werden.

  4. Window auf dem Screen öffnen. Für die Bilddarstellung ist das zwar nicht unbedingt nötig (s. dtpic.c), aber man will ja schließlich Eingaben des Users empfangen.

  5. Bild auf dem Scree/Window darstellen. Dazu gibt es verschiedene Möglichkeiten, auf die ich später eingehe.

Nun zum Vorgehen mit dem PDT43:

Die Dokumentation dazu (picdt43.doc) ist äußerst spärlich, so dass man sich vieles zusammenreimen und notfalls austesten muss. 15 Minuten mehr Zeitaufwand für die Dokumentation hätten Programmierern wie mir viel Zeit erspart. Über alle Programmierer summiert ergibt das sicher einige tausend Stunden ;-)

Um in den Genuss der besonderen Features des PDT43 zukommen, muss man beim Aufruf von NewDTObjectA den Tag PDTA_DestMode mit ti_Data=MODE_43 (1) angeben. Die Alternative, MODE_42 (0), veranlasst den PDT43, eine planare Standard-BitMap mit der Maximaltiefe von 8 bit zurückzugeben, so jedenfalls die Dokumentation.

Bevor man den MODE_43 aktivieren will, stellt sich zunächst die Frage: Liegt der PDT43 überhaupt vor? Um das rauszukriegen, sind drei Vorgehensweisen denkbar:

  1. Prüfen, ob die cybergraphics.library geöffnet ist. Man könnte das mit OpenLibrary tun, hat dann aber Overhead, wenn es ein System ohne Grafikkarte ist.
    Alternative: FindName verwenden

  2. OpenLibrary mit libs:datatypes/picture.datatype,43 aufrufen. Auch hier Overhead, wenn eine ältere Version vorliegt.

  3. PDTA_DestMode auf Verdacht einsetzen und wenn der vorhandene picture.datatype diesen Tag nicht versteht, schlägt NewDTObjectA vermutlich fehl. Man könnte dann einen zweiten Versuch ohne PDTA_DestMode starten. (Habe ich nicht ausprobiert.)

Methode 1 mit FindName erscheint mir die eleganteste. Die cybergraphics.library ist auf jeden Fall schon geöffnet, wenn das System eine Grafikkarte hat. FindName() findet sie also.

Nun das Ergebnis beim Aufruf von NewDTObjectA mit und ohne PDTA_DestMode + MODE_43: Es gab keinen!
Ob in den V43-Modus geschaltet wird oder nicht, hing offensichtlich nur von den SubDataTypes und der Bildtiefe ab. Bei SubDataTypes (ilbm.datatype, gif.datatype...), die den V43-Modus nicht kennen, wird eine planare Standard-BitMap erzeugt, bei den anderen z. T. schon ab 8 bit Tiefe eine nicht planare.

Ob die BitMap planar ist oder nicht, entscheidet über das weitere Vorgehen. Deshalb muss das zunächst geprüft werden. Wenn im BitMapHeader depth > 8 ist, hat man es auf jeden Fall mit einer Nicht-Standard-BitMap zu tun. Wenn sie = 8 ist, muss man entweder graphics.library/GetBitMapAttr mit BMA_FLAGS aufrufen und dann auf BMF_STANDARD prüfen oder die analoge Funktion der cybergraphics.library GetCyberMapAttr verwenden. Diese liefert auch Informationen über das Pixelformat, das man aber nicht zu kennen braucht.

Wie bekomme ich nun das Bild - möglichst in True/HighColor - auf den Screen?

Ich erwähnte schon, dass es mehrere Möglichkeiten gibt:

  1. OpenScreenTagList mit width, height, depth aus dem BitMapHeader und den Tags SA_DisplayID, SA_OverScan, SA_Colors32 aufrufen. Die DisplayID ist die von GetDTAttrsA zurückgelieferte. SA_Colors32 braucht als Argument einen Zeiger auf einen Array, der aus dem CRegs-Array erst erzeugt werden muss. Näheres dazu in ViewDT.c oder graphics/LoadRGB32. Die Angabe von SA_Colors32 ist übrigens überflüssig, wenn depth größer als 8 ist. Die Farben sind in der BitMap dann pixelweise verschlüsselt. Bei nichtplanaren BitMaps mit depth=8 muss SA_Colors32 angegeben werden bzw. die Farben müssen nach Öffnen des Screens mit LoadRGB32 gesetzt werden.

  2. Wenn die BitMap planar ist (und nur dann!), kann man OpenScreenTagList mit SA_BitMap einen Zeiger auf die BitMap übergeben. Nochmals, weil's wichtig ist:

    !!!SA_BitMap NICHT verwenden, wenn keine planare BitMap vorliegt!!!

    Aus diesem Grund stürzen die erwähnten Beispielprogramme mit Ausnahme von dto, das den WorkbenchScreen und PDTA_Remap=TRUE verwendet, ab, sobald ein SubDataType eine Nicht-Standard-BitMap liefert. Aber gesetzt den Fall, man hat eine planare BitMap, dann erscheint das Bild sofort auf dem (nackten) Screen. Öffnet man anschließend ein Window, verschwindet das Bild allerdings wieder, und man braucht es doch unbedingt.

    Der Ausweg aus dem Dilemma ist, ein rahmenloses Window von 1x1 Größe zu öffnen, wodurch nur 1 Pixel des Bildes verdeckt wird. Sinnvoll kann dieses Vorgehen dann sein, wenn der zu öffnende Screen größer ist als der DisplayClip (der sichtbare Bereich) und damit voraussichtlich viel Speicher verschlingt. Man spart nämlich beträchtlich an Speicher durch Angabe von SA_BitMap, weil OpenScreenTagList dann keine eigene (leere) BitMap alloziert. Bei "normalen" Amigas würde dies wertvolles Chip-RAM kosten, bei Amigas mit Grafikkarte wird Fast-RAM alloziert.

  3. Auf dem Screen wird ein Fenster geöffnet mit den Dimensionen des Bildes und anschließend AddDTObject aufgerufen. Das ist die Methode, von der im picdt43.doc behauptet wird, dass man sich nicht darum zu kümmern brauche, ob MODE_43 möglich ist oder nicht. Der picture.datatype erledigt das Rendern des Bildes dann selbst und verwendet den MODE_43, wenn möglich.

    Bevor AddDTObject aufgerufen wird, ist allerdings noch einiges zu beachten: Das Window muss die IDCMP-Flags IDCMPUPDATE und REFRESHWINDOW haben, unter den Window-Flags muss(?) SIMPLE_REFRESH sein. Nach dem Öffnen muss das DataTypeObject noch darüber informiert werden, wo es sich auf dem Window platzieren soll. Deshalb wird mit SetDTAttrsA und den Tags GA_Top, GA_Left, GA_Width, GA_Height alles festgelegt. Außerdem muss es mit ICA_Target, -1 veranlasst werden, seine Meldungen an den UserPort des Windows zu schicken. Nach Eintreffen einer IDCMPUPDATE-Message findet sich im Feld IAddress ein Zeiger auf eine Tagliste, die Einzelheiten über den Typ der Message enthält (DTA_SYNC, DTA_ERRORNUMBER, DTA_PRINTERSTATUS...). Man kann diese Tagliste "zu Fuß" durchgehen oder die Funktionen der utility.libray verwenden.

  4. Statt AddDTObject wird das Bild bzw. die BitMap mit BltBitMapRastPort gerendert. Diese Methode hat bisher immer funktioniert, scheint also die einfachste zu sein. Wenn man allerdings vor hat, dem User die Möglichkeit zu geben, das Bild auszudrucken, dann ist Methode 2 besser, weil PrintDTObjectA() eingesetzt werden kann. Der Druck erfolgt mit dieser Funktion asychron, d. h. das Programm kann sich anderen Dingen zuwenden und bekommt, wenn der Druck beendet ist oder fehlschlug, eine UPDATE-Message vom Typ DTA_PrinterStatus. PrintDTObjectA, ohne AddDTObject vorher aufgerufen zu haben, geht zwar, ist aber gefährlich, weil man keine UPDATE-Message empfängt, und ein vorzeitiges DisposeDTObject zu einem Hänger führt, wenn der Druck noch nicht beendet ist.

So, das war's. Falls jemand andere Erfahrungen gemacht hat und an dieser Stelle darüber berichtet, würde es mich freuen.

Jürgen Klawitter (25.8.00)

Zurück