#![allow(nonstandard_style)]
use cfg_if::cfg_if;
#[cfg(feature = "nvtt_image_integration")]
use image::{Bgra, DynamicImage, ImageBuffer, Luma, Rgba};
use libc::{c_int, c_uint, c_void};
use log::{error, trace};
#[cfg(feature = "nvtt_image_integration")]
use maybe_owned::MaybeOwned;
use nvtt_sys::*;
#[cfg(feature = "nvtt_image_integration")]
use safe_transmute::transmute_to_bytes;
#[cfg(feature = "nvtt_image_integration")]
use std::ops::Deref;
use std::{
cell::{Cell, RefCell},
cmp::PartialEq,
convert::TryFrom,
error::Error as ErrorTrait,
ffi::{CStr, CString, NulError},
fmt, mem,
path::Path,
ptr::NonNull,
slice, thread_local,
};
#[inline(always)]
pub const fn version() -> u32 {
NVTT_VERSION
}
macro_rules! decl_enum {
(
$(#[$($attr:meta)*])*
$v:vis enum $enum_name:ident: $raw:ident {
$(
$(#[$($brnch_attr:meta)*])*
$rust_nm:ident = $sys_nm:ident
),*
$(,)?
}
) => {
$(#[$($attr)*])*
$v enum $enum_name {
$(
$(#[$($brnch_attr)*])*
$rust_nm
),*
}
impl From<&'_ $enum_name> for $raw {
#[inline]
fn from(val: &'_ $enum_name) -> Self {
From::from(*val)
}
}
impl From<$enum_name> for $raw {
#[inline]
fn from(val: $enum_name) -> Self {
match val {
$(
$enum_name :: $rust_nm => $sys_nm
),*
}
}
}
impl TryFrom<$raw> for $enum_name {
type Error = ();
#[allow(unreachable_patterns)]
#[inline]
fn try_from(raw: $raw) -> Result<Self, Self::Error> {
match raw {
$(
$sys_nm => { Ok($enum_name::$rust_nm) }
)*
_ => Err(())
}
}
}
impl PartialEq<$raw> for $enum_name {
#[inline]
fn eq(&self, rhs: &$raw) -> bool {
let lhs: $raw = (*self).into();
lhs == *rhs
}
}
impl PartialEq<$enum_name> for $raw {
#[inline]
fn eq(&self, rhs: &$enum_name) -> bool {
let rhs: $raw = (*rhs).into();
*self == rhs
}
}
};
}
decl_enum! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Container: NvttContainer {
Dds = NvttContainer_NVTT_Container_DDS,
Dds10 = NvttContainer_NVTT_Container_DDS10,
Ktx = NvttContainer_NVTT_Container_KTX,
}
}
decl_enum! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum AlphaMode: NvttAlphaMode {
None = NvttAlphaMode_NVTT_AlphaMode_None,
Premultiplied = NvttAlphaMode_NVTT_AlphaMode_Premultiplied,
Transparency = NvttAlphaMode_NVTT_AlphaMode_Transparency,
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct KaiserParameters {
pub width: f32,
pub alpha: f32,
pub stretch: f32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MipmapFilter {
Box,
Triangle,
Kaiser(Option<KaiserParameters>),
}
impl From<&'_ MipmapFilter> for NvttMipmapFilter {
#[inline]
fn from(filter: &'_ MipmapFilter) -> Self {
match *filter {
MipmapFilter::Box => NvttMipmapFilter_NVTT_MipmapFilter_Box,
MipmapFilter::Triangle => NvttMipmapFilter_NVTT_MipmapFilter_Triangle,
MipmapFilter::Kaiser(_) => NvttMipmapFilter_NVTT_MipmapFilter_Kaiser,
}
}
}
impl From<MipmapFilter> for NvttMipmapFilter {
#[inline]
fn from(filter: MipmapFilter) -> Self {
From::from(&filter)
}
}
impl From<KaiserParameters> for MipmapFilter {
#[inline]
fn from(params: KaiserParameters) -> Self {
MipmapFilter::Kaiser(Some(params))
}
}
impl Default for MipmapFilter {
#[inline]
fn default() -> Self {
MipmapFilter::Box
}
}
decl_enum! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Quality: NvttQuality {
Fastest = NvttQuality_NVTT_Quality_Fastest,
Highest = NvttQuality_NVTT_Quality_Highest,
Normal = NvttQuality_NVTT_Quality_Normal,
Production = NvttQuality_NVTT_Quality_Production,
}
}
impl Default for Quality {
#[inline]
fn default() -> Self {
Quality::Normal
}
}
decl_enum! {
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Format: NvttFormat {
Bc1 = NvttFormat_NVTT_Format_BC1,
Bc1a = NvttFormat_NVTT_Format_BC1a,
Bc2 = NvttFormat_NVTT_Format_BC2,
Bc3 = NvttFormat_NVTT_Format_BC3,
Bc3n = NvttFormat_NVTT_Format_BC3n,
Bc3_Rgbm = NvttFormat_NVTT_Format_BC3_RGBM,
Bc4 = NvttFormat_NVTT_Format_BC4,
Bc5 = NvttFormat_NVTT_Format_BC5,
Bc6 = NvttFormat_NVTT_Format_BC6,
Bc7 = NvttFormat_NVTT_Format_BC7,
Ctx1 = NvttFormat_NVTT_Format_CTX1,
Dxt1 = NvttFormat_NVTT_Format_DXT1,
Dxt1a = NvttFormat_NVTT_Format_DXT1a,
Dxt1n = NvttFormat_NVTT_Format_DXT1n,
Dxt3 = NvttFormat_NVTT_Format_DXT3,
Dxt5 = NvttFormat_NVTT_Format_DXT5,
Dxt5n = NvttFormat_NVTT_Format_DXT5n,
Etc1 = NvttFormat_NVTT_Format_ETC1,
Etc2_R = NvttFormat_NVTT_Format_ETC2_R,
Etc2_Rg = NvttFormat_NVTT_Format_ETC2_RG,
Etc2_Rgb = NvttFormat_NVTT_Format_ETC2_RGB,
Etc2_Rgba = NvttFormat_NVTT_Format_ETC2_RGBA,
Etc2_Rgbm = NvttFormat_NVTT_Format_ETC2_RGBM,
Etc2_Rgb_A1 = NvttFormat_NVTT_Format_ETC2_RGB_A1,
Pvr_2Bpp_Rgb = NvttFormat_NVTT_Format_PVR_2BPP_RGB,
Pvr_2Bpp_Rgba = NvttFormat_NVTT_Format_PVR_2BPP_RGBA,
Pvr_4Bpp_Rgb = NvttFormat_NVTT_Format_PVR_4BPP_RGB,
Pvr_4Bpp_Rgba = NvttFormat_NVTT_Format_PVR_4BPP_RGBA,
Rgb = NvttFormat_NVTT_Format_RGB,
Rgba = NvttFormat_NVTT_Format_RGBA,
}
}
impl Format {
#[inline]
pub fn file_extension(&self) -> Option<&Path> {
let in_range = |low: Format, high: Format| -> bool {
(low as u8..high as u8).contains(&(*self as u8))
};
match *self {
_ if in_range(Self::Dxt1, Self::Dxt5n) => Some(Path::new("dds")),
_ if in_range(Self::Pvr_2Bpp_Rgb, Self::Pvr_4Bpp_Rgba) => Some(Path::new("pvr")),
_ => None,
}
}
}
decl_enum! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum InputFormat: NvttInputFormat {
Bgra8Ub = NvttInputFormat_NVTT_InputFormat_BGRA_8UB,
Rgba16F = NvttInputFormat_NVTT_InputFormat_RGBA_16F,
Rgba32F = NvttInputFormat_NVTT_InputFormat_RGBA_32F,
R32F = NvttInputFormat_NVTT_InputFormat_R_32F,
}
}
decl_enum! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RoundMode: NvttRoundMode {
None = NvttRoundMode_NVTT_RoundMode_None,
ToNearestMultipleOfFour = NvttRoundMode_NVTT_RoundMode_ToNearestMultipleOfFour,
ToNearestPowerOfTwo = NvttRoundMode_NVTT_RoundMode_ToNearestPowerOfTwo,
ToNextMultipleOfFour = NvttRoundMode_NVTT_RoundMode_ToNextMultipleOfFour,
ToNextPowerOfTwo = NvttRoundMode_NVTT_RoundMode_ToNextPowerOfTwo,
ToPreviousMultipleOfFour = NvttRoundMode_NVTT_RoundMode_ToPreviousMultipleOfFour,
ToPreviousPowerOfTwo = NvttRoundMode_NVTT_RoundMode_ToPreviousPowerOfTwo,
}
}
impl Default for RoundMode {
#[inline]
fn default() -> Self {
RoundMode::None
}
}
decl_enum! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum TextureType: NvttTextureType {
_2D = NvttTextureType_NVTT_TextureType_2D,
Cube = NvttTextureType_NVTT_TextureType_Cube,
_3D = NvttTextureType_TextureType_3D,
Array = NvttTextureType_TextureType_Array,
}
}
impl Default for TextureType {
#[inline]
fn default() -> Self {
TextureType::_2D
}
}
decl_enum! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum WrapMode: NvttWrapMode {
Clamp = NvttWrapMode_NVTT_WrapMode_Clamp,
Mirror = NvttWrapMode_NVTT_WrapMode_Mirror,
Repeat = NvttWrapMode_NVTT_WrapMode_Repeat,
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct NormalMapFilter {
pub small: f32,
pub medium: f32,
pub big: f32,
pub large: f32,
}
impl NormalMapFilter {
#[inline]
pub const fn new(small: f32, medium: f32, big: f32, large: f32) -> Self {
Self {
small,
medium,
big,
large,
}
}
}
#[derive(Debug)]
pub struct Compressor(NonNull<NvttCompressor>);
impl Compressor {
#[inline]
pub fn new() -> Result<Self, Error> {
let compressor = unsafe { nvttCreateCompressor() };
NonNull::new(compressor).map(Self).ok_or(Error::Unknown)
}
#[inline]
pub fn into_raw(self) -> *mut NvttCompressor {
let ptr = self.0.as_ptr();
mem::forget(self);
ptr
}
#[inline]
pub fn enable_cuda_acceleration<B: Into<NvttBoolean>>(&mut self, enable: B) -> &mut Self {
unsafe {
nvttEnableCudaAcceleration(self.0.as_ptr(), enable.into());
}
self
}
#[inline]
pub fn is_cuda_acceleration_enabled(&self) -> bool {
unsafe { nvttIsCudaAccelerationEnabled(self.0.as_ptr()).into() }
}
pub fn compress(
&self,
compress_options: &CompressionOptions,
input_options: &InputOptions,
output_options: &OutputOptions,
) -> Result<CompressionOutput, Error> {
thread_local! {
static ERR: Cell<NvttError> = Cell::new(0);
static OUT_DATA: RefCell<Vec<u8>> = RefCell::new(vec![]);
static HEIGHT: Cell<usize> = Cell::new(0);
static WIDTH: Cell<usize> = Cell::new(0);
static DEPTH: Cell<usize> = Cell::new(0);
static FACE: Cell<usize> = Cell::new(0);
static MIPLEVEL: Cell<usize> = Cell::new(0);
}
extern "C" fn err_callback(err: NvttError) {
error!(
"nvtt: Encountered an error while compressing: {}",
Error::try_from(err).unwrap_or(Error::Unknown)
);
ERR.with(|e| e.set(err));
}
extern "C" fn output_begin_callback(
size: c_int,
width: c_int,
height: c_int,
depth: c_int,
face: c_int,
miplevel: c_int,
) {
trace!("Beginning texture compression with image size {} ({} x {} x {}), face = {}, mip = {}",
size, width, height, depth, face, miplevel);
OUT_DATA.with(|d| d.borrow_mut().reserve(size as _));
WIDTH.with(|w| w.set(width as _));
HEIGHT.with(|h| h.set(height as _));
DEPTH.with(|d| d.set(depth as _));
FACE.with(|f| f.set(face as _));
MIPLEVEL.with(|ml| ml.set(miplevel as _));
}
extern "C" fn output_callback(data_ptr: *const c_void, len: c_int) -> bool {
let len = match usize::try_from(len) {
Ok(len) => len,
Err(_) => {
error!("Could not append texture data: len {} is invalid", len);
return false;
}
};
let data = unsafe { slice::from_raw_parts(data_ptr as *const u8, len) };
OUT_DATA.with(|d| d.borrow_mut().extend_from_slice(data));
true
}
OUT_DATA.with(|d| d.borrow_mut().clear());
let res = unsafe {
let out_opts_ptr = output_options.out_opts.as_ptr();
nvttSetOutputOptionsErrorHandler(out_opts_ptr, Some(err_callback));
if !output_options.write_to_file {
nvttSetOutputOptionsOutputHandler(
out_opts_ptr,
Some(output_begin_callback),
Some(output_callback),
None,
);
}
nvttCompress(
self.0.as_ptr(),
input_options.0.as_ptr(),
compress_options.0.as_ptr(),
output_options.out_opts.as_ptr(),
)
};
if res != NvttBoolean::NVTT_True {
let mut err = 0;
ERR.with(|e| err = e.get());
Err(Error::try_from(err).unwrap_or(Error::Unknown))
} else {
if !output_options.write_to_file {
Ok(CompressionOutput::Memory {
data: OUT_DATA.with(|d| d.replace(vec![])),
width: WIDTH.with(|w| w.get()),
height: HEIGHT.with(|h| h.get()),
depth: DEPTH.with(|d| d.get()),
face: FACE.with(|f| f.get()),
miplevel: MIPLEVEL.with(|ml| ml.get()),
})
} else {
Ok(CompressionOutput::File)
}
}
}
#[inline]
pub fn estimate_size(
&self,
input_options: &InputOptions,
compression_options: &CompressionOptions,
) -> usize {
unsafe {
nvttEstimateSize(
self.0.as_ptr(),
input_options.0.as_ptr(),
compression_options.0.as_ptr(),
) as usize
}
}
}
impl Drop for Compressor {
#[inline]
fn drop(&mut self) {
unsafe {
nvttDestroyCompressor(self.0.as_ptr());
}
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum CompressionOutput {
File,
Memory {
data: Vec<u8>,
width: usize,
height: usize,
depth: usize,
face: usize,
miplevel: usize,
},
}
#[derive(Debug)]
pub struct CompressionOptions(NonNull<NvttCompressionOptions>);
impl CompressionOptions {
#[inline]
pub fn new() -> Result<Self, Error> {
let opts = unsafe { nvttCreateCompressionOptions() };
NonNull::new(opts).map(Self).ok_or(Error::Unknown)
}
#[inline]
pub fn into_raw(self) -> *mut NvttCompressionOptions {
let ptr = self.0.as_ptr();
mem::forget(self);
ptr
}
#[inline]
pub fn set_color_weights(&mut self, r: f32, g: f32, b: f32, a: f32) -> &mut Self {
unsafe {
nvttSetCompressionOptionsColorWeights(self.0.as_ptr(), r, g, b, a);
}
self
}
#[inline]
pub fn set_format(&mut self, format: Format) -> &mut Self {
unsafe {
nvttSetCompressionOptionsFormat(self.0.as_ptr(), format.into());
}
self
}
#[inline]
pub fn set_pixel_format(
&mut self,
bitcount: c_uint,
rmask: c_uint,
gmask: c_uint,
bmask: c_uint,
amask: c_uint,
) -> &mut Self {
unsafe {
nvttSetCompressionOptionsPixelFormat(
self.0.as_ptr(),
bitcount,
rmask,
gmask,
bmask,
amask,
)
}
self
}
#[inline]
pub fn set_quality(&mut self, quality: Quality) -> &mut Self {
unsafe {
nvttSetCompressionOptionsQuality(self.0.as_ptr(), quality.into());
}
self
}
#[inline]
pub fn set_quanitzation(
&mut self,
color_dithering: impl Into<NvttBoolean>,
alpha_dithering: impl Into<NvttBoolean>,
binary_alpha: impl Into<NvttBoolean>,
alpha_threshold: i32,
) -> &mut Self {
unsafe {
nvttSetCompressionOptionsQuantization(
self.0.as_ptr(),
color_dithering.into(),
alpha_dithering.into(),
binary_alpha.into(),
alpha_threshold,
)
}
self
}
}
impl Drop for CompressionOptions {
#[inline]
fn drop(&mut self) {
unsafe { nvttDestroyCompressionOptions(self.0.as_ptr()) }
}
}
#[derive(Debug)]
pub struct InputOptions(NonNull<NvttInputOptions>);
impl InputOptions {
#[inline]
pub fn new() -> Result<Self, Error> {
let opts = unsafe { nvttCreateInputOptions() };
NonNull::new(opts).map(Self).ok_or(Error::Unknown)
}
#[inline]
pub fn into_raw(self) -> *mut NvttInputOptions {
let ptr = self.0.as_ptr();
mem::forget(self);
ptr
}
#[inline]
pub fn set_alpha_mode(&mut self, alpha_mode: AlphaMode) -> &mut Self {
unsafe {
nvttSetInputOptionsAlphaMode(self.0.as_ptr(), alpha_mode.into());
}
self
}
#[inline]
pub fn convert_to_normal_map(
&mut self,
convert_to_normal_map: impl Into<NvttBoolean>,
) -> &mut Self {
unsafe {
nvttSetInputOptionsConvertToNormalMap(self.0.as_ptr(), convert_to_normal_map.into());
}
self
}
#[inline]
pub fn set_format(&mut self, format: InputFormat) -> &mut Self {
unsafe {
nvttSetInputOptionsFormat(self.0.as_ptr(), format.into());
}
self
}
#[inline]
pub fn set_gamma(&mut self, input_gamma: f32, output_gamma: f32) -> &mut Self {
unsafe {
nvttSetInputOptionsGamma(self.0.as_ptr(), input_gamma, output_gamma);
}
self
}
#[inline]
pub fn set_height_evaluation(
&mut self,
red_scale: f32,
green_scale: f32,
blue_scale: f32,
alpha_scale: f32,
) -> &mut Self {
unsafe {
nvttSetInputOptionsHeightEvaluation(
self.0.as_ptr(),
red_scale,
green_scale,
blue_scale,
alpha_scale,
);
}
self
}
#[inline]
pub fn set_mipmap_filter(&mut self, mipmap_filter: MipmapFilter) -> &mut Self {
let opts_ptr = self.0.as_ptr();
unsafe {
nvttSetInputOptionsMipmapFilter(opts_ptr, mipmap_filter.into());
}
if let MipmapFilter::Kaiser(Some(KaiserParameters {
width,
alpha,
stretch,
})) = mipmap_filter
{
unsafe {
nvttSetInputOptionsKaiserParameters(opts_ptr, width, alpha, stretch);
}
}
self
}
#[inline]
pub fn set_mipmap_data(
&mut self,
data: &[u8],
w: i32,
h: i32,
d: i32,
face: i32,
mipmap: i32,
) -> Result<&mut Self, Error> {
let result = unsafe {
nvttSetInputOptionsMipmapData(
self.0.as_ptr(),
data.as_ptr() as *const _,
w,
h,
d,
face,
mipmap,
)
};
match result {
NvttBoolean::NVTT_True => Ok(self),
NvttBoolean::NVTT_False => Err(Error::Unknown),
}
}
#[inline]
pub fn reset(&mut self) -> &mut Self {
unsafe { nvttResetInputOptionsTextureLayout(self.0.as_ptr()) }
self
}
#[cfg(feature = "nvtt_image_integration")]
#[inline]
pub fn set_image<'a, I: Into<ValidImage<'a>>>(
&mut self,
image: I,
face: i32,
mipmap: i32,
) -> Result<&mut Self, Error> {
let image = image.into();
let (w, h) = image.image_dimensions();
self.reset()
.set_format(image.format())
.set_texture_layout(TextureType::_2D, w as _, h as _, 1, 1)
.set_mipmap_data(image.data_bytes(), w as _, h as _, 1, face, mipmap)?;
Ok(self)
}
#[inline]
pub fn set_max_extents(&mut self, max_extents: c_int) -> &mut Self {
unsafe {
nvttSetInputOptionsMaxExtents(self.0.as_ptr(), max_extents);
}
self
}
#[inline]
pub fn set_normal_map(&mut self, is_normal_map: impl Into<NvttBoolean>) -> &mut Self {
unsafe {
nvttSetInputOptionsNormalMap(self.0.as_ptr(), is_normal_map.into());
}
self
}
#[inline]
pub fn set_normalize_mipmaps(&mut self, normalize_mips: impl Into<NvttBoolean>) -> &mut Self {
unsafe {
nvttSetInputOptionsNormalizeMipmaps(self.0.as_ptr(), normalize_mips.into());
}
self
}
pub fn set_normal_filter(&mut self, filter: NormalMapFilter) -> &mut Self {
unsafe {
nvttSetInputOptionsNormalFilter(
self.0.as_ptr(),
filter.small,
filter.medium,
filter.big,
filter.large,
);
}
self
}
#[inline]
pub fn set_round_mode(&mut self, round_mode: RoundMode) -> &mut Self {
unsafe {
nvttSetInputOptionsRoundMode(self.0.as_ptr(), round_mode.into());
}
self
}
#[inline]
pub fn set_texture_layout(
&mut self,
texture_type: TextureType,
w: c_int,
h: c_int,
d: c_int,
array_size: c_int,
) -> &mut Self {
unsafe {
nvttSetInputOptionsTextureLayout(
self.0.as_ptr(),
texture_type.into(),
w,
h,
d,
array_size,
)
}
self
}
#[inline]
pub fn set_wrap_mode(&mut self, wrap_mode: WrapMode) -> &mut Self {
unsafe {
nvttSetInputOptionsWrapMode(self.0.as_ptr(), wrap_mode.into());
}
self
}
}
impl Drop for InputOptions {
#[inline]
fn drop(&mut self) {
unsafe { nvttDestroyInputOptions(self.0.as_ptr()) }
}
}
#[cfg(feature = "nvtt_image_integration")]
pub enum ValidImage<'a> {
Bgra(MaybeOwned<'a, ImageBuffer<Bgra<u8>, Vec<u8>>>),
Rgba(MaybeOwned<'a, ImageBuffer<Rgba<f32>, Vec<f32>>>),
Luma(MaybeOwned<'a, ImageBuffer<Luma<f32>, Vec<f32>>>),
}
#[cfg(feature = "nvtt_image_integration")]
impl ValidImage<'_> {
#[inline]
pub fn new<I: Into<Self>>(image: I) -> Self {
image.into()
}
#[inline]
fn format(&self) -> InputFormat {
match *self {
ValidImage::Bgra(_) => InputFormat::Bgra8Ub,
ValidImage::Rgba(_) => InputFormat::Rgba32F,
ValidImage::Luma(_) => InputFormat::R32F,
}
}
#[inline]
fn image_dimensions(&self) -> (u32, u32) {
match *self {
ValidImage::Bgra(ref i) => i.dimensions(),
ValidImage::Rgba(ref i) => i.dimensions(),
ValidImage::Luma(ref i) => i.dimensions(),
}
}
#[inline]
fn data_bytes(&self) -> &[u8] {
match *self {
ValidImage::Bgra(ref i) => i.deref(),
ValidImage::Rgba(ref i) => transmute_to_bytes(i.deref()),
ValidImage::Luma(ref i) => transmute_to_bytes(i.deref()),
}
}
}
#[cfg(feature = "nvtt_image_integration")]
impl From<DynamicImage> for ValidImage<'_> {
#[inline]
fn from(img: DynamicImage) -> Self {
ValidImage::Bgra(MaybeOwned::Owned(img.to_bgra()))
}
}
#[cfg(feature = "nvtt_image_integration")]
impl From<&'_ DynamicImage> for ValidImage<'_> {
#[inline]
fn from(img: &'_ DynamicImage) -> Self {
ValidImage::Bgra(MaybeOwned::Owned(img.to_bgra()))
}
}
#[cfg(feature = "nvtt_image_integration")]
macro_rules! impl_maybeowned_from {
($( ($pix:ident, $subpix:ident) ),+ $(,)?) => {
$(
impl From<ImageBuffer<$pix<$subpix>, Vec<$subpix>>> for ValidImage<'_> {
#[inline]
fn from(buf: ImageBuffer<$pix<$subpix>, Vec<$subpix>>) -> Self {
ValidImage:: $pix (MaybeOwned::from(buf))
}
}
impl<'a> From<&'a ImageBuffer<$pix<$subpix>, Vec<$subpix>>> for ValidImage<'a> {
#[inline]
fn from(buf: &'a ImageBuffer<$pix<$subpix>, Vec<$subpix>>) -> Self {
ValidImage:: $pix (MaybeOwned::from(buf))
}
}
impl<'a> From<MaybeOwned<'a, ImageBuffer<$pix<$subpix>, Vec<$subpix>>>> for ValidImage<'a> {
#[inline]
fn from(img: MaybeOwned<'a, ImageBuffer<$pix<$subpix>, Vec<$subpix>>>) -> Self {
ValidImage::$pix(img)
}
}
)*
};
}
#[cfg(feature = "nvtt_image_integration")]
impl_maybeowned_from! {
(Bgra, u8), (Rgba, f32), (Luma, f32),
}
#[derive(Debug)]
pub struct OutputOptions {
out_opts: NonNull<NvttOutputOptions>,
write_to_file: bool,
}
impl OutputOptions {
#[inline]
pub fn new() -> Result<Self, Error> {
let opts = unsafe { nvttCreateOutputOptions() };
let out_opts = NonNull::new(opts).ok_or(Error::Unknown)?;
Ok(OutputOptions {
out_opts,
write_to_file: true,
})
}
#[inline]
pub fn into_raw(self) -> *mut NvttOutputOptions {
let ptr = self.out_opts.as_ptr();
mem::forget(self);
ptr
}
#[inline]
pub fn set_output_location<'a, T: 'a + ?Sized + Into<OutputLocation<'a>>>(
&mut self,
out_location: T,
) -> Result<&mut Self, PathConvertError> {
#[inline(never)]
fn inner(opts: &mut OutputOptions, loc: OutputLocation<'_>) -> Result<(), PathConvertError> {
match loc {
OutputLocation::File(p) => {
#[inline(always)]
fn to_c_filepath(path: &Path) -> Result<CString, PathConvertError> {
cfg_if! {
if #[cfg(target_family = "windows")] {
match path.to_str() {
Some(s) => {
if !s.is_ascii() {
return Err(PathConvertError::AsciiConvert)
}
CString::new(s.as_bytes()).map_err(From::from)
}
None => Err(PathConvertError::Utf8Convert),
}
} else if #[cfg(target_family = "unix")] {
use std::os::unix::ffi::OsStrExt;
CString::new(path.as_os_str().as_bytes()).map_err(From::from)
} else {
compile_error!("This platform is unsupported");
}
}
}
let out_file = to_c_filepath(p)?;
unsafe {
nvttSetOutputOptionsFileName(opts.out_opts.as_ptr(), out_file.as_ptr());
}
opts.write_to_file = true;
Ok(())
}
OutputLocation::Buffer => {
opts.write_to_file = false;
Ok(())
}
}
}
inner(self, out_location.into())?;
Ok(self)
}
#[inline]
pub fn set_write_header<B: Into<NvttBoolean>>(&mut self, write_header: B) -> &mut Self {
unsafe {
nvttSetOutputOptionsOutputHeader(self.out_opts.as_ptr(), write_header.into());
}
self
}
#[inline]
pub fn set_srgb_flag<B: Into<NvttBoolean>>(&mut self, write_srgb: B) -> &mut Self {
unsafe {
nvttSetOutputOptionsSrgbFlag(self.out_opts.as_ptr(), write_srgb.into());
}
self
}
#[inline]
pub fn set_container(&mut self, container: Container) -> &mut Self {
unsafe {
nvttSetOutputOptionsContainer(self.out_opts.as_ptr(), container.into());
}
self
}
}
impl Drop for OutputOptions {
#[inline]
fn drop(&mut self) {
unsafe { nvttDestroyOutputOptions(self.out_opts.as_ptr()) }
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum OutputLocation<'a> {
File(&'a Path),
Buffer,
}
impl<'a, P: 'a + ?Sized + AsRef<Path>> From<&'a P> for OutputLocation<'a> {
#[inline]
fn from(p: &'a P) -> Self {
OutputLocation::File(p.as_ref())
}
}
decl_enum! {
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Error: NvttError {
CudaError = NvttError_NVTT_Error_CudaError,
FileOpen = NvttError_NVTT_Error_FileOpen,
FileWrite = NvttError_NVTT_Error_FileWrite,
InvalidInput = NvttError_NVTT_Error_InvalidInput,
Unknown = NvttError_NVTT_Error_Unknown,
UnsupportedFeature = NvttError_NVTT_Error_UnsupportedFeature,
UnsupportedOutputFormat = NvttError_NVTT_Error_UnsupportedOutputFormat,
}
}
impl Default for Error {
#[inline]
fn default() -> Self {
Error::Unknown
}
}
impl fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = unsafe { CStr::from_ptr(nvttErrorString(self.into())) };
f.write_str(&s.to_string_lossy())?;
Ok(())
}
}
impl ErrorTrait for Error {
#[inline]
fn description(&self) -> &'static str {
let s = unsafe { CStr::from_ptr(nvttErrorString(self.into())) };
s.to_str().unwrap_or("An unknown error occurred")
}
}
#[derive(Clone, Debug)]
pub enum PathConvertError {
Utf8Convert,
AsciiConvert,
Nul(NulError),
}
impl fmt::Display for PathConvertError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
PathConvertError::Utf8Convert => {
f.write_str("Could not convert path losslessly to UTF8 string")
}
PathConvertError::AsciiConvert => f.write_str("The given path was not valid ASCII"),
PathConvertError::Nul(ref e) => fmt::Display::fmt(e, f),
}
}
}
impl ErrorTrait for PathConvertError {
#[inline]
fn source(&self) -> Option<&(dyn ErrorTrait + 'static)> {
match *self {
PathConvertError::Nul(ref e) => Some(e),
_ => None,
}
}
}
impl From<NulError> for PathConvertError {
#[inline]
fn from(e: NulError) -> Self {
PathConvertError::Nul(e)
}
}