@@ -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+
5462enum 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
7079typedef 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
7989static 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
815866static 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+
821878void 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
9351009void 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