Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ndk/bitmap: Provide detailed implementation for AndroidBitmapInfoFlags #424

Merged
merged 1 commit into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ndk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
118 changes: 110 additions & 8 deletions ndk/src/bitmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand All @@ -43,16 +46,23 @@ fn construct<T>(with_ptr: impl FnOnce(*mut T) -> i32) -> BitmapResult<T> {
}

#[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,
}

Expand All @@ -77,26 +87,42 @@ 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<AndroidBitmapInfo> {
let inner =
construct(|res| unsafe { ffi::AndroidBitmap_getInfo(self.env, self.inner, res) })?;

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<HardwareBufferRef> {
unsafe {
let result =
Expand All @@ -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
}
Expand All @@ -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)
}
}
Loading