1 //! https://pngquant.org/lib/
2 //!
3 //! Converts RGBA images to 8-bit with alpha channel.
4 //!
5 //! This is based on imagequant library, which generates very high quality images.
6 //!
7 //! See `examples/` directory for example code.
8 #![doc(html_logo_url = "https://pngquant.org/pngquant-logo.png")]
9 #![warn(missing_docs)]
10 
11 pub use crate::ffi::liq_error;
12 pub use crate::ffi::liq_error::*;
13 
14 use fallible_collections::FallibleVec;
15 use imagequant_sys as ffi;
16 use std::fmt;
17 use std::marker::PhantomData;
18 use std::mem::MaybeUninit;
19 use std::mem;
20 use std::os::raw::{c_int, c_void};
21 use std::ptr;
22 
23 pub use rgb::RGBA8 as RGBA;
24 
25 /// Allocates all memory used by the library, like [`libc::malloc`].
26 ///
27 /// Must return properly aligned memory (16-bytes on x86, pointer size on other architectures).
28 pub type MallocUnsafeFn = unsafe extern "C" fn(size: usize) -> *mut c_void;
29 
30 /// Frees all memory used by the library, like [`libc::free`].
31 pub type FreeUnsafeFn = unsafe extern "C" fn(*mut c_void);
32 
33 /// 8-bit RGBA. This is the only color format used by the library.
34 pub type Color = ffi::liq_color;
35 
36 /// Number of pixels in a given color
37 ///
38 /// Used if you're building histogram manually. Otherwise see `add_image()`
39 pub type HistogramEntry = ffi::liq_histogram_entry;
40 
41 /// Settings for the conversion process. Start here.
42 pub struct Attributes {
43     handle: *mut ffi::liq_attr,
44     malloc: MallocUnsafeFn,
45     free: FreeUnsafeFn,
46 }
47 
48 /// Describes image dimensions for the library.
49 pub struct Image<'a> {
50     handle: *mut ffi::liq_image,
51     /// Holds row pointers for images with stride
52     _marker: PhantomData<&'a [u8]>,
53 }
54 
55 /// Palette inside.
56 pub struct QuantizationResult {
57     handle: *mut ffi::liq_result,
58 }
59 
60 /// Generate one shared palette for multiple images.
61 pub struct Histogram<'a> {
62     attr: &'a Attributes,
63     handle: *mut ffi::liq_histogram,
64 }
65 
66 impl Drop for Attributes {
67     #[inline]
drop(&mut self)68     fn drop(&mut self) {
69         unsafe {
70             if !self.handle.is_null() {
71                 ffi::liq_attr_destroy(&mut *self.handle);
72             }
73         }
74     }
75 }
76 
77 impl<'a> Drop for Image<'a> {
78     #[inline]
drop(&mut self)79     fn drop(&mut self) {
80         unsafe {
81             ffi::liq_image_destroy(&mut *self.handle);
82         }
83     }
84 }
85 
86 impl Drop for QuantizationResult {
87     #[inline]
drop(&mut self)88     fn drop(&mut self) {
89         unsafe {
90             ffi::liq_result_destroy(&mut *self.handle);
91         }
92     }
93 }
94 
95 impl<'a> Drop for Histogram<'a> {
96     #[inline]
drop(&mut self)97     fn drop(&mut self) {
98         unsafe {
99             ffi::liq_histogram_destroy(&mut *self.handle);
100         }
101     }
102 }
103 
104 impl Clone for Attributes {
105     #[inline]
clone(&self) -> Attributes106     fn clone(&self) -> Attributes {
107         unsafe {
108             Attributes {
109                 handle: ffi::liq_attr_copy(&*self.handle),
110                 malloc: self.malloc,
111                 free: self.free,
112             }
113         }
114     }
115 }
116 
117 impl Default for Attributes {
118     #[inline(always)]
default() -> Attributes119     fn default() -> Attributes {
120         Attributes::new()
121     }
122 }
123 
124 impl Attributes {
125     /// New handle for library configuration
126     ///
127     /// See also `new_image()`
128     #[inline]
129     #[must_use]
new() -> Self130     pub fn new() -> Self {
131         let handle = unsafe { ffi::liq_attr_create() };
132         assert!(!handle.is_null(), "SSE-capable CPU is required for this build.");
133         Attributes {
134             handle,
135             malloc: libc::malloc,
136             free: libc::free,
137         }
138     }
139 
140     /// New handle for library configuration, with specified custom allocator for internal use.
141     ///
142     /// See also `new_image()`
143     ///
144     /// # Safety
145     ///
146     /// * `malloc` and `free` must behave according to their corresponding C specification.
147     /// * `malloc` must return properly aligned memory (16-bytes on x86, pointer-sized on other architectures).
148     #[inline]
149     #[must_use]
with_allocator(malloc: MallocUnsafeFn, free: FreeUnsafeFn) -> Self150     pub unsafe fn with_allocator(malloc: MallocUnsafeFn, free: FreeUnsafeFn) -> Self {
151         let handle = ffi::liq_attr_create_with_allocator(malloc, free);
152         assert!(!handle.is_null(), "SSE-capable CPU is required for this build.");
153         Attributes { handle, malloc, free }
154     }
155 
156     /// It's better to use `set_quality()`
157     #[inline]
set_max_colors(&mut self, value: i32) -> liq_error158     pub fn set_max_colors(&mut self, value: i32) -> liq_error {
159         unsafe { ffi::liq_set_max_colors(&mut *self.handle, value) }
160     }
161 
162     /// Number of least significant bits to ignore.
163     ///
164     /// Useful for generating palettes for VGA, 15-bit textures, or other retro platforms.
165     #[inline]
set_min_posterization(&mut self, value: i32) -> liq_error166     pub fn set_min_posterization(&mut self, value: i32) -> liq_error {
167         unsafe { ffi::liq_set_min_posterization(&mut *self.handle, value) }
168     }
169 
170     /// Returns number of bits of precision truncated
171     #[inline]
min_posterization(&mut self) -> i32172     pub fn min_posterization(&mut self) -> i32 {
173         unsafe { ffi::liq_get_min_posterization(&*self.handle) }
174     }
175 
176     /// Range 0-100, roughly like JPEG.
177     ///
178     /// If minimum quality can't be met, quantization will fail.
179     ///
180     /// Default is min 0, max 100.
181     #[inline]
set_quality(&mut self, min: u32, max: u32) -> liq_error182     pub fn set_quality(&mut self, min: u32, max: u32) -> liq_error {
183         unsafe { ffi::liq_set_quality(&mut *self.handle, min as c_int, max as c_int) }
184     }
185 
186     /// Reads values set with `set_quality`
187     #[inline]
quality(&mut self) -> (u32, u32)188     pub fn quality(&mut self) -> (u32, u32) {
189         unsafe {
190             (ffi::liq_get_min_quality(&*self.handle) as u32,
191              ffi::liq_get_max_quality(&*self.handle) as u32)
192         }
193     }
194 
195     /// 1-10.
196     ///
197     /// Faster speeds generate images of lower quality, but may be useful
198     /// for real-time generation of images.
199     #[inline]
set_speed(&mut self, value: i32) -> liq_error200     pub fn set_speed(&mut self, value: i32) -> liq_error {
201         unsafe { ffi::liq_set_speed(&mut *self.handle, value) }
202     }
203 
204     /// Move transparent color to the last entry in the palette
205     ///
206     /// This is less efficient for PNG, but required by some broken software
207     #[inline]
set_last_index_transparent(&mut self, value: bool)208     pub fn set_last_index_transparent(&mut self, value: bool) {
209         unsafe { ffi::liq_set_last_index_transparent(&mut *self.handle, value as c_int) }
210     }
211 
212     /// Return currently set speed/quality trade-off setting
213     #[inline(always)]
214     #[must_use]
speed(&mut self) -> i32215     pub fn speed(&mut self) -> i32 {
216         unsafe { ffi::liq_get_speed(&*self.handle) }
217     }
218 
219     /// Return max number of colors set
220     #[inline(always)]
221     #[must_use]
max_colors(&mut self) -> i32222     pub fn max_colors(&mut self) -> i32 {
223         unsafe { ffi::liq_get_max_colors(&*self.handle) }
224     }
225 
226     /// Describe dimensions of a slice of RGBA pixels
227     ///
228     /// Use 0.0 for gamma if the image is sRGB (most images are).
229     #[inline]
new_image<'a>(&self, bitmap: &'a [RGBA], width: usize, height: usize, gamma: f64) -> Result<Image<'a>, liq_error>230     pub fn new_image<'a>(&self, bitmap: &'a [RGBA], width: usize, height: usize, gamma: f64) -> Result<Image<'a>, liq_error> {
231         Image::new(self, bitmap, width, height, gamma)
232     }
233 
234     /// Stride is in pixels. Allows defining regions of larger images or images with padding without copying.
235     #[inline]
new_image_stride<'a>(&self, bitmap: &'a [RGBA], width: usize, height: usize, stride: usize, gamma: f64) -> Result<Image<'a>, liq_error>236     pub fn new_image_stride<'a>(&self, bitmap: &'a [RGBA], width: usize, height: usize, stride: usize, gamma: f64) -> Result<Image<'a>, liq_error> {
237         Image::new_stride(self, bitmap, width, height, stride, gamma)
238     }
239 
240     /// Like `new_image_stride`, but makes a copy of the pixels
241     #[inline]
new_image_stride_copy(&self, bitmap: &[RGBA], width: usize, height: usize, stride: usize, gamma: f64) -> Result<Image<'static>, liq_error>242     pub fn new_image_stride_copy(&self, bitmap: &[RGBA], width: usize, height: usize, stride: usize, gamma: f64) -> Result<Image<'static>, liq_error> {
243         Image::new_stride_copy(self, bitmap, width, height, stride, gamma)
244     }
245 
246     /// Create new histogram
247     ///
248     /// Use to make one palette suitable for many images
249     #[inline(always)]
250     #[must_use]
new_histogram(&self) -> Histogram<'_>251     pub fn new_histogram(&self) -> Histogram<'_> {
252         Histogram::new(&self)
253     }
254 
255     /// Generate palette for the image
quantize(&mut self, image: &Image<'_>) -> Result<QuantizationResult, liq_error>256     pub fn quantize(&mut self, image: &Image<'_>) -> Result<QuantizationResult, liq_error> {
257         unsafe {
258             let mut h = ptr::null_mut();
259             match ffi::liq_image_quantize(&*image.handle, &*self.handle, &mut h) {
260                 liq_error::LIQ_OK if !h.is_null() => Ok(QuantizationResult { handle: h }),
261                 err => Err(err),
262             }
263         }
264     }
265 }
266 
267 /// Start here: creates new handle for library configuration
268 #[inline(always)]
269 #[must_use]
new() -> Attributes270 pub fn new() -> Attributes {
271     Attributes::new()
272 }
273 
274 impl<'a> Histogram<'a> {
275     /// Creates histogram object that will be used to collect color statistics from multiple images.
276     ///
277     /// All options should be set on `attr` before the histogram object is created. Options changed later may not have effect.
278     #[inline]
279     #[must_use]
new(attr: &'a Attributes) -> Self280     pub fn new(attr: &'a Attributes) -> Self {
281         Histogram {
282             attr,
283             handle: unsafe { ffi::liq_histogram_create(&*attr.handle) },
284         }
285     }
286 
287     /// "Learns" colors from the image, which will be later used to generate the palette.
288     ///
289     /// Fixed colors added to the image are also added to the histogram. If total number of fixed colors exceeds 256, this function will fail with `LIQ_BUFFER_TOO_SMALL`.
290     #[inline]
add_image(&mut self, image: &mut Image<'_>) -> liq_error291     pub fn add_image(&mut self, image: &mut Image<'_>) -> liq_error {
292         unsafe { ffi::liq_histogram_add_image(&mut *self.handle, &*self.attr.handle, &mut *image.handle) }
293     }
294 
295     /// Alternative to `add_image()`. Intead of counting colors in an image, it directly takes an array of colors and their counts.
296     ///
297     /// This function is only useful if you already have a histogram of the image from another source.
298     #[inline]
add_colors(&mut self, colors: &[HistogramEntry], gamma: f64) -> liq_error299     pub fn add_colors(&mut self, colors: &[HistogramEntry], gamma: f64) -> liq_error {
300         unsafe {
301             ffi::liq_histogram_add_colors(&mut *self.handle, &*self.attr.handle, colors.as_ptr(), colors.len() as c_int, gamma)
302         }
303     }
304 
305     /// Generate palette for all images/colors added to the histogram.
306     ///
307     /// Palette generated using this function won't be improved during remapping.
308     /// If you're generating palette for only one image, it's better not to use the `Histogram`.
309     #[inline]
quantize(&mut self) -> Result<QuantizationResult, liq_error>310     pub fn quantize(&mut self) -> Result<QuantizationResult, liq_error> {
311         unsafe {
312             let mut h = ptr::null_mut();
313             match ffi::liq_histogram_quantize(&*self.handle, &*self.attr.handle, &mut h) {
314                 liq_error::LIQ_OK if !h.is_null() => Ok(QuantizationResult { handle: h }),
315                 err => Err(err),
316             }
317         }
318     }
319 }
320 
321 /// Generate image row on the fly
322 ///
323 /// `output_row` is an array `width` RGBA elements wide.
324 /// `y` is the row (0-indexed) to write to the `output_row`
325 /// `user_data` is the data given to `Image::new_unsafe_fn()`
326 pub type ConvertRowUnsafeFn<UserData> = unsafe extern "C" fn(output_row: *mut Color, y: c_int, width: c_int, user_data: *mut UserData);
327 
328 impl<'bitmap> Image<'bitmap> {
329     /// Describe dimensions of a slice of RGBA pixels.
330     ///
331     /// `bitmap` must be either `&[u8]` or a slice with one element per pixel (`&[RGBA]`).
332     ///
333     /// Use `0.` for gamma if the image is sRGB (most images are).
334     #[inline(always)]
new(attr: &Attributes, bitmap: &'bitmap [RGBA], width: usize, height: usize, gamma: f64) -> Result<Self, liq_error>335     pub fn new(attr: &Attributes, bitmap: &'bitmap [RGBA], width: usize, height: usize, gamma: f64) -> Result<Self, liq_error> {
336         Self::new_stride(attr, bitmap, width, height, width, gamma)
337     }
338 
339     /// Generate rows on demand using a callback function.
340     ///
341     /// The callback function must be cheap (e.g. just byte-swap pixels).
342     /// It will be called multiple times per row. May be called in any order from any thread.
343     ///
344     /// The user data must be compatible with a primitive pointer
345     /// (i.e. not a slice, not a Trait object. `Box` it if you must).
346     #[inline]
new_unsafe_fn<CustomData: Send + Sync + 'bitmap>(attr: &Attributes, convert_row_fn: ConvertRowUnsafeFn<CustomData>, user_data: *mut CustomData, width: usize, height: usize, gamma: f64) -> Result<Self, liq_error>347     pub fn new_unsafe_fn<CustomData: Send + Sync + 'bitmap>(attr: &Attributes, convert_row_fn: ConvertRowUnsafeFn<CustomData>, user_data: *mut CustomData, width: usize, height: usize, gamma: f64) -> Result<Self, liq_error> {
348         unsafe {
349             match ffi::liq_image_create_custom(&*attr.handle, mem::transmute(convert_row_fn), user_data.cast(), width as c_int, height as c_int, gamma) {
350                 handle if !handle.is_null() => Ok(Image { handle, _marker: PhantomData }),
351                 _ => Err(LIQ_INVALID_POINTER),
352             }
353         }
354     }
355 
356     /// Stride is in pixels. Allows defining regions of larger images or images with padding without copying.
357     #[inline(always)]
new_stride(attr: &Attributes, bitmap: &'bitmap [RGBA], width: usize, height: usize, stride: usize, gamma: f64) -> Result<Self, liq_error>358     pub fn new_stride(attr: &Attributes, bitmap: &'bitmap [RGBA], width: usize, height: usize, stride: usize, gamma: f64) -> Result<Self, liq_error> {
359         // Type definition preserves the lifetime, so it's not unsafe
360         unsafe { Self::new_stride_internal(attr, bitmap, width, height, stride, gamma, false) }
361     }
362 
363     /// Create new image by copying `bitmap` to an internal buffer, so that it makes a self-contained type.
364     #[inline(always)]
new_stride_copy(attr: &Attributes, bitmap: &[RGBA], width: usize, height: usize, stride: usize, gamma: f64) -> Result<Image<'static>, liq_error>365     pub fn new_stride_copy(attr: &Attributes, bitmap: &[RGBA], width: usize, height: usize, stride: usize, gamma: f64) -> Result<Image<'static>, liq_error> {
366         // copy guarantees the image doesn't reference the bitmap any more
367         unsafe { Self::new_stride_internal(attr, bitmap, width, height, stride, gamma, true) }
368     }
369 
new_stride_internal<'varies>(attr: &Attributes, bitmap: &[RGBA], width: usize, height: usize, stride: usize, gamma: f64, copy: bool) -> Result<Image<'varies>, liq_error>370     unsafe fn new_stride_internal<'varies>(attr: &Attributes, bitmap: &[RGBA], width: usize, height: usize, stride: usize, gamma: f64, copy: bool) -> Result<Image<'varies>, liq_error> {
371         if bitmap.len() < (stride * height + width - stride) {
372             eprintln!("Buffer length is {} bytes, which is not enough for {}×{}×4 RGBA bytes", bitmap.len()*4, stride, height);
373             return Err(LIQ_BUFFER_TOO_SMALL);
374         }
375         let (bitmap, ownership) = if copy {
376             let copied = (attr.malloc)(4 * bitmap.len()) as *mut RGBA;
377             ptr::copy_nonoverlapping(bitmap.as_ptr(), copied, bitmap.len());
378             (copied as *const _, ffi::liq_ownership::LIQ_OWN_ROWS | ffi::liq_ownership::LIQ_OWN_PIXELS)
379         } else {
380             (bitmap.as_ptr(), ffi::liq_ownership::LIQ_OWN_ROWS)
381         };
382         let rows = Self::malloc_image_rows(bitmap, stride, height, attr.malloc);
383         let h = ffi::liq_image_create_rgba_rows(&*attr.handle, rows, width as c_int, height as c_int, gamma);
384         if h.is_null() {
385             (attr.free)(rows.cast());
386             return Err(LIQ_INVALID_POINTER);
387         }
388         let img = Image {
389             handle: h,
390             _marker: PhantomData,
391         };
392         match ffi::liq_image_set_memory_ownership(&*h, ownership) {
393             LIQ_OK => Ok(img),
394             err => {
395                 drop(img);
396                 (attr.free)(rows.cast());
397                 Err(err)
398             },
399         }
400     }
401 
402     /// For arbitrary stride libimagequant requires rows. It's most convenient if they're allocated using libc,
403     /// so they can be owned and freed automatically by the C library.
malloc_image_rows(bitmap: *const RGBA, stride: usize, height: usize, malloc: MallocUnsafeFn) -> *mut *const u8404     unsafe fn malloc_image_rows(bitmap: *const RGBA, stride: usize, height: usize, malloc: MallocUnsafeFn) -> *mut *const u8 {
405         let mut byte_ptr = bitmap as *const u8;
406         let stride_bytes = stride * 4;
407         let rows = malloc(mem::size_of::<*const u8>() * height) as *mut *const u8;
408         for y in 0..height {
409             *rows.add(y) = byte_ptr;
410             byte_ptr = byte_ptr.add(stride_bytes);
411         }
412         rows
413     }
414 
415     /// Width of the image in pixels
416     #[inline]
417     #[must_use]
width(&self) -> usize418     pub fn width(&self) -> usize {
419         unsafe { ffi::liq_image_get_width(&*self.handle) as usize }
420     }
421 
422     /// Height of the image in pixels
423     #[inline]
424     #[must_use]
height(&self) -> usize425     pub fn height(&self) -> usize {
426         unsafe { ffi::liq_image_get_height(&*self.handle) as usize }
427     }
428 
429     /// Reserves a color in the output palette created from this image. It behaves as if the given color was used in the image and was very important.
430     ///
431     /// RGB values of liq_color are assumed to have the same gamma as the image.
432     ///
433     /// It must be called before the image is quantized.
434     ///
435     /// Returns error if more than 256 colors are added. If image is quantized to fewer colors than the number of fixed colors added, then excess fixed colors will be ignored.
436     #[inline]
add_fixed_color(&mut self, color: ffi::liq_color) -> liq_error437     pub fn add_fixed_color(&mut self, color: ffi::liq_color) -> liq_error {
438         unsafe { ffi::liq_image_add_fixed_color(&mut *self.handle, color) }
439     }
440 
441     /// Remap pixels assuming they will be displayed on this background.
442     ///
443     /// Pixels that match the background color will be made transparent if there's a fully transparent color available in the palette.
444     ///
445     /// The background image's pixels must outlive this image
446     #[inline]
set_background<'own, 'bg: 'own>(&'own mut self, background: Image<'bg>) -> Result<(), liq_error>447     pub fn set_background<'own, 'bg: 'own>(&'own mut self, background: Image<'bg>) -> Result<(), liq_error> {
448         unsafe {
449             ffi::liq_image_set_background(&mut *self.handle, background.into_raw()).ok()
450         }
451     }
452 
453     /// Set which pixels are more important (and more likely to get a palette entry)
454     ///
455     /// The map must be `width`×`height` pixels large. Higher numbers = more important.
456     #[inline]
set_importance_map(&mut self, map: &[u8]) -> Result<(), liq_error>457     pub fn set_importance_map(&mut self, map: &[u8]) -> Result<(), liq_error> {
458         unsafe {
459             ffi::liq_image_set_importance_map(&mut *self.handle, map.as_ptr() as *mut _, map.len(), ffi::liq_ownership::LIQ_COPY_PIXELS).ok()
460         }
461     }
462 
463     #[inline]
into_raw(mut self) -> *mut ffi::liq_image464     fn into_raw(mut self) -> *mut ffi::liq_image {
465         let handle = self.handle;
466         self.handle = ptr::null_mut();
467         handle
468     }
469 }
470 
471 impl QuantizationResult {
472     /// Set to 1.0 to get nice smooth image
473     #[inline]
set_dithering_level(&mut self, value: f32) -> liq_error474     pub fn set_dithering_level(&mut self, value: f32) -> liq_error {
475         unsafe { ffi::liq_set_dithering_level(&mut *self.handle, value) }
476     }
477 
478     /// The default is sRGB gamma (~1/2.2)
479     #[inline]
set_output_gamma(&mut self, value: f64) -> liq_error480     pub fn set_output_gamma(&mut self, value: f64) -> liq_error {
481         unsafe { ffi::liq_set_output_gamma(&mut *self.handle, value) }
482     }
483 
484     /// Approximate gamma correction value used for the output
485     ///
486     /// Colors are converted from input gamma to this gamma
487     #[inline]
488     #[must_use]
output_gamma(&mut self) -> f64489     pub fn output_gamma(&mut self) -> f64 {
490         unsafe { ffi::liq_get_output_gamma(&*self.handle) }
491     }
492 
493     /// Number 0-100 guessing how nice the input image will look if remapped to this palette
494     #[inline]
495     #[must_use]
quantization_quality(&self) -> i32496     pub fn quantization_quality(&self) -> i32 {
497         unsafe { ffi::liq_get_quantization_quality(&*self.handle) as i32 }
498     }
499 
500     /// Approximate mean square error of the palette
501     #[inline]
502     #[must_use]
quantization_error(&self) -> Option<f64>503     pub fn quantization_error(&self) -> Option<f64> {
504         match unsafe { ffi::liq_get_quantization_error(&*self.handle) } {
505             x if x < 0. => None,
506             x => Some(x),
507         }
508     }
509 
510     /// Final palette
511     ///
512     /// It's slighly better if you get palette from the `remapped()` call instead
513     #[must_use]
palette(&mut self) -> Vec<Color>514     pub fn palette(&mut self) -> Vec<Color> {
515         let pal = self.palette_ref();
516         let mut out: Vec<Color> = FallibleVec::try_with_capacity(pal.len()).unwrap();
517         out.extend_from_slice(pal);
518         out
519     }
520 
521     /// Final palette (as a temporary slice)
522     ///
523     /// It's slighly better if you get palette from the `remapped()` call instead
524     ///
525     /// Use when ownership of the palette colors is not needed
526     #[inline]
palette_ref(&mut self) -> &[Color]527     pub fn palette_ref(&mut self) -> &[Color] {
528         unsafe {
529             let pal = &*ffi::liq_get_palette(&mut *self.handle);
530             std::slice::from_raw_parts(pal.entries.as_ptr(), (pal.count as usize).min(pal.entries.len()))
531         }
532     }
533 
534     /// Remap image into a `Vec`
535     ///
536     /// Returns the palette and a 1-byte-per-pixel uncompressed bitmap
remapped(&mut self, image: &mut Image<'_>) -> Result<(Vec<Color>, Vec<u8>), liq_error>537     pub fn remapped(&mut self, image: &mut Image<'_>) -> Result<(Vec<Color>, Vec<u8>), liq_error> {
538         let len = image.width() * image.height();
539         // Capacity is essential here, as it creates uninitialized buffer
540         unsafe {
541             let mut buf: Vec<u8> = FallibleVec::try_with_capacity(len).map_err(|_| liq_error::LIQ_OUT_OF_MEMORY)?;
542             let uninit_slice = std::slice::from_raw_parts_mut(buf.as_ptr() as *mut MaybeUninit<u8>, buf.capacity());
543             self.remap_into(image, uninit_slice)?;
544             buf.set_len(uninit_slice.len());
545             Ok((self.palette(), buf))
546         }
547     }
548 
549     /// Remap image into an existing buffer.
550     ///
551     /// This is a low-level call for use when existing memory has to be reused. Use `remapped()` if possible.
552     ///
553     /// Writes 1-byte-per-pixel uncompressed bitmap into the pre-allocated buffer.
554     ///
555     /// You should call `palette()` or `palette_ref()` _after_ this call, but not before it,
556     /// because remapping changes the palette.
557     #[inline]
remap_into(&mut self, image: &mut Image<'_>, output_buf: &mut [MaybeUninit<u8>]) -> Result<(), liq_error>558     pub fn remap_into(&mut self, image: &mut Image<'_>, output_buf: &mut [MaybeUninit<u8>]) -> Result<(), liq_error> {
559         unsafe {
560             match ffi::liq_write_remapped_image(&mut *self.handle, &mut *image.handle, output_buf.as_mut_ptr().cast(), output_buf.len()) {
561                 LIQ_OK => Ok(()),
562                 err => Err(err),
563             }
564         }
565     }
566 }
567 
568 impl fmt::Debug for QuantizationResult {
569     #[cold]
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result570     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
571         write!(f, "QuantizationResult(q={})", self.quantization_quality())
572     }
573 }
574 
575 unsafe impl Send for Attributes {}
576 unsafe impl Send for QuantizationResult {}
577 unsafe impl<'bitmap> Send for Image<'bitmap> {}
578 unsafe impl<'a> Send for Histogram<'a> {}
579 
580 #[test]
copy_img()581 fn copy_img() {
582     let tmp = vec![RGBA::new(1,2,3,4); 10*100];
583     let liq = Attributes::new();
584     let _ = liq.new_image_stride_copy(&tmp, 10, 100, 10, 0.).unwrap();
585 }
586 
587 #[test]
takes_rgba()588 fn takes_rgba() {
589     let liq = Attributes::new();
590 
591     use rgb::RGBA8 as RGBA;
592     let img = vec![RGBA {r:0, g:0, b:0, a:0}; 8];
593 
594 
595     liq.new_image(&img, 1, 1, 0.0).unwrap();
596     liq.new_image(&img, 4, 2, 0.0).unwrap();
597     liq.new_image(&img, 8, 1, 0.0).unwrap();
598     assert!(liq.new_image(&img, 9, 1, 0.0).is_err());
599     assert!(liq.new_image(&img, 4, 3, 0.0).is_err());
600 }
601 
602 #[test]
histogram()603 fn histogram() {
604     let attr = Attributes::new();
605     let mut hist = attr.new_histogram();
606 
607     let bitmap1 = vec![RGBA {r:0, g:0, b:0, a:0}; 1];
608     let mut image1 = attr.new_image(&bitmap1[..], 1, 1, 0.0).unwrap();
609     hist.add_image(&mut image1);
610 
611     let bitmap2 = vec![RGBA {r:255, g:255, b:255, a:255}; 1];
612     let mut image2 = attr.new_image(&bitmap2[..], 1, 1, 0.0).unwrap();
613     hist.add_image(&mut image2);
614 
615     hist.add_colors(&[HistogramEntry{
616         color: Color::new(255,128,255,128),
617         count: 10,
618     }], 0.0);
619 
620     let mut res = hist.quantize().unwrap();
621     let pal = res.palette();
622     assert_eq!(3, pal.len());
623 }
624 
625 #[test]
poke_it()626 fn poke_it() {
627     let width = 10usize;
628     let height = 10usize;
629     let mut fakebitmap = vec![RGBA::new(255,255,255,255); width*height];
630 
631     fakebitmap[0].r = 0x55;
632     fakebitmap[0].g = 0x66;
633     fakebitmap[0].b = 0x77;
634 
635     // Configure the library
636     let mut liq = Attributes::new();
637     liq.set_speed(5);
638     liq.set_quality(70, 99);
639     liq.set_min_posterization(1);
640     assert_eq!(1, liq.min_posterization());
641     liq.set_min_posterization(0);
642 
643     // Describe the bitmap
644     let ref mut img = liq.new_image(&fakebitmap[..], width, height, 0.0).unwrap();
645 
646     // The magic happens in quantize()
647     let mut res = match liq.quantize(img) {
648         Ok(res) => res,
649         Err(err) => panic!("Quantization failed, because: {:?}", err),
650     };
651 
652     // Enable dithering for subsequent remappings
653     res.set_dithering_level(1.0);
654 
655     // You can reuse the result to generate several images with the same palette
656     let (palette, pixels) = res.remapped(img).unwrap();
657 
658     assert_eq!(width * height, pixels.len());
659     assert_eq!(100, res.quantization_quality());
660     assert_eq!(Color { r: 255, g: 255, b: 255, a: 255 }, palette[0]);
661     assert_eq!(Color { r: 0x55, g: 0x66, b: 0x77, a: 255 }, palette[1]);
662 }
663 
664 #[test]
set_importance_map()665 fn set_importance_map() {
666     use crate::ffi::liq_color as RGBA;
667     let mut liq = new();
668     let bitmap = &[RGBA::new(255, 0, 0, 255), RGBA::new(0u8, 0, 255, 255)];
669     let ref mut img = liq.new_image(&bitmap[..], 2, 1, 0.).unwrap();
670     let map = &[255, 0];
671     img.set_importance_map(map).unwrap();
672     let mut res = liq.quantize(img).unwrap();
673     let pal = res.palette();
674     assert_eq!(1, pal.len());
675     assert_eq!(bitmap[0], pal[0]);
676 }
677 
678 #[test]
thread()679 fn thread() {
680     let liq = Attributes::new();
681     std::thread::spawn(move || {
682         let b = vec![RGBA::new(0,0,0,0);1];
683         liq.new_image(&b, 1, 1, 0.).unwrap();
684     }).join().unwrap();
685 }
686 
687 #[test]
callback_test()688 fn callback_test() {
689     let mut called = 0;
690     let mut res = {
691         let mut a = new();
692         unsafe extern "C" fn get_row(output_row: *mut Color, y: c_int, width: c_int, user_data: *mut i32) {
693             assert!(y >= 0 && y < 5);
694             assert_eq!(123, width);
695             for i in 0..width as isize {
696                 let n = i as u8;
697                 *output_row.offset(i as isize) = Color::new(n,n,n,n);
698             }
699             *user_data += 1;
700         }
701         let mut img = Image::new_unsafe_fn(&a, get_row, &mut called, 123, 5, 0.).unwrap();
702         a.quantize(&mut img).unwrap()
703     };
704     assert!(called > 5 && called < 50);
705     assert_eq!(123, res.palette().len());
706 }
707 
708 #[test]
custom_allocator_test()709 fn custom_allocator_test() {
710     // SAFETY: This is all in one thread.
711     static mut ALLOC_COUNTR: usize = 0;
712     static mut FREE_COUNTR: usize = 0;
713 
714     unsafe extern "C" fn test_malloc(size: usize) -> *mut c_void {
715         ALLOC_COUNTR += 1;
716         libc::malloc(size)
717     }
718 
719     unsafe extern "C" fn test_free(ptr: *mut c_void) {
720         FREE_COUNTR += 1;
721         libc::free(ptr)
722     }
723 
724     let liq = unsafe { Attributes::with_allocator(test_malloc, test_free) };
725     assert_eq!(unsafe { ALLOC_COUNTR }, 1);
726     assert_eq!(unsafe { FREE_COUNTR }, 0);
727 
728     let liq2 = liq.clone();
729     assert_eq!(liq.malloc, liq2.malloc);
730     assert_eq!(liq.free, liq2.free);
731 
732     drop(liq);
733     assert_eq!(unsafe { ALLOC_COUNTR }, 2);
734     assert_eq!(unsafe { FREE_COUNTR }, 1);
735 
736     drop(liq2);
737     assert_eq!(unsafe { ALLOC_COUNTR }, 2);
738     assert_eq!(unsafe { FREE_COUNTR }, 2);
739 }
740