/*============================================================================= bmplib, a simple library to create, modify, and write BMP image files. Copyright (C) 2009-2014 by Zack T Smith. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA The author may be reached at veritas@comcast.net. *============================================================================*/ //-------------------------------------------------- // Change Log // 0.8 ZS Added larger font of my own design. // 0.9 ZS Removed attempt at anti-aliasing. //-------------------------------------------------- #include #include #include #include "BMP.h" #include "font.h" #include "minifont.h" // Narrowest possible numbers. static char* narrow_nums [] = { " # ", "# #", "# #", "# #", "# #", "# #", " # ", " #", "##", " #", " #", " #", " #", " #", " # ", "# #", " #", " ##", "# ", "# ", "###", "###", " #", " # ", "## ", " #", "# #", " # ", "# #", "# #", "# #", "###", " #", " #", " #", "###", "# ", "## ", " #", " #", "# #", " # ", " # ", "# ", "# ", "## ", "# #", "# #", " # ", "###", " #", " #", " # ", " # ", " # ", " # ", " # ", "# #", "# #", " # ", "# #", "# #", " # ", " # ", "# #", "# #", " ##", " #", " # ", "# ", " ", "", "", " ", "", "", "#", }; /*--------------------------------------------------------------------------- * Name: BMP_new * Purpose: Creates new image. *-------------------------------------------------------------------------*/ BMP* BMP_new (int w, int h) { unsigned long size; BMP* nu; if (w<1 || h<1) return NULL; //---------- if (w & 3) w += 4 - (w & 3); if (h & 3) h += 4 - (h & 3); nu = (BMP*) malloc (sizeof (BMP)); if (!nu) return NULL; memset (nu, 0, sizeof (BMP)); nu->width = w; nu->height = h; size = w * h * sizeof (long); nu->pixels = (RGB*) malloc (size); if (!nu->pixels) { free (nu); return NULL; } memset (nu->pixels, 0, size); return nu; } /*--------------------------------------------------------------------------- * Name: BMP_destroy * Purpose: Deallocates image. *-------------------------------------------------------------------------*/ void BMP_destroy (BMP* bmp) { if (!bmp) return; //---------- if (bmp->pixels) free (bmp->pixels); free (bmp); } /*--------------------------------------------------------------------------- * Name: BMP_point * Purpose: Writes pixel into image. *-------------------------------------------------------------------------*/ void BMP_point (BMP *bmp, int x, int y, RGB rgb) { if (!bmp || x<0 || y<0) return; if (x >= bmp->width || y >= bmp->height) return; if (!bmp->pixels) return; //---------- bmp->pixels[y*bmp->width + x] = rgb; } /*--------------------------------------------------------------------------- * Name: BMP_line_core * Purpose: Draws a line in a BMP image. *-------------------------------------------------------------------------*/ void BMP_line_core (BMP *bmp, int x0, int y0, int x1, int y1, RGB rgb, int dashed) { if ((rgb >> 24) == 0xff) return; int dot_counter = 0; if (!dashed && x0 == x1 && y0 == y1) BMP_point (bmp, x0, y0, rgb); else if (!dashed && x0 == x1) BMP_vline (bmp, x0, y0, y1, rgb); else if (!dashed && y0 == y1) BMP_hline (bmp, x0, x1, y0, rgb); else { int j, x, y, dx, dy, e, xchange, s1, s2; // DDA, copied from my FramebufferUI project. x = x0; y = y0; s1 = 1; s2 = 1; dx = x1 - x0; if (dx < 0) { dx = -dx; s1 = -1; } dy = y1 - y0; if (dy < 0) { dy = -dy; s2 = -1; } xchange = 0; if (dy > dx) { int tmp = dx; dx = dy; dy = tmp; xchange = 1; } e = (dy<<1) - dx; j = 0; while (j <= dx) { j++; int draw = 1; if (dashed && (1 & (dot_counter >> 2))) draw = 0; if (draw) BMP_point (bmp, x, y, rgb); dot_counter++; if (e >= 0) { if (xchange) x += s1; else y += s2; e -= (dx << 1); } if (xchange) y += s2; else x += s1; e += (dy << 1); } } } /*--------------------------------------------------------------------------- * Name: BMP_line * Purpose: Draws a line in a BMP image. *-------------------------------------------------------------------------*/ void BMP_line (BMP *bmp, int x0, int y0, int x1, int y1, RGB rgb) { BMP_line_core (bmp, x0, y0, x1, y1, rgb, 0); } /*--------------------------------------------------------------------------- * Name: BMP_line_dashed * Purpose: Draws a dashed line in a BMP image. *-------------------------------------------------------------------------*/ void BMP_line_dashed (BMP *bmp, int x0, int y0, int x1, int y1, RGB rgb) { BMP_line_core (bmp, x0, y0, x1, y1, rgb, 1); } /*--------------------------------------------------------------------------- * Name: BMP_rect * Purpose: Fills a rectangle with a color. *-------------------------------------------------------------------------*/ void BMP_rect (BMP *bmp, int x, int y, int w, int h, RGB rgb) { BMP_hline (bmp, x, x+w-1, y, rgb); BMP_hline (bmp, x, x+w-1, y+h-1, rgb); BMP_vline (bmp, x, y, y+h-1, rgb); BMP_vline (bmp, x+w-1, y, y+h-1, rgb); } /*--------------------------------------------------------------------------- * Name: BMP_fillrect * Purpose: Fills a rectangle with a color. *-------------------------------------------------------------------------*/ void BMP_fillrect (BMP *bmp, int x, int y, int w, int h, RGB rgb) { while (h > 0) { BMP_hline (bmp, x, x+w-1, y, rgb); h--; y++; } } /*--------------------------------------------------------------------------- * Name: BMP_clear * Purpose: Sets all pixels to specified color. *-------------------------------------------------------------------------*/ void BMP_clear (BMP *bmp, RGB rgb) { BMP_fillrect (bmp, 0, 0, bmp->width, bmp->height, rgb); } /*--------------------------------------------------------------------------- * Name: BMP_hline * Purpose: Draws horizontal line. *-------------------------------------------------------------------------*/ void BMP_hline (BMP *bmp, int x0, int x1, int y, RGB rgb) { if (x0 > x1) { int tmp=x1; x1=x0; x0=tmp; } while (x0 <= x1) { BMP_point (bmp, x0++, y, rgb); } } /*--------------------------------------------------------------------------- * Name: BMP_vline * Purpose: Draws vertical line. *-------------------------------------------------------------------------*/ void BMP_vline (BMP *bmp, int x, int y0, int y1, RGB rgb) { if (y0 > y1) { int tmp=y1; y1=y0; y0=tmp; } while (y0 <= y1) { BMP_point (bmp, x, y0++, rgb); } } /*--------------------------------------------------------------------------- * Name: BMP_draw_string * Purpose: Draws ature 5x8 characters into the image. *-------------------------------------------------------------------------*/ int BMP_draw_string (BMP *bmp, const char *string, int x, int y, RGB color) { char ch; const char *s; RGB r,g,b; RGB light, dark; if (!bmp || !string) return 0; if (x >= bmp->width || y >= bmp->height || !*string) return 0; //---------- r = 0xff & (color >> 16); g = 0xff & (color >> 8); b = 0xff & color; r += 3*0xff; b += 3*0xff; g += 3*0xff; r /= 4; g /= 4; b /= 4; light = b | (g << 8) | (r << 16); r = 0xff & (color >> 16); g = 0xff & (color >> 8); b = 0xff & color; r += 0xff; b += 0xff; g += 0xff; r /= 2; g /= 2; b /= 2; dark = b | (g << 8) | (r << 16); const char **chars = get_font_chars (); s = string; while ((ch = *s++)) { int ix = -1; if (ch == ' ') { x += 10; continue; } if (ch > 'z') continue; if (ch > ' ' && ch <= 'z') ix = FONT_HEIGHT * (ch - 33); if (ix >= 0) { int i; int width = 0; for (i=0; i 'z') continue; if (ch > ' ' && ch <= 'z') ix = FONT_HEIGHT * (ch - 33); if (ix >= 0) { int j; int max_w = 0; for (j = 0; j < FONT_HEIGHT; j++) { const char *ptr = _chars [j+ix]; int w = ptr ? strlen (ptr) : 0; if (max_w < w) max_w = w; } width += max_w + 2/* kerning */; } } return width; } /*--------------------------------------------------------------------------- * Name: BMP_draw_mini_string * Purpose: Draws miniature 5x8 characters into the image. *-------------------------------------------------------------------------*/ int BMP_draw_mini_string (BMP *bmp, const char *string, int x, int y, RGB color) { char ch; const char *s; unsigned long r,g,b; unsigned long light, dark; if (!bmp || !string) return 0; if (x >= bmp->width || y >= bmp->height || !*string) return 0; //---------- r = 0xff & (color >> 16); g = 0xff & (color >> 8); b = 0xff & color; r += 3*0xff; b += 3*0xff; g += 3*0xff; r /= 4; g /= 4; b /= 4; light = b | (g << 8) | (r << 16); r = 0xff & (color >> 16); g = 0xff & (color >> 8); b = 0xff & color; r += 0xff; b += 0xff; g += 0xff; r /= 2; g /= 2; b /= 2; dark = b | (g << 8) | (r << 16); const char **mini_chars = get_minifont_chars (); #define MINI_HEIGHT (8) s = string; while ((ch = *s++)) { int ix = -1; if (ch == ' ') { x += 5; continue; } if (ch > 'z') continue; if (ch > ' ' && ch <= 'z') ix = MINI_HEIGHT * (ch - 33); if (ix >= 0) { int i; int width = 0; for (i=0; i 'z') continue; if (ch > ' ' && ch <= 'z') ix = MINI_HEIGHT * (ch - 33); if (ix >= 0) { int max_w = 0; int j; for (j = 0; j < MINI_HEIGHT; j++) { const char *ptr = mini_chars [j+ix]; int w = ptr ? strlen (ptr) : 0; if (max_w < w) max_w = w; } width += max_w + 1/*kerning*/; } } return width; } /*--------------------------------------------------------------------------- * Name: BMP_narrow_numbers * Purpose: Draws miniature 4x7 characters into the image. *-------------------------------------------------------------------------*/ int BMP_draw_narrow_numbers (BMP *bmp, const char *string, int x, int y, RGB color) { char ch; const char *s; if (!bmp || !string) return 0; if (x >= bmp->width || y >= bmp->height || !*string) return 0; //---------- #define NARROW_HEIGHT (7) s = string; while ((ch = *s++)) { int ix = -1; if (ch == ' ') { x += 3; continue; } if (ch >= '0' && ch <= '9') ix = ch - '0'; else if (ch == '.') ix = 10; ix *= NARROW_HEIGHT; if (ix >= 0) { int i; int width = strlen (narrow_nums [ix]); for (i=0; i= bmp->width || y >= bmp->height) return 0; if (!bmp->pixels) return 0; //---------- return bmp->pixels[y*bmp->width + x]; } /*--------------------------------------------------------------------------- * Name: BMP_write * Purpose: Writes image to BMP file. *-------------------------------------------------------------------------*/ int BMP_write (const BMP* bmp, const char *path) { FILE *f; #define HDRLEN (54) unsigned char h[HDRLEN]; unsigned long len; int i, j; if (!bmp || !path) return -1; //---------- memset (h, 0, HDRLEN); //-------------------- // Create the file. // f = fopen (path, "wb"); if (!f) return 0; //-------------------- // Prepare header // len = HDRLEN + 3 * bmp->width * bmp->height; h[0] = 'B'; h[1] = 'M'; h[2] = len & 0xff; h[3] = (len >> 8) & 0xff; h[4] = (len >> 16) & 0xff; h[5] = (len >> 24) & 0xff; h[10] = HDRLEN; h[14] = 40; h[18] = bmp->width & 0xff; h[19] = (bmp->width >> 8) & 0xff; h[20] = (bmp->width >> 16) & 0xff; h[22] = bmp->height & 0xff; h[23] = (bmp->height >> 8) & 0xff; h[24] = (bmp->height >> 16) & 0xff; h[26] = 1; h[28] = 24; h[34] = 16; h[36] = 0x13; // 2835 pixels/meter h[37] = 0x0b; h[42] = 0x13; // 2835 pixels/meter h[43] = 0x0b; //-------------------- // Write header. // if (HDRLEN != fwrite (h, 1, HDRLEN, f)) { fclose (f); return 0; } //---------------------------------------- // Write pixels. // Note that BMP has lower rows first. // for (j=bmp->height-1; j >= 0; j--) { for (i=0; i < bmp->width; i++) { unsigned char rgb[3]; int ix = i + j * bmp->width; unsigned long pixel = bmp->pixels[ix]; rgb[0] = pixel & 0xff; rgb[1] = (pixel >> 8) & 0xff; rgb[2] = (pixel >> 16) & 0xff; if (3 != fwrite (rgb, 1, 3, f)) { fclose (f); return 0; } } } fclose (f); return 1; }