Coverage Report

Created: 2025-01-28 06:33

/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*/