downstream: cosmetic changes (tabulation etc)
[profile/ivi/weston-ivi-shell.git] / shared / cairo-util.c
1 /*
2  * Copyright © 2008 Kristian Høgsberg
3  * Copyright © 2012 Intel Corporation
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting documentation, and
9  * that the name of the copyright holders not be used in advertising or
10  * publicity pertaining to distribution of the software without specific,
11  * written prior permission.  The copyright holders make no representations
12  * about the suitability of this software for any purpose.  It is provided "as
13  * is" without express or implied warranty.
14  *
15  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21  * OF THIS SOFTWARE.
22  */
23
24 #include "config.h"
25
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <math.h>
31 #include <wayland-util.h>
32 #include <cairo.h>
33 #include "cairo-util.h"
34
35 #include "image-loader.h"
36 #include "config-parser.h"
37
38 #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
39
40 void
41 surface_flush_device(cairo_surface_t *surface)
42 {
43         cairo_device_t *device;
44
45         device = cairo_surface_get_device(surface);
46         if (device)
47                 cairo_device_flush(device);
48 }
49
50 static int
51 blur_surface(cairo_surface_t *surface, int margin)
52 {
53         int32_t width, height, stride, x, y, z, w;
54         uint8_t *src, *dst;
55         uint32_t *s, *d, a, p;
56         int i, j, k, size, half;
57         uint32_t kernel[71];
58         double f;
59
60         size = ARRAY_LENGTH(kernel);
61         width = cairo_image_surface_get_width(surface);
62         height = cairo_image_surface_get_height(surface);
63         stride = cairo_image_surface_get_stride(surface);
64         src = cairo_image_surface_get_data(surface);
65
66         dst = malloc(height * stride);
67         if (dst == NULL)
68                 return -1;
69
70         half = size / 2;
71         a = 0;
72         for (i = 0; i < size; i++) {
73                 f = (i - half);
74                 kernel[i] = exp(- f * f / ARRAY_LENGTH(kernel)) * 10000;
75                 a += kernel[i];
76         }
77
78         for (i = 0; i < height; i++) {
79                 s = (uint32_t *) (src + i * stride);
80                 d = (uint32_t *) (dst + i * stride);
81                 for (j = 0; j < width; j++) {
82                         if (margin < j && j < width - margin) {
83                                 d[j] = s[j];
84                                 continue;
85                         }
86
87                         x = 0;
88                         y = 0;
89                         z = 0;
90                         w = 0;
91                         for (k = 0; k < size; k++) {
92                                 if (j - half + k < 0 || j - half + k >= width)
93                                         continue;
94                                 p = s[j - half + k];
95
96                                 x += (p >> 24) * kernel[k];
97                                 y += ((p >> 16) & 0xff) * kernel[k];
98                                 z += ((p >> 8) & 0xff) * kernel[k];
99                                 w += (p & 0xff) * kernel[k];
100                         }
101                         d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
102                 }
103         }
104
105         for (i = 0; i < height; i++) {
106                 s = (uint32_t *) (dst + i * stride);
107                 d = (uint32_t *) (src + i * stride);
108                 for (j = 0; j < width; j++) {
109                         if (margin <= i && i < height - margin) {
110                                 d[j] = s[j];
111                                 continue;
112                         }
113
114                         x = 0;
115                         y = 0;
116                         z = 0;
117                         w = 0;
118                         for (k = 0; k < size; k++) {
119                                 if (i - half + k < 0 || i - half + k >= height)
120                                         continue;
121                                 s = (uint32_t *) (dst + (i - half + k) * stride);
122                                 p = s[j];
123
124                                 x += (p >> 24) * kernel[k];
125                                 y += ((p >> 16) & 0xff) * kernel[k];
126                                 z += ((p >> 8) & 0xff) * kernel[k];
127                                 w += (p & 0xff) * kernel[k];
128                         }
129                         d[j] = (x / a << 24) | (y / a << 16) | (z / a << 8) | w / a;
130                 }
131         }
132
133         free(dst);
134         cairo_surface_mark_dirty(surface);
135
136         return 0;
137 }
138
139 void
140 tile_mask(cairo_t *cr, cairo_surface_t *surface,
141           int x, int y, int width, int height, int margin, int top_margin)
142 {
143         cairo_pattern_t *pattern;
144         cairo_matrix_t matrix;
145         int i, fx, fy, vmargin;
146
147         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
148         pattern = cairo_pattern_create_for_surface (surface);
149         cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
150
151         for (i = 0; i < 4; i++) {
152                 fx = i & 1;
153                 fy = i >> 1;
154
155                 cairo_matrix_init_translate(&matrix,
156                                             -x + fx * (128 - width),
157                                             -y + fy * (128 - height));
158                 cairo_pattern_set_matrix(pattern, &matrix);
159
160                 if (fy)
161                         vmargin = margin;
162                 else
163                         vmargin = top_margin;
164
165                 cairo_reset_clip(cr);
166                 cairo_rectangle(cr,
167                                 x + fx * (width - margin),
168                                 y + fy * (height - vmargin),
169                                 margin, vmargin);
170                 cairo_clip (cr);
171                 cairo_mask(cr, pattern);
172         }
173
174         /* Top stretch */
175         cairo_matrix_init_translate(&matrix, 60, 0);
176         cairo_matrix_scale(&matrix, 8.0 / width, 1);
177         cairo_matrix_translate(&matrix, -x - width / 2, -y);
178         cairo_pattern_set_matrix(pattern, &matrix);
179         cairo_rectangle(cr, x + margin, y, width - 2 * margin, margin);
180
181         cairo_reset_clip(cr);
182         cairo_rectangle(cr,
183                         x + margin,
184                         y,
185                         width - 2 * margin, margin);
186         cairo_clip (cr);
187         cairo_mask(cr, pattern);
188
189         /* Bottom stretch */
190         cairo_matrix_translate(&matrix, 0, -height + 128);
191         cairo_pattern_set_matrix(pattern, &matrix);
192
193         cairo_reset_clip(cr);
194         cairo_rectangle(cr, x + margin, y + height - margin,
195                         width - 2 * margin, margin);
196         cairo_clip (cr);
197         cairo_mask(cr, pattern);
198
199         /* Left stretch */
200         cairo_matrix_init_translate(&matrix, 0, 60);
201         cairo_matrix_scale(&matrix, 1, 8.0 / height);
202         cairo_matrix_translate(&matrix, -x, -y - height / 2);
203         cairo_pattern_set_matrix(pattern, &matrix);
204         cairo_reset_clip(cr);
205         cairo_rectangle(cr, x, y + margin, margin, height - 2 * margin);
206         cairo_clip (cr);
207         cairo_mask(cr, pattern);
208
209         /* Right stretch */
210         cairo_matrix_translate(&matrix, -width + 128, 0);
211         cairo_pattern_set_matrix(pattern, &matrix);
212         cairo_rectangle(cr, x + width - margin, y + margin,
213                         margin, height - 2 * margin);
214         cairo_reset_clip(cr);
215         cairo_clip (cr);
216         cairo_mask(cr, pattern);
217
218         cairo_pattern_destroy(pattern);
219         cairo_reset_clip(cr);
220 }
221
222 void
223 tile_source(cairo_t *cr, cairo_surface_t *surface,
224             int x, int y, int width, int height, int margin, int top_margin)
225 {
226         cairo_pattern_t *pattern;
227         cairo_matrix_t matrix;
228         int i, fx, fy, vmargin;
229
230         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
231         pattern = cairo_pattern_create_for_surface (surface);
232         cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
233         cairo_set_source(cr, pattern);
234         cairo_pattern_destroy(pattern);
235
236         for (i = 0; i < 4; i++) {
237                 fx = i & 1;
238                 fy = i >> 1;
239
240                 cairo_matrix_init_translate(&matrix,
241                                             -x + fx * (128 - width),
242                                             -y + fy * (128 - height));
243                 cairo_pattern_set_matrix(pattern, &matrix);
244
245                 if (fy)
246                         vmargin = margin;
247                 else
248                         vmargin = top_margin;
249
250                 cairo_rectangle(cr,
251                                 x + fx * (width - margin),
252                                 y + fy * (height - vmargin),
253                                 margin, vmargin);
254                 cairo_fill(cr);
255         }
256
257         /* Top stretch */
258         cairo_matrix_init_translate(&matrix, 60, 0);
259         cairo_matrix_scale(&matrix, 8.0 / (width - 2 * margin), 1);
260         cairo_matrix_translate(&matrix, -x - width / 2, -y);
261         cairo_pattern_set_matrix(pattern, &matrix);
262         cairo_rectangle(cr, x + margin, y, width - 2 * margin, top_margin);
263         cairo_fill(cr);
264
265         /* Bottom stretch */
266         cairo_matrix_translate(&matrix, 0, -height + 128);
267         cairo_pattern_set_matrix(pattern, &matrix);
268         cairo_rectangle(cr, x + margin, y + height - margin,
269                         width - 2 * margin, margin);
270         cairo_fill(cr);
271
272         /* Left stretch */
273         cairo_matrix_init_translate(&matrix, 0, 60);
274         cairo_matrix_scale(&matrix, 1, 8.0 / (height - margin - top_margin));
275         cairo_matrix_translate(&matrix, -x, -y - height / 2);
276         cairo_pattern_set_matrix(pattern, &matrix);
277         cairo_rectangle(cr, x, y + top_margin,
278                         margin, height - margin - top_margin);
279         cairo_fill(cr);
280
281         /* Right stretch */
282         cairo_matrix_translate(&matrix, -width + 128, 0);
283         cairo_pattern_set_matrix(pattern, &matrix);
284         cairo_rectangle(cr, x + width - margin, y + top_margin,
285                         margin, height - margin - top_margin);
286         cairo_fill(cr);
287 }
288
289 void
290 rounded_rect(cairo_t *cr, int x0, int y0, int x1, int y1, int radius)
291 {
292         cairo_move_to(cr, x0, y0 + radius);
293         cairo_arc(cr, x0 + radius, y0 + radius, radius, M_PI, 3 * M_PI / 2);
294         cairo_line_to(cr, x1 - radius, y0);
295         cairo_arc(cr, x1 - radius, y0 + radius, radius, 3 * M_PI / 2, 2 * M_PI);
296         cairo_line_to(cr, x1, y1 - radius);
297         cairo_arc(cr, x1 - radius, y1 - radius, radius, 0, M_PI / 2);
298         cairo_line_to(cr, x0 + radius, y1);
299         cairo_arc(cr, x0 + radius, y1 - radius, radius, M_PI / 2, M_PI);
300         cairo_close_path(cr);
301 }
302
303 cairo_surface_t *
304 load_cairo_surface(const char *filename)
305 {
306         pixman_image_t *image;
307         int width, height, stride;
308         void *data;
309
310         image = load_image(filename);
311         if (image == NULL) {
312                 return NULL;
313         }
314
315         data = pixman_image_get_data(image);
316         width = pixman_image_get_width(image);
317         height = pixman_image_get_height(image);
318         stride = pixman_image_get_stride(image);
319
320         return cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32,
321                                                    width, height, stride);
322 }
323
324 void
325 theme_set_background_source(struct theme *t, cairo_t *cr, uint32_t flags)
326 {
327         cairo_pattern_t *pattern;
328
329         if (flags & THEME_FRAME_ACTIVE) {
330                 pattern = cairo_pattern_create_linear(16, 16, 16, 112);
331                 cairo_pattern_add_color_stop_rgb(pattern, 0.0, 1.0, 1.0, 1.0);
332                 cairo_pattern_add_color_stop_rgb(pattern, 0.2, 0.8, 0.8, 0.8);
333                 cairo_set_source(cr, pattern);
334                 cairo_pattern_destroy(pattern);
335         } else {
336                 cairo_set_source_rgba(cr, 0.75, 0.75, 0.75, 1);
337         }
338 }
339
340 struct theme *
341 theme_create(void)
342 {
343         struct theme *t;
344         cairo_t *cr;
345
346         t = malloc(sizeof *t);
347         if (t == NULL)
348                 return NULL;
349
350         t->margin = 32;
351         t->width = 6;
352         t->titlebar_height = 27;
353         t->frame_radius = 3;
354         t->shadow = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 128, 128);
355         cr = cairo_create(t->shadow);
356         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
357         cairo_set_source_rgba(cr, 0, 0, 0, 1);
358         rounded_rect(cr, 32, 32, 96, 96, t->frame_radius);
359         cairo_fill(cr);
360         if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
361                 goto err_shadow;
362         cairo_destroy(cr);
363         if (blur_surface(t->shadow, 64) == -1)
364                 goto err_shadow;
365
366         t->active_frame =
367                 cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 128, 128);
368         cr = cairo_create(t->active_frame);
369         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
370
371         theme_set_background_source(t, cr, THEME_FRAME_ACTIVE);
372         rounded_rect(cr, 0, 0, 128, 128, t->frame_radius);
373         cairo_fill(cr);
374
375         if (cairo_status(cr) != CAIRO_STATUS_SUCCESS)
376                 goto err_active_frame;
377
378         cairo_destroy(cr);
379
380         t->inactive_frame =
381                 cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 128, 128);
382         cr = cairo_create(t->inactive_frame);
383         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
384         theme_set_background_source(t, cr, 0);
385         rounded_rect(cr, 0, 0, 128, 128, t->frame_radius);
386         cairo_fill(cr);
387
388         if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
389                 goto err_inactive_frame;
390
391         cairo_destroy(cr);
392
393         return t;
394
395  err_inactive_frame:
396         cairo_surface_destroy(t->inactive_frame);
397  err_active_frame:
398         cairo_surface_destroy(t->active_frame);
399  err_shadow:
400         cairo_surface_destroy(t->shadow);
401         free(t);
402         return NULL;
403 }
404
405 void
406 theme_destroy(struct theme *t)
407 {
408         cairo_surface_destroy(t->active_frame);
409         cairo_surface_destroy(t->inactive_frame);
410         cairo_surface_destroy(t->shadow);
411         free(t);
412 }
413
414 void
415 theme_render_frame(struct theme *t,
416                    cairo_t *cr, int width, int height,
417                    const char *title, struct wl_list *buttons,
418                    uint32_t flags)
419 {
420         cairo_text_extents_t extents;
421         cairo_font_extents_t font_extents;
422         cairo_surface_t *source;
423         int x, y, margin, top_margin;
424
425         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
426         cairo_set_source_rgba(cr, 0, 0, 0, 0);
427         cairo_paint(cr);
428
429         if (flags & THEME_FRAME_MAXIMIZED)
430                 margin = 0;
431         else {
432                 cairo_set_source_rgba(cr, 0, 0, 0, 0.45);
433                 tile_mask(cr, t->shadow,
434                           2, 2, width + 8, height + 8,
435                           64, 64);
436                 margin = t->margin;
437         }
438
439         if (flags & THEME_FRAME_ACTIVE)
440                 source = t->active_frame;
441         else
442                 source = t->inactive_frame;
443
444         if (title || !wl_list_empty(buttons))
445                 top_margin = t->titlebar_height;
446         else
447                 top_margin = t->width;
448
449         tile_source(cr, source,
450                     margin, margin,
451                     width - margin * 2, height - margin * 2,
452                     t->width, top_margin);
453
454         if (title || !wl_list_empty(buttons)) {
455                 cairo_rectangle (cr, margin + t->width, margin,
456                                  width - (margin + t->width) * 2,
457                                  t->titlebar_height - t->width);
458                 cairo_clip(cr);
459
460                 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
461                 cairo_select_font_face(cr, "sans",
462                                        CAIRO_FONT_SLANT_NORMAL,
463                                        CAIRO_FONT_WEIGHT_BOLD);
464                 cairo_set_font_size(cr, 14);
465                 cairo_text_extents(cr, title, &extents);
466                 cairo_font_extents (cr, &font_extents);
467                 x = (width - extents.width) / 2;
468                 y = margin +
469                         (t->titlebar_height -
470                          font_extents.ascent - font_extents.descent) / 2 +
471                         font_extents.ascent;
472
473                 if (flags & THEME_FRAME_ACTIVE) {
474                         cairo_move_to(cr, x + 1, y  + 1);
475                         cairo_set_source_rgb(cr, 1, 1, 1);
476                         cairo_show_text(cr, title);
477                         cairo_move_to(cr, x, y);
478                         cairo_set_source_rgb(cr, 0, 0, 0);
479                         cairo_show_text(cr, title);
480                 } else {
481                         cairo_move_to(cr, x, y);
482                         cairo_set_source_rgb(cr, 0.4, 0.4, 0.4);
483                         cairo_show_text(cr, title);
484                 }
485         }
486 }
487
488 enum theme_location
489 theme_get_location(struct theme *t, int x, int y,
490                                 int width, int height, int flags)
491 {
492         int vlocation, hlocation, location;
493         int margin, top_margin, grip_size;
494
495         if (flags & THEME_FRAME_MAXIMIZED) {
496                 margin = 0;
497                 grip_size = 0;
498         } else {
499                 margin = t->margin;
500                 grip_size = 8;
501         }
502
503         if (flags & THEME_FRAME_NO_TITLE)
504                 top_margin = t->width;
505         else
506                 top_margin = t->titlebar_height;
507
508         if (x < margin)
509                 hlocation = THEME_LOCATION_EXTERIOR;
510         else if (x < margin + grip_size)
511                 hlocation = THEME_LOCATION_RESIZING_LEFT;
512         else if (x < width - margin - grip_size)
513                 hlocation = THEME_LOCATION_INTERIOR;
514         else if (x < width - margin)
515                 hlocation = THEME_LOCATION_RESIZING_RIGHT;
516         else
517                 hlocation = THEME_LOCATION_EXTERIOR;
518
519         if (y < margin)
520                 vlocation = THEME_LOCATION_EXTERIOR;
521         else if (y < margin + grip_size)
522                 vlocation = THEME_LOCATION_RESIZING_TOP;
523         else if (y < height - margin - grip_size)
524                 vlocation = THEME_LOCATION_INTERIOR;
525         else if (y < height - margin)
526                 vlocation = THEME_LOCATION_RESIZING_BOTTOM;
527         else
528                 vlocation = THEME_LOCATION_EXTERIOR;
529
530         location = vlocation | hlocation;
531         if (location & THEME_LOCATION_EXTERIOR)
532                 location = THEME_LOCATION_EXTERIOR;
533         if (location == THEME_LOCATION_INTERIOR &&
534             y < margin + top_margin)
535                 location = THEME_LOCATION_TITLEBAR;
536         else if (location == THEME_LOCATION_INTERIOR)
537                 location = THEME_LOCATION_CLIENT_AREA;
538
539         return location;
540 }