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