From 7b4589624fa13e84e4683d7c622d04177bed2415 Mon Sep 17 00:00:00 2001 From: Plamen Dimitrov Date: Wed, 4 Jun 2025 18:18:48 +0800 Subject: [PATCH 1/2] Update to Rust edition 2024 with newer image crate dependency --- Cargo.toml | 4 ++-- src/bitmap.rs | 41 +++++++++++++++++++++-------------------- src/internal.rs | 6 +++--- src/screen.rs | 4 ++-- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5bbdff6..e8b4c78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "autopy" version = "4.0.1" -edition = "2021" +edition = "2024" authors = ["Michael Sanders "] [lib] @@ -16,7 +16,7 @@ version = "0.4.0" version = "0.23.5" [dependencies.image] -version = "0.22.4" +version = "0.25.6" [dependencies.either] version = "1.15.0" diff --git a/src/bitmap.rs b/src/bitmap.rs index 5b5d851..f820ff0 100644 --- a/src/bitmap.rs +++ b/src/bitmap.rs @@ -7,7 +7,7 @@ use autopilot::geometry::{Point, Rect, Size}; use image::Pixel; -use image::{ImageOutputFormat, ImageResult, Rgba}; +use image::{ImageFormat, ImageResult, Rgba}; use crate::internal::{rgb_to_hex, hex_to_rgb, FromImageError}; use pyo3::basic::CompareOp; use pyo3::prelude::*; @@ -57,7 +57,7 @@ impl Bitmap { return Err(PyBufferError::new_err("Object is not writable")); } - let bytes = &self.bitmap.image.raw_pixels(); + let bytes = &self.bitmap.image.as_bytes(); unsafe { (*view).buf = bytes.as_ptr() as *mut libc::c_void; @@ -112,7 +112,7 @@ impl Bitmap { let ref mut buffer = File::create(path)?; self.bitmap .image - .write_to(buffer, fmt) + .write_to(buffer, ImageFormat::from(fmt)) .map_err(FromImageError::from)?; Ok(()) } @@ -169,8 +169,8 @@ impl Bitmap { ))) } else { let rgb = self.bitmap.get_pixel(point); - let (r, g, b, _) = rgb.channels4(); - Ok(rgb_to_hex(r, g, b)) + let channels = rgb.channels(); + Ok(rgb_to_hex(channels[0], channels[1], channels[2])) } } @@ -418,29 +418,30 @@ enum AutoPyImageFormat { Unsupported, } -impl From for ImageOutputFormat { - fn from(format: AutoPyImageFormat) -> ImageOutputFormat { - use image::ImageOutputFormat::*; +impl From for ImageFormat { + fn from(format: AutoPyImageFormat) -> ImageFormat { + use image::ImageFormat::*; match format { - AutoPyImageFormat::BMP => BMP, - AutoPyImageFormat::GIF => GIF, - AutoPyImageFormat::JPEG => JPEG(100), - AutoPyImageFormat::PNG => PNG, + AutoPyImageFormat::BMP => Bmp, + AutoPyImageFormat::GIF => Gif, + AutoPyImageFormat::JPEG => Jpeg, + AutoPyImageFormat::PNG => Png, AutoPyImageFormat::Unsupported => { - Unsupported("This image format is unsupported by AutoPy".to_string()) + // choose default format on the image crate side + Bmp } } } } -impl From for AutoPyImageFormat { - fn from(format: ImageOutputFormat) -> AutoPyImageFormat { - use image::ImageOutputFormat::*; +impl From for AutoPyImageFormat { + fn from(format: ImageFormat) -> AutoPyImageFormat { + use image::ImageFormat::*; match format { - BMP => AutoPyImageFormat::BMP, - GIF => AutoPyImageFormat::GIF, - JPEG(_) => AutoPyImageFormat::JPEG, - PNG => AutoPyImageFormat::PNG, + Bmp => AutoPyImageFormat::BMP, + Gif => AutoPyImageFormat::GIF, + Jpeg => AutoPyImageFormat::JPEG, + Png => AutoPyImageFormat::PNG, _ => AutoPyImageFormat::Unsupported, } } diff --git a/src/internal.rs b/src/internal.rs index 95b8f61..3e43e7e 100644 --- a/src/internal.rs +++ b/src/internal.rs @@ -5,7 +5,7 @@ // https://opensource.org/licenses/MIT>, at your option. This file may not be // copied, modified, or distributed except according to those terms. -use image::ImageError; +use image::error::{ImageError, LimitErrorKind}; use pyo3::prelude::*; pub struct FromImageError(ImageError); @@ -30,8 +30,8 @@ impl From for FromImageError { impl From for PyErr { fn from(err: FromImageError) -> PyErr { match err.0 { - ImageError::DimensionError => { - pyo3::exceptions::PyValueError::new_err(format!("{}", err.0)) + ImageError::Limits(limit_err) if matches!(limit_err.kind(), LimitErrorKind::DimensionError) => { + pyo3::exceptions::PyValueError::new_err(format!("{}", limit_err)) } _ => pyo3::exceptions::PyIOError::new_err(format!("{}", err.0)), } diff --git a/src/screen.rs b/src/screen.rs index 1aab6d0..0f4c6d1 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -44,8 +44,8 @@ fn is_point_visible(x: f64, y: f64) -> PyResult { fn get_color(x: f64, y: f64) -> PyResult { let point = Point::new(x, y); let rgb = autopilot::screen::get_color(point).map_err(FromImageError::from)?; - let (r, g, b, _) = rgb.channels4(); - Ok(rgb_to_hex(r, g, b)) + let channels = rgb.channels(); + Ok(rgb_to_hex(channels[0], channels[1], channels[2])) } /// This module contains functions for working with the screen. From 8727b507640a79a311c62864fd48d2a507496357 Mon Sep 17 00:00:00 2001 From: Plamen Dimitrov Date: Wed, 3 Sep 2025 19:23:21 +0800 Subject: [PATCH 2/2] Throw a proper error when converting from unsupported image format The previous behavior used direct matching with an unsupported format enumerable variant but it no longer exists and TryFrom is the more idiomatic way to throw a well localized value error. --- src/bitmap.rs | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/src/bitmap.rs b/src/bitmap.rs index f820ff0..711a6e2 100644 --- a/src/bitmap.rs +++ b/src/bitmap.rs @@ -103,20 +103,12 @@ impl Bitmap { .or(Path::new(path).extension().and_then(|x| x.to_str())) .unwrap_or(""); let fmt = image_output_format_from_extension(format); - match fmt { - AutoPyImageFormat::Unsupported => Err(pyo3::exceptions::PyValueError::new_err(format!( - "Unknown format {}", - format - ))), - _ => { - let ref mut buffer = File::create(path)?; - self.bitmap - .image - .write_to(buffer, ImageFormat::from(fmt)) - .map_err(FromImageError::from)?; - Ok(()) - } - } + let ref mut buffer = File::create(path)?; + self.bitmap + .image + .write_to(buffer, ImageFormat::try_from(fmt)?) + .map_err(FromImageError::from)?; + Ok(()) } /// Copies image to pasteboard. Currently only supported on macOS. @@ -418,17 +410,18 @@ enum AutoPyImageFormat { Unsupported, } -impl From for ImageFormat { - fn from(format: AutoPyImageFormat) -> ImageFormat { +impl TryFrom for ImageFormat { + type Error = pyo3::PyErr; + + fn try_from(format: AutoPyImageFormat) -> Result { use image::ImageFormat::*; match format { - AutoPyImageFormat::BMP => Bmp, - AutoPyImageFormat::GIF => Gif, - AutoPyImageFormat::JPEG => Jpeg, - AutoPyImageFormat::PNG => Png, + AutoPyImageFormat::BMP => Ok(Bmp), + AutoPyImageFormat::GIF => Ok(Gif), + AutoPyImageFormat::JPEG => Ok(Jpeg), + AutoPyImageFormat::PNG => Ok(Png), AutoPyImageFormat::Unsupported => { - // choose default format on the image crate side - Bmp + Err(pyo3::exceptions::PyValueError::new_err("This image format is unsupported by AutoPy")) } } }