/* ip.cpp -- Image processing program Usage: ip input.bmp output.bmp commands... Input and output files are 24 bit uncompressed color bitmap images. Each command is a lowercase letter possibly followed by real number arguments: a C B - Replace each pixel x (in range 0 to 1) with Cx + B. c Cr Br Cg Bg Cb Bb - replace color pixels r,g,b as above. h L R - replace x with Lx + Ry where y is the pixel to the right of x. v T B - replace x with Tx + By where y is below x. r - rotate image 180 degrees. q N - quantize to N bits (0-7) g - gray scale: replace r,g,b triples each with (2r + 4g + b) / 7. Each operation is timed in seconds and clock cycles excluding I/O time. For example: ip lena.bmp out.bmp g h .5 .5 r q 6 reads lena.bmp, converts to greyscale, blurs horizontally, rotates and quantizes to 6 bits, and writes output to new file out.bmp. To compile (MINGW): ml /Cx /c /coff copy.asm g++ ip.cpp copy.obj -s -o ip.exe or (Borland): ml /Cx /c copy.asm bcc32 ip.cpp copy.obj */ #include #include #include // return low 32 bits of CPU counter extern "C" unsigned int rdtsc(); // Convert string to int scaled by 256 e.g. atop("0.5") returns 128 int atop(const char* s) { return int(256*atof(s)); } // Get number of CPU clock cycles since previous call int rdtsc_time() { static unsigned int old_rdtsc; unsigned int t=rdtsc(); unsigned int result=t-old_rdtsc; old_rdtsc=t; return result; } // Get time in seconds since previous call double real_time() { static clock_t old_time; clock_t t = clock(); clock_t result = t - old_time; old_time = t; return (double)result/CLOCKS_PER_SEC; } // Start both timers void start_timer() { real_time(); rdtsc_time(); } int total_rdtsc=0; // total time // Adjust pixels by x = cx * x + bx, where x={r,g,b} is the color void adjust(unsigned char* image, int imagesize, int cr, int br, int cg, int bg, int cb, int bb) { start_timer(); while (imagesize>0) { int x; x=((cb*image[0]) >> 8) + bb; if (x<0) x=0; if (x>255) x=255; image[0]=(int)x; x=((cg*image[1]) >> 8) + bg; if (x<0) x=0; if (x>255) x=255; image[1]=(int)x; x=((cr*image[2]) >> 8) + br; if (x<0) x=0; if (x>255) x=255; image[2]=(int)x; image+=3; imagesize-=3; } printf("adjust %1.2f %1.2f %1.2f %1.2f %1.2f %1.2f in %d clocks, %1.2f sec.\n", cr/256.0, br/256.0, cg/256.0, bg/256.0, cb/256.0, bb/256.0, total_rdtsc+=rdtsc_time(), real_time()); } // Horizotal filter, replace consectutive xy with Lx + Ry, y void horiz(unsigned char* image, int imagesize, int L, int R) { start_timer(); while (imagesize>0) { int x=(L*image[0]+R*image[3]) >> 8; if (x<0) x=0; if (x>255) x=255; image[0]=x; ++image; --imagesize; } printf("horiz %1.2f %1.2f in %d clocks, %1.2f sec.\n", L/256.0, R/256.0, total_rdtsc+=rdtsc_time(), real_time()); } // Vertical filter, replace x above y with Tx + By void vert(unsigned char* image, int imagesize, int width, int T, int B){ start_timer(); width *= -3; image+=imagesize; while (imagesize>0) { --image; int x=(T*image[0]+B*image[width]) >> 8; if (x<0) x=0; if (x>255) x=255; image[0]=x; --imagesize; } printf("vert %1.2f %1.2f in %d clocks, %1.2f sec.\n", T/256.0, B/256.0, total_rdtsc+=rdtsc_time(), real_time()); } // Rotate void rotate(unsigned char* image, int imagesize) { start_timer(); unsigned char *end = image+imagesize-3; while (image8) n=8; unsigned int mask=(((-1) << (8-n))&255) * 0x1010101; while (imagesize>0) { *(int*)image &= mask; imagesize -= 4; image += 4; } printf("quantize %d in %d clocks, %1.2f sec.\n", n, total_rdtsc+=rdtsc_time(), real_time()); } // Convert image to gray scale void grayscale(unsigned char* image, int imagesize) { start_timer(); while (imagesize>0) { int c=((image[0]+image[1]*4+image[2]*2)*73) >> 9; image[0]=image[1]=image[2]=c; imagesize-=3; image+=3; } printf("grayscale in %d clocks, %1.2f sec.\n", total_rdtsc+=rdtsc_time(), real_time()); } int main(int argc, char **argv) { // Check arguments if (argc < 3) { fprintf(stderr, "To process image in.bmp to out.bmp: ip in.bmp out.bmp commands...\n" "Commands are lowercase letters followed by real numbers:\n" " a C B - Replace each pixel x (in range 0 to 1) with Cx + B.\n" " c Cr Br Cg Bg Cb Bb - replace color pixels r,g,b as above.\n" " h L R - replace x with Lx + Ry where y is the pixel to the right of x.\n" " v T B - replace x with Tx + By where y is below x.\n" " r - rotate image 180 degrees.\n" " q N - quantize to N bits (0-7)\n" " g - gray scale: replace r,g,b triples each with (2r + 4g + b) / 7.\n"); exit(1); } // Open input start_timer(); FILE *f=fopen(argv[1],"rb"); if (!f) perror(argv[1]), exit(1); // Read 54 byte bmp header and check for errors. Must be 24 bit color, no // palatte, no compression, under 48 MB. char header[54]; int n=fread(header, 1, 54, f); if (n!=54) fprintf(stderr, "Input is too small (%d bytes)\n", n), exit(1); if (header[0]!='B' || header[1]!='M') fprintf(stderr, "Not BMP\n"), exit(1); int imagesize=*(int*)(header+2)-54; printf("file size = %d\n", imagesize); if (imagesize<=0 || imagesize > 0x3000000) fprintf(stderr, "Image too big: %d bytes\n", imagesize), exit(1); if (*(int*)(header+10)!=54 || *(short*)(header+28)!=24) fprintf(stderr, "Not 24-bit color\n"), exit(1); if (*(int*)(header+30)!=0) fprintf(stderr, "Compression not supported\n"), exit(1); int width=*(int*)(header+18); int height=*(int*)(header+22); // Read image unsigned char *image = (unsigned char*)calloc(imagesize+256+width*6,1); image+=128+width*3; // pad over one scan line top and bottom image-=((int)image)&63; // align on cache page if ((n=fread(image, 1, imagesize+1, f))!=imagesize) fprintf(stderr,"Bad file size: read %d bytes instead of %d\n",n,imagesize), exit(1); fclose(f); printf("Read %s, %d by %d, %d bytes in %d clocks, %1.2f sec.\n", argv[1], width, height, imagesize+54, rdtsc_time(), real_time()); // Process commands char* outfilename = argv[2]; argc-=3; argv+=3; while(argc>0) { char command=argv[0][0]; ++argv; --argc; switch(command) { case 'a': if (argc>=2) { adjust(image, imagesize, atop(argv[0]), atop(argv[1]), atop(argv[0]), atop(argv[1]), atop(argv[0]), atop(argv[1])); argc-=2; argv+=2; } break; case 'c': if (argc>=6) { adjust(image, imagesize, atop(argv[0]), atop(argv[1]), atop(argv[2]), atop(argv[3]), atop(argv[4]), atop(argv[5])); argc-=6; argv+=6; } break; case 'h': if (argc>=2) { horiz(image, imagesize, atop(argv[0]), atop(argv[1])); argc-=2; argv+=2; } break; case 'v': if (argc>=2) { vert(image, imagesize, width, atop(argv[0]), atop(argv[1])); argc-=2; argv+=2; } break; case 'r': rotate(image, imagesize); break; case 'q': if (argc>=1) { quantize(image, imagesize, atoi(argv[0])); --argc; ++argv; } break; case 'g': grayscale(image, imagesize); break; default: printf("Unknown command %s\n", argv[-1]); } } // Write output start_timer(); f=fopen(outfilename,"wb"); if (!f) perror(outfilename), exit(1); n=fwrite(header, 1, 54, f); n+=fwrite(image, 1, imagesize, f); if (n!=54+imagesize) fprintf(stderr, "Wrote %d of %d bytes\n", n, imagesize+54); fclose(f); printf("Wrote %s, %d bytes in %d clocks, %1.2f sec.\n", outfilename, imagesize+54, rdtsc_time(), real_time()); printf("Total clocks: %d (%d/pixel)\n", total_rdtsc, total_rdtsc/(imagesize/3)); return 0; }