Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
webp.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2013 Dimitrios Psychogios.
4 Copyright (C) 2013-2014 Jérémy Rosen.
5 Copyright (C) 2013 Pascal de Bruijn.
6 Copyright (C) 2013-2017, 2019-2020 Tobias Ellinghaus.
7 Copyright (C) 2014, 2018, 2020-2021 Pascal Obry.
8 Copyright (C) 2014, 2016 Roman Lebedev.
9 Copyright (C) 2018 Edouard Gomez.
10 Copyright (C) 2019, 2022, 2025 Aurélien PIERRE.
11 Copyright (C) 2019 Sam Smith.
12 Copyright (C) 2020, 2022 Diederik Ter Rahe.
13 Copyright (C) 2020 Heiko Bauke.
14 Copyright (C) 2021 Hubert Kowalski.
15 Copyright (C) 2022 Martin Bařinka.
16 Copyright (C) 2022 Miloš Komarčević.
17 Copyright (C) 2023 Alynx Zhou.
18
19 darktable is free software: you can redistribute it and/or modify
20 it under the terms of the GNU General Public License as published by
21 the Free Software Foundation, either version 3 of the License, or
22 (at your option) any later version.
23
24 darktable is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
28
29 You should have received a copy of the GNU General Public License
30 along with darktable. If not, see <http://www.gnu.org/licenses/>.
31*/
32
33#ifdef HAVE_CONFIG_H
34#include "config.h"
35#endif
36#include "widgets/bauhaus.h"
37#include "system/macros.h"
38#include "system/mem_alloc.h"
40#include <glib/gstdio.h>
41#include "metadata/exif.h"
44#include "common/conf.h"
46
47#include <stdio.h>
48#include <stdlib.h>
49
50#include <webp/encode.h>
51#include <webp/mux.h>
53
54DT_MODULE(2)
55
56typedef enum
57{
61
62
70
71
79
86
87#define _stringify(a) #a
88#define stringify(a) _stringify(a)
89
90static const char *const EncoderError[] = {
91 "ok",
92 "out_of_memory: out of memory allocating objects",
93 "bitstream_out_of_memory: out of memory re-allocating byte buffer",
94 "null_parameter: null parameter passed to function",
95 "invalid_configuration: configuration is invalid",
96 "bad_dimension: bad picture dimension. maximum width and height "
97 "allowed is " stringify(WEBP_MAX_DIMENSION) " pixels.",
98 "partition0_overflow: partition #0 is too big to fit 512k.\n"
99 "to reduce the size of this partition, try using less segments "
100 "with the -segments option, and eventually reduce the number of "
101 "header bits using -partition_limit. more details are available "
102 "in the manual (`man cwebp`)",
103 "partition_overflow: partition is too big to fit 16M",
104 "bad_write: picture writer returned an i/o error",
105 "file_too_big: file would be too big to fit in 4G",
106 "user_abort: encoding abort requested by user"
107};
108
109const char *get_error_str(int err)
110{
111 if (err < 0 || err >= sizeof(EncoderError)/sizeof(EncoderError[0]))
112 {
113 return "Unknown error. Consider open an issue to ansel to update the webp error list.";
114 }
115 return EncoderError[err];
116}
117
119{
120}
121
125
126int write_image(dt_imageio_module_data_t *webp, const char *filename, const void *in_tmp,
127 dt_colorspaces_color_profile_type_t over_type, const char *over_filename,
128 void *exif, int exif_len, int32_t imgid, int num, int total, struct dt_dev_pixelpipe_t *pipe,
129 const gboolean export_masks)
130{
131 int res = 1;
132 FILE *out = NULL;
133 uint8_t *buf = NULL;
134 WebPPicture pic;
135 WebPMemoryWriter writer;
136 WebPMemoryWriterInit(&writer);
137 WebPData icc_profile;
138 WebPDataInit(&icc_profile);
139 WebPData bitstream;
140 WebPDataInit(&bitstream);
141 WebPData assembled_data;
142 WebPDataInit(&assembled_data);
143 WebPMux *mux = WebPMuxNew();
144 WebPMuxError err;
145
146 dt_imageio_webp_t *webp_data = (dt_imageio_webp_t *)webp;
147
148 // Create, configure and validate a WebPConfig instance
149 WebPConfig config;
150 if(!WebPConfigPreset(&config, webp_data->hint, (float)webp_data->quality)) goto out;
151
152 // TODO(jinxos): expose more config options in the UI
153 config.lossless = webp_data->comp_type;
154 config.image_hint = webp_data->hint;
155 config.method = 6;
156
157 // these are to allow for large image export.
158 // TODO(jinxos): these values should be adjusted as needed and ideally determined at runtime.
159 config.segments = 4;
160 config.partition_limit = 70;
161 if(!WebPValidateConfig(&config))
162 {
163 fprintf(stderr, "[webp export] error validating encoder configuration\n");
164 goto out;
165 }
166
167 // embed ICC profile
168 cmsHPROFILE out_profile = dt_colorspaces_get_output_profile(imgid, &over_type, over_filename)->profile;
169 uint32_t len = 0;
170 cmsSaveProfileToMem(out_profile, NULL, &len);
171 if(len > 0)
172 {
173 buf = (uint8_t *)g_malloc(len);
174 if(buf)
175 {
176 cmsSaveProfileToMem(out_profile, buf, &len);
177 icc_profile.bytes = buf;
178 icc_profile.size = len;
179 err = WebPMuxSetChunk(mux, "ICCP", &icc_profile, 0);
180 if(err != WEBP_MUX_OK)
181 {
182 fprintf(stderr, "[webp export] error adding ICC profile to WebP stream\n");
183 goto out;
184 }
185 }
186 else
187 {
188 fprintf(stderr, "[webp export] error allocating ICC profile buffer\n");
189 goto out;
190 }
191 }
192
193 // encode image data to memory and add to mux
194 if(!WebPPictureInit(&pic)) goto out;
195 pic.width = webp_data->global.width;
196 pic.height = webp_data->global.height;
197 pic.use_argb = !!(config.lossless);
198 pic.writer = WebPMemoryWrite;
199 pic.custom_ptr = &writer;
200
201 WebPPictureImportRGBX(&pic, (const uint8_t *)in_tmp, webp_data->global.width * 4);
202 if(!config.lossless)
203 {
204 // webp is more efficient at coding YUV images, as we go lossy
205 // let the encoder where best to spend its bits instead of forcing it
206 // to spend bits equally on RGB data that doesn't weight the same when
207 // considering the human visual system.
208 // use the slower, but better and sharper YUV conversion.
209 WebPPictureSharpARGBToYUVA(&pic);
210 }
211
212 if(!WebPEncode(&config, &pic))
213 {
214 fprintf(stderr, "[webp export] error during encoding (error: %d - %s)\n",
215 pic.error_code, get_error_str(pic.error_code));
216 goto out;
217 }
218
219 bitstream.bytes = writer.mem;
220 bitstream.size = writer.size;
221 err = WebPMuxSetImage(mux, &bitstream, 0);
222 if(err != WEBP_MUX_OK)
223 {
224 fprintf(stderr, "[webp export] error adding image to WebP stream\n");
225 goto out;
226 }
227
228 // finally write out assembled data to file
229 err = WebPMuxAssemble(mux, &assembled_data);
230 if(err != WEBP_MUX_OK)
231 {
232 fprintf(stderr, "[webp export] error assembling the WebP file\n");
233 goto out;
234 }
235
236 out = g_fopen(filename, "w+b");
237 if(IS_NULL_PTR(out))
238 {
239 fprintf(stderr, "[webp export] error creating file %s\n", filename);
240 goto out;
241 }
242 if(fwrite(assembled_data.bytes, assembled_data.size, 1, out) != 1)
243 {
244 fprintf(stderr, "[webp export] error writing %" G_GSIZE_FORMAT " bytes to file %s\n", assembled_data.size, filename);
245 goto out;
246 }
247
248 res = 0;
249
250out:
251 WebPPictureFree(&pic);
252 WebPMemoryWriterClear(&writer); // no need to WebPDataClear(&bitstream) as well
253 dt_free(buf); // instead of WebPDataClear(&icc_profile)
254 WebPDataClear(&assembled_data);
255 WebPMuxDelete(mux);
256 fclose(out);
257 if(!res && exif) dt_exif_write_blob(exif, exif_len, filename, 1);
258 return res;
259}
260
262{
263 return sizeof(dt_imageio_webp_t);
264}
265
266void *legacy_params(dt_imageio_module_format_t *self, const void *const old_params,
267 const size_t old_params_size, const int old_version, const int new_version,
268 size_t *new_size)
269{
270 if(old_version == 1 && new_version == 2)
271 {
272 typedef struct dt_imageio_webp_v1_t
273 {
274 int max_width, max_height;
275 int width, height;
276 char style[128];
277 int comp_type;
278 int quality;
279 int hint;
280 } dt_imageio_webp_v1_t;
281
282 dt_imageio_webp_v1_t *o = (dt_imageio_webp_v1_t *)old_params;
284
285 n->global.max_width = o->max_width;
286 n->global.max_height = o->max_height;
287 n->global.width = o->width;
288 n->global.height = o->height;
289 g_strlcpy(n->global.style, o->style, sizeof(o->style));
290 n->comp_type = o->comp_type;
291 n->quality = o->quality;
292 n->hint = o->hint;
293 *new_size = self->params_size(self);
294 return n;
295 }
296 return NULL;
297}
298
300{
302 d->comp_type = dt_conf_get_int("plugins/imageio/format/webp/comp_type");
303 if(d->comp_type == webp_lossy)
304 d->quality = dt_conf_get_int("plugins/imageio/format/webp/quality");
305 else
306 d->quality = 100;
307 d->hint = dt_conf_get_int("plugins/imageio/format/webp/hint");
308 return d;
309}
310
311int set_params(dt_imageio_module_format_t *self, const void *params, const int size)
312{
313 if(size != self->params_size(self)) return 1;
314 const dt_imageio_webp_t *d = (dt_imageio_webp_t *)params;
316 dt_bauhaus_combobox_set(g->compression, d->comp_type);
317 dt_bauhaus_slider_set(g->quality, d->quality);
318 dt_bauhaus_combobox_set(g->hint, d->hint);
319 return 0;
320}
321
323{
324 dt_free(params);
325}
326
328{
329 return 8;
330}
331
336
338{
339 // TODO: revisit this when IANA makes it official.
340 return "image/webp";
341}
342
344{
345 return "webp";
346}
347
348const char *name()
349{
350 return _("WebP (8-bit)");
351}
352
353static void compression_changed(GtkWidget *widget, gpointer user_data)
354{
355 const int comp_type = dt_bauhaus_combobox_get(widget);
356 dt_conf_set_int("plugins/imageio/format/webp/comp_type", comp_type);
357
358 if (comp_type == webp_lossless)
359 gtk_widget_set_sensitive(GTK_WIDGET(user_data), FALSE);
360 else
361 gtk_widget_set_sensitive(GTK_WIDGET(user_data), TRUE);
362}
363
364static void quality_changed(GtkWidget *slider, gpointer user_data)
365{
366 const int quality = (int)dt_bauhaus_slider_get(slider);
367 dt_conf_set_int("plugins/imageio/format/webp/quality", quality);
368}
369
370static void hint_combobox_changed(GtkWidget *widget, gpointer user_data)
371{
372 const int hint = dt_bauhaus_combobox_get(widget);
373 dt_conf_set_int("plugins/imageio/format/webp/hint", hint);
374}
375
377{
379 self->gui_data = (void *)gui;
380 const int comp_type = dt_conf_get_int("plugins/imageio/format/webp/comp_type");
381 const int quality = dt_conf_get_int("plugins/imageio/format/webp/quality");
382 const int hint = dt_conf_get_int("plugins/imageio/format/webp/hint");
383
384 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
385
387 dt_bauhaus_widget_set_label(gui->compression, N_("compression type"));
388 dt_bauhaus_combobox_add(gui->compression, _("lossy"));
389 dt_bauhaus_combobox_add(gui->compression, _("lossless"));
390 dt_bauhaus_combobox_set(gui->compression, comp_type);
391 gtk_box_pack_start(GTK_BOX(self->widget), gui->compression, TRUE, TRUE, 0);
392
394 dt_confgen_get_int("plugins/imageio/format/webp/quality", DT_MIN),
395 dt_confgen_get_int("plugins/imageio/format/webp/quality", DT_MAX),
396 1,
397 dt_confgen_get_int("plugins/imageio/format/webp/quality", DT_DEFAULT),
398 0);
399 dt_bauhaus_widget_set_label(gui->quality, N_("quality"));
400 dt_bauhaus_slider_set_default(gui->quality, dt_confgen_get_int("plugins/imageio/format/webp/quality", DT_DEFAULT));
402 gtk_widget_set_tooltip_text(gui->quality, _("applies only to lossy setting"));
403 if(quality > 0 && quality <= 100) dt_bauhaus_slider_set(gui->quality, quality);
404 gtk_box_pack_start(GTK_BOX(self->widget), gui->quality, TRUE, TRUE, 0);
405 g_signal_connect(G_OBJECT(gui->quality), "value-changed", G_CALLBACK(quality_changed), (gpointer)0);
406
407 g_signal_connect(G_OBJECT(gui->compression), "value-changed", G_CALLBACK(compression_changed), (gpointer)gui->quality);
408
409 if (comp_type == webp_lossless)
410 gtk_widget_set_sensitive(gui->quality, FALSE);
411
413 dt_bauhaus_widget_set_label(gui->hint, N_("image hint"));
414 gtk_widget_set_tooltip_text(gui->hint,
415 _("image characteristics hint for the underlying encoder.\n"
416 "picture : digital picture, like portrait, inner shot\n"
417 "photo : outdoor photograph, with natural lighting\n"
418 "graphic : discrete tone image (graph, map-tile etc)"));
419 dt_bauhaus_combobox_add(gui->hint, _("default"));
420 dt_bauhaus_combobox_add(gui->hint, _("picture"));
421 dt_bauhaus_combobox_add(gui->hint, _("photo"));
422 dt_bauhaus_combobox_add(gui->hint, _("graphic"));
423 dt_bauhaus_combobox_set(gui->hint, hint);
424 gtk_box_pack_start(GTK_BOX(self->widget), gui->hint, TRUE, TRUE, 0);
425 g_signal_connect(G_OBJECT(gui->hint), "value-changed", G_CALLBACK(hint_combobox_changed), NULL);
426}
427
429{
430 dt_free(self->gui_data);
431}
432
434{
436 const int comp_type = dt_confgen_get_int("plugins/imageio/format/webp/comp_type", DT_DEFAULT);
437 const int quality = dt_confgen_get_int("plugins/imageio/format/webp/quality", DT_DEFAULT);
438 const int hint = dt_confgen_get_int("plugins/imageio/format/webp/hint", DT_DEFAULT);
439 dt_bauhaus_combobox_set(gui->compression, comp_type);
440 dt_bauhaus_slider_set(gui->quality, quality);
441 dt_bauhaus_combobox_set(gui->hint, hint);
442}
443
445{
446 // TODO(jinxos): support embedded ICC
448}
449
450// clang-format off
451// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
452// vim: shiftwidth=2 expandtab tabstop=2 cindent
453// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
454// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
Definition bauhaus.c:1491
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3280
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2136
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
Definition bauhaus.c:1632
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1698
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3407
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1824
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
int dt_confgen_get_int(const char *name, dt_confgen_value_kind_t kind)
@ DT_DEFAULT
Definition conf.h:135
@ DT_MAX
Definition conf.h:137
@ DT_MIN
Definition conf.h:136
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
const int res
Definition dtpthread.h:351
int dt_exif_write_blob(uint8_t *blob, uint32_t size, const char *path, const int compressed)
Definition exif.cc:2057
What a photograph says about itself: the EXIF, IPTC and XMP tags a camera and a cataloguer write,...
#define DT_GUI_MODULE(x)
int bpp
@ IMAGEIO_RGB
@ IMAGEIO_INT8
@ FORMAT_FLAGS_SUPPORT_XMP
const dt_colorspaces_color_profile_t * dt_colorspaces_get_output_profile(const int32_t imgid, dt_colorspaces_color_profile_type_t *over_type, const char *over_filename)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
size_t size
Definition mipmap_cache.c:3
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
dt_colorspaces_color_profile_type_t
cmsHPROFILE profile
the actual profile; NULL for the three category entries
GModule *GtkWidget * widget
GtkWidget * quality
Definition webp.c:83
GtkWidget * compression
Definition webp.c:82
dt_imageio_module_data_t global
Definition webp.c:74
const char * mime(dt_imageio_module_data_t *data)
Definition webp.c:337
size_t params_size(dt_imageio_module_format_t *self)
Definition webp.c:261
hint_t
Definition webp.c:64
@ hint_graphic
Definition webp.c:68
@ hint_photo
Definition webp.c:67
@ hint_default
Definition webp.c:65
@ hint_picture
Definition webp.c:66
void gui_reset(dt_imageio_module_format_t *self)
Definition webp.c:433
void gui_init(dt_imageio_module_format_t *self)
Definition webp.c:376
const char * extension(dt_imageio_module_data_t *data)
Definition webp.c:343
static const char *const EncoderError[]
Definition webp.c:90
int set_params(dt_imageio_module_format_t *self, const void *params, const int size)
Definition webp.c:311
#define stringify(a)
Definition webp.c:88
const char * name()
Definition webp.c:348
void cleanup(dt_imageio_module_format_t *self)
Definition webp.c:122
int levels(dt_imageio_module_data_t *p)
Definition webp.c:332
void * legacy_params(dt_imageio_module_format_t *self, const void *const old_params, const size_t old_params_size, const int old_version, const int new_version, size_t *new_size)
Definition webp.c:266
void free_params(dt_imageio_module_format_t *self, dt_imageio_module_data_t *params)
Definition webp.c:322
comp_type_t
Definition webp.c:57
@ webp_lossless
Definition webp.c:59
@ webp_lossy
Definition webp.c:58
const char * get_error_str(int err)
Definition webp.c:109
static void compression_changed(GtkWidget *widget, gpointer user_data)
Definition webp.c:353
void init(dt_imageio_module_format_t *self)
Definition webp.c:118
void * get_params(dt_imageio_module_format_t *self)
Definition webp.c:299
void gui_cleanup(dt_imageio_module_format_t *self)
Definition webp.c:428
static void hint_combobox_changed(GtkWidget *widget, gpointer user_data)
Definition webp.c:370
int write_image(dt_imageio_module_data_t *webp, const char *filename, const void *in_tmp, dt_colorspaces_color_profile_type_t over_type, const char *over_filename, void *exif, int exif_len, int32_t imgid, int num, int total, struct dt_dev_pixelpipe_t *pipe, const gboolean export_masks)
Definition webp.c:126
static void quality_changed(GtkWidget *slider, gpointer user_data)
Definition webp.c:364
#define DT_GUI_BOX_SPACING