pub fn decode_step_binary(s: &str) -> Vec<u8> {
let s = s.trim().trim_matches('"');
if s.len() < 3 {
return Vec::new();
}
let hex = s.as_bytes();
let mut out = Vec::with_capacity(hex.len() / 2);
let mut i = 1;
while i + 1 < hex.len() {
match (hex_val(hex[i]), hex_val(hex[i + 1])) {
(Some(h), Some(l)) => out.push((h << 4) | l),
_ => break,
}
i += 2;
}
out
}
#[inline]
fn hex_val(b: u8) -> Option<u8> {
match b {
b'0'..=b'9' => Some(b - b'0'),
b'a'..=b'f' => Some(b - b'a' + 10),
b'A'..=b'F' => Some(b - b'A' + 10),
_ => None,
}
}
pub(super) const MAX_TEX_DIM: u32 = 16384;
fn decode_png(bytes: &[u8]) -> Option<(Vec<u8>, u32, u32)> {
let mut decoder = png::Decoder::new(std::io::Cursor::new(bytes));
decoder.set_transformations(png::Transformations::EXPAND | png::Transformations::STRIP_16);
let mut reader = decoder.read_info().ok()?;
let png_info = reader.info();
if png_info.width == 0
|| png_info.height == 0
|| png_info.width > MAX_TEX_DIM
|| png_info.height > MAX_TEX_DIM
{
return None;
}
let mut buf = vec![0u8; reader.output_buffer_size()?];
let info = reader.next_frame(&mut buf).ok()?;
let (w, h) = (info.width, info.height);
let px = (w as usize) * (h as usize);
let src = &buf[..info.buffer_size()];
let mut rgba = Vec::with_capacity(px * 4);
match info.color_type {
png::ColorType::Rgba => rgba.extend_from_slice(&src[..px * 4]),
png::ColorType::Rgb => {
for c in src.chunks_exact(3) {
rgba.extend_from_slice(&[c[0], c[1], c[2], 255]);
}
}
png::ColorType::Grayscale => {
for &g in src.iter() {
rgba.extend_from_slice(&[g, g, g, 255]);
}
}
png::ColorType::GrayscaleAlpha => {
for c in src.chunks_exact(2) {
rgba.extend_from_slice(&[c[0], c[0], c[0], c[1]]);
}
}
png::ColorType::Indexed => return None,
}
if rgba.len() != px * 4 {
return None;
}
Some((rgba, w, h))
}
fn decode_jpeg(bytes: &[u8]) -> Option<(Vec<u8>, u32, u32)> {
let mut decoder = jpeg_decoder::Decoder::new(bytes);
decoder.read_info().ok()?;
let info = decoder.info()?;
if info.width == 0
|| info.height == 0
|| info.width as u32 > MAX_TEX_DIM
|| info.height as u32 > MAX_TEX_DIM
{
return None;
}
let pixels = decoder.decode().ok()?;
let (w, h) = (info.width as usize, info.height as usize);
let px = w * h;
let mut rgba = Vec::with_capacity(px * 4);
match info.pixel_format {
jpeg_decoder::PixelFormat::RGB24 => {
for c in pixels.chunks_exact(3) {
rgba.extend_from_slice(&[c[0], c[1], c[2], 255]);
}
}
jpeg_decoder::PixelFormat::L8 => {
for &g in pixels.iter() {
rgba.extend_from_slice(&[g, g, g, 255]);
}
}
_ => return None,
}
if rgba.len() != px * 4 {
return None;
}
Some((rgba, w as u32, h as u32))
}
pub(super) fn decode_raster_image(bytes: &[u8]) -> Option<(Vec<u8>, u32, u32)> {
const PNG_MAGIC: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
if bytes.len() >= 8 && bytes[..8] == PNG_MAGIC {
return decode_png(bytes);
}
if bytes.len() >= 3 && bytes[0] == 0xFF && bytes[1] == 0xD8 && bytes[2] == 0xFF {
return decode_jpeg(bytes);
}
None
}