C
PNG
PNG600C
| PNG | |
| PNG | |
C
size_t raw_size = (size_t)out->width * out->height; //
uint8_t* new_data = (uint8_t*)realloc(idat_data, idat_cap);
if (!new_data) goto error_cleanup; // reallocidat_data| inflate_simple | 50 | 1 | |
| png_load_internal | 150 | 2 | |
| MemReader | 40 | 3 | |
| parse_ihdr | 40 | 4 |
1Rust FFI
#[repr(C)]
pub struct PngImageFFI {
pub width: u32, pub height: u32,
pub bit_depth: u8, pub color_type: u8, pub channels: u8,
pub pixels: *mut u8,
}
extern "C" {
pub fn png_load(filepath: *const i8, out: *mut PngImageFFI) -> i32;
pub fn png_free(img: *mut PngImageFFI);
}
//
pub struct PngImage { pub width: u32, pub height: u32, pub pixels: Vec<u8>, /* ... */ }
pub fn load_png(path: &str) -> Result<PngImage, PngError> {
let c_path = CString::new(path).map_err(|e| PngError::FileError(format!("{}", e)))?;
let mut raw = PngImageFFI { /* zeroed */ };
let result = unsafe { png_load(c_path.as_ptr(), &mut raw) };
if result != 0 { return Err(/* ... */); }
let pixel_count = (raw.width as usize) * (raw.height as usize) * 4;
let pixels = unsafe { Vec::from_raw_parts(raw.pixels, pixel_count, pixel_count) };
// raw.pixelsRust Vecpng_free
Ok(PngImage { width: raw.width, height: raw.height, pixels, /* ... */ })
}2Rust
MemReader
Cif (mr->pos + 4 > mr->size) return -1; —
Rust
struct MemReader<'a> { data: &'a [u8], pos: usize }
impl<'a> MemReader<'a> {
fn remaining(&self) -> usize { self.data.len().saturating_sub(self.pos) }
fn read_u32(&mut self) -> Option<u32> {
if self.remaining() < 4 { return None; }
let bytes: [u8; 4] = self.data[self.pos..self.pos+4].try_into().unwrap();
self.pos += 4;
Some(u32::from_be_bytes(bytes))
}
}PNG
C (uint32_t)ihdr[0] << 24 | (uint32_t)ihdr[1] << 16 | ...
Rustu32::from_be_bytes([data[0], data[1], data[2], data[3]])
fn parse_ihdr(reader: &mut MemReader) -> Result<ImageHeader, PngError> {
let chunk_len = reader.read_u32().ok_or(PngError::FormatError("truncated".into()))?;
if chunk_len != 13 { return Err(PngError::FormatError("IHDR must be 13 bytes".into())); }
let chunk_type = reader.read_bytes(4).ok_or(...)?;
if chunk_type != b"IHDR" { return Err(...); }
let data = reader.read_bytes(13).ok_or(...)?;
let width = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
let height = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
//
width.checked_mul(height).ok_or(PngError::FormatError("image too large".into()))?;
Ok(ImageHeader { width, height, /* ... */ })
}PNG
Paeth — Cabs()
int p = (int)a + (int)b - (int)c;
int pa = abs(p - (int)a); // abs(int) Rust
let p = a as i32 + b as i32 - c as i32;
let pa = (p - a as i32).unsigned_abs() as u32; //
let pr = if pa <= pb && pa <= pc { a } else if pb <= pc { b } else { c };
data[idx] = data[idx].wrapping_add(pr); // PNG256Rust
pub fn load_png_rust(data: &[u8]) -> Result<PngImage, PngError> {
let mut reader = MemReader::new(data);
let sig = reader.read_bytes(8).ok_or(...)?;
if sig != PNG_SIGNATURE { return Err(...); }
let header = parse_ihdr(&mut reader)?;
let channels = 4;
let pixel_count = (header.width as usize).checked_mul(header.height as usize)
.ok_or(...)?;
let raw_size = pixel_count.checked_mul(channels).ok_or(...)?;
let mut idat_data = Vec::new();
loop { /* IDAT */ }
let mut pixels = vec![0u8; raw_size];
let decompressed = inflate_zlib(&idat_data)?;
apply_filters(&mut filtered, header.width, header.height, channels);
// filter
Ok(PngImage { width: header.width, height: header.height, pixels, /* ... */ })
}checked_mulCVE
PNGUnfilter
PNG5None, Sub, Up, Average, Paeth
Crow[x] += a x >= 4 Paeth abs(p - a) int
Rust
fn apply_filters(data: &mut [u8], width: u32, height: u32, channels: usize) {
let stride = width as usize * channels + 1;
for y in 0..height as usize {
let filter = data[y * stride];
let row_start = y * stride + 1;
let prev_start = if y > 0 { (y - 1) * stride + 1 } else { row_start + stride };
for x in 0..(width as usize * channels) {
let a = if x >= channels { data[row_start + x - channels] } else { 0 };
let b = if y > 0 { data[prev_start + x] } else { 0 };
let c = if x >= channels && y > 0 { data[prev_start + x - channels] } else { 0 };
let idx = row_start + x;
match filter {
1 => data[idx] = data[idx].wrapping_add(a),
2 => data[idx] = data[idx].wrapping_add(b),
3 => { data[idx] = data[idx].wrapping_add(((a as u16 + b as u16) / 2) as u8); }
4 => {
let p = a as i32 + b as i32 - c as i32;
let pa = (p - a as i32).unsigned_abs();
let pb = (p - b as i32).unsigned_abs();
let pc = (p - c as i32).unsigned_abs();
let pr = if pa <= pb && pa <= pc { a } else if pb <= pc { b } else { c };
data[idx] = data[idx].wrapping_add(pr);
}
_ => {}
}
}
}
}wrapping_add PNG256unsigned_abs()
1:
C
C
2: FFI
extern "C"
CRust Vec
3:
CRC32 → MemReader → → →
4:
Cunsafe
| C | Rust | ||
|---|---|---|---|
| 100x100 | 120us | 115us | -4% |
| 500x500 | 2800us | 2750us | -2% |
| 1000x1000 | 11200us | 10800us | -4% |
Rustchecked_mulslicereleaseLLVM