• Dear Cerberus X User!

    As we prepare to transition the forum ownership from Mike to Phil (TripleHead GmbH), we need your explicit consent to transfer your user data in accordance with our amended Terms and Rules in order to be compliant with data protection laws.

    Important: If you accept the amended Terms and Rules, you agree to the transfer of your user data to the future forum owner!

    Please read the new Terms and Rules below, check the box to agree, and click "Accept" to continue enjoying your Cerberus X Forum experience. The deadline for consent is April 5, 2024.

    Do not accept the amended Terms and Rules if you do not wish your personal data to be transferred to the future forum owner!

    Accepting ensures:

    - Continued access to your account with a short break for the actual transfer.

    - Retention of your data under the same terms.

    Without consent:

    - You don't have further access to your forum user account.

    - Your account and personal data will be deleted after April 5, 2024.

    - Public posts remain, but usernames indicating real identity will be anonymized. If you disagree with a fictitious name you have the option to contact us so we can find a name that is acceptable to you.

    We hope to keep you in our community and see you on the forum soon!

    All the best

    Your Cerberus X Team

Loading BMP files

Pierrou

Well-known member
Joined
Jul 6, 2017
Messages
237
Hello and Merry Christmas
Is there a way to load bmp files in Mojo1 CX? LoadImage only loads PNG and JPG files!
 
Merry christmas to you too. Not that I know of. Is it mandatory that you can load it?
 
In fact in my current Mojo1 project the user can make screenshots using Frederick Raynal's BMP class, it saves the pics to BMP files. I'd like to be able to reuse those BMPs to make some kind of gallery. In fact the gallery is ready and while trying to run it I discovered CerberusX couln't read them.
The BMP class goes like this :
Cerberus:
Class    BmpFile

    Field    Width:Int              ' bmp image width
    Field    Height:Int            ' bmp image height
   
    Field    Pixels:Int[1]        ' array for screen pixels

    Field    FileSize:Int        ' bmp file size
    Field    Buffer:DataBuffer    ' bmp file data (header + pixels)

    Field    PadLineWidth:Int    ' bmp real line size
   
    ' default header for 24bpp image
    Field    Header:Int[] = [ $42,$4D,$D6,$83,$00,$00,$00,$00,$00,$00,$36,$00,$00,$00,$28,$00,$00,$00,$6C,$00,$00,$00,$68,$00,$00,
                            $00,$01,$00,$18,$00,$00,$00,$00,$00,$00,$00,$00,$00,$C3,$0E,$00,$00,$C3,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ]  
    Const    FILE_SIZE        :=    $2  
    Const    IMAGE_WIDTH        :=    $12
    Const    IMAGE_HEIGHT    :=    $16
    Const    RAW_DATA_SIZE    :=    $22

    Method Grab:Void( x:Int, y:Int, w:Int, h:Int )    ' Call within OnRender after drawing the image to grab
   
        Width = w
        Height = h
        PadLineWidth = (((Width*3) + 3) / 4) * 4
   
        Pixels = Pixels.Resize( Width * Height )      
        ReadPixels Pixels, x, y, Width, Height
           
        FileSize = Header.Length() + PadLineWidth * Height
        Buffer = New DataBuffer( FileSize )
       
        local ptr:Int
        for ptr = 0 until Header.Length
            Buffer.PokeByte ptr, Header[ptr]
        next
       
        Buffer.PokeInt FILE_SIZE, FileSize
        Buffer.PokeInt IMAGE_WIDTH, Width
        Buffer.PokeInt IMAGE_HEIGHT, Height
        Buffer.PokeInt RAW_DATA_SIZE, PadLineWidth * Height

        local i:Int = 0
        local pix:Int
        for local ys:Int = 0 until Height
            ptr = (Height - ys - 1) * PadLineWidth + Header.Length
            for local xs:Int = 0 until Width
                pix = Pixels[i]
                Buffer.PokeByte ptr+0, pix & $ff
                Buffer.PokeByte ptr+1, (pix shr 8) & $ff
                Buffer.PokeByte ptr+2, (pix shr 16) & $ff
                ptr += 3
                i += 1
            next
        next
       
    End
     
    Method    Save:Void( filename:String )    ' filename without .bmp

        Local file:=FileStream.Open( "monkey://internal/"+filename+".bmp","w" )
        If file
            file.Write Buffer, 0, FileSize
            file.Close
        Endif
       
    End
   
End

No Load Method and I don't think I have the skills to write one but I guess it's doable? Something using ReadPixels instead of WritePixels, one would have to figure out first how to get data from the BMP file to an array, it's definitely something a little bit too big for me but I will try since it's very frustrating to have to give up so close to the goal.

Thanks for your answer Mike!
 
Last edited:
OK I can store and get the data from the file using PeekInt and so on, should be able to reverse Fred's code to write it to an image...
 
OK this comes from Fred's Grab method shown above.

He stores the RGB pixel data in the Pixels:Int[] array
Cerberus:
Pixels = Pixels.Resize( Width * Height )
        ReadPixels Pixels, x, y, Width, Height
and then stores each of the Pixel array's data into the Buffer,

line Buffer.PokeByte ptr+0, pix & $ff coding for Blue,
Buffer.PokeByte ptr + 1, (pix shr 8) & $ff for Green
and Buffer.PokeByte ptr+2, (pix shr 16) & $ff for Red

Cerberus:
for local ys:Int = 0 until Height
            ptr = (Height - ys - 1) * PadLineWidth + Header.Length
            for local xs:Int = 0 until Width
                pix = Pixels[i]
                Buffer.PokeByte ptr+0, pix & $ff
                Buffer.PokeByte ptr + 1, (pix shr 8) & $ff
                Buffer.PokeByte ptr+2, (pix shr 16) & $ff
                ptr += 3
                i += 1
            next
        next

He then saves the databuffer data into the BMP file, beginning at the bottom left I think (which is how one writes BMP files apparently)
Cerberus:
If file
            file.Write Buffer, 0, FileSize
            file.Close
        EndIf

When reading the Pixels Array data I get numbers looking like -8547088 Or -7560193, not being sure what they really mean

When the values of pix, pix shr8 and pix shr16 are pix = -827044 pix shr8 = -3231 and pix shr16 = -13 for example
I can see that
pix & $ff = 92
(pix shr 8) & $ff = 97
(pix shr 16) & $ff = 243
(the pixel being read's RGB values being 140,163,255 indeed)

So my question is how does that & ff suffix work and what does it mean exactly?


Then, in the Load method I'm trying to write I'm able to get data from the file and store it back to a DataBuffer, with the BMP File Header data and then the RGB data

Some code sample:
Cerberus:
For Local ptr:Int = Header.Length To Header.Length + 1000
                        Print Buffer.PeekByte(ptr)
                    Next
If I get the data from the example above, The Print command will print
255
163
140
which are the right RGB values for the first pixel at the bottom.

So, so far so good I can read the file length, the image width and height from the file header, and then every pixel's RGB data
but what I can't do for now is storing back every RGB info into an image using CreateImage and then image.WritePixels(...)
What the doc says about the WritePixels Method:
Method WritePixels : Void ( pixels:Int[], x:Int, y:Int, width:Int, height:Int, arrayOffset:Int=0, arrayPitch:Int=0, frame:Int=0 )

Copies a rectangular section of pixels from an int array to the image.

The pixel data must be stored in int-per-pixel ARGB format, with the alpha component stored in bits 24-31, the red component in bits 16-23, the green component in bits 8-15 and the blue component in bits 0-7.

So my question do you know how to store my RGB data back into some Pixels array?
Thanks!!

EDIT : working on it, very badly and slowly, getting an image displayed at least but wrong colours and other problems... I'm about to figure out how it works..
 
Last edited:
Here is a LoadBmp function to be used alongside with Fred's BmpFile Class above.

Cerberus:
Function LoadBmp:Image(path:String)
   
    Local file:= FileStream.Open(path, "r")
    Local bmpimage:BmpFile
    Local image:Image
    bmpimage = New BmpFile
    bmpimage.Buffer = New DataBuffer(file.Length)
    If file
        file.ReadAll(bmpimage.Buffer, 0, file.Length)
        file.Close
        bmpimage.Width = bmpimage.Buffer.PeekInt($12)
        bmpimage.Height = bmpimage.Buffer.PeekInt($16)
                   
        bmpimage.Pixels = bmpimage.Pixels.Resize(bmpimage.Width * bmpimage.Height)
       
        Local ptr:Int = 0
        Local scanpixelsarray:Int = (bmpimage.Height - 1) * bmpimage.Width 'starting to write into the Pixels array where the data for the last line of the image begins
        For Local i:Int = bmpimage.Height - 1 To 0 Step - 1 ' pixel info in the BMP file is coded upside down for some reason
            For Local j:Int = 0 To bmpimage.Width - 1
                Local b:Int = bmpimage.Buffer.PeekInt(ptr + 0) ' blue component
                Local g:Int = bmpimage.Buffer.PeekInt(ptr + 1) ' green component
                Local r:Int = bmpimage.Buffer.PeekInt(ptr + 2) ' red component
                bmpimage.Pixels[scanpixelsarray + j] = (255 Shl 24) | (r Shl 16) | (g Shl 8) | b ' Data in the Pixels array must be stored in int-per-pixel ARGB format, with the alpha component stored in bits 24-31, the red component in bits 16-23, the green component in bits 8-15 and the blue component in bits 0-7
                ptr += 3
                If j = bmpimage.Width - 1 Then scanpixelsarray = (i - 1) * bmpimage.Width
            Next
        Next
       
       
        image = CreateImage(bmpimage.Width, bmpimage.Height)
        image.WritePixels(bmpimage.Pixels, 0, 0, bmpimage.Width, bmpimage.Height, 0, 0)
       
    EndIf
    Return image
End Function

Is it working? Nope.
bmpbug.png

The bmp images are supposed to be displayed in the frames. The far left one and the right one look OK. The two in the middle look rather weird... It's really too tricky for me :(
BlitzMax used to load BMP files out of the box (never had a use for that but it was cool anyway), weird that Monkey/CX doesn't.
 

Attachments

  • bmpbug.png
    bmpbug.png
    161.7 KB · Views: 125
Last edited:
Try this one:
Cerberus:
Function LoadBMP:Image(path:String)
    Local db:DataBuffer = DataBuffer.Load(path)
    If db
        'Print("length="+db.Length())
        'Print("type="+db.PeekString(0,2))
        'Print("size="+db.PeekInt(2))
        Local  offset:Int=db.PeekInt(10)
        'Print("offet="+offset)
        'Print("header size="+db.PeekInt(14))
        Local width:Int=db.PeekInt(18)
        'Print("image width="+width)
        Local height:Int=db.PeekInt(22)
        Local img:Image = CreateImage( width, height, 1 )
        'Print("image height="+height)
        'Print("image planes="+db.PeekShort(26))
        Local bitcount:Int= +db.PeekShort(28)
        'Print("image bitcount="+bitcount)
        'Print("image compression="+db.PeekInt(30))
        Local imagesize:Int=db.PeekInt(34)
        'Print("image Size="+imagesize)
        Local pixels:Int[width*height]
        For Local y:Int= 0 To (height-1)
            For Local x:Int=0 To (width-1)
                Local index:Int=y*width+x
                Local i:Int = offset+index*(bitcount/8)
                Local alpha:Int = 255
                Local red:Int = db.PeekByte(i)+256
                Local green:Int = db.PeekByte(i+1)+256
                Local blue:Int = db.PeekByte(i+2)+256
                Local rgba:Int = alpha Shl 24 | blue Shl 16 | green Shl 8 | red
                pixels[width*height-index-1]=rgba
            Next
        Next
        If img
            img.WritePixels(pixels,0,0,width,height)
            Return img
        Endif
    Else
        Print ("Error loading file "+path)
    Endif
    Return Null
End
 
Thanks Mike!!
Same result unfortunately except that the images are horizontally reverted :)
I've only tried with BMPs made within my game using Fred's Class I'll try with BMPs made with GIMP.
 
Using 24bit BMPs made with Paint lead to the same result
1577313751515.png

was supposed to look more or less like
1577313789191.png
 
Cerberus:
Strict

'Simple mojo(1) script
'It should behave the same on all targets
#IMAGE_FILES+="*.bmp"
Import mojo
Import brl.databuffer

'-----------------------------------------------------------------
Class myClass Extends App
    Field angle:Float = 0.0
    Field img:Image
    '-----------------------------------------------------------------
    Method OnCreate:Int()
        SetUpdateRate 60
        img = LoadBMP("cerberus://data/gold2.bmp")
        
        Return 0
    End
    '-----------------------------------------------------------------
    Method OnUpdate:Int()
        ' update your content here
        angle += 0.5
        If angle > 360.0 Then angle -= 360.0
        Return 0
    End
    '-----------------------------------------------------------------
    Method OnRender:Int()
        ' here you render your app
        Cls 0,0,255
        DrawImage(img,10,10)
        Return 0
    End
End
Function LoadBMP:Image(path:String)
    Local db:DataBuffer = DataBuffer.Load(path)
    If db
        'Print("length="+db.Length())
        'Print("type="+db.PeekString(0,2))
        'Print("size="+db.PeekInt(2))
        Local  offset:Int=db.PeekInt(10)
        'Print("offet="+offset)
        'Print("header size="+db.PeekInt(14))
        Local width:Int=db.PeekInt(18)
        'Print("image width="+width)
        Local height:Int=db.PeekInt(22)
        Local img:Image = CreateImage( width, height, 1 )
        'Print("image height="+height)
        'Print("image planes="+db.PeekShort(26))
        Local bitcount:Int= +db.PeekShort(28)
        'Print("image bitcount="+bitcount)
        'Print("image compression="+db.PeekInt(30))
        Local imagesize:Int=db.PeekInt(34)
        'Print("image Size="+imagesize)
        Local pixels:Int[width*height]
        For Local y:Int= 0 To (height-1)
            For Local x:Int=0 To (width-1)
                Local index:Int=y*width+x
                Local i:Int = offset+index*(bitcount/8)
                Local alpha:Int = 255
                Local red:Int = db.PeekByte(i)+256
                Local green:Int = db.PeekByte(i+1)+256
                Local blue:Int = db.PeekByte(i+2)+256
                Local rgba:Int = alpha Shl 24 | blue Shl 16 | green Shl 8 | red
                pixels[width*height-index-1]=rgba
            Next
        Next
        If img
            img.WritePixels(pixels,0,0,width,height)
            Return img
        Endif
    Else
        Print ("Error loading file "+path)
    Endif
    Return Null
End
'-----------------------------------------------------------------
Function Main:Int()
    New myClass
    Return 0
End
 
I have attached my test project. The gold2 file was saved in Paint.
 

Attachments

  • bmpfileread.zip
    73.7 KB · Views: 134
Well, I downloaded a few bmp pics from Google Images : some work and some don't, whether I use your Function or mine (same result except for the flipping)
 
It might have something to do with row lengths being or not being multiples of 4?
Padding bytes (not necessarily 0) must be appended to the end of the rows in order to bring up the length of the rows to a multiple of four bytes. When the pixel array is loaded into memory, each row must begin at a memory address that is a multiple of 4. This address/offset restriction is mandatory only for Pixel Arrays loaded in memory. For file storage purposes, only the size of each row must be a multiple of 4 bytes while the file offset can be arbitrary.[4] A 24-bit bitmap with Width=1, would have 3 bytes of data per row (blue, green, red) and 1 byte of padding, while Width=2 would have 2 bytes of padding, Width=3 would have 3 bytes of padding, and Width=4 would not have any padding at all.

When resizing the pictures they look OK again...
[EDIT : definitely a matter of multiples of 4, when resizing the bull pic to 492x436 it looks good again. Let's try to manage padding]
[EDIT 2 : not quite sure how to do it :\ but it's getting close]
 
Last edited:
This piece of code seems to work :

Cerberus:
Function LoadBmp:Image(path:String)
    Local Width:Int              ' bmp image width
    Local Height:Int            ' bmp image height
    Local Pixels:Int[1]        ' array for screen pixels
    Local Buffer:DataBuffer    ' bmp file data (header + pixels)
    Local file:= FileStream.Open(path, "r")
    Local image:Image
    Buffer = New DataBuffer(file.Length)
    If file
        file.ReadAll(Buffer, 0, file.Length)
        file.Close
        Width = Buffer.PeekInt($12)
       
        Height = Buffer.PeekInt($16)
        Pixels = Pixels.Resize(Width * Height)
        Local PadLineWidth:Int = ( ( (Width * 3) + 3) / 4) * 4
        Local OriginalLineWidth:Int = Width * 3
        Local Padding:Int = PadLineWidth - OriginalLineWidth
        Local ptr:Int = 0
        Local scanpixelsarray:Int = (Height - 1) * Width 'starting to write into the Pixels array where the data for the last line of the image begins
        For Local i:Int = Height - 1 To 0 Step - 1 ' pixel info in the BMP file is coded upside down for some reason
            For Local j:Int = 0 To Width - 1
                Local b:Int = Buffer.PeekInt(ptr + 0) ' blue component
                Local g:Int = Buffer.PeekInt(ptr + 1) ' green component
                Local r:Int = Buffer.PeekInt(ptr + 2) ' red component
               
                       Pixels[scanpixelsarray + j] = (255 Shl 24) | (r Shl 16) | (g Shl 8) | b ' Data in the Pixels array must be stored in int-per-pixel ARGB format, with the alpha component stored in bits 24-31, the red component in bits 16-23, the green component in bits 8-15 and the blue component in bits 0-7
                ptr += 3
                If j = Width - 1
                scanpixelsarray = (i - 1) * Width
                If Padding<> 0 Then ptr += Padding
                EndIf
            Next
        Next
       
       
        image = CreateImage(Width, Height)
        image.WritePixels(Pixels, 0, 0, Width, Height, 0, 0)
       
    EndIf
    Return image
End Function
 
Last edited:
No one seemed to be missing it so far but adding a few lines and make it possible won't hurt.
Thanks again for your help, glad it works, now I can finish my game!
 
Your function has a little problem (the picture is off by several pixels to the right):

1577437737310.png


And because the file is loaded with FileStream, you can't use it in HTML5.
Better load it into a databuffer directly?
 
Hello
Yes I noticed the offset problem afterwards too. About FileStream, it was already needed in my game but yes I saw that you loaded the data directly into the databuffer, I just took some code here and there, mainly from Fred's BMP class and tried to make it work backwards without exactly knowing what was going on :oops::oops::oops:
"My" function works okayish for what I need but the one you wrote is probably a better and safer start. Don't have time to think about it for now, no emergency I unfortunately have to focus on my day(and sometimes night-)job for a few days...
 
Last edited:
Back
Top Bottom