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