1#![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 const LIQ_OWN_ROWS = 4;
62 const LIQ_OWN_PIXELS = 8;
64 const LIQ_COPY_PIXELS = 16;
66 }
67}
68
69#[repr(transparent)]
70#[derive(PartialEq, Debug, Copy, Clone)]
71pub(crate) struct MagicTag(*const u8);
72unsafe 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#[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
173unsafe 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#[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 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}