1use api::{
6 ColorF, ColorU, RasterSpace,
7 LineOrientation, LineStyle, PremultipliedColorF, Shadow,
8};
9use api::units::*;
10use euclid::Scale;
11use crate::gpu_types::ImageBrushPrimitiveData;
12use crate::render_task::{RenderTask, RenderTaskKind};
13use crate::render_task_cache::{RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent};
14use crate::render_task_graph::RenderTaskId;
15use crate::renderer::GpuBufferAddress;
16use crate::scene_building::{CreateShadow, IsVisible};
17use crate::frame_builder::{FrameBuildingContext, FrameBuildingState};
18use crate::intern;
19use crate::internal_types::LayoutPrimitiveInfo;
20use crate::prim_store::{
21 PrimKey, PrimTemplate, PrimTemplateCommonData,
22 InternablePrimitive, PrimitiveStore,
23};
24use crate::prim_store::PrimitiveKind;
25use crate::spatial_tree::SpatialNodeIndex;
26use crate::util::clamp_to_scale_factor;
27
28pub const MAX_LINE_DECORATION_RESOLUTION: u32 = 4096;
30
31#[derive(Copy, Clone, Debug)]
33#[cfg_attr(feature = "capture", derive(Serialize))]
34pub struct LineDecorationScratch {
35 pub task_id: RenderTaskId,
36 pub gpu_address: GpuBufferAddress,
37}
38
39#[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
40#[cfg_attr(feature = "capture", derive(Serialize))]
41#[cfg_attr(feature = "replay", derive(Deserialize))]
42pub struct LineDecorationCacheKey {
43 pub style: LineStyle,
44 pub orientation: LineOrientation,
45 pub wavy_line_thickness: Au,
46 pub size: LayoutSizeAu,
47}
48
49#[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)]
55#[cfg_attr(feature = "capture", derive(Serialize))]
56#[cfg_attr(feature = "replay", derive(Deserialize))]
57pub struct LineDecoration {
58 pub style: LineStyle,
59 pub orientation: LineOrientation,
60 pub wavy_line_thickness: Au,
61 pub color: ColorU,
62}
63
64pub type LineDecorationKey = PrimKey<LineDecoration>;
65
66impl LineDecorationKey {
67 pub fn new(
68 info: &LayoutPrimitiveInfo,
69 line_dec: LineDecoration,
70 ) -> Self {
71 LineDecorationKey {
72 common: info.into(),
73 kind: line_dec,
74 }
75 }
76}
77
78impl intern::InternDebug for LineDecorationKey {}
79
80#[cfg_attr(feature = "capture", derive(Serialize))]
81#[cfg_attr(feature = "replay", derive(Deserialize))]
82#[derive(MallocSizeOf)]
83pub struct LineDecorationData {
84 pub style: LineStyle,
85 pub orientation: LineOrientation,
86 pub wavy_line_thickness: Au,
87 pub color: ColorF,
88}
89
90impl LineDecorationData {
91 pub fn prepare(
96 &self,
97 prim_size: LayoutSize,
98 prim_spatial_node_index: SpatialNodeIndex,
99 frame_context: &FrameBuildingContext,
100 frame_state: &mut FrameBuildingState,
101 ) -> (RenderTaskId, GpuBufferAddress) {
102 let cache_key = get_line_decoration_size(
103 &prim_size,
104 self.orientation,
105 self.style,
106 self.wavy_line_thickness.to_f32_px(),
107 ).map(|size| LineDecorationCacheKey {
108 style: self.style,
109 orientation: self.orientation,
110 wavy_line_thickness: self.wavy_line_thickness,
111 size: size.to_au(),
112 });
113
114 let mut writer = frame_state.frame_gpu_data.f32.write_blocks(3);
115 match cache_key.as_ref() {
116 Some(cache_key) => {
117 writer.push(&ImageBrushPrimitiveData {
118 color: self.color.premultiplied(),
119 background_color: PremultipliedColorF::WHITE,
120 stretch_size: LayoutSize::new(
121 cache_key.size.width.to_f32_px(),
122 cache_key.size.height.to_f32_px(),
123 ),
124 });
125 }
126 None => {
127 writer.push_one(self.color.premultiplied());
128 }
129 }
130 let gpu_address = writer.finish();
131
132 let task_id = match cache_key {
133 Some(cache_key) => self.allocate_render_task(
134 cache_key,
135 prim_spatial_node_index,
136 frame_context,
137 frame_state,
138 ),
139 None => RenderTaskId::INVALID,
140 };
141
142 (task_id, gpu_address)
143 }
144
145 fn allocate_render_task(
146 &self,
147 cache_key: LineDecorationCacheKey,
148 prim_spatial_node_index: SpatialNodeIndex,
149 frame_context: &FrameBuildingContext,
150 frame_state: &mut FrameBuildingState,
151 ) -> RenderTaskId {
152 let scale = frame_context
155 .spatial_tree
156 .get_world_transform(prim_spatial_node_index)
157 .scale_factors();
158
159 let scale_width = clamp_to_scale_factor(scale.0, false);
168 let scale_height = clamp_to_scale_factor(scale.1, false);
169 let scale_factor = LayoutToDeviceScale::new(scale_width.max(scale_height));
171
172 let task_size_f = (LayoutSize::from_au(cache_key.size) * scale_factor).ceil();
173 let mut task_size = if task_size_f.width > MAX_LINE_DECORATION_RESOLUTION as f32 ||
174 task_size_f.height > MAX_LINE_DECORATION_RESOLUTION as f32 {
175 let max_extent = task_size_f.width.max(task_size_f.height);
176 let task_scale_factor = Scale::new(MAX_LINE_DECORATION_RESOLUTION as f32 / max_extent);
177 let task_size = (LayoutSize::from_au(cache_key.size) * scale_factor * task_scale_factor)
178 .ceil().to_i32();
179 task_size
180 } else {
181 task_size_f.to_i32()
182 };
183
184 task_size.width = task_size.width.max(1);
189 task_size.height = task_size.height.max(1);
190
191 frame_state.resource_cache.request_render_task(
193 Some(RenderTaskCacheKey {
194 origin: DeviceIntPoint::zero(),
195 size: task_size,
196 kind: RenderTaskCacheKeyKind::LineDecoration(cache_key.clone()),
197 }),
198 false,
199 RenderTaskParent::Surface,
200 &mut frame_state.frame_gpu_data.f32,
201 frame_state.rg_builder,
202 &mut frame_state.surface_builder,
203 &mut |rg_builder, _| {
204 rg_builder.add().init(RenderTask::new_dynamic(
205 task_size,
206 RenderTaskKind::new_line_decoration(
207 cache_key.style,
208 cache_key.orientation,
209 cache_key.wavy_line_thickness.to_f32_px(),
210 LayoutSize::from_au(cache_key.size),
211 ),
212 ))
213 }
214 )
215 }
216}
217
218pub type LineDecorationTemplate = PrimTemplate<LineDecorationData>;
219
220impl From<LineDecorationKey> for LineDecorationTemplate {
221 fn from(line_dec: LineDecorationKey) -> Self {
222 let common = PrimTemplateCommonData::with_key_common(line_dec.common);
223 LineDecorationTemplate {
224 common,
225 kind: LineDecorationData {
226 style: line_dec.kind.style,
227 orientation: line_dec.kind.orientation,
228 wavy_line_thickness: line_dec.kind.wavy_line_thickness,
229 color: line_dec.kind.color.into(),
230 }
231 }
232 }
233}
234
235pub type LineDecorationDataHandle = intern::Handle<LineDecoration>;
236
237impl intern::Internable for LineDecoration {
238 type Key = LineDecorationKey;
239 type StoreData = LineDecorationTemplate;
240 type InternData = ();
241 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_LINE_DECORATIONS;
242}
243
244impl InternablePrimitive for LineDecoration {
245 fn into_key(
246 self,
247 info: &LayoutPrimitiveInfo,
248 ) -> LineDecorationKey {
249 LineDecorationKey::new(
250 info,
251 self,
252 )
253 }
254
255 fn make_instance_kind(
256 _key: LineDecorationKey,
257 data_handle: LineDecorationDataHandle,
258 _: &mut PrimitiveStore,
259 ) -> PrimitiveKind {
260 PrimitiveKind::LineDecoration {
261 data_handle,
262 }
263 }
264}
265
266impl CreateShadow for LineDecoration {
267 fn create_shadow(
268 &self,
269 shadow: &Shadow,
270 _: bool,
271 _: RasterSpace,
272 ) -> Self {
273 LineDecoration {
274 style: self.style,
275 orientation: self.orientation,
276 wavy_line_thickness: self.wavy_line_thickness,
277 color: shadow.color.into(),
278 }
279 }
280}
281
282impl IsVisible for LineDecoration {
283 fn is_visible(&self) -> bool {
284 self.color.a > 0
285 }
286}
287
288pub fn get_line_decoration_size(
309 rect_size: &LayoutSize,
310 orientation: LineOrientation,
311 style: LineStyle,
312 wavy_line_thickness: f32,
313) -> Option<LayoutSize> {
314 let h = match orientation {
315 LineOrientation::Horizontal => rect_size.height,
316 LineOrientation::Vertical => rect_size.width,
317 };
318
319 let (parallel, perpendicular) = match style {
326 LineStyle::Solid => {
327 return None;
328 }
329 LineStyle::Dashed => {
330 let dash_length = (3.0 * h).min(64.0).max(1.0);
331
332 (2.0 * dash_length, 4.0)
333 }
334 LineStyle::Dotted => {
335 let diameter = h.min(64.0).max(1.0);
336 let period = 2.0 * diameter;
337
338 (period, diameter)
339 }
340 LineStyle::Wavy => {
341 let line_thickness = wavy_line_thickness.max(1.0);
342 let slope_length = h - line_thickness;
343 let flat_length = ((line_thickness - 1.0) * 2.0).max(1.0);
344 let approx_period = 2.0 * (slope_length + flat_length);
345
346 (approx_period, h)
347 }
348 };
349
350 Some(match orientation {
351 LineOrientation::Horizontal => LayoutSize::new(parallel, perpendicular),
352 LineOrientation::Vertical => LayoutSize::new(perpendicular, parallel),
353 })
354}
355
356#[test]
357#[cfg(target_pointer_width = "64")]
358fn test_struct_sizes() {
359 use std::mem;
360 assert_eq!(mem::size_of::<LineDecoration>(), 12, "LineDecoration size changed");
367 assert_eq!(mem::size_of::<LineDecorationTemplate>(), 32, "LineDecorationTemplate size changed");
368 assert_eq!(mem::size_of::<LineDecorationKey>(), 16, "LineDecorationKey size changed");
369}