1df1a35850d5c2b5125e8c6b8ec00aca565ebdf7
[silc.git] / apps / silcmap / silcmap_bitmap.c
1 /*
2
3   silcmap_bitmap.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2003 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19
20 #include "silcincludes.h"
21 #include "silcclient.h"
22 #include <math.h>
23 #include "silcmap.h"
24 #include "data.h"
25
26 /******* Bitmap Routines *****************************************************/
27
28 /* Load a bitmap file.  The file is the map image that is loaded into
29    the SilcMap context.  This is no perfect PPM loader. */
30
31 bool silc_map_load_ppm(SilcMap map, const char *filename)
32 {
33   FILE *fp;
34   char type[3];
35   int ret, retval = TRUE;
36
37   SILC_LOG_DEBUG(("Load PPM '%s'", filename));
38
39   fp = fopen(filename, "r");
40   if (!fp) {
41     fprintf(stderr, "fopen: %s: %s\n", strerror(errno), filename);
42     return FALSE;
43   }
44
45   /* Read width and height */
46   ret = fscanf(fp, "%s %ld %ld %ld ",
47                type, &map->width, &map->height, &map->maxcolor);
48   if (ret < 4) {
49     fprintf(stderr, "Invalid PPM file");
50     retval = FALSE;
51     goto out;
52   }
53
54   /* Read the picture */
55   map->bitsilc_map_size = map->width * 3 * map->height;
56   map->bitmap = silc_malloc(map->bitsilc_map_size);
57   ret = fread(map->bitmap, map->bitsilc_map_size, 1, fp);
58   if (ret < 0) {
59     fprintf(stderr, "fread: %s\n", strerror(errno));
60     retval = FALSE;
61     goto out;
62   }
63
64  out:
65   fclose(fp);
66   return retval;
67 }
68
69 /* Write the map into a bitmap file. */
70
71 bool silc_map_write_ppm(SilcMap map, const char *filename)
72 {
73   FILE *fp;
74   int retval = TRUE;
75   int i, k;
76
77   SILC_LOG_DEBUG(("Write PPM '%s'", filename));
78
79   fp = fopen(filename, "w+");
80   if (!fp) {
81     fprintf(stderr, "fopen: %s: %s\n", strerror(errno), filename);
82     return FALSE;
83   }
84
85   /* Write the header */
86   fprintf(fp, "P6 %ld %ld %ld\n", map->width, map->height, map->maxcolor);
87
88   /* Write the bitmap */
89   for (i = 0; i < map->height; i++)
90     for (k = 0; k < map->width; k++) {
91       putc(map->bitmap[i * (map->width * 3) + (k * 3)    ], fp);   /* R */
92       putc(map->bitmap[i * (map->width * 3) + (k * 3) + 1], fp);   /* G */
93       putc(map->bitmap[i * (map->width * 3) + (k * 3) + 2], fp);   /* B */
94     }
95
96   fclose(fp);
97   return retval;
98 }
99
100 /* Cut the map into a `width' * `height' size chunk at `x' and `y'.  This
101    returns the allocated map bitmap into `ret_bitmap'.  The original map
102    is not modified. */
103
104 bool silc_map_cut(SilcMap map, SilcInt32 x, SilcInt32 y,
105                   SilcUInt32 width, SilcUInt32 height,
106                   SilcMap *ret_map)
107 {
108   int i;
109
110   SILC_LOG_DEBUG(("cut"));
111
112   /* Sanity checks */
113   if (height > map->height - y) {
114     fprintf(stderr, "Requesting too much height: %ld\n", height);
115     return FALSE;
116   }
117   if (width > map->width - x) {
118     fprintf(stderr, "Requesting too much width: %ld\n", width);
119     return FALSE;
120   }
121
122   /* Compute coordinates in the bitmap */
123   y = (map->width * 3) * y;
124   x = (x * 3);
125
126   /* Allocate new SilcMap context */
127   *ret_map = silc_calloc(1, sizeof(**ret_map));
128   (*ret_map)->width = width;
129   (*ret_map)->height = height;
130   (*ret_map)->maxcolor = map->maxcolor;
131   (*ret_map)->bitsilc_map_size = (width * 3) * height;
132   (*ret_map)->bitmap = silc_malloc((*ret_map)->bitsilc_map_size);
133
134   /* Copy the requested area */
135   for (i = 0; i < height; i++) {
136     memcpy((*ret_map)->bitmap + (i * width * 3),
137            map->bitmap + y + x, width * 3);
138
139     /* Next line */
140     y += (map->width * 3);
141   }
142
143   return TRUE;
144 }
145
146 /* Draw a bitmap indicated by `bitmap' of size of `width' * 'height'
147    into the SilcMap context into the coordinates `x' and `y' (the upper left
148    corner of the bitmap will be at x and y).  The `bitmap' must be RGB
149    color bitmap. */
150
151 bool silc_map_draw(SilcMap map,
152                    SilcInt32 x, SilcInt32 y,
153                    const unsigned char *bitmap,
154                    SilcUInt32 width, SilcUInt32 height)
155 {
156   int i, k;
157   unsigned char val;
158
159   /* Compute coordinates in the bitmap */
160   y = (map->width * 3) * y;
161   x = (x * 3);
162
163   /* Draw the bitmap into the map bitmap */
164   for (i = 0; i < height; i++) {
165     for (k = 0; k < width; k++) {
166       val = bitmap[i * (width * 3) + (k * 3)];
167       map->bitmap[y + x + (k * 3)    ] = val;             /* R */
168
169       val = bitmap[i * (width * 3) + (k * 3) + 1];
170       map->bitmap[y + x + (k * 3) + 1] = val;             /* G */
171
172       val = bitmap[i * (width * 3) + (k * 3) + 2];
173       map->bitmap[y + x + (k * 3) + 2] = val;             /* B */
174     }
175
176     /* Next line */
177     y += (map->width * 3);
178   }
179
180   return TRUE;
181 }
182
183 /* Same as silc_map_draw but the `bitmap' is a grayscale bitmap
184    and the RGB color information is provided as argument to this function. */
185
186 bool silc_map_draw_raw(SilcMap map,
187                        SilcInt32 x, SilcInt32 y,
188                        const unsigned char *bitmap,
189                        SilcUInt32 width, SilcUInt32 height,
190                        SilcInt16 r, SilcInt16 g, SilcInt16 b)
191 {
192   int i, k;
193   unsigned char val;
194
195   /* Compute coordinates in the bitmap */
196   y = (map->width * 3) * y;
197   x = (x * 3);
198
199   /* Draw the bitmap into the map bitmap */
200   for (i = 0; i < height; i++) {
201     for (k = 0; k < width; k++) {
202       val = bitmap[i * width + k];
203       if (val != 0) {
204         map->bitmap[y + x + (k * 3)    ] = r;             /* R */
205         map->bitmap[y + x + (k * 3) + 1] = g;             /* G */
206         map->bitmap[y + x + (k * 3) + 2] = b;             /* B */
207       }
208     }
209
210     /* Next line */
211     y += (map->width * 3);
212   }
213
214   return TRUE;
215 }
216
217 /* Draw a straight line between points a and b.  The coordinates for the
218    points are provided as arguments.  The `width' is the line width in
219    pixels.  The RGB color for the line can be provided too.  Implements
220    DDA algorithm. */
221
222 bool silc_map_draw_line(SilcMap map, SilcUInt32 width,
223                         SilcInt32 a_x, SilcInt32 a_y,
224                         SilcInt32 b_x, SilcInt32 b_y,
225                         SilcInt16 r, SilcInt16 g, SilcInt16 b)
226 {
227   unsigned char p[3] = { r, g, b };
228   int xdiff, ydiff, i;
229   double x, y, slox, sloy;
230
231   SILC_LOG_DEBUG(("draw_line"));
232
233   /* Compute the difference of points */
234   xdiff = b_x - a_x;
235   ydiff = b_y - a_y;
236   if (!xdiff && !ydiff)
237     return FALSE;
238
239   /* Draw the line */
240   if (abs(xdiff) > abs(ydiff)) {
241     sloy = (double)ydiff / (double)xdiff;
242     y = a_y + 0.5;                       /* rounding */
243     if (xdiff > 0) {
244       for (x = a_x; x <= b_x; x++)  {
245         for (i = 0; i < width; i++)
246           silc_map_draw(map, x + i, floor(y), p, 1, 1);
247         y += sloy;
248       }
249     } else {
250       for (x = a_x; x >= b_x; x--)  {
251         for (i = 0; i < width; i++)
252           silc_map_draw(map, x + i, floor(y), p, 1, 1);
253         y -= sloy;
254       }
255     }
256   } else  {
257     slox = (double)xdiff / (double)ydiff;
258     x = a_x + 0.5;                       /* rounding */
259     if (ydiff > 0) {
260       for (y = a_y; y <= b_y; y++)  {
261         for (i = 0; i < width; i++)
262           silc_map_draw(map, floor(x + i), y, p, 1, 1);
263         x += slox;
264       }
265     } else {
266       for (y = a_y; y >= b_y; y--)  {
267         for (i = 0; i < width; i++)
268           silc_map_draw(map, floor(x + i), y, p, 1, 1);
269         x -= slox;
270       }
271     }
272   }
273
274   return TRUE;
275 }
276
277 /* Print the text string `text' on the bitmap at `x' and `y'.  The color
278    for the text can be provided as argument. */
279
280 bool silc_map_draw_text(SilcMap map, const char *text,
281                         SilcInt32 x, SilcInt32 y,
282                         SilcInt16 r, SilcInt16 g, SilcInt16 b)
283 {
284   int k, w;
285   int c;
286
287   SILC_LOG_DEBUG(("draw_text"));
288
289   /* Write the text. */
290   w = 0;
291   for (k = 0; k < strlen(text); k++) {
292     c = text[k] - 33;
293     silc_map_draw_raw(map, x + w, y,
294                       map->font.font[c].data,
295                       map->font.font[c].width,
296                       map->font.height, r, g, b);
297     w += map->font.font[c].width;
298   }
299
300   return TRUE;
301 }
302
303 /* Draw circle on the bitmap map at `x' and `y'.  The center of the
304    circle will be at the `x' and `y'.  If the `label' is provided the
305    text will appear with the circle at `lposx' and `lposy' in relation
306    with the circle. */
307
308 bool silc_map_draw_circle(SilcMap map, SilcInt32 x, SilcInt32 y,
309                           SilcInt16 r, SilcInt16 g, SilcInt16 b,
310                           const char *label, SilcInt32 lposx, SilcInt32 lposy,
311                           SilcInt16 lr, SilcInt16 lg, SilcInt16 lb)
312 {
313   bool ret;
314
315   SILC_LOG_DEBUG(("draw_circle"));
316
317   y = y - (silc_map_circle.height / 2);
318   x = x - (silc_map_circle.width / 2);
319
320   ret = silc_map_draw_raw(map, x, y,
321                           silc_map_circle.data,
322                           silc_map_circle.width, silc_map_circle.height,
323                           r, g, b);
324   if (!ret)
325     return FALSE;
326
327   if (label)
328     ret = silc_map_draw_text(map, label, x + lposx, y - lposy, lr, lg, lb);
329
330   return ret;
331 }
332
333 /* Draw rectangle on the bitmap map at `x' and `y'.  The center of the
334    rectangle will be at the `x' and `y'.  If the `label' is provided the
335    text will appear with the circle at `lposx' and `lposy' in relation
336    with the circle. */
337
338 bool silc_map_draw_rectangle(SilcMap map, SilcInt32 x, SilcInt32 y,
339                              SilcInt16 r, SilcInt16 g, SilcInt16 b,
340                              const char *label,
341                              SilcInt32 lposx, SilcInt32 lposy,
342                              SilcInt16 lr, SilcInt16 lg, SilcInt16 lb)
343 {
344   bool ret;
345
346   SILC_LOG_DEBUG(("draw_rectangle"));
347
348   y = y - (silc_map_rectangle.height / 2);
349   x = x - (silc_map_rectangle.width / 2);
350
351   ret = silc_map_draw_raw(map, x, y,
352                           silc_map_rectangle.data, silc_map_rectangle.width,
353                           silc_map_rectangle.height,
354                           r, g, b);
355   if (!ret)
356     return FALSE;
357
358   if (label)
359     ret = silc_map_draw_text(map, label, x + lposx, y - lposy, lr, lg, lb);
360
361   return ret;
362 }
363
364 /* Parses the degree position string.  For example, longitude 40 23 10,
365    as in 40 degrees, 23 minutes and 10 seconds east.  Negative degree is to
366    West.  For latitude positive is north and negative south. */
367
368 double silc_map_parse_pos(char *pos)
369 {
370   double d = 0, m = 0, s = 0;
371   int ret;
372
373   ret = sscanf(pos, "%lf %lf %lf", &d, &m, &s);
374   if (ret < 1) {
375     fprintf(stderr, "Malfromed position string '%s'\n", pos);
376     return 0;
377   }
378
379   return ((d < 0 ? -1 : d > 0 ? 1 : 0) *
380           abs(d) + (m / 60) + (s / 3600));
381 }
382
383 /* Converts longitude into position in the bitmap */
384
385 int silc_map_lon2x(SilcMap map, char *longitude)
386 {
387   double meridian, aspmul, lon;
388
389   /* Parse position string */
390   lon = silc_map_parse_pos(longitude);
391
392   /* Compute "aspect ratio multiplier" to get the position in the map. */
393   meridian = (double)map->width / (double)2.0;
394   aspmul = meridian / 180.0;
395
396   /* Compute the position in the bitmap map */
397   return (int)(double)(meridian + (lon * aspmul));
398 }
399
400 /* Converts latitude into position in the bitmap */
401
402 int silc_map_lat2y(SilcMap map, char *latitude)
403 {
404   double meridian, aspmul, lat;
405
406   /* Parse position string */
407   lat = silc_map_parse_pos(latitude);
408
409   /* Compute "aspect ratio multiplier" to get the position in the map. */
410   meridian = (double)map->height / (double)2.0;
411   aspmul = meridian / 90.0;
412
413   /* Compute the position in the bitmap map */
414   return (int)(double)(meridian - (lat * aspmul));
415 }
416
417 /* Parses RGB color string. */
418
419 bool silc_map_parse_color(const char *color,
420                           SilcInt16 *r, SilcInt16 *g, SilcInt16 *b)
421 {
422   int ret;
423   int rr, gg, bb;
424
425   ret = sscanf(color, "%d %d %d", &rr, &gg, &bb);
426   if (ret < 3) {
427     fprintf(stderr, "Invalid color string: %s\n", color);
428     return FALSE;
429   }
430
431   *r = (SilcInt16)rr;
432   *g = (SilcInt16)gg;
433   *b = (SilcInt16)bb;
434
435   return TRUE;
436 }
437
438 /* Loads a font file.  The font file format is the following:
439
440    height\n
441    width
442    font data
443    width
444    font data
445    etc.
446
447    If this function is called multiple times the new font replaces the
448    old font. */
449
450 bool silc_map_load_font(SilcMap map, const char *filename)
451 {
452   FILE *fp;
453   int i, x, y;
454
455   /* Load the file */
456   fp = fopen(filename, "r");
457   if (!fp) {
458     fprintf(stderr, "fopen: %s\n", strerror(errno));
459     return FALSE;
460   }
461
462   /* Read the font height */
463   i = fscanf(fp, "%d\n", &map->font.height);
464   if (i < 1)
465     return FALSE;
466
467   /* Read the font data */
468   for (i = 0; i < 94; i++) {
469     map->font.font[i].width = fgetc(fp);
470
471     for (y = 0; y < map->font.height; y++)
472       for (x = 0; x < map->font.font[i].width; x++)
473         map->font.font[i].data[(y * map->font.font[i].width) + x] = fgetc(fp);
474   }
475
476   return TRUE;
477 }