//! Image processing operations for Seven Segment OCR.
//!
//! This module contains image thresholding, morphology (dilation, erosion, opening, closing),
//! cropping, rotating, shearing, mirroring, and color conversion algorithms optimized
//! for enhancing seven-segment displays before recognition.

use crate::defines::{
    ABSOLUTE_THRESHOLD, DEBUG_OUTPUT, DO_ITERATIVE_THRESHOLD, Direction, LuminanceType, MAXRGB,
    SSOCR_BLACK, VERBOSE,
};
use image::{GenericImageView, Rgba, RgbaImage};

/// Clips an integer value to a given minimum and maximum bound.
///
/// # Arguments
/// * `val` - The value to clip.
/// * `min_val` - The lower bound.
/// * `max_val` - The upper bound.
#[inline]
pub fn clip(val: i32, min_val: i32, max_val: i32) -> i32 {
    if val < min_val {
        min_val
    } else if val > max_val {
        max_val
    } else {
        val
    }
}

/// Calculates the luminance of a pixel based on the specified [`LuminanceType`].
///
/// # Arguments
/// * `color` - The RGBA pixel.
/// * `lt` - The luminance formula/method to use.
pub fn get_lum(color: &Rgba<u8>, lt: LuminanceType) -> f64 {
    let r = color[0] as f64;
    let g = color[1] as f64;
    let b = color[2] as f64;
    match lt {
        LuminanceType::Rec709 => 0.2125 * r + 0.7154 * g + 0.0721 * b,
        LuminanceType::Rec601 => 0.299 * r + 0.587 * g + 0.114 * b,
        LuminanceType::Linear => (r + g + b) / 3.0,
        LuminanceType::Minimum => r.min(g).min(b),
        LuminanceType::Maximum => r.max(g).max(b),
        LuminanceType::Red => r,
        LuminanceType::Green => g,
        LuminanceType::Blue => b,
    }
}

/// Determines if a pixel's luminance value is considered set/active under a given threshold and foreground color.
///
/// # Arguments
/// * `value` - The pixel luminance value.
/// * `threshold` - The threshold percent (0.0 to 100.0).
/// * `foreground` - The foreground color representation (black/white).
pub fn is_pixel_set(value: f64, threshold: f64, foreground: u8) -> bool {
    let limit = threshold / 100.0 * MAXRGB;
    if foreground == SSOCR_BLACK {
        value < limit
    } else {
        value >= limit
    }
}

/// Computes the threshold value (in percent) within a subregion of the image.
///
/// Finds the minimum and maximum luminance in the subregion and calculates
/// a relative threshold using the given fraction.
///
/// # Arguments
/// * `img` - The input image.
/// * `fraction` - The fraction between 0.0 and 1.0 (threshold percent / 100.0).
/// * `lt` - The luminance formula to use.
/// * `x` - Left coordinate of the subregion.
/// * `y` - Top coordinate of the subregion.
/// * `w` - Width of the subregion (-1 for full image width).
/// * `h` - Height of the subregion (-1 for full image height).
pub fn get_threshold(
    img: &RgbaImage,
    fraction: f64,
    lt: LuminanceType,
    mut x: i32,
    mut y: i32,
    mut w: i32,
    mut h: i32,
) -> f64 {
    let width = img.width() as i32;
    let height = img.height() as i32;

    if w == -1 {
        w = width;
    }
    if h == -1 {
        h = height;
    }

    if x + w > width {
        x = width - w;
    }
    if y + h > height {
        y = height - h;
    }
    if x < 0 {
        x = 0;
    }
    if y < 0 {
        y = 0;
    }

    let mut minval = MAXRGB;
    let mut maxval = 0.0;

    for xi in 0..w {
        let px = x + xi;
        if px < 0 || px >= width {
            continue;
        }
        for yi in 0..h {
            let py = y + yi;
            if py < 0 || py >= height {
                continue;
            }
            let color = img.get_pixel(px as u32, py as u32);
            let lum = get_lum(color, lt);
            if lum < minval {
                minval = lum;
            }
            if lum > maxval {
                maxval = lum;
            }
        }
    }

    (minval + fraction * (maxval - minval)) * 100.0 / MAXRGB
}

/// Finds an optimal threshold using iterative clustering (similar to Otsu's method).
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The initial threshold.
/// * `lt` - The luminance formula to use.
pub fn iterative_threshold(img: &RgbaImage, thresh: f64, lt: LuminanceType) -> f64 {
    let mut new_thresh = thresh / 100.0;
    let width = img.width();
    let height = img.height();
    const EPSILON: f64 = 0.0000001;

    loop {
        let thresh_lum = MAXRGB * new_thresh;
        let old_thresh = new_thresh;
        let mut size_black = 0u64;
        let mut sum_black = 0u64;
        let mut size_white = 0u64;
        let mut sum_white = 0u64;

        for xi in 0..width {
            for yi in 0..height {
                let color = img.get_pixel(xi, yi);
                let lum = get_lum(color, lt).round() as u64;
                if (lum as f64) <= thresh_lum {
                    size_black += 1;
                    sum_black += lum;
                } else {
                    size_white += 1;
                    sum_white += lum;
                }
            }
        }

        if size_white == 0 || size_black == 0 {
            // Error case, return original thresh
            return thresh;
        }

        let avg_white = sum_white as f64 / size_white as f64;
        let avg_black = sum_black as f64 / size_black as f64;
        new_thresh = (avg_white + avg_black) / (2.0 * MAXRGB);

        if (new_thresh - old_thresh).abs() <= EPSILON {
            break;
        }
    }

    new_thresh * 100.0
}

/// Adjusts the threshold based on image properties and configuration flags.
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The current threshold.
/// * `lt` - The luminance formula to use.
/// * `flags` - Configuration flags.
/// * `force_update` - Forces threshold recalculation even if already adapted.
/// * `is_adapted` - Mutable reference tracking whether the threshold has been adapted.
pub fn adapt_threshold(
    img: &RgbaImage,
    thresh: f64,
    lt: LuminanceType,
    flags: u32,
    force_update: bool,
    is_adapted: &mut bool,
) -> f64 {
    let mut t = thresh;
    if *is_adapted && !force_update {
        if flags & DEBUG_OUTPUT != 0 {
            eprintln!("threshold is already adjusted to image");
        }
    } else if flags & ABSOLUTE_THRESHOLD == 0 {
        if flags & DEBUG_OUTPUT != 0 {
            eprint!("adjusting threshold to image: {:.2} ->", t);
        }
        t = get_threshold(img, thresh / 100.0, lt, 0, 0, -1, -1);
        if flags & DEBUG_OUTPUT != 0 {
            eprintln!(" {:.2}", t);
        }
        if flags & DO_ITERATIVE_THRESHOLD != 0 {
            if flags & DEBUG_OUTPUT != 0 {
                eprint!("doing iterative_thresholding: {:.2} ->", t);
            }
            t = iterative_threshold(img, t, lt);
            if flags & DEBUG_OUTPUT != 0 {
                eprintln!(" {:.2}", t);
            }
        }
        *is_adapted = true;
    }
    if (flags & VERBOSE != 0) || (flags & DEBUG_OUTPUT != 0) {
        eprintln!("using threshold {:.2}", t);
    }
    t
}

/// Finds the minimum and maximum luminance values in the image.
///
/// # Arguments
/// * `img` - The input image.
/// * `lt` - The luminance formula to use.
pub fn get_minmaxval(img: &RgbaImage, lt: LuminanceType) -> (f64, f64) {
    let mut min_val = MAXRGB;
    let mut max_val = 0.0;
    for pixel in img.pixels() {
        let lum = get_lum(pixel, lt).clamp(0.0, 255.0);
        if lum < min_val {
            min_val = lum;
        }
        if lum > max_val {
            max_val = lum;
        }
    }
    (min_val, max_val)
}

fn draw_fg_pixel(img: &mut RgbaImage, x: u32, y: u32, fg: u8) {
    img.put_pixel(x, y, Rgba([fg, fg, fg, 255]));
}

fn draw_bg_pixel(img: &mut RgbaImage, x: u32, y: u32, bg: u8) {
    img.put_pixel(x, y, Rgba([bg, bg, bg, 255]));
}

/// Applies a neighbor filter to set foreground/background pixels based on a mask threshold.
///
/// For each pixel, counts the number of set/active neighbors in a 3x3 window (including itself).
/// If count is >= `mask`, the pixel is set to foreground, otherwise background.
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The threshold percent.
/// * `lt` - The luminance formula.
/// * `mask` - Minimum number of set neighbor pixels required.
/// * `fg` - Foreground color value.
/// * `bg` - Background color value.
pub fn set_pixels_filter(
    img: &RgbaImage,
    thresh: f64,
    lt: LuminanceType,
    mask: i32,
    fg: u8,
    bg: u8,
) -> RgbaImage {
    let width = img.width();
    let height = img.height();
    let mut new_image = img.clone();

    for x in 0..width {
        for y in 0..height {
            let mut set_pixel = 0;
            for i in (x as i32 - 1)..=(x as i32 + 1) {
                for j in (y as i32 - 1)..=(y as i32 + 1) {
                    if i >= 0 && i < width as i32 && j >= 0 && j < height as i32 {
                        let color = img.get_pixel(i as u32, j as u32);
                        let lum = get_lum(color, lt);
                        if is_pixel_set(lum, thresh, fg) {
                            set_pixel += 1;
                        }
                    }
                }
            }
            if set_pixel >= mask {
                draw_fg_pixel(&mut new_image, x, y, fg);
            } else {
                draw_bg_pixel(&mut new_image, x, y, bg);
            }
        }
    }
    new_image
}

/// Iteratively applies the [`set_pixels_filter`] function `iter` times.
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The threshold percent.
/// * `lt` - The luminance formula.
/// * `mask` - Minimum number of set neighbor pixels required.
/// * `iter` - The number of iterations to apply the filter.
/// * `fg` - Foreground color value.
/// * `bg` - Background color value.
pub fn set_pixels_filter_iter(
    img: &RgbaImage,
    thresh: f64,
    lt: LuminanceType,
    mask: i32,
    iter: i32,
    fg: u8,
    bg: u8,
) -> RgbaImage {
    let mut temp = img.clone();
    for _ in 0..iter {
        temp = set_pixels_filter(&temp, thresh, lt, mask, fg, bg);
    }
    temp
}

/// Performs morphological dilation on the image.
///
/// This is implemented as `set_pixels_filter_iter` with a mask of 1.
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The threshold percent.
/// * `lt` - The luminance formula.
/// * `iter` - The number of dilation iterations.
/// * `fg` - Foreground color value.
/// * `bg` - Background color value.
pub fn dilation(
    img: &RgbaImage,
    thresh: f64,
    lt: LuminanceType,
    iter: i32,
    fg: u8,
    bg: u8,
) -> RgbaImage {
    set_pixels_filter_iter(img, thresh, lt, 1, iter, fg, bg)
}

/// Performs morphological erosion on the image.
///
/// This is implemented as `set_pixels_filter_iter` with a mask of 9.
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The threshold percent.
/// * `lt` - The luminance formula.
/// * `iter` - The number of erosion iterations.
/// * `fg` - Foreground color value.
/// * `bg` - Background color value.
pub fn erosion(
    img: &RgbaImage,
    thresh: f64,
    lt: LuminanceType,
    iter: i32,
    fg: u8,
    bg: u8,
) -> RgbaImage {
    set_pixels_filter_iter(img, thresh, lt, 9, iter, fg, bg)
}

/// Performs morphological closing (dilation followed by erosion).
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The threshold percent.
/// * `lt` - The luminance formula.
/// * `iter` - The number of closing iterations.
/// * `fg` - Foreground color value.
/// * `bg` - Background color value.
pub fn closing(
    img: &RgbaImage,
    thresh: f64,
    lt: LuminanceType,
    iter: i32,
    fg: u8,
    bg: u8,
) -> RgbaImage {
    let temp = dilation(img, thresh, lt, iter, fg, bg);
    erosion(&temp, thresh, lt, iter, fg, bg)
}

/// Performs morphological opening (erosion followed by dilation).
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The threshold percent.
/// * `lt` - The luminance formula.
/// * `iter` - The number of opening iterations.
/// * `fg` - Foreground color value.
/// * `bg` - Background color value.
pub fn opening(
    img: &RgbaImage,
    thresh: f64,
    lt: LuminanceType,
    iter: i32,
    fg: u8,
    bg: u8,
) -> RgbaImage {
    let temp = erosion(img, thresh, lt, iter, fg, bg);
    dilation(&temp, thresh, lt, iter, fg, bg)
}

/// Retains pixels only if they have a minimum number of set neighbors.
///
/// Differs from [`set_pixels_filter`] in that it only checks neighbors of
/// pixels that are already set/active, and the mask does not include the checked pixel itself.
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The threshold percent.
/// * `lt` - The luminance formula.
/// * `mask` - Minimum number of neighbor pixels required.
/// * `fg` - Foreground color value.
/// * `bg` - Background color value.
pub fn keep_pixels_filter(
    img: &RgbaImage,
    thresh: f64,
    lt: LuminanceType,
    mask: i32,
    fg: u8,
    bg: u8,
) -> RgbaImage {
    let width = img.width();
    let height = img.height();
    let mut new_image = img.clone();

    for x in 0..width {
        for y in 0..height {
            let mut set_pixel = 0;
            let color = img.get_pixel(x, y);
            let lum = get_lum(color, lt);
            if is_pixel_set(lum, thresh, fg) {
                for i in (x as i32 - 1)..=(x as i32 + 1) {
                    for j in (y as i32 - 1)..=(y as i32 + 1) {
                        if i >= 0 && i < width as i32 && j >= 0 && j < height as i32 {
                            let color = img.get_pixel(i as u32, j as u32);
                            let lum = get_lum(color, lt);
                            if is_pixel_set(lum, thresh, fg) {
                                set_pixel += 1;
                            }
                        }
                    }
                }
            }
            if set_pixel > mask {
                draw_fg_pixel(&mut new_image, x, y, fg);
            } else {
                draw_bg_pixel(&mut new_image, x, y, bg);
            }
        }
    }
    new_image
}

/// Removes isolated foreground pixels.
///
/// This is implemented as `keep_pixels_filter` with a mask of 1.
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The threshold percent.
/// * `lt` - The luminance formula.
/// * `fg` - Foreground color value.
/// * `bg` - Background color value.
pub fn remove_isolated(
    img: &RgbaImage,
    thresh: f64,
    lt: LuminanceType,
    fg: u8,
    bg: u8,
) -> RgbaImage {
    keep_pixels_filter(img, thresh, lt, 1, fg, bg)
}

/// Stretches the luminance values from the interval `[t1, t2]` to `[0, 255]`.
///
/// Values below `t1` are set to `0`, and values above `t2` are set to `255`.
///
/// # Arguments
/// * `img` - The input image.
/// * `t1` - The lower threshold bound (luminance value).
/// * `t2` - The upper threshold bound (luminance value).
/// * `lt` - The luminance formula to use.
pub fn gray_stretch(img: &RgbaImage, t1: f64, t2: f64, lt: LuminanceType) -> RgbaImage {
    if t1 >= t2 {
        eprintln!("error: gray_stretch(): t1={:.2} >= t2={:.2}", t1, t2);
        std::process::exit(99);
    }
    if t1 <= 0.0 {
        eprintln!("error: gray_stretch(): t1={:.2} <= 0.0", t1);
        std::process::exit(99);
    }
    if t2 >= MAXRGB {
        eprintln!("error: gray_stretch(): t2={:.2} >= 255.0", t2);
        std::process::exit(99);
    }

    let width = img.width();
    let height = img.height();
    let mut new_image = img.clone();

    for x in 0..width {
        for y in 0..height {
            let color = img.get_pixel(x, y);
            let lum = get_lum(color, lt);
            let alpha = color[3];

            let new_lum = if lum <= t1 {
                0
            } else if lum >= t2 {
                255
            } else {
                clip(((lum - t1) * 255.0 / (t2 - t1)) as i32, 0, 255) as u8
            };

            new_image.put_pixel(x, y, Rgba([new_lum, new_lum, new_lum, alpha]));
        }
    }
    new_image
}

/// Applies local adaptive thresholding over a moving window of size `ww` x `wh`.
///
/// # Arguments
/// * `img` - The input image.
/// * `t` - The threshold factor.
/// * `lt` - The luminance formula to use.
/// * `ww` - Width of the window.
/// * `wh` - Height of the window.
/// * `fg` - Foreground color value.
/// * `bg` - Background color value.
pub fn dynamic_threshold(
    img: &RgbaImage,
    t: f64,
    lt: LuminanceType,
    ww: i32,
    wh: i32,
    fg: u8,
    bg: u8,
) -> RgbaImage {
    let width = img.width();
    let height = img.height();
    let mut new_image = img.clone();

    for x in 0..width {
        for y in 0..height {
            let color = img.get_pixel(x, y);
            let lum = get_lum(color, lt);
            let thresh = get_threshold(
                img,
                t / 100.0,
                lt,
                x as i32 - ww / 2,
                y as i32 - ww / 2,
                ww,
                wh,
            );
            if is_pixel_set(lum, thresh, fg) {
                draw_fg_pixel(&mut new_image, x, y, fg);
            } else {
                draw_bg_pixel(&mut new_image, x, y, bg);
            }
        }
    }
    new_image
}

/// Converts the image to monochrome based on a global threshold.
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The threshold percent.
/// * `lt` - The luminance formula to use.
/// * `fg` - Foreground color value.
/// * `bg` - Background color value.
pub fn make_mono(img: &RgbaImage, thresh: f64, lt: LuminanceType, fg: u8, bg: u8) -> RgbaImage {
    let width = img.width();
    let height = img.height();
    let mut new_image = img.clone();

    for x in 0..width {
        for y in 0..height {
            let color = img.get_pixel(x, y);
            let lum = get_lum(color, lt);
            if is_pixel_set(lum, thresh, fg) {
                draw_fg_pixel(&mut new_image, x, y, fg);
            } else {
                draw_bg_pixel(&mut new_image, x, y, bg);
            }
        }
    }
    new_image
}

/// Adds a background-colored border of specified width around the image.
///
/// # Arguments
/// * `img` - The input image.
/// * `bdwidth` - Width of the border to add.
/// * `bg` - Background color value.
pub fn white_border(img: &RgbaImage, mut bdwidth: i32, bg: u8) -> RgbaImage {
    let width = img.width() as i32;
    let height = img.height() as i32;
    let mut new_image = img.clone();

    if bdwidth > width / 2 {
        bdwidth = width / 2;
    }
    if bdwidth > height / 2 {
        bdwidth = height / 2;
    }

    for x in 0..bdwidth {
        for y in x..(height - x) {
            new_image.put_pixel(x as u32, y as u32, Rgba([bg, bg, bg, 255]));
            new_image.put_pixel((width - 1 - x) as u32, y as u32, Rgba([bg, bg, bg, 255]));
        }
    }
    for y in 0..bdwidth {
        for x in y..(width - y) {
            new_image.put_pixel(x as u32, y as u32, Rgba([bg, bg, bg, 255]));
            new_image.put_pixel(x as u32, (height - 1 - y) as u32, Rgba([bg, bg, bg, 255]));
        }
    }

    new_image
}

/// Shears the image horizontally by a specified offset.
///
/// # Arguments
/// * `img` - The input image.
/// * `offset` - Horizontal shift offset (at the bottom) to the right.
/// * `bg` - Background color value.
pub fn shear(img: &RgbaImage, offset: i32, bg: u8) -> RgbaImage {
    let width = img.width() as i32;
    let height = img.height() as i32;
    let mut new_image = img.clone();

    for y in 1..height {
        let shift = y * offset / (height - 1);
        // copy pixels
        for x in (shift.max(0)..width).rev() {
            let src_x = x - shift;
            if src_x >= 0 && src_x < width {
                let color = img.get_pixel(src_x as u32, y as u32);
                new_image.put_pixel(x as u32, y as u32, *color);
            }
        }
        // fill with background
        for x in 0..shift.max(0).min(width) {
            draw_bg_pixel(&mut new_image, x as u32, y as u32, bg);
        }
    }
    new_image
}

/// Rotates the image clockwise by a specified angle in degrees.
///
/// # Arguments
/// * `img` - The input image.
/// * `theta_deg` - Clockwise rotation angle in degrees.
/// * `bg` - Background color value.
pub fn rotate(img: &RgbaImage, theta_deg: f64, bg: u8) -> RgbaImage {
    let width = img.width() as i32;
    let height = img.height() as i32;
    let mut new_image = img.clone();

    let theta = theta_deg / 360.0 * 2.0 * std::f64::consts::PI;

    for x in 0..width {
        for y in 0..height {
            let sx = ((x - width / 2) as f64 * theta.cos()
                + (y - height / 2) as f64 * theta.sin()
                + (width / 2) as f64) as i32;
            let sy = ((y - height / 2) as f64 * theta.cos() - (x - width / 2) as f64 * theta.sin()
                + (height / 2) as f64) as i32;
            if sx >= 0 && sx < width && sy >= 0 && sy < height {
                let c = img.get_pixel(sx as u32, sy as u32);
                new_image.put_pixel(x as u32, y as u32, *c);
            } else {
                draw_bg_pixel(&mut new_image, x as u32, y as u32, bg);
            }
        }
    }
    new_image
}

/// Mirrors the image horizontally or vertically.
///
/// # Arguments
/// * `img` - The input image.
/// * `direction` - Mirroring direction ([`Direction::Horizontal`] or [`Direction::Vertical`]).
pub fn mirror(img: &RgbaImage, direction: Direction) -> RgbaImage {
    let width = img.width();
    let height = img.height();
    let mut new_image = img.clone();

    if direction == Direction::Horizontal {
        for x in 0..width {
            for y in 0..height {
                let c = img.get_pixel(width - 1 - x, y);
                new_image.put_pixel(x, y, *c);
            }
        }
    } else {
        for x in 0..width {
            for y in 0..height {
                let c = img.get_pixel(x, height - 1 - y);
                new_image.put_pixel(x, y, *c);
            }
        }
    }
    new_image
}

/// Converts the image to grayscale using the specified luminance formula.
///
/// # Arguments
/// * `img` - The input image.
/// * `lt` - The luminance formula to use.
pub fn grayscale(img: &RgbaImage, lt: LuminanceType) -> RgbaImage {
    let width = img.width();
    let height = img.height();
    let mut new_image = img.clone();

    for x in 0..width {
        for y in 0..height {
            let color = img.get_pixel(x, y);
            let lum = clip(get_lum(color, lt) as i32, 0, 255) as u8;
            new_image.put_pixel(x, y, Rgba([lum, lum, lum, color[3]]));
        }
    }
    new_image
}

/// Inverts the monochrome image (swapping foreground and background colors).
///
/// # Arguments
/// * `img` - The input image.
/// * `thresh` - The threshold percent.
/// * `lt` - The luminance formula to use.
/// * `fg` - Foreground color value.
/// * `bg` - Background color value.
pub fn invert(img: &RgbaImage, thresh: f64, lt: LuminanceType, fg: u8, bg: u8) -> RgbaImage {
    let width = img.width();
    let height = img.height();
    let mut new_image = img.clone();

    for x in 0..width {
        for y in 0..height {
            let color = img.get_pixel(x, y);
            let lum = get_lum(color, lt);
            if is_pixel_set(lum, thresh, fg) {
                draw_bg_pixel(&mut new_image, x, y, bg);
            } else {
                draw_fg_pixel(&mut new_image, x, y, fg);
            }
        }
    }
    new_image
}

/// Crops a subregion from the image.
///
/// # Arguments
/// * `img` - The input image.
/// * `x` - Left coordinate of the crop box.
/// * `y` - Top coordinate of the crop box.
/// * `w` - Width of the crop box.
/// * `h` - Height of the crop box.
pub fn crop(img: &RgbaImage, mut x: i32, mut y: i32, mut w: i32, mut h: i32) -> RgbaImage {
    let width = img.width() as i32;
    let height = img.height() as i32;

    if x < 0 {
        x = 0;
    }
    if y < 0 {
        y = 0;
    }
    if x >= width {
        x = width - 1;
    }
    if y >= height {
        y = height - 1;
    }
    if x + w > width {
        w = width - x;
    }
    if y + h > height {
        h = height - y;
    }

    if w <= 0 || h <= 0 {
        return RgbaImage::new(1, 1);
    }

    let sub = img.view(x as u32, y as u32, w as u32, h as u32);
    sub.to_image()
}
