use std::ffi::{c_int, c_void};
use std::slice;
pub struct DecodedYuv {
pub width: u32,
pub height: u32,
pub y: Vec<u8>,
pub u: Vec<u8>,
pub v: Vec<u8>,
}
#[must_use]
pub fn libwebp_decode_yuv(webp: &[u8]) -> DecodedYuv {
let mut width: c_int = 0;
let mut height: c_int = 0;
let mut u_ptr: *mut u8 = std::ptr::null_mut();
let mut v_ptr: *mut u8 = std::ptr::null_mut();
let mut stride: c_int = 0;
let mut uv_stride: c_int = 0;
let y_ptr = unsafe {
libwebp_sys::WebPDecodeYUV(
webp.as_ptr(),
webp.len(),
&mut width,
&mut height,
&mut u_ptr,
&mut v_ptr,
&mut stride,
&mut uv_stride,
)
};
assert!(!y_ptr.is_null(), "libwebp YUV decode failed");
let (w, h) = (width as usize, height as usize);
let (cw, ch) = (w.div_ceil(2), h.div_ceil(2));
let copy_plane = |ptr: *const u8, row_stride: usize, pw: usize, ph: usize| {
let mut out = vec![0u8; pw * ph];
for row in 0..ph {
let src = unsafe { slice::from_raw_parts(ptr.add(row * row_stride), pw) };
out[row * pw..row * pw + pw].copy_from_slice(src);
}
out
};
let y = copy_plane(y_ptr, stride as usize, w, h);
let u = copy_plane(u_ptr, uv_stride as usize, cw, ch);
let v = copy_plane(v_ptr, uv_stride as usize, cw, ch);
unsafe { libwebp_sys::WebPFree(y_ptr.cast::<c_void>()) };
DecodedYuv {
width: width as u32,
height: height as u32,
y,
u,
v,
}
}
#[must_use]
pub fn libwebp_rgba_to_yuv(rgba: &[u8], width: u32, height: u32) -> DecodedYuv {
let expected = width as usize * height as usize * 4;
assert_eq!(
rgba.len(),
expected,
"RGBA buffer is not width*height*4 bytes"
);
unsafe {
let mut pic: libwebp_sys::WebPPicture = std::mem::zeroed();
assert!(
libwebp_sys::WebPPictureInit(&mut pic) != 0,
"WebPPictureInit failed"
);
pic.width = width as c_int;
pic.height = height as c_int;
assert!(
libwebp_sys::WebPPictureImportRGBA(&mut pic, rgba.as_ptr(), width as c_int * 4) != 0,
"WebPPictureImportRGBA failed"
);
let (w, h) = (width as usize, height as usize);
let (cw, ch) = (w.div_ceil(2), h.div_ceil(2));
let copy_plane = |ptr: *const u8, row_stride: usize, pw: usize, ph: usize| {
let mut out = vec![0u8; pw * ph];
for row in 0..ph {
let src = slice::from_raw_parts(ptr.add(row * row_stride), pw);
out[row * pw..row * pw + pw].copy_from_slice(src);
}
out
};
let y = copy_plane(pic.y, pic.y_stride as usize, w, h);
let u = copy_plane(pic.u, pic.uv_stride as usize, cw, ch);
let v = copy_plane(pic.v, pic.uv_stride as usize, cw, ch);
libwebp_sys::WebPPictureFree(&mut pic);
DecodedYuv {
width,
height,
y,
u,
v,
}
}
}
pub struct DecodedRgba {
pub width: u32,
pub height: u32,
pub rgba: Vec<u8>,
}
#[must_use]
pub fn libwebp_encode_lossless_rgba(rgba: &[u8], width: u32, height: u32) -> Vec<u8> {
let expected = width as usize * height as usize * 4;
assert_eq!(
rgba.len(),
expected,
"RGBA buffer is not width*height*4 bytes"
);
let mut out_ptr: *mut u8 = std::ptr::null_mut();
let stride = width as c_int * 4;
let size = unsafe {
libwebp_sys::WebPEncodeLosslessRGBA(
rgba.as_ptr(),
width as c_int,
height as c_int,
stride,
&mut out_ptr,
)
};
assert!(
size != 0 && !out_ptr.is_null(),
"libwebp lossless encode failed"
);
let bytes = unsafe { slice::from_raw_parts(out_ptr, size) }.to_vec();
unsafe { libwebp_sys::WebPFree(out_ptr.cast::<c_void>()) };
bytes
}
#[must_use]
pub fn libwebp_encode_lossy_rgba(rgba: &[u8], width: u32, height: u32, quality: f32) -> Vec<u8> {
let expected = width as usize * height as usize * 4;
assert_eq!(
rgba.len(),
expected,
"RGBA buffer is not width*height*4 bytes"
);
let mut out_ptr: *mut u8 = std::ptr::null_mut();
let stride = width as c_int * 4;
let size = unsafe {
libwebp_sys::WebPEncodeRGBA(
rgba.as_ptr(),
width as c_int,
height as c_int,
stride,
quality,
&mut out_ptr,
)
};
assert!(
size != 0 && !out_ptr.is_null(),
"libwebp lossy encode failed"
);
let bytes = unsafe { slice::from_raw_parts(out_ptr, size) }.to_vec();
unsafe { libwebp_sys::WebPFree(out_ptr.cast::<c_void>()) };
bytes
}
#[derive(Clone, Copy, Debug)]
pub struct LibwebpLossyConfig {
pub quality: f32,
pub filter_type: i32,
pub segments: i32,
pub method: i32,
pub filter_strength: i32,
}
extern "C" fn collect_writer(
data: *const u8,
data_size: usize,
picture: *const libwebp_sys::WebPPicture,
) -> c_int {
unsafe {
let out = &mut *((*picture).custom_ptr.cast::<Vec<u8>>());
out.extend_from_slice(slice::from_raw_parts(data, data_size));
}
1
}
#[must_use]
pub fn libwebp_encode_lossy_rgba_config(
rgba: &[u8],
width: u32,
height: u32,
cfg: &LibwebpLossyConfig,
) -> Vec<u8> {
let expected = width as usize * height as usize * 4;
assert_eq!(
rgba.len(),
expected,
"RGBA buffer is not width*height*4 bytes"
);
let mut out: Vec<u8> = Vec::new();
unsafe {
let mut config: libwebp_sys::WebPConfig = std::mem::zeroed();
assert!(
libwebp_sys::WebPConfigInit(&mut config) != 0,
"WebPConfigInit failed (version mismatch?)"
);
config.quality = cfg.quality;
config.filter_type = cfg.filter_type;
config.segments = cfg.segments;
config.method = cfg.method;
config.filter_strength = cfg.filter_strength;
assert!(
libwebp_sys::WebPValidateConfig(&config) != 0,
"WebPValidateConfig rejected {cfg:?}"
);
let mut pic: libwebp_sys::WebPPicture = std::mem::zeroed();
assert!(
libwebp_sys::WebPPictureInit(&mut pic) != 0,
"WebPPictureInit failed (version mismatch?)"
);
pic.width = width as c_int;
pic.height = height as c_int;
assert!(
libwebp_sys::WebPPictureImportRGBA(&mut pic, rgba.as_ptr(), width as c_int * 4) != 0,
"WebPPictureImportRGBA failed"
);
pic.writer = Some(collect_writer);
pic.custom_ptr = std::ptr::from_mut(&mut out).cast::<c_void>();
let ok = libwebp_sys::WebPEncode(&config, &mut pic);
libwebp_sys::WebPPictureFree(&mut pic);
assert!(ok != 0, "WebPEncode failed for {cfg:?}");
}
out
}
#[must_use]
pub fn libwebp_get_info(webp: &[u8]) -> Option<(u32, u32)> {
let mut width: c_int = 0;
let mut height: c_int = 0;
let ok =
unsafe { libwebp_sys::WebPGetInfo(webp.as_ptr(), webp.len(), &mut width, &mut height) };
if ok == 0 {
return None;
}
Some((width as u32, height as u32))
}
#[must_use]
pub fn libwebp_decode_rgba(webp: &[u8]) -> DecodedRgba {
let mut width: c_int = 0;
let mut height: c_int = 0;
let ptr =
unsafe { libwebp_sys::WebPDecodeRGBA(webp.as_ptr(), webp.len(), &mut width, &mut height) };
assert!(!ptr.is_null(), "libwebp decode failed");
let len = width as usize * height as usize * 4;
let rgba = unsafe { slice::from_raw_parts(ptr, len) }.to_vec();
unsafe { libwebp_sys::WebPFree(ptr.cast::<c_void>()) };
DecodedRgba {
width: width as u32,
height: height as u32,
rgba,
}
}
#[must_use]
pub fn pattern_rgba(width: u32, height: u32) -> Vec<u8> {
let mut rgba = Vec::with_capacity(width as usize * height as usize * 4);
for y in 0..height {
for x in 0..width {
rgba.push(((x * 7 + y * 3) & 0xff) as u8); rgba.push(((x ^ (y * 5)) & 0xff) as u8); rgba.push(((x * x + y) & 0xff) as u8); rgba.push(0xff); }
}
rgba
}
#[must_use]
pub fn photo_like_rgba(width: u32, height: u32, seed: u32) -> Vec<u8> {
let (w, h) = (i64::from(width).max(1), i64::from(height).max(1));
let hash = |x: i64, y: i64| -> i64 {
let mut v = x.wrapping_mul(374_761_393)
^ y.wrapping_mul(668_265_263)
^ i64::from(seed).wrapping_mul(2_246_822_519);
v = (v ^ (v >> 13)).wrapping_mul(1_274_126_177);
(v ^ (v >> 16)) & 0xff
};
let clamp = |v: i64| v.clamp(0, 255) as u8;
let mut rgba = Vec::with_capacity(width as usize * height as usize * 4);
for y in 0..h {
for x in 0..w {
let base_r = x * 200 / w + y * 30 / h;
let base_g = y * 200 / h + (x + y) * 20 / (w + h);
let base_b = (x + y) * 160 / (w + h) + 40;
let detail = (hash(x, y) - 128) / 12;
let edge = if (x * 3 / w) % 2 == 0 && (y * 3 / h) % 2 == 0 {
40
} else {
0
};
rgba.push(clamp(base_r + detail + edge)); rgba.push(clamp(base_g + detail)); rgba.push(clamp(base_b - detail + edge)); rgba.push(0xff); }
}
rgba
}