1 ! Copyright (C) 2009 Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors arrays byte-arrays combinators
4 compression.run-length fry grouping images images.loader io
5 io.binary io.encodings.8-bit io.encodings.binary
6 io.encodings.string io.streams.limited kernel math math.bitwise
7 sequences specialized-arrays.ushort summary ;
8 QUALIFIED-WITH: bitstreams b
9 IN: images.bitmap.loading
11 SINGLETON: bitmap-image
12 "bmp" bitmap-image register-image-class
14 ! http://www.fileformat.info/format/bmp/egff.htm
15 ! http://www.digicamsoft.com/bmp/bmp.html
17 ERROR: unknown-component-order bitmap ;
18 ERROR: unknown-bitmap-header n ;
20 : read2 ( -- n ) 2 read le> ;
21 : read4 ( -- n ) 4 read le> ;
25 color-palette color-index bitfields ;
28 { magic initial: "BM" }
30 { reserved1 initial: 0 }
31 { reserved2 initial: 0 }
39 { bit-count initial: 0 }
40 { compression initial: 0 }
41 { image-size initial: 0 }
42 { x-resolution initial: 0 }
43 { y-resolution initial: 0 }
44 { colors-used initial: 0 }
45 { colors-important initial: 0 } ;
47 TUPLE: v4-header < v3-header
48 { red-mask initial: 0 }
49 { green-mask initial: 0 }
50 { blue-mask initial: 0 }
51 { alpha-mask initial: 0 }
52 { cs-type initial: 0 }
53 { end-points initial: 0 }
54 { gamma-red initial: 0 }
55 { gamma-green initial: 0 }
56 { gamma-blue initial: 0 } ;
58 TUPLE: v5-header < v4-header
60 { profile-data initial: 0 }
61 { profile-size initial: 0 }
62 { reserved3 initial: 0 } ;
68 { bit-count initial: 0 } ;
70 TUPLE: os2v2-header < os2v1-header
71 { compression initial: 0 }
72 { image-size initial: 0 }
73 { x-resolution initial: 0 }
74 { y-resolution initial: 0 }
75 { colors-used initial: 0 }
76 { colors-important initial: 0 }
78 { reserved initial: 0 }
79 { recording initial: 0 }
80 { rendering initial: 0 }
83 { color-encoding initial: 0 }
84 { identifier initial: 0 } ;
86 UNION: v-header v3-header v4-header v5-header ;
87 UNION: os2-header os2v1-header os2v2-header ;
89 : parse-file-header ( -- file-header )
91 2 read latin1 decode >>magic
96 read4 >>header-length ;
98 : read-v3-header-data ( header -- header )
100 read4 32 >signed >>height
108 read4 >>colors-important ;
110 : read-v3-header ( -- header )
112 read-v3-header-data ;
114 : read-v4-header-data ( header -- header )
120 read4 read4 read4 3array >>end-points
125 : read-v4-header ( -- v4-header )
128 read-v4-header-data ;
130 : read-v5-header-data ( v5-header -- v5-header )
136 : read-v5-header ( -- loading-bitmap )
140 read-v5-header-data ;
142 : read-os2v1-header ( -- os2v1-header )
145 read2 16 >signed >>height
149 : read-os2v2-header-data ( os2v2-header -- os2v2-header )
151 read4 32 >signed >>height
159 read4 >>colors-important
166 read4 >>color-encoding
169 : read-os2v2-header ( -- os2v2-header )
171 read-os2v2-header-data ;
173 : parse-header ( n -- header )
175 { 12 [ read-os2v1-header ] }
176 { 64 [ read-os2v2-header ] }
177 { 40 [ read-v3-header ] }
178 { 108 [ read-v4-header ] }
179 { 124 [ read-v5-header ] }
180 [ unknown-bitmap-header ]
183 : color-index-length ( header -- n )
187 [ bit-count>> * 31 + 32 /i 4 * ]
191 : color-palette-length ( loading-bitmap -- n )
193 [ offset>> 14 - ] [ header-length>> ] bi - ;
195 : parse-color-palette ( loading-bitmap -- loading-bitmap )
196 dup color-palette-length read >>color-palette ;
198 GENERIC: parse-color-data* ( loading-bitmap header -- loading-bitmap )
200 : parse-color-data ( loading-bitmap -- loading-bitmap )
201 dup header>> parse-color-data* ;
203 M: os2v1-header parse-color-data* ( loading-bitmap header -- loading-bitmap )
204 color-index-length read >>color-index ;
206 M: object parse-color-data* ( loading-bitmap header -- loading-bitmap )
207 dup image-size>> [ 0 ] unless* dup 0 >
208 [ nip ] [ drop color-index-length ] if read >>color-index ;
210 : alpha-used? ( loading-bitmap -- ? )
211 color-index>> 4 <sliced-groups> [ fourth 0 = ] all? not ;
213 GENERIC: bitmap>component-order* ( loading-bitmap header -- object )
215 : bitmap>component-order ( loading-bitmap -- object )
216 dup header>> bitmap>component-order* ;
218 : simple-bitmap>component-order ( loading-bitamp -- object )
219 header>> bit-count>> {
226 [ unknown-component-order ]
229 : advanced-bitmap>component-order ( loading-bitmap -- object )
230 [ ] [ header>> bit-count>> ] [ alpha-used? ] tri 2array {
231 { { 32 t } [ drop BGRA ] }
232 { { 32 f } [ drop BGRX ] }
233 [ drop simple-bitmap>component-order ]
236 : color-lookup3 ( loading-bitmap -- seq )
237 [ color-index>> >array ]
238 [ color-palette>> 3 <sliced-groups> ] bi
239 '[ _ nth ] map concat ;
241 : color-lookup4 ( loading-bitmap -- seq )
242 [ color-index>> >array ]
243 [ color-palette>> 4 <sliced-groups> [ 3 head-slice ] map ] bi
244 '[ _ nth ] map concat ;
246 ! os2v1 is 3bytes each, all others are 3 + 1 unused
247 : color-lookup ( loading-bitmap -- seq )
248 dup file-header>> header-length>> {
249 { 12 [ color-lookup3 ] }
250 { 64 [ color-lookup4 ] }
251 { 40 [ color-lookup4 ] }
252 { 108 [ color-lookup4 ] }
253 { 124 [ color-lookup4 ] }
256 M: os2v1-header bitmap>component-order* drop simple-bitmap>component-order ;
257 M: os2v2-header bitmap>component-order* drop simple-bitmap>component-order ;
258 M: v3-header bitmap>component-order* drop simple-bitmap>component-order ;
259 M: v4-header bitmap>component-order* drop advanced-bitmap>component-order ;
260 M: v5-header bitmap>component-order* drop advanced-bitmap>component-order ;
262 : uncompress-bitfield ( seq masks -- bytes' )
265 [ bitand ] [ bit-count ] [ log2 ] tri - shift
267 ] { } map-as B{ } concat-as ;
269 ERROR: bmp-not-supported n ;
271 : bitmap>bytes ( loading-bitmap -- byte-array )
272 dup header>> bit-count>>
274 { 32 [ color-index>> ] }
275 { 24 [ color-index>> ] }
278 ! byte-array>ushort-array
281 ! { HEX: f800 HEX: 7e0 HEX: 1f } uncompress-bitfield
283 { HEX: 7c00 HEX: 3e0 HEX: 1f } uncompress-bitfield
287 { 8 [ color-lookup ] }
288 { 4 [ [ 4 b:byte-array-n>seq ] change-color-index color-lookup ] }
289 { 1 [ [ 1 b:byte-array-n>seq ] change-color-index color-lookup ] }
290 [ bmp-not-supported ]
293 : set-bitfield-widths ( loading-bitmap -- loading-bitmap' )
294 dup header>> bit-count>> {
295 { 16 [ dup color-palette>> 4 group [ le> ] map ] }
296 { 32 [ { HEX: ff0000 HEX: ff00 HEX: ff } ] }
297 } case reverse >>bitfields ;
299 ERROR: unsupported-bitfield-widths n ;
301 M: unsupported-bitfield-widths summary
302 drop "Bitmaps only support bitfield compression in 16/32bit images" ;
304 : uncompress-bitfield-widths ( loading-bitmap -- loading-bitmap' )
306 dup header>> bit-count>> {
309 byte-array>ushort-array _ uncompress-bitfield
313 [ unsupported-bitfield-widths ]
316 ERROR: unsupported-bitmap-compression compression ;
318 GENERIC: uncompress-bitmap* ( loading-bitmap header -- loading-bitmap )
320 : uncompress-bitmap ( loading-bitmap -- loading-bitmap )
321 dup header>> uncompress-bitmap* ;
323 M: os2-header uncompress-bitmap* ( loading-bitmap header -- loading-bitmap' )
326 : do-run-length-uncompress ( loading-bitmap word -- loading-bitmap )
328 _ header>> [ width>> ] [ height>> ] bi
330 ] change-color-index ; inline
332 M: v-header uncompress-bitmap* ( loading-bitmap header -- loading-bitmap' )
336 { 1 [ \ run-length-uncompress-bitmap8 do-run-length-uncompress ] }
337 { 2 [ \ run-length-uncompress-bitmap4 do-run-length-uncompress ] }
338 { 3 [ uncompress-bitfield-widths ] }
339 { 4 [ "jpeg" unsupported-bitmap-compression ] }
340 { 5 [ "png" unsupported-bitmap-compression ] }
343 ERROR: unsupported-bitmap-file magic ;
345 : load-bitmap ( stream -- loading-bitmap )
348 parse-file-header [ >>file-header ] [ ] bi magic>> {
350 dup file-header>> header-length>> parse-header >>header
354 ! { "BA" [ parse-os2-bitmap-array ] }
355 ! { "CI" [ parse-os2-color-icon ] }
356 ! { "CP" [ parse-os2-color-pointer ] }
357 ! { "IC" [ parse-os2-icon ] }
358 ! { "PT" [ parse-os2-pointer ] }
359 [ unsupported-bitmap-file ]
361 ] with-input-stream ;
363 : loading-bitmap>bytes ( loading-bitmap -- byte-array )
364 uncompress-bitmap bitmap>bytes ;
366 M: bitmap-image stream>image ( stream bitmap-image -- bitmap )
370 [ loading-bitmap>bytes >>bitmap ]
371 [ header>> [ width>> ] [ height>> abs ] bi 2array >>dim ]
372 [ header>> height>> 0 < not >>upside-down? ]
373 [ bitmap>component-order >>component-order ubyte-components >>component-type ]