pub fn try_find_smaller_bounds(current_bounds: IntegerBounds, pixel_at: impl Fn(Vec2<usize>) -> bool) -> Option<IntegerBounds> {
    assert_ne!(current_bounds.size.area(), 0, "cannot find smaller bounds of an image with zero width or height");
    let Vec2(width, height) = current_bounds.size;

    // scans top to bottom (left to right)
    let first_top_left_pixel = (0 .. height)
        .flat_map(|y| (0 .. width).map(move |x| Vec2(x,y)))
        .find(|&position| pixel_at(position))?; // return none if no pixel should be kept

    // scans bottom to top (right to left)
    let first_bottom_right_pixel = (first_top_left_pixel.y() + 1 .. height) // excluding the top line
        .flat_map(|y| (0 .. width).map(move |x| Vec2(x, y))) // x search cannot start at first_top.x, because this must catch all bottom pixels
        .rev().find(|&position| pixel_at(position))
        .unwrap_or(first_top_left_pixel); // did not find any at bottom, but we know top has some pixel

    // now we know exactly how much we can throw away top and bottom,
    // but we don't know exactly about left or right
    let top = first_top_left_pixel.y();
    let bottom = first_bottom_right_pixel.y();

    // we only now some arbitrary left and right bounds which we need to refine.
    // because the actual image contents might be wider than the corner points.
    // we know that we do not need to look in the center between min x and max x,
    // as these must be included in any case.
    let mut min_left_x = first_top_left_pixel.x().min(first_bottom_right_pixel.x());
    let mut max_right_x = first_bottom_right_pixel.x().max(first_top_left_pixel.x());

    // requires for loop, because bounds change while searching
    for y in top ..= bottom {

        // escape the loop if there is nothing left to crop
        if min_left_x == 0 && max_right_x == width - 1 { break; }

        // search from right image edge towards image center, until known max x, for existing pixels,
        // possibly including some pixels that would have been cropped otherwise
        if max_right_x != width - 1 {
            max_right_x = (max_right_x + 1 .. width).rev() // excluding current max
                .find(|&x| pixel_at(Vec2(x, y)))
                .unwrap_or(max_right_x);
        }

        // search from left image edge towards image center, until known min x, for existing pixels,
        // possibly including some pixels that would have been cropped otherwise
        if min_left_x != 0 {
            min_left_x = (0 .. min_left_x) // excluding current min
                .find(|&x| pixel_at(Vec2(x, y)))
                .unwrap_or(min_left_x);
        }
    }

    // TODO add 1px margin to avoid interpolation issues?
    let local_start = Vec2(min_left_x, top);
    let local_end = Vec2(max_right_x + 1, bottom + 1);
    Some(IntegerBounds::new(
        current_bounds.position + local_start.to_i32(),
        local_end - local_start
    ))
}
/// Return the smallest bounding rectangle including all pixels that satisfy the predicate.
/// Worst case: Fully transparent image, visits each pixel once.
/// Best case: Fully opaque image, visits two pixels.
/// Returns `None` if the image is fully transparent.
/// Returns `[(0,0), size]` if the image is fully opaque.
/// Designed to be cache-friendly linear search. Optimized for row-major image vectors.
Return the smallest bounding rectangle including all pixels that satisfy the predicate.
Worst case: Fully transparent image, visits each pixel once.
Best case: Fully opaque image, visits two pixels.
Returns `None` if the image is fully transparent.
Returns `[(0,0), size]` if the image is fully opaque.
Designed to be cache-friendly linear search. Optimized for row-major image vectors.
Return the smallest bounding rectangle including all pixels that satisfy the predicate . Worst case : Fully transparent image visits each pixel once . Best case : Fully opaque image visits two pixels . Returns ` None ` if the image is fully transparent . Returns ` [ ( 0 0 ) size ] ` if the image is fully opaque . Designed to be cache - friendly linear search . Optimized for row - major image vectors .
pub fn try_find_smaller_bounds(current_bounds: IntegerBounds, pixel_at: impl Fn(Vec2<usize>) -> bool) -> Option<IntegerBounds> {
    assert_ne!(current_bounds.size.area(), 0, "cannot find smaller bounds of an image with zero width or height");
    let Vec2(width, height) = current_bounds.size;
    // scans top to bottom (left to right)
    let first_top_left_pixel = (0 .. height)
        .flat_map(|y| (0 .. width).map(move |x| Vec2(x,y)))
        .find(|&position| pixel_at(position))?; // return none if no pixel should be kept
    // scans bottom to top (right to left)
    let first_bottom_right_pixel = (first_top_left_pixel.y() + 1 .. height) // excluding the top line
        .flat_map(|y| (0 .. width).map(move |x| Vec2(x, y))) // x search cannot start at first_top.x, because this must catch all bottom pixels
        .rev().find(|&position| pixel_at(position))
        .unwrap_or(first_top_left_pixel); // did not find any at bottom, but we know top has some pixel
    // now we know exactly how much we can throw away top and bottom,
    // but we don't know exactly about left or right
    let top = first_top_left_pixel.y();
    let bottom = first_bottom_right_pixel.y();
    // we only now some arbitrary left and right bounds which we need to refine.
    // because the actual image contents might be wider than the corner points.
    // we know that we do not need to look in the center between min x and max x,
    // as these must be included in any case.
    let mut min_left_x = first_top_left_pixel.x().min(first_bottom_right_pixel.x());
    let mut max_right_x = first_bottom_right_pixel.x().max(first_top_left_pixel.x());
    // requires for loop, because bounds change while searching
    for y in top ..= bottom {
        // escape the loop if there is nothing left to crop
        if min_left_x == 0 && max_right_x == width - 1 { break; }
        // search from right image edge towards image center, until known max x, for existing pixels,
        // possibly including some pixels that would have been cropped otherwise
        if max_right_x != width - 1 {
            max_right_x = (max_right_x + 1 .. width).rev() // excluding current max
                .find(|&x| pixel_at(Vec2(x, y)))
                .unwrap_or(max_right_x);
        }
        // search from left image edge towards image center, until known min x, for existing pixels,
        // possibly including some pixels that would have been cropped otherwise
        if min_left_x != 0 {
            min_left_x = (0 .. min_left_x) // excluding current min
                .find(|&x| pixel_at(Vec2(x, y)))
                .unwrap_or(min_left_x);
        }
    }
    // TODO add 1px margin to avoid interpolation issues?
    let local_start = Vec2(min_left_x, top);
    let local_end = Vec2(max_right_x + 1, bottom + 1);
    Some(IntegerBounds::new(
        current_bounds.position + local_start.to_i32(),
        local_end - local_start
    ))
}
pub fn try_find_smaller_bounds ( current_bounds : IntegerBounds , pixel_at : impl Fn ( Vec2 < usize > ) -> bool ) -> Option < IntegerBounds > { assert_ne ! ( current_bounds size area ( ) 0 " " ) ; let Vec2 ( width , height ) = current_bounds . size ; // scans top to bottom (left to right) let first_top_left_pixel = ( 0 .. height ) . flat_map ( | y | ( 0 .. width ) . map ( move | x | Vec2 ( x , y ) ) ) . find ( | & position | pixel_at ( position ) ) ? ; // return none if no pixel should be kept // scans bottom to top (right to left) let first_bottom_right_pixel = ( first_top_left_pixel . y ( ) + 1 .. height ) // excluding the top line . flat_map ( | y | ( 0 .. width ) . map ( move | x | Vec2 ( x , y ) ) ) // x search cannot start at first_top.x, because this must catch all bottom pixels . rev ( ) . find ( | & position | pixel_at ( position ) ) . unwrap_or ( first_top_left_pixel ) ; // did not find any at bottom, but we know top has some pixel // now we know exactly how much we can throw away top and bottom, // but we don't know exactly about left or right let top = first_top_left_pixel . y ( ) ; let bottom = first_bottom_right_pixel . y ( ) ; // we only now some arbitrary left and right bounds which we need to refine. // because the actual image contents might be wider than the corner points. // we know that we do not need to look in the center between min x and max x, // as these must be included in any case. let mut min_left_x = first_top_left_pixel . x ( ) . min ( first_bottom_right_pixel . x ( ) ) ; let mut max_right_x = first_bottom_right_pixel . x ( ) . max ( first_top_left_pixel . x ( ) ) ; // requires for loop, because bounds change while searching for y in top ..= bottom { // escape the loop if there is nothing left to crop if min_left_x == 0 && max_right_x == width - 1 { break ; } // search from right image edge towards image center, until known max x, for existing pixels, // possibly including some pixels that would have been cropped otherwise if max_right_x != width - 1 { max_right_x = ( max_right_x + 1 .. width ) . rev ( ) // excluding current max . find ( | & x | pixel_at ( Vec2 ( x , y ) ) ) . unwrap_or ( max_right_x ) ; } // search from left image edge towards image center, until known min x, for existing pixels, // possibly including some pixels that would have been cropped otherwise if min_left_x != 0 { min_left_x = ( 0 .. min_left_x ) // excluding current min . find ( | & x | pixel_at ( Vec2 ( x , y ) ) ) . unwrap_or ( min_left_x ) ; } } // TODO add 1px margin to avoid interpolation issues? let local_start = Vec2 ( min_left_x , top ) ; let local_end = Vec2 ( max_right_x + 1 , bottom + 1 ) ; Some ( IntegerBounds :: new ( current_bounds . position + local_start . to_i32 ( ) , local_end - local_start ) ) }
Return the smallest bounding rectangle including all pixels that satisfy the predicate.
Return the smallest bounding rectangle including all pixels that satisfy the predicate .
current_bounds
IntegerBounds
pixel_at
impl Fn(Vec2<usize>) -> bool
current_bounds
IntegerBounds
pixel_at
impl Fn(Vec2<usize>) -> bool