1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 use api::{
6 ColorF, ColorU,ExtendMode, GradientStop, LayoutPoint, LayoutSize,
7 LayoutPrimitiveInfo, PremultipliedColorF, LayoutVector2D,
8 };
9 use display_list_flattener::{AsInstanceKind, IsVisible};
10 use frame_builder::FrameBuildingState;
11 use gpu_cache::{GpuCacheHandle, GpuDataRequest};
12 use intern::{Internable, InternDebug};
13 use intern_types;
14 use prim_store::{BrushSegment, GradientTileRange};
15 use prim_store::{PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData};
16 use prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore};
17 use prim_store::{NinePatchDescriptor, PointKey, SizeKey};
18 use std::{hash, ops::{Deref, DerefMut}, mem};
19 use util::pack_as_float;
20
21 /// A hashable gradient stop that can be used in primitive keys.
22 #[cfg_attr(feature = "capture", derive(Serialize))]
23 #[cfg_attr(feature = "replay", derive(Deserialize))]
24 #[derive(Debug, Clone, MallocSizeOf, PartialEq)]
25 pub struct GradientStopKey {
26 pub offset: f32,
27 pub color: ColorU,
28 }
29
30 impl Eq for GradientStopKey {}
31
32 impl hash::Hash for GradientStopKey {
hash<H: hash::Hasher>(&self, state: &mut H)33 fn hash<H: hash::Hasher>(&self, state: &mut H) {
34 self.offset.to_bits().hash(state);
35 self.color.hash(state);
36 }
37 }
38
39 /// Identifying key for a line decoration.
40 #[cfg_attr(feature = "capture", derive(Serialize))]
41 #[cfg_attr(feature = "replay", derive(Deserialize))]
42 #[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)]
43 pub struct LinearGradientKey {
44 pub common: PrimKeyCommonData,
45 pub extend_mode: ExtendMode,
46 pub start_point: PointKey,
47 pub end_point: PointKey,
48 pub stretch_size: SizeKey,
49 pub tile_spacing: SizeKey,
50 pub stops: Vec<GradientStopKey>,
51 pub reverse_stops: bool,
52 pub nine_patch: Option<Box<NinePatchDescriptor>>,
53 }
54
55 impl LinearGradientKey {
new( is_backface_visible: bool, prim_size: LayoutSize, linear_grad: LinearGradient, ) -> Self56 pub fn new(
57 is_backface_visible: bool,
58 prim_size: LayoutSize,
59 linear_grad: LinearGradient,
60 ) -> Self {
61 LinearGradientKey {
62 common: PrimKeyCommonData {
63 is_backface_visible,
64 prim_size: prim_size.into(),
65 },
66 extend_mode: linear_grad.extend_mode,
67 start_point: linear_grad.start_point,
68 end_point: linear_grad.end_point,
69 stretch_size: linear_grad.stretch_size,
70 tile_spacing: linear_grad.tile_spacing,
71 stops: linear_grad.stops,
72 reverse_stops: linear_grad.reverse_stops,
73 nine_patch: linear_grad.nine_patch,
74 }
75 }
76 }
77
78 impl InternDebug for LinearGradientKey {}
79
80 impl AsInstanceKind<LinearGradientDataHandle> for LinearGradientKey {
81 /// Construct a primitive instance that matches the type
82 /// of primitive key.
as_instance_kind( &self, data_handle: LinearGradientDataHandle, _prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind83 fn as_instance_kind(
84 &self,
85 data_handle: LinearGradientDataHandle,
86 _prim_store: &mut PrimitiveStore,
87 _reference_frame_relative_offset: LayoutVector2D,
88 ) -> PrimitiveInstanceKind {
89 PrimitiveInstanceKind::LinearGradient {
90 data_handle,
91 visible_tiles_range: GradientTileRange::empty(),
92 }
93 }
94 }
95
96 #[cfg_attr(feature = "capture", derive(Serialize))]
97 #[cfg_attr(feature = "replay", derive(Deserialize))]
98 #[derive(MallocSizeOf)]
99 pub struct LinearGradientTemplate {
100 pub common: PrimTemplateCommonData,
101 pub extend_mode: ExtendMode,
102 pub start_point: LayoutPoint,
103 pub end_point: LayoutPoint,
104 pub stretch_size: LayoutSize,
105 pub tile_spacing: LayoutSize,
106 pub stops_opacity: PrimitiveOpacity,
107 pub stops: Vec<GradientStop>,
108 pub brush_segments: Vec<BrushSegment>,
109 pub reverse_stops: bool,
110 pub stops_handle: GpuCacheHandle,
111 }
112
113 impl Deref for LinearGradientTemplate {
114 type Target = PrimTemplateCommonData;
deref(&self) -> &Self::Target115 fn deref(&self) -> &Self::Target {
116 &self.common
117 }
118 }
119
120 impl DerefMut for LinearGradientTemplate {
deref_mut(&mut self) -> &mut Self::Target121 fn deref_mut(&mut self) -> &mut Self::Target {
122 &mut self.common
123 }
124 }
125
126 impl From<LinearGradientKey> for LinearGradientTemplate {
from(item: LinearGradientKey) -> Self127 fn from(item: LinearGradientKey) -> Self {
128 let common = PrimTemplateCommonData::with_key_common(item.common);
129 let mut min_alpha: f32 = 1.0;
130
131 // Convert the stops to more convenient representation
132 // for the current gradient builder.
133 let stops = item.stops.iter().map(|stop| {
134 let color: ColorF = stop.color.into();
135 min_alpha = min_alpha.min(color.a);
136
137 GradientStop {
138 offset: stop.offset,
139 color,
140 }
141 }).collect();
142
143 let mut brush_segments = Vec::new();
144
145 if let Some(ref nine_patch) = item.nine_patch {
146 brush_segments = nine_patch.create_segments(common.prim_size);
147 }
148
149 // Save opacity of the stops for use in
150 // selecting which pass this gradient
151 // should be drawn in.
152 let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha);
153
154 LinearGradientTemplate {
155 common,
156 extend_mode: item.extend_mode,
157 start_point: item.start_point.into(),
158 end_point: item.end_point.into(),
159 stretch_size: item.stretch_size.into(),
160 tile_spacing: item.tile_spacing.into(),
161 stops_opacity,
162 stops,
163 brush_segments,
164 reverse_stops: item.reverse_stops,
165 stops_handle: GpuCacheHandle::new(),
166 }
167 }
168 }
169
170 impl LinearGradientTemplate {
171 /// Update the GPU cache for a given primitive template. This may be called multiple
172 /// times per frame, by each primitive reference that refers to this interned
173 /// template. The initial request call to the GPU cache ensures that work is only
174 /// done if the cache entry is invalid (due to first use or eviction).
update( &mut self, frame_state: &mut FrameBuildingState, )175 pub fn update(
176 &mut self,
177 frame_state: &mut FrameBuildingState,
178 ) {
179 if let Some(mut request) =
180 frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
181 // write_prim_gpu_blocks
182 request.push([
183 self.start_point.x,
184 self.start_point.y,
185 self.end_point.x,
186 self.end_point.y,
187 ]);
188 request.push([
189 pack_as_float(self.extend_mode as u32),
190 self.stretch_size.width,
191 self.stretch_size.height,
192 0.0,
193 ]);
194
195 // write_segment_gpu_blocks
196 for segment in &self.brush_segments {
197 // has to match VECS_PER_SEGMENT
198 request.write_segment(
199 segment.local_rect,
200 segment.extra_data,
201 );
202 }
203 }
204
205 if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) {
206 GradientGpuBlockBuilder::build(
207 self.reverse_stops,
208 &mut request,
209 &self.stops,
210 );
211 }
212
213 self.opacity = {
214 // If the coverage of the gradient extends to or beyond
215 // the primitive rect, then the opacity can be determined
216 // by the colors of the stops. If we have tiling / spacing
217 // then we just assume the gradient is translucent for now.
218 // (In the future we could consider segmenting in some cases).
219 let stride = self.stretch_size + self.tile_spacing;
220 if stride.width >= self.common.prim_size.width &&
221 stride.height >= self.common.prim_size.height {
222 self.stops_opacity
223 } else {
224 PrimitiveOpacity::translucent()
225 }
226 }
227 }
228 }
229
230 pub type LinearGradientDataHandle = intern_types::linear_grad::Handle;
231
232 pub struct LinearGradient {
233 pub extend_mode: ExtendMode,
234 pub start_point: PointKey,
235 pub end_point: PointKey,
236 pub stretch_size: SizeKey,
237 pub tile_spacing: SizeKey,
238 pub stops: Vec<GradientStopKey>,
239 pub reverse_stops: bool,
240 pub nine_patch: Option<Box<NinePatchDescriptor>>,
241 }
242
243 impl Internable for LinearGradient {
244 type Marker = intern_types::linear_grad::Marker;
245 type Source = LinearGradientKey;
246 type StoreData = LinearGradientTemplate;
247 type InternData = PrimitiveSceneData;
248
249 /// Build a new key from self with `info`.
build_key( self, info: &LayoutPrimitiveInfo, ) -> LinearGradientKey250 fn build_key(
251 self,
252 info: &LayoutPrimitiveInfo,
253 ) -> LinearGradientKey {
254 LinearGradientKey::new(
255 info.is_backface_visible,
256 info.rect.size,
257 self
258 )
259 }
260 }
261
262 impl IsVisible for LinearGradient {
is_visible(&self) -> bool263 fn is_visible(&self) -> bool {
264 true
265 }
266 }
267
268 ////////////////////////////////////////////////////////////////////////////////
269
270 /// Hashable radial gradient parameters, for use during prim interning.
271 #[cfg_attr(feature = "capture", derive(Serialize))]
272 #[cfg_attr(feature = "replay", derive(Deserialize))]
273 #[derive(Debug, Clone, MallocSizeOf, PartialEq)]
274 pub struct RadialGradientParams {
275 pub start_radius: f32,
276 pub end_radius: f32,
277 pub ratio_xy: f32,
278 }
279
280 impl Eq for RadialGradientParams {}
281
282 impl hash::Hash for RadialGradientParams {
hash<H: hash::Hasher>(&self, state: &mut H)283 fn hash<H: hash::Hasher>(&self, state: &mut H) {
284 self.start_radius.to_bits().hash(state);
285 self.end_radius.to_bits().hash(state);
286 self.ratio_xy.to_bits().hash(state);
287 }
288 }
289
290 /// Identifying key for a line decoration.
291 #[cfg_attr(feature = "capture", derive(Serialize))]
292 #[cfg_attr(feature = "replay", derive(Deserialize))]
293 #[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)]
294 pub struct RadialGradientKey {
295 pub common: PrimKeyCommonData,
296 pub extend_mode: ExtendMode,
297 pub center: PointKey,
298 pub params: RadialGradientParams,
299 pub stretch_size: SizeKey,
300 pub stops: Vec<GradientStopKey>,
301 pub tile_spacing: SizeKey,
302 pub nine_patch: Option<Box<NinePatchDescriptor>>,
303 }
304
305 impl RadialGradientKey {
new( is_backface_visible: bool, prim_size: LayoutSize, radial_grad: RadialGradient, ) -> Self306 pub fn new(
307 is_backface_visible: bool,
308 prim_size: LayoutSize,
309 radial_grad: RadialGradient,
310 ) -> Self {
311 RadialGradientKey {
312 common: PrimKeyCommonData {
313 is_backface_visible,
314 prim_size: prim_size.into(),
315 },
316 extend_mode: radial_grad.extend_mode,
317 center: radial_grad.center,
318 params: radial_grad.params,
319 stretch_size: radial_grad.stretch_size,
320 stops: radial_grad.stops,
321 tile_spacing: radial_grad.tile_spacing,
322 nine_patch: radial_grad.nine_patch,
323 }
324 }
325 }
326
327 impl InternDebug for RadialGradientKey {}
328
329 impl AsInstanceKind<RadialGradientDataHandle> for RadialGradientKey {
330 /// Construct a primitive instance that matches the type
331 /// of primitive key.
as_instance_kind( &self, data_handle: RadialGradientDataHandle, _prim_store: &mut PrimitiveStore, _reference_frame_relative_offset: LayoutVector2D, ) -> PrimitiveInstanceKind332 fn as_instance_kind(
333 &self,
334 data_handle: RadialGradientDataHandle,
335 _prim_store: &mut PrimitiveStore,
336 _reference_frame_relative_offset: LayoutVector2D,
337 ) -> PrimitiveInstanceKind {
338 PrimitiveInstanceKind::RadialGradient {
339 data_handle,
340 visible_tiles_range: GradientTileRange::empty(),
341 }
342 }
343 }
344
345 #[cfg_attr(feature = "capture", derive(Serialize))]
346 #[cfg_attr(feature = "replay", derive(Deserialize))]
347 #[derive(MallocSizeOf)]
348 pub struct RadialGradientTemplate {
349 pub common: PrimTemplateCommonData,
350 pub extend_mode: ExtendMode,
351 pub center: LayoutPoint,
352 pub params: RadialGradientParams,
353 pub stretch_size: LayoutSize,
354 pub tile_spacing: LayoutSize,
355 pub brush_segments: Vec<BrushSegment>,
356 pub stops: Vec<GradientStop>,
357 pub stops_handle: GpuCacheHandle,
358 }
359
360 impl Deref for RadialGradientTemplate {
361 type Target = PrimTemplateCommonData;
deref(&self) -> &Self::Target362 fn deref(&self) -> &Self::Target {
363 &self.common
364 }
365 }
366
367 impl DerefMut for RadialGradientTemplate {
deref_mut(&mut self) -> &mut Self::Target368 fn deref_mut(&mut self) -> &mut Self::Target {
369 &mut self.common
370 }
371 }
372
373 impl From<RadialGradientKey> for RadialGradientTemplate {
from(item: RadialGradientKey) -> Self374 fn from(item: RadialGradientKey) -> Self {
375 let common = PrimTemplateCommonData::with_key_common(item.common);
376 let mut brush_segments = Vec::new();
377
378 if let Some(ref nine_patch) = item.nine_patch {
379 brush_segments = nine_patch.create_segments(common.prim_size);
380 }
381
382 let stops = item.stops.iter().map(|stop| {
383 GradientStop {
384 offset: stop.offset,
385 color: stop.color.into(),
386 }
387 }).collect();
388
389 RadialGradientTemplate {
390 common,
391 center: item.center.into(),
392 extend_mode: item.extend_mode,
393 params: item.params,
394 stretch_size: item.stretch_size.into(),
395 tile_spacing: item.tile_spacing.into(),
396 brush_segments: brush_segments,
397 stops,
398 stops_handle: GpuCacheHandle::new(),
399 }
400 }
401 }
402
403 impl RadialGradientTemplate {
404 /// Update the GPU cache for a given primitive template. This may be called multiple
405 /// times per frame, by each primitive reference that refers to this interned
406 /// template. The initial request call to the GPU cache ensures that work is only
407 /// done if the cache entry is invalid (due to first use or eviction).
update( &mut self, frame_state: &mut FrameBuildingState, )408 pub fn update(
409 &mut self,
410 frame_state: &mut FrameBuildingState,
411 ) {
412 if let Some(mut request) =
413 frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
414 // write_prim_gpu_blocks
415 request.push([
416 self.center.x,
417 self.center.y,
418 self.params.start_radius,
419 self.params.end_radius,
420 ]);
421 request.push([
422 self.params.ratio_xy,
423 pack_as_float(self.extend_mode as u32),
424 self.stretch_size.width,
425 self.stretch_size.height,
426 ]);
427
428 // write_segment_gpu_blocks
429 for segment in &self.brush_segments {
430 // has to match VECS_PER_SEGMENT
431 request.write_segment(
432 segment.local_rect,
433 segment.extra_data,
434 );
435 }
436 }
437
438 if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) {
439 GradientGpuBlockBuilder::build(
440 false,
441 &mut request,
442 &self.stops,
443 );
444 }
445
446 self.opacity = PrimitiveOpacity::translucent();
447 }
448 }
449
450 pub type RadialGradientDataHandle = intern_types::radial_grad::Handle;
451
452 pub struct RadialGradient {
453 pub extend_mode: ExtendMode,
454 pub center: PointKey,
455 pub params: RadialGradientParams,
456 pub stretch_size: SizeKey,
457 pub stops: Vec<GradientStopKey>,
458 pub tile_spacing: SizeKey,
459 pub nine_patch: Option<Box<NinePatchDescriptor>>,
460 }
461
462 impl Internable for RadialGradient {
463 type Marker = intern_types::radial_grad::Marker;
464 type Source = RadialGradientKey;
465 type StoreData = RadialGradientTemplate;
466 type InternData = PrimitiveSceneData;
467
468 /// Build a new key from self with `info`.
build_key( self, info: &LayoutPrimitiveInfo, ) -> RadialGradientKey469 fn build_key(
470 self,
471 info: &LayoutPrimitiveInfo,
472 ) -> RadialGradientKey {
473 RadialGradientKey::new(
474 info.is_backface_visible,
475 info.rect.size,
476 self,
477 )
478 }
479 }
480
481 impl IsVisible for RadialGradient {
is_visible(&self) -> bool482 fn is_visible(&self) -> bool {
483 true
484 }
485 }
486
487 ////////////////////////////////////////////////////////////////////////////////
488
489 // The gradient entry index for the first color stop
490 pub const GRADIENT_DATA_FIRST_STOP: usize = 0;
491 // The gradient entry index for the last color stop
492 pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1;
493
494 // The start of the gradient data table
495 pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1;
496 // The exclusive bound of the gradient data table
497 pub const GRADIENT_DATA_TABLE_END: usize = GRADIENT_DATA_LAST_STOP;
498 // The number of entries in the gradient data table.
499 pub const GRADIENT_DATA_TABLE_SIZE: usize = 128;
500
501 // The number of entries in a gradient data: GRADIENT_DATA_TABLE_SIZE + first stop entry + last stop entry
502 pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2;
503
504 #[derive(Debug)]
505 #[repr(C)]
506 // An entry in a gradient data table representing a segment of the gradient color space.
507 pub struct GradientDataEntry {
508 pub start_color: PremultipliedColorF,
509 pub end_color: PremultipliedColorF,
510 }
511
512 // TODO(gw): Tidy this up to be a free function / module?
513 struct GradientGpuBlockBuilder {}
514
515 impl GradientGpuBlockBuilder {
516 /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating
517 /// from start_color to end_color.
fill_colors( start_idx: usize, end_idx: usize, start_color: &PremultipliedColorF, end_color: &PremultipliedColorF, entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE], )518 fn fill_colors(
519 start_idx: usize,
520 end_idx: usize,
521 start_color: &PremultipliedColorF,
522 end_color: &PremultipliedColorF,
523 entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE],
524 ) {
525 // Calculate the color difference for individual steps in the ramp.
526 let inv_steps = 1.0 / (end_idx - start_idx) as f32;
527 let step_r = (end_color.r - start_color.r) * inv_steps;
528 let step_g = (end_color.g - start_color.g) * inv_steps;
529 let step_b = (end_color.b - start_color.b) * inv_steps;
530 let step_a = (end_color.a - start_color.a) * inv_steps;
531
532 let mut cur_color = *start_color;
533
534 // Walk the ramp writing start and end colors for each entry.
535 for index in start_idx .. end_idx {
536 let entry = &mut entries[index];
537 entry.start_color = cur_color;
538 cur_color.r += step_r;
539 cur_color.g += step_g;
540 cur_color.b += step_b;
541 cur_color.a += step_a;
542 entry.end_color = cur_color;
543 }
544 }
545
546 /// Compute an index into the gradient entry table based on a gradient stop offset. This
547 /// function maps offsets from [0, 1] to indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END].
548 #[inline]
get_index(offset: f32) -> usize549 fn get_index(offset: f32) -> usize {
550 (offset.max(0.0).min(1.0) * GRADIENT_DATA_TABLE_SIZE as f32 +
551 GRADIENT_DATA_TABLE_BEGIN as f32)
552 .round() as usize
553 }
554
555 // Build the gradient data from the supplied stops, reversing them if necessary.
build( reverse_stops: bool, request: &mut GpuDataRequest, src_stops: &[GradientStop], )556 fn build(
557 reverse_stops: bool,
558 request: &mut GpuDataRequest,
559 src_stops: &[GradientStop],
560 ) {
561 // Preconditions (should be ensured by DisplayListBuilder):
562 // * we have at least two stops
563 // * first stop has offset 0.0
564 // * last stop has offset 1.0
565 let mut src_stops = src_stops.into_iter();
566 let mut cur_color = match src_stops.next() {
567 Some(stop) => {
568 debug_assert_eq!(stop.offset, 0.0);
569 stop.color.premultiplied()
570 }
571 None => {
572 error!("Zero gradient stops found!");
573 PremultipliedColorF::BLACK
574 }
575 };
576
577 // A table of gradient entries, with two colors per entry, that specify the start and end color
578 // within the segment of the gradient space represented by that entry. To lookup a gradient result,
579 // first the entry index is calculated to determine which two colors to interpolate between, then
580 // the offset within that entry bucket is used to interpolate between the two colors in that entry.
581 // This layout preserves hard stops, as the end color for a given entry can differ from the start
582 // color for the following entry, despite them being adjacent. Colors are stored within in BGRA8
583 // format for texture upload. This table requires the gradient color stops to be normalized to the
584 // range [0, 1]. The first and last entries hold the first and last color stop colors respectively,
585 // while the entries in between hold the interpolated color stop values for the range [0, 1].
586 let mut entries: [GradientDataEntry; GRADIENT_DATA_SIZE] = unsafe { mem::uninitialized() };
587
588 if reverse_stops {
589 // Fill in the first entry (for reversed stops) with the first color stop
590 GradientGpuBlockBuilder::fill_colors(
591 GRADIENT_DATA_LAST_STOP,
592 GRADIENT_DATA_LAST_STOP + 1,
593 &cur_color,
594 &cur_color,
595 &mut entries,
596 );
597
598 // Fill in the center of the gradient table, generating a color ramp between each consecutive pair
599 // of gradient stops. Each iteration of a loop will fill the indices in [next_idx, cur_idx). The
600 // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END).
601 let mut cur_idx = GRADIENT_DATA_TABLE_END;
602 for next in src_stops {
603 let next_color = next.color.premultiplied();
604 let next_idx = Self::get_index(1.0 - next.offset);
605
606 if next_idx < cur_idx {
607 GradientGpuBlockBuilder::fill_colors(
608 next_idx,
609 cur_idx,
610 &next_color,
611 &cur_color,
612 &mut entries,
613 );
614 cur_idx = next_idx;
615 }
616
617 cur_color = next_color;
618 }
619 if cur_idx != GRADIENT_DATA_TABLE_BEGIN {
620 error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx);
621 GradientGpuBlockBuilder::fill_colors(
622 GRADIENT_DATA_TABLE_BEGIN,
623 cur_idx,
624 &PremultipliedColorF::WHITE,
625 &cur_color,
626 &mut entries,
627 );
628 }
629
630 // Fill in the last entry (for reversed stops) with the last color stop
631 GradientGpuBlockBuilder::fill_colors(
632 GRADIENT_DATA_FIRST_STOP,
633 GRADIENT_DATA_FIRST_STOP + 1,
634 &cur_color,
635 &cur_color,
636 &mut entries,
637 );
638 } else {
639 // Fill in the first entry with the first color stop
640 GradientGpuBlockBuilder::fill_colors(
641 GRADIENT_DATA_FIRST_STOP,
642 GRADIENT_DATA_FIRST_STOP + 1,
643 &cur_color,
644 &cur_color,
645 &mut entries,
646 );
647
648 // Fill in the center of the gradient table, generating a color ramp between each consecutive pair
649 // of gradient stops. Each iteration of a loop will fill the indices in [cur_idx, next_idx). The
650 // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END).
651 let mut cur_idx = GRADIENT_DATA_TABLE_BEGIN;
652 for next in src_stops {
653 let next_color = next.color.premultiplied();
654 let next_idx = Self::get_index(next.offset);
655
656 if next_idx > cur_idx {
657 GradientGpuBlockBuilder::fill_colors(
658 cur_idx,
659 next_idx,
660 &cur_color,
661 &next_color,
662 &mut entries,
663 );
664 cur_idx = next_idx;
665 }
666
667 cur_color = next_color;
668 }
669 if cur_idx != GRADIENT_DATA_TABLE_END {
670 error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx);
671 GradientGpuBlockBuilder::fill_colors(
672 cur_idx,
673 GRADIENT_DATA_TABLE_END,
674 &PremultipliedColorF::WHITE,
675 &cur_color,
676 &mut entries,
677 );
678 }
679
680 // Fill in the last entry with the last color stop
681 GradientGpuBlockBuilder::fill_colors(
682 GRADIENT_DATA_LAST_STOP,
683 GRADIENT_DATA_LAST_STOP + 1,
684 &cur_color,
685 &cur_color,
686 &mut entries,
687 );
688 }
689
690 for entry in entries.iter() {
691 request.push(entry.start_color);
692 request.push(entry.end_color);
693 }
694 }
695 }
696
697 #[test]
698 #[cfg(target_pointer_width = "64")]
test_struct_sizes()699 fn test_struct_sizes() {
700 use std::mem;
701 // The sizes of these structures are critical for performance on a number of
702 // talos stress tests. If you get a failure here on CI, there's two possibilities:
703 // (a) You made a structure smaller than it currently is. Great work! Update the
704 // test expectations and move on.
705 // (b) You made a structure larger. This is not necessarily a problem, but should only
706 // be done with care, and after checking if talos performance regresses badly.
707 assert_eq!(mem::size_of::<LinearGradient>(), 72, "LinearGradient size changed");
708 assert_eq!(mem::size_of::<LinearGradientTemplate>(), 112, "LinearGradientTemplate size changed");
709 assert_eq!(mem::size_of::<LinearGradientKey>(), 80, "LinearGradientKey size changed");
710
711 assert_eq!(mem::size_of::<RadialGradient>(), 72, "RadialGradient size changed");
712 assert_eq!(mem::size_of::<RadialGradientTemplate>(), 120, "RadialGradientTemplate size changed");
713 assert_eq!(mem::size_of::<RadialGradientKey>(), 88, "RadialGradientKey size changed");
714 }
715