irondash_texture 0.5.0

Rust Bindings for Flutter External Textures.
Documentation
use std::{cell::RefCell, marker::PhantomData, slice, sync::Arc};

use irondash_engine_context::EngineContext;
use jni::objects::{GlobalRef, JObject};
use ndk_sys::{
    AHardwareBuffer_Format, ANativeWindow, ANativeWindow_Buffer, ANativeWindow_acquire,
    ANativeWindow_fromSurface, ANativeWindow_lock, ANativeWindow_release,
    ANativeWindow_setBuffersGeometry, ANativeWindow_unlockAndPost,
};

use crate::{
    log::OkLog, BoxedPixelData, PayloadProvider, PixelFormat, PlatformTextureWithProvider,
    PlatformTextureWithoutProvider, Result,
};

#[derive(PartialEq, Eq, Clone, Copy)]
struct Geometry {
    width: i32,
    height: i32,
    format: i32,
}

pub struct PlatformTexture<Type> {
    id: i64,
    texture_entry: GlobalRef,
    surface: GlobalRef,
    native_window: *mut ANativeWindow,
    last_geometry: RefCell<Option<Geometry>>,
    pixel_data_provider: Option<Arc<dyn PayloadProvider<BoxedPixelData>>>,
    _phantom: PhantomData<Type>,
}

pub(crate) const PIXEL_DATA_FORMAT: PixelFormat = PixelFormat::RGBA;

impl<Type> PlatformTexture<Type> {
    pub fn id(&self) -> i64 {
        self.id
    }

    fn new(
        engine_handle: i64,
        pixel_buffer_provider: Option<Arc<dyn PayloadProvider<BoxedPixelData>>>,
    ) -> Result<Self> {
        let java_vm = EngineContext::get_java_vm()?;
        let mut env = java_vm.attach_current_thread()?;
        let engine_context = EngineContext::get()?;
        let texture_registry = engine_context.get_texture_registry(engine_handle)?;
        let texture_entry = env
            .call_method(
                texture_registry.as_obj(),
                "createSurfaceTexture",
                "()Lio/flutter/view/TextureRegistry$SurfaceTextureEntry;",
                &[],
            )?
            .l()?;
        let surface_texture = env
            .call_method(
                &texture_entry,
                "surfaceTexture",
                "()Landroid/graphics/SurfaceTexture;",
                &[],
            )?
            .l()?;
        let surface_class = env.find_class("android/view/Surface")?;

        env.push_local_frame(16)?;

        let surface = env.new_object(
            surface_class,
            "(Landroid/graphics/SurfaceTexture;)V",
            &[(&surface_texture).into()],
        )?;

        let native_window =
            unsafe { ANativeWindow_fromSurface(env.get_native_interface(), surface.as_raw()) };

        let id = env.call_method(&texture_entry, "id", "()J", &[])?.j()?;

        let res = Self {
            id,
            texture_entry: env.new_global_ref(texture_entry)?,
            surface: env.new_global_ref(surface)?,
            native_window,
            last_geometry: RefCell::new(None),
            pixel_data_provider: pixel_buffer_provider,
            _phantom: PhantomData {},
        };
        unsafe {
            env.pop_local_frame(&JObject::null())?;
        }
        Ok(res)
    }

    fn destroy(&mut self) -> Result<()> {
        let java_vm = EngineContext::get_java_vm()?;
        let mut env = java_vm.attach_current_thread()?;
        env.call_method(self.texture_entry.as_obj(), "release", "()V", &[])?;
        unsafe {
            ANativeWindow_release(self.native_window);
        }
        Ok(())
    }

    pub fn mark_frame_available(&self) -> Result<()> {
        if let Some(provider) = self.pixel_data_provider.as_ref() {
            let payload = provider.get_payload();
            let payload = payload.get();
            let geometry = Geometry {
                width: payload.width,
                height: payload.height,
                format: AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM.0 as i32,
            };
            let mut last_geometry = self.last_geometry.borrow_mut();
            if *last_geometry != Some(geometry) {
                unsafe {
                    ANativeWindow_setBuffersGeometry(
                        self.native_window,
                        geometry.width,
                        geometry.height,
                        geometry.format,
                    );
                }
                last_geometry.replace(geometry);
            }
            let mut buf: ANativeWindow_Buffer = unsafe { std::mem::zeroed() };

            let data = unsafe {
                ANativeWindow_lock(self.native_window, &mut buf as *mut _, std::ptr::null_mut());
                slice::from_raw_parts_mut(
                    buf.bits as *mut u8,
                    (buf.height * buf.stride * 4) as usize,
                )
            };

            if buf.stride == buf.width {
                assert!(buf.stride * buf.height * 4 == payload.data.len() as i32);
                data.copy_from_slice(payload.data);
            } else {
                let src_stride = payload.width * 4;
                let dst_stride = buf.stride * 4;
                let min_stride = std::cmp::min(src_stride, dst_stride);
                let mut src_offset: usize = 0;
                let mut dst_offset: usize = 0;
                for _ in 0..payload.height {
                    let src_slice = &payload.data[src_offset..src_offset + min_stride as usize];
                    let dst_slice = &mut data[dst_offset..dst_offset + min_stride as usize];
                    dst_slice.copy_from_slice(src_slice);
                    src_offset += src_stride as usize;
                    dst_offset += dst_stride as usize;
                }
            }

            unsafe { ANativeWindow_unlockAndPost(self.native_window) };
        }
        Ok(())
    }
}

impl<Type> Drop for PlatformTexture<Type> {
    fn drop(&mut self) {
        self.destroy().ok_log();
    }
}

impl PlatformTextureWithProvider for BoxedPixelData {
    fn create_texture(
        engine_handle: i64,
        payload_provider: Arc<dyn PayloadProvider<Self>>,
    ) -> Result<PlatformTexture<BoxedPixelData>> {
        PlatformTexture::new(engine_handle, Some(payload_provider))
    }
}

pub struct NativeWindow {
    native_window: *mut ANativeWindow,
}

impl NativeWindow {
    fn new(native_window: *mut ANativeWindow) -> Self {
        unsafe { ANativeWindow_acquire(native_window) };
        Self { native_window }
    }

    pub fn get_native_window(&self) -> *mut ANativeWindow {
        self.native_window
    }
}

impl Clone for NativeWindow {
    fn clone(&self) -> Self {
        Self::new(self.native_window)
    }
}

impl Drop for NativeWindow {
    fn drop(&mut self) {
        unsafe {
            ANativeWindow_release(self.native_window);
        }
    }
}

impl PlatformTextureWithoutProvider for NativeWindow {
    fn create_texture(engine_handle: i64) -> Result<PlatformTexture<NativeWindow>> {
        PlatformTexture::new(engine_handle, None)
    }

    fn get(texture: &PlatformTexture<Self>) -> Self {
        Self::new(texture.native_window)
    }
}

pub struct Surface(pub GlobalRef);

impl PlatformTextureWithoutProvider for Surface {
    fn create_texture(engine_handle: i64) -> Result<PlatformTexture<Surface>> {
        PlatformTexture::new(engine_handle, None)
    }

    fn get(texture: &PlatformTexture<Self>) -> Self {
        Self(texture.surface.clone())
    }
}