Contents of /trunk/src/osm-gps-map-osd-classic.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 74 - (show annotations)
Mon Aug 24 09:23:36 2009 UTC (14 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 14364 byte(s)
OSD color matching
1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
2 /* vim:set et sw=4 ts=4 cino=t0,(0: */
3 /*
4 * Copyright (C) Till Harbaum 2009 <till@harbaum.org>
5 *
6 * osm-gps-map is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * osm-gps-map is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "config.h"
21 #include <stdlib.h> // abs
22 #include <math.h> // M_PI
23
24 /* parameters that can be overwritten from the config file: */
25 /* OSM_GPS_MAP_OSD_DIAMETER */
26
27 #ifndef USE_CAIRO
28 #error "OSD control display lacks a non-cairo implementation!"
29 #endif
30
31 #include <cairo.h>
32
33 #include "osm-gps-map.h"
34 #include "osm-gps-map-osd-classic.h"
35
36 //the osd controls
37 typedef struct {
38 /* the offscreen representation of the OSD */
39 cairo_surface_t *overlay;
40
41 /* stuff required to restore the original map content */
42 GdkPixmap *backup;
43 gint backup_x, backup_y;
44
45 GdkColor bg, fg, da;
46
47 } osd_priv_t;
48
49 /* position and extent of bounding box */
50 #define OSD_X (10)
51 #define OSD_Y (10)
52
53 /* parameters of the direction shape */
54 #ifndef OSM_GPS_MAP_OSD_DIAMETER
55 #define D_RAD (30) // diameter of dpad
56 #else
57 #define D_RAD (OSM_GPS_MAP_OSD_DIAMETER)
58 #endif
59 #define D_TIP (4*D_RAD/5) // distance of arrow tip from dpad center
60 #define D_LEN (D_RAD/4) // length of arrow
61 #define D_WID (D_LEN) // width of arrow
62
63 /* parameters of the "zoom" pad */
64 #define Z_STEP (D_RAD/4) // distance between dpad and zoom
65 #define Z_RAD (D_RAD/2) // radius of "caps" of zoom bar
66
67 #ifdef OSD_SHADOW_ENABLE
68 /* shadow also depends on control size */
69 #define OSD_SHADOW (D_RAD/6)
70 #else
71 #define OSD_SHADOW (0)
72 #endif
73
74 /* total width and height of controls incl. shadow */
75 #define OSD_W (2*D_RAD + OSD_SHADOW)
76 #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
77
78 #ifdef OSD_SHADOW_ENABLE
79 #define OSD_LBL_SHADOW (OSD_SHADOW/2)
80 #endif
81
82 #define Z_TOP (2 * D_RAD + Z_STEP)
83 #define Z_MID (Z_TOP + Z_RAD)
84 #define Z_BOT (Z_MID + Z_RAD)
85 #define Z_LEFT (Z_RAD)
86 #define Z_RIGHT (2 * D_RAD - Z_RAD)
87
88
89 /* create the cairo shape used for the zoom buttons */
90 static void
91 osm_gps_map_osd_zoom_shape(cairo_t *cr, gint x, gint y)
92 {
93 cairo_move_to (cr, x+Z_LEFT, y+Z_TOP);
94 cairo_line_to (cr, x+Z_RIGHT, y+Z_TOP);
95 cairo_arc (cr, x+Z_RIGHT, y+Z_MID, Z_RAD, -M_PI/2, M_PI/2);
96 cairo_line_to (cr, x+Z_LEFT, y+Z_BOT);
97 cairo_arc (cr, x+Z_LEFT, y+Z_MID, Z_RAD, M_PI/2, -M_PI/2);
98 }
99
100 /* create the cairo shape used for the dpad */
101 static void
102 osm_gps_map_osd_dpad_shape(cairo_t *cr, gint x, gint y)
103 {
104 cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
105 }
106
107 static gboolean
108 osm_gps_map_in_circle(gint x, gint y, gint cx, gint cy, gint rad)
109 {
110 return( pow(cx - x, 2) + pow(cy - y, 2) < rad * rad);
111 }
112
113 /* check whether x/y is within the dpad */
114 static osd_button_t
115 osm_gps_map_osd_check_dpad(gint x, gint y)
116 {
117 /* within entire dpad circle */
118 if( osm_gps_map_in_circle(x, y, OSD_X + D_RAD, OSD_Y + D_RAD, D_RAD))
119 {
120 /* convert into position relative to dpads centre */
121 x -= (OSD_X + D_RAD);
122 y -= (OSD_Y + D_RAD);
123
124 /* check for dpad center goes here! */
125 if( osm_gps_map_in_circle(x, y, 0, 0, D_RAD/3))
126 return OSD_GPS;
127
128 if( y < 0 && abs(x) < abs(y))
129 return OSD_UP;
130
131 if( y > 0 && abs(x) < abs(y))
132 return OSD_DOWN;
133
134 if( x < 0 && abs(y) < abs(x))
135 return OSD_LEFT;
136
137 if( x > 0 && abs(y) < abs(x))
138 return OSD_RIGHT;
139
140 return OSD_BG;
141 }
142 return OSD_NONE;
143 }
144
145 /* check whether x/y is within the zoom pads */
146 static osd_button_t
147 osm_gps_map_osd_check_zoom(gint x, gint y) {
148 if( x > OSD_X && x < (OSD_X + OSD_W) && y > Z_TOP && y < (OSD_Y+Z_BOT)) {
149
150 /* within circle around (-) label */
151 if( osm_gps_map_in_circle(x, y, OSD_X + Z_LEFT, OSD_Y + Z_MID, Z_RAD))
152 return OSD_OUT;
153
154 /* between center of (-) button and center of entire zoom control area */
155 if(x > OSD_LEFT && x < OSD_X + D_RAD)
156 return OSD_OUT;
157
158 /* within circle around (+) label */
159 if( osm_gps_map_in_circle(x, y, OSD_X + Z_RIGHT, OSD_Y + Z_MID, Z_RAD))
160 return OSD_IN;
161
162 /* between center of (+) button and center of entire zoom control area */
163 if(x < OSD_RIGHT && x > OSD_X + D_RAD)
164 return OSD_IN;
165 }
166
167 return OSD_NONE;
168 }
169
170 static osd_button_t
171 osm_gps_map_osd_check(gint x, gint y) {
172 osd_button_t but = OSD_NONE;
173
174 /* first do a rough test for the OSD area. */
175 /* this is just to avoid an unnecessary detailed test */
176 if(x > OSD_X && x < OSD_X + OSD_W &&
177 y > OSD_Y && y < OSD_Y + OSD_H) {
178 but = osm_gps_map_osd_check_dpad(x, y);
179
180 if(but == OSD_NONE)
181 but = osm_gps_map_osd_check_zoom(x, y);
182 }
183
184 return but;
185 }
186
187 #ifdef OSD_SHADOW_ENABLE
188 static void
189 osm_gps_map_osd_shape_shadow(cairo_t *cr) {
190 cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
191 cairo_fill (cr);
192 cairo_stroke (cr);
193 }
194 #endif
195
196 #ifndef OSD_COLOR
197 /* if no color has been specified we just use the gdks default colors */
198 static void
199 osm_gps_map_osd_shape(cairo_t *cr, GdkColor *bg, GdkColor *fg) {
200 gdk_cairo_set_source_color(cr, bg);
201 cairo_fill_preserve (cr);
202 gdk_cairo_set_source_color(cr, fg);
203 cairo_set_line_width (cr, 1);
204 cairo_stroke (cr);
205 }
206 #else
207 static void
208 osm_gps_map_osd_shape(cairo_t *cr) {
209 cairo_set_source_rgb (cr, OSD_COLOR_BG);
210 cairo_fill_preserve (cr);
211 cairo_set_source_rgb (cr, OSD_COLOR);
212 cairo_set_line_width (cr, 1);
213 cairo_stroke (cr);
214 }
215 #endif
216
217 static void
218 osm_gps_map_osd_dpad_labels(cairo_t *cr, gint x, gint y) {
219 /* move reference to dpad center */
220 x += D_RAD;
221 y += D_RAD;
222
223 const static gint offset[][3][2] = {
224 /* left arrow/triangle */
225 { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
226 /* right arrow/triangle */
227 { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
228 /* top arrow/triangle */
229 { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
230 /* bottom arrow/triangle */
231 { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
232 };
233
234 int i;
235 for(i=0;i<4;i++) {
236 cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
237 cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
238 cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
239 }
240 }
241
242 /* draw the sattelite dish icon in the center of the dpad */
243 #define GPS_V0 (D_RAD/8)
244 #define GPS_V1 (D_RAD/10)
245 #define GPS_V2 (D_RAD/5)
246
247 /* draw a satellite receiver dish */
248 static void
249 osm_gps_map_osd_dpad_gps(cairo_t *cr, gint x, gint y) {
250 /* move reference to dpad center */
251 x += D_RAD;
252 y += D_RAD + GPS_V0;
253
254 cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
255 cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
256 cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
257 cairo_close_path (cr);
258
259 cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
260 cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
261 cairo_close_path (cr);
262
263 x += GPS_V1;
264 cairo_move_to (cr, x, y-GPS_V2);
265 cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
266 }
267
268 #define Z_LEN (2*Z_RAD/3)
269
270 static void
271 osm_gps_map_osd_zoom_labels(cairo_t *cr, gint x, gint y) {
272 cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
273 cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
274
275 cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
276 cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
277 cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
278 cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
279 }
280
281 #ifndef OSD_COLOR
282 /* if no color has been specified we just use the gdks default colors */
283 static void
284 osm_gps_map_osd_labels(cairo_t *cr, gint width, gboolean enabled,
285 GdkColor *fg, GdkColor *disabled) {
286 if(enabled) gdk_cairo_set_source_color(cr, fg);
287 else gdk_cairo_set_source_color(cr, disabled);
288 cairo_set_line_width (cr, width);
289 cairo_stroke (cr);
290 }
291 #else
292 static void
293 osm_gps_map_osd_labels(cairo_t *cr, gint width, gboolean enabled) {
294 if(enabled) cairo_set_source_rgb (cr, OSD_COLOR);
295 else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED);
296 cairo_set_line_width (cr, width);
297 cairo_stroke (cr);
298 }
299 #endif
300
301 #ifdef OSD_SHADOW_ENABLE
302 static void
303 osm_gps_map_osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) {
304 cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15);
305 cairo_set_line_width (cr, width);
306 cairo_stroke (cr);
307 }
308 #endif
309
310 static void
311 osm_gps_map_osd_render(osm_gps_map_osd_t *osd) {
312 osd_priv_t *priv = (osd_priv_t*)osd->priv;
313
314 /* first fill with transparency */
315 cairo_t *cr = cairo_create(priv->overlay);
316 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
317 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
318 cairo_paint(cr);
319
320 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
321
322 /* --------- draw zoom and dpad shape shadow ----------- */
323 gint x = 0, y = 0;
324
325 #ifdef OSD_SHADOW_ENABLE
326 osm_gps_map_osd_zoom_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW);
327 osm_gps_map_osd_shape_shadow(cr);
328 osm_gps_map_osd_dpad_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW);
329 osm_gps_map_osd_shape_shadow(cr);
330 #endif
331
332 /* --------- draw zoom and dpad shape ----------- */
333
334 osm_gps_map_osd_zoom_shape(cr, x, y);
335 #ifndef OSD_COLOR
336 osm_gps_map_osd_shape(cr, &priv->bg, &priv->fg);
337 #else
338 osm_gps_map_osd_shape(cr);
339 #endif
340 osm_gps_map_osd_dpad_shape(cr, x, y);
341 #ifndef OSD_COLOR
342 osm_gps_map_osd_shape(cr, &priv->bg, &priv->fg);
343 #else
344 osm_gps_map_osd_shape(cr);
345 #endif
346
347 /* --------- draw zoom and dpad labels --------- */
348
349 #ifdef OSD_SHADOW_ENABLE
350 osm_gps_map_osd_zoom_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
351 osm_gps_map_osd_dpad_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
352 osm_gps_map_osd_labels_shadow(cr, Z_RAD/3, TRUE);
353 osm_gps_map_osd_dpad_gps(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW);
354 osm_gps_map_osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
355 #endif
356
357 osm_gps_map_osd_zoom_labels(cr, x, y);
358 osm_gps_map_osd_dpad_labels(cr, x, y);
359 #ifndef OSD_COLOR
360 osm_gps_map_osd_labels(cr, Z_RAD/3, TRUE, &priv->fg, &priv->da);
361 #else
362 osm_gps_map_osd_labels(cr, Z_RAD/3, TRUE);
363 #endif
364 osm_gps_map_osd_dpad_gps(cr, x, y);
365 #ifndef OSD_COLOR
366 osm_gps_map_osd_labels(cr, Z_RAD/6, osd->cb != NULL, &priv->fg, &priv->da);
367 #else
368 osm_gps_map_osd_labels(cr, Z_RAD/6, osd->cb != NULL);
369 #endif
370
371 cairo_destroy(cr);
372 }
373
374 static void
375 osm_gps_map_osd_draw(osm_gps_map_osd_t *osd, gint xoffset, gint yoffset)
376 {
377 osd_priv_t *priv = (osd_priv_t*)osd->priv;
378
379 /* backup previous contents */
380 if(!priv->backup)
381 priv->backup = gdk_pixmap_new(osd->pixmap, OSD_W+2, OSD_H+2, -1);
382
383 gint x = OSD_X + xoffset;
384 gint y = OSD_Y + yoffset;
385
386 /* create backup of background */
387 gdk_draw_drawable(priv->backup,
388 osd->widget->style->fg_gc[GTK_WIDGET_STATE(osd->widget)],
389 osd->pixmap, x-1, y-1, 0, 0, OSD_W+2, OSD_H+2);
390
391 priv->backup_x = x-1;
392 priv->backup_y = y-1;
393
394 /* OSD itself uses some off-screen rendering, so check if the */
395 /* offscreen buffer is present and create it if not */
396 if(!priv->overlay) {
397 /* create overlay ... */
398 priv->overlay =
399 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W, OSD_H);
400 /* ... and render it */
401 osm_gps_map_osd_render(osd);
402 }
403
404 // now draw this onto the original context
405 cairo_t *cr = gdk_cairo_create(osd->pixmap);
406 cairo_set_source_surface(cr, priv->overlay, x, y);
407 cairo_paint(cr);
408 cairo_destroy(cr);
409 }
410
411 static void
412 osm_gps_map_osd_restore (osm_gps_map_osd_t *osd)
413 {
414 osd_priv_t *priv = (osd_priv_t*)osd->priv;
415
416 /* restore backup of previous contents */
417 if(priv->backup) {
418 /* create backup of background */
419 gdk_draw_drawable(osd->pixmap,
420 osd->widget->style->fg_gc[GTK_WIDGET_STATE(osd->widget)],
421 priv->backup, 0, 0,
422 priv->backup_x, priv->backup_y, OSD_W+2, OSD_H+2);
423 }
424 }
425
426 static void
427 osm_gps_map_osd_free(osm_gps_map_osd_t *osd)
428 {
429 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
430
431 if (priv->backup)
432 g_object_unref(priv->backup);
433
434 if (priv->overlay)
435 cairo_surface_destroy(priv->overlay);
436
437 g_free(priv);
438 }
439
440 static osm_gps_map_osd_t osd_classic = {
441 .restore = osm_gps_map_osd_restore,
442 .draw = osm_gps_map_osd_draw,
443 .check = osm_gps_map_osd_check,
444 .render = osm_gps_map_osd_render,
445 .free = osm_gps_map_osd_free,
446
447 .cb = NULL,
448 .data = NULL,
449
450 .priv = NULL
451 };
452
453 /* this is the only function that's externally visible */
454 void
455 osm_gps_map_osd_classic_init(OsmGpsMap *map)
456 {
457 osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1);
458
459 osd_classic.priv = priv;
460
461 /* extract style info from the widget */
462 priv->bg = GTK_WIDGET(map)->style->bg[GTK_STATE_NORMAL];
463 priv->fg = GTK_WIDGET(map)->style->fg[GTK_STATE_NORMAL];
464 priv->da = GTK_WIDGET(map)->style->fg[GTK_STATE_INSENSITIVE];
465
466 osm_gps_map_register_osd(map, &osd_classic);
467 }
468
469
470 /* below are osd specific functions which aren't used by osm-gps-map */
471 /* but instead are to be used by the main application */
472 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb, gpointer data) {
473 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
474
475 g_return_if_fail (osd);
476
477 osd->cb = cb;
478 osd->data = data;
479
480 /* this may have changed the state of the gps button */
481 /* we thus re-render the overlay */
482 osd->render(osd);
483
484 osm_gps_map_redraw(map);
485 }