1use std::hash::{Hash, Hasher};
2
3use crate::{
4 Brush, Color, ColorFilter, CornerRadii, DrawPrimitive, FxHasher, ImageBitmap, LayerShape,
5 Point, Rect, RenderEffect, RuntimeShader, ShadowPrimitive, Stroke,
6};
7
8pub trait RenderHash {
11 fn render_hash(&self) -> u64;
14}
15
16impl RenderHash for Color {
17 fn render_hash(&self) -> u64 {
18 finish_hash(|state| hash_color(*self, state))
19 }
20}
21
22impl RenderHash for Point {
23 fn render_hash(&self) -> u64 {
24 finish_hash(|state| hash_point(*self, state))
25 }
26}
27
28impl RenderHash for Rect {
29 fn render_hash(&self) -> u64 {
30 finish_hash(|state| hash_rect(*self, state))
31 }
32}
33
34impl RenderHash for CornerRadii {
35 fn render_hash(&self) -> u64 {
36 finish_hash(|state| hash_corner_radii(*self, state))
37 }
38}
39
40impl RenderHash for LayerShape {
41 fn render_hash(&self) -> u64 {
42 finish_hash(|state| hash_layer_shape(*self, state))
43 }
44}
45
46impl RenderHash for Stroke {
47 fn render_hash(&self) -> u64 {
48 finish_hash(|state| hash_stroke(*self, state))
49 }
50}
51
52impl RenderHash for Brush {
53 fn render_hash(&self) -> u64 {
54 finish_hash(|state| hash_brush(self, state))
55 }
56}
57
58impl RenderHash for ColorFilter {
59 fn render_hash(&self) -> u64 {
60 finish_hash(|state| hash_color_filter(*self, state))
61 }
62}
63
64impl RenderHash for ImageBitmap {
65 fn render_hash(&self) -> u64 {
66 finish_hash(|state| self.id().hash(state))
67 }
68}
69
70impl RenderHash for RuntimeShader {
71 fn render_hash(&self) -> u64 {
72 finish_hash(|state| hash_runtime_shader(self, state))
73 }
74}
75
76impl RenderHash for RenderEffect {
77 fn render_hash(&self) -> u64 {
78 finish_hash(|state| hash_render_effect(self, state))
79 }
80}
81
82impl RenderHash for DrawPrimitive {
83 fn render_hash(&self) -> u64 {
84 finish_hash(|state| hash_draw_primitive(self, state))
85 }
86}
87
88impl RenderHash for ShadowPrimitive {
89 fn render_hash(&self) -> u64 {
90 finish_hash(|state| hash_shadow_primitive(self, state))
91 }
92}
93
94fn finish_hash(write: impl FnOnce(&mut FxHasher)) -> u64 {
95 let mut hasher = FxHasher::default();
96 write(&mut hasher);
97 hasher.finish()
98}
99
100fn hash_f32_bits<H: Hasher>(value: f32, state: &mut H) {
101 value.to_bits().hash(state);
102}
103
104fn hash_color<H: Hasher>(color: Color, state: &mut H) {
105 hash_f32_bits(color.0, state);
106 hash_f32_bits(color.1, state);
107 hash_f32_bits(color.2, state);
108 hash_f32_bits(color.3, state);
109}
110
111fn hash_point<H: Hasher>(point: Point, state: &mut H) {
112 hash_f32_bits(point.x, state);
113 hash_f32_bits(point.y, state);
114}
115
116fn hash_rect<H: Hasher>(rect: Rect, state: &mut H) {
117 hash_f32_bits(rect.x, state);
118 hash_f32_bits(rect.y, state);
119 hash_f32_bits(rect.width, state);
120 hash_f32_bits(rect.height, state);
121}
122
123fn hash_corner_radii<H: Hasher>(radii: CornerRadii, state: &mut H) {
124 hash_f32_bits(radii.top_left, state);
125 hash_f32_bits(radii.top_right, state);
126 hash_f32_bits(radii.bottom_right, state);
127 hash_f32_bits(radii.bottom_left, state);
128}
129
130fn hash_stroke<H: Hasher>(stroke: Stroke, state: &mut H) {
134 hash_f32_bits(stroke.width, state);
135 stroke.cap.hash(state);
136 stroke.join.hash(state);
137}
138
139fn hash_optional_stroke<H: Hasher>(stroke: Option<Stroke>, state: &mut H) {
140 match stroke {
141 Some(stroke) => {
142 1u8.hash(state);
143 hash_stroke(stroke, state);
144 }
145 None => 0u8.hash(state),
146 }
147}
148
149fn hash_layer_shape<H: Hasher>(shape: LayerShape, state: &mut H) {
150 match shape {
151 LayerShape::Rectangle => 0u8.hash(state),
152 LayerShape::Rounded(shape) => {
153 1u8.hash(state);
154 hash_corner_radii(shape.radii(), state);
155 }
156 }
157}
158
159fn hash_brush<H: Hasher>(brush: &Brush, state: &mut H) {
160 match brush {
161 Brush::Solid(color) => {
162 0u8.hash(state);
163 hash_color(*color, state);
164 }
165 Brush::LinearGradient {
166 colors,
167 stops,
168 start,
169 end,
170 tile_mode,
171 } => {
172 1u8.hash(state);
173 hash_color_slice(colors, state);
174 hash_optional_stop_list(stops.as_deref(), state);
175 hash_point(*start, state);
176 hash_point(*end, state);
177 tile_mode.hash(state);
178 }
179 Brush::RadialGradient {
180 colors,
181 stops,
182 center,
183 radius,
184 tile_mode,
185 } => {
186 2u8.hash(state);
187 hash_color_slice(colors, state);
188 hash_optional_stop_list(stops.as_deref(), state);
189 hash_point(*center, state);
190 hash_f32_bits(*radius, state);
191 tile_mode.hash(state);
192 }
193 Brush::SweepGradient {
194 colors,
195 stops,
196 center,
197 } => {
198 3u8.hash(state);
199 hash_color_slice(colors, state);
200 hash_optional_stop_list(stops.as_deref(), state);
201 hash_point(*center, state);
202 }
203 }
204}
205
206fn hash_color_slice<H: Hasher>(colors: &[Color], state: &mut H) {
207 colors.len().hash(state);
208 for color in colors {
209 hash_color(*color, state);
210 }
211}
212
213fn hash_optional_stop_list<H: Hasher>(stops: Option<&[f32]>, state: &mut H) {
214 match stops {
215 Some(stops) => {
216 1u8.hash(state);
217 stops.len().hash(state);
218 for stop in stops {
219 hash_f32_bits(*stop, state);
220 }
221 }
222 None => 0u8.hash(state),
223 }
224}
225
226fn hash_color_filter<H: Hasher>(filter: ColorFilter, state: &mut H) {
227 match filter {
228 ColorFilter::Tint(color) => {
229 0u8.hash(state);
230 hash_color(color, state);
231 }
232 ColorFilter::Modulate(color) => {
233 1u8.hash(state);
234 hash_color(color, state);
235 }
236 ColorFilter::Matrix(matrix) => {
237 2u8.hash(state);
238 for value in matrix {
239 hash_f32_bits(value, state);
240 }
241 }
242 }
243}
244
245fn hash_runtime_shader<H: Hasher>(shader: &RuntimeShader, state: &mut H) {
246 shader.source_hash().hash(state);
247 shader.overrides_hash().hash(state);
248 shader.position_independent().hash(state);
249 hash_f32_bits(shader.input_padding(), state);
250 hash_f32_bits(shader.output_padding(), state);
251 for rect in [shader.output_support(), shader.sample_domain()] {
252 match rect {
253 Some(rect) => {
254 1u8.hash(state);
255 hash_f32_bits(rect.x, state);
256 hash_f32_bits(rect.y, state);
257 hash_f32_bits(rect.width, state);
258 hash_f32_bits(rect.height, state);
259 }
260 None => 0u8.hash(state),
261 }
262 }
263 shader.hash_substrates(state);
264 shader.uniforms().len().hash(state);
265 for uniform in shader.uniforms() {
266 hash_f32_bits(*uniform, state);
267 }
268}
269
270fn hash_render_effect<H: Hasher>(effect: &RenderEffect, state: &mut H) {
271 match effect {
272 RenderEffect::Blur {
273 radius_x,
274 radius_y,
275 edge_treatment,
276 } => {
277 0u8.hash(state);
278 hash_f32_bits(*radius_x, state);
279 hash_f32_bits(*radius_y, state);
280 edge_treatment.hash(state);
281 }
282 RenderEffect::Offset { offset_x, offset_y } => {
283 1u8.hash(state);
284 hash_f32_bits(*offset_x, state);
285 hash_f32_bits(*offset_y, state);
286 }
287 RenderEffect::Shader { shader } => {
288 2u8.hash(state);
289 hash_runtime_shader(shader, state);
290 }
291 RenderEffect::Chain { first, second } => {
292 3u8.hash(state);
293 hash_render_effect(first, state);
294 hash_render_effect(second, state);
295 }
296 }
297}
298
299fn hash_draw_primitive<H: Hasher>(primitive: &DrawPrimitive, state: &mut H) {
300 match primitive {
301 DrawPrimitive::Content => {
302 0u8.hash(state);
303 }
304 DrawPrimitive::Blend {
305 primitive,
306 blend_mode,
307 } => {
308 1u8.hash(state);
309 blend_mode.hash(state);
310 hash_draw_primitive(primitive, state);
311 }
312 DrawPrimitive::Rect {
313 rect,
314 brush,
315 stroke,
316 } => {
317 2u8.hash(state);
318 hash_rect(*rect, state);
319 hash_brush(brush, state);
320 hash_optional_stroke(*stroke, state);
321 }
322 DrawPrimitive::RoundRect {
323 rect,
324 brush,
325 radii,
326 stroke,
327 } => {
328 3u8.hash(state);
329 hash_rect(*rect, state);
330 hash_brush(brush, state);
331 hash_corner_radii(*radii, state);
332 hash_optional_stroke(*stroke, state);
333 }
334 DrawPrimitive::Arc {
335 rect,
336 brush,
337 center,
338 radius,
339 start_angle,
340 sweep_angle,
341 stroke,
342 inner_radius,
343 } => {
344 6u8.hash(state);
345 hash_rect(*rect, state);
346 hash_brush(brush, state);
347 hash_point(*center, state);
348 hash_f32_bits(*radius, state);
349 hash_f32_bits(*start_angle, state);
350 hash_f32_bits(*sweep_angle, state);
351 hash_optional_stroke(*stroke, state);
352 hash_f32_bits(*inner_radius, state);
353 }
354 DrawPrimitive::Line {
355 rect,
356 brush,
357 start,
358 end,
359 stroke,
360 } => {
361 9u8.hash(state);
362 hash_rect(*rect, state);
363 hash_brush(brush, state);
364 hash_point(*start, state);
365 hash_point(*end, state);
366 hash_optional_stroke(Some(*stroke), state);
367 }
368 DrawPrimitive::Trapezoid {
369 rect,
370 brush,
371 trapezoid,
372 } => {
373 10u8.hash(state);
374 hash_rect(*rect, state);
375 hash_brush(brush, state);
376 for value in [trapezoid.left, trapezoid.right]
377 .into_iter()
378 .chain(trapezoid.top)
379 .chain(trapezoid.bottom)
380 {
381 hash_f32_bits(value, state);
382 }
383 [trapezoid.open_left, trapezoid.open_right].hash(state);
384 }
385 DrawPrimitive::Image {
386 rect,
387 image,
388 alpha,
389 color_filter,
390 sampling,
391 src_rect,
392 } => {
393 4u8.hash(state);
394 hash_rect(*rect, state);
395 image.id().hash(state);
396 hash_f32_bits(*alpha, state);
397 sampling.hash(state);
398 match color_filter {
399 Some(filter) => {
400 1u8.hash(state);
401 hash_color_filter(*filter, state);
402 }
403 None => 0u8.hash(state),
404 }
405 match src_rect {
406 Some(rect) => {
407 1u8.hash(state);
408 hash_rect(*rect, state);
409 }
410 None => 0u8.hash(state),
411 }
412 }
413 DrawPrimitive::Text(text) => {
414 7u8.hash(state);
415 hash_rect(text.rect, state);
416 text.text.hash(state);
417 hash_text_style(&text.style, state);
418 hash_color(text.color, state);
419 }
420 DrawPrimitive::Shadow(shadow) => {
421 5u8.hash(state);
422 hash_shadow_primitive(shadow, state);
423 }
424 }
425}
426
427fn hash_text_style<H: Hasher>(style: &crate::DrawTextStyle, state: &mut H) {
428 style.font_family.hash(state);
429 hash_f32_bits(style.font_size, state);
430 style.font_weight.hash(state);
431 style.font_style.hash(state);
432 hash_f32_bits(style.letter_spacing, state);
433 match style.line_height {
434 Some(line_height) => {
435 1u8.hash(state);
436 hash_f32_bits(line_height, state);
437 }
438 None => 0u8.hash(state),
439 }
440 style.align.hash(state);
441 style.vertical_align.hash(state);
442}
443
444fn hash_shadow_primitive<H: Hasher>(shadow: &ShadowPrimitive, state: &mut H) {
445 match shadow {
446 ShadowPrimitive::Drop {
447 shape,
448 cutout,
449 blur_radius,
450 blend_mode,
451 } => {
452 0u8.hash(state);
453 hash_draw_primitive(shape, state);
454 match cutout {
455 Some(cutout) => {
456 1u8.hash(state);
457 hash_draw_primitive(cutout, state);
458 }
459 None => 0u8.hash(state),
460 }
461 hash_f32_bits(*blur_radius, state);
462 blend_mode.hash(state);
463 }
464 ShadowPrimitive::Inner {
465 fill,
466 cutout,
467 blur_radius,
468 blend_mode,
469 clip_rect,
470 } => {
471 1u8.hash(state);
472 hash_draw_primitive(fill, state);
473 hash_draw_primitive(cutout, state);
474 hash_f32_bits(*blur_radius, state);
475 blend_mode.hash(state);
476 hash_rect(*clip_rect, state);
477 }
478 }
479}
480
481#[cfg(test)]
482#[path = "tests/render_hash_tests.rs"]
483mod tests;