Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

1
vendor/ndk-0.8.0/.cargo-checksum.json vendored Normal file
View File

@@ -0,0 +1 @@
{"files":{"CHANGELOG.md":"ce751cc9ed5dc25d0e36c8f126afc8a02926a2d2b00fd5dae92dd159bcf8044d","Cargo.toml":"d910dadd28722aa016a6e4b2eb2be5b8f3c09577e2030fd6e4b2b8c7a674cf52","README.md":"16d18a45d1b4ae9b2830d0f9ac747ddb2d7af54a2821d33e3be558f71056e5fd","src/asset.rs":"d12fe272759c5c48807a472841d619645bbfb366744749167dbd684c38e9f78b","src/audio.rs":"aa24ceaedfff7ed8bf3cb852a27e6f848d889f199b3b18fe5fde777ba3e10d3e","src/bitmap.rs":"520414371d5dd34f83e1b887346c9d8cc8ec5616536a6ff31887b3dc39de5630","src/configuration.rs":"bb47bb175c2e3eb9869c5391b5a8180a5c09dea82459bab55296df89588c66e6","src/data_space.rs":"296c966c84165653868894bc7b64fe57be3a36095684bd1425464f713beb120c","src/event.rs":"a5e0ced23a1bff23086a7b0f37164a2baf45d122d2c4ca11f1d0b1759d4c4c8b","src/font.rs":"d6cbdef45260f2b8be7e5c15c911e8b30a01a0d0492f51690e703e95eadc10f7","src/hardware_buffer.rs":"70e07d5c6c4bfbebd990118dd51b3a2478ea2c0d222418e057a6ce53fe7dff8a","src/hardware_buffer_format.rs":"001e2506dba90c9139403931f1583b088cdbf9f292fe20d6af822b9d8539102e","src/input_queue.rs":"dd32fcd473c72f2f82f57e80af3e183983350d9e1c2566fa9630b863b9212bf1","src/lib.rs":"e36fe74be270e989dc0d0208828b3bcc96488118ea08228edbeaa497f113b243","src/looper.rs":"5ef79c61414f8247919a5122486e8eedbe02fdf4a165ccaab54f69be43cd5106","src/media/image_reader.rs":"6ff2f0b26ca4cf753b238a7784eb4826a6378fe3cb8a233ff2986b96b50f4fde","src/media/media_codec.rs":"9b6d2d83ee4dbf2935980eab267a09ff4983a764d0e97f5defd9b854730061b8","src/media/mod.rs":"1de19b670dd525707d0d3583023c61ab8cde4e9fd978fb463507dfc475ae7c9b","src/media_error.rs":"ec930e69be0482b1bfe6dd9995bcb440e62f798474104d25f6415ee07207ec3e","src/native_activity.rs":"18e76f6098e8a0f217176fdfa76dae8fa0411c1db2bd67cb734a9810da8ae507","src/native_window.rs":"400af219aeb7b66e0cdfe21b57a4095cb8360d2e977344dd45079b1ce13fdd78","src/shared_memory.rs":"59bd11f04b18e8e5310f1149d0b57e6efeb5c7bb9ec2e725aa64594b4a537ca7","src/surface_texture.rs":"2d2a37361fd0b01a4b99b9c21dad3de16a2a703088f5baaf4fc4636e9fc219e7","src/sync.rs":"415b66a2b9d441a69171b1cb69291f0ed75a7257589aad13e4dcef0689daae06","src/trace.rs":"c546adaf3e065cfac2650f6113a9a03a4142639f8d38d27fdc6404f0c00cc601","src/utils.rs":"3c8cd12af90ea3bea30e0ac836a1b91d583e78c69a648675d31b6d7f348b6d5f"},"package":"2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"}

106
vendor/ndk-0.8.0/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,106 @@
# Unreleased
# 0.8.0 (2023-10-15)
- event: Add `tool_type` getter for `Pointer`. (#323)
- input_queue: Allow any non-zero return code from `pre_dispatch()` again, as per documentation. (#325)
- asset: Use entire asset length when mapping buffer. (#387)
- Bump MSRV to 1.66 for `raw-window-handle 0.5.1`, `num_enum`'s `catch_all` with arbitrary enum discriminants. (#388, #431)
- Bump optional `jni` dependency for doctest example from `0.19` to `0.21`. (#390)
- **Breaking:** Upgrade to [`ndk-sys 0.5.0`](../ndk-sys/CHANGELOG.md#050-TODO). (#370)
- **Breaking:** Upgrade `bitflags` crate from `1` to `2`. (#394)
- bitmap: Add `try_format()` to `AndroidBitmapInfo` to handle unexpected formats without panicking. (#395)
- Add `Font` bindings. (#397)
- **Breaking:** Upgrade `num_enum` crate from `0.5.1` to `0.7`. (#398, #419)
- **Breaking:** Renamed, moved and flattened "`media`" error types and helpers to a new `media_error` module. (#399, #432)
- **Breaking:** media_codec: Wrap common dequeued-buffer status codes in enum. (#401)
- **Breaking:** media_codec: Return `MaybeUninit` bytes in `buffer_mut()`. (#403)
- native_window: Add `lock()` to blit raw pixel data. (#404)
- hardware_buffer_format: Add `YCbCr_P010` and `R8_UNORM` variants. (#405)
- **Breaking:** hardware_buffer_format: Add catch-all variant. (#407)
- asset: Add missing `is_allocated()` and `open_file_descriptor()` methods. (#409)
- **Breaking:** media_codec: Add support for asynchronous notification callbacks. (#410)
- Add panic guards to callbacks. (#412)
- looper: Add `remove_fd()` to unregister events/callbacks for a file descriptor. (#416)
- **Breaking:** Use `BorrowedFd` and `OwnedFd` to clarify possible ownership transitions. (#417)
- **Breaking:** Upgrade to [`ndk-sys 0.5.0`](../ndk-sys/CHANGELOG.md#050-2023-10-15). (#420)
- Add bindings for `sync.h`. (#423)
- **Breaking:** bitmap: Provide detailed implementation for `AndroidBitmapInfoFlags`. (#424)
- native_window: Add `set_buffers_transform()`, `try_allocate_buffers()` and `set_frame_rate*()`. (#425)
- Add bindings for `ASharedMemory`. (#427)
- hardware_buffer: Add `id()` to retrieve a system-wide unique identifier for a `HardwareBuffer`. (#428)
- **Breaking:** bitmap: Strip `Android` prefix from structs and enums, and `Bitmap` from `Result`. (#430)
- **Breaking:** `raw-window-handle 0.5` support is now behind an _optional_ `rwh_05` crate feature and `raw-window-handle` `0.4` and `0.6` support is provided via the new `rwh_04` and (default-enabled) `rwh_06` crate features. (#434)
- **Breaking:** looper: Provide `event` value to file descriptor poll callback. (#435)
- **Breaking:** `HardwareBufferFormat` is no longer exported from `hardware_buffer` and `native_window`, and can only be reached through the `hardware_buffer_format` module. (#436)
- **Breaking:** `get_` prefixes have been removed from all public functions in light of the [C-GETTER](https://rust-lang.github.io/api-guidelines/naming.html#getter-names-follow-rust-convention-c-getter) convention. (#437)
- Add `DataSpace` type and relevant functions on `Bitmap` and `NativeWindow`. (#438)
- bitmap: Add `Bitmap::compress()` and `Bitmap::compress_raw()` functions. (#440)
- **Breaking:** Turn `BitmapError` into a `non_exhaustive` `enum`. (#440)
- **Breaking:** audio: Rename `AudioErrorResult` to `AudioResult` and turn into a `non_exhaustive` `enum`. (#441)
# 0.7.0 (2022-07-24)
- hardware_buffer: Make `HardwareBuffer::as_ptr()` public for interop with Vulkan. (#213)
- **Breaking:** `Configuration::country()` now returns `None` when the country is unset (akin to `Configuration::language()`). (#220)
- Add `MediaCodec` and `MediaFormat` bindings. (#216)
- **Breaking:** Upgrade to [`ndk-sys 0.4.0`](../ndk-sys/CHANGELOG.md#040-2022-07-24) and use new `enum` newtype wrappers. (#245)
- native_window: Use `release`/`acquire` for `Drop` and `Clone` respectively. (#207)
- **Breaking:** audio: Rename from `aaudio` to `audio` and drop `A` prefix. (#273)
- Implement `HasRawWindowHandle` directly on `NativeWindow`. (#274, #319)
- **Breaking:** native_activity: Replace `CStr` return types with `Path`. (#279)
- native_window: Add `format()` getter and `set_buffers_geometry()` setter. (#276)
- native_activity: Add `set_window_format()` setter. (#277)
- native_activity: Add `set_window_flags()` to change window behavior. (#278)
- Add `SurfaceTexture` bindings. (#267)
- Improve library and structure documentation, linking back to the NDK docs more rigorously. (#290)
- **Breaking:** input_queue: `get_event()` now returns a `Result` with `std::io::Error`; `InputQueueError` has been removed. (#292)
- **Breaking:** input_queue: `has_events()` now returns a `bool` directly without being wrapped in `Result`. (#294)
- **Breaking:** hardware_buffer: `HardwareBufferError` has been removed and replaced with `std::io::Error` in return types. (#295)
- Fixed `HardwareBuffer` leak on buffers returned from `AndroidBitmap::get_hardware_buffer()`. (#296)
- Bump optional `jni` dependency for doctest example from `0.18` to `0.19`. (#300)
- hardware_buffer: Made `HardwareBufferDesc` fields `pub`. (#313)
- **Breaking:** Remove `hardware_buffer` and `trace` features in favour of using `api-level-26` or `api-level-23` directly. (#320)
# 0.6.0 (2022-01-05)
- **Breaking:** Upgrade to [`ndk-sys 0.3.0`](../ndk-sys/CHANGELOG.md#030-2022-01-05) and migrate to `jni-sys` types that it now directly uses in its bindings. (#209 / #214)
# 0.5.0 (2021-11-22)
- **Breaking:** Replace `add_fd_with_callback` `ident` with constant value `ALOOPER_POLL_CALLBACK`,
as per <https://developer.android.com/ndk/reference/group/looper#alooper_addfd>.
- **Breaking:** Accept unboxed closure in `add_fd_with_callback`.
- aaudio: Replace "Added in" comments with missing `#[cfg(feature)]`.
- aaudio: Add missing `fn get_allowed_capture_policy()`.
- configuration: Add missing `api-level-30` feature to `fn screen_round()`.
# 0.4.0 (2021-08-02)
- **Breaking:** Model looper file descriptor events integer as `bitflags`.
# 0.3.0 (2021-01-30)
- **Breaking:** Looper `ident` not passed in `data` pointer anymore.
`attach_looper` now only sets the `ident` field when attaching an
`InputQueue` to a `ForeignLooper`.
If you are relying on `Poll::Event::data` to tell event fd and
input queue apart, please use `Poll::Event::ident` and the new
constants introduced in `ndk-glue`!
# 0.2.1 (2020-10-15)
- Fix documentation build on docs.rs
# 0.2.0 (2020-09-15)
- **Breaking:** Updated to use [ndk-sys 0.2.0](../ndk-sys/CHANGELOG.md#020-2020-09-15)
- Added `media` bindings
- Added `bitmap` and `hardware_buffer` bindings
- Added `aaudio` bindings
- Fixed assets directory path to be relative to the manifest
- Added `trace` feature for native tracing
# 0.1.0 (2020-04-22)
- Initial release! 🎉

121
vendor/ndk-0.8.0/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,121 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.66"
name = "ndk"
version = "0.8.0"
authors = ["The Rust Mobile contributors"]
description = "Safe Rust bindings to the Android NDK"
homepage = "https://github.com/rust-mobile/ndk"
documentation = "https://docs.rs/ndk"
readme = "README.md"
keywords = [
"android",
"ndk",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-mobile/ndk"
resolver = "1"
[package.metadata.docs.rs]
features = [
"jni",
"all",
]
rustdoc-args = [
"--cfg",
"docsrs",
]
targets = [
"aarch64-linux-android",
"armv7-linux-androideabi",
"i686-linux-android",
"x86_64-linux-android",
]
[dependencies.bitflags]
version = "2.0.0"
[dependencies.ffi]
version = "0.5.0"
package = "ndk-sys"
[dependencies.jni]
version = "0.21"
optional = true
[dependencies.jni-sys]
version = "0.3.0"
[dependencies.log]
version = "0.4"
[dependencies.num_enum]
version = "0.7"
[dependencies.rwh_04]
version = "0.4"
optional = true
package = "raw-window-handle"
[dependencies.rwh_05]
version = "0.5"
optional = true
package = "raw-window-handle"
[dependencies.rwh_06]
version = "0.6"
optional = true
package = "raw-window-handle"
[dependencies.thiserror]
version = "1.0.23"
[dev-dependencies.libc]
version = "0.2"
[features]
all = [
"audio",
"bitmap",
"media",
"api-level-31",
"rwh_04",
"rwh_05",
"rwh_06",
]
api-level-23 = []
api-level-24 = ["api-level-23"]
api-level-25 = ["api-level-24"]
api-level-26 = ["api-level-25"]
api-level-27 = ["api-level-26"]
api-level-28 = ["api-level-27"]
api-level-29 = ["api-level-28"]
api-level-30 = ["api-level-29"]
api-level-31 = ["api-level-30"]
audio = [
"ffi/audio",
"api-level-26",
]
bitmap = ["ffi/bitmap"]
default = ["rwh_06"]
media = ["ffi/media"]
sync = [
"ffi/sync",
"api-level-26",
]
test = [
"ffi/test",
"jni",
"all",
]

21
vendor/ndk-0.8.0/README.md vendored Normal file
View File

@@ -0,0 +1,21 @@
> **Note**
> This repository was recently [modularized](https://github.com/rust-mobile/ndk/issues/372) and the following crates were split into separate repositories:
>
> Crate | New Location | Notes
> ------|--------------|------
> ndk-context | https://github.com/rust-mobile/ndk-context |
> ndk-glue | https://github.com/rust-mobile/ndk-glue | _deprecated_ - see [android-activity](https://github.com/rust-mobile/android-activity)
> ndk-macro | https://github.com/rust-mobile/ndk-glue | _deprecated_ - see [android-activity](https://github.com/rust-mobile/android-activity)
> ndk-build | https://github.com/rust-mobile/cargo-apk |
> cargo-apk | https://github.com/rust-mobile/cargo-apk |
[![ci](https://github.com/rust-mobile/ndk/actions/workflows/rust.yml/badge.svg)](https://github.com/rust-mobile/ndk/actions/workflows/rust.yml) ![MIT license](https://img.shields.io/badge/License-MIT-green.svg) ![APACHE2 license](https://img.shields.io/badge/License-APACHE2-green.svg)
Rust bindings to the [Android NDK](https://developer.android.com/ndk)
Name | Description | Badges
--- | --- | ---
[`ndk-sys`](./ndk-sys) | Raw FFI bindings to the NDK | [![crates.io](https://img.shields.io/crates/v/ndk-sys.svg)](https://crates.io/crates/ndk-sys) [![Docs](https://docs.rs/ndk-sys/badge.svg)](https://docs.rs/ndk-sys) [![MSRV](https://img.shields.io/badge/rustc-1.60.0+-ab6000.svg)](https://blog.rust-lang.org/2022/04/07/Rust-1.60.0.html)
[`ndk`](./ndk) | Safe abstraction of the bindings | [![crates.io](https://img.shields.io/crates/v/ndk.svg)](https://crates.io/crates/ndk) [![Docs](https://docs.rs/ndk/badge.svg)](https://docs.rs/ndk) [![MSRV](https://img.shields.io/badge/rustc-1.64.0+-ab6000.svg)](https://blog.rust-lang.org/2022/09/22/Rust-1.64.0.html)
See these [`ndk-examples`](https://github.com/rust-mobile/cargo-apk/tree/main/examples/examples) and these [`rust-android-examples`](https://github.com/rust-mobile/rust-android-examples) for examples using the NDK.

323
vendor/ndk-0.8.0/src/asset.rs vendored Normal file
View File

@@ -0,0 +1,323 @@
//! Bindings for [`AAsset`], [`AAssetDir`] and [`AAssetManager`]
//!
//! [`AAsset`]: https://developer.android.com/ndk/reference/group/asset#aasset
//! [`AAssetDir`]: https://developer.android.com/ndk/reference/group/asset#aassetdir
//! [`AAssetManager`]: https://developer.android.com/ndk/reference/group/asset#aassetmanager
use std::{
ffi::{CStr, CString},
io,
os::fd::{FromRawFd, OwnedFd},
ptr::NonNull,
};
/// A native [`AAssetManager *`]
///
/// [`AAssetManager *`]: https://developer.android.com/ndk/reference/group/asset#aassetmanager
#[derive(Debug)]
#[doc(alias = "AAssetManager")]
pub struct AssetManager {
ptr: NonNull<ffi::AAssetManager>,
}
// AAssetManager is thread safe.
// See https://developer.android.com/ndk/reference/group/asset#aassetmanager
unsafe impl Send for AssetManager {}
unsafe impl Sync for AssetManager {}
impl AssetManager {
/// Create an `AssetManager` from a pointer
///
/// # Safety
/// By calling this function, you assert that the pointer is a valid pointer to a native
/// `AAssetManager`.
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetManager>) -> Self {
Self { ptr }
}
/// Returns the pointer to the native `AAssetManager`.
pub fn ptr(&self) -> NonNull<ffi::AAssetManager> {
self.ptr
}
/// Open the asset. Returns [`None`] if opening the asset fails.
///
/// This currently always opens the asset in the streaming mode.
#[doc(alias = "AAssetManager_open")]
pub fn open(&self, filename: &CStr) -> Option<Asset> {
unsafe {
let ptr = ffi::AAssetManager_open(
self.ptr.as_ptr(),
filename.as_ptr(),
ffi::AASSET_MODE_STREAMING as i32,
);
Some(Asset::from_ptr(NonNull::new(ptr)?))
}
}
/// Open an asset directory. Returns [`None`] if opening the directory fails.
#[doc(alias = "AAssetManager_openDir")]
pub fn open_dir(&self, filename: &CStr) -> Option<AssetDir> {
unsafe {
let ptr = ffi::AAssetManager_openDir(self.ptr.as_ptr(), filename.as_ptr());
Some(AssetDir::from_ptr(NonNull::new(ptr)?))
}
}
}
/// A native [`AAssetDir *`]
///
/// ```no_run
/// # use std::ffi::CString;
/// # use ndk::asset::AssetManager;
/// # let asset_manager: AssetManager = unimplemented!();
/// use std::io::Read;
///
/// let mut my_dir = asset_manager
/// .open_dir(&CString::new("my_dir").unwrap())
/// .expect("Could not open directory");
///
/// // Use it as an iterator
/// let all_files = my_dir.collect::<Vec<CString>>();
///
/// // Reset the iterator
/// my_dir.rewind();
///
/// // Use .with_next() to iterate without allocating `CString`s
/// while let Some(asset) = my_dir.with_next(|cstr| asset_manager.open(cstr).unwrap()) {
/// let mut text = String::new();
/// asset.read_to_string(&mut text);
/// // ...
/// }
/// ```
///
/// [`AAssetDir *`]: https://developer.android.com/ndk/reference/group/asset#aassetdir
#[derive(Debug)]
#[doc(alias = "AAssetDir")]
pub struct AssetDir {
ptr: NonNull<ffi::AAssetDir>,
}
// It's unclear if AAssetDir is thread safe.
// However, AAsset is not, so there's a good chance that AAssetDir is not either.
impl Drop for AssetDir {
#[doc(alias = "AAssetDir_close")]
fn drop(&mut self) {
unsafe { ffi::AAssetDir_close(self.ptr.as_ptr()) }
}
}
impl AssetDir {
/// Construct an `AssetDir` from the native `AAssetDir *`. This gives ownership of the
/// `AAssetDir *` to the `AssetDir`, which will handle closing the asset. Avoid using
/// the pointer after calling this function.
///
/// # Safety
/// By calling this function, you assert that it points to a valid native `AAssetDir`.
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAssetDir>) -> Self {
Self { ptr }
}
/// The corresponding native `AAssetDir *`
pub fn ptr(&self) -> NonNull<ffi::AAssetDir> {
self.ptr
}
/// Get the next filename, if any, and process it. Like [`Iterator::next()`], but performs
/// no additional allocation.
///
/// The filenames are in the correct format to be passed to [`AssetManager::open()`].
#[doc(alias = "AAssetDir_getNextFileName")]
pub fn with_next<T>(&mut self, f: impl for<'a> FnOnce(&'a CStr) -> T) -> Option<T> {
unsafe {
let next_name = ffi::AAssetDir_getNextFileName(self.ptr.as_ptr());
if next_name.is_null() {
None
} else {
Some(f(CStr::from_ptr(next_name)))
}
}
}
/// Reset the iteration state
#[doc(alias = "AAssetDir_rewind")]
pub fn rewind(&mut self) {
unsafe {
ffi::AAssetDir_rewind(self.ptr.as_ptr());
}
}
}
impl Iterator for AssetDir {
type Item = CString;
fn next(&mut self) -> Option<CString> {
self.with_next(|cstr| cstr.to_owned())
}
}
/// A native [`AAsset *`], opened in streaming mode
///
/// ```no_run
/// # use std::ffi::CString;
/// # use ndk::asset::AssetManager;
/// # let asset_manager: AssetManager = unimplemented!();
/// use std::io::Read;
///
/// let asset = asset_manager
/// .open(&CString::new("path/to/asset").unwrap())
/// .expect("Could not open asset");
///
/// let mut data = vec![];
/// asset.read_to_end(&mut data);
/// // ... use data ...
/// ```
///
/// [`AAsset *`]: https://developer.android.com/ndk/reference/group/asset#aasset
#[derive(Debug)]
#[doc(alias = "AAsset")]
pub struct Asset {
ptr: NonNull<ffi::AAsset>,
}
// AAsset is *not* thread safe.
// See https://developer.android.com/ndk/reference/group/asset#aasset
impl Drop for Asset {
#[doc(alias = "AAsset_close")]
fn drop(&mut self) {
unsafe { ffi::AAsset_close(self.ptr.as_ptr()) }
}
}
impl Asset {
/// Construct an `Asset` from the native `AAsset *`. This gives ownership of the `AAsset *` to
/// the `Asset`, which will handle closing the asset. Avoid using the pointer after calling
/// this function.
///
/// # Safety
/// By calling this function, you assert that it points to a valid native `AAsset`, open
/// in the streaming mode.
pub unsafe fn from_ptr(ptr: NonNull<ffi::AAsset>) -> Self {
Self { ptr }
}
/// The corresponding native `AAsset *`
pub fn ptr(&self) -> NonNull<ffi::AAsset> {
self.ptr
}
/// Returns the total length of the asset, in bytes
#[doc(alias = "AAsset_getLength64")]
pub fn length(&self) -> usize {
unsafe { ffi::AAsset_getLength64(self.ptr.as_ptr()) as usize }
}
/// Returns the remaining length of the asset, in bytes
#[doc(alias = "AAsset_getRemainingLength64")]
pub fn remaining_length(&self) -> usize {
unsafe { ffi::AAsset_getRemainingLength64(self.ptr.as_ptr()) as usize }
}
/// Maps all data into a buffer and returns it
#[doc(alias = "AAsset_getBuffer")]
pub fn buffer(&mut self) -> io::Result<&[u8]> {
unsafe {
let buf_ptr = ffi::AAsset_getBuffer(self.ptr.as_ptr());
if buf_ptr.is_null() {
Err(io::Error::new(
io::ErrorKind::Other,
"Android Asset error creating buffer",
))
} else {
Ok(std::slice::from_raw_parts(
buf_ptr as *const u8,
self.length(),
))
}
}
}
/// Returns whether this asset's internal buffer is allocated in ordinary RAM (i.e. not `mmap`ped).
#[doc(alias = "AAsset_isAllocated")]
pub fn is_allocated(&self) -> bool {
unsafe { ffi::AAsset_isAllocated(self.ptr.as_ptr()) != 0 }
}
/// Open a new file descriptor that can be used to read the asset data.
///
/// Returns an error if direct fd access is not possible (for example, if the asset is compressed).
#[doc(alias = "AAsset_openFileDescriptor64")]
pub fn open_file_descriptor(&self) -> io::Result<OpenedFileDescriptor> {
let mut offset = 0;
let mut size = 0;
let res =
unsafe { ffi::AAsset_openFileDescriptor64(self.ptr.as_ptr(), &mut offset, &mut size) };
if res >= 0 {
Ok(OpenedFileDescriptor {
fd: unsafe { OwnedFd::from_raw_fd(res) },
offset: offset as usize,
size: size as usize,
})
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"Android Asset openFileDescriptor error",
))
}
}
}
impl io::Read for Asset {
#[doc(alias = "AAsset_read")]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unsafe {
let res = ffi::AAsset_read(self.ptr.as_ptr(), buf.as_mut_ptr() as *mut _, buf.len());
if res >= 0 {
Ok(res as usize)
} else {
Err(io::Error::new(
io::ErrorKind::Other,
"Android Asset read error",
))
}
}
}
}
impl io::Seek for Asset {
#[doc(alias = "AAsset_seek64")]
fn seek(&mut self, seek: io::SeekFrom) -> io::Result<u64> {
unsafe {
let res = match seek {
io::SeekFrom::Start(x) => {
ffi::AAsset_seek64(self.ptr.as_ptr(), x as i64, ffi::SEEK_SET as i32)
}
io::SeekFrom::Current(x) => {
ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_CUR as i32)
}
io::SeekFrom::End(x) => {
ffi::AAsset_seek64(self.ptr.as_ptr(), x, ffi::SEEK_END as i32)
}
};
if res < 0 {
Err(io::Error::new(
io::ErrorKind::Other,
"Android Asset seek error",
))
} else {
Ok(res as u64)
}
}
}
}
/// Contains the opened file descriptor returned by [`Asset::open_file_descriptor()`], together
/// with the offset and size of the given asset within that file descriptor.
#[derive(Debug)]
pub struct OpenedFileDescriptor {
pub fd: OwnedFd,
pub offset: usize,
pub size: usize,
}

1423
vendor/ndk-0.8.0/src/audio.rs vendored Normal file

File diff suppressed because it is too large Load Diff

494
vendor/ndk-0.8.0/src/bitmap.rs vendored Normal file
View File

@@ -0,0 +1,494 @@
//! Bindings for [`AndroidBitmap`] functions
//!
//! These functions operate directly on a JNI [`android.graphics.Bitmap`] instance.
//!
//! [`AndroidBitmap`]: https://developer.android.com/ndk/reference/group/bitmap
//! [`android.graphics.Bitmap`]: https://developer.android.com/reference/android/graphics/Bitmap
#![cfg(feature = "bitmap")]
use jni_sys::{jobject, JNIEnv};
use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError};
use std::{error, fmt, mem::MaybeUninit};
use thiserror::Error;
#[cfg(feature = "api-level-30")]
use crate::data_space::DataSpace;
#[cfg(feature = "api-level-30")]
use crate::hardware_buffer::HardwareBufferRef;
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
#[non_exhaustive]
pub enum BitmapError {
#[doc(alias = "ANDROID_BITMAP_RESULT_ALLOCATION_FAILED")]
AllocationFailed = ffi::ANDROID_BITMAP_RESULT_ALLOCATION_FAILED,
#[doc(alias = "ANDROID_BITMAP_RESULT_BAD_PARAMETER")]
BadParameter = ffi::ANDROID_BITMAP_RESULT_BAD_PARAMETER,
#[doc(alias = "ANDROID_BITMAP_RESULT_JNI_EXCEPTION")]
JniException = ffi::ANDROID_BITMAP_RESULT_JNI_EXCEPTION,
// Use the OK discriminant, as no-one will be able to call `as i32` and only has access to the
// constants via `From` provided by `IntoPrimitive` which reads the contained value.
#[num_enum(catch_all)]
Unknown(i32) = ffi::AAUDIO_OK,
}
impl fmt::Display for BitmapError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl error::Error for BitmapError {}
pub type Result<T, E = BitmapError> = std::result::Result<T, E>;
impl BitmapError {
pub(crate) fn from_status(status: i32) -> Result<()> {
match status {
ffi::ANDROID_BITMAP_RESULT_SUCCESS => Ok(()),
x => Err(Self::from(x)),
}
}
}
fn construct<T>(with_ptr: impl FnOnce(*mut T) -> i32) -> Result<T> {
let mut result = MaybeUninit::uninit();
let status = with_ptr(result.as_mut_ptr());
BitmapError::from_status(status).map(|()| unsafe { result.assume_init() })
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)]
#[allow(non_camel_case_types)]
#[doc(alias = "AndroidBitmapFormat")]
pub enum BitmapFormat {
#[doc(alias = "ANDROID_BITMAP_FORMAT_NONE")]
NONE = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_NONE.0,
#[doc(alias = "ANDROID_BITMAP_FORMAT_RGBA_8888")]
RGBA_8888 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGBA_8888.0,
#[doc(alias = "ANDROID_BITMAP_FORMAT_RGB_565")]
RGB_565 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGB_565.0,
#[deprecated = "Deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead."]
#[doc(alias = "ANDROID_BITMAP_FORMAT_RGBA_4444")]
RGBA_4444 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGBA_4444.0,
#[doc(alias = "ANDROID_BITMAP_FORMAT_A_8")]
A_8 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_A_8.0,
#[doc(alias = "ANDROID_BITMAP_FORMAT_RGBA_F16")]
RGBA_F16 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGBA_F16.0,
#[doc(alias = "ANDROID_BITMAP_FORMAT_RGBA_1010102")]
RGBA_1010102 = ffi::AndroidBitmapFormat::ANDROID_BITMAP_FORMAT_RGBA_1010102.0,
}
/// An immediate wrapper over [`android.graphics.Bitmap`]
///
/// [`android.graphics.Bitmap`]: https://developer.android.com/reference/android/graphics/Bitmap
#[derive(Debug)]
pub struct Bitmap {
env: *mut JNIEnv,
inner: jobject,
}
impl Bitmap {
/// Create a [`Bitmap`] wrapper from JNI pointers
///
/// # Safety
/// This function should be called with a healthy JVM pointer and with a non-null
/// [`android.graphics.Bitmap`], which must be kept alive on the Java/Kotlin side.
///
/// [`android.graphics.Bitmap`]: https://developer.android.com/reference/android/graphics/Bitmap
pub unsafe fn from_jni(env: *mut JNIEnv, bitmap: jobject) -> Self {
Self { env, inner: bitmap }
}
/// Fills out and returns the [`BitmapInfo`] struct for the given Java bitmap object.
#[doc(alias = "AndroidBitmap_getInfo")]
pub fn info(&self) -> Result<BitmapInfo> {
let inner =
construct(|res| unsafe { ffi::AndroidBitmap_getInfo(self.env, self.inner, res) })?;
Ok(BitmapInfo { inner })
}
/// Returns the [`DataSpace`] of this [`Bitmap`].
///
/// Note that [`DataSpace`] only exposes a few values. This may return [`DataSpace::Unknown`],
/// even for Named ColorSpaces, if they have no corresponding [`DataSpace`].
#[cfg(feature = "api-level-30")]
#[doc(alias = "AndroidBitmap_getDataSpace")]
pub fn data_space(&self) -> Result<DataSpace, TryFromPrimitiveError<DataSpace>> {
let value = unsafe { ffi::AndroidBitmap_getDataSpace(self.env, self.inner) };
DataSpace::try_from_primitive(
value
.try_into()
.expect("AndroidBitmap_getDataSpace returned negative value"),
)
}
/// Attempt to lock the pixel address.
///
/// Locking will ensure that the memory for the pixels will not move until the
/// [`Bitmap::unlock_pixels()`] call, and ensure that, if the pixels had been previously purged,
/// they will have been restored.
///
/// If this call succeeds, it must be balanced by a call to [`Bitmap::unlock_pixels()`], after
/// which time the address of the pixels should no longer be used.
#[doc(alias = "AndroidBitmap_lockPixels")]
pub fn lock_pixels(&self) -> Result<*mut std::os::raw::c_void> {
construct(|res| unsafe { ffi::AndroidBitmap_lockPixels(self.env, self.inner, res) })
}
/// Call this to balance a successful call to [`Bitmap::lock_pixels()`].
#[doc(alias = "AndroidBitmap_unlockPixels")]
pub fn unlock_pixels(&self) -> Result<()> {
let status = unsafe { ffi::AndroidBitmap_unlockPixels(self.env, self.inner) };
BitmapError::from_status(status)
}
/// Retrieve the native object associated with an [`ffi::ANDROID_BITMAP_FLAGS_IS_HARDWARE`]
/// [`Bitmap`] (requires [`BitmapInfoFlags::is_hardware()`] on [`BitmapInfo::flags()`] to return
/// [`true`]).
///
/// Client must not modify it while a [`Bitmap`] is wrapping it.
#[cfg(feature = "api-level-30")]
#[doc(alias = "AndroidBitmap_getHardwareBuffer")]
pub fn hardware_buffer(&self) -> Result<HardwareBufferRef> {
unsafe {
let result =
construct(|res| ffi::AndroidBitmap_getHardwareBuffer(self.env, self.inner, res))?;
let non_null = if cfg!(debug_assertions) {
std::ptr::NonNull::new(result).expect("result should never be null")
} else {
std::ptr::NonNull::new_unchecked(result)
};
Ok(HardwareBufferRef::from_ptr(non_null))
}
}
/// [Lock] the pixels in `self` and compress them as described by [`info()`].
///
/// Unlike [`compress_raw()`] this requires a [`Bitmap`] object (as `self`) backed by a
/// [`jobject`].
///
/// # Parameters
/// - `format`: [`BitmapCompressFormat`] to compress to.
/// - `quality`: Hint to the compressor, `0-100`. The value is interpreted differently
/// depending on [`BitmapCompressFormat`].
/// - `compress_callback`: Closure that writes the compressed data. Will be called on the
/// current thread, each time the compressor has compressed more data that is ready to be
/// written. May be called more than once for each call to this method.
///
/// [Lock]: Self::lock_pixels()
/// [`info()`]: Self::info()
/// [`compress_raw()`]: Self::compress_raw()
#[cfg(feature = "api-level-30")]
#[doc(alias = "AndroidBitmap_compress")]
pub fn compress<F: FnMut(&[u8]) -> Result<(), ()>>(
&self,
format: BitmapCompressFormat,
quality: i32,
compress_callback: F,
) -> Result<(), BitmapCompressError> {
let info = self.info()?;
let data_space = self.data_space()?;
let pixels = self.lock_pixels()?;
// SAFETY: When lock_pixels() succeeds, assume it returns a valid pointer that stays
// valid until we call unlock_pixels().
let result = unsafe {
Self::compress_raw(
&info,
data_space,
pixels,
format,
quality,
compress_callback,
)
};
self.unlock_pixels()?;
result
}
/// Compress `pixels` as described by `info`.
///
/// Unlike [`compress()`] this takes a raw pointer to pixels and does not need a [`Bitmap`]
/// object backed by a [`jobject`].
///
/// # Parameters
/// - `info`: Description of the pixels to compress.
/// - `data_space`: [`DataSpace`] describing the color space of the pixels. Should _not_ be
/// [`DataSpace::Unknown`] [^1].
/// - `pixels`: Pointer to pixels to compress.
/// - `format`: [`BitmapCompressFormat`] to compress to.
/// - `quality`: Hint to the compressor, `0-100`. The value is interpreted differently
/// depending on [`BitmapCompressFormat`].
/// - `compress_callback`: Closure that writes the compressed data. Will be called on the
/// current thread, each time the compressor has compressed more data that is ready to be
/// written. May be called more than once for each call to this method.
///
/// [`compress()`]: Self::compress()
/// [^1]: <https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/libs/hwui/apex/android_bitmap.cpp;l=275-279;drc=7ba5c2fb3d1e35eb37a9cc522b30ba51f49ea491>
#[cfg(feature = "api-level-30")]
#[doc(alias = "AndroidBitmap_compress")]
pub unsafe fn compress_raw<F: FnMut(&[u8]) -> Result<(), ()>>(
info: &BitmapInfo,
data_space: DataSpace,
pixels: *const std::ffi::c_void,
format: BitmapCompressFormat,
quality: i32,
compress_callback: F,
) -> Result<(), BitmapCompressError> {
if data_space == DataSpace::Unknown {
return Err(BitmapCompressError::DataSpaceUnknown);
}
use std::{any::Any, ffi::c_void, panic::AssertUnwindSafe};
struct CallbackState<F: FnMut(&[u8]) -> Result<(), ()>> {
callback: F,
panic: Option<Box<dyn Any + Send>>,
}
let mut cb_state = CallbackState::<F> {
callback: compress_callback,
panic: None,
};
extern "C" fn compress_cb<F: FnMut(&[u8]) -> Result<(), ()>>(
context: *mut c_void,
data: *const c_void,
size: usize,
) -> bool {
// SAFETY: This callback will only be called serially on a single thread. Both the
// panic state and the FnMut context need to be available mutably.
let cb_state = unsafe { context.cast::<CallbackState<F>>().as_mut() }.unwrap();
let data = unsafe { std::slice::from_raw_parts(data.cast(), size) };
let panic = std::panic::catch_unwind(AssertUnwindSafe(|| (cb_state.callback)(data)));
match panic {
Ok(r) => r.is_ok(),
Err(e) => {
cb_state.panic = Some(e);
false
}
}
}
let status = unsafe {
ffi::AndroidBitmap_compress(
&info.inner,
u32::from(data_space)
.try_into()
.expect("i32 overflow in DataSpace"),
pixels,
format as i32,
quality,
<*mut _>::cast(&mut cb_state),
Some(compress_cb::<F>),
)
};
if let Some(panic) = cb_state.panic {
std::panic::resume_unwind(panic)
}
Ok(BitmapError::from_status(status)?)
}
}
/// Possible values for [`ffi::ANDROID_BITMAP_FLAGS_ALPHA_MASK`] within [`BitmapInfoFlags`]
#[cfg(feature = "api-level-30")]
#[derive(Clone, Copy, Debug)]
#[doc(alias = "ANDROID_BITMAP_FLAGS_ALPHA_MASK")]
pub enum BitmapInfoFlagsAlpha {
/// Pixel components are premultiplied by alpha.
#[doc(alias = "ANDROID_BITMAP_FLAGS_ALPHA_PREMUL")]
Premultiplied,
/// Pixels are opaque.
#[doc(alias = "ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE")]
Opaque,
/// Pixel components are independent of alpha.
#[doc(alias = "ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL")]
Unpremultiplied,
}
/// Bitfield containing information about the bitmap.
#[cfg(feature = "api-level-30")]
#[repr(transparent)]
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct BitmapInfoFlags(u32);
#[cfg(feature = "api-level-30")]
impl std::fmt::Debug for BitmapInfoFlags {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"BitmapInfoFlags({:#x}, alpha: {:?}, is_hardware: {})",
self.0,
self.alpha(),
self.is_hardware()
)
}
}
#[cfg(feature = "api-level-30")]
impl BitmapInfoFlags {
/// Returns the alpha value contained in the [`ffi::ANDROID_BITMAP_FLAGS_ALPHA_MASK`] bit range
#[doc(alias = "ANDROID_BITMAP_FLAGS_ALPHA_MASK")]
pub fn alpha(self) -> BitmapInfoFlagsAlpha {
// Note that ffi::ANDROID_BITMAP_FLAGS_ALPHA_SHIFT is 0 and hence irrelevant.
match self.0 & ffi::ANDROID_BITMAP_FLAGS_ALPHA_MASK {
ffi::ANDROID_BITMAP_FLAGS_ALPHA_PREMUL => BitmapInfoFlagsAlpha::Premultiplied,
ffi::ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE => BitmapInfoFlagsAlpha::Opaque,
ffi::ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL => BitmapInfoFlagsAlpha::Unpremultiplied,
3 => todo!("ALPHA_MASK value 3"),
_ => unreachable!(),
}
}
/// Returns [`true`] when [`ffi::ANDROID_BITMAP_FLAGS_IS_HARDWARE`] is set, meaning this
/// [`Bitmap`] uses "HARDWARE Config" and its [`HardwareBufferRef`] can be retrieved via
/// [`Bitmap::hardware_buffer()`].
#[doc(alias = "ANDROID_BITMAP_FLAGS_IS_HARDWARE")]
pub fn is_hardware(self) -> bool {
// This constant is defined in a separate anonymous enum which bindgen treats as i32.
(self.0 & ffi::ANDROID_BITMAP_FLAGS_IS_HARDWARE as u32) != 0
}
}
/// A native [`AndroidBitmapInfo`]
///
/// [`AndroidBitmapInfo`]: https://developer.android.com/ndk/reference/struct/android-bitmap-info#struct_android_bitmap_info
#[derive(Clone, Copy)]
#[doc(alias = "AndroidBitmapInfo")]
pub struct BitmapInfo {
inner: ffi::AndroidBitmapInfo,
}
impl std::fmt::Debug for BitmapInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut f = f.debug_struct("BitmapInfo");
f.field("width", &self.width())
.field("height", &self.height())
.field("stride", &self.stride())
.field("format", &self.try_format());
#[cfg(feature = "api-level-30")]
f.field("flags", &self.flags());
f.finish()
}
}
impl BitmapInfo {
pub fn new(width: u32, height: u32, stride: u32, format: BitmapFormat) -> Self {
Self {
inner: ffi::AndroidBitmapInfo {
width,
height,
stride,
format: u32::from(format) as i32,
flags: 0,
},
}
}
#[cfg(feature = "api-level-30")]
pub fn new_with_flags(
width: u32,
height: u32,
stride: u32,
format: BitmapFormat,
flags: BitmapInfoFlags,
) -> Self {
Self {
inner: ffi::AndroidBitmapInfo {
flags: flags.0,
..Self::new(width, height, stride, format).inner
},
}
}
/// The bitmap width in pixels.
pub fn width(&self) -> u32 {
self.inner.width
}
/// The bitmap height in pixels.
pub fn height(&self) -> u32 {
self.inner.height
}
/// The number of byte per row.
pub fn stride(&self) -> u32 {
self.inner.stride
}
/// Convert the internal, native [`ffi::AndroidBitmapInfo::format`] into a [`BitmapFormat`].
///
/// # Panics
///
/// This function panics if the underlying value does not have a corresponding variant in
/// [`BitmapFormat`]. Use [`try_format()`][BitmapInfo::try_format()] for an infallible version
/// of this function.
pub fn format(&self) -> BitmapFormat {
self.try_format().unwrap()
}
/// Attempt to convert the internal, native [`ffi::AndroidBitmapInfo::format`] into a
/// [`BitmapFormat`]. This may fail if the value does not have a corresponding Rust enum
/// variant.
pub fn try_format(&self) -> Result<BitmapFormat, TryFromPrimitiveError<BitmapFormat>> {
let format = self.inner.format as u32;
format.try_into()
}
/// Bitfield containing information about the bitmap.
#[cfg(feature = "api-level-30")]
pub fn flags(&self) -> BitmapInfoFlags {
BitmapInfoFlags(self.inner.flags)
}
}
/// Specifies the formats that can be compressed to with [`Bitmap::compress()`] and
/// [`Bitmap::compress_raw()`].
#[cfg(feature = "api-level-30")]
#[repr(u32)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[doc(alias = "AndroidBitmapCompressFormat")]
pub enum BitmapCompressFormat {
/// Compress to the JPEG format.
///
/// quality of `0` means compress for the smallest size. `100` means compress for max visual
/// quality.
#[doc(alias = "ANDROID_BITMAP_COMPRESS_FORMAT_JPEG")]
Jpeg = ffi::AndroidBitmapCompressFormat::ANDROID_BITMAP_COMPRESS_FORMAT_JPEG.0,
/// Compress to the PNG format.
///
/// PNG is lossless, so quality is ignored.
#[doc(alias = "ANDROID_BITMAP_COMPRESS_FORMAT_PNG")]
Png = ffi::AndroidBitmapCompressFormat::ANDROID_BITMAP_COMPRESS_FORMAT_PNG.0,
/// Compress to the WEBP lossless format.
///
/// quality refers to how much effort to put into compression. A value of `0` means to
/// compress quickly, resulting in a relatively large file size. `100` means to spend more time
/// compressing, resulting in a smaller file.
#[doc(alias = "ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY")]
WebPLossy = ffi::AndroidBitmapCompressFormat::ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSY.0,
/// Compress to the WEBP lossy format.
///
/// quality of `0` means compress for the smallest size. `100` means compress for max visual quality.
#[doc(alias = "ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS")]
WebPLossless = ffi::AndroidBitmapCompressFormat::ANDROID_BITMAP_COMPRESS_FORMAT_WEBP_LOSSLESS.0,
}
/// Encapsulates possible errors returned by [`Bitmap::compress()`] or [`Bitmap::compress_raw()`].
#[derive(Debug, Error)]
pub enum BitmapCompressError {
#[error(transparent)]
BitmapError(#[from] BitmapError),
/// Only returned when [`Bitmap::compress()`] fails to read a valid [`DataSpace`] via
/// [`Bitmap::data_space()`].
#[error(transparent)]
DataSpaceFromPrimitiveError(#[from] TryFromPrimitiveError<DataSpace>),
/// [`Bitmap`] compression requires a known [`DataSpace`]. [`DataSpace::Unknown`] is invalid
/// even though it is typically treated as `sRGB`, for that [`DataSpace::Srgb`] has to be passed
/// explicitly.
#[error("The dataspace for this Bitmap is Unknown")]
DataSpaceUnknown,
}

572
vendor/ndk-0.8.0/src/configuration.rs vendored Normal file
View File

@@ -0,0 +1,572 @@
//! Bindings for [`AConfiguration`]
//!
//! See also the [NDK docs](https://developer.android.com/ndk/reference/group/configuration) for
//! [`AConfiguration`], as well as the [docs for providing
//! resources](https://developer.android.com/guide/topics/resources/providing-resources.html),
//! which explain many of the configuration values. The [`android.content.res.Configuration`
//! javadoc](https://developer.android.com/reference/android/content/res/Configuration.html) may
//! also have useful information.
//!
//! [`AConfiguration`]: https://developer.android.com/ndk/reference/group/configuration#aconfiguration
use crate::asset::AssetManager;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::fmt;
use std::ptr::NonNull;
/// A native [`AConfiguration *`]
///
/// [`Configuration`] is an opaque type used to get and set various subsystem configurations.
///
/// [`AConfiguration *`]: https://developer.android.com/ndk/reference/group/configuration#aconfiguration
pub struct Configuration {
ptr: NonNull<ffi::AConfiguration>,
}
unsafe impl Send for Configuration {}
unsafe impl Sync for Configuration {}
impl Drop for Configuration {
fn drop(&mut self) {
unsafe { ffi::AConfiguration_delete(self.ptr.as_ptr()) }
}
}
impl Clone for Configuration {
fn clone(&self) -> Self {
let mut new = Self::new();
new.copy(self);
new
}
}
impl PartialEq for Configuration {
fn eq(&self, other: &Self) -> bool {
self.diff(other).0 == 0
}
}
impl Eq for Configuration {}
impl fmt::Debug for Configuration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Configuration")
.field("mcc", &self.mcc())
.field("mnc", &self.mnc())
.field("lang", &self.language())
.field("country", &self.country())
.field("orientation", &self.orientation())
.field("touchscreen", &self.touchscreen())
.field("density", &self.density())
.field("keyboard", &self.keyboard())
.field("navigation", &self.navigation())
.field("keys_hidden", &self.keys_hidden())
.field("nav_hidden", &self.nav_hidden())
.field("sdk_version", &self.sdk_version())
.field("screen_size", &self.screen_size())
.field("screen_long", &self.screen_long())
.field("ui_mode_type", &self.ui_mode_type())
.field("ui_mode_night", &self.ui_mode_night())
.finish()
}
}
impl Configuration {
/// Construct a `Configuration` from a pointer.
///
/// # Safety
/// By calling this function, you assert that it is a valid pointer to a native
/// `AConfiguration`, and give ownership of it to the `Configuration` instance.
pub unsafe fn from_ptr(ptr: NonNull<ffi::AConfiguration>) -> Self {
Self { ptr }
}
/// Create a new `Configuration`, with the same contents as the `AConfiguration` referenced by
/// the pointer.
///
/// This is useful if you have a pointer, but not ownership of it.
///
/// # Safety
/// By calling this function, you assert that it is a valid pointer to a native
/// `AConfiguration`.
pub unsafe fn clone_from_ptr(ptr: NonNull<ffi::AConfiguration>) -> Self {
let conf = Self::new();
ffi::AConfiguration_copy(conf.ptr.as_ptr(), ptr.as_ptr());
conf
}
/// The pointer to the native `AConfiguration`. Keep in mind that the `Configuration` object
/// still has ownership, and will free it when dropped.
pub fn ptr(&self) -> NonNull<ffi::AConfiguration> {
self.ptr
}
pub fn from_asset_manager(am: &AssetManager) -> Self {
let config = Self::new();
unsafe {
ffi::AConfiguration_fromAssetManager(config.ptr().as_mut(), am.ptr().as_mut());
}
config
}
/// Create a new `Configuration`, with none of the values set.
pub fn new() -> Self {
unsafe {
Self {
ptr: NonNull::new(ffi::AConfiguration_new()).unwrap(),
}
}
}
/// `dest.copy(&src)` copies the contents of `src` to `dest`
pub fn copy(&mut self, other: &Self) {
unsafe { ffi::AConfiguration_copy(self.ptr.as_ptr(), other.ptr.as_ptr()) }
}
/// Information about what fields differ between the two configurations
pub fn diff(&self, other: &Self) -> DiffResult {
unsafe {
DiffResult(ffi::AConfiguration_diff(self.ptr.as_ptr(), other.ptr.as_ptr()) as u32)
}
}
/// Returns false if anything in `self` conflicts with `requested`
pub fn matches(&self, requested: &Self) -> bool {
unsafe { ffi::AConfiguration_match(self.ptr.as_ptr(), requested.ptr.as_ptr()) != 0 }
}
/// Returns the country code, as a [`String`] of two characters, if set
pub fn country(&self) -> Option<String> {
let mut chars = [0u8; 2];
unsafe {
ffi::AConfiguration_getCountry(self.ptr.as_ptr(), chars.as_mut_ptr().cast());
}
if chars[0] == 0 {
None
} else {
Some(std::str::from_utf8(chars.as_slice()).unwrap().to_owned())
}
}
/// Returns the screen density in dpi.
///
/// On some devices it can return values outside of the density enum.
pub fn density(&self) -> Option<u32> {
let density = unsafe { ffi::AConfiguration_getDensity(self.ptr.as_ptr()) as u32 };
match density {
ffi::ACONFIGURATION_DENSITY_DEFAULT => Some(160),
ffi::ACONFIGURATION_DENSITY_ANY => None,
ffi::ACONFIGURATION_DENSITY_NONE => None,
density => Some(density),
}
}
/// Returns the keyboard type.
pub fn keyboard(&self) -> Keyboard {
unsafe {
(ffi::AConfiguration_getKeyboard(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
/// Returns keyboard visibility/availability.
pub fn keys_hidden(&self) -> KeysHidden {
unsafe {
(ffi::AConfiguration_getKeysHidden(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
/// Returns the language, as a [`String`] of two characters, if set
pub fn language(&self) -> Option<String> {
let mut chars = [0u8; 2];
unsafe {
ffi::AConfiguration_getLanguage(self.ptr.as_ptr(), chars.as_mut_ptr().cast());
}
if chars[0] == 0 {
None
} else {
Some(std::str::from_utf8(chars.as_slice()).unwrap().to_owned())
}
}
/// Returns the layout direction
pub fn layout_direction(&self) -> LayoutDir {
unsafe {
(ffi::AConfiguration_getLayoutDirection(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
/// Returns the mobile country code.
pub fn mcc(&self) -> i32 {
unsafe { ffi::AConfiguration_getMcc(self.ptr.as_ptr()) }
}
/// Returns the mobile network code, if one is defined
pub fn mnc(&self) -> Option<i32> {
unsafe {
match ffi::AConfiguration_getMnc(self.ptr.as_ptr()) {
0 => None,
x if x == ffi::ACONFIGURATION_MNC_ZERO as i32 => Some(0),
x => Some(x),
}
}
}
pub fn nav_hidden(&self) -> NavHidden {
unsafe {
(ffi::AConfiguration_getNavHidden(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn navigation(&self) -> Navigation {
unsafe {
(ffi::AConfiguration_getNavigation(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn orientation(&self) -> Orientation {
unsafe {
(ffi::AConfiguration_getOrientation(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn screen_height_dp(&self) -> Option<i32> {
unsafe {
let height = ffi::AConfiguration_getScreenHeightDp(self.ptr.as_ptr());
if height == ffi::ACONFIGURATION_SCREEN_HEIGHT_DP_ANY as i32 {
None
} else {
Some(height)
}
}
}
pub fn screen_width_dp(&self) -> Option<i32> {
unsafe {
let width = ffi::AConfiguration_getScreenWidthDp(self.ptr.as_ptr());
if width == ffi::ACONFIGURATION_SCREEN_WIDTH_DP_ANY as i32 {
None
} else {
Some(width)
}
}
}
pub fn screen_long(&self) -> ScreenLong {
unsafe {
(ffi::AConfiguration_getScreenLong(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
#[cfg(feature = "api-level-30")]
pub fn screen_round(&self) -> ScreenRound {
unsafe {
(ffi::AConfiguration_getScreenRound(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn screen_size(&self) -> ScreenSize {
unsafe {
(ffi::AConfiguration_getScreenSize(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn sdk_version(&self) -> i32 {
unsafe { ffi::AConfiguration_getSdkVersion(self.ptr.as_ptr()) }
}
pub fn smallest_screen_width_dp(&self) -> Option<i32> {
unsafe {
let width = ffi::AConfiguration_getSmallestScreenWidthDp(self.ptr.as_ptr());
if width == ffi::ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY as i32 {
None
} else {
Some(width)
}
}
}
pub fn touchscreen(&self) -> Touchscreen {
unsafe {
(ffi::AConfiguration_getTouchscreen(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn ui_mode_night(&self) -> UiModeNight {
unsafe {
(ffi::AConfiguration_getUiModeNight(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
pub fn ui_mode_type(&self) -> UiModeType {
unsafe {
(ffi::AConfiguration_getUiModeType(self.ptr.as_ptr()) as u32)
.try_into()
.unwrap()
}
}
}
/// A bitfield representing the differences between two [`Configuration`]s
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct DiffResult(pub u32);
impl DiffResult {
pub fn mcc(self) -> bool {
self.0 & ffi::ACONFIGURATION_MCC != 0
}
pub fn mnc(self) -> bool {
self.0 & ffi::ACONFIGURATION_MNC != 0
}
pub fn locale(self) -> bool {
self.0 & ffi::ACONFIGURATION_LOCALE != 0
}
pub fn touchscreen(self) -> bool {
self.0 & ffi::ACONFIGURATION_TOUCHSCREEN != 0
}
pub fn keyboard(self) -> bool {
self.0 & ffi::ACONFIGURATION_KEYBOARD != 0
}
pub fn keyboard_hidden(self) -> bool {
self.0 & ffi::ACONFIGURATION_KEYBOARD_HIDDEN != 0
}
pub fn navigation(self) -> bool {
self.0 & ffi::ACONFIGURATION_NAVIGATION != 0
}
pub fn orientation(self) -> bool {
self.0 & ffi::ACONFIGURATION_ORIENTATION != 0
}
pub fn density(self) -> bool {
self.0 & ffi::ACONFIGURATION_DENSITY != 0
}
pub fn screen_size(self) -> bool {
self.0 & ffi::ACONFIGURATION_SCREEN_SIZE != 0
}
pub fn version(self) -> bool {
self.0 & ffi::ACONFIGURATION_VERSION != 0
}
pub fn screen_layout(self) -> bool {
self.0 & ffi::ACONFIGURATION_SCREEN_LAYOUT != 0
}
pub fn ui_mode(self) -> bool {
self.0 & ffi::ACONFIGURATION_UI_MODE != 0
}
pub fn smallest_screen_size(self) -> bool {
self.0 & ffi::ACONFIGURATION_SMALLEST_SCREEN_SIZE != 0
}
pub fn layout_dir(self) -> bool {
self.0 & ffi::ACONFIGURATION_LAYOUTDIR != 0
}
pub fn screen_round(self) -> bool {
self.0 & ffi::ACONFIGURATION_SCREEN_ROUND != 0
}
pub fn color_mode(self) -> bool {
self.0 & ffi::ACONFIGURATION_COLOR_MODE != 0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Orientation {
Any = ffi::ACONFIGURATION_ORIENTATION_ANY,
Port = ffi::ACONFIGURATION_ORIENTATION_PORT,
Land = ffi::ACONFIGURATION_ORIENTATION_LAND,
Square = ffi::ACONFIGURATION_ORIENTATION_SQUARE,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Touchscreen {
Any = ffi::ACONFIGURATION_TOUCHSCREEN_ANY,
NoTouch = ffi::ACONFIGURATION_TOUCHSCREEN_NOTOUCH,
Stylus = ffi::ACONFIGURATION_TOUCHSCREEN_STYLUS,
Finger = ffi::ACONFIGURATION_TOUCHSCREEN_FINGER,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Density {
Default = ffi::ACONFIGURATION_DENSITY_DEFAULT,
Low = ffi::ACONFIGURATION_DENSITY_LOW,
Medium = ffi::ACONFIGURATION_DENSITY_MEDIUM,
TV = ffi::ACONFIGURATION_DENSITY_TV,
High = ffi::ACONFIGURATION_DENSITY_HIGH,
XHigh = ffi::ACONFIGURATION_DENSITY_XHIGH,
XXHigh = ffi::ACONFIGURATION_DENSITY_XXHIGH,
XXXHigh = ffi::ACONFIGURATION_DENSITY_XXXHIGH,
Any = ffi::ACONFIGURATION_DENSITY_ANY,
None = ffi::ACONFIGURATION_DENSITY_NONE,
}
impl Density {
/// The DPI associated with the density class.
/// See [the Android screen density
/// docs](https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp)
///
/// There are some `Density` values that have no associated DPI; these values return `None`.
pub fn dpi(self) -> Option<u32> {
match self {
Self::Default => Some(160), // Or should it be None?
Self::Low => Some(120),
Self::Medium => Some(160),
Self::High => Some(240),
Self::XHigh => Some(320),
Self::XXHigh => Some(480),
Self::XXXHigh => Some(640),
Self::TV => Some(213),
Self::Any => None,
Self::None => None,
}
}
/// The Hi-DPI factor associated with the density class. This is the factor by which an
/// image/resource should be scaled to match its size across devices. The baseline is a 160dpi
/// screen (i.e., Hi-DPI factor = DPI / 160).
/// See [the Android screen density
/// docs](https://developer.android.com/training/multiscreen/screendensities#TaskProvideAltBmp)
///
/// There are some `Density` values that have no associated DPI; these values return `None`.
pub fn approx_hidpi_factor(self) -> Option<f64> {
match self {
Self::Default => Some(1.), // Or should it be None?
Self::Low => Some(0.75),
Self::Medium => Some(1.),
Self::High => Some(1.5),
Self::XHigh => Some(2.),
Self::XXHigh => Some(3.),
Self::XXXHigh => Some(4.),
Self::TV => Some(4. / 3.),
Self::Any => None,
Self::None => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Keyboard {
Any = ffi::ACONFIGURATION_KEYBOARD_ANY,
NoKeys = ffi::ACONFIGURATION_KEYBOARD_NOKEYS,
Qwerty = ffi::ACONFIGURATION_KEYBOARD_QWERTY,
TwelveKey = ffi::ACONFIGURATION_KEYBOARD_12KEY,
}
// FIXME is it a bitmask?
// FIXME are they all bitmasks?
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Navigation {
Any = ffi::ACONFIGURATION_NAVIGATION_ANY,
NoNav = ffi::ACONFIGURATION_NAVIGATION_NONAV,
DPad = ffi::ACONFIGURATION_NAVIGATION_DPAD,
Trackball = ffi::ACONFIGURATION_NAVIGATION_TRACKBALL,
Wheel = ffi::ACONFIGURATION_NAVIGATION_WHEEL,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum KeysHidden {
Any = ffi::ACONFIGURATION_KEYSHIDDEN_ANY,
No = ffi::ACONFIGURATION_KEYSHIDDEN_NO,
Yes = ffi::ACONFIGURATION_KEYSHIDDEN_YES,
Soft = ffi::ACONFIGURATION_KEYSHIDDEN_SOFT,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum NavHidden {
Any = ffi::ACONFIGURATION_NAVHIDDEN_ANY,
No = ffi::ACONFIGURATION_NAVHIDDEN_NO,
Yes = ffi::ACONFIGURATION_NAVHIDDEN_YES,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum ScreenSize {
Any = ffi::ACONFIGURATION_SCREENSIZE_ANY,
Small = ffi::ACONFIGURATION_SCREENSIZE_SMALL,
Normal = ffi::ACONFIGURATION_SCREENSIZE_NORMAL,
Large = ffi::ACONFIGURATION_SCREENSIZE_LARGE,
XLarge = ffi::ACONFIGURATION_SCREENSIZE_XLARGE,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum ScreenLong {
Any = ffi::ACONFIGURATION_SCREENLONG_ANY,
No = ffi::ACONFIGURATION_SCREENLONG_NO,
Yes = ffi::ACONFIGURATION_SCREENLONG_YES,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum ScreenRound {
Any = ffi::ACONFIGURATION_SCREENROUND_ANY,
No = ffi::ACONFIGURATION_SCREENROUND_NO,
Yes = ffi::ACONFIGURATION_SCREENROUND_YES,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum WideColorGamut {
Any = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_ANY,
No = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_NO,
Yes = ffi::ACONFIGURATION_WIDE_COLOR_GAMUT_YES,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum HDR {
Any = ffi::ACONFIGURATION_HDR_ANY,
No = ffi::ACONFIGURATION_HDR_NO,
Yes = ffi::ACONFIGURATION_HDR_YES,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum LayoutDir {
Any = ffi::ACONFIGURATION_LAYOUTDIR_ANY,
Ltr = ffi::ACONFIGURATION_LAYOUTDIR_LTR,
Rtl = ffi::ACONFIGURATION_LAYOUTDIR_RTL,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum UiModeType {
Any = ffi::ACONFIGURATION_UI_MODE_TYPE_ANY,
Normal = ffi::ACONFIGURATION_UI_MODE_TYPE_NORMAL,
Desk = ffi::ACONFIGURATION_UI_MODE_TYPE_DESK,
Car = ffi::ACONFIGURATION_UI_MODE_TYPE_CAR,
Television = ffi::ACONFIGURATION_UI_MODE_TYPE_TELEVISION,
Applicance = ffi::ACONFIGURATION_UI_MODE_TYPE_APPLIANCE,
Watch = ffi::ACONFIGURATION_UI_MODE_TYPE_WATCH,
VrHeadset = ffi::ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum UiModeNight {
Any = ffi::ACONFIGURATION_UI_MODE_NIGHT_ANY,
No = ffi::ACONFIGURATION_UI_MODE_NIGHT_NO,
Yes = ffi::ACONFIGURATION_UI_MODE_NIGHT_YES,
}

613
vendor/ndk-0.8.0/src/data_space.rs vendored Normal file
View File

@@ -0,0 +1,613 @@
//! Bindings for [`ADataSpace`]
//!
//! [`ADataSpace`]: https://developer.android.com/ndk/reference/group/a-data-space#group___a_data_space_1ga2759ad19cae46646cc5f7002758c4a1c
#![cfg(feature = "api-level-28")]
use std::fmt;
use num_enum::{IntoPrimitive, TryFromPrimitive, TryFromPrimitiveError};
/// Describes how to interpret colors.
///
/// <https://developer.android.com/ndk/reference/group/a-data-space#group___a_data_space_1ga2759ad19cae46646cc5f7002758c4a1c>
#[repr(u32)]
#[derive(Clone, Copy, Hash, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[doc(alias = "ADataSpace")]
#[non_exhaustive]
pub enum DataSpace {
/// Default-assumption data space, when not explicitly specified.
///
/// It is safest to assume the buffer is an image with `sRGB` primaries and encoding ranges,
/// but the consumer and/or the producer of the data may simply be using defaults. No automatic
/// gamma transform should be expected, except for a possible display gamma transform when drawn
/// to a screen.
#[doc(alias = "ADATASPACE_UNKNOWN")]
Unknown = ffi::ADataSpace::ADATASPACE_UNKNOWN.0,
/// Adobe RGB.
///
/// Uses [full range], [gamma `2.2` transfer] and [Adobe RGB standard].
///
/// Note: Application is responsible for gamma encoding the data as a `2.2` gamma encoding is
/// not supported in HW.
///
/// [full range]: DataSpaceRange::Full
/// [gamma `2.2` transfer]: DataSpaceTransfer::Gamma2_2
/// [Adobe RGB standard]: DataSpaceStandard::AdobeRgb
#[doc(alias = "ADATASPACE_ADOBE_RGB")]
AdobeRgb = ffi::ADataSpace::ADATASPACE_ADOBE_RGB.0,
/// ITU-R Recommendation 2020 (`BT.2020`).
///
/// Ultra High-definition television.
///
/// Uses [full range], [`SMPTE 170M` transfer] and [`BT2020` standard].
///
/// [full range]: DataSpaceRange::Full
/// [`SMPTE 170M` transfer]: DataSpaceTransfer::Smpte170M
/// [`BT2020` standard]: DataSpaceStandard::Bt2020
#[doc(alias = "ADATASPACE_BT2020")]
Bt2020 = ffi::ADataSpace::ADATASPACE_BT2020.0,
/// Hybrid Log Gamma encoding.
///
/// Uses [full range], [hybrid log gamma transfer] and [`BT2020` standard].
///
/// [full range]: DataSpaceRange::Full
/// [hybrid log gamma transfer]: DataSpaceTransfer::HLG
/// [`BT2020` standard]: DataSpaceStandard::Bt2020
#[doc(alias = "ADATASPACE_BT2020_HLG")]
Bt2020Hlg = ffi::ADataSpace::ADATASPACE_BT2020_HLG.0,
/// ITU Hybrid Log Gamma encoding.
///
/// Uses [limited range], [hybrid log gamma transfer] and [`BT2020` standard].
///
/// [limited range]: DataSpaceRange::Limited
/// [hybrid log gamma transfer]: DataSpaceTransfer::HLG
/// [`BT2020` standard]: DataSpaceStandard::Bt2020
#[doc(alias = "ADATASPACE_BT2020_ITU_HLG")]
Bt2020ItuHlg = ffi::ADataSpace::ADATASPACE_BT2020_ITU_HLG.0,
/// ITU-R Recommendation 2020 (`BT.2020`).
///
/// Ultra High-definition television.
///
/// Uses [limited range], [`SMPTE 2084 (PQ)` transfer] and [`BT2020` standard].
///
/// [limited range]: DataSpaceRange::Limited
/// [`SMPTE 2084 (PQ)` transfer]: DataSpaceTransfer::St2084
/// [`BT2020` standard]: DataSpaceStandard::Bt2020
#[doc(alias = "ADATASPACE_BT2020_ITU_PQ")]
Bt2020ItuPq = ffi::ADataSpace::ADATASPACE_BT2020_ITU_PQ.0,
/// ITU-R Recommendation 2020 (`BT.2020`).
///
/// Ultra High-definition television.
///
/// Uses [full range], [`SMPTE 2084 (PQ)` transfer] and [`BT2020` standard].
///
/// [full range]: DataSpaceRange::Full
/// [`SMPTE 2084 (PQ)` transfer]: DataSpaceTransfer::St2084
/// [`BT2020` standard]: DataSpaceStandard::Bt2020
#[doc(alias = "ADATASPACE_BT2020_PQ")]
Bt2020Pq = ffi::ADataSpace::ADATASPACE_BT2020_PQ.0,
/// ITU-R Recommendation 601 (`BT.601`) - 525-line.
///
/// Standard-definition television, 525 Lines (NTSC).
///
/// Uses [limited range], [`SMPTE 170M` transfer] and [`BT.601_525` standard].
///
/// [limited range]: DataSpaceRange::Limited
/// [`SMPTE 170M` transfer]: DataSpaceTransfer::Smpte170M
/// [`BT.601_525` standard]: DataSpaceStandard::Bt601_525
#[doc(alias = "ADATASPACE_BT601_525")]
Bt601_525 = ffi::ADataSpace::ADATASPACE_BT601_525.0,
/// ITU-R Recommendation 601 (`BT.601`) - 625-line.
///
/// Standard-definition television, 625 Lines (PAL).
///
/// Uses [limited range], [`SMPTE 170M` transfer] and [`BT.601_625` standard].
///
/// [limited range]: DataSpaceRange::Limited
/// [`SMPTE 170M` transfer]: DataSpaceTransfer::Smpte170M
/// [`BT.601_625` standard]: DataSpaceStandard::Bt601_625
#[doc(alias = "ADATASPACE_BT601_625")]
Bt601_625 = ffi::ADataSpace::ADATASPACE_BT601_625.0,
/// ITU-R Recommendation 709 (`BT.709`).
///
/// High-definition television.
///
/// Uses [limited range], [`SMPTE 170M` transfer] and [`BT.709` standard].
///
/// [limited range]: DataSpaceRange::Limited
/// [`SMPTE 170M` transfer]: DataSpaceTransfer::Smpte170M
/// [`BT.709` standard]: DataSpaceStandard::Bt709
#[doc(alias = "ADATASPACE_BT709")]
Bt709 = ffi::ADataSpace::ADATASPACE_BT709.0,
/// `SMPTE EG 432-1` and `SMPTE RP 431-2`.
///
/// Digital Cinema `DCI-P3`.
///
/// Uses [full range], [gamma `2.6` transfer] and [`D65` `DCI-P3` standard].
///
/// Note: Application is responsible for gamma encoding the data as a `2.6` gamma encoding is
/// not supported in HW.
///
/// [full range]: DataSpaceRange::Full
/// [gamma `2.6` transfer]: DataSpaceTransfer::Gamma2_6
/// [`D65` `DCI-P3` standard]: DataSpaceStandard::DciP3
#[doc(alias = "ADATASPACE_DCI_P3")]
DciP3 = ffi::ADataSpace::ADATASPACE_DCI_P3.0,
/// Display P3.
///
/// Uses [full range], [`sRGB` transfer] and [`D65` `DCI-P3` standard].
///
/// [full range]: DataSpaceRange::Full
/// [`sRGB` transfer]: DataSpaceTransfer::Srgb
/// [`D65` `DCI-P3` standard]: DataSpaceStandard::DciP3
#[doc(alias = "ADATASPACE_DISPLAY_P3")]
DisplayP3 = ffi::ADataSpace::ADATASPACE_DISPLAY_P3.0,
/// JPEG File Interchange Format (`JFIF`).
///
/// Same model as `BT.601-625`, but all values (`Y`, `Cb`, `Cr`) range from `0` to `255`.
///
/// Uses [full range], [`SMPTE 170M` transfer] and [`BT.601_625` standard].
///
/// [full range]: DataSpaceRange::Full
/// [`SMPTE 170M` transfer]: DataSpaceTransfer::Smpte170M
/// [`BT.601_625` standard]: DataSpaceStandard::Bt601_625
#[doc(alias = "ADATASPACE_JFIF")]
Jfif = ffi::ADataSpace::ADATASPACE_JFIF.0,
/// `scRGB`.
///
/// The `red`, `green`, and `blue` components are stored in [extended][extended range] `sRGB`
/// space, and gamma- encoded using the [`sRGB` transfer] function.
///
/// The values are floating point. A pixel value of `1.0`, `1.0`, `1.0` corresponds to `sRGB`
/// white (`D65`) at `80` nits. Values beyond the range `[0.0 - 1.0]` would correspond to other
/// colors spaces and/or HDR content.
///
/// Uses [extended range], [`sRGB` transfer] and [`BT.709` standard].
///
/// [extended range]: DataSpaceRange::Extended
/// [`sRGB` transfer]: DataSpaceTransfer::Srgb
/// [`BT.709` standard]: DataSpaceStandard::Bt709
#[doc(alias = "ADATASPACE_SCRGB")]
Scrgb = ffi::ADataSpace::ADATASPACE_SCRGB.0,
/// `scRGB` linear encoding
///
/// The `red`, `green`, and `blue` components are stored in [extended][extended range] `sRGB`
/// space, but are linear, not gamma-encoded.
///
/// The values are floating point. A pixel value of `1.0`, `1.0`, `1.0` corresponds to `sRGB`
/// white (`D65`) at `80` nits. Values beyond the range `[0.0 - 1.0]` would correspond to other
/// colors spaces and/or HDR content.
///
/// Uses [extended range], [linear transfer] and [`BT.709` standard].
///
/// [extended range]: DataSpaceRange::Extended
/// [linear transfer]: DataSpaceTransfer::Linear
/// [`BT.709` standard]: DataSpaceStandard::Bt709
#[doc(alias = "ADATASPACE_SCRGB_LINEAR")]
ScrgbLinear = ffi::ADataSpace::ADATASPACE_SCRGB_LINEAR.0,
/// `sRGB` gamma encoding.
///
/// The `red`, `green` and `blue` components are stored in `sRGB` space, and converted to linear
/// space when read, using the [`sRGB` transfer] function for each of the `R`, `G` and `B`
/// components. When written, the inverse transformation is performed.
///
/// The `alpha` component, if present, is always stored in linear space and is left unmodified
/// when read or written.
///
/// Uses [full range], [`sRGB` transfer] and [`BT.709` standard].
///
/// [full range]: DataSpaceRange::Full
/// [`sRGB` transfer]: DataSpaceTransfer::Srgb
/// [`BT.709` standard]: DataSpaceStandard::Bt709
#[doc(alias = "ADATASPACE_SRGB")]
Srgb = ffi::ADataSpace::ADATASPACE_SRGB.0,
/// `sRGB` linear encoding.
///
/// The `red`, `green`, and `blue` components are stored in `sRGB` space, but are linear, not
/// gamma-encoded. The `RGB` primaries and the white point are the same as [`BT.709]`.
///
/// The values are encoded using the [full range] (`[0, 255]` for 8-bit) for all components.
///
/// Uses [full range], [linear transfer] and [`BT.709` standard].
///
/// [full range]: DataSpaceRange::Full
/// [linear transfer]: DataSpaceTransfer::Linear
/// [`BT.709` standard]: DataSpaceStandard::Bt709
#[doc(alias = "ADATASPACE_SRGB_LINEAR")]
SrgbLinear = ffi::ADataSpace::ADATASPACE_SRGB_LINEAR.0,
/// Depth.
///
/// This value is valid with formats [`HAL_PIXEL_FORMAT_Y16`] and [`HAL_PIXEL_FORMAT_BLOB`].
///
/// [`HAL_PIXEL_FORMAT_Y16`]: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/native/libs/nativewindow/include/vndk/hardware_buffer.h;l=74-75;drc=45317f5c7c966fc816843217adc96a2ddea8bf29
/// [`HAL_PIXEL_FORMAT_BLOB`]: super::hardware_buffer_format::HardwareBufferFormat::BLOB
#[doc(alias = "DEPTH")]
Depth = ffi::ADataSpace::DEPTH.0,
/// ISO `16684-1:2011(E)` Dynamic Depth.
///
/// Embedded depth metadata following the dynamic depth specification.
#[doc(alias = "DYNAMIC_DEPTH")]
DynamicDepth = ffi::ADataSpace::DYNAMIC_DEPTH.0,
}
impl fmt::Display for DataSpace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
DataSpace::Unknown => "Unknown",
DataSpace::AdobeRgb => "AdobeRgb",
DataSpace::Bt2020 => "Bt2020",
DataSpace::Bt2020Hlg => "Bt2020Hlg",
DataSpace::Bt2020ItuHlg => "Bt2020ItuHlg",
DataSpace::Bt2020ItuPq => "Bt2020ItuPq",
DataSpace::Bt2020Pq => "Bt2020Pq",
DataSpace::Bt601_525 => "Bt601_525",
DataSpace::Bt601_625 => "Bt601_625",
DataSpace::Bt709 => "Bt709",
DataSpace::DciP3 => "DciP3",
DataSpace::DisplayP3 => "DisplayP3",
DataSpace::Jfif => "Jfif",
DataSpace::Scrgb => "Scrgb",
DataSpace::ScrgbLinear => "ScrgbLinear",
DataSpace::Srgb => "Srgb",
DataSpace::SrgbLinear => "SrgbLinear",
DataSpace::Depth => "Depth",
DataSpace::DynamicDepth => "DynamicDepth",
})
}
}
impl fmt::Debug for DataSpace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"DataSpace({}, standard: {:?}, transfer: {:?}, range: {:?})",
self,
self.standard(),
self.transfer(),
self.range(),
)
}
}
impl DataSpace {
/// Construct a [`DataSpace`] from individual `standard`, `transfer` and `range` components.
///
/// Together these should correspond to a single format.
pub fn from_parts(
standard: DataSpaceStandard,
transfer: DataSpaceTransfer,
range: DataSpaceRange,
) -> Result<Self, TryFromPrimitiveError<Self>> {
Self::try_from_primitive(standard as u32 | transfer as u32 | range as u32)
}
/// Extracts and returns the color-description aspect from this [`DataSpace`].
#[doc(alias = "STANDARD_MASK")]
pub fn standard(self) -> Result<DataSpaceStandard, TryFromPrimitiveError<DataSpaceStandard>> {
let standard = self as u32 & ffi::ADataSpace::STANDARD_MASK.0;
DataSpaceStandard::try_from(standard)
}
/// Extracts and returns the transfer aspect from this [`DataSpace`].
#[doc(alias = "TRANSFER_MASK")]
pub fn transfer(self) -> Result<DataSpaceTransfer, TryFromPrimitiveError<DataSpaceTransfer>> {
let transfer = self as u32 & ffi::ADataSpace::TRANSFER_MASK.0;
DataSpaceTransfer::try_from(transfer)
}
/// Extracts and returns the range aspect from this [`DataSpace`].
#[doc(alias = "RANGE_MASK")]
pub fn range(self) -> Result<DataSpaceRange, TryFromPrimitiveError<DataSpaceRange>> {
let range = self as u32 & ffi::ADataSpace::RANGE_MASK.0;
DataSpaceRange::try_from(range)
}
}
/// Color-description aspects.
///
/// The following aspects define various characteristics of the color specification. These represent
/// bitfields, so that a data space value can specify each of them independently. Standard aspect
/// defines the chromaticity coordinates of the source primaries in terms of the CIE 1931 definition
/// of `x` and `y` specified in ISO 11664-1.
#[repr(u32)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[doc(alias = "STANDARD_MASK")]
#[non_exhaustive]
pub enum DataSpaceStandard {
/// Chromacity coordinates are unknown or are determined by the application. Implementations
/// shall use the following suggested standards:
///
/// All `YCbCr` formats: [`BT.709`] if size is `720p` or larger (since most video content is
/// letterboxed this corresponds to width is `1280` or greater, or height
/// is 720 or greater). [`BT.601_625`] if size is smaller than `720p` or
/// is `JPEG`.
/// All `RGB` formats: [`BT.709`].
///
/// For all other formats the standard is undefined, and implementations should use an
/// appropriate standard for the data represented.
///
/// [`BT.709`]: Self::Bt709
/// [`BT.601_625`]: Self::Bt601_625
#[doc(alias = "STANDARD_UNSPECIFIED")]
Unspecified = ffi::ADataSpace::STANDARD_UNSPECIFIED.0,
/// | Primaries | x | y |
/// | ----------- | ------ | ------ |
/// | green | 0.300 | 0.600 |
/// | blue | 0.150 | 0.060 |
/// | red | 0.640 | 0.330 |
/// | white (D65) | 0.3127 | 0.3290 |
///
/// Use the unadjusted `KR = 0.2126`, `KB = 0.0722` luminance interpretation for `RGB`
/// conversion.
#[doc(alias = "STANDARD_BT709")]
Bt709 = ffi::ADataSpace::STANDARD_BT709.0,
/// | Primaries | x | y |
/// | ----------- | ------ | ------ |
/// | green | 0.290 | 0.600 |
/// | blue | 0.150 | 0.060 |
/// | red | 0.640 | 0.330 |
/// | white (D65) | 0.3127 | 0.3290 |
///
/// `KR = 0.299`, `KB = 0.114`. This adjusts the luminance interpretation for `RGB` conversion
/// from the one purely determined by the primaries to minimize the color shift into `RGB`
/// space that uses [`BT.709`] primaries.
///
/// [`BT.709`]: Self::Bt709
#[doc(alias = "STANDARD_BT601_625")]
Bt601_625 = ffi::ADataSpace::STANDARD_BT601_625.0,
/// | Primaries | x | y |
/// | ----------- | ------ | ------ |
/// | green | 0.290 | 0.600 |
/// | blue | 0.150 | 0.060 |
/// | red | 0.640 | 0.330 |
/// | white (D65) | 0.3127 | 0.3290 |
///
/// Use the unadjusted `KR = 0.222`, `KB = 0.071` luminance interpretation for `RGB` conversion.
#[doc(alias = "STANDARD_BT601_625_UNADJUSTED")]
Bt601_625Unadjusted = ffi::ADataSpace::STANDARD_BT601_625_UNADJUSTED.0,
/// | Primaries | x | y |
/// | ----------- | ------ | ------ |
/// | green | 0.310 | 0.595 |
/// | blue | 0.155 | 0.070 |
/// | red | 0.630 | 0.340 |
/// | white (D65) | 0.3127 | 0.3290 |
///
/// `KR = 0.299`, `KB = 0.114`. This adjusts the luminance interpretation for `RGB` conversion
/// from the one purely determined by the primaries to minimize the color shift into `RGB` space
/// that uses [`BT.709`] primaries.
///
/// [`BT.709`]: Self::Bt709
#[doc(alias = "STANDARD_BT601_525")]
Bt601_525 = ffi::ADataSpace::STANDARD_BT601_525.0,
/// | Primaries | x | y |
/// | ----------- | ------ | ------ |
/// | green | 0.310 | 0.595 |
/// | blue | 0.155 | 0.070 |
/// | red | 0.630 | 0.340 |
/// | white (D65) | 0.3127 | 0.3290 |
///
/// Use the unadjusted `KR = 0.212`, `KB = 0.087` luminance interpretation
/// for `RGB` conversion (as in `SMPTE 240M`).
#[doc(alias = "STANDARD_BT601_525_UNADJUSTED")]
Bt601_525Unadjusted = ffi::ADataSpace::STANDARD_BT601_525_UNADJUSTED.0,
/// | Primaries | x | y |
/// | ----------- | ------ | ------ |
/// | green | 0.170 | 0.797 |
/// | blue | 0.131 | 0.046 |
/// | red | 0.708 | 0.292 |
/// | white (D65) | 0.3127 | 0.3290 |
///
/// Use the unadjusted `KR = 0.2627`, `KB = 0.0593` luminance interpretation for `RGB`
/// conversion.
#[doc(alias = "STANDARD_BT2020")]
Bt2020 = ffi::ADataSpace::STANDARD_BT2020.0,
/// | Primaries | x | y |
/// | ----------- | ------ | ------ |
/// | green | 0.170 | 0.797 |
/// | blue | 0.131 | 0.046 |
/// | red | 0.708 | 0.292 |
/// | white (D65) | 0.3127 | 0.3290 |
///
/// Use the unadjusted `KR = 0.2627`, `KB = 0.0593` luminance interpretation for `RGB`
/// conversion using the linear domain.
#[doc(alias = "STANDARD_BT2020_CONSTANT_LUMINANCE")]
Bt2020ConstantLuminance = ffi::ADataSpace::STANDARD_BT2020_CONSTANT_LUMINANCE.0,
/// | Primaries | x | y |
/// | --------- | ----- | ---- |
/// | green | 0.21 |0.71 |
/// | blue | 0.14 |0.08 |
/// | red | 0.67 |0.33 |
/// | white (C) | 0.310 |0.316 |
///
/// Use the unadjusted `KR = 0.30`, `KB = 0.11` luminance interpretation for `RGB` conversion.
#[doc(alias = "STANDARD_BT470M")]
Bt470M = ffi::ADataSpace::STANDARD_BT470M.0,
/// | Primaries | x | y |
/// | --------- | ----- | ----- |
/// | green | 0.243 | 0.692 |
/// | blue | 0.145 | 0.049 |
/// | red | 0.681 | 0.319 |
/// | white (C) | 0.310 | 0.316 |
///
/// Use the unadjusted `KR = 0.254`, `KB = 0.068` luminance interpretation for `RGB` conversion.
#[doc(alias = "STANDARD_FILM")]
Film = ffi::ADataSpace::STANDARD_FILM.0,
/// `SMPTE EG 432-1` and `SMPTE RP 431-2`. (`DCI-P3`)
///
/// | Primaries | x | y |
/// | ----------- | ------ | ------ |
/// | green | 0.265 | 0.690 |
/// | blue | 0.150 | 0.060 |
/// | red | 0.680 | 0.320 |
/// | white (D65) | 0.3127 | 0.3290 |
#[doc(alias = "STANDARD_DCI_P3")]
DciP3 = ffi::ADataSpace::STANDARD_DCI_P3.0,
/// Adobe RGB
///
/// | Primaries | x | y |
/// | ----------- | ------ | ------ |
/// | green | 0.210 | 0.710 |
/// | blue | 0.150 | 0.060 |
/// | red | 0.640 | 0.330 |
/// | white (D65) | 0.3127 | 0.3290 |
#[doc(alias = "STANDARD_ADOBE_RGB")]
AdobeRgb = ffi::ADataSpace::STANDARD_ADOBE_RGB.0,
}
/// Transfer aspect.
///
/// Transfer characteristics are the opto-electronic transfer characteristic at the source as a
///function of linear optical intensity (luminance).
///
/// For digital signals, `E` corresponds to the recorded value. Normally, the transfer function is
/// applied in `RGB` space to each of the `R`, `G` and `B` components independently. This may result
/// in color shift that can be minimized by applying the transfer function in `Lab` space only for
/// the `L` component. Implementation may apply the transfer function in `RGB` space for all pixel
/// formats if desired.
#[repr(u32)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[doc(alias = "TRANSFER_MASK")]
#[non_exhaustive]
pub enum DataSpaceTransfer {
/// Transfer characteristics are unknown or are determined by the application.
///
/// Implementations should use the following transfer functions:
///
/// - For `YCbCr` formats: use [`DataSpaceTransfer::Smpte170M`]
/// - For `RGB` formats: use [`DataSpaceTransfer::Srgb`]
///
/// For all other formats the transfer function is undefined, and implementations should use an
/// appropriate standard for the data represented.
#[doc(alias = "TRANSFER_UNSPECIFIED")]
Unspecified = ffi::ADataSpace::TRANSFER_UNSPECIFIED.0,
/// Linear transfer.
///
/// Transfer characteristic curve:
/// ```ignore
/// E = L
/// ```
/// - `L`: luminance of image `0 <= L <= 1` for conventional colorimetry
/// - `E`: corresponding electrical signal
#[doc(alias = "TRANSFER_LINEAR")]
Linear = ffi::ADataSpace::TRANSFER_LINEAR.0,
/// `sRGB` transfer.
///
/// Transfer characteristic curve:
///
/// ```ignore
/// E = 1.055 * L^(1/2.4) - 0.055 for 0.0031308 <= L <= 1
/// = 12.92 * L for 0 <= L < 0.0031308
/// ```
/// - `L`: luminance of image `0 <= L <= 1` for conventional colorimetry
/// - `E`: corresponding electrical signal
#[doc(alias = "TRANSFER_SRGB")]
Srgb = ffi::ADataSpace::TRANSFER_SRGB.0,
/// SMPTE 170M transfer.
///
/// Transfer characteristic curve:
/// ```ignore
/// E = 1.099 * L ^ 0.45 - 0.099 for 0.018 <= L <= 1
/// = 4.500 * L for 0 <= L < 0.018
/// ```
/// - `L`: luminance of image `0 <= L <= 1` for conventional colorimetry
/// - `E`: corresponding electrical signal
#[doc(alias = "TRANSFER_SMPTE_170M")]
Smpte170M = ffi::ADataSpace::TRANSFER_SMPTE_170M.0,
/// Display gamma `2.2`.
///
/// Transfer characteristic curve:
/// ```ignore
/// E = L ^ (1/2.2)
/// ```
/// - `L`: luminance of image `0 <= L <= 1` for conventional colorimetry
/// - `E`: corresponding electrical signal
#[doc(alias = "TRANSFER_GAMMA2_2")]
Gamma2_2 = ffi::ADataSpace::TRANSFER_GAMMA2_2.0,
/// Display gamma `2.6`.
///
/// Transfer characteristic curve:
/// ```ignore
/// E = L ^ (1/2.6)
/// ```
/// - `L`: luminance of image `0 <= L <= 1` for conventional colorimetry
/// - `E`: corresponding electrical signal
#[doc(alias = "TRANSFER_GAMMA2_6")]
Gamma2_6 = ffi::ADataSpace::TRANSFER_GAMMA2_6.0,
/// Display gamma `2.8`.
///
/// Transfer characteristic curve:
/// ```ignore
/// E = L ^ (1/2.8)
/// ```
/// - `L`: luminance of image `0 <= L <= 1` for conventional colorimetry
/// - `E`: corresponding electrical signal
#[doc(alias = "TRANSFER_GAMMA2_8")]
Gamma2_8 = ffi::ADataSpace::TRANSFER_GAMMA2_8.0,
/// SMPTE ST 2084 (Dolby Perceptual Quantizer).
///
/// Transfer characteristic curve:
/// ```ignore
/// E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
/// c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
/// c2 = 32 * 2413 / 4096 = 18.8515625
/// c3 = 32 * 2392 / 4096 = 18.6875
/// m = 128 * 2523 / 4096 = 78.84375
/// n = 0.25 * 2610 / 4096 = 0.1593017578125
/// ```
/// - `L`: luminance of image 0 <= L <= 1 for HDR colorimetry.
/// `L = 1` corresponds to `10000 cd/m2`
#[doc(alias = "TRANSFER_ST2084")]
St2084 = ffi::ADataSpace::TRANSFER_ST2084.0,
/// ARIB STD-B67 Hybrid Log Gamma.
///
/// Transfer characteristic curve:
/// ```ignore
/// E = r * L^0.5 for 0 <= L <= 1
/// = a * ln(L - b) + c for 1 < L
/// a = 0.17883277
/// b = 0.28466892
/// c = 0.55991073
/// r = 0.5
/// ```
/// - `L`: luminance of image `0 <= L` for HDR colorimetry.
/// `L = 1` corresponds to reference white level of `100 cd/m2`
/// - `E`: corresponding electrical signal
#[doc(alias = "TRANSFER_HLG")]
HLG = ffi::ADataSpace::TRANSFER_HLG.0,
}
/// Range aspect.
///
/// Defines the range of values corresponding to the unit range of `0-1`. This is defined for
/// `YCbCr` only, but can be expanded to `RGB` space.
#[repr(u32)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[doc(alias = "RANGE_MASK")]
#[non_exhaustive]
pub enum DataSpaceRange {
/// Full range uses all values for `Y`, `Cb` and `Cr` from `0` to `2^b-1`, where `b` is the bit
/// depth of the color format.
#[doc(alias = "RANGE_FULL")]
Full = ffi::ADataSpace::RANGE_FULL.0,
/// Limited range uses values `16/256*2^b` to `235/256*2^b` for `Y`, and `1/16*2^b` to
/// `15/16*2^b` for `Cb`, `Cr`, `R`, `G` and `B`, where `b` is the bit depth of the color
/// format.
///
/// E.g. For 8-bit-depth formats: Luma (`Y`) samples should range from `16` to `235`, inclusive
/// Chroma `(Cb, Cr)` samples should range from `16` to `240`, inclusive.
///
/// For 10-bit-depth formats: Luma (`Y`) samples should range from `64` to `940`, inclusive
/// Chroma `(Cb, Cr)` samples should range from `64` to `960`, inclusive.
#[doc(alias = "RANGE_LIMITED")]
Limited = ffi::ADataSpace::RANGE_LIMITED.0,
/// Extended range is used for `scRGB`.
///
/// Intended for use with floating point pixel formats. `[0.0 - 1.0]` is the standard `sRGB`
/// space. Values outside the range `0.0 - 1.0` can encode color outside the `sRGB` gamut. Used
/// to blend / merge multiple dataspaces on a single display.
#[doc(alias = "RANGE_EXTENDED")]
Extended = ffi::ADataSpace::RANGE_EXTENDED.0,
}

1499
vendor/ndk-0.8.0/src/event.rs vendored Normal file

File diff suppressed because it is too large Load Diff

549
vendor/ndk-0.8.0/src/font.rs vendored Normal file
View File

@@ -0,0 +1,549 @@
//! Bindings for [`AFont`], [`AFontMatcher`], and [`ASystemFontIterator`]
//!
//! [`AFont`]: https://developer.android.com/ndk/reference/group/font
//! [`AFontMatcher`]: https://developer.android.com/ndk/reference/group/font#afontmatcher_create
//! [`ASystemFontIterator`]: https://developer.android.com/ndk/reference/group/font#asystemfontiterator_open
#![cfg(feature = "api-level-29")]
use std::convert::TryFrom;
use std::ffi::{CStr, OsStr};
use std::fmt::{self, Write};
use std::os::unix::prelude::OsStrExt;
use std::path::Path;
use std::ptr::NonNull;
/// An integer holding a valid font weight value between 1 and 1000.
///
/// See the following definitions for more details:
/// * [`AFONT_WEIGHT_*`]
/// * [`Font::weight`]
///
/// [`AFONT_WEIGHT_*`]: https://developer.android.com/ndk/reference/group/font#anonymous-enum-33
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct FontWeight(u16);
impl FontWeight {
pub const fn new(value: u16) -> Result<Self, FontWeightValueError> {
if Self::MIN.0 <= value && value <= Self::MAX.0 {
Ok(Self(value))
} else {
Err(FontWeightValueError(()))
}
}
pub const fn to_u16(self) -> u16 {
self.0
}
/// The minimum value for the font weight value. Unlike [`ffi::AFONT_WEIGHT_MIN`] being `0`,
/// [`FontWeight::MIN`] is `1` to make the `MIN..MAX` range be inclusive, keeping consistency
/// between [`FontWeight`] and other types like `std::num::NonZeroU*`.
pub const MIN: FontWeight = FontWeight(ffi::AFONT_WEIGHT_MIN as u16 + 1);
/// A font weight value for the thin weight.
pub const THIN: FontWeight = FontWeight(ffi::AFONT_WEIGHT_THIN as u16);
/// A font weight value for the extra-light weight.
pub const EXTRA_LIGHT: FontWeight = FontWeight(ffi::AFONT_WEIGHT_EXTRA_LIGHT as u16);
/// A font weight value for the light weight.
pub const LIGHT: FontWeight = FontWeight(ffi::AFONT_WEIGHT_LIGHT as u16);
/// A font weight value for the normal weight.
pub const NORMAL: FontWeight = FontWeight(ffi::AFONT_WEIGHT_NORMAL as u16);
/// A font weight value for the medium weight.
pub const MEDIUM: FontWeight = FontWeight(ffi::AFONT_WEIGHT_MEDIUM as u16);
/// A font weight value for the semi-bold weight.
pub const SEMI_BOLD: FontWeight = FontWeight(ffi::AFONT_WEIGHT_SEMI_BOLD as u16);
/// A font weight value for the bold weight.
pub const BOLD: FontWeight = FontWeight(ffi::AFONT_WEIGHT_BOLD as u16);
/// A font weight value for the extra-bold weight.
pub const EXTRA_BOLD: FontWeight = FontWeight(ffi::AFONT_WEIGHT_EXTRA_BOLD as u16);
/// A font weight value for the black weight.
pub const BLACK: FontWeight = FontWeight(ffi::AFONT_WEIGHT_BLACK as u16);
/// The maximum value for the font weight value.
pub const MAX: FontWeight = FontWeight(ffi::AFONT_WEIGHT_MAX as u16);
}
impl fmt::Display for FontWeight {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
FontWeight::THIN => f.write_str("Thin"),
FontWeight::EXTRA_LIGHT => f.write_str("Extra Light (Ultra Light)"),
FontWeight::LIGHT => f.write_str("Light"),
FontWeight::NORMAL => f.write_str("Normal (Regular)"),
FontWeight::MEDIUM => f.write_str("Medium"),
FontWeight::SEMI_BOLD => f.write_str("Semi Bold (Demi Bold)"),
FontWeight::BOLD => f.write_str("Bold"),
FontWeight::EXTRA_BOLD => f.write_str("Extra Bold (Ultra Bold)"),
FontWeight::BLACK => f.write_str("Black (Heavy)"),
_ => writeln!(f, "{}", self.0),
}
}
}
/// The error type returned when an invalid font weight value is passed.
#[derive(Debug)]
pub struct FontWeightValueError(());
impl fmt::Display for FontWeightValueError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.write_str("font weight must be positive and less than or equal to 1000")
}
}
impl std::error::Error for FontWeightValueError {}
impl TryFrom<u16> for FontWeight {
type Error = FontWeightValueError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
FontWeight::new(value)
}
}
/// A 4-byte integer representing an OpenType axis tag.
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct AxisTag(u32);
impl AxisTag {
/// Checks whether the given 4-byte array can construct a valid axis tag and returns
/// [`Ok(AxisTag)`] if the array is valid.
///
/// Each byte in a tag must be in the range 0x20 to 0x7E. A space character cannot be followed
/// by a non-space character. A tag must have one to four non-space characters. See the
/// [OpenType spec] for more details.
///
/// [OpenType spec]: https://learn.microsoft.com/en-us/typography/opentype/spec/otff#data-types
pub const fn from_be_bytes_checked(value: [u8; 4]) -> Result<Self, AxisTagValueError> {
// Each byte in a tag must be in the range 0x20 to 0x7E.
macro_rules! check_byte_range {
($($e:expr)+) => {
$(
if !(value[$e] as char).is_ascii_graphic() && value[$e] != b' ' {
return Err(AxisTagValueError::InvalidCharacter);
}
)+
};
}
check_byte_range!(0 1 2 3);
if value[0] == b' ' {
return Err(
if value[1] == b' ' && value[2] == b' ' && value[3] == b' ' {
// A tag must have one to four non-space characters.
AxisTagValueError::EmptyTag
} else {
// A space character cannot be followed by a non-space character.
AxisTagValueError::InvalidSpacePadding
},
);
}
macro_rules! check_if_valid {
($e:expr ; $($f:expr)+) => {
if value[$e] == b' ' {
return if true $(&& value[$f] == b' ')+ {
Ok(Self(u32::from_be_bytes(value)))
} else {
// A space character cannot be followed by a non-space character.
Err(AxisTagValueError::InvalidSpacePadding)
};
}
};
}
check_if_valid!(1; 2 3);
check_if_valid!(2; 3);
// Whether or not value[3] is b' ', value is a valid axis tag.
Ok(Self(u32::from_be_bytes(value)))
}
/// Checks whether the given 4-byte array can construct a valid axis tag and returns
/// [`Ok(AxisTag)`] if the array is valid.
///
/// See [`AxisTag::from_be()`] for more details.
pub const fn from_be_checked(value: u32) -> Result<Self, AxisTagValueError> {
Self::from_be_bytes_checked(value.to_be_bytes())
}
/// Construct an axis tag from the given 4-byte array. If the resulting axis tag is invalid,
/// this function panics.
///
/// See [`AxisTag::from_be()`] for more details.
pub const fn from_be_bytes(value: [u8; 4]) -> Self {
Self::unwrap_result(Self::from_be_bytes_checked(value))
}
/// Construct an axis tag from the given 4-byte integer. If the resulting axis tag is invalid,
/// this function panics.
///
/// See [`AxisTag::from_be()`] for more details.
pub const fn from_be(value: u32) -> Self {
Self::unwrap_result(Self::from_be_checked(value))
}
/// const-version of [`Result::unwrap`]. Should be removed when [`Option::unwrap`] or
/// [`Result::unwrap`] become `const`-stable.
const fn unwrap_result(result: Result<Self, AxisTagValueError>) -> Self {
match result {
Ok(t) => t,
Err(e) => panic!("{}", e.as_str()),
}
}
pub const fn to_u32(self) -> u32 {
self.0
}
pub const fn to_be_bytes(self) -> [u8; 4] {
self.0.to_be_bytes()
}
}
impl fmt::Display for AxisTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let bytes = self.to_be_bytes();
f.write_char(bytes[0] as char)?;
f.write_char(bytes[1] as char)?;
f.write_char(bytes[2] as char)?;
f.write_char(bytes[3] as char)
}
}
impl fmt::Debug for AxisTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "AxisTag({} {:#x})", self, self.0)
}
}
/// The error type returned when an invalid axis tag value is passed.
#[derive(Clone, Copy, Debug)]
pub enum AxisTagValueError {
/// There is a byte not in the range 0x20 to 0x7E.
InvalidCharacter,
/// There is a space character followed by a non-space character.
InvalidSpacePadding,
/// The tag only consists of space characters.
EmptyTag,
}
impl AxisTagValueError {
pub const fn as_str(&self) -> &'static str {
match self {
Self::InvalidCharacter => "each byte in an axis tag must be in the range 0x20 to 0x7E",
Self::InvalidSpacePadding => {
"a space character cannot be followed by a non-space character"
}
Self::EmptyTag => "a tag must have one to four non-space characters",
}
}
}
impl fmt::Display for AxisTagValueError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.write_str(self.as_str())
}
}
impl std::error::Error for AxisTagValueError {}
/// A native [`AFont *`]
///
/// [`AFont *`]: https://developer.android.com/ndk/reference/group/font
#[derive(Debug)]
pub struct Font {
ptr: NonNull<ffi::AFont>,
}
impl Font {
/// Assumes ownership of `ptr`.
///
/// # Safety
/// `ptr` must be a valid owning pointer to an Android [`ffi::AFont`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::AFont>) -> Self {
Self { ptr }
}
/// Returns s the pointer to the native [`ffi::AFont`].
pub fn ptr(&self) -> NonNull<ffi::AFont> {
self.ptr
}
/// Returns a count of font variation settings associated with the current font.
///
/// The font variation settings are provided as multiple tag-value pairs.
///
/// For example, bold italic font may have following font variation settings: `'wght' 700`,
/// `'slnt' -12`. In this case, [`Font::axis_count()`] returns `2` and [`Font::axis_tag_at()`] and
/// [`Font::axis_value_at()`] return those variation names and the corresponding values.
///
/// ```no_run
/// use ndk::font::Font;
///
/// let font: Font = todo!();
/// for idx in 0..font.axis_count() {
/// log::debug!("{}: {}", font.axis_tag_at(idx), font.axis_value_at(idx));
/// }
/// // Output:
/// // wght: 700
/// // slnt: -12
/// ```
pub fn axis_count(&self) -> usize {
unsafe { ffi::AFont_getAxisCount(self.ptr.as_ptr()) }
}
/// Returns an OpenType axis tag associated with the current font.
///
/// See [`Font::axis_count()`] for more details.
pub fn axis_tag_at(&self, idx: usize) -> AxisTag {
// Android returns Axis Tag in big-endian.
// See https://cs.android.com/android/platform/superproject/+/refs/heads/master:frameworks/base/native/android/system_fonts.cpp;l=197 for details
AxisTag(unsafe { ffi::AFont_getAxisTag(self.ptr.as_ptr(), idx as u32) })
}
/// Returns an OpenType axis value associated with the current font.
///
/// See [`Font::axis_count()`] for more details.
pub fn axis_value_at(&self, idx: usize) -> f32 {
unsafe { ffi::AFont_getAxisValue(self.ptr.as_ptr(), idx as u32) }
}
/// Returns a font collection index value associated with the current font.
///
/// In case the target font file is a font collection (e.g. `.ttc` or `.otc`), this returns a
/// non-negative value as a font offset in the collection. This always returns 0 if the target
/// font file is a regular font.
pub fn collection_index(&self) -> usize {
unsafe { ffi::AFont_getCollectionIndex(self.ptr.as_ptr()) }
}
/// Returns an absolute path to the current font file.
///
/// Here is a list of font formats returned by this method:
///
/// * OpenType
/// * OpenType Font Collection
/// * TrueType
/// * TrueType Collection
///
/// The file extension could be one of `*.otf`, `*.ttf`, `*.otc` or `*.ttc`.
/// The font file specified by the returned path is guaranteed to be openable with `O_RDONLY`.
pub fn path(&self) -> &Path {
let path = unsafe { CStr::from_ptr(ffi::AFont_getFontFilePath(self.ptr.as_ptr())) };
OsStr::from_bytes(path.to_bytes()).as_ref()
}
/// Returns an IETF BCP47 compliant language tag associated with the current font.
///
/// For information about IETF BCP47, read [`Locale.forLanguageTag(java.lang.String)`].
///
/// [`Locale.forLanguageTag(java.lang.String)`]: https://developer.android.com/reference/java/util/Locale.html#forLanguageTag(java.lang.String)
pub fn locale(&self) -> Option<&CStr> {
let ptr = unsafe { ffi::AFont_getLocale(self.ptr.as_ptr()) };
if ptr.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(ptr) })
}
}
/// Returns a weight value associated with the current font.
///
/// The weight values are positive and less than or equal to 1000. Here are pairs of the common
/// names and their values.
///
/// | Value | Name | NDK Definition |
/// | ----- | ------------------------- | --------------------------- |
/// | 100 | Thin | [`FontWeight::THIN`] |
/// | 200 | Extra Light (Ultra Light) | [`FontWeight::EXTRA_LIGHT`] |
/// | 300 | Light | [`FontWeight::LIGHT`] |
/// | 400 | Normal (Regular) | [`FontWeight::NORMAL`] |
/// | 500 | Medium | [`FontWeight::MEDIUM`] |
/// | 600 | Semi Bold (Demi Bold) | [`FontWeight::SEMI_BOLD`] |
/// | 700 | Bold | [`FontWeight::BOLD`] |
/// | 800 | Extra Bold (Ultra Bold) | [`FontWeight::EXTRA_BOLD`] |
/// | 900 | Black (Heavy) | [`FontWeight::BLACK`] |
pub fn weight(&self) -> FontWeight {
FontWeight(unsafe { ffi::AFont_getWeight(self.ptr.as_ptr()) })
}
/// Returns [`true`] if the current font is italic, otherwise returns [`false`].
pub fn is_italic(&self) -> bool {
unsafe { ffi::AFont_isItalic(self.ptr.as_ptr()) }
}
}
impl Drop for Font {
fn drop(&mut self) {
unsafe { ffi::AFont_close(self.ptr.as_ptr()) }
}
}
/// Corresponds to [`AFAMILY_VARIANT_*`].
///
/// [`AFAMILY_VARIANT_*`]: https://developer.android.com/ndk/reference/group/font#group___font_1gga96a58e29e8dbf2b5bdeb775cba46556ea662aafc7016e35d6758da93416fc0833
#[repr(u32)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum FamilyVariant {
/// A family variant value for the compact font family variant.
/// The compact font family has Latin-based vertical metrics.
Compact = ffi::AFAMILY_VARIANT_COMPACT as _,
/// A family variant value for the system default variant.
Default = ffi::AFAMILY_VARIANT_DEFAULT as _,
/// A family variant value for the elegant font family variant.
/// The elegant font family may have larger vertical metrics than Latin font.
Elegant = ffi::AFAMILY_VARIANT_ELEGANT as _,
}
/// A native [`AFontMatcher *`]
///
/// [`AFontMatcher *`]: https://developer.android.com/ndk/reference/group/font#afontmatcher_create
#[derive(Debug)]
pub struct FontMatcher {
ptr: NonNull<ffi::AFontMatcher>,
}
impl FontMatcher {
/// Assumes ownership of `ptr`.
///
/// # Safety
/// `ptr` must be a valid owning pointer to an Android [`ffi::AFontMatcher`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::AFontMatcher>) -> Self {
Self { ptr }
}
/// Returns s the pointer to the native [`ffi::AFontMatcher`].
pub fn ptr(&self) -> NonNull<ffi::AFontMatcher> {
self.ptr
}
/// Creates a new [`FontMatcher`] object. [`FontMatcher`] selects the best font from the
/// parameters set by the user.
pub fn new() -> Self {
let ptr = NonNull::new(unsafe { ffi::AFontMatcher_create() })
.expect("AFontMatcher_create returned NULL");
unsafe { FontMatcher::from_ptr(ptr) }
}
/// Performs the matching from the generic font family for the text and select one font.
///
/// For more information about generic font families, please read the
/// [W3C spec](https://www.w3.org/TR/css-fonts-4/#generic-font-families).
///
/// Even if no font can render the given text, this function will return a non-null result for
/// drawing Tofu character.
///
/// # Parameters
///
/// - `family_name`: A font family name.
/// - `text`: A UTF-16 encoded text buffer to be rendered. If an empty string is given, this
/// function will panic.
/// - `run_length_out`: Set this to [`Some`] if you want to get the length of the text run with
/// the font returned.
pub fn match_font(
&mut self,
family_name: &CStr,
text: &[u16],
run_length_out: Option<&mut u32>,
) -> Font {
if text.is_empty() {
panic!("text is empty");
}
unsafe {
Font::from_ptr(
NonNull::new(ffi::AFontMatcher_match(
self.ptr.as_ptr(),
family_name.as_ptr(),
text.as_ptr(),
text.len() as _,
run_length_out.map_or(std::ptr::null_mut(), |u| u),
))
.expect("AFontMatcher_match returned NULL"),
)
}
}
/// Sets the family variant of the font to be matched.
///
/// If this function is not called, the match is performed with [`FamilyVariant::Default`].
pub fn set_family_variant(&mut self, family_variant: FamilyVariant) {
unsafe { ffi::AFontMatcher_setFamilyVariant(self.ptr.as_ptr(), family_variant as u32) }
}
/// Sets the locale of the font to be matched.
///
/// If this function is not called, the match is performed with an empty locale list.
///
/// # Parameters
///
/// - `language_tags`: comma separated IETF BCP47 compliant language tags.
pub fn set_locales(&mut self, language_tags: &CStr) {
unsafe { ffi::AFontMatcher_setLocales(self.ptr.as_ptr(), language_tags.as_ptr()) }
}
/// Sets the style of the font to be matched.
///
/// If this function is not called, the match is performed with [`FontWeight::NORMAL`] with non-italic style.
pub fn set_style(&mut self, weight: FontWeight, italic: bool) {
unsafe { ffi::AFontMatcher_setStyle(self.ptr.as_ptr(), weight.to_u16(), italic) }
}
}
impl Drop for FontMatcher {
fn drop(&mut self) {
unsafe { ffi::AFontMatcher_destroy(self.ptr.as_ptr()) }
}
}
/// A native [`ASystemFontIterator *`]
///
/// [`ASystemFontIterator *`]: https://developer.android.com/ndk/reference/group/font#asystemfontiterator_open
#[derive(Debug)]
pub struct SystemFontIterator {
ptr: NonNull<ffi::ASystemFontIterator>,
}
impl SystemFontIterator {
/// Assumes ownership of `ptr`.
///
/// # Safety
/// `ptr` must be a valid owning pointer to an Android [`ffi::ASystemFontIterator`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::ASystemFontIterator>) -> Self {
Self { ptr }
}
/// Returns the pointer to the native [`ffi::ASystemFontIterator`].
pub fn ptr(&self) -> NonNull<ffi::ASystemFontIterator> {
self.ptr
}
/// Creates a system font iterator.
pub fn new() -> Option<Self> {
NonNull::new(unsafe { ffi::ASystemFontIterator_open() })
.map(|p| unsafe { SystemFontIterator::from_ptr(p) })
}
}
impl Iterator for SystemFontIterator {
type Item = Font;
fn next(&mut self) -> Option<Self::Item> {
NonNull::new(unsafe { ffi::ASystemFontIterator_next(self.ptr.as_ptr()) })
.map(|p| unsafe { Font::from_ptr(p) })
}
}
impl Drop for SystemFontIterator {
fn drop(&mut self) {
unsafe { ffi::ASystemFontIterator_close(self.ptr.as_ptr()) }
}
}

543
vendor/ndk-0.8.0/src/hardware_buffer.rs vendored Normal file
View File

@@ -0,0 +1,543 @@
//! Bindings for [`AHardwareBuffer`]
//!
//! [`AHardwareBuffer`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer
#![cfg(feature = "api-level-26")]
use std::{
io::Result,
mem::MaybeUninit,
ops::Deref,
os::{
fd::{AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd},
raw::c_void,
},
ptr::NonNull,
};
use jni_sys::{jobject, JNIEnv};
use super::{hardware_buffer_format::HardwareBufferFormat, utils::status_to_io_result};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HardwareBufferUsage(pub ffi::AHardwareBuffer_UsageFlags);
impl HardwareBufferUsage {
pub const CPU_READ_NEVER: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_NEVER);
pub const CPU_READ_RARELY: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_RARELY);
pub const CPU_READ_OFTEN: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
pub const CPU_READ_MASK: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_MASK);
pub const CPU_WRITE_NEVER: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER);
pub const CPU_WRITE_RARELY: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY);
pub const CPU_WRITE_OFTEN: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN);
pub const CPU_WRITE_MASK: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK);
pub const GPU_SAMPLED_IMAGE: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE);
pub const GPU_FRAMEBUFFER: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER);
pub const COMPOSER_OVERLAY: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_COMPOSER_OVERLAY);
pub const PROTECTED_CONTENT: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
pub const VIDEO_ENCODE: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VIDEO_ENCODE);
pub const SENSOR_DIRECT_DATA: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA);
pub const GPU_DATA_BUFFER: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER);
pub const GPU_CUBE_MAP: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP);
pub const GPU_MIPMAP_COMPLETE: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_GPU_MIPMAP_COMPLETE);
pub const VENDOR_0: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_0);
pub const VENDOR_1: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_1);
pub const VENDOR_2: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_2);
pub const VENDOR_3: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_3);
pub const VENDOR_4: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_4);
pub const VENDOR_5: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_5);
pub const VENDOR_6: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_6);
pub const VENDOR_7: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_7);
pub const VENDOR_8: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_8);
pub const VENDOR_9: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_9);
pub const VENDOR_10: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_10);
pub const VENDOR_11: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_11);
pub const VENDOR_12: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_12);
pub const VENDOR_13: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_13);
pub const VENDOR_14: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_14);
pub const VENDOR_15: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_15);
pub const VENDOR_16: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_16);
pub const VENDOR_17: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_17);
pub const VENDOR_18: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_18);
pub const VENDOR_19: Self =
Self(ffi::AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_VENDOR_19);
}
pub type Rect = ffi::ARect;
fn construct<T>(with_ptr: impl FnOnce(*mut T) -> i32) -> Result<T> {
let mut result = MaybeUninit::uninit();
let status = with_ptr(result.as_mut_ptr());
status_to_io_result(status).map(|()| unsafe { result.assume_init() })
}
/// A native [`AHardwareBuffer *`]
///
/// [`HardwareBuffer`] objects represent chunks of memory that can be accessed by various hardware
/// components in the system.
///
/// It can be easily converted to the Java counterpart [`android.hardware.HardwareBuffer`] and
/// passed between processes using Binder. All operations involving [`HardwareBuffer`] and
/// [`android.hardware.HardwareBuffer`] are zero-copy, i.e., passing [`HardwareBuffer`] to another
/// process creates a shared view of the same region of memory.
///
/// [`HardwareBuffer`] can be bound to EGL/OpenGL and Vulkan primitives. For EGL, use the extension
/// function [`eglGetNativeClientBufferANDROID`] to obtain an `EGLClientBuffer` and pass it
/// directly to [`eglCreateImageKHR`]. Refer to the EGL extensions
/// [`EGL_ANDROID_get_native_client_buffer`] and [`EGL_ANDROID_image_native_buffer`] for more
/// information. In Vulkan, the contents of the [`HardwareBuffer`] can be accessed as [external
/// memory]. See the [`VK_ANDROID_external_memory_android_hardware_buffer`] extension for details.
///
/// [`AHardwareBuffer *`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer
/// [`android.hardware.HardwareBuffer`]: https://developer.android.com/reference/android/hardware/HardwareBuffer
/// [`eglGetNativeClientBufferANDROID`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_get_native_client_buffer.txt
/// [`eglCreateImageKHR`]: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_image_base.txt
/// [`EGL_ANDROID_get_native_client_buffer`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_get_native_client_buffer.txt
/// [`EGL_ANDROID_image_native_buffer`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_image_native_buffer.txt
/// [external memory]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_KHR_external_memory.html
/// [`VK_ANDROID_external_memory_android_hardware_buffer`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_ANDROID_external_memory_android_hardware_buffer.html
#[derive(Debug)]
pub struct HardwareBuffer {
inner: NonNull<ffi::AHardwareBuffer>,
}
impl HardwareBuffer {
/// Create an _unowned_ [`HardwareBuffer`] from a native pointer
///
/// To wrap a strong reference (that is `release`d on [`Drop`]), call
/// [`HardwareBufferRef::from_ptr()`] instead.
///
/// # Safety
/// By calling this function, you assert that it is a valid pointer to an NDK
/// [`ffi::AHardwareBuffer`] that is kept alive externally, or retrieve a strong reference
/// using [`HardwareBuffer::acquire()`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::AHardwareBuffer>) -> Self {
Self { inner: ptr }
}
/// Returns the underlying [`ffi::AHardwareBuffer`] pointer
///
/// See the top-level [`HardwareBuffer`] struct documentation for (graphics) APIs that accept
/// this pointer.
pub fn as_ptr(&self) -> *mut ffi::AHardwareBuffer {
self.inner.as_ptr()
}
/// Allocates a buffer that matches the passed [`HardwareBufferDesc`].
///
/// If allocation succeeds, the buffer can be used according to the usage flags specified in
/// its description. If a buffer is used in ways not compatible with its usage flags, the
/// results are undefined and may include program termination.
pub fn allocate(desc: HardwareBufferDesc) -> Result<HardwareBufferRef> {
unsafe {
let ptr = construct(|res| ffi::AHardwareBuffer_allocate(&desc.into_native(), res))?;
Ok(HardwareBufferRef::from_ptr(NonNull::new_unchecked(ptr)))
}
}
/// Create a [`HardwareBuffer`] from JNI pointers
///
/// # Safety
/// By calling this function, you assert that these are valid pointers to JNI objects.
///
/// This method does not acquire any additional reference to the AHardwareBuffer that is
/// returned. To keep the [`HardwareBuffer`] alive after the [Java `HardwareBuffer`] object
/// is closed, explicitly or by the garbage collector, be sure to retrieve a strong reference
/// using [`HardwareBuffer::acquire()`].
///
/// [Java `HardwareBuffer`]: https://developer.android.com/reference/android/hardware/HardwareBuffer
pub unsafe fn from_jni(env: *mut JNIEnv, hardware_buffer: jobject) -> Self {
let ptr = ffi::AHardwareBuffer_fromHardwareBuffer(env, hardware_buffer);
Self::from_ptr(NonNull::new_unchecked(ptr))
}
/// # Safety
/// By calling this function, you assert that `env` is a valid pointer to a [`JNIEnv`].
pub unsafe fn to_jni(&self, env: *mut JNIEnv) -> jobject {
ffi::AHardwareBuffer_toHardwareBuffer(env, self.as_ptr())
}
/// Return a description of the [`HardwareBuffer`] in the passed [`HardwareBufferDesc`] struct.
pub fn describe(&self) -> HardwareBufferDesc {
let desc = unsafe {
let mut result = MaybeUninit::uninit();
ffi::AHardwareBuffer_describe(self.as_ptr(), result.as_mut_ptr());
result.assume_init()
};
HardwareBufferDesc {
width: desc.width,
height: desc.height,
layers: desc.layers,
format: desc.format.into(),
usage: HardwareBufferUsage(ffi::AHardwareBuffer_UsageFlags(desc.usage)),
stride: desc.stride,
}
}
/// Test whether the given format and usage flag combination is allocatable.
///
/// If this function returns [`true`], it means that a buffer with the given description can
/// be allocated on this implementation, unless resource exhaustion occurs. If this function
/// returns [`false`], it means that the allocation of the given description will never
/// succeed.
///
/// The return value of this function may depend on all fields in the description, except
/// [`HardwareBufferDesc::stride`], which is always ignored. For example, some implementations
/// have implementation-defined limits on texture size and layer count.
#[cfg(feature = "api-level-29")]
pub fn is_supported(desc: HardwareBufferDesc) -> bool {
let res = unsafe { ffi::AHardwareBuffer_isSupported(&desc.into_native()) };
res == 1
}
/// Get the system-wide unique id for this [`HardwareBuffer`].
#[cfg(feature = "api-level-31")]
#[doc(alias = "AHardwareBuffer_getId")]
pub fn id(&self) -> Result<u64> {
construct(|res| unsafe { ffi::AHardwareBuffer_getId(self.as_ptr(), res) })
}
/// Lock the [`HardwareBuffer`] for direct CPU access.
///
/// This function can lock the buffer for either reading or writing. It may block if the
/// hardware needs to finish rendering, if CPU caches need to be synchronized, or possibly for
/// other implementation-specific reasons.
///
/// The [`HardwareBuffer`] must have one layer, otherwise the call will fail.
///
/// If `fence` is not [`None`], it specifies a fence file descriptor on which to wait before
/// locking the buffer. If it's [`None`], the caller is responsible for ensuring that writes
/// to the buffer have completed before calling this function. Using this parameter is more
/// efficient than waiting on the fence and then calling this function.
///
/// The `usage` parameter may only specify `HardwareBufferUsage::CPU_*`. If set, then the
/// address of the buffer in virtual memory is returned. The flags must also be compatible with
/// usage flags specified at buffer creation: if a read flag is passed, the buffer must have
/// been created with [`HardwareBufferUsage::CPU_READ_RARELY`] or
/// [`HardwareBufferUsage::CPU_READ_OFTEN`]. If a write flag is passed, it must have been
/// created with [`HardwareBufferUsage::CPU_WRITE_RARELY`] or
/// [`HardwareBufferUsage::CPU_WRITE_OFTEN`].
///
/// If `rect` is not [`None`], the caller promises to modify only data in the area specified by
/// `rect`. If rect is [`None`], the caller may modify the contents of the entire buffer. The
/// content of the buffer outside of the specified rect is NOT modified by this call.
///
/// It is legal for several different threads to lock a buffer for read access; none of the
/// threads are blocked.
///
/// Locking a buffer simultaneously for write or read/write is undefined, but will neither
/// terminate the process nor block the caller. This function may return an error or leave the
/// buffer's content in an indeterminate state.
///
/// If the buffer has [`HardwareBufferFormat::BLOB`], it is legal lock it for reading and
/// writing in multiple threads and/or processes simultaneously, and the contents of the buffer
/// behave like shared memory.
pub fn lock(
&self,
usage: HardwareBufferUsage,
fence: Option<OwnedFd>,
rect: Option<Rect>,
) -> Result<*mut c_void> {
let fence = fence.map_or(-1, IntoRawFd::into_raw_fd);
let rect = match rect {
Some(rect) => &rect,
None => std::ptr::null(),
};
construct(|res| unsafe {
ffi::AHardwareBuffer_lock(self.as_ptr(), usage.0 .0, fence, rect, res)
})
}
/// Lock a [`HardwareBuffer`] for direct CPU access.
///
/// This function is the same as the above [`lock()`][Self::lock()] function, but passes back
/// additional information about the bytes per pixel and the bytes per stride of the locked
/// buffer. If the bytes per pixel or bytes per stride are unknown or variable, or if the
/// underlying mapper implementation does not support returning additional information, then
/// this call will fail with [`std::io::Error::kind()`] = [`std::io::ErrorKind::Unsupported`].
#[cfg(feature = "api-level-29")]
pub fn lock_and_get_info(
&self,
usage: HardwareBufferUsage,
fence: Option<OwnedFd>,
rect: Option<Rect>,
) -> Result<LockedPlaneInfo> {
let fence = fence.map_or(-1, IntoRawFd::into_raw_fd);
let rect = match rect {
Some(rect) => &rect,
None => std::ptr::null(),
};
let mut virtual_address = MaybeUninit::uninit();
let mut bytes_per_pixel = MaybeUninit::uninit();
let mut bytes_per_stride = MaybeUninit::uninit();
let status = unsafe {
ffi::AHardwareBuffer_lockAndGetInfo(
self.as_ptr(),
usage.0 .0,
fence,
rect,
virtual_address.as_mut_ptr(),
bytes_per_pixel.as_mut_ptr(),
bytes_per_stride.as_mut_ptr(),
)
};
status_to_io_result(status).map(|()| unsafe {
LockedPlaneInfo {
virtual_address: virtual_address.assume_init(),
bytes_per_pixel: bytes_per_pixel.assume_init() as u32,
bytes_per_stride: bytes_per_stride.assume_init() as u32,
}
})
}
/// Lock a potentially multi-planar [`HardwareBuffer`] for direct CPU access.
///
/// This function is similar to [`lock()`][Self::lock()], but can lock multi-planar formats.
/// Note, that multi-planar should not be confused with multi-layer images, which this locking
/// function does not support.
///
/// YUV formats are always represented by three separate planes of data, one for each color
/// plane. The order of planes in the array is guaranteed such that plane #0 is always `Y`,
/// plane #1 is always `U` (`Cb`), and plane #2 is always `V` (`Cr`). All other formats are
/// represented by a single plane.
///
/// Additional information always accompanies the buffers, describing the row stride and the
/// pixel stride for each plane.
///
/// In case the buffer cannot be locked, this will return zero planes.
///
/// See the [`lock()`][Self::lock()] documentation for all other locking semantics.
#[cfg(feature = "api-level-29")]
pub fn lock_planes(
&self,
usage: HardwareBufferUsage,
fence: Option<OwnedFd>,
rect: Option<Rect>,
) -> Result<HardwareBufferPlanes> {
let fence = fence.map_or(-1, IntoRawFd::into_raw_fd);
let rect = match rect {
Some(rect) => &rect,
None => std::ptr::null(),
};
let planes = construct(|res| unsafe {
ffi::AHardwareBuffer_lockPlanes(self.as_ptr(), usage.0 .0, fence, rect, res)
})?;
Ok(HardwareBufferPlanes {
inner: planes,
index: 0,
})
}
/// Unlock the [`HardwareBuffer`] from direct CPU access.
///
/// Must be called after all changes to the buffer are completed by the caller. The function
/// will block until all work is completed. See [`unlock_async()`][Self::unlock_async()] for
/// a non-blocking variant that returns a file descriptor to be signaled on unlocking instead.
pub fn unlock(&self) -> Result<()> {
let status = unsafe { ffi::AHardwareBuffer_unlock(self.as_ptr(), std::ptr::null_mut()) };
status_to_io_result(status)
}
/// Unlock the [`HardwareBuffer`] from direct CPU access.
///
/// Returns a fence file descriptor that will become signaled when unlocking is completed, or
/// [`None`] if unlocking is already finished. The caller is responsible for closing the file
/// descriptor once it's no longer needed. See [`unlock()`][Self::unlock()] for a variant that
/// blocks instead.
pub fn unlock_async(&self) -> Result<Option<OwnedFd>> {
let fence = construct(|res| unsafe { ffi::AHardwareBuffer_unlock(self.as_ptr(), res) })?;
Ok(match fence {
-1 => None,
fence => Some(unsafe { OwnedFd::from_raw_fd(fence) }),
})
}
/// Receive a [`HardwareBuffer`] from an `AF_UNIX` socket.
///
/// `AF_UNIX` sockets are wrapped by [`std::os::unix::net::UnixListener`] and
/// [`std::os::unix::net::UnixStream`] in Rust and have a corresponding
/// [`std::os::unix::io::AsFd::as_fd()`] implementation.
pub fn recv_handle_from_unix_socket(socket_fd: BorrowedFd<'_>) -> Result<Self> {
unsafe {
let ptr = construct(|res| {
ffi::AHardwareBuffer_recvHandleFromUnixSocket(socket_fd.as_raw_fd(), res)
})?;
Ok(Self::from_ptr(NonNull::new_unchecked(ptr)))
}
}
/// Send the [`HardwareBuffer`] to an `AF_UNIX` socket.
///
/// `AF_UNIX` sockets are wrapped by [`std::os::unix::net::UnixListener`] and
/// [`std::os::unix::net::UnixStream`] in Rust and have a corresponding
/// [`std::os::unix::io::AsFd::as_fd()`] implementation.
pub fn send_handle_to_unix_socket(&self, socket_fd: BorrowedFd<'_>) -> Result<()> {
let status = unsafe {
ffi::AHardwareBuffer_sendHandleToUnixSocket(self.as_ptr(), socket_fd.as_raw_fd())
};
status_to_io_result(status)
}
/// Acquire a reference on the given [`HardwareBuffer`] object.
///
/// This prevents the object from being deleted until the last strong reference, represented
/// by [`HardwareBufferRef`], is [`drop()`]ped.
pub fn acquire(&self) -> HardwareBufferRef {
unsafe {
ffi::AHardwareBuffer_acquire(self.as_ptr());
HardwareBufferRef::from_ptr(self.inner)
}
}
}
/// A [`HardwareBuffer`] with an owned reference, that is released when dropped.
/// It behaves much like a strong [`std::rc::Rc`] reference.
#[derive(Debug)]
pub struct HardwareBufferRef {
inner: HardwareBuffer,
}
impl HardwareBufferRef {
/// Create an _owned_ [`HardwareBuffer`] from a native pointer
///
/// To wrap a weak reference (that is **not** `release`d on [`Drop`]), call
/// [`HardwareBuffer::from_ptr()`] instead.
///
/// # Safety
/// By calling this function, you assert that it is a valid pointer to an NDK
/// [`ffi::AHardwareBuffer`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::AHardwareBuffer>) -> Self {
Self {
inner: HardwareBuffer { inner: ptr },
}
}
}
impl Deref for HardwareBufferRef {
type Target = HardwareBuffer;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl Drop for HardwareBufferRef {
fn drop(&mut self) {
unsafe { ffi::AHardwareBuffer_release(self.inner.as_ptr()) }
}
}
impl Clone for HardwareBufferRef {
fn clone(&self) -> Self {
self.acquire()
}
}
/// Buffer description.
///
/// Used for allocating new buffers and querying parameters of existing ones.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HardwareBufferDesc {
pub width: u32,
pub height: u32,
pub layers: u32,
pub format: HardwareBufferFormat,
pub usage: HardwareBufferUsage,
pub stride: u32,
}
impl HardwareBufferDesc {
fn into_native(self) -> ffi::AHardwareBuffer_Desc {
ffi::AHardwareBuffer_Desc {
width: self.width,
height: self.height,
layers: self.layers,
format: self.format.into(),
usage: self.usage.0 .0,
stride: self.stride,
rfu0: 0,
rfu1: 0,
}
}
}
/// A native [`AHardwareBuffer_Plane`]
///
/// Contains the same fields as [`ffi::AHardwareBuffer_Plane`].
///
/// [`AHardwareBuffer_Plane`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer_plane
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct LockedPlaneInfo {
pub virtual_address: *mut c_void,
pub bytes_per_pixel: u32,
pub bytes_per_stride: u32,
}
/// Iterator over [`ffi::AHardwareBuffer_Planes`], containing a list of [`LockedPlaneInfo`].
#[derive(Debug)]
pub struct HardwareBufferPlanes {
inner: ffi::AHardwareBuffer_Planes,
index: u32,
}
impl Iterator for HardwareBufferPlanes {
type Item = LockedPlaneInfo;
fn next(&mut self) -> Option<LockedPlaneInfo> {
if self.index == self.inner.planeCount {
None
} else {
let plane = self.inner.planes[self.index as usize];
self.index += 1;
Some(LockedPlaneInfo {
virtual_address: plane.data,
bytes_per_pixel: plane.pixelStride,
bytes_per_stride: plane.rowStride,
})
}
}
}

View File

@@ -0,0 +1,99 @@
//! Bindings for [`AHardwareBuffer_Format`]
//!
//! [`AHardwareBuffer_Format`]: https://developer.android.com/ndk/reference/group/a-hardware-buffer#ahardwarebuffer_format
use num_enum::{FromPrimitive, IntoPrimitive};
/// Buffer pixel formats.
#[repr(u32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
#[allow(non_camel_case_types)]
pub enum HardwareBufferFormat {
/// Matches deprecated [`ffi::ANativeWindow_LegacyFormat::WINDOW_FORMAT_RGBA_8888`].0.
#[doc(alias = "AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM")]
R8G8B8A8_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM.0,
/// Matches deprecated [`ffi::ANativeWindow_LegacyFormat::WINDOW_FORMAT_RGBX_8888`].0.
#[doc(alias = "AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM")]
R8G8B8X8_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM")]
R8G8B8_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM.0,
/// Matches deprecated [`ffi::ANativeWindow_LegacyFormat::WINDOW_FORMAT_RGB_565`].0.
#[doc(alias = "AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM")]
R5G6B5_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT")]
R16G16B16A16_FLOAT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM")]
R10G10B10A2_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_BLOB")]
BLOB = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_BLOB.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_D16_UNORM")]
D16_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D16_UNORM.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_D24_UNORM")]
D24_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D24_UNORM.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT")]
D24_UNORM_S8_UINT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_D32_FLOAT")]
D32_FLOAT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D32_FLOAT.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT")]
D32_FLOAT_S8_UINT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_S8_UINT")]
S8_UINT = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_S8_UINT.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420")]
Y8Cb8Cr8_420 = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420.0,
#[cfg(feature = "api-level-26")]
#[doc(alias = "AHARDWAREBUFFER_FORMAT_YCbCr_P010")]
YCbCr_P010 = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_YCbCr_P010.0,
#[cfg(feature = "api-level-26")]
R8_UNORM = ffi::AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8_UNORM.0,
#[num_enum(catch_all)]
Unknown(u32),
}
impl HardwareBufferFormat {
/// Returns [`None`] when there is no immediate byte size available for this format, for
/// example on planar buffer formats.
pub fn bytes_per_pixel(self) -> Option<usize> {
Some(match self {
Self::R8G8B8A8_UNORM | Self::R8G8B8X8_UNORM => 4,
#[cfg(feature = "api-level-26")]
Self::R8G8B8_UNORM => 3,
Self::R5G6B5_UNORM => 2,
#[cfg(feature = "api-level-26")]
Self::R16G16B16A16_FLOAT => 8,
#[cfg(feature = "api-level-26")]
Self::R10G10B10A2_UNORM => 4,
#[cfg(feature = "api-level-26")]
Self::BLOB => 1,
#[cfg(feature = "api-level-26")]
Self::D16_UNORM => 2,
#[cfg(feature = "api-level-26")]
Self::D24_UNORM => 3,
#[cfg(feature = "api-level-26")]
Self::D24_UNORM_S8_UINT => 4,
#[cfg(feature = "api-level-26")]
Self::D32_FLOAT => 4,
#[cfg(feature = "api-level-26")]
Self::D32_FLOAT_S8_UINT => 5,
#[cfg(feature = "api-level-26")]
Self::S8_UINT => 1,
#[cfg(feature = "api-level-26")]
Self::Y8Cb8Cr8_420 => 3,
#[cfg(feature = "api-level-26")]
Self::YCbCr_P010 => return None,
#[cfg(feature = "api-level-26")]
Self::R8_UNORM => 1,
Self::Unknown(_) => return None,
})
}
}

119
vendor/ndk-0.8.0/src/input_queue.rs vendored Normal file
View File

@@ -0,0 +1,119 @@
//! Bindings for [`AInputQueue`]
//!
//! [`AInputQueue`]: https://developer.android.com/ndk/reference/group/input#ainputqueue
use std::io::Result;
use std::os::raw::c_int;
use std::ptr::{self, NonNull};
use crate::event::InputEvent;
#[cfg(doc)]
use crate::event::KeyEvent;
use crate::looper::ForeignLooper;
use crate::utils::status_to_io_result;
/// A native [`AInputQueue *`]
///
/// An input queue is the facility through which you retrieve input events.
///
/// [`AInputQueue *`]: https://developer.android.com/ndk/reference/group/input#ainputqueue
#[derive(Debug)]
pub struct InputQueue {
ptr: NonNull<ffi::AInputQueue>,
}
// It gets shared between threads in `ndk-glue`
unsafe impl Send for InputQueue {}
unsafe impl Sync for InputQueue {}
impl InputQueue {
/// Construct an [`InputQueue`] from the native pointer.
///
/// # Safety
/// By calling this function, you assert that the pointer is a valid pointer to an NDK [`ffi::AInputQueue`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::AInputQueue>) -> Self {
Self { ptr }
}
pub fn ptr(&self) -> NonNull<ffi::AInputQueue> {
self.ptr
}
/// Returns the next available [`InputEvent`] from the queue.
///
/// Returns [`None`] if no event is available.
#[doc(alias = "AInputQueue_getEvent")]
pub fn event(&self) -> Result<Option<InputEvent>> {
let mut out_event = ptr::null_mut();
let status = unsafe { ffi::AInputQueue_getEvent(self.ptr.as_ptr(), &mut out_event) };
match status_to_io_result(status) {
Ok(()) => {
debug_assert!(!out_event.is_null());
Ok(Some(unsafe {
InputEvent::from_ptr(NonNull::new_unchecked(out_event))
}))
}
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(None),
Err(e) => Err(e),
}
}
/// Returns [`true`] if there are one or more events available in the input queue.
#[doc(alias = "AInputQueue_hasEvents")]
pub fn has_events(&self) -> bool {
match unsafe { ffi::AInputQueue_hasEvents(self.ptr.as_ptr()) } {
0 => false,
1 => true,
r => unreachable!("AInputQueue_hasEvents returned non-boolean {}", r),
}
}
/// Sends the key for standard pre-dispatching that is, possibly deliver it to the current IME
/// to be consumed before the app.
///
/// Returns [`Some`] if it was not pre-dispatched, meaning you can process it right now. If
/// [`None`] is returned, you must abandon the current event processing and allow the event to
/// appear again in the event queue (if it does not get consumed during pre-dispatching).
///
/// Also returns [`None`] if `event` is not a [`KeyEvent`].
#[doc(alias = "AInputQueue_preDispatchEvent")]
pub fn pre_dispatch(&self, event: InputEvent) -> Option<InputEvent> {
match unsafe { ffi::AInputQueue_preDispatchEvent(self.ptr.as_ptr(), event.ptr().as_ptr()) }
{
0 => Some(event),
_ => None,
}
}
/// Report that dispatching has finished with the given [`InputEvent`].
///
/// This must be called after receiving an event with [`InputQueue::event()`].
#[doc(alias = "AInputQueue_finishEvent")]
pub fn finish_event(&self, event: InputEvent, handled: bool) {
unsafe {
ffi::AInputQueue_finishEvent(self.ptr.as_ptr(), event.ptr().as_ptr(), handled as c_int)
}
}
/// Add this input queue to a [`ForeignLooper`] for processing.
///
/// See [`ForeignLooper::add_fd()`] for information on the `ident`, `callback`, and `data` params.
#[doc(alias = "AInputQueue_attachLooper")]
pub fn attach_looper(&self, looper: &ForeignLooper, id: i32) {
unsafe {
ffi::AInputQueue_attachLooper(
self.ptr.as_ptr(),
looper.ptr().as_ptr(),
id,
None,
std::ptr::null_mut(),
)
}
}
/// Remove this input queue from the [`ForeignLooper`] it is currently attached to.
#[doc(alias = "AInputQueue_detachLooper")]
pub fn detach_looper(&self) {
unsafe { ffi::AInputQueue_detachLooper(self.ptr.as_ptr()) }
}
}

33
vendor/ndk-0.8.0/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,33 @@
//! # Android NDK
//!
//! Bindings to the [Android NDK].
//!
//! [Android NDK]: https://developer.android.com/ndk/reference
#![warn(
missing_debug_implementations,
rust_2018_idioms,
trivial_casts,
unused_qualifications
)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
pub mod asset;
pub mod audio;
pub mod bitmap;
pub mod configuration;
pub mod data_space;
pub mod event;
pub mod font;
pub mod hardware_buffer;
pub mod hardware_buffer_format;
pub mod input_queue;
pub mod looper;
pub mod media;
pub mod media_error;
pub mod native_activity;
pub mod native_window;
pub mod shared_memory;
pub mod surface_texture;
pub mod sync;
pub mod trace;
mod utils;

395
vendor/ndk-0.8.0/src/looper.rs vendored Normal file
View File

@@ -0,0 +1,395 @@
//! Bindings for [`ALooper`]
//!
//! In Android, [`ALooper`]s are inherently thread-local. Due to this, there are two different
//! [`ALooper`] interfaces exposed in this module:
//!
//! * [`ThreadLooper`], which has methods for the operations performable with a looper in one's own
//! thread; and
//! * [`ForeignLooper`], which has methods for the operations performable with any thread's looper.
//!
//! [`ALooper`]: https://developer.android.com/ndk/reference/group/looper#alooper
use bitflags::bitflags;
use std::mem::ManuallyDrop;
use std::os::{
fd::{AsRawFd, BorrowedFd, RawFd},
raw::c_void,
};
use std::ptr;
use std::time::Duration;
use thiserror::Error;
use crate::utils::abort_on_panic;
/// A thread-local native [`ALooper *`]. This promises that there is a looper associated with the
/// current thread.
///
/// [`ALooper *`]: https://developer.android.com/ndk/reference/group/looper#alooper
#[derive(Debug)]
pub struct ThreadLooper {
_marker: std::marker::PhantomData<*mut ()>, // Not send or sync
foreign: ForeignLooper,
}
bitflags! {
/// Flags for file descriptor events that a looper can monitor.
///
/// These flag bits can be combined to monitor multiple events at once.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct FdEvent: u32 {
/// The file descriptor is available for read operations.
#[doc(alias = "ALOOPER_EVENT_INPUT")]
const INPUT = ffi::ALOOPER_EVENT_INPUT;
/// The file descriptor is available for write operations.
#[doc(alias = "ALOOPER_EVENT_OUTPUT")]
const OUTPUT = ffi::ALOOPER_EVENT_OUTPUT;
/// The file descriptor has encountered an error condition.
///
/// The looper always sends notifications about errors; it is not necessary to specify this
/// event flag in the requested event set.
#[doc(alias = "ALOOPER_EVENT_ERROR")]
const ERROR = ffi::ALOOPER_EVENT_ERROR;
/// The file descriptor was hung up.
///
/// For example, indicates that the remote end of a pipe or socket was closed.
///
/// The looper always sends notifications about hangups; it is not necessary to specify this
/// event flag in the requested event set.
#[doc(alias = "ALOOPER_EVENT_HANGUP")]
const HANGUP = ffi::ALOOPER_EVENT_HANGUP;
/// The file descriptor is invalid.
///
/// For example, the file descriptor was closed prematurely.
///
/// The looper always sends notifications about invalid file descriptors; it is not
/// necessary to specify this event flag in the requested event set.
#[doc(alias = "ALOOPER_EVENT_INVALID")]
const INVALID = ffi::ALOOPER_EVENT_INVALID;
}
}
/// The poll result from a [`ThreadLooper`].
#[derive(Debug)]
pub enum Poll<'fd> {
/// This looper was woken using [`ForeignLooper::wake()`]
Wake,
/// For [`ThreadLooper::poll_once*()`][ThreadLooper::poll_once()], an event was received and processed using a callback.
Callback,
/// For [`ThreadLooper::poll_*_timeout()`][ThreadLooper::poll_once_timeout()], the requested timeout was reached before any events.
Timeout,
/// An event was received
Event {
ident: i32,
/// # Safety
/// The caller should guarantee that this file descriptor remains open after it was added
/// via [`ForeignLooper::add_fd()`] or [`ForeignLooper::add_fd_with_callback()`].
fd: BorrowedFd<'fd>,
events: FdEvent,
data: *mut c_void,
},
}
#[derive(Debug, Copy, Clone, Error)]
#[error("Android Looper error")]
pub struct LooperError;
impl ThreadLooper {
/// Prepares a looper for the current thread and returns it
pub fn prepare() -> Self {
unsafe {
let ptr = ffi::ALooper_prepare(ffi::ALOOPER_PREPARE_ALLOW_NON_CALLBACKS as _);
let foreign = ForeignLooper::from_ptr(ptr::NonNull::new(ptr).expect("looper non null"));
Self {
_marker: std::marker::PhantomData,
foreign,
}
}
}
/// Returns the looper associated with the current thread, if any.
pub fn for_thread() -> Option<Self> {
Some(Self {
_marker: std::marker::PhantomData,
foreign: ForeignLooper::for_thread()?,
})
}
/// Polls the looper, blocking on processing an event, but with a timeout in milliseconds.
/// Give a timeout of `0` to make this non-blocking.
fn poll_once_ms(&self, ms: i32) -> Result<Poll<'_>, LooperError> {
let mut fd = -1;
let mut events = -1;
let mut data: *mut c_void = ptr::null_mut();
match unsafe { ffi::ALooper_pollOnce(ms, &mut fd, &mut events, &mut data) } {
ffi::ALOOPER_POLL_WAKE => Ok(Poll::Wake),
ffi::ALOOPER_POLL_CALLBACK => Ok(Poll::Callback),
ffi::ALOOPER_POLL_TIMEOUT => Ok(Poll::Timeout),
ffi::ALOOPER_POLL_ERROR => Err(LooperError),
ident if ident >= 0 => Ok(Poll::Event {
ident,
// SAFETY: Even though this FD at least shouldn't outlive self, a user could have
// closed it after calling add_fd or add_fd_with_callback.
fd: unsafe { BorrowedFd::borrow_raw(fd) },
events: FdEvent::from_bits(events as u32)
.expect("poll event contains unknown bits"),
data,
}),
_ => unreachable!(),
}
}
/// Polls the looper, blocking on processing an event.
#[inline]
pub fn poll_once(&self) -> Result<Poll<'_>, LooperError> {
self.poll_once_ms(-1)
}
/// Polls the looper, blocking on processing an event, but with a timeout. Give a timeout of
/// [`Duration::ZERO`] to make this non-blocking.
///
/// It panics if the timeout is larger than expressible as an [`i32`] of milliseconds (roughly 25
/// days).
#[inline]
pub fn poll_once_timeout(&self, timeout: Duration) -> Result<Poll<'_>, LooperError> {
self.poll_once_ms(
timeout
.as_millis()
.try_into()
.expect("Supplied timeout is too large"),
)
}
/// Repeatedly polls the looper, blocking on processing an event, but with a timeout in
/// milliseconds. Give a timeout of `0` to make this non-blocking.
///
/// This function will never return [`Poll::Callback`].
fn poll_all_ms(&self, ms: i32) -> Result<Poll<'_>, LooperError> {
let mut fd = -1;
let mut events = -1;
let mut data: *mut c_void = ptr::null_mut();
match unsafe { ffi::ALooper_pollAll(ms, &mut fd, &mut events, &mut data) } {
ffi::ALOOPER_POLL_WAKE => Ok(Poll::Wake),
ffi::ALOOPER_POLL_TIMEOUT => Ok(Poll::Timeout),
ffi::ALOOPER_POLL_ERROR => Err(LooperError),
ident if ident >= 0 => Ok(Poll::Event {
ident,
// SAFETY: Even though this FD at least shouldn't outlive self, a user could have
// closed it after calling add_fd or add_fd_with_callback.
fd: unsafe { BorrowedFd::borrow_raw(fd) },
events: FdEvent::from_bits(events as u32)
.expect("poll event contains unknown bits"),
data,
}),
_ => unreachable!(),
}
}
/// Repeatedly polls the looper, blocking on processing an event.
///
/// This function will never return [`Poll::Callback`].
#[inline]
pub fn poll_all(&self) -> Result<Poll<'_>, LooperError> {
self.poll_all_ms(-1)
}
/// Repeatedly polls the looper, blocking on processing an event, but with a timeout. Give a
/// timeout of [`Duration::ZERO`] to make this non-blocking.
///
/// This function will never return [`Poll::Callback`].
///
/// It panics if the timeout is larger than expressible as an [`i32`] of milliseconds (roughly 25
/// days).
#[inline]
pub fn poll_all_timeout(&self, timeout: Duration) -> Result<Poll<'_>, LooperError> {
self.poll_all_ms(
timeout
.as_millis()
.try_into()
.expect("Supplied timeout is too large"),
)
}
/// Returns a reference to the [`ForeignLooper`] that is associated with the current thread.
pub fn as_foreign(&self) -> &ForeignLooper {
&self.foreign
}
pub fn into_foreign(self) -> ForeignLooper {
self.foreign
}
}
/// A native [`ALooper *`], not necessarily allocated with the current thread.
///
/// [`ALooper *`]: https://developer.android.com/ndk/reference/group/looper#alooper
#[derive(Debug)]
pub struct ForeignLooper {
ptr: ptr::NonNull<ffi::ALooper>,
}
unsafe impl Send for ForeignLooper {}
unsafe impl Sync for ForeignLooper {}
impl Drop for ForeignLooper {
fn drop(&mut self) {
unsafe { ffi::ALooper_release(self.ptr.as_ptr()) }
}
}
impl Clone for ForeignLooper {
fn clone(&self) -> Self {
unsafe {
ffi::ALooper_acquire(self.ptr.as_ptr());
Self { ptr: self.ptr }
}
}
}
impl ForeignLooper {
/// Returns the looper associated with the current thread, if any.
#[inline]
pub fn for_thread() -> Option<Self> {
ptr::NonNull::new(unsafe { ffi::ALooper_forThread() })
.map(|ptr| unsafe { Self::from_ptr(ptr) })
}
/// Construct a [`ForeignLooper`] object from the given pointer.
///
/// # Safety
/// By calling this function, you guarantee that the pointer is a valid, non-null pointer to an
/// NDK [`ffi::ALooper`].
#[inline]
pub unsafe fn from_ptr(ptr: ptr::NonNull<ffi::ALooper>) -> Self {
ffi::ALooper_acquire(ptr.as_ptr());
Self { ptr }
}
/// Returns a pointer to the NDK `ALooper` object.
#[inline]
pub fn ptr(&self) -> ptr::NonNull<ffi::ALooper> {
self.ptr
}
/// Wakes the looper. An event of [`Poll::Wake`] will be sent.
pub fn wake(&self) {
unsafe { ffi::ALooper_wake(self.ptr.as_ptr()) }
}
/// Adds a file descriptor to be polled, without a callback.
///
/// See also [the NDK
/// docs](https://developer.android.com/ndk/reference/group/looper.html#alooper_addfd).
///
/// # Safety
/// The caller should guarantee that this file descriptor stays open until it is removed via
/// [`remove_fd()`][Self::remove_fd()], and for however long the caller wishes to use this file
/// descriptor when it is returned in [`Poll::Event::fd`].
// `ALooper_addFd` won't dereference `data`; it will only pass it on to the event.
// Optionally dereferencing it there already enforces `unsafe` context.
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn add_fd(
&self,
fd: BorrowedFd<'_>,
ident: i32,
events: FdEvent,
data: *mut c_void,
) -> Result<(), LooperError> {
match unsafe {
ffi::ALooper_addFd(
self.ptr.as_ptr(),
fd.as_raw_fd(),
ident,
events.bits() as i32,
None,
data,
)
} {
1 => Ok(()),
-1 => Err(LooperError),
_ => unreachable!(),
}
}
/// Adds a file descriptor to be polled, with a callback.
///
/// The callback takes as an argument the file descriptor, and should return [`true`] to
/// continue receiving callbacks, or [`false`] to have the callback unregistered.
///
/// See also [the NDK
/// docs](https://developer.android.com/ndk/reference/group/looper.html#alooper_addfd).
///
/// Note that this will leak a [`Box`] unless the callback returns [`false`] to unregister
/// itself.
///
/// # Safety
/// The caller should guarantee that this file descriptor stays open until it is removed via
/// [`remove_fd()`][Self::remove_fd()] or by returning [`false`] from the callback, and for
/// however long the caller wishes to use this file descriptor inside and after the callback.
#[doc(alias = "ALooper_addFd")]
pub fn add_fd_with_callback<F: FnMut(BorrowedFd<'_>, FdEvent) -> bool>(
&self,
fd: BorrowedFd<'_>,
events: FdEvent,
callback: F,
) -> Result<(), LooperError> {
extern "C" fn cb_handler<F: FnMut(BorrowedFd<'_>, FdEvent) -> bool>(
fd: RawFd,
events: i32,
data: *mut c_void,
) -> i32 {
abort_on_panic(|| unsafe {
let mut cb = ManuallyDrop::new(Box::<F>::from_raw(data as *mut _));
let events = FdEvent::from_bits_retain(
events.try_into().expect("Unexpected sign bit in `events`"),
);
let keep_registered = cb(BorrowedFd::borrow_raw(fd), events);
if !keep_registered {
ManuallyDrop::into_inner(cb);
}
keep_registered as i32
})
}
let data = Box::into_raw(Box::new(callback)) as *mut _;
match unsafe {
ffi::ALooper_addFd(
self.ptr.as_ptr(),
fd.as_raw_fd(),
ffi::ALOOPER_POLL_CALLBACK,
events.bits() as i32,
Some(cb_handler::<F>),
data,
)
} {
1 => Ok(()),
-1 => Err(LooperError),
_ => unreachable!(),
}
}
/// Removes a previously added file descriptor from the looper.
///
/// Returns [`true`] if the file descriptor was removed, [`false`] if it was not previously
/// registered.
///
/// # Safety
/// When this method returns, it is safe to close the file descriptor since the looper will no
/// longer have a reference to it. However, it is possible for the callback to already be
/// running or for it to run one last time if the file descriptor was already signalled.
/// Calling code is responsible for ensuring that this case is safely handled. For example, if
/// the callback takes care of removing itself during its own execution either by returning `0`
/// or by calling this method, then it can be guaranteed to not be invoked again at any later
/// time unless registered anew.
///
/// Note that unregistering a file descriptor with callback will leak a [`Box`] created in
/// [`add_fd_with_callback()`][Self::add_fd_with_callback()]. Consider returning [`false`]
/// from the callback instead to drop it.
pub fn remove_fd(&self, fd: BorrowedFd<'_>) -> Result<bool, LooperError> {
match unsafe { ffi::ALooper_removeFd(self.ptr.as_ptr(), fd.as_raw_fd()) } {
1 => Ok(true),
0 => Ok(false),
-1 => Err(LooperError),
_ => unreachable!(),
}
}
}

View File

@@ -0,0 +1,404 @@
//! Bindings for [`AImageReader`] and [`AImage`]
//!
//! [`AImageReader`]: https://developer.android.com/ndk/reference/group/media#aimagereader
//! [`AImage`]: https://developer.android.com/ndk/reference/group/media#aimage
#![cfg(feature = "api-level-24")]
use crate::media_error::{construct, construct_never_null, MediaError, Result};
use crate::native_window::NativeWindow;
use crate::utils::abort_on_panic;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::{
ffi::c_void,
fmt::{self, Debug, Formatter},
mem::MaybeUninit,
ptr::NonNull,
};
#[cfg(feature = "api-level-26")]
use std::os::fd::{FromRawFd, IntoRawFd, OwnedFd};
#[cfg(feature = "api-level-26")]
use crate::hardware_buffer::{HardwareBuffer, HardwareBufferUsage};
#[repr(u32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[allow(non_camel_case_types)]
pub enum ImageFormat {
RGBA_8888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGBA_8888.0,
RGBX_8888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGBX_8888.0,
RGB_888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGB_888.0,
RGB_565 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGB_565.0,
RGBA_FP16 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RGBA_FP16.0,
YUV_420_888 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_YUV_420_888.0,
JPEG = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_JPEG.0,
RAW16 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW16.0,
RAW_PRIVATE = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW_PRIVATE.0,
RAW10 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW10.0,
RAW12 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_RAW12.0,
DEPTH16 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_DEPTH16.0,
DEPTH_POINT_CLOUD = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_DEPTH_POINT_CLOUD.0,
PRIVATE = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_PRIVATE.0,
Y8 = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_Y8.0,
HEIC = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_HEIC.0,
DEPTH_JPEG = ffi::AIMAGE_FORMATS::AIMAGE_FORMAT_DEPTH_JPEG.0,
}
pub type ImageListener = Box<dyn FnMut(&ImageReader)>;
#[cfg(feature = "api-level-26")]
pub type BufferRemovedListener = Box<dyn FnMut(&ImageReader, &HardwareBuffer)>;
/// A native [`AImageReader *`]
///
/// [`AImageReader *`]: https://developer.android.com/ndk/reference/group/media#aimagereader
pub struct ImageReader {
inner: NonNull<ffi::AImageReader>,
image_cb: Option<Box<ImageListener>>,
#[cfg(feature = "api-level-26")]
buffer_removed_cb: Option<Box<BufferRemovedListener>>,
}
impl Debug for ImageReader {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("ImageReader")
.field("inner", &self.inner)
.field(
"image_cb",
match &self.image_cb {
Some(_) => &"Some(_)",
None => &"None",
},
)
.finish()
}
}
impl ImageReader {
fn from_ptr(inner: NonNull<ffi::AImageReader>) -> Self {
Self {
inner,
image_cb: None,
#[cfg(feature = "api-level-26")]
buffer_removed_cb: None,
}
}
fn as_ptr(&self) -> *mut ffi::AImageReader {
self.inner.as_ptr()
}
pub fn new(width: i32, height: i32, format: ImageFormat, max_images: i32) -> Result<Self> {
let inner = construct_never_null(|res| unsafe {
ffi::AImageReader_new(width, height, format as i32, max_images, res)
})?;
Ok(Self::from_ptr(inner))
}
#[cfg(feature = "api-level-26")]
pub fn new_with_usage(
width: i32,
height: i32,
format: ImageFormat,
usage: HardwareBufferUsage,
max_images: i32,
) -> Result<Self> {
let inner = construct_never_null(|res| unsafe {
ffi::AImageReader_newWithUsage(
width,
height,
format as i32,
usage.0 .0,
max_images,
res,
)
})?;
Ok(Self::from_ptr(inner))
}
#[doc(alias = "AImageReader_setImageListener")]
pub fn set_image_listener(&mut self, listener: ImageListener) -> Result<()> {
let mut boxed = Box::new(listener);
let ptr: *mut ImageListener = &mut *boxed;
// keep listener alive until Drop or new listener is assigned
self.image_cb = Some(boxed);
unsafe extern "C" fn on_image_available(
context: *mut c_void,
reader: *mut ffi::AImageReader,
) {
abort_on_panic(|| {
let reader = ImageReader::from_ptr(NonNull::new_unchecked(reader));
let listener: *mut ImageListener = context as *mut _;
(*listener)(&reader);
std::mem::forget(reader);
})
}
let mut listener = ffi::AImageReader_ImageListener {
context: ptr as _,
onImageAvailable: Some(on_image_available),
};
let status = unsafe { ffi::AImageReader_setImageListener(self.as_ptr(), &mut listener) };
MediaError::from_status(status)
}
#[cfg(feature = "api-level-26")]
#[doc(alias = "AImageReader_setBufferRemovedListener")]
pub fn set_buffer_removed_listener(&mut self, listener: BufferRemovedListener) -> Result<()> {
let mut boxed = Box::new(listener);
let ptr: *mut BufferRemovedListener = &mut *boxed;
// keep listener alive until Drop or new listener is assigned
self.buffer_removed_cb = Some(boxed);
unsafe extern "C" fn on_buffer_removed(
context: *mut c_void,
reader: *mut ffi::AImageReader,
buffer: *mut ffi::AHardwareBuffer,
) {
abort_on_panic(|| {
let reader = ImageReader::from_ptr(NonNull::new_unchecked(reader));
let buffer = HardwareBuffer::from_ptr(NonNull::new_unchecked(buffer));
let listener: *mut BufferRemovedListener = context as *mut _;
(*listener)(&reader, &buffer);
std::mem::forget(reader);
})
}
let mut listener = ffi::AImageReader_BufferRemovedListener {
context: ptr as _,
onBufferRemoved: Some(on_buffer_removed),
};
let status =
unsafe { ffi::AImageReader_setBufferRemovedListener(self.as_ptr(), &mut listener) };
MediaError::from_status(status)
}
/// Get a [`NativeWindow`] that can be used to produce [`Image`]s for this [`ImageReader`].
///
/// <https://developer.android.com/ndk/reference/group/media#aimagereader_getwindow>
#[doc(alias = "AImageReader_getWindow")]
pub fn window(&self) -> Result<NativeWindow> {
unsafe {
let ptr = construct_never_null(|res| ffi::AImageReader_getWindow(self.as_ptr(), res))?;
Ok(NativeWindow::clone_from_ptr(ptr))
}
}
#[doc(alias = "AImageReader_getWidth")]
pub fn width(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImageReader_getWidth(self.as_ptr(), res) })
}
#[doc(alias = "AImageReader_getHeight")]
pub fn height(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImageReader_getHeight(self.as_ptr(), res) })
}
#[doc(alias = "AImageReader_getFormat")]
pub fn format(&self) -> Result<ImageFormat> {
let format = construct(|res| unsafe { ffi::AImageReader_getFormat(self.as_ptr(), res) })?;
Ok((format as u32).try_into().unwrap())
}
#[doc(alias = "AImageReader_getMaxImages")]
pub fn max_images(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImageReader_getMaxImages(self.as_ptr(), res) })
}
#[doc(alias = "AImageReader_acquireNextImage")]
pub fn acquire_next_image(&self) -> Result<Option<Image>> {
let res = construct_never_null(|res| unsafe {
ffi::AImageReader_acquireNextImage(self.as_ptr(), res)
});
match res {
Ok(inner) => Ok(Some(Image { inner })),
Err(MediaError::ImgreaderNoBufferAvailable) => Ok(None),
Err(e) => Err(e),
}
}
/// Acquire the next [`Image`] from the image reader's queue asynchronously.
///
/// # Safety
/// If the returned file descriptor is not [`None`], it must be awaited before attempting to
/// access the [`Image`] returned.
///
/// <https://developer.android.com/ndk/reference/group/media#aimagereader_acquirenextimageasync>
#[cfg(feature = "api-level-26")]
#[doc(alias = "AImageReader_acquireNextImageAsync")]
pub unsafe fn acquire_next_image_async(&self) -> Result<(Image, Option<OwnedFd>)> {
let mut fence = MaybeUninit::uninit();
let inner = construct_never_null(|res| {
ffi::AImageReader_acquireNextImageAsync(self.as_ptr(), res, fence.as_mut_ptr())
})?;
let image = Image { inner };
Ok(match fence.assume_init() {
-1 => (image, None),
fence => (image, Some(unsafe { OwnedFd::from_raw_fd(fence) })),
})
}
#[doc(alias = "AImageReader_acquireLatestImage")]
pub fn acquire_latest_image(&self) -> Result<Option<Image>> {
let res = construct_never_null(|res| unsafe {
ffi::AImageReader_acquireLatestImage(self.as_ptr(), res)
});
if let Err(MediaError::ImgreaderNoBufferAvailable) = res {
return Ok(None);
}
Ok(Some(Image { inner: res? }))
}
/// Acquire the latest [`Image`] from the image reader's queue asynchronously, dropping older images.
///
/// # Safety
/// If the returned file descriptor is not [`None`], it must be awaited before attempting to
/// access the [`Image`] returned.
///
/// <https://developer.android.com/ndk/reference/group/media#aimagereader_acquirelatestimageasync>
#[cfg(feature = "api-level-26")]
#[doc(alias = "AImageReader_acquireLatestImageAsync")]
pub fn acquire_latest_image_async(&self) -> Result<(Image, Option<OwnedFd>)> {
let mut fence = MaybeUninit::uninit();
let inner = construct_never_null(|res| unsafe {
ffi::AImageReader_acquireLatestImageAsync(self.as_ptr(), res, fence.as_mut_ptr())
})?;
let image = Image { inner };
Ok(match unsafe { fence.assume_init() } {
-1 => (image, None),
fence => (image, Some(unsafe { OwnedFd::from_raw_fd(fence) })),
})
}
}
impl Drop for ImageReader {
#[doc(alias = "AImageReader_delete")]
fn drop(&mut self) {
unsafe { ffi::AImageReader_delete(self.as_ptr()) };
}
}
/// A native [`AImage *`]
///
/// [`AImage *`]: https://developer.android.com/ndk/reference/group/media#aimage
#[derive(Debug)]
#[doc(alias = "AImage")]
pub struct Image {
inner: NonNull<ffi::AImage>,
}
#[doc(alias = "AImageCropRect")]
pub type CropRect = ffi::AImageCropRect;
impl Image {
fn as_ptr(&self) -> *mut ffi::AImage {
self.inner.as_ptr()
}
#[doc(alias = "AImage_getPlaneData")]
pub fn plane_data(&self, plane_idx: i32) -> Result<&[u8]> {
let mut result_ptr = MaybeUninit::uninit();
let mut result_len = MaybeUninit::uninit();
let status = unsafe {
ffi::AImage_getPlaneData(
self.as_ptr(),
plane_idx,
result_ptr.as_mut_ptr(),
result_len.as_mut_ptr(),
)
};
MediaError::from_status(status).map(|()| unsafe {
std::slice::from_raw_parts(result_ptr.assume_init(), result_len.assume_init() as _)
})
}
#[doc(alias = "AImage_getPlanePixelStride")]
pub fn plane_pixel_stride(&self, plane_idx: i32) -> Result<i32> {
construct(|res| unsafe { ffi::AImage_getPlanePixelStride(self.as_ptr(), plane_idx, res) })
}
#[doc(alias = "AImage_getPlaneRowStride")]
pub fn plane_row_stride(&self, plane_idx: i32) -> Result<i32> {
construct(|res| unsafe { ffi::AImage_getPlaneRowStride(self.as_ptr(), plane_idx, res) })
}
#[doc(alias = "AImage_getCropRect")]
pub fn crop_rect(&self) -> Result<CropRect> {
construct(|res| unsafe { ffi::AImage_getCropRect(self.as_ptr(), res) })
}
#[doc(alias = "AImage_getWidth")]
pub fn width(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImage_getWidth(self.as_ptr(), res) })
}
#[doc(alias = "AImage_getHeight")]
pub fn height(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImage_getHeight(self.as_ptr(), res) })
}
#[doc(alias = "AImage_getFormat")]
pub fn format(&self) -> Result<ImageFormat> {
let format = construct(|res| unsafe { ffi::AImage_getFormat(self.as_ptr(), res) })?;
Ok((format as u32).try_into().unwrap())
}
#[doc(alias = "AImage_getTimestamp")]
pub fn timestamp(&self) -> Result<i64> {
construct(|res| unsafe { ffi::AImage_getTimestamp(self.as_ptr(), res) })
}
#[doc(alias = "AImage_getNumberOfPlanes")]
pub fn number_of_planes(&self) -> Result<i32> {
construct(|res| unsafe { ffi::AImage_getNumberOfPlanes(self.as_ptr(), res) })
}
/// Get the hardware buffer handle of the input image intended for GPU and/or hardware access.
///
/// Note that no reference on the returned [`HardwareBuffer`] handle is acquired automatically.
/// Once the [`Image`] or the parent [`ImageReader`] is deleted, the [`HardwareBuffer`] handle
/// from previous [`Image::hardware_buffer()`] becomes invalid.
///
/// If the caller ever needs to hold on a reference to the [`HardwareBuffer`] handle after the
/// [`Image`] or the parent [`ImageReader`] is deleted, it must call
/// [`HardwareBuffer::acquire()`] to acquire an extra reference, and [`drop()`] it when
/// finished using it in order to properly deallocate the underlying memory managed by
/// [`HardwareBuffer`]. If the caller has acquired an extra reference on a [`HardwareBuffer`]
/// returned from this function, it must also register a listener using
/// [`ImageReader::set_buffer_removed_listener()`] to be notified when the buffer is no longer
/// used by [`ImageReader`].
#[cfg(feature = "api-level-26")]
#[doc(alias = "AImage_getHardwareBuffer")]
pub fn hardware_buffer(&self) -> Result<HardwareBuffer> {
unsafe {
let ptr =
construct_never_null(|res| ffi::AImage_getHardwareBuffer(self.as_ptr(), res))?;
Ok(HardwareBuffer::from_ptr(ptr))
}
}
#[cfg(feature = "api-level-26")]
#[doc(alias = "AImage_deleteAsync")]
pub fn delete_async(self, release_fence_fd: OwnedFd) {
unsafe { ffi::AImage_deleteAsync(self.as_ptr(), release_fence_fd.into_raw_fd()) };
std::mem::forget(self);
}
}
impl Drop for Image {
#[doc(alias = "AImage_delete")]
fn drop(&mut self) {
unsafe { ffi::AImage_delete(self.as_ptr()) };
}
}

View File

@@ -0,0 +1,827 @@
//! Bindings for [`AMediaFormat`] and [`AMediaCodec`]
//!
//! [`AMediaFormat`]: https://developer.android.com/ndk/reference/group/media#amediaformat
//! [`AMediaCodec`]: https://developer.android.com/ndk/reference/group/media#amediacodec
use crate::media_error::{MediaError, Result};
use crate::native_window::NativeWindow;
use crate::utils::abort_on_panic;
use std::{
ffi::{c_char, c_void, CStr, CString},
fmt::Display,
mem::MaybeUninit,
pin::Pin,
ptr::{self, NonNull},
slice,
time::Duration,
};
#[derive(Debug, PartialEq, Eq)]
pub enum MediaCodecDirection {
Decoder,
Encoder,
}
/// A native [`AMediaFormat *`]
///
/// [`AMediaFormat *`]: https://developer.android.com/ndk/reference/group/media#amediaformat
#[derive(Debug)]
pub struct MediaFormat {
inner: NonNull<ffi::AMediaFormat>,
}
impl Display for MediaFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let c_str = unsafe { CStr::from_ptr(ffi::AMediaFormat_toString(self.as_ptr())) };
f.write_str(c_str.to_str().unwrap())
}
}
impl Default for MediaFormat {
fn default() -> Self {
Self::new()
}
}
impl MediaFormat {
/// Assumes ownership of `ptr`
///
/// # Safety
/// `ptr` must be a valid pointer to an Android [`ffi::AMediaFormat`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::AMediaFormat>) -> Self {
Self { inner: ptr }
}
fn as_ptr(&self) -> *mut ffi::AMediaFormat {
self.inner.as_ptr()
}
pub fn new() -> Self {
Self {
inner: NonNull::new(unsafe { ffi::AMediaFormat_new() }).unwrap(),
}
}
pub fn i32(&self, key: &str) -> Option<i32> {
let name = CString::new(key).unwrap();
let mut out = 0;
if unsafe { ffi::AMediaFormat_getInt32(self.as_ptr(), name.as_ptr(), &mut out) } {
Some(out)
} else {
None
}
}
pub fn i64(&self, key: &str) -> Option<i64> {
let name = CString::new(key).unwrap();
let mut out = 0;
if unsafe { ffi::AMediaFormat_getInt64(self.as_ptr(), name.as_ptr(), &mut out) } {
Some(out)
} else {
None
}
}
pub fn f32(&self, key: &str) -> Option<f32> {
let name = CString::new(key).unwrap();
let mut out = 0.0;
if unsafe { ffi::AMediaFormat_getFloat(self.as_ptr(), name.as_ptr(), &mut out) } {
Some(out)
} else {
None
}
}
pub fn usize(&self, key: &str) -> Option<usize> {
let name = CString::new(key).unwrap();
let mut out = 0;
if unsafe { ffi::AMediaFormat_getSize(self.as_ptr(), name.as_ptr(), &mut out) } {
Some(out)
} else {
None
}
}
pub fn buffer(&self, key: &str) -> Option<&[u8]> {
let name = CString::new(key).unwrap();
let mut out_buffer = ptr::null_mut();
let mut out_size = 0;
unsafe {
ffi::AMediaFormat_getBuffer(
self.as_ptr(),
name.as_ptr(),
&mut out_buffer,
&mut out_size,
)
}
.then(|| unsafe { slice::from_raw_parts(out_buffer.cast(), out_size) })
}
pub fn str(&self, key: &str) -> Option<&str> {
let name = CString::new(key).unwrap();
let mut out = ptr::null();
unsafe { ffi::AMediaFormat_getString(self.as_ptr(), name.as_ptr(), &mut out) }
.then(|| unsafe { CStr::from_ptr(out) }.to_str().unwrap())
}
pub fn set_i32(&self, key: &str, value: i32) {
let name = CString::new(key).unwrap();
unsafe { ffi::AMediaFormat_setInt32(self.as_ptr(), name.as_ptr(), value) };
}
pub fn set_i64(&self, key: &str, value: i64) {
let name = CString::new(key).unwrap();
unsafe { ffi::AMediaFormat_setInt64(self.as_ptr(), name.as_ptr(), value) };
}
pub fn set_f32(&self, key: &str, value: f32) {
let name = CString::new(key).unwrap();
unsafe { ffi::AMediaFormat_setFloat(self.as_ptr(), name.as_ptr(), value) };
}
pub fn set_str(&self, key: &str, value: &str) {
let name = CString::new(key).unwrap();
let c_string = CString::new(value).unwrap();
unsafe { ffi::AMediaFormat_setString(self.as_ptr(), name.as_ptr(), c_string.as_ptr()) };
}
pub fn set_buffer(&self, key: &str, value: &[u8]) {
let name = CString::new(key).unwrap();
unsafe {
ffi::AMediaFormat_setBuffer(
self.as_ptr(),
name.as_ptr(),
value.as_ptr().cast(),
value.len(),
)
};
}
#[cfg(feature = "api-level-28")]
pub fn f64(&self, key: &str) -> Option<f64> {
let name = CString::new(key).unwrap();
let mut out = 0.0;
if unsafe { ffi::AMediaFormat_getDouble(self.as_ptr(), name.as_ptr(), &mut out) } {
Some(out)
} else {
None
}
}
/// Returns (left, top, right, bottom)
#[cfg(feature = "api-level-28")]
pub fn rect(&self, key: &str) -> Option<(i32, i32, i32, i32)> {
let name = CString::new(key).unwrap();
let mut left = 0;
let mut top = 0;
let mut right = 0;
let mut bottom = 0;
if unsafe {
ffi::AMediaFormat_getRect(
self.as_ptr(),
name.as_ptr(),
&mut left,
&mut top,
&mut right,
&mut bottom,
)
} {
Some((left, top, right, bottom))
} else {
None
}
}
#[cfg(feature = "api-level-28")]
pub fn set_f64(&self, key: &str, value: f64) {
let name = CString::new(key).unwrap();
unsafe { ffi::AMediaFormat_setDouble(self.as_ptr(), name.as_ptr(), value) };
}
#[cfg(feature = "api-level-28")]
pub fn set_rect(&self, key: &str, left: i32, top: i32, right: i32, bottom: i32) {
let name = CString::new(key).unwrap();
unsafe {
ffi::AMediaFormat_setRect(self.as_ptr(), name.as_ptr(), left, top, right, bottom)
};
}
#[cfg(feature = "api-level-28")]
pub fn set_usize(&self, key: &str, value: usize) {
let name = CString::new(key).unwrap();
unsafe { ffi::AMediaFormat_setSize(self.as_ptr(), name.as_ptr(), value) };
}
}
impl Drop for MediaFormat {
fn drop(&mut self) {
let status = unsafe { ffi::AMediaFormat_delete(self.as_ptr()) };
MediaError::from_status(status).unwrap();
}
}
/// A native [`AMediaCodec *`]
///
/// [`AMediaCodec *`]: https://developer.android.com/ndk/reference/group/media#amediacodec
#[derive(Debug)]
pub struct MediaCodec {
inner: NonNull<ffi::AMediaCodec>,
async_notify_callback: Option<Pin<Box<AsyncNotifyCallback>>>,
}
pub struct AsyncNotifyCallback {
/// Called when an input buffer becomes available.
///
/// The specified index is the index of the available input buffer.
pub on_input_available: Option<InputAvailableCallback>,
/// Called when an output buffer becomes available.
///
/// The specified index is the index of the available output buffer. The specified
/// [`BufferInfo`] contains information regarding the available output buffer.
pub on_output_available: Option<OutputAvailableCallback>,
/// Called when the output format has changed.
///
/// The specified format contains the new output format.
pub on_format_changed: Option<FormatChangedCallback>,
/// Called when the [`MediaCodec`] encountered an error.
///
/// The specified [`ActionCode`] indicates the possible actions that client can take, and it can
/// be checked by calling [`ActionCode::is_recoverable`] or [`ActionCode::is_transient`]. If
/// both [`ActionCode::is_recoverable`] and [`ActionCode::is_transient`] return [`false`], then
/// the codec error is fatal and the codec must be deleted. The specified detail string may
/// contain more detailed messages about this error.
pub on_error: Option<ErrorCallback>,
}
impl std::fmt::Debug for AsyncNotifyCallback {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AsyncNotifyCallback")
.field(
"on_input_available",
match &self.on_input_available {
Some(_) => &"Some(_)",
None => &"None",
},
)
.field(
"on_output_available",
match &self.on_output_available {
Some(_) => &"Some(_)",
None => &"None",
},
)
.field(
"on_format_changed",
match &self.on_format_changed {
Some(_) => &"Some(_)",
None => &"None",
},
)
.field(
"on_error",
match &self.on_error {
Some(_) => &"Some(_)",
None => &"None",
},
)
.finish()
}
}
pub type InputAvailableCallback = Box<dyn FnMut(usize)>;
pub type OutputAvailableCallback = Box<dyn FnMut(usize, &BufferInfo)>;
pub type FormatChangedCallback = Box<dyn FnMut(&MediaFormat)>;
pub type ErrorCallback = Box<dyn FnMut(MediaError, ActionCode, &CStr)>;
impl MediaCodec {
fn as_ptr(&self) -> *mut ffi::AMediaCodec {
self.inner.as_ptr()
}
pub fn from_codec_name(name: &str) -> Option<Self> {
let c_string = CString::new(name).unwrap();
Some(Self {
inner: NonNull::new(unsafe { ffi::AMediaCodec_createCodecByName(c_string.as_ptr()) })?,
async_notify_callback: None,
})
}
pub fn from_decoder_type(mime_type: &str) -> Option<Self> {
let c_string = CString::new(mime_type).unwrap();
Some(Self {
inner: NonNull::new(unsafe {
ffi::AMediaCodec_createDecoderByType(c_string.as_ptr())
})?,
async_notify_callback: None,
})
}
pub fn from_encoder_type(mime_type: &str) -> Option<Self> {
let c_string = CString::new(mime_type).unwrap();
Some(Self {
inner: NonNull::new(unsafe {
ffi::AMediaCodec_createEncoderByType(c_string.as_ptr())
})?,
async_notify_callback: None,
})
}
/// Set an asynchronous callback for actionable [`MediaCodec`] events.
///
/// When asynchronous callback is enabled, it is an error for the client to call
/// [`MediaCodec::dequeue_input_buffer()`] or [`MediaCodec::dequeue_output_buffer()`].
///
/// [`MediaCodec::flush()`] behaves differently in asynchronous mode. After calling
/// [`MediaCodec::flush()`], the client must call [`MediaCodec::start()`] to "resume" receiving
/// input buffers. Even if the client does not receive
/// [`AsyncNotifyCallback::on_input_available`] callbacks from video encoders configured with an
/// input surface, the client still needs to call [`MediaCodec::start()`] to resume the input
/// surface to send buffers to the encoders.
///
/// When called with [`None`] callback, this method unregisters any previously set callback.
///
/// Refer to the definition of [`AsyncNotifyCallback`] on how each callback function is called
/// and what are specified.
///
/// Once the callback is unregistered or the codec is reset / released, the previously
/// registered callback will not be called.
///
/// All callbacks are fired on one NDK internal thread.
/// [`MediaCodec::set_async_notify_callback()`] should not be called on the callback thread. No
/// heavy duty task should be performed on callback thread.
#[cfg(feature = "api-level-28")]
pub fn set_async_notify_callback(
&mut self,
callback: Option<AsyncNotifyCallback>,
) -> Result<()> {
unsafe extern "C" fn ffi_on_input_available(
_codec: *mut ffi::AMediaCodec,
user_data: *mut c_void,
index: i32,
) {
abort_on_panic(|| {
let callback = &mut *(user_data as *mut AsyncNotifyCallback);
if let Some(f) = callback.on_input_available.as_mut() {
f(index as usize);
}
})
}
unsafe extern "C" fn ffi_on_output_available(
_codec: *mut ffi::AMediaCodec,
user_data: *mut c_void,
index: i32,
buffer_info: *mut ffi::AMediaCodecBufferInfo,
) {
abort_on_panic(|| {
let callback = &mut *(user_data as *mut AsyncNotifyCallback);
if let Some(f) = callback.on_output_available.as_mut() {
let buffer_info = BufferInfo {
inner: *buffer_info,
};
f(index as usize, &buffer_info);
}
})
}
unsafe extern "C" fn ffi_on_format_changed(
_codec: *mut ffi::AMediaCodec,
user_data: *mut c_void,
format: *mut ffi::AMediaFormat,
) {
abort_on_panic(|| {
// Ownership of the format is not documented, but the implementation allocates a new instance and does
// not free it, so assume it is ok for us to do so
// https://cs.android.com/android/platform/superproject/main/+/refs/heads/main:frameworks/av/media/ndk/NdkMediaCodec.cpp;l=248-254;drc=5e15c3e22f3fa32d64e57302201123ce41589adf
let format = MediaFormat::from_ptr(NonNull::new_unchecked(format));
let callback = &mut *(user_data as *mut AsyncNotifyCallback);
if let Some(f) = callback.on_format_changed.as_mut() {
f(&format);
}
})
}
unsafe extern "C" fn ffi_on_error(
_codec: *mut ffi::AMediaCodec,
user_data: *mut c_void,
error: ffi::media_status_t,
action_code: i32,
detail: *const c_char,
) {
abort_on_panic(|| {
let callback = &mut *(user_data as *mut AsyncNotifyCallback);
if let Some(f) = callback.on_error.as_mut() {
f(
MediaError::from_status(error).unwrap_err(),
ActionCode(action_code),
CStr::from_ptr(detail),
);
}
})
}
let (callback, ffi_callback, user_data) = if let Some(callback) = callback {
// On Android 12 and earlier, due to faulty null checks, if a callback is not set, but at least one other
// callback *is* set, then it will segfault in when trying to invoke the unset callback. See for example:
// https://cs.android.com/android/platform/superproject/+/android-12.0.0_r34:frameworks/av/media/ndk/NdkMediaCodec.cpp;l=161-162;drc=ef058464777739e2d9ffad5f00d0e57b186d9a13
// To work around this we just enable all callbacks and do nothing if the corresponding callback is not set
// in AsyncNotifyCallback
let ffi_callback = ffi::AMediaCodecOnAsyncNotifyCallback {
onAsyncInputAvailable: Some(ffi_on_input_available),
onAsyncOutputAvailable: Some(ffi_on_output_available),
onAsyncFormatChanged: Some(ffi_on_format_changed),
onAsyncError: Some(ffi_on_error),
};
let mut boxed = Box::pin(callback);
let ptr: *mut AsyncNotifyCallback = &mut *boxed;
(Some(boxed), ffi_callback, ptr as *mut c_void)
} else {
let ffi_callback = ffi::AMediaCodecOnAsyncNotifyCallback {
onAsyncInputAvailable: None,
onAsyncOutputAvailable: None,
onAsyncFormatChanged: None,
onAsyncError: None,
};
(None, ffi_callback, ptr::null_mut())
};
let status = unsafe {
ffi::AMediaCodec_setAsyncNotifyCallback(self.as_ptr(), ffi_callback, user_data)
};
let result = MediaError::from_status(status);
// This behavior is not documented, but the implementation always clears the callback on failure, so we must
// clear any callback that may have been previously registered
// https://cs.android.com/android/platform/superproject/main/+/main:frameworks/av/media/ndk/NdkMediaCodec.cpp;l=581-584;drc=8c4e619c7461ac1a8c20c55364643662e9185e4d
if result.is_ok() {
self.async_notify_callback = callback;
} else {
self.async_notify_callback = None;
}
result
}
pub fn configure(
&self,
format: &MediaFormat,
surface: Option<&NativeWindow>,
direction: MediaCodecDirection,
) -> Result<()> {
let status = unsafe {
ffi::AMediaCodec_configure(
self.as_ptr(),
format.as_ptr(),
surface.map_or(ptr::null_mut(), |s| s.ptr().as_ptr()),
ptr::null_mut(),
if direction == MediaCodecDirection::Encoder {
ffi::AMEDIACODEC_CONFIGURE_FLAG_ENCODE as u32
} else {
0
},
)
};
MediaError::from_status(status)
}
#[cfg(feature = "api-level-26")]
pub fn create_input_surface(&self) -> Result<NativeWindow> {
use crate::media_error::construct_never_null;
unsafe {
let ptr = construct_never_null(|res| {
ffi::AMediaCodec_createInputSurface(self.as_ptr(), res)
})?;
Ok(NativeWindow::from_ptr(ptr))
}
}
#[cfg(feature = "api-level-26")]
pub fn create_persistent_input_surface() -> Result<NativeWindow> {
use crate::media_error::construct_never_null;
unsafe {
let ptr =
construct_never_null(|res| ffi::AMediaCodec_createPersistentInputSurface(res))?;
Ok(NativeWindow::from_ptr(ptr))
}
}
pub fn dequeue_input_buffer(&self, timeout: Duration) -> Result<DequeuedInputBufferResult<'_>> {
let result = unsafe {
ffi::AMediaCodec_dequeueInputBuffer(
self.as_ptr(),
timeout
.as_micros()
.try_into()
.expect("Supplied timeout is too large"),
)
};
if result == ffi::AMEDIACODEC_INFO_TRY_AGAIN_LATER as isize {
Ok(DequeuedInputBufferResult::TryAgainLater)
} else {
let index = MediaError::from_status_if_negative(result)? as usize;
Ok(DequeuedInputBufferResult::Buffer(InputBuffer {
codec: self,
index,
}))
}
}
pub fn dequeue_output_buffer(
&self,
timeout: Duration,
) -> Result<DequeuedOutputBufferInfoResult<'_>> {
let mut info = MaybeUninit::uninit();
let result = unsafe {
ffi::AMediaCodec_dequeueOutputBuffer(
self.as_ptr(),
info.as_mut_ptr(),
timeout
.as_micros()
.try_into()
.expect("Supplied timeout is too large"),
)
};
if result == ffi::AMEDIACODEC_INFO_TRY_AGAIN_LATER as isize {
Ok(DequeuedOutputBufferInfoResult::TryAgainLater)
} else if result == ffi::AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED as isize {
Ok(DequeuedOutputBufferInfoResult::OutputFormatChanged)
} else if result == ffi::AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED as isize {
Ok(DequeuedOutputBufferInfoResult::OutputBuffersChanged)
} else {
let index = MediaError::from_status_if_negative(result)? as usize;
Ok(DequeuedOutputBufferInfoResult::Buffer(OutputBuffer {
codec: self,
index,
info: BufferInfo {
inner: unsafe { info.assume_init() },
},
}))
}
}
pub fn flush(&self) -> Result<()> {
let status = unsafe { ffi::AMediaCodec_flush(self.as_ptr()) };
MediaError::from_status(status)
}
pub fn input_buffer(&self, index: usize) -> Option<&mut [MaybeUninit<u8>]> {
unsafe {
let mut out_size = 0;
let buffer_ptr = ffi::AMediaCodec_getInputBuffer(self.as_ptr(), index, &mut out_size);
if buffer_ptr.is_null() {
return None;
}
Some(slice::from_raw_parts_mut(buffer_ptr.cast(), out_size))
}
}
pub fn output_buffer(&self, index: usize) -> Option<&[u8]> {
unsafe {
let mut out_size = 0;
let buffer_ptr = ffi::AMediaCodec_getOutputBuffer(self.as_ptr(), index, &mut out_size);
if buffer_ptr.is_null() {
return None;
}
Some(slice::from_raw_parts(buffer_ptr, out_size))
}
}
#[cfg(feature = "api-level-28")]
pub fn input_format(&self) -> MediaFormat {
let inner = NonNull::new(unsafe { ffi::AMediaCodec_getInputFormat(self.as_ptr()) })
.expect("AMediaCodec_getInputFormat returned NULL");
MediaFormat { inner }
}
pub fn output_format(&self) -> MediaFormat {
let inner = NonNull::new(unsafe { ffi::AMediaCodec_getOutputFormat(self.as_ptr()) })
.expect("AMediaCodec_getOutputFormat returned NULL");
MediaFormat { inner }
}
#[cfg(feature = "api-level-28")]
pub fn name(&self) -> Result<String> {
use crate::media_error::construct;
unsafe {
let name_ptr = construct(|name| ffi::AMediaCodec_getName(self.as_ptr(), name))?;
let name = CStr::from_ptr(name_ptr).to_str().unwrap().to_owned();
ffi::AMediaCodec_releaseName(self.as_ptr(), name_ptr);
Ok(name)
}
}
pub fn queue_input_buffer(
&self,
buffer: InputBuffer<'_>,
offset: usize,
size: usize,
time: u64,
flags: u32,
) -> Result<()> {
debug_assert!(ptr::eq(self, buffer.codec));
self.queue_input_buffer_by_index(buffer.index, offset, size, time, flags)
}
pub fn queue_input_buffer_by_index(
&self,
buffer_index: usize,
offset: usize,
size: usize,
time: u64,
flags: u32,
) -> Result<()> {
let status = unsafe {
ffi::AMediaCodec_queueInputBuffer(
self.as_ptr(),
buffer_index,
offset as ffi::off_t,
size,
time,
flags,
)
};
MediaError::from_status(status)
}
pub fn release_output_buffer(&self, buffer: OutputBuffer<'_>, render: bool) -> Result<()> {
debug_assert!(ptr::eq(self, buffer.codec));
self.release_output_buffer_by_index(buffer.index, render)
}
pub fn release_output_buffer_by_index(&self, buffer_index: usize, render: bool) -> Result<()> {
let status =
unsafe { ffi::AMediaCodec_releaseOutputBuffer(self.as_ptr(), buffer_index, render) };
MediaError::from_status(status)
}
pub fn release_output_buffer_at_time(
&self,
buffer: OutputBuffer<'_>,
timestamp_ns: i64,
) -> Result<()> {
debug_assert!(ptr::eq(self, buffer.codec));
self.release_output_buffer_at_time_by_index(buffer.index, timestamp_ns)
}
pub fn release_output_buffer_at_time_by_index(
&self,
buffer_index: usize,
timestamp_ns: i64,
) -> Result<()> {
let status = unsafe {
ffi::AMediaCodec_releaseOutputBufferAtTime(self.as_ptr(), buffer_index, timestamp_ns)
};
MediaError::from_status(status)
}
#[cfg(feature = "api-level-26")]
pub fn set_input_surface(&self, surface: &NativeWindow) -> Result<()> {
let status =
unsafe { ffi::AMediaCodec_setInputSurface(self.as_ptr(), surface.ptr().as_ptr()) };
MediaError::from_status(status)
}
pub fn set_output_surface(&self, surface: &NativeWindow) -> Result<()> {
let status =
unsafe { ffi::AMediaCodec_setOutputSurface(self.as_ptr(), surface.ptr().as_ptr()) };
MediaError::from_status(status)
}
#[cfg(feature = "api-level-26")]
pub fn set_parameters(&self, params: MediaFormat) -> Result<()> {
let status = unsafe { ffi::AMediaCodec_setParameters(self.as_ptr(), params.as_ptr()) };
MediaError::from_status(status)
}
#[cfg(feature = "api-level-26")]
pub fn set_signal_end_of_input_stream(&self) -> Result<()> {
let status = unsafe { ffi::AMediaCodec_signalEndOfInputStream(self.as_ptr()) };
MediaError::from_status(status)
}
pub fn start(&self) -> Result<()> {
let status = unsafe { ffi::AMediaCodec_start(self.as_ptr()) };
MediaError::from_status(status)
}
pub fn stop(&self) -> Result<()> {
let status = unsafe { ffi::AMediaCodec_stop(self.as_ptr()) };
MediaError::from_status(status)
}
}
impl Drop for MediaCodec {
fn drop(&mut self) {
let status = unsafe { ffi::AMediaCodec_delete(self.as_ptr()) };
MediaError::from_status(status).unwrap();
}
}
#[derive(Debug)]
pub struct InputBuffer<'a> {
codec: &'a MediaCodec,
index: usize,
}
impl InputBuffer<'_> {
pub fn buffer_mut(&mut self) -> &mut [MaybeUninit<u8>] {
self.codec.input_buffer(self.index).unwrap_or_else(|| {
panic!(
"AMediaCodec_getInputBuffer returned NULL for index {}",
self.index
)
})
}
}
#[derive(Debug)]
pub enum DequeuedInputBufferResult<'a> {
Buffer(InputBuffer<'a>),
TryAgainLater,
}
#[derive(Debug)]
pub struct OutputBuffer<'a> {
codec: &'a MediaCodec,
index: usize,
info: BufferInfo,
}
impl OutputBuffer<'_> {
pub fn buffer(&self) -> &[u8] {
self.codec.output_buffer(self.index).unwrap_or_else(|| {
panic!(
"AMediaCodec_getOutputBuffer returned NULL for index {}",
self.index
)
})
}
#[cfg(feature = "api-level-28")]
pub fn format(&self) -> MediaFormat {
let inner = NonNull::new(unsafe {
ffi::AMediaCodec_getBufferFormat(self.codec.as_ptr(), self.index)
})
.expect("AMediaCodec_getBufferFormat returned NULL");
MediaFormat { inner }
}
pub fn info(&self) -> &BufferInfo {
&self.info
}
}
#[derive(Debug)]
pub enum DequeuedOutputBufferInfoResult<'a> {
Buffer(OutputBuffer<'a>),
TryAgainLater,
OutputFormatChanged,
OutputBuffersChanged,
}
#[derive(Copy, Clone, Debug)]
pub struct BufferInfo {
inner: ffi::AMediaCodecBufferInfo,
}
impl BufferInfo {
pub fn offset(&self) -> i32 {
self.inner.offset
}
pub fn size(&self) -> i32 {
self.inner.size
}
pub fn presentation_time_us(&self) -> i64 {
self.inner.presentationTimeUs
}
pub fn flags(&self) -> u32 {
self.inner.flags
}
}
#[derive(Copy, Clone, Debug)]
pub struct ActionCode(pub i32);
impl ActionCode {
pub fn is_recoverable(self) -> bool {
unsafe { ffi::AMediaCodecActionCode_isRecoverable(self.0) }
}
pub fn is_transient(self) -> bool {
unsafe { ffi::AMediaCodecActionCode_isTransient(self.0) }
}
}

7
vendor/ndk-0.8.0/src/media/mod.rs vendored Normal file
View File

@@ -0,0 +1,7 @@
//! Bindings for the NDK media classes.
//!
//! See also [the NDK docs](https://developer.android.com/ndk/reference/group/media)
#![cfg(feature = "media")]
pub mod image_reader;
pub mod media_codec;

141
vendor/ndk-0.8.0/src/media_error.rs vendored Normal file
View File

@@ -0,0 +1,141 @@
//! Bindings for NDK media status codes.
//!
//! Also used outside of `libmediandk.so` in `libamidi.so` for example.
#![cfg(feature = "media")]
// The cfg(feature) bounds for some pub(crate) fn uses are non-trivial and will become even more
// complex going forward. Allow them to be unused when compiling with certain feature combinations.
#![allow(dead_code)]
use std::{fmt, mem::MaybeUninit, ptr::NonNull};
use num_enum::{FromPrimitive, IntoPrimitive};
pub type Result<T, E = MediaError> = std::result::Result<T, E>;
/// Media Status codes for [`media_status_t`](https://developer.android.com/ndk/reference/group/media#group___media_1ga009a49041fe39f7bdc6d8b5cddbe760c)
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
#[doc(alias = "media_status_t")]
#[non_exhaustive]
pub enum MediaError {
#[doc(alias = "AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE")]
CodecErrorInsufficientResource = ffi::media_status_t::AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE.0,
#[doc(alias = "AMEDIACODEC_ERROR_RECLAIMED")]
CodecErrorReclaimed = ffi::media_status_t::AMEDIACODEC_ERROR_RECLAIMED.0,
#[doc(alias = "AMEDIA_ERROR_UNKNOWN")]
ErrorUnknown = ffi::media_status_t::AMEDIA_ERROR_UNKNOWN.0,
#[doc(alias = "AMEDIA_ERROR_MALFORMED")]
ErrorMalformed = ffi::media_status_t::AMEDIA_ERROR_MALFORMED.0,
#[doc(alias = "AMEDIA_ERROR_UNSUPPORTED")]
ErrorUnsupported = ffi::media_status_t::AMEDIA_ERROR_UNSUPPORTED.0,
#[doc(alias = "AMEDIA_ERROR_INVALID_OBJECT")]
ErrorInvalidObject = ffi::media_status_t::AMEDIA_ERROR_INVALID_OBJECT.0,
#[doc(alias = "AMEDIA_ERROR_INVALID_PARAMETER")]
ErrorInvalidParameter = ffi::media_status_t::AMEDIA_ERROR_INVALID_PARAMETER.0,
#[doc(alias = "AMEDIA_ERROR_INVALID_OPERATION")]
ErrorInvalidOperation = ffi::media_status_t::AMEDIA_ERROR_INVALID_OPERATION.0,
#[doc(alias = "AMEDIA_ERROR_END_OF_STREAM")]
ErrorEndOfStream = ffi::media_status_t::AMEDIA_ERROR_END_OF_STREAM.0,
#[doc(alias = "AMEDIA_ERROR_IO")]
ErrorIo = ffi::media_status_t::AMEDIA_ERROR_IO.0,
#[doc(alias = "AMEDIA_ERROR_WOULD_BLOCK")]
ErrorWouldBlock = ffi::media_status_t::AMEDIA_ERROR_WOULD_BLOCK.0,
#[doc(alias = "AMEDIA_DRM_ERROR_BASE")]
DrmErrorBase = ffi::media_status_t::AMEDIA_DRM_ERROR_BASE.0,
#[doc(alias = "AMEDIA_DRM_NOT_PROVISIONED")]
DrmNotProvisioned = ffi::media_status_t::AMEDIA_DRM_NOT_PROVISIONED.0,
#[doc(alias = "AMEDIA_DRM_RESOURCE_BUSY")]
DrmResourceBusy = ffi::media_status_t::AMEDIA_DRM_RESOURCE_BUSY.0,
#[doc(alias = "AMEDIA_DRM_DEVICE_REVOKED")]
DrmDeviceRevoked = ffi::media_status_t::AMEDIA_DRM_DEVICE_REVOKED.0,
#[doc(alias = "AMEDIA_DRM_SHORT_BUFFER")]
DrmShortBuffer = ffi::media_status_t::AMEDIA_DRM_SHORT_BUFFER.0,
#[doc(alias = "AMEDIA_DRM_SESSION_NOT_OPENED")]
DrmSessionNotOpened = ffi::media_status_t::AMEDIA_DRM_SESSION_NOT_OPENED.0,
#[doc(alias = "AMEDIA_DRM_TAMPER_DETECTED")]
DrmTamperDetected = ffi::media_status_t::AMEDIA_DRM_TAMPER_DETECTED.0,
#[doc(alias = "AMEDIA_DRM_VERIFY_FAILED")]
DrmVerifyFailed = ffi::media_status_t::AMEDIA_DRM_VERIFY_FAILED.0,
#[doc(alias = "AMEDIA_DRM_NEED_KEY")]
DrmNeedKey = ffi::media_status_t::AMEDIA_DRM_NEED_KEY.0,
#[doc(alias = "AMEDIA_DRM_LICENSE_EXPIRED")]
DrmLicenseExpired = ffi::media_status_t::AMEDIA_DRM_LICENSE_EXPIRED.0,
#[doc(alias = "AMEDIA_IMGREADER_ERROR_BASE")]
ImgreaderErrorBase = ffi::media_status_t::AMEDIA_IMGREADER_ERROR_BASE.0,
#[doc(alias = "AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE")]
ImgreaderNoBufferAvailable = ffi::media_status_t::AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE.0,
#[doc(alias = "AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED")]
ImgreaderMaxImagesAcquired = ffi::media_status_t::AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED.0,
#[doc(alias = "AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE")]
ImgreaderCannotLockImage = ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE.0,
#[doc(alias = "AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE")]
ImgreaderCannotUnlockImage = ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE.0,
#[doc(alias = "AMEDIA_IMGREADER_IMAGE_NOT_LOCKED")]
ImgreaderImageNotLocked = ffi::media_status_t::AMEDIA_IMGREADER_IMAGE_NOT_LOCKED.0,
/// This error code is unknown to the [`ndk`][crate] crate. Please report an issue if you
/// believe this code needs to be added to our mapping.
// Use the OK discriminant, as no-one will be able to call `as i32` and only has access to the
// constants via `From` provided by `IntoPrimitive` which reads the contained value.
#[num_enum(catch_all)]
Unknown(i32) = ffi::media_status_t::AMEDIA_OK.0,
}
impl fmt::Display for MediaError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for MediaError {}
impl MediaError {
/// Returns [`Ok`] on [`ffi::media_status_t::AMEDIA_OK`], [`Err`] otherwise (including positive
/// values).
///
/// Note that some known error codes (currently only for `AMediaCodec`) are positive.
pub(crate) fn from_status(status: ffi::media_status_t) -> Result<()> {
match status {
ffi::media_status_t::AMEDIA_OK => Ok(()),
x => Err(Self::from(x.0)),
}
}
/// Returns the original value in [`Ok`] if it is not negative, [`Err`] otherwise.
///
/// Note that some [`ffi::media_status_t`] codes are positive but will never be returned as
/// [`Err`] from this function. As of writing these codes are specific to the `AMediaCodec` API
/// and should not be handled generically.
pub(crate) fn from_status_if_negative<T: Into<isize> + Copy>(value: T) -> Result<T> {
let v = value.into();
if v >= 0 {
Ok(value)
} else {
Err(Self::from(
i32::try_from(v).expect("Error code out of bounds"),
))
}
}
}
/// Calls the `with_ptr` construction function with a pointer to uninitialized stack memory,
/// expecting `with_ptr` to initialize it or otherwise return an error code.
pub(crate) fn construct<T>(with_ptr: impl FnOnce(*mut T) -> ffi::media_status_t) -> Result<T> {
let mut result = MaybeUninit::uninit();
let status = with_ptr(result.as_mut_ptr());
MediaError::from_status(status).map(|()| unsafe { result.assume_init() })
}
/// Calls the `with_ptr` construction function with a pointer to a pointer, and expects `with_ptr`
/// to initialize the second pointer to a valid address. That address is returned in the form of a
/// [`NonNull`] object.
pub(crate) fn construct_never_null<T>(
with_ptr: impl FnOnce(*mut *mut T) -> ffi::media_status_t,
) -> Result<NonNull<T>> {
let result = construct(with_ptr)?;
let non_null = if cfg!(debug_assertions) {
NonNull::new(result).expect("result should never be null")
} else {
unsafe { NonNull::new_unchecked(result) }
};
Ok(non_null)
}

246
vendor/ndk-0.8.0/src/native_activity.rs vendored Normal file
View File

@@ -0,0 +1,246 @@
//! Bindings for [`ANativeActivity`]
//!
//! [`ANativeActivity`]: https://developer.android.com/ndk/reference/group/native-activity#anativeactivity
use super::hardware_buffer_format::HardwareBufferFormat;
use bitflags::bitflags;
use std::{
ffi::{CStr, OsStr},
os::{raw::c_void, unix::prelude::OsStrExt},
path::Path,
ptr::NonNull,
};
bitflags! {
/// Window flags, as per the Java API at [`android.view.WindowManager.LayoutParams`].
///
/// <https://developer.android.com/ndk/reference/group/native-activity#group___native_activity_1ga2f1398dba5e4a5616b83437528bdb28e>
///
/// [`android.view.WindowManager.LayoutParams`]: https://developer.android.com/reference/android/view/WindowManager.LayoutParams
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct WindowFlags: u32 {
const ALLOW_LOCK_WHILE_SCREEN_ON = ffi::AWINDOW_FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
const DIM_BEHIND = ffi::AWINDOW_FLAG_DIM_BEHIND;
#[deprecated = "Deprecated. Blurring is no longer supported."]
const BLUR_BEHIND = ffi::AWINDOW_FLAG_BLUR_BEHIND;
const NOT_FOCUSABLE = ffi::AWINDOW_FLAG_NOT_FOCUSABLE;
const NOT_TOUCHABLE = ffi::AWINDOW_FLAG_NOT_TOUCHABLE;
const NOT_TOUCH_MODAL = ffi::AWINDOW_FLAG_NOT_TOUCH_MODAL;
#[deprecated = "This constant was deprecated in API level 20. This flag has no effect."]
const TOUCHABLE_WHEN_WAKING = ffi::AWINDOW_FLAG_TOUCHABLE_WHEN_WAKING;
const KEEP_SCREEN_ON = ffi::AWINDOW_FLAG_KEEP_SCREEN_ON;
const LAYOUT_IN_SCREEN = ffi::AWINDOW_FLAG_LAYOUT_IN_SCREEN;
const LAYOUT_NO_LIMITS = ffi::AWINDOW_FLAG_LAYOUT_NO_LIMITS;
const FULLSCREEN = ffi::AWINDOW_FLAG_FULLSCREEN;
#[cfg_attr(feature = "api-level-30", deprecated = "This constant was deprecated in API level 30. This value became API \"by accident\", and shouldn't be used by 3rd party applications.")]
const FORCE_NOT_FULLSCREEN = ffi::AWINDOW_FLAG_FORCE_NOT_FULLSCREEN;
#[deprecated = "This constant was deprecated in API level 17. This flag is no longer used."]
const DITHER = ffi::AWINDOW_FLAG_DITHER;
const SECURE = ffi::AWINDOW_FLAG_SECURE;
const SCALED = ffi::AWINDOW_FLAG_SCALED;
const IGNORE_CHEEK_PRESSES = ffi::AWINDOW_FLAG_IGNORE_CHEEK_PRESSES;
const LAYOUT_INSET_DECOR = ffi::AWINDOW_FLAG_LAYOUT_INSET_DECOR;
const ALT_FOCUSABLE_IM = ffi::AWINDOW_FLAG_ALT_FOCUSABLE_IM;
const WATCH_OUTSIDE_TOUCH = ffi::AWINDOW_FLAG_WATCH_OUTSIDE_TOUCH;
const SHOW_WHEN_LOCKED = ffi::AWINDOW_FLAG_SHOW_WHEN_LOCKED;
const SHOW_WALLPAPER = ffi::AWINDOW_FLAG_SHOW_WALLPAPER;
const TURN_SCREEN_ON = ffi::AWINDOW_FLAG_TURN_SCREEN_ON;
#[cfg_attr(feature = "api-level-26", deprecated = "This constant was deprecated in API level 26. Use `SHOW_WHEN_LOCKED` instead.")]
const DISMISS_KEYGUARD = ffi::AWINDOW_FLAG_DISMISS_KEYGUARD;
const ATTACHED_IN_DECOR = 0x40000000;
}
}
/// A native [`ANativeActivity *`]
///
/// This is either provided in [`ffi::ANativeActivity_onCreate()`], or accessible through
/// `ndk_glue::native_activity()`.
///
/// [`ANativeActivity *`]: https://developer.android.com/ndk/reference/struct/a-native-activity
#[derive(Debug)]
pub struct NativeActivity {
ptr: NonNull<ffi::ANativeActivity>,
}
// It gets shared between threads in `ndk-glue`
unsafe impl Send for NativeActivity {}
unsafe impl Sync for NativeActivity {}
impl NativeActivity {
/// Create a [`NativeActivity`] from a pointer
///
/// # Safety
/// By calling this function, you assert that it is a valid pointer to a native
/// [`ffi::ANativeActivity`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::ANativeActivity>) -> Self {
Self { ptr }
}
/// The pointer to the native `ANativeActivity`
pub fn ptr(&self) -> NonNull<ffi::ANativeActivity> {
self.ptr
}
}
/// Methods that relate to fields of the struct itself
///
/// The relevant NDK docs can be found
/// [here](https://developer.android.com/ndk/reference/struct/a-native-activity).
impl NativeActivity {
/// The platform's SDK version code
pub fn sdk_version(&self) -> i32 {
unsafe { self.ptr.as_ref().sdkVersion }
}
/// Path to this application's internal data directory
pub fn internal_data_path(&self) -> &Path {
OsStr::from_bytes(unsafe { CStr::from_ptr(self.ptr.as_ref().internalDataPath) }.to_bytes())
.as_ref()
}
/// Path to this application's external (removable, mountable) data directory
pub fn external_data_path(&self) -> &Path {
OsStr::from_bytes(unsafe { CStr::from_ptr(self.ptr.as_ref().externalDataPath) }.to_bytes())
.as_ref()
}
/// This app's asset manager, which can be used to access assets from the `.apk` file.
pub fn asset_manager(&self) -> crate::asset::AssetManager {
unsafe {
crate::asset::AssetManager::from_ptr(
NonNull::new(self.ptr.as_ref().assetManager).unwrap(),
)
}
}
/// Instance data associated with the activity
pub fn instance(&self) -> *mut c_void {
unsafe { self.ptr.as_ref().instance }
}
/// Set the instance data associated with the activity
///
/// # Safety
/// This can invalidate assumptions held by `ndk-glue`, as well as cause data
/// races with concurrent access to the instance data.
pub unsafe fn set_instance(&mut self, data: *mut c_void) {
// FIXME Does this create undefined behavior by creating a mutable reference to what could
// also be accessed immutably at the same time?
//
// I think that as long as we warn the users to avoid concurrent access, and we pass along
// the `unsafe` burden, it's OK.
self.ptr.as_mut().instance = data;
}
/// This process's `JavaVM` object.
///
/// Usage with [__jni__](https://crates.io/crates/jni) crate:
/// ```no_run
/// # use ndk::native_activity::NativeActivity;
/// # let native_activity: NativeActivity = unimplemented!();
/// let vm_ptr = native_activity.vm();
/// let vm = unsafe { jni::JavaVM::from_raw(vm_ptr) }.unwrap();
/// let env = vm.attach_current_thread();
/// // Do JNI with env ...
/// ```
pub fn vm(&self) -> *mut jni_sys::JavaVM {
unsafe { self.ptr.as_ref() }.vm
}
/// The [`android.app.NativeActivity`] instance
///
/// In the JNI, this is named `clazz`; however, as the docs say, "it should really be named
/// 'activity' instead of 'clazz', since it's a reference to the NativeActivity instance".
///
/// [`android.app.NativeActivity`]: https://developer.android.com/reference/android/app/NativeActivity
pub fn activity(&self) -> jni_sys::jobject {
unsafe { self.ptr.as_ref() }.clazz
}
/// Path to the directory with the application's OBB files.
///
/// # Safety
/// Only available as of Honeycomb (Android 3.0+, API level 11+)
pub unsafe fn obb_path(&self) -> &Path {
OsStr::from_bytes(CStr::from_ptr(self.ptr.as_ref().obbPath).to_bytes()).as_ref()
}
}
/// Methods that relate to `ANativeActivity_*` functions.
///
/// The relevant NDK docs can be found
/// [here](https://developer.android.com/ndk/reference/group/native-activity).
impl NativeActivity {
/// Sends a destroy event to the activity and stops it.
pub fn finish(&self) {
unsafe { ffi::ANativeActivity_finish(self.ptr.as_ptr()) }
}
/// Shows the IME (the on-screen keyboard).
///
/// If `force` is true, the `SHOW_FORCED` flag is used; otherwise, the `SHOW_IMPLICIT` flag is
/// used. Depending on the value of this flag, the `hide_soft_input` method with behave
/// differently. See [the relevant
/// javadoc](https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#constants_2)
/// for more information.
pub fn show_soft_input(&self, force: bool) {
let flag = if force {
ffi::ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED
} else {
ffi::ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT
};
unsafe { ffi::ANativeActivity_showSoftInput(self.ptr.as_ptr(), flag) }
}
/// Hides the IME (the on-screen keyboard).
///
/// If `not_always` is true, the `HIDE_NOT_ALWAYS` flag is used; otherwise, the
/// `HIDE_IMPLICIT_ONLY` flag is used. Depending on the value of this flag and the way the IME
/// was shown, it may or may not be hidden. See [the relevant
/// javadoc](https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#constants_2)
/// for more information.
pub fn hide_soft_input(&self, not_always: bool) {
let flag = if not_always {
ffi::ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS
} else {
ffi::ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY
};
unsafe { ffi::ANativeActivity_hideSoftInput(self.ptr.as_ptr(), flag) }
}
/// Change the window format of the given activity.
///
/// Calls [`getWindow().setFormat()`] of the given activity. Note that this method can be
/// called from any thread; it will send a message to the main thread of the process where the
/// Java finish call will take place.
///
/// [`getWindow().setFormat()`]: https://developer.android.com/reference/android/view/Window#setFormat(int)
pub fn set_window_format(&self, format: HardwareBufferFormat) {
unsafe {
ffi::ANativeActivity_setWindowFormat(
self.ptr.as_ptr(),
u32::from(format)
.try_into()
.expect("i32 overflow in set_window_format()"),
)
}
}
/// Change the window flags of the given activity.
///
/// Calls [`getWindow().setFlags()`] of the given activity.
///
/// Note that this method can be called from any thread; it will send a message to the main
/// thread of the process where the Java finish call will take place.
///
/// [`getWindow().setFlags()`]: https://developer.android.com/reference/android/view/Window#setFlags(int,%20int)
pub fn set_window_flags(&self, add_flags: WindowFlags, remove_flags: WindowFlags) {
unsafe {
ffi::ANativeActivity_setWindowFlags(
self.ptr.as_ptr(),
add_flags.bits(),
remove_flags.bits(),
)
}
}
}

482
vendor/ndk-0.8.0/src/native_window.rs vendored Normal file
View File

@@ -0,0 +1,482 @@
//! Bindings for [`ANativeWindow`]
//!
//! [`ANativeWindow`]: https://developer.android.com/ndk/reference/group/a-native-window#anativewindow
use std::{ffi::c_void, io, mem::MaybeUninit, ptr::NonNull};
use jni_sys::{jobject, JNIEnv};
#[cfg(feature = "api-level-28")]
use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
#[cfg(feature = "api-level-28")]
use thiserror::Error;
use super::{hardware_buffer_format::HardwareBufferFormat, utils::status_to_io_result};
#[cfg(feature = "api-level-28")]
use crate::data_space::DataSpace;
pub type Rect = ffi::ARect;
// [`NativeWindow`] represents the producer end of an image queue
///
/// It is the C counterpart of the [`android.view.Surface`] object in Java, and can be converted
/// both ways. Depending on the consumer, images submitted to [`NativeWindow`] can be shown on the
/// display or sent to other consumers, such as video encoders.
///
/// [`android.view.Surface`]: https://developer.android.com/reference/android/view/Surface
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct NativeWindow {
ptr: NonNull<ffi::ANativeWindow>,
}
unsafe impl Send for NativeWindow {}
unsafe impl Sync for NativeWindow {}
impl Drop for NativeWindow {
fn drop(&mut self) {
unsafe { ffi::ANativeWindow_release(self.ptr.as_ptr()) }
}
}
impl Clone for NativeWindow {
fn clone(&self) -> Self {
unsafe { ffi::ANativeWindow_acquire(self.ptr.as_ptr()) }
Self { ptr: self.ptr }
}
}
#[cfg(feature = "rwh_04")]
unsafe impl rwh_04::HasRawWindowHandle for NativeWindow {
fn raw_window_handle(&self) -> rwh_04::RawWindowHandle {
let mut handle = rwh_04::AndroidNdkHandle::empty();
handle.a_native_window = self.ptr.as_ptr().cast();
rwh_04::RawWindowHandle::AndroidNdk(handle)
}
}
#[cfg(feature = "rwh_05")]
unsafe impl rwh_05::HasRawWindowHandle for NativeWindow {
fn raw_window_handle(&self) -> rwh_05::RawWindowHandle {
let mut handle = rwh_05::AndroidNdkWindowHandle::empty();
handle.a_native_window = self.ptr.as_ptr().cast();
rwh_05::RawWindowHandle::AndroidNdk(handle)
}
}
#[cfg(feature = "rwh_06")]
impl rwh_06::HasWindowHandle for NativeWindow {
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
let handle = rwh_06::AndroidNdkWindowHandle::new(self.ptr.cast());
let handle = rwh_06::RawWindowHandle::AndroidNdk(handle);
// SAFETY: All fields of the "raw" `AndroidNdkWindowHandle` struct are filled out. The
// returned pointer is also kept valid by `NativeWindow` (until `Drop`), which is lifetime-
// borrowed in the returned `WindowHandle<'_>` and cannot be outlived. Its value won't
// change throughout the lifetime of this `NativeWindow`.
Ok(unsafe { rwh_06::WindowHandle::borrow_raw(handle) })
}
}
impl NativeWindow {
/// Assumes ownership of `ptr`
///
/// # Safety
/// `ptr` must be a valid pointer to an Android [`ffi::ANativeWindow`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::ANativeWindow>) -> Self {
Self { ptr }
}
/// Acquires ownership of `ptr`
///
/// # Safety
/// `ptr` must be a valid pointer to an Android [`ffi::ANativeWindow`].
pub unsafe fn clone_from_ptr(ptr: NonNull<ffi::ANativeWindow>) -> Self {
ffi::ANativeWindow_acquire(ptr.as_ptr());
Self::from_ptr(ptr)
}
pub fn ptr(&self) -> NonNull<ffi::ANativeWindow> {
self.ptr
}
pub fn height(&self) -> i32 {
unsafe { ffi::ANativeWindow_getHeight(self.ptr.as_ptr()) }
}
pub fn width(&self) -> i32 {
unsafe { ffi::ANativeWindow_getWidth(self.ptr.as_ptr()) }
}
/// Return the current pixel format ([`HardwareBufferFormat`]) of the window surface.
pub fn format(&self) -> HardwareBufferFormat {
let value = unsafe { ffi::ANativeWindow_getFormat(self.ptr.as_ptr()) };
let value = u32::try_from(value).unwrap();
value.into()
}
/// Change the format and size of the window buffers.
///
/// The width and height control the number of pixels in the buffers, not the dimensions of the
/// window on screen. If these are different than the window's physical size, then its buffer
/// will be scaled to match that size when compositing it to the screen. The width and height
/// must be either both zero or both non-zero.
///
/// For all of these parameters, if `0` or [`None`] is supplied then the window's base value
/// will come back in force.
pub fn set_buffers_geometry(
&self,
width: i32,
height: i32,
format: Option<HardwareBufferFormat>,
) -> io::Result<()> {
let format = format.map_or(0, |f| {
u32::from(f)
.try_into()
.expect("i32 overflow in set_buffers_geometry")
});
let status = unsafe {
ffi::ANativeWindow_setBuffersGeometry(self.ptr.as_ptr(), width, height, format)
};
status_to_io_result(status)
}
/// Set a transform that will be applied to future buffers posted to the window.
#[cfg(feature = "api-level-26")]
pub fn set_buffers_transform(&self, transform: NativeWindowTransform) -> io::Result<()> {
let status = unsafe {
ffi::ANativeWindow_setBuffersTransform(self.ptr.as_ptr(), transform.bits() as i32)
};
status_to_io_result(status)
}
/// All buffers queued after this call will be associated with the dataSpace parameter
/// specified.
///
/// `data_space` specifies additional information about the buffer. For example, it can be used
/// to convey the color space of the image data in the buffer, or it can be used to indicate
/// that the buffers contain depth measurement data instead of color images. The default
/// dataSpace is `0`, [`DataSpace::Unknown`], unless it has been overridden by the producer.
#[cfg(feature = "api-level-28")]
#[doc(alias = "ANativeWindow_setBuffersDataSpace")]
pub fn set_buffers_data_space(&self, data_space: DataSpace) -> io::Result<()> {
let data_space = (data_space as u32)
.try_into()
.expect("Sign bit should be unused");
let status =
unsafe { ffi::ANativeWindow_setBuffersDataSpace(self.ptr.as_ptr(), data_space) };
status_to_io_result(status)
}
/// Get the dataspace of the buffers in this [`NativeWindow`].
#[cfg(feature = "api-level-28")]
#[doc(alias = "ANativeWindow_getBuffersDataSpace")]
pub fn buffers_data_space(&self) -> Result<DataSpace, GetDataSpaceError> {
let status = unsafe { ffi::ANativeWindow_getBuffersDataSpace(self.ptr.as_ptr()) };
if status >= 0 {
Ok(DataSpace::try_from_primitive(status as u32)?)
} else {
Err(status_to_io_result(status).unwrap_err().into())
}
}
/// Sets the intended frame rate for this window.
///
/// Same as [`set_frame_rate_with_change_strategy(window, frame_rate, compatibility, ChangeFrameRateStrategy::OnlyIfSeamless)`][`NativeWindow::set_frame_rate_with_change_strategy()`].
///
#[cfg_attr(
not(feature = "api-level-31"),
doc = "[`NativeWindow::set_frame_rate_with_change_strategy()`]: https://developer.android.com/ndk/reference/group/a-native-window#anativewindow_setframeratewithchangestrategy"
)]
#[cfg(feature = "api-level-30")]
#[doc(alias = "ANativeWindow_setFrameRate")]
pub fn set_frame_rate(
&self,
frame_rate: f32,
compatibility: FrameRateCompatibility,
) -> io::Result<()> {
let compatibility = (compatibility as u32)
.try_into()
.expect("i8 overflow in FrameRateCompatibility");
let status = unsafe {
ffi::ANativeWindow_setFrameRate(self.ptr.as_ptr(), frame_rate, compatibility)
};
status_to_io_result(status)
}
/// Sets the intended frame rate for this window.
///
/// On devices that are capable of running the display at different refresh rates, the system
/// may choose a display refresh rate to better match this window's frame rate. Usage of this
/// API won't introduce frame rate throttling, or affect other aspects of the application's
/// frame production pipeline. However, because the system may change the display refresh rate,
/// calls to this function may result in changes to Choreographer callback timings, and changes
/// to the time interval at which the system releases buffers back to the application.
///
/// Note that this only has an effect for windows presented on the display. If this
/// [`NativeWindow`] is consumed by something other than the system compositor, e.g. a media
/// codec, this call has no effect.
///
/// You can register for changes in the refresh rate using
/// [`ffi::AChoreographer_registerRefreshRateCallback()`].
///
/// # Parameters
///
/// - `frame_rate`: The intended frame rate of this window, in frames per second. `0` is a
/// special value that indicates the app will accept the system's choice for the display
/// frame rate, which is the default behavior if this function isn't called. The `frame_rate`
/// param does not need to be a valid refresh rate for this device's display - e.g., it's
/// fine to pass `30`fps to a device that can only run the display at `60`fps.
/// - `compatibility`: The frame rate compatibility of this window. The compatibility value may
/// influence the system's choice of display refresh rate. See the [`FrameRateCompatibility`]
/// values for more info. This parameter is ignored when `frame_rate` is `0`.
/// - `change_frame_rate_strategy`: Whether display refresh rate transitions caused by this
/// window should be seamless. A seamless transition is one that doesn't have any visual
/// interruptions, such as a black screen for a second or two. See the
/// [`ChangeFrameRateStrategy`] values. This parameter is ignored when `frame_rate` is `0`.
#[cfg(feature = "api-level-31")]
#[doc(alias = "ANativeWindow_setFrameRateWithChangeStrategy")]
pub fn set_frame_rate_with_change_strategy(
&self,
frame_rate: f32,
compatibility: FrameRateCompatibility,
change_frame_rate_strategy: ChangeFrameRateStrategy,
) -> io::Result<()> {
let compatibility = (compatibility as u32)
.try_into()
.expect("i8 overflow in FrameRateCompatibility");
let strategy = (change_frame_rate_strategy as u32)
.try_into()
.expect("i8 overflow in ChangeFrameRateStrategy");
let status = unsafe {
ffi::ANativeWindow_setFrameRateWithChangeStrategy(
self.ptr.as_ptr(),
frame_rate,
compatibility,
strategy,
)
};
status_to_io_result(status)
}
/// Provides a hint to the window that buffers should be preallocated ahead of time.
///
/// Note that the window implementation is not guaranteed to preallocate any buffers, for
/// instance if an implementation disallows allocation of new buffers, or if there is
/// insufficient memory in the system to preallocate additional buffers
#[cfg(feature = "api-level-30")]
pub fn try_allocate_buffers(&self) {
unsafe { ffi::ANativeWindow_tryAllocateBuffers(self.ptr.as_ptr()) }
}
/// Return the [`NativeWindow`] associated with a JNI [`android.view.Surface`] pointer.
///
/// # Safety
/// By calling this function, you assert that `env` is a valid pointer to a [`JNIEnv`] and
/// `surface` is a valid pointer to an [`android.view.Surface`].
///
/// [`android.view.Surface`]: https://developer.android.com/reference/android/view/Surface
pub unsafe fn from_surface(env: *mut JNIEnv, surface: jobject) -> Option<Self> {
let ptr = ffi::ANativeWindow_fromSurface(env, surface);
Some(Self::from_ptr(NonNull::new(ptr)?))
}
/// Return a JNI [`android.view.Surface`] pointer derived from this [`NativeWindow`].
///
/// # Safety
/// By calling this function, you assert that `env` is a valid pointer to a [`JNIEnv`].
///
/// [`android.view.Surface`]: https://developer.android.com/reference/android/view/Surface
#[cfg(feature = "api-level-26")]
pub unsafe fn to_surface(&self, env: *mut JNIEnv) -> jobject {
ffi::ANativeWindow_toSurface(env, self.ptr().as_ptr())
}
/// Lock the window's next drawing surface for writing.
///
/// Optionally pass the region you intend to draw into `dirty_bounds`. When this function
/// returns it is updated (commonly enlarged) with the actual area the caller needs to redraw.
pub fn lock(
&self,
dirty_bounds: Option<&mut Rect>,
) -> io::Result<NativeWindowBufferLockGuard<'_>> {
let dirty_bounds = match dirty_bounds {
Some(dirty_bounds) => dirty_bounds,
None => std::ptr::null_mut(),
};
let mut buffer = MaybeUninit::uninit();
let status = unsafe {
ffi::ANativeWindow_lock(self.ptr.as_ptr(), buffer.as_mut_ptr(), dirty_bounds)
};
status_to_io_result(status)?;
Ok(NativeWindowBufferLockGuard {
window: self,
buffer: unsafe { buffer.assume_init() },
})
}
}
/// Lock holding the next drawing surface for writing. It is unlocked and posted on [`drop()`].
#[derive(Debug)]
pub struct NativeWindowBufferLockGuard<'a> {
window: &'a NativeWindow,
buffer: ffi::ANativeWindow_Buffer,
}
impl<'a> NativeWindowBufferLockGuard<'a> {
/// The number of pixels that are shown horizontally.
pub fn width(&self) -> usize {
usize::try_from(self.buffer.width).unwrap()
}
// The number of pixels that are shown vertically.
pub fn height(&self) -> usize {
usize::try_from(self.buffer.height).unwrap()
}
/// The number of _pixels_ that a line in the buffer takes in memory.
///
/// This may be `>= width`.
pub fn stride(&self) -> usize {
usize::try_from(self.buffer.stride).unwrap()
}
/// The format of the buffer. One of [`HardwareBufferFormat`].
pub fn format(&self) -> HardwareBufferFormat {
let format = u32::try_from(self.buffer.format).unwrap();
format.into()
}
/// The actual bits.
///
/// This points to a memory segment of [`stride()`][Self::stride()] *
/// [`height()`][Self::height()] * [`HardwareBufferFormat::bytes_per_pixel()`] bytes.
///
/// Only [`width()`][Self::width()] pixels are visible for each [`stride()`][Self::stride()]
/// line of pixels in the buffer.
///
/// See [`bytes()`][Self::bytes()] for safe access to these bytes.
pub fn bits(&mut self) -> *mut c_void {
self.buffer.bits
}
/// Safe write access to likely uninitialized pixel buffer data.
///
/// Returns [`None`] when there is no [`HardwareBufferFormat::bytes_per_pixel()`] size
/// available for this [`format()`][Self::format()].
///
/// The returned slice consists of [`stride()`][Self::stride()] * [`height()`][Self::height()]
/// \* [`HardwareBufferFormat::bytes_per_pixel()`] bytes.
///
/// Only [`width()`][Self::width()] pixels are visible for each [`stride()`][Self::stride()]
/// line of pixels in the buffer.
pub fn bytes(&mut self) -> Option<&mut [MaybeUninit<u8>]> {
let num_pixels = self.stride() * self.height();
let num_bytes = num_pixels * self.format().bytes_per_pixel()?;
Some(unsafe { std::slice::from_raw_parts_mut(self.bits().cast(), num_bytes) })
}
/// Returns a slice of bytes for each line of visible pixels in the buffer, ignoring any
/// padding pixels incurred by the stride.
///
/// See [`bits()`][Self::bits()] and [`bytes()`][Self::bytes()] for contiguous access to the
/// underlying buffer.
pub fn lines(&mut self) -> Option<impl Iterator<Item = &mut [MaybeUninit<u8>]>> {
let bpp = self.format().bytes_per_pixel()?;
let scanline_bytes = bpp * self.stride();
let width_bytes = bpp * self.width();
let bytes = self.bytes()?;
Some(
bytes
.chunks_exact_mut(scanline_bytes)
.map(move |scanline| &mut scanline[..width_bytes]),
)
}
}
impl<'a> Drop for NativeWindowBufferLockGuard<'a> {
fn drop(&mut self) {
let ret = unsafe { ffi::ANativeWindow_unlockAndPost(self.window.ptr.as_ptr()) };
assert_eq!(ret, 0);
}
}
#[cfg(feature = "api-level-26")]
bitflags::bitflags! {
/// Transforms that can be applied to buffers as they are displayed to a window.
///
/// Supported transforms are any combination of horizontal mirror, vertical mirror, and
/// clockwise 90 degree rotation, in that order. Rotations of 180 and 270 degrees are made up
/// of those basic transforms.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
#[doc(alias = "ANativeWindowTransform")]
pub struct NativeWindowTransform : u32 {
#[doc(alias = "ANATIVEWINDOW_TRANSFORM_IDENTITY")]
const TRANSFORM_IDENTITY = ffi::ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_IDENTITY.0;
#[doc(alias = "ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL")]
const TRANSFORM_MIRROR_HORIZONTAL = ffi::ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_MIRROR_HORIZONTAL.0;
#[doc(alias = "ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL")]
const TRANSFORM_MIRROR_VERTICAL = ffi::ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_MIRROR_VERTICAL.0;
#[doc(alias = "ANATIVEWINDOW_TRANSFORM_ROTATE_90")]
const TRANSFORM_ROTATE_90 = ffi::ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_90.0;
#[doc(alias = "ANATIVEWINDOW_TRANSFORM_ROTATE_180")]
const TRANSFORM_ROTATE_180 = ffi::ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_180.0;
#[doc(alias = "ANATIVEWINDOW_TRANSFORM_ROTATE_270")]
const TRANSFORM_ROTATE_270 = ffi::ANativeWindowTransform::ANATIVEWINDOW_TRANSFORM_ROTATE_270.0;
}
}
#[cfg(feature = "api-level-28")]
#[derive(Debug, Error)]
pub enum GetDataSpaceError {
#[error(transparent)]
IoError(#[from] io::Error),
#[error(transparent)]
TryFromPrimitiveError(#[from] TryFromPrimitiveError<DataSpace>),
}
/// Compatibility value for [`NativeWindow::set_frame_rate()`]
#[cfg_attr(
feature = "api-level-31",
doc = " and [`NativeWindow::set_frame_rate_with_change_strategy()`]"
)]
/// .
#[cfg(feature = "api-level-30")]
#[repr(u32)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
#[doc(alias = "ANativeWindow_FrameRateCompatibility")]
#[non_exhaustive]
pub enum FrameRateCompatibility {
/// There are no inherent restrictions on the frame rate of this window.
///
/// When the system selects a frame rate other than what the app requested, the app will be
/// able to run at the system frame rate without requiring pull down. This value should be used
/// when displaying game content, UIs, and anything that isn't video.
#[doc(alias = "ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT")]
Default =
ffi::ANativeWindow_FrameRateCompatibility::ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT.0,
/// This window is being used to display content with an inherently fixed frame rate, e.g. a
/// video that has a specific frame rate.
///
/// When the system selects a frame rate other than what the app requested, the app will need
/// to do pull down or use some other technique to adapt to the system's frame rate. The user
/// experience is likely to be worse (e.g. more frame stuttering) than it would be if the
/// system had chosen the app's requested frame rate. This value should be used for video
/// content.
#[doc(alias = "ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE")]
FixedSource = ffi::ANativeWindow_FrameRateCompatibility::ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE.0,
}
/// Change frame rate strategy value for [`NativeWindow::set_frame_rate_with_change_strategy()`].
#[cfg(feature = "api-level-31")]
#[repr(u32)]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
#[doc(alias = "ANativeWindow_ChangeFrameRateStrategy")]
#[non_exhaustive]
pub enum ChangeFrameRateStrategy {
/// Change the frame rate only if the transition is going to be seamless.
#[doc(alias = "ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS")]
OnlyIfSeamless = ffi::ANativeWindow_ChangeFrameRateStrategy::ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS.0,
/// Change the frame rate even if the transition is going to be non-seamless, i.e. with visual interruptions for the user.
#[doc(alias = "ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS")]
Always = ffi::ANativeWindow_ChangeFrameRateStrategy::ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS.0,
}

153
vendor/ndk-0.8.0/src/shared_memory.rs vendored Normal file
View File

@@ -0,0 +1,153 @@
//! Bindings for [`ASharedMemory`]
//!
//! [`ASharedMemory`]: https://developer.android.com/ndk/reference/group/memory
#![cfg(feature = "api-level-26")]
use std::{
ffi::CStr,
io::{Error, Result},
// TODO: Import from std::os::fd::{} since Rust 1.66
os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd},
ptr,
};
#[cfg(feature = "api-level-27")]
use jni_sys::{jobject, JNIEnv};
/// Enables the creation, mapping, and protection control over anonymous shared memory.
#[derive(Debug)]
#[doc(alias = "ASharedMemory")]
pub struct SharedMemory(OwnedFd);
impl SharedMemory {
/// Create a shared memory region.
///
/// Creates shared memory region and returns a file descriptor. The resulting file descriptor
/// can be `mmap`'ed to process memory space with `PROT_READ | PROT_WRITE | PROT_EXEC`. Access
/// to this shared memory region can be restricted with [`set_prot()`][Self::set_prot()].
///
/// Use [`android.os.ParcelFileDescriptor`] to pass the file descriptor to another process.
/// File descriptors may also be sent to other processes over a Unix domain socket with
/// `sendmsg` and `SCM_RIGHTS`. See `sendmsg(3)` and `cmsg(3)` man pages for more information.
///
/// If you intend to share this file descriptor with a child process after calling `exec(3)`,
/// note that you will need to use `fcntl(2)` with `F_SETFD` to clear the `FD_CLOEXEC` flag for
/// this to work on all versions of Android.
///
/// [`android.os.ParcelFileDescriptor`]: https://developer.android.com/reference/android/os/ParcelFileDescriptor
#[doc(alias = "ASharedMemory_create")]
pub fn create(name: Option<&CStr>, size: usize) -> Result<Self> {
let fd =
unsafe { ffi::ASharedMemory_create(name.map_or(ptr::null(), |p| p.as_ptr()), size) };
if fd < 0 {
Err(Error::last_os_error())
} else {
Ok(unsafe { Self::from_raw_fd(fd) })
}
}
/// Returns a `dup`'d FD from the given Java [`android.os.SharedMemory`] object.
///
/// The returned file descriptor has all the same properties & capabilities as the FD returned
/// from [`create()`][Self::create()], however the protection flags will be the same as those
/// of the [`android.os.SharedMemory`] object.
///
/// [`android.os.SharedMemory`]: https://developer.android.com/reference/android/os/SharedMemory
#[cfg(feature = "api-level-27")]
#[doc(alias = "ASharedMemory_dupFromJava")]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn dup_from_java(env: *mut JNIEnv, shared_memory: jobject) -> Result<Self> {
let fd = unsafe { ffi::ASharedMemory_dupFromJava(env, shared_memory) };
if fd < 0 {
Err(Error::last_os_error())
} else {
Ok(unsafe { Self::from_raw_fd(fd) })
}
}
/// Get the size of the shared memory region.
#[doc(alias = "ASharedMemory_getSize")]
pub fn size(&self) -> usize {
unsafe { ffi::ASharedMemory_getSize(self.as_raw_fd()) }
}
/// Restrict access of shared memory region.
///
/// This function restricts access of a shared memory region. Access can only be removed. The
/// effect applies globally to all file descriptors in all processes across the system that
/// refer to this shared memory region. Existing memory mapped regions are not affected.
///
/// It is a common use case to create a shared memory region, map it read/write locally to
/// initialize content, and then send the shared memory to another process with read only
/// access. Code example as below:
///
/// ```no_run
/// # use ndk::shared_memory::SharedMemory;
/// # // TODO: Import from std::os::fd::{} since Rust 1.66
/// # use std::os::unix::io::AsRawFd;
/// # use std::ffi::CStr;
/// # unsafe {
/// let mem = SharedMemory::create(Some(CStr::from_bytes_with_nul_unchecked(b"memory\0")), 127).unwrap();
/// // By default it has PROT_READ | PROT_WRITE | PROT_EXEC.
/// let size = mem.size();
/// let buffer = libc::mmap(
/// std::ptr::null_mut(),
/// size,
/// libc::PROT_READ | libc::PROT_WRITE,
/// libc::MAP_SHARED,
/// mem.as_raw_fd(),
/// 0,
/// );
/// let buffer_slice = std::slice::from_raw_parts_mut(buffer.cast(), size);
///
/// // trivially initialize content
/// buffer_slice[..7].copy_from_slice(b"hello!\0");
///
/// // Existing mappings will retain their protection flags (PROT_WRITE here) after set_prod()
/// // unless it is unmapped:
/// libc::munmap(buffer, size);
///
/// // limit access to read only
/// mem.set_prot(libc::PROT_READ);
///
/// // share fd with another process here and the other process can only map with PROT_READ.
/// # }
/// ```
#[doc(alias = "ASharedMemory_setProt")]
pub fn set_prot(&self, prot: i32) -> Result<()> {
let status = unsafe { ffi::ASharedMemory_setProt(self.as_raw_fd(), prot) };
if status < 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
impl AsFd for SharedMemory {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl AsRawFd for SharedMemory {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl IntoRawFd for SharedMemory {
fn into_raw_fd(self) -> RawFd {
self.0.into_raw_fd()
}
}
impl FromRawFd for SharedMemory {
/// # Safety
///
/// The resource pointed to by `fd` must be open and suitable for assuming
/// ownership. The resource must not require any cleanup other than `close`.
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self(OwnedFd::from_raw_fd(fd))
}
}

159
vendor/ndk-0.8.0/src/surface_texture.rs vendored Normal file
View File

@@ -0,0 +1,159 @@
//! Bindings for [`ASurfaceTexture`]
//!
//! See <https://source.android.com/devices/graphics/arch-st> for an architectural overview of
//! [`SurfaceTexture`] internals.
//!
//! [`ASurfaceTexture`]: https://developer.android.com/ndk/reference/group/surface-texture
#![cfg(feature = "api-level-28")]
use crate::{native_window::NativeWindow, utils::status_to_io_result};
use jni_sys::{jobject, JNIEnv};
use std::{io::Result, ptr::NonNull, time::Duration};
/// An opaque type to manage [`android.graphics.SurfaceTexture`] from native code
///
/// [`android.graphics.SurfaceTexture`]: https://developer.android.com/reference/android/graphics/SurfaceTexture
#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SurfaceTexture {
ptr: NonNull<ffi::ASurfaceTexture>,
}
unsafe impl Send for SurfaceTexture {}
impl Drop for SurfaceTexture {
fn drop(&mut self) {
unsafe { ffi::ASurfaceTexture_release(self.ptr.as_ptr()) }
}
}
impl SurfaceTexture {
/// Assumes ownership of `ptr`
///
/// # Safety
/// `ptr` must be a valid pointer to an Android [`ffi::ASurfaceTexture`].
pub unsafe fn from_ptr(ptr: NonNull<ffi::ASurfaceTexture>) -> Self {
Self { ptr }
}
/// Get a reference to the native [`SurfaceTexture`] from the corresponding Java object.
///
/// # Safety
///
/// This function should be called with a healthy JVM pointer and with a non-null
/// [`android.graphics.SurfaceTexture`], which must be kept alive on the Java/Kotlin side.
///
/// The caller must keep a reference to the Java [`android.graphics.SurfaceTexture`] during the
/// lifetime of the returned [`SurfaceTexture`]. Failing to do so could result in the
/// [`SurfaceTexture`] to stop functioning properly once the Java object gets finalized.
/// However, this will not result in program termination.
///
/// [`android.graphics.SurfaceTexture`]: https://developer.android.com/reference/android/graphics/SurfaceTexture
pub unsafe fn from_surface_texture(env: *mut JNIEnv, surface_texture: jobject) -> Option<Self> {
let a_surface_texture_ptr = ffi::ASurfaceTexture_fromSurfaceTexture(env, surface_texture);
let s = NonNull::new(a_surface_texture_ptr)?;
Some(SurfaceTexture::from_ptr(s))
}
/// Returns a pointer to the native [`ffi::ASurfaceTexture`].
pub fn ptr(&self) -> NonNull<ffi::ASurfaceTexture> {
self.ptr
}
/// Returns a reference to a [`NativeWindow`] (i.e. the Producer) for this [`SurfaceTexture`].
///
/// This is equivalent to Java's:
/// ```java
/// Surface sur = new Surface(surfaceTexture);
/// ```
pub fn acquire_native_window(&self) -> Option<NativeWindow> {
let native_window = unsafe { ffi::ASurfaceTexture_acquireANativeWindow(self.ptr.as_ptr()) };
let n = NonNull::new(native_window)?;
Some(unsafe { NativeWindow::from_ptr(n) })
}
/// Attach the [`SurfaceTexture`] to the OpenGL ES context that is current on the calling
/// thread.
///
/// A new OpenGL ES texture object is created and populated with the [`SurfaceTexture`] image
/// frame that was current at the time of the last call to
/// [`detach_from_gl_context()`][Self::detach_from_gl_context()]. This new texture is bound to
/// the `GL_TEXTURE_EXTERNAL_OES` texture target.
///
/// This can be used to access the [`SurfaceTexture`] image contents from multiple OpenGL ES
/// contexts. Note, however, that the image contents are only accessible from one OpenGL ES
/// context at a time.
pub fn attach_to_gl_context(&self, tex_name: u32) -> Result<()> {
let status = unsafe { ffi::ASurfaceTexture_attachToGLContext(self.ptr.as_ptr(), tex_name) };
status_to_io_result(status)
}
/// Detach the [`SurfaceTexture`] from the OpenGL ES context that owns the OpenGL ES texture
/// object.
///
/// This call must be made with the OpenGL ES context current on the calling thread. The OpenGL
/// ES texture object will be deleted as a result of this call. After calling this method all
/// calls to [`update_tex_image()`][Self::update_tex_image()] will fail until a successful call
/// to [`attach_to_gl_context()`][Self::attach_to_gl_context()] is made.
///
/// This can be used to access the [`SurfaceTexture`] image contents from multiple OpenGL ES
/// contexts. Note, however, that the image contents are only accessible from one OpenGL ES
/// context at a time.
pub fn detach_from_gl_context(&self) -> Result<()> {
let status = unsafe { ffi::ASurfaceTexture_detachFromGLContext(self.ptr.as_ptr()) };
status_to_io_result(status)
}
/// Retrieve the 4x4 texture coordinate transform matrix associated with the texture image set
/// by the most recent call to [`update_tex_image()`][Self::update_tex_image()].
///
/// This transform matrix maps 2D homogeneous texture coordinates of the form `(s, t, 0, 1)`
/// with `s` and `t` in the inclusive range `[0, 1]` to the texture coordinate that should be
/// used to sample that location from the texture. Sampling the texture outside of the range of
/// this transform is undefined.
///
/// The matrix is stored in column-major order so that it may be passed directly to OpenGL ES
/// via the [`glLoadMatrixf()`] or [`glUniformMatrix4fv()`] functions.
///
/// [`glLoadMatrixf()`]: https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glLoadMatrix.xml
/// [`gluniformmatrix4fv()`]: https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glUniform.xhtml
pub fn transform_matrix(&self) -> [f32; 16] {
let mut r = [0f32; 16];
unsafe { ffi::ASurfaceTexture_getTransformMatrix(self.ptr.as_ptr(), r.as_mut_ptr()) };
r
}
/// Retrieve the timestamp associated with the texture image set by the most recent call to
/// [`update_tex_image()`][Self::update_tex_image()].
///
/// This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
/// should be unaffected by time-of-day adjustments, and for a camera should be strictly
/// monotonic but for a [`MediaPlayer`] may be reset when the position is set. The specific
/// meaning and zero point of the timestamp depends on the source providing images to the
/// [`SurfaceTexture`]. Unless otherwise specified by the image source, timestamps cannot
/// generally be compared across [`SurfaceTexture`] instances, or across multiple program
/// invocations. It is mostly useful for determining time offsets between subsequent frames.
///
/// For EGL/Vulkan producers, this timestamp is the desired present time set with the
/// [`EGL_ANDROID_presentation_time`] or [`VK_GOOGLE_display_timing`] extensions.
///
/// [`MediaPlayer`]: https://developer.android.com/reference/android/media/MediaPlayer
/// [`EGL_ANDROID_presentation_time`]: https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_presentation_time.txt
/// [`VK_GOOGLE_display_timing`]: https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_GOOGLE_display_timing.html
pub fn timestamp(&self) -> Duration {
Duration::from_nanos(
unsafe { ffi::ASurfaceTexture_getTimestamp(self.ptr.as_ptr()) }
.try_into()
.unwrap(),
)
}
/// Update the texture image to the most recent frame from the image stream.
///
/// This may only be called while the OpenGL ES context that owns the texture is current on the
/// calling thread. It will implicitly bind its texture to the `GL_TEXTURE_EXTERNAL_OES`
/// texture target.
pub fn update_tex_image(&self) -> Result<()> {
let status = unsafe { ffi::ASurfaceTexture_updateTexImage(self.ptr.as_ptr()) };
status_to_io_result(status)
}
}

143
vendor/ndk-0.8.0/src/sync.rs vendored Normal file
View File

@@ -0,0 +1,143 @@
//! Bindings for [sync functions]
//!
//! [sync functions]: https://developer.android.com/ndk/reference/group/sync
#![cfg(feature = "sync")]
use std::{
ffi::CStr,
fmt::Debug,
// TODO: Import from std::os::fd::{} since Rust 1.66
os::unix::io::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd},
ptr::NonNull,
};
#[doc(alias = "sync_file_info")]
#[repr(transparent)]
pub struct SyncFileInfo {
inner: NonNull<ffi::sync_file_info>,
}
impl Debug for SyncFileInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SyncFileInfo")
.field("name", &self.name())
.field("status", &self.status())
.field("flags", &self.flags())
.field("num_fences", &self.num_fences())
.field("fence_info", &self.fence_info())
.finish()
}
}
impl SyncFileInfo {
/// Retrieve detailed information about a sync file and its fences.
#[doc(alias = "sync_file_info")]
pub fn new(fd: BorrowedFd<'_>) -> Option<Self> {
let inner = NonNull::new(unsafe { ffi::sync_file_info(fd.as_raw_fd()) })?;
Some(Self { inner })
}
pub fn name(&self) -> &CStr {
let inner = unsafe { self.inner.as_ref() };
// TODO: Switch to CStr::from_bytes_until_nul (with c_char -> u8 transmute) since MSRV 1.69
// https://github.com/ash-rs/ash/pull/746
unsafe { CStr::from_ptr(inner.name.as_ptr()) }
}
pub fn status(&self) -> i32 {
let inner = unsafe { self.inner.as_ref() };
inner.status
}
pub fn flags(&self) -> u32 {
let inner = unsafe { self.inner.as_ref() };
inner.flags
}
pub fn num_fences(&self) -> usize {
let inner = unsafe { self.inner.as_ref() };
inner.num_fences as usize
}
/// Get the array of fence infos from the sync file's info.
#[doc(alias = "sync_get_fence_info")]
pub fn fence_info(&self) -> &[SyncFenceInfo] {
let inner = unsafe { self.inner.as_ref() };
if inner.num_fences == 0 {
&[]
} else {
let sync_fence_info = NonNull::new(inner.sync_fence_info as *mut _)
.expect("sync_fence_info cannot be null if num_fences > 0");
unsafe {
std::slice::from_raw_parts(sync_fence_info.as_ptr(), inner.num_fences as usize)
}
}
}
}
impl Drop for SyncFileInfo {
/// Free a [`struct@ffi::sync_file_info`] structure.
#[doc(alias = "sync_file_info_free")]
fn drop(&mut self) {
unsafe { ffi::sync_file_info_free(self.inner.as_ptr()) }
}
}
#[doc(alias = "sync_fence_info")]
#[repr(transparent)]
pub struct SyncFenceInfo(ffi::sync_fence_info);
impl Debug for SyncFenceInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SyncFenceInfo")
.field("obj_name", &self.obj_name())
.field("driver_name", &self.driver_name())
.field("status", &self.status())
.field("flags", &self.flags())
.field("timestamp_ns", &self.timestamp_ns())
.finish()
}
}
impl SyncFenceInfo {
pub fn obj_name(&self) -> &CStr {
// TODO: Switch to CStr::from_bytes_until_nul (with c_char -> u8 transmute) since MSRV 1.69
unsafe { CStr::from_ptr(self.0.obj_name.as_ptr()) }
}
pub fn driver_name(&self) -> &CStr {
// TODO: Switch to CStr::from_bytes_until_nul (with c_char -> u8 transmute) since MSRV 1.69
unsafe { CStr::from_ptr(self.0.driver_name.as_ptr()) }
}
pub fn status(&self) -> i32 {
self.0.status
}
pub fn flags(&self) -> u32 {
self.0.flags
}
pub fn timestamp_ns(&self) -> u64 {
self.0.timestamp_ns
}
}
/// Merge two sync files.
///
/// This produces a new sync file with the given name which has the union of the two original sync
/// file's fences; redundant fences may be removed.
///
/// If one of the input sync files is signaled or invalid, then this function may behave like
/// `dup()`: the new file descriptor refers to the valid/unsignaled sync file with its original
/// name, rather than a new sync file.
pub fn sync_merge(name: &CStr, fd1: BorrowedFd<'_>, fd2: BorrowedFd<'_>) -> OwnedFd {
unsafe {
OwnedFd::from_raw_fd(ffi::sync_merge(
name.as_ptr(),
fd1.as_raw_fd(),
fd2.as_raw_fd(),
))
}
}

92
vendor/ndk-0.8.0/src/trace.rs vendored Normal file
View File

@@ -0,0 +1,92 @@
//! Bindings for the NDK tracing API.
//!
//! See also [the NDK docs](https://developer.android.com/ndk/reference/group/tracing)
#![cfg(feature = "api-level-23")]
use std::ffi::{CString, NulError};
use std::marker::PhantomData;
pub fn is_trace_enabled() -> bool {
unsafe { ffi::ATrace_isEnabled() }
}
#[derive(Debug)]
pub struct Section {
// Section is !Sync and !Send
_pd: PhantomData<*mut ()>,
}
impl Section {
pub fn new(name: &str) -> Result<Self, NulError> {
let section_name = CString::new(name)?;
unsafe { ffi::ATrace_beginSection(section_name.as_ptr()) };
Ok(Self { _pd: PhantomData })
}
pub fn end(self) {
drop(self)
}
}
impl Drop for Section {
fn drop(&mut self) {
unsafe { ffi::ATrace_endSection() };
}
}
/// Unique identifier for distinguishing simultaneous events
#[derive(Debug)]
#[cfg(feature = "api-level-29")]
pub struct Cookie(pub i32);
#[derive(Debug)]
#[cfg(feature = "api-level-29")]
pub struct AsyncSection {
section_name: CString,
cookie: Cookie,
// AsyncSection is !Sync
_pd: PhantomData<&'static ()>,
}
#[cfg(feature = "api-level-29")]
impl AsyncSection {
pub fn new(name: &str, cookie: Cookie) -> Result<Self, NulError> {
let section_name = CString::new(name)?;
unsafe { ffi::ATrace_beginAsyncSection(section_name.as_ptr(), cookie.0) };
Ok(Self {
section_name,
cookie,
_pd: PhantomData,
})
}
pub fn end(self) {
drop(self)
}
}
#[cfg(feature = "api-level-29")]
impl Drop for AsyncSection {
fn drop(&mut self) {
unsafe { ffi::ATrace_endAsyncSection(self.section_name.as_ptr(), self.cookie.0) };
}
}
#[cfg(feature = "api-level-29")]
#[derive(Debug)]
pub struct Counter {
name: CString,
}
#[cfg(feature = "api-level-29")]
impl Counter {
pub fn new(name: &str) -> Result<Self, NulError> {
let name = CString::new(name)?;
Ok(Self { name })
}
pub fn set_value(&self, value: i64) {
unsafe { ffi::ATrace_setCounter(self.name.as_ptr(), value) }
}
}

70
vendor/ndk-0.8.0/src/utils.rs vendored Normal file
View File

@@ -0,0 +1,70 @@
//! Internal utilities
use log::{error, log_enabled, Level};
use std::ffi::{c_int, CStr, CString};
use std::io::{Error, Result};
/// Turns standard `<errno.h>` status codes - typically rewrapped by Android's [`Errors.h`] - into
/// Rust's [`std::io::Error`].
///
/// [`Errors.h`]: https://cs.android.com/android/platform/superproject/+/master:system/core/libutils/include/utils/Errors.h
pub(crate) fn status_to_io_result(status: i32) -> Result<()> {
match status {
0 => Ok(()),
r if r < 0 => Err(Error::from_raw_os_error(-r)),
r => unreachable!("Status is positive integer {}", r),
}
}
pub(crate) fn android_log(level: Level, tag: &CStr, msg: &CStr) {
let prio = match level {
Level::Error => ffi::android_LogPriority::ANDROID_LOG_ERROR,
Level::Warn => ffi::android_LogPriority::ANDROID_LOG_WARN,
Level::Info => ffi::android_LogPriority::ANDROID_LOG_INFO,
Level::Debug => ffi::android_LogPriority::ANDROID_LOG_DEBUG,
Level::Trace => ffi::android_LogPriority::ANDROID_LOG_VERBOSE,
};
unsafe {
ffi::__android_log_write(prio.0 as c_int, tag.as_ptr(), msg.as_ptr());
}
}
pub(crate) fn log_panic(panic: Box<dyn std::any::Any + Send>) {
fn log_panic(panic_str: &str) {
const RUST_PANIC_TAG: &CStr =
unsafe { CStr::from_bytes_with_nul_unchecked(b"RustPanic\0") };
let panic_str = CString::new(panic_str).unwrap_or_default();
// Use the Rust logger if installed and enabled, otherwise fall back to the Android system
// logger so there is at least some record of the panic
if log_enabled!(Level::Error) {
error!("RustPanic: {}", panic_str.to_string_lossy());
log::logger().flush();
} else {
android_log(Level::Error, RUST_PANIC_TAG, &panic_str);
}
}
match panic.downcast::<String>() {
Ok(panic_string) => log_panic(&panic_string),
Err(panic) => match panic.downcast::<&str>() {
Ok(panic_str) => log_panic(&panic_str),
Err(_) => log_panic("Unknown panic message type"),
},
}
}
/// Run a closure and abort the program if it panics.
///
/// This is generally used to ensure Rust callbacks won't unwind past the FFI boundary, which leads
/// to undefined behaviour.
pub(crate) fn abort_on_panic<R>(f: impl FnOnce() -> R) -> R {
std::panic::catch_unwind(std::panic::AssertUnwindSafe(f)).unwrap_or_else(|panic| {
// Try logging the panic before aborting
//
// Just in case our attempt to log a panic could itself cause a panic we use a
// second catch_unwind here.
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| log_panic(panic)));
std::process::abort();
})
}