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