Created
October 11, 2012 05:17
-
-
Save weisi/3870322 to your computer and use it in GitHub Desktop.
Black-and-white Windows Bitmap -> CompuServe RLE converter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# Weisi Dai <multiple1902@gmail.com> | |
# 09055029, Xi'an Jiaotong University | |
# Oct 11, 2012 | |
# Released under MIT License | |
# Reads a black-and-white Windows Bitmaap, parses it and converts it to CompuServe RLE format. | |
import struct | |
from types import * # assert | |
from collections import namedtuple | |
data_filename = "test-32-32.bmp" | |
output_filename = "test-32-32.rle" | |
data_bytearr = open(data_filename, "rb").read() | |
s_bmfh = ''.join(['<', # Windows Bitmap is declared little endian | |
'2s', # 0x0000 ... 0x0001: BM | |
'L', # 0x0002 ... 0x0005: File Size: dword | |
'2H', # 0x0006 ... 0x0009: Reserved: must be zero | |
'L', # 0x000A ... 0x000D: Bitmap data offset | |
]) | |
size_bmfh_0 = 14 | |
size_bmfh = struct.calcsize(s_bmfh) | |
s_bmih = ''.join(['<', # Little endian | |
'L', # 0x000E ... 0x0011: Bitmap info header size. Should be 0x28. | |
'L', # 0x0012 ... 0x0015: Bitmap width. | |
'L', # 0x0016 ... 0x0019: Bitmap height. | |
'H', # 0x001A ... 0x001B: Bitmap planes. Should be 1. | |
'H', # 0x001C ... 0x001D: Bits per pixel. Should be 1 for black-white. | |
'L', # 0x001E ... 0x0021: Compression method. 0 for BI_RGB. | |
'L', # 0x0022 ... 0x0025: Raw bitmap data size. Can be 0 for BI_RGB files. | |
'l', # 0x0026 ... 0x0029: Horizontal resolution. Signed. | |
'l', # 0x002A ... 0x002D: Vertical resolution. Signed. | |
'L', # 0x002E ... 0x0031: Colors in the palette. 0 for 2^n. | |
'L', # 0x0032 ... 0x0035: Important colors. Ignored. | |
]) | |
size_bmih_0 = 40 | |
size_bmih = struct.calcsize(s_bmih) | |
s_palette = ''.join(['<', # Little endian | |
'8B', # R1, G1, B1, NO1, R2, G2, B2, NO2 | |
]) | |
size_palette_0 = 8 | |
size_palette = struct.calcsize(s_palette) | |
assert size_bmfh is size_bmfh_0, "BitMapFileHeader: Size is {0}, should be {1}.".format(size_bmfh, size_bmfh_0) | |
assert size_bmih is size_bmih_0, "BitMapInfoHeader: Size is {0}, should be {1}.".format(size_bmih, size_bmih_0) | |
assert size_palette is size_palette_0, "BitMapPalette: Size is {0}, should be {1}.".format(size_palette, size_palette_0) | |
iReadPointer = 0 | |
data_bmfh = data_bytearr[iReadPointer : iReadPointer + size_bmfh] | |
iReadPointer += size_bmfh | |
data_bmih = data_bytearr[iReadPointer : iReadPointer + size_bmih] | |
iReadPointer += size_bmih | |
data_palette = data_bytearr[iReadPointer : iReadPointer + size_palette] | |
iReadPointer += size_palette | |
BitMapFileHeader_Tuple = namedtuple('BitMapFileHeader', ' '.join([ | |
'MagicNumber', | |
'FileSize', | |
'Reserved1', | |
'Reserved2', | |
'BitmapDataOffset', | |
]) | |
) | |
BitMapInfoHeader_Tuple = namedtuple('BitMapInfoHeader', ' '.join([ | |
'InfoHeaderSize', | |
'Width', | |
'Height', | |
'ColorPlanes', | |
'BitsPerPixel', | |
'CompressionMethod', | |
'ImageSize', | |
'HorizontalResolution', | |
'VerticalResolution', | |
'ColorsInPalette', | |
'ImportantColors', | |
]) | |
) | |
BitMapPalette_Tuple = namedtuple('BitMapPalette', ' '.join([ | |
'R1', | |
'G1', | |
'B1', | |
'NO1', | |
'R2', | |
'G2', | |
'B2', | |
'NO2', | |
]) | |
) | |
BitMapFileHeader = BitMapFileHeader_Tuple._make(struct.unpack(s_bmfh, data_bmfh)) | |
assert BitMapFileHeader.Reserved1 == 0, "BitMapFileHeader.Reserved1 is not zero." | |
assert BitMapFileHeader.Reserved2 == 0, "BitMapFileHeader.Reserved2 is not zero." | |
BitMapInfoHeader = BitMapInfoHeader_Tuple._make(struct.unpack(s_bmih, data_bmih)) | |
assert BitMapInfoHeader.ColorPlanes == 1, "BitMapInfoHeader.ColorPlanes is not 1" | |
assert BitMapInfoHeader.BitsPerPixel == 1, "BitMapInfoHeader.BitsPerPixel is not 1" | |
BitMapPalette = BitMapPalette_Tuple._make(struct.unpack(s_palette, data_palette)) | |
print(BitMapFileHeader) | |
print(BitMapInfoHeader) | |
print(BitMapPalette) | |
BitMapDataBit = [] | |
BitMapDataByte = data_bytearr[iReadPointer:] | |
for a_byte in BitMapDataByte: | |
for i in range(8): | |
BitMapDataBit.append(1 if (a_byte & (1 << (7 - i))) else 0) | |
assert len(BitMapDataBit) == BitMapInfoHeader.Width * BitMapInfoHeader.Height, "Wrong BitMap Data Size" | |
iBitPointer = len(BitMapDataBit) | |
BitMapArray = [[0 for i in range(256)] for j in range(192)] | |
for i in range(BitMapInfoHeader.Height): | |
for j in range(BitMapInfoHeader.Width): | |
iBitPointer -= 1 | |
if BitMapDataBit[iBitPointer] == 1: | |
BitMapArray[i][BitMapInfoHeader.Width - j - 1] = 1 | |
s_rle_header = ''.join(['<', # Little endian | |
'B', # Esc, 0x1B | |
'B', # G, 0x47 | |
'B', # Size, 0x48 | |
]) | |
s_rle_footer = ''.join (['<', # Little endian | |
'B', # Esc 1, 0x1B | |
'B', # Esc 2, 0x1B; | |
'B', # G, 0x47 | |
'B', # N, 0x4E | |
]) | |
RleHeader = struct.pack(s_rle_header, 0x1B, 0x47, 0x48) | |
RleFooter = struct.pack(s_rle_footer, 0x1B, 0x1B, 0x47, 0x4E) | |
BlackCount = 0x20 | |
WhiteCount = 0x20 | |
iBlackFlag = 1 | |
RleBody = bytearray() | |
for i in range(192): | |
for j in range(256): | |
if iBlackFlag == 1: | |
if BitMapArray[i][j] == 0: | |
BlackCount += 1 | |
else: | |
WhiteCount += 1 | |
iBlackFlag = 0 | |
if BlackCount == 0x7E: | |
iBlackFlag = 0 | |
else: # iBlackFlag == 0 | |
if BitMapArray[i][j] == 1: | |
WhiteCount += 1 | |
else: | |
RleBody.append(BlackCount) | |
RleBody.append(WhiteCount) | |
BlackCount = 0x21 | |
WhiteCount = 0x20 | |
iBlackFlag = 1 | |
if WhiteCount == 0x7E: | |
RleBody.append(BlackCount) | |
RleBody.append(WhiteCount) | |
BlackCount = 0x20 | |
WhiteCount = 0x20 | |
iBlackFlag = 1 | |
RleBody.append(BlackCount) | |
RleBody.append(WhiteCount) | |
RleContent = RleHeader + RleBody + RleFooter | |
open(output_filename, 'wb').write(RleContent) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment