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