#!/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()