"Fossies" - the Fresh Open Source Software Archive

Member "darktable-3.6.1/src/imageio/format/avif.c" (10 Sep 2021, 27987 Bytes) of package /linux/misc/darktable-3.6.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "avif.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3.4.1.1_vs_3.6.0.

    1 /*
    2  * This file is part of darktable,
    3  * Copyright (C) 2019-2021 darktable developers.
    4  *
    5  *  Copyright (c) 2019      Andreas Schneider
    6  *
    7  *  darktable is free software: you can redistribute it and/or modify
    8  *  it under the terms of the GNU General Public License as published by
    9  *  the Free Software Foundation, either version 3 of the License, or
   10  *  (at your option) any later version.
   11  *
   12  *  darktable is distributed in the hope that it will be useful,
   13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15  *  GNU General Public License for more details.
   16  *
   17  *  You should have received a copy of the GNU General Public License
   18  *  along with darktable.  If not, see <http://www.gnu.org/licenses/>.
   19  */
   20 
   21 #ifdef HAVE_CONFIG_H
   22 #include "config.h"
   23 #endif
   24 
   25 #include <inttypes.h>
   26 #include <stdio.h>
   27 #include <stdlib.h>
   28 
   29 #include "bauhaus/bauhaus.h"
   30 #include "common/colorspaces.h"
   31 #include "common/darktable.h"
   32 #include "common/imageio.h"
   33 #include "common/imageio_module.h"
   34 #include "control/conf.h"
   35 #include "imageio/format/imageio_format_api.h"
   36 
   37 #include <avif/avif.h>
   38 
   39 #define AVIF_MIN_TILE_SIZE 512
   40 #define AVIF_MAX_TILE_SIZE 3072
   41 #define AVIF_DEFAULT_TILE_SIZE AVIF_MIN_TILE_SIZE * 2
   42 
   43 DT_MODULE(1)
   44 
   45 enum avif_compression_type_e
   46 {
   47   AVIF_COMP_LOSSLESS = 0,
   48   AVIF_COMP_LOSSY = 1,
   49 };
   50 
   51 enum avif_tiling_e
   52 {
   53   AVIF_TILING_ON = 0,
   54   AVIF_TILING_OFF
   55 };
   56 
   57 enum avif_color_mode_e
   58 {
   59   AVIF_COLOR_MODE_RGB = 0,
   60   AVIF_COLOR_MODE_GRAYSCALE,
   61 };
   62 
   63 typedef struct dt_imageio_avif_t
   64 {
   65   dt_imageio_module_data_t global;
   66   uint32_t bit_depth;
   67   uint32_t color_mode;
   68   uint32_t compression_type;
   69   uint32_t quality;
   70   uint32_t tiling;
   71 } dt_imageio_avif_t;
   72 
   73 typedef struct dt_imageio_avif_gui_t
   74 {
   75   GtkWidget *bit_depth;
   76   GtkWidget *color_mode;
   77   GtkWidget *compression_type;
   78   GtkWidget *quality;
   79   GtkWidget *tiling;
   80 } dt_imageio_avif_gui_t;
   81 
   82 static const struct
   83 {
   84   char     *name;
   85   uint32_t bit_depth;
   86 } avif_bit_depth[] = {
   87   {
   88     .name = N_("8 bit"),
   89     .bit_depth  = 8
   90   },
   91   {
   92     .name = N_("10 bit"),
   93     .bit_depth  = 10
   94   },
   95   {
   96     .name = N_("12 bit"),
   97     .bit_depth  = 12
   98   },
   99   {
  100     .name = NULL,
  101   }
  102 };
  103 
  104 static const char *avif_get_compression_string(enum avif_compression_type_e comp)
  105 {
  106   switch(comp)
  107   {
  108     case AVIF_COMP_LOSSLESS:
  109       return "lossless";
  110     case AVIF_COMP_LOSSY:
  111       return "lossy";
  112   }
  113 
  114   return "unknown";
  115 }
  116 
  117 /* Lookup table for tiling choices */
  118 static int floor_log2(int i)
  119 {
  120   static const int floor_log2_table[] =
  121     /* 0   1,  2,  3,  4,  5,  6,  7,  8,  9 */
  122     {  0,  0,  2,  2,  4,  4,  4,  4,  8,  8,
  123        8,  8,  8,  8,  8,  8, 16, 16, 16, 16,
  124       16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
  125       16, 16, 32, 32, 32, 32, 32, 32, 32, 32,
  126       32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
  127       32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
  128       32, 32, 32, 32 };
  129     /* 0   1,  2,  3,  4,  5,  6,  7,  8,  9 */
  130 
  131   if(i >= 64)
  132   {
  133     return 64;
  134   }
  135 
  136   return floor_log2_table[i];
  137 }
  138 
  139 void init(dt_imageio_module_format_t *self)
  140 {
  141   const char *codecName = avifCodecName(AVIF_CODEC_CHOICE_AUTO,
  142                                         AVIF_CODEC_FLAG_CAN_ENCODE);
  143   if(codecName == NULL)
  144   {
  145     dt_print(DT_DEBUG_IMAGEIO,
  146              "libavif doesn't offer encoding support!\n");
  147     self->ready = FALSE;
  148     return;
  149   }
  150 
  151 #ifdef USE_LUA
  152   /* bit depth */
  153   dt_lua_register_module_member(darktable.lua_state.state,
  154                                 self,
  155                                 dt_imageio_avif_t,
  156                                 bit_depth,
  157                                 int);
  158   luaA_enum(darktable.lua_state.state,
  159             enum avif_color_mode_e);
  160   luaA_enum_value(darktable.lua_state.state,
  161                   enum avif_color_mode_e,
  162                   AVIF_COLOR_MODE_GRAYSCALE);
  163   luaA_enum_value(darktable.lua_state.state,
  164                   enum avif_color_mode_e,
  165                   AVIF_COLOR_MODE_GRAYSCALE);
  166 
  167   luaA_enum(darktable.lua_state.state,
  168             enum avif_tiling_e);
  169   luaA_enum_value(darktable.lua_state.state,
  170                   enum avif_tiling_e,
  171                   AVIF_TILING_ON);
  172   luaA_enum_value(darktable.lua_state.state,
  173                   enum avif_tiling_e,
  174                   AVIF_TILING_OFF);
  175 
  176   /* compression type */
  177   luaA_enum(darktable.lua_state.state,
  178             enum avif_compression_type_e);
  179   luaA_enum_value(darktable.lua_state.state,
  180                   enum avif_compression_type_e,
  181                   AVIF_COMP_LOSSLESS);
  182   luaA_enum_value(darktable.lua_state.state,
  183                   enum avif_compression_type_e,
  184                   AVIF_COMP_LOSSY);
  185 
  186   dt_lua_register_module_member(darktable.lua_state.state,
  187                                 self,
  188                                 dt_imageio_avif_t,
  189                                 compression_type,
  190                                 enum avif_compression_type_e);
  191 
  192   /* quality */
  193   dt_lua_register_module_member(darktable.lua_state.state,
  194                                 self,
  195                                 dt_imageio_avif_t,
  196                                 quality,
  197                                 int);
  198 #endif
  199 }
  200 
  201 void cleanup(dt_imageio_module_format_t *self)
  202 {
  203 }
  204 
  205 int write_image(struct dt_imageio_module_data_t *data,
  206                 const char *filename,
  207                 const void *in,
  208                 dt_colorspaces_color_profile_type_t over_type,
  209                 const char *over_filename,
  210                 void *exif,
  211                 int exif_len,
  212                 int imgid,
  213                 int num,
  214                 int total,
  215                 struct dt_dev_pixelpipe_t *pipe,
  216                 const gboolean export_masks)
  217 {
  218   dt_imageio_avif_t *d = (dt_imageio_avif_t *)data;
  219 
  220   avifPixelFormat format = AVIF_PIXEL_FORMAT_NONE;
  221   avifImage *image = NULL;
  222   avifRGBImage rgb = { .format = AVIF_RGB_FORMAT_RGB, };
  223   avifEncoder *encoder = NULL;
  224   uint8_t *icc_profile_data = NULL;
  225   uint32_t icc_profile_len;
  226   avifResult result;
  227   int rc;
  228 
  229   const size_t width = d->global.width;
  230   const size_t height = d->global.height;
  231   const size_t bit_depth = d->bit_depth > 0 ? d->bit_depth : 0;
  232   enum avif_color_mode_e color_mode = d->color_mode;
  233 
  234   switch(color_mode)
  235   {
  236     case AVIF_COLOR_MODE_RGB:
  237       switch(d->compression_type)
  238       {
  239         case AVIF_COMP_LOSSLESS:
  240           format = AVIF_PIXEL_FORMAT_YUV444;
  241           break;
  242         case AVIF_COMP_LOSSY:
  243           if(d->quality > 90)
  244           {
  245               format = AVIF_PIXEL_FORMAT_YUV444;
  246           }
  247           else if(d->quality > 80)
  248           {
  249               format = AVIF_PIXEL_FORMAT_YUV422;
  250           }
  251           else
  252           {
  253             format = AVIF_PIXEL_FORMAT_YUV420;
  254           }
  255           break;
  256       }
  257 
  258       break;
  259     case AVIF_COLOR_MODE_GRAYSCALE:
  260       format = AVIF_PIXEL_FORMAT_YUV400;
  261       break;
  262   }
  263 
  264   image = avifImageCreate(width, height, bit_depth, format);
  265   if(image == NULL)
  266   {
  267     dt_print(DT_DEBUG_IMAGEIO,
  268              "Failed to create AVIF image for writing [%s]\n",
  269              filename);
  270     rc = 1;
  271     goto out;
  272   }
  273 
  274   dt_print(DT_DEBUG_IMAGEIO,
  275            "Exporting AVIF image [%s] "
  276            "[width: %zu, height: %zu, bit depth: %zu, comp: %s, quality: %u]\n",
  277            filename,
  278            width,
  279            height,
  280            bit_depth,
  281            avif_get_compression_string(d->compression_type),
  282            d->quality);
  283 
  284   if(imgid > 0)
  285   {
  286     gboolean use_icc = FALSE;
  287 
  288     /*
  289      * Set these in advance so any upcoming RGB -> YUV use the proper
  290      * coefficients.
  291      */
  292     switch(over_type)
  293     {
  294       case DT_COLORSPACE_SRGB:
  295           image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
  296           image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
  297           image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT709;
  298           break;
  299       case DT_COLORSPACE_REC709:
  300           image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
  301           image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_BT709;
  302           image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT709;
  303         break;
  304       case DT_COLORSPACE_LIN_REC709:
  305           image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
  306           image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_LINEAR;
  307           image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT709;
  308         break;
  309       case DT_COLORSPACE_LIN_REC2020:
  310           image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT2020;
  311           image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_LINEAR;
  312           image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT2020_NCL;
  313         break;
  314       case DT_COLORSPACE_PQ_REC2020:
  315           image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT2020;
  316           image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SMPTE2084;
  317           image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT2020_NCL;
  318         break;
  319       case DT_COLORSPACE_HLG_REC2020:
  320           image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT2020;
  321           image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_HLG;
  322           image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT2020_NCL;
  323         break;
  324       case DT_COLORSPACE_PQ_P3:
  325           image->colorPrimaries = AVIF_COLOR_PRIMARIES_SMPTE432;
  326           image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SMPTE2084;
  327           image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL;
  328         break;
  329       case DT_COLORSPACE_HLG_P3:
  330           image->colorPrimaries = AVIF_COLOR_PRIMARIES_SMPTE432;
  331           image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_HLG;
  332           image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL;
  333         break;
  334       default:
  335         break;
  336     }
  337 
  338     // no change from default, unspecified CICP (2/2/2)
  339     if(image->colorPrimaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED)
  340     {
  341       use_icc = TRUE;
  342     }
  343 
  344     dt_print(DT_DEBUG_IMAGEIO, "[avif colorprofile profile: %s - %s]\n",
  345              dt_colorspaces_get_name(over_type, filename),
  346              use_icc ? "icc" : "nclx");
  347 
  348     if(use_icc)
  349     {
  350       const dt_colorspaces_color_profile_t *cp =
  351         dt_colorspaces_get_output_profile(imgid,
  352                                           over_type,
  353                                           over_filename);
  354       cmsHPROFILE out_profile = cp->profile;
  355 
  356       cmsSaveProfileToMem(out_profile, 0, &icc_profile_len);
  357       if(icc_profile_len > 0)
  358       {
  359         icc_profile_data = malloc(sizeof(uint8_t) * icc_profile_len);
  360         if(icc_profile_data == NULL)
  361         {
  362           rc = 1;
  363           goto out;
  364         }
  365         cmsSaveProfileToMem(out_profile, icc_profile_data, &icc_profile_len);
  366         avifImageSetProfileICC(image,
  367                                icc_profile_data,
  368                                icc_profile_len);
  369       }
  370     }
  371   }
  372 
  373   /*
  374    * Set the YUV range before conversion.
  375    *
  376    * Limited range (aka "studio range", "studio swing", etc) is simply when you
  377    * cut off the ends of the actual range you have to avoid the actual minimum
  378    * and maximum of the signal. For example, instead of having full range 8bpc
  379    * ([0-255]) in each channel, you'd only use [16-235]. Anything 16 or below
  380    * is treated as a 0.0 signal, and anything 235 or higher is treated as a 1.0
  381    * signal.
  382    *
  383    * The *reason* this exists, is largely vestigial from the analog era.
  384    *
  385    * For picture we always want the full range.
  386    */
  387   image->yuvRange = AVIF_RANGE_FULL;
  388 
  389   avifRGBImageSetDefaults(&rgb, image);
  390   rgb.format = AVIF_RGB_FORMAT_RGB;
  391 
  392   avifRGBImageAllocatePixels(&rgb);
  393 
  394   const float max_channel_f = (float)((1 << bit_depth) - 1);
  395 
  396   const size_t rowbytes = rgb.rowBytes;
  397 
  398   const float *const restrict in_data = (const float *)in;
  399   uint8_t *const restrict out = (uint8_t *)rgb.pixels;
  400 
  401   switch(bit_depth)
  402   {
  403     case 12:
  404     case 10:
  405     {
  406 #ifdef _OPENMP
  407 #pragma omp parallel for simd default(none) \
  408   dt_omp_firstprivate(in_data, width, height, out, rowbytes, max_channel_f) \
  409   schedule(simd:static) \
  410   collapse(2)
  411 #endif
  412     for(size_t y = 0; y < height; y++)
  413     {
  414       for(size_t x = 0; x < width; x++)
  415       {
  416           const float *in_pixel = &in_data[(size_t)4 * ((y * width) + x)];
  417           uint16_t *out_pixel = (uint16_t *)&out[(y * rowbytes) + (3 * sizeof(uint16_t) * x)];
  418 
  419           out_pixel[0] = (uint16_t)roundf(CLAMP(in_pixel[0] * max_channel_f, 0, max_channel_f));
  420           out_pixel[1] = (uint16_t)roundf(CLAMP(in_pixel[1] * max_channel_f, 0, max_channel_f));
  421           out_pixel[2] = (uint16_t)roundf(CLAMP(in_pixel[2] * max_channel_f, 0, max_channel_f));
  422       }
  423     }
  424     break;
  425     }
  426     case 8:
  427     {
  428 #ifdef _OPENMP
  429 #pragma omp parallel for simd default(none) \
  430   dt_omp_firstprivate(in_data, width, height, out, rowbytes, max_channel_f) \
  431   schedule(simd:static) \
  432   collapse(2)
  433 #endif
  434     for(size_t y = 0; y < height; y++)
  435     {
  436       for(size_t x = 0; x < width; x++)
  437       {
  438           const float *in_pixel = &in_data[(size_t)4 * ((y * width) + x)];
  439           uint8_t *out_pixel = (uint8_t *)&out[(y * rowbytes) + (3 * sizeof(uint8_t) * x)];
  440 
  441           out_pixel[0] = (uint8_t)roundf(CLAMP(in_pixel[0] * max_channel_f, 0, max_channel_f));
  442           out_pixel[1] = (uint8_t)roundf(CLAMP(in_pixel[1] * max_channel_f, 0, max_channel_f));
  443           out_pixel[2] = (uint8_t)roundf(CLAMP(in_pixel[2] * max_channel_f, 0, max_channel_f));
  444       }
  445     }
  446     break;
  447     }
  448     default:
  449       dt_control_log(_("invalid AVIF bit depth!"));
  450       rc = 1;
  451       goto out;
  452   }
  453 
  454   avifImageRGBToYUV(image, &rgb);
  455 
  456 
  457   avifImageSetMetadataExif(image, exif, exif_len);
  458 
  459   encoder = avifEncoderCreate();
  460   if(encoder == NULL)
  461   {
  462     dt_print(DT_DEBUG_IMAGEIO,
  463              "Failed to create AVIF encoder for image [%s]\n",
  464              filename);
  465     rc = 1;
  466     goto out;
  467   }
  468 
  469   switch(d->compression_type)
  470   {
  471     case AVIF_COMP_LOSSLESS:
  472       /* It isn't recommend to use the extremities */
  473       encoder->speed = AVIF_SPEED_SLOWEST + 1;
  474 
  475       encoder->minQuantizer = AVIF_QUANTIZER_LOSSLESS;
  476       encoder->maxQuantizer = AVIF_QUANTIZER_LOSSLESS;
  477 
  478       break;
  479     case AVIF_COMP_LOSSY:
  480       encoder->speed = AVIF_SPEED_DEFAULT;
  481 
  482       encoder->maxQuantizer = 100 - d->quality;
  483       encoder->maxQuantizer = CLAMP(encoder->maxQuantizer, 0, 63);
  484 
  485       encoder->minQuantizer = 64 - d->quality;
  486       encoder->minQuantizer = CLAMP(encoder->minQuantizer, 0, 63);
  487       break;
  488   }
  489 
  490   /*
  491    * Tiling reduces the image quality but it has a negligible impact on
  492    * still images.
  493    *
  494    * The minimum size for a tile is 512x512. We use a default tile size of
  495    * 1024x1024.
  496    */
  497   switch(d->tiling)
  498   {
  499     case AVIF_TILING_ON:
  500     {
  501       size_t width_tile_size  = AVIF_DEFAULT_TILE_SIZE;
  502       size_t height_tile_size = AVIF_DEFAULT_TILE_SIZE;
  503       size_t max_threads;
  504 
  505       if(width >= 6144)
  506       {
  507         width_tile_size = AVIF_MIN_TILE_SIZE * 4;
  508       }
  509       else if (width >= 8192) {
  510         width_tile_size = AVIF_MAX_TILE_SIZE;
  511       }
  512       if(height >= 6144)
  513       {
  514         height_tile_size = AVIF_MIN_TILE_SIZE * 4;
  515       }
  516       else if (height >= 8192) {
  517         height_tile_size = AVIF_MAX_TILE_SIZE;
  518       }
  519 
  520       encoder->tileColsLog2 = floor_log2(width / width_tile_size) / 2;
  521       encoder->tileRowsLog2 = floor_log2(height / height_tile_size) / 2;
  522 
  523       /*
  524        * This should be set to the final number of tiles, based on
  525        * encoder->tileColsLog2 and encoder->tileRowsLog2.
  526        */
  527       max_threads = (1 << encoder->tileRowsLog2) * (1 << encoder->tileColsLog2);
  528 
  529       encoder->maxThreads = MIN(max_threads, dt_get_num_threads());
  530     }
  531     case AVIF_TILING_OFF:
  532       break;
  533   }
  534 
  535   dt_print(DT_DEBUG_IMAGEIO,
  536            "[avif quality: %u => maxQuantizer: %u, minQuantizer: %u, "
  537            "tileColsLog2: %u, tileRowsLog2: %u, threads: %u]\n",
  538            d->quality,
  539            encoder->maxQuantizer,
  540            encoder->minQuantizer,
  541            encoder->tileColsLog2,
  542            encoder->tileRowsLog2,
  543            encoder->maxThreads);
  544 
  545   avifRWData output = AVIF_DATA_EMPTY;
  546 
  547   result = avifEncoderWrite(encoder, image, &output);
  548   if(result != AVIF_RESULT_OK)
  549   {
  550     dt_print(DT_DEBUG_IMAGEIO,
  551              "Failed to encode AVIF image [%s]: %s\n",
  552              filename, avifResultToString(result));
  553     rc = 1;
  554     goto out;
  555   }
  556 
  557   if(output.size == 0 || output.data == NULL)
  558   {
  559     dt_print(DT_DEBUG_IMAGEIO,
  560              "AVIF encoder returned empty data for [%s]\n",
  561              filename);
  562     rc = 1;
  563     goto out;
  564   }
  565 
  566   /*
  567    * Write image to disk
  568    */
  569   FILE *f = NULL;
  570   size_t cnt = 0;
  571 
  572   f = g_fopen(filename, "wb");
  573   if(f == NULL)
  574   {
  575     rc = 1;
  576     goto out;
  577   }
  578 
  579   cnt = fwrite(output.data, 1, output.size, f);
  580   fclose(f);
  581   if(cnt != output.size)
  582   {
  583     g_unlink(filename);
  584     rc = 1;
  585     goto out;
  586   }
  587 
  588   rc = 0; /* success */
  589 out:
  590   avifRGBImageFreePixels(&rgb);
  591   avifImageDestroy(image);
  592   avifEncoderDestroy(encoder);
  593   avifRWDataFree(&output);
  594   free(icc_profile_data);
  595 
  596   return rc;
  597 }
  598 
  599 
  600 size_t params_size(dt_imageio_module_format_t *self)
  601 {
  602   return sizeof(dt_imageio_avif_t);
  603 }
  604 
  605 void *get_params(dt_imageio_module_format_t *self)
  606 {
  607   dt_imageio_avif_t *d = (dt_imageio_avif_t *)calloc(1, sizeof(dt_imageio_avif_t));
  608 
  609   if(d == NULL)
  610   {
  611     return NULL;
  612   }
  613 
  614   gchar * bpp = dt_conf_get_string("plugins/imageio/format/avif/bpp");
  615   d->bit_depth = atoi(bpp);
  616   g_free(bpp);
  617   if(d->bit_depth < 8 || d->bit_depth > 12)
  618   {
  619       d->bit_depth = 8;
  620   }
  621 
  622   d->color_mode = dt_conf_get_int("plugins/imageio/format/avif/color_mode");
  623   d->compression_type = dt_conf_get_int("plugins/imageio/format/avif/compression_type");
  624 
  625   switch(d->compression_type)
  626   {
  627     case AVIF_COMP_LOSSLESS:
  628       d->quality = 100;
  629       break;
  630     case AVIF_COMP_LOSSY:
  631       d->quality = dt_conf_get_int("plugins/imageio/format/avif/quality");
  632       if(d->quality > 100)
  633       {
  634         d->quality = 100;
  635       }
  636       break;
  637   }
  638 
  639   d->tiling = !dt_conf_get_bool("plugins/imageio/format/avif/tiling");
  640 
  641   return d;
  642 }
  643 
  644 int set_params(dt_imageio_module_format_t *self,
  645                const void *params,
  646                const int size)
  647 {
  648   if(size != self->params_size(self))
  649     return 1;
  650   const dt_imageio_avif_t *d = (dt_imageio_avif_t *)params;
  651 
  652   dt_imageio_avif_gui_t *g = (dt_imageio_avif_gui_t *)self->gui_data;
  653   dt_bauhaus_combobox_set(g->bit_depth, d->bit_depth);
  654   dt_bauhaus_combobox_set(g->color_mode, d->color_mode);
  655   dt_bauhaus_combobox_set(g->tiling, d->tiling);
  656   dt_bauhaus_combobox_set(g->compression_type, d->compression_type);
  657   dt_bauhaus_slider_set(g->quality, d->quality);
  658 
  659   return 0;
  660 }
  661 
  662 void free_params(dt_imageio_module_format_t *self,
  663                  dt_imageio_module_data_t *params)
  664 {
  665   free(params);
  666 }
  667 
  668 
  669 int bpp(struct dt_imageio_module_data_t *data)
  670 {
  671   return 32; /* always request float */
  672 }
  673 
  674 int levels(struct dt_imageio_module_data_t *data)
  675 {
  676   return IMAGEIO_RGB|IMAGEIO_FLOAT;
  677 }
  678 
  679 const char *mime(dt_imageio_module_data_t *data)
  680 {
  681   return "image/avif";
  682 }
  683 
  684 const char *extension(dt_imageio_module_data_t *data)
  685 {
  686   return "avif";
  687 }
  688 
  689 const char *name()
  690 {
  691   return _("AVIF (8/10/12-bit)");
  692 }
  693 
  694 int flags(struct dt_imageio_module_data_t *data)
  695 {
  696   return FORMAT_FLAGS_SUPPORT_XMP;
  697 }
  698 
  699 static void bit_depth_changed(GtkWidget *widget, gpointer user_data)
  700 {
  701   const uint32_t idx = dt_bauhaus_combobox_get(widget);
  702 
  703   dt_conf_set_int("plugins/imageio/format/avif/bpp", avif_bit_depth[idx].bit_depth);
  704 }
  705 
  706 static void color_mode_changed(GtkWidget *widget, gpointer user_data)
  707 {
  708   const enum avif_color_mode_e color_mode = dt_bauhaus_combobox_get(widget);
  709 
  710   dt_conf_set_int("plugins/imageio/format/avif/color_mode", color_mode);
  711 }
  712 
  713 static void tiling_changed(GtkWidget *widget, gpointer user_data)
  714 {
  715   const enum avif_tiling_e tiling = dt_bauhaus_combobox_get(widget);
  716 
  717   dt_conf_set_bool("plugins/imageio/format/avif/tiling", !tiling);
  718 }
  719 
  720 static void compression_type_changed(GtkWidget *widget, gpointer user_data)
  721 {
  722   const enum avif_compression_type_e compression_type = dt_bauhaus_combobox_get(widget);
  723   dt_imageio_module_format_t *module = (dt_imageio_module_format_t *)user_data;
  724   dt_imageio_avif_gui_t *gui = (dt_imageio_avif_gui_t *)module->gui_data;
  725 
  726   dt_conf_set_int("plugins/imageio/format/avif/compression_type", compression_type);
  727 
  728   switch(compression_type)
  729   {
  730     case AVIF_COMP_LOSSLESS:
  731       gtk_widget_set_sensitive(gui->quality, FALSE);
  732       break;
  733     case AVIF_COMP_LOSSY:
  734       gtk_widget_set_sensitive(gui->quality, TRUE);
  735       break;
  736   }
  737 }
  738 
  739 static void quality_changed(GtkWidget *slider, gpointer user_data)
  740 {
  741   const uint32_t quality = (int)dt_bauhaus_slider_get(slider);
  742   dt_conf_set_int("plugins/imageio/format/avif/quality", quality);
  743 }
  744 
  745 void gui_init(dt_imageio_module_format_t *self)
  746 {
  747   dt_imageio_avif_gui_t *gui =
  748       (dt_imageio_avif_gui_t *)malloc(sizeof(dt_imageio_avif_gui_t));
  749   const uint32_t bit_depth = dt_conf_get_int("plugins/imageio/format/avif/bit_depth");
  750   const enum avif_color_mode_e color_mode = dt_conf_get_int("plugins/imageio/format/avif/color_mode");
  751   const enum avif_tiling_e tiling = !dt_conf_get_bool("plugins/imageio/format/avif/tiling");
  752   const enum avif_compression_type_e compression_type = dt_conf_get_int("plugins/imageio/format/avif/compression_type");
  753   const uint32_t quality = dt_conf_get_int("plugins/imageio/format/avif/quality");
  754 
  755   self->gui_data = (void *)gui;
  756 
  757   self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  758 
  759   /*
  760    * Bit depth combo box
  761    */
  762   gui->bit_depth = dt_bauhaus_combobox_new(NULL);
  763 
  764   dt_bauhaus_widget_set_label(gui->bit_depth, NULL, N_("bit depth"));
  765   size_t idx = 0;
  766   for(size_t i = 0; avif_bit_depth[i].name != NULL; i++)
  767   {
  768     dt_bauhaus_combobox_add(gui->bit_depth,  _(avif_bit_depth[i].name));
  769     if(avif_bit_depth[i].bit_depth == bit_depth)
  770     {
  771       idx = i;
  772     }
  773   }
  774   dt_bauhaus_combobox_set(gui->bit_depth, idx);
  775 
  776   gtk_widget_set_tooltip_text(gui->bit_depth,
  777           _("color information stored in an image, higher is better"));
  778 
  779   gtk_box_pack_start(GTK_BOX(self->widget), gui->bit_depth, TRUE, TRUE, 0);
  780 
  781   /*
  782    * Color mode combo box
  783    */
  784   gui->color_mode = dt_bauhaus_combobox_new(NULL);
  785   dt_bauhaus_widget_set_label(gui->color_mode,
  786                               NULL,
  787                               _("color mode"));
  788   dt_bauhaus_combobox_add(gui->color_mode,
  789                           _("rgb colors"));
  790   dt_bauhaus_combobox_add(gui->color_mode,
  791                           _("grayscale"));
  792   dt_bauhaus_combobox_set(gui->color_mode, color_mode);
  793 
  794   gtk_widget_set_tooltip_text(gui->color_mode,
  795           _("saving as grayscale will reduce the size for black & white images"));
  796 
  797   gtk_box_pack_start(GTK_BOX(self->widget),
  798                      gui->color_mode,
  799                      TRUE,
  800                      TRUE,
  801                      0);
  802   /*
  803    * Tiling combo box
  804    */
  805   gui->tiling = dt_bauhaus_combobox_new(NULL);
  806   dt_bauhaus_widget_set_label(gui->tiling,
  807                               NULL,
  808                               N_("tiling"));
  809   dt_bauhaus_combobox_add(gui->tiling,
  810                           _("on"));
  811   dt_bauhaus_combobox_add(gui->tiling,
  812                           _("off"));
  813   dt_bauhaus_combobox_set(gui->tiling, tiling);
  814 
  815   gtk_widget_set_tooltip_text(gui->tiling,
  816           _("tile an image into segments.\n"
  817             "\n"
  818             "makes encoding faster. the impact on quality reduction "
  819             "is negligible, but increases the file size."));
  820 
  821   gtk_box_pack_start(GTK_BOX(self->widget),
  822                      gui->tiling,
  823                      TRUE,
  824                      TRUE,
  825                      0);
  826 
  827   /*
  828    * Compression type combo box
  829    */
  830   gui->compression_type = dt_bauhaus_combobox_new(NULL);
  831   dt_bauhaus_widget_set_label(gui->compression_type,
  832                               NULL,
  833                               N_("compression type"));
  834   dt_bauhaus_combobox_add(gui->compression_type,
  835                           _(avif_get_compression_string(AVIF_COMP_LOSSLESS)));
  836   dt_bauhaus_combobox_add(gui->compression_type,
  837                           _(avif_get_compression_string(AVIF_COMP_LOSSY)));
  838   dt_bauhaus_combobox_set(gui->compression_type, compression_type);
  839 
  840   gtk_widget_set_tooltip_text(gui->compression_type,
  841           _("the compression for the image"));
  842 
  843   gtk_box_pack_start(GTK_BOX(self->widget),
  844                      gui->compression_type,
  845                      TRUE,
  846                      TRUE,
  847                      0);
  848 
  849   /*
  850    * Quality combo box
  851    */
  852   gui->quality = dt_bauhaus_slider_new_with_range(NULL,
  853                                                   dt_confgen_get_int("plugins/imageio/format/avif/quality", DT_MIN), /* min */
  854                                                   dt_confgen_get_int("plugins/imageio/format/avif/quality", DT_MAX), /* max */
  855                                                   1, /* step */
  856                                                   dt_confgen_get_int("plugins/imageio/format/avif/quality", DT_DEFAULT), /* default */
  857                                                   0); /* digits */
  858   dt_bauhaus_widget_set_label(gui->quality,  NULL, N_("quality"));
  859   dt_bauhaus_slider_set_default(gui->quality, dt_confgen_get_int("plugins/imageio/format/avif/quality", DT_DEFAULT));
  860   dt_bauhaus_slider_set_format(gui->quality, "%.2f%%");
  861 
  862   gtk_widget_set_tooltip_text(gui->quality,
  863           _("the quality of an image, less quality means fewer details.\n"
  864             "\n"
  865             "the following applies only to lossy setting\n"
  866             "\n"
  867             "pixelformat based on quality:\n"
  868             "\n"
  869             "    91% - 100% -> YUV444\n"
  870             "    81% -  90% -> YUV422\n"
  871             "     5% -  80% -> YUV420\n"));
  872 
  873   if(quality > 0 && quality <= 100)
  874   {
  875       dt_bauhaus_slider_set(gui->quality, quality);
  876   }
  877   gtk_box_pack_start(GTK_BOX(self->widget), gui->quality, TRUE, TRUE, 0);
  878 
  879   switch(compression_type)
  880   {
  881     case AVIF_COMP_LOSSLESS:
  882       gtk_widget_set_sensitive(gui->quality, FALSE);
  883       break;
  884     case AVIF_COMP_LOSSY:
  885       break;
  886   }
  887 
  888   g_signal_connect(G_OBJECT(gui->bit_depth),
  889                    "value-changed",
  890                    G_CALLBACK(bit_depth_changed),
  891                    NULL);
  892   g_signal_connect(G_OBJECT(gui->color_mode),
  893                    "value-changed",
  894                    G_CALLBACK(color_mode_changed),
  895                    (gpointer)self);
  896   g_signal_connect(G_OBJECT(gui->tiling),
  897                    "value-changed",
  898                    G_CALLBACK(tiling_changed),
  899                    (gpointer)self);
  900   g_signal_connect(G_OBJECT(gui->compression_type),
  901                    "value-changed",
  902                    G_CALLBACK(compression_type_changed),
  903                    (gpointer)self);
  904   g_signal_connect(G_OBJECT(gui->quality),
  905                    "value-changed",
  906                    G_CALLBACK(quality_changed),
  907                    NULL);
  908 }
  909 
  910 void gui_cleanup(dt_imageio_module_format_t *self)
  911 {
  912   free(self->gui_data);
  913 }
  914 
  915 void gui_reset(dt_imageio_module_format_t *self)
  916 {
  917   dt_imageio_avif_gui_t *gui = (dt_imageio_avif_gui_t *)self->gui_data;
  918 
  919   const enum avif_color_mode_e color_mode = dt_confgen_get_int("plugins/imageio/format/avif/color_mode", DT_DEFAULT);
  920   const enum avif_tiling_e tiling = !dt_confgen_get_bool("plugins/imageio/format/avif/tiling", DT_DEFAULT);
  921   const enum avif_compression_type_e compression_type = dt_confgen_get_int("plugins/imageio/format/avif/compression_type", DT_DEFAULT);
  922   const uint32_t quality = dt_confgen_get_int("plugins/imageio/format/avif/quality", DT_DEFAULT);
  923 
  924   dt_bauhaus_combobox_set(gui->bit_depth, 0); //8bpp
  925   dt_bauhaus_combobox_set(gui->color_mode, color_mode);
  926   dt_bauhaus_combobox_set(gui->tiling, tiling);
  927   dt_bauhaus_combobox_set(gui->compression_type, compression_type);
  928   dt_bauhaus_slider_set(gui->quality, quality);
  929 
  930   compression_type_changed(GTK_WIDGET(gui->compression_type), self);
  931   quality_changed(GTK_WIDGET(gui->quality), self);
  932   bit_depth_changed(GTK_WIDGET(gui->bit_depth), self);
  933 }