Module TinyIFF (Images Amiga IFF ILBM PBM)

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Avatar de l’utilisateur
Flype
Messages : 2431
Inscription : jeu. 29/janv./2004 0:26
Localisation : Nantes

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par Flype »

Ok, oui cool, çà à l'air fiable (si je rajoute 1 au total), testé sur une 100aines d'images.
J'avais procédé autrement car j'avais quelques soucis à un moment avec çà et certaines images, mais en fait çà à l'air d'aller.

Donc je remplace ma routine unpackBitsSize() par çà :

Code : Tout sélectionner

unpackedSize = 1 + ( ( *bmhd\w * *bmhd\h * *bmhd\nPlanes ) / 8 )
Code corrigé dans le 1er post.

Sinon, oui, en effet, le HAM c'est complexe. 'tin comment j'étais content quand j'ai réussi à obtenir un affichage parfait :lol:
Et il y a plein de cas différents, mais je suis arrivé à factoriser comme il faut je crois pour supporter HAM5/6/8.

Pour le EHB, en revanche c'est très facile quand tu as compris le principe.
En version courte :
- Si BitmapHeader\camg (viewmode de l'amiga) & $80 alors ==> Mode EHB
- Si Mode EHB alors la colorMap doit faire en principe 32 couleurs (dans le fichier)
- Si oui alors il faut créer une nouvelle colorMap2 de 64 couleurs (allocation)
- On recopie les 32 couleurs définies de la colorMap1 dans la colorMap2
- Puis, on calcule les 32 couleurs manquantes, ce sont les mêmes couleurs mais moitié moins 'lumineuses' (Half Bright) :

Code : Tout sélectionner

De 0 à 31 :
colorMap2[i]\r = colorMap1[i]\r
colorMap2[i]\g = colorMap1[i]\g
colorMap2[i]\b = colorMap1[i]\b
De 31 à 63 :
colorMap2[i]\r = colorMap1[i]\r / 2
colorMap2[i]\g = colorMap1[i]\g / 2
colorMap2[i]\b = colorMap1[i]\b / 2
Image
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par djes »

Bluffante la gestion du HAM, fallait oser ! Chapeau !
Avatar de l’utilisateur
Flype
Messages : 2431
Inscription : jeu. 29/janv./2004 0:26
Localisation : Nantes

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par Flype »

Merci, j'en ai bavé des ronds de chapeau avec l'HAM :? C'est tordu, ils sont allé le chercher loin le concept les ingénieurs Amiga, se souvenir de la couleur précédente en la modifiant en partie pour économiser des bits à chaque pixel. C'est pas con en fait 8) Et puis il y a l'HAM5 et 6, l'HAM8, le Slice HAM (ou Dynamic HAM)... [EDIT:] et même l'HAM10 (du jamais sorti chipset AAA).

Pour ceux que çà intéresse, un article/avis intéressant par IBM sur le format IFF, voir en particulier le paragraphe 'Lessons learned' (about IFF) :

http://www.ibm.com/developerworks/power ... -lnxw07IFF

En substance, l'IFF avait tout pour plaire, encore aujourd'hui, juste avec quelques adaptations mineures.
Mais l'industrie choisie parfois d'autres standards pas beaucoup mieux pensés voire moins bien pensés, du coup il faut se battre maintenant avec une foison de formats très différents. L'IFF savait déjà stocker tout types de media (images, sons, vidéos, document type word, excel, ...)
Image
Avatar de l’utilisateur
Ar-S
Messages : 9478
Inscription : dim. 09/oct./2005 16:51
Contact :

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par Ar-S »

30 piges, nom de diou...
Image
Je joue encore à celui là (nuclear war) sur mon smartphone via l'emulateur et l'adf :mrgreen:
~~~~Règles du forum ~~~~
⋅.˳˳.⋅ॱ˙˙ॱ⋅.˳Ar-S ˳.⋅ॱ˙˙ॱ⋅.˳˳.⋅
W11x64 PB 6.x
Section HORS SUJET : ICI
LDV MULTIMEDIA : Dépannage informatique & mes Logiciels PB
UPLOAD D'IMAGES : Uploader des images de vos logiciels
Avatar de l’utilisateur
Flype
Messages : 2431
Inscription : jeu. 29/janv./2004 0:26
Localisation : Nantes

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par Flype »

Excellent.

Je me permets une petite pub à l'occasion des 30 ans de l'amiga - il y a un projet de refonte des coques pour amiga 1200 en couleur ou en édition limitée. C'est cher car la fabrication des moules coûte un long bras mais nombreux sont intéressés, je me suis pris le boitier Scoopex, avec des touches noires, malgré le prix, pour soutenir. Aujourd'hui, la communauté Amiga se pose la question de savoir si elle est encore assez grande pour s'offrir le luxe de se payer un moule industriel. Par sûr mais c'est pas mal parti.

Image

https://www.kickstarter.com/projects/a1 ... escription
Image
Avatar de l’utilisateur
Flype
Messages : 2431
Inscription : jeu. 29/janv./2004 0:26
Localisation : Nantes

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par Flype »

@Spock
Oui c'est sûr mais çà n'aura pas (à prix raisonnable) la même qualité qu'un moule industriel - le but étant de fabriquer des coques parfaites qui durent plusieurs décennies, qui sont très solides et résistantes au jaunissement, aux rayures, ... Ceci dit, le fichier 3D qui sera utilisé pour fabriquer le moule sera distribué à la communauté si le KS est financé. Donc, tout le monde en profitera à terme.


J'ai continué à investiguer le format IFF et en particulier le format IFF ILBM 24 bits.
Ci-dessous, un petit défi, afficher le plus rapidement possible une image IFF bien lourde de 3.4 Mo, 1419 x 1001 x 24 bits, 1 420 419 pixels, compressés et entrelacés (planar). Dans ce format (24bits), il n'y a pas de ColorMap, les couleurs sont réparties dans les 24 plans. Au début sur ma machine çà prenait plus 3 secondes (sans debugger) à afficher l'image. Après quelques optimisations, çà prends maintenant 370 ms (environ 8 secondes, debugger activé). Comme quoi, oui, PureBasic est très rapide. Je pense que c'est possible de faire mieux en décompressant ligne par ligne.

L'image du test - MARBLE.IFF :
https://www.filepicker.io/api/file/Q9OC0dZLSluVtNrwizD7

Image

La routine de chargement - TinyIFF24.pbi :

Code : Tout sélectionner

;----------------------------------------------------------
; Name:        Module TinyIFF24.pbi
; Description: A tiny module for loading 24bits IFF images.
; Author:      flype, flype44(at)gmail(dot)com
; Revision:    1.0 (2015-09-13)
;----------------------------------------------------------

DeclareModule TinyIFF24
  Declare Load(fileName.s)
  Declare Catch(*memory, size.q)
EndDeclareModule

Module TinyIFF24
  
  EnableExplicit
  
  #ID_FORM = $4D524F46
  #ID_ILBM = $4D424C49
  #ID_BMHD = $44484D42
  #ID_BODY = $59444F42
  
  Macro UINT16(a)
    ((((a)<<8)&$FF00)|(((a)>>8)&$FF))
  EndMacro
  
  Macro UINT32(a)
    ((((a)&$FF)<<24)|(((a)&$FF00)<<8)|(((a)>>8)&$FF00)|(((a)>>24)&$FF))
  EndMacro
  
  Structure BYTES
    b.b[0]
  EndStructure
  
  Structure UBYTES
    b.a[0]
  EndStructure
  
  Structure IFF_RGB8
    r.a
    g.a
    b.a
  EndStructure
  
  Structure IFF_BMHD
    w.u           ; UWORD
    h.u           ; UWORD
    x.w           ; WORD
    y.w           ; WORD
    nPlanes.a     ; UBYTE
    masking.a     ; UBYTE
    compression.a ; UBYTE
    pad.a         ; UBYTE
    tColor.u      ; UWORD
    xAspect.a     ; UBYTE
    yAspect.a     ; UBYTE
    pageWidth.w   ; WORD
    pageHeight.w  ; WORD
  EndStructure
  
  Structure IFF_Chunk
    code.l
    size.l
    bytes.UBYTES
  EndStructure
  
  Structure IFF_Header
    code.l
    size.l
    format.l
    chunk.UBYTES
  EndStructure
  
  Procedure UnPackBits(*bmhd.IFF_BMHD, *packedBits.BYTES, packedSize, rowBytes)
    
    Protected i, j, k, v, unpackedSize, *unpackedBits.BYTES
    
    unpackedSize = 1 + ( *bmhd\h * rowBytes * *bmhd\nPlanes )
    If unpackedSize
      *unpackedBits = AllocateMemory(unpackedSize)
      If *unpackedBits
        While i < packedSize
          v = *packedBits\b[i]
          If v >= 0
            For j = 0 To v
              *unpackedBits\b[k] = *packedBits\b[i + 1 + j]
              k + 1
            Next
            i + j
          ElseIf v <> -128
            For j = 0 To -v
              *unpackedBits\b[k] = *packedBits\b[i + 1]
              k + 1
            Next
            i + 1
          EndIf
          i + 1
        Wend
      EndIf
    EndIf
    
    ProcedureReturn *unpackedBits
    
  EndProcedure
  
  Procedure Catch(*m.IFF_Header, size.q)
    
    Protected image, col, row, plane, bits, rowBytes, totalBytes
    Protected *ck.IFF_Chunk, *bh.IFF_BMHD, *bp.UBYTES, *eof, *bodyUnpacked
    
    If *m And *m\code = #ID_FORM And *m\format = #ID_ILBM
      *m\size = UINT32(*m\size)
      If *m\size > 0 And *m\size < size
        *eof = *m + size
        *ck = *m\chunk
        While *ck
          *ck\size = UINT32(*ck\size)
          If *ck\size & 1
            *ck\size + 1
          EndIf
          Select *ck\code
            Case #ID_BMHD
              *bh = *ck\bytes
              *bh\w = UINT16(*bh\w)
              *bh\h = UINT16(*bh\h)
              rowBytes = ( ( (*bh\w + 15 ) / 16 ) * 2 )
            Case #ID_BODY
              *bp = *ck\bytes
              If *bh\compression = 1
               *bodyUnpacked = UnPackBits(*bh, *ck\bytes, *ck\size, rowBytes)
               *bp = *bodyUnpacked
              EndIf
              If *bp And *bh And *bh\nPlanes = 24
                image = CreateImage(#PB_Any, *bh\w, *bh\h)
                If image
                  Protected Dim m(*bh\w)
                  Protected Dim r(*bh\w)
                  Protected Dim g(*bh\w)
                  Protected Dim b(*bh\w)
                  For col = 0 To *bh\w - 1
                    m(col) = 128 >> ( col % 8 )
                  Next
                  If StartDrawing(ImageOutput(image))
                    For row = 0 To *bh\h - 1
                      For plane = 0 To 23
                        If plane < 8
                          bits = 1 << plane
                          For col = 0 To *bh\w - 1
                            If *bp\b[col >> 3] & m(col)
                              r(col) | bits
                            EndIf
                          Next
                        ElseIf plane > 15
                          bits = 1 << ( plane - 16 )
                          For col = 0 To *bh\w - 1
                            If *bp\b[col >> 3] & m(col)
                              b(col) | bits
                            EndIf
                          Next
                        Else
                          bits = 1 << ( plane - 8 )
                          For col = 0 To *bh\w - 1
                            If *bp\b[col >> 3] & m(col)
                              g(col) | bits
                            EndIf
                          Next
                        EndIf
                        *bp + rowBytes
                      Next
                      For col = 0 To *bh\w - 1
                        Plot(col, row, RGB(r(col), g(col), b(col)))
                        r(col) = 0
                        g(col) = 0
                        b(col) = 0
                      Next
                    Next
                    StopDrawing()
                  EndIf
                EndIf
              EndIf
              If *bodyUnpacked
                FreeMemory(*bodyUnpacked)
              EndIf
              Break
          EndSelect
          If *ck < *eof
            *ck + 8 + *ck\size
          Else
            *ck = 0
          EndIf
        Wend
      EndIf
    EndIf
    
    ProcedureReturn image
    
  EndProcedure
  
  Procedure Load(fileName.s)
    
    Protected image.i, file.i, fileSize.q, *fileData
    
    file = ReadFile(#PB_Any, fileName)
    If file
      fileSize = Lof(file)
      If fileSize > 0
        *fileData = AllocateMemory(fileSize, #PB_Memory_NoClear)
        If *fileData
          If ReadData(file, *fileData, fileSize) > 0
            image = Catch(*fileData, fileSize)
          EndIf
          FreeMemory(*fileData)
        EndIf
      EndIf
      CloseFile(file)
    EndIf
    
    ProcedureReturn image
    
  EndProcedure
  
EndModule
et l'afficheur :

Code : Tout sélectionner

;==============================================================================
;== Drag and display IFF-ILBM 24 bits files on the window
;==============================================================================

IncludeFile "TinyIFF24.pbi"

EnableExplicit

Procedure load(file.s)
  
  Protected image, iw, ih, ww, wh, t1, t2
  
  t1 = ElapsedMilliseconds()
  image = TinyIFF24::Load(file)
  t2 = ElapsedMilliseconds()
  
  If image
    ww = WindowWidth(0)
    wh = WindowHeight(0)
    iw = ImageWidth(image)
    ih = ImageHeight(image)
    ResizeGadget(0, ( ww - iw ) / 2, ( wh - ih ) / 2, #PB_Ignore, #PB_Ignore)
    SetWindowTitle(0, GetFilePart(file))
    If StartDrawing(ImageOutput(image))
      FrontColor(0)
      DrawingMode(#PB_2DDrawing_Transparent)
      DrawText(15, 15, "File: " + file)
      DrawText(15, 35, "Format: IFF ILBM 24Bits (Compressed and Interleaved)")
      DrawText(15, 55, "Dimension : " + Str(iw) + " x " + Str(ih))
      DrawText(15, 75, "Nb Pixels : " + Str(iw * ih))
      DrawText(15, 95, "Loaded in " + Str(t2 - t1) + "ms")
      StopDrawing()
    EndIf
    SetGadgetState(0, ImageID(image))
    FreeImage(image)
  EndIf
  
EndProcedure

;==============================================================================

If OpenWindow(0, 0, 0, 1430, 1010, "", #PB_Window_ScreenCentered)
  SetWindowColor(0, 0)
  ImageGadget(0, 0, 0, WindowWidth(0), WindowHeight(0), 0)
  EnableWindowDrop(0, #PB_Drop_Files, #PB_Drag_Link)
  load("MARBLES.IFF")
  Repeat
    Select WaitWindowEvent()
      Case #PB_Event_CloseWindow: Break
      Case #PB_Event_WindowDrop: load(StringField(EventDropFiles(), 1, Chr(10)))
    EndSelect
  ForEver
EndIf

;==============================================================================
Image
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par djes »

Juste en regardant le code je me demande s'il ne serait pas plus rapide pour les macros uint de simplement lire octet par octet plutôt que de faire des décalages de bits. Beau boulot sinon, comme la dernière fois :-)
Avatar de l’utilisateur
Flype
Messages : 2431
Inscription : jeu. 29/janv./2004 0:26
Localisation : Nantes

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par Flype »

Salut djes,
Je ne pense pas non car dans les boucles critiques je n'ai pas besoins de ces macros. C'est juste dans les headers / chunks mais pas dans les datas.
Image
Avatar de l’utilisateur
djes
Messages : 4252
Inscription : ven. 11/févr./2005 17:34
Localisation : Arras, France

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par djes »

Flype a écrit :Salut djes,
Je ne pense pas non car dans les boucles critiques je n'ai pas besoins de ces macros. C'est juste dans les headers / chunks mais pas dans les datas.
Ah ok nickel
Avatar de l’utilisateur
Flype
Messages : 2431
Inscription : jeu. 29/janv./2004 0:26
Localisation : Nantes

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par Flype »

Spock, je peux pas te dire comme ça. .. je ne suis pas devant mon pc mais c'est peut être parce que je n'ai pas typé certaines variables (image au lieu de image.i par ex) et que je n'ai pas vu le problème car j'utilise le compilateur 64bits. A voir.

Pour l'utilisation du code, si je poste un code (à moi) c'est qu'il est devenu public, d'autant que je n'invente rien ici. Le format IFF/ILBM est lui même public.
Image
Avatar de l’utilisateur
Flype
Messages : 2431
Inscription : jeu. 29/janv./2004 0:26
Localisation : Nantes

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par Flype »

Spock a écrit :je ne savais pas qu'une constante declaré dans le prg principal
n'avait pas d'existence dans un module !

j'en apprends tout les jours :)

C'est tout l'intérêt des modules justement. Étanchéité de la portée des variables/procédures pour éviter les effets de bords. Ça facilite l'intégration d'un code dans un autre sans conflit....
Image
Ollivier
Messages : 4190
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par Ollivier »

Bonjour Spock,

(Attention, je me risque: je ne connais du tout ces standards, mais vraiment niet) En cherchant les entiers longs (32bits):
'MROF', 'DHMB' et 'YDOB'.

Code : Tout sélectionner

#ID_FORM = MAKEID('F','O','R','M') ; IFF file
#ID_ILBM = MAKEID('I','L','B','M') ; Interleaved Bitmap (Planar)
#ID_PBM = MAKEID('P','B','M',' ') ; Portable Bitmap (Chunky)
#ID_BMHD = MAKEID('B','M','H','D') ; Bitmap Header
#ID_CMAP = MAKEID('C','M','A','P') ; ColorMap
#ID_CAMG = MAKEID('C','A','M','G') ; ViewModes
#ID_BODY = MAKEID('B','O','D','Y') ; Bitmap Data
@Flype

Code très lisible, bien qu'il soit stratosphérique pour moi.
Avatar de l’utilisateur
TazNormand
Messages : 1294
Inscription : ven. 27/oct./2006 12:19
Localisation : Calvados (14)

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par TazNormand »

Sans avoir regardé le code en détail non plus, peut-être du côté de la structure BitMap Header "Structure IFF_BMHD" ?

Code : Tout sélectionner

	Structure IFF_BMHD
		w.u           ; UWORD
		h.u           ; UWORD
		x.w           ; WORD
		y.w           ; WORD
		nPlanes.a     ; UBYTE
		masking.a     ; UBYTE
		compression.a ; UBYTE
		pad.a         ; UBYTE
		tColor.u      ; UWORD
		xAspect.a     ; UBYTE
		yAspect.a     ; UBYTE
		pageWidth.w   ; WORD
		pageHeight.w  ; WORD
	EndStructure
nPlanes donnant le nombre de "bitplanes", soit le nombre de couleur en 2^bitplanes (2 puissance nPlanes)
Image
Image
Avatar de l’utilisateur
Flype
Messages : 2431
Inscription : jeu. 29/janv./2004 0:26
Localisation : Nantes

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par Flype »

Tant mieux si tu as réussi à intégrer ça dans ton programme. Pour ma part, je vais continuer à l'améliorer et tout mettre en seul module. Je me suis constitué une collection d'images de tests dans différents formats. J'ai trouvé quelques bugs et fait quelques optimisations encore. Je posterai la MAJ quand ce sera prêt.
Image
Avatar de l’utilisateur
Flype
Messages : 2431
Inscription : jeu. 29/janv./2004 0:26
Localisation : Nantes

Re: Module TinyIFF (Images Amiga IFF ILBM PBM)

Message par Flype »

Mise à jour du loader d'images IFF.

Réduction importante du code.

Ajout d'options de chargement : #ImageID, KeepAspect, ResizeMode

Code : Tout sélectionner

DeclareModule TinyIFF
  
  ; @TinyIFF::Load()
  ; Charge une image à partir de l'emplacement disque spécifié.
  ; #ImageID   : Le numéro d'identification de l'image à charger. 
  ; FileName$  : Le chemin et le nom du fichier à charger. 
  ; KeepAspect : Garde l'aspect original de l'image (Tient compte de xAspect/yAspect).
  ; ResizeMode : Mode de redimensionnement de l'image (avec ou sans interpolation).
  Declare Load(ImageID.l, FileName$, KeepAspect.l = #True, ResizeMode.l = #PB_Image_Raw)
  
  ; @TinyIFF::Catch()
  ; Charge une image à partir de l'emplacement mémoire spécifié.
  ; #ImageID   : Le numéro d'identification de l'image à charger. 
  ; *Memory    : Adresse de l'emplacement mémoire.
  ; MemSize.q  : Taille de l'emplacement mémoire (en octets).
  ; KeepAspect : Garde l'aspect original de l'image (Tient compte de xAspect/yAspect).
  ; ResizeMode : Mode de redimensionnement de l'image (avec ou sans interpolation).
  Declare Catch(ImageID.l, *Memory, MemSize.q, KeepAspect.l = #True, ResizeMode.l = #PB_Image_Raw)
  
  ; @Parameter KeepAspect
  ; #True  : Garde l'aspect original de l'image (défaut).
  ; #False : Redimensionne l'image en tenant compte de xAspect/yAspect.
  
  ; @Parameter ResizeMode
  ; #PB_Image_Raw    : Redimensionne l'image avec interpolation (défaut).
  ; #PB_Image_Smooth : Redimensionne l'image sans interpolation.
  
EndDeclareModule
Compatible avec les formats suivants :

Code : Tout sélectionner

. [X] FORM ILBM (2 à 256 couleurs)
. [X] FORM ILBM EHB (64 couleurs)
. [X] FORM ILBM HAM6 (4096 couleurs)
. [_] FORM ILBM SHAM (4096 à 9216 couleurs) (non-implémenté)
. [X] FORM ILBM HAM8 (262144 à 16777216 couleurs)
. [X] FORM ILBM 24bits (16777216 couleurs)
. [X] FORM PBM 8bits (2 à 256 couleurs)
. [X] FORM PBM 24bits (16777216 couleurs) (théorique, non-testé, pas d'images de test)

Code : Tout sélectionner

;--------------------------------------------------------------------------------------------------
; Module:      TinyIFF.pbi
; Description: Module pour charger des images IFF-ILBM ou IFF-PBM.
; Auteur:      flype, flype44(at)gmail(dot)com
; Révision:    1.5 (2015-09-17)
; Compilateur: PureBasic 5.40 LTS Beta 3 (x64) Linux
;--------------------------------------------------------------------------------------------------

DeclareModule TinyIFF
  
  ; @TinyIFF::Load()
  ; Charge une image à partir de l'emplacement disque spécifié.
  ; #ImageID   : Le numéro d'identification de l'image à charger. 
  ; FileName$  : Le chemin et le nom du fichier à charger. 
  ; KeepAspect : Garde l'aspect original de l'image (Tient compte de xAspect/yAspect).
  ; ResizeMode : Mode de redimensionnement de l'image (avec ou sans interpolation).
  Declare Load(ImageID.l, FileName$, KeepAspect.l = #True, ResizeMode.l = #PB_Image_Raw)
  
  ; @TinyIFF::Catch()
  ; Charge une image à partir de l'emplacement mémoire spécifié.
  ; #ImageID   : Le numéro d'identification de l'image à charger. 
  ; *Memory    : Adresse de l'emplacement mémoire.
  ; MemSize.q  : Taille de l'emplacement mémoire (en octets).
  ; KeepAspect : Garde l'aspect original de l'image (Tient compte de xAspect/yAspect).
  ; ResizeMode : Mode de redimensionnement de l'image (avec ou sans interpolation).
  Declare Catch(ImageID.l, *Memory, MemSize.q, KeepAspect.l = #True, ResizeMode.l = #PB_Image_Raw)
  
  ; @Parameter KeepAspect
  ; #True  : Garde l'aspect original de l'image (défaut).
  ; #False : Redimensionne l'image en tenant compte de xAspect/yAspect.
  
  ; @Parameter ResizeMode
  ; #PB_Image_Raw    : Redimensionne l'image avec interpolation (défaut).
  ; #PB_Image_Smooth : Redimensionne l'image sans interpolation.
  
EndDeclareModule

;--------------------------------------------------------------------------------------------------
  
Module TinyIFF
  
  ;------------------------------------------------------------------------------------------------
  
  EnableExplicit
  
  ;------------------------------------------------------------------------------------------------
  
  Macro UINT16(a)
    ((((a)<<8)&$FF00)|(((a)>>8)&$FF))
  EndMacro
  
  Macro UINT32(a)
    ((((a)&$FF)<<24)|(((a)&$FF00)<<8)|(((a)>>8)&$FF00)|(((a)>>24)&$FF))
  EndMacro
  
  Macro MAKEID(a, b, c, d)
    ((a)|((b)<<8)|((c)<<16)|((d)<<24))
  EndMacro
  
  ;------------------------------------------------------------------------------------------------
  
  Enumeration ChunkIDs
    #ID_FORM = MAKEID('F','O','R','M') ; IFF file
    #ID_ILBM = MAKEID('I','L','B','M') ; Interleaved Bitmap (Planar)
    #ID_PBM  = MAKEID('P','B','M',' ') ; Portable Bitmap (Chunky)
    #ID_BMHD = MAKEID('B','M','H','D') ; Bitmap Header
    #ID_CMAP = MAKEID('C','M','A','P') ; Color Map
    #ID_CAMG = MAKEID('C','A','M','G') ; View Modes
    #ID_BODY = MAKEID('B','O','D','Y') ; Bitmap Data
  EndEnumeration
  
  Enumeration ViewModes
    #camgLace       = $0004 ; Interlaced
    #camgEHB        = $0080 ; Extra Half Bright
    #camgHAM        = $0800 ; Hold And Modify
    #camgHiRes      = $8000 ; High Resolution
    #camgSuperHiRes = $0020 ; Super High Resolution
  EndEnumeration
  
  Enumeration BitmapHeaderCmp
    #cmpNone     ; No compression
    #cmpByteRun1 ; ByteRun1 encoding
  EndEnumeration
  
  ;------------------------------------------------------------------------------------------------
  
  Structure BYTES
    b.b[0]
  EndStructure
  
  Structure UBYTES
    b.a[0]
  EndStructure
  
  Structure IFF_RGB8
    r.a
    g.a
    b.a
  EndStructure
  
  Structure IFF_BMHD
    w.u           ; UWORD
    h.u           ; UWORD
    x.w           ; WORD
    y.w           ; WORD
    nPlanes.a     ; UBYTE
    masking.a     ; UBYTE
    compression.a ; UBYTE
    pad.a         ; UBYTE
    tColor.u      ; UWORD
    xAspect.a     ; UBYTE
    yAspect.a     ; UBYTE
    pageWidth.w   ; WORD
    pageHeight.w  ; WORD
  EndStructure
  
  Structure IFF_CMAP
    c.IFF_RGB8[0]
  EndStructure
  
  Structure IFF_Chunk
    id.l
    size.l
    bytes.UBYTES
  EndStructure
  
  Structure IFF_Header
    id.l
    size.l
    name.l
    chunk.UBYTES
  EndStructure
  
  ;------------------------------------------------------------------------------------------------
  
  Procedure UnPackBits(*bh.IFF_BMHD, *packedBits.BYTES, packedSize, rowBytes)
    Protected i, j, k, v, unpackedSize, *unpackedBits.BYTES
    unpackedSize = 1 + ( *bh\h * rowBytes * *bh\nPlanes )
    If unpackedSize
      *unpackedBits = AllocateMemory(unpackedSize)
      If *unpackedBits
        While i < packedSize
          v = *packedBits\b[i]
          If v >= 0
            For j = 0 To v
              *unpackedBits\b[k] = *packedBits\b[i + 1 + j]
              k + 1
            Next
            i + j
          ElseIf v <> -128
            For j = 0 To -v
              *unpackedBits\b[k] = *packedBits\b[i + 1]
              k + 1
            Next
            i + 1
          EndIf
          i + 1
        Wend
      EndIf
    EndIf
    ProcedureReturn *unpackedBits
  EndProcedure
  
  ;------------------------------------------------------------------------------------------------
  
  Procedure Catch_PBM_8(*bh.IFF_BMHD, *bp.UBYTES, Array cmap.l(1))
    Protected x, y, i
    For y = 0 To *bh\h - 1
      For x = 0 To *bh\w - 1
        Plot(x, y, cmap(*bp\b[i]))
        i + 1
      Next
    Next
  EndProcedure
  
  Procedure Catch_PBM_24(*bh.IFF_BMHD, *bp.UBYTES)
    Protected x, y, i
    For y = 0 To *bh\h - 1
      For x = 0 To *bh\w - 1
        Plot(x, y, RGB(*bp\b[i], *bp\b[i+1], *bp\b[i+2]))
        i + 3
      Next
    Next
  EndProcedure
  
  ;------------------------------------------------------------------------------------------------
  
  Procedure Catch_ILBM_8(*bh.IFF_BMHD, *bp.UBYTES, rowBytes.w, camg.l, cmapSize.l, Array cmap.l(1))
    Protected i, x, y, c, p, plane, mbits, mask, hbits, Dim pixels(*bh\w)
    If camg & #camgHAM
      hbits = 4
      If *bh\nPlanes > 6 : hbits + 2 : EndIf
      mbits = 8 - hbits
      mask = ( 1 << hbits ) - 1
    EndIf
    If camg & #camgEHB
      For i = 0 To ( cmapSize / 3 ) - 1
        cmap(i+32) = RGB(Red(cmap(i)) >> 1, Green(cmap(i)) >> 1, Blue(cmap(i)) >> 1)
      Next
    EndIf
    For y = 0 To *bh\h - 1
      For plane = 0 To *bh\nPlanes - 1
        For x = 0 To *bh\w - 1
          If *bp\b[x >> 3] & ( 128 >> ( x % 8 ) )
            pixels(x) | ( 1 << plane )
          EndIf
        Next
        *bp + rowBytes
      Next
      For x = 0 To *bh\w - 1
        If camg & #camgHAM
          p = pixels(x)
          Select p >> hbits
            Case 0: c = cmap(p & mask)
            Case 1: c = RGB(Red(c), Green(c), ( p & mask ) << mbits)
            Case 2: c = RGB(( p & mask ) << mbits, Green(c), Blue(c))
            Case 3: c = RGB(Red(c), ( p & mask ) << mbits, Blue(c))
          EndSelect
        Else
          c = cmap(pixels(x))
        EndIf
        Plot(x, y, c)
        pixels(x) = 0
      Next
      c = 0
    Next
  EndProcedure
  
  Procedure Catch_ILBM_24(*bh.IFF_BMHD, *bp.UBYTES, rowBytes.l)
    Protected x, y, w, h, p, plane, p0, p1, p2
    Protected Dim m(*bh\w), Dim r(*bh\w), Dim g(*bh\w), Dim b(*bh\w)
    w = *bh\w - 1 : h = *bh\h - 1 : p = *bh\nPlanes - 1
    For x = 0 To w : m(x) = 128 >> ( x % 8 ) : Next
    For y = 0 To h
      For plane = 0 To p
        p0 = 1 <<   plane
        p1 = 1 << ( plane -  8 )
        p2 = 1 << ( plane - 16 )
        If plane < 8
          For x = 0 To w
            If *bp\b[x >> 3] & m(x) : r(x) | p0 : EndIf
          Next
        ElseIf plane > 15
          For x = 0 To w
            If *bp\b[x >> 3] & m(x) : b(x) | p2 : EndIf
          Next
        Else
          For x = 0 To w
            If *bp\b[x >> 3] & m(x) : g(x) | p1 : EndIf
          Next
        EndIf
        *bp + rowBytes
      Next
      For x = 0 To w
        Plot(x, y, RGB(r(x), g(x), b(x)))
        r(x) = 0 : g(x) = 0 : b(x) = 0
      Next
    Next
  EndProcedure
  
  ;------------------------------------------------------------------------------------------------
  
  Procedure Catch(ImageID.l, *m.IFF_Header, MemSize.q, KeepAspect.l = #True, ResizeMode.l = #PB_Image_Raw)
    Protected i.l, image.i, rowBytes.w, camg.l, cmapSize.l, *imageOutput, *bp, *eof, *bodyUnpacked
    Protected *ck.IFF_Chunk, *bh.IFF_BMHD, *cmap.IFF_CMAP, Dim cmap.l(256)
    If *m And *m\id = #ID_FORM And ( *m\name = #ID_ILBM Or *m\name = #ID_PBM )
      *m\size = UINT32(*m\size)
      If *m\size > 0 And *m\size < MemSize
        *eof = *m + MemSize
        *ck = *m\chunk
        While *ck
          *ck\size = UINT32(*ck\size)
          If *ck\size & 1
            *ck\size + 1
          EndIf
          Select *ck\id
            Case #ID_BMHD
              *bh = *ck\bytes
              *bh\w = UINT16(*bh\w)
              *bh\h = UINT16(*bh\h)
              rowBytes = ( ( ( *bh\w + 15 ) >> 4 ) << 1 )
            Case #ID_CAMG
              camg = UINT32(PeekL(*ck\bytes))
              Debug "camg = %" + RSet(Bin(camg, #PB_Long), 32, "0")
            Case #ID_CMAP
              *cmap = *ck\bytes
              cmapSize = *ck\size
              For i = 0 To ( cmapSize / 3 ) - 1
                cmap(i) = RGB(*cmap\c[i]\r, *cmap\c[i]\g, *cmap\c[i]\b)
              Next
            Case #ID_BODY
              *bp = *ck\bytes
              If *bh\compression = #cmpByteRun1
                *bodyUnpacked = UnPackBits(*bh, *ck\bytes, *ck\size, rowBytes)
                *bp = *bodyUnpacked
              EndIf
              If *bp And *bh
                image = CreateImage(ImageID, *bh\w, *bh\h, 24, RGB(0, 0, 0))
                If image
                  If ImageID = #PB_Any
                    *imageOutput = ImageOutput(image)
                  Else
                    *imageOutput = ImageOutput(ImageID)
                  EndIf
                  If StartDrawing(*imageOutput)
                    Select *m\name
                      Case #ID_ILBM
                        If *bh\nPlanes = 24
                          Catch_ILBM_24(*bh, *bp, rowBytes)
                        Else
                          Catch_ILBM_8(*bh, *bp, rowBytes, camg, cmapSize, cmap())
                        EndIf
                      Case #ID_PBM
                        If *bh\nPlanes = 24
                          Catch_PBM_24(*bh, *bp)
                        Else
                          Catch_PBM_8(*bh, *bp, cmap())
                        EndIf
                    EndSelect
                    StopDrawing()
                  EndIf
                EndIf
                If KeepAspect = #False
                  If *bh\xAspect = 0 Or *bh\yAspect = 0
                    *bh\xAspect = 10 : *bh\yAspect = 11
                  EndIf
                  Protected xRes.d = 1.0 + ( *bh\xAspect / *bh\yAspect )
                  Protected yRes.d = 1.0 + ( *bh\yAspect / *bh\xAspect )
                  If ImageID = #PB_Any
                    ResizeImage(image, *bh\w * xRes, *bh\h * yRes, ResizeMode)
                  Else
                    ResizeImage(ImageID, *bh\w * xRes, *bh\h * yRes, ResizeMode)
                  EndIf
                EndIf
              EndIf
              If *bodyUnpacked
                FreeMemory(*bodyUnpacked)
              EndIf
              Break
          EndSelect
          If *ck < *eof
            *ck + 8 + *ck\size
          Else
            *ck = 0
          EndIf
        Wend
      EndIf
    EndIf
    ProcedureReturn image
  EndProcedure
  
  Procedure Load(ImageID.l, FileName$, KeepAspect.l = #True, ResizeMode.l = #PB_Image_Raw)
    Protected image.i, file.i, fileSize.q, *fileData
    file = ReadFile(#PB_Any, FileName$)
    If file
      fileSize = Lof(file)
      If fileSize > 0
        *fileData = AllocateMemory(fileSize, #PB_Memory_NoClear)
        If *fileData
          If ReadData(file, *fileData, fileSize) > 0
            image = Catch(ImageID, *fileData, fileSize, KeepAspect, ResizeMode)
          EndIf
          FreeMemory(*fileData)
        EndIf
      EndIf
      CloseFile(file)
    EndIf
    ProcedureReturn image
  EndProcedure
  
EndModule

;--------------------------------------------------------------------------------------------------
Image
Répondre