// Copyright (c) the JPEG XL Project Authors. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file.
// This file contains a generic implementation of large (>32x32) 2d IDCTs. // They are not implemented in the same way as smaller 2d IDCTs to reduce code size.
#![allow(clippy::excessive_precision)]
use std::f32::consts::SQRT_2;
use jxl_simd::F32SimdVec; use jxl_simd::SimdDescriptor;
for i in0..n / 2 { let mul = D::F32Vec::splat(d, wc_weights[i]);
data[i] = second_half[i].mul_add(mul, first_half[i]);
data[n - i - 1] = second_half[i].neg_mul_add(mul, first_half[i]);
}
}
#[inline(always)] fn do_idct<D: SimdDescriptor>(
d: D,
data: &mut [<D::F32Vec as F32SimdVec>::UnderlyingArray],
stride: usize,
storage: &mut [D::F32Vec],
scratch: &mut [D::F32Vec],
) { let n = storage.len();
assert!((n - 1) * stride < data.len()); for i in0..n {
storage[i] = D::F32Vec::load_array(d, &data[i * stride]);
}
d.call( #[inline(always)]
|d| idct_impl_inner(d, storage, scratch),
); for i in0..n {
storage[i].store_array(&mut data[i * stride]);
}
}
#[inline(always)] fn do_idct_rowblock<D: SimdDescriptor>(
d: D,
data: &mut [<D::F32Vec as F32SimdVec>::UnderlyingArray],
storage: &mut [D::F32Vec],
scratch: &mut [D::F32Vec],
) { let n = storage.len();
assert!(n.is_multiple_of(D::F32Vec::LEN));
assert!(data.len() >= n);
let row_stride = n / D::F32Vec::LEN; for i in0..n {
storage[i] = D::F32Vec::load_array(
d,
&data[row_stride * (i % D::F32Vec::LEN) + (i / D::F32Vec::LEN)],
);
}
d.call( #[inline(always)]
|d| idct_impl_inner(d, storage, scratch),
); for i in0..n {
storage[i].store_array(&mut data[row_stride * (i % D::F32Vec::LEN) + (i / D::F32Vec::LEN)]);
}
}
#[inline(always)] fn do_idct_trh<D: SimdDescriptor>(
d: D,
data: &mut [<D::F32Vec as F32SimdVec>::UnderlyingArray],
storage: &mut [D::F32Vec],
scratch: &mut [D::F32Vec],
) { let n = storage.len();
assert!(n.is_multiple_of(D::F32Vec::LEN));
assert!(data.len() >= n);
let row_stride = n / (2 * D::F32Vec::LEN); for i in0..n / 2 {
storage[i] = D::F32Vec::load_array(d, &data[row_stride * 2 * i]);
storage[i + n / 2] = D::F32Vec::load_array(d, &data[row_stride * (2 * i + 1)]);
}
d.call( #[inline(always)]
|d| idct_impl_inner(d, storage, scratch),
); for i in0..n {
storage[i].store_array(&mut data[row_stride * i]);
}
}
#[inline(always)] fn idct2d_square<D: SimdDescriptor>(
d: D,
data: &mut [f32],
n: usize,
storage: &mut [D::F32Vec],
scratch: &mut [D::F32Vec],
) { let data = D::F32Vec::make_array_slice_mut(data); let chunks = n / D::F32Vec::LEN; // Step 1: do column-DCTs on the first K columns. for i in0..chunks {
d.call( #[inline(always)]
|_| do_idct(d, &mut data[i..], chunks, &mut storage[..n], scratch),
);
} // Step 2: do column-DCTs on groups of K columns, transposing KxK blocks and // swapping them in their final place as we do so. for i in0..chunks {
D::F32Vec::transpose_square(d, &mut data[i * n + i..], chunks); for j in i + 1..chunks {
D::F32Vec::transpose_square(d, &mut data[j * n + i..], chunks);
D::F32Vec::transpose_square(d, &mut data[i * n + j..], chunks); for k in0..D::F32Vec::LEN {
data.swap(i * n + j + k * chunks, j * n + i + k * chunks);
}
}
d.call( #[inline(always)]
|_| do_idct(d, &mut data[i..], chunks, &mut storage[..n], scratch),
);
}
}
#[inline(always)] fn idct2d_wide<D: SimdDescriptor>(
d: D,
data: &mut [f32],
c: usize,
r: usize,
storage: &mut [D::F32Vec],
scratch: &mut [D::F32Vec],
) {
assert!(r < c); let data = D::F32Vec::make_array_slice_mut(data); let column_chunks = c / D::F32Vec::LEN; let row_chunks = r / D::F32Vec::LEN; // Step 1: do rowblock-DCTs on the first K rows, transposing KxK blocks first. for i in0..row_chunks { for j in0..column_chunks {
D::F32Vec::transpose_square(d, &mut data[i * c + j..], column_chunks);
}
d.call( #[inline(always)]
|_| do_idct_rowblock(d, &mut data[i * c..], &mut storage[..c], scratch),
);
} // Step 2: do column-DCTs on groups of K columns, transposing KxK blocks back. for i in0..column_chunks { for j in0..row_chunks {
D::F32Vec::transpose_square(d, &mut data[j * c + i..], column_chunks);
}
d.call( #[inline(always)]
|_| do_idct(d, &mut data[i..], column_chunks, &mut storage[..r], scratch),
);
}
}
#[inline(always)] fn idct2d_thin<D: SimdDescriptor>(
d: D,
data: &mut [f32],
c: usize,
r: usize,
storage: &mut [D::F32Vec],
scratch: &mut [D::F32Vec],
) {
assert!(r > c); let data = D::F32Vec::make_array_slice_mut(data); let column_chunks = c / D::F32Vec::LEN; let row_chunks = r / D::F32Vec::LEN; // Note: input is transposed, so in the beginning it has ROWS *columns* and COLS *rows*. // Step 1: do column-DCTs on columns. for i in0..row_chunks {
d.call( #[inline(always)]
|_| do_idct(d, &mut data[i..], row_chunks, &mut storage[..c], scratch),
);
} // Step 2: Incrementally transpose each square sub-block of the matrix, then do a column-IDCT which also completes the transpose. for i in0..column_chunks { let tr_block = |data: &mut [<D::F32Vec as F32SimdVec>::UnderlyingArray], i, j, l| {
D::F32Vec::transpose_square(d, &mut data[i * r + j + l * column_chunks..], row_chunks)
};
(0..2).for_each(|l| tr_block(data, i, i, l)); for j in i + 1..column_chunks {
(0..2).for_each(|l| tr_block(data, i, j, l));
(0..2).for_each(|l| tr_block(data, j, i, l)); for l in0..2 { for k in0..D::F32Vec::LEN {
data.swap(
i * r + j + k * row_chunks + l * column_chunks,
j * r + i + k * row_chunks + l * column_chunks,
);
}
}
}
d.call( #[inline(always)]
|_| do_idct_trh(d, &mut data[i..], &mut storage[..r], scratch),
);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.