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