1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 use super::image::*;
16
17 use std::os::raw::c_char;
18 use std::os::raw::c_int;
19 use std::os::raw::c_void;
20
21 use crate::utils::clap::*;
22 use crate::*;
23
24 #[repr(C)]
25 #[derive(PartialEq)]
26 pub enum avifResult {
27 Ok = 0,
28 UnknownError = 1,
29 InvalidFtyp = 2,
30 NoContent = 3,
31 NoYuvFormatSelected = 4,
32 ReformatFailed = 5,
33 UnsupportedDepth = 6,
34 EncodeColorFailed = 7,
35 EncodeAlphaFailed = 8,
36 BmffParseFailed = 9,
37 MissingImageItem = 10,
38 DecodeColorFailed = 11,
39 DecodeAlphaFailed = 12,
40 ColorAlphaSizeMismatch = 13,
41 IspeSizeMismatch = 14,
42 NoCodecAvailable = 15,
43 NoImagesRemaining = 16,
44 InvalidExifPayload = 17,
45 InvalidImageGrid = 18,
46 InvalidCodecSpecificOption = 19,
47 TruncatedData = 20,
48 IoNotSet = 21,
49 IoError = 22,
50 WaitingOnIo = 23,
51 InvalidArgument = 24,
52 NotImplemented = 25,
53 OutOfMemory = 26,
54 CannotChangeSetting = 27,
55 IncompatibleImage = 28,
56 EncodeGainMapFailed = 29,
57 DecodeGainMapFailed = 30,
58 InvalidToneMappedImage = 31,
59 }
60
61 impl From<&AvifError> for avifResult {
from(err: &AvifError) -> Self62 fn from(err: &AvifError) -> Self {
63 match err {
64 AvifError::Ok => avifResult::Ok,
65 AvifError::UnknownError(_) => avifResult::UnknownError,
66 AvifError::InvalidFtyp => avifResult::InvalidFtyp,
67 AvifError::NoContent => avifResult::NoContent,
68 AvifError::NoYuvFormatSelected => avifResult::NoYuvFormatSelected,
69 AvifError::ReformatFailed => avifResult::ReformatFailed,
70 AvifError::UnsupportedDepth => avifResult::UnsupportedDepth,
71 AvifError::EncodeColorFailed => avifResult::EncodeColorFailed,
72 AvifError::EncodeAlphaFailed => avifResult::EncodeAlphaFailed,
73 AvifError::BmffParseFailed(_) => avifResult::BmffParseFailed,
74 AvifError::MissingImageItem => avifResult::MissingImageItem,
75 AvifError::DecodeColorFailed => avifResult::DecodeColorFailed,
76 AvifError::DecodeAlphaFailed => avifResult::DecodeAlphaFailed,
77 AvifError::ColorAlphaSizeMismatch => avifResult::ColorAlphaSizeMismatch,
78 AvifError::IspeSizeMismatch => avifResult::IspeSizeMismatch,
79 AvifError::NoCodecAvailable => avifResult::NoCodecAvailable,
80 AvifError::NoImagesRemaining => avifResult::NoImagesRemaining,
81 AvifError::InvalidExifPayload => avifResult::InvalidExifPayload,
82 AvifError::InvalidImageGrid(_) => avifResult::InvalidImageGrid,
83 AvifError::InvalidCodecSpecificOption => avifResult::InvalidCodecSpecificOption,
84 AvifError::TruncatedData => avifResult::TruncatedData,
85 AvifError::IoNotSet => avifResult::IoNotSet,
86 AvifError::IoError => avifResult::IoError,
87 AvifError::WaitingOnIo => avifResult::WaitingOnIo,
88 AvifError::InvalidArgument => avifResult::InvalidArgument,
89 AvifError::NotImplemented => avifResult::NotImplemented,
90 AvifError::OutOfMemory => avifResult::OutOfMemory,
91 AvifError::CannotChangeSetting => avifResult::CannotChangeSetting,
92 AvifError::IncompatibleImage => avifResult::IncompatibleImage,
93 AvifError::EncodeGainMapFailed => avifResult::EncodeGainMapFailed,
94 AvifError::DecodeGainMapFailed => avifResult::DecodeGainMapFailed,
95 AvifError::InvalidToneMappedImage(_) => avifResult::InvalidToneMappedImage,
96 }
97 }
98 }
99
100 impl From<avifResult> for AvifError {
from(res: avifResult) -> Self101 fn from(res: avifResult) -> Self {
102 match res {
103 avifResult::Ok => AvifError::Ok,
104 avifResult::UnknownError => AvifError::UnknownError("".into()),
105 avifResult::InvalidFtyp => AvifError::InvalidFtyp,
106 avifResult::NoContent => AvifError::NoContent,
107 avifResult::NoYuvFormatSelected => AvifError::NoYuvFormatSelected,
108 avifResult::ReformatFailed => AvifError::ReformatFailed,
109 avifResult::UnsupportedDepth => AvifError::UnsupportedDepth,
110 avifResult::EncodeColorFailed => AvifError::EncodeColorFailed,
111 avifResult::EncodeAlphaFailed => AvifError::EncodeAlphaFailed,
112 avifResult::BmffParseFailed => AvifError::BmffParseFailed("".into()),
113 avifResult::MissingImageItem => AvifError::MissingImageItem,
114 avifResult::DecodeColorFailed => AvifError::DecodeColorFailed,
115 avifResult::DecodeAlphaFailed => AvifError::DecodeAlphaFailed,
116 avifResult::ColorAlphaSizeMismatch => AvifError::ColorAlphaSizeMismatch,
117 avifResult::IspeSizeMismatch => AvifError::IspeSizeMismatch,
118 avifResult::NoCodecAvailable => AvifError::NoCodecAvailable,
119 avifResult::NoImagesRemaining => AvifError::NoImagesRemaining,
120 avifResult::InvalidExifPayload => AvifError::InvalidExifPayload,
121 avifResult::InvalidImageGrid => AvifError::InvalidImageGrid("".into()),
122 avifResult::InvalidCodecSpecificOption => AvifError::InvalidCodecSpecificOption,
123 avifResult::TruncatedData => AvifError::TruncatedData,
124 avifResult::IoNotSet => AvifError::IoNotSet,
125 avifResult::IoError => AvifError::IoError,
126 avifResult::WaitingOnIo => AvifError::WaitingOnIo,
127 avifResult::InvalidArgument => AvifError::InvalidArgument,
128 avifResult::NotImplemented => AvifError::NotImplemented,
129 avifResult::OutOfMemory => AvifError::OutOfMemory,
130 avifResult::CannotChangeSetting => AvifError::CannotChangeSetting,
131 avifResult::IncompatibleImage => AvifError::IncompatibleImage,
132 avifResult::EncodeGainMapFailed => AvifError::EncodeGainMapFailed,
133 avifResult::DecodeGainMapFailed => AvifError::DecodeGainMapFailed,
134 avifResult::InvalidToneMappedImage => AvifError::InvalidToneMappedImage("".into()),
135 }
136 }
137 }
138
139 impl avifResult {
to_usize(&self) -> usize140 pub fn to_usize(&self) -> usize {
141 match self {
142 Self::Ok => 0,
143 Self::UnknownError => 1,
144 Self::InvalidFtyp => 2,
145 Self::NoContent => 3,
146 Self::NoYuvFormatSelected => 4,
147 Self::ReformatFailed => 5,
148 Self::UnsupportedDepth => 6,
149 Self::EncodeColorFailed => 7,
150 Self::EncodeAlphaFailed => 8,
151 Self::BmffParseFailed => 9,
152 Self::MissingImageItem => 10,
153 Self::DecodeColorFailed => 11,
154 Self::DecodeAlphaFailed => 12,
155 Self::ColorAlphaSizeMismatch => 13,
156 Self::IspeSizeMismatch => 14,
157 Self::NoCodecAvailable => 15,
158 Self::NoImagesRemaining => 16,
159 Self::InvalidExifPayload => 17,
160 Self::InvalidImageGrid => 18,
161 Self::InvalidCodecSpecificOption => 19,
162 Self::TruncatedData => 20,
163 Self::IoNotSet => 21,
164 Self::IoError => 22,
165 Self::WaitingOnIo => 23,
166 Self::InvalidArgument => 24,
167 Self::NotImplemented => 25,
168 Self::OutOfMemory => 26,
169 Self::CannotChangeSetting => 27,
170 Self::IncompatibleImage => 28,
171 Self::EncodeGainMapFailed => 29,
172 Self::DecodeGainMapFailed => 30,
173 Self::InvalidToneMappedImage => 31,
174 }
175 }
176 }
177
178 pub type avifBool = c_int;
179 pub const AVIF_TRUE: c_int = 1;
180 pub const AVIF_FALSE: c_int = 0;
181
182 pub const AVIF_STRICT_DISABLED: u32 = 0;
183 pub const AVIF_STRICT_PIXI_REQUIRED: u32 = 1 << 0;
184 pub const AVIF_STRICT_CLAP_VALID: u32 = 1 << 1;
185 pub const AVIF_STRICT_ALPHA_ISPE_REQUIRED: u32 = 1 << 2;
186 pub const AVIF_STRICT_ENABLED: u32 =
187 AVIF_STRICT_PIXI_REQUIRED | AVIF_STRICT_CLAP_VALID | AVIF_STRICT_ALPHA_ISPE_REQUIRED;
188 pub type avifStrictFlags = u32;
189
190 pub const AVIF_IMAGE_CONTENT_NONE: u32 = 0;
191 pub const AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA: u32 = 1 << 0 | 1 << 1;
192 pub const AVIF_IMAGE_CONTENT_GAIN_MAP: u32 = 1 << 2;
193 pub const AVIF_IMAGE_CONTENT_ALL: u32 =
194 AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA | AVIF_IMAGE_CONTENT_GAIN_MAP;
195 pub type avifImageContentTypeFlags = u32;
196
197 #[repr(C)]
198 pub struct avifDecoderData {}
199
200 pub const AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE: usize = 256;
201 #[repr(C)]
202 pub struct avifDiagnostics {
203 error: [c_char; AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE],
204 }
205
206 impl Default for avifDiagnostics {
default() -> Self207 fn default() -> Self {
208 Self {
209 error: [0; AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE],
210 }
211 }
212 }
213
214 impl avifDiagnostics {
set_from_result<T>(&mut self, res: &AvifResult<T>)215 pub fn set_from_result<T>(&mut self, res: &AvifResult<T>) {
216 match res {
217 Ok(_) => self.set_error_empty(),
218 Err(AvifError::BmffParseFailed(s))
219 | Err(AvifError::UnknownError(s))
220 | Err(AvifError::InvalidImageGrid(s))
221 | Err(AvifError::InvalidToneMappedImage(s)) => self.set_error_string(s),
222 _ => self.set_error_empty(),
223 }
224 }
225
set_error_string(&mut self, error: &str)226 fn set_error_string(&mut self, error: &str) {
227 if let Ok(s) = std::ffi::CString::new(error.to_owned()) {
228 let len = std::cmp::min(
229 s.as_bytes_with_nul().len(),
230 AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE,
231 );
232 self.error
233 .get_mut(..len)
234 .unwrap()
235 .iter_mut()
236 .zip(&s.as_bytes_with_nul()[..len])
237 .for_each(|(dst, src)| *dst = *src as c_char);
238 self.error[AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE - 1] = 0;
239 } else {
240 self.set_error_empty();
241 }
242 }
243
set_error_empty(&mut self)244 pub fn set_error_empty(&mut self) {
245 self.error[0] = 0;
246 }
247 }
248
249 #[repr(C)]
250 pub enum avifCodecChoice {
251 Auto = 0,
252 Aom = 1,
253 Dav1d = 2,
254 Libgav1 = 3,
255 Rav1e = 4,
256 Svt = 5,
257 Avm = 6,
258 }
259
to_avifBool(val: bool) -> avifBool260 pub fn to_avifBool(val: bool) -> avifBool {
261 if val {
262 AVIF_TRUE
263 } else {
264 AVIF_FALSE
265 }
266 }
267
to_avifResult<T>(res: &AvifResult<T>) -> avifResult268 pub fn to_avifResult<T>(res: &AvifResult<T>) -> avifResult {
269 match res {
270 Ok(_) => avifResult::Ok,
271 Err(err) => {
272 let res: avifResult = err.into();
273 res
274 }
275 }
276 }
277
278 const RESULT_TO_STRING: &[&str] = &[
279 "Ok\0",
280 "Unknown Error\0",
281 "Invalid ftyp\0",
282 "No content\0",
283 "No YUV format selected\0",
284 "Reformat failed\0",
285 "Unsupported depth\0",
286 "Encoding of color planes failed\0",
287 "Encoding of alpha plane failed\0",
288 "BMFF parsing failed\0",
289 "Missing or empty image item\0",
290 "Decoding of color planes failed\0",
291 "Decoding of alpha plane failed\0",
292 "Color and alpha planes size mismatch\0",
293 "Plane sizes don't match ispe values\0",
294 "No codec available\0",
295 "No images remaining\0",
296 "Invalid Exif payload\0",
297 "Invalid image grid\0",
298 "Invalid codec-specific option\0",
299 "Truncated data\0",
300 "IO not set\0",
301 "IO Error\0",
302 "Waiting on IO\0",
303 "Invalid argument\0",
304 "Not implemented\0",
305 "Out of memory\0",
306 "Cannot change some setting during encoding\0",
307 "The image is incompatible with already encoded images\0",
308 "Encoding of gain map planes failed\0",
309 "Decoding of gain map planes failed\0",
310 "Invalid tone mapped image item\0",
311 ];
312
313 #[no_mangle]
crabby_avifResultToString(res: avifResult) -> *const c_char314 pub unsafe extern "C" fn crabby_avifResultToString(res: avifResult) -> *const c_char {
315 unsafe {
316 std::ffi::CStr::from_bytes_with_nul_unchecked(RESULT_TO_STRING[res.to_usize()].as_bytes())
317 .as_ptr() as *const _
318 }
319 }
320
321 pub type avifCropRect = CropRect;
322
323 #[no_mangle]
crabby_avifCropRectConvertCleanApertureBox( cropRect: *mut avifCropRect, clap: *const avifCleanApertureBox, imageW: u32, imageH: u32, yuvFormat: PixelFormat, _diag: *mut avifDiagnostics, ) -> avifBool324 pub unsafe extern "C" fn crabby_avifCropRectConvertCleanApertureBox(
325 cropRect: *mut avifCropRect,
326 clap: *const avifCleanApertureBox,
327 imageW: u32,
328 imageH: u32,
329 yuvFormat: PixelFormat,
330 _diag: *mut avifDiagnostics,
331 ) -> avifBool {
332 let rust_clap: CleanAperture = unsafe { (&(*clap)).into() };
333 let rect = unsafe { &mut (*cropRect) };
334 *rect = match CropRect::create_from(&rust_clap, imageW, imageH, yuvFormat) {
335 Ok(x) => x,
336 Err(_) => return AVIF_FALSE,
337 };
338 AVIF_TRUE
339 }
340
341 // Constants and definitions from libavif that are not used in rust.
342
343 pub const AVIF_PLANE_COUNT_YUV: usize = 3;
344 pub const AVIF_REPETITION_COUNT_INFINITE: i32 = -1;
345 pub const AVIF_REPETITION_COUNT_UNKNOWN: i32 = -2;
346
347 /// cbindgen:rename-all=ScreamingSnakeCase
348 #[repr(C)]
349 pub enum avifPlanesFlag {
350 AvifPlanesYuv = 1 << 0,
351 AvifPlanesA = 1 << 1,
352 AvifPlanesAll = 0xFF,
353 }
354 pub type avifPlanesFlags = u32;
355
356 /// cbindgen:rename-all=ScreamingSnakeCase
357 #[repr(C)]
358 pub enum avifChannelIndex {
359 AvifChanY = 0,
360 AvifChanU = 1,
361 AvifChanV = 2,
362 AvifChanA = 3,
363 }
364
365 /// cbindgen:rename-all=ScreamingSnakeCase
366 #[repr(C)]
367 pub enum avifHeaderFormat {
368 AvifHeaderFull,
369 AvifHeaderReduced,
370 }
371
372 #[repr(C)]
373 pub struct avifPixelFormatInfo {
374 monochrome: avifBool,
375 chromaShiftX: c_int,
376 chromaShiftY: c_int,
377 }
378
379 #[no_mangle]
crabby_avifGetPixelFormatInfo( format: PixelFormat, info: *mut avifPixelFormatInfo, )380 pub unsafe extern "C" fn crabby_avifGetPixelFormatInfo(
381 format: PixelFormat,
382 info: *mut avifPixelFormatInfo,
383 ) {
384 if info.is_null() {
385 return;
386 }
387 let info = unsafe { &mut (*info) };
388 match format {
389 PixelFormat::Yuv444 => {
390 info.chromaShiftX = 0;
391 info.chromaShiftY = 0;
392 info.monochrome = AVIF_FALSE;
393 }
394 PixelFormat::Yuv422 => {
395 info.chromaShiftX = 1;
396 info.chromaShiftY = 0;
397 info.monochrome = AVIF_FALSE;
398 }
399 PixelFormat::Yuv420 => {
400 info.chromaShiftX = 1;
401 info.chromaShiftY = 1;
402 info.monochrome = AVIF_FALSE;
403 }
404 PixelFormat::Yuv400 => {
405 info.chromaShiftX = 1;
406 info.chromaShiftY = 1;
407 info.monochrome = AVIF_TRUE;
408 }
409 _ => {}
410 }
411 }
412
413 #[no_mangle]
crabby_avifDiagnosticsClearError(diag: *mut avifDiagnostics)414 pub unsafe extern "C" fn crabby_avifDiagnosticsClearError(diag: *mut avifDiagnostics) {
415 if diag.is_null() {
416 return;
417 }
418 unsafe {
419 (*diag).error[0] = 0;
420 }
421 }
422
423 #[repr(C)]
424 pub enum avifCodecFlag {
425 CanDecode = (1 << 0),
426 CanEncode = (1 << 1),
427 }
428 pub type avifCodecFlags = u32;
429
430 pub const AVIF_TRANSFORM_NONE: u32 = 0;
431 pub const AVIF_TRANSFORM_PASP: u32 = 1 << 0;
432 pub const AVIF_TRANSFORM_CLAP: u32 = 1 << 1;
433 pub const AVIF_TRANSFORM_IROT: u32 = 1 << 2;
434 pub const AVIF_TRANSFORM_IMIR: u32 = 1 << 3;
435 pub type avifTransformFlags = u32;
436
437 pub const AVIF_COLOR_PRIMARIES_BT709: u32 = 1;
438 pub const AVIF_COLOR_PRIMARIES_IEC61966_2_4: u32 = 1;
439 pub const AVIF_COLOR_PRIMARIES_BT2100: u32 = 9;
440 pub const AVIF_COLOR_PRIMARIES_DCI_P3: u32 = 12;
441 pub const AVIF_TRANSFER_CHARACTERISTICS_SMPTE2084: u32 = 16;
442
443 #[no_mangle]
crabby_avifAlloc(size: usize) -> *mut c_void444 pub unsafe extern "C" fn crabby_avifAlloc(size: usize) -> *mut c_void {
445 let mut data: Vec<u8> = Vec::new();
446 data.reserve_exact(size);
447 data.resize(size, 0);
448 let mut boxed_slice = data.into_boxed_slice();
449 let ptr = boxed_slice.as_mut_ptr();
450 std::mem::forget(boxed_slice);
451 ptr as *mut c_void
452 }
453
454 #[no_mangle]
crabby_avifFree(p: *mut c_void)455 pub unsafe extern "C" fn crabby_avifFree(p: *mut c_void) {
456 if !p.is_null() {
457 let _ = unsafe { Box::from_raw(p as *mut u8) };
458 }
459 }
460