maug
Quick and dirty C mini-augmentation library.
Loading...
Searching...
No Matches
mfmt.h
Go to the documentation of this file.
1
2#ifndef MFMT_H
3#define MFMT_H
4
5#include <mfile.h>
6
11
15
21
22#define MFMT_BMPINFO_OFS_WIDTH 4
23#define MFMT_BMPINFO_OFS_HEIGHT 8
24#define MFMT_BMPINFO_OFS_COLOR_PLANES 12
25#define MFMT_BMPINFO_OFS_BPP 14
26#define MFMT_BMPINFO_OFS_COMPRESSION 16
27#define MFMT_BMPINFO_OFS_SZ 20
28#define MFMT_BMPINFO_OFS_HRES 24
29#define MFMT_BMPINFO_OFS_VRES 28
30#define MFMT_BMPINFO_OFS_PAL_SZ 32
31#define MFMT_BMPINFO_OFS_IMP_COLORS 36
32
34#define MFMT_BMP_COMPRESSION_NONE (0)
36#define MFMT_BMP_COMPRESSION_RLE8 (1)
38#define MFMT_BMP_COMPRESSION_RLE4 (2)
39 /* maug_fmt_bmp */
41
42#define MFMT_DECOMP_FLAG_4BIT 0x01
43#define MFMT_DECOMP_FLAG_8BIT 0x02
44
45#define MFMT_PX_FLAG_INVERT_Y 0x01
46
47#ifndef MFMT_TRACE_BMP_LVL
48# define MFMT_TRACE_BMP_LVL 0
49#endif /* !MFMT_TRACE_BMP_LVL */
50
51#ifndef MFMT_TRACE_RLE_LVL
52# define MFMT_TRACE_RLE_LVL 0
53#endif /* !MFMT_TRACE_RLE_LVL */
54
64 uint32_t sz;
65};
66
71
72#define mfmt_bmp_check_header() \
73 debug_printf( MFMT_TRACE_BMP_LVL, \
74 "bmp file sz: " U32_FMT ", magic: %c%c", \
75 header->sz, (((char*)header)[0]), (((char*)header)[1]) ); \
76 if( 40 == header->sz ) { \
77 debug_printf( MFMT_TRACE_BMP_LVL, "bmp info header detected by sz" ); \
78 header_bmp_info = (struct MFMT_STRUCT_BMPINFO*)header; \
79 } else if( \
80 'B' == (((char*)header)[0]) && 'M' == (((char*)header)[1]) \
81 ) { \
82 debug_printf( MFMT_TRACE_BMP_LVL, "bmp file header detected by sig" ); \
83 header_bmp_file = (struct MFMT_STRUCT_BMPFILE*)header; \
84 header_bmp_info = &(header_bmp_file->info); \
85 } else { \
86 error_printf( "unable to select read header!" ); \
87 retval = MERROR_FILE; \
88 goto cleanup; \
89 }
90
94 uint32_t sz;
96 int32_t width;
98 int32_t height;
100 uint16_t color_planes;
102 uint16_t bpp;
104 uint32_t compression;
106 uint32_t img_sz;
108 uint32_t hres;
110 uint32_t vres;
114 uint32_t imp_colors;
115};
116
118 char magic[2];
119 uint32_t file_sz;
120 uint16_t reserved1;
121 uint16_t reserved2;
122 uint32_t px_offset;
123 struct MFMT_STRUCT_BMPINFO info;
124};
125 /* maug_fmt_bmp */
127
138 mfile_t* p_file_in, off_t file_offset, off_t file_sz, size_t line_w,
139 MAUG_MHANDLE buffer_out, off_t buffer_out_sz, uint8_t flags );
140
145 struct MFMT_STRUCT* header, mfile_t* p_file_in,
146 uint32_t file_offset, off_t file_sz, uint8_t* p_flags );
147
152 struct MFMT_STRUCT* header, uint32_t* palette, size_t palette_sz,
153 mfile_t* p_file_in, uint32_t file_offset, off_t file_sz,
154 uint8_t flags );
155
160 struct MFMT_STRUCT* header, uint8_t SEG_FAR* px, off_t px_sz,
161 mfile_t* p_file_in, uint32_t file_offset, off_t file_sz,
162 uint8_t flags );
163
168 mfile_t* p_file_in, off_t file_offset, off_t file_sz, size_t line_w,
169 MAUG_MHANDLE buffer_out, off_t buffer_out_sz, uint8_t flags );
170
171MERROR_RETVAL mfmt_read_bmp_header(
172 struct MFMT_STRUCT* header, mfile_t* p_file_in,
173 uint32_t file_offset, off_t file_sz, uint8_t* p_flags );
174
175MERROR_RETVAL mfmt_read_bmp_palette(
176 struct MFMT_STRUCT* header, uint32_t* palette, size_t palette_sz,
177 mfile_t* p_file_in, uint32_t file_offset, off_t file_sz,
178 uint8_t flags );
179
184 struct MFMT_STRUCT* header, uint8_t SEG_FAR* px, off_t px_sz,
185 mfile_t* p_file_in, uint32_t file_offset, off_t file_sz,
186 uint8_t flags );
187
188#ifdef MFMT_C
189
191 mfile_t* p_file_in, off_t file_offset, off_t file_sz, size_t line_w,
192 MAUG_MHANDLE buffer_out_h, off_t buffer_out_sz, uint8_t flags
193) {
194 MERROR_RETVAL retval = MERROR_OK;
195 uint8_t* buffer_out = NULL;
196 off_t in_byte_cur = 0,
197 out_byte_cur = 0;
198 uint8_t out_mask_cur = 0xf0,
199 run_char = 0,
200 run_count = 0,
201 decode_state = 0,
202 unpadded_written = 0,
203 line_px_written = 0,
204 lines_out = 0,
205 byte_buffer = 0;
206
207 #define MFMT_RLE_DECODE_RUN 0
208 #define MFMT_RLE_DECODE_CHAR 1
209 #define MFMT_RLE_DECODE_ABS_RIGHT 2
210 #define MFMT_RLE_DECODE_ABS_DOWN 3
211 #define MFMT_RLE_DECODE_ESC 4
212 #define MFMT_RLE_DECODE_LITERAL 5
213 #define MFMT_RLE_DECODE_LITERAL_PAD 6
214
215 assert( flags == MFMT_DECOMP_FLAG_4BIT );
216
217 #define mfmt_decode_rle_state( new_state ) \
218 debug_printf( MFMT_TRACE_RLE_LVL, "new state: %s", #new_state ); \
219 decode_state = new_state;
220
221 #define mfmt_decode_rle_check_eol() \
222 if( line_px_written >= line_w ) { \
223 debug_printf( MFMT_TRACE_RLE_LVL, \
224 "EOL: %u px written (between runs)", \
225 line_px_written ); \
226 mfmt_decode_rle_reset_line(); \
227 }
228
229 /* Flip between odd/even nibble as advanced. */
230 #define mfmt_decode_rle_advance_mask() \
231 out_mask_cur >>= 4; \
232 if( 0 == out_mask_cur ) { \
233 out_byte_cur++; \
234 if( out_byte_cur > buffer_out_sz ) { \
235 error_printf( \
236 "out byte " OFF_T_FMT " outside of " OFF_T_FMT \
237 " pixel buffer!", out_byte_cur, buffer_out_sz ); \
238 retval = MERROR_OVERFLOW; \
239 goto cleanup; \
240 } else if( out_byte_cur < buffer_out_sz ) { \
241 /* We're not at the end of the file yet! */ \
242 buffer_out[out_byte_cur] = 0; \
243 } \
244 out_mask_cur = 0xf0; \
245 }
246
247 #define mfmt_decode_rle_reset_line() \
248 if( line_w != line_px_written ) { \
249 error_printf( \
250 "line written pixels %u does not match line width " SIZE_T_FMT, \
251 line_px_written, line_w ); \
252 retval = MERROR_OVERFLOW; \
253 goto cleanup; \
254 } \
255 line_px_written = 0; \
256 out_mask_cur = 0xf0; \
257 lines_out++; \
258 debug_printf( MFMT_TRACE_RLE_LVL, "now on line: %u", lines_out );
259
260 #define mfmt_decode_rle_inc_line_w( incr ) \
261 line_px_written += incr; \
262 if( line_w < line_px_written ) { \
263 error_printf( \
264 "line byte %u outside of " SIZE_T_FMT \
265 " line width!", line_px_written, line_w ); \
266 retval = MERROR_OVERFLOW; \
267 goto cleanup; \
268 }
269
270 debug_printf( 1, "decompressing RLE into temporary buffer..." );
271
272 maug_mlock( buffer_out_h, buffer_out );
273
274 maug_mzero( buffer_out, buffer_out_sz );
275
276 do {
277 retval = p_file_in->seek( p_file_in, file_offset + in_byte_cur++ );
278 maug_cleanup_if_not_ok();
279 retval = p_file_in->read_byte( p_file_in, &byte_buffer );
280 maug_cleanup_if_not_ok();
281
282 /*
283 mfile_cread_at(
284 p_file_in, &(byte_buffer), file_offset + in_byte_cur++ );
285 */
286 debug_printf( MFMT_TRACE_RLE_LVL, "in byte " OFF_T_FMT
287 ": 0x%02x, out byte " OFF_T_FMT ", line px: %u",
288 in_byte_cur, byte_buffer, out_byte_cur, line_px_written );
289
290 switch( byte_buffer ) {
291 case 0:
292 if( MFMT_RLE_DECODE_RUN == decode_state ) {
293 mfmt_decode_rle_state( MFMT_RLE_DECODE_ESC );
294 break;
295
296 } else if( MFMT_RLE_DECODE_LITERAL_PAD == decode_state ) {
297 /* This is just a padding byte to make sure literals are %16. */
298 assert( 0 == byte_buffer );
299 mfmt_decode_rle_state( MFMT_RLE_DECODE_RUN );
300 break;
301
302 } else if( MFMT_RLE_DECODE_ESC == decode_state ) {
303 /* This is an EOL marker. */
304 debug_printf( MFMT_TRACE_RLE_LVL,
305 "EOL: %u px written", line_px_written );
306 while( line_px_written < line_w ) {
307 /* Pad out the end of the line. */
308 assert( 0 == line_px_written % 2 );
309 buffer_out[out_byte_cur++] = 0x00;
310 mfmt_decode_rle_inc_line_w( 2 );
311 debug_printf( MFMT_TRACE_RLE_LVL,
312 "padded line (%u written)", line_px_written );
313 }
314 mfmt_decode_rle_reset_line();
315
316 /* Diversion over, go back to hunting for runs. */
317 mfmt_decode_rle_state( MFMT_RLE_DECODE_RUN );
318 break;
319 }
320
321 case 1:
322 if( MFMT_RLE_DECODE_ESC == decode_state ) {
323 debug_printf( MFMT_TRACE_RLE_LVL, "EOBM" );
324 /* End of bitmap, so pad the rest of the file. */
325
326 while( out_byte_cur < buffer_out_sz ) {
327 /* Pad out the end of the line. */
328 assert( 0 == line_px_written % 2 );
329 mfmt_decode_rle_check_eol();
330 buffer_out[out_byte_cur++] = 0x00;
331 mfmt_decode_rle_inc_line_w( 2 );
332 debug_printf( MFMT_TRACE_RLE_LVL,
333 "padded file (%u written)", line_px_written );
334 }
335
336 mfmt_decode_rle_state( MFMT_RLE_DECODE_RUN );
337 break;
338 }
339
340 case 2:
341 if( MFMT_RLE_DECODE_ESC == decode_state ) {
342 debug_printf( MFMT_TRACE_RLE_LVL, "absolute mode: right" );
343 /* TODO: Absolute mode. */
344 assert( 1 == 0 );
345 mfmt_decode_rle_state( MFMT_RLE_DECODE_ABS_RIGHT );
346 break;
347 }
348
349 default:
350 switch( decode_state ) {
351 case MFMT_RLE_DECODE_LITERAL:
352
353 mfmt_decode_rle_check_eol();
354
355 run_count -= 2;
356 unpadded_written += 2;
357 mfmt_decode_rle_inc_line_w( 2 );
358 debug_printf( MFMT_TRACE_RLE_LVL,
359 "writing literal: 0x%02x (%u left, unpadded run val: %u)",
360 byte_buffer, run_count, unpadded_written );
361 buffer_out[out_byte_cur++] = byte_buffer;
362
363 if( 0 == run_count ) {
364 if( 0 != unpadded_written % 4 ) {
365 /* Uneven number of literals copied. */
366 /* Ignore the byte, as it's a pad to word-size/16-bits. */
367 debug_printf( MFMT_TRACE_RLE_LVL,
368 "unpadded: %u, next is pad byte", unpadded_written );
369 /* assert( 0 == byte_buffer ); */
370 mfmt_decode_rle_state( MFMT_RLE_DECODE_LITERAL_PAD );
371 } else {
372
373 /* Diversion over, go back to hunting for runs. */
374 mfmt_decode_rle_state( MFMT_RLE_DECODE_RUN );
375 }
376 }
377 break;
378
379 case MFMT_RLE_DECODE_ESC:
380 run_count = byte_buffer;
381 unpadded_written = 0;
382 debug_printf( MFMT_TRACE_RLE_LVL,
383 "literal mode: %u nibbles", run_count );
384 assert( 0 == run_count % 2 );
385 mfmt_decode_rle_state( MFMT_RLE_DECODE_LITERAL );
386 break;
387
388 case MFMT_RLE_DECODE_ABS_RIGHT:
389 debug_printf( MFMT_TRACE_RLE_LVL, "absolute mode: up" );
390 /* TODO: Absolute mode. */
391 assert( 1 == 0 );
392 mfmt_decode_rle_state( MFMT_RLE_DECODE_ABS_DOWN );
393 break;
394
395 case MFMT_RLE_DECODE_RUN:
396
397 mfmt_decode_rle_check_eol();
398
399 run_count = byte_buffer;
400 debug_printf( MFMT_TRACE_RLE_LVL,
401 "starting run: %u nibbles", run_count );
402 mfmt_decode_rle_state( MFMT_RLE_DECODE_CHAR );
403 break;
404
405 case MFMT_RLE_DECODE_CHAR:
406 assert( 0 != run_count );
407 run_char = byte_buffer;
408 debug_printf( MFMT_TRACE_RLE_LVL,
409 "%u-long run of 0x%02x...", run_count, run_char );
410 do {
411 /* Expand the run into the prescribed number of nibbles. */
412 debug_printf( MFMT_TRACE_RLE_LVL,
413 "writing 0x%02x & 0x%02x #%u (line px #%u)...",
414 run_char, out_mask_cur, run_count, line_px_written );
415 buffer_out[out_byte_cur] |= (run_char & out_mask_cur);
416 mfmt_decode_rle_advance_mask();
417 mfmt_decode_rle_inc_line_w( 1 );
418 run_count--;
419
420 } while( 0 < run_count );
421
422 /* Diversion over, go back to hunting for runs. */
423 mfmt_decode_rle_state( MFMT_RLE_DECODE_RUN );
424 break;
425
426 }
427 break;
428 }
429 } while( in_byte_cur < file_sz );
430
431 debug_printf(
432 MFMT_TRACE_RLE_LVL, "wrote " OFF_T_FMT " bytes (%u lines)",
433 out_byte_cur, lines_out );
434
435cleanup:
436
437 if( NULL != buffer_out ) {
438 maug_munlock( buffer_out_h, buffer_out );
439 }
440
441 return retval;
442}
443
444MERROR_RETVAL mfmt_read_bmp_header(
445 struct MFMT_STRUCT* header, mfile_t* p_file_in,
446 uint32_t file_offset, off_t file_sz, uint8_t* p_flags
447) {
448 MERROR_RETVAL retval = MERROR_OK;
449 struct MFMT_STRUCT_BMPINFO* header_bmp_info = NULL;
450 struct MFMT_STRUCT_BMPFILE* header_bmp_file = NULL;
451 uint32_t file_hdr_sz = 0;
452 off_t header_offset = 0;
453
454 mfmt_bmp_check_header();
455
456 if( NULL != header_bmp_file ) {
457 header_offset = 14; /* Size of info header. */
458
459 /* Grab file header info. */
460 retval = p_file_in->seek( p_file_in, file_offset + 2 );
461 maug_cleanup_if_not_ok();
462 retval = p_file_in->read_int( p_file_in,
463 (uint8_t*)&(header_bmp_file->file_sz), 4, MFILE_READ_FLAG_LSBF );
464 maug_cleanup_if_not_ok();
465
466 retval = p_file_in->seek( p_file_in, file_offset + 10 );
467 maug_cleanup_if_not_ok();
468 retval = p_file_in->read_int( p_file_in,
469 (uint8_t*)&(header_bmp_file->px_offset), 4, MFILE_READ_FLAG_LSBF );
470 maug_cleanup_if_not_ok();
471
472 debug_printf( MFMT_TRACE_BMP_LVL,
473 "bitmap file " U32_FMT " bytes long, px at "
474 U32_FMT " bytes",
475 header_bmp_file->file_sz,
476 header_bmp_file->px_offset );
477 }
478
479 /* Read the bitmap image header. */
480 retval = p_file_in->seek( p_file_in, file_offset + header_offset );
481 maug_cleanup_if_not_ok();
482 retval = p_file_in->read_int( p_file_in,
483 (uint8_t*)&file_hdr_sz, 4, MFILE_READ_FLAG_LSBF );
484 maug_cleanup_if_not_ok();
485 if( 40 != file_hdr_sz ) { /* Windows BMP. */
486 error_printf( "invalid header size: " U32_FMT, file_hdr_sz );
487 retval = MERROR_FILE;
488 goto cleanup;
489 }
490 debug_printf(
491 MFMT_TRACE_BMP_LVL, "bitmap header is " U32_FMT " bytes",
492 file_hdr_sz );
493
494 if( 40 > file_sz - (file_offset + header_offset) ) {
495 error_printf(
496 "bitmap header overflow! (only " OFF_T_FMT " bytes remain!)",
497 file_sz - (file_offset + header_offset) );
498 retval = MERROR_OVERFLOW;
499 goto cleanup;
500 }
501
502 /* Read bitmap image dimensions. */
503 retval = p_file_in->seek( p_file_in,
504 file_offset + header_offset + MFMT_BMPINFO_OFS_WIDTH );
505 maug_cleanup_if_not_ok();
506 retval = p_file_in->read_int( p_file_in,
507 (uint8_t*)&(header_bmp_info->width), 4, MFILE_READ_FLAG_LSBF );
508 maug_cleanup_if_not_ok();
509
510 retval = p_file_in->seek( p_file_in,
511 file_offset + header_offset + MFMT_BMPINFO_OFS_HEIGHT );
512 maug_cleanup_if_not_ok();
513 retval = p_file_in->read_int( p_file_in,
514 (uint8_t*)&(header_bmp_info->height), 4, MFILE_READ_FLAG_LSBF );
515 maug_cleanup_if_not_ok();
516
517 if( 0 > header_bmp_info->height ) {
518 debug_printf(
519 MFMT_TRACE_BMP_LVL, "bitmap Y coordinate is inverted..." );
520 *p_flags |= MFMT_PX_FLAG_INVERT_Y;
521 }
522
523 retval = p_file_in->seek( p_file_in,
524 file_offset + header_offset + MFMT_BMPINFO_OFS_SZ );
525 maug_cleanup_if_not_ok();
526 retval = p_file_in->read_int( p_file_in,
527 (uint8_t*)&(header_bmp_info->img_sz), 4, MFILE_READ_FLAG_LSBF );
528 maug_cleanup_if_not_ok();
529
530 /* Check that we're a palettized image. */
531 retval = p_file_in->seek( p_file_in,
532 file_offset + header_offset + MFMT_BMPINFO_OFS_BPP );
533 maug_cleanup_if_not_ok();
534 retval = p_file_in->read_int( p_file_in,
535 (uint8_t*)&(header_bmp_info->bpp), 2, MFILE_READ_FLAG_LSBF );
536 maug_cleanup_if_not_ok();
537
538 if( 8 < header_bmp_info->bpp ) {
539 error_printf( "invalid bitmap bpp: %u",
540 header_bmp_info->bpp );
541 retval = MERROR_FILE;
542 goto cleanup;
543 }
544
545 /* Make sure there's no weird compression. */
546 retval = p_file_in->seek( p_file_in,
547 file_offset + header_offset + MFMT_BMPINFO_OFS_COMPRESSION );
548 maug_cleanup_if_not_ok();
549 retval = p_file_in->read_int( p_file_in,
550 (uint8_t*)&(header_bmp_info->compression), 4, MFILE_READ_FLAG_LSBF );
551 maug_cleanup_if_not_ok();
552
553 if(
555 header_bmp_info->compression &&
557 header_bmp_info->compression
558 ) {
559 error_printf( "invalid bitmap compression: " U32_FMT,
560 header_bmp_info->compression );
561 retval = MERROR_FILE;
562 goto cleanup;
563 }
564
565 /* Get the number of palette colors. */
566
567 retval = p_file_in->seek( p_file_in,
568 file_offset + header_offset + MFMT_BMPINFO_OFS_PAL_SZ );
569 maug_cleanup_if_not_ok();
570 retval = p_file_in->read_int( p_file_in,
571 (uint8_t*)&(header_bmp_info->palette_ncolors), 4, MFILE_READ_FLAG_LSBF );
572 maug_cleanup_if_not_ok();
573
574 debug_printf( 2, "bitmap is " S32_FMT " x " S32_FMT
575 ", %u bpp (palette has " U32_FMT " colors)",
576 header_bmp_info->width,
577 header_bmp_info->height,
578 header_bmp_info->bpp,
579 header_bmp_info->palette_ncolors );
580
581cleanup:
582
583 return retval;
584}
585
586MERROR_RETVAL mfmt_read_bmp_palette(
587 struct MFMT_STRUCT* header, uint32_t* palette, size_t palette_sz,
588 mfile_t* p_file_in, uint32_t file_offset, off_t file_sz, uint8_t flags
589) {
590 MERROR_RETVAL retval = MERROR_OK;
591 struct MFMT_STRUCT_BMPINFO* header_bmp_info = NULL;
592 struct MFMT_STRUCT_BMPFILE* header_bmp_file = NULL;
593 off_t i = 0;
594
595 mfmt_bmp_check_header();
596
597 retval = p_file_in->seek( p_file_in, file_offset );
598 maug_cleanup_if_not_ok();
599 for( i = 0 ; header_bmp_info->palette_ncolors > i ; i++ ) {
600 if( i * 4 > palette_sz ) {
601 error_printf( "palette overflow!" );
602 retval = MERROR_OVERFLOW;
603 goto cleanup;
604 }
605
606 retval = p_file_in->read_int( p_file_in,
607 (uint8_t*)&(palette[i]), 4, MFILE_READ_FLAG_LSBF );
608 maug_cleanup_if_not_ok();
609
610 debug_printf( MFMT_TRACE_BMP_LVL,
611 "set palette entry " OFF_T_FMT " to " X32_FMT,
612 i, palette[i] );
613 }
614
615cleanup:
616
617 return retval;
618}
619
621 struct MFMT_STRUCT* header, uint8_t SEG_FAR* px, off_t px_sz,
622 mfile_t* p_file_in, uint32_t file_offset, off_t file_sz, uint8_t flags
623) {
624 MERROR_RETVAL retval = MERROR_OK;
625 struct MFMT_STRUCT_BMPINFO* header_bmp_info = NULL;
626 struct MFMT_STRUCT_BMPFILE* header_bmp_file = NULL;
627 int32_t x = 0,
628 y = 0,
629 width_mod_4 = 0;
630 off_t i = 0,
631 byte_in_idx = 0,
632 byte_out_idx = 0,
633 bit_idx = 0;
634 uint8_t byte_buffer = 0,
635 byte_mask = 0,
636 pixel_buffer = 0;
637 MAUG_MHANDLE decomp_buffer_h = (MAUG_MHANDLE)NULL;
638 mfile_t file_decomp;
639 mfile_t *p_file_bmp = p_file_in;
640
641 /* Check header for validation and info on how to decode pixels. */
642
643 mfmt_bmp_check_header();
644
645 if( 0 == header_bmp_info->height ) {
646 error_printf( "bitmap height is 0!" );
647 retval = MERROR_FILE;
648 goto cleanup;
649 }
650
651 if( 0 == header_bmp_info->width ) {
652 error_printf( "bitmap width is 0!" );
653 retval = MERROR_FILE;
654 goto cleanup;
655 }
656
657 if( 0 == header_bmp_info->bpp ) {
658 error_printf( "bitmap BPP is 0!" );
659 retval = MERROR_FILE;
660 goto cleanup;
661 }
662
663 if( 8 < header_bmp_info->bpp ) {
664 error_printf( ">8BPP bitmaps not supported!" );
665 retval = MERROR_FILE;
666 goto cleanup;
667 }
668
669 maug_mzero( &file_decomp, sizeof( mfile_t ) );
670 if(
671 MFMT_BMP_COMPRESSION_RLE4 == header_bmp_info->compression
672 ) {
673 debug_printf( 1, "allocating decompression buffer..." );
674
675 /* Create a temporary memory buffer and decompress into it. */
676 decomp_buffer_h = maug_malloc(
677 header_bmp_info->width,
678 header_bmp_info->height );
679 maug_cleanup_if_null_alloc( MAUG_MHANDLE, decomp_buffer_h );
680
681 retval = mfmt_decode_rle(
682 p_file_in, file_offset, header_bmp_info->img_sz,
683 header_bmp_info->width,
684 decomp_buffer_h,
685 header_bmp_info->width *
686 header_bmp_info->height,
687 MFMT_DECOMP_FLAG_4BIT );
688 maug_cleanup_if_not_ok();
689
690 retval = mfile_lock_buffer(
691 decomp_buffer_h,
692 header_bmp_info->width *
693 header_bmp_info->height,
694 &file_decomp );
695 maug_cleanup_if_not_ok();
696
697 /* Switch out the file used below for the decomp buffer mfile_t. */
698 p_file_bmp = &file_decomp;
699 }
700
701 /* TODO: Handle padding for non-conforming images. */
702 width_mod_4 = header_bmp_info->width % 4;
703 maug_cleanup_if_ne( (int32_t)0, width_mod_4, S32_FMT, MERROR_GUI );
704
705 #define mfmt_read_bmp_px_out_idx() \
706 (MFMT_PX_FLAG_INVERT_Y == (MFMT_PX_FLAG_INVERT_Y & flags) ? \
707 ((header_bmp_info->height - y - 1) * \
708 header_bmp_info->width) : \
709 ((y) * header_bmp_info->width))
710
711 y = header_bmp_info->height - 1;
712 byte_out_idx = mfmt_read_bmp_px_out_idx();
713 if( p_file_bmp == p_file_in ) {
714 /* Only seek to offset if we're using the original (not translated from
715 * RLE or something.
716 */
717 p_file_bmp->seek( p_file_bmp, file_offset );
718 }
719 while( 0 <= y ) {
720 /* Each iteration is a single, fresh pixel. */
721 pixel_buffer = 0;
722
723 debug_printf( MFMT_TRACE_BMP_LVL,
724 "byte in: " OFF_T_FMT " (" OFF_T_FMT
725 "), bit " OFF_T_FMT ", y: " U32_FMT
726 ", x: " U32_FMT "), byte out: " OFF_T_FMT,
727 byte_in_idx, file_sz, bit_idx, y, x, byte_out_idx );
728
729 /* Buffer bounds check. */
730 if( px_sz <= byte_out_idx ) {
731 error_printf(
732 "byte " OFF_T_FMT " outside of " OFF_T_FMT
733 " pixel buffer!", byte_out_idx, px_sz );
734 retval = MERROR_OVERFLOW;
735 goto cleanup;
736 }
737
738 /* Byte finished check. */
739 if( 0 == bit_idx ) {
740 if( byte_in_idx >= file_sz ) {
741 /* TODO: Figure out why ICO parser messes up size. */
742 error_printf(
743 "input bitmap has insufficient size " OFF_T_FMT " bytes)!",
744 file_sz );
745 /* retval = MERROR_OVERFLOW;
746 goto cleanup; */
747 }
748
749 /* Move on to a new byte. */
750 /* TODO: Bad cursor? */
751 retval = p_file_bmp->read_byte( p_file_bmp, &byte_buffer );
752 maug_cleanup_if_not_ok();
753 /*
754 mfile_cread( p_file_bmp, &(byte_buffer) );
755 */
756 byte_in_idx++;
757
758 /* Start at 8 bits from the right (0 from the left). */
759 bit_idx = 8;
760
761 /* Build a bitwise mask based on the bitmap's BPP. */
762 byte_mask = 0;
763 for( i = 0 ; header_bmp_info->bpp > i ; i++ ) {
764 byte_mask >>= 1;
765 byte_mask |= 0x80;
766 }
767 }
768
769 /* Use the byte mask to place the bits for this pixel in the
770 * pixel buffer.
771 */
772 pixel_buffer |= byte_buffer & byte_mask;
773
774 /* Shift the pixel buffer so the index lines up at the first bit. */
775 pixel_buffer >>=
776 /* Index starts from the right, so the current bits from the left
777 * minus 1 * bpp.
778 */
779 (bit_idx - header_bmp_info->bpp);
780 debug_printf( MFMT_TRACE_BMP_LVL,
781 "byte_mask: 0x%02x, bit_idx: " OFF_T_FMT
782 ", pixel_buffer: 0x%02x",
783 byte_mask, bit_idx, pixel_buffer );
784
785 /* Place the pixel buffer at the X/Y in the grid. */
786 debug_printf( MFMT_TRACE_BMP_LVL,
787 "writing byte " OFF_T_FMT " (x: " S32_FMT
788 ", y: " S32_FMT ")",
789 byte_out_idx, x, y );
790 px[byte_out_idx] = pixel_buffer;
791 byte_out_idx++;
792
793 /* Increment the bits position byte mask by the bpp so it's pointing
794 * to the next pixel in the bitmap for the next go around.
795 */
796 byte_mask >>= header_bmp_info->bpp;
797 bit_idx -= header_bmp_info->bpp;
798
799 /* Move to the next pixel. */
800 x++;
801 if( x >= header_bmp_info->width ) {
802 /* Move to the next row of the input. */
803 y--;
804 x = 0;
805 while( byte_in_idx % 4 != 0 ) {
806 byte_in_idx++;
807 p_file_bmp->seek( p_file_bmp, file_offset + byte_in_idx );
808 }
809
810 /* Move to the next row of the output. */
811 byte_out_idx = mfmt_read_bmp_px_out_idx();
812
813 /* TODO Get past the padding. */
814
815 debug_printf( MFMT_TRACE_BMP_LVL,
816 "new row starting at byte_out_idx: " OFF_T_FMT,
817 byte_out_idx );
818 }
819 }
820
821cleanup:
822
823 mfile_close( &file_decomp );
824 /* decomp_buffer_h = file_decomp.h.mem; */
825
826 if( NULL != decomp_buffer_h ) {
827 debug_printf( 1, "freeing decomp buffer %p...", decomp_buffer_h );
828 maug_mfree( decomp_buffer_h );
829 }
830
831 return retval;
832}
833
834#endif /* MFMT_C */
835 /* maug_fmt */
837
838#endif /* !MFMT_H */
839
int MERROR_RETVAL
Return type indicating function returns a value from this list.
Definition merror.h:19
#define MFMT_BMP_COMPRESSION_NONE
MFMT_STRUCT_BMPINFO::compression value indicating none.
Definition mfmt.h:34
#define MFMT_BMP_COMPRESSION_RLE4
MFMT_STRUCT_BMPINFO::compression value indicating 4-bit RLE.
Definition mfmt.h:38
MERROR_RETVAL mfmt_read_bmp_px(struct MFMT_STRUCT *header, uint8_t SEG_FAR *px, off_t px_sz, mfile_t *p_file_in, uint32_t file_offset, off_t file_sz, uint8_t flags)
Read mfmt_bitmap pixels into an 8-bit memory bitmap.
MERROR_RETVAL(* mfmt_read_header_cb)(struct MFMT_STRUCT *header, mfile_t *p_file_in, uint32_t file_offset, off_t file_sz, uint8_t *p_flags)
Callback to read image header and get properties.
Definition mfmt.h:144
MERROR_RETVAL mfmt_decode_rle(mfile_t *p_file_in, off_t file_offset, off_t file_sz, size_t line_w, MAUG_MHANDLE buffer_out, off_t buffer_out_sz, uint8_t flags)
Decode RLE-encoded data from an input file into a memory buffer.
MERROR_RETVAL(* mfmt_read_px_cb)(struct MFMT_STRUCT *header, uint8_t SEG_FAR *px, off_t px_sz, mfile_t *p_file_in, uint32_t file_offset, off_t file_sz, uint8_t flags)
Callback to read image pixels into 8-bit values.
Definition mfmt.h:159
MERROR_RETVAL(* mfmt_read_palette_cb)(struct MFMT_STRUCT *header, uint32_t *palette, size_t palette_sz, mfile_t *p_file_in, uint32_t file_offset, off_t file_sz, uint8_t flags)
Callback to read image palette into 24-bit RGB values.
Definition mfmt.h:151
MERROR_RETVAL(* mfmt_decode)(mfile_t *p_file_in, off_t file_offset, off_t file_sz, size_t line_w, MAUG_MHANDLE buffer_out, off_t buffer_out_sz, uint8_t flags)
Callback to decode compressed data.
Definition mfmt.h:137
#define maug_mzero(ptr, sz)
Zero the block of memory pointed to by ptr.
Definition mmem.h:65
#define MFILE_READ_FLAG_LSBF
Flag for mfile_read_int_t() indicating integer should always be read least significant byte first.
Definition mfile.h:66
void mfile_close(mfile_t *p_file)
Close a file opened with mfile_open_read().
MERROR_RETVAL mfile_lock_buffer(MAUG_MHANDLE, off_t, mfile_t *p_file)
Lock a buffer and assign it to an mfile_t to read/write.
Definition mfmt.h:117
BITMAPINFO struct that comes before Windows bitmap data.
Definition mfmt.h:92
uint32_t compression
Type of compression used.
Definition mfmt.h:104
uint32_t vres
Vertical resolution in pixels per inch (unsupported).
Definition mfmt.h:110
uint16_t bpp
Number of bits per pixel (only =<8 are supported).
Definition mfmt.h:102
uint16_t color_planes
Number of color planes (only 0 or 1 are supported).
Definition mfmt.h:100
uint32_t sz
Size of this struct in bytes (only 40 is supported).
Definition mfmt.h:94
uint32_t imp_colors
Definition mfmt.h:114
uint32_t img_sz
Size of pixel data in bytes.
Definition mfmt.h:106
uint32_t hres
Horizontal resolution in pixels per inch (unsupported).
Definition mfmt.h:108
uint32_t palette_ncolors
Number of palette colors in this bitmap (<256 supported).
Definition mfmt.h:112
int32_t width
Width of the bitmap in pixels.
Definition mfmt.h:96
int32_t height
Height of the bitmap in pixels.
Definition mfmt.h:98
Generic image description struct.
Definition mfmt.h:62
uint32_t sz
Size of this struct (use to tell apart).
Definition mfmt.h:64