imagequant_sys/
ffi.rs

1//! Exports API for C programs and C-FFI-compatible languages. See `libimagequant.h` or <https://pngquant.org/lib/> for C docs.
2//!
3//! This crate is not supposed to be used in Rust directly. For Rust, see the parent [imagequant](https://lib.rs/imagequant) crate.
4#![allow(non_camel_case_types)]
5#![allow(clippy::missing_safety_doc)]
6#![allow(clippy::wildcard_imports)]
7#![allow(clippy::items_after_statements)]
8#![allow(clippy::cast_possible_truncation)]
9#![allow(clippy::cast_possible_wrap)]
10
11use imagequant::capi::*;
12use imagequant::Error::LIQ_OK;
13use imagequant::*;
14use std::ffi::CString;
15use std::mem::{ManuallyDrop, MaybeUninit};
16use std::os::raw::{c_char, c_int, c_uint, c_void};
17use std::ptr;
18
19pub use imagequant::Error as liq_error;
20
21#[repr(C)]
22pub struct liq_attr {
23    magic_header: MagicTag,
24    inner: Attributes,
25    c_api_free: unsafe extern "C" fn(*mut c_void),
26}
27
28#[repr(C)]
29pub struct liq_image<'pixels> {
30    magic_header: MagicTag,
31    inner: ManuallyDrop<Image<'pixels>>,
32    c_api_free: unsafe extern "C" fn(*mut c_void),
33}
34
35#[repr(C)]
36pub struct liq_result {
37    magic_header: MagicTag,
38    inner: QuantizationResult,
39}
40
41#[repr(C)]
42pub struct liq_histogram {
43    magic_header: MagicTag,
44    inner: Histogram,
45}
46
47pub type liq_palette = Palette;
48pub type liq_histogram_entry = HistogramEntry;
49pub type liq_color = RGBA;
50
51pub type liq_log_callback_function = unsafe extern "C" fn(liq: &liq_attr, message: *const c_char, user_info: AnySyncSendPtr);
52pub type liq_log_flush_callback_function = unsafe extern "C" fn(liq: &liq_attr, user_info: AnySyncSendPtr);
53pub type liq_progress_callback_function = unsafe extern "C" fn(progress_percent: f32, user_info: AnySyncSendPtr) -> c_int;
54pub type liq_image_get_rgba_row_callback = unsafe extern "C" fn(row_out: *mut MaybeUninit<RGBA>, row: c_int, width: c_int, user_info: AnySyncSendPtr);
55
56bitflags::bitflags! {
57    #[repr(C)]
58    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
59    pub struct liq_ownership: c_int {
60        /// Moves ownership of the rows array. It will free it using `free()` or custom allocator.
61        const LIQ_OWN_ROWS = 4;
62        /// Moves ownership of the pixel data. It will free it using `free()` or custom allocator.
63        const LIQ_OWN_PIXELS = 8;
64        /// Makes a copy of the pixels, so the `liq_image` is not tied to pixel's lifetime.
65        const LIQ_COPY_PIXELS = 16;
66    }
67}
68
69#[repr(transparent)]
70#[derive(PartialEq, Debug, Copy, Clone)]
71pub(crate) struct MagicTag(*const u8);
72// Safety: Rust overreacts about C pointers. Data behind this ptr isn't used.
73unsafe impl Sync for MagicTag {}
74unsafe impl Send for MagicTag {}
75
76pub(crate) static LIQ_ATTR_MAGIC: MagicTag = MagicTag(b"liq_attr_magic\0".as_ptr());
77pub(crate) static LIQ_IMAGE_MAGIC: MagicTag = MagicTag(b"liq_image_magic\0".as_ptr());
78pub(crate) static LIQ_RESULT_MAGIC: MagicTag = MagicTag(b"liq_result_magic\0".as_ptr());
79pub(crate) static LIQ_HISTOGRAM_MAGIC: MagicTag = MagicTag(b"liq_histogram_magic\0".as_ptr());
80pub(crate) static LIQ_FREED_MAGIC: MagicTag = MagicTag(b"liq_freed_magic\0".as_ptr());
81
82#[no_mangle]
83#[inline(never)]
84unsafe extern "C" fn liq_received_invalid_pointer(ptr: *const u8) -> bool {
85    if ptr.is_null() {
86        return true;
87    }
88    let _ = ptr::read_volatile(ptr);
89    false
90}
91
92macro_rules! bad_object {
93    ($obj:expr, $tag:expr) => {{
94        let obj = &*$obj;
95        #[allow(unused_unsafe)]
96        #[allow(clippy::ptr_as_ptr)]
97        let bork = if cfg!(miri) { false } else { unsafe { liq_received_invalid_pointer((obj as *const _ as *const u8)) } };
98        (bork || (($obj).magic_header != $tag))
99    }};
100}
101
102impl Drop for liq_attr {
103    fn drop(&mut self) {
104        if bad_object!(self, LIQ_ATTR_MAGIC) { return; }
105        self.magic_header = LIQ_FREED_MAGIC;
106    }
107}
108impl Drop for liq_image<'_> {
109    fn drop(&mut self) {
110        if bad_object!(self, LIQ_IMAGE_MAGIC) { return; }
111        unsafe { ManuallyDrop::drop(&mut self.inner); }
112        self.magic_header = LIQ_FREED_MAGIC;
113    }
114}
115impl Drop for liq_result {
116    fn drop(&mut self) {
117        if bad_object!(self, LIQ_RESULT_MAGIC) { return; }
118        self.magic_header = LIQ_FREED_MAGIC;
119    }
120}
121impl Drop for liq_histogram {
122    fn drop(&mut self) {
123        if bad_object!(self, LIQ_HISTOGRAM_MAGIC) { return; }
124        self.magic_header = LIQ_FREED_MAGIC;
125    }
126}
127
128#[no_mangle]
129#[inline(never)]
130pub extern "C" fn liq_version() -> c_uint {
131    LIQ_VERSION
132}
133
134#[no_mangle]
135#[inline(never)]
136#[deprecated]
137pub extern "C" fn liq_set_min_opacity(_: &mut liq_attr, _: c_int) -> liq_error {
138    LIQ_OK
139}
140
141#[no_mangle]
142#[inline(never)]
143#[deprecated]
144pub extern "C" fn liq_get_min_opacity(_: &liq_attr) -> c_int {
145    0
146}
147
148#[no_mangle]
149#[inline(never)]
150pub extern "C" fn liq_set_last_index_transparent(attr: &mut liq_attr, is_last: c_int) {
151    if bad_object!(attr, LIQ_ATTR_MAGIC) { return; }
152    attr.inner.set_last_index_transparent(is_last != 0);
153}
154
155#[no_mangle]
156#[inline(never)]
157pub extern "C" fn liq_get_palette(result: &mut liq_result) -> Option<&liq_palette> {
158    if bad_object!(result, LIQ_RESULT_MAGIC) { return None; }
159    Some(liq_get_palette_impl(&mut result.inner))
160}
161
162/// A `void*` pointer to any data, as long as it's thread-safe
163#[repr(transparent)]
164#[derive(Clone, Copy)]
165pub struct AnySyncSendPtr(pub *mut c_void);
166
167impl Default for AnySyncSendPtr {
168    fn default() -> Self {
169        Self(ptr::null_mut())
170    }
171}
172
173/// C callback user is responsible for ensuring safety
174unsafe impl Send for AnySyncSendPtr {}
175unsafe impl Sync for AnySyncSendPtr {}
176
177#[no_mangle]
178#[inline(never)]
179pub unsafe extern "C" fn liq_attr_set_progress_callback(attr: &mut liq_attr, callback: liq_progress_callback_function, user_info: AnySyncSendPtr) {
180    if bad_object!(attr, LIQ_ATTR_MAGIC) { return; }
181    let cb = move |f| if callback(f, user_info) == 0 { ControlFlow::Break} else { ControlFlow::Continue};
182    attr.inner.set_progress_callback(cb);
183}
184
185#[no_mangle]
186#[inline(never)]
187pub unsafe extern "C" fn liq_result_set_progress_callback(result: &mut liq_result, callback: liq_progress_callback_function, user_info: AnySyncSendPtr) {
188    if bad_object!(result, LIQ_RESULT_MAGIC) { return; }
189    result.inner.set_progress_callback(move |f| if callback(f, user_info) == 0 { ControlFlow::Break} else { ControlFlow::Continue});
190}
191
192#[allow(clippy::cast_ptr_alignment)]
193unsafe fn attr_to_liq_attr_ptr(ptr: &Attributes) -> &liq_attr {
194    let liq_attr = std::ptr::NonNull::<liq_attr>::dangling();
195    let outer_addr = std::ptr::addr_of!(*liq_attr.as_ptr()) as isize;
196    let inner_addr = std::ptr::addr_of!((*liq_attr.as_ptr()).inner) as isize;
197
198    &*(ptr as *const Attributes).cast::<u8>().offset(outer_addr - inner_addr).cast::<liq_attr>()
199}
200
201#[no_mangle]
202#[inline(never)]
203pub unsafe extern "C" fn liq_set_log_callback(attr: &mut liq_attr, callback: liq_log_callback_function, user_info: AnySyncSendPtr) {
204    if bad_object!(attr, LIQ_ATTR_MAGIC) { return; }
205    attr.inner.set_log_callback(move |attr, msg| {
206        if let Ok(tmp) = CString::new(msg) {
207            callback(attr_to_liq_attr_ptr(attr), tmp.as_ptr(), user_info);
208        }
209    });
210}
211
212#[no_mangle]
213#[inline(never)]
214pub unsafe extern "C" fn liq_set_log_flush_callback(attr: &mut liq_attr, callback: liq_log_flush_callback_function, user_info: AnySyncSendPtr) {
215    if bad_object!(attr, LIQ_ATTR_MAGIC) { return; }
216    attr.inner.set_log_flush_callback(move |attr| callback(attr_to_liq_attr_ptr(attr), user_info));
217}
218
219#[no_mangle]
220#[inline(never)]
221pub extern "C" fn liq_set_max_colors(attr: &mut liq_attr, colors: c_uint) -> liq_error {
222    if bad_object!(attr, LIQ_ATTR_MAGIC) { return Error::InvalidPointer; }
223    attr.inner.set_max_colors(colors).err().unwrap_or(LIQ_OK)
224}
225
226#[no_mangle]
227#[inline(never)]
228pub extern "C" fn liq_get_max_colors(attr: &liq_attr) -> c_uint {
229    if bad_object!(attr, LIQ_ATTR_MAGIC) { return !0; }
230    attr.inner.max_colors()
231}
232
233#[no_mangle]
234#[inline(never)]
235pub extern "C" fn liq_set_min_posterization(attr: &mut liq_attr, bits: c_int) -> liq_error {
236    if bad_object!(attr, LIQ_ATTR_MAGIC) { return Error::InvalidPointer; }
237    attr.inner.set_min_posterization(bits as u8).err().unwrap_or(LIQ_OK)
238}
239
240#[no_mangle]
241#[inline(never)]
242pub extern "C" fn liq_get_min_posterization(attr: &liq_attr) -> c_uint {
243    if bad_object!(attr, LIQ_ATTR_MAGIC) { return !0; }
244    attr.inner.min_posterization().into()
245}
246
247#[no_mangle]
248#[inline(never)]
249pub extern "C" fn liq_set_speed(attr: &mut liq_attr, speed: c_int) -> liq_error {
250    attr.inner.set_speed(speed).err().unwrap_or(LIQ_OK)
251}
252
253#[no_mangle]
254#[inline(never)]
255pub extern "C" fn liq_get_speed(attr: &liq_attr) -> c_uint {
256    if bad_object!(attr, LIQ_ATTR_MAGIC) { return !0; }
257    attr.inner.speed()
258}
259
260#[no_mangle]
261#[inline(never)]
262pub extern "C" fn liq_set_quality(attr: &mut liq_attr, minimum: c_uint, target: c_uint) -> liq_error {
263    if bad_object!(attr, LIQ_ATTR_MAGIC) { return Error::InvalidPointer; }
264    attr.inner.set_quality(minimum as u8, target as u8).err().unwrap_or(LIQ_OK)
265}
266
267#[no_mangle]
268#[inline(never)]
269pub extern "C" fn liq_get_min_quality(attr: &liq_attr) -> c_uint {
270    if bad_object!(attr, LIQ_ATTR_MAGIC) { return !0; }
271    attr.inner.quality().0.into()
272}
273
274#[no_mangle]
275#[inline(never)]
276pub extern "C" fn liq_get_max_quality(attr: &liq_attr) -> c_uint {
277    if bad_object!(attr, LIQ_ATTR_MAGIC) { return !0; }
278    attr.inner.quality().1.into()
279}
280
281#[no_mangle]
282#[inline(never)]
283pub extern "C" fn liq_quantize_image(attr: &mut liq_attr, img: &mut liq_image) -> Option<Box<liq_result>> {
284    if bad_object!(attr, LIQ_ATTR_MAGIC) ||
285       bad_object!(img, LIQ_IMAGE_MAGIC) { return None; }
286    let img = &mut img.inner;
287    let attr = &mut attr.inner;
288
289    attr.quantize(img).ok().map(|inner| Box::new(liq_result {
290        magic_header: LIQ_RESULT_MAGIC,
291        inner,
292    }))
293}
294
295#[no_mangle]
296#[inline(never)]
297pub unsafe extern "C" fn liq_write_remapped_image(result: &mut liq_result, input_image: &mut liq_image, buffer_bytes: *mut MaybeUninit<u8>, buffer_size: usize) -> liq_error {
298    if bad_object!(result, LIQ_RESULT_MAGIC) ||
299       bad_object!(input_image, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
300    let input_image = &mut input_image.inner;
301    let result = &mut result.inner;
302
303    if liq_received_invalid_pointer(buffer_bytes.cast()) { return Error::InvalidPointer; }
304
305    let required_size = (input_image.width()) * (input_image.height());
306    if buffer_size < required_size { return Error::BufferTooSmall; }
307    let buffer_bytes = std::slice::from_raw_parts_mut(buffer_bytes, required_size);
308    liq_write_remapped_image_impl(result, input_image, buffer_bytes).err().unwrap_or(LIQ_OK)
309}
310
311#[no_mangle]
312#[inline(never)]
313pub unsafe extern "C" fn liq_write_remapped_image_rows(result: &mut liq_result, input_image: &mut liq_image, row_pointers: *mut *mut MaybeUninit<u8>) -> liq_error {
314    if bad_object!(result, LIQ_RESULT_MAGIC) ||
315       bad_object!(input_image, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
316    let input_image = &mut input_image.inner;
317    let result = &mut result.inner;
318
319    if liq_received_invalid_pointer(row_pointers.cast()) { return Error::InvalidPointer; }
320    let rows = std::slice::from_raw_parts_mut(row_pointers, input_image.height());
321
322    liq_write_remapped_image_rows_impl(result, input_image, rows).err().unwrap_or(LIQ_OK)
323}
324
325#[no_mangle]
326#[inline(never)]
327pub extern "C" fn liq_image_add_fixed_color(img: &mut liq_image, color: liq_color) -> liq_error {
328    if bad_object!(img, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
329    img.inner.add_fixed_color(color).err().unwrap_or(LIQ_OK)
330}
331
332#[no_mangle]
333#[inline(never)]
334pub extern "C" fn liq_histogram_add_fixed_color(hist: &mut liq_histogram, color: liq_color, gamma: f64) -> liq_error {
335    if bad_object!(hist, LIQ_HISTOGRAM_MAGIC) { return Error::InvalidPointer; }
336    let hist = &mut hist.inner;
337
338    hist.add_fixed_color(color, gamma).err().unwrap_or(LIQ_OK)
339}
340
341#[no_mangle]
342#[inline(never)]
343pub extern "C" fn liq_image_get_width(img: &liq_image) -> c_uint {
344    if bad_object!(img, LIQ_IMAGE_MAGIC) { return !0; }
345    img.inner.width() as _
346}
347
348#[no_mangle]
349#[inline(never)]
350pub extern "C" fn liq_image_get_height(img: &liq_image) -> c_uint {
351    if bad_object!(img, LIQ_IMAGE_MAGIC) { return !0; }
352    img.inner.height() as _
353}
354
355#[no_mangle]
356#[inline(never)]
357pub extern "C" fn liq_image_destroy(_: Option<Box<liq_image>>) {}
358
359#[no_mangle]
360#[inline(never)]
361pub extern "C" fn liq_image_set_background<'pixels>(img: &mut liq_image<'pixels>, background: Box<liq_image<'pixels>>) -> liq_error {
362    if bad_object!(img, LIQ_IMAGE_MAGIC) ||
363       bad_object!(background, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
364    let background = unsafe { ManuallyDrop::take(&mut ManuallyDrop::new(background).inner) };
365    img.inner.set_background(background).err().unwrap_or(LIQ_OK)
366}
367
368#[no_mangle]
369#[inline(never)]
370pub unsafe extern "C" fn liq_image_set_importance_map(img: &mut liq_image, importance_map: *mut u8, buffer_size: usize, ownership: liq_ownership) -> liq_error {
371    if bad_object!(img, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
372    let free_fn = img.c_api_free;
373    let img = &mut img.inner;
374
375    if buffer_size == 0 || liq_received_invalid_pointer(importance_map) { return Error::InvalidPointer; }
376    let required_size = img.width() * img.height();
377    if buffer_size < required_size {
378        return Error::BufferTooSmall;
379    }
380
381    let importance_map_slice = std::slice::from_raw_parts(importance_map, required_size);
382    if ownership == liq_ownership::LIQ_COPY_PIXELS {
383        img.set_importance_map(importance_map_slice).err().unwrap_or(LIQ_OK)
384    } else if ownership == liq_ownership::LIQ_OWN_PIXELS {
385        let copy: Box<[u8]> = importance_map_slice.into();
386        free_fn(importance_map.cast());
387        img.set_importance_map(copy).err().unwrap_or(LIQ_OK);
388        LIQ_OK
389    } else {
390        Error::Unsupported
391    }
392}
393
394#[no_mangle]
395#[inline(never)]
396pub unsafe extern "C" fn liq_image_set_memory_ownership(img: &mut liq_image, ownership_flags: liq_ownership) -> liq_error {
397    if bad_object!(img, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
398
399    let both = liq_ownership::LIQ_OWN_ROWS | liq_ownership::LIQ_OWN_PIXELS;
400
401    if ownership_flags.is_empty() || (ownership_flags | both) != both {
402        return Error::ValueOutOfRange;
403    }
404
405    let own_rows = ownership_flags.contains(liq_ownership::LIQ_OWN_ROWS);
406    let own_pixels = ownership_flags.contains(liq_ownership::LIQ_OWN_PIXELS);
407    liq_image_set_memory_ownership_impl(&mut img.inner, own_rows, own_pixels, img.c_api_free).err().unwrap_or(LIQ_OK)
408}
409
410#[no_mangle]
411#[inline(never)]
412pub extern "C" fn liq_histogram_create(attr: &liq_attr) -> Option<Box<liq_histogram>> {
413    if bad_object!(attr, LIQ_ATTR_MAGIC) { return None; }
414    Some(Box::new(liq_histogram {
415        magic_header: LIQ_HISTOGRAM_MAGIC,
416        inner: Histogram::new(&attr.inner),
417    }))
418}
419
420#[no_mangle]
421#[inline(never)]
422pub extern "C" fn liq_histogram_destroy(_hist: Option<Box<liq_histogram>>) {}
423
424#[no_mangle]
425#[inline(never)]
426#[deprecated(note = "custom allocators are no longer supported")]
427pub extern "C" fn liq_attr_create_with_allocator(_unused: *mut c_void, free: unsafe extern "C" fn(*mut c_void)) -> Option<Box<liq_attr>> {
428    let attr = Box::new(liq_attr {
429        magic_header: LIQ_ATTR_MAGIC,
430        inner: Attributes::new(),
431        c_api_free: free,
432    });
433    debug_assert_eq!(std::ptr::addr_of!(*attr), unsafe { attr_to_liq_attr_ptr(&attr.inner) } as *const liq_attr);
434    Some(attr)
435}
436
437#[no_mangle]
438#[inline(never)]
439#[allow(deprecated)]
440pub extern "C" fn liq_attr_create() -> Option<Box<liq_attr>> {
441    liq_attr_create_with_allocator(ptr::null_mut(), libc::free)
442}
443
444#[no_mangle]
445#[inline(never)]
446pub extern "C" fn liq_attr_copy(attr: &liq_attr) -> Option<Box<liq_attr>> {
447    if bad_object!(attr, LIQ_ATTR_MAGIC) { return None; }
448    Some(Box::new(liq_attr {
449        magic_header: LIQ_ATTR_MAGIC,
450        inner: attr.inner.clone(),
451        c_api_free: attr.c_api_free,
452    }))
453}
454
455#[no_mangle]
456#[inline(never)]
457pub extern "C" fn liq_attr_destroy(_attr: Option<Box<liq_attr>>) {}
458
459#[no_mangle]
460#[inline(never)]
461pub extern "C" fn liq_result_destroy(_res: Option<Box<liq_result>>) {}
462
463#[no_mangle]
464#[inline(never)]
465pub extern "C" fn liq_set_output_gamma(result: &mut liq_result, gamma: f64) -> liq_error {
466    if bad_object!(result, LIQ_RESULT_MAGIC) { return Error::InvalidPointer; }
467    result.inner.set_output_gamma(gamma).err().unwrap_or(LIQ_OK)
468}
469
470#[no_mangle]
471#[inline(never)]
472pub extern "C" fn liq_set_dithering_level(result: &mut liq_result, dither_level: f32) -> liq_error {
473    if bad_object!(result, LIQ_RESULT_MAGIC) { return Error::InvalidPointer; }
474    result.inner.set_dithering_level(dither_level).err().unwrap_or(LIQ_OK)
475}
476
477#[no_mangle]
478#[inline(never)]
479pub extern "C" fn liq_get_output_gamma(result: &liq_result) -> f64 {
480    if bad_object!(result, LIQ_RESULT_MAGIC) { return -1.; }
481    result.inner.output_gamma()
482}
483
484#[no_mangle]
485#[inline(never)]
486pub extern "C" fn liq_get_quantization_error(result: &liq_result) -> f64 {
487    if bad_object!(result, LIQ_RESULT_MAGIC) { return -1.; }
488    result.inner.quantization_error().unwrap_or(-1.)
489}
490
491#[no_mangle]
492#[inline(never)]
493pub extern "C" fn liq_get_remapping_error(result: &liq_result) -> f64 {
494    if bad_object!(result, LIQ_RESULT_MAGIC) { return -1.; }
495    result.inner.remapping_error().unwrap_or(-1.)
496}
497
498#[no_mangle]
499#[inline(never)]
500pub extern "C" fn liq_get_quantization_quality(result: &liq_result) -> c_int {
501    if bad_object!(result, LIQ_RESULT_MAGIC) { return -1; }
502    result.inner.quantization_quality().map_or(-1, c_int::from)
503}
504
505#[no_mangle]
506#[inline(never)]
507pub extern "C" fn liq_get_remapping_quality(result: &liq_result) -> c_int {
508    if bad_object!(result, LIQ_RESULT_MAGIC) { return -1; }
509    result.inner.remapping_quality().map_or(-1, c_int::from)
510}
511
512#[no_mangle]
513#[inline(never)]
514pub extern "C" fn liq_image_quantize(img: &mut liq_image, attr: &mut liq_attr, write_only_output: &mut MaybeUninit<Option<Box<liq_result>>>) -> liq_error {
515    if bad_object!(attr, LIQ_ATTR_MAGIC) ||
516       bad_object!(img, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
517    let attr = &mut attr.inner;
518    let img = &mut img.inner;
519
520    let res = attr.quantize(img)
521        .map(|inner| liq_result {
522            magic_header: LIQ_RESULT_MAGIC,
523            inner,
524        });
525    store_boxed_result(res, write_only_output)
526}
527
528#[no_mangle]
529#[inline(never)]
530pub extern "C" fn liq_histogram_quantize(hist: &mut liq_histogram, attr: &liq_attr, write_only_output: &mut MaybeUninit<Option<Box<liq_result>>>) -> liq_error {
531    if bad_object!(attr, LIQ_ATTR_MAGIC) ||
532       bad_object!(hist, LIQ_HISTOGRAM_MAGIC) { return Error::InvalidPointer; }
533    let attr = &attr.inner;
534    let hist = &mut hist.inner;
535
536    let res = hist.quantize(attr)
537        .map(|inner| liq_result {
538            magic_header: LIQ_RESULT_MAGIC,
539            inner,
540        });
541    store_boxed_result(res, write_only_output)
542}
543
544#[no_mangle]
545#[inline(never)]
546pub unsafe extern "C" fn liq_result_from_palette(
547    attr: &liq_attr,
548    palette: *const RGBA,
549    palette_size: c_uint,
550    gamma: f64,
551    write_only_output: &mut MaybeUninit<Option<Box<liq_result>>>,
552) -> liq_error {
553    if bad_object!(attr, LIQ_ATTR_MAGIC) {
554        return Error::InvalidPointer;
555    }
556    let Ok(palette_size) = palette_size.try_into() else {
557        return Error::ValueOutOfRange;
558    };
559    if liq_received_invalid_pointer(palette.cast()) {
560        return Error::InvalidPointer;
561    }
562
563    let attr = &attr.inner;
564    let palette = std::slice::from_raw_parts(palette, palette_size);
565
566    let res = QuantizationResult::from_palette(attr, palette, gamma).map(|inner| liq_result {
567        magic_header: LIQ_RESULT_MAGIC,
568        inner,
569    });
570    store_boxed_result(res, write_only_output)
571}
572
573#[inline]
574fn store_boxed_result<T>(res: Result<T, liq_error>, out: &mut MaybeUninit<Option<Box<T>>>) -> liq_error {
575    match res {
576        Ok(res) => { out.write(Some(Box::new(res))); LIQ_OK },
577        Err(err) => { out.write(None); err },
578    }
579}
580
581pub(crate) fn check_image_size(attr: &liq_attr, width: u32, height: u32) -> bool {
582    if bad_object!(attr, LIQ_ATTR_MAGIC) { return false; }
583
584    if width == 0 || height == 0 {
585        return false;
586    }
587
588    if width as usize > c_int::MAX as usize / std::mem::size_of::<liq_color>() / height as usize ||
589       width as usize > c_int::MAX as usize / 16 / std::mem::size_of::<f32>() ||
590       height as usize > c_int::MAX as usize / std::mem::size_of::<usize>()
591    {
592        return false;
593    }
594    true
595}
596
597#[no_mangle]
598#[inline(never)]
599pub unsafe extern "C" fn liq_image_create_custom(attr: &liq_attr, row_callback: liq_image_get_rgba_row_callback, user_info: AnySyncSendPtr, width: c_uint, height: c_uint, gamma: f64)
600 -> Option<Box<liq_image<'static>>> {
601    let cb: Box<dyn Fn(&mut [MaybeUninit<RGBA>], usize) + Send + Sync> = Box::new(move |row, y| row_callback(row.as_mut_ptr(), y as _, row.len() as _, user_info));
602    liq_image_create_custom_impl(&attr.inner, cb, width as _, height as _, gamma)
603        .map(move |inner| Box::new(liq_image {
604            magic_header: LIQ_IMAGE_MAGIC,
605            inner: ManuallyDrop::new(inner),
606            c_api_free: attr.c_api_free,
607        }))
608}
609
610#[no_mangle]
611#[inline(never)]
612pub unsafe extern "C" fn liq_image_create_rgba_rows<'rows>(attr: &liq_attr, rows: *const *const RGBA, width: c_uint, height: c_uint, gamma: f64) -> Option<Box<liq_image<'rows>>> {
613    if !check_image_size(attr, width, height) { return None; }
614    if rows.is_null() { return None; }
615    let rows = std::slice::from_raw_parts(rows, height as _);
616    liq_image_create_rgba_rows_impl(&attr.inner, rows, width as _,  height as _, gamma)
617        .map(move |inner| Box::new(liq_image {
618            magic_header: LIQ_IMAGE_MAGIC,
619            inner: ManuallyDrop::new(inner),
620            c_api_free: attr.c_api_free,
621        }))
622}
623
624#[no_mangle]
625#[inline(never)]
626pub unsafe extern "C" fn liq_image_create_rgba<'pixels>(attr: &liq_attr, pixels: *const liq_color, width: c_uint, height: c_uint, gamma: f64) -> Option<Box<liq_image<'pixels>>> {
627    if liq_received_invalid_pointer(pixels.cast()) { return None; }
628    if !check_image_size(attr, width, height) { return None; }
629    let rows = (0..height as usize).map(move |i| pixels.add(width as usize * i)).collect();
630    liq_image_create_rgba_bitmap_impl(&attr.inner, rows, width as _, height as _, gamma)
631        .map(move |inner| Box::new(liq_image {
632            magic_header: LIQ_IMAGE_MAGIC,
633            inner: ManuallyDrop::new(inner),
634            c_api_free: attr.c_api_free,
635        }))
636}
637
638#[no_mangle]
639#[inline(never)]
640pub unsafe extern "C" fn liq_histogram_add_colors(input_hist: &mut liq_histogram, attr: &liq_attr, entries: *const HistogramEntry, num_entries: c_int, gamma: f64) -> liq_error {
641    if bad_object!(attr, LIQ_ATTR_MAGIC) ||
642       bad_object!(input_hist, LIQ_HISTOGRAM_MAGIC) { return Error::InvalidPointer; }
643    let input_hist = &mut input_hist.inner;
644    if num_entries == 0 {
645        return LIQ_OK;
646    }
647
648    let Ok(num_entries) = num_entries.try_into() else {
649        return Error::ValueOutOfRange;
650    };
651
652    if liq_received_invalid_pointer(entries.cast()) { return Error::InvalidPointer; }
653
654    let entries = std::slice::from_raw_parts(entries, num_entries);
655
656    input_hist.add_colors(entries, gamma).err().unwrap_or(LIQ_OK)
657}
658
659#[no_mangle]
660#[inline(never)]
661pub extern "C" fn liq_histogram_add_image(input_hist: &mut liq_histogram, attr: &liq_attr, input_image: &mut liq_image) -> liq_error {
662    if bad_object!(attr, LIQ_ATTR_MAGIC) ||
663       bad_object!(input_hist, LIQ_HISTOGRAM_MAGIC) ||
664       bad_object!(input_image, LIQ_IMAGE_MAGIC) { return Error::InvalidPointer; }
665    let attr = &attr.inner;
666    let input_hist = &mut input_hist.inner;
667    let input_image = &mut input_image.inner;
668
669    input_hist.add_image(attr, input_image).err().unwrap_or(LIQ_OK)
670}
671
672/// This is just to exist in backtraces of crashes that aren't mine
673#[no_mangle]
674#[inline(never)]
675pub unsafe extern "Rust" fn liq_executing_user_callback(callback: liq_image_get_rgba_row_callback, temp_row: &mut [MaybeUninit<liq_color>], row: usize, user_info: AnySyncSendPtr) {
676    callback(temp_row.as_mut_ptr(), row as _, temp_row.len() as _, user_info);
677}
678
679#[test]
680fn links_and_runs() {
681    use std::ptr;
682    unsafe {
683        assert!(liq_version() >= 40000);
684        let attr = liq_attr_create().unwrap();
685        let mut hist = liq_histogram_create(&attr).unwrap();
686        assert_eq!(LIQ_OK, liq_histogram_add_fixed_color(&mut hist, liq_color {r: 0, g: 0, b: 0, a: 0}, 0.));
687        liq_histogram_add_colors(&mut hist, &attr, ptr::null(), 0, 0.);
688
689        let mut res = MaybeUninit::uninit();
690
691        // this is fine, because there is 1 fixed color to generate
692        assert_eq!(LIQ_OK, liq_histogram_quantize(&mut hist, &attr, &mut res));
693        let res = res.assume_init().unwrap();
694
695        liq_result_destroy(Some(res));
696        liq_histogram_destroy(Some(hist));
697        liq_attr_destroy(Some(attr));
698    }
699}
700
701#[test]
702#[allow(deprecated)]
703fn link_every_symbol() {
704    use std::os::raw::c_void;
705
706    let x = liq_attr_create as *const c_void as usize
707        + liq_attr_create_with_allocator as *const c_void as usize
708        + liq_attr_copy as *const c_void as usize
709        + liq_attr_destroy as *const c_void as usize
710        + liq_set_max_colors as *const c_void as usize
711        + liq_get_max_colors as *const c_void as usize
712        + liq_set_speed as *const c_void as usize
713        + liq_get_speed as *const c_void as usize
714        + liq_set_min_posterization as *const c_void as usize
715        + liq_get_min_posterization as *const c_void as usize
716        + liq_set_quality as *const c_void as usize
717        + liq_get_min_quality as *const c_void as usize
718        + liq_get_max_quality as *const c_void as usize
719        + liq_set_last_index_transparent as *const c_void as usize
720        + liq_image_create_rgba_rows as *const c_void as usize
721        + liq_image_create_rgba as *const c_void as usize
722        + liq_image_set_memory_ownership as *const c_void as usize
723        + liq_set_log_callback as *const c_void as usize
724        + liq_set_log_flush_callback as *const c_void as usize
725        + liq_attr_set_progress_callback as *const c_void as usize
726        + liq_result_set_progress_callback as *const c_void as usize
727        + liq_image_create_custom as *const c_void as usize
728        + liq_image_set_background as *const c_void as usize
729        + liq_image_set_importance_map as *const c_void as usize
730        + liq_image_add_fixed_color as *const c_void as usize
731        + liq_image_get_width as *const c_void as usize
732        + liq_image_get_height as *const c_void as usize
733        + liq_image_destroy as *const c_void as usize
734        + liq_histogram_create as *const c_void as usize
735        + liq_histogram_add_image as *const c_void as usize
736        + liq_histogram_add_colors as *const c_void as usize
737        + liq_histogram_add_fixed_color as *const c_void as usize
738        + liq_histogram_destroy as *const c_void as usize
739        + liq_quantize_image as *const c_void as usize
740        + liq_histogram_quantize as *const c_void as usize
741        + liq_image_quantize as *const c_void as usize
742        + liq_result_from_palette as *const c_void as usize
743        + liq_set_dithering_level as *const c_void as usize
744        + liq_set_output_gamma as *const c_void as usize
745        + liq_get_output_gamma as *const c_void as usize
746        + liq_get_palette as *const c_void as usize
747        + liq_write_remapped_image as *const c_void as usize
748        + liq_write_remapped_image_rows as *const c_void as usize
749        + liq_get_quantization_error as *const c_void as usize
750        + liq_get_quantization_quality as *const c_void as usize
751        + liq_result_destroy as *const c_void as usize
752        + liq_get_remapping_error as *const c_void as usize
753        + liq_get_remapping_quality as *const c_void as usize
754        + liq_version as *const c_void as usize;
755    assert_ne!(!0, x);
756}
757
758#[test]
759fn c_callback_test_c() {
760    use std::mem::MaybeUninit;
761
762    let mut called = 0;
763    let mut res = unsafe {
764        let mut a = liq_attr_create().unwrap();
765        unsafe extern "C" fn get_row(output_row: *mut MaybeUninit<RGBA>, y: c_int, width: c_int, user_data: AnySyncSendPtr) {
766            assert!((0..5).contains(&y));
767            assert_eq!(123, width);
768            for i in 0..width as isize {
769                let n = i as u8;
770                (*output_row.offset(i)).write(RGBA::new(n, n, n, n));
771            }
772            let user_data = user_data.0.cast::<i32>();
773            *user_data += 1;
774        }
775        let mut img = liq_image_create_custom(&a, get_row, AnySyncSendPtr(std::ptr::addr_of_mut!(called).cast::<c_void>()), 123, 5, 0.).unwrap();
776        liq_quantize_image(&mut a, &mut img).unwrap()
777    };
778    assert!(called > 5 && called < 50);
779    let pal = liq_get_palette(&mut res).unwrap();
780    assert_eq!(123, pal.count);
781}
782
783
784
785#[test]
786fn ownership_bitflags() {
787    assert_eq!(4+16, (liq_ownership::LIQ_OWN_ROWS | liq_ownership::LIQ_COPY_PIXELS).bits());
788}