SSJX.CO.UK
Content

Bitmap to RLE compressed array using Python

Some of the web games on this site (like Chuckie Egg Returns) have their game maps embedded in the source. To make the JavaScript game file smaller and therefore quicker to download, they are run through a program like the one below. As the game maps are simple maps (only around 10 elements), the maps can be reduced in size by around 50% using Run Length Encoding (RLE).

The process I use is:

Python Source

#
# Bmp2Rle by ssjx (https://ssjx.co.uk)
#
import sys

fn=""
if len(sys.argv)==2:
    fn=sys.argv[1]
else:
    print("Bmp2Rle Py by ssjx (https://ssjx.co.uk)")
    print("---------------------------------------")
    print("Usage: py bmp2rle.py myfile.bmp")
    exit(0)
    
try:
    myfile=open(fn,"rb")
except:
    print("Could not open the file...")
    exit()    

bmp_header=myfile.read(14)
                                         
if (chr(bmp_header[0])!='B' and chr(bmp_header[1])!='M'):
    print("This file does not look like a bitmap image?")
    exit()
            
bmp_info=myfile.read(40)
width=int.from_bytes(bmp_info[4:8],'little')
height=int.from_bytes(bmp_info[8:12],'little')
depth=bmp_info[14]
 
print(fn+": "+str(width)+" x "+str(height)+" x "+str(depth)) 
    
if (width % 4)!=0:
    print("The width should be divisible by 4.")
    exit()

if depth!=8:
    print("Images should be 8bit colour.")
    exit()                
                
# Got this far so probably a valid bitmap!
offset=int.from_bytes(bmp_header[10:14],'little')
print("Data Offset: ",offset)
    
myfile.seek(offset,0)    

data=myfile.read(width*height)    
myfile.close()

for j in range(0,height):
    no=1                    # byte count
    outstr=""               # clear output text
    pos=width*(height-j-1)  # flipped position in bitmap
    pb=data[pos]            # previous byte (first byte of line)

    for i in range(1,width):
        cb=data[pos+i]      # current byte
        
        if (cb==pb) and (no<200):
            no+=1
        else:
            outstr+=str(no)+","+str(pb)+","
            no=1
        pb=cb
    
    
    if (no>0):
        outstr+=str(no)+","+str(pb)+","
    
    print(outstr)

Running the program

Save the above as something, bit2rle.py for example, then run it as usual specifying a bitmap file. The bit in blue is the part that would go into the JavaScript array in your game (remove the final comma).

#skip>py bmp2rle.py 1.bmp
1.bmp: 40 x 20 x 8
Data Offset:  1078
40,1,
1,1,38,0,1,1,
1,1,1,0,1,6,36,0,1,1,
1,1,9,0,1,7,1,0,1,2,1,0,1,4,2,0,1,3,3,0,1,3,1,0,1,7,3,0,1,3,2,0,1,3,6,0,1,7,1,0,1,1,
1,1,9,0,4,1,1,4,3,1,2,0,14,1,2,0,4,1,
1,1,13,0,1,4,6,0,1,7,17,0,1,1,
1,1,7,0,1,7,1,4,1,0,1,2,1,3,1,0,1,4,3,0,4,1,17,0,1,1,
1,1,7,0,1,1,1,4,4,1,1,4,24,0,1,1,
1,1,8,0,1,4,4,0,1,4,15,0,3,1,6,0,1,1,
1,1,8,0,1,4,4,0,1,4,12,0,3,1,9,0,1,1,
1,1,5,0,1,7,2,0,1,4,1,0,1,2,1,3,1,0,1,4,6,0,1,3,1,7,1,0,3,1,12,0,1,1,
1,1,4,0,9,1,1,4,3,1,2,0,4,1,11,0,1,7,3,0,1,1,
1,1,13,0,1,4,16,0,6,1,2,0,1,1,
1,1,13,0,1,4,24,0,1,1,
1,1,2,0,1,7,1,0,1,3,1,0,1,4,1,2,1,0,1,3,3,0,1,4,2,0,1,3,3,0,1,4,3,0,1,2,1,0,1,3,1,7,4,0,1,4,2,0,1,7,2,0,1,1,
1,1,2,0,4,1,1,4,6,1,1,4,6,1,1,4,11,1,1,4,3,1,2,0,1,1,
1,1,6,0,1,4,6,0,1,4,6,0,1,4,11,0,1,4,5,0,1,1,
1,1,6,0,1,4,6,0,1,4,6,0,1,4,11,0,1,4,5,0,1,1,
1,1,4,0,1,3,1,0,1,4,1,2,1,0,1,3,3,0,1,4,1,7,1,0,1,3,1,5,2,0,1,4,5,0,1,3,2,0,1,2,2,0,1,4,5,0,1,1,
40,1,

Decompressing using JavaScript

Not going to go into to much depth, but to decompress, the first number is the number of times to repeat the second number. If you stored the above data in an array called tmp, you would decompress to an array called map using something like the code below:

for(let i=0;i<tmp.length;i+=2){
	for(let x=0;x<tmp[i];x++){
		map[pos++]=tmp[i+1];
	}
}#skip

Created 19/07/2025