#Titel Programmieren/Anzeige von Bildern mit DataTypes
#Logo gadget29:pinsel/AG.Prog
#Font topaz 8
#C10
PROGRAMMIERTIP

#Font Losse 16
#C31
   Anzeige von Bildern mit DataTypes
   ---------------------------------
#Font topaz 8
#C21
Vor einigen Monaten kam mir die Idee, aus einem eigenen Programm heraus
Bilder anzuzeigen. Da ich von Grafik auf dem Amiga und den verschiedenen
Formaten (IFF, GIF, JPEG...) wenig bis gar keine Ahnung habe, blieb nur
die Möglichkeit, DataTypes zu verwenden. Diese kleinen Helfer erledigen
angeblich alles selbständig, also genau das Richtige für einen ahnungslosen
und faulen Menschen wie mich.

Von der Idee bis zum Erscheinen des ersten Bildes in voller Farbenpracht
auf dem Bildschirm waren allerdings einige Hürden zu nehmen. Es fängt damit
an, daß in der gedruckten Dokumentation zum Amiga (ROM Kernel Manuals)
DataTypes fehlen, weil sie erst ab OS3.0 auftauchen, die Manuals aber nur
bis OS2 reichen. Also besorgte ich mir die von Olaf 'Olsen' Barthel
zusammengestellte Developer CD V1.1 - sie war gerade erschienen - und suchte
die passenden Informationen. Leider sind includes, autodocs, sourcecodes über
die ganze CD verteilt und in den verschiedensten Verzeichnissen versteckt,
so daß ich erst einmal ein Verzeichnis auf meiner Festplatte einrichtete und
dorthin alles kopierte, was brauchbar erschien. Die includes (datatypes.i,
datatypesclass.i, pictureclass.i) erwiesen sich als traditionell sparsam ;-)
kommentiert, und die Beispiel-Sourcecodes (leider nur in C) waren nicht genau
das, was ich brauchte.

Zum Glück fand ich in AMINET14:Aminet/util/dtype/png_dt.lha den C-Source
eines Bildanzeigers "ViewDT", den die Firma Cloanto (Italien)
freundlicherweise als PD veröffentlicht hatte, so daß ich ihn ohne
Gewissensbisse in Assembler umsetzten konnte. Die folgenden Angaben basieren
überwiegend auf ViewDT.c, enthalten aber verschiedene Optimierungen, auf die
ich beim Herumexperimentieren gestoßen bin.

#C10
--------------------------- TIP START ----------------------------------
#C21

Nach der langen Vorrede geht es hier nun los. Ziel ist, ein Bild mit
maximal möglicher Farbenzahl auf eigenem Screen anzuzeigen. Wenn man sich
damit begnügen will, ein Bild auf dem Workbench Screen mit reduzierter
Farbenzahl anzuzeigen, kann man sich die Sache sehr einfach machen: Die
amiguide.library ab V39 benutzt selbst DataTypes und zeigt Bilder in einem
Workbench Window an. Man braucht also nur den Namen der Bilddatei an
OpenAmigaGuideA() zu übergeben, alles andere wird automatisch erledigt.
Das Ergebnis ist allerdings meist nicht sehr befriedigend, und das Umrechnen
auf Workbenchfarben - man nennt das 'remappen' - dauert so seine Zeit.

Der Ablauf bei Verwendung der datatypes.library ist wie folgt:

#C10
        1. Prüfen, ob die anzuzeigende Datei eine Bilddatei ist
        2. Mit NewDTObjectA() ein DataType-Objekt erzeugen
        3. Attribute (=Eigenschaften des Bildes) in Erfahrung bringen
        4. CustomScreen mit der erforderlichen Tiefe und Größe öffnen
        5. Window auf dem Screen öffnen, um Benutzereingaben zu empfangen
        6. Screen und Window schließen und alle Resourcen zurückgeben
#C21

Die einzelnen Schritte werden nun erläutert. Am Ende dieses Beitrag befindet
sich der Programmcode in Assembler (tut mir leid, ich kann nichts anderes).
#C31
1. Dateityp prüfen
------------------
#C21
Nachdem wir die datytypes.library geöffnet haben, benutzen wir die Funktion
ObtainDataTypeA().

#C10
     SYNOPSIS
        dtn = ObtainDataTypeA (type, handle, attrs);
        d0                      d0    a0      a1

#C21
Als 'type' wird DTST_FILE angegeben, wenn wir eine normale Datei untersuchen
wollen. 'handle' ist ein Lock auf die Datei, den wir mit der Dos-Funktion
Lock() holen. 'attrs' wird auf Null gesetzt, da noch nicht implementiert.

Bei Erfolg bekommen wir einen Zeiger auf eine DataType-Struktur. Bei Mißerfolg
(d0=0) kann IoErr() aufgerufen werden und mittels GetDTString() oder Fault()
ein lokalisierte Fehlermeldung erhalten werden. Welche Funktion einzusetzen
ist, hängt davon ab, welchen Wert IoErr() zurückgibt. Werte bis 306 sind
DosErrors, ab 2000 DataTypeErrors.
Innerhalb der DataType-Struktur befindet sich ein Zeiger (dtn_Header) auf die
DataTypeHeader-Struktur. In dieser finden wir endlich die benötigte
Information. Ist die sog. GroupID = 'pict', haben wir es tatsächlich mit einem
Bild zu tun und können zu Schritt 2 übergehen. Vorher werden ReleaseDataType()
und UnLock() aufgerufen.

#C31
2. DataType-Objekt erzeugen
---------------------------
#C21
Hierzu ist NewObjectA aufzurufen:

#C10
     SYNOPSIS
        o = NewDTObjectA (name, attrs);
        d0                d0   a0

#C21
Übergeben werden der Dateiname und in a0 der Zeiger auf eine TagList.
Aus Platzgründen verzichte ich darauf, die möglichen Tags zu erläutern.
Man kommt ohnehin nicht daran vorbei, die Originaldokumentation zu lesen.
Nur so viel: DTA_SourceType muß nicht angegeben werden (default DTST_FILE),
ebensowenig DTA_GroupID, 'pict'. Letzteres wäre nur erforderlich, wenn wir
noch nicht wüßten, daß die Datei eine Bilddatei ist, und sichergehen wollten,
daß nur von Bildern ein Objekt erzeugt wird.

Wir müssen aber den Tag PDTA_Remap angeben und auf FALSE setzen, damit keine
Umrechnung auf Workbenchfarben erfolgt. Die Tagliste sieht dann wie folgt aus:

#C10
    PDTA_Remap, FALSE (=Null)
    TAG_DONE
#C21

Falls alles gut geht, erhalten wir einen Zeiger auf das neue Objekt, den wir
noch mehrfach brauchen. Bei Mißerfolg kann wieder IoErr() aufgerufen werden.

Mit dem nächsten Aufruf senden wir eine Layout-Message an den DataType der
unser Bild vorbereitet.

#C10
     SYNOPSIS
        retval = DoDTMethodA (o, win, req, msg);
        d0                    a0 a1   a2   a3

#C21
Übergeben werden in a0 ein Zeiger auf das von NewDTObjectA() erzeugte Objekt
und in a3 die Message, die aus 3 Langworten besteht:

#C10
    MethodID=DTM_PROCLAYOUT
    gpl_GInfo=NULL
    gpl_Initial=TRUE (=1)

#C21
a1,a2 sind auf Null zu setzen. Bei Erfolg wird ein Wert ungleich Null zurück-
gegeben. IoErr() kann in diesem Fall nicht verwendet werden.

#C31
3.Attribute in Erfahrung bringen
--------------------------------
#C21
Um das Bild auf einem Screen anzeigen zu können, müssen wir Höhe, Breite,
Tiefe ModeID des Screens und Farben kennen. Daher erfolgt nun ein Aufruf von
GetDTAttrsA().

#C10
     SYNOPSIS
        retval = GetDTAttrsA (o, attrs);
        d0                    a0  a2

#C21
'attrs' ist ein Zeiger auf eine Tagliste, die wie folgt aussieht:

#C10
    PDTA_BitMap,bmap
    PDTA_CRegs,cregs
    PDTA_BitMapHeader,bmh
    PDTA_ModeID,display_ID
    TAG_DONE

#C21
Das zweite Element jedes Tagpaares (bmap, cregs...) ist ein Zeiger auf einen
eigenen Speicherbereich (je 4 bytes), der von der Funktion mit Zeigern bzw.
bei der ModeID mit einem Wert gefüllt wird.

GetDTAttrsA gibt in d0 die Zahl der Anforderungen, die erfüllt werden konnten,
zurück. Da wir alle angeforderten Werte brauchen, geben wir uns nur mit einem
Returncode von 4 zufrieden. Ansonsten muß abgebrochen und das Objekt mit
DisposeDTObject() freigegeben werden.

Die erhaltenen Werte müssen für OpenScreenTagList() noch bearbeitet bzw.
geprüft werden. Die ModeID darf z.B. nicht den Wert INVALID_ID (-1) haben.
Der BitMapHeader enthält drei für uns wichtige Werte: Width, Height, Depth.
Left und Top sind immer auf Null, der Rest ist unwichtig.

#C10
    STRUCTURE BitMapHeader,0
        UWORD    bmh_Width;             ; Width in pixels
        UWORD    bmh_Height             ; Height in pixels
        WORD     bmh_Left               ; Left position
        WORD     bmh_Top                ; Top position
        UBYTE    bmh_Depth              ; Number of planes
        UBYTE    bmh_Masking            ; Masking type
        .....

#C21
Aus Depth muß die Zahl der Farben berechnet werden (numcolors=2^Depth-1).
Wir benötigen diesen Wert, um eine Farbtabelle im Speicher anzulegen. Sie
wird später OpenScreenTagList() mit dem Tag SA_Colors32 übergeben. Die Tabelle
hat folgenden Aufbau (beschrieben in grahics.doc/LoadRGB32):
#C10

    WORD Zahl der Farben
    WORD Nummer der ersten Farbe (immer Null)
    LONG Rotwert von Farbe 0
    LONG Grünwert von Farbe 0
    LONG Blauwert von Farbe 0
    ...  für weitere Farben je 3 Longs
    LONG NULL

#C21
Die Größe der Farbtabelle ist demnach numcolors*12+8. Sie wird mit AllocVec()
angelegt und dann initialisiert. cregs zeigt auf die Farbwerte, sie müssen
nur noch ab Offset 4 in den eben besorgten Speicher kopiert werden.

#C31
4. Screen öffnen
----------------
#C21
Nun ist alles vorbereitet für das Öffnen des Customscreens. Welche Flags zu
setzen sind und welche Tags erforderlich sind, ist dem Beispiel-Source zu
entnehmen.
Wichtig ist vielleicht folgender Hinweis: In ViewDT.c wird der Screen erst
geöffnet und dann die BitMap mit BltBitMap() hineingeblittet. Da für den
Screen von Intuition ebenfalls eine (leere) BitMap alloziert wird, kann das
bei großen und tiefen Bildern einen zusätzlichen Bedarf von 500K oder mehr
an ChipMem ergeben. Es ist daher besser, die vom DataType erzeugte BitMap
beim Öffnen des Screens mit dem Tag SA_BitMap gleich anzugeben. Intuition
verzichtet dann darauf, eine eigene BitMap zu allozieren. Das Verfahren hat
auch einen Nachteil, auf den ich gleich komme.

#C31
5. Window öffnen
----------------
#C21
Nachdem der Screen erfolgreich geöffnet wurde, muß auf ihm ein rahmenloses
Fenster gesetzt werden. Damit es das auf dem Screen bereits sichtbare Bild
nicht verdeckt, habe ich mich zu der Lösung entschlossen, es nur 2*2 Pixel
groß zu machen. Das funktioniert! Wenn aber der User mit der linken Maustaste
auf einen Bereich außerhalb des Fensters klickt, wird es inaktiviert und
empfängt keine Eingaben mehr. Meine Lösung: Ich setze (neben anderen) das
IDCMP_Flag INACTIVEWINDOW und beende bei Inaktivierung die Anzeige durch
Schließen von Window und Screen. Falls mir jemand eine bessere Lösung nennen
kann, würde es mich freuen.

Eigentlich hatte ich vor, zur Demonstration ein lauffähiges Programm zu
schreiben. Leider reichte die Zeit nicht. Falls Andreas es für sinnvoll hält,
kann ich es mit der nächsten Gadget-Ausgabe nachliefern. Hallo Andreas!

#C10
[ Nur zu. :) (an) ]
#C31
                                    Jürgen Klawitter

#C10
----------- Sourcefragment zur Bildanzeige mit DataTypes ----------------
#C21

Autor:
Datum:
Größe:
#Y-30
#C10
       Jürgen Klawitter
       19-03-97
       ca. 600 bytes assembliert

#C21
Vorbemerkung: a4 zeigt auf einen programmeigenen Puffer, in dem alle möglichen
Variablen gespeichert sind bzw. werden.

#C10
        ...
        bsr     _IsDataType
        bne     errormsg
        bsr     _GetDataPic
        bne     errormsg
        bra     all_ok      ;Resourcen zurückgeben, libs schließen etc.

_IsDataType
        ;Subroutine zur Ermittlung des Dateityps
        ;d4=filename >>d0=0 (success), -1 (nopic), >1 (error)
        moveq   #106,d3
        add.l   d3,d3                   ;obj nicht vom geforderten Typ
        move.l  d4,d1
        moveq   #-2,d2                  ;access_read
        DosCall Lock
        move.l  d0,d6                   ;Datei existiert nicht
        beq     \nolock
        move.l  DTBase(a4),d0
        bne     \gotDTlib               ;datatypes.lib schon geöffnet
        lea     DTName(pc),a0
        moveq   #39,d0
        bsr     _OpenLib
        move.l  d0,DTBase(a4)
        beq     \unlock                 ;keine lib
\gotDTlib
        move.l  d0,a6
        move.l  d6,a0                   ;Lock (filename)
        moveq   #DTST_FILE,d0
        suba.l  a1,a1
        Call    ObtainDataTypeA
        tst.l   d0
        beq     \unlock
        move.l  d0,a0
        move.l  dtn_Header(a0),a0
        moveq   #-1,d3                  ;RC=nopic
        cmp.l   #'pict',$10(a0)         ;GroupID 'pict'
        bne     \release
        Zero    d3                      ;wir haben ein Bild
\release
        move.l  d0,a0
        Call    ReleaseDataType
\unlock move.l  d6,d1
        DosCall UnLock
\nolock move.l  d3,d0                   ;RC nach d0
        rts

_GetDataPic
        ;Subroutine für alles übrige
        ;d4=filename

        movem.l d4/d5/a2/a3,-(sp)
        moveq   #212/2,d7
        add.l   d7,d7                   ;RC=oject not of required type
        clr.l   palette(a4)

;TagList für NewDTObjectA aufbauen
        move.l  a4,a0                   ;Allzweckpuffer
        move.l  #PDTA_Remap,(a0)+
        clr.l   (a0)+                   ;FALSE
        clr.l   (a0)                    ;TAG_DONE
        move.l  d4,d0                   ;filename
        move.l  a4,a0                   ;tags
        move.l  DTBase(a4),a6
        Call    NewDTObjectA
        move.l  d0,object(a4)
        beq     \geterror               ;IoErr() aufrufen

        move.l  d0,a0                   ;object
        pea     1
        clr.l   -(sp)
        pea     DTM_PROCLAYOUT
        move.l  sp,a3
        suba.l  a1,a1                   ;no win
        suba.l  a2,a2                   ;no req
        Call    DoDTMethodA
        lea     12(sp),sp
        tst.l   d0
        beq     \goterror               ;IoErr() nicht aufrufen, RC s.o.

;Attribute holen
        clr.l   -(sp)
        pea     bmh(a4)
        move.l  #PDTA_BitMapHeader,-(sp)
        pea     display_ID(a4)
        move.l  #PDTA_ModeID,-(sp)
        pea     cregs(a4)
        move.l  #PDTA_CRegs,-(sp)
        pea     bmap(a4)
        move.l  #PDTA_BitMap,-(sp)
        move.l  sp,a2
        move.l  object(a4),a0
        Call    GetDTAttrsA
        lea     36(sp),sp
        subq.l  #4,d0
        bne     \goterror                ;weniger als 4 Attribute
        move.l  display_ID(a4),d7
        bmi     \goterror                ;darf nicht -1 sein

;Attribute auswerten
        move.l  bmh(a4),a0
        move.l  (a0),d4                 ;width+height
        Zero    d6
        move.b  8(a0),d6                ;depth

        move.l  d6,d0
        moveq   #2,d3
        subq.b  #1,d0
        lsl.l   d0,d3                   ;2<<(depth-1)=numcolors

        moveq   #12,d2
        mulu    d3,d2
        move.l  d2,d0
        addq.l  #8,d0
        moveq   #1,d1
        swap    d1                      ;MEMF_Clear für palette
        SysCall AllocVec
        moveq   #103,d7                 ;RC=no mem
        move.l  d0,palette(a4)
        beq     \goterror
        move.l  d0,a1
        swap    d3
        move.l  d3,(a1)+                ;WORD numcolors + WORD Farbe 0
        move.l  cregs(a4),a0            ;Zeiger auf Colors
\cpycolors
        move.b  (a0)+,(a1)+             ;Farben kopieren
        subq.l  #1,d2
        bne     \cpycolors
        bra     ViewPic
\geterror
        DosCall IoErr
        tst.l   d0
        beq     \goterror
        move.l  d0,d7
\goterror
        bra     freepic

ViewPic moveq   #1,d3                   ;TRUE

;NewScreen-Struktur für OpenScreenTaglist
        move.l  a4,a0
        clr.l   (a0)+
        move.l  d4,(a0)+                ;width+height
        move.w  d6,(a0)+                ;depth
        clr.l   (a0)+                   ;pens, viewmode
        move.w
 #CUSTOMSCREEN!SCREENBEHIND!SCREENQUIET!CUSTOMBITMAP!AUTOSCROLL,(a0)+
        clr.l   (a0)+
        clr.l   (a0)+
        clr.l   (a0)+
        move.l  bmap(a4),(a0)+          ;Ende NewScreen

        move.l  a0,a1                   ;tags
        move.l  #SA_DisplayID,(a0)+
        move.l  display_ID(a4),(a0)+
        move.l  #SA_OverScan,(a0)+
        move.l  d3,(a0)+                ;TRUE
        move.l  #SA_Colors32,(a0)+
        move.l  palette(a4),(a0)+
        clr.l   (a0)
        move.l  a4,a0
        IntCall OpenScreenTagList
        tst.l   d0
        beq     freepic
        move.l  d0,a3

;NewWindow

        move.l  a4,a0
        clr.l   (a0)+
        moveq   #2,d0                   ;window nur 2x2, spart bis zu 500K
        move.w  d0,(a0)+                ;width
        move.w  d0,(a0)+                ;height
        clr.w   (a0)+
        move.l  #MOUSEBUTTONS!VANILLAKEY!INACTIVEWINDOW,(a0)+
        move.l  #BORDERLESS!ACTIVATE!RMBTRAP,(a0)+
        clr.l   (a0)+
        clr.l   (a0)+
        clr.l   (a0)+
        move.l  a3,(a0)+                ;Zeiger auf Screen
        clr.l   (a0)+
        clr.l   (a0)+
        clr.l   (a0)+
        move.w  #CUSTOMSCREEN,(a0)
        move.l  a4,a0
        Call    OpenWindow
        tst.l   d0
        beq     \closescreen
        move.l  d0,a5

        Zero    d7                      ;RC=0, alles hat geklappt
        move.l  a3,a0
        Call    ScreenToFront
\wait   move.l  wd_UserPort(a5),a0
        move.l  a0,d3
        SysCall WaitPort
        move.l  d3,a0
        Call    GetMsg
        tst.l   d0
        beq     \wait
\msgloop                                ;User hat geklickt oder Taste gedr.
        move.l  d0,a1
        Call    ReplyMsg
        move.l  d3,a0
        Call    GetMsg
        tst.l   d0
        bne     \msgloop
        move.l  a5,a0
        IntCall CloseWindow
\closescreen
        move.l  a3,a0
        Call    CloseScreen
freepic move.l  object(a4),d0
        beq     \palette
        move.l  d0,a0
        move.l  DTBase(a4),a6
        Call    DisposeDTObject
\palette
        move.l  palette(a4),d0
        beq     \endfree
        move.l  d0,a1
        SysCall FreeVec
\endfree
        move.l  d7,d0                   ;RC nach d0
        movem.l (sp)+,d4/d5/a2/a3
        rts

        END
