1use euclid::{vec2, size2};
12use api::{ColorU, ExtendMode, GradientStop};
13use api::units::*;
14use crate::pattern::gradient::{radial_gradient_pattern};
15use crate::pattern::{Pattern, PatternBuilder, PatternBuilderContext, PatternBuilderState};
16use crate::scene_building::IsVisible;
17use crate::intern::{Internable, InternDebug, Handle as InternHandle};
18use crate::internal_types::LayoutPrimitiveInfo;
19use crate::prim_store::{InternablePrimitive};
20use crate::prim_store::{PrimitiveKind, PrimitiveOpacity};
21use crate::prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore};
22use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey};
23use crate::segment::EdgeMask;
24
25use std::{hash, ops::{Deref, DerefMut}};
26use super::{
27 stops_and_min_alpha, GradientStopKey,
28 apply_gradient_local_clip,
29};
30
31#[cfg_attr(feature = "capture", derive(Serialize))]
33#[cfg_attr(feature = "replay", derive(Deserialize))]
34#[derive(Debug, Clone, MallocSizeOf, PartialEq)]
35pub struct RadialGradientParams {
36 pub start_radius: f32,
37 pub end_radius: f32,
38 pub ratio_xy: f32,
39}
40
41impl Eq for RadialGradientParams {}
42
43impl hash::Hash for RadialGradientParams {
44 fn hash<H: hash::Hasher>(&self, state: &mut H) {
45 self.start_radius.to_bits().hash(state);
46 self.end_radius.to_bits().hash(state);
47 self.ratio_xy.to_bits().hash(state);
48 }
49}
50
51#[cfg_attr(feature = "capture", derive(Serialize))]
53#[cfg_attr(feature = "replay", derive(Deserialize))]
54#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)]
55pub struct RadialGradientKey {
56 pub common: PrimKeyCommonData,
57 pub extend_mode: ExtendMode,
58 pub center: PointKey,
59 pub params: RadialGradientParams,
60 pub stretch_ratio: SizeKey,
63 pub stops: Vec<GradientStopKey>,
64 pub tile_spacing: SizeKey,
65 pub nine_patch: Option<Box<NinePatchDescriptor>>,
66}
67
68impl RadialGradientKey {
69 pub fn new(
70 info: &LayoutPrimitiveInfo,
71 radial_grad: RadialGradient,
72 ) -> Self {
73 RadialGradientKey {
74 common: info.into(),
75 extend_mode: radial_grad.extend_mode,
76 center: radial_grad.center,
77 params: radial_grad.params,
78 stretch_ratio: radial_grad.stretch_ratio,
79 stops: radial_grad.stops,
80 tile_spacing: radial_grad.tile_spacing,
81 nine_patch: radial_grad.nine_patch,
82 }
83 }
84}
85
86impl InternDebug for RadialGradientKey {}
87
88#[cfg_attr(feature = "capture", derive(Serialize))]
89#[cfg_attr(feature = "replay", derive(Deserialize))]
90#[derive(MallocSizeOf)]
91#[derive(Debug)]
92pub struct RadialGradientTemplate {
93 pub common: PrimTemplateCommonData,
94 pub extend_mode: ExtendMode,
95 pub params: RadialGradientParams,
96 pub center: LayoutPoint,
97 pub stretch_ratio: LayoutSize,
101 pub tile_spacing: LayoutSize,
102 pub border_nine_patch: Option<Box<NinePatchDescriptor>>,
103 pub stops_opacity: PrimitiveOpacity,
104 pub stops: Vec<GradientStop>,
105}
106
107impl PatternBuilder for RadialGradientTemplate {
108 fn build(
109 &self,
110 _sub_rect: Option<DeviceRect>,
111 offset: LayoutVector2D,
112 ctx: &PatternBuilderContext,
113 state: &mut PatternBuilderState,
114 ) -> Pattern {
115 let no_scale = DeviceVector2D::one();
118
119 let center = self.center.cast_unit() + ctx.prim_origin.to_vector() + offset;
123
124 radial_gradient_pattern(
125 center,
126 no_scale,
127 self.params.start_radius,
128 self.params.end_radius,
129 self.params.ratio_xy,
130 self.extend_mode,
131 &self.stops,
132 ctx.fb_config.is_software,
133 state.frame_gpu_data,
134 )
135 }
136}
137
138impl Deref for RadialGradientTemplate {
139 type Target = PrimTemplateCommonData;
140 fn deref(&self) -> &Self::Target {
141 &self.common
142 }
143}
144
145impl DerefMut for RadialGradientTemplate {
146 fn deref_mut(&mut self) -> &mut Self::Target {
147 &mut self.common
148 }
149}
150
151impl From<RadialGradientKey> for RadialGradientTemplate {
152 fn from(item: RadialGradientKey) -> Self {
153 let common = PrimTemplateCommonData::with_key_common(item.common);
154
155 let (stops, min_alpha) = stops_and_min_alpha(&item.stops);
156
157 let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha);
161
162 RadialGradientTemplate {
163 common,
164 center: item.center.into(),
165 extend_mode: item.extend_mode,
166 params: item.params,
167 stretch_ratio: item.stretch_ratio.into(),
168 tile_spacing: item.tile_spacing.into(),
169 border_nine_patch: item.nine_patch,
170 stops_opacity,
171 stops,
172 }
173 }
174}
175
176pub type RadialGradientDataHandle = InternHandle<RadialGradient>;
177
178#[derive(Debug, MallocSizeOf)]
179#[cfg_attr(feature = "capture", derive(Serialize))]
180#[cfg_attr(feature = "replay", derive(Deserialize))]
181pub struct RadialGradient {
182 pub extend_mode: ExtendMode,
183 pub center: PointKey,
184 pub params: RadialGradientParams,
185 pub stretch_ratio: SizeKey,
188 pub stops: Vec<GradientStopKey>,
189 pub tile_spacing: SizeKey,
190 pub nine_patch: Option<Box<NinePatchDescriptor>>,
191}
192
193impl Internable for RadialGradient {
194 type Key = RadialGradientKey;
195 type StoreData = RadialGradientTemplate;
196 type InternData = ();
197 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_RADIAL_GRADIENTS;
198}
199
200impl InternablePrimitive for RadialGradient {
201 fn into_key(
202 self,
203 info: &LayoutPrimitiveInfo,
204 ) -> RadialGradientKey {
205 RadialGradientKey::new(info, self)
206 }
207
208 fn make_instance_kind(
209 _key: RadialGradientKey,
210 data_handle: RadialGradientDataHandle,
211 _prim_store: &mut PrimitiveStore,
212 ) -> PrimitiveKind {
213 PrimitiveKind::RadialGradient {
214 data_handle,
215 }
216 }
217}
218
219impl IsVisible for RadialGradient {
220 fn is_visible(&self) -> bool {
221 true
222 }
223}
224
225
226pub fn optimize_radial_gradient(
244 prim_rect: &mut LayoutRect,
245 stretch_size: &mut LayoutSize,
246 center: &mut LayoutPoint,
247 tile_spacing: &mut LayoutSize,
248 aa_mask: &mut EdgeMask,
249 clip_rect: &LayoutRect,
250 radius: LayoutSize,
251 end_offset: f32,
252 extend_mode: ExtendMode,
253 stops: &[GradientStopKey],
254 solid_parts: &mut dyn FnMut(&LayoutRect, ColorU, EdgeMask),
255) {
256 let offset = apply_gradient_local_clip(
257 prim_rect,
258 stretch_size,
259 tile_spacing,
260 clip_rect
261 );
262
263 *center += offset;
264
265 if extend_mode != ExtendMode::Clamp || stops.is_empty() {
266 return;
267 }
268
269 let min = prim_rect.min + center.to_vector() - radius.to_vector() * end_offset;
271 let max = prim_rect.min + center.to_vector() + radius.to_vector() * end_offset;
272
273 let gradient_rect = LayoutRect::from_origin_and_size(
275 prim_rect.min,
276 *stretch_size,
277 );
278
279 let mut l = (min.x - gradient_rect.min.x).max(0.0).floor();
282 let mut t = (min.y - gradient_rect.min.y).max(0.0).floor();
283 let mut r = (gradient_rect.max.x - max.x).max(0.0).floor();
284 let mut b = (gradient_rect.max.y - max.y).max(0.0).floor();
285
286 let is_tiled = prim_rect.width() > stretch_size.width + tile_spacing.width
287 || prim_rect.height() > stretch_size.height + tile_spacing.height;
288
289 let bg_color = stops.last().unwrap().color;
290
291 if bg_color.a != 0 && is_tiled {
292 return;
295 }
296
297 if bg_color.a != 0 || (is_tiled && tile_spacing.is_empty()) {
304 let threshold = 128.0;
305 if l < threshold { l = 0.0 }
306 if t < threshold { t = 0.0 }
307 if r < threshold { r = 0.0 }
308 if b < threshold { b = 0.0 }
309 }
310
311 if l + t + r + b == 0.0 {
312 return;
314 }
315
316 if bg_color.a != 0 {
319 if l != 0.0 && t != 0.0 {
320 let solid_rect = LayoutRect::from_origin_and_size(
321 gradient_rect.min,
322 size2(l, t),
323 );
324 solid_parts(&solid_rect, bg_color, EdgeMask::LEFT | EdgeMask::TOP);
325 }
326
327 if l != 0.0 && b != 0.0 {
328 let solid_rect = LayoutRect::from_origin_and_size(
329 gradient_rect.bottom_left() - vec2(0.0, b),
330 size2(l, b),
331 );
332 solid_parts(&solid_rect, bg_color, EdgeMask::LEFT | EdgeMask::BOTTOM);
333 }
334
335 if t != 0.0 && r != 0.0 {
336 let solid_rect = LayoutRect::from_origin_and_size(
337 gradient_rect.top_right() - vec2(r, 0.0),
338 size2(r, t),
339 );
340 solid_parts(&solid_rect, bg_color, EdgeMask::TOP | EdgeMask::RIGHT);
341 }
342
343 if r != 0.0 && b != 0.0 {
344 let solid_rect = LayoutRect::from_origin_and_size(
345 gradient_rect.bottom_right() - vec2(r, b),
346 size2(r, b),
347 );
348 solid_parts(&solid_rect, bg_color, EdgeMask::RIGHT | EdgeMask::BOTTOM);
349 }
350
351 if l != 0.0 {
352 let solid_rect = LayoutRect::from_origin_and_size(
353 gradient_rect.min + vec2(0.0, t),
354 size2(l, gradient_rect.height() - t - b),
355 );
356 let mut solid_aa = EdgeMask::LEFT;
357 solid_aa.set(EdgeMask::TOP, t == 0.0);
358 solid_aa.set(EdgeMask::BOTTOM, b == 0.0);
359 solid_parts(&solid_rect, bg_color, solid_aa);
360 aa_mask.remove(EdgeMask::LEFT);
361 }
362
363 if r != 0.0 {
364 let solid_rect = LayoutRect::from_origin_and_size(
365 gradient_rect.top_right() + vec2(-r, t),
366 size2(r, gradient_rect.height() - t - b),
367 );
368 let mut solid_aa = EdgeMask::RIGHT;
369 solid_aa.set(EdgeMask::TOP, t == 0.0);
370 solid_aa.set(EdgeMask::BOTTOM, b == 0.0);
371 solid_parts(&solid_rect, bg_color, solid_aa);
372 aa_mask.remove(EdgeMask::RIGHT);
373 }
374
375 if t != 0.0 {
376 let solid_rect = LayoutRect::from_origin_and_size(
377 gradient_rect.min + vec2(l, 0.0),
378 size2(gradient_rect.width() - l - r, t),
379 );
380 let mut solid_aa = EdgeMask::TOP;
381 solid_aa.set(EdgeMask::LEFT, l == 0.0);
382 solid_aa.set(EdgeMask::RIGHT, r == 0.0);
383 solid_parts(&solid_rect, bg_color, solid_aa);
384 aa_mask.remove(EdgeMask::TOP);
385 }
386
387 if b != 0.0 {
388 let solid_rect = LayoutRect::from_origin_and_size(
389 gradient_rect.bottom_left() + vec2(l, -b),
390 size2(gradient_rect.width() - l - r, b),
391 );
392 let mut solid_aa = EdgeMask::BOTTOM;
393 solid_aa.set(EdgeMask::LEFT, l == 0.0);
394 solid_aa.set(EdgeMask::RIGHT, r == 0.0);
395 solid_parts(&solid_rect, bg_color, solid_aa);
396 aa_mask.remove(EdgeMask::BOTTOM);
397 }
398 }
399
400 prim_rect.min.x += l;
403 prim_rect.min.y += t;
404
405 stretch_size.width -= l + r;
406 stretch_size.height -= b + t;
407
408 center.x -= l;
409 center.y -= t;
410
411 tile_spacing.width += l + r;
412 tile_spacing.height += t + b;
413}