1*4882a593Smuzhiyun#!/usr/bin/env python 2*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0 */ 3*4882a593Smuzhiyun# -*- coding: utf-8 -*- 4*4882a593Smuzhiyun 5*4882a593Smuzhiyunfrom struct import unpack, pack 6*4882a593Smuzhiyunimport sys 7*4882a593Smuzhiyunimport getopt 8*4882a593Smuzhiyun 9*4882a593Smuzhiyun 10*4882a593Smuzhiyunclass BMPFile: 11*4882a593Smuzhiyun 12*4882a593Smuzhiyun def __init__(self, file_path, force_revers=0, force_swap=0): 13*4882a593Smuzhiyun self.file = open(file_path, "rb+") 14*4882a593Smuzhiyun # bmp head 14bit 15*4882a593Smuzhiyun self.bfType = unpack("<h", self.file.read(2))[0] 16*4882a593Smuzhiyun self.bfSize = unpack("<i", self.file.read(4))[0] 17*4882a593Smuzhiyun self.bfReserved1 = unpack("<h", self.file.read(2))[0] 18*4882a593Smuzhiyun self.bfReserved2 = unpack("<h", self.file.read(2))[0] 19*4882a593Smuzhiyun self.bfOffBits = unpack("<i", self.file.read(4))[0] 20*4882a593Smuzhiyun 21*4882a593Smuzhiyun self.biSize = unpack("<i", self.file.read(4))[0] 22*4882a593Smuzhiyun self.biWidth = unpack("<i", self.file.read(4))[0] 23*4882a593Smuzhiyun self.biHeight = unpack("<i", self.file.read(4))[0] 24*4882a593Smuzhiyun self.biPlanes = unpack("<h", self.file.read(2))[0] 25*4882a593Smuzhiyun self.biBitCount = unpack("<h", self.file.read(2))[0] 26*4882a593Smuzhiyun # bmp parameter 40 bit normaally 27*4882a593Smuzhiyun self.biCompression = unpack("<i", self.file.read(4))[0] 28*4882a593Smuzhiyun self.biSizeImage = unpack("<i", self.file.read(4))[0] 29*4882a593Smuzhiyun self.biXPixelsPerMeter = unpack("<i", self.file.read(4))[0] 30*4882a593Smuzhiyun self.biYPixelsPerMeter = unpack("<i", self.file.read(4))[0] 31*4882a593Smuzhiyun self.biClrUsed = unpack("<i", self.file.read(4))[0] 32*4882a593Smuzhiyun self.biClrImportant = unpack("<i", self.file.read(4))[0] 33*4882a593Smuzhiyun self.head = [] 34*4882a593Smuzhiyun self.color_map = [] 35*4882a593Smuzhiyun self.bmp_data = [] 36*4882a593Smuzhiyun self.bf_map = [] 37*4882a593Smuzhiyun self.force_revers = force_revers 38*4882a593Smuzhiyun self.force_swap = force_swap 39*4882a593Smuzhiyun # some software change parameter size more than 40 bit 40*4882a593Smuzhiyun if self.biSize > 40: 41*4882a593Smuzhiyun self.read_other(self.biSize-40) 42*4882a593Smuzhiyun 43*4882a593Smuzhiyun if self.biBitCount == 16 and self.biCompression == 3: 44*4882a593Smuzhiyun for i in range(4): 45*4882a593Smuzhiyun self.bf_map.append( 46*4882a593Smuzhiyun [unpack("<i", self.file.read(4))[0]] 47*4882a593Smuzhiyun ) 48*4882a593Smuzhiyun if self.biBitCount == 24: 49*4882a593Smuzhiyun self.get_24bit_data() 50*4882a593Smuzhiyun elif self.biBitCount == 16: 51*4882a593Smuzhiyun if self.biCompression == 3: 52*4882a593Smuzhiyun self.bmp16bit_to_24bit_bf() 53*4882a593Smuzhiyun else: 54*4882a593Smuzhiyun self.bmp16bit_to_24bit() 55*4882a593Smuzhiyun elif self.biBitCount == 8: 56*4882a593Smuzhiyun # Not convert 8bit bmp logo to 24 bit 57*4882a593Smuzhiyun self.file.close() 58*4882a593Smuzhiyun return 59*4882a593Smuzhiyun else: 60*4882a593Smuzhiyun self.bmp32bit_to_24bit() 61*4882a593Smuzhiyun self.rb_swap = 0 62*4882a593Smuzhiyun if self.bfReserved1 != 8399 and self.biHeight > 0: 63*4882a593Smuzhiyun self.reverse_bmp_data() 64*4882a593Smuzhiyun print("reverse data at first time") 65*4882a593Smuzhiyun if self.force_revers: 66*4882a593Smuzhiyun self.reverse_bmp_data() 67*4882a593Smuzhiyun print("reverse data by force") 68*4882a593Smuzhiyun if self.force_swap: 69*4882a593Smuzhiyun self.rb_swap = 1 70*4882a593Smuzhiyun print("swap rb by force'") 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun if self.bfReserved1 == 8399: 73*4882a593Smuzhiyun self.file.close() 74*4882a593Smuzhiyun return 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun self.write_24bit(self.rb_swap) 77*4882a593Smuzhiyun self.file.close() 78*4882a593Smuzhiyun 79*4882a593Smuzhiyun def read_other(self, n): 80*4882a593Smuzhiyun for i in range(n): 81*4882a593Smuzhiyun self.file.read(1) 82*4882a593Smuzhiyun 83*4882a593Smuzhiyun def reverse_bmp_data(self): 84*4882a593Smuzhiyun self.bmp_data.reverse() 85*4882a593Smuzhiyun 86*4882a593Smuzhiyun @staticmethod 87*4882a593Smuzhiyun def get_16bit_bgr_bf(pixel): 88*4882a593Smuzhiyun red = (pixel[1] & 0xf8) << 0 89*4882a593Smuzhiyun green = ((pixel[1] & 0x07) << 5) | ((pixel[0] & 0xe0) >> 3) 90*4882a593Smuzhiyun blue = ((pixel[0] & 0x1f) << 3) 91*4882a593Smuzhiyun new_pixel = [blue, green, red] 92*4882a593Smuzhiyun return new_pixel 93*4882a593Smuzhiyun 94*4882a593Smuzhiyun def bmp32bit_to_24bit(self): 95*4882a593Smuzhiyun for height in range(abs(self.biHeight)): 96*4882a593Smuzhiyun bmp_data_row = [] 97*4882a593Smuzhiyun # bmp file 4 align 98*4882a593Smuzhiyun count = 0 99*4882a593Smuzhiyun for width in range(self.biWidth): 100*4882a593Smuzhiyun bmp_data_row.append( 101*4882a593Smuzhiyun [unpack("<B", self.file.read(1))[0], unpack("<B", self.file.read(1))[0], unpack("<B", self.file.read(1))[0]]) 102*4882a593Smuzhiyun self.file.read(1) 103*4882a593Smuzhiyun count = count + 4 104*4882a593Smuzhiyun while count % 4 != 0: 105*4882a593Smuzhiyun self.file.read(1) 106*4882a593Smuzhiyun count = count + 1 107*4882a593Smuzhiyun self.bmp_data.append(bmp_data_row) 108*4882a593Smuzhiyun 109*4882a593Smuzhiyun def bmp16bit_to_24bit_bf(self): 110*4882a593Smuzhiyun self.get_16bit_data() 111*4882a593Smuzhiyun temp_data = self.bmp_data 112*4882a593Smuzhiyun self.bmp_data = [] 113*4882a593Smuzhiyun for height in range(abs(self.biHeight)): 114*4882a593Smuzhiyun bmp_data_row = [] 115*4882a593Smuzhiyun for width in range(self.biWidth): 116*4882a593Smuzhiyun bmp_data_row.append( 117*4882a593Smuzhiyun self.get_16bit_bgr_bf(temp_data[height][width]) 118*4882a593Smuzhiyun ) 119*4882a593Smuzhiyun self.bmp_data.append(bmp_data_row) 120*4882a593Smuzhiyun 121*4882a593Smuzhiyun def bmp8bit_to_24bit_rle(self): 122*4882a593Smuzhiyun bmp_data_row = [] 123*4882a593Smuzhiyun data_x = [] 124*4882a593Smuzhiyun t_count = 0 125*4882a593Smuzhiyun loop = 1 126*4882a593Smuzhiyun while loop: 127*4882a593Smuzhiyun data1 = unpack("<B", self.file.read(1))[0] 128*4882a593Smuzhiyun if data1 > 0: 129*4882a593Smuzhiyun data2 = unpack("<B", self.file.read(1))[0] 130*4882a593Smuzhiyun for n in range(data1): 131*4882a593Smuzhiyun bmp_data_row.append(self.color_map[data2]) 132*4882a593Smuzhiyun if data1 == 0: 133*4882a593Smuzhiyun data2 = unpack("<B", self.file.read(1))[0] 134*4882a593Smuzhiyun if data2 > 2: 135*4882a593Smuzhiyun data_count = data2 136*4882a593Smuzhiyun data_temp = unpack("<B", self.file.read(1))[0] 137*4882a593Smuzhiyun data_x.append(data_temp) 138*4882a593Smuzhiyun while data_temp != 0: 139*4882a593Smuzhiyun data_temp = unpack("<B", self.file.read(1))[0] 140*4882a593Smuzhiyun data_x.append(data_temp) 141*4882a593Smuzhiyun for m in range(data_count): 142*4882a593Smuzhiyun bmp_data_row.append(self.color_map[data_x[m]]) 143*4882a593Smuzhiyun if data2 == 2: 144*4882a593Smuzhiyun print("data2 == 2") 145*4882a593Smuzhiyun if data2 == 0: 146*4882a593Smuzhiyun t_count += 1 147*4882a593Smuzhiyun self.bmp_data.append(bmp_data_row) 148*4882a593Smuzhiyun bmp_data_row = [] 149*4882a593Smuzhiyun if data2 == 1: 150*4882a593Smuzhiyun print("encode over!") 151*4882a593Smuzhiyun loop = 0 152*4882a593Smuzhiyun 153*4882a593Smuzhiyun def bmp8bit_to_24bit(self): 154*4882a593Smuzhiyun for height in range(abs(self.biHeight)): 155*4882a593Smuzhiyun bmp_data_row = [] 156*4882a593Smuzhiyun count = 0 157*4882a593Smuzhiyun for width in range(self.biWidth): 158*4882a593Smuzhiyun bmp_data_row.append( 159*4882a593Smuzhiyun self.color_map[unpack("<B", self.file.read(1))[0]]) 160*4882a593Smuzhiyun count = count + 1 161*4882a593Smuzhiyun while count % 4 != 0: 162*4882a593Smuzhiyun self.file.read(1) 163*4882a593Smuzhiyun count = count + 1 164*4882a593Smuzhiyun self.bmp_data.append(bmp_data_row) 165*4882a593Smuzhiyun 166*4882a593Smuzhiyun @staticmethod 167*4882a593Smuzhiyun def get_16bit_bgr(pixel): 168*4882a593Smuzhiyun red = (pixel[1] & 0x7c) << 1 169*4882a593Smuzhiyun green = ((pixel[1] & 0x03) << 6) | ((pixel[0] & 0xe0) >> 2) 170*4882a593Smuzhiyun blue = ((pixel[0] & 0x1f) << 3) 171*4882a593Smuzhiyun new_pixel = [blue, green, red] 172*4882a593Smuzhiyun return new_pixel 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun def bmp16bit_to_24bit(self): 175*4882a593Smuzhiyun self.get_16bit_data() 176*4882a593Smuzhiyun temp_data = self.bmp_data 177*4882a593Smuzhiyun self.bmp_data = [] 178*4882a593Smuzhiyun for height in range(abs(self.biHeight)): 179*4882a593Smuzhiyun bmp_data_row = [] 180*4882a593Smuzhiyun for width in range(self.biWidth): 181*4882a593Smuzhiyun bmp_data_row.append( 182*4882a593Smuzhiyun self.get_16bit_bgr(temp_data[height][width]) 183*4882a593Smuzhiyun ) 184*4882a593Smuzhiyun self.bmp_data.append(bmp_data_row) 185*4882a593Smuzhiyun 186*4882a593Smuzhiyun def get_head(self): 187*4882a593Smuzhiyun self.file.seek(0, 0) 188*4882a593Smuzhiyun for i in range(54): 189*4882a593Smuzhiyun self.head.append(unpack("<B", self.file.read(1))[0]) 190*4882a593Smuzhiyun return self.head 191*4882a593Smuzhiyun 192*4882a593Smuzhiyun def get_16bit_data(self): 193*4882a593Smuzhiyun for height in range(abs(self.biHeight)): 194*4882a593Smuzhiyun bmp_data_row = [] 195*4882a593Smuzhiyun count = 0 196*4882a593Smuzhiyun for width in range(self.biWidth): 197*4882a593Smuzhiyun bmp_data_row.append( 198*4882a593Smuzhiyun [unpack("<B", self.file.read(1))[0], unpack("<B", self.file.read(1))[0]]) 199*4882a593Smuzhiyun count = count + 2 200*4882a593Smuzhiyun while count % 4 != 0: 201*4882a593Smuzhiyun self.file.read(1) 202*4882a593Smuzhiyun count = count + 1 203*4882a593Smuzhiyun self.bmp_data.append(bmp_data_row) 204*4882a593Smuzhiyun 205*4882a593Smuzhiyun def get_24bit_data(self): 206*4882a593Smuzhiyun for height in range(abs(self.biHeight)): 207*4882a593Smuzhiyun bmp_data_row = [] 208*4882a593Smuzhiyun count = 0 209*4882a593Smuzhiyun for width in range(self.biWidth): 210*4882a593Smuzhiyun bmp_data_row.append( 211*4882a593Smuzhiyun [unpack("<B", self.file.read(1))[0], unpack("<B", self.file.read(1))[0], unpack("<B", self.file.read(1))[0]]) 212*4882a593Smuzhiyun count = count + 3 213*4882a593Smuzhiyun while count % 4 != 0: 214*4882a593Smuzhiyun self.file.read(1) 215*4882a593Smuzhiyun count = count + 1 216*4882a593Smuzhiyun self.bmp_data.append(bmp_data_row) 217*4882a593Smuzhiyun 218*4882a593Smuzhiyun def write_24bit(self,rb_swap): 219*4882a593Smuzhiyun self.file.seek(0, 0) 220*4882a593Smuzhiyun self.write_head_24bit() 221*4882a593Smuzhiyun self.write_data_24bit(rb_swap) 222*4882a593Smuzhiyun 223*4882a593Smuzhiyun def write_head(self): 224*4882a593Smuzhiyun self.file.write(pack("<h", self.bfType)) 225*4882a593Smuzhiyun self.file.write(pack("<i", self.bfSize)) 226*4882a593Smuzhiyun self.file.write(pack("<h", self.bfReserved1)) 227*4882a593Smuzhiyun self.file.write(pack("<h", self.bfReserved2)) 228*4882a593Smuzhiyun self.file.write(pack("<i", self.bfOffBits)) # bfOffBits 229*4882a593Smuzhiyun self.file.write(pack("<i", self.biSize)) # biSize 230*4882a593Smuzhiyun self.file.write(pack("<i", self.biWidth)) 231*4882a593Smuzhiyun 232*4882a593Smuzhiyun self.file.write(pack("<i", self.biHeight)) 233*4882a593Smuzhiyun self.file.write(pack("<h", self.biPlanes)) 234*4882a593Smuzhiyun self.file.write(pack("<h", self.biBitCount)) # biBitCount 235*4882a593Smuzhiyun self.file.write(pack("<i", self.biCompression)) # biCompression 236*4882a593Smuzhiyun self.file.write(pack("<i", self.biSizeImage)) # biSizeImage 237*4882a593Smuzhiyun self.file.write(pack("<i", self.biXPixelsPerMeter)) # biXPixelsPerMeter 238*4882a593Smuzhiyun self.file.write(pack("<i", self.biYPixelsPerMeter)) # biYPixelsPerMeter 239*4882a593Smuzhiyun self.file.write(pack("<i", self.biClrUsed)) # biClrUsed 240*4882a593Smuzhiyun self.file.write(pack("<i", self.biClrImportant)) # biClrImportant try to mark whether is reversed 241*4882a593Smuzhiyun 242*4882a593Smuzhiyun def write_head_24bit(self): 243*4882a593Smuzhiyun temp_bi_width = self.biWidth * 3 244*4882a593Smuzhiyun while temp_bi_width % 4 != 0: 245*4882a593Smuzhiyun temp_bi_width = temp_bi_width + 1 246*4882a593Smuzhiyun new_bf_size = temp_bi_width * abs(self.biHeight) + 54 247*4882a593Smuzhiyun self.file.write(pack("<h", self.bfType)) 248*4882a593Smuzhiyun self.file.write(pack("<i", new_bf_size)) 249*4882a593Smuzhiyun self.file.write(pack("<h", 8399)) # a mark for uboot dealing 250*4882a593Smuzhiyun self.file.write(pack("<h", self.bfReserved2)) 251*4882a593Smuzhiyun self.file.write(pack("<i", 54)) # bfOffBits 252*4882a593Smuzhiyun self.file.write(pack("<i", 40)) # biSize 253*4882a593Smuzhiyun self.file.write(pack("<i", self.biWidth)) 254*4882a593Smuzhiyun # force height to negative let logo show normal in windows 255*4882a593Smuzhiyun # the uboot code can deal with negative height 256*4882a593Smuzhiyun if self.biHeight < 0: 257*4882a593Smuzhiyun self.file.write(pack("<i", self.biHeight)) 258*4882a593Smuzhiyun else: 259*4882a593Smuzhiyun self.file.write(pack("<i", self.biHeight * -1)) 260*4882a593Smuzhiyun self.file.write(pack("<h", self.biPlanes)) 261*4882a593Smuzhiyun self.file.write(pack("<h", 24)) # biBitCount 262*4882a593Smuzhiyun self.file.write(pack("<i", 0)) # biCompression 263*4882a593Smuzhiyun self.file.write(pack("<i", 0)) # biSizeImage 264*4882a593Smuzhiyun self.file.write(pack("<i", 0)) # biXPixelsPerMeter 265*4882a593Smuzhiyun self.file.write(pack("<i", 0)) # biYPixelsPerMeter 266*4882a593Smuzhiyun self.file.write(pack("<i", 0)) # biClrUsed 267*4882a593Smuzhiyun self.file.write(pack("<i", 0)) # biClrImportant try to mark whether is reversed 268*4882a593Smuzhiyun 269*4882a593Smuzhiyun def write_data_24bit(self, rb_swap): 270*4882a593Smuzhiyun for hg in range(abs(self.biHeight)): 271*4882a593Smuzhiyun count = 0 272*4882a593Smuzhiyun for wd in range(self.biWidth): 273*4882a593Smuzhiyun if rb_swap: 274*4882a593Smuzhiyun self.file.write(pack("<B", self.bmp_data[hg][wd][2])) 275*4882a593Smuzhiyun self.file.write(pack("<B", self.bmp_data[hg][wd][1])) 276*4882a593Smuzhiyun self.file.write(pack("<B", self.bmp_data[hg][wd][0])) 277*4882a593Smuzhiyun else: 278*4882a593Smuzhiyun self.file.write(pack("<B", self.bmp_data[hg][wd][0])) 279*4882a593Smuzhiyun self.file.write(pack("<B", self.bmp_data[hg][wd][1])) 280*4882a593Smuzhiyun self.file.write(pack("<B", self.bmp_data[hg][wd][2])) 281*4882a593Smuzhiyun count = count + 3 282*4882a593Smuzhiyun while count % 4 != 0: 283*4882a593Smuzhiyun self.file.write(pack("<B", 0)) 284*4882a593Smuzhiyun count = count + 1 285*4882a593Smuzhiyun 286*4882a593Smuzhiyun 287*4882a593Smuzhiyunif __name__ == "__main__": 288*4882a593Smuzhiyun 289*4882a593Smuzhiyun swap = 0 290*4882a593Smuzhiyun revers = 0 291*4882a593Smuzhiyun par = len(sys.argv[1:]) 292*4882a593Smuzhiyun if par == 1: 293*4882a593Smuzhiyun bmp = BMPFile(sys.argv[1]) 294*4882a593Smuzhiyun elif par == 0: 295*4882a593Smuzhiyun print("This program is trying to convert different format of bmpfile to a same format" 296*4882a593Smuzhiyun "to make vop can handle bmpfile easily") 297*4882a593Smuzhiyun print("try such cmd to make it work python bmpconvert xxx/xxx.bmp") 298*4882a593Smuzhiyun else: 299*4882a593Smuzhiyun try: 300*4882a593Smuzhiyun opts, args = getopt.getopt(sys.argv[2:], "hrs", ["help", "reverse", "swap"]) 301*4882a593Smuzhiyun for opt, arg in opts: 302*4882a593Smuzhiyun 303*4882a593Smuzhiyun if opt in ('-h','--help'): 304*4882a593Smuzhiyun print("add -r option will force program to reverse data") 305*4882a593Smuzhiyun print("add -s option will force program to swap data of rb") 306*4882a593Smuzhiyun if opt in ("-r", "--reverse"): 307*4882a593Smuzhiyun revers = 1 308*4882a593Smuzhiyun if opt in ("-s", "--swap"): 309*4882a593Smuzhiyun swap = 1 310*4882a593Smuzhiyun bmp = BMPFile(sys.argv[1], revers, swap) 311*4882a593Smuzhiyun except getopt.GetoptError: 312*4882a593Smuzhiyun sys.exit(1) 313*4882a593Smuzhiyun 314