Hi everyone, This script can decode 4-Bit ( 16-Color ) and 8-Bit ( 256-Color ) Windows BMP files encoded with BI_RLE compression algorithm. However, I haven't got acceptable compressed BMP files ( row,column,zigzag etc. ), i could not try move offset marker data of BI_RLE.
I'm waiting your comments for better solutions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | #!/usr/bin/python
#-*- coding:utf-8 -*-
#brsyuksel.com
from tempfile import TemporaryFile
class BI_RLE(object):
def __init__(self,i,o):
self.i = open(i)
self.o = open(o,"w")
self.tmp = TemporaryFile()
if self.i.read(2) != 'BM':
raise IOError, "Not BMP file"
self.i.seek(10)
of = self.i.read(4)#offset to start image data
self.offset = sum([ord(of[i])<<8*i for i in range(len(of))])
self.i.seek(18)
w = self.i.read(4)#image width
self.w = sum([ord(w[i])<<8*i for i in range(len(w))])
h = self.i.read(4)#image height
self.h = sum([ord(h[i])<<8*i for i in range(len(h))])
self.i.seek(28)
b = self.i.read(2)#channel:bit per pixel
self.bpp = sum([ord(b[i])<<8*i for i in range(len(b))])
if self.bpp != 4 and self.bpp != 8:
raise IOError, "Not 4-Bit or 8-Bit BMP file"
c = self.i.read(4)#compression type
self.comp = sum([ord(c[i])<<8*i for i in range(len(c))])
if self.comp != 2 and self.comp != 1:
raise IOError, "Not Compressed file"
self.tPix = self.w * self.h
self.rPix = 0
self.lns = 1
self.c = 0
self.EORLED = False#fix for EORLE
self.i.seek(self.offset)
self.enc = self.i.read()
self.dec = ""
self.buf = ""
def Decode(self):
mrk = {0:self.EOSL,1:self.EORLE,2:self.MOFF}#funcs for RLE Data markers
while((self.lns*self.w)<=self.tPix):
b = self.enc[self.c:self.c+2]
self.c += 2
if len(b) != 2: break
b0, b1 = ord(b[0]), ord(b[1])
if b0 == 0:
mrk.get(b1,self.UENCD)(b0,b1)
else:
self.DENCD(b0,b1)
def HPIX(self,pixel):
""" Half-Byte Packing for 4-Bit and Pixel Data Handler """
if self.bpp == 4:
if self.buf == "":
self.buf = chr(pixel<<4)
else:
self.buf = chr(ord(self.buf) | pixel)
self.tmp.write(self.buf)
self.buf = ""
else:
self.tmp.write(chr(pixel))
def EOSL(self,*arg):
""" 00 00: End Of Scan Line """
remain = self.w - self.rPix
if not self.EORLED:
self.rPix = 0
self.lns += 1
if remain == 0: remain = 2#fix for EOSL
for i in range(remain):
self.HPIX(0x00)
def MOFF(self,*arg):
""" 00 02: Move Offset """
mov = self.enc[self.c:self.c+2]
self.c += 2
mov = ord(mov[0]) + ord(mov[1])*self.w
for i in range(mov):
self.HPIX(0x00)
self.rPix += mov
self.lns += self.rPix // mov
self.rPix %= mov
def UENCD(self,*arg):
""" 00 NN: Unencoded Data """
p = arg[1] #unencoded pixels data
if self.bpp == 4:
#read bytes with padding byte for 4 bit
b = int(round(p/2)) + (int(round(p/2))%2 | p%2)
else:
#read bytes with padding byte for 8 bit
b = p + p%2
ue = self.enc[self.c:self.c+b]
self.c += b
delta = self.rPix + p
for i in range(b):
if self.rPix == delta: break
if self.bpp == 4:
for j in range(2):
if self.rPix == delta: break
self.HPIX((ord(ue[i])&(0x0F<<(4*((j+1)%2))))>>(4*((j+1)%2)))
self.rPix += 1
else:
self.HPIX(ord(ue[i]))
self.rPix += 1
def DENCD(self,*arg):
""" NN PP: Decode Encoded Data """
b0, b1 = arg[0], arg[1] #piece, 2 pixels data
for i in range(b0):
if self.bpp == 4:
self.HPIX((b1&(0x0F<<(4*((i+1)%2))))>>(4*((i+1)%2)))
else:
self.HPIX(b1)
self.rPix += 1
def EORLE(self,*arg):
""" 00 01: End Of RLE Data, Writing Decoded File """
self.EORLED = True
self.EOSL()
if not self.buf == "": self.tmp.write(self.buf)
self.tmp.seek(0)
self.dec = self.tmp.read()
self.tmp.close()
self.i.seek(0)
self.o.write(self.i.read(2)) #'BM' signature
fs = self.offset + len(self.dec) #FileSize: (Header + Color Palette) + ImageData
fsize = "" #filesize string value
for i in range(4): fsize+=chr((fs & (0xFF<<8*i))>>8*i)#ordering as little-endian
self.o.write(fsize)
self.i.seek(6)
self.o.write(self.i.read(24))#writing 24-byte same data from 6 offset
self.o.write('\x00\x00\x00\x00')#compression-type: none
imgdsize = ""#image data size string value
for i in range(4): imgdsize+=chr((len(self.dec)&(0xFF<<8*i))>>8*i)
self.o.write(imgdsize)
self.i.seek(38)
self.o.write(self.i.read(self.offset-38))#writing left same data from 38
self.o.write(self.dec)
self.o.close()
self.i.close()
#BI_RLE(inputfile,outputfile)
#obj = BI_RLE("/home/brs/m/4bitc.bmp","/home/brs/m/4bitc-dec.bmp")
#obj.Decode()
|