Take the sign for minutes and second from degrees sign
[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   if (d < 0) {
380     m = (m < 0 ? m : -m);
381     s = (s < 0 ? s : -s);
382   }
383
384   return ((d < 0 ? -1 : d > 0 ? 1 : 0) *
385           abs(d) + (m / 60) + (s / 3600));
386 }
387
388 /* Converts longitude into position in the bitmap */
389
390 int silc_map_lon2x(SilcMap map, char *longitude)
391 {
392   double meridian, aspmul, lon;
393
394   /* Parse position string */
395   lon = silc_map_parse_pos(longitude);
396
397   /* Compute "aspect ratio multiplier" to get the position in the map. */
398   meridian = (double)map->width / (double)2.0;
399   aspmul = meridian / 180.0;
400
401   /* Compute the position in the bitmap map */
402   return (int)(double)(meridian + (lon * aspmul));
403 }
404
405 /* Converts latitude into position in the bitmap */
406
407 int silc_map_lat2y(SilcMap map, char *latitude)
408 {
409   double meridian, aspmul, lat;
410
411   /* Parse position string */
412   lat = silc_map_parse_pos(latitude);
413
414   /* Compute "aspect ratio multiplier" to get the position in the map. */
415   meridian = (double)map->height / (double)2.0;
416   aspmul = meridian / 90.0;
417
418   /* Compute the position in the bitmap map */
419   return (int)(double)(meridian - (lat * aspmul));
420 }
421
422 /* Parses RGB color string. */
423
424 bool silc_map_parse_color(const char *color,
425                           SilcInt16 *r, SilcInt16 *g, SilcInt16 *b)
426 {
427   int ret;
428   int rr, gg, bb;
429
430   ret = sscanf(color, "%d %d %d", &rr, &gg, &bb);
431   if (ret < 3) {
432     fprintf(stderr, "Invalid color string: %s\n", color);
433     return FALSE;
434   }
435
436   *r = (SilcInt16)rr;
437   *g = (SilcInt16)gg;
438   *b = (SilcInt16)bb;
439
440   return TRUE;
441 }
442
443 /* Loads a font file.  The font file format is the following:
444
445    height\n
446    width
447    font data
448    width
449    font data
450    etc.
451
452    If this function is called multiple times the new font replaces the
453    old font. */
454
455 bool silc_map_load_font(SilcMap map, const char *filename)
456 {
457   FILE *fp;
458   int i, x, y;
459
460   /* Load the file */
461   fp = fopen(filename, "r");
462   if (!fp) {
463     fprintf(stderr, "fopen: %s\n", strerror(errno));
464     return FALSE;
465   }
466
467   /* Read the font height */
468   i = fscanf(fp, "%d\n", &map->font.height);
469   if (i < 1)
470     return FALSE;
471
472   /* Read the font data */
473   for (i = 0; i < 94; i++) {
474     map->font.font[i].width = fgetc(fp);
475
476     for (y = 0; y < map->font.height; y++)
477       for (x = 0; x < map->font.font[i].width; x++)
478         map->font.font[i].data[(y * map->font.font[i].width) + x] = fgetc(fp);
479   }
480
481   return TRUE;
482 }