/* * BMPautomata.c * Marco Vitanza * 04/11/2009 * * Public domain * (do whatever you want with it) */ #include #include #include // parameters for image and automata struct params { unsigned int rule, randInitial; unsigned int imgw, imgh; int fgR, fgG, fgB; int bgR, bgG, bgB; } p; // BMP file header (4-byte values are little-endian) unsigned char buf[54] = { 0x42, 0x4D, 0x00, 0x00, 0x00, 0x00, // file size: 54 + imgw*imgh*3 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // imgw 0x00, 0x00, 0x00, 0x00, // imgh 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // imgw*imgh*3 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // stderr stream (set in main) FILE *err; /* * compute cells row by row, then write BMP to stdout */ void runRule() { unsigned char cells[p.imgh][p.imgw]; int r, i; // fill first row with single ON cell if (p.randInitial == 0) { for (i = 0; i < p.imgw; i++) cells[0][i] = 0; cells[0][p.imgw/2] = 1; } // or fill first row with random cells else { for (i = 0; i < p.imgw; i++) cells[0][i] = random() & 0x1; } // compute row by row using wrap-around unsigned char uppers; for (r = 1; r < p.imgh; r++) { for (i = 0; i < p.imgw; i++) { // middle upper neighbor uppers = cells[r-1][i] << 1; // left upper neighbor if ( (i == 0 && cells[r-1][p.imgw-1]) || (i != 0 && cells[r-1][i-1]) ) uppers |= 0x4; // right upper neighbor if ( (i == p.imgw-1 && cells[r-1][0]) || (i != p.imgw-1 && cells[r-1][i+1]) ) uppers |= 0x1; // compute next cell value cells[r][i] = (p.rule >> uppers) & 0x1; } } /********** OUTPUT BMP FILE **********/ // set image width field buf[18] = (p.imgw >> 0) & 0xFF; buf[19] = (p.imgw >> 8) & 0xFF; buf[20] = (p.imgw >> 16) & 0xFF; buf[21] = (p.imgw >> 24) & 0xFF; // set image height field buf[22] = (p.imgh >> 0) & 0xFF; buf[23] = (p.imgh >> 8) & 0xFF; buf[24] = (p.imgh >> 16) & 0xFF; buf[25] = (p.imgh >> 24) & 0xFF; // set raw bitmap size field i = p.imgw * p.imgh * 3; buf[34] = (i >> 0) & 0xFF; buf[35] = (i >> 8) & 0xFF; buf[36] = (i >> 16) & 0xFF; buf[37] = (i >> 24) & 0xFF; // set file size field i += 54; buf[2] = (i >> 0) & 0xFF; buf[3] = (i >> 8) & 0xFF; buf[4] = (i >> 16) & 0xFF; buf[5] = (i >> 24) & 0xFF; // write bitmap header FILE *fp = fdopen(1, "w"); if (fp == NULL) { fprintf(err, "Could not write to stdout.\n"); exit(-1); } fwrite(buf, 1, 54, fp); // write raw bitmap data (rows in reverse order) for (r = p.imgh-1; r >= 0; r--) { for (i = 0; i < p.imgw; i++) { if (cells[r][i]) { fputc(p.fgB, fp); fputc(p.fgG, fp); fputc(p.fgR, fp); } else { fputc(p.bgB, fp); fputc(p.bgG, fp); fputc(p.bgR, fp); } } // pad rows to be multiple of 4-bytes int bytesPerRow = p.imgw * 3; while (bytesPerRow % 4 != 0) { fputc(0, fp); bytesPerRow++; } } fclose(fp); } /* * simple string to int converter */ unsigned int strtoi(char *s) { unsigned int ret = 0; while (*s) { if (*s < '0' || *s > '9') break; ret = ret * 10 + (*s - '0'); s++; } return ret; } /* * simple hex char to int converter */ int hexchartoi(char *s) { int c; if (*s >= '0' && *s <= '9') c = *s - '0'; else if (*s >= 'A' && *s <= 'F') c = *s - 'A' + 10; else if (*s >= 'a' && *s <= 'f') c = *s - 'a' + 10; else { fprintf(err, "Invalid hex color code.\n"); exit(-1); } return c; } /* * main() - parse args then run automata */ int main(int argc, char** argv) { // check # of args err = fdopen(2, "w"); if (argc < 4 || argc > 7) { fprintf(err, "Usage: BMPautomata [-r] rule imgW imgH [bgColor fgColor]\n"); return -1; } // init temps and parameters unsigned int tmp, a = 1; memset(&p, 0, sizeof(p)); p.bgR = p.bgG = p.bgB = 255; // check for random flag if (strcmp(argv[a],"-r") == 0) { p.randInitial = 1; a++; } // parse rule number tmp = strtoi(argv[a]); if (tmp < 256) { p.rule = tmp; a++; } else { fprintf(err, "Rule must be 0-255.\n"); return -1; } // parse image width tmp = strtoi(argv[a]); if (tmp > 0) { p.imgw = tmp; a++; } else { fprintf(err, "Image width must be > 0.\n"); return -1; } // parse image height tmp = strtoi(argv[a]); if (tmp > 0) { p.imgh = tmp; a++; } else { fprintf(err, "Image height must be > 0.\n"); return -1; } // parse bg and fg colors, if specified if (a < argc) { if (argc - a != 2) { fprintf(err, "You must specific both FG and BG colors (or neither).\n"); return -1; } if (strlen(argv[a]) != 6 || strlen(argv[a+1]) != 6) { fprintf(err, "Colors must be specified as 6-char hex: RRGGBB.\n"); return -1; } p.fgR = hexchartoi(argv[a] ) * 16 + hexchartoi(argv[a]+1); p.fgG = hexchartoi(argv[a]+2) * 16 + hexchartoi(argv[a]+3); p.fgB = hexchartoi(argv[a]+4) * 16 + hexchartoi(argv[a]+5); a++; p.bgR = hexchartoi(argv[a] ) * 16 + hexchartoi(argv[a]+1); p.bgG = hexchartoi(argv[a]+2) * 16 + hexchartoi(argv[a]+3); p.bgB = hexchartoi(argv[a]+4) * 16 + hexchartoi(argv[a]+5); } // run automata and output BMP file runRule(); fclose(err); return 0; }