/* bmp.c : PUBLIC DOMAIN - Jon Mayo - August 17, 2006 * simple BMP save program. supports 1-bit, 4-bit and 8-bit formats. * * define STAND_ALONE to run the example routine * Updated: July 21, 2010 * * - You may remove any comments you wish, modify this code any way you wish, * and distribute any way you wish. */ #include #include #include #include #include #include /* return a valid BMP depth based on the number of colors. 0 on error. */ static unsigned get_depth(unsigned colors) { unsigned depth; /* calculate bit depth */ for(colors--,depth=1;colors>>=1;depth++) ; if(depth>8) { fprintf(stderr, "This program only supports 8 bits per pixel or less.\n"); return 0; /* failure */ } if(depth==1 || depth==2 || depth==4) return depth; return 8; } static unsigned get_bytes_per_line(unsigned width, unsigned depth) { return (width*depth+CHAR_BIT-1)/CHAR_BIT; } /* returns the number of bytes needed for the image. * round up each row to next byte */ static size_t image_bytes(unsigned width, unsigned height, unsigned depth) { return height*get_bytes_per_line(width, depth); } static void dword_le(unsigned char *dest, unsigned val) { assert(dest != NULL); dest[0]=val%256; dest[1]=(val/256)%256; dest[2]=(val/65536L)%256; dest[3]=(val/16777216L)%256; } /* saves an image to a BMP file * colors can be 2 to 256. (for 1bpp to 8bpp) * if palette == NULL then do grayscale * */ static int bmp_save(const char *filename, unsigned char *data, unsigned width, unsigned height, unsigned colors, unsigned char palette[][3]) { unsigned y, c, offset; FILE *f; unsigned char header[54] = { /** BitmapFileHeader **/ 'B', 'M', /* Type */ 0, 0, 0, 0, /* Size */ 0, 0, /* Reserved1 */ 0, 0, /* Reserved2 */ 0, 0, 0, 0, /* OffsetBits - actually byte offer */ /** BitmapInfoHeader **/ 40, 0, 0, 0, /* biSize */ 0, 0, 0, 0, /* biWidth */ 0, 0, 0, 0, /* biHeight */ 1, 0, /* biPlanes */ 8, 0, /* biBitCount */ 0, 0, 0, 0, /* biCompression */ 0, 0, 0, 0, /* biSizeImage */ 0, 0, 0, 0, /* biXPelsPerMeter */ 0, 0, 0, 0, /* biYPelsPerMeter */ 0, 0, 0, 0, /* biClrUsed */ 0, 0, 0, 0, /* biClrImportant */ }; unsigned char quad[4]; /* for RGB values */ unsigned depth; unsigned bytes_per_line; const unsigned char zero[3] = {0}; /* used to pad lines */ depth=get_depth(colors); if(!depth) return 0; /* width */ dword_le(header+18, width); /* height */ dword_le(header+22, height); offset=sizeof header + colors * sizeof quad; /* offset */ dword_le(header+10, offset); header[28]=depth; /* biBitCount */ f=fopen(filename, "wb"); if(!f) { perror(filename); return 0; } if(fwrite(header, sizeof header, 1, f)!=1) { perror(filename); fclose(f); return 0; } /* output palette data */ quad[3]=0; for(c=0;c CHAR_BIT) { r_bits=3; g_bits=3; b_bits=2; } /* assume there are enough nr colors for all the bits. */ for(i=0;i>b_bits)&((1<>(g_bits+b_bits))&((1<=CHAR_BIT) { unsigned i; i=y*bytes_per_line+x*depth/CHAR_BIT; idata[i]=pixel&255; bitcount-=CHAR_BIT; } } if(bitcount) { idata[y*bytes_per_line+x*depth/CHAR_BIT]=pixel&255; bitcount=0; pixel=0; } } } /* palette pattern. */ static void pal_pattern(unsigned char *idata, unsigned width, unsigned height, unsigned depth, unsigned colors) { unsigned x, y; unsigned pixel, bitcount; unsigned bytes_per_line=get_bytes_per_line(width, depth); unsigned cell_width, cell_height; /* number of cells */ /* find something roughly square. */ for(cell_width=1,cell_height=colors;cell_width=CHAR_BIT) { unsigned i; i=y*bytes_per_line+x*depth/CHAR_BIT; idata[i]=pixel&255; bitcount-=CHAR_BIT; } } /* handle end pixel found in some usual depths. */ if(bitcount) { idata[y*bytes_per_line+x*depth/CHAR_BIT]=pixel&255; bitcount=0; pixel=0; } } } int main(int argc, char **argv) { unsigned char palette[256][3], *idata; unsigned width=320, height=240, colors=256; unsigned depth; unsigned pattern=4, palette_type=1; const char *outfile="test.bmp"; int c; while((c=getopt(argc, argv, "P:c:h:o:p:w:"))>0) switch(c) { case 'P': palette_type=strtoul(optarg, 0, 0); break; /* palette_type */ case 'c': colors=strtoul(optarg, 0, 0); break; /* colors */ case 'h': height=strtoul(optarg, 0, 0); break; /* height */ case 'o': outfile=optarg; break; /* outfile */ case 'p': pattern=strtoul(optarg, 0, 0); break; /* pattern */ case 'w': width=strtoul(optarg, 0, 0); break; /* width */ default: usage: fprintf(stderr, "Usage: %s [-w ] [-h ] [-c ] [-p ] [-o ]\n", argv[0]); fprintf(stderr, "Patterns:\n" " -p 0 memset 0\n" " -p 1 memset 85\n" " -p 2 memset 170\n" " -p 3 memset 255\n" " -p 4 xor\n" " -p 5 palette sample\n" "Palettes:\n" " -P 0 grayscale\n" " -P 1 random\n" " -P 2 red and blue\n" " -P 3 rgb332\n" " -P 4 rgb222 (64 colors)\n" " -P 5 rgb111 (8 colors)\n" ); return EXIT_FAILURE; } if(optind!=argc) goto usage; /* we really only support 1, 4, 8. * other bmp formats do not actually seem to load */ if(colors<=2) colors=2; else if(colors<=16) colors=16; /* calculate the bit depth */ depth=get_depth(colors); if(!depth) return EXIT_FAILURE; /* make an ugly color palette */ switch(palette_type) { default: case 0: gen_palette(colors, palette, 0, 0, 0); break; /* greyscale */ case 1: gen_palette_random(colors, palette); break; /* random colors. */ case 2: gen_palette(colors, palette, 0, 0, ~0); break; /* red and blue */ case 3: gen_palette_rgb(colors, palette, 3, 3, 2); break; case 4: gen_palette_rgb(colors, palette, 2, 2, 2); break; case 5: gen_palette_rgb(colors, palette, 1, 1, 1); break; } /* create buffer for our image */ idata=malloc(image_bytes(width, height, depth)); switch(pattern) { default: case 0: memset(idata, 0x00, image_bytes(width, height, depth)); break; case 1: memset(idata, 0x55, image_bytes(width, height, depth)); break; case 2: memset(idata, 0xaa, image_bytes(width, height, depth)); break; case 3: memset(idata, 0xff, image_bytes(width, height, depth)); break; case 4: xor_pattern(idata, width, height, depth, colors); break; case 5: pal_pattern(idata, width, height, depth, colors); break; } /* save the image */ if(!bmp_save(outfile, idata, width, height, colors, palette)) { fprintf(stderr, "FAILED!\n"); return EXIT_FAILURE; } free(idata); return 0; } #endif