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