blob: cadb718c7d1d4ee94d37ed0004ab33fe1a123ed6 [file] [log] [blame] [edit]
/*=============================================================================
bmplib, a simple library to create, modify, and write BMP image files.
Copyright (C) 2009-2010 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 fbui@comcast.net.
*============================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "BMP.h"
// Mini characters, 8 pixels high.
static char* mini_chars [] =
{
"#",
"#",
"#",
"#",
"#",
" ",
"#",
"",
"## ##",
" # #",
"# #",
" ",
" ",
" ",
" ",
"",
" # # ",
" # # ",
"#####",
" # # ",
"#####",
" # # ",
" # # ",
"",
" # ",
" ####",
"# # ",
" ### ",
" # #",
"####",
" # ",
"",
"## #",
" #",
" #",
" #",
" #",
"#",
"# ##",
"",
" # ",
"# # ",
"## ",
" ## #",
"# ## ",
"# # ",
" ## #",
"",
"##",
" #",
"#",
"",
"",
"",
"",
"",
" #",
"#",
"#",
"#",
"#",
"#",
"#",
" #",
"# ",
" #",
" #",
" #",
" #",
" #",
" #",
"#",
" ",
"# # #",
" ###",
" #",
" ###",
"# # #",
"",
"",
" ",
" #",
" #",
"#####",
" #",
" #",
"",
"",
" ",
"",
"",
"",
"",
"##",
" #",
"#",
" ",
"",
"",
"#####",
"",
"",
"",
"",
" ",
"",
"",
"",
"",
"",
"#",
"",
" #",
" #",
" #",
" #",
" #",
"#",
"#",
"",
" ## ",
"# #",
"# #",
"# #",
"# #",
"# #",
" ## ",
"",
" #",
"##",
" #",
" #",
" #",
" #",
" #",
"",
" ## ",
"# #",
" #",
" ###",
"# ",
"# ",
"####",
"",
"####",
" #",
" # ",
" ## ",
" #",
"# #",
" ## ",
"",
"# # ",
"# #",
"# #",
"####",
" #",
" #",
" #",
"",
"####",
"# ",
"### ",
" #",
" #",
"# #",
" ## ",
"",
" ## ",
"# ",
"# ",
"### ",
"# #",
"# #",
" ## ",
"",
"####",
" #",
" #",
" # ",
" # ",
" # ",
" # ",
"",
" ## ",
"# #",
"# #",
" ## ",
"# #",
"# #",
" ## ",
"",
" ## ",
"# #",
"# #",
" ###",
" #",
" # ",
" # ",
"",
" ",
"",
"",
"#",
"",
"#",
"",
"",
" ",
"",
" ",
"##",
" ",
"##",
" #",
"#",
" #",
" #",
" #",
"#",
" #",
" #",
" #",
"",
" ",
"",
"",
"#####",
" ",
"#####",
"",
"",
"# ",
" #",
" #",
" #",
" #",
" #",
"#",
"",
" ### ",
"# #",
" #",
" ## ",
" #",
"",
" #",
"",
" ### ",
"# #",
"# .##",
"# # #",
"# .##",
"# ",
" ###",
"",
" # ",
" # # ",
"# #",
"# #",
"#####",
"# #",
"# #",
"",
"#### ",
"# #",
"# #",
"#### ",
"# #",
"# #",
"####",
"",
" ### ",
"# #",
"# ",
"# ",
"# ",
"# #",
" ###",
"",
"#### ",
"# #",
"# #",
"# #",
"# #",
"# #",
"####",
"",
"#####",
"#",
"#",
"###",
"#",
"#",
"#####",
"",
"#####",
"# ",
"# ",
"###",
"# ",
"# ",
"#",
"",
" ### ",
"# #",
"# ",
"# ##",
"# #",
"# #",
" ###.",
"",
"# #",
"# #",
"# #",
"#####",
"# #",
"# #",
"# #",
"",
"###",
" #",
" #",
" #",
" #",
" #",
"###",
"",
" ###",
" #",
" #",
" #",
" #",
"# #",
" ##",
"",
"# #",
"# #",
"# #",
"##",
"# #",
"# #",
"# #",
"",
"# ",
"#",
"#",
"#",
"#",
"#",
"#####",
"",
"# #",
"## ##",
"# # #",
"# #",
"# #",
"# #",
"# #",
"",
"# #",
"## #",
"# # #",
"# ##",
"# #",
"# #",
"# #",
"",
" ### ",
"# #",
"# #",
"# #",
"# #",
"# #",
" ###",
"",
"#### ",
"# #",
"# #",
"#### ",
"# ",
"# ",
"# ",
"",
" ### ",
"# #",
"# #",
"# #",
"# # #",
"# # ",
" ## #",
"",
"#### ",
"# #",
"# #",
"#### ",
"# # ",
"# # ",
"# #",
"",
" ### ",
"# #",
"# ",
" ### ",
" #",
"# #",
" ###",
"",
"#####",
" #",
" #",
" #",
" #",
" #",
" #",
"",
"# #",
"# #",
"# #",
"# #",
"# #",
"# #",
" ###",
"",
"# #",
"# #",
"# #",
"# #",
"# #",
" # # ",
" #",
"",
"# #",
"# #",
"# #",
"# . #",
"# # #",
"## ##",
"# #",
"",
"# #",
"# #",
" # #",
" #",
" # #",
"# #",
"# #",
"",
"# #",
"# #",
"# #",
" # #",
" #",
" #",
" #",
"",
"#####",
" #",
" #",
" #",
" #",
"#",
"#####",
"",
"##",
"#",
"#",
"#",
"#",
"#",
"#",
"##",
"# ",
"#",
" #",
" #",
" #",
" #",
" #",
"",
"##",
" #",
" #",
" #",
" #",
" #",
" #",
"##",
" # ",
" #.#",
"# #",
"",
"",
"",
"",
"",
" ",
"",
"",
"",
"",
"",
"",
"####",
"##",
"#",
" #",
"",
"",
"",
"",
"",
" ",
" ",
" ## ",
" #",
" ###",
"# #",
".###",
"",
"# ",
"# ",
"### ",
"# #",
"# #",
"# #",
"### ",
"",
" ",
" ",
" ###",
"# ",
"# ",
"# ",
" ###",
"",
" #",
" #",
" ###",
"# #",
"# #",
"# #",
" ###",
"",
" ",
" ",
" ## ",
"# #",
"####",
"# ",
" ###",
"",
" ##",
" # ",
"### ",
" # ",
" # ",
" # ",
"### ",
"",
" ",
" ",
" ###",
"# #",
"# #",
" ###",
" #",
"### ",
"# ",
"# ",
"### ",
"# #",
"# #",
"# #",
"# #",
"",
" # ",
" ",
"## ",
" # ",
" # ",
" # ",
"###",
"",
" #",
" ",
" ##",
" #",
" #",
" #",
" #",
"## ",
"# ",
"# ",
"# #",
"# # ",
"## ",
"# # ",
"# #",
"",
"## ",
" # ",
" # ",
" # ",
" # ",
" # ",
"###",
"",
" ",
"",
"####",
"# # #",
"# # #",
"# # #",
"# # #",
"",
" ",
" ",
"###",
"# #",
"# #",
"# #",
"# #",
"",
" ",
" ",
" ## ",
"# #",
"# #",
"# #",
" ## ",
"",
" ",
"",
"###",
"# #",
"# #",
"###",
"#",
"#",
" ",
"",
" ###",
"# #",
"# #",
" ###",
" #",
" # ",
" ",
" ",
"# ##",
"## ",
"# ",
"# ",
"# ",
"",
" ",
" ",
" ###",
"# ",
" ##",
" #",
"### ",
"",
" # ",
" #",
"###",
" #",
" #",
" #",
" ##",
"",
" ",
"",
"# #",
"# #",
"# #",
"# #",
" ###",
"",
" ",
"",
"# #",
"# #",
"# #",
" # #",
" #",
"",
" ",
"",
"# # #",
"# # #",
"# # #",
"# # #",
" # #",
"",
" ",
"",
"# #",
" # #",
" #",
" # #",
"# #",
"",
" ",
" ",
"# #",
"# #",
"# #",
" ###",
" #",
"### ",
" ",
"",
"#####",
" #",
" #",
" # ",
"#####",
"",
};
// 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 = (unsigned long*) malloc (size);
if (!nu->pixels) {
free (nu);
return NULL;
}
memset (nu->pixels, 0, size);
return nu;
}
/*===========================================================================
* Name: BMP_delete
* Purpose: Deallocates image.
*/
void
BMP_delete (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, unsigned long 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
* Purpose: Draws a line in a BMP image.
*/
void
BMP_line (BMP *bmp, int x0, int y0, int x1, int y1, unsigned long rgb)
{
if ((rgb >> 24) == 0xff)
return;
if (x0 == x1 && y0 == y1)
BMP_point (bmp, x0, y0, rgb);
else if (x0 == x1)
BMP_vline (bmp, x0, y0, y1, rgb);
else if (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++;
BMP_point (bmp, x, y, rgb);
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_rect
* Purpose: Fills a rectangle with a color.
*/
void
BMP_rect (BMP *bmp, int x, int y, int w, int h, unsigned long 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, unsigned long 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, unsigned long 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, unsigned long 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, unsigned long rgb)
{
if (y0 > y1) {
int tmp=y1;
y1=y0;
y0=tmp;
}
while (y0 <= y1) {
BMP_point (bmp, x, y0++, rgb);
}
}
/*===========================================================================
* Name: BMP_draw_mini_string
* Purpose: Draws miniature 5x8 characters into the image.
* Note: Full ASCII character set not supported.
*/
int
BMP_draw_mini_string (BMP *bmp, char *string, int x, int y, unsigned long color)
{
char ch, *s;
unsigned long r,g,b;
unsigned long light;
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);
#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 = strlen (mini_chars[ix]);
for (i=0; i<MINI_HEIGHT; i++) {
int j=0;
char ch2, *s2 = mini_chars[ix + i];
while ((ch2 = *s2++)) {
switch (ch2) {
case '#':
BMP_point (bmp,x+j, y+i, color);
break;
case '.':
BMP_point (bmp,x+j, y+i, light);
break;
}
j++;
}
}
x += width + 1;
}
}
return x;
}
/*===========================================================================
* Name: BMP_mini_string_width
* Purpose: Gets width of miniature 5x8 characters.
* Note: Full ASCII character set not supported.
*/
int
BMP_mini_string_width (char *string)
{
char ch, *s;
int width = 0;
if (!string)
return 0;
//----------
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 w = strlen (mini_chars[ix]);
width += w + 1;
}
}
return width;
}
/*===========================================================================
* Name: BMP_narrow_numbers
* Purpose: Draws miniature 4x7 characters into the image.
* Note: Full ASCII character set not supported.
*/
int
BMP_draw_narrow_numbers (BMP *bmp, char *string, int x, int y, unsigned long color)
{
char ch, *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, *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.
*/
unsigned long
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 (BMP* bmp, 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;
}