Vendor rawler 0.7.2 unmodified

The crate as crates.io publishes it, minus .cargo-ok, its Cargo.lock and
data/testdata (13 MB of sample files only its own tests read). Not yet
routed through [patch.crates-io]; the next commit is the patch.
This commit is contained in:
2026-09-27 17:33:19 -04:00
parent 0d9feb0556
commit 77a1925bac
940 changed files with 66954 additions and 0 deletions
+579
View File
@@ -0,0 +1,579 @@
use std::cell::UnsafeCell;
use multiversion::multiversion;
use rayon::prelude::*;
use crate::{
decoders::decode_threaded_prealloc,
formats::tiff::Rational,
imgop::{Dim2, Point, Rect},
};
pub trait SubPixel: Default + std::fmt::Debug + Clone + Copy + Send + Sync + Into<Rational> {
fn as_f32(self) -> f32;
fn as_u16(self) -> u16;
}
impl SubPixel for u16 {
fn as_f32(self) -> f32 {
self as f32
}
fn as_u16(self) -> u16 {
self as u16
}
}
impl SubPixel for f32 {
fn as_f32(self) -> f32 {
self as f32
}
fn as_u16(self) -> u16 {
self as u16
}
}
pub type LineMut<'a, T> = &'a mut [T];
pub type Line<'a, T> = &'a [T];
#[derive(Clone)]
pub struct Pix2D<T: SubPixel> {
pub width: usize,
pub height: usize,
pub data: Vec<T>,
pub initialized: bool,
}
pub type PixU16 = Pix2D<u16>;
pub type PixF32 = Pix2D<f32>;
impl<T> Pix2D<T>
where
T: SubPixel,
{
pub fn new_with(data: Vec<T>, width: usize, height: usize) -> Self {
assert_eq!(data.len(), height * width);
Self {
data,
width,
height,
initialized: true,
}
}
pub fn new(width: usize, height: usize) -> Self {
let data = vec![T::default(); width * height];
Self {
data,
width,
height,
initialized: true,
}
}
pub fn new_uninit(width: usize, height: usize) -> Self {
let data = Vec::with_capacity(width * height);
Self {
data,
width,
height,
initialized: false,
}
}
pub fn is_initialized(&self) -> bool {
self.initialized
}
pub fn into_inner(self) -> Vec<T> {
self.data
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn dim(&self) -> Dim2 {
Dim2::new(self.width, self.height)
}
pub fn rect(&self) -> Rect {
Rect::new(Point::default(), Dim2::new(self.width, self.height))
}
pub fn update_dimension(&mut self, dim: Dim2) {
if self.width * self.height == dim.w * dim.h {
self.width = dim.w;
self.height = dim.h;
} else {
panic!("Can not change dimension: mismatch with old dimension: {:?} vs. {:?}", self.dim(), dim);
}
}
pub fn pixels(&self) -> &[T] {
debug_assert!(self.initialized);
&self.data
}
pub fn pixels_mut(&mut self) -> &mut [T] {
debug_assert!(self.initialized);
&mut self.data
}
pub fn pixel_rows(&self) -> std::slice::ChunksExact<'_, T> {
debug_assert!(self.initialized);
self.data.chunks_exact(self.width)
}
pub fn pixel_rows_mut(&mut self) -> std::slice::ChunksExactMut<'_, T> {
debug_assert!(self.initialized);
self.data.chunks_exact_mut(self.width)
}
pub fn par_pixel_rows_mut(&mut self) -> rayon::slice::ChunksExactMut<'_, T> {
debug_assert!(self.initialized);
self.data.par_chunks_exact_mut(self.width)
}
#[inline(always)]
pub fn row(&self, row: usize) -> &[T] {
debug_assert!(self.initialized);
let start = row * self.width;
&self.data[start..start + self.width]
}
#[inline(always)]
pub fn row_mut(&mut self, row: usize) -> &mut [T] {
debug_assert!(self.initialized);
let start = row * self.width;
&mut self.data[start..start + self.width]
}
#[inline(always)]
pub fn at(&self, row: usize, col: usize) -> &T {
debug_assert!(self.initialized);
#[cfg(debug_assertions)]
{
&self.data[row * self.width + col]
}
#[cfg(not(debug_assertions))]
unsafe {
self.data.get_unchecked(row * self.width + col)
}
}
#[inline(always)]
pub fn at_mut(&mut self, row: usize, col: usize) -> &mut T {
debug_assert!(self.initialized);
#[cfg(debug_assertions)]
{
&mut self.data[row * self.width + col]
}
#[cfg(not(debug_assertions))]
unsafe {
self.data.get_unchecked_mut(row * self.width + col)
}
}
#[inline(always)]
pub fn for_each<F>(&mut self, op: F)
where
F: Fn(T) -> T + Send + Sync,
{
assert!(self.initialized);
self.data.par_iter_mut().for_each(|v| *v = op(*v));
}
#[inline(always)]
pub fn for_each_index<F>(&mut self, op: F)
where
F: Fn(T, usize, usize) -> T,
{
assert!(self.initialized);
self
.pixel_rows_mut()
.enumerate()
.for_each(|(row, rowbuf)| rowbuf.iter_mut().enumerate().for_each(|(col, v)| *v = op(*v, row, col)));
}
pub fn crop(&self, area: Rect) -> Self {
assert!(self.initialized);
crop(&self, area)
}
pub fn into_crop(self, area: Rect) -> Self {
if self.dim() == area.d && area.p == Point::zero() {
self // No-Op
} else {
crop(&self, area)
}
}
}
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
fn crop<T>(pixbuf: &Pix2D<T>, area: Rect) -> Pix2D<T>
where
T: SubPixel,
{
let mut output;
if pixbuf.initialized {
output = Pix2D::<T>::new(area.width(), area.height());
output.par_pixel_rows_mut().enumerate().for_each(|(row, line)| {
let src_row = pixbuf.row(area.y() + row);
line.copy_from_slice(&src_row[area.x()..area.x() + line.len()]);
});
} else {
output = Pix2D::<T>::new_uninit(area.width(), area.height());
}
output
}
#[multiversion(targets("x86_64+avx+avx2", "x86+sse", "aarch64+neon"))]
pub(crate) fn deinterleave2x2<T>(pixbuf: &Pix2D<T>) -> crate::Result<Pix2D<T>>
where
T: SubPixel,
{
if pixbuf.initialized {
let mut output = Pix2D::<T>::new(pixbuf.width, pixbuf.height);
let line_width = pixbuf.width;
let half_width = line_width / 2;
let line_distance = line_width;
let ch0 = &pixbuf[..];
let ch1 = &pixbuf[half_width..];
let ch2 = &pixbuf[pixbuf.len() / 2..];
let ch3 = &pixbuf[pixbuf.len() / 2 + half_width..];
decode_threaded_prealloc(&mut output, &|line, row| {
let src_row = row / 2;
let offset = src_row * line_distance;
let ch_a;
let ch_b;
if row & 1 == 0 {
// For even rows, we take top-left and top-right channel data.
ch_a = &ch0[offset..offset + half_width];
ch_b = &ch1[offset..offset + half_width];
} else {
// For odd rows, we take bottom-left and bottom-right channel data.
ch_a = &ch2[offset..offset + half_width];
ch_b = &ch3[offset..offset + half_width];
}
debug_assert_eq!(ch_a.len(), ch_b.len());
debug_assert_eq!(ch_a.len() + ch_b.len(), line.len());
debug_assert_eq!(line_width, line.len());
line.chunks_exact_mut(2).zip(ch_a.iter().zip(ch_b.iter())).for_each(|(dst, (a, b))| {
dst[0] = *a;
dst[1] = *b;
});
Ok(())
})?;
Ok(output)
} else {
Ok(pixbuf.clone())
}
}
/*
impl<T> Index<usize> for Pix2D<T> {
type Output = T;
fn index<'a>(&'a self, i: usize) -> &'a T {
&self.data[i]
}
}
*/
impl<I, T> std::ops::Index<I> for Pix2D<T>
where
I: std::slice::SliceIndex<[T]>,
T: SubPixel,
{
type Output = I::Output;
fn index(&self, index: I) -> &Self::Output {
&self.data[index]
}
}
impl<I, T> std::ops::IndexMut<I> for Pix2D<T>
where
I: std::slice::SliceIndex<[T]>,
T: SubPixel,
{
fn index_mut<'a>(&mut self, index: I) -> &mut Self::Output {
&mut self.data[index]
}
}
/*
impl<T> Default for Pix2D<T>
where
T: Default,
{
fn default() -> Self {
Self {
width: 0,
height: 0,
data: Default::default(),
initialized: false,
}
}
}
*/
/// An ugly hack to get multiple mutable references to Pix2D
pub struct SharedPix2D<T: SubPixel> {
pub inner: UnsafeCell<Pix2D<T>>,
}
impl<T> SharedPix2D<T>
where
T: SubPixel,
{
pub fn new(inner: Pix2D<T>) -> Self {
Self { inner: inner.into() }
}
/// Get inner Pix2D<> reference
///
/// # Safety
/// Only use this inside Rayon parallel iterators.
#[allow(clippy::mut_from_ref)]
pub unsafe fn inner_mut(&self) -> &mut Pix2D<T> {
unsafe { &mut *self.inner.get() }
}
pub fn into_inner(self) -> Pix2D<T> {
self.inner.into_inner()
}
}
unsafe impl<T> Sync for SharedPix2D<T> where T: SubPixel {}
#[derive(Clone)]
pub struct Color2D<T, const N: usize> {
pub width: usize,
pub height: usize,
pub data: Vec<[T; N]>,
}
pub type RgbF32 = Color2D<f32, 3>;
pub type Ch4F32 = Color2D<f32, 4>;
impl<T, const N: usize> Color2D<T, N>
where
T: Copy + Clone + Default + Send,
[T; N]: Default,
{
pub fn new_with(data: Vec<[T; N]>, width: usize, height: usize) -> Self {
debug_assert_eq!(data.len(), height * width);
Self { data, width, height }
}
pub fn new(width: usize, height: usize) -> Self {
let data = vec![<[T; N]>::default(); width * height];
Self { data, width, height }
}
pub fn into_inner(self) -> Vec<[T; N]> {
self.data
}
pub fn dim(&self) -> Dim2 {
Dim2::new(self.width, self.height)
}
pub fn rect(&self) -> Rect {
Rect::new(Point::default(), Dim2::new(self.width, self.height))
}
pub fn flatten(&self) -> Vec<T> {
self.data.iter().flatten().copied().collect::<Vec<T>>()
}
pub fn data_ptr(&self) -> Color2DPtr<T, N> {
Color2DPtr::new(self)
}
pub fn pixels(&self) -> &[[T; N]] {
&self.data
}
pub fn pixels_mut(&mut self) -> &mut [[T; N]] {
&mut self.data
}
pub fn pixel_rows(&self) -> std::slice::ChunksExact<'_, [T; N]> {
self.data.chunks_exact(self.width)
}
pub fn pixel_rows_mut(&mut self) -> std::slice::ChunksExactMut<'_, [T; N]> {
self.data.chunks_exact_mut(self.width)
}
#[inline(always)]
pub fn at(&self, row: usize, col: usize) -> &[T; N] {
#[cfg(debug_assertions)]
{
&self.data[row * self.width + col]
}
#[cfg(not(debug_assertions))]
unsafe {
self.data.get_unchecked(row * self.width + col)
}
}
#[inline(always)]
pub fn at_mut(&mut self, row: usize, col: usize) -> &mut [T; N] {
#[cfg(debug_assertions)]
{
&mut self.data[row * self.width + col]
}
#[cfg(not(debug_assertions))]
unsafe {
self.data.get_unchecked_mut(row * self.width + col)
}
}
#[inline(always)]
pub fn for_each<F>(&mut self, op: F)
where
F: Fn([T; N]) -> [T; N] + Send + Sync,
{
self.data.par_iter_mut().for_each(|v| *v = op(*v));
}
#[inline(always)]
pub fn for_each_row<F>(&mut self, op: F)
where
F: Fn(usize, &mut [[T; N]]) + Send + Sync,
{
self.data.par_chunks_exact_mut(self.width).enumerate().for_each(|(row, data)| op(row, data));
}
// TODO: use par_iterator
#[inline(always)]
pub fn for_each_index<F>(&mut self, op: F)
where
F: Fn([T; N], usize, usize) -> [T; N],
{
self
.pixel_rows_mut()
.enumerate()
.for_each(|(row, rowbuf)| rowbuf.iter_mut().enumerate().for_each(|(col, v)| *v = op(*v, row, col)));
}
pub fn crop(&self, area: Rect) -> Self {
let mut output = Vec::with_capacity(area.d.h * area.d.w);
assert!(area.p.y + area.d.h <= self.height);
assert!(area.p.x + area.d.w <= self.width);
output.extend(
self
.pixels()
.chunks_exact(self.width)
.skip(area.p.y)
.take(area.d.h)
.flat_map(|row| row[area.p.x..area.p.x + area.d.w].iter())
.cloned(),
);
Self::new_with(output, area.d.w, area.d.h)
}
}
impl<T, const N: usize> Default for Color2D<T, N>
where
T: Default,
{
fn default() -> Self {
Self {
width: 0,
height: 0,
data: Default::default(),
}
}
}
#[derive(Clone, Debug)]
pub struct Color2DPtr<T, const N: usize> {
ptr: *const [T; N],
pub width: usize,
pub height: usize,
}
impl<T, const N: usize> Color2DPtr<T, N>
where
T: Copy + Clone,
{
fn new(orig: &Color2D<T, N>) -> Self {
Self {
ptr: orig.data.as_slice().as_ptr(),
width: orig.width,
height: orig.height,
}
}
/// Get a pixel from raw pointer
/// # Safety
/// TODO
#[inline(always)]
pub unsafe fn at(&self, row: usize, col: usize) -> &[T; N] {
unsafe {
debug_assert!(row * col < self.height * self.width);
&*self.ptr.add(row * self.width + col)
}
}
}
unsafe impl<T, const N: usize> Sync for Color2DPtr<T, N> {}
#[macro_export]
macro_rules! alloc_image_f32_plain {
($width:expr, $height:expr, $dummy: expr) => {{
if $width * $height > 500000000 || $width > 50000 || $height > 50000 {
panic!("rawler: surely there's no such thing as a >500MP or >50000 px wide/tall image!");
}
if $dummy {
$crate::pixarray::PixF32::new_uninit($width, $height)
} else {
$crate::pixarray::PixF32::new($width, $height)
}
}};
}
#[macro_export]
macro_rules! alloc_image_plain {
($width:expr, $height:expr, $dummy: expr) => {{
if $width * $height > 500000000 || $width > 50000 || $height > 50000 {
panic!("rawler: surely there's no such thing as a >500MP or >50000 px wide/tall image!");
}
if $dummy {
$crate::pixarray::PixU16::new_uninit($width, $height)
} else {
$crate::pixarray::PixU16::new($width, $height)
}
}};
}
#[macro_export]
macro_rules! alloc_image {
($width:expr, $height:expr, $dummy: expr) => {{
if $dummy {
return $crate::pixarray::PixU16::new_uninit($width, $height);
} else {
$crate::alloc_image_plain!($width, $height, $dummy)
}
}};
}
#[macro_export]
macro_rules! alloc_image_ok {
($width:expr, $height:expr, $dummy: expr) => {{
if $dummy {
return Ok($crate::pixarray::PixU16::new_uninit($width, $height));
} else {
$crate::alloc_image_plain!($width, $height, $dummy)
}
}};
}