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
52#define MFMT_PX_FLAG_NEW_LINE 0x02
53
54#ifndef MFMT_TRACE_BMP_LVL
55# define MFMT_TRACE_BMP_LVL 0
56#endif /* !MFMT_TRACE_BMP_LVL */
57
58#ifndef MFMT_TRACE_RLE_LVL
59# define MFMT_TRACE_RLE_LVL 0
60#endif /* !MFMT_TRACE_RLE_LVL */
61
71 uint32_t sz;
72};
73
78
79#define mfmt_bmp_check_header() \
80 debug_printf( MFMT_TRACE_BMP_LVL, \
81 "bmp file sz: " U32_FMT ", magic: %c%c", \
82 header->sz, (((char*)header)[0]), (((char*)header)[1]) ); \
83 if( 40 == header->sz ) { \
84 debug_printf( MFMT_TRACE_BMP_LVL, "bmp info header detected by sz" ); \
85 header_bmp_info = (struct MFMT_STRUCT_BMPINFO*)header; \
86 } else if( \
87 'B' == (((char*)header)[0]) && 'M' == (((char*)header)[1]) \
88 ) { \
89 debug_printf( MFMT_TRACE_BMP_LVL, "bmp file header detected by sig" ); \
90 header_bmp_file = (struct MFMT_STRUCT_BMPFILE*)header; \
91 header_bmp_info = &(header_bmp_file->info); \
92 } else { \
93 error_printf( "unable to select read header!" ); \
94 retval = MERROR_FILE; \
95 goto cleanup; \
96 }
97
101 uint32_t sz;
103 int32_t width;
105 int32_t height;
107 uint16_t color_planes;
109 uint16_t bpp;
111 uint32_t compression;
113 uint32_t img_sz;
115 uint32_t hres;
117 uint32_t vres;
121 uint32_t imp_colors;
122};
123
125 char magic[2];
126 uint32_t file_sz;
127 uint16_t reserved1;
128 uint16_t reserved2;
129 uint32_t px_offset;
130 struct MFMT_STRUCT_BMPINFO info;
131};
132
138 off_t byte_idx;
139 uint8_t SEG_FAR* px;
140 off_t px_sz;
141};
142 /* maug_fmt_bmp */
144
155 mfile_t* p_file_in, off_t file_offset, off_t file_sz, size_t line_w,
156 MAUG_MHANDLE buffer_out, off_t buffer_out_sz, uint8_t flags );
157
162 struct MFMT_STRUCT* header, mfile_t* p_file_in,
163 uint32_t file_offset, off_t file_sz, uint8_t* p_flags );
164
169 struct MFMT_STRUCT* header, uint32_t* palette, size_t palette_sz,
170 mfile_t* p_file_in, uint32_t file_offset, off_t file_sz,
171 uint8_t flags );
172
177 struct MFMT_STRUCT* header, uint8_t SEG_FAR* px, off_t px_sz,
178 mfile_t* p_file_in, uint32_t file_offset, off_t file_sz,
179 uint8_t flags );
180
186 void* data, uint8_t px, int32_t x, int32_t y,
187 void* header_info, uint8_t flags );
188
193 mfile_t* p_file_in, off_t file_offset, off_t file_sz, size_t line_w,
194 MAUG_MHANDLE buffer_out, off_t buffer_out_sz, uint8_t flags );
195
196MERROR_RETVAL mfmt_read_bmp_header(
197 struct MFMT_STRUCT* header, mfile_t* p_file_in,
198 off_t file_offset, off_t file_sz, uint8_t* p_flags );
199
200MERROR_RETVAL mfmt_read_bmp_palette(
201 struct MFMT_STRUCT* header, uint32_t* palette, size_t palette_sz,
202 mfile_t* p_file_in, uint32_t file_offset, off_t file_sz,
203 uint8_t flags );
204
210 struct MFMT_STRUCT_BMPINFO* header_bmp_info, mfile_t* p_bmp_in,
211 size_t px_offset, MAUG_MHANDLE* p_px_h );
212
217 struct MFMT_STRUCT* header,
218 mfile_t* p_file_in, uint32_t px_offset, off_t file_sz, uint8_t flags,
219 mfmt_read_1px_cb px_cb, void* px_cb_data );
220
225 struct MFMT_STRUCT* header, uint8_t SEG_FAR* px, off_t px_sz,
226 mfile_t* p_file_in, uint32_t file_offset, off_t file_sz,
227 uint8_t flags );
228
229#ifdef MFMT_C
230
232 mfile_t* p_file_in, off_t file_offset, off_t file_sz, size_t line_w,
233 MAUG_MHANDLE buffer_out_h, off_t buffer_out_sz, uint8_t flags
234) {
235 MERROR_RETVAL retval = MERROR_OK;
236 uint8_t* buffer_out = NULL;
237 off_t in_byte_cur = 0,
238 out_byte_cur = 0;
239 uint8_t out_mask_cur = 0xf0,
240 run_char = 0,
241 run_count = 0,
242 decode_state = 0,
243 unpadded_written = 0,
244 line_px_written = 0,
245 lines_out = 0,
246 byte_buffer = 0;
247
248 #define MFMT_RLE_DECODE_RUN 0
249 #define MFMT_RLE_DECODE_CHAR 1
250 #define MFMT_RLE_DECODE_ABS_RIGHT 2
251 #define MFMT_RLE_DECODE_ABS_DOWN 3
252 #define MFMT_RLE_DECODE_ESC 4
253 #define MFMT_RLE_DECODE_LITERAL 5
254 #define MFMT_RLE_DECODE_LITERAL_PAD 6
255
256 assert( flags == MFMT_DECOMP_FLAG_4BIT );
257
258 #define mfmt_decode_rle_state( new_state ) \
259 debug_printf( MFMT_TRACE_RLE_LVL, "new state: %s", #new_state ); \
260 decode_state = new_state;
261
262 #define mfmt_decode_rle_check_eol() \
263 if( line_px_written >= line_w ) { \
264 debug_printf( MFMT_TRACE_RLE_LVL, \
265 "EOL: %u px written (between runs)", \
266 line_px_written ); \
267 mfmt_decode_rle_reset_line(); \
268 }
269
270 /* Flip between odd/even nibble as advanced. */
271 #define mfmt_decode_rle_advance_mask() \
272 out_mask_cur >>= 4; \
273 if( 0 == out_mask_cur ) { \
274 out_byte_cur++; \
275 if( out_byte_cur > buffer_out_sz ) { \
276 error_printf( \
277 "out byte " OFF_T_FMT " outside of " OFF_T_FMT \
278 " pixel buffer!", out_byte_cur, buffer_out_sz ); \
279 retval = MERROR_OVERFLOW; \
280 goto cleanup; \
281 } else if( out_byte_cur < buffer_out_sz ) { \
282 /* We're not at the end of the file yet! */ \
283 buffer_out[out_byte_cur] = 0; \
284 } \
285 out_mask_cur = 0xf0; \
286 }
287
288 #define mfmt_decode_rle_reset_line() \
289 if( line_w != line_px_written ) { \
290 error_printf( \
291 "line written pixels %u does not match line width " SIZE_T_FMT, \
292 line_px_written, line_w ); \
293 retval = MERROR_OVERFLOW; \
294 goto cleanup; \
295 } \
296 line_px_written = 0; \
297 out_mask_cur = 0xf0; \
298 lines_out++; \
299 debug_printf( MFMT_TRACE_RLE_LVL, "now on line: %u", lines_out );
300
301 #define mfmt_decode_rle_inc_line_w( incr ) \
302 line_px_written += incr; \
303 if( line_w < line_px_written ) { \
304 error_printf( \
305 "line byte %u outside of " SIZE_T_FMT \
306 " line width!", line_px_written, line_w ); \
307 retval = MERROR_OVERFLOW; \
308 goto cleanup; \
309 }
310
311#if MFMT_TRACE_RLE_LVL > 0
312 debug_printf( MFMT_TRACE_RLE_LVL,
313 "decompressing RLE into temporary buffer..." );
314#endif /* MFMT_TRACE_RLE_LVL */
315
316 maug_mlock( buffer_out_h, buffer_out );
317
318 maug_mzero( buffer_out, buffer_out_sz );
319
320 do {
321 retval = p_file_in->seek( p_file_in, file_offset + in_byte_cur++ );
322 maug_cleanup_if_not_ok();
323 retval = p_file_in->read_byte( p_file_in, &byte_buffer );
324 maug_cleanup_if_not_ok();
325
326 debug_printf( MFMT_TRACE_RLE_LVL, "in byte " OFF_T_FMT
327 ": 0x%02x, out byte " OFF_T_FMT ", line px: %u",
328 in_byte_cur, byte_buffer, out_byte_cur, line_px_written );
329
330 switch( byte_buffer ) {
331 case 0:
332 if( MFMT_RLE_DECODE_RUN == decode_state ) {
333 mfmt_decode_rle_state( MFMT_RLE_DECODE_ESC );
334 break;
335
336 } else if( MFMT_RLE_DECODE_LITERAL_PAD == decode_state ) {
337 /* This is just a padding byte to make sure literals are %16. */
338 assert( 0 == byte_buffer );
339 mfmt_decode_rle_state( MFMT_RLE_DECODE_RUN );
340 break;
341
342 } else if( MFMT_RLE_DECODE_ESC == decode_state ) {
343 /* This is an EOL marker. */
344 debug_printf( MFMT_TRACE_RLE_LVL,
345 "EOL: %u px written", line_px_written );
346 while( line_px_written < line_w ) {
347 /* Pad out the end of the line. */
348 assert( 0 == line_px_written % 2 );
349 buffer_out[out_byte_cur++] = 0x00;
350 mfmt_decode_rle_inc_line_w( 2 );
351 debug_printf( MFMT_TRACE_RLE_LVL,
352 "padded line (%u written)", line_px_written );
353 }
354 mfmt_decode_rle_reset_line();
355
356 /* Diversion over, go back to hunting for runs. */
357 mfmt_decode_rle_state( MFMT_RLE_DECODE_RUN );
358 break;
359 }
360
361 case 1:
362 if( MFMT_RLE_DECODE_ESC == decode_state ) {
363 debug_printf( MFMT_TRACE_RLE_LVL, "EOBM" );
364 /* End of bitmap, so pad the rest of the file. */
365
366 while( out_byte_cur < buffer_out_sz ) {
367 /* Pad out the end of the line. */
368 assert( 0 == line_px_written % 2 );
369 mfmt_decode_rle_check_eol();
370 buffer_out[out_byte_cur++] = 0x00;
371 mfmt_decode_rle_inc_line_w( 2 );
372 debug_printf( MFMT_TRACE_RLE_LVL,
373 "padded file (%u written)", line_px_written );
374 }
375
376 mfmt_decode_rle_state( MFMT_RLE_DECODE_RUN );
377 break;
378 }
379
380 case 2:
381 if( MFMT_RLE_DECODE_ESC == decode_state ) {
382 debug_printf( MFMT_TRACE_RLE_LVL, "absolute mode: right" );
383 /* TODO: Absolute mode. */
384 assert( 1 == 0 );
385 mfmt_decode_rle_state( MFMT_RLE_DECODE_ABS_RIGHT );
386 break;
387 }
388
389 default:
390 switch( decode_state ) {
391 case MFMT_RLE_DECODE_LITERAL:
392
393 mfmt_decode_rle_check_eol();
394
395 run_count -= 2;
396 unpadded_written += 2;
397 mfmt_decode_rle_inc_line_w( 2 );
398 debug_printf( MFMT_TRACE_RLE_LVL,
399 "writing literal: 0x%02x (%u left, unpadded run val: %u)",
400 byte_buffer, run_count, unpadded_written );
401 buffer_out[out_byte_cur++] = byte_buffer;
402
403 if( 0 == run_count ) {
404 if( 0 != unpadded_written % 4 ) {
405 /* Uneven number of literals copied. */
406 /* Ignore the byte, as it's a pad to word-size/16-bits. */
407 debug_printf( MFMT_TRACE_RLE_LVL,
408 "unpadded: %u, next is pad byte", unpadded_written );
409 /* assert( 0 == byte_buffer ); */
410 mfmt_decode_rle_state( MFMT_RLE_DECODE_LITERAL_PAD );
411 } else {
412
413 /* Diversion over, go back to hunting for runs. */
414 mfmt_decode_rle_state( MFMT_RLE_DECODE_RUN );
415 }
416 }
417 break;
418
419 case MFMT_RLE_DECODE_ESC:
420 run_count = byte_buffer;
421 unpadded_written = 0;
422 debug_printf( MFMT_TRACE_RLE_LVL,
423 "literal mode: %u nibbles", run_count );
424 assert( 0 == run_count % 2 );
425 mfmt_decode_rle_state( MFMT_RLE_DECODE_LITERAL );
426 break;
427
428 case MFMT_RLE_DECODE_ABS_RIGHT:
429 debug_printf( MFMT_TRACE_RLE_LVL, "absolute mode: up" );
430 /* TODO: Absolute mode. */
431 assert( 1 == 0 );
432 mfmt_decode_rle_state( MFMT_RLE_DECODE_ABS_DOWN );
433 break;
434
435 case MFMT_RLE_DECODE_RUN:
436
437 mfmt_decode_rle_check_eol();
438
439 run_count = byte_buffer;
440 debug_printf( MFMT_TRACE_RLE_LVL,
441 "starting run: %u nibbles", run_count );
442 mfmt_decode_rle_state( MFMT_RLE_DECODE_CHAR );
443 break;
444
445 case MFMT_RLE_DECODE_CHAR:
446 assert( 0 != run_count );
447 run_char = byte_buffer;
448 debug_printf( MFMT_TRACE_RLE_LVL,
449 "%u-long run of 0x%02x...", run_count, run_char );
450 do {
451 /* Expand the run into the prescribed number of nibbles. */
452 debug_printf( MFMT_TRACE_RLE_LVL,
453 "writing 0x%02x & 0x%02x #%u (line px #%u)...",
454 run_char, out_mask_cur, run_count, line_px_written );
455 buffer_out[out_byte_cur] |= (run_char & out_mask_cur);
456 mfmt_decode_rle_advance_mask();
457 mfmt_decode_rle_inc_line_w( 1 );
458 run_count--;
459
460 } while( 0 < run_count );
461
462 /* Diversion over, go back to hunting for runs. */
463 mfmt_decode_rle_state( MFMT_RLE_DECODE_RUN );
464 break;
465
466 }
467 break;
468 }
469 } while( in_byte_cur < file_sz );
470
471 debug_printf(
472 MFMT_TRACE_RLE_LVL, "wrote " OFF_T_FMT " bytes (%u lines)",
473 out_byte_cur, lines_out );
474
475cleanup:
476
477 if( NULL != buffer_out ) {
478 maug_munlock( buffer_out_h, buffer_out );
479 }
480
481 return retval;
482}
483
484/* === */
485
486MERROR_RETVAL mfmt_read_bmp_header(
487 struct MFMT_STRUCT* header, mfile_t* p_file_in,
488 off_t file_offset, off_t file_sz, uint8_t* p_flags
489) {
490 MERROR_RETVAL retval = MERROR_OK;
491 struct MFMT_STRUCT_BMPINFO* header_bmp_info = NULL;
492 struct MFMT_STRUCT_BMPFILE* header_bmp_file = NULL;
493 uint32_t file_hdr_sz = 0;
494 off_t header_offset = 0;
495
496 mfmt_bmp_check_header();
497
498 if( NULL != header_bmp_file ) {
499 header_offset = 14; /* Size of info header. */
500
501 debug_printf( MFMT_TRACE_BMP_LVL,
502 "reading bitmap file header at " OFF_T_FMT " bytes...",
503 file_offset + 2 );
504
505 /* Grab file header info. */
506 retval = p_file_in->seek( p_file_in, file_offset + 2 );
507 maug_cleanup_if_not_ok();
508 retval = p_file_in->read_int( p_file_in,
509 (uint8_t*)&(header_bmp_file->file_sz), 4, MFILE_READ_FLAG_LSBF );
510 maug_cleanup_if_not_ok();
511
512 retval = p_file_in->seek( p_file_in, file_offset + 10 );
513 maug_cleanup_if_not_ok();
514 retval = p_file_in->read_int( p_file_in,
515 (uint8_t*)&(header_bmp_file->px_offset), 4, MFILE_READ_FLAG_LSBF );
516 maug_cleanup_if_not_ok();
517
518 debug_printf( MFMT_TRACE_BMP_LVL,
519 "bitmap file " U32_FMT " bytes long, px at "
520 U32_FMT " bytes",
521 header_bmp_file->file_sz,
522 header_bmp_file->px_offset );
523 }
524
525 /* Read the bitmap image header. */
526 retval = p_file_in->seek( p_file_in, file_offset + header_offset );
527 maug_cleanup_if_not_ok();
528 retval = p_file_in->read_int( p_file_in,
529 (uint8_t*)&file_hdr_sz, 4, MFILE_READ_FLAG_LSBF );
530 maug_cleanup_if_not_ok();
531 if( 40 != file_hdr_sz ) { /* Windows BMP. */
532 error_printf( "invalid header size: " U32_FMT, file_hdr_sz );
533 retval = MERROR_FILE;
534 goto cleanup;
535 }
536 debug_printf(
537 MFMT_TRACE_BMP_LVL, "bitmap header is " U32_FMT " bytes",
538 file_hdr_sz );
539
540 if( 40 > file_sz - (file_offset + header_offset) ) {
541 error_printf(
542 "bitmap header overflow! (only " OFF_T_FMT " bytes remain!)",
543 file_sz - (file_offset + header_offset) );
544 retval = MERROR_OVERFLOW;
545 goto cleanup;
546 }
547
548 /* Read bitmap image dimensions. */
549 retval = p_file_in->seek( p_file_in,
550 file_offset + header_offset + MFMT_BMPINFO_OFS_WIDTH );
551 maug_cleanup_if_not_ok();
552 retval = p_file_in->read_int( p_file_in,
553 (uint8_t*)&(header_bmp_info->width), 4, MFILE_READ_FLAG_LSBF );
554 maug_cleanup_if_not_ok();
555
556 retval = p_file_in->seek( p_file_in,
557 file_offset + header_offset + MFMT_BMPINFO_OFS_HEIGHT );
558 maug_cleanup_if_not_ok();
559 retval = p_file_in->read_int( p_file_in,
560 (uint8_t*)&(header_bmp_info->height), 4, MFILE_READ_FLAG_LSBF );
561 maug_cleanup_if_not_ok();
562
563 if( 0 > header_bmp_info->height ) {
564 debug_printf(
565 MFMT_TRACE_BMP_LVL, "bitmap Y coordinate is inverted..." );
566 *p_flags |= MFMT_PX_FLAG_INVERT_Y;
567 }
568
569 retval = p_file_in->seek( p_file_in,
570 file_offset + header_offset + MFMT_BMPINFO_OFS_SZ );
571 maug_cleanup_if_not_ok();
572 retval = p_file_in->read_int( p_file_in,
573 (uint8_t*)&(header_bmp_info->img_sz), 4, MFILE_READ_FLAG_LSBF );
574 maug_cleanup_if_not_ok();
575
576 /* Check that we're a palettized image. */
577 retval = p_file_in->seek( p_file_in,
578 file_offset + header_offset + MFMT_BMPINFO_OFS_BPP );
579 maug_cleanup_if_not_ok();
580 retval = p_file_in->read_int( p_file_in,
581 (uint8_t*)&(header_bmp_info->bpp), 2, MFILE_READ_FLAG_LSBF );
582 maug_cleanup_if_not_ok();
583
584 if( 8 < header_bmp_info->bpp ) {
585 error_printf( "invalid bitmap bpp: %u",
586 header_bmp_info->bpp );
587 retval = MERROR_FILE;
588 goto cleanup;
589 }
590
591 /* Make sure there's no weird compression. */
592 retval = p_file_in->seek( p_file_in,
593 file_offset + header_offset + MFMT_BMPINFO_OFS_COMPRESSION );
594 maug_cleanup_if_not_ok();
595 retval = p_file_in->read_int( p_file_in,
596 (uint8_t*)&(header_bmp_info->compression), 4, MFILE_READ_FLAG_LSBF );
597 maug_cleanup_if_not_ok();
598
599 if(
601 header_bmp_info->compression &&
603 header_bmp_info->compression
604 ) {
605 error_printf( "invalid bitmap compression: " U32_FMT,
606 header_bmp_info->compression );
607 retval = MERROR_FILE;
608 goto cleanup;
609 }
610
611 /* Get the number of palette colors. */
612
613 retval = p_file_in->seek( p_file_in,
614 file_offset + header_offset + MFMT_BMPINFO_OFS_PAL_SZ );
615 maug_cleanup_if_not_ok();
616 retval = p_file_in->read_int( p_file_in,
617 (uint8_t*)&(header_bmp_info->palette_ncolors), 4, MFILE_READ_FLAG_LSBF );
618 maug_cleanup_if_not_ok();
619
620 debug_printf( 2, "bitmap is " S32_FMT " x " S32_FMT
621 ", %u bpp (palette has " U32_FMT " colors)",
622 header_bmp_info->width,
623 header_bmp_info->height,
624 header_bmp_info->bpp,
625 header_bmp_info->palette_ncolors );
626
627cleanup:
628
629 return retval;
630}
631
632/* === */
633
634MERROR_RETVAL mfmt_read_bmp_palette(
635 struct MFMT_STRUCT* header, uint32_t* palette, size_t palette_sz,
636 mfile_t* p_file_in, uint32_t file_offset, off_t file_sz, uint8_t flags
637) {
638 MERROR_RETVAL retval = MERROR_OK;
639 struct MFMT_STRUCT_BMPINFO* header_bmp_info = NULL;
640 struct MFMT_STRUCT_BMPFILE* header_bmp_file = NULL;
641 off_t i = 0;
642
643 mfmt_bmp_check_header();
644
645 retval = p_file_in->seek( p_file_in, file_offset );
646 maug_cleanup_if_not_ok();
647 for( i = 0 ; header_bmp_info->palette_ncolors > i ; i++ ) {
648 if( i * 4 > palette_sz ) {
649 error_printf( "palette overflow!" );
650 retval = MERROR_OVERFLOW;
651 goto cleanup;
652 }
653
654 retval = p_file_in->read_int( p_file_in,
655 (uint8_t*)&(palette[i]), 4, MFILE_READ_FLAG_LSBF );
656 maug_cleanup_if_not_ok();
657
658 debug_printf( MFMT_TRACE_BMP_LVL,
659 "set palette entry " OFF_T_FMT " to " X32_FMT,
660 i, palette[i] );
661 }
662
663cleanup:
664
665 return retval;
666}
667
668/* === */
669
671 struct MFMT_STRUCT_BMPINFO* header_bmp_info, mfile_t* p_bmp_in,
672 size_t px_offset, MAUG_MHANDLE* p_px_h
673) {
674 MERROR_RETVAL retval = MERROR_OK;
675
676 /* Check header for validation and info on how to decode pixels. */
677
678 if( 0 == header_bmp_info->height ) {
679 error_printf( "bitmap height is 0!" );
680 retval = MERROR_FILE;
681 goto cleanup;
682 }
683
684 if( 0 == header_bmp_info->width ) {
685 error_printf( "bitmap width is 0!" );
686 retval = MERROR_FILE;
687 goto cleanup;
688 }
689
690 if( 0 == header_bmp_info->bpp ) {
691 error_printf( "bitmap BPP is 0!" );
692 retval = MERROR_FILE;
693 goto cleanup;
694 }
695
696 if( 8 < header_bmp_info->bpp ) {
697 error_printf( ">8BPP bitmaps not supported!" );
698 retval = MERROR_FILE;
699 goto cleanup;
700 }
701
702 if(
703 MFMT_BMP_COMPRESSION_RLE4 == header_bmp_info->compression
704 ) {
705 debug_printf( 1, "allocating decompression buffer..." );
706
707 /* Create a temporary memory buffer and decompress into it. */
708 *p_px_h = maug_malloc(
709 header_bmp_info->width,
710 header_bmp_info->height );
711 maug_cleanup_if_null_alloc( MAUG_MHANDLE, *p_px_h );
712
713 retval = mfmt_decode_rle(
714 p_bmp_in, px_offset, header_bmp_info->img_sz,
715 header_bmp_info->width,
716 *p_px_h,
717 header_bmp_info->width * header_bmp_info->height,
718 MFMT_DECOMP_FLAG_4BIT );
719 maug_cleanup_if_not_ok();
720 }
721
722cleanup:
723
724 return retval;
725}
726
727/* === */
728
730 struct MFMT_STRUCT* header,
731 mfile_t* p_file_in, uint32_t px_offset, off_t file_sz, uint8_t flags,
732 mfmt_read_1px_cb px_cb, void* px_cb_data
733) {
734 MERROR_RETVAL retval = MERROR_OK;
735 struct MFMT_STRUCT_BMPINFO* header_bmp_info = NULL;
736 struct MFMT_STRUCT_BMPFILE* header_bmp_file = NULL;
737 int32_t x = 0,
738 y = 0,
739 width_mod_4 = 0;
740 off_t i = 0,
741 byte_in_idx = 0,
742 bit_idx = 0;
743 uint8_t byte_buffer = 0,
744 byte_mask = 0,
745 pixel_buffer = 0;
746 MAUG_MHANDLE decomp_buffer_h = (MAUG_MHANDLE)NULL;
747 mfile_t file_decomp;
748 mfile_t *p_file_bmp = p_file_in;
749
750 mfmt_bmp_check_header();
751
752 maug_mzero( &file_decomp, sizeof( mfile_t ) );
753
754 retval = mfmt_get_px_ptr(
755 header_bmp_info, p_file_in, px_offset, &decomp_buffer_h );
756 maug_cleanup_if_not_ok();
757
758 if( (MAUG_MHANDLE)NULL != decomp_buffer_h ) {
759 retval = mfile_lock_buffer(
760 decomp_buffer_h, NULL,
761 header_bmp_info->width * header_bmp_info->height,
762 &file_decomp );
763 maug_cleanup_if_not_ok();
764
765 /* Switch out the file used below for the decomp buffer mfile_t. */
766 p_file_bmp = &file_decomp;
767 }
768
769 /* TODO: Handle padding for non-conforming images. */
770 width_mod_4 = header_bmp_info->width % 4;
771 maug_cleanup_if_ne( (int32_t)0, width_mod_4, S32_FMT, MERROR_GUI );
772
773 y = header_bmp_info->height - 1;
774 retval = px_cb(
775 px_cb_data, 0, x, y, header_bmp_info, flags | MFMT_PX_FLAG_NEW_LINE );
776 maug_cleanup_if_not_ok();
777 if( p_file_bmp == p_file_in ) {
778 /* Only seek to offset if we're using the original (not translated from
779 * RLE or something.
780 */
781 p_file_bmp->seek( p_file_bmp, px_offset );
782 }
783 while( 0 <= y ) {
784 /* Each iteration is a single, fresh pixel. */
785 pixel_buffer = 0;
786
787 debug_printf( MFMT_TRACE_BMP_LVL,
788 "byte in: " OFF_T_FMT " (" OFF_T_FMT
789 "), bit " OFF_T_FMT ", y: " U32_FMT ", x: " U32_FMT ")",
790 byte_in_idx, file_sz, bit_idx, y, x );
791
792 /* Byte finished check. */
793 if( 0 == bit_idx ) {
794 if( byte_in_idx >= file_sz ) {
795 /* TODO: Figure out why ICO parser messes up size. */
796 error_printf(
797 "input bitmap has insufficient size " OFF_T_FMT " bytes)!",
798 file_sz );
799 /* retval = MERROR_OVERFLOW;
800 goto cleanup; */
801 }
802
803 /* Move on to a new byte. */
804 /* TODO: Bad cursor? */
805 retval = p_file_bmp->read_byte( p_file_bmp, &byte_buffer );
806 maug_cleanup_if_not_ok();
807 byte_in_idx++;
808
809 /* Start at 8 bits from the right (0 from the left). */
810 bit_idx = 8;
811
812 /* Build a bitwise mask based on the bitmap's BPP. */
813 byte_mask = 0;
814 for( i = 0 ; header_bmp_info->bpp > i ; i++ ) {
815 byte_mask >>= 1;
816 byte_mask |= 0x80;
817 }
818 }
819
820 /* Use the byte mask to place the bits for this pixel in the
821 * pixel buffer.
822 */
823 pixel_buffer |= byte_buffer & byte_mask;
824
825 /* Shift the pixel buffer so the index lines up at the first bit. */
826 pixel_buffer >>=
827 /* Index starts from the right, so the current bits from the left
828 * minus 1 * bpp.
829 */
830 (bit_idx - header_bmp_info->bpp);
831 debug_printf( MFMT_TRACE_BMP_LVL,
832 "byte_mask: 0x%02x, bit_idx: " OFF_T_FMT
833 ", pixel_buffer: 0x%02x",
834 byte_mask, bit_idx, pixel_buffer );
835
836 /* Place the pixel buffer at the X/Y in the grid. */
837 retval = px_cb( px_cb_data, pixel_buffer, x, y, header_bmp_info, flags );
838 maug_cleanup_if_not_ok();
839
840 /* Increment the bits position byte mask by the bpp so it's pointing
841 * to the next pixel in the bitmap for the next go around.
842 */
843 byte_mask >>= header_bmp_info->bpp;
844 bit_idx -= header_bmp_info->bpp;
845
846 /* Move to the next pixel. */
847 x++;
848 if( x >= header_bmp_info->width ) {
849 /* Move to the next row of the input. */
850 y--;
851 x = 0;
852 while( byte_in_idx % 4 != 0 ) {
853 byte_in_idx++;
854 p_file_bmp->seek( p_file_bmp, px_offset + byte_in_idx );
855 }
856
857 /* Move to the next row of the output. */
858 retval = px_cb(
859 px_cb_data, 0, x, y, header_bmp_info,
860 flags | MFMT_PX_FLAG_NEW_LINE );
861 maug_cleanup_if_not_ok();
862
863 /* TODO Get past the padding. */
864
865 }
866 }
867
868cleanup:
869
870 mfile_close( &file_decomp );
871 /* decomp_buffer_h = file_decomp.h.mem; */
872
873 if( (MAUG_MHANDLE)NULL != decomp_buffer_h ) {
874 debug_printf( 1, "freeing decomp buffer %p...", decomp_buffer_h );
875 maug_mfree( decomp_buffer_h );
876 }
877
878 return retval;
879}
880
881/* === */
882
883static MERROR_RETVAL _mfmt_read_bmp_px_cb_px(
884 void* data, uint8_t px, int32_t x, int32_t y,
885 void* header_info, uint8_t flags
886) {
887 MERROR_RETVAL retval = MERROR_OK;
888 struct MFMT_PX_OUT_STAT* p_out = (struct MFMT_PX_OUT_STAT*)data;
889 struct MFMT_STRUCT_BMPINFO* header_bmp_info =
890 (struct MFMT_STRUCT_BMPINFO*)header_info;
891
892 /* Buffer bounds check. */
893 if( p_out->px_sz < p_out->byte_idx ) {
894 error_printf(
895 "byte " OFF_T_FMT " outside of " OFF_T_FMT
896 " pixel buffer!", p_out->byte_idx, p_out->px_sz );
897 retval = MERROR_OVERFLOW;
898 goto cleanup;
899 }
900
902 /* Set the output index to a new line. */
903 debug_printf( MFMT_TRACE_BMP_LVL,
904 "new row starting at byte_out_idx: " OFF_T_FMT,
905 p_out->byte_idx );
906 p_out->byte_idx =
907 (MFMT_PX_FLAG_INVERT_Y == (MFMT_PX_FLAG_INVERT_Y & flags) ?
908 ((header_bmp_info->height - y - 1) *
909 header_bmp_info->width) :
910 ((y) * header_bmp_info->width));
911
912 } else {
913 debug_printf( MFMT_TRACE_BMP_LVL,
914 "writing byte " OFF_T_FMT " (x: " S32_FMT
915 ", y: " S32_FMT ")",
916 p_out->byte_idx, x, y );
917 p_out->px[p_out->byte_idx] = px;
918 p_out->byte_idx++;
919 }
920
921cleanup:
922
923 return retval;
924}
925
926/* === */
927
929 struct MFMT_STRUCT* header, uint8_t SEG_FAR* px, off_t px_sz,
930 mfile_t* p_file_in, uint32_t px_offset, off_t file_sz, uint8_t flags
931) {
932 MERROR_RETVAL retval = MERROR_OK;
933 struct MFMT_PX_OUT_STAT out;
934
935 maug_mzero( &out, sizeof( struct MFMT_PX_OUT_STAT ) );
936 out.px = px;
937 out.px_sz = px_sz;
938
939 retval = mfmt_read_bmp_px_cb(
940 header, p_file_in, px_offset, file_sz, flags,
941 _mfmt_read_bmp_px_cb_px, &out );
942
943 return retval;
944}
945
946#endif /* MFMT_C */
947 /* maug_fmt */
949
950#endif /* !MFMT_H */
951
uint16_t 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_1px_cb)(void *data, uint8_t px, int32_t x, int32_t y, void *header_info, uint8_t flags)
Parameter for mfmt_read_px_cb() functions to process individual pixel data.
Definition mfmt.h:185
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_get_px_ptr(struct MFMT_STRUCT_BMPINFO *header_bmp_info, mfile_t *p_bmp_in, size_t px_offset, MAUG_MHANDLE *p_px_h)
Read mfmt_bitmap header and return a handle to the bitmap's decompressed pixel data if it is compress...
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:161
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:176
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:168
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:154
MERROR_RETVAL mfmt_read_bmp_px_cb(struct MFMT_STRUCT *header, mfile_t *p_file_in, uint32_t px_offset, off_t file_sz, uint8_t flags, mfmt_read_1px_cb px_cb, void *px_cb_data)
Read mfmt_bitmap pixels and process them using a callback.
#define MFMT_PX_FLAG_NEW_LINE
Flag for mfmt_read_bmp_px_t() indicating a new line has started.
Definition mfmt.h:52
MERROR_RETVAL mfile_lock_buffer(MAUG_MHANDLE, void *ptr, off_t, mfile_t *p_file)
Lock a buffer and assign it to an mfile_t to read/write.
#define MFILE_READ_FLAG_LSBF
Flag for mfile_read_int_t() indicating integer should always be read least significant byte first.
Definition mfile.h:92
void mfile_close(mfile_t *p_file)
Close a file opened with mfile_open_read().
Utility struct for mfmt_read_bmp_px() to keep track out of output bitmap info.
Definition mfmt.h:137
Definition mfmt.h:124
BITMAPINFO struct that comes before Windows bitmap data.
Definition mfmt.h:99
uint32_t compression
Type of compression used.
Definition mfmt.h:111
uint32_t vres
Vertical resolution in pixels per inch (unsupported).
Definition mfmt.h:117
uint16_t bpp
Number of bits per pixel (only =<8 are supported).
Definition mfmt.h:109
uint16_t color_planes
Number of color planes (only 0 or 1 are supported).
Definition mfmt.h:107
uint32_t sz
Size of this struct in bytes (only 40 is supported).
Definition mfmt.h:101
uint32_t imp_colors
Definition mfmt.h:121
uint32_t img_sz
Size of pixel data in bytes.
Definition mfmt.h:113
uint32_t hres
Horizontal resolution in pixels per inch (unsupported).
Definition mfmt.h:115
uint32_t palette_ncolors
Number of palette colors in this bitmap (<256 supported).
Definition mfmt.h:119
int32_t width
Width of the bitmap in pixels.
Definition mfmt.h:103
int32_t height
Height of the bitmap in pixels.
Definition mfmt.h:105
Generic image description struct.
Definition mfmt.h:69
uint32_t sz
Size of this struct (use to tell apart).
Definition mfmt.h:71