1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3 * Copyright (C) 2011 Red Hat, Inc.
4 *
5 * Authors: Carlos Garnacho <carlosg@gnome.org>
6 * Cosimo Cecchi <cosimoc@gnome.org>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library 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 GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23
24 #include "gtkrenderborderprivate.h"
25
26 #include <cairo-gobject.h>
27 #include <math.h>
28
29 #include "gtkcssbordervalueprivate.h"
30 #include "gtkcssenumvalueprivate.h"
31 #include "gtkcssimagevalueprivate.h"
32 #include "gtkcssnumbervalueprivate.h"
33 #include "gtkcssrepeatvalueprivate.h"
34 #include "gtkcssrgbavalueprivate.h"
35 #include "gtkcssstyleprivate.h"
36 #include "gtkhslaprivate.h"
37 #include "gtkroundedboxprivate.h"
38
39 /* this is in case round() is not provided by the compiler,
40 * such as in the case of C89 compilers, like MSVC
41 */
42 #include "fallback-c89.c"
43
44 typedef struct _GtkBorderImage GtkBorderImage;
45
46 struct _GtkBorderImage {
47 GtkCssImage *source;
48
49 GtkCssValue *slice;
50 GtkCssValue *width;
51 GtkCssValue *repeat;
52 };
53
54 static gboolean
gtk_border_image_init(GtkBorderImage * image,GtkCssStyle * style)55 gtk_border_image_init (GtkBorderImage *image,
56 GtkCssStyle *style)
57 {
58 image->source = _gtk_css_image_value_get_image (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_IMAGE_SOURCE));
59 if (image->source == NULL)
60 return FALSE;
61
62 image->slice = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_IMAGE_SLICE);
63 image->width = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_IMAGE_WIDTH);
64 image->repeat = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_IMAGE_REPEAT);
65
66 return TRUE;
67 }
68
69 typedef struct _GtkBorderImageSliceSize GtkBorderImageSliceSize;
70 struct _GtkBorderImageSliceSize {
71 double offset;
72 double size;
73 };
74
75 static void
gtk_border_image_compute_border_size(GtkBorderImageSliceSize sizes[3],double offset,double area_size,double start_border_width,double end_border_width,const GtkCssValue * start_border,const GtkCssValue * end_border)76 gtk_border_image_compute_border_size (GtkBorderImageSliceSize sizes[3],
77 double offset,
78 double area_size,
79 double start_border_width,
80 double end_border_width,
81 const GtkCssValue *start_border,
82 const GtkCssValue *end_border)
83 {
84 double start, end;
85
86 if (gtk_css_number_value_get_dimension (start_border) == GTK_CSS_DIMENSION_NUMBER)
87 start = start_border_width * _gtk_css_number_value_get (start_border, 100);
88 else
89 start = _gtk_css_number_value_get (start_border, area_size);
90 if (gtk_css_number_value_get_dimension (end_border) == GTK_CSS_DIMENSION_NUMBER)
91 end = end_border_width * _gtk_css_number_value_get (end_border, 100);
92 else
93 end = _gtk_css_number_value_get (end_border, area_size);
94
95 /* XXX: reduce vertical and horizontal by the same factor */
96 if (start + end > area_size)
97 {
98 start = start * area_size / (start + end);
99 end = end * area_size / (start + end);
100 }
101
102 sizes[0].offset = offset;
103 sizes[0].size = start;
104 sizes[1].offset = offset + start;
105 sizes[1].size = area_size - start - end;
106 sizes[2].offset = offset + area_size - end;
107 sizes[2].size = end;
108 }
109
110 static void
gtk_border_image_render_slice(cairo_t * cr,cairo_surface_t * slice,double slice_width,double slice_height,double x,double y,double width,double height,GtkCssRepeatStyle hrepeat,GtkCssRepeatStyle vrepeat)111 gtk_border_image_render_slice (cairo_t *cr,
112 cairo_surface_t *slice,
113 double slice_width,
114 double slice_height,
115 double x,
116 double y,
117 double width,
118 double height,
119 GtkCssRepeatStyle hrepeat,
120 GtkCssRepeatStyle vrepeat)
121 {
122 double hscale, vscale;
123 double xstep, ystep;
124 cairo_extend_t extend = CAIRO_EXTEND_PAD;
125 cairo_matrix_t matrix;
126 cairo_pattern_t *pattern;
127
128 /* We can't draw center tiles yet */
129 g_assert (hrepeat == GTK_CSS_REPEAT_STYLE_STRETCH || vrepeat == GTK_CSS_REPEAT_STYLE_STRETCH);
130
131 hscale = width / slice_width;
132 vscale = height / slice_height;
133 xstep = width;
134 ystep = height;
135
136 switch (hrepeat)
137 {
138 case GTK_CSS_REPEAT_STYLE_REPEAT:
139 extend = CAIRO_EXTEND_REPEAT;
140 hscale = vscale;
141 break;
142 case GTK_CSS_REPEAT_STYLE_SPACE:
143 {
144 double space, n;
145
146 extend = CAIRO_EXTEND_NONE;
147 hscale = vscale;
148
149 xstep = hscale * slice_width;
150 n = floor (width / xstep);
151 space = (width - n * xstep) / (n + 1);
152 xstep += space;
153 x += space;
154 width -= 2 * space;
155 }
156 break;
157 case GTK_CSS_REPEAT_STYLE_STRETCH:
158 break;
159 case GTK_CSS_REPEAT_STYLE_ROUND:
160 extend = CAIRO_EXTEND_REPEAT;
161 hscale = width / (slice_width * MAX (round (width / (slice_width * vscale)), 1));
162 break;
163 default:
164 g_assert_not_reached ();
165 break;
166 }
167
168 switch (vrepeat)
169 {
170 case GTK_CSS_REPEAT_STYLE_REPEAT:
171 extend = CAIRO_EXTEND_REPEAT;
172 vscale = hscale;
173 break;
174 case GTK_CSS_REPEAT_STYLE_SPACE:
175 {
176 double space, n;
177
178 extend = CAIRO_EXTEND_NONE;
179 vscale = hscale;
180
181 ystep = vscale * slice_height;
182 n = floor (height / ystep);
183 space = (height - n * ystep) / (n + 1);
184 ystep += space;
185 y += space;
186 height -= 2 * space;
187 }
188 break;
189 case GTK_CSS_REPEAT_STYLE_STRETCH:
190 break;
191 case GTK_CSS_REPEAT_STYLE_ROUND:
192 extend = CAIRO_EXTEND_REPEAT;
193 vscale = height / (slice_height * MAX (round (height / (slice_height * hscale)), 1));
194 break;
195 default:
196 g_assert_not_reached ();
197 break;
198 }
199
200 pattern = cairo_pattern_create_for_surface (slice);
201
202 cairo_matrix_init_translate (&matrix,
203 hrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? slice_width / 2 : 0,
204 vrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? slice_height / 2 : 0);
205 cairo_matrix_scale (&matrix, 1 / hscale, 1 / vscale);
206 cairo_matrix_translate (&matrix,
207 hrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? - width / 2 : 0,
208 vrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? - height / 2 : 0);
209
210 cairo_pattern_set_matrix (pattern, &matrix);
211 cairo_pattern_set_extend (pattern, extend);
212
213 cairo_save (cr);
214 cairo_translate (cr, x, y);
215
216 for (y = 0; y < height; y += ystep)
217 {
218 for (x = 0; x < width; x += xstep)
219 {
220 cairo_save (cr);
221 cairo_translate (cr, x, y);
222 cairo_set_source (cr, pattern);
223 cairo_rectangle (cr, 0, 0, xstep, ystep);
224 cairo_fill (cr);
225 cairo_restore (cr);
226 }
227 }
228
229 cairo_restore (cr);
230
231 cairo_pattern_destroy (pattern);
232 }
233
234 static void
gtk_border_image_compute_slice_size(GtkBorderImageSliceSize sizes[3],int surface_size,int start_size,int end_size)235 gtk_border_image_compute_slice_size (GtkBorderImageSliceSize sizes[3],
236 int surface_size,
237 int start_size,
238 int end_size)
239 {
240 sizes[0].size = MIN (start_size, surface_size);
241 sizes[0].offset = 0;
242
243 sizes[2].size = MIN (end_size, surface_size);
244 sizes[2].offset = surface_size - sizes[2].size;
245
246 sizes[1].size = MAX (0, surface_size - sizes[0].size - sizes[2].size);
247 sizes[1].offset = sizes[0].size;
248 }
249
250 static void
gtk_border_image_render(GtkBorderImage * image,const double border_width[4],cairo_t * cr,gdouble x,gdouble y,gdouble width,gdouble height)251 gtk_border_image_render (GtkBorderImage *image,
252 const double border_width[4],
253 cairo_t *cr,
254 gdouble x,
255 gdouble y,
256 gdouble width,
257 gdouble height)
258 {
259 cairo_surface_t *surface, *slice;
260 GtkBorderImageSliceSize vertical_slice[3], horizontal_slice[3];
261 GtkBorderImageSliceSize vertical_border[3], horizontal_border[3];
262 double source_width, source_height;
263 int h, v;
264
265 _gtk_css_image_get_concrete_size (image->source,
266 0, 0,
267 width, height,
268 &source_width, &source_height);
269
270 /* XXX: Optimize for (source_width == width && source_height == height) */
271
272 surface = _gtk_css_image_get_surface (image->source,
273 cairo_get_target (cr),
274 source_width, source_height);
275
276 gtk_border_image_compute_slice_size (horizontal_slice,
277 source_width,
278 _gtk_css_number_value_get (_gtk_css_border_value_get_left (image->slice), source_width),
279 _gtk_css_number_value_get (_gtk_css_border_value_get_right (image->slice), source_width));
280 gtk_border_image_compute_slice_size (vertical_slice,
281 source_height,
282 _gtk_css_number_value_get (_gtk_css_border_value_get_top (image->slice), source_height),
283 _gtk_css_number_value_get (_gtk_css_border_value_get_bottom (image->slice), source_height));
284 gtk_border_image_compute_border_size (horizontal_border,
285 x,
286 width,
287 border_width[GTK_CSS_LEFT],
288 border_width[GTK_CSS_RIGHT],
289 _gtk_css_border_value_get_left (image->width),
290 _gtk_css_border_value_get_right (image->width));
291 gtk_border_image_compute_border_size (vertical_border,
292 y,
293 height,
294 border_width[GTK_CSS_TOP],
295 border_width[GTK_CSS_BOTTOM],
296 _gtk_css_border_value_get_top (image->width),
297 _gtk_css_border_value_get_bottom(image->width));
298
299 for (v = 0; v < 3; v++)
300 {
301 if (vertical_slice[v].size == 0 ||
302 vertical_border[v].size == 0)
303 continue;
304
305 for (h = 0; h < 3; h++)
306 {
307 if (horizontal_slice[h].size == 0 ||
308 horizontal_border[h].size == 0)
309 continue;
310
311 if (h == 1 && v == 1)
312 continue;
313
314 slice = cairo_surface_create_for_rectangle (surface,
315 horizontal_slice[h].offset,
316 vertical_slice[v].offset,
317 horizontal_slice[h].size,
318 vertical_slice[v].size);
319
320 gtk_border_image_render_slice (cr,
321 slice,
322 horizontal_slice[h].size,
323 vertical_slice[v].size,
324 horizontal_border[h].offset,
325 vertical_border[v].offset,
326 horizontal_border[h].size,
327 vertical_border[v].size,
328 h == 1 ? _gtk_css_border_repeat_value_get_x (image->repeat) : GTK_CSS_REPEAT_STYLE_STRETCH,
329 v == 1 ? _gtk_css_border_repeat_value_get_y (image->repeat) : GTK_CSS_REPEAT_STYLE_STRETCH);
330
331 cairo_surface_destroy (slice);
332 }
333 }
334
335 cairo_surface_destroy (surface);
336 }
337
338 static void
hide_border_sides(double border[4],GtkBorderStyle border_style[4],guint hidden_side)339 hide_border_sides (double border[4],
340 GtkBorderStyle border_style[4],
341 guint hidden_side)
342 {
343 guint i;
344
345 for (i = 0; i < 4; i++)
346 {
347 if (hidden_side & (1 << i))
348 border[i] = 0;
349 }
350 }
351
352 static void
render_frame_fill(cairo_t * cr,GtkRoundedBox * border_box,const double border_width[4],GdkRGBA colors[4],guint hidden_side)353 render_frame_fill (cairo_t *cr,
354 GtkRoundedBox *border_box,
355 const double border_width[4],
356 GdkRGBA colors[4],
357 guint hidden_side)
358 {
359 GtkRoundedBox padding_box;
360 guint i, j;
361
362 padding_box = *border_box;
363 _gtk_rounded_box_shrink (&padding_box,
364 border_width[GTK_CSS_TOP],
365 border_width[GTK_CSS_RIGHT],
366 border_width[GTK_CSS_BOTTOM],
367 border_width[GTK_CSS_LEFT]);
368
369 if (hidden_side == 0 &&
370 gdk_rgba_equal (&colors[0], &colors[1]) &&
371 gdk_rgba_equal (&colors[0], &colors[2]) &&
372 gdk_rgba_equal (&colors[0], &colors[3]))
373 {
374 gdk_cairo_set_source_rgba (cr, &colors[0]);
375
376 _gtk_rounded_box_path (border_box, cr);
377 _gtk_rounded_box_path (&padding_box, cr);
378 cairo_fill (cr);
379 }
380 else
381 {
382 for (i = 0; i < 4; i++)
383 {
384 if (hidden_side & (1 << i))
385 continue;
386
387 for (j = 0; j < 4; j++)
388 {
389 if (hidden_side & (1 << j))
390 continue;
391
392 if (i == j ||
393 (gdk_rgba_equal (&colors[i], &colors[j])))
394 {
395 /* We were already painted when i == j */
396 if (i > j)
397 break;
398
399 if (j == 0)
400 _gtk_rounded_box_path_top (border_box, &padding_box, cr);
401 else if (j == 1)
402 _gtk_rounded_box_path_right (border_box, &padding_box, cr);
403 else if (j == 2)
404 _gtk_rounded_box_path_bottom (border_box, &padding_box, cr);
405 else if (j == 3)
406 _gtk_rounded_box_path_left (border_box, &padding_box, cr);
407 }
408 }
409 /* We were already painted when i == j */
410 if (i > j)
411 continue;
412
413 gdk_cairo_set_source_rgba (cr, &colors[i]);
414
415 cairo_fill (cr);
416 }
417 }
418 }
419
420 static void
set_stroke_style(cairo_t * cr,double line_width,GtkBorderStyle style,double length)421 set_stroke_style (cairo_t *cr,
422 double line_width,
423 GtkBorderStyle style,
424 double length)
425 {
426 double segments[2];
427 double n;
428
429 cairo_set_line_width (cr, line_width);
430
431 if (style == GTK_BORDER_STYLE_DOTTED)
432 {
433 n = round (0.5 * length / line_width);
434
435 segments[0] = 0;
436 segments[1] = n ? length / n : 2;
437 cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
438
439 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
440 cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
441 }
442 else
443 {
444 n = length / line_width;
445 /* Optimize the common case of an integer-sized rectangle
446 * Again, we care about focus rectangles.
447 */
448 if (n == nearbyint (n))
449 {
450 segments[0] = line_width;
451 segments[1] = 2 * line_width;
452 }
453 else
454 {
455 n = round ((1. / 3) * n);
456
457 segments[0] = n ? (1. / 3) * length / n : 1;
458 segments[1] = 2 * segments[0];
459 }
460 cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
461
462 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
463 cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
464 }
465 }
466
467 static void
render_frame_stroke(cairo_t * cr,GtkRoundedBox * border_box,const double border_width[4],GdkRGBA colors[4],guint hidden_side,GtkBorderStyle stroke_style)468 render_frame_stroke (cairo_t *cr,
469 GtkRoundedBox *border_box,
470 const double border_width[4],
471 GdkRGBA colors[4],
472 guint hidden_side,
473 GtkBorderStyle stroke_style)
474 {
475 gboolean different_colors, different_borders;
476 GtkRoundedBox stroke_box;
477 guint i;
478
479 different_colors = !gdk_rgba_equal (&colors[0], &colors[1]) ||
480 !gdk_rgba_equal (&colors[0], &colors[2]) ||
481 !gdk_rgba_equal (&colors[0], &colors[3]);
482 different_borders = border_width[0] != border_width[1] ||
483 border_width[0] != border_width[2] ||
484 border_width[0] != border_width[3] ;
485
486 stroke_box = *border_box;
487 _gtk_rounded_box_shrink (&stroke_box,
488 border_width[GTK_CSS_TOP] / 2.0,
489 border_width[GTK_CSS_RIGHT] / 2.0,
490 border_width[GTK_CSS_BOTTOM] / 2.0,
491 border_width[GTK_CSS_LEFT] / 2.0);
492
493 if (!different_colors && !different_borders && hidden_side == 0)
494 {
495 double length = 0;
496
497 /* FAST PATH:
498 * Mostly expected to trigger for focus rectangles */
499 for (i = 0; i < 4; i++)
500 {
501 length += _gtk_rounded_box_guess_length (&stroke_box, i);
502 }
503
504 _gtk_rounded_box_path (&stroke_box, cr);
505 gdk_cairo_set_source_rgba (cr, &colors[0]);
506 set_stroke_style (cr, border_width[0], stroke_style, length);
507 cairo_stroke (cr);
508 }
509 else
510 {
511 GtkRoundedBox padding_box;
512
513 padding_box = *border_box;
514 _gtk_rounded_box_shrink (&padding_box,
515 border_width[GTK_CSS_TOP],
516 border_width[GTK_CSS_RIGHT],
517 border_width[GTK_CSS_BOTTOM],
518 border_width[GTK_CSS_LEFT]);
519
520 for (i = 0; i < 4; i++)
521 {
522 if (hidden_side & (1 << i))
523 continue;
524
525 if (border_width[i] == 0)
526 continue;
527
528 cairo_save (cr);
529
530 if (i == 0)
531 _gtk_rounded_box_path_top (border_box, &padding_box, cr);
532 else if (i == 1)
533 _gtk_rounded_box_path_right (border_box, &padding_box, cr);
534 else if (i == 2)
535 _gtk_rounded_box_path_bottom (border_box, &padding_box, cr);
536 else if (i == 3)
537 _gtk_rounded_box_path_left (border_box, &padding_box, cr);
538 cairo_clip (cr);
539
540 _gtk_rounded_box_path_side (&stroke_box, cr, i);
541
542 gdk_cairo_set_source_rgba (cr, &colors[i]);
543 set_stroke_style (cr,
544 border_width[i],
545 stroke_style,
546 _gtk_rounded_box_guess_length (&stroke_box, i));
547 cairo_stroke (cr);
548
549 cairo_restore (cr);
550 }
551 }
552 }
553
554 static void
color_shade(const GdkRGBA * color,gdouble factor,GdkRGBA * color_return)555 color_shade (const GdkRGBA *color,
556 gdouble factor,
557 GdkRGBA *color_return)
558 {
559 GtkHSLA hsla;
560
561 _gtk_hsla_init_from_rgba (&hsla, color);
562 _gtk_hsla_shade (&hsla, &hsla, factor);
563 _gdk_rgba_init_from_hsla (color_return, &hsla);
564 }
565
566 static void
render_border(cairo_t * cr,GtkRoundedBox * border_box,const double border_width[4],guint hidden_side,GdkRGBA colors[4],GtkBorderStyle border_style[4])567 render_border (cairo_t *cr,
568 GtkRoundedBox *border_box,
569 const double border_width[4],
570 guint hidden_side,
571 GdkRGBA colors[4],
572 GtkBorderStyle border_style[4])
573 {
574 guint i, j;
575
576 cairo_save (cr);
577
578 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
579
580 for (i = 0; i < 4; i++)
581 {
582 if (hidden_side & (1 << i))
583 continue;
584
585 /* NB: code below divides by this value */
586 /* a border smaller than this will not noticably modify
587 * pixels on screen, and since we don't compare with 0,
588 * we'll use this value */
589 if (border_width[i] < 1.0 / 1024)
590 continue;
591
592 switch (border_style[i])
593 {
594 case GTK_BORDER_STYLE_NONE:
595 case GTK_BORDER_STYLE_HIDDEN:
596 case GTK_BORDER_STYLE_SOLID:
597 break;
598 case GTK_BORDER_STYLE_INSET:
599 if (i == 1 || i == 2)
600 color_shade (&colors[i], 1.8, &colors[i]);
601 break;
602 case GTK_BORDER_STYLE_OUTSET:
603 if (i == 0 || i == 3)
604 color_shade (&colors[i], 1.8, &colors[i]);
605 break;
606 case GTK_BORDER_STYLE_DOTTED:
607 case GTK_BORDER_STYLE_DASHED:
608 {
609 guint dont_draw = hidden_side;
610
611 for (j = 0; j < 4; j++)
612 {
613 if (border_style[j] == border_style[i])
614 hidden_side |= (1 << j);
615 else
616 dont_draw |= (1 << j);
617 }
618
619 render_frame_stroke (cr, border_box, border_width, colors, dont_draw, border_style[i]);
620 }
621 break;
622 case GTK_BORDER_STYLE_DOUBLE:
623 {
624 GtkRoundedBox other_box;
625 double other_border[4];
626 guint dont_draw = hidden_side;
627
628 for (j = 0; j < 4; j++)
629 {
630 if (border_style[j] == GTK_BORDER_STYLE_DOUBLE)
631 hidden_side |= (1 << j);
632 else
633 dont_draw |= (1 << j);
634
635 other_border[j] = border_width[j] / 3;
636 }
637
638 render_frame_fill (cr, border_box, other_border, colors, dont_draw);
639
640 other_box = *border_box;
641 _gtk_rounded_box_shrink (&other_box,
642 2 * other_border[GTK_CSS_TOP],
643 2 * other_border[GTK_CSS_RIGHT],
644 2 * other_border[GTK_CSS_BOTTOM],
645 2 * other_border[GTK_CSS_LEFT]);
646 render_frame_fill (cr, &other_box, other_border, colors, dont_draw);
647 }
648 break;
649 case GTK_BORDER_STYLE_GROOVE:
650 case GTK_BORDER_STYLE_RIDGE:
651 {
652 GtkRoundedBox other_box;
653 GdkRGBA other_colors[4];
654 guint dont_draw = hidden_side;
655 double other_border[4];
656
657 for (j = 0; j < 4; j++)
658 {
659 other_colors[j] = colors[j];
660 if ((j == 0 || j == 3) ^ (border_style[j] == GTK_BORDER_STYLE_RIDGE))
661 color_shade (&other_colors[j], 1.8, &other_colors[j]);
662 else
663 color_shade (&colors[j], 1.8, &colors[j]);
664 if (border_style[j] == GTK_BORDER_STYLE_GROOVE ||
665 border_style[j] == GTK_BORDER_STYLE_RIDGE)
666 hidden_side |= (1 << j);
667 else
668 dont_draw |= (1 << j);
669 other_border[j] = border_width[j] / 2;
670 }
671
672 render_frame_fill (cr, border_box, other_border, colors, dont_draw);
673
674 other_box = *border_box;
675 _gtk_rounded_box_shrink (&other_box,
676 other_border[GTK_CSS_TOP],
677 other_border[GTK_CSS_RIGHT],
678 other_border[GTK_CSS_BOTTOM],
679 other_border[GTK_CSS_LEFT]);
680 render_frame_fill (cr, &other_box, other_border, other_colors, dont_draw);
681 }
682 break;
683 default:
684 g_assert_not_reached ();
685 break;
686 }
687 }
688
689 render_frame_fill (cr, border_box, border_width, colors, hidden_side);
690
691 cairo_restore (cr);
692 }
693
694 gboolean
gtk_css_style_render_has_border(GtkCssStyle * style)695 gtk_css_style_render_has_border (GtkCssStyle *style)
696 {
697 if (_gtk_css_image_value_get_image (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_IMAGE_SOURCE)))
698 return TRUE;
699
700 return _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100) > 0
701 || _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100) > 0
702 || _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100) > 0
703 || _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100) > 0;
704 }
705
706 void
gtk_css_style_render_border(GtkCssStyle * style,cairo_t * cr,gdouble x,gdouble y,gdouble width,gdouble height,guint hidden_side,GtkJunctionSides junction)707 gtk_css_style_render_border (GtkCssStyle *style,
708 cairo_t *cr,
709 gdouble x,
710 gdouble y,
711 gdouble width,
712 gdouble height,
713 guint hidden_side,
714 GtkJunctionSides junction)
715 {
716 GtkBorderImage border_image;
717 double border_width[4];
718
719 border_width[0] = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_WIDTH), 100);
720 border_width[1] = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH), 100);
721 border_width[2] = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH), 100);
722 border_width[3] = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH), 100);
723
724 if (gtk_border_image_init (&border_image, style))
725 {
726 gtk_border_image_render (&border_image, border_width, cr, x, y, width, height);
727 }
728 else
729 {
730 GtkBorderStyle border_style[4];
731 GtkRoundedBox border_box;
732 GdkRGBA colors[4];
733
734 /* Optimize the most common case of "This widget has no border" */
735 if (border_width[0] == 0 &&
736 border_width[1] == 0 &&
737 border_width[2] == 0 &&
738 border_width[3] == 0)
739 return;
740
741 border_style[0] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
742 border_style[1] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_RIGHT_STYLE));
743 border_style[2] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_BOTTOM_STYLE));
744 border_style[3] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_LEFT_STYLE));
745
746 hide_border_sides (border_width, border_style, hidden_side);
747
748 colors[0] = *_gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_COLOR));
749 colors[1] = *_gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_RIGHT_COLOR));
750 colors[2] = *_gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_BOTTOM_COLOR));
751 colors[3] = *_gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_LEFT_COLOR));
752
753 _gtk_rounded_box_init_rect (&border_box, x, y, width, height);
754 _gtk_rounded_box_apply_border_radius_for_style (&border_box, style, junction);
755
756 render_border (cr, &border_box, border_width, hidden_side, colors, border_style);
757 }
758 }
759
760 gboolean
gtk_css_style_render_border_get_clip(GtkCssStyle * style,gdouble x,gdouble y,gdouble width,gdouble height,GdkRectangle * out_clip)761 gtk_css_style_render_border_get_clip (GtkCssStyle *style,
762 gdouble x,
763 gdouble y,
764 gdouble width,
765 gdouble height,
766 GdkRectangle *out_clip)
767 {
768 if (!gtk_css_style_render_has_border (style))
769 return FALSE;
770
771 out_clip->x = floor (x);
772 out_clip->y = floor (y);
773 out_clip->width = ceil (x + width) - out_clip->x;
774 out_clip->height = ceil (y + height) - out_clip->y;
775
776 return TRUE;
777 }
778
779 gboolean
gtk_css_style_render_has_outline(GtkCssStyle * style)780 gtk_css_style_render_has_outline (GtkCssStyle *style)
781 {
782 return _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_OUTLINE_WIDTH), 100) > 0;
783 }
784
785 static void
compute_outline_rect(GtkCssStyle * style,gdouble x,gdouble y,gdouble width,gdouble height,cairo_rectangle_t * out_rect)786 compute_outline_rect (GtkCssStyle *style,
787 gdouble x,
788 gdouble y,
789 gdouble width,
790 gdouble height,
791 cairo_rectangle_t *out_rect)
792 {
793 double offset, owidth;
794
795 owidth = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_OUTLINE_WIDTH), 100);
796 offset = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_OUTLINE_OFFSET), 100);
797
798 if (width <= -2 * offset)
799 {
800 x += width / 2;
801 out_rect->x = x - owidth;
802 out_rect->width = 2 * owidth;
803 }
804 else
805 {
806 out_rect->x = x - offset - owidth;
807 out_rect->width = width + 2 * (offset + owidth);
808 }
809
810 if (height <= -2 * offset)
811 {
812 y += height / 2;
813 out_rect->y = y - owidth;
814 out_rect->height = 2 * owidth;
815 }
816 else
817 {
818 out_rect->y = y - offset - owidth;
819 out_rect->height = height + 2 * (offset + owidth);
820 }
821
822 }
823
824 void
gtk_css_style_render_outline(GtkCssStyle * style,cairo_t * cr,gdouble x,gdouble y,gdouble width,gdouble height)825 gtk_css_style_render_outline (GtkCssStyle *style,
826 cairo_t *cr,
827 gdouble x,
828 gdouble y,
829 gdouble width,
830 gdouble height)
831 {
832 GtkBorderStyle border_style[4];
833 GtkRoundedBox border_box;
834 double border_width[4];
835 GdkRGBA colors[4];
836
837 border_style[0] = _gtk_css_border_style_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_OUTLINE_STYLE));
838 if (border_style[0] != GTK_BORDER_STYLE_NONE)
839 {
840 cairo_rectangle_t rect;
841
842 compute_outline_rect (style, x, y, width, height, &rect);
843
844 border_style[1] = border_style[2] = border_style[3] = border_style[0];
845 border_width[0] = _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_OUTLINE_WIDTH), 100);
846 border_width[3] = border_width[2] = border_width[1] = border_width[0];
847 colors[0] = *_gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_OUTLINE_COLOR));
848 colors[3] = colors[2] = colors[1] = colors[0];
849
850 _gtk_rounded_box_init_rect (&border_box, rect.x, rect.y, rect.width, rect.height);
851 _gtk_rounded_box_apply_outline_radius_for_style (&border_box, style, GTK_JUNCTION_NONE);
852
853 render_border (cr, &border_box, border_width, 0, colors, border_style);
854 }
855 }
856
857 gboolean
gtk_css_style_render_outline_get_clip(GtkCssStyle * style,gdouble x,gdouble y,gdouble width,gdouble height,GdkRectangle * out_clip)858 gtk_css_style_render_outline_get_clip (GtkCssStyle *style,
859 gdouble x,
860 gdouble y,
861 gdouble width,
862 gdouble height,
863 GdkRectangle *out_clip)
864 {
865 cairo_rectangle_t rect;
866
867 if (!gtk_css_style_render_has_outline (style))
868 return FALSE;
869
870 compute_outline_rect (style, x, y, width, height, &rect);
871
872 out_clip->x = floor (rect.x);
873 out_clip->y = floor (rect.y);
874 out_clip->width = ceil (rect.x + rect.width) - out_clip->x;
875 out_clip->height = ceil (rect.y + rect.height) - out_clip->y;
876
877 return TRUE;
878 }
879