egui-sharkplayer 0.1.0

A hardware accelerated video player for egui using mpv
Documentation
use std::sync::Arc;

use crate::backend::{FramebufferSize, PlayerState};
use eframe::egui;
use eframe::glow::HasContext as _;
use tracing::error;

pub struct SharkPlayer<'a> {
    backend: &'a mut PlayerState,
}

impl<'a> SharkPlayer<'a> {
    pub fn new(backend: &'a mut PlayerState) -> Self { Self { backend } }
}

impl<'a> egui::Widget for SharkPlayer<'a> {
    fn ui(self, ui: &mut egui::Ui) -> egui::Response {
        let (rect, response) = ui.allocate_exact_size(ui.available_size(), egui::Sense::click());

        // Handle play/pause
        if response.clicked()
            && let Err(e) = self.backend.toggle_pause()
        {
            error!("Failed to toggle playback: {e}");
        }

        let (fb, fb_size) = match self.backend.get_current_framebuffer(FramebufferSize {
            width:  rect.width() as i32,
            height: rect.height() as i32,
        }) {
            Ok((fb, fb_sz)) => (fb, fb_sz),
            Err(e) if cfg!(debug_assertions) => {
                error!("Failed to get current framebuffer: {e}");
                return response;
            }
            Err(_) => return response,
        };

        let cb = eframe::egui_glow::CallbackFn::new(move |info, painter| {
            let gl = painter.gl();

            unsafe {
                let prev_read_fb = gl.get_parameter_i32(eframe::glow::READ_FRAMEBUFFER_BINDING);
                gl.bind_framebuffer(eframe::glow::READ_FRAMEBUFFER, Some(fb));

                let p_per_point = info.pixels_per_point;
                let screen_h = info.screen_size_px[1] as f32;

                gl.blit_framebuffer(
                    0,
                    0,
                    fb_size.width,
                    fb_size.height,
                    (rect.min.x * p_per_point) as i32,
                    (screen_h - rect.max.y * p_per_point) as i32,
                    (rect.max.x * p_per_point) as i32,
                    (screen_h - rect.min.y * p_per_point) as i32,
                    eframe::glow::COLOR_BUFFER_BIT,
                    eframe::glow::LINEAR,
                );

                let prev_fb =
                    std::num::NonZeroU32::new(prev_read_fb as u32).map(eframe::glow::NativeFramebuffer);
                gl.bind_framebuffer(eframe::glow::READ_FRAMEBUFFER, prev_fb);
            }
        });

        ui.painter().add(egui::PaintCallback {
            rect,
            callback: Arc::new(cb),
        });

        response
    }
}