5f67b388c25d9090e6fedd4852ed1d906123a441
[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 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   fwrite(map->bitmap, map->bitsilc_map_size, 1, fp);
90   fclose(fp);
91
92   return retval;
93 }
94
95 /* Cut the map into a `width' * `height' size chunk at `x' and `y'.  This
96    returns the allocated map bitmap into `ret_bitmap'.  The original map
97    is not modified. */
98
99 bool silc_map_cut(SilcMap map, SilcInt32 x, SilcInt32 y,
100                   SilcUInt32 width, SilcUInt32 height,
101                   SilcMap *ret_map)
102 {
103   int i;
104
105   SILC_LOG_DEBUG(("cut"));
106
107   /* Sanity checks */
108   if (height > map->height - y) {
109     fprintf(stderr, "Requesting too much height: %ld\n", height);
110     return FALSE;
111   }
112   if (width > map->width - x) {
113     fprintf(stderr, "Requesting too much width: %ld\n", width);
114     return FALSE;
115   }
116
117   /* Compute coordinates in the bitmap */
118   y = (map->width * 3) * y;
119   x = (x * 3);
120
121   /* Allocate new SilcMap context */
122   *ret_map = silc_calloc(1, sizeof(**ret_map));
123   (*ret_map)->width = width;
124   (*ret_map)->height = height;
125   (*ret_map)->maxcolor = map->maxcolor;
126   (*ret_map)->bitsilc_map_size = (width * 3) * height;
127   (*ret_map)->bitmap = silc_malloc((*ret_map)->bitsilc_map_size);
128
129   /* Copy the requested area */
130   for (i = 0; i < height; i++) {
131     memcpy((*ret_map)->bitmap + (i * width * 3),
132            map->bitmap + y + x, width * 3);
133
134     /* Next line */
135     y += (map->width * 3);
136   }
137
138   return TRUE;
139 }
140
141 /* Draw a bitmap indicated by `bitmap' of size of `width' * 'height'
142    into the SilcMap context into the coordinates `x' and `y' (the upper left
143    corner of the bitmap will be at x and y).  The `bitmap' must be RGB
144    color bitmap. */
145
146 bool silc_map_draw(SilcMap map,
147                    SilcInt32 x, SilcInt32 y,
148                    const unsigned char *bitmap,
149                    SilcUInt32 width, SilcUInt32 height)
150 {
151   int i, k;
152   unsigned char val;
153
154   /* Compute coordinates in the bitmap */
155   y = (map->width * 3) * y;
156   x = (x * 3);
157
158   /* Draw the bitmap into the map bitmap */
159   for (i = 0; i < height; i++) {
160     for (k = 0; k < width; k++) {
161       val = bitmap[i * (width * 3) + (k * 3)];
162       map->bitmap[y + x + (k * 3)    ] = val;             /* R */
163
164       val = bitmap[i * (width * 3) + (k * 3) + 1];
165       map->bitmap[y + x + (k * 3) + 1] = val;             /* G */
166
167       val = bitmap[i * (width * 3) + (k * 3) + 2];
168       map->bitmap[y + x + (k * 3) + 2] = val;             /* B */
169     }
170
171     /* Next line */
172     y += (map->width * 3);
173   }
174
175   return TRUE;
176 }
177
178 /* Same as silc_map_draw but the `bitmap' is a grayscale bitmap
179    and the RGB color information is provided as argument to this function. */
180
181 bool silc_map_draw_raw(SilcMap map,
182                        SilcInt32 x, SilcInt32 y,
183                        const unsigned char *bitmap,
184                        SilcUInt32 width, SilcUInt32 height,
185                        SilcInt16 r, SilcInt16 g, SilcInt16 b)
186 {
187   int i, k;
188   unsigned char val;
189
190   /* Compute coordinates in the bitmap */
191   y = (map->width * 3) * y;
192   x = (x * 3);
193
194   /* Draw the bitmap into the map bitmap */
195   for (i = 0; i < height; i++) {
196     for (k = 0; k < width; k++) {
197       val = bitmap[i * width + k];
198       if (val != 0) {
199         map->bitmap[y + x + (k * 3)    ] = r;             /* R */
200         map->bitmap[y + x + (k * 3) + 1] = g;             /* G */
201         map->bitmap[y + x + (k * 3) + 2] = b;             /* B */
202       }
203     }
204
205     /* Next line */
206     y += (map->width * 3);
207   }
208
209   return TRUE;
210 }
211
212 /* Draw a straight line between points a and b.  The coordinates for the
213    points are provided as arguments.  The `width' is the line width in
214    pixels.  The RGB color for the line can be provided too.  Implements
215    DDA algorithm. */
216
217 bool silc_map_draw_line(SilcMap map, SilcUInt32 width,
218                         SilcInt32 a_x, SilcInt32 a_y,
219                         SilcInt32 b_x, SilcInt32 b_y,
220                         SilcInt16 r, SilcInt16 g, SilcInt16 b)
221 {
222   unsigned char p[3] = { r, g, b };
223   int xdiff, ydiff, i;
224   double x, y, slox, sloy;
225
226   SILC_LOG_DEBUG(("draw_line"));
227
228   /* Compute the difference of points */
229   xdiff = b_x - a_x;
230   ydiff = b_y - a_y;
231   if (!xdiff && !ydiff)
232     return FALSE;
233
234   /* Draw the line */
235   if (abs(xdiff) > abs(ydiff)) {
236     sloy = (double)ydiff / (double)xdiff;
237     y = a_y + 0.5;                       /* rounding */
238     if (xdiff > 0) {
239       for (x = a_x; x <= b_x; x++)  {
240         for (i = 0; i < width; i++)
241           silc_map_draw(map, x + i, floor(y), p, 1, 1);
242         y += sloy;
243       }
244     } else {
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     }
251   } else  {
252     slox = (double)xdiff / (double)ydiff;
253     x = a_x + 0.5;                       /* rounding */
254     if (ydiff > 0) {
255       for (y = a_y; y <= b_y; y++)  {
256         for (i = 0; i < width; i++)
257           silc_map_draw(map, floor(x + i), y, p, 1, 1);
258         x += slox;
259       }
260     } else {
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     }
267   }
268
269   return TRUE;
270 }
271
272 /* Print the text string `text' on the bitmap at `x' and `y'.  The color
273    for the text can be provided as argument. */
274
275 bool silc_map_draw_text(SilcMap map, const char *text,
276                         SilcInt32 x, SilcInt32 y,
277                         SilcInt16 r, SilcInt16 g, SilcInt16 b)
278 {
279   int k, w;
280   int c;
281
282   SILC_LOG_DEBUG(("draw_text"));
283
284   /* Write the text. */
285   w = 0;
286   for (k = 0; k < strlen(text); k++) {
287     c = text[k] - 33;
288     silc_map_draw_raw(map, x + w, y,
289                       map->font.font[c].data,
290                       map->font.font[c].width,
291                       map->font.height, r, g, b);
292     w += map->font.font[c].width;
293   }
294
295   return TRUE;
296 }
297
298 /* Draw circle on the bitmap map at `x' and `y'.  The center of the
299    circle will be at the `x' and `y'.  If the `label' is provided the
300    text will appear with the circle at `lposx' and `lposy' in relation
301    with the circle. */
302
303 bool silc_map_draw_circle(SilcMap map, SilcInt32 x, SilcInt32 y,
304                           SilcInt16 r, SilcInt16 g, SilcInt16 b,
305                           const char *label, SilcInt32 lposx, SilcInt32 lposy,
306                           SilcInt16 lr, SilcInt16 lg, SilcInt16 lb)
307 {
308   bool ret;
309
310   SILC_LOG_DEBUG(("draw_circle"));
311
312   y = y - (silc_map_circle.height / 2);
313   x = x - (silc_map_circle.width / 2);
314
315   ret = silc_map_draw_raw(map, x, y,
316                           silc_map_circle.data,
317                           silc_map_circle.width, silc_map_circle.height,
318                           r, g, b);
319   if (!ret)
320     return FALSE;
321
322   if (label)
323     ret = silc_map_draw_text(map, label, x + lposx, y - lposy, lr, lg, lb);
324
325   return ret;
326 }
327
328 /* Draw rectangle on the bitmap map at `x' and `y'.  The center of the
329    rectangle will be at the `x' and `y'.  If the `label' is provided the
330    text will appear with the circle at `lposx' and `lposy' in relation
331    with the circle. */
332
333 bool silc_map_draw_rectangle(SilcMap map, SilcInt32 x, SilcInt32 y,
334                              SilcInt16 r, SilcInt16 g, SilcInt16 b,
335                              const char *label,
336                              SilcInt32 lposx, SilcInt32 lposy,
337                              SilcInt16 lr, SilcInt16 lg, SilcInt16 lb)
338 {
339   bool ret;
340
341   SILC_LOG_DEBUG(("draw_rectangle"));
342
343   y = y - (silc_map_rectangle.height / 2);
344   x = x - (silc_map_rectangle.width / 2);
345
346   ret = silc_map_draw_raw(map, x, y,
347                           silc_map_rectangle.data, silc_map_rectangle.width,
348                           silc_map_rectangle.height,
349                           r, g, b);
350   if (!ret)
351     return FALSE;
352
353   if (label)
354     ret = silc_map_draw_text(map, label, x + lposx, y - lposy, lr, lg, lb);
355
356   return ret;
357 }
358
359 /* Parses the degree position string.  For example, longitude 40 23 10,
360    as in 40 degrees, 23 minutes and 10 seconds east.  Negative degree is to
361    West.  For latitude positive is north and negative south. */
362
363 double silc_map_parse_pos(char *pos)
364 {
365   double d = 0, m = 0, s = 0;
366   int ret;
367
368   ret = sscanf(pos, "%lf %lf %lf", &d, &m, &s);
369   if (ret < 1) {
370     fprintf(stderr, "Malfromed position string '%s'\n", pos);
371     return 0;
372   }
373
374   if (d < 0) {
375     m = (m < 0 ? m : -m);
376     s = (s < 0 ? s : -s);
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: %s\n", strerror(errno), filename);
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 }