Welcome, guest | Sign In | My Account | Store | Cart

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.

Python, 170 lines
  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()
Created by Baris Yuksel on Tue, 31 Aug 2010 (GPL3)
Python recipes (4591)
Baris Yuksel's recipes (1)

Required Modules

Other Information and Tasks