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