1use std::{
7 f32::consts::{FRAC_1_SQRT_2, PI, SQRT_2},
8 iter::{self, zip},
9 ops,
10};
11
12use crate::{
13 bit_reader::BitReader,
14 entropy_coding::decode::{Histograms, SymbolReader, unpack_signed},
15 error::{Error, Result},
16 frame::color_correlation_map::ColorCorrelationParams,
17 util::{
18 CeilLog2, MemoryTracker, NewWithCapacity, fast_cos, fast_erff_simd, tracing_wrappers::*,
19 },
20};
21use jxl_simd::{F32SimdVec, ScalarDescriptor, SimdDescriptor, simd_function};
22const MAX_NUM_CONTROL_POINTS: u32 = 1 << 20;
23const MAX_NUM_CONTROL_POINTS_PER_PIXEL_RATIO: u32 = 2;
24const DELTA_LIMIT: i64 = 1 << 30;
25const SPLINE_POS_LIMIT: isize = 1 << 23;
26
27const QUANTIZATION_ADJUSTMENT_CONTEXT: usize = 0;
28const STARTING_POSITION_CONTEXT: usize = 1;
29const NUM_SPLINES_CONTEXT: usize = 2;
30const NUM_CONTROL_POINTS_CONTEXT: usize = 3;
31const CONTROL_POINTS_CONTEXT: usize = 4;
32const DCT_CONTEXT: usize = 5;
33const NUM_SPLINE_CONTEXTS: usize = 6;
34const DESIRED_RENDERING_DISTANCE: f32 = 1.0;
35
36#[derive(Debug, Clone, Copy, Default)]
37pub struct Point {
38 pub x: f32,
39 pub y: f32,
40}
41
42impl Point {
43 fn new(x: f32, y: f32) -> Self {
44 Point { x, y }
45 }
46 fn abs(&self) -> f32 {
47 self.x.hypot(self.y)
48 }
49}
50
51impl PartialEq for Point {
52 fn eq(&self, other: &Self) -> bool {
53 (self.x - other.x).abs() < 1e-3 && (self.y - other.y).abs() < 1e-3
54 }
55}
56
57impl ops::Add<Point> for Point {
58 type Output = Point;
59 fn add(self, rhs: Point) -> Point {
60 Point {
61 x: self.x + rhs.x,
62 y: self.y + rhs.y,
63 }
64 }
65}
66
67impl ops::Sub<Point> for Point {
68 type Output = Point;
69 fn sub(self, rhs: Point) -> Point {
70 Point {
71 x: self.x - rhs.x,
72 y: self.y - rhs.y,
73 }
74 }
75}
76
77impl ops::Mul<f32> for Point {
78 type Output = Point;
79 fn mul(self, rhs: f32) -> Point {
80 Point {
81 x: self.x * rhs,
82 y: self.y * rhs,
83 }
84 }
85}
86
87impl ops::Div<f32> for Point {
88 type Output = Point;
89 fn div(self, rhs: f32) -> Point {
90 let inv = 1.0 / rhs;
91 Point {
92 x: self.x * inv,
93 y: self.y * inv,
94 }
95 }
96}
97
98#[derive(Default, Debug)]
99pub struct Spline {
100 control_points: Vec<Point>,
101 color_dct: [Dct32; 3],
103 sigma_dct: Dct32,
106 estimated_area_reached: u64,
108}
109
110impl Spline {
111 pub fn validate_adjacent_point_coincidence(&self) -> Result<()> {
112 if let Some(((index, p0), p1)) = zip(
113 self.control_points
114 .iter()
115 .take(self.control_points.len() - 1)
116 .enumerate(),
117 self.control_points.iter().skip(1),
118 )
119 .find(|((_, p0), p1)| **p0 == **p1)
120 {
121 return Err(Error::SplineAdjacentCoincidingControlPoints(
122 index,
123 *p0,
124 index + 1,
125 *p1,
126 ));
127 }
128 Ok(())
129 }
130}
131
132#[derive(Debug, Default, Clone)]
133pub struct QuantizedSpline {
134 pub control_points: Vec<(i64, i64)>,
136 pub color_dct: [[i32; 32]; 3],
137 pub sigma_dct: [i32; 32],
138}
139
140fn inv_adjusted_quant(adjustment: i32) -> f32 {
141 if adjustment >= 0 {
142 1.0 / (1.0 + 0.125 * adjustment as f32)
143 } else {
144 1.0 - 0.125 * adjustment as f32
145 }
146}
147
148fn validate_spline_point_pos<T: num_traits::ToPrimitive>(x: T, y: T) -> Result<()> {
149 let xi = x.to_i32().unwrap();
150 let yi = y.to_i32().unwrap();
151 let ok_range = -(1i32 << 23)..(1i32 << 23);
152 if !ok_range.contains(&xi) {
153 return Err(Error::SplinesPointOutOfRange(
154 Point {
155 x: xi as f32,
156 y: yi as f32,
157 },
158 xi,
159 ok_range,
160 ));
161 }
162 if !ok_range.contains(&yi) {
163 return Err(Error::SplinesPointOutOfRange(
164 Point {
165 x: xi as f32,
166 y: yi as f32,
167 },
168 yi,
169 ok_range,
170 ));
171 }
172 Ok(())
173}
174
175const CHANNEL_WEIGHT: [f32; 4] = [0.0042, 0.075, 0.07, 0.3333];
176
177fn area_limit(image_size: u64) -> u64 {
178 1024u64
180 .saturating_mul(image_size)
181 .saturating_add(1u64 << 32)
182 .min(1u64 << 42)
183}
184
185impl QuantizedSpline {
186 #[instrument(level = "debug", skip(br), ret, err)]
187 pub fn read(
188 br: &mut BitReader,
189 splines_histograms: &Histograms,
190 splines_reader: &mut SymbolReader,
191 max_control_points: u32,
192 total_num_control_points: &mut u32,
193 ) -> Result<QuantizedSpline> {
194 let num_control_points =
195 splines_reader.read_unsigned(splines_histograms, br, NUM_CONTROL_POINTS_CONTEXT);
196 *total_num_control_points += num_control_points;
197 if *total_num_control_points > max_control_points {
198 return Err(Error::SplinesTooManyControlPoints(
199 *total_num_control_points,
200 max_control_points,
201 ));
202 }
203 let mut control_points = Vec::new_with_capacity(num_control_points as usize)?;
204 for _ in 0..num_control_points {
205 let x =
206 splines_reader.read_signed(splines_histograms, br, CONTROL_POINTS_CONTEXT) as i64;
207 let y =
208 splines_reader.read_signed(splines_histograms, br, CONTROL_POINTS_CONTEXT) as i64;
209 control_points.push((x, y));
210 let max_delta_delta = x.abs().max(y.abs());
212 if max_delta_delta >= DELTA_LIMIT {
213 return Err(Error::SplinesDeltaLimit(max_delta_delta, DELTA_LIMIT));
214 }
215 }
216 let mut color_dct = [[0; 32]; 3];
218 let mut sigma_dct = [0; 32];
219
220 let mut decode_dct = |dct: &mut [i32; 32]| -> Result<()> {
221 for value in dct.iter_mut() {
222 *value = splines_reader.read_signed(splines_histograms, br, DCT_CONTEXT);
223 }
224 Ok(())
225 };
226
227 for channel in &mut color_dct {
228 decode_dct(channel)?;
229 }
230 decode_dct(&mut sigma_dct)?;
231
232 Ok(QuantizedSpline {
233 control_points,
234 color_dct,
235 sigma_dct,
236 })
237 }
238
239 pub fn dequantize(
240 &self,
241 starting_point: &Point,
242 quantization_adjustment: i32,
243 y_to_x: f32,
244 y_to_b: f32,
245 image_size: u64,
246 ) -> Result<Spline> {
247 let area_limit = area_limit(image_size);
248
249 let mut result = Spline {
250 control_points: Vec::new_with_capacity(self.control_points.len() + 1)?,
251 ..Default::default()
252 };
253
254 let px = starting_point.x.round();
255 let py = starting_point.y.round();
256 validate_spline_point_pos(px, py)?;
257
258 let mut current_x = px as i32;
259 let mut current_y = py as i32;
260 result
261 .control_points
262 .push(Point::new(current_x as f32, current_y as f32));
263
264 let mut current_delta_x = 0i32;
265 let mut current_delta_y = 0i32;
266 let mut manhattan_distance = 0u64;
267
268 for &(dx, dy) in &self.control_points {
269 current_delta_x += dx as i32;
270 current_delta_y += dy as i32;
271 validate_spline_point_pos(current_delta_x, current_delta_y)?;
272
273 manhattan_distance +=
274 current_delta_x.unsigned_abs() as u64 + current_delta_y.unsigned_abs() as u64;
275
276 if manhattan_distance > area_limit {
277 return Err(Error::SplinesDistanceTooLarge(
278 manhattan_distance,
279 area_limit,
280 ));
281 }
282
283 current_x += current_delta_x;
284 current_y += current_delta_y;
285 validate_spline_point_pos(current_x, current_y)?;
286
287 result
288 .control_points
289 .push(Point::new(current_x as f32, current_y as f32));
290 }
291
292 let inv_quant = inv_adjusted_quant(quantization_adjustment);
293
294 for (c, weight) in CHANNEL_WEIGHT.iter().enumerate().take(3) {
295 for i in 0..32 {
296 let inv_dct_factor = if i == 0 { FRAC_1_SQRT_2 } else { 1.0 };
297 result.color_dct[c].0[i] =
298 self.color_dct[c][i] as f32 * inv_dct_factor * weight * inv_quant;
299 }
300 }
301
302 for i in 0..32 {
303 result.color_dct[0].0[i] += y_to_x * result.color_dct[1].0[i];
304 result.color_dct[2].0[i] += y_to_b * result.color_dct[1].0[i];
305 }
306
307 let mut width_estimate = 0;
308 let mut color = [0u64; 3];
309
310 for (c, color_val) in color.iter_mut().enumerate() {
311 for i in 0..32 {
312 *color_val += (inv_quant * self.color_dct[c][i].abs() as f32).ceil() as u64;
313 }
314 }
315
316 color[0] += y_to_x.abs().ceil() as u64 * color[1];
317 color[2] += y_to_b.abs().ceil() as u64 * color[1];
318
319 let max_color = color[0].max(color[1]).max(color[2]);
320 let logcolor = 1u64.max((1u64 + max_color).ceil_log2());
321
322 let weight_limit =
323 (((area_limit as f32 / logcolor as f32) / manhattan_distance.max(1) as f32).sqrt())
324 .ceil();
325
326 for i in 0..32 {
327 let inv_dct_factor = if i == 0 { FRAC_1_SQRT_2 } else { 1.0 };
328 result.sigma_dct.0[i] =
329 self.sigma_dct[i] as f32 * inv_dct_factor * CHANNEL_WEIGHT[3] * inv_quant;
330
331 let weight_f = (inv_quant * self.sigma_dct[i].abs() as f32).ceil();
332 let weight = weight_limit.min(weight_f.max(1.0)) as u64;
333 width_estimate += weight * weight * logcolor;
334 }
335
336 result.estimated_area_reached = width_estimate * manhattan_distance;
337
338 Ok(result)
339 }
340}
341
342#[derive(Debug, Clone, Copy, Default)]
343struct SplineSegment {
344 center_x: f32,
345 center_y: f32,
346 maximum_distance: f32,
347 inv_sigma: f32,
348 sigma_over_4_times_intensity: f32,
349 color: [f32; 3],
350}
351
352#[derive(Debug, Default, Clone)]
353pub struct Splines {
354 pub quantization_adjustment: i32,
355 pub splines: Vec<QuantizedSpline>,
356 pub starting_points: Vec<Point>,
357 segments: Vec<SplineSegment>,
358 segment_indices: Vec<usize>,
359 segment_y_start: Vec<u64>,
360}
361
362fn draw_centripetal_catmull_rom_spline(points: &[Point]) -> Result<Vec<Point>> {
363 if points.is_empty() {
364 return Ok(vec![]);
365 }
366 if points.len() == 1 {
367 return Ok(vec![points[0]]);
368 }
369 const NUM_POINTS: usize = 16;
370 let extended_points = iter::once(points[0] + (points[0] - points[1]))
372 .chain(points.iter().cloned())
373 .chain(iter::once(
374 points[points.len() - 1] + (points[points.len() - 1] - points[points.len() - 2]),
375 ));
376 let points_and_deltas = extended_points
378 .chain(iter::once(Point::default()))
379 .scan(Point::default(), |previous, p| {
380 let result = Some((*previous, (p - *previous).abs().sqrt()));
381 *previous = p;
382 result
383 })
384 .skip(1);
385 let windowed_points = points_and_deltas
387 .scan([(Point::default(), 0.0); 4], |window, p| {
388 (window[0], window[1], window[2], window[3]) =
389 (window[1], window[2], window[3], (p.0, p.1));
390 Some([window[0], window[1], window[2], window[3]])
391 })
392 .skip(3);
393 let result = windowed_points
395 .flat_map(|p| {
396 let mut window_result = [Point::default(); NUM_POINTS];
397 window_result[0] = p[1].0;
398 let mut t = [0.0; 4];
399 for k in 0..3 {
400 t[k + 1] = t[k] + p[k].1;
402 }
403 for (i, window_point) in window_result.iter_mut().enumerate().skip(1) {
404 let tt = p[0].1 + ((i as f32) / (NUM_POINTS as f32)) * p[1].1;
405 let mut a = [Point::default(); 3];
406 for k in 0..3 {
407 a[k] = p[k].0 + (p[k + 1].0 - p[k].0) * ((tt - t[k]) / p[k].1);
409 }
410 let mut b = [Point::default(); 2];
411 for k in 0..2 {
412 b[k] = a[k] + (a[k + 1] - a[k]) * ((tt - t[k]) / (p[k].1 + p[k + 1].1));
413 }
414 *window_point = b[0] + (b[1] - b[0]) * ((tt - t[1]) / p[1].1);
415 }
416 window_result
417 })
418 .chain(iter::once(points[points.len() - 1]))
419 .collect();
420 Ok(result)
421}
422
423fn for_each_equally_spaced_point<F: FnMut(Point, f32)>(
424 points: &[Point],
425 desired_distance: f32,
426 mut f: F,
427) {
428 if points.is_empty() {
429 return;
430 }
431 let mut accumulated_distance = 0.0;
432 f(points[0], desired_distance);
433 if points.len() == 1 {
434 return;
435 }
436 for index in 0..(points.len() - 1) {
437 let mut current = points[index];
438 let next = points[index + 1];
439 let segment = next - current;
440 let segment_length = segment.abs();
441 let unit_step = segment / segment_length;
442 if accumulated_distance + segment_length >= desired_distance {
443 current = current + unit_step * (desired_distance - accumulated_distance);
444 f(current, desired_distance);
445 accumulated_distance -= desired_distance;
446 }
447 accumulated_distance += segment_length;
448 while accumulated_distance >= desired_distance {
449 current = current + unit_step * desired_distance;
450 f(current, desired_distance);
451 accumulated_distance -= desired_distance;
452 }
453 }
454 f(points[points.len() - 1], accumulated_distance);
455}
456
457const DCT_MULTIPLIERS: [f32; 32] = [
459 PI / 32.0 * 0.0,
460 PI / 32.0 * 1.0,
461 PI / 32.0 * 2.0,
462 PI / 32.0 * 3.0,
463 PI / 32.0 * 4.0,
464 PI / 32.0 * 5.0,
465 PI / 32.0 * 6.0,
466 PI / 32.0 * 7.0,
467 PI / 32.0 * 8.0,
468 PI / 32.0 * 9.0,
469 PI / 32.0 * 10.0,
470 PI / 32.0 * 11.0,
471 PI / 32.0 * 12.0,
472 PI / 32.0 * 13.0,
473 PI / 32.0 * 14.0,
474 PI / 32.0 * 15.0,
475 PI / 32.0 * 16.0,
476 PI / 32.0 * 17.0,
477 PI / 32.0 * 18.0,
478 PI / 32.0 * 19.0,
479 PI / 32.0 * 20.0,
480 PI / 32.0 * 21.0,
481 PI / 32.0 * 22.0,
482 PI / 32.0 * 23.0,
483 PI / 32.0 * 24.0,
484 PI / 32.0 * 25.0,
485 PI / 32.0 * 26.0,
486 PI / 32.0 * 27.0,
487 PI / 32.0 * 28.0,
488 PI / 32.0 * 29.0,
489 PI / 32.0 * 30.0,
490 PI / 32.0 * 31.0,
491];
492
493struct PrecomputedCosines([f32; 32]);
496
497impl PrecomputedCosines {
498 #[inline]
501 fn new(t: f32) -> Self {
502 let tandhalf = t + 0.5;
503 PrecomputedCosines(core::array::from_fn(|i| {
504 fast_cos(DCT_MULTIPLIERS[i] * tandhalf)
505 }))
506 }
507}
508
509#[derive(Default, Clone, Copy, Debug)]
510struct Dct32([f32; 32]);
511
512impl Dct32 {
513 #[inline]
516 fn continuous_idct_fast(&self, precomputed: &PrecomputedCosines) -> f32 {
517 zip(self.0, precomputed.0)
520 .map(|(coeff, cos)| coeff * cos)
521 .sum::<f32>()
522 * SQRT_2
523 }
524}
525
526#[inline(always)]
527fn draw_segment_inner<D: SimdDescriptor>(
528 d: D,
529 row: &mut [&mut [f32]],
530 row_pos: (usize, usize),
531 x_range: (usize, usize),
532 segment: &SplineSegment,
533) -> usize {
534 let (x_start, x_end) = x_range;
535 let (row_x0, y) = row_pos;
536 let len = D::F32Vec::LEN;
537 if x_start + len > x_end {
538 return x_start;
539 }
540
541 let inv_sigma = D::F32Vec::splat(d, segment.inv_sigma);
542 let half = D::F32Vec::splat(d, 0.5);
543 let one_over_2s2 = D::F32Vec::splat(d, 0.353_553_38);
544 let sigma_over_4_times_intensity = D::F32Vec::splat(d, segment.sigma_over_4_times_intensity);
545 let center_x = D::F32Vec::splat(d, segment.center_x);
546 let center_y = D::F32Vec::splat(d, segment.center_y);
547 let dy = D::F32Vec::splat(d, y as f32) - center_y;
548 let dy2 = dy * dy;
549
550 let mut x_base_arr = [0.0f32; 16];
551 for (i, val) in x_base_arr.iter_mut().enumerate() {
552 *val = i as f32;
553 }
554 let vx_base = D::F32Vec::load(d, &x_base_arr);
555
556 let start_offset = x_start - row_x0;
557 let end_offset = x_end - row_x0;
558
559 let [r0, r1, r2] = row else { unreachable!() };
560
561 let mut it0 = r0[start_offset..end_offset].chunks_exact_mut(len);
562 let mut it1 = r1[start_offset..end_offset].chunks_exact_mut(len);
563 let mut it2 = r2[start_offset..end_offset].chunks_exact_mut(len);
564
565 let cm0 = D::F32Vec::splat(d, segment.color[0]);
566 let cm1 = D::F32Vec::splat(d, segment.color[1]);
567 let cm2 = D::F32Vec::splat(d, segment.color[2]);
568
569 let num_chunks = (end_offset - start_offset) / len;
570 let mut x = x_start;
571 for _ in 0..num_chunks {
572 let vx = D::F32Vec::splat(d, x as f32) + vx_base;
573 let dx = vx - center_x;
574 let sqd = dx.mul_add(dx, dy2);
575 let distance = sqd.sqrt();
576
577 let arg1 = distance.mul_add(half, one_over_2s2) * inv_sigma;
578 let arg2 = distance.mul_add(half, D::F32Vec::splat(d, -0.353_553_38)) * inv_sigma;
579 let one_dimensional_factor = fast_erff_simd(d, arg1) - fast_erff_simd(d, arg2);
580 let local_intensity =
581 sigma_over_4_times_intensity * one_dimensional_factor * one_dimensional_factor;
582
583 let c0 = it0.next().unwrap();
584 cm0.mul_add(local_intensity, D::F32Vec::load(d, c0))
585 .store(c0);
586 let c1 = it1.next().unwrap();
587 cm1.mul_add(local_intensity, D::F32Vec::load(d, c1))
588 .store(c1);
589 let c2 = it2.next().unwrap();
590 cm2.mul_add(local_intensity, D::F32Vec::load(d, c2))
591 .store(c2);
592
593 x += len;
594 }
595 x
596}
597
598simd_function!(
599 draw_segment_dispatch,
600 d: D,
601 fn draw_segment_simd(
602 row: &mut [&mut [f32]],
603 row_pos: (usize, usize),
604 xsize: usize,
605 segment: &SplineSegment,
606 ) {
607 let (x0, y) = row_pos;
608 let x1 = x0 + xsize;
609 let clamped_x0 = x0.max((segment.center_x - segment.maximum_distance).round() as usize);
610 let clamped_x1 = x1.min((segment.center_x + segment.maximum_distance).round() as usize + 1);
611
612 if clamped_x1 <= clamped_x0 {
613 return;
614 }
615
616 let x = clamped_x0;
617 let x = draw_segment_inner(d, row, (x0, y), (x, clamped_x1), segment);
618 let d = d.maybe_downgrade_256bit();
619 let x = draw_segment_inner(d, row, (x0, y), (x, clamped_x1), segment);
620 let d = d.maybe_downgrade_128bit();
621 let x = draw_segment_inner(d, row, (x0, y), (x, clamped_x1), segment);
622 draw_segment_inner(ScalarDescriptor, row, (x0, y), (x, clamped_x1), segment);
623 }
624);
625
626impl Splines {
627 #[cfg(test)]
628 pub fn create(
629 quantization_adjustment: i32,
630 splines: Vec<QuantizedSpline>,
631 starting_points: Vec<Point>,
632 ) -> Splines {
633 Splines {
634 quantization_adjustment,
635 splines,
636 starting_points,
637 segments: vec![],
638 segment_indices: vec![],
639 segment_y_start: vec![],
640 }
641 }
642 pub fn draw_segments(&self, row: &mut [&mut [f32]], row_pos: (usize, usize), xsize: usize) {
643 let first_segment_index_pos = self.segment_y_start[row_pos.1];
644 let last_segment_index_pos = self.segment_y_start[row_pos.1 + 1];
645 for segment_index_pos in first_segment_index_pos..last_segment_index_pos {
646 draw_segment_dispatch(
647 row,
648 row_pos,
649 xsize,
650 &self.segments[self.segment_indices[segment_index_pos as usize]],
651 );
652 }
653 }
654
655 fn add_segment(
656 &mut self,
657 center: &Point,
658 intensity: f32,
659 color: [f32; 3],
660 sigma: f32,
661 high_precision: bool,
662 segments_by_y: &mut Vec<(u64, usize)>,
663 ) {
664 if sigma.is_infinite()
665 || sigma == 0.0
666 || (1.0 / sigma).is_infinite()
667 || intensity.is_infinite()
668 {
669 return;
670 }
671 let distance_exp: f32 = if high_precision { 5.0 } else { 3.0 };
672 let max_color = [0.01, color[0], color[1], color[2]]
673 .iter()
674 .map(|chan| (chan * intensity).abs())
675 .max_by(|a, b| a.total_cmp(b))
676 .unwrap();
677 let max_distance =
678 (-2.0 * sigma * sigma * (0.1f32.ln() * distance_exp - max_color.ln())).sqrt();
679 let segment = SplineSegment {
680 center_x: center.x,
681 center_y: center.y,
682 color,
683 inv_sigma: 1.0 / sigma,
684 sigma_over_4_times_intensity: 0.25 * sigma * intensity,
685 maximum_distance: max_distance,
686 };
687 let y0 = (center.y - max_distance).round() as i64;
688 let y1 = (center.y + max_distance).round() as i64 + 1;
689 for y in 0.max(y0)..y1 {
690 segments_by_y.push((y as u64, self.segments.len()));
691 }
692 self.segments.push(segment);
693 }
694
695 fn add_segments_from_points(
696 &mut self,
697 spline: &Spline,
698 points_to_draw: &[(Point, f32)],
699 length: f32,
700 desired_distance: f32,
701 high_precision: bool,
702 segments_by_y: &mut Vec<(u64, usize)>,
703 ) {
704 let inv_length = 1.0 / length;
705 for (point_index, (point, multiplier)) in points_to_draw.iter().enumerate() {
706 let progress = (point_index as f32 * desired_distance * inv_length).min(1.0);
707 let t = (32.0 - 1.0) * progress;
708
709 let precomputed = PrecomputedCosines::new(t);
711
712 let mut color = [0.0; 3];
714 for (index, coeffs) in spline.color_dct.iter().enumerate() {
715 color[index] = coeffs.continuous_idct_fast(&precomputed);
716 }
717 let sigma = spline.sigma_dct.continuous_idct_fast(&precomputed);
718
719 self.add_segment(
720 point,
721 *multiplier,
722 color,
723 sigma,
724 high_precision,
725 segments_by_y,
726 );
727 }
728 }
729
730 pub fn initialize_draw_cache(
731 &mut self,
732 image_xsize: u64,
733 image_ysize: u64,
734 color_correlation_params: &ColorCorrelationParams,
735 high_precision: bool,
736 ) -> Result<()> {
737 let mut total_estimated_area_reached = 0u64;
738 let mut splines = Vec::new();
739 let image_area = image_xsize.saturating_mul(image_ysize);
741 let area_limit = area_limit(image_area);
742 for (index, qspline) in self.splines.iter().enumerate() {
743 let spline = qspline.dequantize(
744 &self.starting_points[index],
745 self.quantization_adjustment,
746 color_correlation_params.y_to_x_lf(),
747 color_correlation_params.y_to_b_lf(),
748 image_area,
749 )?;
750 total_estimated_area_reached += spline.estimated_area_reached;
751 if total_estimated_area_reached > area_limit {
752 return Err(Error::SplinesAreaTooLarge(
753 total_estimated_area_reached,
754 area_limit,
755 ));
756 }
757 spline.validate_adjacent_point_coincidence()?;
758 splines.push(spline);
759 }
760
761 if total_estimated_area_reached
762 > (8 * image_xsize * image_ysize + (1u64 << 25)).min(1u64 << 30)
763 {
764 warn!(
765 "Large total_estimated_area_reached, expect slower decoding:{}",
766 total_estimated_area_reached
767 );
768 }
769
770 let mut segments_by_y = Vec::new();
771
772 self.segments.clear();
773 for spline in splines {
774 let mut points_to_draw = Vec::<(Point, f32)>::new();
775 let intermediate_points = draw_centripetal_catmull_rom_spline(&spline.control_points)?;
776 for_each_equally_spaced_point(
777 &intermediate_points,
778 DESIRED_RENDERING_DISTANCE,
779 |p, d| points_to_draw.push((p, d)),
780 );
781 let length = (points_to_draw.len() as isize - 2) as f32 * DESIRED_RENDERING_DISTANCE
782 + points_to_draw[points_to_draw.len() - 1].1;
783 if length <= 0.0 {
784 continue;
785 }
786 self.add_segments_from_points(
787 &spline,
788 &points_to_draw,
789 length,
790 DESIRED_RENDERING_DISTANCE,
791 high_precision,
792 &mut segments_by_y,
793 );
794 }
795
796 segments_by_y.sort_by_key(|segment| segment.0);
798
799 self.segment_indices.clear();
800 self.segment_indices.try_reserve(segments_by_y.len())?;
801 self.segment_indices.resize(segments_by_y.len(), 0);
802
803 self.segment_y_start.clear();
804 self.segment_y_start.try_reserve(image_ysize as usize + 1)?;
805 self.segment_y_start.resize(image_ysize as usize + 1, 0);
806
807 for (i, segment) in segments_by_y.iter().enumerate() {
808 self.segment_indices[i] = segment.1;
809 let y = segment.0;
810 if y < image_ysize {
811 self.segment_y_start[y as usize + 1] += 1;
812 }
813 }
814 for y in 0..image_ysize {
815 self.segment_y_start[y as usize + 1] += self.segment_y_start[y as usize];
816 }
817 Ok(())
818 }
819
820 #[instrument(level = "debug", skip(br, memory_tracker), ret, err)]
821 pub fn read(
822 br: &mut BitReader,
823 num_pixels: u32,
824 max_spline_points: Option<u32>,
825 memory_tracker: &MemoryTracker,
826 ) -> Result<Splines> {
827 trace!(pos = br.total_bits_read());
828 let splines_histograms = Histograms::decode(NUM_SPLINE_CONTEXTS, br, true)?;
829 let mut splines_reader = SymbolReader::new(&splines_histograms, br, None)?;
830 let num_splines = splines_reader
831 .read_unsigned(&splines_histograms, br, NUM_SPLINES_CONTEXT)
832 .saturating_add(1);
833 let hard_limit = max_spline_points.unwrap_or(MAX_NUM_CONTROL_POINTS);
835 let max_control_points =
836 hard_limit.min(num_pixels / MAX_NUM_CONTROL_POINTS_PER_PIXEL_RATIO);
837 if num_splines > max_control_points {
838 return Err(Error::SplinesTooMany(num_splines, max_control_points));
839 }
840
841 let mut starting_points = Vec::new();
842 let mut last_x = 0;
843 let mut last_y = 0;
844 for i in 0..num_splines {
845 let unsigned_x =
846 splines_reader.read_unsigned(&splines_histograms, br, STARTING_POSITION_CONTEXT);
847 let unsigned_y =
848 splines_reader.read_unsigned(&splines_histograms, br, STARTING_POSITION_CONTEXT);
849
850 let (x, y) = if i != 0 {
851 (
852 unpack_signed(unsigned_x) as isize + last_x,
853 unpack_signed(unsigned_y) as isize + last_y,
854 )
855 } else {
856 (unsigned_x as isize, unsigned_y as isize)
857 };
858 let max_coordinate = x.abs().max(y.abs());
860 if max_coordinate >= SPLINE_POS_LIMIT {
861 return Err(Error::SplinesCoordinatesLimit(
862 max_coordinate,
863 SPLINE_POS_LIMIT,
864 ));
865 }
866
867 starting_points.push(Point {
868 x: x as f32,
869 y: y as f32,
870 });
871
872 last_x = x;
873 last_y = y;
874 }
875
876 let quantization_adjustment =
877 splines_reader.read_signed(&splines_histograms, br, QUANTIZATION_ADJUSTMENT_CONTEXT);
878
879 memory_tracker
881 .check_alloc(max_control_points as u64 * std::mem::size_of::<Point>() as u64 * 2)?;
882 let mut splines = Vec::new();
883 let mut num_control_points = 0u32;
884 for _ in 0..num_splines {
885 splines.push(QuantizedSpline::read(
886 br,
887 &splines_histograms,
888 &mut splines_reader,
889 max_control_points,
890 &mut num_control_points,
891 )?);
892 }
893 splines_reader.check_final_state(&splines_histograms, br)?;
894 Ok(Splines {
895 quantization_adjustment,
896 splines,
897 starting_points,
898 ..Splines::default()
899 })
900 }
901}
902
903#[cfg(test)]
904#[allow(clippy::excessive_precision)]
905mod test_splines {
906 use std::{f32::consts::SQRT_2, iter::zip};
907 use test_log::test;
908
909 use crate::{
910 error::{Error, Result},
911 features::spline::SplineSegment,
912 frame::color_correlation_map::ColorCorrelationParams,
913 util::test::{assert_all_almost_abs_eq, assert_almost_abs_eq, assert_almost_eq},
914 };
915
916 use super::{
917 DCT_MULTIPLIERS, DESIRED_RENDERING_DISTANCE, Dct32, Point, PrecomputedCosines,
918 QuantizedSpline, Spline, Splines, draw_centripetal_catmull_rom_spline,
919 for_each_equally_spaced_point,
920 };
921 use crate::util::fast_cos;
922
923 impl Dct32 {
924 fn continuous_idct(&self, t: f32) -> f32 {
926 let tandhalf = t + 0.5;
927 zip(DCT_MULTIPLIERS, self.0)
928 .map(|(multiplier, coeff)| SQRT_2 * coeff * fast_cos(multiplier * tandhalf))
929 .sum()
930 }
931 }
932
933 #[test]
934 fn dequantize() -> Result<(), Error> {
935 let quantized_and_dequantized = [
937 (
938 QuantizedSpline {
939 control_points: vec![
940 (109, 105),
941 (-247, -261),
942 (168, 427),
943 (-46, -360),
944 (-61, 181),
945 ],
946 color_dct: [
947 [
948 12223, 9452, 5524, 16071, 1048, 17024, 14833, 7690, 21952, 2405, 2571,
949 2190, 1452, 2500, 18833, 1667, 5857, 21619, 1310, 20000, 10429, 11667,
950 7976, 18786, 12976, 18548, 14786, 12238, 8667, 3405, 19929, 8429,
951 ],
952 [
953 177, 712, 127, 999, 969, 356, 105, 12, 1132, 309, 353, 415, 1213, 156,
954 988, 524, 316, 1100, 64, 36, 816, 1285, 183, 889, 839, 1099, 79, 1316,
955 287, 105, 689, 841,
956 ],
957 [
958 780, -201, -38, -695, -563, -293, -88, 1400, -357, 520, 979, 431, -118,
959 590, -971, -127, 157, 206, 1266, 204, -320, -223, 704, -687, -276,
960 -716, 787, -1121, 40, 292, 249, -10,
961 ],
962 ],
963 sigma_dct: [
964 139, 65, 133, 5, 137, 272, 88, 178, 71, 256, 254, 82, 126, 252, 152, 53,
965 281, 15, 8, 209, 285, 156, 73, 56, 36, 287, 86, 244, 270, 94, 224, 156,
966 ],
967 },
968 Spline {
969 control_points: vec![
970 Point { x: 109.0, y: 54.0 },
971 Point { x: 218.0, y: 159.0 },
972 Point { x: 80.0, y: 3.0 },
973 Point { x: 110.0, y: 274.0 },
974 Point { x: 94.0, y: 185.0 },
975 Point { x: 17.0, y: 277.0 },
976 ],
977 color_dct: [
978 Dct32([
979 36.300457,
980 39.69839859,
981 23.20079994,
982 67.49819946,
983 4.401599884,
984 71.50080109,
985 62.29859924,
986 32.29800034,
987 92.19839478,
988 10.10099983,
989 10.79819965,
990 9.197999954,
991 6.098399639,
992 10.5,
993 79.09859467,
994 7.001399517,
995 24.59939957,
996 90.79979706,
997 5.501999855,
998 84.0,
999 43.80179977,
1000 49.00139999,
1001 33.49919891,
1002 78.90119934,
1003 54.49919891,
1004 77.90159607,
1005 62.10119629,
1006 51.39959717,
1007 36.40139771,
1008 14.30099964,
1009 83.70179749,
1010 35.40179825,
1011 ]),
1012 Dct32([
1013 9.386842728,
1014 53.40000153,
1015 9.525000572,
1016 74.92500305,
1017 72.67500305,
1018 26.70000076,
1019 7.875000477,
1020 0.9000000358,
1021 84.90000153,
1022 23.17500114,
1023 26.47500038,
1024 31.12500191,
1025 90.9750061,
1026 11.70000076,
1027 74.1000061,
1028 39.30000305,
1029 23.70000076,
1030 82.5,
1031 4.800000191,
1032 2.700000048,
1033 61.20000076,
1034 96.37500763,
1035 13.72500038,
1036 66.67500305,
1037 62.92500305,
1038 82.42500305,
1039 5.925000191,
1040 98.70000458,
1041 21.52500153,
1042 7.875000477,
1043 51.67500305,
1044 63.07500076,
1045 ]),
1046 Dct32([
1047 47.99487305,
1048 39.33000183,
1049 6.865000725,
1050 26.27500153,
1051 33.2650032,
1052 6.190000534,
1053 1.715000629,
1054 98.90000153,
1055 59.91000366,
1056 59.57500458,
1057 95.00499725,
1058 61.29500198,
1059 82.71500397,
1060 53.0,
1061 6.130004883,
1062 30.41000366,
1063 34.69000244,
1064 96.91999817,
1065 93.4200058,
1066 16.97999954,
1067 38.80000305,
1068 80.76500702,
1069 63.00499725,
1070 18.5850029,
1071 43.60500336,
1072 32.30500412,
1073 61.01499939,
1074 20.23000336,
1075 24.32500076,
1076 28.31500053,
1077 69.10500336,
1078 62.375,
1079 ]),
1080 ],
1081 sigma_dct: Dct32([
1082 32.75933838,
1083 21.66449928,
1084 44.32889938,
1085 1.666499972,
1086 45.66209793,
1087 90.6576004,
1088 29.33039856,
1089 59.32740021,
1090 23.66429901,
1091 85.32479858,
1092 84.6581955,
1093 27.33059883,
1094 41.99580002,
1095 83.99160004,
1096 50.66159821,
1097 17.66489983,
1098 93.65729523,
1099 4.999499798,
1100 2.666399956,
1101 69.65969849,
1102 94.9905014,
1103 51.99480057,
1104 24.33090019,
1105 18.66479874,
1106 11.99880028,
1107 95.65709686,
1108 28.66379929,
1109 81.32519531,
1110 89.99099731,
1111 31.3302002,
1112 74.65919495,
1113 51.99480057,
1114 ]),
1115 estimated_area_reached: 19843491681,
1116 },
1117 ),
1118 (
1119 QuantizedSpline {
1120 control_points: vec![
1121 (24, -32),
1122 (-178, -7),
1123 (226, 151),
1124 (121, -172),
1125 (-184, 39),
1126 (-201, -182),
1127 (301, 404),
1128 ],
1129 color_dct: [
1130 [
1131 5051, 6881, 5238, 1571, 9952, 19762, 2048, 13524, 16405, 2310, 1286,
1132 4714, 16857, 21429, 12500, 15524, 1857, 5595, 6286, 17190, 15405,
1133 20738, 310, 16071, 10952, 16286, 15571, 8452, 6929, 3095, 9905, 5690,
1134 ],
1135 [
1136 899, 1059, 836, 388, 1291, 247, 235, 203, 1073, 747, 1283, 799, 356,
1137 1281, 1231, 561, 477, 720, 309, 733, 1013, 477, 779, 1183, 32, 1041,
1138 1275, 367, 88, 1047, 321, 931,
1139 ],
1140 [
1141 -78, 244, -883, 943, -682, 752, 107, 262, -75, 557, -202, -575, -231,
1142 -731, -605, 732, 682, 650, 592, -14, -1035, 913, -188, -95, 286, -574,
1143 -509, 67, 86, -1056, 592, 380,
1144 ],
1145 ],
1146 sigma_dct: [
1147 308, 8, 125, 7, 119, 237, 209, 60, 277, 215, 126, 186, 90, 148, 211, 136,
1148 188, 142, 140, 124, 272, 140, 274, 165, 24, 209, 76, 254, 185, 83, 11, 141,
1149 ],
1150 },
1151 Spline {
1152 control_points: vec![
1153 Point { x: 172.0, y: 309.0 },
1154 Point { x: 196.0, y: 277.0 },
1155 Point { x: 42.0, y: 238.0 },
1156 Point { x: 114.0, y: 350.0 },
1157 Point { x: 307.0, y: 290.0 },
1158 Point { x: 316.0, y: 269.0 },
1159 Point { x: 124.0, y: 66.0 },
1160 Point { x: 233.0, y: 267.0 },
1161 ],
1162 color_dct: [
1163 Dct32([
1164 15.00070381,
1165 28.90019989,
1166 21.99959946,
1167 6.598199844,
1168 41.79839706,
1169 83.00039673,
1170 8.601599693,
1171 56.80079651,
1172 68.90100098,
1173 9.701999664,
1174 5.401199818,
1175 19.79879951,
1176 70.79940033,
1177 90.00180054,
1178 52.5,
1179 65.20079803,
1180 7.799399853,
1181 23.49899864,
1182 26.40119934,
1183 72.19799805,
1184 64.7009964,
1185 87.09959412,
1186 1.301999927,
1187 67.49819946,
1188 45.99839783,
1189 68.40119934,
1190 65.39820099,
1191 35.49839783,
1192 29.10179901,
1193 12.9989996,
1194 41.60099792,
1195 23.89799881,
1196 ]),
1197 Dct32([
1198 47.67667389,
1199 79.42500305,
1200 62.70000076,
1201 29.10000038,
1202 96.82500458,
1203 18.52500153,
1204 17.625,
1205 15.22500038,
1206 80.4750061,
1207 56.02500153,
1208 96.2250061,
1209 59.92500305,
1210 26.70000076,
1211 96.07500458,
1212 92.32500458,
1213 42.07500076,
1214 35.77500153,
1215 54.00000381,
1216 23.17500114,
1217 54.97500229,
1218 75.9750061,
1219 35.77500153,
1220 58.42500305,
1221 88.7250061,
1222 2.400000095,
1223 78.07500458,
1224 95.625,
1225 27.52500153,
1226 6.600000381,
1227 78.52500153,
1228 24.07500076,
1229 69.82500458,
1230 ]),
1231 Dct32([
1232 43.81587219,
1233 96.50500488,
1234 0.8899993896,
1235 95.11000061,
1236 49.0850029,
1237 71.16500092,
1238 25.11499977,
1239 33.56500244,
1240 75.2250061,
1241 95.01499939,
1242 82.08500671,
1243 19.67500305,
1244 10.53000069,
1245 44.90500259,
1246 49.9750061,
1247 93.31500244,
1248 83.51499939,
1249 99.5,
1250 64.61499786,
1251 53.99500275,
1252 3.525009155,
1253 99.68499756,
1254 45.2650032,
1255 82.07500458,
1256 22.42000008,
1257 37.89500427,
1258 59.99499893,
1259 32.21500015,
1260 12.62000084,
1261 4.605003357,
1262 65.51499939,
1263 96.42500305,
1264 ]),
1265 ],
1266 sigma_dct: Dct32([
1267 72.58903503,
1268 2.666399956,
1269 41.66249847,
1270 2.333099842,
1271 39.66270065,
1272 78.99209595,
1273 69.65969849,
1274 19.99799919,
1275 92.32409668,
1276 71.65950012,
1277 41.99580002,
1278 61.9937973,
1279 29.99699974,
1280 49.32839966,
1281 70.32630157,
1282 45.3288002,
1283 62.66040039,
1284 47.32859802,
1285 46.66199875,
1286 41.32920074,
1287 90.6576004,
1288 46.66199875,
1289 91.32419586,
1290 54.99449921,
1291 7.999199867,
1292 69.65969849,
1293 25.3307991,
1294 84.6581955,
1295 61.66049957,
1296 27.66390038,
1297 3.66629982,
1298 46.99530029,
1299 ]),
1300 estimated_area_reached: 25829781306,
1301 },
1302 ),
1303 (
1304 QuantizedSpline {
1305 control_points: vec![
1306 (157, -89),
1307 (-244, 41),
1308 (-58, 168),
1309 (429, -185),
1310 (-361, 198),
1311 (230, -269),
1312 (-416, 203),
1313 (167, 65),
1314 (460, -344),
1315 ],
1316 color_dct: [
1317 [
1318 5691, 15429, 1000, 2524, 5595, 4048, 18881, 1357, 14381, 3952, 22595,
1319 15167, 20857, 2500, 905, 14548, 5452, 19500, 19143, 9643, 10929, 6048,
1320 9476, 7143, 11952, 21524, 6643, 22310, 15500, 11476, 5310, 10452,
1321 ],
1322 [
1323 470, 880, 47, 1203, 1295, 211, 475, 8, 907, 528, 325, 1145, 769, 1035,
1324 633, 905, 57, 72, 1216, 780, 1, 696, 47, 637, 843, 580, 1144, 477, 669,
1325 479, 256, 643,
1326 ],
1327 [
1328 1169, -301, 1041, -725, -43, -22, 774, 134, -822, 499, 456, -287, -713,
1329 -776, 76, 449, 750, 580, -207, -643, 956, -426, 377, -64, 101, -250,
1330 -164, 259, 169, -240, 430, -22,
1331 ],
1332 ],
1333 sigma_dct: [
1334 354, 5, 75, 56, 140, 226, 84, 187, 151, 70, 257, 288, 137, 99, 100, 159,
1335 79, 176, 59, 210, 278, 68, 171, 65, 230, 263, 69, 199, 107, 107, 170, 202,
1336 ],
1337 },
1338 Spline {
1339 control_points: vec![
1340 Point { x: 100.0, y: 186.0 },
1341 Point { x: 257.0, y: 97.0 },
1342 Point { x: 170.0, y: 49.0 },
1343 Point { x: 25.0, y: 169.0 },
1344 Point { x: 309.0, y: 104.0 },
1345 Point { x: 232.0, y: 237.0 },
1346 Point { x: 385.0, y: 101.0 },
1347 Point { x: 122.0, y: 168.0 },
1348 Point { x: 26.0, y: 300.0 },
1349 Point { x: 390.0, y: 88.0 },
1350 ],
1351 color_dct: [
1352 Dct32([
1353 16.90140724,
1354 64.80179596,
1355 4.199999809,
1356 10.60079956,
1357 23.49899864,
1358 17.00160027,
1359 79.30019379,
1360 5.699399948,
1361 60.40019608,
1362 16.59840012,
1363 94.89899445,
1364 63.70139694,
1365 87.59939575,
1366 10.5,
1367 3.80099988,
1368 61.10159683,
1369 22.89839935,
1370 81.8999939,
1371 80.40059662,
1372 40.50059891,
1373 45.90179825,
1374 25.40159988,
1375 39.79919815,
1376 30.00059891,
1377 50.19839859,
1378 90.40079498,
1379 27.90059853,
1380 93.70199585,
1381 65.09999847,
1382 48.19919968,
1383 22.30200005,
1384 43.89839935,
1385 ]),
1386 Dct32([
1387 24.92551422,
1388 66.0,
1389 3.525000095,
1390 90.2250061,
1391 97.12500763,
1392 15.82500076,
1393 35.625,
1394 0.6000000238,
1395 68.02500153,
1396 39.60000229,
1397 24.37500191,
1398 85.875,
1399 57.67500305,
1400 77.625,
1401 47.47500229,
1402 67.875,
1403 4.275000095,
1404 5.400000095,
1405 91.20000458,
1406 58.50000381,
1407 0.07500000298,
1408 52.20000076,
1409 3.525000095,
1410 47.77500153,
1411 63.22500229,
1412 43.5,
1413 85.80000305,
1414 35.77500153,
1415 50.17500305,
1416 35.92500305,
1417 19.20000076,
1418 48.22500229,
1419 ]),
1420 Dct32([
1421 82.78805542,
1422 44.93000031,
1423 76.39500427,
1424 39.4750061,
1425 94.11500549,
1426 14.2850008,
1427 89.80500031,
1428 9.980000496,
1429 10.48500061,
1430 74.52999878,
1431 56.29500198,
1432 65.78500366,
1433 7.765003204,
1434 23.30500031,
1435 52.79500198,
1436 99.30500031,
1437 56.77500153,
1438 46.0,
1439 76.71000671,
1440 13.49000549,
1441 66.99499512,
1442 22.38000107,
1443 29.91499901,
1444 43.29500198,
1445 70.2950058,
1446 26.0,
1447 74.31999969,
1448 53.90499878,
1449 62.00500488,
1450 19.12500381,
1451 49.30000305,
1452 46.68500137,
1453 ]),
1454 ],
1455 sigma_dct: Dct32([
1456 83.43025208,
1457 1.666499972,
1458 24.99749947,
1459 18.66479874,
1460 46.66199875,
1461 75.32579803,
1462 27.99720001,
1463 62.32709885,
1464 50.32830048,
1465 23.33099937,
1466 85.65809631,
1467 95.99040222,
1468 45.66209793,
1469 32.99670029,
1470 33.32999802,
1471 52.99469757,
1472 26.33069992,
1473 58.66079712,
1474 19.66469955,
1475 69.99299622,
1476 92.65740204,
1477 22.6644001,
1478 56.99430084,
1479 21.66449928,
1480 76.65899658,
1481 87.65789795,
1482 22.99769974,
1483 66.3266983,
1484 35.6631012,
1485 35.6631012,
1486 56.6609993,
1487 67.32659912,
1488 ]),
1489 estimated_area_reached: 47263284396,
1490 },
1491 ),
1492 ];
1493 for (quantized, want_dequantized) in quantized_and_dequantized {
1494 let got_dequantized = quantized.dequantize(
1495 &want_dequantized.control_points[0],
1496 0,
1497 0.0,
1498 1.0,
1499 2u64 << 30,
1500 )?;
1501 assert_eq!(
1502 got_dequantized.control_points.len(),
1503 want_dequantized.control_points.len()
1504 );
1505 assert_all_almost_abs_eq(
1506 got_dequantized
1507 .control_points
1508 .iter()
1509 .map(|p| p.x)
1510 .collect::<Vec<f32>>(),
1511 want_dequantized
1512 .control_points
1513 .iter()
1514 .map(|p| p.x)
1515 .collect::<Vec<f32>>(),
1516 1e-6,
1517 );
1518 assert_all_almost_abs_eq(
1519 got_dequantized
1520 .control_points
1521 .iter()
1522 .map(|p| p.y)
1523 .collect::<Vec<f32>>(),
1524 want_dequantized
1525 .control_points
1526 .iter()
1527 .map(|p| p.y)
1528 .collect::<Vec<f32>>(),
1529 1e-6,
1530 );
1531 for index in 0..got_dequantized.color_dct.len() {
1532 assert_all_almost_abs_eq(
1533 got_dequantized.color_dct[index].0,
1534 want_dequantized.color_dct[index].0,
1535 1e-4,
1536 );
1537 }
1538 assert_all_almost_abs_eq(
1539 got_dequantized.sigma_dct.0,
1540 want_dequantized.sigma_dct.0,
1541 1e-4,
1542 );
1543 assert_eq!(
1544 got_dequantized.estimated_area_reached,
1545 want_dequantized.estimated_area_reached,
1546 );
1547 }
1548 Ok(())
1549 }
1550
1551 #[test]
1552 fn centripetal_catmull_rom_spline() -> Result<(), Error> {
1553 let control_points = vec![Point { x: 1.0, y: 2.0 }, Point { x: 4.0, y: 3.0 }];
1554 let want_result = [
1555 Point { x: 1.0, y: 2.0 },
1556 Point {
1557 x: 1.187500119,
1558 y: 2.0625,
1559 },
1560 Point { x: 1.375, y: 2.125 },
1561 Point {
1562 x: 1.562499881,
1563 y: 2.1875,
1564 },
1565 Point {
1566 x: 1.750000119,
1567 y: 2.25,
1568 },
1569 Point {
1570 x: 1.9375,
1571 y: 2.3125,
1572 },
1573 Point { x: 2.125, y: 2.375 },
1574 Point {
1575 x: 2.312500238,
1576 y: 2.4375,
1577 },
1578 Point {
1579 x: 2.500000238,
1580 y: 2.5,
1581 },
1582 Point {
1583 x: 2.6875,
1584 y: 2.5625,
1585 },
1586 Point {
1587 x: 2.875000477,
1588 y: 2.625,
1589 },
1590 Point {
1591 x: 3.062499762,
1592 y: 2.6875,
1593 },
1594 Point { x: 3.25, y: 2.75 },
1595 Point {
1596 x: 3.4375,
1597 y: 2.8125,
1598 },
1599 Point {
1600 x: 3.624999762,
1601 y: 2.875,
1602 },
1603 Point {
1604 x: 3.812500238,
1605 y: 2.9375,
1606 },
1607 Point { x: 4.0, y: 3.0 },
1608 ];
1609 let got_result = draw_centripetal_catmull_rom_spline(&control_points)?;
1610 assert_all_almost_abs_eq(
1611 got_result.iter().map(|p| p.x).collect::<Vec<f32>>(),
1612 want_result.iter().map(|p| p.x).collect::<Vec<f32>>(),
1613 1e-10,
1614 );
1615 Ok(())
1616 }
1617
1618 #[test]
1619 fn equally_spaced_points() -> Result<(), Error> {
1620 let desired_rendering_distance = 10.0f32;
1621 let segments = [
1622 Point { x: 0.0, y: 0.0 },
1623 Point { x: 5.0, y: 0.0 },
1624 Point { x: 35.0, y: 0.0 },
1625 Point { x: 35.0, y: 10.0 },
1626 ];
1627 let want_results = [
1628 (Point { x: 0.0, y: 0.0 }, desired_rendering_distance),
1629 (Point { x: 10.0, y: 0.0 }, desired_rendering_distance),
1630 (Point { x: 20.0, y: 0.0 }, desired_rendering_distance),
1631 (Point { x: 30.0, y: 0.0 }, desired_rendering_distance),
1632 (Point { x: 35.0, y: 5.0 }, desired_rendering_distance),
1633 (Point { x: 35.0, y: 10.0 }, 5.0f32),
1634 ];
1635 let mut got_results = Vec::<(Point, f32)>::new();
1636 for_each_equally_spaced_point(&segments, desired_rendering_distance, |p, d| {
1637 got_results.push((p, d))
1638 });
1639 assert_all_almost_abs_eq(
1640 got_results.iter().map(|(p, _)| p.x).collect::<Vec<f32>>(),
1641 want_results.iter().map(|(p, _)| p.x).collect::<Vec<f32>>(),
1642 1e-9,
1643 );
1644 assert_all_almost_abs_eq(
1645 got_results.iter().map(|(p, _)| p.y).collect::<Vec<f32>>(),
1646 want_results.iter().map(|(p, _)| p.y).collect::<Vec<f32>>(),
1647 1e-9,
1648 );
1649 assert_all_almost_abs_eq(
1650 got_results.iter().map(|(_, d)| *d).collect::<Vec<f32>>(),
1651 want_results.iter().map(|(_, d)| *d).collect::<Vec<f32>>(),
1652 1e-9,
1653 );
1654 Ok(())
1655 }
1656
1657 #[test]
1658 fn dct32() -> Result<(), Error> {
1659 let mut dct = Dct32::default();
1660 for (i, coeff) in dct.0.iter_mut().enumerate() {
1661 *coeff = 0.05f32 * i as f32;
1662 }
1663 let want_out = [
1665 16.7353153229,
1666 -18.6041717529,
1667 7.9931735992,
1668 -7.1250801086,
1669 4.6699867249,
1670 -4.3367614746,
1671 3.2450540066,
1672 -3.0694460869,
1673 2.4446771145,
1674 -2.3350939751,
1675 1.9243829250,
1676 -1.8484034538,
1677 1.5531382561,
1678 -1.4964176416,
1679 1.2701368332,
1680 -1.2254891396,
1681 1.0434474945,
1682 -1.0067725182,
1683 0.8544843197,
1684 -0.8232427835,
1685 0.6916543841,
1686 -0.6642799377,
1687 0.5473306179,
1688 -0.5226536393,
1689 0.4161090851,
1690 -0.3933961987,
1691 0.2940555215,
1692 -0.2726306915,
1693 0.1781132221,
1694 -0.1574717760,
1695 0.0656886101,
1696 -0.0454511642,
1697 ];
1698 for (t, want) in want_out.iter().enumerate() {
1699 let got_out = dct.continuous_idct(t as f32);
1700 assert_almost_abs_eq(got_out, *want, 1e-4);
1701 }
1702 Ok(())
1703 }
1704
1705 #[test]
1706 fn dct32_fast_matches_original() {
1707 let mut dct = Dct32::default();
1709 for (i, coeff) in dct.0.iter_mut().enumerate() {
1710 *coeff = 0.05f32 * i as f32;
1711 }
1712
1713 for t in 0..32 {
1714 let t_val = t as f32;
1715 let original = dct.continuous_idct(t_val);
1716 let precomputed = PrecomputedCosines::new(t_val);
1717 let fast = dct.continuous_idct_fast(&precomputed);
1718 assert_almost_abs_eq(fast, original, 1e-5);
1719 }
1720 }
1721
1722 fn verify_segment_almost_equal(seg1: &SplineSegment, seg2: &SplineSegment) {
1723 assert_almost_eq(seg1.center_x, seg2.center_x, 1e-2, 1e-4);
1724 assert_almost_eq(seg1.center_y, seg2.center_y, 1e-2, 1e-4);
1725 for (got, want) in zip(seg1.color.iter(), seg2.color.iter()) {
1726 assert_almost_eq(*got, *want, 1e-2, 1e-4);
1727 }
1728 assert_almost_eq(seg1.inv_sigma, seg2.inv_sigma, 1e-2, 1e-4);
1729 assert_almost_eq(seg1.maximum_distance, seg2.maximum_distance, 1e-2, 1e-4);
1730 assert_almost_eq(
1731 seg1.sigma_over_4_times_intensity,
1732 seg2.sigma_over_4_times_intensity,
1733 1e-2,
1734 1e-4,
1735 );
1736 }
1737
1738 #[test]
1739 fn spline_segments_add_segment() -> Result<(), Error> {
1740 let mut splines = Splines::default();
1741 let mut segments_by_y = Vec::<(u64, usize)>::new();
1742
1743 splines.add_segment(
1744 &Point { x: 10.0, y: 20.0 },
1745 0.5,
1746 [0.5, 0.6, 0.7],
1747 0.8,
1748 true,
1749 &mut segments_by_y,
1750 );
1751 let want_segment = SplineSegment {
1753 center_x: 10.0,
1754 center_y: 20.0,
1755 color: [0.5, 0.6, 0.7],
1756 inv_sigma: 1.25,
1757 maximum_distance: 3.65961,
1758 sigma_over_4_times_intensity: 0.1,
1759 };
1760 assert_eq!(splines.segments.len(), 1);
1761 verify_segment_almost_equal(&splines.segments[0], &want_segment);
1762 let want_segments_by_y = [
1763 (16, 0),
1764 (17, 0),
1765 (18, 0),
1766 (19, 0),
1767 (20, 0),
1768 (21, 0),
1769 (22, 0),
1770 (23, 0),
1771 (24, 0),
1772 ];
1773 for (got, want) in zip(segments_by_y.iter(), want_segments_by_y.iter()) {
1774 assert_eq!(got.0, want.0);
1775 assert_eq!(got.1, want.1);
1776 }
1777 Ok(())
1778 }
1779
1780 #[test]
1781 fn spline_segments_add_segments_from_points() -> Result<(), Error> {
1782 let mut splines = Splines::default();
1783 let mut segments_by_y = Vec::<(u64, usize)>::new();
1784 let mut color_dct = [Dct32::default(); 3];
1785 for (channel_index, channel_dct) in color_dct.iter_mut().enumerate() {
1786 for (coeff_index, coeff) in channel_dct.0.iter_mut().enumerate() {
1787 *coeff = 0.1 * channel_index as f32 + 0.05 * coeff_index as f32;
1788 }
1789 }
1790 let mut sigma_dct = Dct32::default();
1791 for (coeff_index, coeff) in sigma_dct.0.iter_mut().enumerate() {
1792 *coeff = 0.06 * coeff_index as f32;
1793 }
1794 let spline = Spline {
1795 control_points: vec![],
1796 color_dct,
1797 sigma_dct,
1798 estimated_area_reached: 0,
1799 };
1800 let points_to_draw = vec![
1801 (Point { x: 10.0, y: 20.0 }, 1.0),
1802 (Point { x: 11.0, y: 21.0 }, 1.0),
1803 (Point { x: 12.0, y: 21.0 }, 1.0),
1804 ];
1805 splines.add_segments_from_points(
1806 &spline,
1807 &points_to_draw,
1808 SQRT_2 + 1.0,
1809 DESIRED_RENDERING_DISTANCE,
1810 true,
1811 &mut segments_by_y,
1812 );
1813 let want_segments = [
1815 SplineSegment {
1816 center_x: 10.0,
1817 center_y: 20.0,
1818 color: [16.73531532, 19.68646049, 22.63760757],
1819 inv_sigma: 0.04979490861,
1820 maximum_distance: 108.6400299,
1821 sigma_over_4_times_intensity: 5.020593643,
1822 },
1823 SplineSegment {
1824 center_x: 11.0,
1825 center_y: 21.0,
1826 color: [-0.8199231625, -0.7960500717, -0.7721766233],
1827 inv_sigma: -1.016355753,
1828 maximum_distance: 4.680418015,
1829 sigma_over_4_times_intensity: -0.2459768653,
1830 },
1831 SplineSegment {
1832 center_x: 12.0,
1833 center_y: 21.0,
1834 color: [-0.7767754197, -0.7544237971, -0.7320720553],
1835 inv_sigma: -1.072811365,
1836 maximum_distance: 4.423510075,
1837 sigma_over_4_times_intensity: -0.2330325693,
1838 },
1839 ];
1840 assert_eq!(splines.segments.len(), want_segments.len());
1841 for (got, want) in zip(splines.segments.iter(), want_segments.iter()) {
1842 verify_segment_almost_equal(got, want);
1843 }
1844 let want_segments_by_y: Vec<(u64, usize)> = (0..=129)
1845 .map(|c| (c, 0))
1846 .chain((16..=26).map(|c| (c, 1)))
1847 .chain((17..=25).map(|c| (c, 2)))
1848 .collect();
1849 for (got, want) in zip(segments_by_y.iter(), want_segments_by_y.iter()) {
1850 assert_eq!(got.0, want.0);
1851 assert_eq!(got.1, want.1);
1852 }
1853 Ok(())
1854 }
1855
1856 #[test]
1857 fn init_draw_cache() -> Result<(), Error> {
1858 let mut splines = Splines {
1859 splines: vec![
1860 QuantizedSpline {
1861 control_points: vec![
1862 (109, 105),
1863 (-247, -261),
1864 (168, 427),
1865 (-46, -360),
1866 (-61, 181),
1867 ],
1868 color_dct: [
1869 [
1870 12223, 9452, 5524, 16071, 1048, 17024, 14833, 7690, 21952, 2405, 2571,
1871 2190, 1452, 2500, 18833, 1667, 5857, 21619, 1310, 20000, 10429, 11667,
1872 7976, 18786, 12976, 18548, 14786, 12238, 8667, 3405, 19929, 8429,
1873 ],
1874 [
1875 177, 712, 127, 999, 969, 356, 105, 12, 1132, 309, 353, 415, 1213, 156,
1876 988, 524, 316, 1100, 64, 36, 816, 1285, 183, 889, 839, 1099, 79, 1316,
1877 287, 105, 689, 841,
1878 ],
1879 [
1880 780, -201, -38, -695, -563, -293, -88, 1400, -357, 520, 979, 431, -118,
1881 590, -971, -127, 157, 206, 1266, 204, -320, -223, 704, -687, -276,
1882 -716, 787, -1121, 40, 292, 249, -10,
1883 ],
1884 ],
1885 sigma_dct: [
1886 139, 65, 133, 5, 137, 272, 88, 178, 71, 256, 254, 82, 126, 252, 152, 53,
1887 281, 15, 8, 209, 285, 156, 73, 56, 36, 287, 86, 244, 270, 94, 224, 156,
1888 ],
1889 },
1890 QuantizedSpline {
1891 control_points: vec![
1892 (24, -32),
1893 (-178, -7),
1894 (226, 151),
1895 (121, -172),
1896 (-184, 39),
1897 (-201, -182),
1898 (301, 404),
1899 ],
1900 color_dct: [
1901 [
1902 5051, 6881, 5238, 1571, 9952, 19762, 2048, 13524, 16405, 2310, 1286,
1903 4714, 16857, 21429, 12500, 15524, 1857, 5595, 6286, 17190, 15405,
1904 20738, 310, 16071, 10952, 16286, 15571, 8452, 6929, 3095, 9905, 5690,
1905 ],
1906 [
1907 899, 1059, 836, 388, 1291, 247, 235, 203, 1073, 747, 1283, 799, 356,
1908 1281, 1231, 561, 477, 720, 309, 733, 1013, 477, 779, 1183, 32, 1041,
1909 1275, 367, 88, 1047, 321, 931,
1910 ],
1911 [
1912 -78, 244, -883, 943, -682, 752, 107, 262, -75, 557, -202, -575, -231,
1913 -731, -605, 732, 682, 650, 592, -14, -1035, 913, -188, -95, 286, -574,
1914 -509, 67, 86, -1056, 592, 380,
1915 ],
1916 ],
1917 sigma_dct: [
1918 308, 8, 125, 7, 119, 237, 209, 60, 277, 215, 126, 186, 90, 148, 211, 136,
1919 188, 142, 140, 124, 272, 140, 274, 165, 24, 209, 76, 254, 185, 83, 11, 141,
1920 ],
1921 },
1922 ],
1923 starting_points: vec![Point { x: 10.0, y: 20.0 }, Point { x: 5.0, y: 40.0 }],
1924 ..Default::default()
1925 };
1926 splines.initialize_draw_cache(
1927 1 << 15,
1928 1 << 15,
1929 &ColorCorrelationParams {
1930 color_factor: 1,
1931 base_correlation_x: 0.0,
1932 base_correlation_b: 0.0,
1933 ytox_lf: 0,
1934 ytob_lf: 0,
1935 },
1936 true,
1937 )?;
1938 assert_eq!(splines.segments.len(), 1940);
1939 let want_segments_sample = [
1940 (
1941 22,
1942 SplineSegment {
1943 center_x: 25.77652359,
1944 center_y: 35.33295059,
1945 color: [-524.996582, -509.9048462, 43.3883667],
1946 inv_sigma: -0.00197347207,
1947 maximum_distance: 3021.377197,
1948 sigma_over_4_times_intensity: -126.6802902,
1949 },
1950 ),
1951 (
1952 474,
1953 SplineSegment {
1954 center_x: -16.45600891,
1955 center_y: 78.81845856,
1956 color: [-117.6707535, -133.5515594, 343.5632629],
1957 inv_sigma: -0.002631845651,
1958 maximum_distance: 2238.376221,
1959 sigma_over_4_times_intensity: -94.9903717,
1960 },
1961 ),
1962 (
1963 835,
1964 SplineSegment {
1965 center_x: -71.93701172,
1966 center_y: 230.0635529,
1967 color: [44.79507446, 298.9411621, -395.3574524],
1968 inv_sigma: 0.01869126037,
1969 maximum_distance: 316.4499207,
1970 sigma_over_4_times_intensity: 13.3752346,
1971 },
1972 ),
1973 (
1974 1066,
1975 SplineSegment {
1976 center_x: -126.2593002,
1977 center_y: -22.97857094,
1978 color: [-136.4196625, 194.757019, -98.18778992],
1979 inv_sigma: 0.007531851064,
1980 maximum_distance: 769.2540283,
1981 sigma_over_4_times_intensity: 33.19237137,
1982 },
1983 ),
1984 (
1985 1328,
1986 SplineSegment {
1987 center_x: 73.70871735,
1988 center_y: 56.31413269,
1989 color: [-13.44394779, 162.6139221, 93.78419495],
1990 inv_sigma: 0.003664178308,
1991 maximum_distance: 1572.710327,
1992 sigma_over_4_times_intensity: 68.2281189,
1993 },
1994 ),
1995 (
1996 1545,
1997 SplineSegment {
1998 center_x: 77.48892975,
1999 center_y: -92.33877563,
2000 color: [-220.6807556, 66.13040924, -32.26184082],
2001 inv_sigma: 0.03166157752,
2002 maximum_distance: 183.6748352,
2003 sigma_over_4_times_intensity: 7.89600563,
2004 },
2005 ),
2006 (
2007 1774,
2008 SplineSegment {
2009 center_x: -16.43594933,
2010 center_y: -144.8626556,
2011 color: [57.31535339, -46.36843109, 92.14952087],
2012 inv_sigma: -0.01524505392,
2013 maximum_distance: 371.4827271,
2014 sigma_over_4_times_intensity: -16.39876175,
2015 },
2016 ),
2017 (
2018 1929,
2019 SplineSegment {
2020 center_x: 61.19338608,
2021 center_y: -10.70717049,
2022 color: [-69.78807068, 300.6082458, -476.5135803],
2023 inv_sigma: 0.003229281865,
2024 maximum_distance: 1841.37854,
2025 sigma_over_4_times_intensity: 77.41659546,
2026 },
2027 ),
2028 ];
2029 for (index, segment) in want_segments_sample {
2030 verify_segment_almost_equal(&segment, &splines.segments[index]);
2031 }
2032 Ok(())
2033 }
2034}