diff options
Diffstat (limited to 'BMP.c')
-rwxr-xr-x | BMP.c | 796 |
1 files changed, 796 insertions, 0 deletions
@@ -0,0 +1,796 @@ + +/*============================================================================= + 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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<FONT_HEIGHT ; i++) { + int j=0; + char ch2; + const char *s2 = chars[ix + i]; + int width2 = s2 ? strlen (s2) : 0; + if (width < width2) + width = width2; + while ((ch2 = *s2++)) { + RGB color_to_use; + char draw = 1; + switch (ch2) { + case '#': + color_to_use = color; + break; + default: + draw = 0; + } + if (draw) + BMP_point (bmp,x+j, y+i, color_to_use); + j++; + } + } + + x += width + 2/* kerning */; + } + } + + return x; +} + +/*--------------------------------------------------------------------------- + * Name: BMP_string_width + * Purpose: Gets width of 10x16 characters. + *-------------------------------------------------------------------------*/ +int +BMP_string_width (const char *string) +{ + char ch; + const char *s; + int width = 0; + + if (!string) + return 0; + //---------- + + const char **_chars = get_font_chars (); + + s = string; + while ((ch = *s++)) { + int ix = -1; + if (ch == ' ') { + width += 10; + continue; + } + if (ch > '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<MINI_HEIGHT; i++) { + int j=0; + char ch2; + const char *s2 = mini_chars[ix + i]; + int width2 = s2 ? strlen (s2) : 0; + if (width < width2) + width = width2; + while ((ch2 = *s2++)) { + RGB color_to_use; + char draw = 1; + switch (ch2) { + case '#': + color_to_use = color; + break; + default: + draw = 0; + } + if (draw) + BMP_point (bmp,x+j, y+i, color_to_use); + j++; + } + } + + x += width + 1/* kerning */; + } + } + + return x; +} + +/*--------------------------------------------------------------------------- + * Name: BMP_mini_string_width + * Purpose: Gets width of miniature 5x8 characters. + *-------------------------------------------------------------------------*/ +int +BMP_mini_string_width (const char *string) +{ + char ch; + const char *s; + int width = 0; + + if (!string) + return 0; + //---------- + + const char **mini_chars = get_minifont_chars (); + + s = string; + while ((ch = *s++)) { + int ix = -1; + if (ch == ' ') { + width += 5; + continue; + } + if (ch > '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<NARROW_HEIGHT; i++) { + int j=0; + char ch2; + const char *s2 = narrow_nums [ix + i]; + while ((ch2 = *s2++)) { + if (ch2 == '#') { + BMP_point (bmp, + x+j, y+i, color); + } + j++; + } + } + + x += width + 1; + } + } + + return x; +} + +/*--------------------------------------------------------------------------- + * Name: BMP_getpixel + * Purpose: Reads pixel out of image. + *-------------------------------------------------------------------------*/ +RGB +BMP_getpixel (BMP *bmp, int x, int y) +{ + if (!bmp || x<0 || y<0) + return 0; + if (x >= 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; +} + + |