diff --git a/ndk/CHANGELOG.md b/ndk/CHANGELOG.md index 29ae0498..211964f9 100644 --- a/ndk/CHANGELOG.md +++ b/ndk/CHANGELOG.md @@ -24,6 +24,7 @@ - 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-beta0-2023-08-15). (#420) +- **Breaking:** bitmap: Provide detailed implementation for `AndroidBitmapInfoFlags`. (#424) - hardware_buffer: Add `id()` to retrieve a system-wide unique identifier for a `HardwareBuffer`. (#428) # 0.7.0 (2022-07-24) diff --git a/ndk/src/bitmap.rs b/ndk/src/bitmap.rs index f9b58c8d..1b7b23bf 100644 --- a/ndk/src/bitmap.rs +++ b/ndk/src/bitmap.rs @@ -14,11 +14,14 @@ use std::mem::MaybeUninit; use crate::hardware_buffer::HardwareBufferRef; #[repr(i32)] -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum BitmapError { Unknown, + #[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, } @@ -43,16 +46,23 @@ fn construct(with_ptr: impl FnOnce(*mut T) -> i32) -> BitmapResult { } #[repr(u32)] -#[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)] #[allow(non_camel_case_types)] 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, } @@ -77,6 +87,8 @@ impl AndroidBitmap { Self { env, inner: bitmap } } + /// Fills out and returns the [`AndroidBitmapInfo`] struct for the given Java bitmap object. + #[doc(alias = "AndroidBitmap_getInfo")] pub fn get_info(&self) -> BitmapResult { let inner = construct(|res| unsafe { ffi::AndroidBitmap_getInfo(self.env, self.inner, res) })?; @@ -84,19 +96,33 @@ impl AndroidBitmap { Ok(AndroidBitmapInfo { inner }) } + /// Attempt to lock the pixel address. + /// + /// Locking will ensure that the memory for the pixels will not move until the + /// [`AndroidBitmap::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 [`AndroidBitmap::unlock_pixels()`], + /// after which time the address of the pixels should no longer be used. + #[doc(alias = "AndroidBitmap_lockPixels")] pub fn lock_pixels(&self) -> BitmapResult<*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 [`AndroidBitmap::lock_pixels()`]. + #[doc(alias = "AndroidBitmap_unlockPixels")] pub fn unlock_pixels(&self) -> BitmapResult<()> { let status = unsafe { ffi::AndroidBitmap_unlockPixels(self.env, self.inner) }; BitmapError::from_status(status) } - /// Retrieve the native object associated with a `HARDWARE` [`AndroidBitmap`]. + /// Retrieve the native object associated with an [`ffi::ANDROID_BITMAP_FLAGS_IS_HARDWARE`] + /// [`AndroidBitmap`] (requires [`AndroidBitmapInfoFlags::is_hardware()`] on + /// [`AndroidBitmapInfo::flags()`] to return [`true`]). /// /// Client must not modify it while an [`AndroidBitmap`] is wrapping it. #[cfg(feature = "api-level-30")] + #[doc(alias = "AndroidBitmap_getHardwareBuffer")] pub fn get_hardware_buffer(&self) -> BitmapResult { unsafe { let result = @@ -111,27 +137,102 @@ impl AndroidBitmap { } } +/// Possible values for [`ffi::ANDROID_BITMAP_FLAGS_ALPHA_MASK`] within [`AndroidBitmapInfoFlags`] +#[cfg(feature = "api-level-30")] +#[derive(Clone, Copy, Debug)] +pub enum AndroidBitmapInfoFlagsAlpha { + /// 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")] +#[derive(Clone, Copy, Hash, PartialEq, Eq)] +#[repr(transparent)] +pub struct AndroidBitmapInfoFlags(u32); + +#[cfg(feature = "api-level-30")] +impl std::fmt::Debug for AndroidBitmapInfoFlags { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "AndroidBitmapInfoFlags({:#x}, alpha: {:?}, is_hardware: {})", + self.0, + self.alpha(), + self.is_hardware() + ) + } +} + +#[cfg(feature = "api-level-30")] +impl AndroidBitmapInfoFlags { + /// 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) -> AndroidBitmapInfoFlagsAlpha { + // 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 => AndroidBitmapInfoFlagsAlpha::Premultiplied, + ffi::ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE => AndroidBitmapInfoFlagsAlpha::Opaque, + ffi::ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL => { + AndroidBitmapInfoFlagsAlpha::Unpremultiplied + } + 3 => todo!("ALPHA_MASK value 3"), + _ => unreachable!(), + } + } + + /// Returns [`true`] when [`ffi::ANDROID_BITMAP_FLAGS_IS_HARDWARE`] is set, meaning this + /// [`AndroidBitmap`] uses "HARDWARE Config" and its [`HardwareBufferRef`] can be retrieved via + /// [`AndroidBitmap::get_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(Copy, Clone, Debug)] +#[derive(Clone, Copy)] pub struct AndroidBitmapInfo { inner: ffi::AndroidBitmapInfo, } -// TODO: flesh out when API 30 is released -#[cfg(feature = "api-level-30")] -pub type AndroidBitmapInfoFlags = u32; +impl std::fmt::Debug for AndroidBitmapInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut f = f.debug_struct("AndroidBitmapInfo"); + 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 AndroidBitmapInfo { + /// 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 } @@ -155,8 +256,9 @@ impl AndroidBitmapInfo { format.try_into() } + /// Bitfield containing information about the bitmap. #[cfg(feature = "api-level-30")] pub fn flags(&self) -> AndroidBitmapInfoFlags { - self.inner.flags + AndroidBitmapInfoFlags(self.inner.flags) } }