tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

linear.rs (27546B)


      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 //! Linear gradients
      6 //!
      7 //! Specification: https://drafts.csswg.org/css-images-4/#linear-gradients
      8 //!
      9 //! Linear gradients are rendered via cached render tasks and composited with the image brush.
     10 
     11 use euclid::approxeq::ApproxEq;
     12 use euclid::{point2, vec2, size2};
     13 use api::{ExtendMode, GradientStop, LineOrientation, PremultipliedColorF, ColorF, ColorU};
     14 use api::units::*;
     15 use crate::gpu_types::{ImageBrushPrimitiveData, LinearGradientBrushData};
     16 use crate::pattern::{Pattern, PatternBuilder, PatternBuilderContext, PatternBuilderState, PatternKind, PatternShaderInput, PatternTextureInput};
     17 use crate::prim_store::gradient::{gpu_gradient_stops_blocks, write_gpu_gradient_stops_tree, GradientKind};
     18 use crate::scene_building::IsVisible;
     19 use crate::frame_builder::FrameBuildingState;
     20 use crate::intern::{Internable, InternDebug, Handle as InternHandle};
     21 use crate::internal_types::LayoutPrimitiveInfo;
     22 use crate::image_tiling::simplify_repeated_primitive;
     23 use crate::prim_store::{BrushSegment, GradientTileRange, VECS_PER_SEGMENT};
     24 use crate::prim_store::{PrimitiveInstanceKind, PrimitiveOpacity};
     25 use crate::prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore};
     26 use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, InternablePrimitive};
     27 use crate::render_task::{RenderTask, RenderTaskKind};
     28 use crate::render_task_graph::RenderTaskId;
     29 use crate::render_task_cache::{RenderTaskCacheKeyKind, RenderTaskCacheKey, RenderTaskParent};
     30 use crate::renderer::{GpuBufferAddress, GpuBufferBuilder};
     31 use crate::segment::EdgeAaSegmentMask;
     32 use super::{stops_and_min_alpha, GradientStopKey, GradientGpuBlockBuilder, apply_gradient_local_clip};
     33 use std::ops::{Deref, DerefMut};
     34 use std::mem::swap;
     35 
     36 pub const MAX_CACHED_SIZE: f32 = 1024.0;
     37 
     38 /// Identifying key for a linear gradient.
     39 #[cfg_attr(feature = "capture", derive(Serialize))]
     40 #[cfg_attr(feature = "replay", derive(Deserialize))]
     41 #[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)]
     42 pub struct LinearGradientKey {
     43    pub common: PrimKeyCommonData,
     44    pub extend_mode: ExtendMode,
     45    pub start_point: PointKey,
     46    pub end_point: PointKey,
     47    pub stretch_size: SizeKey,
     48    pub tile_spacing: SizeKey,
     49    pub stops: Vec<GradientStopKey>,
     50    pub reverse_stops: bool,
     51    pub cached: bool,
     52    pub nine_patch: Option<Box<NinePatchDescriptor>>,
     53    pub edge_aa_mask: EdgeAaSegmentMask,
     54    pub enable_dithering: bool,
     55 }
     56 
     57 impl LinearGradientKey {
     58    pub fn new(
     59        info: &LayoutPrimitiveInfo,
     60        linear_grad: LinearGradient,
     61    ) -> Self {
     62        LinearGradientKey {
     63            common: info.into(),
     64            extend_mode: linear_grad.extend_mode,
     65            start_point: linear_grad.start_point,
     66            end_point: linear_grad.end_point,
     67            stretch_size: linear_grad.stretch_size,
     68            tile_spacing: linear_grad.tile_spacing,
     69            stops: linear_grad.stops,
     70            reverse_stops: linear_grad.reverse_stops,
     71            cached: linear_grad.cached,
     72            nine_patch: linear_grad.nine_patch,
     73            edge_aa_mask: linear_grad.edge_aa_mask,
     74            enable_dithering: linear_grad.enable_dithering,
     75        }
     76    }
     77 }
     78 
     79 impl InternDebug for LinearGradientKey {}
     80 
     81 #[cfg_attr(feature = "capture", derive(Serialize))]
     82 #[cfg_attr(feature = "replay", derive(Deserialize))]
     83 #[derive(Debug, MallocSizeOf)]
     84 pub struct LinearGradientTemplate {
     85    pub common: PrimTemplateCommonData,
     86    pub extend_mode: ExtendMode,
     87    pub start_point: LayoutPoint,
     88    pub end_point: LayoutPoint,
     89    pub task_size: DeviceIntSize,
     90    pub scale: DeviceVector2D,
     91    pub stretch_size: LayoutSize,
     92    pub tile_spacing: LayoutSize,
     93    pub stops_opacity: PrimitiveOpacity,
     94    pub stops: Vec<GradientStop>,
     95    pub brush_segments: Vec<BrushSegment>,
     96    pub reverse_stops: bool,
     97    pub is_fast_path: bool,
     98    pub cached: bool,
     99    pub src_color: Option<RenderTaskId>,
    100 }
    101 
    102 impl PatternBuilder for LinearGradientTemplate {
    103    fn build(
    104        &self,
    105        _sub_rect: Option<DeviceRect>,
    106        ctx: &PatternBuilderContext,
    107        state: &mut PatternBuilderState,
    108    ) -> Pattern {
    109        let (start, end) = if self.reverse_stops {
    110            (self.end_point, self.start_point)
    111        } else {
    112            (self.start_point, self.end_point)
    113        };
    114 
    115        linear_gradient_pattern(
    116            start,
    117            end,
    118            self.extend_mode,
    119            &self.stops,
    120            ctx.fb_config.is_software,
    121            state.frame_gpu_data,
    122        )
    123    }
    124 
    125    fn get_base_color(
    126        &self,
    127        _ctx: &PatternBuilderContext,
    128    ) -> ColorF {
    129        ColorF::WHITE
    130    }
    131 
    132    fn use_shared_pattern(
    133        &self,
    134    ) -> bool {
    135        true
    136    }
    137 }
    138 
    139 impl Deref for LinearGradientTemplate {
    140    type Target = PrimTemplateCommonData;
    141    fn deref(&self) -> &Self::Target {
    142        &self.common
    143    }
    144 }
    145 
    146 impl DerefMut for LinearGradientTemplate {
    147    fn deref_mut(&mut self) -> &mut Self::Target {
    148        &mut self.common
    149    }
    150 }
    151 
    152 /// Perform a few optimizations to the gradient that are relevant to scene building.
    153 ///
    154 /// Returns true if the gradient was decomposed into fast-path primitives, indicating
    155 /// that we shouldn't emit a regular gradient primitive after this returns.
    156 pub fn optimize_linear_gradient(
    157    prim_rect: &mut LayoutRect,
    158    tile_size: &mut LayoutSize,
    159    mut tile_spacing: LayoutSize,
    160    clip_rect: &LayoutRect,
    161    start: &mut LayoutPoint,
    162    end: &mut LayoutPoint,
    163    extend_mode: ExtendMode,
    164    stops: &mut [GradientStopKey],
    165    enable_dithering: bool,
    166    // Callback called for each fast-path segment (rect, start end, stops).
    167    callback: &mut dyn FnMut(&LayoutRect, LayoutPoint, LayoutPoint, &[GradientStopKey], EdgeAaSegmentMask)
    168 ) -> bool {
    169    // First sanitize the gradient parameters. See if we can remove repetitions,
    170    // tighten the primitive bounds, etc.
    171 
    172    simplify_repeated_primitive(&tile_size, &mut tile_spacing, prim_rect);
    173 
    174    let vertical = start.x.approx_eq(&end.x);
    175    let horizontal = start.y.approx_eq(&end.y);
    176 
    177    let mut horizontally_tiled = prim_rect.width() > tile_size.width;
    178    let mut vertically_tiled = prim_rect.height() > tile_size.height;
    179 
    180    // Check whether the tiling is equivalent to stretching on either axis.
    181    // Stretching the gradient is more efficient than repeating it.
    182    if vertically_tiled && horizontal && tile_spacing.height == 0.0 {
    183        tile_size.height = prim_rect.height();
    184        vertically_tiled = false;
    185    }
    186 
    187    if horizontally_tiled && vertical && tile_spacing.width == 0.0 {
    188        tile_size.width = prim_rect.width();
    189        horizontally_tiled = false;
    190    }
    191 
    192    let offset = apply_gradient_local_clip(
    193        prim_rect,
    194        &tile_size,
    195        &tile_spacing,
    196        &clip_rect
    197    );
    198 
    199    // The size of gradient render tasks depends on the tile_size. No need to generate
    200    // large stretch sizes that will be clipped to the bounds of the primitive.
    201    tile_size.width = tile_size.width.min(prim_rect.width());
    202    tile_size.height = tile_size.height.min(prim_rect.height());
    203 
    204    *start += offset;
    205    *end += offset;
    206 
    207    // Next, in the case of axis-aligned gradients, see if it is worth
    208    // decomposing the gradient into multiple gradients with only two
    209    // gradient stops per segment to get a faster shader.
    210 
    211    if extend_mode != ExtendMode::Clamp || stops.is_empty() {
    212        return false;
    213    }
    214 
    215    if !vertical && !horizontal {
    216        return false;
    217    }
    218 
    219    if vertical && horizontal {
    220        return false;
    221    }
    222 
    223    if !tile_spacing.is_empty() || vertically_tiled || horizontally_tiled {
    224        return false;
    225    }
    226 
    227    // If the gradient is small, no need to bother with decomposing it.
    228    if !enable_dithering &&
    229        ((horizontal && tile_size.width < 256.0)
    230        || (vertical && tile_size.height < 256.0)) {
    231        return false;
    232    }
    233 
    234    // Flip x and y if need be so that we only deal with the horizontal case.
    235 
    236    // From now on don't return false. We are going modifying the caller's
    237    // variables and not bother to restore them. If the control flow changes,
    238    // Make sure to to restore &mut parameters to sensible values before
    239    // returning false.
    240 
    241    let adjust_rect = &mut |rect: &mut LayoutRect| {
    242        if vertical {
    243            swap(&mut rect.min.x, &mut rect.min.y);
    244            swap(&mut rect.max.x, &mut rect.max.y);
    245        }
    246    };
    247 
    248    let adjust_size = &mut |size: &mut LayoutSize| {
    249        if vertical { swap(&mut size.width, &mut size.height); }
    250    };
    251 
    252    let adjust_point = &mut |p: &mut LayoutPoint| {
    253        if vertical { swap(&mut p.x, &mut p.y); }
    254    };
    255 
    256    let clip_rect = match clip_rect.intersection(prim_rect) {
    257        Some(clip) => clip,
    258        None => {
    259            return false;
    260        }
    261    };
    262 
    263    adjust_rect(prim_rect);
    264    adjust_point(start);
    265    adjust_point(end);
    266    adjust_size(tile_size);
    267 
    268    let length = (end.x - start.x).abs();
    269 
    270    // Decompose the gradient into simple segments. This lets us:
    271    // - separate opaque from semi-transparent segments,
    272    // - compress long segments into small render tasks,
    273    // - make sure hard stops stay so even if the primitive is large.
    274 
    275    let reverse_stops = start.x > end.x;
    276 
    277    // Handle reverse stops so we can assume stops are arranged in increasing x.
    278    if reverse_stops {
    279        stops.reverse();
    280        swap(start, end);
    281    }
    282 
    283    // Use fake gradient stop to emulate the potential constant color sections
    284    // before and after the gradient endpoints.
    285    let mut prev = *stops.first().unwrap();
    286    let mut last = *stops.last().unwrap();
    287 
    288    // Set the offsets of the fake stops to position them at the edges of the primitive.
    289    prev.offset = -start.x / length;
    290    last.offset = (tile_size.width - start.x) / length;
    291    if reverse_stops {
    292        prev.offset = 1.0 - prev.offset;
    293        last.offset = 1.0 - last.offset;
    294    }
    295 
    296    let (side_edges, first_edge, last_edge) = if vertical {
    297        (
    298            EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT,
    299            EdgeAaSegmentMask::TOP,
    300            EdgeAaSegmentMask::BOTTOM
    301        )
    302    } else {
    303        (
    304            EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM,
    305            EdgeAaSegmentMask::LEFT,
    306            EdgeAaSegmentMask::RIGHT
    307        )
    308    };
    309 
    310    let mut is_first = true;
    311    let last_offset = last.offset;
    312    for stop in stops.iter().chain((&[last]).iter()) {
    313        let prev_stop = prev;
    314        prev = *stop;
    315 
    316        if prev_stop.color.a == 0 && stop.color.a == 0 {
    317            continue;
    318        }
    319 
    320 
    321        let prev_offset = if reverse_stops { 1.0 - prev_stop.offset } else { prev_stop.offset };
    322        let offset = if reverse_stops { 1.0 - stop.offset } else { stop.offset };
    323 
    324        // In layout space, relative to the primitive.
    325        let segment_start = start.x + prev_offset * length;
    326        let segment_end = start.x + offset * length;
    327        let segment_length = segment_end - segment_start;
    328 
    329        if segment_length <= 0.0 {
    330            continue;
    331        }
    332 
    333        let mut segment_rect = *prim_rect;
    334        segment_rect.min.x += segment_start;
    335        segment_rect.max.x = segment_rect.min.x + segment_length;
    336 
    337        let mut start = point2(0.0, 0.0);
    338        let mut end = point2(segment_length, 0.0);
    339 
    340        adjust_point(&mut start);
    341        adjust_point(&mut end);
    342        adjust_rect(&mut segment_rect);
    343 
    344        let origin_before_clip = segment_rect.min;
    345        segment_rect = match segment_rect.intersection(&clip_rect) {
    346            Some(rect) => rect,
    347            None => {
    348                continue;
    349            }
    350        };
    351        let offset = segment_rect.min - origin_before_clip;
    352 
    353        // Account for the clipping since start and end are relative to the origin.
    354        start -= offset;
    355        end -= offset;
    356 
    357        let mut edge_flags = side_edges;
    358        if is_first {
    359            edge_flags |= first_edge;
    360            is_first = false;
    361        }
    362        if stop.offset == last_offset {
    363            edge_flags |= last_edge;
    364        }
    365 
    366        callback(
    367            &segment_rect,
    368            start,
    369            end,
    370            &[
    371                GradientStopKey { offset: 0.0, .. prev_stop },
    372                GradientStopKey { offset: 1.0, .. *stop },
    373            ],
    374            edge_flags,
    375        );
    376    }
    377 
    378    true
    379 }
    380 
    381 impl From<LinearGradientKey> for LinearGradientTemplate {
    382    fn from(item: LinearGradientKey) -> Self {
    383 
    384        let mut common = PrimTemplateCommonData::with_key_common(item.common);
    385        common.edge_aa_mask = item.edge_aa_mask;
    386 
    387        let (mut stops, min_alpha) = stops_and_min_alpha(&item.stops);
    388 
    389        let mut brush_segments = Vec::new();
    390 
    391        if let Some(ref nine_patch) = item.nine_patch {
    392            brush_segments = nine_patch.create_segments(common.prim_rect.size());
    393        }
    394 
    395        // Save opacity of the stops for use in
    396        // selecting which pass this gradient
    397        // should be drawn in.
    398        let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha);
    399 
    400        let start_point = LayoutPoint::new(item.start_point.x, item.start_point.y);
    401        let end_point = LayoutPoint::new(item.end_point.x, item.end_point.y);
    402        let tile_spacing: LayoutSize = item.tile_spacing.into();
    403        let stretch_size: LayoutSize = item.stretch_size.into();
    404        let mut task_size: DeviceSize = stretch_size.cast_unit();
    405 
    406        let horizontal = !item.enable_dithering &&
    407            start_point.y.approx_eq(&end_point.y);
    408        let vertical = !item.enable_dithering &&
    409            start_point.x.approx_eq(&end_point.x);
    410 
    411        if horizontal {
    412            // Completely horizontal, we can stretch the gradient vertically.
    413            task_size.height = 1.0;
    414        }
    415 
    416        if vertical {
    417            // Completely vertical, we can stretch the gradient horizontally.
    418            task_size.width = 1.0;
    419        }
    420 
    421        // See if we can render the gradient using a special fast-path shader.
    422        // The fast path path only works with two gradient stops.
    423        let mut is_fast_path = false;
    424        if item.cached && stops.len() == 2 && brush_segments.is_empty() {
    425            if horizontal
    426                && stretch_size.width >= common.prim_rect.width()
    427                && start_point.x.approx_eq(&0.0)
    428                && end_point.x.approx_eq(&stretch_size.width) {
    429                is_fast_path = true;
    430                task_size.width = task_size.width.min(256.0);
    431            }
    432            if vertical
    433                && stretch_size.height >= common.prim_rect.height()
    434                && start_point.y.approx_eq(&0.0)
    435                && end_point.y.approx_eq(&stretch_size.height) {
    436                is_fast_path = true;
    437                task_size.height = task_size.height.min(256.0);
    438            }
    439 
    440            if stops[0].color == stops[1].color {
    441                is_fast_path = true;
    442                task_size = size2(1.0, 1.0);
    443            }
    444 
    445            if is_fast_path && item.reverse_stops {
    446                // The fast path doesn't use the gradient gpu blocks builder so handle
    447                // reversed stops here.
    448                stops.swap(0, 1);
    449            }
    450        }
    451 
    452        // Avoid rendering enormous gradients. Linear gradients are mostly made of soft transitions,
    453        // so it is unlikely that rendering at a higher resolution than 1024 would produce noticeable
    454        // differences, especially with 8 bits per channel.
    455 
    456        let mut scale = vec2(1.0, 1.0);
    457 
    458        if task_size.width > MAX_CACHED_SIZE {
    459            scale.x = task_size.width / MAX_CACHED_SIZE;
    460            task_size.width = MAX_CACHED_SIZE;
    461        }
    462 
    463        if task_size.height > MAX_CACHED_SIZE {
    464            scale.y = task_size.height / MAX_CACHED_SIZE;
    465            task_size.height = MAX_CACHED_SIZE;
    466        }
    467 
    468        LinearGradientTemplate {
    469            common,
    470            extend_mode: item.extend_mode,
    471            start_point,
    472            end_point,
    473            task_size: task_size.ceil().to_i32(),
    474            scale,
    475            stretch_size,
    476            tile_spacing,
    477            stops_opacity,
    478            stops,
    479            brush_segments,
    480            reverse_stops: item.reverse_stops,
    481            is_fast_path,
    482            cached: item.cached,
    483            src_color: None,
    484        }
    485    }
    486 }
    487 
    488 impl LinearGradientTemplate {
    489    /// Update the GPU cache for a given primitive template. This may be called multiple
    490    /// times per frame, by each primitive reference that refers to this interned
    491    /// template. The initial request call to the GPU cache ensures that work is only
    492    /// done if the cache entry is invalid (due to first use or eviction).
    493    pub fn update(
    494        &mut self,
    495        frame_state: &mut FrameBuildingState,
    496    ) {
    497        let mut writer = frame_state.frame_gpu_data.f32.write_blocks(3 + self.brush_segments.len() * VECS_PER_SEGMENT);
    498 
    499        // Write_prim_gpu_blocks
    500        if self.cached {
    501            writer.push(&ImageBrushPrimitiveData {
    502                color: PremultipliedColorF::WHITE,
    503                background_color: PremultipliedColorF::WHITE,
    504                stretch_size: self.stretch_size,
    505            });
    506        } else {
    507            // We are using the gradient brush.
    508            writer.push(&LinearGradientBrushData {
    509                start: self.start_point,
    510                end: self.end_point,
    511                extend_mode: self.extend_mode,
    512                stretch_size: self.stretch_size,
    513            });
    514        }
    515 
    516        // write_segment_gpu_blocks
    517        for segment in &self.brush_segments {
    518            segment.write_gpu_blocks(&mut writer);
    519        }
    520 
    521        self.common.gpu_buffer_address = writer.finish();
    522 
    523        // Tile spacing is always handled by decomposing into separate draw calls so the
    524        // primitive opacity is equivalent to stops opacity. This might change to being
    525        // set to non-opaque in the presence of tile spacing if/when tile spacing is handled
    526        // in the same way as with the image primitive.
    527        self.opacity = self.stops_opacity;
    528 
    529        if !self.cached {
    530            return;
    531        }
    532 
    533        let task_id = if self.is_fast_path {
    534            let orientation = if self.task_size.width > self.task_size.height {
    535                LineOrientation::Horizontal
    536            } else {
    537                LineOrientation::Vertical
    538            };
    539 
    540            let gradient = FastLinearGradientTask {
    541                color0: self.stops[0].color.into(),
    542                color1: self.stops[1].color.into(),
    543                orientation,
    544            };
    545 
    546            frame_state.resource_cache.request_render_task(
    547                Some(RenderTaskCacheKey {
    548                    size: self.task_size,
    549                    kind: RenderTaskCacheKeyKind::FastLinearGradient(gradient),
    550                }),
    551                false,
    552                RenderTaskParent::Surface,
    553                &mut frame_state.frame_gpu_data.f32,
    554                frame_state.rg_builder,
    555                &mut frame_state.surface_builder,
    556                &mut |rg_builder, _| {
    557                    rg_builder.add().init(RenderTask::new_dynamic(
    558                        self.task_size,
    559                        RenderTaskKind::FastLinearGradient(gradient),
    560                    ))
    561                }
    562            )
    563        } else {
    564            let cache_key = LinearGradientCacheKey {
    565                size: self.task_size,
    566                start: PointKey { x: self.start_point.x, y: self.start_point.y },
    567                end: PointKey { x: self.end_point.x, y: self.end_point.y },
    568                scale: PointKey { x: self.scale.x, y: self.scale.y },
    569                extend_mode: self.extend_mode,
    570                stops: self.stops.iter().map(|stop| (*stop).into()).collect(),
    571                reversed_stops: self.reverse_stops,
    572            };
    573 
    574            frame_state.resource_cache.request_render_task(
    575                Some(RenderTaskCacheKey {
    576                    size: self.task_size,
    577                    kind: RenderTaskCacheKeyKind::LinearGradient(cache_key),
    578                }),
    579                false,
    580                RenderTaskParent::Surface,
    581                &mut frame_state.frame_gpu_data.f32,
    582                frame_state.rg_builder,
    583                &mut frame_state.surface_builder,
    584                &mut |rg_builder, gpu_buffer_builder| {
    585                    let stops = Some(GradientGpuBlockBuilder::build(
    586                        self.reverse_stops,
    587                        gpu_buffer_builder,
    588                        &self.stops,
    589                    ));
    590 
    591                    rg_builder.add().init(RenderTask::new_dynamic(
    592                        self.task_size,
    593                        RenderTaskKind::LinearGradient(LinearGradientTask {
    594                            // Cached brush gradients are rasteried with 1 layout
    595                            // pixel = 1 device pixel (regardless of potential
    596                            // scaling factors).
    597                            start: self.start_point.cast_unit(),
    598                            end: self.end_point.cast_unit(),
    599                            scale: self.scale,
    600                            extend_mode: self.extend_mode,
    601                            stops: stops.unwrap(),
    602                        }),
    603                    ))
    604                }
    605            )
    606        };
    607 
    608        self.src_color = Some(task_id);
    609    }
    610 }
    611 
    612 pub type LinearGradientDataHandle = InternHandle<LinearGradient>;
    613 
    614 #[derive(Debug, MallocSizeOf)]
    615 #[cfg_attr(feature = "capture", derive(Serialize))]
    616 #[cfg_attr(feature = "replay", derive(Deserialize))]
    617 pub struct LinearGradient {
    618    pub extend_mode: ExtendMode,
    619    pub start_point: PointKey,
    620    pub end_point: PointKey,
    621    pub stretch_size: SizeKey,
    622    pub tile_spacing: SizeKey,
    623    pub stops: Vec<GradientStopKey>,
    624    pub reverse_stops: bool,
    625    pub nine_patch: Option<Box<NinePatchDescriptor>>,
    626    pub cached: bool,
    627    pub edge_aa_mask: EdgeAaSegmentMask,
    628    pub enable_dithering: bool,
    629 }
    630 
    631 impl Internable for LinearGradient {
    632    type Key = LinearGradientKey;
    633    type StoreData = LinearGradientTemplate;
    634    type InternData = ();
    635    const PROFILE_COUNTER: usize = crate::profiler::INTERNED_LINEAR_GRADIENTS;
    636 }
    637 
    638 impl InternablePrimitive for LinearGradient {
    639    fn into_key(
    640        self,
    641        info: &LayoutPrimitiveInfo,
    642    ) -> LinearGradientKey {
    643        LinearGradientKey::new(info, self)
    644    }
    645 
    646    fn make_instance_kind(
    647        key: LinearGradientKey,
    648        data_handle: LinearGradientDataHandle,
    649        _prim_store: &mut PrimitiveStore,
    650    ) -> PrimitiveInstanceKind {
    651        if key.cached {
    652            PrimitiveInstanceKind::CachedLinearGradient {
    653                data_handle,
    654                visible_tiles_range: GradientTileRange::empty(),
    655            }
    656        } else {
    657            PrimitiveInstanceKind::LinearGradient {
    658                data_handle,
    659                visible_tiles_range: GradientTileRange::empty(),
    660                use_legacy_path: true,
    661            }
    662        }
    663    }
    664 }
    665 
    666 impl IsVisible for LinearGradient {
    667    fn is_visible(&self) -> bool {
    668        true
    669    }
    670 }
    671 
    672 #[derive(Debug)]
    673 #[cfg_attr(feature = "capture", derive(Serialize))]
    674 pub struct LinearGradientPrimitive {
    675    pub cache_segments: Vec<CachedGradientSegment>,
    676    pub visible_tiles_range: GradientTileRange,
    677 }
    678 
    679 #[derive(Debug)]
    680 #[cfg_attr(feature = "capture", derive(Serialize))]
    681 pub struct CachedGradientSegment {
    682    pub render_task: RenderTaskId,
    683    pub local_rect: LayoutRect,
    684 }
    685 
    686 
    687 #[derive(Copy, Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
    688 #[cfg_attr(feature = "capture", derive(Serialize))]
    689 #[cfg_attr(feature = "replay", derive(Deserialize))]
    690 pub struct FastLinearGradientTask {
    691    pub color0: ColorU,
    692    pub color1: ColorU,
    693    pub orientation: LineOrientation,
    694 }
    695 
    696 impl FastLinearGradientTask {
    697    pub fn to_instance(&self, target_rect: &DeviceIntRect) -> FastLinearGradientInstance {
    698        FastLinearGradientInstance {
    699            task_rect: target_rect.to_f32(),
    700            color0: ColorF::from(self.color0).premultiplied(),
    701            color1: ColorF::from(self.color1).premultiplied(),
    702            axis_select: match self.orientation {
    703                LineOrientation::Horizontal => 0.0,
    704                LineOrientation::Vertical => 1.0,
    705            },
    706        }
    707    }
    708 }
    709 
    710 pub type FastLinearGradientCacheKey = FastLinearGradientTask;
    711 
    712 /// The per-instance shader input of a fast-path linear gradient render task.
    713 ///
    714 /// Must match the FAST_LINEAR_GRADIENT instance description in renderer/vertex.rs.
    715 #[cfg_attr(feature = "capture", derive(Serialize))]
    716 #[cfg_attr(feature = "replay", derive(Deserialize))]
    717 #[repr(C)]
    718 #[derive(Clone, Debug)]
    719 pub struct FastLinearGradientInstance {
    720    pub task_rect: DeviceRect,
    721    pub color0: PremultipliedColorF,
    722    pub color1: PremultipliedColorF,
    723    pub axis_select: f32,
    724 }
    725 
    726 #[derive(Debug)]
    727 #[cfg_attr(feature = "capture", derive(Serialize))]
    728 #[cfg_attr(feature = "replay", derive(Deserialize))]
    729 pub struct LinearGradientTask {
    730    pub start: DevicePoint,
    731    pub end: DevicePoint,
    732    pub scale: DeviceVector2D,
    733    pub extend_mode: ExtendMode,
    734    pub stops: GpuBufferAddress,
    735 }
    736 
    737 impl LinearGradientTask {
    738    pub fn to_instance(&self, target_rect: &DeviceIntRect) -> LinearGradientInstance {
    739        LinearGradientInstance {
    740            task_rect: target_rect.to_f32(),
    741            start: self.start,
    742            end: self.end,
    743            scale: self.scale,
    744            extend_mode: self.extend_mode as i32,
    745            gradient_stops_address: self.stops.as_int(),
    746        }
    747    }
    748 }
    749 
    750 /// The per-instance shader input of a linear gradient render task.
    751 ///
    752 /// Must match the LINEAR_GRADIENT instance description in renderer/vertex.rs.
    753 #[cfg_attr(feature = "capture", derive(Serialize))]
    754 #[cfg_attr(feature = "replay", derive(Deserialize))]
    755 #[repr(C)]
    756 #[derive(Clone, Debug)]
    757 pub struct LinearGradientInstance {
    758    pub task_rect: DeviceRect,
    759    pub start: DevicePoint,
    760    pub end: DevicePoint,
    761    pub scale: DeviceVector2D,
    762    pub extend_mode: i32,
    763    pub gradient_stops_address: i32,
    764 }
    765 
    766 #[derive(Clone, Debug, Hash, PartialEq, Eq)]
    767 #[cfg_attr(feature = "capture", derive(Serialize))]
    768 #[cfg_attr(feature = "replay", derive(Deserialize))]
    769 pub struct LinearGradientCacheKey {
    770    pub size: DeviceIntSize,
    771    pub start: PointKey,
    772    pub end: PointKey,
    773    pub scale: PointKey,
    774    pub extend_mode: ExtendMode,
    775    pub stops: Vec<GradientStopKey>,
    776    pub reversed_stops: bool,
    777 }
    778 
    779 pub fn linear_gradient_pattern(
    780    start: LayoutPoint,
    781    end: LayoutPoint,
    782    extend_mode: ExtendMode,
    783    stops: &[GradientStop],
    784    _is_software: bool,
    785    gpu_buffer_builder: &mut GpuBufferBuilder
    786 ) -> Pattern {
    787    let num_blocks = 2 + gpu_gradient_stops_blocks(stops.len());
    788    let mut writer = gpu_buffer_builder.f32.write_blocks(num_blocks);
    789    writer.push_one([
    790        start.x,
    791        start.y,
    792        end.x,
    793        end.y,
    794    ]);
    795    writer.push_one([
    796        0.0,
    797        0.0,
    798        0.0,
    799        0.0,
    800    ]);
    801 
    802    let is_opaque = write_gpu_gradient_stops_tree(stops, GradientKind::Linear, extend_mode, &mut writer);
    803 
    804    let gradient_address = writer.finish();
    805 
    806    Pattern {
    807        kind: PatternKind::Gradient,
    808        shader_input: PatternShaderInput(
    809            gradient_address.as_int(),
    810            0,
    811        ),
    812        texture_input: PatternTextureInput::default(),
    813        base_color: ColorF::WHITE,
    814        is_opaque,
    815    }
    816 }