1use api::{
6 ColorF, ColorU, ExtendMode, GradientStop,
7 PremultipliedColorF, LineOrientation, PrimitiveFlags,
8};
9use api::units::{LayoutPoint, LayoutSize, LayoutVector2D};
10use crate::scene_building::IsVisible;
11use euclid::approxeq::ApproxEq;
12use crate::frame_builder::FrameBuildingState;
13use crate::gpu_cache::{GpuCacheHandle, GpuDataRequest};
14use crate::intern::{Internable, InternDebug, Handle as InternHandle};
15use crate::internal_types::LayoutPrimitiveInfo;
16use crate::prim_store::{BrushSegment, GradientTileRange, VectorKey};
17use crate::prim_store::{PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData};
18use crate::prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore};
19use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, InternablePrimitive};
20use crate::render_task_cache::RenderTaskCacheEntryHandle;
21use std::{hash, ops::{Deref, DerefMut}};
22use crate::util::pack_as_float;
23
24pub const GRADIENT_FP_STOPS: usize = 4;
26
27#[cfg_attr(feature = "capture", derive(Serialize))]
29#[cfg_attr(feature = "replay", derive(Deserialize))]
30#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)]
31pub struct GradientStopKey {
32 pub offset: f32,
33 pub color: ColorU,
34}
35
36impl GradientStopKey {
37 pub fn empty() -> Self {
38 GradientStopKey {
39 offset: 0.0,
40 color: ColorU::new(0, 0, 0, 0),
41 }
42 }
43}
44
45impl Into<GradientStopKey> for GradientStop {
46 fn into(self) -> GradientStopKey {
47 GradientStopKey {
48 offset: self.offset,
49 color: self.color.into(),
50 }
51 }
52}
53
54impl Eq for GradientStopKey {}
55
56impl hash::Hash for GradientStopKey {
57 fn hash<H: hash::Hasher>(&self, state: &mut H) {
58 self.offset.to_bits().hash(state);
59 self.color.hash(state);
60 }
61}
62
63#[cfg_attr(feature = "capture", derive(Serialize))]
65#[cfg_attr(feature = "replay", derive(Deserialize))]
66#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)]
67pub struct LinearGradientKey {
68 pub common: PrimKeyCommonData,
69 pub extend_mode: ExtendMode,
70 pub start_point: PointKey,
71 pub end_point: PointKey,
72 pub stretch_size: SizeKey,
73 pub tile_spacing: SizeKey,
74 pub stops: Vec<GradientStopKey>,
75 pub reverse_stops: bool,
76 pub nine_patch: Option<Box<NinePatchDescriptor>>,
77}
78
79impl LinearGradientKey {
80 pub fn new(
81 flags: PrimitiveFlags,
82 prim_size: LayoutSize,
83 linear_grad: LinearGradient,
84 ) -> Self {
85 LinearGradientKey {
86 common: PrimKeyCommonData {
87 flags,
88 prim_size: prim_size.into(),
89 },
90 extend_mode: linear_grad.extend_mode,
91 start_point: linear_grad.start_point,
92 end_point: linear_grad.end_point,
93 stretch_size: linear_grad.stretch_size,
94 tile_spacing: linear_grad.tile_spacing,
95 stops: linear_grad.stops,
96 reverse_stops: linear_grad.reverse_stops,
97 nine_patch: linear_grad.nine_patch,
98 }
99 }
100}
101
102impl InternDebug for LinearGradientKey {}
103
104#[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
105#[cfg_attr(feature = "capture", derive(Serialize))]
106#[cfg_attr(feature = "replay", derive(Deserialize))]
107pub struct GradientCacheKey {
108 pub orientation: LineOrientation,
109 pub start_stop_point: VectorKey,
110 pub stops: [GradientStopKey; GRADIENT_FP_STOPS],
111}
112
113#[cfg_attr(feature = "capture", derive(Serialize))]
114#[cfg_attr(feature = "replay", derive(Deserialize))]
115#[derive(MallocSizeOf)]
116pub struct LinearGradientTemplate {
117 pub common: PrimTemplateCommonData,
118 pub extend_mode: ExtendMode,
119 pub start_point: LayoutPoint,
120 pub end_point: LayoutPoint,
121 pub stretch_size: LayoutSize,
122 pub tile_spacing: LayoutSize,
123 pub stops_opacity: PrimitiveOpacity,
124 pub stops: Vec<GradientStop>,
125 pub brush_segments: Vec<BrushSegment>,
126 pub reverse_stops: bool,
127 pub stops_handle: GpuCacheHandle,
128 pub supports_caching: bool,
131}
132
133impl Deref for LinearGradientTemplate {
134 type Target = PrimTemplateCommonData;
135 fn deref(&self) -> &Self::Target {
136 &self.common
137 }
138}
139
140impl DerefMut for LinearGradientTemplate {
141 fn deref_mut(&mut self) -> &mut Self::Target {
142 &mut self.common
143 }
144}
145
146impl From<LinearGradientKey> for LinearGradientTemplate {
147 fn from(item: LinearGradientKey) -> Self {
148 let common = PrimTemplateCommonData::with_key_common(item.common);
149 let mut min_alpha: f32 = 1.0;
150
151 let mut supports_caching =
156 item.extend_mode == ExtendMode::Clamp &&
158 item.tile_spacing.w + item.stretch_size.w >= common.prim_size.width &&
160 item.tile_spacing.h + item.stretch_size.h >= common.prim_size.height &&
161 (item.start_point.x.approx_eq(&item.end_point.x) ||
163 item.start_point.y.approx_eq(&item.end_point.y)) &&
164 item.stops.len() <= GRADIENT_FP_STOPS &&
166 item.nine_patch.is_none();
168
169 let mut prev_offset = None;
170 let stops: Vec<GradientStop> = item.stops.iter().map(|stop| {
173 let color: ColorF = stop.color.into();
174 min_alpha = min_alpha.min(color.a);
175
176 if prev_offset == Some(stop.offset) {
181 supports_caching = false;
182 }
183
184 prev_offset = Some(stop.offset);
185
186 GradientStop {
187 offset: stop.offset,
188 color,
189 }
190 }).collect();
191
192 let mut brush_segments = Vec::new();
193
194 if let Some(ref nine_patch) = item.nine_patch {
195 brush_segments = nine_patch.create_segments(common.prim_size);
196 }
197
198 let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha);
202
203 LinearGradientTemplate {
204 common,
205 extend_mode: item.extend_mode,
206 start_point: item.start_point.into(),
207 end_point: item.end_point.into(),
208 stretch_size: item.stretch_size.into(),
209 tile_spacing: item.tile_spacing.into(),
210 stops_opacity,
211 stops,
212 brush_segments,
213 reverse_stops: item.reverse_stops,
214 stops_handle: GpuCacheHandle::new(),
215 supports_caching,
216 }
217 }
218}
219
220impl LinearGradientTemplate {
221 pub fn update(
226 &mut self,
227 frame_state: &mut FrameBuildingState,
228 ) {
229 if let Some(mut request) =
230 frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
231 request.push([
233 self.start_point.x,
234 self.start_point.y,
235 self.end_point.x,
236 self.end_point.y,
237 ]);
238 request.push([
239 pack_as_float(self.extend_mode as u32),
240 self.stretch_size.width,
241 self.stretch_size.height,
242 0.0,
243 ]);
244
245 for segment in &self.brush_segments {
247 request.write_segment(
249 segment.local_rect,
250 segment.extra_data,
251 );
252 }
253 }
254
255 if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) {
256 GradientGpuBlockBuilder::build(
257 self.reverse_stops,
258 &mut request,
259 &self.stops,
260 );
261 }
262
263 self.opacity = {
264 let stride = self.stretch_size + self.tile_spacing;
270 if stride.width >= self.common.prim_size.width &&
271 stride.height >= self.common.prim_size.height {
272 self.stops_opacity
273 } else {
274 PrimitiveOpacity::translucent()
275 }
276 }
277 }
278}
279
280pub type LinearGradientDataHandle = InternHandle<LinearGradient>;
281
282#[derive(Debug, MallocSizeOf)]
283#[cfg_attr(feature = "capture", derive(Serialize))]
284#[cfg_attr(feature = "replay", derive(Deserialize))]
285pub struct LinearGradient {
286 pub extend_mode: ExtendMode,
287 pub start_point: PointKey,
288 pub end_point: PointKey,
289 pub stretch_size: SizeKey,
290 pub tile_spacing: SizeKey,
291 pub stops: Vec<GradientStopKey>,
292 pub reverse_stops: bool,
293 pub nine_patch: Option<Box<NinePatchDescriptor>>,
294}
295
296impl Internable for LinearGradient {
297 type Key = LinearGradientKey;
298 type StoreData = LinearGradientTemplate;
299 type InternData = PrimitiveSceneData;
300}
301
302impl InternablePrimitive for LinearGradient {
303 fn into_key(
304 self,
305 info: &LayoutPrimitiveInfo,
306 ) -> LinearGradientKey {
307 LinearGradientKey::new(
308 info.flags,
309 info.rect.size,
310 self
311 )
312 }
313
314 fn make_instance_kind(
315 _key: LinearGradientKey,
316 data_handle: LinearGradientDataHandle,
317 prim_store: &mut PrimitiveStore,
318 _reference_frame_relative_offset: LayoutVector2D,
319 ) -> PrimitiveInstanceKind {
320 let gradient_index = prim_store.linear_gradients.push(LinearGradientPrimitive {
321 cache_handle: None,
322 visible_tiles_range: GradientTileRange::empty(),
323 });
324
325 PrimitiveInstanceKind::LinearGradient {
326 data_handle,
327 gradient_index,
328 }
329 }
330}
331
332impl IsVisible for LinearGradient {
333 fn is_visible(&self) -> bool {
334 true
335 }
336}
337
338#[derive(Debug)]
339#[cfg_attr(feature = "capture", derive(Serialize))]
340pub struct LinearGradientPrimitive {
341 pub cache_handle: Option<RenderTaskCacheEntryHandle>,
342 pub visible_tiles_range: GradientTileRange,
343}
344
345#[cfg_attr(feature = "capture", derive(Serialize))]
349#[cfg_attr(feature = "replay", derive(Deserialize))]
350#[derive(Debug, Clone, MallocSizeOf, PartialEq)]
351pub struct RadialGradientParams {
352 pub start_radius: f32,
353 pub end_radius: f32,
354 pub ratio_xy: f32,
355}
356
357impl Eq for RadialGradientParams {}
358
359impl hash::Hash for RadialGradientParams {
360 fn hash<H: hash::Hasher>(&self, state: &mut H) {
361 self.start_radius.to_bits().hash(state);
362 self.end_radius.to_bits().hash(state);
363 self.ratio_xy.to_bits().hash(state);
364 }
365}
366
367#[cfg_attr(feature = "capture", derive(Serialize))]
369#[cfg_attr(feature = "replay", derive(Deserialize))]
370#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)]
371pub struct RadialGradientKey {
372 pub common: PrimKeyCommonData,
373 pub extend_mode: ExtendMode,
374 pub center: PointKey,
375 pub params: RadialGradientParams,
376 pub stretch_size: SizeKey,
377 pub stops: Vec<GradientStopKey>,
378 pub tile_spacing: SizeKey,
379 pub nine_patch: Option<Box<NinePatchDescriptor>>,
380}
381
382impl RadialGradientKey {
383 pub fn new(
384 flags: PrimitiveFlags,
385 prim_size: LayoutSize,
386 radial_grad: RadialGradient,
387 ) -> Self {
388 RadialGradientKey {
389 common: PrimKeyCommonData {
390 flags,
391 prim_size: prim_size.into(),
392 },
393 extend_mode: radial_grad.extend_mode,
394 center: radial_grad.center,
395 params: radial_grad.params,
396 stretch_size: radial_grad.stretch_size,
397 stops: radial_grad.stops,
398 tile_spacing: radial_grad.tile_spacing,
399 nine_patch: radial_grad.nine_patch,
400 }
401 }
402}
403
404impl InternDebug for RadialGradientKey {}
405
406#[cfg_attr(feature = "capture", derive(Serialize))]
407#[cfg_attr(feature = "replay", derive(Deserialize))]
408#[derive(MallocSizeOf)]
409pub struct RadialGradientTemplate {
410 pub common: PrimTemplateCommonData,
411 pub extend_mode: ExtendMode,
412 pub center: LayoutPoint,
413 pub params: RadialGradientParams,
414 pub stretch_size: LayoutSize,
415 pub tile_spacing: LayoutSize,
416 pub brush_segments: Vec<BrushSegment>,
417 pub stops: Vec<GradientStop>,
418 pub stops_handle: GpuCacheHandle,
419}
420
421impl Deref for RadialGradientTemplate {
422 type Target = PrimTemplateCommonData;
423 fn deref(&self) -> &Self::Target {
424 &self.common
425 }
426}
427
428impl DerefMut for RadialGradientTemplate {
429 fn deref_mut(&mut self) -> &mut Self::Target {
430 &mut self.common
431 }
432}
433
434impl From<RadialGradientKey> for RadialGradientTemplate {
435 fn from(item: RadialGradientKey) -> Self {
436 let common = PrimTemplateCommonData::with_key_common(item.common);
437 let mut brush_segments = Vec::new();
438
439 if let Some(ref nine_patch) = item.nine_patch {
440 brush_segments = nine_patch.create_segments(common.prim_size);
441 }
442
443 let stops = item.stops.iter().map(|stop| {
444 GradientStop {
445 offset: stop.offset,
446 color: stop.color.into(),
447 }
448 }).collect();
449
450 RadialGradientTemplate {
451 common,
452 center: item.center.into(),
453 extend_mode: item.extend_mode,
454 params: item.params,
455 stretch_size: item.stretch_size.into(),
456 tile_spacing: item.tile_spacing.into(),
457 brush_segments,
458 stops,
459 stops_handle: GpuCacheHandle::new(),
460 }
461 }
462}
463
464impl RadialGradientTemplate {
465 pub fn update(
470 &mut self,
471 frame_state: &mut FrameBuildingState,
472 ) {
473 if let Some(mut request) =
474 frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
475 request.push([
477 self.center.x,
478 self.center.y,
479 self.params.start_radius,
480 self.params.end_radius,
481 ]);
482 request.push([
483 self.params.ratio_xy,
484 pack_as_float(self.extend_mode as u32),
485 self.stretch_size.width,
486 self.stretch_size.height,
487 ]);
488
489 for segment in &self.brush_segments {
491 request.write_segment(
493 segment.local_rect,
494 segment.extra_data,
495 );
496 }
497 }
498
499 if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) {
500 GradientGpuBlockBuilder::build(
501 false,
502 &mut request,
503 &self.stops,
504 );
505 }
506
507 self.opacity = PrimitiveOpacity::translucent();
508 }
509}
510
511pub type RadialGradientDataHandle = InternHandle<RadialGradient>;
512
513#[derive(Debug, MallocSizeOf)]
514#[cfg_attr(feature = "capture", derive(Serialize))]
515#[cfg_attr(feature = "replay", derive(Deserialize))]
516pub struct RadialGradient {
517 pub extend_mode: ExtendMode,
518 pub center: PointKey,
519 pub params: RadialGradientParams,
520 pub stretch_size: SizeKey,
521 pub stops: Vec<GradientStopKey>,
522 pub tile_spacing: SizeKey,
523 pub nine_patch: Option<Box<NinePatchDescriptor>>,
524}
525
526impl Internable for RadialGradient {
527 type Key = RadialGradientKey;
528 type StoreData = RadialGradientTemplate;
529 type InternData = PrimitiveSceneData;
530}
531
532impl InternablePrimitive for RadialGradient {
533 fn into_key(
534 self,
535 info: &LayoutPrimitiveInfo,
536 ) -> RadialGradientKey {
537 RadialGradientKey::new(
538 info.flags,
539 info.rect.size,
540 self,
541 )
542 }
543
544 fn make_instance_kind(
545 _key: RadialGradientKey,
546 data_handle: RadialGradientDataHandle,
547 _prim_store: &mut PrimitiveStore,
548 _reference_frame_relative_offset: LayoutVector2D,
549 ) -> PrimitiveInstanceKind {
550 PrimitiveInstanceKind::RadialGradient {
551 data_handle,
552 visible_tiles_range: GradientTileRange::empty(),
553 }
554 }
555}
556
557impl IsVisible for RadialGradient {
558 fn is_visible(&self) -> bool {
559 true
560 }
561}
562
563pub const GRADIENT_DATA_FIRST_STOP: usize = 0;
567pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1;
569
570pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1;
572pub const GRADIENT_DATA_TABLE_END: usize = GRADIENT_DATA_LAST_STOP;
574pub const GRADIENT_DATA_TABLE_SIZE: usize = 128;
576
577pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2;
579
580#[derive(Debug, Copy, Clone)]
583#[repr(C)]
584struct GradientDataEntry {
585 start_color: PremultipliedColorF,
586 end_color: PremultipliedColorF,
587}
588
589impl GradientDataEntry {
590 fn white() -> Self {
591 Self {
592 start_color: PremultipliedColorF::WHITE,
593 end_color: PremultipliedColorF::WHITE,
594 }
595 }
596}
597
598struct GradientGpuBlockBuilder {}
600
601impl GradientGpuBlockBuilder {
602 fn fill_colors(
605 start_idx: usize,
606 end_idx: usize,
607 start_color: &PremultipliedColorF,
608 end_color: &PremultipliedColorF,
609 entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE],
610 ) {
611 let inv_steps = 1.0 / (end_idx - start_idx) as f32;
613 let step_r = (end_color.r - start_color.r) * inv_steps;
614 let step_g = (end_color.g - start_color.g) * inv_steps;
615 let step_b = (end_color.b - start_color.b) * inv_steps;
616 let step_a = (end_color.a - start_color.a) * inv_steps;
617
618 let mut cur_color = *start_color;
619
620 for index in start_idx .. end_idx {
622 let entry = &mut entries[index];
623 entry.start_color = cur_color;
624 cur_color.r += step_r;
625 cur_color.g += step_g;
626 cur_color.b += step_b;
627 cur_color.a += step_a;
628 entry.end_color = cur_color;
629 }
630 }
631
632 #[inline]
635 fn get_index(offset: f32) -> usize {
636 (offset.max(0.0).min(1.0) * GRADIENT_DATA_TABLE_SIZE as f32 +
637 GRADIENT_DATA_TABLE_BEGIN as f32)
638 .round() as usize
639 }
640
641 fn build(
643 reverse_stops: bool,
644 request: &mut GpuDataRequest,
645 src_stops: &[GradientStop],
646 ) {
647 let mut src_stops = src_stops.into_iter();
652 let mut cur_color = match src_stops.next() {
653 Some(stop) => {
654 debug_assert_eq!(stop.offset, 0.0);
655 stop.color.premultiplied()
656 }
657 None => {
658 error!("Zero gradient stops found!");
659 PremultipliedColorF::BLACK
660 }
661 };
662
663 let mut entries = [GradientDataEntry::white(); GRADIENT_DATA_SIZE];
673
674 if reverse_stops {
675 GradientGpuBlockBuilder::fill_colors(
677 GRADIENT_DATA_LAST_STOP,
678 GRADIENT_DATA_LAST_STOP + 1,
679 &cur_color,
680 &cur_color,
681 &mut entries,
682 );
683
684 let mut cur_idx = GRADIENT_DATA_TABLE_END;
688 for next in src_stops {
689 let next_color = next.color.premultiplied();
690 let next_idx = Self::get_index(1.0 - next.offset);
691
692 if next_idx < cur_idx {
693 GradientGpuBlockBuilder::fill_colors(
694 next_idx,
695 cur_idx,
696 &next_color,
697 &cur_color,
698 &mut entries,
699 );
700 cur_idx = next_idx;
701 }
702
703 cur_color = next_color;
704 }
705 if cur_idx != GRADIENT_DATA_TABLE_BEGIN {
706 error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx);
707 }
708
709 GradientGpuBlockBuilder::fill_colors(
711 GRADIENT_DATA_FIRST_STOP,
712 GRADIENT_DATA_FIRST_STOP + 1,
713 &cur_color,
714 &cur_color,
715 &mut entries,
716 );
717 } else {
718 GradientGpuBlockBuilder::fill_colors(
720 GRADIENT_DATA_FIRST_STOP,
721 GRADIENT_DATA_FIRST_STOP + 1,
722 &cur_color,
723 &cur_color,
724 &mut entries,
725 );
726
727 let mut cur_idx = GRADIENT_DATA_TABLE_BEGIN;
731 for next in src_stops {
732 let next_color = next.color.premultiplied();
733 let next_idx = Self::get_index(next.offset);
734
735 if next_idx > cur_idx {
736 GradientGpuBlockBuilder::fill_colors(
737 cur_idx,
738 next_idx,
739 &cur_color,
740 &next_color,
741 &mut entries,
742 );
743 cur_idx = next_idx;
744 }
745
746 cur_color = next_color;
747 }
748 if cur_idx != GRADIENT_DATA_TABLE_END {
749 error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx);
750 }
751
752 GradientGpuBlockBuilder::fill_colors(
754 GRADIENT_DATA_LAST_STOP,
755 GRADIENT_DATA_LAST_STOP + 1,
756 &cur_color,
757 &cur_color,
758 &mut entries,
759 );
760 }
761
762 for entry in entries.iter() {
763 request.push(entry.start_color);
764 request.push(entry.end_color);
765 }
766 }
767}
768
769#[test]
770#[cfg(target_pointer_width = "64")]
771fn test_struct_sizes() {
772 use std::mem;
773 assert_eq!(mem::size_of::<LinearGradient>(), 72, "LinearGradient size changed");
780 assert_eq!(mem::size_of::<LinearGradientTemplate>(), 112, "LinearGradientTemplate size changed");
781 assert_eq!(mem::size_of::<LinearGradientKey>(), 80, "LinearGradientKey size changed");
782
783 assert_eq!(mem::size_of::<RadialGradient>(), 72, "RadialGradient size changed");
784 assert_eq!(mem::size_of::<RadialGradientTemplate>(), 120, "RadialGradientTemplate size changed");
785 assert_eq!(mem::size_of::<RadialGradientKey>(), 88, "RadialGradientKey size changed");
786}