// // Set of simple functions to create monochrome bitmap files. // #include #include #include #include #include "bmp1_lib.h" // // Define global bitmap data. // struct { char *bitmap; long height; long width; long width_pad_32; long width_bytes; long clip_count; long min_x, min_y, max_x, max_y; } Bmp; // // Define windows bitmap file header(s) format. // typedef struct { char Type[2]; } BMP_HDR1; typedef struct { long FileSize; long Reserved; long DataOffset; } BMP_HDR2; typedef struct { long HeaderSize; long Width; long Height; short Planes; short BitsPerPixel; long CompressionType; long ImageSize; long xPixelsPerMeter; long yPixelsPerMeter; long ColorsUsed; long ColorsImportant; } BMP_HDR3; // // Define color map entry. // typedef struct _rgb { unsigned char Blue; unsigned char Green; unsigned char Red; unsigned char Unused; } RGB, *PRGB; // // Define some bitmap macros. // #define BITS_PER_PIXEL 1 #define SUM_HDR_SIZE ( sizeof(BMP_HDR1) + sizeof(BMP_HDR2) + sizeof(BMP_HDR3) ) #define COLOR_MAP_SIZE ( (1 << BITS_PER_PIXEL) * sizeof(RGB) ) // // Get a single bit from the in-memory bitmap. // int bit_get (char *a, int n) { int index = n / 8; int mask = 1 << (7 - (n & 7)); return (a[index] & mask) != 0; } // // Set a single bit in the in-memory bitmap. // void bit_set (char *a, int n, int b) { int index = n / 8; int mask = 1 << (7 - (n & 7)); if (b) { a[index] |= mask; } else { a[index] &= ~mask; } return; } // // One-time call to initialize the in-memory bitmap. // void bmp1_alloc (int columns, int rows) { long size; Bmp.width = columns; Bmp.height = rows; Bmp.width_pad_32 = ((Bmp.width + 31) / 32) * 32; Bmp.width_bytes = Bmp.width_pad_32 / 8; size = Bmp.height * Bmp.width_bytes; Bmp.bitmap = malloc(size); if (Bmp.bitmap == NULL) { fprintf(stderr, "bit1_alloc(%d, %d) malloc(%ld) failed\n", columns, rows, size); exit(1); } memset(Bmp.bitmap, 0x0, size); bmp1_clip_rect(0, 0, columns, rows); Bmp.clip_count = 0; return; } // // One-time call to write the completed in-memory bitmap to output file. // void bmp1_write (char *filename, int height) { BMP_HDR1 hdr1; BMP_HDR2 hdr2; BMP_HDR3 hdr3; int i; long ImageSize, FileSize; FILE *output; RGB rgb; #ifdef _WIN32 struct _stat st; char mode[] = "wb"; #else struct stat st; char mode[] = "w"; #endif // Open output file. output = fopen(filename, mode); if (output == NULL) { fprintf(stderr, "bmp1_write(%s) open failed\n", filename); exit(1); } // Optional: set new smaller size of image. if (height > 0 && height < Bmp.height) { Bmp.height = height; } ImageSize = Bmp.width_pad_32 * Bmp.height * BITS_PER_PIXEL / 8; FileSize = SUM_HDR_SIZE + COLOR_MAP_SIZE + ImageSize; // Write header(s). hdr1.Type[0] = 'B'; hdr1.Type[1] = 'M'; fwrite(&hdr1, sizeof hdr1, 1, output); hdr2.FileSize = FileSize; hdr2.Reserved = 0; hdr2.DataOffset = SUM_HDR_SIZE + COLOR_MAP_SIZE; fwrite(&hdr2, sizeof hdr2, 1, output); hdr3.HeaderSize = sizeof hdr3; hdr3.Width = Bmp.width; hdr3.Height = Bmp.height; hdr3.Planes = 1; hdr3.BitsPerPixel = BITS_PER_PIXEL; hdr3.CompressionType = 0; hdr3.ImageSize = ImageSize; hdr3.xPixelsPerMeter = 0; hdr3.yPixelsPerMeter = 0; hdr3.ColorsUsed = 0; hdr3.ColorsImportant = 0; fwrite(&hdr3, sizeof hdr3, 1, output); // Color index 0 - background (white). rgb.Red = 0xFF; rgb.Green = 0xFF; rgb.Blue = 0xFF; rgb.Unused = 0; fwrite(&rgb, sizeof rgb, 1, output); // Color index 1 - stroke (black). rgb.Red = 0x00; rgb.Green = 0x00; rgb.Blue = 0x00; rgb.Unused = 0; fwrite(&rgb, sizeof rgb, 1, output); // Write each row, bottom to top (the BMP convention). for (i = Bmp.height - 1; i >= 0; i -= 1) { fwrite(&Bmp.bitmap[i * Bmp.width_bytes], Bmp.width_bytes, 1, output); } // Close file, free bitmap. fclose(output); free(Bmp.bitmap); Bmp.bitmap = NULL; // Integrity check. if (_stat(filename, &st) != 0) { fprintf(stderr, "bmp1_write(%s) stat failed\n", filename); exit(1); } if (st.st_size != FileSize) { fprintf(stderr, "bmp1_write(%s) file size error (%ld != %ld)\n", filename, st.st_size, FileSize); exit(1); } return; } // // Check if column and row are within bounds. // int clip_check (int column, int row) { // Silent clip x. if (column < Bmp.min_x || column >= Bmp.max_x) { return 1; } // Count clip y. if (row < Bmp.min_y || row >= Bmp.max_y) { Bmp.clip_count += 1; if (getenv("CLIP_CHECK") != NULL) { fprintf(stderr, "clip_check(%d, %d) out of bounds\n", column, row); } return 1; } return 0; } // // Set state of one pixel addressed by integer column (x) and row (y). // void bmp1_point (int column, int row) { if (clip_check(column, row) == 0) { bit_set(&Bmp.bitmap[row * Bmp.width_bytes] + column / 8, column % 8, 1); } } #define ROUND(f) ( (int) ((f) + 0.5) ) // // Draw one dot, given floating point x and y. // void bmp1_xy (double x, double y) { bmp1_point(ROUND(x), ROUND(y)); } // // Draw a square. // void bmp1_square (int column, int row, int radius) { int i, j; for (j = -radius; j <= radius; j += 1) { for (i = -radius; i <= radius; i += 1) { bmp1_point(column + i, row + j); } } } // // Draw a line. // void bmp1_lineto(int oldx, int oldy, int newx, int newy) { int dx, dy; double x, y; dx = newx - oldx; dy = newy - oldy; if (abs(dx) >= abs(dy)) { if (dx > 0) { for (x = oldx; x <= newx; x += 1) { y = oldy + (x - oldx) * (double)dy / dx; bmp1_xy(x, y); } } if (dx < 0) { for (x = newx; x <= oldx; x += 1) { y = oldy + (x - oldx) * (double)dy / dx; bmp1_xy(x, y); } } } else { if (dy > 0) { for (y = oldy; y <= newy; y += 1) { x = oldx + (y - oldy) * (double)dx / dy; bmp1_xy(x, y); } } if (dy < 0) { for (y = newy; y <= oldy; y += 1) { x = oldx + (y - oldy) * (double)dx / dy; bmp1_xy(x, y); } } } } // // Set clip region. // void bmp1_clip_rect (int x1, int y1, int x2, int y2) { Bmp.min_x = (x1 < x2) ? x1 : x2; Bmp.min_y = (y1 < y2) ? y1 : y2; Bmp.max_x = (x1 > x2) ? x1 : x2; Bmp.max_y = (y1 > y2) ? y1 : y2; } long bmp1_clip_count (void) { return Bmp.clip_count; }