1use euclid::{vec2, size2};
12use api::{ColorF, ColorU, ExtendMode, GradientStop, PremultipliedColorF};
13use api::units::*;
14use crate::pattern::{Pattern, PatternBuilder, PatternBuilderContext, PatternBuilderState, PatternKind, PatternShaderInput, PatternTextureInput};
15use crate::scene_building::IsVisible;
16use crate::frame_builder::FrameBuildingState;
17use crate::intern::{Internable, InternDebug, Handle as InternHandle};
18use crate::internal_types::LayoutPrimitiveInfo;
19use crate::prim_store::{BrushSegment, GradientTileRange, InternablePrimitive};
20use crate::prim_store::{PrimitiveInstanceKind, PrimitiveOpacity};
21use crate::prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore};
22use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, FloatKey};
23use crate::render_task::{RenderTask, RenderTaskKind};
24use crate::render_task_graph::RenderTaskId;
25use crate::render_task_cache::{RenderTaskCacheKeyKind, RenderTaskCacheKey, RenderTaskParent};
26use crate::renderer::{GpuBufferAddress, GpuBufferBuilder};
27
28use std::{hash, ops::{Deref, DerefMut}};
29use super::{
30 stops_and_min_alpha, GradientStopKey, GradientGpuBlockBuilder,
31 apply_gradient_local_clip,
32};
33
34#[cfg_attr(feature = "capture", derive(Serialize))]
36#[cfg_attr(feature = "replay", derive(Deserialize))]
37#[derive(Debug, Clone, MallocSizeOf, PartialEq)]
38pub struct RadialGradientParams {
39 pub start_radius: f32,
40 pub end_radius: f32,
41 pub ratio_xy: f32,
42}
43
44impl Eq for RadialGradientParams {}
45
46impl hash::Hash for RadialGradientParams {
47 fn hash<H: hash::Hasher>(&self, state: &mut H) {
48 self.start_radius.to_bits().hash(state);
49 self.end_radius.to_bits().hash(state);
50 self.ratio_xy.to_bits().hash(state);
51 }
52}
53
54#[cfg_attr(feature = "capture", derive(Serialize))]
56#[cfg_attr(feature = "replay", derive(Deserialize))]
57#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)]
58pub struct RadialGradientKey {
59 pub common: PrimKeyCommonData,
60 pub extend_mode: ExtendMode,
61 pub center: PointKey,
62 pub params: RadialGradientParams,
63 pub stretch_size: SizeKey,
64 pub stops: Vec<GradientStopKey>,
65 pub tile_spacing: SizeKey,
66 pub nine_patch: Option<Box<NinePatchDescriptor>>,
67}
68
69impl RadialGradientKey {
70 pub fn new(
71 info: &LayoutPrimitiveInfo,
72 radial_grad: RadialGradient,
73 ) -> Self {
74 RadialGradientKey {
75 common: info.into(),
76 extend_mode: radial_grad.extend_mode,
77 center: radial_grad.center,
78 params: radial_grad.params,
79 stretch_size: radial_grad.stretch_size,
80 stops: radial_grad.stops,
81 tile_spacing: radial_grad.tile_spacing,
82 nine_patch: radial_grad.nine_patch,
83 }
84 }
85}
86
87impl InternDebug for RadialGradientKey {}
88
89#[cfg_attr(feature = "capture", derive(Serialize))]
90#[cfg_attr(feature = "replay", derive(Deserialize))]
91#[derive(MallocSizeOf)]
92#[derive(Debug)]
93pub struct RadialGradientTemplate {
94 pub common: PrimTemplateCommonData,
95 pub extend_mode: ExtendMode,
96 pub params: RadialGradientParams,
97 pub center: DevicePoint,
98 pub task_size: DeviceIntSize,
99 pub scale: DeviceVector2D,
100 pub stretch_size: LayoutSize,
101 pub tile_spacing: LayoutSize,
102 pub brush_segments: Vec<BrushSegment>,
103 pub stops_opacity: PrimitiveOpacity,
104 pub stops: Vec<GradientStop>,
105 pub src_color: Option<RenderTaskId>,
106}
107
108impl PatternBuilder for RadialGradientTemplate {
109 fn build(
110 &self,
111 _sub_rect: Option<DeviceRect>,
112 _ctx: &PatternBuilderContext,
113 state: &mut PatternBuilderState,
114 ) -> Pattern {
115 let no_scale = DeviceVector2D::one();
118
119 radial_gradient_pattern(
120 self.center,
121 no_scale,
122 &self.params,
123 self.extend_mode,
124 &self.stops,
125 state.frame_gpu_data,
126 )
127 }
128
129 fn get_base_color(
130 &self,
131 _ctx: &PatternBuilderContext,
132 ) -> ColorF {
133 ColorF::WHITE
134 }
135
136 fn use_shared_pattern(
137 &self,
138 ) -> bool {
139 true
140 }
141}
142
143impl Deref for RadialGradientTemplate {
144 type Target = PrimTemplateCommonData;
145 fn deref(&self) -> &Self::Target {
146 &self.common
147 }
148}
149
150impl DerefMut for RadialGradientTemplate {
151 fn deref_mut(&mut self) -> &mut Self::Target {
152 &mut self.common
153 }
154}
155
156impl From<RadialGradientKey> for RadialGradientTemplate {
157 fn from(item: RadialGradientKey) -> Self {
158 let common = PrimTemplateCommonData::with_key_common(item.common);
159 let mut brush_segments = Vec::new();
160
161 if let Some(ref nine_patch) = item.nine_patch {
162 brush_segments = nine_patch.create_segments(common.prim_rect.size());
163 }
164
165 let (stops, min_alpha) = stops_and_min_alpha(&item.stops);
166
167 let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha);
171
172 let mut stretch_size: LayoutSize = item.stretch_size.into();
173 stretch_size.width = stretch_size.width.min(common.prim_rect.width());
174 stretch_size.height = stretch_size.height.min(common.prim_rect.height());
175
176 const MAX_SIZE: f32 = 1024.0;
180 let mut task_size: DeviceSize = stretch_size.cast_unit();
181 let mut scale = vec2(1.0, 1.0);
182 if task_size.width > MAX_SIZE {
183 scale.x = task_size.width/ MAX_SIZE;
184 task_size.width = MAX_SIZE;
185 }
186 if task_size.height > MAX_SIZE {
187 scale.y = task_size.height /MAX_SIZE;
188 task_size.height = MAX_SIZE;
189 }
190
191 RadialGradientTemplate {
192 common,
193 center: DevicePoint::new(item.center.x, item.center.y),
194 extend_mode: item.extend_mode,
195 params: item.params,
196 stretch_size,
197 task_size: task_size.ceil().to_i32(),
198 scale,
199 tile_spacing: item.tile_spacing.into(),
200 brush_segments,
201 stops_opacity,
202 stops,
203 src_color: None,
204 }
205 }
206}
207
208impl RadialGradientTemplate {
209 pub fn update(
214 &mut self,
215 frame_state: &mut FrameBuildingState,
216 ) {
217 if let Some(mut request) =
218 frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
219 request.push(PremultipliedColorF::WHITE);
221 request.push(PremultipliedColorF::WHITE);
222 request.push([
223 self.stretch_size.width,
224 self.stretch_size.height,
225 0.0,
226 0.0,
227 ]);
228
229 for segment in &self.brush_segments {
231 request.write_segment(
233 segment.local_rect,
234 segment.extra_data,
235 );
236 }
237 }
238
239 let task_size = self.task_size;
240 let cache_key = RadialGradientCacheKey {
241 size: task_size,
242 center: PointKey { x: self.center.x, y: self.center.y },
243 scale: PointKey { x: self.scale.x, y: self.scale.y },
244 start_radius: FloatKey(self.params.start_radius),
245 end_radius: FloatKey(self.params.end_radius),
246 ratio_xy: FloatKey(self.params.ratio_xy),
247 extend_mode: self.extend_mode,
248 stops: self.stops.iter().map(|stop| (*stop).into()).collect(),
249 };
250
251 let task_id = frame_state.resource_cache.request_render_task(
252 Some(RenderTaskCacheKey {
253 size: task_size,
254 kind: RenderTaskCacheKeyKind::RadialGradient(cache_key),
255 }),
256 false,
257 RenderTaskParent::Surface,
258 frame_state.gpu_cache,
259 &mut frame_state.frame_gpu_data.f32,
260 frame_state.rg_builder,
261 &mut frame_state.surface_builder,
262 &mut |rg_builder, gpu_buffer_builder, _| {
263 let stops = GradientGpuBlockBuilder::build(
264 false,
265 gpu_buffer_builder,
266 &self.stops,
267 );
268
269 rg_builder.add().init(RenderTask::new_dynamic(
270 task_size,
271 RenderTaskKind::RadialGradient(RadialGradientTask {
272 extend_mode: self.extend_mode,
273 center: self.center,
274 scale: self.scale,
275 params: self.params.clone(),
276 stops,
277 }),
278 ))
279 }
280 );
281
282 self.src_color = Some(task_id);
283
284 self.opacity = self.stops_opacity;
289 }
290}
291
292pub type RadialGradientDataHandle = InternHandle<RadialGradient>;
293
294#[derive(Debug, MallocSizeOf)]
295#[cfg_attr(feature = "capture", derive(Serialize))]
296#[cfg_attr(feature = "replay", derive(Deserialize))]
297pub struct RadialGradient {
298 pub extend_mode: ExtendMode,
299 pub center: PointKey,
300 pub params: RadialGradientParams,
301 pub stretch_size: SizeKey,
302 pub stops: Vec<GradientStopKey>,
303 pub tile_spacing: SizeKey,
304 pub nine_patch: Option<Box<NinePatchDescriptor>>,
305}
306
307impl Internable for RadialGradient {
308 type Key = RadialGradientKey;
309 type StoreData = RadialGradientTemplate;
310 type InternData = ();
311 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_RADIAL_GRADIENTS;
312}
313
314impl InternablePrimitive for RadialGradient {
315 fn into_key(
316 self,
317 info: &LayoutPrimitiveInfo,
318 ) -> RadialGradientKey {
319 RadialGradientKey::new(info, self)
320 }
321
322 fn make_instance_kind(
323 _key: RadialGradientKey,
324 data_handle: RadialGradientDataHandle,
325 _prim_store: &mut PrimitiveStore,
326 ) -> PrimitiveInstanceKind {
327 PrimitiveInstanceKind::RadialGradient {
328 data_handle,
329 visible_tiles_range: GradientTileRange::empty(),
330 cached: true,
331 }
332 }
333}
334
335impl IsVisible for RadialGradient {
336 fn is_visible(&self) -> bool {
337 true
338 }
339}
340
341#[derive(Debug)]
342#[cfg_attr(feature = "capture", derive(Serialize))]
343#[cfg_attr(feature = "replay", derive(Deserialize))]
344pub struct RadialGradientTask {
345 pub extend_mode: ExtendMode,
346 pub center: DevicePoint,
347 pub scale: DeviceVector2D,
348 pub params: RadialGradientParams,
349 pub stops: GpuBufferAddress,
350}
351
352impl RadialGradientTask {
353 pub fn to_instance(&self, target_rect: &DeviceIntRect) -> RadialGradientInstance {
354 RadialGradientInstance {
355 task_rect: target_rect.to_f32(),
356 center: self.center,
357 scale: self.scale,
358 start_radius: self.params.start_radius,
359 end_radius: self.params.end_radius,
360 ratio_xy: self.params.ratio_xy,
361 extend_mode: self.extend_mode as i32,
362 gradient_stops_address: self.stops.as_int(),
363 }
364 }
365}
366
367#[cfg_attr(feature = "capture", derive(Serialize))]
371#[cfg_attr(feature = "replay", derive(Deserialize))]
372#[repr(C)]
373#[derive(Clone, Debug)]
374pub struct RadialGradientInstance {
375 pub task_rect: DeviceRect,
376 pub center: DevicePoint,
377 pub scale: DeviceVector2D,
378 pub start_radius: f32,
379 pub end_radius: f32,
380 pub ratio_xy: f32,
381 pub extend_mode: i32,
382 pub gradient_stops_address: i32,
383}
384
385#[derive(Clone, Debug, Hash, PartialEq, Eq)]
386#[cfg_attr(feature = "capture", derive(Serialize))]
387#[cfg_attr(feature = "replay", derive(Deserialize))]
388pub struct RadialGradientCacheKey {
389 pub size: DeviceIntSize,
390 pub center: PointKey,
391 pub scale: PointKey,
392 pub start_radius: FloatKey,
393 pub end_radius: FloatKey,
394 pub ratio_xy: FloatKey,
395 pub extend_mode: ExtendMode,
396 pub stops: Vec<GradientStopKey>,
397}
398
399pub fn optimize_radial_gradient(
412 prim_rect: &mut LayoutRect,
413 stretch_size: &mut LayoutSize,
414 center: &mut LayoutPoint,
415 tile_spacing: &mut LayoutSize,
416 clip_rect: &LayoutRect,
417 radius: LayoutSize,
418 end_offset: f32,
419 extend_mode: ExtendMode,
420 stops: &[GradientStopKey],
421 solid_parts: &mut dyn FnMut(&LayoutRect, ColorU),
422) {
423 let offset = apply_gradient_local_clip(
424 prim_rect,
425 stretch_size,
426 tile_spacing,
427 clip_rect
428 );
429
430 *center += offset;
431
432 if extend_mode != ExtendMode::Clamp || stops.is_empty() {
433 return;
434 }
435
436 let min = prim_rect.min + center.to_vector() - radius.to_vector() * end_offset;
438 let max = prim_rect.min + center.to_vector() + radius.to_vector() * end_offset;
439
440 let gradient_rect = LayoutRect::from_origin_and_size(
442 prim_rect.min,
443 *stretch_size,
444 );
445
446 let mut l = (min.x - gradient_rect.min.x).max(0.0).floor();
449 let mut t = (min.y - gradient_rect.min.y).max(0.0).floor();
450 let mut r = (gradient_rect.max.x - max.x).max(0.0).floor();
451 let mut b = (gradient_rect.max.y - max.y).max(0.0).floor();
452
453 let is_tiled = prim_rect.width() > stretch_size.width + tile_spacing.width
454 || prim_rect.height() > stretch_size.height + tile_spacing.height;
455
456 let bg_color = stops.last().unwrap().color;
457
458 if bg_color.a != 0 && is_tiled {
459 return;
462 }
463
464 if bg_color.a != 0 || (is_tiled && tile_spacing.is_empty()) {
471 let threshold = 128.0;
472 if l < threshold { l = 0.0 }
473 if t < threshold { t = 0.0 }
474 if r < threshold { r = 0.0 }
475 if b < threshold { b = 0.0 }
476 }
477
478 if l + t + r + b == 0.0 {
479 return;
481 }
482
483 if bg_color.a != 0 {
486 if l != 0.0 && t != 0.0 {
487 let solid_rect = LayoutRect::from_origin_and_size(
488 gradient_rect.min,
489 size2(l, t),
490 );
491 solid_parts(&solid_rect, bg_color);
492 }
493
494 if l != 0.0 && b != 0.0 {
495 let solid_rect = LayoutRect::from_origin_and_size(
496 gradient_rect.bottom_left() - vec2(0.0, b),
497 size2(l, b),
498 );
499 solid_parts(&solid_rect, bg_color);
500 }
501
502 if t != 0.0 && r != 0.0 {
503 let solid_rect = LayoutRect::from_origin_and_size(
504 gradient_rect.top_right() - vec2(r, 0.0),
505 size2(r, t),
506 );
507 solid_parts(&solid_rect, bg_color);
508 }
509
510 if r != 0.0 && b != 0.0 {
511 let solid_rect = LayoutRect::from_origin_and_size(
512 gradient_rect.bottom_right() - vec2(r, b),
513 size2(r, b),
514 );
515 solid_parts(&solid_rect, bg_color);
516 }
517
518 if l != 0.0 {
519 let solid_rect = LayoutRect::from_origin_and_size(
520 gradient_rect.min + vec2(0.0, t),
521 size2(l, gradient_rect.height() - t - b),
522 );
523 solid_parts(&solid_rect, bg_color);
524 }
525
526 if r != 0.0 {
527 let solid_rect = LayoutRect::from_origin_and_size(
528 gradient_rect.top_right() + vec2(-r, t),
529 size2(r, gradient_rect.height() - t - b),
530 );
531 solid_parts(&solid_rect, bg_color);
532 }
533
534 if t != 0.0 {
535 let solid_rect = LayoutRect::from_origin_and_size(
536 gradient_rect.min + vec2(l, 0.0),
537 size2(gradient_rect.width() - l - r, t),
538 );
539 solid_parts(&solid_rect, bg_color);
540 }
541
542 if b != 0.0 {
543 let solid_rect = LayoutRect::from_origin_and_size(
544 gradient_rect.bottom_left() + vec2(l, -b),
545 size2(gradient_rect.width() - l - r, b),
546 );
547 solid_parts(&solid_rect, bg_color);
548 }
549 }
550
551 prim_rect.min.x += l;
554 prim_rect.min.y += t;
555
556 stretch_size.width -= l + r;
557 stretch_size.height -= b + t;
558
559 center.x -= l;
560 center.y -= t;
561
562 tile_spacing.width += l + r;
563 tile_spacing.height += t + b;
564}
565
566pub fn radial_gradient_pattern(
567 center: DevicePoint,
568 scale: DeviceVector2D,
569 params: &RadialGradientParams,
570 extend_mode: ExtendMode,
571 stops: &[GradientStop],
572 gpu_buffer_builder: &mut GpuBufferBuilder
573) -> Pattern {
574 let mut writer = gpu_buffer_builder.f32.write_blocks(2);
575 writer.push_one([
576 center.x,
577 center.y,
578 scale.x,
579 scale.y,
580 ]);
581 writer.push_one([
582 params.start_radius,
583 params.end_radius,
584 params.ratio_xy,
585 if extend_mode == ExtendMode::Repeat { 1.0 } else { 0.0 }
586 ]);
587 let gradient_address = writer.finish();
588
589 let stops_address = GradientGpuBlockBuilder::build(
590 false,
591 &mut gpu_buffer_builder.f32,
592 &stops,
593 );
594
595 let is_opaque = stops.iter().all(|stop| stop.color.a >= 1.0);
596
597 Pattern {
598 kind: PatternKind::RadialGradient,
599 shader_input: PatternShaderInput(
600 gradient_address.as_int(),
601 stops_address.as_int(),
602 ),
603 texture_input: PatternTextureInput::default(),
604 base_color: ColorF::WHITE,
605 is_opaque,
606 }
607}