///|
/// 8-bit RGBA image, row-major, top-left origin. Alpha is kept and ignored by
/// the recognition normalizer.
pub(all) struct Rgba {
width : Int32
height : Int32
pixels : Bytes
} derive(Eq, Debug)
///|
pub extend Rgba with Eq::{not_equal, equal}
///|
pub extend Rgba with @debug.Debug::{to_repr}
///|
/// Build an image from packed RGBA bytes.
///
/// # Example
/// ```mbt check
/// test {
/// let image = @paddleocr.Rgba::from_rgba(1, 1, [
/// b'\x01', b'\x02', b'\x03', b'\xff',
/// ])
/// inspect(image.width, content="1")
/// inspect(image.height, content="1")
/// }
/// ```
pub fn Rgba::from_rgba(
width : Int32,
height : Int32,
pixels : Bytes,
) -> Rgba raise OcrError {
if width <= 0 || height <= 0 {
raise OcrError::Image("image size must be positive")
}
if width > 1000000 || height > 1000000 {
raise OcrError::Image("image side is too large")
}
if pixels.length() != width * height * 4 {
raise OcrError::Image("rgba byte length does not match width*height*4")
}
{ width, height, pixels, }
}
///|
/// Crop a rectangle and clamp it to the image. The result is still RGBA.
pub fn Rgba::crop(
self : Rgba,
x : Int32,
y : Int32,
width : Int32,
height : Int32,
) -> Rgba raise OcrError {
if width <= 0 || height <= 0 {
raise OcrError::Image("crop is empty")
}
let x0 = if x < 0 { 0 } else { x }
let y0 = if y < 0 { 0 } else { y }
let x1 = if x + width > self.width { self.width } else { x + width }
let y1 = if y + height > self.height { self.height } else { y + height }
if x1 <= x0 || y1 <= y0 {
raise OcrError::Image("crop is empty")
}
let crop_width = x1 - x0
let crop_height = y1 - y0
let pixels = Bytes::makei(crop_width * crop_height * 4, fn(index) {
let pixel = index / 4
let channel = index % 4
let column = pixel % crop_width
let row = pixel / crop_width
let source = ((y0 + row) * self.width + (x0 + column)) * 4 + channel
self.pixels[source]
})
{ width: crop_width, height: crop_height, pixels, }
}