1use euclid::vec2;
12use api::{ColorF, ExtendMode, GradientStop, PremultipliedColorF};
13use api::units::*;
14use crate::pattern::{Pattern, PatternBuilder, PatternBuilderContext, PatternBuilderState, PatternKind, PatternShaderInput, PatternTextureInput};
15use crate::prim_store::gradient::{gpu_gradient_stops_blocks, write_gpu_gradient_stops_tree, GradientKind};
16use crate::scene_building::IsVisible;
17use crate::frame_builder::FrameBuildingState;
18use crate::intern::{Internable, InternDebug, Handle as InternHandle};
19use crate::internal_types::LayoutPrimitiveInfo;
20use crate::prim_store::{BrushSegment, GradientTileRange};
21use crate::prim_store::{PrimitiveInstanceKind, PrimitiveOpacity, FloatKey};
22use crate::prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore};
23use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, InternablePrimitive};
24use crate::render_task::{RenderTask, RenderTaskKind};
25use crate::render_task_graph::RenderTaskId;
26use crate::render_task_cache::{RenderTaskCacheKeyKind, RenderTaskCacheKey, RenderTaskParent};
27use crate::renderer::{GpuBufferAddress, GpuBufferBuilder};
28
29use std::{hash, ops::{Deref, DerefMut}};
30use super::{stops_and_min_alpha, GradientStopKey, GradientGpuBlockBuilder};
31
32#[cfg_attr(feature = "capture", derive(Serialize))]
34#[cfg_attr(feature = "replay", derive(Deserialize))]
35#[derive(Debug, Clone, MallocSizeOf, PartialEq)]
36pub struct ConicGradientParams {
37 pub angle: f32, pub start_offset: f32,
39 pub end_offset: f32,
40}
41
42impl Eq for ConicGradientParams {}
43
44impl hash::Hash for ConicGradientParams {
45 fn hash<H: hash::Hasher>(&self, state: &mut H) {
46 self.angle.to_bits().hash(state);
47 self.start_offset.to_bits().hash(state);
48 self.end_offset.to_bits().hash(state);
49 }
50}
51
52#[cfg_attr(feature = "capture", derive(Serialize))]
54#[cfg_attr(feature = "replay", derive(Deserialize))]
55#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)]
56pub struct ConicGradientKey {
57 pub common: PrimKeyCommonData,
58 pub extend_mode: ExtendMode,
59 pub center: PointKey,
60 pub params: ConicGradientParams,
61 pub stretch_size: SizeKey,
62 pub stops: Vec<GradientStopKey>,
63 pub tile_spacing: SizeKey,
64 pub nine_patch: Option<Box<NinePatchDescriptor>>,
65}
66
67impl ConicGradientKey {
68 pub fn new(
69 info: &LayoutPrimitiveInfo,
70 conic_grad: ConicGradient,
71 ) -> Self {
72 ConicGradientKey {
73 common: info.into(),
74 extend_mode: conic_grad.extend_mode,
75 center: conic_grad.center,
76 params: conic_grad.params,
77 stretch_size: conic_grad.stretch_size,
78 stops: conic_grad.stops,
79 tile_spacing: conic_grad.tile_spacing,
80 nine_patch: conic_grad.nine_patch,
81 }
82 }
83}
84
85impl InternDebug for ConicGradientKey {}
86
87#[cfg_attr(feature = "capture", derive(Serialize))]
88#[cfg_attr(feature = "replay", derive(Deserialize))]
89#[derive(MallocSizeOf)]
90pub struct ConicGradientTemplate {
91 pub common: PrimTemplateCommonData,
92 pub extend_mode: ExtendMode,
93 pub center: DevicePoint,
94 pub params: ConicGradientParams,
95 pub task_size: DeviceIntSize,
96 pub scale: DeviceVector2D,
97 pub stretch_size: LayoutSize,
98 pub tile_spacing: LayoutSize,
99 pub brush_segments: Vec<BrushSegment>,
100 pub stops_opacity: PrimitiveOpacity,
101 pub stops: Vec<GradientStop>,
102 pub src_color: Option<RenderTaskId>,
103}
104
105impl PatternBuilder for ConicGradientTemplate {
106 fn build(
107 &self,
108 _sub_rect: Option<DeviceRect>,
109 ctx: &PatternBuilderContext,
110 state: &mut PatternBuilderState,
111 ) -> Pattern {
112 let no_scale = DeviceVector2D::one();
115
116 if ctx.fb_config.precise_conic_gradients {
117 conic_gradient_pattern(
118 self.center,
119 no_scale,
120 &self.params,
121 self.extend_mode,
122 &self.stops,
123 state.frame_gpu_data,
124 )
125 } else {
126 conic_gradient_pattern_with_table(
127 self.center,
128 no_scale,
129 &self.params,
130 self.extend_mode,
131 &self.stops,
132 state.frame_gpu_data,
133 )
134 }
135 }
136
137 fn get_base_color(
138 &self,
139 _ctx: &PatternBuilderContext,
140 ) -> ColorF {
141 ColorF::WHITE
142 }
143
144 fn use_shared_pattern(
145 &self,
146 ) -> bool {
147 true
148 }
149}
150
151impl Deref for ConicGradientTemplate {
152 type Target = PrimTemplateCommonData;
153 fn deref(&self) -> &Self::Target {
154 &self.common
155 }
156}
157
158impl DerefMut for ConicGradientTemplate {
159 fn deref_mut(&mut self) -> &mut Self::Target {
160 &mut self.common
161 }
162}
163
164impl From<ConicGradientKey> for ConicGradientTemplate {
165 fn from(item: ConicGradientKey) -> Self {
166 let common = PrimTemplateCommonData::with_key_common(item.common);
167 let mut brush_segments = Vec::new();
168
169 if let Some(ref nine_patch) = item.nine_patch {
170 brush_segments = nine_patch.create_segments(common.prim_rect.size());
171 }
172
173 let (stops, min_alpha) = stops_and_min_alpha(&item.stops);
174
175 let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha);
179
180 let mut stretch_size: LayoutSize = item.stretch_size.into();
181 stretch_size.width = stretch_size.width.min(common.prim_rect.width());
182 stretch_size.height = stretch_size.height.min(common.prim_rect.height());
183
184 fn approx_eq(a: f32, b: f32) -> bool { (a - b).abs() < 0.01 }
185
186 let mut has_hard_stops = false;
192 let mut prev_stop = None;
193 let offset_range = item.params.end_offset - item.params.start_offset;
194 for stop in &stops {
195 if offset_range <= 0.0 {
196 break;
197 }
198 if let Some(prev_offset) = prev_stop {
199 if stop.offset < prev_offset + 0.005 / offset_range {
201 let a = item.params.angle / (2.0 * std::f32::consts::PI)
205 + item.params.start_offset
206 + stop.offset / offset_range;
207 let a = a.rem_euclid(0.25);
208
209 if !approx_eq(a, 0.0) && !approx_eq(a, 0.25) {
210 has_hard_stops = true;
211 break;
212 }
213 }
214 }
215 prev_stop = Some(stop.offset);
216 }
217
218 let max_size = if has_hard_stops {
219 2048.0
220 } else {
221 1024.0
222 };
223
224 let mut task_size: DeviceSize = stretch_size.cast_unit();
228 let mut scale = vec2(1.0, 1.0);
229 if task_size.width > max_size {
230 scale.x = task_size.width / max_size;
231 task_size.width = max_size;
232 }
233 if task_size.height > max_size {
234 scale.y = task_size.height / max_size;
235 task_size.height = max_size;
236 }
237
238 ConicGradientTemplate {
239 common,
240 center: DevicePoint::new(item.center.x, item.center.y),
241 extend_mode: item.extend_mode,
242 params: item.params,
243 stretch_size,
244 task_size: task_size.ceil().to_i32(),
245 scale,
246 tile_spacing: item.tile_spacing.into(),
247 brush_segments,
248 stops_opacity,
249 stops,
250 src_color: None,
251 }
252 }
253}
254
255impl ConicGradientTemplate {
256 pub fn update(
261 &mut self,
262 frame_state: &mut FrameBuildingState,
263 ) {
264 if let Some(mut request) =
265 frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
266 request.push(PremultipliedColorF::WHITE);
268 request.push(PremultipliedColorF::WHITE);
269 request.push([
270 self.stretch_size.width,
271 self.stretch_size.height,
272 0.0,
273 0.0,
274 ]);
275
276 for segment in &self.brush_segments {
278 request.write_segment(
280 segment.local_rect,
281 segment.extra_data,
282 );
283 }
284 }
285
286 let cache_key = ConicGradientCacheKey {
287 size: self.task_size,
288 center: PointKey { x: self.center.x, y: self.center.y },
289 scale: PointKey { x: self.scale.x, y: self.scale.y },
290 start_offset: FloatKey(self.params.start_offset),
291 end_offset: FloatKey(self.params.end_offset),
292 angle: FloatKey(self.params.angle),
293 extend_mode: self.extend_mode,
294 stops: self.stops.iter().map(|stop| (*stop).into()).collect(),
295 };
296
297 let task_id = frame_state.resource_cache.request_render_task(
298 Some(RenderTaskCacheKey {
299 size: self.task_size,
300 kind: RenderTaskCacheKeyKind::ConicGradient(cache_key),
301 }),
302 false,
303 RenderTaskParent::Surface,
304 frame_state.gpu_cache,
305 &mut frame_state.frame_gpu_data.f32,
306 frame_state.rg_builder,
307 &mut frame_state.surface_builder,
308 &mut |rg_builder, gpu_buffer_builder, _| {
309 let stops = GradientGpuBlockBuilder::build(
310 false,
311 gpu_buffer_builder,
312 &self.stops,
313 );
314
315 rg_builder.add().init(RenderTask::new_dynamic(
316 self.task_size,
317 RenderTaskKind::ConicGradient(ConicGradientTask {
318 extend_mode: self.extend_mode,
319 scale: self.scale,
320 center: self.center,
321 params: self.params.clone(),
322 stops,
323 }),
324 ))
325 }
326 );
327
328 self.src_color = Some(task_id);
329
330 self.opacity = self.stops_opacity;
335 }
336}
337
338pub type ConicGradientDataHandle = InternHandle<ConicGradient>;
339
340#[derive(Debug, MallocSizeOf)]
341#[cfg_attr(feature = "capture", derive(Serialize))]
342#[cfg_attr(feature = "replay", derive(Deserialize))]
343pub struct ConicGradient {
344 pub extend_mode: ExtendMode,
345 pub center: PointKey,
346 pub params: ConicGradientParams,
347 pub stretch_size: SizeKey,
348 pub stops: Vec<GradientStopKey>,
349 pub tile_spacing: SizeKey,
350 pub nine_patch: Option<Box<NinePatchDescriptor>>,
351}
352
353impl Internable for ConicGradient {
354 type Key = ConicGradientKey;
355 type StoreData = ConicGradientTemplate;
356 type InternData = ();
357 const PROFILE_COUNTER: usize = crate::profiler::INTERNED_CONIC_GRADIENTS;
358}
359
360impl InternablePrimitive for ConicGradient {
361 fn into_key(
362 self,
363 info: &LayoutPrimitiveInfo,
364 ) -> ConicGradientKey {
365 ConicGradientKey::new(info, self)
366 }
367
368 fn make_instance_kind(
369 _key: ConicGradientKey,
370 data_handle: ConicGradientDataHandle,
371 _prim_store: &mut PrimitiveStore,
372 ) -> PrimitiveInstanceKind {
373 PrimitiveInstanceKind::ConicGradient {
374 data_handle,
375 visible_tiles_range: GradientTileRange::empty(),
376 use_legacy_path: true,
377 }
378 }
379}
380
381impl IsVisible for ConicGradient {
382 fn is_visible(&self) -> bool {
383 true
384 }
385}
386
387#[derive(Debug)]
388#[cfg_attr(feature = "capture", derive(Serialize))]
389#[cfg_attr(feature = "replay", derive(Deserialize))]
390pub struct ConicGradientTask {
391 pub extend_mode: ExtendMode,
392 pub center: DevicePoint,
393 pub scale: DeviceVector2D,
394 pub params: ConicGradientParams,
395 pub stops: GpuBufferAddress,
396}
397
398impl ConicGradientTask {
399 pub fn to_instance(&self, target_rect: &DeviceIntRect) -> ConicGradientInstance {
400 ConicGradientInstance {
401 task_rect: target_rect.to_f32(),
402 center: self.center,
403 scale: self.scale,
404 start_offset: self.params.start_offset,
405 end_offset: self.params.end_offset,
406 angle: self.params.angle,
407 extend_mode: self.extend_mode as i32,
408 gradient_stops_address: self.stops.as_int(),
409 }
410 }
411}
412
413#[cfg_attr(feature = "capture", derive(Serialize))]
417#[cfg_attr(feature = "replay", derive(Deserialize))]
418#[repr(C)]
419#[derive(Clone, Debug)]
420pub struct ConicGradientInstance {
421 pub task_rect: DeviceRect,
422 pub center: DevicePoint,
423 pub scale: DeviceVector2D,
424 pub start_offset: f32,
425 pub end_offset: f32,
426 pub angle: f32,
427 pub extend_mode: i32,
428 pub gradient_stops_address: i32,
429}
430
431#[derive(Clone, Debug, Hash, PartialEq, Eq)]
432#[cfg_attr(feature = "capture", derive(Serialize))]
433#[cfg_attr(feature = "replay", derive(Deserialize))]
434pub struct ConicGradientCacheKey {
435 pub size: DeviceIntSize,
436 pub center: PointKey,
437 pub scale: PointKey,
438 pub start_offset: FloatKey,
439 pub end_offset: FloatKey,
440 pub angle: FloatKey,
441 pub extend_mode: ExtendMode,
442 pub stops: Vec<GradientStopKey>,
443}
444
445pub fn conic_gradient_pattern_with_table(
446 center: DevicePoint,
447 scale: DeviceVector2D,
448 params: &ConicGradientParams,
449 extend_mode: ExtendMode,
450 stops: &[GradientStop],
451 gpu_buffer_builder: &mut GpuBufferBuilder
452) -> Pattern {
453 let mut writer = gpu_buffer_builder.f32.write_blocks(2);
454 writer.push_one([
455 center.x,
456 center.y,
457 scale.x,
458 scale.y,
459 ]);
460 writer.push_one([
461 params.start_offset,
462 params.end_offset,
463 params.angle,
464 if extend_mode == ExtendMode::Repeat { 1.0 } else { 0.0 }
465 ]);
466 let gradient_address = writer.finish();
467
468 let stops_address = GradientGpuBlockBuilder::build(
469 false,
470 &mut gpu_buffer_builder.f32,
471 &stops,
472 );
473
474 let is_opaque = stops.iter().all(|stop| stop.color.a >= 1.0);
475
476 Pattern {
477 kind: PatternKind::ConicGradient,
478 shader_input: PatternShaderInput(
479 gradient_address.as_int(),
480 stops_address.as_int(),
481 ),
482 texture_input: PatternTextureInput::default(),
483 base_color: ColorF::WHITE,
484 is_opaque,
485 }
486}
487
488pub fn conic_gradient_pattern(
489 center: DevicePoint,
490 scale: DeviceVector2D,
491 params: &ConicGradientParams,
492 extend_mode: ExtendMode,
493 stops: &[GradientStop],
494 gpu_buffer_builder: &mut GpuBufferBuilder
495) -> Pattern {
496 let num_blocks = 2 + gpu_gradient_stops_blocks(stops.len(), true);
497 let mut writer = gpu_buffer_builder.f32.write_blocks(num_blocks);
498 writer.push_one([
499 center.x,
500 center.y,
501 scale.x,
502 scale.y,
503 ]);
504 writer.push_one([
505 params.start_offset,
506 params.end_offset,
507 params.angle,
508 0.0,
509 ]);
510 let is_opaque = write_gpu_gradient_stops_tree(stops, GradientKind::Conic, extend_mode, &mut writer);
511 let gradient_address = writer.finish();
512
513 Pattern {
514 kind: PatternKind::Gradient,
515 shader_input: PatternShaderInput(
516 gradient_address.as_int(),
517 0,
518 ),
519 texture_input: PatternTextureInput::default(),
520 base_color: ColorF::WHITE,
521 is_opaque,
522 }
523}