@* Introduction. It is very common nowadays that we use rounded corner background images in web pages. I have no idea how the concept of ``Rounded rectangle'' was introduced into graphical user interface. But I think it was probably done by Steve Jobs. Search ``Round Rects Are Everywhere'' on Google. This program generates a rounded bitmap. User can specify parameters such as the width, height and radius of the rectangle. Interestingly, a radius of zero indicates a regular rectangle. For help, please see ``nifty --help''. @c #include #include #include #include @; @; @; int main(int argc, char *argv[]) { BYTE *imgbits = 0; @; imgbits = (BYTE*)malloc(width*height*(BITS_PER_PIXEL / 8)); if (imgbits) { fill(imgbits, width, height, fgcolor); rounded_corner(imgbits, width, height, radius, bgcolor, top); write_image(imgbits, width, height, outfile); free(imgbits); } } @* Bitmap Related Structures and Operations. @d BYTE unsigned char @d WORD unsigned short @d DWORD unsigned long @d LONG long @d BI_RGB 0L @d BITS_PER_PIXEL 24 @ @= #pragma pack (1) typedef struct tagBITMAPFILEHEADER { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER; typedef struct tagBITMAPINFOHEADER { DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER; #pragma pack() @ @= void write_image(BYTE *pBits, int nSrcWidth, int nSrcHeight, const char *filename); @ Procedure |write_image|. Write the image bits into a BMP file. @c void write_image(BYTE *pBits, int nSrcWidth, int nSrcHeight, const char *filename) { BITMAPINFOHEADER bmih; FILE *fp; BITMAPFILEHEADER bmfh; memset(&bmih, 0, sizeof(BITMAPINFOHEADER)); bmih.biSize = sizeof(BITMAPINFOHEADER); bmih.biBitCount = BITS_PER_PIXEL; bmih.biPlanes = 1; bmih.biWidth = nSrcWidth; bmih.biHeight = nSrcHeight; bmih.biCompression = BI_RGB; memset(&bmfh, 0, sizeof(BITMAPFILEHEADER)); bmfh.bfType = 0x4d42; bmfh.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER); bmfh.bfSize = bmfh.bfOffBits + nSrcWidth * nSrcHeight * (BITS_PER_PIXEL / 8); fp = fopen(filename, "wb"); if (fp) { fwrite(&bmfh, 1, sizeof(BITMAPFILEHEADER), fp); fwrite(&bmih, 1, sizeof(BITMAPINFOHEADER), fp); fwrite(pBits, 1, nSrcWidth*nSrcHeight*3, fp); fclose(fp); } } @ @= void rgb(unsigned long color, BYTE* r, BYTE* g, BYTE*b); @ Procedure |rgb|. extract R, G, B values from an integer. @c void rgb(unsigned long color, BYTE* r, BYTE* g, BYTE*b) { *r = (BYTE)((color & 0xff0000) >> 16); *g = (BYTE)((color & 0xff00) >> 8); *b = (BYTE)(color & 0xff); } @ @= unsigned long fgcolor = 0; unsigned long bgcolor = 0xffffff; int width = 200; int radius = 20; int height = 20; int top = 1; char outfile[100] = "out.bmp"; @ Parse the arguments from user. @d EQ(s,t) (strcmp(s,t)==0) @= { int i = 0; for (i = 1; i < argc; i++) { if (EQ(argv[i], "-fg")) { fgcolor = strtol(argv[++i], 0, 16); } else if (EQ(argv[i], "-bg")) { bgcolor = strtol(argv[++i], 0, 16); } else if (EQ(argv[i], "-r")) { radius = strtol(argv[++i], 0, 10); } else if (EQ(argv[i], "-w")) { width = strtol(argv[++i], 0, 10); } else if (EQ(argv[i], "-h")) { height = strtol(argv[++i], 0, 10); } else if (EQ(argv[i], "-top")) { top = 1; } else if (EQ(argv[i], "-bot")) { top = 0; } else if (EQ(argv[i], "-o")) { strcpy(outfile, argv[++i]); } else if (EQ(argv[i], "--help")) { usage(); } } if (height < radius) height = radius; } @ @= void usage(); @ Procedure |usage|. Print help message. @c void usage() { fprintf(stderr, "\n" @/ " NIFTY - Generate background image (*.bmp) with round corners\n" @/ "\n" @/ "Usage: nifty [options]\n" @/ "Options:\n" @/ " -fg css hex color for foreground, e.g. c7eacc\n" @/ " -bg css hex color for background, e.g. ffffff\n" @/ " -r corner radius, default 20\n" @/ " -w output width, default 200\n" @/ " -h output height, default 20\n" @/ " -top generate round corners at top. This is default\n" @/ " -bot generate round corners at bottom\n" @/ " -o output bmp file, default name is \"out.bmp\"\n" @/ " --help print this message\n" @/ "\n" @/ "Example: nifty -w 800 -h 30 -r 20 -fg c7eacc -bot -o example.bmp\n" @/ "\n" @/ "Please send bug report to .\n" @/ ); exit(1); } @* Drawing. @= void rounded_corner(BYTE* imgbits, int width, int height, int radius, unsigned long bgcolor, int top); void fill(BYTE *imgbits, int width, int height, unsigned long fgcolor); @ Procedure |fill|. Fill image buffer with single color. @c void fill(BYTE *imgbits, int width, int height, unsigned long fgcolor) { BYTE r,g,b; int n = 0; rgb(fgcolor, &r,&g,&b); n = width * height; while (n-- > 0) { *imgbits++ = r; *imgbits++ = g; *imgbits++ = b; } } @ Procedure |rounded_corner|. @c void rounded_corner(BYTE* imgbits, int width, int height, int radius, unsigned long bgcolor, int top) { int stride; int step; int x; BYTE r,g,b; rgb(bgcolor, &r,&g,&b); stride = width * BITS_PER_PIXEL / 8; if (radius > height) radius = height; if (radius > width) radius = width; if (!top) { step = stride; } else { step = - stride; imgbits += stride * (height - 1); } for (x = radius; x > 0; x--, imgbits += step) { int y, i; BYTE *p, *q; y = (int)floor((double)radius - sqrt((double)(radius*radius - x*x))); if (y == radius) { if (x > 1) { int d; d = y - (int)floor((double)radius - sqrt((double)(radius*radius - (x-1)*(x-1)))); if (d > 2) { y -= d / 2; } } } p = imgbits; q = imgbits + stride; for (i = 0; i < y; i++) { *p++ = r; *p++ = g; *p++ = b; *--q = b; *--q = g; *--q = r; } } } @*Index.