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