3232__version__ = "0.0.0-auto.0"
3333__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git"
3434
35- def load (file , width , height , data_start , colors , color_depth , * , bitmap = None , palette = None ):
35+ def load (file , width , height , data_start , colors , color_depth , compression , * , bitmap = None , palette = None ):
3636 """Loads indexed bitmap data into bitmap and palette objects.
3737
3838 :param file file: The open bmp file
3939 :param int width: Image width in pixels
4040 :param int height: Image height in pixels
4141 :param int data_start: Byte location where the data starts (after headers)
4242 :param int colors: Number of distinct colors in the image
43- :param int color_depth: Number of bits used to store a value"""
43+ :param int color_depth: Number of bits used to store a value
44+ :param int compression: 0 - none, 1 - 8bit RLE, 2 - 4bit RLE"""
4445 # pylint: disable=too-many-arguments,too-many-locals
4546 if palette :
4647 palette = palette (colors )
@@ -70,7 +71,6 @@ def load(file, width, height, data_start, colors, color_depth, *, bitmap=None, p
7071 if line_size % 4 != 0 :
7172 line_size += (4 - line_size % 4 )
7273
73- chunk = bytearray (line_size )
7474 mask = (1 << minimum_color_depth ) - 1
7575 if height > 0 :
7676 range1 = height - 1
@@ -80,14 +80,138 @@ def load(file, width, height, data_start, colors, color_depth, *, bitmap=None, p
8080 range1 = 0
8181 range2 = abs (height )
8282 range3 = 1
83- for y in range (range1 , range2 , range3 ):
84- file .readinto (chunk )
85- pixels_per_byte = 8 // color_depth
86- offset = y * width
87-
88- for x in range (width ):
89- i = x // pixels_per_byte
90- pixel = (chunk [i ] >> (8 - color_depth * (x % pixels_per_byte + 1 ))) & mask
91- bitmap [offset + x ] = pixel
83+
84+ if compression == 0 :
85+ chunk = bytearray (line_size )
86+ for y in range (range1 , range2 , range3 ):
87+ file .readinto (chunk )
88+ pixels_per_byte = 8 // color_depth
89+ offset = y * width
90+
91+ for x in range (width ):
92+ i = x // pixels_per_byte
93+ pixel = (chunk [i ] >> (8 - color_depth * (x % pixels_per_byte + 1 ))) & mask
94+ bitmap [offset + x ] = pixel
95+ elif compression == 1 or compression == 2 :
96+ # RLE algorithm, either 8-bit (1) or 4-bit (2)
97+ #
98+ # Ref: http://www.fileformat.info/format/bmp/egff.htm
99+
100+ is_4bit = compression == 2
101+
102+ # This will store the 2-byte run commands, which are either an
103+ # amount to repeat and a value to repeat, or a 0x00 and command
104+ # marker.
105+ run_buf = bytearray (2 )
106+
107+ # We need to be prepared to load up to 256 pixels of literal image
108+ # data. (0xFF is max literal length, but odd literal runs are padded
109+ # up to an even byte count, so we need space for 256 in the case of
110+ # 8-bit.) 4-bit images can get away with half that.
111+ literal_buf = bytearray (128 if is_4bit else 256 )
112+
113+ # We iterate with numbers rather than a range because the "delta"
114+ # command can cause us to jump forward arbitrarily in the output
115+ # image.
116+ #
117+ # In theory RLE images are only stored in bottom-up scan line order,
118+ # but we support either.
119+ y = range1
120+ x = 0
121+
122+ while y * range3 < range2 * range3 :
123+ offset = y * width + x
124+
125+ # We keep track of how much space is left in our row so that we
126+ # can avoid writing extra data outside of the Bitmap. While the
127+ # reference above seems to say that the "end run" command is
128+ # optional and that image data should wrap from one scan line to
129+ # the next, in practice (looking at the output of ImageMagick
130+ # and GIMP, and what Preview renders) the bitmap part of the
131+ # image can contain data that goes beyond the image’s stated
132+ # width that should just be ignored. For example, the 8bit RLE
133+ # file is 15px wide but has data for 16px.
134+ width_remaining = width - x
135+
136+ file .readinto (run_buf )
137+
138+ if run_buf [0 ] == 0 :
139+ # A repeat length of "0" is a special command. The next byte
140+ # tells us what needs to happen.
141+ if run_buf [1 ] == 0 :
142+ # end of the current scan line
143+ y = y + range3
144+ x = 0
145+ elif run_buf [1 ] == 1 :
146+ # end of image
147+ break
148+ elif run_buf [1 ] == 2 :
149+ # delta command jumps us ahead in the bitmap output by
150+ # the x, y amounts stored in the next 2 bytes.
151+ file .readinto (run_buf )
152+
153+ x = x + run_buf [0 ]
154+ y = y + run_buf [1 ] * range3
155+ else :
156+ # command values of 3 or more indicate that many pixels
157+ # of literal (uncompressed) image data. For 8-bit mode,
158+ # this is raw bytes, but 4-bit mode counts in nibbles.
159+ literal_length_px = run_buf [1 ]
160+
161+ # Inverting the value here to get round-up integer division
162+ read_length_bytes = - (- literal_length_px // 2 ) if is_4bit else literal_length_px
163+
164+ # If the run has an odd length then there’s a 1-byte padding
165+ # we need to consume but not write into the output
166+ if read_length_bytes % 2 == 1 :
167+ read_length_bytes += 1
168+
169+ # We use memoryview to artificially limit the length of
170+ # literal_buf so that readinto only reads the amount
171+ # that we want.
172+ literal_buf_mem = memoryview (literal_buf )
173+ file .readinto (literal_buf_mem [0 :read_length_bytes ])
174+
175+ if is_4bit :
176+ for i in range (0 , min (literal_length_px , width_remaining )):
177+ # Expanding the two nibbles of the 4-bit data
178+ # into two bytes for our output bitmap.
179+ if i % 2 == 0 :
180+ bitmap [offset + i ] = literal_buf [i // 2 ] >> 4
181+ else :
182+ bitmap [offset + i ] = literal_buf [i // 2 ] & 0x0F
183+ else :
184+ # 8-bit values are just a raw copy (limited by
185+ # what’s left in the row so we don’t overflow out of
186+ # the buffer)
187+ for i in range (0 , min (literal_length_px , width_remaining )):
188+ bitmap [offset + i ] = literal_buf [i ]
189+
190+ x = x + literal_length_px
191+ else :
192+ # first byte was not 0, which means it tells us how much to
193+ # repeat the next byte into the output
194+ run_length_px = run_buf [0 ]
195+
196+ if is_4bit :
197+ # In 4 bit mode, we repeat the *two* values that are
198+ # packed into the next byte. The repeat amount is based
199+ # on pixels, not bytes, though, so if we were to repeat
200+ # 0xab 3 times, the output pixel values would be: 0x0a
201+ # 0x0b 0x0a (notice how it ends at 0x0a) rather than
202+ # 0x0a 0x0b 0x0a 0x0b 0x0a 0x0b
203+ run_values = [
204+ run_buf [1 ] >> 4 ,
205+ run_buf [1 ] & 0x0F
206+ ]
207+ for i in range (0 , min (run_length_px , width_remaining )):
208+ bitmap [offset + i ] = run_values [i % 2 ]
209+ else :
210+ run_value = run_buf [1 ]
211+ for i in range (0 , min (run_length_px , width_remaining )):
212+ bitmap [offset + i ] = run_value
213+
214+
215+ x = x + run_length_px
92216
93217 return bitmap , palette
0 commit comments