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())
}
}