No issues found
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * st-shadow.c: Boxed type holding for -st-shadow attributes
4 *
5 * Copyright 2009, 2010 Florian Mç«Żllner
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation, either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include "st-shadow.h"
24 #include "st-private.h"
25
26 G_DEFINE_BOXED_TYPE (StShadow, st_shadow, st_shadow_ref, st_shadow_unref)
27 G_DEFINE_BOXED_TYPE (StShadowHelper, st_shadow_helper, st_shadow_helper_copy, st_shadow_helper_free)
28
29 /**
30 * SECTION: st-shadow
31 * @short_description: Boxed type for -st-shadow attributes
32 *
33 * #StShadow is a boxed type for storing attributes of the -st-shadow
34 * property, modelled liberally after the CSS3 box-shadow property.
35 * See http://www.css3.info/preview/box-shadow/
36 *
37 */
38
39 /**
40 * st_shadow_new:
41 * @color: shadow's color
42 * @xoffset: horizontal offset
43 * @yoffset: vertical offset
44 * @blur: blur radius
45 * @spread: spread radius
46 * @inset: whether the shadow should be inset
47 *
48 * Creates a new #StShadow
49 *
50 * Returns: the newly allocated shadow. Use st_shadow_free() when done
51 */
52 StShadow *
53 st_shadow_new (ClutterColor *color,
54 gdouble xoffset,
55 gdouble yoffset,
56 gdouble blur,
57 gdouble spread,
58 gboolean inset)
59 {
60 StShadow *shadow;
61
62 shadow = g_slice_new (StShadow);
63
64 shadow->color = *color;
65 shadow->xoffset = xoffset;
66 shadow->yoffset = yoffset;
67 shadow->blur = blur;
68 shadow->spread = spread;
69 shadow->inset = inset;
70 shadow->ref_count = 1;
71
72 return shadow;
73 }
74
75 /**
76 * st_shadow_ref:
77 * @shadow: a #StShadow
78 *
79 * Atomically increments the reference count of @shadow by one.
80 *
81 * Returns: the passed in #StShadow.
82 */
83 StShadow *
84 st_shadow_ref (StShadow *shadow)
85 {
86 g_return_val_if_fail (shadow != NULL, NULL);
87 g_return_val_if_fail (shadow->ref_count > 0, shadow);
88
89 g_atomic_int_inc (&shadow->ref_count);
90 return shadow;
91 }
92
93 /**
94 * st_shadow_unref:
95 * @shadow: a #StShadow
96 *
97 * Atomically decrements the reference count of @shadow by one.
98 * If the reference count drops to 0, all memory allocated by the
99 * #StShadow is released.
100 */
101 void
102 st_shadow_unref (StShadow *shadow)
103 {
104 g_return_if_fail (shadow != NULL);
105 g_return_if_fail (shadow->ref_count > 0);
106
107 if (g_atomic_int_dec_and_test (&shadow->ref_count))
108 g_slice_free (StShadow, shadow);
109 }
110
111 /**
112 * st_shadow_equal:
113 * @shadow: a #StShadow
114 * @other: a different #StShadow
115 *
116 * Check if two shadow objects are identical. Note that two shadows may
117 * compare non-identically if they differ only by floating point rounding
118 * errors.
119 *
120 * Return value: %TRUE if the two shadows are identical
121 */
122 gboolean
123 st_shadow_equal (StShadow *shadow,
124 StShadow *other)
125 {
126 g_return_val_if_fail (shadow != NULL, FALSE);
127 g_return_val_if_fail (other != NULL, FALSE);
128
129 /* We use strict equality to compare double quantities; this means
130 * that, for example, a shadow offset of 0.25in does not necessarily
131 * compare equal to a shadow offset of 18pt in this test. Assume
132 * that a few false negatives are mostly harmless.
133 */
134
135 return (clutter_color_equal (&shadow->color, &other->color) &&
136 shadow->xoffset == other->xoffset &&
137 shadow->yoffset == other->yoffset &&
138 shadow->blur == other->blur &&
139 shadow->spread == other->spread &&
140 shadow->inset == other->inset);
141 }
142
143 /**
144 * st_shadow_get_box:
145 * @shadow: a #StShadow
146 * @actor_box: the box allocated to a #ClutterAlctor
147 * @shadow_box: computed box occupied by @shadow
148 *
149 * Gets the box used to paint @shadow, which will be partly
150 * outside of @actor_box
151 */
152 void
153 st_shadow_get_box (StShadow *shadow,
154 const ClutterActorBox *actor_box,
155 ClutterActorBox *shadow_box)
156 {
157 g_return_if_fail (shadow != NULL);
158 g_return_if_fail (actor_box != NULL);
159 g_return_if_fail (shadow_box != NULL);
160
161 /* Inset shadows are drawn below the border, so returning
162 * the original box is not actually correct; still, it's
163 * good enough for the purpose of determing additional space
164 * required outside the actor box.
165 */
166 if (shadow->inset)
167 {
168 *shadow_box = *actor_box;
169 return;
170 }
171
172 shadow_box->x1 = actor_box->x1 + shadow->xoffset
173 - shadow->blur - shadow->spread;
174 shadow_box->x2 = actor_box->x2 + shadow->xoffset
175 + shadow->blur + shadow->spread;
176 shadow_box->y1 = actor_box->y1 + shadow->yoffset
177 - shadow->blur - shadow->spread;
178 shadow_box->y2 = actor_box->y2 + shadow->yoffset
179 + shadow->blur + shadow->spread;
180 }
181
182 /**
183 * SECTION:st-shadow-helper:
184 *
185 * An helper for implementing a drop shadow on a actor.
186 * The actor is expected to recreate the helper whenever its contents
187 * or size change. Then, it would call st_shadow_helper_paint() inside
188 * its paint() virtual function.
189 */
190
191 struct _StShadowHelper {
192 StShadow *shadow;
193 CoglMaterial *material;
194
195 gfloat width;
196 gfloat height;
197 };
198
199 /**
200 * st_shadow_helper_new:
201 * @shadow: a #StShadow representing the shadow properties
202 *
203 * Builds a #StShadowHelper that will build a drop shadow
204 * using @source as the mask.
205 *
206 * Returns: (transfer full): a new #StShadowHelper
207 */
208 StShadowHelper *
209 st_shadow_helper_new (StShadow *shadow)
210 {
211 StShadowHelper *helper;
212
213 helper = g_slice_new0 (StShadowHelper);
214 helper->shadow = st_shadow_ref (shadow);
215
216 return helper;
217 }
218
219 void
220 st_shadow_helper_update (StShadowHelper *helper,
221 ClutterActor *source)
222 {
223 gfloat width, height;
224
225 clutter_actor_get_size (source, &width, &height);
226
227 if (helper->material == NULL ||
228 helper->width != width ||
229 helper->height != height)
230 {
231 if (helper->material)
232 cogl_object_unref (helper->material);
233
234 helper->material = _st_create_shadow_material_from_actor (helper->shadow, source);
235 helper->width = width;
236 helper->height = height;
237 }
238 }
239
240 /**
241 * st_shadow_helper_copy:
242 * @helper: the #StShadowHelper to copy
243 *
244 * Returns: (transfer full): a copy of @helper
245 */
246 StShadowHelper *
247 st_shadow_helper_copy (StShadowHelper *helper)
248 {
249 StShadowHelper *copy;
250
251 copy = g_slice_new (StShadowHelper);
252 *copy = *helper;
253 if (copy->material)
254 cogl_object_ref (copy->material);
255 st_shadow_ref (copy->shadow);
256
257 return copy;
258 }
259
260 /**
261 * st_shadow_helper_free:
262 * @helper: a #StShadowHelper
263 *
264 * Free resources associated with @helper.
265 */
266 void
267 st_shadow_helper_free (StShadowHelper *helper)
268 {
269 if (helper->material)
270 cogl_object_unref (helper->material);
271 st_shadow_unref (helper->shadow);
272
273 g_slice_free (StShadowHelper, helper);
274 }
275
276 /**
277 * st_shadow_helper_paint:
278 * @helper: a #StShadowHelper
279 * @actor_box: the bounding box of the shadow
280 * @paint_opacity: the opacity at which the shadow is painted
281 *
282 * Paints the shadow associated with @helper This must only
283 * be called from the implementation of ClutterActor::paint().
284 */
285 void
286 st_shadow_helper_paint (StShadowHelper *helper,
287 ClutterActorBox *actor_box,
288 guint8 paint_opacity)
289 {
290 ClutterActorBox allocation;
291 float width, height;
292
293 clutter_actor_box_get_size (actor_box, &width, &height);
294
295 allocation.x1 = (width - helper->width) / 2;
296 allocation.y1 = (height - helper->height) / 2;
297 allocation.x2 = allocation.x1 + helper->width;
298 allocation.y2 = allocation.y1 + helper->height;
299
300 _st_paint_shadow_with_opacity (helper->shadow,
301 helper->material,
302 &allocation,
303 paint_opacity);
304 }