/src/libvips/libvips/foreign/quantise.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* quantise an image |
2 | | * |
3 | | * 20/6/18 |
4 | | * - from vipspng.c |
5 | | */ |
6 | | |
7 | | /* |
8 | | |
9 | | This file is part of VIPS. |
10 | | |
11 | | VIPS is free software; you can redistribute it and/or modify |
12 | | it under the terms of the GNU Lesser General Public License as published by |
13 | | the Free Software Foundation; either version 2 of the License, or |
14 | | (at your option) any later version. |
15 | | |
16 | | This program is distributed in the hope that it will be useful, |
17 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | | GNU Lesser General Public License for more details. |
20 | | |
21 | | You should have received a copy of the GNU Lesser General Public License |
22 | | along with this program; if not, write to the Free Software |
23 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
24 | | 02110-1301 USA |
25 | | |
26 | | */ |
27 | | |
28 | | /* |
29 | | |
30 | | These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk |
31 | | |
32 | | */ |
33 | | |
34 | | /* |
35 | | #define DEBUG |
36 | | #define VIPS_DEBUG |
37 | | */ |
38 | | |
39 | | #ifdef HAVE_CONFIG_H |
40 | | #include <config.h> |
41 | | #endif /*HAVE_CONFIG_H*/ |
42 | | #include <glib/gi18n-lib.h> |
43 | | |
44 | | #include <vips/vips.h> |
45 | | |
46 | | #include "quantise.h" |
47 | | |
48 | | #ifdef HAVE_QUANTIZATION |
49 | | |
50 | | #ifdef HAVE_IMAGEQUANT |
51 | | |
52 | | VipsQuantiseAttr * |
53 | | vips__quantise_attr_create() |
54 | 0 | { |
55 | 0 | return liq_attr_create(); |
56 | 0 | } |
57 | | |
58 | | VipsQuantiseError |
59 | | vips__quantise_set_max_colors(VipsQuantiseAttr *attr, int colors) |
60 | 0 | { |
61 | 0 | return liq_set_max_colors(attr, colors); |
62 | 0 | } |
63 | | |
64 | | VipsQuantiseError |
65 | | vips__quantise_set_quality(VipsQuantiseAttr *attr, int minimum, int maximum) |
66 | 0 | { |
67 | 0 | return liq_set_quality(attr, minimum, maximum); |
68 | 0 | } |
69 | | |
70 | | VipsQuantiseError |
71 | | vips__quantise_set_speed(VipsQuantiseAttr *attr, int speed) |
72 | 0 | { |
73 | 0 | return liq_set_speed(attr, speed); |
74 | 0 | } |
75 | | |
76 | | VipsQuantiseImage * |
77 | | vips__quantise_image_create_rgba(const VipsQuantiseAttr *attr, |
78 | | const void *bitmap, int width, int height, double gamma) |
79 | 0 | { |
80 | 0 | return liq_image_create_rgba(attr, bitmap, width, height, gamma); |
81 | 0 | } |
82 | | |
83 | | VipsQuantiseError |
84 | | vips__quantise_image_quantize(VipsQuantiseImage *const input_image, |
85 | | VipsQuantiseAttr *const options, VipsQuantiseResult **result_output) |
86 | 0 | { |
87 | 0 | return liq_image_quantize(input_image, options, result_output); |
88 | 0 | } |
89 | | |
90 | | /* Like vips__quantise_image_quantize(), but make a fixed palette that won't |
91 | | * get remapped during dithering. |
92 | | */ |
93 | | VipsQuantiseError |
94 | | vips__quantise_image_quantize_fixed(VipsQuantiseImage *const input_image, |
95 | | VipsQuantiseAttr *const options, VipsQuantiseResult **result_output) |
96 | 0 | { |
97 | 0 | int i; |
98 | 0 | liq_result *result; |
99 | 0 | const liq_palette *palette; |
100 | 0 | liq_error err; |
101 | 0 | liq_image *fake_image; |
102 | 0 | char fake_image_pixels[4] = { 0 }; |
103 | | |
104 | | /* First, quantize the image and get its palette. |
105 | | */ |
106 | 0 | err = liq_image_quantize(input_image, options, &result); |
107 | 0 | if (err != LIQ_OK) |
108 | 0 | return err; |
109 | | |
110 | 0 | palette = liq_get_palette(result); |
111 | | |
112 | | /* Now, we need a fake 1 pixel image that will be quantized on the |
113 | | * next step. Its pixel color doesn't matter since we'll add all the |
114 | | * colors from the palette further. |
115 | | */ |
116 | 0 | fake_image = |
117 | 0 | liq_image_create_rgba(options, fake_image_pixels, 1, 1, 0); |
118 | 0 | if (!fake_image) { |
119 | 0 | liq_result_destroy(result); |
120 | 0 | return LIQ_OUT_OF_MEMORY; |
121 | 0 | } |
122 | | |
123 | | /* Add all the colors from the palette as fixed colors to the fake |
124 | | * image. Since the fixed colors number is the same as required colors |
125 | | * number, no new colors will be added. |
126 | | */ |
127 | 0 | for (i = 0; i < palette->count; i++) |
128 | 0 | liq_image_add_fixed_color(fake_image, palette->entries[i]); |
129 | |
|
130 | 0 | liq_result_destroy(result); |
131 | | |
132 | | /* Finally, quantize the fake image with fixed colors to make a |
133 | | * VipsQuantiseResult with a fixed palette. |
134 | | */ |
135 | 0 | err = liq_image_quantize(fake_image, options, result_output); |
136 | |
|
137 | 0 | liq_image_destroy(fake_image); |
138 | |
|
139 | 0 | return err; |
140 | 0 | } |
141 | | |
142 | | VipsQuantiseError |
143 | | vips__quantise_set_dithering_level(VipsQuantiseResult *res, |
144 | | float dither_level) |
145 | 0 | { |
146 | 0 | return liq_set_dithering_level(res, dither_level); |
147 | 0 | } |
148 | | |
149 | | const VipsQuantisePalette * |
150 | | vips__quantise_get_palette(VipsQuantiseResult *result) |
151 | 0 | { |
152 | 0 | return liq_get_palette(result); |
153 | 0 | } |
154 | | |
155 | | VipsQuantiseError |
156 | | vips__quantise_write_remapped_image(VipsQuantiseResult *result, |
157 | | VipsQuantiseImage *input_image, void *buffer, size_t buffer_size) |
158 | 0 | { |
159 | 0 | return liq_write_remapped_image( |
160 | 0 | result, input_image, buffer, buffer_size); |
161 | 0 | } |
162 | | |
163 | | void |
164 | | vips__quantise_result_destroy(VipsQuantiseResult *result) |
165 | 0 | { |
166 | 0 | liq_result_destroy(result); |
167 | 0 | } |
168 | | |
169 | | void |
170 | | vips__quantise_image_destroy(VipsQuantiseImage *img) |
171 | 0 | { |
172 | 0 | liq_image_destroy(img); |
173 | 0 | } |
174 | | |
175 | | void |
176 | | vips__quantise_attr_destroy(VipsQuantiseAttr *attr) |
177 | 0 | { |
178 | 0 | liq_attr_destroy(attr); |
179 | 0 | } |
180 | | |
181 | | #elif defined(HAVE_QUANTIZR) /*!HAVE_IMAGEQUANT*/ |
182 | | |
183 | | VipsQuantiseAttr * |
184 | | vips__quantise_attr_create() |
185 | | { |
186 | | return quantizr_new_options(); |
187 | | } |
188 | | |
189 | | VipsQuantiseError |
190 | | vips__quantise_set_max_colors(VipsQuantiseAttr *attr, int colors) |
191 | | { |
192 | | return quantizr_set_max_colors(attr, colors); |
193 | | } |
194 | | |
195 | | VipsQuantiseError |
196 | | vips__quantise_set_quality(VipsQuantiseAttr *attr, int minimum, int maximum) |
197 | | { |
198 | | /* Not supported by quantizr |
199 | | */ |
200 | | return 0; |
201 | | } |
202 | | |
203 | | VipsQuantiseError |
204 | | vips__quantise_set_speed(VipsQuantiseAttr *attr, int speed) |
205 | | { |
206 | | /* Not supported by quantizr |
207 | | */ |
208 | | return 0; |
209 | | } |
210 | | |
211 | | VipsQuantiseImage * |
212 | | vips__quantise_image_create_rgba(const VipsQuantiseAttr *attr, |
213 | | const void *bitmap, int width, int height, double gamma) |
214 | | { |
215 | | /* attr and gamma ununused by quantizr |
216 | | */ |
217 | | return quantizr_create_image_rgba( |
218 | | (unsigned char *) bitmap, width, height); |
219 | | } |
220 | | |
221 | | VipsQuantiseError |
222 | | vips__quantise_image_quantize(VipsQuantiseImage *const input_image, |
223 | | VipsQuantiseAttr *const options, VipsQuantiseResult **result_output) |
224 | | { |
225 | | *result_output = quantizr_quantize(input_image, options); |
226 | | return 0; |
227 | | } |
228 | | |
229 | | VipsQuantiseError |
230 | | vips__quantise_image_quantize_fixed(VipsQuantiseImage *const input_image, |
231 | | VipsQuantiseAttr *const options, VipsQuantiseResult **result_output) |
232 | | { |
233 | | /* Quantizr doesn't change the palette during remapping, so we don't |
234 | | * need a special implementation for this |
235 | | */ |
236 | | return vips__quantise_image_quantize(input_image, options, |
237 | | result_output); |
238 | | } |
239 | | |
240 | | VipsQuantiseError |
241 | | vips__quantise_set_dithering_level(VipsQuantiseResult *res, |
242 | | float dither_level) |
243 | | { |
244 | | return quantizr_set_dithering_level(res, dither_level); |
245 | | } |
246 | | |
247 | | const VipsQuantisePalette * |
248 | | vips__quantise_get_palette(VipsQuantiseResult *result) |
249 | | { |
250 | | return quantizr_get_palette(result); |
251 | | } |
252 | | |
253 | | VipsQuantiseError |
254 | | vips__quantise_write_remapped_image(VipsQuantiseResult *result, |
255 | | VipsQuantiseImage *input_image, void *buffer, size_t buffer_size) |
256 | | { |
257 | | return quantizr_remap(result, input_image, buffer, buffer_size); |
258 | | } |
259 | | |
260 | | void |
261 | | vips__quantise_result_destroy(VipsQuantiseResult *result) |
262 | | { |
263 | | quantizr_free_result(result); |
264 | | } |
265 | | |
266 | | void |
267 | | vips__quantise_image_destroy(VipsQuantiseImage *img) |
268 | | { |
269 | | quantizr_free_image(img); |
270 | | } |
271 | | |
272 | | void |
273 | | vips__quantise_attr_destroy(VipsQuantiseAttr *attr) |
274 | | { |
275 | | quantizr_free_options(attr); |
276 | | } |
277 | | |
278 | | #endif /*HAVE_IMAGEQUANT*/ |
279 | | |
280 | | /* Track during a quantisation. |
281 | | */ |
282 | | typedef struct _Quantise { |
283 | | VipsImage *in; |
284 | | VipsImage **index_out; |
285 | | VipsImage **palette_out; |
286 | | int colours; |
287 | | int Q; |
288 | | double dither; |
289 | | int effort; |
290 | | |
291 | | VipsQuantiseAttr *attr; |
292 | | VipsQuantiseImage *input_image; |
293 | | VipsQuantiseResult *quantisation_result; |
294 | | VipsImage *t[5]; |
295 | | } Quantise; |
296 | | |
297 | | static void |
298 | | vips__quantise_free(Quantise *quantise) |
299 | 0 | { |
300 | 0 | int i; |
301 | |
|
302 | 0 | VIPS_FREEF(vips__quantise_result_destroy, quantise->quantisation_result); |
303 | 0 | VIPS_FREEF(vips__quantise_image_destroy, quantise->input_image); |
304 | 0 | VIPS_FREEF(vips__quantise_attr_destroy, quantise->attr); |
305 | |
|
306 | 0 | for (i = 0; i < VIPS_NUMBER(quantise->t); i++) |
307 | 0 | VIPS_UNREF(quantise->t[i]); |
308 | |
|
309 | 0 | VIPS_FREE(quantise); |
310 | 0 | } |
311 | | |
312 | | static Quantise * |
313 | | vips__quantise_new(VipsImage *in, |
314 | | VipsImage **index_out, VipsImage **palette_out, |
315 | | int colours, int Q, double dither, int effort) |
316 | 0 | { |
317 | 0 | Quantise *quantise; |
318 | 0 | int i; |
319 | |
|
320 | 0 | quantise = VIPS_NEW(NULL, Quantise); |
321 | 0 | quantise->in = in; |
322 | 0 | quantise->index_out = index_out; |
323 | 0 | quantise->palette_out = palette_out; |
324 | 0 | quantise->colours = colours; |
325 | 0 | quantise->Q = Q; |
326 | 0 | quantise->dither = dither; |
327 | 0 | quantise->effort = effort; |
328 | 0 | for (i = 0; i < VIPS_NUMBER(quantise->t); i++) |
329 | 0 | quantise->t[i] = NULL; |
330 | |
|
331 | 0 | return quantise; |
332 | 0 | } |
333 | | |
334 | | int |
335 | | vips__quantise_image(VipsImage *in, |
336 | | VipsImage **index_out, VipsImage **palette_out, |
337 | | int colours, int Q, double dither, int effort, |
338 | | gboolean threshold_alpha) |
339 | 0 | { |
340 | 0 | Quantise *quantise; |
341 | 0 | VipsImage *index; |
342 | 0 | VipsImage *palette; |
343 | 0 | const VipsQuantisePalette *lp; |
344 | 0 | gint64 i; |
345 | 0 | VipsPel *restrict p; |
346 | 0 | gboolean added_alpha; |
347 | |
|
348 | 0 | quantise = vips__quantise_new(in, index_out, palette_out, |
349 | 0 | colours, Q, dither, effort); |
350 | | |
351 | | /* Ensure input is sRGB. |
352 | | */ |
353 | 0 | if (in->Type != VIPS_INTERPRETATION_sRGB) { |
354 | 0 | if (vips_colourspace(in, &quantise->t[0], |
355 | 0 | VIPS_INTERPRETATION_sRGB, NULL)) { |
356 | 0 | vips__quantise_free(quantise); |
357 | 0 | return -1; |
358 | 0 | } |
359 | 0 | in = quantise->t[0]; |
360 | 0 | } |
361 | | |
362 | | /* Add alpha channel if missing. |
363 | | */ |
364 | 0 | added_alpha = FALSE; |
365 | 0 | if (!vips_image_hasalpha(in)) { |
366 | 0 | if (vips_bandjoin_const1(in, &quantise->t[1], 255, NULL)) { |
367 | 0 | vips__quantise_free(quantise); |
368 | 0 | return -1; |
369 | 0 | } |
370 | 0 | added_alpha = TRUE; |
371 | 0 | in = quantise->t[1]; |
372 | 0 | } |
373 | | |
374 | 0 | if (!(quantise->t[2] = vips_image_copy_memory(in))) { |
375 | 0 | vips__quantise_free(quantise); |
376 | 0 | return -1; |
377 | 0 | } |
378 | 0 | in = quantise->t[2]; |
379 | | |
380 | | /* Threshold alpha channel. |
381 | | */ |
382 | 0 | if (threshold_alpha && |
383 | 0 | !added_alpha) { |
384 | 0 | const guint64 n_pels = VIPS_IMAGE_N_PELS(in); |
385 | |
|
386 | 0 | p = VIPS_IMAGE_ADDR(in, 0, 0); |
387 | 0 | for (i = 0; i < n_pels; i++) { |
388 | 0 | p[3] = p[3] > 128 ? 255 : 0; |
389 | 0 | p += 4; |
390 | 0 | } |
391 | 0 | } |
392 | |
|
393 | 0 | quantise->attr = vips__quantise_attr_create(); |
394 | 0 | vips__quantise_set_max_colors(quantise->attr, colours); |
395 | 0 | vips__quantise_set_quality(quantise->attr, 0, Q); |
396 | 0 | vips__quantise_set_speed(quantise->attr, 11 - effort); |
397 | |
|
398 | 0 | quantise->input_image = vips__quantise_image_create_rgba(quantise->attr, |
399 | 0 | VIPS_IMAGE_ADDR(in, 0, 0), in->Xsize, in->Ysize, 0); |
400 | |
|
401 | 0 | if (vips__quantise_image_quantize(quantise->input_image, quantise->attr, |
402 | 0 | &quantise->quantisation_result)) { |
403 | 0 | vips_error("quantise", "%s", _("quantisation failed")); |
404 | 0 | vips__quantise_free(quantise); |
405 | 0 | return -1; |
406 | 0 | } |
407 | | |
408 | 0 | vips__quantise_set_dithering_level(quantise->quantisation_result, dither); |
409 | |
|
410 | 0 | index = quantise->t[3] = vips_image_new_memory(); |
411 | 0 | vips_image_init_fields(index, |
412 | 0 | in->Xsize, in->Ysize, 1, VIPS_FORMAT_UCHAR, |
413 | 0 | VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0); |
414 | |
|
415 | 0 | if (vips_image_write_prepare(index)) { |
416 | 0 | vips__quantise_free(quantise); |
417 | 0 | return -1; |
418 | 0 | } |
419 | | |
420 | 0 | if (vips__quantise_write_remapped_image(quantise->quantisation_result, |
421 | 0 | quantise->input_image, |
422 | 0 | VIPS_IMAGE_ADDR(index, 0, 0), VIPS_IMAGE_N_PELS(index))) { |
423 | 0 | vips_error("quantise", "%s", _("quantisation failed")); |
424 | 0 | vips__quantise_free(quantise); |
425 | 0 | return -1; |
426 | 0 | } |
427 | | |
428 | 0 | lp = vips__quantise_get_palette(quantise->quantisation_result); |
429 | |
|
430 | 0 | palette = quantise->t[4] = vips_image_new_memory(); |
431 | 0 | vips_image_init_fields(palette, lp->count, 1, 4, |
432 | 0 | VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, |
433 | 0 | 1.0, 1.0); |
434 | |
|
435 | 0 | if (vips_image_write_prepare(palette)) { |
436 | 0 | vips__quantise_free(quantise); |
437 | 0 | return -1; |
438 | 0 | } |
439 | | |
440 | 0 | p = VIPS_IMAGE_ADDR(palette, 0, 0); |
441 | 0 | for (i = 0; i < lp->count; i++) { |
442 | 0 | p[0] = lp->entries[i].r; |
443 | 0 | p[1] = lp->entries[i].g; |
444 | 0 | p[2] = lp->entries[i].b; |
445 | 0 | p[3] = lp->entries[i].a; |
446 | |
|
447 | 0 | p += 4; |
448 | 0 | } |
449 | |
|
450 | 0 | *index_out = index; |
451 | 0 | g_object_ref(index); |
452 | 0 | *palette_out = palette; |
453 | 0 | g_object_ref(palette); |
454 | |
|
455 | 0 | vips__quantise_free(quantise); |
456 | |
|
457 | 0 | return 0; |
458 | 0 | } |
459 | | |
460 | | #else /*!HAVE_QUANTIZATION*/ |
461 | | |
462 | | int |
463 | | vips__quantise_image(VipsImage *in, |
464 | | VipsImage **index_out, VipsImage **palette_out, |
465 | | int colours, int Q, double dither, int effort, |
466 | | gboolean threshold_alpha) |
467 | | { |
468 | | vips_error("vips__quantise_image", |
469 | | "%s", _("libvips not built with quantisation support")); |
470 | | |
471 | | return -1; |
472 | | } |
473 | | |
474 | | #endif /*HAVE_QUANTIZATION*/ |