Skip to content

Commit 6cd1733

Browse files
authored
feat(avif): add chroma subsampling control for AVIF export (#19595)
* feat(avif): add chroma subsampling control for AVIF export Add a new chroma subsampling option to the AVIF export module, allowing users to manually select subsampling modes (4:4:4, 4:2:2, 4:2:0) or use auto mode based on quality thresholds. This provides finer control over compression and quality trade-offs in AVIF images. * feat(avif): add chroma subsampling configuration Add a new configuration option for AVIF chroma subsampling in the imageio plugin settings. This allows users to control subsampling levels (0-3) for AVIF exports, defaulting to 0. * feat(avif): conditionally disable chroma subsampling UI for lossless mode When AVIF compression is set to lossless, the subsample control is now hidden from the GUI to prevent irrelevant options, and marked with no-show-all for better layout management. This improves user experience by simplifying the interface during lossless exports.
1 parent c7b5d6f commit 6cd1733

File tree

2 files changed

+97
-14
lines changed

2 files changed

+97
-14
lines changed

data/darktableconfig.xml.in

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2913,6 +2913,13 @@
29132913
<shortdescription/>
29142914
<longdescription/>
29152915
</dtconfig>
2916+
<dtconfig>
2917+
<name>plugins/imageio/format/avif/subsample</name>
2918+
<type min="0" max="3">int</type>
2919+
<default>0</default>
2920+
<shortdescription>AVIF chroma subsampling</shortdescription>
2921+
<longdescription/>
2922+
</dtconfig>
29162923
<dtconfig>
29172924
<name>plugins/imageio/format/xcf/bpp</name>
29182925
<type>

src/imageio/format/avif.c

Lines changed: 90 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ enum avif_tiling_e
5151
AVIF_TILING_OFF
5252
};
5353

54+
enum avif_subsample_e
55+
{
56+
AVIF_SUBSAMPLE_AUTO = 0,
57+
AVIF_SUBSAMPLE_444,
58+
AVIF_SUBSAMPLE_422,
59+
AVIF_SUBSAMPLE_420
60+
};
61+
5462
enum avif_color_mode_e
5563
{
5664
AVIF_COLOR_MODE_RGB = 0,
@@ -65,6 +73,7 @@ typedef struct dt_imageio_avif_t
6573
uint32_t compression_type;
6674
uint32_t quality;
6775
uint32_t tiling;
76+
uint32_t subsample;
6877
} dt_imageio_avif_t;
6978

7079
typedef struct dt_imageio_avif_gui_t
@@ -74,6 +83,7 @@ typedef struct dt_imageio_avif_gui_t
7483
GtkWidget *compression_type;
7584
GtkWidget *quality;
7685
GtkWidget *tiling;
86+
GtkWidget *subsample;
7787
} dt_imageio_avif_gui_t;
7888

7989
static const struct
@@ -207,6 +217,28 @@ void init(dt_imageio_module_format_t *self)
207217
dt_imageio_avif_t,
208218
quality,
209219
int);
220+
221+
/* subsample */
222+
luaA_enum(darktable.lua_state.state,
223+
enum avif_subsample_e);
224+
luaA_enum_value(darktable.lua_state.state,
225+
enum avif_subsample_e,
226+
AVIF_SUBSAMPLE_AUTO);
227+
luaA_enum_value(darktable.lua_state.state,
228+
enum avif_subsample_e,
229+
AVIF_SUBSAMPLE_444);
230+
luaA_enum_value(darktable.lua_state.state,
231+
enum avif_subsample_e,
232+
AVIF_SUBSAMPLE_422);
233+
luaA_enum_value(darktable.lua_state.state,
234+
enum avif_subsample_e,
235+
AVIF_SUBSAMPLE_420);
236+
237+
dt_lua_register_module_member(darktable.lua_state.state,
238+
self,
239+
dt_imageio_avif_t,
240+
subsample,
241+
enum avif_subsample_e);
210242
#endif
211243
}
212244

@@ -250,17 +282,33 @@ int write_image(struct dt_imageio_module_data_t *data,
250282
format = AVIF_PIXEL_FORMAT_YUV444;
251283
break;
252284
case AVIF_COMP_LOSSY:
253-
if(d->quality > 90)
285+
// Determine pixel format based on subsample setting
286+
switch(d->subsample)
254287
{
288+
case AVIF_SUBSAMPLE_AUTO:
289+
// Auto mode: use quality thresholds
290+
if(d->quality > 90)
291+
{
292+
format = AVIF_PIXEL_FORMAT_YUV444;
293+
}
294+
else if(d->quality > 80)
295+
{
296+
format = AVIF_PIXEL_FORMAT_YUV422;
297+
}
298+
else
299+
{
300+
format = AVIF_PIXEL_FORMAT_YUV420;
301+
}
302+
break;
303+
case AVIF_SUBSAMPLE_444:
255304
format = AVIF_PIXEL_FORMAT_YUV444;
256-
}
257-
else if(d->quality > 80)
258-
{
305+
break;
306+
case AVIF_SUBSAMPLE_422:
259307
format = AVIF_PIXEL_FORMAT_YUV422;
260-
}
261-
else
262-
{
263-
format = AVIF_PIXEL_FORMAT_YUV420;
308+
break;
309+
case AVIF_SUBSAMPLE_420:
310+
format = AVIF_PIXEL_FORMAT_YUV420;
311+
break;
264312
}
265313
break;
266314
}
@@ -703,6 +751,7 @@ void *get_params(dt_imageio_module_format_t *self)
703751
}
704752

705753
d->tiling = !dt_conf_get_bool("plugins/imageio/format/avif/tiling");
754+
d->subsample = dt_conf_get_int("plugins/imageio/format/avif/subsample");
706755

707756
return d;
708757
}
@@ -721,6 +770,7 @@ int set_params(dt_imageio_module_format_t *self,
721770
dt_bauhaus_combobox_set(g->tiling, d->tiling);
722771
dt_bauhaus_combobox_set(g->compression_type, d->compression_type);
723772
dt_bauhaus_slider_set(g->quality, d->quality);
773+
dt_bauhaus_combobox_set(g->subsample, d->subsample);
724774

725775
return 0;
726776
}
@@ -810,6 +860,7 @@ static void compression_type_changed(GtkWidget *widget, gpointer user_data)
810860
dt_conf_set_int("plugins/imageio/format/avif/compression_type", compression_type);
811861

812862
gtk_widget_set_visible(gui->quality, compression_type != AVIF_COMP_LOSSLESS);
863+
gtk_widget_set_visible(gui->subsample, compression_type != AVIF_COMP_LOSSLESS);
813864
}
814865

815866
static void quality_changed(GtkWidget *slider, gpointer user_data)
@@ -818,6 +869,12 @@ static void quality_changed(GtkWidget *slider, gpointer user_data)
818869
dt_conf_set_int("plugins/imageio/format/avif/quality", quality);
819870
}
820871

872+
static void subsample_changed(GtkWidget *widget, gpointer user_data)
873+
{
874+
const enum avif_subsample_e subsample = dt_bauhaus_combobox_get(widget);
875+
dt_conf_set_int("plugins/imageio/format/avif/subsample", subsample);
876+
}
877+
821878
void gui_init(dt_imageio_module_format_t *self)
822879
{
823880
dt_imageio_avif_gui_t *gui = malloc(sizeof(dt_imageio_avif_gui_t));
@@ -904,17 +961,34 @@ void gui_init(dt_imageio_module_format_t *self)
904961
dt_bauhaus_widget_set_label(gui->quality, NULL, N_("quality"));
905962

906963
gtk_widget_set_tooltip_text(gui->quality,
907-
_("the quality of an image, less quality means fewer details.\n"
908-
"\n"
909-
"pixel format is controlled by quality:\n"
910-
"\n"
911-
"5-80: YUV420, 81-90: YUV422, 91-100: YUV444"));
964+
_("the quality of an image, less quality means fewer details"));
912965

913966
dt_bauhaus_slider_set(gui->quality, quality);
914967

915968
gtk_widget_set_visible(gui->quality, compression_type != AVIF_COMP_LOSSLESS);
916969
gtk_widget_set_no_show_all(gui->quality, TRUE);
917970

971+
/*
972+
* Chroma subsampling combo box
973+
*/
974+
const enum avif_subsample_e subsample = dt_conf_get_int("plugins/imageio/format/avif/subsample");
975+
976+
DT_BAUHAUS_COMBOBOX_NEW_FULL(gui->subsample, self, NULL, N_("chroma subsampling"),
977+
_("chroma subsampling setting for AVIF encoder.\n"
978+
"auto - use subsampling determined by the quality value\n"
979+
" (5-80: YUV420, 81-90: YUV422, 91-100: YUV444)\n"
980+
"4:4:4 - no chroma subsampling\n"
981+
"4:2:2 - color sampling rate halved horizontally\n"
982+
"4:2:0 - color sampling rate halved horizontally and vertically"),
983+
subsample, subsample_changed, self,
984+
N_("auto"), N_("4:4:4"), N_("4:2:2"), N_("4:2:0"));
985+
986+
dt_bauhaus_combobox_set_default(gui->subsample,
987+
dt_confgen_get_int("plugins/imageio/format/avif/subsample", DT_DEFAULT));
988+
989+
gtk_widget_set_visible(gui->subsample, compression_type != AVIF_COMP_LOSSLESS);
990+
gtk_widget_set_no_show_all(gui->subsample, TRUE);
991+
918992
g_signal_connect(G_OBJECT(gui->bit_depth),
919993
"value-changed",
920994
G_CALLBACK(bit_depth_changed),
@@ -929,7 +1003,7 @@ void gui_init(dt_imageio_module_format_t *self)
9291003
NULL);
9301004

9311005
self->widget = dt_gui_vbox(gui->bit_depth, gui->color_mode, gui->tiling,
932-
gui->compression_type, gui->quality);
1006+
gui->compression_type, gui->quality, gui->subsample);
9331007
}
9341008

9351009
void gui_cleanup(dt_imageio_module_format_t *self)
@@ -946,6 +1020,7 @@ void gui_reset(dt_imageio_module_format_t *self)
9461020
const enum avif_tiling_e tiling = !dt_confgen_get_bool("plugins/imageio/format/avif/tiling", DT_DEFAULT);
9471021
const enum avif_compression_type_e compression_type = dt_confgen_get_int("plugins/imageio/format/avif/compression_type", DT_DEFAULT);
9481022
const uint32_t quality = dt_confgen_get_int("plugins/imageio/format/avif/quality", DT_DEFAULT);
1023+
const enum avif_subsample_e subsample = dt_confgen_get_int("plugins/imageio/format/avif/subsample", DT_DEFAULT);
9491024

9501025
size_t idx = 0;
9511026
for(size_t i = 0; avif_bit_depth[i].name != NULL; ++i)
@@ -961,6 +1036,7 @@ void gui_reset(dt_imageio_module_format_t *self)
9611036
dt_bauhaus_combobox_set(gui->tiling, tiling);
9621037
dt_bauhaus_combobox_set(gui->compression_type, compression_type);
9631038
dt_bauhaus_slider_set(gui->quality, quality);
1039+
dt_bauhaus_combobox_set(gui->subsample, subsample);
9641040
}
9651041

9661042
// clang-format off

0 commit comments

Comments
 (0)