Xpm viewer/converter

Share your advanced PureBasic knowledge/code with the community.
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by El_Choni.

Hi,

This is the first working code of this little app. If you don't know what's its use, I tell you Xpm is an image format coded in ASCII, used widely in Linux and not so widely in Windows. The code should work in Linux, but I'm sure it won't. Anyway, I can't get CompilerIf #OS#Windows to work, it's commented in the code.

Feel free to suggest changes, optimizations, etc. Note that it will doesn't check XPM validity, so a crash may happen if your XPM file is malformed. It doesn't support named colors, but it does recognize and handle 48 bit codes (I don't understand why do they use them sometimes). It does not support compressed BMP formats.

I think 16 bpp Xpm files should be loaded as 24 bit so not to lose quality (and give after the posibility of saving as 16 bpp or 24). Well, feedback is welcome.

Code: Select all

; XpmTool Lite v. 0.9
; Xpm viewer/converter to Bmp
; written in PureBasic by El_Choni
; TODO: xpm2ico, bmp2xpm, ico2xpm, load Bmp from memory to view, stretch Bmp, multiple windows, optimizations (maybe a lib)

Structure ImageData
  width.l
  height.l
  colors.l
  chars.l
  bits.b
EndStructure

#BI_RGB = 0

;CompilerIf #OS  #Windows
;  Structure BITMAPFILEHEADERX
;    bfType.w
;    bfSize.l
;    bfReserved1.w
;    bfReserved2.w
;    bfOffBits.l
;  EndStructure
;  Structure BITMAPINFOHEADERX
;    biSize.l
;    biWidth.l
;    biHeight.l
;    biPlanes.w
;    biBitCount.w
;    biCompression.l
;    biSizeImage.l
;    biXPelsPerMeter.l
;    biYPelsPerMeter.l
;    biClrUsed.l
;    biClrImportant.l
;  EndStructure
;CompilerEndIf

BmpFile.BITMAPFILEHEADER
BmpInfo.BITMAPINFOHEADER

BmpFileName.s
extension.s
XpmData.ImageData
XpmFile.s
pkb.b
ch.s
chcomp.s
bit.b
position.f
md.f
dpad.f

Procedure Mod(dividend.l, divisor.l)
  If divisor0
    integerquotient = dividend/divisor
    remainder = dividend-(integerquotient*divisor)
  Else
    remainder = 0
  EndIf
  ProcedureReturn remainder
EndProcedure

Procedure Hex2Num(hexa.s, flag16.b)
  If Len(hexa)=12
    hexa = Mid(hexa, 1, 2)+Mid(hexa, 5, 2)+Mid(hexa, 9, 2)
  EndIf
  num = 0
  For i = 1 To 5 Step 2
    hexcode1 = Asc(Mid(hexa, i, 1)) - 48
    If hexcode1>48
      hexcode1-39
    Else
      If hexcode1>16
        hexcode1-7
      EndIf
    EndIf
    hexcode2 = Asc(Mid(hexa, i+1, 1)) - 48
    If hexcode2>48
      hexcode2-39
    Else
      If hexcode2>16
        hexcode2-7
      EndIf
    EndIf
    If flag16 = 16
      hexcode = (hexcode1 > 3
      hexcode 0
    XpmFile = ProgramParameter()
    If Len(XpmFile)>0
      extension.s = Right(XpmFile, 4)
      If extension = ".xpm" Or extension = ".XPM"
        Gosub ProcessFile
        Gosub ShowImage
      EndIf
    Else
      Files = 0
    EndIf
  Wend

  Repeat

    EventID.l = WaitWindowEvent()

    Select EventID
      Case #PB_EventMenu
        Select EventMenuID()
          Case 0 ; Open
            XpmFile = OpenFileRequester("Open file","","Xpm files (*.xpm)|*.xpm", 0)
            If Len(XpmFile)>0
              extension.s = Right(XpmFile, 4)
              If extension = ".xpm" Or extension = ".XPM"
                Gosub ProcessFile
                Gosub ShowImage
              EndIf
            EndIf
          Case 1 ; Quit
            Quit = 1
          Case 2 ; About
            If OpenWindow(1, WindowX()+64, WindowY()+32, 320, 96, #PB_Window_SystemMenu, "About XpmTool Lite")
              If CreateGadgetList(WindowID())
                TextGadget(0, 5, 5, WindowWidth()-10, WindowHeight()-10, "XpmTool Lite v. 0.9"+Chr(10)+"View and convert between Xpm and Bmp files."+Chr(10)+"Written in PureBasic by El_Choni")
              EndIf
            EndIf
        EndSelect
      Case #PB_EventCloseWindow
        If EventWindowID() = 0
          Quit = 1
        Else
          CloseWindow(1)
        EndIf
      Case #PB_EventRepaint
        If loaded
          UseWindow(0)
          StartDrawing(WindowOutput())
          DrawImage(UseImage(0), 4, 4)
          StopDrawing()
        EndIf
    EndSelect
  Until Quit = 1
EndIf

End

ProcessFile:

If ReadFile(0, XpmFile)
  FileSize = FileSize(XpmFile)
  Buffer = AllocateMemory(0, FileSize, 0)
  ReadData(Buffer, FileSize)
  CloseFile(0)
  Seeker = Buffer
  While PeekB(Seeker)34
    Seeker+1
  Wend
  Seeker+1
  ; Header data
  XpmHeader = Seeker
  stop = 0
  While stop = 0
    pkb = PeekB(Seeker)
    If pkb=32 Or pkb=9
      Seeker+1
    Else
      stop = 1
    EndIf
  Wend
  For n = 0 To 3
    Anchor = Seeker
    stop = 0
    While stop = 0
      pkb = PeekB(Seeker)
      If pkb32 And pkb9 And pkb34
        Seeker+1
      Else
        stop = 1
      EndIf
    Wend
    PokeB(Seeker, 0)
    PokeL(@XpmData+(n*4), Val(PeekS(Anchor)))
    PokeB(Seeker, pkb)
    stop = 0
    If n32 And pkb9
          stop = 1
        Else
          Seeker+1
        EndIf
      Wend
    EndIf
  Next n
  If loaded
    FreeImage(0)
  EndIf
  Seeker+1
  While PeekB(Seeker)  34
    Seeker+1
  Wend
  While PeekB(Seeker)  34
    Seeker+1
  Wend
  Seeker+1
  XpmPalette = Seeker
  Codes = AllocateMemory(1, XpmData\colors*XpmData\chars, 0)
  Palette = AllocateMemory(2, XpmData\colors*4, 0)
  If XpmData\colors>2)>2)>2)32 And pkb9 And pkb34
          Seeker+1
        Else
          PokeB(Seeker, 0)
          ch = PeekS(Anchor)
          PokeB(Seeker, pkb)
          stop = 1
        EndIf
      Wend
      PokeL(Palette+((colors-1)*4), Hex2Num(ch, XpmData\bits))
    EndIf
    For n = 0 To 1
      While PeekB(Seeker)34
        Seeker+1
      Wend
      Seeker+1
    Next n
  Next colors
  ; Image data
  XpmImageData = Seeker
  md = XpmData\bits/8
  Raw = AllocateMemory(3, XpmData\width*XpmData\height*md+(XpmData\height*dpad), 0)
  For rows = 1 To XpmData\height
    For cols = 1 To XpmData\width
      ch = ""
      For n = 1 To XpmData\chars
        ch = ch + Chr(PeekB(Seeker+n-1))
      Next n
      For t = 1 To XpmData\colors
        chcomp = ""
        For j = 1 To XpmData\chars
          chcomp = chcomp + Chr(PeekB(Codes+((t-1)*XpmData\chars)+(j-1)))
        Next j
        If chcomp = ch
          position = ((XpmData\height-rows)*XpmData\width)+(cols-1)
          Select XpmData\bits
            Case 1
              pad = Round((position+((XpmData\height-rows)*dpad*8))/8, 0)
              If (t-1)>0
                longbyte = $80
                For n = 1 To Mod(cols+7, 8)
                  longbyte = longbyte >> 1
                Next n
                longbyte | PeekB(Raw+pad)
                PokeB(Raw+pad, longbyte)
              EndIf
            Case 4
              pad = Round((position/2)+((XpmData\height-rows)*dpad), 0)
              If Mod(cols, 2)
                PokeB(Raw+pad, (t-1)*16)
              Else
                PokeB(Raw+pad, PeekB(Raw+pad)|(t-1));
              EndIf
            Case 8
              PokeB(Raw+position+((XpmData\height-rows)*pad), t-1)
            Case 16
              PokeW(Raw+(position*2)+((XpmData\height-rows)*pad), PeekW(Palette+((t-1)*4)))
            Case 24
              PokeL(Raw+(position*3)+((XpmData\height-rows)*pad), PeekL(Palette+((t-1)*4)))
            Case 32
              PokeL(Raw+(position*4), PeekL(Palette+((t-1)*4)))
          EndSelect
          t = XpmData\colors
        EndIf
      Next t
      Seeker+XpmData\chars
    Next cols
    If PeekB(Seeker) = 34
      Seeker+1
      If rows34
          Seeker+1
        Wend
        Seeker+1
      EndIf
    EndIf
  Next rows
  FreeMemory(0)
  FreeMemory(1)
  ; Bmp creating
  BmpFile\bfType = 19778
  If XpmData\bits>8
    RGBQuad = 0
  Else
    RGBQuad = (XpmData\colors*4)
  EndIf
  BmpFile\bfOffBits = SizeOf(BITMAPFILEHEADER)+SizeOf(BITMAPINFOHEADER)+RGBQuad+2
  BmpFile\bfSize = BmpFile\bfOffBits+(XpmData\width*XpmData\height*md+(XpmData\height*dpad))
  BmpFile\bfReserved1 = 0
  BmpFile\bfReserved2 = 0
  BmpInfo\biSize = SizeOf(BITMAPINFOHEADER)
  BmpInfo\biWidth = XpmData\width
  BmpInfo\biHeight = XpmData\height
  BmpInfo\biPlanes = 1
  BmpInfo\biBitCount = XpmData\bits
  BmpInfo\biCompression = #BI_RGB; Currently supports only uncompressed images
  If BmpInfo\biBitCount 	c #9EFEA2",
", 	c #FEC6BE",
"' 	c #B2E6BA",
") 	c #CEE6D6",
"! 	c #FE7662",
"~ 	c #223A4E",
"{ 	c #DEDEDE",
"] 	c #E2E2E2",
"^ 	c #E2E6E6",
"/ 	c #0ACA1A",
"( 	c #4E6AB2",
"_ 	c #264A5A",
": 	c #DAFEDA",
".	c #FEAEA6",
",.	c #465A9E",
"'.	c #8AFE8A",
").	c #FEFEFE",
"!.	c #3EFE42",
"~.	c #E2FEE2",
"{.	c #AAFEAE",
"].	c #566EBE",
"^.	c #FEA296",
"/.	c #121A2A",
"(.	c #3A4A82",
"_.	c #2E3A66",
":.	c #364676",
"+	c #FE5A46",
",+	c #FE9286",
"'+	c #0ACE22",
")+	c #C6FECA",
"!+	c #0ED226",
"                                (.E.L V B.K.d.i |.|.|.|.|.|.|.|.",
"                                u (.E.v.V ( K.q.i I |.|.|.|.|.|.",
"                                q :.t.E.,. | ;.I.H ).).",
").).).`.5 ).).W h ).t `.J j a g #+& ).).).).).).$+).).).0.: ).).",
").).).U o.).).b.=.,+=.a r o.[.& ).5.6 ).).).).).9 S )+F.I.H ).).",
").).).).n.).).).f c f ).).`.h n.).z U ).).).).).` -+* G.@+).).).",
").).).).).).).).).).).).).).).).).).).).).).).).e n n > n ).).).",
").).).).).).).).).).).).).).).).).).).).).).).).e > n U.B ).).).",
").).).).).).).).).).).).).).).).).).).).).).).).).).).).* ~.).).",
").).).).).).).).).).).).).).).).).).).).).).).).e > n >  > > R ).).).",
").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).",
").).).).).).).).).).).).).).).).).).).).).).).).).).).).).).).)."};
Have a nice day,

El_Choni

Edited by - EL_Choni on 12 March 2002 01:47:44

Edited by - EL_Choni on 12 March 2002 01:51:34
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Franco.

Amazing work El_Choni!

Tested your code (with 30+ little xpm images) and it worked fine...
...except for one xpm file.

IrfanView is able to open and display it correctly (your code crashes) so I
suppose it would be the best if I show you the xpm file:

/* XPM */
static char *mondrian_xpm[] = {
/* columns rows colors chars-per-pixel */
"32 32 6 1",
" c Black",
". c Blue",
"X c #00bf00",
"o c Red",
"O c Yellow",
"+ c Gray100",
/* pixels */
" ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" oooooo +++++++++++++++++++++++ ",
" ",
" ++++++ ++++++++++++++++++ .... ",
" ++++++ ++++++++++++++++++ .... ",
" ++++++ ++++++++++++++++++ .... ",
" ++++++ ++++++++++++++++++ .... ",
" ++++++ ++++++++++++++++++ .... ",
" ++++++ ++++++++++++++++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++++++++++++++++ ++++ ",
" ++++++ ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
" "
};



Hope you can find the discrepancy.


Have a nice day...
Franco

Sometimes you have to go a lonely way to accomplish genius things.
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by El_Choni.

Hi Franco,

Nice to hear from you again... Anyway, I'll make it brief: the XPM file you attached won't work because, as stated just before the code I posted, the app doesn't support named colors, that is "black", "yellow", etc. It wouldn't be a great issue to add that support, but I will only add it if I have a document stating which are standard names for colours used in an XPM file. No prob for "black", "yellow", but what about "tan", "lemon schifron", etc.?

Can you send me an example of those? A color table with names equivalents? If so, I will add named colors support inmediatly!

Bye,

El_Choni

PS: and, of course, what do these empty pixels mean? All lines must have the width stated at the header. If Xpm specification says other thing, tell me and I will try to support that.

Edited by - El_Choni on 02 March 2002 06:26:19
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Franco.
... but I will only add it if I have a document stating which are standard names for colours used in an XPM file.
I can understand that
Can you send me an example of those color table with names equivalents?
If so, I will add named colors support inmediatly!

PS: and, of course, what do these empty pixels mean? All lines must have the width stated at the header. If Xpm specification says other thing, tell me and I will try to support that.
Sorry, nothing handy have to check the net for that...
but not now, I gotta go to bed (10:35pm) because I can sleep...


Have a nice day...
Franco

Sometimes you have to go a lonely way to accomplish genius things.
Post Reply