Skip to content

Instantly share code, notes, and snippets.

@lubosz
Created February 15, 2026 21:41
Show Gist options
  • Select an option

  • Save lubosz/a65c6daba314c50ef124a8ce6baabee4 to your computer and use it in GitHub Desktop.

Select an option

Save lubosz/a65c6daba314c50ef124a8ce6baabee4 to your computer and use it in GitHub Desktop.
Rust port of minimalized heif_view.cc from libheif examples.
extern crate sdl3;
use std::env;
use libheif_rs::{HeifContext, DecodingOptions, ColorSpace, Chroma, HeifErrorCode, HeifError};
fn main() -> Result<(), ()> {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
eprintln!("Usage: {} <input_filename>", args[0]);
return Err(());
}
let ctx = HeifContext::read_from_file(&args[1]).expect("Could not load image.");
let sdl_context = sdl3::init().expect("Failed to init sdl context.");
let video_subsystem = sdl_context.video().expect("Failed to init video subsystem.");
if !ctx.has_sequence() {
eprintln!("File contains no image sequence");
return Err(());
}
// Get visual track
let track = ctx.track(0);
let image_resolution = track.image_resolution().expect("Failed to get image resoltion.");
// Reduce image size to a multiple of 8
let w = (image_resolution.width & !7) as u32;
let h = (image_resolution.height & !7) as u32;
let window = video_subsystem
.window("heif-view", w, h)
.build()
.map_err(|e| e.to_string()).unwrap();
let mut canvas = window.into_canvas();
let texture_creator = canvas.texture_creator();
let mut texture = texture_creator.create_texture(sdl3::pixels::PixelFormat::YV12, sdl3::render::TextureAccess::Streaming, w, h).unwrap();
// Decoding loop
let start_time = sdl3::timer::ticks();
let mut next_frame_pts: u64 = 0;
loop {
let mut decoding_options = DecodingOptions::new().unwrap();
decoding_options.set_convert_hdr_to_8bit(true);
match track.decode_next_image(ColorSpace::YCbCr(Chroma::C420), Some(decoding_options)) {
Ok(image) => {
// Wait for image presentation time
let duration_ms = (image.duration() as u64) * 1000 / (track.timescale() as u64);
let now_time = sdl3::timer::ticks();
let elapsed_time = now_time - start_time;
if elapsed_time < next_frame_pts {
sdl3::timer::delay((next_frame_pts - elapsed_time) as u32);
}
next_frame_pts += duration_ms;
// Display image
let planes = image.planes();
let p_y = planes.y.unwrap();
let p_cb = planes.cb.unwrap();
let p_cr = planes.cr.unwrap();
texture.with_lock(None, |buffer: &mut [u8], pitch: usize| {
let rect_w = w as usize;
let rect_h = h as usize;
if p_y.stride == pitch && p_cb.stride == pitch / 2 {
// Fast copy
let y_size = rect_w * rect_h;
let chroma_size = y_size / 4;
buffer[..y_size].copy_from_slice(&p_y.data[..y_size]);
let cr_start = y_size;
let cr_end = cr_start + chroma_size;
buffer[cr_start..cr_end].copy_from_slice(&p_cr.data[..chroma_size]);
let cb_start = cr_end;
let cb_end = cb_start + chroma_size;
buffer[cb_start..cb_end].copy_from_slice(&p_cb.data[..chroma_size]);
} else {
// Copy line by line because sizes are different
let mut offset = 0;
for y in 0..rect_h {
let src_start = y * p_y.stride;
let dest_start = offset;
buffer[dest_start..dest_start + rect_w].copy_from_slice(&p_y.data[src_start..src_start + rect_w]);
offset += pitch;
}
let chroma_w = rect_w / 2;
let chroma_h = rect_h / 2;
let chroma_pitch = pitch / 2;
for y in 0..chroma_h {
let src_start = y * p_cr.stride;
let dest_start = offset;
buffer[dest_start..dest_start + chroma_w].copy_from_slice(&p_cr.data[src_start..src_start + chroma_w]);
offset += chroma_pitch;
}
for y in 0..chroma_h {
let src_start = y * p_cb.stride;
let dest_start = offset;
buffer[dest_start..dest_start + chroma_w].copy_from_slice(&p_cb.data[src_start..src_start + chroma_w]);
offset += chroma_pitch;
}
}
}).expect("Failed to write to locked texture.");
canvas.copy(&texture, None, None).expect("Failed to copy texture to canvas.");
canvas.present();
}
Err(HeifError { code: HeifErrorCode::EndOfSequence, .. }) => {
break;
}
Err(e) => {
println!("Unexpected error occurred: {:?}", e);
break;
}
}
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment