/* silcmap_bitmap.c Author: Pekka Riikonen Copyright (C) 2003 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. 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. */ #include "silcincludes.h" #include "silcclient.h" #include #include "silcmap.h" #include "data.h" /******* Bitmap Routines *****************************************************/ /* Load a bitmap file. The file is the map image that is loaded into the SilcMap context. This is no perfect PPM loader. */ bool silc_map_load_ppm(SilcMap map, const char *filename) { FILE *fp; char type[3]; int ret, retval = TRUE; SILC_LOG_DEBUG(("Load PPM '%s'", filename)); fp = fopen(filename, "r"); if (!fp) { fprintf(stderr, "fopen: %s: %s\n", strerror(errno), filename); return FALSE; } /* Read width and height */ ret = fscanf(fp, "%s %ld %ld %ld ", type, &map->width, &map->height, &map->maxcolor); if (ret < 4) { fprintf(stderr, "Invalid PPM file"); retval = FALSE; goto out; } /* Read the picture */ map->bitsilc_map_size = map->width * 3 * map->height; map->bitmap = silc_malloc(map->bitsilc_map_size); ret = fread(map->bitmap, map->bitsilc_map_size, 1, fp); if (ret < 0) { fprintf(stderr, "fread: %s\n", strerror(errno)); retval = FALSE; goto out; } out: fclose(fp); return retval; } /* Write the map into a bitmap file. */ bool silc_map_write_ppm(SilcMap map, const char *filename) { FILE *fp; int retval = TRUE; int i, k; SILC_LOG_DEBUG(("Write PPM '%s'", filename)); fp = fopen(filename, "w+"); if (!fp) { fprintf(stderr, "fopen: %s: %s\n", strerror(errno), filename); return FALSE; } /* Write the header */ fprintf(fp, "P6 %ld %ld %ld\n", map->width, map->height, map->maxcolor); /* Write the bitmap */ for (i = 0; i < map->height; i++) for (k = 0; k < map->width; k++) { putc(map->bitmap[i * (map->width * 3) + (k * 3) ], fp); /* R */ putc(map->bitmap[i * (map->width * 3) + (k * 3) + 1], fp); /* G */ putc(map->bitmap[i * (map->width * 3) + (k * 3) + 2], fp); /* B */ } fclose(fp); return retval; } /* Cut the map into a `width' * `height' size chunk at `x' and `y'. This returns the allocated map bitmap into `ret_bitmap'. The original map is not modified. */ bool silc_map_cut(SilcMap map, SilcInt32 x, SilcInt32 y, SilcUInt32 width, SilcUInt32 height, SilcMap *ret_map) { int i; SILC_LOG_DEBUG(("cut")); /* Sanity checks */ if (height > map->height - y) { fprintf(stderr, "Requesting too much height: %ld\n", height); return FALSE; } if (width > map->width - x) { fprintf(stderr, "Requesting too much width: %ld\n", width); return FALSE; } /* Compute coordinates in the bitmap */ y = (map->width * 3) * y; x = (x * 3); /* Allocate new SilcMap context */ *ret_map = silc_calloc(1, sizeof(**ret_map)); (*ret_map)->width = width; (*ret_map)->height = height; (*ret_map)->maxcolor = map->maxcolor; (*ret_map)->bitsilc_map_size = (width * 3) * height; (*ret_map)->bitmap = silc_malloc((*ret_map)->bitsilc_map_size); /* Copy the requested area */ for (i = 0; i < height; i++) { memcpy((*ret_map)->bitmap + (i * width * 3), map->bitmap + y + x, width * 3); /* Next line */ y += (map->width * 3); } return TRUE; } /* Draw a bitmap indicated by `bitmap' of size of `width' * 'height' into the SilcMap context into the coordinates `x' and `y' (the upper left corner of the bitmap will be at x and y). The `bitmap' must be RGB color bitmap. */ bool silc_map_draw(SilcMap map, SilcInt32 x, SilcInt32 y, const unsigned char *bitmap, SilcUInt32 width, SilcUInt32 height) { int i, k; unsigned char val; /* Compute coordinates in the bitmap */ y = (map->width * 3) * y; x = (x * 3); /* Draw the bitmap into the map bitmap */ for (i = 0; i < height; i++) { for (k = 0; k < width; k++) { val = bitmap[i * (width * 3) + (k * 3)]; map->bitmap[y + x + (k * 3) ] = val; /* R */ val = bitmap[i * (width * 3) + (k * 3) + 1]; map->bitmap[y + x + (k * 3) + 1] = val; /* G */ val = bitmap[i * (width * 3) + (k * 3) + 2]; map->bitmap[y + x + (k * 3) + 2] = val; /* B */ } /* Next line */ y += (map->width * 3); } return TRUE; } /* Same as silc_map_draw but the `bitmap' is a grayscale bitmap and the RGB color information is provided as argument to this function. */ bool silc_map_draw_raw(SilcMap map, SilcInt32 x, SilcInt32 y, const unsigned char *bitmap, SilcUInt32 width, SilcUInt32 height, SilcInt16 r, SilcInt16 g, SilcInt16 b) { int i, k; unsigned char val; /* Compute coordinates in the bitmap */ y = (map->width * 3) * y; x = (x * 3); /* Draw the bitmap into the map bitmap */ for (i = 0; i < height; i++) { for (k = 0; k < width; k++) { val = bitmap[i * width + k]; if (val != 0) { map->bitmap[y + x + (k * 3) ] = r; /* R */ map->bitmap[y + x + (k * 3) + 1] = g; /* G */ map->bitmap[y + x + (k * 3) + 2] = b; /* B */ } } /* Next line */ y += (map->width * 3); } return TRUE; } /* Draw a straight line between points a and b. The coordinates for the points are provided as arguments. The `width' is the line width in pixels. The RGB color for the line can be provided too. Implements DDA algorithm. */ bool silc_map_draw_line(SilcMap map, SilcUInt32 width, SilcInt32 a_x, SilcInt32 a_y, SilcInt32 b_x, SilcInt32 b_y, SilcInt16 r, SilcInt16 g, SilcInt16 b) { unsigned char p[3] = { r, g, b }; int xdiff, ydiff, i; double x, y, slox, sloy; SILC_LOG_DEBUG(("draw_line")); /* Compute the difference of points */ xdiff = b_x - a_x; ydiff = b_y - a_y; if (!xdiff && !ydiff) return FALSE; /* Draw the line */ if (abs(xdiff) > abs(ydiff)) { sloy = (double)ydiff / (double)xdiff; y = a_y + 0.5; /* rounding */ if (xdiff > 0) { for (x = a_x; x <= b_x; x++) { for (i = 0; i < width; i++) silc_map_draw(map, x + i, floor(y), p, 1, 1); y += sloy; } } else { for (x = a_x; x >= b_x; x--) { for (i = 0; i < width; i++) silc_map_draw(map, x + i, floor(y), p, 1, 1); y -= sloy; } } } else { slox = (double)xdiff / (double)ydiff; x = a_x + 0.5; /* rounding */ if (ydiff > 0) { for (y = a_y; y <= b_y; y++) { for (i = 0; i < width; i++) silc_map_draw(map, floor(x + i), y, p, 1, 1); x += slox; } } else { for (y = a_y; y >= b_y; y--) { for (i = 0; i < width; i++) silc_map_draw(map, floor(x + i), y, p, 1, 1); x -= slox; } } } return TRUE; } /* Print the text string `text' on the bitmap at `x' and `y'. The color for the text can be provided as argument. */ bool silc_map_draw_text(SilcMap map, const char *text, SilcInt32 x, SilcInt32 y, SilcInt16 r, SilcInt16 g, SilcInt16 b) { int k, w; int c; SILC_LOG_DEBUG(("draw_text")); /* Write the text. */ w = 0; for (k = 0; k < strlen(text); k++) { c = text[k] - 33; silc_map_draw_raw(map, x + w, y, map->font.font[c].data, map->font.font[c].width, map->font.height, r, g, b); w += map->font.font[c].width; } return TRUE; } /* Draw circle on the bitmap map at `x' and `y'. The center of the circle will be at the `x' and `y'. If the `label' is provided the text will appear with the circle at `lposx' and `lposy' in relation with the circle. */ bool silc_map_draw_circle(SilcMap map, SilcInt32 x, SilcInt32 y, SilcInt16 r, SilcInt16 g, SilcInt16 b, const char *label, SilcInt32 lposx, SilcInt32 lposy, SilcInt16 lr, SilcInt16 lg, SilcInt16 lb) { bool ret; SILC_LOG_DEBUG(("draw_circle")); y = y - (silc_map_circle.height / 2); x = x - (silc_map_circle.width / 2); ret = silc_map_draw_raw(map, x, y, silc_map_circle.data, silc_map_circle.width, silc_map_circle.height, r, g, b); if (!ret) return FALSE; if (label) ret = silc_map_draw_text(map, label, x + lposx, y - lposy, lr, lg, lb); return ret; } /* Draw rectangle on the bitmap map at `x' and `y'. The center of the rectangle will be at the `x' and `y'. If the `label' is provided the text will appear with the circle at `lposx' and `lposy' in relation with the circle. */ bool silc_map_draw_rectangle(SilcMap map, SilcInt32 x, SilcInt32 y, SilcInt16 r, SilcInt16 g, SilcInt16 b, const char *label, SilcInt32 lposx, SilcInt32 lposy, SilcInt16 lr, SilcInt16 lg, SilcInt16 lb) { bool ret; SILC_LOG_DEBUG(("draw_rectangle")); y = y - (silc_map_rectangle.height / 2); x = x - (silc_map_rectangle.width / 2); ret = silc_map_draw_raw(map, x, y, silc_map_rectangle.data, silc_map_rectangle.width, silc_map_rectangle.height, r, g, b); if (!ret) return FALSE; if (label) ret = silc_map_draw_text(map, label, x + lposx, y - lposy, lr, lg, lb); return ret; } /* Parses the degree position string. For example, longitude 40 23 10, as in 40 degrees, 23 minutes and 10 seconds east. Negative degree is to West. For latitude positive is north and negative south. */ double silc_map_parse_pos(char *pos) { double d = 0, m = 0, s = 0; int ret; ret = sscanf(pos, "%lf %lf %lf", &d, &m, &s); if (ret < 1) { fprintf(stderr, "Malfromed position string '%s'\n", pos); return 0; } return ((d < 0 ? -1 : d > 0 ? 1 : 0) * abs(d) + (m / 60) + (s / 3600)); } /* Converts longitude into position in the bitmap */ int silc_map_lon2x(SilcMap map, char *longitude) { double meridian, aspmul, lon; /* Parse position string */ lon = silc_map_parse_pos(longitude); /* Compute "aspect ratio multiplier" to get the position in the map. */ meridian = (double)map->width / (double)2.0; aspmul = meridian / 180.0; /* Compute the position in the bitmap map */ return (int)(double)(meridian + (lon * aspmul)); } /* Converts latitude into position in the bitmap */ int silc_map_lat2y(SilcMap map, char *latitude) { double meridian, aspmul, lat; /* Parse position string */ lat = silc_map_parse_pos(latitude); /* Compute "aspect ratio multiplier" to get the position in the map. */ meridian = (double)map->height / (double)2.0; aspmul = meridian / 90.0; /* Compute the position in the bitmap map */ return (int)(double)(meridian - (lat * aspmul)); } /* Parses RGB color string. */ bool silc_map_parse_color(const char *color, SilcInt16 *r, SilcInt16 *g, SilcInt16 *b) { int ret; int rr, gg, bb; ret = sscanf(color, "%d %d %d", &rr, &gg, &bb); if (ret < 3) { fprintf(stderr, "Invalid color string: %s\n", color); return FALSE; } *r = (SilcInt16)rr; *g = (SilcInt16)gg; *b = (SilcInt16)bb; return TRUE; } /* Loads a font file. The font file format is the following: height\n width font data width font data etc. If this function is called multiple times the new font replaces the old font. */ bool silc_map_load_font(SilcMap map, const char *filename) { FILE *fp; int i, x, y; /* Load the file */ fp = fopen(filename, "r"); if (!fp) { fprintf(stderr, "fopen: %s\n", strerror(errno)); return FALSE; } /* Read the font height */ i = fscanf(fp, "%d\n", &map->font.height); if (i < 1) return FALSE; /* Read the font data */ for (i = 0; i < 94; i++) { map->font.font[i].width = fgetc(fp); for (y = 0; y < map->font.height; y++) for (x = 0; x < map->font.font[i].width; x++) map->font.font[i].data[(y * map->font.font[i].width) + x] = fgetc(fp); } return TRUE; }