PNM image formats

Share your advanced PureBasic knowledge/code with the community.
infratec
Always Here
Always Here
Posts: 7618
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

PNM image formats

Post by infratec »

Today I came across the PNM image files (https://www.purebasic.fr/english/viewtopic.php?t=83512)

I only found an old version here:
https://www.purebasic.fr/english/viewtopic.php?t=27462

And on my disk I found something from me, but it looked incomplete.
So I decided to give it a new try.

P1 to P6 should always work.
P7 should now also work. (IrfanView can not load a *.pam file at all :wink: )

One limitation: only one image per file
Maybe I add this feature later.

Code: Select all

;
; PNM.pbi
;
; An include file for PNM picture files
; https://www.purebasic.fr/english/viewtopic.php?p=615861
;
; https://en.wikipedia.org/wiki/Netpbm
; 
; V1.2.0 Added SaveImagePNM
; V1.1.0 Added transparency for P7 / pam files
; V1.0.2 Fixed a bug with pam files (officially pam is only binary, but I found examples with ASCII)
; V1.0.1 Fixed a bug with pam files (no value after ENDHDR)
; V1.0.0 LoadImagePNM() implemented
;


CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf


Enumeration PNM_Coding
  #PNM_Coding_None
  #PNM_Coding_Ascii
  #PNM_Coding_Binary
  #PNM_Coding_Arbitrary
EndEnumeration

Enumeration PNM_FileType
  #PNM_Filetype_None
  #PNM_Filetype_Bitmap
  #PNM_Filetype_Graymap
  #PNM_Filetype_Pixmap
EndEnumeration

Enumeration PNM_HeaderState
  #PNM_HeaderState_Coding
  #PNM_HeaderState_Width
  #PNM_HeaderState_Height
  #PNM_HeaderState_Max
EndEnumeration


Macro IsWhiteSpacePNM(Char)
  Bool(Char = ' ' Or Char = #TAB Or Char = #CR Or Char = #LF)
EndMacro



Procedure.i LoadImagePNM(Img.i, Filename$)
  
  Protected File.i, FileExtension$, *Buffer, *BufferEnd, *BufferPtr.Ascii, State.i
  Protected Coding.i, FileType.i, HeaderComplete.i, Width.i, Height.i, Max.i, Depth.i, Alpha.i, Transparency.i
  Protected x.i, y.i, Color.i, ColorFactor.f, Red.i, Green.i, Blue.i, BitMask.i
  Protected Width$, Height$, Max$, Color$, Line$, Value$, Depth$, Tupltype$, NewLine$
  
  
  File = ReadFile(#PB_Any, Filename$)
  If File
    
    *Buffer = AllocateMemory(Lof(File), #PB_Memory_NoClear)
    If *Buffer
      
      If ReadData(File, *Buffer, MemorySize(*Buffer)) = MemorySize(*Buffer)
        
        *BufferPtr = *Buffer
        *BufferEnd = *Buffer + MemorySize(*Buffer)
        
        FileExtension$ = GetExtensionPart(Filename$)
        
        If *BufferPtr\a = 'P'
          *BufferPtr + 1
          
          Select *BufferPtr\a
            Case '1' : FileType = #PNM_Filetype_Bitmap : Coding = #PNM_Coding_Ascii
            Case '2' : FileType = #PNM_Filetype_Graymap : Coding = #PNM_Coding_Ascii
            Case '3' : FileType = #PNM_Filetype_Pixmap : Coding = #PNM_Coding_Ascii
            Case '4' : FileType = #PNM_Filetype_Bitmap : Coding = #PNM_Coding_Binary
            Case '5' : FileType = #PNM_Filetype_Graymap : Coding = #PNM_Coding_Binary
            Case '6' : FileType = #PNM_Filetype_Pixmap : Coding = #PNM_Coding_Binary
            Case '7' : Coding = #PNM_Coding_Arbitrary
          EndSelect
          
          If Coding <> #PNM_Coding_None
            Debug "Coding Ok"
            *BufferPtr + 1
            If Coding = #PNM_Coding_Arbitrary
              
              NewLine$ = Chr(*BufferPtr\a)
              *BufferPtr + 1
              If *BufferPtr\a = #LF Or *BufferPtr\a = #CR
                NewLine$ + Chr(*BufferPtr\a)
              EndIf
              *BufferPtr - 1
              
              While Not HeaderComplete And *BufferPtr <= *BufferEnd
                
                While IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                  *BufferPtr + 1
                Wend
                
                Line$ = ""
                While Not IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                  Line$ + Chr(*BufferPtr\a)
                  *BufferPtr + 1
                Wend
                Debug Line$
                
                If Line$ <> "ENDHDR" And Left(Line$, 1) <> "#"
                  
                  While IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                    *BufferPtr + 1
                  Wend
                  
                  Value$ = ""
                  While Not IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                    Value$ + Chr(*BufferPtr\a)
                    *BufferPtr + 1
                  Wend
                  Debug Value$
                  
                EndIf
                
                *BufferPtr + Len(NewLine$)
                
                Select Left(Line$, 4)
                  Case "WIDT"
                    Width$ = Value$
                  Case "HEIG"
                    Height$ = Value$
                  Case "DEPT"
                    Depth$ = Value$
                    Depth = Val(Depth$)
                    If Depth = 4
                      Transparency = #True
                    EndIf
                  Case "MAXV"
                    Max$ = Value$
                    Max = Val(Max$)
                    ColorFactor = 255 / Max
                  Case "TUPL"
                    Tupltype$ = Value$
                    Select LCase(Tupltype$)
                      Case "blackandwhite"
                        FileType = #PNM_Filetype_Bitmap
                      Case "blackandwhite_alpha"
                        FileType = #PNM_Filetype_Bitmap
                        Transparency = #True
                      Case "grayscale"
                        FileType = #PNM_Filetype_Graymap
                      Case "grayscale_alpha"
                        FileType = #PNM_Filetype_Graymap
                        Transparency = #True
                      Case "color"
                        FileType = #PNM_Filetype_Pixmap
                      Case "rgb"
                        FileType = #PNM_Filetype_Pixmap
                      Case "rgb_alpha"
                        FileType = #PNM_Filetype_Pixmap
                        Transparency = #True
                      Case "transparent"
                        Transparency = #True
                    EndSelect
                  Case "ENDH"
                    
                    If Tupltype$ = ""
                      Select Depth
                        Case 1
                          If Max = 1
                            FileType = #PNM_Filetype_Bitmap
                          Else
                            FileType = #PNM_Filetype_Graymap
                          EndIf
                        Case 2
                          FileType = #PNM_Filetype_Graymap
                        Case 3, 4
                          FileType = #PNM_Filetype_Pixmap
                      EndSelect
                    EndIf
                    
                    If *BufferPtr\a = ' ' Or (*BufferPtr\a >= '0' And *BufferPtr\a <= '9')
                      Coding = #PNM_Coding_Ascii
                    Else
                      Coding = #PNM_Coding_Binary
                    EndIf
                    
                    HeaderComplete = #True
                EndSelect
                
              Wend
              
            Else
              
              State = #PNM_HeaderState_Width
              While Not HeaderComplete And *BufferPtr <= *BufferEnd
                While IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                  *BufferPtr + 1
                Wend
                
                If *BufferPtr\a = '#'
                  While (*BufferPtr\a <> #CR And *BufferPtr\a <> #LF) And *BufferPtr <= *BufferEnd
                    *BufferPtr + 1
                  Wend
                Else
                  While Not IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                    Select State
                      Case #PNM_HeaderState_Width
                        Width$ + Chr(*BufferPtr\a)
                      Case #PNM_HeaderState_Height
                        Height$ + Chr(*BufferPtr\a)
                      Case #PNM_HeaderState_Max
                        Max$ + Chr(*BufferPtr\a)
                    EndSelect
                    *BufferPtr + 1
                  Wend
                  
                  State + 1
                  If State = #PNM_HeaderState_Max And FileType = #PNM_Filetype_Bitmap
                    HeaderComplete = #True
                  EndIf
                  If State > #PNM_HeaderState_Max
                    Max = Val(Max$)
                    ColorFactor = 255 / Max
                    Debug ColorFactor
                    HeaderComplete = #True
                  EndIf
                  
                EndIf
                
              Wend
              
            EndIf
            
            Debug Width$ + "x" + Height$ + " Max: " + Max$
            
            If *BufferPtr < *BufferEnd
              Width = Val(Width$)
              Height = Val(Height$)
              If Img = #PB_Any
                If Transparency
                  Img = CreateImage(#PB_Any, Width, Height, 32, #PB_Image_Transparent)
                Else
                  Img = CreateImage(#PB_Any, Width, Height)
                EndIf
              Else
                If Transparency
                  CreateImage(Img, Width, Height, 32, #PB_Image_Transparent)
                Else
                  CreateImage(Img, Width, Height)
                EndIf
              EndIf
            EndIf
            
            If IsImage(Img)
              
              If StartDrawing(ImageOutput(Img))
                
                If Transparency
                  DrawingMode(#PB_2DDrawing_AllChannels)
                EndIf
                
                While IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                  *BufferPtr + 1
                Wend
                
                While *BufferPtr <= *BufferEnd
                  
                  Select Coding
                    Case #PNM_Coding_Ascii
                      
                      While IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                        *BufferPtr + 1
                      Wend
                      
                      Select FileType
                        Case #PNM_Filetype_Bitmap
                          If *BufferPtr\a = '0'
                            Plot(x, y, $FFFFFF)
                          Else
                            Plot(x, y, $000000)
                          EndIf
                          *BufferPtr + 1
                          
                        Case #PNM_Filetype_Graymap
                          Color$ = ""
                          While Not IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                            Color$ + Chr(*BufferPtr\a)
                            *BufferPtr + 1
                          Wend
                          Color = Val(Color$) * ColorFactor
                          Plot(x, y, RGB(Color, Color, Color)) 
                          
                        Case #PNM_Filetype_Pixmap
                          Color$ = ""
                          While Not IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                            Color$ + Chr(*BufferPtr\a)
                            *BufferPtr + 1
                          Wend
                          Red = Val(Color$) * ColorFactor
                          
                          While IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                            *BufferPtr + 1
                          Wend
                          
                          Color$ = ""
                          While Not IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                            Color$ + Chr(*BufferPtr\a)
                            *BufferPtr + 1
                          Wend
                          Green = Val(Color$) * ColorFactor
                          
                          While IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                            *BufferPtr + 1
                          Wend
                          
                          Color$ = ""
                          While Not IsWhiteSpacePNM(*BufferPtr\a) And *BufferPtr <= *BufferEnd
                            Color$ + Chr(*BufferPtr\a)
                            *BufferPtr + 1
                          Wend
                          Blue = Val(Color$) * ColorFactor
                          
                          Plot(x, y, RGB(Red, Green, Blue)) 
                          
                      EndSelect
                      
                      x + 1
                      If x = Width
                        x = 0
                        y + 1
                        If y = Height
                          *BufferPtr = *BufferEnd + 1
                        EndIf
                      EndIf
                      
                    Case #PNM_Coding_Binary
                      Select FileType
                        Case #PNM_Filetype_Bitmap
                          If Depth = 0
                            BitMask = $80
                            While BitMask
                              If *BufferPtr\a & BitMask
                                Plot(x, y, $000000)
                              Else
                                Plot(x, y, $FFFFFF)
                              EndIf
                              BitMask >> 1
                              
                              x + 1
                              If x = Width
                                x = 0
                                y + 1
                                If y = Height
                                  *BufferPtr = *BufferEnd + 1
                                  BitMask = 0
                                EndIf
                              EndIf
                              
                            Wend
                          Else
                            
                            If *BufferPtr\a = 0
                              If Transparency
                                *BufferPtr + 1
                                Alpha = *BufferPtr\a
                                Plot(x, y, RGBA(0, 0, 0, Alpha))
                              Else
                                Plot(x, y, 0)
                              EndIf
                            Else
                              If Transparency
                                *BufferPtr + 1
                                Alpha = *BufferPtr\a
                                Plot(x, y, RGBA(255, 255, 255, Alpha))
                              Else
                                Plot(x, y, $FFFFFF)
                              EndIf
                            EndIf
                            
                            x + 1
                            If x = Width
                              x = 0
                              y + 1
                              If y = Height
                                *BufferPtr = *BufferEnd + 1
                                BitMask = 0
                              EndIf
                            EndIf
                            
                          EndIf
                          *BufferPtr + 1
                          
                        Case #PNM_Filetype_Graymap
                          
                          Color = *BufferPtr\a
                          If Max > 255
                            *BufferPtr + 1
                            Color << 8
                            Color | *BufferPtr\a
                          EndIf
                          Color = Color * ColorFactor
                          
                          If Transparency
                            Alpha = *BufferPtr\a
                            If Max > 255
                              *BufferPtr + 1
                              Alpha << 8
                              Alpha | *BufferPtr\a
                            EndIf
                            Alpha = Alpha * ColorFactor
                            Plot(x, y, RGBA(Color, Color, Color, Alpha))
                          Else
                            Plot(x, y, RGB(Color, Color, Color))
                          EndIf
                          
                          x + 1
                          If x = Width
                            x = 0
                            y + 1
                            If y = Height
                              *BufferPtr = *BufferEnd
                              BitMask = 0
                            EndIf
                          EndIf
                          
                          *BufferPtr + 1
                          
                        Case #PNM_Filetype_Pixmap
                          Red = *BufferPtr\a
                          If Max > 255
                            *BufferPtr + 1
                            Red << 8
                            Red | *BufferPtr\a
                          EndIf
                          Red = Red * ColorFactor
                          
                          *BufferPtr + 1
                          Green = *BufferPtr\a
                          If Max > 255
                            *BufferPtr + 1
                            Green << 8
                            Green | *BufferPtr\a
                          EndIf
                          Green = Green * ColorFactor
                          
                          *BufferPtr + 1
                          Blue = *BufferPtr\a
                          If Max > 255
                            *BufferPtr + 1
                            Blue << 8
                            Blue | *BufferPtr\a
                          EndIf
                          Blue = Blue * ColorFactor
                          
                          If Transparency
                            *BufferPtr + 1
                            Alpha = *BufferPtr\a
                            If Max > 255
                              *BufferPtr + 1
                              Alpha << 8
                              Alpha | *BufferPtr\a
                            EndIf
                            Alpha = Alpha * ColorFactor
                            Debug Hex(Alpha)
                            Plot(x, y, RGBA(Red, Green, Blue, Alpha))
                          Else
                            Plot(x, y, RGB(Red, Green, Blue))
                          EndIf
                          
                          x + 1
                          If x = Width
                            x = 0
                            y + 1
                            If y = Height
                              *BufferPtr = *BufferEnd
                              BitMask = 0
                            EndIf
                          EndIf
                          
                          *BufferPtr + 1
                          
                      EndSelect
                      
                  EndSelect
                  
                Wend
                StopDrawing()
              EndIf
              
            Else
              Img = 0
            EndIf
            
          EndIf
        EndIf
        
      EndIf
      
      FreeMemory(*Buffer)
    EndIf
    
    CloseFile(File)
  EndIf
  
  ProcedureReturn Img
  
EndProcedure



;(#Image, Filename$ [, ImagePlugin [, Flags [, Depth]]])
Procedure.i SaveImagePNM(Img.i, Filename$, Filetype.i=#PNM_Filetype_Pixmap, Coding.i=#PNM_Coding_Binary, Transparency.i=#False)
  
  Protected.a Byte
  Protected.i File, x, y, xMax, yMax, Color, BitMask
  Protected.f Gray
  
  
  If IsImage(Img)
    
    If StartDrawing(ImageOutput(Img))
      
      If Transparency
        DrawingMode(#PB_2DDrawing_AllChannels)
      EndIf
      
      File = CreateFile(#PB_Any, Filename$, #PB_Ascii)
      If File
        
        xmax = ImageWidth(Img)
        ymax = ImageHeight(Img)
        
        If Coding = #PNM_Coding_Arbitrary
          WriteStringN(File, "P7")
          WriteStringN(File, "WIDTH " + Str(xmax))
          WriteStringN(File, "HEIGHT " + Str(ymax))
          Select Filetype
            Case #PNM_Filetype_Bitmap
              If Transparency
                WriteStringN(File, "DEPTH 2")
                WriteStringN(File, "TUPLTYPE blackandwhite_alpha")
              Else
                WriteStringN(File, "DEPTH 1")
                WriteStringN(File, "TUPLTYPE blackandwhite")
              EndIf
              WriteStringN(File, "MAXVAL 1")
            Case #PNM_Filetype_Graymap
              If Transparency
                WriteStringN(File, "DEPTH 2")
                WriteStringN(File, "TUPLTYPE grayscale_alpha")
              Else
                WriteStringN(File, "DEPTH 1")
                WriteStringN(File, "TUPLTYPE grayscale")
              EndIf
              WriteStringN(File, "MAXVAL 255")
            Case #PNM_Filetype_Pixmap
              If Transparency
                WriteStringN(File, "DEPTH 4")
                WriteStringN(File, "TUPLTYPE rgb_alpha")
              Else
                WriteStringN(File, "DEPTH 3")
                WriteStringN(File, "TUPLTYPE rgb")
              EndIf
              WriteStringN(File, "MAXVAL 255")
          EndSelect
          WriteStringN(File, "ENDHDR")
        Else
          Select Filetype
            Case #PNM_Filetype_Bitmap
              If Coding = #PNM_Coding_Ascii
                WriteString(File, "P1" + #LF$)
              Else
                WriteString(File, "P4" + #LF$)
              EndIf
              WriteString(File, Str(xmax) + #LF$)
              WriteString(File, Str(ymax) + #LF$)
            Case #PNM_Filetype_Graymap
              If Coding = #PNM_Coding_Ascii
                WriteString(File, "P2" + #LF$)
              Else
                WriteString(File, "P5" + #LF$)
              EndIf
              WriteString(File, Str(xmax) + #LF$)
              WriteString(File, Str(ymax) + #LF$)
              WriteString(File, "255" + #LF$)
            Case #PNM_Filetype_Pixmap
              If Coding = #PNM_Coding_Ascii
                WriteString(File, "P3" + #LF$)
              Else
                WriteString(File, "P6" + #LF$)
              EndIf
              WriteString(File, Str(xmax) + #LF$)
              WriteString(File, Str(ymax) + #LF$)
              WriteString(File, "255" + #LF$)
          EndSelect
          
        EndIf
        
        xmax - 1
        ymax - 1
        BitMask = $80
        For y = 0 To ymax
          For x = 0 To xmax
            
            If Coding = #PNM_Coding_Ascii
              
              Select Filetype
                Case #PNM_Filetype_Bitmap
                  Color = Point(x, y)
                  Gray = 0.3 * Red(color) + 0.59 * Green(Color) + 0.11 * Blue(color)
                  If Gray > 128
                    WriteString(File, "0")
                  Else
                    WriteString(File, "1")
                  EndIf
                  If Transparency
                    WriteString(File, " " + Str(Alpha(color)))
                  EndIf
                  WriteStringN(File, "")
                  
                Case #PNM_Filetype_Graymap
                  Color = Point(x, y)
                  Gray = 0.3 * Red(color) + 0.59 * Green(Color) + 0.11 * Blue(color)
                  WriteString(File, StrF(Gray, 0))
                  If Transparency
                    WriteString(File, " " + Str(Alpha(color)))
                  EndIf
                  WriteStringN(File, "")
                  
                Case #PNM_Filetype_Pixmap
                  Color = Point(x, y)
                  WriteString(File, Str(Red(Color)) + " ")
                  WriteString(File, Str(Green(Color)) + " ")
                  WriteString(File, Str(Blue(Color)))
                  If Transparency
                    WriteString(File, " " + Str(Alpha(Color)))
                  EndIf
                  WriteStringN(File, "")
              EndSelect
              
            Else
              
              Select Filetype
                Case #PNM_Filetype_Bitmap
                  Color = Point(x, y)
                  Gray = 0.3 * Red(color) + 0.59 * Green(Color) + 0.11 * Blue(color)
                  If Coding = #PNM_Coding_Arbitrary
                    If Gray > 128
                      WriteByte(File, 1)
                    Else
                      WriteByte(File, 0)
                    EndIf
                  Else
                    If Gray < 128
                      Byte | BitMask
                    EndIf
                    BitMask >> 1
                    If BitMask = $00
                      WriteByte(File, Byte)
                      Byte = 0
                      BitMask = $80
                    EndIf
                  EndIf
                Case #PNM_Filetype_Graymap
                  Color = Point(x, y)
                  Gray = 0.3 * Red(color) + 0.59 * Green(Color) + 0.11 * Blue(color)
                  WriteByte(File, Int(Gray))
                  If Transparency
                    WriteByte(File, Alpha(Color))
                  EndIf
                Case #PNM_Filetype_Pixmap
                  Color = Point(x, y)
                  WriteByte(File, Red(color))
                  WriteByte(File, Green(color))
                  WriteByte(File, Blue(color))
                  If Transparency
                    WriteByte(File, Alpha(Color))
                  EndIf
              EndSelect
              
            EndIf
            
          Next x
        Next y
        
        If BitMask <> $00
          WriteByte(File, Byte)
        EndIf
        
        CloseFile(File)
      EndIf
      
      StopDrawing()
    EndIf
  EndIf
  
  ProcedureReturn File
  
EndProcedure






;-Demo
CompilerIf #PB_Compiler_IsMainFile
  
  Define.i Img
  Define Filename$
  
  
  Filename$ = OpenFileRequester("Choose a PNM file", "", "PNM|*.pnm;*.pbm;*.pgm;*.ppm;*.pam", 0)
  If Filename$
    Img = LoadImagePNM(#PB_Any, Filename$)
    If Img
      
      SaveImagePNM(Img, GetPathPart(ProgramFilename()) + GetFilePart(ProgramFilename(), #PB_FileSystem_NoExtension) + "_save.pnm", #PNM_Filetype_Bitmap, #PNM_Coding_Binary)
      
      OpenWindow(0, 0, 0, ImageWidth(Img), ImageHeight(Img), "PNM Demo", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
      ImageGadget(0, 0, 0, 0, 0, ImageID(Img))
      Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
      FreeImage(Img)
    EndIf
  EndIf
  
CompilerEndIf
Last edited by infratec on Mon Feb 12, 2024 8:26 pm, edited 4 times in total.
infratec
Always Here
Always Here
Posts: 7618
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: PNM image formats

Post by infratec »

v1.0.1

Fixed a bug with PAM files.
After the ENDHDR is no value.
threedslider
Enthusiast
Enthusiast
Posts: 397
Joined: Sat Feb 12, 2022 7:15 pm

Re: PNM image formats

Post by threedslider »

Great ! It works for loading my file in PPM :)

Thank you for sharing and I am looking forward to your progress :wink:
infratec
Always Here
Always Here
Posts: 7618
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: PNM image formats

Post by infratec »

V1.0.2

Officially P7 (pam) only uses binary format for the data, but I found exaples with ASCII as data.
Try to find out what is in use.
infratec
Always Here
Always Here
Posts: 7618
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: PNM image formats

Post by infratec »

V1.1.0

Added transparance for P7 / pam files.

Tested with a PNG converted online to PAM.
infratec
Always Here
Always Here
Posts: 7618
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: PNM image formats

Post by infratec »

V1.2.0

Added SaveImagePNM()
Post Reply