#define _POSIX_C_SOURCE 200809L #include /* assert */ #include /* memcmp */ #include /* write, read, lseek, close */ #include /* uint32_t, uint8_t */ #include /* open */ #include /* err, errx, warn */ /* Vaším úkolem je naprogramovat proceduru ‹togray›, která převede * obrázek ve formátu BMP z barevného na černobílý (do stupňů šedi). * * Procedura přijímá parametry: * * • ‹fd_in› – popisovač vstupu, * • ‹fd_out› – popisovač výstupu. * * Vstupní soubor bude používat formát, kdy jeden bajt = jeden pixel * (tedy 8 bitů na pixel) s použitím tzv. palety – hodnota pixelu se * použije jako index do pole čtyřbajtových hodnot, které udávají * barvu. Tato paleta je součástí hlavičky a začíná na 54. bajtu. * * Samotné pixely následují těsně za paletou. * * Barvy v paletě jsou uloženy ve formátu RGBA32, tzn. význam * jednotlivých bajtů je: červená, zelená, modrá, průhlednost. * * Pro konverzi použijte tyto váhy: * * • 0,299 pro červenou, * • 0,587 pro zelenou, * • 0,114 pro modrou. * * Hodnotu průhlednosti zachovejte. * * Návratová hodnota: * • 0 – úspěch, * • -1 – systémová chyba, * • -2 – chybný formát vstupu. * * Při testování může přijít vhod příkaz ‹od -t x1› na prohlížení * jednotlivých bajtů obrázku, a/nebo libovolný prohlížeč obrázků. */ int togray( int fd_in, int fd_out ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include static void write_buffer( int fd, const void *data, int nbytes ) { int written = write( fd, data, nbytes ); if ( written == -1 ) err( 2, "write" ); if ( written < nbytes ) errx( 2, "write couldn't write all data" ); } static void convert_be_le( unsigned char *buf, int bytes ) { assert( bytes <= 4 ); unsigned char tmp; for (int i = 0; i < bytes / 2; i++) { tmp = buf[ i ]; buf[ i ] = buf[ bytes - i - 1 ]; buf[ bytes - i - 1 ] = tmp; } } static void write_le( int fd, int bytes, uint32_t num ) { assert( bytes <= 4 ); char out[ 4 ]; for ( int i = 0; i < bytes; ++i ) out[ i ] = ( num >> ( i * 8 ) ) & 0xff; write_buffer( fd, out, bytes ); } static void write_header( int fd, uint32_t w, uint32_t h ) { const uint32_t bmap_size = w * h * 3 + ( w % 4 ) * h; const uint32_t header_size = 54 + 256 * 4; const uint32_t total = header_size + bmap_size; /* file header */ write_buffer( fd, "BM", 2 ); write_le( fd, 4, total ); write_le( fd, 4, 0 ); write_le( fd, 4, header_size ); /* DIB header */ write_le( fd, 4, 0x28 ); /* size of the DIB header */ write_le( fd, 4, w ); write_le( fd, 4, h ); write_le( fd, 2, 1 ); /* 1 layer */ write_le( fd, 2, 8 ); /* 8 bits per pixel */ write_le( fd, 4, 0 ); write_le( fd, 4, bmap_size ); write_le( fd, 4, 315 ); /* horizontal DPI */ write_le( fd, 4, 315 ); /* vertical DPI */ write_le( fd, 4, 0 ); write_le( fd, 4, 0 ); } static void write_palette( int fd, const unsigned char *pal, size_t size ) { uint8_t palette[ 256 * 4 ] = { 0 }; memcpy( palette, pal, size ); for ( size_t i = 0; i < sizeof( palette ) ; i += 4) { convert_be_le( palette + i, sizeof( uint32_t ) ); } write_buffer( fd, palette, sizeof( palette ) ); } static int create_file( const char *file ) { int fd = openat( AT_FDCWD, file, O_CREAT | O_TRUNC | O_RDWR, 0666 ); if ( fd == -1 ) err( 2, "creating %s", file ); return fd; } static int mk_bmp( const char *file, int w, int h, const unsigned char *palette, int pal_len, const unsigned char *data, int len ) { int fd = create_file( file ); write_header( fd, w, h ); write_palette( fd, palette, pal_len ); write_buffer( fd, ( const char * ) data, len ); if ( lseek( fd, 0, SEEK_SET ) == -1 ) err( 1, "seeking in %s", file ); return fd; } static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static int open_or_die( const char *name, int oflag ) { int fd = openat( AT_FDCWD, name, oflag ); if ( fd == -1 ) err( 2, "opening %s", name ); return fd; } static void lseek_or_die( int fd, off_t offset, int whence ) { int status = lseek( fd, offset, whence ); if ( status < 0 ) err( 2, "lseek fd %d", fd ); } static int read_or_die( int fd, char *buffer, int nbytes ) { int bytes_read = read( fd, buffer, nbytes ); if ( bytes_read == -1 ) err( 1, "reading %d bytes", nbytes ); return bytes_read; } static int diff_f ( int fd1, int fd2 ) { char buffer1[ 256 ], buffer2[ 256 ]; int bytes1, bytes2, cmp; lseek_or_die( fd1, 0, SEEK_SET ); lseek_or_die( fd2, 0, SEEK_SET ); do { bytes1 = read_or_die( fd1, buffer1, sizeof( buffer1 ) ); bytes2 = read_or_die( fd2, buffer2, sizeof( buffer2 ) ); if ( bytes1 != bytes2 ) return bytes1 - bytes2; cmp = memcmp( buffer1, buffer2, bytes1 ); if ( cmp != 0 ) return cmp; } while ( bytes1 != 0 && bytes2 != 0 ); return 0; } static void close2_or_warn( const int fd1, const char *name1, const int fd2, const char *name2 ) { close_or_warn( fd1, name1 ); close_or_warn( fd2, name2 ); } static int diff( const char *file1, const char *file2 ) { int fd1 = -1, fd2 = -1, cmp; fd1 = open_or_die( file1, O_RDONLY ); fd2 = open_or_die( file2, O_RDONLY ); cmp = diff_f( fd1, fd2 ); close2_or_warn( fd1, file1, fd2, file2 ); return cmp; } static void unlink_if_exists( const char *file ) { if ( unlink( file ) == -1 && errno != ENOENT ) err( 2, "unlink" ); } int main( void ) { int in_fd, out_fd; const char *in_file_1 = "zt.r6_small.bmp"; const char *out_file_1 = "zt.r6_small_out.bmp"; const char *exp_file_1 = "zt.r6_small_exp.bmp"; const char *in_file_2 = "zt.r6_img.bmp"; const char *out_file_2 = "zt.r6_img_out.bmp"; const char *exp_file_2 = "zt.r6_img_exp.bmp"; // ARGB format uint8_t palette_1[] = { 0x00, 0x00, 0x00, 0x00, // black 0x00, 0xff, 0xff, 0xff, // white 0x00, 0xff, 0x00, 0x00, // red 0x00, 0x00, 0xff, 0x00, // green 0x00, 0x00, 0x00, 0xff, // blue 0x00, 0x00, 0xff, 0xff, // cyan 0x00, 0xff, 0x00, 0xff, // magenta 0x00, 0xff, 0xff, 0x00 // yellow }; /* Testovací bitmapová data. Za povšimnutí stojí, že řádky jdou * odspodu nahoru, tedy v opačném pořadí, než jak je vidíme * v prohlížeči obrázků. */ const unsigned char img_1[] = { 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04 }; uint8_t out_palette_1[] = { 0x00, 0x00, 0x00, 0x00, // black 0x00, 0xff, 0xff, 0xff, // white 0x00, 0x4c, 0x4c, 0x4c, // red 0x00, 0x95, 0x95, 0x95, // green 0x00, 0x1d, 0x1d, 0x1d, // blue 0x00, 0xb2, 0xb2, 0xb2, // cyan 0x00, 0x69, 0x69, 0x69, // magenta 0x00, 0xe1, 0xe1, 0xe1 // yellow }; uint8_t palette_2[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x61, 0x75, 0x00, 0xe0, 0x9e, 0x10, 0x00, 0x91, 0x73, 0x33, 0x00, 0x33, 0x91, 0x8e, 0x00, 0x00, 0xff, 0x6c, 0x00, 0xff, 0x00, 0xe4, 0x00, 0xff, 0x5c, 0x68, 0x00, 0xfb, 0x48, 0x55, 0x00, 0xfb, 0x33, 0x41, 0x00, 0xff, 0x16, 0x26, 0x00, 0xff, 0x00, 0x12 }; uint8_t img_2[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x05, 0x05, 0x05, 0x05, 0x05, 0x01, 0x01, 0x06, 0x05, 0x01, 0x01, 0x01, 0x07, 0x08, 0x09, 0x0a, 0x01, 0x01, 0x00, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x05, 0x01, 0x01, 0x06, 0x01, 0x01, 0x07, 0x08, 0x01, 0x0a, 0x0b, 0x01, 0x00, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x06, 0x01, 0x01, 0x05, 0x06, 0x01, 0x07, 0x08, 0x09, 0x0a, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x06, 0x05, 0x01, 0x07, 0x08, 0x01, 0x0a, 0x0b, 0x01, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x06, 0x05, 0x06, 0x05, 0x06, 0x01, 0x07, 0x08, 0x09, 0x0a, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, }; uint8_t out_palette_2[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x5e, 0x5e, 0x00, 0xa1, 0xa1, 0xa1, 0x00, 0x74, 0x74, 0x74, 0x00, 0x74, 0x74, 0x74, 0x00, 0xa1, 0xa1, 0xa1, 0x00, 0x66, 0x66, 0x66, 0x00, 0x8e, 0x8e, 0x8e, 0x00, 0x7f, 0x7f, 0x7f, 0x00, 0x70, 0x70, 0x70, 0x00, 0x5d, 0x5d, 0x5d, 0x00, 0x4e, 0x4e, 0x4e }; out_fd = mk_bmp( exp_file_1, 4, 2, out_palette_1, sizeof ( out_palette_1 ), img_1, sizeof ( img_1 ) ); close_or_warn( out_fd, exp_file_1 ); out_fd = mk_bmp( exp_file_2, 19, 9, out_palette_2, sizeof ( out_palette_2 ), img_2, sizeof ( img_2 ) ); close_or_warn( out_fd, exp_file_2 ); // test 1 in_fd = mk_bmp( in_file_1, 4, 2, palette_1, sizeof ( palette_1 ), img_1, sizeof ( img_1 ) ); out_fd = create_file( out_file_1 ); assert( togray( in_fd, out_fd ) == 0 ); close2_or_warn( in_fd, in_file_1, out_fd, out_file_1 ); assert( diff( out_file_1, exp_file_1 ) == 0 ); // test 2 in_fd = mk_bmp( in_file_2, 19, 9, palette_2, sizeof ( palette_2 ), img_2, sizeof ( img_2 ) ); out_fd = create_file( out_file_2 ); assert( togray( in_fd, out_fd ) == 0 ); close2_or_warn( in_fd, in_file_2, out_fd, out_file_2 ); assert( diff( out_file_2, exp_file_2 ) == 0 ); // test 3 in_fd = open_or_die( in_file_2, O_RDONLY ); out_fd = open_or_die( "/dev/null", O_WRONLY ); lseek_or_die( in_fd, -4 * 256, SEEK_END ); assert( togray( in_fd, out_fd ) == -2 ); close_or_warn( in_fd, in_file_2 ); // test 4 assert( togray( out_fd, out_fd ) == -1 ); close_or_warn( out_fd, "/dev/null" ); unlink_if_exists( in_file_1 ); unlink_if_exists( out_file_1 ); unlink_if_exists( exp_file_1 ); unlink_if_exists( in_file_2 ); unlink_if_exists( out_file_2 ); unlink_if_exists( exp_file_2 ); return 0; }