32
32
__version__ = "0.0.0-auto.0"
33
33
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ImageLoad.git"
34
34
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 ):
36
37
"""Loads indexed bitmap data into bitmap and palette objects.
37
38
38
39
:param file file: The open bmp file
39
40
:param int width: Image width in pixels
40
41
:param int height: Image height in pixels
41
42
:param int data_start: Byte location where the data starts (after headers)
42
43
: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
45
47
if palette :
46
48
palette = palette (colors )
47
49
@@ -70,7 +72,6 @@ def load(file, width, height, data_start, colors, color_depth, *, bitmap=None, p
70
72
if line_size % 4 != 0 :
71
73
line_size += (4 - line_size % 4 )
72
74
73
- chunk = bytearray (line_size )
74
75
mask = (1 << minimum_color_depth ) - 1
75
76
if height > 0 :
76
77
range1 = height - 1
@@ -80,14 +81,153 @@ def load(file, width, height, data_start, colors, color_depth, *, bitmap=None, p
80
81
range1 = 0
81
82
range2 = abs (height )
82
83
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
84
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 )
92
103
93
104
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