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