// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Fernando Sahmkow
// AUTOGENERATED by tools/codegen/emit_rust.py — do not edit.
// Regenerate via: python -m tools.codegen.codegen textures --backend rust
#![allow(clippy::too_many_arguments)]
// Which of these a module needs depends on its shapes; the modules that
// have no span accessors would otherwise trip the unused-import lint.
#[allow(unused_imports)]
use crate::support::{BorrowedSlice, Bytes};
/// GPU pixel / block-compression format.
///
/// Uncompressed formats store one pixel per "block"; BCn formats store a 4×4 pixel tile per block.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PixelFormat {
/// 8-bit single channel (1 byte per pixel).
R8 = 0,
/// 16-bit single channel UNORM (2 bytes per pixel).
R16 = 1,
/// Single-precision single channel (4 bytes per pixel).
R32F = 2,
/// 8-bit dual channel (2 bytes per pixel).
RG8 = 3,
/// 16-bit dual channel UNORM (4 bytes per pixel).
RG16 = 4,
/// Single-precision dual channel (8 bytes per pixel).
RG32F = 5,
/// 8-bit RGBA (4 bytes per pixel).
RGBA8 = 6,
/// 16-bit RGBA UNORM (8 bytes per pixel).
RGBA16 = 7,
/// Single-precision RGBA (16 bytes per pixel).
RGBA32F = 8,
/// DXT1 – 8 bytes per 4×4 block (RGB + optional 1-bit alpha).
BC1 = 9,
/// DXT3 – 16 bytes per 4×4 block (explicit 4-bit alpha).
BC2 = 10,
/// DXT5 – 16 bytes per 4×4 block (interpolated alpha).
BC3 = 11,
/// Single-channel – 8 bytes per 4×4 block.
BC4 = 12,
/// Dual-channel – 16 bytes per 4×4 block.
BC5 = 13,
/// HDR RGB – 16 bytes per 4×4 block (half-float output).
BC6H = 14,
/// High-quality RGBA – 16 bytes per 4×4 block.
BC7 = 15,
}
impl TryFrom<i32> for PixelFormat {
type Error = crate::Error;
fn try_from(v: i32) -> Result<Self, crate::Error> {
match v {
0 => Ok(PixelFormat::R8),
1 => Ok(PixelFormat::R16),
2 => Ok(PixelFormat::R32F),
3 => Ok(PixelFormat::RG8),
4 => Ok(PixelFormat::RG16),
5 => Ok(PixelFormat::RG32F),
6 => Ok(PixelFormat::RGBA8),
7 => Ok(PixelFormat::RGBA16),
8 => Ok(PixelFormat::RGBA32F),
9 => Ok(PixelFormat::BC1),
10 => Ok(PixelFormat::BC2),
11 => Ok(PixelFormat::BC3),
12 => Ok(PixelFormat::BC4),
13 => Ok(PixelFormat::BC5),
14 => Ok(PixelFormat::BC6H),
15 => Ok(PixelFormat::BC7),
other => Err(crate::Error::UnknownEnum {
name: "PixelFormat",
value: other,
}),
}
}
}
/// Semantic role of a texture in a material.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TextureKind {
/// Unknown or application-specific usage.
Other = 0,
/// Diffuse / base colour (legacy).
Diffuse = 1,
/// Tangent-space normal map.
Normal = 2,
/// Specular intensity / colour.
Specular = 3,
/// ORM packed texture (R=AO, G=Roughness, B=Metalness, A=Unused).
ORM = 4,
/// PBR base colour (albedo).
Albedo = 5,
/// Roughness (single channel).
Roughness = 6,
/// Metalness (single channel).
Metalness = 7,
/// Ambient occlusion (single channel).
AmbientOcclusion = 8,
/// Gloss / smoothness (single channel).
Gloss = 9,
/// Emissive colour / intensity.
Emissive = 10,
/// Opacity / alpha mask (single channel, linear).
AlphaMask = 11,
/// Hard binary mask (0 or 1); alpha-coverage-preserving filter.
BinaryMask = 12,
/// Smooth transparency mask; alpha-coverage-preserving, continuous values.
TransparencyMask = 13,
/// Blend weight mask; alpha-coverage-preserving with soft transitions.
BlendMask = 14,
/// Lightmap or baked light contribution (HDR colour).
Lightmap = 15,
/// Environment / reflection map (equirectangular, GGX prefiltered).
EnvironmentPBR = 16,
/// Environment map (equirectangular, spherical Kaiser-filtered).
EnvironmentLegacy = 17,
/// Packed multi-channel texture where each channel carries a distinct semantic role. Use setChannelKind() / channelKind() to assign and query the per-channel kinds. generateMipmaps() will apply a kind-appropriate filter to every channel independently.
Multikind = 18,
/// Channel is not used and carries no semantic meaning. Only valid as a per-channel kind on a Multikind texture (set via setChannelKind()). generateMipmaps() applies a plain box filter to Unused channels.
Unused = 19,
}
impl TryFrom<i32> for TextureKind {
type Error = crate::Error;
fn try_from(v: i32) -> Result<Self, crate::Error> {
match v {
0 => Ok(TextureKind::Other),
1 => Ok(TextureKind::Diffuse),
2 => Ok(TextureKind::Normal),
3 => Ok(TextureKind::Specular),
4 => Ok(TextureKind::ORM),
5 => Ok(TextureKind::Albedo),
6 => Ok(TextureKind::Roughness),
7 => Ok(TextureKind::Metalness),
8 => Ok(TextureKind::AmbientOcclusion),
9 => Ok(TextureKind::Gloss),
10 => Ok(TextureKind::Emissive),
11 => Ok(TextureKind::AlphaMask),
12 => Ok(TextureKind::BinaryMask),
13 => Ok(TextureKind::TransparencyMask),
14 => Ok(TextureKind::BlendMask),
15 => Ok(TextureKind::Lightmap),
16 => Ok(TextureKind::EnvironmentPBR),
17 => Ok(TextureKind::EnvironmentLegacy),
18 => Ok(TextureKind::Multikind),
19 => Ok(TextureKind::Unused),
other => Err(crate::Error::UnknownEnum {
name: "TextureKind",
value: other,
}),
}
}
}
/// Dimensionality / topology of a texture resource.
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum TextureType {
/// Standard 2D image (1 layer).
Texture2D = 0,
/// Volume texture (depth > 1, depth halves each mip).
Texture3D = 1,
/// Cube map (6 square layers, one per face).
TextureCube = 2,
/// Array of 2D images (arraySize layers).
Texture2DArray = 3,
/// Array of cube maps (6 × arraySize layers).
TextureCubeArray = 4,
}
impl TryFrom<i32> for TextureType {
type Error = crate::Error;
fn try_from(v: i32) -> Result<Self, crate::Error> {
match v {
0 => Ok(TextureType::Texture2D),
1 => Ok(TextureType::Texture3D),
2 => Ok(TextureType::TextureCube),
3 => Ok(TextureType::Texture2DArray),
4 => Ok(TextureType::TextureCubeArray),
other => Err(crate::Error::UnknownEnum {
name: "TextureType",
value: other,
}),
}
}
}
/// Individual colour / data channel within a pixel.
///
/// The numeric value matches the zero-based channel index used by every uncompressed PixelFormat (R=0, G=1, B=2, A=3).
#[repr(i32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Channel {
/// Red (or single-channel value for R* formats).
R = 0,
/// Green (or second channel for RG* formats).
G = 1,
/// Blue (RGBA* formats only).
B = 2,
/// Alpha (RGBA* formats only).
A = 3,
}
impl TryFrom<i32> for Channel {
type Error = crate::Error;
fn try_from(v: i32) -> Result<Self, crate::Error> {
match v {
0 => Ok(Channel::R),
1 => Ok(Channel::G),
2 => Ok(Channel::B),
3 => Ok(Channel::A),
other => Err(crate::Error::UnknownEnum {
name: "Channel",
value: other,
}),
}
}
}
/// An owned list of [`Texture`] produced by the library.
pub struct TextureList {
raw: core::ptr::NonNull<ffi::whiteout_TextureList>,
}
impl TextureList {
/// # Safety
/// `raw` must be a live list this value takes ownership of.
#[allow(dead_code)]
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_TextureList) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| TextureList { raw })
}
pub fn len(&self) -> usize {
// SAFETY: the list is live for `&self`.
unsafe { ffi::whiteout_textures_TextureList_size(self.raw.as_ptr()) }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Borrow element `index`. `None` when out of range.
pub fn get(&self, index: usize) -> Option<crate::support::Ref<'_, Texture>> {
if index >= self.len() {
return None;
}
// SAFETY: index checked; the pointer is interior to the list and
// is never freed by the `Ref`.
unsafe {
Some(crate::support::Ref::new(Texture {
raw: core::ptr::NonNull::new_unchecked(ffi::whiteout_textures_TextureList_at(
self.raw.as_ptr(),
index,
)),
}))
}
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = crate::support::Ref<'_, Texture>> {
(0..self.len()).map(move |i| self.get(i).expect("index below len"))
}
}
impl Drop for TextureList {
fn drop(&mut self) {
// SAFETY: the list was transferred to us and is freed once.
unsafe { ffi::whiteout_textures_TextureList_delete(self.raw.as_ptr()) }
}
}
impl core::fmt::Debug for TextureList {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TextureList")
.field("len", &self.len())
.finish()
}
}
// SAFETY: a plain heap vector with no thread affinity, owned exclusively.
unsafe impl Send for TextureList {}
/// Describes a single mip level within a Texture's data buffer.
pub struct MipLevel {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_MipLevel>,
}
impl Drop for MipLevel {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_MipLevel_delete(self.raw.as_ptr()) }
}
}
impl MipLevel {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_MipLevel) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| MipLevel { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for MipLevel {}
impl core::fmt::Debug for MipLevel {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("MipLevel").finish_non_exhaustive()
}
}
impl MipLevel {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_MipLevel_new();
Self::from_raw(raw).expect("native MipLevel allocation failed")
}
}
/// Width of this mip in pixels.
pub fn width(&self) -> u32 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_MipLevel_get_width(self.raw.as_ptr()) }
}
pub fn set_width(&mut self, value: u32) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_MipLevel_set_width(self.raw.as_ptr(), value) }
}
/// Height of this mip in pixels.
pub fn height(&self) -> u32 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_MipLevel_get_height(self.raw.as_ptr()) }
}
pub fn set_height(&mut self, value: u32) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_MipLevel_set_height(self.raw.as_ptr(), value) }
}
/// Depth of this mip (always 1 for 2D / cube textures).
pub fn depth(&self) -> u32 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_MipLevel_get_depth(self.raw.as_ptr()) }
}
pub fn set_depth(&mut self, value: u32) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_MipLevel_set_depth(self.raw.as_ptr(), value) }
}
/// Byte offset into the Texture data buffer.
pub fn offset(&self) -> u64 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_MipLevel_get_offset(self.raw.as_ptr()) }
}
pub fn set_offset(&mut self, value: u64) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_MipLevel_set_offset(self.raw.as_ptr(), value) }
}
/// Byte size of this mip's data.
pub fn size(&self) -> u64 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_MipLevel_get_size(self.raw.as_ptr()) }
}
pub fn set_size(&mut self, value: u64) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_MipLevel_set_size(self.raw.as_ptr(), value) }
}
}
impl Default for MipLevel {
fn default() -> Self {
Self::new()
}
}
/// Format-agnostic GPU texture container
///
/// Texture is the central interchange object used by every format-specific parser and writer in the library. It owns a contiguous pixel-data buffer and a mip chain describing the layout of every mip level and layer.
///
/// Use the static factory methods (`create2D`, `create3D`, `createCube`) to allocate a new texture, or obtain one from a parser.
///
/// Supports in-place and copying format conversion between all PixelFormat values (uncompressed ↔ BCn) via `format()` and `copyAsFormat()`.
///
/// Uses the PImpl (Pointer to Implementation) idiom to hide internals.
pub struct Texture {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_Texture>,
}
impl Drop for Texture {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_Texture_delete(self.raw.as_ptr()) }
}
}
impl Texture {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_Texture) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| Texture { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for Texture {}
impl core::fmt::Debug for Texture {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Texture").finish_non_exhaustive()
}
}
impl Texture {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_Texture_new();
Self::from_raw(raw).expect("native Texture allocation failed")
}
}
/// Convert this texture to a new pixel format in-place.
///
/// Replaces the internal data with the converted result. Equivalent to `*this = copyAsFormat(new_fmt)`.
///
/// @param new_fmt Target pixel format.
pub fn convert_to(&mut self, new_fmt: PixelFormat) {
// SAFETY: handle is live for the duration of the call.
unsafe {
ffi::whiteout_textures_Texture_format(self.raw.as_ptr(), new_fmt as i32);
}
}
/// @return The pixel format of the stored data.
pub fn format(&self) -> PixelFormat {
// SAFETY: handle is live for the duration of the call.
unsafe {
PixelFormat::try_from(ffi::whiteout_textures_Texture_format_overload2(
self.raw.as_ptr(),
))
.expect("unknown enum discriminant from the native library (ABI version skew)")
}
}
/// Return a copy of this texture converted to a different pixel format.
///
/// Conversion path: - Same format → plain copy. - BCn → decoded to native format (R8 for BC4, RG8 for BC5, RGBA32F for BC6H, RGBA8 for others), then recurse. - Uncompressed → uncompressed → per-pixel conversion. - Uncompressed → BCn → encode via the appropriate codec.
///
/// @param new_fmt Target pixel format. @param pool Optional WorkerPool for parallel BCn encode/decode work. Ignored for purely uncompressed-to-uncompressed conversions. @return A new Texture with the converted data.
pub fn copy_as_format(
&self,
new_fmt: PixelFormat,
pool: Option<&crate::interfaces::HostWorkerPool>,
) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_Texture_copyAsFormat(
self.raw.as_ptr(),
new_fmt as i32,
pool.map_or(core::ptr::null_mut(), |v| v.as_ptr()),
))
}
}
/// Swap two channels in-place across all mip levels and array layers.
///
/// Operates directly on the stored pixel data without any intermediate copy. Supports all uncompressed PixelFormats (R*, RG*, RGBA*).
///
/// Failure conditions (returns false): - The texture uses a BCn block-compressed format. - Either channel is not present in the current pixel format (e.g. Channel::B on an RG8 texture).
///
/// @param a First channel to swap. @param b Second channel to swap. @return true on success (including when @p a == @p b, which is a no-op), false when the operation is not valid for this texture.
pub fn swap_channels(&mut self, a: Channel, b: Channel) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe {
ffi::whiteout_textures_Texture_swapChannels(self.raw.as_ptr(), a as i32, b as i32) != 0
}
}
/// Invert a single channel in-place across all mip levels and array layers.
///
/// Each sample value @c v is replaced with @c max_value - v, where @c max_value is the maximum representable value for the channel's underlying type (255 for u8, 65535 for u16, 1.0 for f32).
///
/// Operates directly on the stored pixel data without any intermediate copy. Supports all uncompressed PixelFormats (R*, RG*, RGBA*).
///
/// Failure conditions (returns false): - The texture uses a BCn block-compressed format. - The requested channel is not present in the current pixel format (e.g. Channel::B on an RG8 texture).
///
/// @param ch Channel to invert. @return true on success, false when the operation is not valid for this texture.
pub fn invert_channel(&mut self, ch: Channel) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_Texture_invertChannel(self.raw.as_ptr(), ch as i32) != 0 }
}
/// Reconstruct the Z component of a tangent-space normal map in-place.
///
/// Interprets channels @p a and @p b as the packed X and Y components of a unit normal vector, computes Z = sqrt(max(0, 1 - x² - y²)), and writes the result back to channel @p c.
///
/// Channel values are decoded from the UNORM `[0, 1]` storage convention to the signed [-1, 1] range before the computation (i.e. x = 2v - 1), and the reconstructed Z is re-encoded as (z + 1) / 2 before being written. This matches the encoding used by all other normal-map utilities in the library.
///
/// Operates directly on the stored pixel data without any intermediate copy. Supports all uncompressed PixelFormats (R*, RG*, RGBA*).
///
/// Failure conditions (returns false): - The texture uses a BCn block-compressed format. - Any of the three channel indices is not present in the current pixel format (e.g. Channel::B on an RG8 texture).
///
/// @param xChannel Channel storing the packed X component (source, read-only). @param yChannel Channel storing the packed Y component (source, read-only). @param zChannel Channel to receive the reconstructed Z component (write target). @return true on success, false when the operation is not valid for this texture.
pub fn expand_normal(
&mut self,
x_channel: Channel,
y_channel: Channel,
z_channel: Channel,
) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe {
ffi::whiteout_textures_Texture_expandNormal(
self.raw.as_ptr(),
x_channel as i32,
y_channel as i32,
z_channel as i32,
) != 0
}
}
/// Fill a single channel with a constant value across all mip levels and array layers.
///
/// The floating-point value is quantised to the channel's underlying type (clamped to `[0, 255]` for u8, `[0, 65535]` for u16, stored directly for f32).
///
/// Returns false for BCn formats or if the channel index exceeds the format's channel count.
///
/// @param target Channel to fill. @param value Value to write (interpreted as `[0, 1]` for integer formats). @return true on success, false when the operation is not valid.
pub fn fill_channel(&mut self, target: Channel, value: f32) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe {
ffi::whiteout_textures_Texture_fillChannel(self.raw.as_ptr(), target as i32, value) != 0
}
}
/// Split selected channels into individual single-channel textures.
///
/// Each requested channel produces a separate Texture with a single-channel format matching the source bit depth (R8, R16, or R32F). All mip levels and layers are copied. The returned textures inherit the source's sRGB flag but their kind is set to TextureKind::Other.
///
/// Returns std::nullopt if the source is BCn-compressed or if any requested channel index exceeds the source channel count.
///
/// @param channels Channels to extract (e.g. {Channel::R, Channel::G}). @return One Texture per requested channel, or std::nullopt on failure.
pub fn split_channels(&self, channels: &[Channel]) -> Option<TextureList> {
// SAFETY: the native side transfers ownership of
// the list; null means the operation produced none.
unsafe {
TextureList::from_raw(ffi::whiteout_textures_Texture_splitChannels(
self.raw.as_ptr(),
channels.as_ptr() as *const i32,
channels.len(),
))
}
}
/// Merge single-channel textures into one multi-channel texture.
///
/// Each source texture is written into the corresponding target channel of a new RGBA-width texture whose bit depth matches the sources (RGBA8, RGBA16, or RGBA32F). All sources must share the same format, dimensions, mip count, and texture type. Channels not covered by the input list are zero-filled.
///
/// @param sources Single-channel textures to combine. @param targetChannels Destination channel for each source (same length as @p sources). @return The combined RGBA texture, or std::nullopt on failure.
pub fn merge_channels(sources: &[&Texture], target_channels: &[Channel]) -> Option<Texture> {
let sources_ptrs: Vec<_> = sources.iter().map(|v| v.raw.as_ptr()).collect();
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_Texture_mergeChannels(
sources_ptrs.as_ptr(),
sources.len(),
target_channels.as_ptr() as *const i32,
target_channels.len(),
))
}
}
/// Return a copy of a 2-channel normal map expanded to RGBA8.
///
/// Only supported for textures whose kind() is TextureKind::Normal and whose format is RG8, RG16, RG32F, or BC5. The returned texture keeps the original shape, mip chain, kind, and sRGB flag, but stores data as RGBA8 with Z reconstructed from the packed X/Y normal in R/G.
///
/// @param pool Optional WorkerPool for parallel BCn decode work when the source texture is compressed. @return Expanded RGBA8 texture, or std::nullopt when unsupported.
pub fn copy_from_normal_to_rgba(
&self,
pool: Option<&crate::interfaces::HostWorkerPool>,
) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_Texture_copyFromNormalToRGBA(
self.raw.as_ptr(),
pool.map_or(core::ptr::null_mut(), |v| v.as_ptr()),
))
}
}
/// Generate all mip levels from the base image (mip 0).
///
/// Every mip level is generated directly from the original full-resolution image using an appropriately-sized filter kernel, rather than cascading from the previous mip level. This eliminates cumulative blur.
///
/// Selects the best filter and pipeline for the texture's kind(): - Diffuse / Albedo — Lanczos3; sRGB linearize/delinearize when isSrgb() is true. - Normal — Kaiser(β=6) with unpack / Toksvig / renormalize / pack. - Specular — Kaiser(β=6); sRGB linearize/delinearize when isSrgb(). - Roughness — Kaiser(β=6.5) variance-preserving: r→r², filter, √. - Gloss — convert to roughness, apply variance filter, convert back. - Metalness — Kaiser(β=5.5) mean filtering. - AmbientOcclusion — Kaiser(β=6) mean filtering. - Emissive — Lanczos3; sRGB linearize/delinearize when isSrgb(). - ORM (deprecated) — same as Multikind with R=AO/G=Roughness/B=Metalness. - Multikind — per-channel kind-appropriate pipeline; each channel's kind is queried via channelKind(). Unused channels use a box filter. - AlphaMask — Box filter; no sRGB conversion (linear mask data). - Lightmap — Lanczos3; clamp channels to [0, ∞) (no sRGB). - EnvironmentPBR — GGX importance-sampled convolution (equirectangular); roughness increases with each mip level. - EnvironmentLegacy — Solid-angle-weighted spherical Kaiser convolution (equirectangular); no roughness encoding. - Other — Box filter; sRGB linearize/delinearize when isSrgb().
///
/// The texture must use an uncompressed pixel format. BCn textures should be decompressed first. No-op if the texture has ≤ 1 mip.
///
/// @param newMipCount Desired number of mip levels in the output texture. Pass kKeepMipCount (0) to preserve the existing mip count. Must be between 1 and computeMaxMipCount(width, height, depth). When 1, the mip chain is truncated to the base level only and the function returns immediately. @param pool Optional WorkerPool used to parallelize mip generation across mip levels and layers. If null, generation runs on the calling thread. @return std::nullopt on success; std::`optional<std::string>` with error message on failure. No exceptions are thrown.
pub fn generate_mipmaps(
&mut self,
new_mip_count: u32,
pool: Option<&crate::interfaces::HostWorkerPool>,
) -> Option<String> {
// SAFETY: handle is live for the duration of the call.
unsafe {
crate::support::take_string_opt(ffi::whiteout_textures_Texture_generateMipmaps(
self.raw.as_ptr(),
new_mip_count,
pool.map_or(core::ptr::null_mut(), |v| v.as_ptr()),
))
}
}
/// @overload Preserves existing mip count; optional worker pool.
pub fn generate_mipmaps_default(
&mut self,
pool: Option<&crate::interfaces::HostWorkerPool>,
) -> Option<String> {
// SAFETY: handle is live for the duration of the call.
unsafe {
crate::support::take_string_opt(ffi::whiteout_textures_Texture_generateMipmaps_pool(
self.raw.as_ptr(),
pool.map_or(core::ptr::null_mut(), |v| v.as_ptr()),
))
}
}
/// Downscale the texture by dropping leading mip levels.
///
/// Increases the mip count by @p levels (clamped to the maximum), regenerates all mip levels from the base image, then drops the first @p levels mips — effectively halving the resolution @p levels times while preserving the original mip chain length.
///
/// The texture must use an uncompressed pixel format (same requirement as generateMipmaps). Returns an error if @p levels would reduce every dimension to zero.
///
/// @param levels Number of mip levels to drop (default 1). @param pool Optional WorkerPool for parallel mip generation. @return std::nullopt on success; error message on failure.
pub fn downscale(
&mut self,
levels: u32,
pool: Option<&crate::interfaces::HostWorkerPool>,
) -> Option<String> {
// SAFETY: handle is live for the duration of the call.
unsafe {
crate::support::take_string_opt(ffi::whiteout_textures_Texture_downscale(
self.raw.as_ptr(),
levels,
pool.map_or(core::ptr::null_mut(), |v| v.as_ptr()),
))
}
}
/// Create a 2D texture. @param fmt Pixel format. @param width Width in pixels. @param height Height in pixels. @param mipCount Number of mip levels (0 = auto-compute full chain). @return A zero-filled Texture with the requested layout.
pub fn create_2d(fmt: PixelFormat, width: u32, height: u32, mip_count: u32) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_Texture_create2D(
fmt as i32, width, height, mip_count,
))
}
}
/// Create a 3D (volume) texture. @param fmt Pixel format. @param width Width in pixels. @param height Height in pixels. @param depth Depth in slices. @param mipCount Number of mip levels (0 = auto-compute full chain). @return A zero-filled Texture with the requested layout.
pub fn create_3d(
fmt: PixelFormat,
width: u32,
height: u32,
depth: u32,
mip_count: u32,
) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_Texture_create3D(
fmt as i32, width, height, depth, mip_count,
))
}
}
/// Create a cube-map texture. @param fmt Pixel format. @param size Face edge length in pixels (faces are square). @param mipCount Number of mip levels (0 = auto-compute full chain). @return A zero-filled Texture with 6 layers.
pub fn create_cube(fmt: PixelFormat, size: u32, mip_count: u32) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_Texture_createCube(
fmt as i32, size, mip_count,
))
}
}
/// Create a 2D texture array. @param fmt Pixel format. @param width Width in pixels. @param height Height in pixels. @param arraySize Number of array slices (must be ≥ 1). @param mipCount Number of mip levels (0 = auto-compute full chain). @return A zero-filled Texture with @p arraySize layers.
pub fn create_2d_array(
fmt: PixelFormat,
width: u32,
height: u32,
array_size: u32,
mip_count: u32,
) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_Texture_create2DArray(
fmt as i32, width, height, array_size, mip_count,
))
}
}
/// Create a cube-map texture array. @param fmt Pixel format. @param size Face edge length in pixels (faces are square). @param arraySize Number of cube-map entries in the array (must be ≥ 1). The final layer count is 6 × @p arraySize. @param mipCount Number of mip levels (0 = auto-compute full chain). @return A zero-filled Texture with 6 × arraySize layers.
pub fn create_cube_array(
fmt: PixelFormat,
size: u32,
array_size: u32,
mip_count: u32,
) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_Texture_createCubeArray(
fmt as i32, size, array_size, mip_count,
))
}
}
/// @return The texture dimensionality / topology.
pub fn texture_type(&self) -> TextureType {
// SAFETY: handle is live for the duration of the call.
unsafe {
TextureType::try_from(ffi::whiteout_textures_Texture_type(self.raw.as_ptr()))
.expect("unknown enum discriminant from the native library (ABI version skew)")
}
}
/// @return The semantic kind of this texture.
pub fn kind(&self) -> TextureKind {
// SAFETY: handle is live for the duration of the call.
unsafe {
TextureKind::try_from(ffi::whiteout_textures_Texture_kind(self.raw.as_ptr()))
.expect("unknown enum discriminant from the native library (ABI version skew)")
}
}
/// Set the semantic kind of this texture. @note TextureKind::Unused is not valid as a top-level kind; use setChannelKind() on a Multikind texture for per-channel Unused.
pub fn set_kind(&mut self, k: TextureKind) {
// SAFETY: handle is live for the duration of the call.
unsafe {
ffi::whiteout_textures_Texture_setKind(self.raw.as_ptr(), k as i32);
}
}
/// @return The per-channel kind for channel @p ch.
///
/// Only meaningful when kind() == TextureKind::Multikind. Returns TextureKind::Other by default for all other kinds. @param ch Channel to query (R/G/B/A).
pub fn channel_kind(&self, ch: Channel) -> TextureKind {
// SAFETY: handle is live for the duration of the call.
unsafe {
TextureKind::try_from(ffi::whiteout_textures_Texture_channelKind(
self.raw.as_ptr(),
ch as i32,
))
.expect("unknown enum discriminant from the native library (ABI version skew)")
}
}
/// Set the per-channel kind for channel @p ch.
///
/// Only meaningful when kind() == TextureKind::Multikind. TextureKind::Unused is permitted here to mark a channel as unused. @param ch Channel to configure. @param kind Kind to assign, including TextureKind::Unused.
pub fn set_channel_kind(&mut self, ch: Channel, kind: TextureKind) {
// SAFETY: handle is live for the duration of the call.
unsafe {
ffi::whiteout_textures_Texture_setChannelKind(
self.raw.as_ptr(),
ch as i32,
kind as i32,
);
}
}
/// @return The default fill value for channel @p ch.
///
/// This value is used by consumers (e.g. channel merging, material baking) when the channel carries no source data. Defaults to 1.0f for all channels. @param ch Channel to query (R/G/B/A).
pub fn channel_default(&self, ch: Channel) -> f32 {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_Texture_channelDefault(self.raw.as_ptr(), ch as i32) }
}
/// Set the default fill value for channel @p ch.
///
/// The value is stored as-is (normalised `[0, 1]` float for integer formats, linear scale for f32 formats). No clamping is applied at storage time. @param ch Channel to configure (R/G/B/A). @param value Default fill value; 1.0f by convention.
pub fn set_channel_default(&mut self, ch: Channel, value: f32) {
// SAFETY: handle is live for the duration of the call.
unsafe {
ffi::whiteout_textures_Texture_setChannelDefault(self.raw.as_ptr(), ch as i32, value);
}
}
/// @return True if the texture data is in sRGB colour space.
pub fn is_srgb(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_Texture_isSrgb(self.raw.as_ptr()) != 0 }
}
/// Mark the texture as sRGB or linear.
pub fn set_srgb(&mut self, srgb: bool) {
// SAFETY: handle is live for the duration of the call.
unsafe {
ffi::whiteout_textures_Texture_setSrgb(self.raw.as_ptr(), if srgb { 1 } else { 0 });
}
}
/// @return Base mip width in pixels.
pub fn width(&self) -> u32 {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_Texture_width(self.raw.as_ptr()) }
}
/// @return Base mip height in pixels.
pub fn height(&self) -> u32 {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_Texture_height(self.raw.as_ptr()) }
}
/// @return Base mip depth (1 for 2D / cube textures).
pub fn depth(&self) -> u32 {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_Texture_depth(self.raw.as_ptr()) }
}
/// @return Number of array layers. - Texture2D / Texture3D: 1. - TextureCube: 6. - Texture2DArray: arraySize(). - TextureCubeArray: 6 × arraySize().
pub fn layer_count(&self) -> u32 {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_Texture_layerCount(self.raw.as_ptr()) }
}
/// @return Number of array slices (1 for non-array textures). For a TextureCubeArray, this is the number of cube-maps in the array (the layer count is 6 × this value).
pub fn array_size(&self) -> u32 {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_Texture_arraySize(self.raw.as_ptr()) }
}
/// @return Number of mip levels per layer.
pub fn mip_count(&self) -> u32 {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_Texture_mipCount(self.raw.as_ptr()) }
}
/// Get the mip-level descriptor for a given mip index and layer. @param mip Mip level index (0 = base). @param layer Array layer index (0 for 2D / 3D textures). @return Reference to the MipLevel struct.
pub fn mip_level(&self, mip: u32, layer: u32) -> Option<crate::support::Ref<'_, MipLevel>> {
// SAFETY: the native side returns an interior
// pointer borrowed from `self`; `Ref` derefs to it
// and never frees it.
unsafe {
core::ptr::NonNull::new(ffi::whiteout_textures_Texture_mipLevel(
self.raw.as_ptr(),
mip,
layer,
))
.map(|raw| crate::support::Ref::new(MipLevel { raw }))
}
}
/// @return Total byte size of the pixel-data buffer.
pub fn data_size(&self) -> u64 {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_Texture_dataSize(self.raw.as_ptr()) }
}
/// @return Read-only span over the entire pixel-data buffer.
pub fn data(&self) -> BorrowedSlice<'_> {
let mut __size: usize = 0;
// SAFETY: the returned pointer borrows `self`; the
// lifetime on BorrowedSlice keeps it from outliving us.
unsafe {
let __b = ffi::whiteout_textures_Texture_data(self.raw.as_ptr());
__size = __b.size;
BorrowedSlice::new(__b.data, __size)
}
}
/// Get a read-only span for a specific mip / layer. @param mip Mip level index. @param layer Array layer (default 0).
pub fn mip_data(&self, mip: u32, layer: u32) -> BorrowedSlice<'_> {
let mut __size: usize = 0;
// SAFETY: the returned pointer borrows `self`; the
// lifetime on BorrowedSlice keeps it from outliving us.
unsafe {
let __b = ffi::whiteout_textures_Texture_mipData(self.raw.as_ptr(), mip, layer);
__size = __b.size;
BorrowedSlice::new(__b.data, __size)
}
}
/// Move the data vector out of the texture (destructive).
///
/// After this call the texture's dimensions and mip chain are cleared. @return The owned pixel-data buffer.
pub fn take_data(&mut self) -> Bytes {
// SAFETY: handle is live for the duration of the call.
unsafe {
Bytes::from_raw(ffi::whiteout_textures_Texture_takeData(self.raw.as_ptr()))
.unwrap_or_else(Bytes::empty)
}
}
/// Replace the pixel-data buffer.
///
/// The new buffer must match the existing allocation size. @param new_data Replacement data.
pub fn set_data(&mut self, new_data: &[u8]) {
// SAFETY: handle is live for the duration of the call.
unsafe {
ffi::whiteout_textures_Texture_setData(
self.raw.as_ptr(),
new_data.as_ptr(),
new_data.len(),
);
}
}
}
impl Texture {
/// Mutable, zero-copy view of the underlying buffer.
///
/// Writes land directly in the C++ allocation — nothing is marshalled.
/// The borrow of `self` is what makes that safe: the buffer cannot be
/// resized or freed while this slice exists.
pub fn data_mut(&mut self) -> &mut [u8] {
let mut size: usize = 0;
// SAFETY: the pointer borrows `self` mutably for the returned
// lifetime, so no aliasing or reallocation can occur meanwhile.
unsafe {
let p = tier_a::whiteout_v_Texture_data_mut(self.raw.as_ptr().cast(), &mut size);
if p.is_null() || size == 0 {
&mut []
} else {
core::slice::from_raw_parts_mut(p, size)
}
}
}
/// Mutable, zero-copy view of the underlying buffer.
///
/// Writes land directly in the C++ allocation — nothing is marshalled.
/// The borrow of `self` is what makes that safe: the buffer cannot be
/// resized or freed while this slice exists.
pub fn mip_data_mut(&mut self, mip: u32, layer: u32) -> &mut [u8] {
let mut size: usize = 0;
// SAFETY: the pointer borrows `self` mutably for the returned
// lifetime, so no aliasing or reallocation can occur meanwhile.
unsafe {
let p = tier_a::whiteout_v_Texture_mipData_mut(
self.raw.as_ptr().cast(),
mip,
layer,
&mut size,
);
if p.is_null() || size == 0 {
&mut []
} else {
core::slice::from_raw_parts_mut(p, size)
}
}
}
}
impl Default for Texture {
fn default() -> Self {
Self::new()
}
}
/// Parser for BLP texture files
///
/// The Parser reads binary BLP files and converts them into the Texture structure. It can handle both BLP1 (Warcraft III) and BLP2 (World of Warcraft) variants. Parsing is non-throwing — issues are collected via `hasIssues()` / `getIssues()` and `parse()` returns `std::nullopt` on failure.
///
/// Uses the PImpl (Pointer to Implementation) idiom to hide implementation details.
pub struct BlpParser {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_BlpParser>,
}
impl Drop for BlpParser {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_BlpParser_delete(self.raw.as_ptr()) }
}
}
impl BlpParser {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_BlpParser) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| BlpParser { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for BlpParser {}
impl core::fmt::Debug for BlpParser {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BlpParser").finish_non_exhaustive()
}
}
impl BlpParser {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_BlpParser_new();
Self::from_raw(raw).expect("native BlpParser allocation failed")
}
}
/// Parse a BLP file from memory buffer @param buffer Memory buffer containing BLP data @return Parsed texture data, or std::nullopt on failure @throws std::runtime_error If parsing fails in strict mode
pub fn parse(&mut self, buffer: &[u8]) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_BlpParser_parse(
self.raw.as_ptr(),
buffer.as_ptr(),
buffer.len(),
))
}
}
/// Check if parsing encountered any issues @return True if there were warnings or recoverable errors
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_BlpParser_hasIssues(self.raw.as_ptr()) != 0 }
}
/// Get list of issues encountered during parsing @return Vector of issue description strings
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_BlpParser_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_BlpParser_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for BlpParser {
fn default() -> Self {
Self::new()
}
}
/// Writer for BLP texture files
///
/// The Writer takes a Texture and encodes it into BLP1 or BLP2 binary format. It supports palettized, JPEG, DXT, and BGRA encodings.
///
/// Uses the PImpl (Pointer to Implementation) idiom to hide implementation details.
pub struct BlpWriter {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_BlpWriter>,
}
impl Drop for BlpWriter {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_BlpWriter_delete(self.raw.as_ptr()) }
}
}
impl BlpWriter {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_BlpWriter) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| BlpWriter { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for BlpWriter {}
impl core::fmt::Debug for BlpWriter {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BlpWriter").finish_non_exhaustive()
}
}
impl BlpWriter {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_BlpWriter_new();
Self::from_raw(raw).expect("native BlpWriter allocation failed")
}
}
/// Write a BLP file to a byte buffer with default options
pub fn write(&mut self, texture: &Texture) -> Bytes {
// SAFETY: handle is live for the duration of the call.
unsafe {
Bytes::from_raw(ffi::whiteout_textures_BlpWriter_write(
self.raw.as_ptr(),
texture.raw.as_ptr(),
))
.unwrap_or_else(Bytes::empty)
}
}
/// Check if writing encountered any issues @return True if there were warnings or recoverable errors
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_BlpWriter_hasIssues(self.raw.as_ptr()) != 0 }
}
/// Get list of issues encountered during writing @return Vector of issue description strings
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_BlpWriter_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_BlpWriter_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for BlpWriter {
fn default() -> Self {
Self::new()
}
}
/// Per-frame metadata for an animated PNG (APNG).
pub struct PngApngFrameInfo {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_PngApngFrameInfo>,
}
impl Drop for PngApngFrameInfo {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_delete(self.raw.as_ptr()) }
}
}
impl PngApngFrameInfo {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_PngApngFrameInfo) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| PngApngFrameInfo { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for PngApngFrameInfo {}
impl core::fmt::Debug for PngApngFrameInfo {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PngApngFrameInfo").finish_non_exhaustive()
}
}
impl PngApngFrameInfo {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_PngApngFrameInfo_new();
Self::from_raw(raw).expect("native PngApngFrameInfo allocation failed")
}
}
/// Frame sub-rectangle width.
pub fn width(&self) -> u32 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_get_width(self.raw.as_ptr()) }
}
pub fn set_width(&mut self, value: u32) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_set_width(self.raw.as_ptr(), value) }
}
/// Frame sub-rectangle height.
pub fn height(&self) -> u32 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_get_height(self.raw.as_ptr()) }
}
pub fn set_height(&mut self, value: u32) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_set_height(self.raw.as_ptr(), value) }
}
/// Frame sub-rectangle X offset on the canvas.
pub fn x_offset(&self) -> u32 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_get_xOffset(self.raw.as_ptr()) }
}
pub fn set_x_offset(&mut self, value: u32) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_set_xOffset(self.raw.as_ptr(), value) }
}
/// Frame sub-rectangle Y offset on the canvas.
pub fn y_offset(&self) -> u32 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_get_yOffset(self.raw.as_ptr()) }
}
pub fn set_y_offset(&mut self, value: u32) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_set_yOffset(self.raw.as_ptr(), value) }
}
/// Frame display duration in milliseconds.
pub fn delay_ms(&self) -> u32 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_get_delayMs(self.raw.as_ptr()) }
}
pub fn set_delay_ms(&mut self, value: u32) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_set_delayMs(self.raw.as_ptr(), value) }
}
/// 0 = NONE, 1 = BACKGROUND, 2 = PREVIOUS.
pub fn dispose_op(&self) -> u32 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_get_disposeOp(self.raw.as_ptr()) }
}
pub fn set_dispose_op(&mut self, value: u32) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_set_disposeOp(self.raw.as_ptr(), value) }
}
/// 0 = SOURCE, 1 = OVER.
pub fn blend_op(&self) -> u32 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_get_blendOp(self.raw.as_ptr()) }
}
pub fn set_blend_op(&mut self, value: u32) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrameInfo_set_blendOp(self.raw.as_ptr(), value) }
}
}
impl Default for PngApngFrameInfo {
fn default() -> Self {
Self::new()
}
}
/// Reads a PNG file or byte buffer and decodes it into a Texture.
///
/// Animated PNG (APNG) is supported: `parse()` still returns the single default image, while the animation frames are exposed via `isAnimated()`, `frameCount()`, `frame()`, `frameDelayMs()` and `frameInfo()`.
pub struct PngParser {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_PngParser>,
}
impl Drop for PngParser {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_PngParser_delete(self.raw.as_ptr()) }
}
}
impl PngParser {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_PngParser) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| PngParser { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for PngParser {}
impl core::fmt::Debug for PngParser {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PngParser").finish_non_exhaustive()
}
}
impl PngParser {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_PngParser_new();
Self::from_raw(raw).expect("native PngParser allocation failed")
}
}
/// Parse a PNG byte buffer.
pub fn parse(&mut self, buffer: &[u8]) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_PngParser_parse(
self.raw.as_ptr(),
buffer.as_ptr(),
buffer.len(),
))
}
}
/// @return true if the last parse produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_PngParser_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last parse call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_PngParser_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_PngParser_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
/// @return true if the last parsed PNG carried APNG animation chunks.
pub fn is_animated(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_PngParser_isAnimated(self.raw.as_ptr()) != 0 }
}
/// @return number of animation frames (0 when not animated).
pub fn frame_count(&self) -> u32 {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_PngParser_frameCount(self.raw.as_ptr()) }
}
/// @return APNG loop count from the `acTL` chunk; 0 means loop forever.
pub fn loop_count(&self) -> u32 {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_PngParser_loopCount(self.raw.as_ptr()) }
}
/// @return animation frame @p index, fully composited to the canvas size as an RGBA8 texture. In lenient mode an out-of-range index yields an empty texture; in strict mode it throws. @param index Zero-based frame index.
pub fn frame(&self, index: u32) -> Option<crate::support::Ref<'_, Texture>> {
// SAFETY: the native side returns an interior
// pointer borrowed from `self`; `Ref` derefs to it
// and never frees it.
unsafe {
core::ptr::NonNull::new(ffi::whiteout_textures_PngParser_frame(
self.raw.as_ptr(),
index,
))
.map(|raw| crate::support::Ref::new(Texture { raw }))
}
}
/// @return display duration of frame @p index in milliseconds. @param index Zero-based frame index.
pub fn frame_delay_ms(&self, index: u32) -> u32 {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_PngParser_frameDelayMs(self.raw.as_ptr(), index) }
}
/// @return raw per-frame metadata for frame @p index. @param index Zero-based frame index.
pub fn frame_info(&self, index: u32) -> Option<crate::support::Ref<'_, PngApngFrameInfo>> {
// SAFETY: the native side returns an interior
// pointer borrowed from `self`; `Ref` derefs to it
// and never frees it.
unsafe {
core::ptr::NonNull::new(ffi::whiteout_textures_PngParser_frameInfo(
self.raw.as_ptr(),
index,
))
.map(|raw| crate::support::Ref::new(PngApngFrameInfo { raw }))
}
}
}
impl Default for PngParser {
fn default() -> Self {
Self::new()
}
}
/// One frame of an animated PNG (APNG), with its display duration.
pub struct PngApngFrame {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_PngApngFrame>,
}
impl Drop for PngApngFrame {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_PngApngFrame_delete(self.raw.as_ptr()) }
}
}
impl PngApngFrame {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_PngApngFrame) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| PngApngFrame { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for PngApngFrame {}
impl core::fmt::Debug for PngApngFrame {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PngApngFrame").finish_non_exhaustive()
}
}
impl PngApngFrame {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_PngApngFrame_new();
Self::from_raw(raw).expect("native PngApngFrame allocation failed")
}
}
/// Full-canvas frame image (converted to RGBA8 on write).
/// Borrows the field in place — no copy, no allocation.
pub fn image(&self) -> crate::support::Ref<'_, Texture> {
// SAFETY: an interior pointer into `self`, valid for this
// borrow and never freed by the `Ref`.
unsafe {
crate::support::Ref::new(Texture {
raw: core::ptr::NonNull::new_unchecked(
ffi::whiteout_textures_PngApngFrame_get_image(self.raw.as_ptr()),
),
})
}
}
pub fn image_mut(&mut self) -> crate::support::RefMut<'_, Texture> {
// SAFETY: as above; `&mut self` guarantees exclusivity.
unsafe {
crate::support::RefMut::new(Texture {
raw: core::ptr::NonNull::new_unchecked(
ffi::whiteout_textures_PngApngFrame_get_image(self.raw.as_ptr()),
),
})
}
}
/// Display duration in milliseconds.
pub fn delay_ms(&self) -> u32 {
// SAFETY: plain scalar read through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrame_get_delayMs(self.raw.as_ptr()) }
}
pub fn set_delay_ms(&mut self, value: u32) {
// SAFETY: plain scalar write through a live handle.
unsafe { ffi::whiteout_textures_PngApngFrame_set_delayMs(self.raw.as_ptr(), value) }
}
}
impl Default for PngApngFrame {
fn default() -> Self {
Self::new()
}
}
/// Options controlling animated PNG (APNG) encoding.
#[derive(Clone, Debug, PartialEq)]
pub struct PngApngSaveOptions {
/// Number of times to loop; 0 means loop forever.
pub loop_count: u32,
}
impl Default for PngApngSaveOptions {
fn default() -> Self {
// SAFETY: `_new` always returns a live handle; freed before return.
unsafe {
let h = ffi::whiteout_textures_PngApngSaveOptions_new();
let out = PngApngSaveOptions {
loop_count: ffi::whiteout_textures_PngApngSaveOptions_get_loopCount(h),
};
ffi::whiteout_textures_PngApngSaveOptions_delete(h);
out
}
}
}
impl PngApngSaveOptions {
/// Build a native handle carrying these values. Caller frees it.
#[allow(dead_code)] // consumed once the methods taking these options bind
pub(crate) unsafe fn to_native(&self) -> *mut ffi::whiteout_PngApngSaveOptions {
unsafe {
let h = ffi::whiteout_textures_PngApngSaveOptions_new();
ffi::whiteout_textures_PngApngSaveOptions_set_loopCount(h, self.loop_count);
h
}
}
/// Free a handle produced by [`Self::to_native`].
///
/// # Safety
/// `h` must have come from `to_native` and not been freed already.
#[allow(dead_code)]
pub(crate) unsafe fn free_native(h: *mut ffi::whiteout_PngApngSaveOptions) {
unsafe { ffi::whiteout_textures_PngApngSaveOptions_delete(h) }
}
}
/// Encodes a Texture into PNG format.
///
/// In addition to single-image PNG, the writer can emit an animated PNG (APNG) from a sequence of frames via `writeAnimated()`. Each frame is written full-canvas with no inter-frame optimisation.
pub struct PngWriter {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_PngWriter>,
}
impl Drop for PngWriter {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_PngWriter_delete(self.raw.as_ptr()) }
}
}
impl PngWriter {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_PngWriter) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| PngWriter { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for PngWriter {}
impl core::fmt::Debug for PngWriter {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PngWriter").finish_non_exhaustive()
}
}
impl PngWriter {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_PngWriter_new();
Self::from_raw(raw).expect("native PngWriter allocation failed")
}
}
/// Serialize the texture to a PNG byte buffer.
pub fn write(&mut self, texture: &Texture) -> Bytes {
// SAFETY: handle is live for the duration of the call.
unsafe {
Bytes::from_raw(ffi::whiteout_textures_PngWriter_write(
self.raw.as_ptr(),
texture.raw.as_ptr(),
))
.unwrap_or_else(Bytes::empty)
}
}
/// Serialize a sequence of frames into an animated PNG (APNG) byte buffer.
///
/// All frames must share the same dimensions (frame 0 defines the canvas). Each frame is emitted full-canvas with disposal NONE and blend SOURCE. Returns an empty buffer on failure (lenient mode). @param frames Ordered animation frames; must be non-empty. @param opts Encoding options (loop count).
pub fn write_animated(&mut self, frames: &[&PngApngFrame], opts: &PngApngSaveOptions) -> Bytes {
let frames_ptrs: Vec<_> = frames.iter().map(|v| v.raw.as_ptr()).collect();
let opts_native = unsafe { opts.to_native() };
// SAFETY: handle is live for the call; the staged
// option handles are freed immediately after.
unsafe {
let __r = Bytes::from_raw(ffi::whiteout_textures_PngWriter_writeAnimated(
self.raw.as_ptr(),
frames_ptrs.as_ptr(),
frames.len(),
opts_native,
))
.unwrap_or_else(Bytes::empty);
PngApngSaveOptions::free_native(opts_native);
__r
}
}
/// @return true if the last write produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_PngWriter_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last write call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_PngWriter_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_PngWriter_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for PngWriter {
fn default() -> Self {
Self::new()
}
}
/// Reads a JPEG file or byte buffer and decodes it into a Texture.
pub struct JpegParser {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_JpegParser>,
}
impl Drop for JpegParser {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_JpegParser_delete(self.raw.as_ptr()) }
}
}
impl JpegParser {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_JpegParser) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| JpegParser { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for JpegParser {}
impl core::fmt::Debug for JpegParser {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("JpegParser").finish_non_exhaustive()
}
}
impl JpegParser {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_JpegParser_new();
Self::from_raw(raw).expect("native JpegParser allocation failed")
}
}
/// Parse a JPEG byte buffer.
pub fn parse(&mut self, buffer: &[u8]) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_JpegParser_parse(
self.raw.as_ptr(),
buffer.as_ptr(),
buffer.len(),
))
}
}
/// @return true if the last parse produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_JpegParser_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last parse call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_JpegParser_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_JpegParser_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for JpegParser {
fn default() -> Self {
Self::new()
}
}
/// Encodes a Texture into JPEG format.
pub struct JpegWriter {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_JpegWriter>,
}
impl Drop for JpegWriter {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_JpegWriter_delete(self.raw.as_ptr()) }
}
}
impl JpegWriter {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_JpegWriter) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| JpegWriter { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for JpegWriter {}
impl core::fmt::Debug for JpegWriter {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("JpegWriter").finish_non_exhaustive()
}
}
impl JpegWriter {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_JpegWriter_new();
Self::from_raw(raw).expect("native JpegWriter allocation failed")
}
}
/// Serialize the texture to a JPEG byte buffer.
pub fn write(&mut self, texture: &Texture) -> Bytes {
// SAFETY: handle is live for the duration of the call.
unsafe {
Bytes::from_raw(ffi::whiteout_textures_JpegWriter_write(
self.raw.as_ptr(),
texture.raw.as_ptr(),
))
.unwrap_or_else(Bytes::empty)
}
}
/// @return true if the last write produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_JpegWriter_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last write call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_JpegWriter_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_JpegWriter_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for JpegWriter {
fn default() -> Self {
Self::new()
}
}
/// Reads a DDS file or byte buffer and decodes it into a Texture.
pub struct DdsParser {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_DdsParser>,
}
impl Drop for DdsParser {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_DdsParser_delete(self.raw.as_ptr()) }
}
}
impl DdsParser {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_DdsParser) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| DdsParser { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for DdsParser {}
impl core::fmt::Debug for DdsParser {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("DdsParser").finish_non_exhaustive()
}
}
impl DdsParser {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_DdsParser_new();
Self::from_raw(raw).expect("native DdsParser allocation failed")
}
}
/// Parse a DDS byte buffer.
pub fn parse(&mut self, buffer: &[u8]) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_DdsParser_parse(
self.raw.as_ptr(),
buffer.as_ptr(),
buffer.len(),
))
}
}
/// @return true if the last parse produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_DdsParser_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last parse call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_DdsParser_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_DdsParser_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for DdsParser {
fn default() -> Self {
Self::new()
}
}
/// Encodes a Texture into DDS format.
pub struct DdsWriter {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_DdsWriter>,
}
impl Drop for DdsWriter {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_DdsWriter_delete(self.raw.as_ptr()) }
}
}
impl DdsWriter {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_DdsWriter) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| DdsWriter { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for DdsWriter {}
impl core::fmt::Debug for DdsWriter {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("DdsWriter").finish_non_exhaustive()
}
}
impl DdsWriter {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_DdsWriter_new();
Self::from_raw(raw).expect("native DdsWriter allocation failed")
}
}
/// Serialize the texture to a DDS byte buffer.
pub fn write(&mut self, texture: &Texture) -> Bytes {
// SAFETY: handle is live for the duration of the call.
unsafe {
Bytes::from_raw(ffi::whiteout_textures_DdsWriter_write(
self.raw.as_ptr(),
texture.raw.as_ptr(),
))
.unwrap_or_else(Bytes::empty)
}
}
/// @return true if the last write produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_DdsWriter_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last write call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_DdsWriter_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_DdsWriter_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for DdsWriter {
fn default() -> Self {
Self::new()
}
}
/// Reads a BMP file or byte buffer and decodes it into a Texture.
pub struct BmpParser {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_BmpParser>,
}
impl Drop for BmpParser {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_BmpParser_delete(self.raw.as_ptr()) }
}
}
impl BmpParser {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_BmpParser) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| BmpParser { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for BmpParser {}
impl core::fmt::Debug for BmpParser {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BmpParser").finish_non_exhaustive()
}
}
impl BmpParser {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_BmpParser_new();
Self::from_raw(raw).expect("native BmpParser allocation failed")
}
}
/// Parse a BMP byte buffer.
pub fn parse(&mut self, buffer: &[u8]) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_BmpParser_parse(
self.raw.as_ptr(),
buffer.as_ptr(),
buffer.len(),
))
}
}
/// @return true if the last parse produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_BmpParser_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last parse call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_BmpParser_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_BmpParser_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for BmpParser {
fn default() -> Self {
Self::new()
}
}
/// Encodes a Texture into BMP format.
pub struct BmpWriter {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_BmpWriter>,
}
impl Drop for BmpWriter {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_BmpWriter_delete(self.raw.as_ptr()) }
}
}
impl BmpWriter {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_BmpWriter) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| BmpWriter { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for BmpWriter {}
impl core::fmt::Debug for BmpWriter {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("BmpWriter").finish_non_exhaustive()
}
}
impl BmpWriter {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_BmpWriter_new();
Self::from_raw(raw).expect("native BmpWriter allocation failed")
}
}
/// Serialize the texture to a BMP byte buffer.
pub fn write(&mut self, texture: &Texture) -> Bytes {
// SAFETY: handle is live for the duration of the call.
unsafe {
Bytes::from_raw(ffi::whiteout_textures_BmpWriter_write(
self.raw.as_ptr(),
texture.raw.as_ptr(),
))
.unwrap_or_else(Bytes::empty)
}
}
/// @return true if the last write produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_BmpWriter_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last write call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_BmpWriter_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_BmpWriter_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for BmpWriter {
fn default() -> Self {
Self::new()
}
}
/// Reads a TGA file or byte buffer and decodes it into a Texture.
pub struct TgaParser {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_TgaParser>,
}
impl Drop for TgaParser {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_TgaParser_delete(self.raw.as_ptr()) }
}
}
impl TgaParser {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_TgaParser) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| TgaParser { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for TgaParser {}
impl core::fmt::Debug for TgaParser {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TgaParser").finish_non_exhaustive()
}
}
impl TgaParser {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_TgaParser_new();
Self::from_raw(raw).expect("native TgaParser allocation failed")
}
}
/// Parse a TGA byte buffer.
pub fn parse(&mut self, buffer: &[u8]) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_TgaParser_parse(
self.raw.as_ptr(),
buffer.as_ptr(),
buffer.len(),
))
}
}
/// @return true if the last parse produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_TgaParser_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last parse call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_TgaParser_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_TgaParser_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for TgaParser {
fn default() -> Self {
Self::new()
}
}
/// Encodes a Texture into TGA format.
pub struct TgaWriter {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_TgaWriter>,
}
impl Drop for TgaWriter {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_TgaWriter_delete(self.raw.as_ptr()) }
}
}
impl TgaWriter {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_TgaWriter) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| TgaWriter { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for TgaWriter {}
impl core::fmt::Debug for TgaWriter {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TgaWriter").finish_non_exhaustive()
}
}
impl TgaWriter {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_TgaWriter_new();
Self::from_raw(raw).expect("native TgaWriter allocation failed")
}
}
/// Serialize the texture to a TGA byte buffer.
pub fn write(&mut self, texture: &Texture) -> Bytes {
// SAFETY: handle is live for the duration of the call.
unsafe {
Bytes::from_raw(ffi::whiteout_textures_TgaWriter_write(
self.raw.as_ptr(),
texture.raw.as_ptr(),
))
.unwrap_or_else(Bytes::empty)
}
}
/// @return true if the last write produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_TgaWriter_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last write call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_TgaWriter_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_TgaWriter_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for TgaWriter {
fn default() -> Self {
Self::new()
}
}
/// Reads a TIFF file or byte buffer and decodes it into a Texture.
pub struct TiffParser {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_TiffParser>,
}
impl Drop for TiffParser {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_TiffParser_delete(self.raw.as_ptr()) }
}
}
impl TiffParser {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_TiffParser) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| TiffParser { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for TiffParser {}
impl core::fmt::Debug for TiffParser {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TiffParser").finish_non_exhaustive()
}
}
impl TiffParser {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_TiffParser_new();
Self::from_raw(raw).expect("native TiffParser allocation failed")
}
}
/// Parse a TIFF byte buffer.
pub fn parse(&mut self, buffer: &[u8]) -> Option<Texture> {
// SAFETY: handle is live for the duration of the call.
unsafe {
Texture::from_raw(ffi::whiteout_textures_TiffParser_parse(
self.raw.as_ptr(),
buffer.as_ptr(),
buffer.len(),
))
}
}
/// @return true if the last parse produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_TiffParser_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last parse call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_TiffParser_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_TiffParser_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for TiffParser {
fn default() -> Self {
Self::new()
}
}
/// Encodes a Texture into TIFF format.
pub struct TiffWriter {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_TiffWriter>,
}
impl Drop for TiffWriter {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_TiffWriter_delete(self.raw.as_ptr()) }
}
}
impl TiffWriter {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_TiffWriter) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| TiffWriter { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for TiffWriter {}
impl core::fmt::Debug for TiffWriter {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TiffWriter").finish_non_exhaustive()
}
}
impl TiffWriter {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_TiffWriter_new();
Self::from_raw(raw).expect("native TiffWriter allocation failed")
}
}
/// Serialize the texture to a TIFF byte buffer.
pub fn write(&mut self, texture: &Texture) -> Bytes {
// SAFETY: handle is live for the duration of the call.
unsafe {
Bytes::from_raw(ffi::whiteout_textures_TiffWriter_write(
self.raw.as_ptr(),
texture.raw.as_ptr(),
))
.unwrap_or_else(Bytes::empty)
}
}
/// @return true if the last write produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_TiffWriter_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last write call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_TiffWriter_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_TiffWriter_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for TiffWriter {
fn default() -> Self {
Self::new()
}
}
/// Per-write options for GIF encoding.
#[derive(Clone, Debug, PartialEq)]
pub struct GifSaveOptions {
/// Delay between frames in centiseconds (1/100 s). 0 = unspecified.
pub delay_cs: u16,
/// Number of times the animation should loop. 0 = loop forever.
pub loop_count: u16,
/// Enable blue-noise ordered dithering when mapping pixels to the palette.
pub dither: bool,
/// Dither strength in `[0, 1]`. 0 = no visible dithering, 1 = full.
pub dither_strength: f32,
/// Emit a transparent background. Pixels whose source alpha is below 50% become the GIF's transparent palette index; the rest are quantised normally. GIF transparency is 1-bit, so partially-covered (anti- aliased) edge pixels are forced fully opaque or fully transparent.
pub transparent: bool,
}
impl Default for GifSaveOptions {
fn default() -> Self {
// SAFETY: `_new` always returns a live handle; freed before return.
unsafe {
let h = ffi::whiteout_textures_GifSaveOptions_new();
let out = GifSaveOptions {
delay_cs: ffi::whiteout_textures_GifSaveOptions_get_delayCs(h),
loop_count: ffi::whiteout_textures_GifSaveOptions_get_loopCount(h),
dither: ffi::whiteout_textures_GifSaveOptions_get_dither(h) != 0,
dither_strength: ffi::whiteout_textures_GifSaveOptions_get_ditherStrength(h),
transparent: ffi::whiteout_textures_GifSaveOptions_get_transparent(h) != 0,
};
ffi::whiteout_textures_GifSaveOptions_delete(h);
out
}
}
}
impl GifSaveOptions {
/// Build a native handle carrying these values. Caller frees it.
#[allow(dead_code)] // consumed once the methods taking these options bind
pub(crate) unsafe fn to_native(&self) -> *mut ffi::whiteout_GifSaveOptions {
unsafe {
let h = ffi::whiteout_textures_GifSaveOptions_new();
ffi::whiteout_textures_GifSaveOptions_set_delayCs(h, self.delay_cs);
ffi::whiteout_textures_GifSaveOptions_set_loopCount(h, self.loop_count);
ffi::whiteout_textures_GifSaveOptions_set_dither(h, if self.dither { 1 } else { 0 });
ffi::whiteout_textures_GifSaveOptions_set_ditherStrength(h, self.dither_strength);
ffi::whiteout_textures_GifSaveOptions_set_transparent(
h,
if self.transparent { 1 } else { 0 },
);
h
}
}
/// Free a handle produced by [`Self::to_native`].
///
/// # Safety
/// `h` must have come from `to_native` and not been freed already.
#[allow(dead_code)]
pub(crate) unsafe fn free_native(h: *mut ffi::whiteout_GifSaveOptions) {
unsafe { ffi::whiteout_textures_GifSaveOptions_delete(h) }
}
}
/// Encodes a sequence of Texture frames into GIF89a format.
///
/// Unlike the single-image writers (BMP, TGA, …), this writer accepts a vector of frames. It does **not** inherit from `textures::Writer`.
pub struct GifWriter {
pub(crate) raw: core::ptr::NonNull<ffi::whiteout_GifWriter>,
}
impl Drop for GifWriter {
fn drop(&mut self) {
// SAFETY: `raw` came from a native constructor and Drop runs once.
unsafe { ffi::whiteout_textures_GifWriter_delete(self.raw.as_ptr()) }
}
}
impl GifWriter {
/// # Safety
/// `raw` must be a live handle this value takes ownership of.
#[allow(dead_code)] // used by whichever methods return this type
pub(crate) unsafe fn from_raw(raw: *mut ffi::whiteout_GifWriter) -> Option<Self> {
core::ptr::NonNull::new(raw).map(|raw| GifWriter { raw })
}
}
// SAFETY: handles are plain heap pointers with no thread affinity. `Sync`
// is deliberately NOT implemented — the C++ types make no documented
// guarantee about concurrent use, and claiming one we haven't verified
// would be unsound. See `@bind thread_safe` in the plan.
unsafe impl Send for GifWriter {}
impl core::fmt::Debug for GifWriter {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("GifWriter").finish_non_exhaustive()
}
}
impl GifWriter {
/// # Panics
/// Panics if the native allocation fails.
pub fn new() -> Self {
// SAFETY: the native constructor returns a live handle; a null here
// means the library is unusable.
unsafe {
let raw = ffi::whiteout_textures_GifWriter_new();
Self::from_raw(raw).expect("native GifWriter allocation failed")
}
}
/// Write frames to a GIF file on disk using default options.
pub fn write(&mut self, file_path: &str, frames: &[&Texture]) {
let file_path_cstr = std::ffi::CString::new(file_path).unwrap_or_default();
let frames_ptrs: Vec<_> = frames.iter().map(|v| v.raw.as_ptr()).collect();
// SAFETY: handle is live for the duration of the call.
unsafe {
ffi::whiteout_textures_GifWriter_write(
self.raw.as_ptr(),
file_path_cstr.as_ptr(),
frames_ptrs.as_ptr(),
frames.len(),
);
}
}
/// Write frames to a GIF byte buffer using default options.
pub fn write_frames(&mut self, frames: &[&Texture]) -> Bytes {
let frames_ptrs: Vec<_> = frames.iter().map(|v| v.raw.as_ptr()).collect();
// SAFETY: handle is live for the duration of the call.
unsafe {
Bytes::from_raw(ffi::whiteout_textures_GifWriter_write_frames(
self.raw.as_ptr(),
frames_ptrs.as_ptr(),
frames.len(),
))
.unwrap_or_else(Bytes::empty)
}
}
/// Write frames to a GIF file on disk with explicit options.
pub fn write_file_path_frames_opts(
&mut self,
file_path: &str,
frames: &[&Texture],
opts: &GifSaveOptions,
) {
let file_path_cstr = std::ffi::CString::new(file_path).unwrap_or_default();
let frames_ptrs: Vec<_> = frames.iter().map(|v| v.raw.as_ptr()).collect();
let opts_native = unsafe { opts.to_native() };
// SAFETY: handle is live for the call; the staged
// option handles are freed immediately after.
unsafe {
ffi::whiteout_textures_GifWriter_write_filePath_frames_opts(
self.raw.as_ptr(),
file_path_cstr.as_ptr(),
frames_ptrs.as_ptr(),
frames.len(),
opts_native,
);
GifSaveOptions::free_native(opts_native);
}
}
/// Write frames to a GIF byte buffer with explicit options.
pub fn write_frames_opts(&mut self, frames: &[&Texture], opts: &GifSaveOptions) -> Bytes {
let frames_ptrs: Vec<_> = frames.iter().map(|v| v.raw.as_ptr()).collect();
let opts_native = unsafe { opts.to_native() };
// SAFETY: handle is live for the call; the staged
// option handles are freed immediately after.
unsafe {
let __r = Bytes::from_raw(ffi::whiteout_textures_GifWriter_write_frames_opts(
self.raw.as_ptr(),
frames_ptrs.as_ptr(),
frames.len(),
opts_native,
))
.unwrap_or_else(Bytes::empty);
GifSaveOptions::free_native(opts_native);
__r
}
}
/// @return true if the last write produced any issues.
pub fn has_issues(&self) -> bool {
// SAFETY: handle is live for the duration of the call.
unsafe { ffi::whiteout_textures_GifWriter_hasIssues(self.raw.as_ptr()) != 0 }
}
/// @return accumulated issues from the last write call.
pub fn issues(&self) -> Vec<String> {
// SAFETY: index stays below the reported count.
unsafe {
let n = ffi::whiteout_textures_GifWriter_getIssues_count(self.raw.as_ptr());
(0..n)
.map(|i| {
crate::support::take_string(ffi::whiteout_textures_GifWriter_getIssues_at(
self.raw.as_ptr(),
i,
))
})
.collect()
}
}
}
impl Default for GifWriter {
fn default() -> Self {
Self::new()
}
}
/// Value-ABI mutable span accessors (`bindings/c/whiteout_v.h`).
#[doc(hidden)]
pub mod tier_a {
#![allow(missing_debug_implementations)]
#[repr(C)]
pub struct Opaque {
_private: [u8; 0],
}
extern "C" {
pub fn whiteout_v_Texture_data_mut(self_: *mut Opaque, out_size: *mut usize) -> *mut u8;
pub fn whiteout_v_Texture_mipData_mut(
self_: *mut Opaque,
mip: u32,
layer: u32,
out_size: *mut usize,
) -> *mut u8;
}
}
#[doc(hidden)]
pub mod ffi {
#![allow(missing_debug_implementations)]
#[allow(unused_imports)]
use crate::support::{RawBytes, RawCString};
#[repr(C)]
pub struct whiteout_TextureList {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_MipLevel {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_Texture {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_BlpParser {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_BlpWriter {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_PngApngFrameInfo {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_PngParser {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_PngApngFrame {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_PngApngSaveOptions {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_PngWriter {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_JpegParser {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_JpegWriter {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_DdsParser {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_DdsWriter {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_BmpParser {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_BmpWriter {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_TgaParser {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_TgaWriter {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_TiffParser {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_TiffWriter {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_GifSaveOptions {
_private: [u8; 0],
}
#[repr(C)]
pub struct whiteout_GifWriter {
_private: [u8; 0],
}
extern "C" {
pub fn whiteout_textures_TextureList_size(self_: *mut whiteout_TextureList) -> usize;
pub fn whiteout_textures_TextureList_at(
self_: *mut whiteout_TextureList,
index: usize,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_TextureList_delete(self_: *mut whiteout_TextureList);
// MipLevel
pub fn whiteout_textures_MipLevel_new() -> *mut whiteout_MipLevel;
pub fn whiteout_textures_MipLevel_delete(self_: *mut whiteout_MipLevel);
pub fn whiteout_textures_MipLevel_get_width(self_: *mut whiteout_MipLevel) -> u32;
pub fn whiteout_textures_MipLevel_set_width(self_: *mut whiteout_MipLevel, value: u32);
pub fn whiteout_textures_MipLevel_get_height(self_: *mut whiteout_MipLevel) -> u32;
pub fn whiteout_textures_MipLevel_set_height(self_: *mut whiteout_MipLevel, value: u32);
pub fn whiteout_textures_MipLevel_get_depth(self_: *mut whiteout_MipLevel) -> u32;
pub fn whiteout_textures_MipLevel_set_depth(self_: *mut whiteout_MipLevel, value: u32);
pub fn whiteout_textures_MipLevel_get_offset(self_: *mut whiteout_MipLevel) -> u64;
pub fn whiteout_textures_MipLevel_set_offset(self_: *mut whiteout_MipLevel, value: u64);
pub fn whiteout_textures_MipLevel_get_size(self_: *mut whiteout_MipLevel) -> u64;
pub fn whiteout_textures_MipLevel_set_size(self_: *mut whiteout_MipLevel, value: u64);
// Texture
pub fn whiteout_textures_Texture_new() -> *mut whiteout_Texture;
pub fn whiteout_textures_Texture_delete(self_: *mut whiteout_Texture);
pub fn whiteout_textures_Texture_format(self_: *mut whiteout_Texture, new_fmt: i32);
pub fn whiteout_textures_Texture_format_overload2(self_: *mut whiteout_Texture) -> i32;
pub fn whiteout_textures_Texture_copyAsFormat(
self_: *mut whiteout_Texture,
new_fmt: i32,
pool: *mut core::ffi::c_void,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_Texture_swapChannels(
self_: *mut whiteout_Texture,
a: i32,
b: i32,
) -> i32;
pub fn whiteout_textures_Texture_invertChannel(
self_: *mut whiteout_Texture,
ch: i32,
) -> i32;
pub fn whiteout_textures_Texture_expandNormal(
self_: *mut whiteout_Texture,
x_channel: i32,
y_channel: i32,
z_channel: i32,
) -> i32;
pub fn whiteout_textures_Texture_fillChannel(
self_: *mut whiteout_Texture,
target: i32,
value: f32,
) -> i32;
pub fn whiteout_textures_Texture_splitChannels(
self_: *mut whiteout_Texture,
channels: *const i32,
channels_size: usize,
) -> *mut whiteout_TextureList;
pub fn whiteout_textures_Texture_mergeChannels(
sources: *const *mut whiteout_Texture,
sources_size: usize,
target_channels: *const i32,
target_channels_size: usize,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_Texture_copyFromNormalToRGBA(
self_: *mut whiteout_Texture,
pool: *mut core::ffi::c_void,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_Texture_generateMipmaps(
self_: *mut whiteout_Texture,
new_mip_count: u32,
pool: *mut core::ffi::c_void,
) -> RawCString;
pub fn whiteout_textures_Texture_generateMipmaps_pool(
self_: *mut whiteout_Texture,
pool: *mut core::ffi::c_void,
) -> RawCString;
pub fn whiteout_textures_Texture_downscale(
self_: *mut whiteout_Texture,
levels: u32,
pool: *mut core::ffi::c_void,
) -> RawCString;
pub fn whiteout_textures_Texture_create2D(
fmt: i32,
width: u32,
height: u32,
mip_count: u32,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_Texture_create3D(
fmt: i32,
width: u32,
height: u32,
depth: u32,
mip_count: u32,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_Texture_createCube(
fmt: i32,
size: u32,
mip_count: u32,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_Texture_create2DArray(
fmt: i32,
width: u32,
height: u32,
array_size: u32,
mip_count: u32,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_Texture_createCubeArray(
fmt: i32,
size: u32,
array_size: u32,
mip_count: u32,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_Texture_type(self_: *mut whiteout_Texture) -> i32;
pub fn whiteout_textures_Texture_kind(self_: *mut whiteout_Texture) -> i32;
pub fn whiteout_textures_Texture_setKind(self_: *mut whiteout_Texture, k: i32);
pub fn whiteout_textures_Texture_channelKind(self_: *mut whiteout_Texture, ch: i32) -> i32;
pub fn whiteout_textures_Texture_setChannelKind(
self_: *mut whiteout_Texture,
ch: i32,
kind: i32,
);
pub fn whiteout_textures_Texture_channelDefault(
self_: *mut whiteout_Texture,
ch: i32,
) -> f32;
pub fn whiteout_textures_Texture_setChannelDefault(
self_: *mut whiteout_Texture,
ch: i32,
value: f32,
);
pub fn whiteout_textures_Texture_isSrgb(self_: *mut whiteout_Texture) -> i32;
pub fn whiteout_textures_Texture_setSrgb(self_: *mut whiteout_Texture, srgb: i32);
pub fn whiteout_textures_Texture_width(self_: *mut whiteout_Texture) -> u32;
pub fn whiteout_textures_Texture_height(self_: *mut whiteout_Texture) -> u32;
pub fn whiteout_textures_Texture_depth(self_: *mut whiteout_Texture) -> u32;
pub fn whiteout_textures_Texture_layerCount(self_: *mut whiteout_Texture) -> u32;
pub fn whiteout_textures_Texture_arraySize(self_: *mut whiteout_Texture) -> u32;
pub fn whiteout_textures_Texture_mipCount(self_: *mut whiteout_Texture) -> u32;
pub fn whiteout_textures_Texture_mipLevel(
self_: *mut whiteout_Texture,
mip: u32,
layer: u32,
) -> *mut whiteout_MipLevel;
pub fn whiteout_textures_Texture_dataSize(self_: *mut whiteout_Texture) -> u64;
pub fn whiteout_textures_Texture_data(self_: *mut whiteout_Texture) -> RawBytes;
pub fn whiteout_textures_Texture_mipData(
self_: *mut whiteout_Texture,
mip: u32,
layer: u32,
) -> RawBytes;
pub fn whiteout_textures_Texture_takeData(self_: *mut whiteout_Texture) -> RawBytes;
pub fn whiteout_textures_Texture_setData(
self_: *mut whiteout_Texture,
new_data: *const u8,
new_data_size: usize,
);
// BlpParser
pub fn whiteout_textures_BlpParser_new() -> *mut whiteout_BlpParser;
pub fn whiteout_textures_BlpParser_delete(self_: *mut whiteout_BlpParser);
pub fn whiteout_textures_BlpParser_parse(
self_: *mut whiteout_BlpParser,
buffer: *const u8,
buffer_size: usize,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_BlpParser_hasIssues(self_: *mut whiteout_BlpParser) -> i32;
pub fn whiteout_textures_BlpParser_getIssues_count(self_: *mut whiteout_BlpParser)
-> usize;
pub fn whiteout_textures_BlpParser_getIssues_at(
self_: *mut whiteout_BlpParser,
index: usize,
) -> RawCString;
// BlpWriter
pub fn whiteout_textures_BlpWriter_new() -> *mut whiteout_BlpWriter;
pub fn whiteout_textures_BlpWriter_new_pool(
_0: *mut core::ffi::c_void,
) -> *mut whiteout_BlpWriter;
pub fn whiteout_textures_BlpWriter_delete(self_: *mut whiteout_BlpWriter);
pub fn whiteout_textures_BlpWriter_write(
self_: *mut whiteout_BlpWriter,
texture: *mut whiteout_Texture,
) -> RawBytes;
pub fn whiteout_textures_BlpWriter_hasIssues(self_: *mut whiteout_BlpWriter) -> i32;
pub fn whiteout_textures_BlpWriter_getIssues_count(self_: *mut whiteout_BlpWriter)
-> usize;
pub fn whiteout_textures_BlpWriter_getIssues_at(
self_: *mut whiteout_BlpWriter,
index: usize,
) -> RawCString;
// PngApngFrameInfo
pub fn whiteout_textures_PngApngFrameInfo_new() -> *mut whiteout_PngApngFrameInfo;
pub fn whiteout_textures_PngApngFrameInfo_delete(self_: *mut whiteout_PngApngFrameInfo);
pub fn whiteout_textures_PngApngFrameInfo_get_width(
self_: *mut whiteout_PngApngFrameInfo,
) -> u32;
pub fn whiteout_textures_PngApngFrameInfo_set_width(
self_: *mut whiteout_PngApngFrameInfo,
value: u32,
);
pub fn whiteout_textures_PngApngFrameInfo_get_height(
self_: *mut whiteout_PngApngFrameInfo,
) -> u32;
pub fn whiteout_textures_PngApngFrameInfo_set_height(
self_: *mut whiteout_PngApngFrameInfo,
value: u32,
);
pub fn whiteout_textures_PngApngFrameInfo_get_xOffset(
self_: *mut whiteout_PngApngFrameInfo,
) -> u32;
pub fn whiteout_textures_PngApngFrameInfo_set_xOffset(
self_: *mut whiteout_PngApngFrameInfo,
value: u32,
);
pub fn whiteout_textures_PngApngFrameInfo_get_yOffset(
self_: *mut whiteout_PngApngFrameInfo,
) -> u32;
pub fn whiteout_textures_PngApngFrameInfo_set_yOffset(
self_: *mut whiteout_PngApngFrameInfo,
value: u32,
);
pub fn whiteout_textures_PngApngFrameInfo_get_delayMs(
self_: *mut whiteout_PngApngFrameInfo,
) -> u32;
pub fn whiteout_textures_PngApngFrameInfo_set_delayMs(
self_: *mut whiteout_PngApngFrameInfo,
value: u32,
);
pub fn whiteout_textures_PngApngFrameInfo_get_disposeOp(
self_: *mut whiteout_PngApngFrameInfo,
) -> u32;
pub fn whiteout_textures_PngApngFrameInfo_set_disposeOp(
self_: *mut whiteout_PngApngFrameInfo,
value: u32,
);
pub fn whiteout_textures_PngApngFrameInfo_get_blendOp(
self_: *mut whiteout_PngApngFrameInfo,
) -> u32;
pub fn whiteout_textures_PngApngFrameInfo_set_blendOp(
self_: *mut whiteout_PngApngFrameInfo,
value: u32,
);
// PngParser
pub fn whiteout_textures_PngParser_new() -> *mut whiteout_PngParser;
pub fn whiteout_textures_PngParser_delete(self_: *mut whiteout_PngParser);
pub fn whiteout_textures_PngParser_parse(
self_: *mut whiteout_PngParser,
buffer: *const u8,
buffer_size: usize,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_PngParser_hasIssues(self_: *mut whiteout_PngParser) -> i32;
pub fn whiteout_textures_PngParser_getIssues_count(self_: *mut whiteout_PngParser)
-> usize;
pub fn whiteout_textures_PngParser_getIssues_at(
self_: *mut whiteout_PngParser,
index: usize,
) -> RawCString;
pub fn whiteout_textures_PngParser_isAnimated(self_: *mut whiteout_PngParser) -> i32;
pub fn whiteout_textures_PngParser_frameCount(self_: *mut whiteout_PngParser) -> u32;
pub fn whiteout_textures_PngParser_loopCount(self_: *mut whiteout_PngParser) -> u32;
pub fn whiteout_textures_PngParser_frame(
self_: *mut whiteout_PngParser,
index: u32,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_PngParser_frameDelayMs(
self_: *mut whiteout_PngParser,
index: u32,
) -> u32;
pub fn whiteout_textures_PngParser_frameInfo(
self_: *mut whiteout_PngParser,
index: u32,
) -> *mut whiteout_PngApngFrameInfo;
// PngApngFrame
pub fn whiteout_textures_PngApngFrame_new() -> *mut whiteout_PngApngFrame;
pub fn whiteout_textures_PngApngFrame_delete(self_: *mut whiteout_PngApngFrame);
pub fn whiteout_textures_PngApngFrame_get_image(
self_: *mut whiteout_PngApngFrame,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_PngApngFrame_set_image(
self_: *mut whiteout_PngApngFrame,
value: *const whiteout_Texture,
);
pub fn whiteout_textures_PngApngFrame_get_delayMs(self_: *mut whiteout_PngApngFrame)
-> u32;
pub fn whiteout_textures_PngApngFrame_set_delayMs(
self_: *mut whiteout_PngApngFrame,
value: u32,
);
// PngApngSaveOptions
pub fn whiteout_textures_PngApngSaveOptions_new() -> *mut whiteout_PngApngSaveOptions;
pub fn whiteout_textures_PngApngSaveOptions_delete(self_: *mut whiteout_PngApngSaveOptions);
pub fn whiteout_textures_PngApngSaveOptions_get_loopCount(
self_: *mut whiteout_PngApngSaveOptions,
) -> u32;
pub fn whiteout_textures_PngApngSaveOptions_set_loopCount(
self_: *mut whiteout_PngApngSaveOptions,
value: u32,
);
// PngWriter
pub fn whiteout_textures_PngWriter_new() -> *mut whiteout_PngWriter;
pub fn whiteout_textures_PngWriter_delete(self_: *mut whiteout_PngWriter);
pub fn whiteout_textures_PngWriter_write(
self_: *mut whiteout_PngWriter,
texture: *mut whiteout_Texture,
) -> RawBytes;
pub fn whiteout_textures_PngWriter_writeAnimated(
self_: *mut whiteout_PngWriter,
frames: *const *mut whiteout_PngApngFrame,
frames_size: usize,
opts: *mut whiteout_PngApngSaveOptions,
) -> RawBytes;
pub fn whiteout_textures_PngWriter_hasIssues(self_: *mut whiteout_PngWriter) -> i32;
pub fn whiteout_textures_PngWriter_getIssues_count(self_: *mut whiteout_PngWriter)
-> usize;
pub fn whiteout_textures_PngWriter_getIssues_at(
self_: *mut whiteout_PngWriter,
index: usize,
) -> RawCString;
// JpegParser
pub fn whiteout_textures_JpegParser_new() -> *mut whiteout_JpegParser;
pub fn whiteout_textures_JpegParser_new_pool(
_0: *mut core::ffi::c_void,
) -> *mut whiteout_JpegParser;
pub fn whiteout_textures_JpegParser_delete(self_: *mut whiteout_JpegParser);
pub fn whiteout_textures_JpegParser_parse(
self_: *mut whiteout_JpegParser,
buffer: *const u8,
buffer_size: usize,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_JpegParser_hasIssues(self_: *mut whiteout_JpegParser) -> i32;
pub fn whiteout_textures_JpegParser_getIssues_count(
self_: *mut whiteout_JpegParser,
) -> usize;
pub fn whiteout_textures_JpegParser_getIssues_at(
self_: *mut whiteout_JpegParser,
index: usize,
) -> RawCString;
// JpegWriter
pub fn whiteout_textures_JpegWriter_new() -> *mut whiteout_JpegWriter;
pub fn whiteout_textures_JpegWriter_new_quality_pool_progressive(
_0: *mut core::ffi::c_void,
_1: *mut core::ffi::c_void,
_2: *mut core::ffi::c_void,
) -> *mut whiteout_JpegWriter;
pub fn whiteout_textures_JpegWriter_delete(self_: *mut whiteout_JpegWriter);
pub fn whiteout_textures_JpegWriter_write(
self_: *mut whiteout_JpegWriter,
texture: *mut whiteout_Texture,
) -> RawBytes;
pub fn whiteout_textures_JpegWriter_hasIssues(self_: *mut whiteout_JpegWriter) -> i32;
pub fn whiteout_textures_JpegWriter_getIssues_count(
self_: *mut whiteout_JpegWriter,
) -> usize;
pub fn whiteout_textures_JpegWriter_getIssues_at(
self_: *mut whiteout_JpegWriter,
index: usize,
) -> RawCString;
// DdsParser
pub fn whiteout_textures_DdsParser_new() -> *mut whiteout_DdsParser;
pub fn whiteout_textures_DdsParser_delete(self_: *mut whiteout_DdsParser);
pub fn whiteout_textures_DdsParser_parse(
self_: *mut whiteout_DdsParser,
buffer: *const u8,
buffer_size: usize,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_DdsParser_hasIssues(self_: *mut whiteout_DdsParser) -> i32;
pub fn whiteout_textures_DdsParser_getIssues_count(self_: *mut whiteout_DdsParser)
-> usize;
pub fn whiteout_textures_DdsParser_getIssues_at(
self_: *mut whiteout_DdsParser,
index: usize,
) -> RawCString;
// DdsWriter
pub fn whiteout_textures_DdsWriter_new() -> *mut whiteout_DdsWriter;
pub fn whiteout_textures_DdsWriter_delete(self_: *mut whiteout_DdsWriter);
pub fn whiteout_textures_DdsWriter_write(
self_: *mut whiteout_DdsWriter,
texture: *mut whiteout_Texture,
) -> RawBytes;
pub fn whiteout_textures_DdsWriter_hasIssues(self_: *mut whiteout_DdsWriter) -> i32;
pub fn whiteout_textures_DdsWriter_getIssues_count(self_: *mut whiteout_DdsWriter)
-> usize;
pub fn whiteout_textures_DdsWriter_getIssues_at(
self_: *mut whiteout_DdsWriter,
index: usize,
) -> RawCString;
// BmpParser
pub fn whiteout_textures_BmpParser_new() -> *mut whiteout_BmpParser;
pub fn whiteout_textures_BmpParser_delete(self_: *mut whiteout_BmpParser);
pub fn whiteout_textures_BmpParser_parse(
self_: *mut whiteout_BmpParser,
buffer: *const u8,
buffer_size: usize,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_BmpParser_hasIssues(self_: *mut whiteout_BmpParser) -> i32;
pub fn whiteout_textures_BmpParser_getIssues_count(self_: *mut whiteout_BmpParser)
-> usize;
pub fn whiteout_textures_BmpParser_getIssues_at(
self_: *mut whiteout_BmpParser,
index: usize,
) -> RawCString;
// BmpWriter
pub fn whiteout_textures_BmpWriter_new() -> *mut whiteout_BmpWriter;
pub fn whiteout_textures_BmpWriter_delete(self_: *mut whiteout_BmpWriter);
pub fn whiteout_textures_BmpWriter_write(
self_: *mut whiteout_BmpWriter,
texture: *mut whiteout_Texture,
) -> RawBytes;
pub fn whiteout_textures_BmpWriter_hasIssues(self_: *mut whiteout_BmpWriter) -> i32;
pub fn whiteout_textures_BmpWriter_getIssues_count(self_: *mut whiteout_BmpWriter)
-> usize;
pub fn whiteout_textures_BmpWriter_getIssues_at(
self_: *mut whiteout_BmpWriter,
index: usize,
) -> RawCString;
// TgaParser
pub fn whiteout_textures_TgaParser_new() -> *mut whiteout_TgaParser;
pub fn whiteout_textures_TgaParser_delete(self_: *mut whiteout_TgaParser);
pub fn whiteout_textures_TgaParser_parse(
self_: *mut whiteout_TgaParser,
buffer: *const u8,
buffer_size: usize,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_TgaParser_hasIssues(self_: *mut whiteout_TgaParser) -> i32;
pub fn whiteout_textures_TgaParser_getIssues_count(self_: *mut whiteout_TgaParser)
-> usize;
pub fn whiteout_textures_TgaParser_getIssues_at(
self_: *mut whiteout_TgaParser,
index: usize,
) -> RawCString;
// TgaWriter
pub fn whiteout_textures_TgaWriter_new() -> *mut whiteout_TgaWriter;
pub fn whiteout_textures_TgaWriter_delete(self_: *mut whiteout_TgaWriter);
pub fn whiteout_textures_TgaWriter_write(
self_: *mut whiteout_TgaWriter,
texture: *mut whiteout_Texture,
) -> RawBytes;
pub fn whiteout_textures_TgaWriter_hasIssues(self_: *mut whiteout_TgaWriter) -> i32;
pub fn whiteout_textures_TgaWriter_getIssues_count(self_: *mut whiteout_TgaWriter)
-> usize;
pub fn whiteout_textures_TgaWriter_getIssues_at(
self_: *mut whiteout_TgaWriter,
index: usize,
) -> RawCString;
// TiffParser
pub fn whiteout_textures_TiffParser_new() -> *mut whiteout_TiffParser;
pub fn whiteout_textures_TiffParser_delete(self_: *mut whiteout_TiffParser);
pub fn whiteout_textures_TiffParser_parse(
self_: *mut whiteout_TiffParser,
buffer: *const u8,
buffer_size: usize,
) -> *mut whiteout_Texture;
pub fn whiteout_textures_TiffParser_hasIssues(self_: *mut whiteout_TiffParser) -> i32;
pub fn whiteout_textures_TiffParser_getIssues_count(
self_: *mut whiteout_TiffParser,
) -> usize;
pub fn whiteout_textures_TiffParser_getIssues_at(
self_: *mut whiteout_TiffParser,
index: usize,
) -> RawCString;
// TiffWriter
pub fn whiteout_textures_TiffWriter_new() -> *mut whiteout_TiffWriter;
pub fn whiteout_textures_TiffWriter_delete(self_: *mut whiteout_TiffWriter);
pub fn whiteout_textures_TiffWriter_write(
self_: *mut whiteout_TiffWriter,
texture: *mut whiteout_Texture,
) -> RawBytes;
pub fn whiteout_textures_TiffWriter_hasIssues(self_: *mut whiteout_TiffWriter) -> i32;
pub fn whiteout_textures_TiffWriter_getIssues_count(
self_: *mut whiteout_TiffWriter,
) -> usize;
pub fn whiteout_textures_TiffWriter_getIssues_at(
self_: *mut whiteout_TiffWriter,
index: usize,
) -> RawCString;
// GifSaveOptions
pub fn whiteout_textures_GifSaveOptions_new() -> *mut whiteout_GifSaveOptions;
pub fn whiteout_textures_GifSaveOptions_delete(self_: *mut whiteout_GifSaveOptions);
pub fn whiteout_textures_GifSaveOptions_get_delayCs(
self_: *mut whiteout_GifSaveOptions,
) -> u16;
pub fn whiteout_textures_GifSaveOptions_set_delayCs(
self_: *mut whiteout_GifSaveOptions,
value: u16,
);
pub fn whiteout_textures_GifSaveOptions_get_loopCount(
self_: *mut whiteout_GifSaveOptions,
) -> u16;
pub fn whiteout_textures_GifSaveOptions_set_loopCount(
self_: *mut whiteout_GifSaveOptions,
value: u16,
);
pub fn whiteout_textures_GifSaveOptions_get_dither(
self_: *mut whiteout_GifSaveOptions,
) -> i32;
pub fn whiteout_textures_GifSaveOptions_set_dither(
self_: *mut whiteout_GifSaveOptions,
value: i32,
);
pub fn whiteout_textures_GifSaveOptions_get_ditherStrength(
self_: *mut whiteout_GifSaveOptions,
) -> f32;
pub fn whiteout_textures_GifSaveOptions_set_ditherStrength(
self_: *mut whiteout_GifSaveOptions,
value: f32,
);
pub fn whiteout_textures_GifSaveOptions_get_transparent(
self_: *mut whiteout_GifSaveOptions,
) -> i32;
pub fn whiteout_textures_GifSaveOptions_set_transparent(
self_: *mut whiteout_GifSaveOptions,
value: i32,
);
// GifWriter
pub fn whiteout_textures_GifWriter_new() -> *mut whiteout_GifWriter;
pub fn whiteout_textures_GifWriter_new_pool(
_0: *mut core::ffi::c_void,
) -> *mut whiteout_GifWriter;
pub fn whiteout_textures_GifWriter_delete(self_: *mut whiteout_GifWriter);
pub fn whiteout_textures_GifWriter_write(
self_: *mut whiteout_GifWriter,
file_path: *const core::ffi::c_char,
frames: *const *mut whiteout_Texture,
frames_size: usize,
);
pub fn whiteout_textures_GifWriter_write_frames(
self_: *mut whiteout_GifWriter,
frames: *const *mut whiteout_Texture,
frames_size: usize,
) -> RawBytes;
pub fn whiteout_textures_GifWriter_write_filePath_frames_opts(
self_: *mut whiteout_GifWriter,
file_path: *const core::ffi::c_char,
frames: *const *mut whiteout_Texture,
frames_size: usize,
opts: *mut whiteout_GifSaveOptions,
);
pub fn whiteout_textures_GifWriter_write_frames_opts(
self_: *mut whiteout_GifWriter,
frames: *const *mut whiteout_Texture,
frames_size: usize,
opts: *mut whiteout_GifSaveOptions,
) -> RawBytes;
pub fn whiteout_textures_GifWriter_hasIssues(self_: *mut whiteout_GifWriter) -> i32;
pub fn whiteout_textures_GifWriter_getIssues_count(self_: *mut whiteout_GifWriter)
-> usize;
pub fn whiteout_textures_GifWriter_getIssues_at(
self_: *mut whiteout_GifWriter,
index: usize,
) -> RawCString;
}
}