View | Details | Raw Unified | Return to bug 266125
Collapse All | Expand All

(-)b/graphics/darktable/Makefile (-3 / +6 lines)
Lines 1-6 Link Here
1
PORTNAME=	darktable
1
PORTNAME=	darktable
2
PORTVERSION=	3.8.1
2
PORTVERSION=	3.8.1
3
PORTREVISION=	8
3
PORTREVISION=	9
4
CATEGORIES=	graphics
4
CATEGORIES=	graphics
5
MASTER_SITES=	https://github.com/darktable-org/${PORTNAME}/releases/download/release-${PORTVERSION:C/\.rc/rc/}/
5
MASTER_SITES=	https://github.com/darktable-org/${PORTNAME}/releases/download/release-${PORTVERSION:C/\.rc/rc/}/
6
6
Lines 51-59 LDFLAGS+= -L${LOCALBASE}/lib Link Here
51
51
52
WRKSRC=		${WRKDIR}/${PORTNAME}-${PORTVERSION:C/\.rc/~rc/}
52
WRKSRC=		${WRKDIR}/${PORTNAME}-${PORTVERSION:C/\.rc/~rc/}
53
53
54
OPTIONS_DEFINE=		AVIF DOCS GEO GPHOTO GMIC GRAPHMAGICK HEIF LIBSECRET LTO \
54
OPTIONS_DEFINE=		AVIF DOCS GEO GPHOTO GMIC GRAPHMAGICK HEIF JXL LIBSECRET LTO \
55
			LUA NLS OPENEXR OPENJPEG PRINT TOOLS WEBP
55
			LUA NLS OPENEXR OPENJPEG PRINT TOOLS WEBP
56
OPTIONS_DEFAULT=	AVIF GEO GPHOTO HEIF LTO LUA OPENEXR OPENJPEG PRINT WEBP
56
OPTIONS_DEFAULT=	AVIF GEO GPHOTO HEIF JXL LTO LUA OPENEXR OPENJPEG PRINT WEBP
57
OPTIONS_SUB=		yes
57
OPTIONS_SUB=		yes
58
58
59
GEO_DESC=		Support geotagging
59
GEO_DESC=		Support geotagging
Lines 78-83 AVIF_CMAKE_BOOL= USE_AVIF Link Here
78
HEIF_LIB_DEPENDS=	libheif.so:graphics/libheif
78
HEIF_LIB_DEPENDS=	libheif.so:graphics/libheif
79
HEIF_CMAKE_BOOL=	USE_HEIF
79
HEIF_CMAKE_BOOL=	USE_HEIF
80
80
81
JXL_LIB_DEPENDS=	libjxl.so:graphics/libjxl
82
JXL_CMAKE_BOOL=		USE_JXL
83
81
LIBSECRET_LIB_DEPENDS=	libsecret-1.so:security/libsecret
84
LIBSECRET_LIB_DEPENDS=	libsecret-1.so:security/libsecret
82
LIBSECRET_CMAKE_BOOL=	USE_LIBSECRET
85
LIBSECRET_CMAKE_BOOL=	USE_LIBSECRET
83
86
(-)b/graphics/darktable/files/patch-jxl (+842 lines)
Added Link Here
1
https://github.com/darktable-org/darktable/pull/10044 (475a8bf version)
2
3
--- DefineOptions.cmake.orig	2022-02-09 10:21:58 UTC
4
+++ DefineOptions.cmake
5
@@ -18,6 +18,7 @@ option(USE_OPENJPEG "Enable JPEG 2000 support" ON)
6
 option(BINARY_PACKAGE_BUILD "Sets march optimization to generic" OFF)
7
 option(USE_XMLLINT "Run xmllint to test if darktableconfig.xml is valid" ON)
8
 option(USE_OPENJPEG "Enable JPEG 2000 support" ON)
9
+option(USE_JXL "Enable JPEG XL export support" ON)
10
 option(USE_WEBP "Enable WebP export support" ON)
11
 option(USE_AVIF "Enable AVIF support" ON)
12
 option(USE_HEIF "Enable HEIF/HEIC support" ON)
13
--- cmake/modules/FindJXL.cmake.orig	2022-08-31 08:07:07 UTC
14
+++ cmake/modules/FindJXL.cmake
15
@@ -0,0 +1,38 @@
16
+# Find libjxl
17
+# Will define:
18
+# - JXL_FOUND
19
+# - JXL_INCLUDE_DIRS directory to include for libjxl headers
20
+# - JXL_LIBRARIES libraries to link to
21
+
22
+include(LibFindMacros)
23
+
24
+# Use pkg-config to get hints about paths
25
+# libfind_pkg_check_modules(JXL_PKGCONF libjxl) <- this isn't working?
26
+pkg_check_modules(JXL_PKGCONF QUIET libjxl)
27
+
28
+find_path(JXL_INCLUDE_DIR
29
+  NAMES jxl/encode.h
30
+  HINTS ${JXL_PKGCONF_INCLUDE_DIRS}
31
+)
32
+
33
+find_library(JXL_LIBRARY
34
+  NAMES jxl
35
+  HINTS ${JXL_PKGCONF_LIBRARY_DIRS}
36
+)
37
+
38
+find_library(JXL_THREADS_LIBRARY
39
+  NAMES jxl_threads
40
+  HINTS ${JXL_PKGCONF_LIBRARY_DIRS}
41
+)
42
+
43
+include(FindPackageHandleStandardArgs)
44
+find_package_handle_standard_args(JXL DEFAULT_MSG JXL_LIBRARY JXL_INCLUDE_DIR)
45
+
46
+if(JXL_PKGCONF_VERSION VERSION_LESS JXL_FIND_VERSION)
47
+  set(JXL_FOUND false)
48
+endif()
49
+
50
+if(JXL_FOUND)
51
+  set(JXL_LIBRARIES ${JXL_LIBRARY} ${JXL_THREADS_LIBRARY})
52
+  set(JXL_INCLUDE_DIRS ${JXL_INCLUDE_DIR})
53
+endif(JXL_FOUND)
54
--- cmake/windows-macros.cmake.orig	2022-02-09 10:21:58 UTC
55
+++ cmake/windows-macros.cmake
56
@@ -157,6 +157,15 @@ if (WIN32 AND NOT BUILD_MSYS2_INSTALL)
57
     )
58
     list(APPEND CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ${TMP_SYSTEM_RUNTIME_LIBS})
59
   endif()
60
+  
61
+  if(JXL_FOUND)
62
+    file(GLOB TMP_SYSTEM_RUNTIME_LIBS
63
+      #LIBJXL
64
+      ${MINGW_PATH}/libjxl.dll
65
+      ${MINGW_PATH}/libjxl_threads.dll
66
+    )
67
+    list(APPEND CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ${TMP_SYSTEM_RUNTIME_LIBS})
68
+  endif()
69
 
70
   if(WebP_FOUND)
71
     file(GLOB TMP_SYSTEM_RUNTIME_LIBS
72
--- data/darktableconfig.xml.in.orig	2022-02-09 10:21:58 UTC
73
+++ data/darktableconfig.xml.in
74
@@ -2407,6 +2407,41 @@
75
     <longdescription/>
76
   </dtconfig>
77
   <dtconfig>
78
+    <name>plugins/imageio/format/jxl/bpp</name>
79
+    <type min="0" max="5">int</type>
80
+    <default>0</default>
81
+    <shortdescription/>
82
+    <longdescription/>
83
+  </dtconfig>
84
+  <dtconfig>
85
+    <name>plugins/imageio/format/jxl/quality</name>
86
+    <type min="0" max="100">int</type>
87
+    <default>95</default>
88
+    <shortdescription/>
89
+    <longdescription/>
90
+  </dtconfig>
91
+  <dtconfig>
92
+    <name>plugins/imageio/format/jxl/original</name>
93
+    <type>bool</type>
94
+    <default>true</default>
95
+    <shortdescription/>
96
+    <longdescription/>
97
+  </dtconfig>
98
+  <dtconfig>
99
+    <name>plugins/imageio/format/jxl/effort</name>
100
+    <type min="1" max="9">int</type>
101
+    <default>7</default>
102
+    <shortdescription/>
103
+    <longdescription/>
104
+  </dtconfig>
105
+  <dtconfig>
106
+    <name>plugins/imageio/format/jxl/tier</name>
107
+    <type min="0" max="4">int</type>
108
+    <default>0</default>
109
+    <shortdescription/>
110
+    <longdescription/>
111
+  </dtconfig>
112
+  <dtconfig>
113
     <name>plugins/imageio/format/webp/comp_type</name>
114
     <type min="0" max="1">int</type>
115
     <default>0</default>
116
--- src/CMakeLists.txt.orig	2022-02-09 10:21:58 UTC
117
+++ src/CMakeLists.txt
118
@@ -340,6 +340,15 @@ endif(USE_OPENEXR)
119
   endif(OpenEXR_FOUND)
120
 endif(USE_OPENEXR)
121
 
122
+if(USE_JXL)
123
+  find_package(JXL 0.7.0)
124
+  if(JXL_FOUND)
125
+    include_directories(SYSTEM ${JXL_INCLUDE_DIRS})
126
+    list(APPEND LIBS ${JXL_LIBRARIES})
127
+    add_definitions(${JXL_DEFINITIONS})
128
+  endif(JXL_FOUND)
129
+endif(USE_JXL)
130
+
131
 if(USE_WEBP)
132
   find_package(WebP 0.3.0)
133
   if(WebP_FOUND)
134
--- src/imageio/format/CMakeLists.txt.orig	2022-02-09 10:21:58 UTC
135
+++ src/imageio/format/CMakeLists.txt
136
@@ -17,6 +17,16 @@ add_library(tiff MODULE "tiff.c")
137
 add_library(pfm MODULE "pfm.c")
138
 add_library(tiff MODULE "tiff.c")
139
 
140
+if(JXL_FOUND)
141
+  list(APPEND MODULES "jxl_format")
142
+  add_library(jxl_format MODULE "jxl.c")
143
+  # At least on Windows, have to link to libjxl explicitly here as symbols
144
+  # from libdarktable get stripped (until read support is added). As a
145
+  # consequence, have to also change the module name to not be libjxl.
146
+  target_link_libraries(jxl_format PUBLIC ${JXL_LIBRARIES})
147
+  set_target_properties(jxl_format PROPERTIES OUTPUT_NAME jpegxl)
148
+endif(JXL_FOUND)
149
+
150
 if(WebP_FOUND)
151
   list(APPEND MODULES "webp")
152
   add_library(webp MODULE "webp.c")
153
--- src/imageio/format/jxl.c.orig	2022-08-31 08:07:07 UTC
154
+++ src/imageio/format/jxl.c
155
@@ -0,0 +1,668 @@
156
+/*
157
+    This file is part of darktable,
158
+    Copyright (C) 2021-2022 darktable developers.
159
+
160
+    darktable is free software: you can redistribute it and/or modify
161
+    it under the terms of the GNU General Public License as published by
162
+    the Free Software Foundation, either version 3 of the License, or
163
+    (at your option) any later version.
164
+
165
+    darktable is distributed in the hope that it will be useful,
166
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
167
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
168
+    GNU General Public License for more details.
169
+
170
+    You should have received a copy of the GNU General Public License
171
+    along with darktable.  If not, see <http://www.gnu.org/licenses/>.
172
+*/
173
+
174
+#include "bauhaus/bauhaus.h"
175
+#include "common/colorspaces.h"
176
+#include "common/darktable.h"
177
+#include "common/exif.h"
178
+#include "common/imageio.h"
179
+#include "control/conf.h"
180
+#include "imageio/format/imageio_format_api.h"
181
+
182
+#include "jxl/encode.h"
183
+#include "jxl/resizable_parallel_runner.h"
184
+
185
+DT_MODULE(1)
186
+
187
+typedef struct dt_imageio_jxl_t
188
+{
189
+  dt_imageio_module_data_t global;
190
+  int bpp;
191
+  int quality;
192
+  int original;
193
+  int effort;
194
+  int tier;
195
+} dt_imageio_jxl_t;
196
+
197
+typedef struct dt_imageio_jxl_gui_data_t
198
+{
199
+  // Int (0:8b, 1:10b, 2:12b, 3:16b, 4:half, 5:float)
200
+  GtkWidget *bpp;
201
+  // Int (0-100): the quality of the image, roughly corresponding to JPEG quality (100 is lossless)
202
+  GtkWidget *quality;
203
+  // Bool: whether to encode using the original color profile or the internal XYB one
204
+  GtkWidget *original;
205
+  // Int (1-9): effort with which to encode output; higher is slower (default is 7)
206
+  GtkWidget *effort;
207
+  // Int (0-4): higher value favors decoding speed vs quality (default is 0)
208
+  GtkWidget *tier;
209
+} dt_imageio_jxl_gui_data_t;
210
+
211
+
212
+void init(dt_imageio_module_format_t *self)
213
+{
214
+#ifdef USE_LUA
215
+  dt_lua_register_module_member(darktable.lua_state.state, self, dt_imageio_jxl_t, bpp, int);
216
+
217
+  dt_lua_register_module_member(darktable.lua_state.state, self, dt_imageio_jxl_t, quality, int);
218
+
219
+  dt_lua_register_module_member(darktable.lua_state.state, self, dt_imageio_jxl_t, original, int);
220
+
221
+  dt_lua_register_module_member(darktable.lua_state.state, self, dt_imageio_jxl_t, effort, int);
222
+
223
+  dt_lua_register_module_member(darktable.lua_state.state, self, dt_imageio_jxl_t, tier, int);
224
+#endif
225
+}
226
+
227
+void cleanup(dt_imageio_module_format_t *self)
228
+{
229
+}
230
+
231
+
232
+const char *mime(dt_imageio_module_data_t *data)
233
+{
234
+  return "image/jxl";
235
+}
236
+
237
+const char *extension(dt_imageio_module_data_t *data)
238
+{
239
+  return "jxl";
240
+}
241
+
242
+int dimension(struct dt_imageio_module_format_t *self, struct dt_imageio_module_data_t *data, uint32_t *width,
243
+              uint32_t *height)
244
+{
245
+  // The maximum dimensions supported by jxl images
246
+  *width = 1073741823U;
247
+  *height = 1073741823U;
248
+  return 1;
249
+}
250
+
251
+int bpp(dt_imageio_module_data_t *data)
252
+{
253
+  return 32; /* always request float */
254
+}
255
+
256
+
257
+int write_image(struct dt_imageio_module_data_t *data, const char *filename, const void *in_tmp,
258
+                dt_colorspaces_color_profile_type_t over_type, const char *over_filename, void *exif, int exif_len,
259
+                int imgid, int num, int total, struct dt_dev_pixelpipe_t *pipe, const gboolean export_masks)
260
+{
261
+  // Return error code by default
262
+  int ret = 1;
263
+
264
+  float *pixels = NULL;
265
+  uint8_t *out_buf = NULL;
266
+  FILE *out_file = NULL;
267
+  uint8_t *icc_buf = NULL;
268
+  uint8_t *exif_buf = NULL;
269
+  char *xmp_string = NULL;
270
+
271
+#define LIBJXL_ASSERT(code)                                                                                       \
272
+  {                                                                                                               \
273
+    if((JxlEncoderStatus)code != JXL_ENC_SUCCESS)                                                                 \
274
+    {                                                                                                             \
275
+      JxlEncoderError err = JxlEncoderGetError(encoder);                                                          \
276
+      dt_print(DT_DEBUG_IMAGEIO, "[jxl] libjxl call failed with err %d (src/imageio/format/jxl.c#L%d)\n", err,    \
277
+               __LINE__);                                                                                         \
278
+      goto end;                                                                                                   \
279
+    }                                                                                                             \
280
+  }
281
+
282
+#define JXL_FAIL(msg, ...)                                                                                        \
283
+  {                                                                                                               \
284
+    dt_print(DT_DEBUG_IMAGEIO, "[jxl] " msg "\n", ##__VA_ARGS__);                                                 \
285
+    goto end;                                                                                                     \
286
+  }
287
+
288
+  const dt_imageio_jxl_t *params = (dt_imageio_jxl_t *)data;
289
+  const uint32_t width = (uint32_t)params->global.width;
290
+  const uint32_t height = (uint32_t)params->global.height;
291
+
292
+  JxlEncoder *encoder = JxlEncoderCreate(NULL);
293
+
294
+  const uint32_t num_threads = JxlResizableParallelRunnerSuggestThreads(width, height);
295
+  void *runner = JxlResizableParallelRunnerCreate(NULL);
296
+  if(!runner) JXL_FAIL("could not create resizable parallel runner");
297
+  JxlResizableParallelRunnerSetThreads(runner, num_threads);
298
+  LIBJXL_ASSERT(JxlEncoderSetParallelRunner(encoder, JxlResizableParallelRunner, runner));
299
+
300
+  // Automatically freed when we destroy the encoder
301
+  JxlEncoderFrameSettings *frame_settings = JxlEncoderFrameSettingsCreate(encoder, NULL);
302
+
303
+  // Set encoder basic info
304
+  JxlBasicInfo basic_info;
305
+  JxlEncoderInitBasicInfo(&basic_info);
306
+  basic_info.xsize = width;
307
+  basic_info.ysize = height;
308
+  switch(params->bpp)
309
+  {
310
+    case 0:
311
+      basic_info.bits_per_sample = 8;
312
+      basic_info.exponent_bits_per_sample = 0;
313
+      break;
314
+    case 1:
315
+      basic_info.bits_per_sample = 10;
316
+      basic_info.exponent_bits_per_sample = 0;
317
+      break;
318
+    case 2:
319
+      basic_info.bits_per_sample = 12;
320
+      basic_info.exponent_bits_per_sample = 0;
321
+      break;
322
+    case 3:
323
+      basic_info.bits_per_sample = 16;
324
+      basic_info.exponent_bits_per_sample = 0;
325
+      break;
326
+    case 4:
327
+      basic_info.bits_per_sample = 16;
328
+      basic_info.exponent_bits_per_sample = 5;
329
+      break;
330
+    default:
331
+      basic_info.bits_per_sample = 32;
332
+      basic_info.exponent_bits_per_sample = 8;
333
+      break;
334
+  }
335
+  // Lossless only makes sense for integer modes
336
+  if(basic_info.exponent_bits_per_sample == 0 && params->quality == 100)
337
+  {
338
+    // Must preserve original profile for lossless mode
339
+    basic_info.uses_original_profile = JXL_TRUE;
340
+    LIBJXL_ASSERT(JxlEncoderSetFrameDistance(frame_settings, 0.0f));
341
+    LIBJXL_ASSERT(JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE));
342
+  }
343
+  else
344
+  {
345
+    basic_info.uses_original_profile = params->original == FALSE ? JXL_FALSE : JXL_TRUE;
346
+    float distance = params->quality >= 30 ? 0.1f + (100 - params->quality) * 0.09f
347
+                                           : 6.4f + powf(2.5f, (30 - params->quality) / 5.0f) / 6.25f;
348
+    LIBJXL_ASSERT(JxlEncoderSetFrameDistance(frame_settings, distance));
349
+  }
350
+
351
+  LIBJXL_ASSERT(JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, params->effort));
352
+
353
+  LIBJXL_ASSERT(
354
+      JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_DECODING_SPEED, params->tier));
355
+
356
+  // Codestream level should be chosen automatically given the settings
357
+  LIBJXL_ASSERT(JxlEncoderSetBasicInfo(encoder, &basic_info));
358
+
359
+  // Determine and set the encoder color space
360
+  const dt_colorspaces_color_profile_t *output_profile
361
+      = dt_colorspaces_get_output_profile(imgid, over_type, over_filename);
362
+  const cmsHPROFILE out_profile = output_profile->profile;
363
+  // Previous call will give us a more accurate color profile type
364
+  // (not what the user requested in the export menu but what the image is actually using)
365
+  over_type = output_profile->type;
366
+
367
+  // If possible we want libjxl to save the color encoding in its own format, rather
368
+  // than as an ICC binary blob which is possible.
369
+  // If we are unable to find the required color encoding data for libjxl we will
370
+  // just fallback to providing an ICC blob (and hope we can at least do that!).
371
+  bool write_color_natively = true;
372
+
373
+  JxlColorEncoding color_encoding;
374
+  color_encoding.color_space = JXL_COLOR_SPACE_RGB;
375
+  // If not explicitly set in the export menu, use the intent of the actual output profile
376
+  if(pipe->icc_intent >= DT_INTENT_PERCEPTUAL && pipe->icc_intent < DT_INTENT_LAST)
377
+    color_encoding.rendering_intent = (JxlRenderingIntent)pipe->icc_intent;
378
+  else
379
+    color_encoding.rendering_intent = (JxlRenderingIntent)cmsGetHeaderRenderingIntent(out_profile);
380
+
381
+  // Attempt to find and set the known white point, primaries and transfer function.
382
+  // If we can't find any of these we fall back to an ICC binary blob.
383
+  switch(over_type)
384
+  {
385
+    case DT_COLORSPACE_SRGB:
386
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
387
+      color_encoding.primaries = JXL_PRIMARIES_SRGB;
388
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
389
+      break;
390
+    case DT_COLORSPACE_LIN_REC709:
391
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
392
+      color_encoding.primaries = JXL_PRIMARIES_SRGB;
393
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
394
+      break;
395
+    case DT_COLORSPACE_LIN_REC2020:
396
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
397
+      color_encoding.primaries = JXL_PRIMARIES_2100;
398
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
399
+      break;
400
+    // TODO: enable when JXL_PRIMARIES_XYZ are added to libjxl
401
+    //  case DT_COLORSPACE_XYZ:
402
+    //    color_encoding.white_point = JXL_WHITE_POINT_E;
403
+    //    color_encoding.primaries = JXL_PRIMARIES_XYZ;
404
+    //    color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
405
+    //    break;
406
+    case DT_COLORSPACE_REC709:
407
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
408
+      color_encoding.primaries = JXL_PRIMARIES_SRGB;
409
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_709;
410
+      break;
411
+    case DT_COLORSPACE_PQ_REC2020:
412
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
413
+      color_encoding.primaries = JXL_PRIMARIES_2100;
414
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_PQ;
415
+      break;
416
+    case DT_COLORSPACE_HLG_REC2020:
417
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
418
+      color_encoding.primaries = JXL_PRIMARIES_2100;
419
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_HLG;
420
+      break;
421
+    case DT_COLORSPACE_PQ_P3:
422
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
423
+      color_encoding.primaries = JXL_PRIMARIES_P3;
424
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_PQ;
425
+      break;
426
+    case DT_COLORSPACE_HLG_P3:
427
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
428
+      color_encoding.primaries = JXL_PRIMARIES_P3;
429
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_HLG;
430
+      break;
431
+    default:
432
+      write_color_natively = false;
433
+      break;
434
+  }
435
+
436
+  if(write_color_natively)
437
+  {
438
+    JxlEncoderSetColorEncoding(encoder, &color_encoding);
439
+  }
440
+  else
441
+  {
442
+    // If we didn't manage to write the color encoding natively we need to fallback to ICC
443
+    dt_print(DT_DEBUG_IMAGEIO, "[jxl] could not generate color encoding structure, falling back to ICC\n");
444
+
445
+    cmsUInt32Number icc_size = 0;
446
+    // First find the size of the ICC buffer
447
+    if(!cmsSaveProfileToMem(out_profile, NULL, &icc_size)) JXL_FAIL("error finding ICC data length");
448
+    if(icc_size > 0) icc_buf = g_malloc(icc_size);
449
+    if(!icc_buf) JXL_FAIL("could not allocate ICC buffer of size %u", icc_size);
450
+
451
+    // Fill the ICC buffer
452
+    if(!cmsSaveProfileToMem(out_profile, icc_buf, &icc_size)) JXL_FAIL("error writing ICC data");
453
+
454
+    LIBJXL_ASSERT(JxlEncoderSetICCProfile(encoder, icc_buf, icc_size));
455
+  }
456
+
457
+  // We assume that the user wants the JXL image in a BMFF container.
458
+  // JXL images can be stored without any container so they are smaller, but
459
+  // this removes the possibility of storing extra metadata like Exif and XMP.
460
+  LIBJXL_ASSERT(JxlEncoderUseBoxes(encoder));
461
+
462
+  JxlPixelFormat pixel_format = { 3, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0 };
463
+
464
+  // Fix pixel stride
465
+  const size_t pixels_size = width * height * 3 * sizeof(float);
466
+  pixels = g_malloc(pixels_size);
467
+  if(!pixels) JXL_FAIL("could not allocate output pixel buffer of size %zu", pixels_size);
468
+#ifdef _OPENMP
469
+#pragma omp parallel for simd default(none) dt_omp_firstprivate(in_tmp, pixels, width, height) schedule(simd      \
470
+                                                                                                        : static) \
471
+    collapse(2)
472
+#endif
473
+  for(uint32_t y = 0; y < height; ++y)
474
+  {
475
+    for(uint32_t x = 0; x < width; ++x)
476
+    {
477
+      const float *in_pixel = (const float *)in_tmp + 4 * ((y * width) + x);
478
+      float *out_pixel = pixels + 3 * ((y * width) + x);
479
+
480
+      out_pixel[0] = in_pixel[0];
481
+      out_pixel[1] = in_pixel[1];
482
+      out_pixel[2] = in_pixel[2];
483
+    }
484
+  }
485
+
486
+  LIBJXL_ASSERT(JxlEncoderAddImageFrame(frame_settings, &pixel_format, pixels, pixels_size));
487
+
488
+  /* TODO: workaround; remove when exiv2 implements JXL BMFF write support and use dt_exif_write_blob() after
489
+   * closing file instead */
490
+  if(exif && exif_len > 0)
491
+  {
492
+    // Prepend the 4 byte (zero) offset to the blob before writing
493
+    // (as required in the equivalent HEIF/JPEG XS Exif box specs)
494
+    exif_buf = g_malloc0(exif_len + 4);
495
+    if(!exif_buf) JXL_FAIL("could not allocate Exif buffer of size %zu", (size_t)(exif_len + 4));
496
+    memmove(exif_buf + 4, exif, exif_len);
497
+    // Exiv2 doesn't support Brotli compressed boxes yet
498
+    LIBJXL_ASSERT(JxlEncoderAddBox(encoder, "Exif", exif_buf, exif_len + 4, JXL_FALSE));
499
+  }
500
+
501
+  /* TODO: workaround; remove when exiv2 implements JXL BMFF write support and update flags() */
502
+  xmp_string = dt_exif_xmp_read_string(imgid);
503
+  size_t xmp_len;
504
+  if(xmp_string && (xmp_len = strlen(xmp_string)) > 0)
505
+  {
506
+    // Exiv2 doesn't support Brotli compressed boxes
507
+    LIBJXL_ASSERT(JxlEncoderAddBox(encoder, "xml ", (const uint8_t *)xmp_string, xmp_len, JXL_FALSE));
508
+  }
509
+
510
+  // No more image frames nor metadata boxes to add
511
+  JxlEncoderCloseInput(encoder);
512
+
513
+  // Write the image codestream to a buffer, starting with a chunk of 64 KiB.
514
+  // TODO: Can we better estimate what the optimal size of chunks is for this image?
515
+  size_t chunk_size = 1 << 16;
516
+  size_t out_len = chunk_size;
517
+  out_buf = g_malloc(out_len);
518
+  if(!out_buf) JXL_FAIL("could not allocate codestream buffer of size %zu", out_len);
519
+  uint8_t *out_cur = out_buf;
520
+  size_t out_avail = out_len;
521
+
522
+  JxlEncoderStatus out_status = JXL_ENC_NEED_MORE_OUTPUT;
523
+  while(out_status == JXL_ENC_NEED_MORE_OUTPUT)
524
+  {
525
+    out_status = JxlEncoderProcessOutput(encoder, &out_cur, &out_avail);
526
+
527
+    if(out_status == JXL_ENC_NEED_MORE_OUTPUT)
528
+    {
529
+      const size_t offset = out_cur - out_buf;
530
+      if(chunk_size < 1 << 20) chunk_size *= 2;
531
+      out_len += chunk_size;
532
+      out_buf = g_realloc(out_buf, out_len);
533
+      if(!out_buf)
534
+      {
535
+        JXL_FAIL("could not reallocate codestream buffer to size %zu", out_len);
536
+        goto end;
537
+      }
538
+      out_cur = out_buf + offset;
539
+      out_avail = out_len - offset;
540
+    }
541
+  }
542
+  LIBJXL_ASSERT(out_status);
543
+  // Update actual length of codestream written
544
+  out_len = out_cur - out_buf;
545
+
546
+  // Write codestream contents to file
547
+  out_file = g_fopen(filename, "wb");
548
+  if(!out_file) JXL_FAIL("could not open output file `%s'", filename);
549
+
550
+  if(fwrite(out_buf, sizeof(uint8_t), out_len, out_file) != out_len)
551
+    JXL_FAIL("could not write bytes to `%s'", filename);
552
+
553
+  // Finally, successful write: set to success code
554
+  ret = 0;
555
+
556
+end:
557
+  if(runner) JxlResizableParallelRunnerDestroy(runner);
558
+  if(encoder) JxlEncoderDestroy(encoder);
559
+  if(out_file) fclose(out_file);
560
+  g_free(pixels);
561
+  g_free(icc_buf);
562
+  g_free(exif_buf);
563
+  g_free(xmp_string);
564
+  g_free(out_buf);
565
+
566
+  return ret;
567
+}
568
+
569
+int levels(dt_imageio_module_data_t *data)
570
+{
571
+  return IMAGEIO_RGB | IMAGEIO_FLOAT;
572
+}
573
+
574
+int flags(dt_imageio_module_data_t *data)
575
+{
576
+  /*
577
+   * As of exiv2 0.27.5 there is no write support for the JXL BMFF format,
578
+   * so we do not return the XMP supported flag currently.
579
+   * Once exiv2 write support is there, the flag can be returned, and the
580
+   * direct XMP embedding workaround using JxlEncoderAddBox("xml ") above
581
+   * can be removed.
582
+   */
583
+  return 0; /* FORMAT_FLAGS_SUPPORT_XMP; */
584
+}
585
+
586
+
587
+size_t params_size(dt_imageio_module_format_t *self)
588
+{
589
+  return sizeof(dt_imageio_jxl_t);
590
+}
591
+
592
+void *get_params(dt_imageio_module_format_t *self)
593
+{
594
+  dt_imageio_jxl_t *d = g_malloc0(sizeof(dt_imageio_jxl_t));
595
+
596
+  if(!d) return NULL;
597
+
598
+  d->bpp = dt_conf_get_int("plugins/imageio/format/jxl/bpp");
599
+
600
+  d->quality = dt_conf_get_int("plugins/imageio/format/jxl/quality");
601
+
602
+  d->original = dt_conf_get_bool("plugins/imageio/format/jxl/original") & 1;
603
+
604
+  d->effort = dt_conf_get_int("plugins/imageio/format/jxl/effort");
605
+
606
+  d->tier = dt_conf_get_int("plugins/imageio/format/jxl/tier");
607
+
608
+  return d;
609
+}
610
+
611
+void free_params(dt_imageio_module_format_t *self, dt_imageio_module_data_t *params)
612
+{
613
+  g_free(params);
614
+}
615
+
616
+int set_params(dt_imageio_module_format_t *self, const void *params, const int size)
617
+{
618
+  if(size != self->params_size(self)) return 1;
619
+
620
+  const dt_imageio_jxl_t *d = (dt_imageio_jxl_t *)params;
621
+  dt_imageio_jxl_gui_data_t *g = (dt_imageio_jxl_gui_data_t *)self->gui_data;
622
+
623
+  int bpp = d->bpp;
624
+  if(bpp < 0)
625
+    bpp = 0;
626
+  else if(bpp > 5)
627
+    bpp = 5;
628
+  dt_bauhaus_combobox_set(g->bpp, bpp);
629
+
630
+  int quality = d->quality;
631
+  if(quality < 0)
632
+    quality = 0;
633
+  else if(quality > 100)
634
+    quality = 100;
635
+  dt_bauhaus_slider_set(g->quality, quality);
636
+
637
+  int original = d->original;
638
+  dt_bauhaus_combobox_set(g->original, original & 1);
639
+
640
+  int effort = d->effort;
641
+  if(effort < 1)
642
+    effort = 1;
643
+  else if(effort > 9)
644
+    effort = 9;
645
+  dt_bauhaus_slider_set(g->effort, effort);
646
+
647
+  int tier = d->tier;
648
+  if(tier < 0)
649
+    tier = 0;
650
+  else if(tier > 4)
651
+    tier = 4;
652
+  dt_bauhaus_slider_set(g->tier, tier);
653
+
654
+  return 0;
655
+}
656
+
657
+
658
+const char *name()
659
+{
660
+  return _("JPEG XL");
661
+}
662
+
663
+static void bpp_changed(GtkWidget *bpp, dt_imageio_module_format_t *self)
664
+{
665
+  const int bpp_enum = dt_bauhaus_combobox_get(bpp);
666
+  dt_conf_set_int("plugins/imageio/format/jxl/bpp", bpp_enum);
667
+
668
+  dt_imageio_jxl_gui_data_t *g = (dt_imageio_jxl_gui_data_t *)self->gui_data;
669
+  const int quality_val = (int)dt_bauhaus_slider_get(g->quality);
670
+
671
+  if(bpp_enum < 4 && quality_val == 100)
672
+  {
673
+    dt_bauhaus_combobox_set(g->original, 1);
674
+    gtk_widget_set_sensitive(g->original, FALSE);
675
+  }
676
+  else
677
+    gtk_widget_set_sensitive(g->original, TRUE);
678
+}
679
+
680
+static void quality_changed(GtkWidget *quality, dt_imageio_module_format_t *self)
681
+{
682
+  const int quality_val = (int)dt_bauhaus_slider_get(quality);
683
+  dt_conf_set_int("plugins/imageio/format/jxl/quality", quality_val);
684
+
685
+  dt_imageio_jxl_gui_data_t *g = (dt_imageio_jxl_gui_data_t *)self->gui_data;
686
+  const int bpp_enum = dt_bauhaus_combobox_get(g->bpp);
687
+
688
+  if(bpp_enum < 4 && quality_val == 100)
689
+  {
690
+    dt_bauhaus_combobox_set(g->original, 1);
691
+    gtk_widget_set_sensitive(g->original, FALSE);
692
+  }
693
+  else
694
+    gtk_widget_set_sensitive(g->original, TRUE);
695
+}
696
+
697
+static void original_changed(GtkWidget *original, dt_imageio_module_format_t *self)
698
+{
699
+  dt_conf_set_bool("plugins/imageio/format/jxl/original", dt_bauhaus_combobox_get(original));
700
+}
701
+
702
+static void effort_changed(GtkWidget *effort, dt_imageio_module_format_t *self)
703
+{
704
+  dt_conf_set_int("plugins/imageio/format/jxl/effort", (int)dt_bauhaus_slider_get(effort));
705
+}
706
+
707
+static void tier_changed(GtkWidget *tier, dt_imageio_module_format_t *self)
708
+{
709
+  dt_conf_set_int("plugins/imageio/format/jxl/tier", (int)dt_bauhaus_slider_get(tier));
710
+}
711
+
712
+void gui_init(dt_imageio_module_format_t *self)
713
+{
714
+  dt_imageio_jxl_gui_data_t *gui = g_malloc0(sizeof(dt_imageio_jxl_gui_data_t));
715
+  if(!gui) return;
716
+  self->gui_data = gui;
717
+
718
+  GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
719
+  self->widget = box;
720
+
721
+  // bits per sample combobox
722
+  GtkWidget *bpp = dt_bauhaus_combobox_new(NULL);
723
+  dt_bauhaus_combobox_add(bpp, _("8 bit"));
724
+  dt_bauhaus_combobox_add(bpp, _("10 bit"));
725
+  dt_bauhaus_combobox_add(bpp, _("12 bit"));
726
+  dt_bauhaus_combobox_add(bpp, _("16 bit"));
727
+  dt_bauhaus_combobox_add(bpp, _("16 bit (half)"));
728
+  dt_bauhaus_combobox_add(bpp, _("32 bit (float)"));
729
+  const int bpp_enum = dt_conf_get_int("plugins/imageio/format/jxl/bpp");
730
+  dt_bauhaus_combobox_set(bpp, bpp_enum);
731
+  dt_bauhaus_widget_set_label(bpp, NULL, N_("bit depth"));
732
+  g_signal_connect(G_OBJECT(bpp), "value-changed", G_CALLBACK(bpp_changed), self);
733
+  gtk_box_pack_start(GTK_BOX(box), bpp, TRUE, TRUE, 0);
734
+  gui->bpp = bpp;
735
+
736
+  // quality slider
737
+  GtkWidget *quality
738
+      = dt_bauhaus_slider_new_with_range(NULL, dt_confgen_get_int("plugins/imageio/format/jxl/quality", DT_MIN),
739
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/quality", DT_MAX), 1,
740
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/quality", DT_DEFAULT), 0);
741
+  const int quality_val = dt_conf_get_int("plugins/imageio/format/jxl/quality");
742
+  dt_bauhaus_slider_set(quality, quality_val);
743
+  dt_bauhaus_widget_set_label(quality, NULL, _("quality"));
744
+  gtk_widget_set_tooltip_text(quality, _("the quality of the output image\n0-29 = very lossy\n30-99 = JPEG "
745
+                                         "quality comparable\n100 = lossless (integer bith depth only)"));
746
+  g_signal_connect(G_OBJECT(quality), "value-changed", G_CALLBACK(quality_changed), self);
747
+  gtk_box_pack_start(GTK_BOX(box), quality, TRUE, TRUE, 0);
748
+  gui->quality = quality;
749
+
750
+  // encoding color profile combobox
751
+  GtkWidget *original = dt_bauhaus_combobox_new(NULL);
752
+  dt_bauhaus_combobox_add(original, _("internal"));
753
+  dt_bauhaus_combobox_add(original, _("original"));
754
+  dt_bauhaus_combobox_set_default(original,
755
+                                  dt_confgen_get_bool("plugins/imageio/format/jxl/original", DT_DEFAULT) & 1);
756
+  if(bpp_enum < 4 && quality_val == 100)
757
+  {
758
+    dt_bauhaus_combobox_set(original, 1);
759
+    gtk_widget_set_sensitive(original, FALSE);
760
+  }
761
+  else
762
+    dt_bauhaus_combobox_set(original, dt_conf_get_bool("plugins/imageio/format/jxl/original") & 1);
763
+  dt_bauhaus_widget_set_label(original, NULL, N_("encoding color profile"));
764
+  g_signal_connect(G_OBJECT(original), "value-changed", G_CALLBACK(original_changed), self);
765
+  gtk_box_pack_start(GTK_BOX(box), original, TRUE, TRUE, 0);
766
+  gui->original = original;
767
+
768
+  // encoding effort slider
769
+  GtkWidget *effort
770
+      = dt_bauhaus_slider_new_with_range(NULL, dt_confgen_get_int("plugins/imageio/format/jxl/effort", DT_MIN),
771
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/effort", DT_MAX), 1,
772
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/effort", DT_DEFAULT), 0);
773
+  dt_bauhaus_slider_set(effort, dt_conf_get_int("plugins/imageio/format/jxl/effort"));
774
+  dt_bauhaus_widget_set_label(effort, NULL, _("encoding effort"));
775
+  gtk_widget_set_tooltip_text(effort, _("the effort used to encode the image, higher efforts will have "
776
+                                        "better results at the expense of longer encode times"));
777
+  g_signal_connect(G_OBJECT(effort), "value-changed", G_CALLBACK(effort_changed), self);
778
+  gtk_box_pack_start(GTK_BOX(box), effort, TRUE, TRUE, 0);
779
+  gui->effort = effort;
780
+
781
+  // decoding speed (tier) slider
782
+  GtkWidget *tier
783
+      = dt_bauhaus_slider_new_with_range(NULL, dt_confgen_get_int("plugins/imageio/format/jxl/tier", DT_MIN),
784
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/tier", DT_MAX), 1,
785
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/tier", DT_DEFAULT), 0);
786
+  dt_bauhaus_slider_set(tier, dt_conf_get_int("plugins/imageio/format/jxl/tier"));
787
+  dt_bauhaus_widget_set_label(tier, NULL, _("decoding speed"));
788
+  gtk_widget_set_tooltip_text(tier, _("the preffered decoding speed with some sacrifice of quality"));
789
+  g_signal_connect(G_OBJECT(tier), "value-changed", G_CALLBACK(tier_changed), self);
790
+  gtk_box_pack_start(GTK_BOX(box), tier, TRUE, TRUE, 0);
791
+  gui->tier = tier;
792
+}
793
+
794
+void gui_cleanup(dt_imageio_module_format_t *self)
795
+{
796
+  g_free(self->gui_data);
797
+}
798
+
799
+void gui_reset(dt_imageio_module_format_t *self)
800
+{
801
+  dt_imageio_jxl_gui_data_t *gui = (dt_imageio_jxl_gui_data_t *)self->gui_data;
802
+
803
+  const int bpp = dt_confgen_get_int("plugins/imageio/format/jxl/bpp", DT_DEFAULT);
804
+  dt_bauhaus_combobox_set(gui->bpp, bpp);
805
+
806
+  const int quality = dt_confgen_get_int("plugins/imageio/format/jxl/quality", DT_DEFAULT);
807
+  dt_bauhaus_slider_set(gui->quality, quality);
808
+
809
+  const int original = dt_confgen_get_bool("plugins/imageio/format/jxl/original", DT_DEFAULT);
810
+  dt_bauhaus_combobox_set(gui->original, original & 1);
811
+
812
+  const int effort = dt_confgen_get_int("plugins/imageio/format/jxl/effort", DT_DEFAULT);
813
+  dt_bauhaus_slider_set(gui->effort, effort);
814
+
815
+  const int tier = dt_confgen_get_int("plugins/imageio/format/jxl/tier", DT_DEFAULT);
816
+  dt_bauhaus_slider_set(gui->tier, tier);
817
+}
818
+
819
+// clang-format off
820
+// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
821
+// vim: shiftwidth=2 expandtab tabstop=2 cindent
822
+// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
823
+// clang-format on
824
--- src/imageio/format/tiff.c.orig	2022-02-09 10:21:58 UTC
825
+++ src/imageio/format/tiff.c
826
@@ -823,6 +823,8 @@ void gui_init(dt_imageio_module_format_t *self)
827
   dt_bauhaus_combobox_add(gui->compress, _("uncompressed"));
828
   dt_bauhaus_combobox_add(gui->compress, _("deflate"));
829
   dt_bauhaus_combobox_add(gui->compress, _("deflate with predictor"));
830
+  dt_bauhaus_combobox_set_default(gui->compress,
831
+                                  dt_confgen_get_int("plugins/imageio/format/tiff/compress", DT_DEFAULT));
832
   dt_bauhaus_combobox_set(gui->compress, compress);
833
   gtk_box_pack_start(GTK_BOX(self->widget), gui->compress, TRUE, TRUE, 0);
834
 
835
@@ -862,6 +864,7 @@ void gui_reset(dt_imageio_module_format_t *self)
836
 {
837
   dt_imageio_tiff_gui_t *gui = (dt_imageio_tiff_gui_t *)self->gui_data;
838
   dt_bauhaus_combobox_set(gui->bpp, 0); //8bpp
839
+  dt_bauhaus_combobox_set(gui->compress, dt_confgen_get_int("plugins/imageio/format/tiff/compress", DT_DEFAULT));
840
   dt_bauhaus_slider_set(gui->compresslevel, dt_confgen_get_int("plugins/imageio/format/tiff/compresslevel", DT_DEFAULT));
841
   dt_bauhaus_combobox_set(gui->shortfiles, dt_confgen_get_int("plugins/imageio/format/tiff/shortfile", DT_DEFAULT));
842
 }
(-)b/graphics/darktable/pkg-plist (-1 / +1 lines)
Lines 11-16 lib/darktable/plugins/imageio/format/libcopy.so Link Here
11
%%OPENEXR%%lib/darktable/plugins/imageio/format/libexr.so
11
%%OPENEXR%%lib/darktable/plugins/imageio/format/libexr.so
12
%%OPENJPEG%%lib/darktable/plugins/imageio/format/libj2k.so
12
%%OPENJPEG%%lib/darktable/plugins/imageio/format/libj2k.so
13
lib/darktable/plugins/imageio/format/libjpeg.so
13
lib/darktable/plugins/imageio/format/libjpeg.so
14
%%JXL%%lib/darktable/plugins/imageio/format/libjpegxl.so
14
lib/darktable/plugins/imageio/format/libpdf.so
15
lib/darktable/plugins/imageio/format/libpdf.so
15
lib/darktable/plugins/imageio/format/libpfm.so
16
lib/darktable/plugins/imageio/format/libpfm.so
16
lib/darktable/plugins/imageio/format/libpng.so
17
lib/darktable/plugins/imageio/format/libpng.so
17
--
18
graphics/darktable40/Makefile        |   9 +-
18
graphics/darktable40/Makefile        |   9 +-
19
graphics/darktable40/files/patch-jxl | 842 +++++++++++++++++++++++++++
19
graphics/darktable40/files/patch-jxl | 842 +++++++++++++++++++++++++++
20
graphics/darktable40/pkg-plist       |   1 +
20
graphics/darktable40/pkg-plist       |   1 +
21
3 files changed, 849 insertions(+), 3 deletions(-)
21
3 files changed, 849 insertions(+), 3 deletions(-)
22
create mode 100644 graphics/darktable40/files/patch-jxl
22
create mode 100644 graphics/darktable40/files/patch-jxl
(-)b/graphics/darktable40/Makefile (-3 / +6 lines)
Lines 1-6 Link Here
1
PORTNAME=	darktable
1
PORTNAME=	darktable
2
PORTVERSION=	4.0.0
2
PORTVERSION=	4.0.0
3
PORTREVISION=	1
3
PORTREVISION=	2
4
CATEGORIES=	graphics
4
CATEGORIES=	graphics
5
MASTER_SITES=	https://github.com/darktable-org/${PORTNAME}/releases/download/release-${PORTVERSION:C/\.rc/rc/}/
5
MASTER_SITES=	https://github.com/darktable-org/${PORTNAME}/releases/download/release-${PORTVERSION:C/\.rc/rc/}/
6
PKGNAMESUFFIX=	40
6
PKGNAMESUFFIX=	40
Lines 54-62 LDFLAGS+= -L${LOCALBASE}/lib Link Here
54
54
55
WRKSRC=		${WRKDIR}/${PORTNAME}-${PORTVERSION:C/\.rc/~rc/}
55
WRKSRC=		${WRKDIR}/${PORTNAME}-${PORTVERSION:C/\.rc/~rc/}
56
56
57
OPTIONS_DEFINE=		AVIF DOCS GEO GPHOTO GMIC GRAPHMAGICK HEIF LIBSECRET LTO \
57
OPTIONS_DEFINE=		AVIF DOCS GEO GPHOTO GMIC GRAPHMAGICK HEIF JXL LIBSECRET LTO \
58
			LUA NLS OPENEXR OPENJPEG PRINT TOOLS WEBP
58
			LUA NLS OPENEXR OPENJPEG PRINT TOOLS WEBP
59
OPTIONS_DEFAULT=	AVIF GEO GPHOTO HEIF LTO LUA OPENEXR OPENJPEG PRINT WEBP
59
OPTIONS_DEFAULT=	AVIF GEO GPHOTO HEIF JXL LTO LUA OPENEXR OPENJPEG PRINT WEBP
60
OPTIONS_SUB=		yes
60
OPTIONS_SUB=		yes
61
61
62
GEO_DESC=		Support geotagging
62
GEO_DESC=		Support geotagging
Lines 81-86 AVIF_CMAKE_BOOL= USE_AVIF Link Here
81
HEIF_LIB_DEPENDS=	libheif.so:graphics/libheif
81
HEIF_LIB_DEPENDS=	libheif.so:graphics/libheif
82
HEIF_CMAKE_BOOL=	USE_HEIF
82
HEIF_CMAKE_BOOL=	USE_HEIF
83
83
84
JXL_LIB_DEPENDS=	libjxl.so:graphics/libjxl
85
JXL_CMAKE_BOOL=		USE_JXL
86
84
LIBSECRET_LIB_DEPENDS=	libsecret-1.so:security/libsecret
87
LIBSECRET_LIB_DEPENDS=	libsecret-1.so:security/libsecret
85
LIBSECRET_CMAKE_BOOL=	USE_LIBSECRET
88
LIBSECRET_CMAKE_BOOL=	USE_LIBSECRET
86
89
(-)b/graphics/darktable40/files/patch-jxl (+842 lines)
Added Link Here
1
https://github.com/darktable-org/darktable/pull/10044 (475a8bf version)
2
3
--- DefineOptions.cmake.orig	2022-02-09 10:21:58 UTC
4
+++ DefineOptions.cmake
5
@@ -18,6 +18,7 @@ option(USE_OPENJPEG "Enable JPEG 2000 support" ON)
6
 option(BINARY_PACKAGE_BUILD "Sets march optimization to generic" OFF)
7
 option(USE_XMLLINT "Run xmllint to test if darktableconfig.xml is valid" ON)
8
 option(USE_OPENJPEG "Enable JPEG 2000 support" ON)
9
+option(USE_JXL "Enable JPEG XL export support" ON)
10
 option(USE_WEBP "Enable WebP export support" ON)
11
 option(USE_AVIF "Enable AVIF support" ON)
12
 option(USE_HEIF "Enable HEIF/HEIC support" ON)
13
--- cmake/modules/FindJXL.cmake.orig	2022-08-31 08:07:07 UTC
14
+++ cmake/modules/FindJXL.cmake
15
@@ -0,0 +1,38 @@
16
+# Find libjxl
17
+# Will define:
18
+# - JXL_FOUND
19
+# - JXL_INCLUDE_DIRS directory to include for libjxl headers
20
+# - JXL_LIBRARIES libraries to link to
21
+
22
+include(LibFindMacros)
23
+
24
+# Use pkg-config to get hints about paths
25
+# libfind_pkg_check_modules(JXL_PKGCONF libjxl) <- this isn't working?
26
+pkg_check_modules(JXL_PKGCONF QUIET libjxl)
27
+
28
+find_path(JXL_INCLUDE_DIR
29
+  NAMES jxl/encode.h
30
+  HINTS ${JXL_PKGCONF_INCLUDE_DIRS}
31
+)
32
+
33
+find_library(JXL_LIBRARY
34
+  NAMES jxl
35
+  HINTS ${JXL_PKGCONF_LIBRARY_DIRS}
36
+)
37
+
38
+find_library(JXL_THREADS_LIBRARY
39
+  NAMES jxl_threads
40
+  HINTS ${JXL_PKGCONF_LIBRARY_DIRS}
41
+)
42
+
43
+include(FindPackageHandleStandardArgs)
44
+find_package_handle_standard_args(JXL DEFAULT_MSG JXL_LIBRARY JXL_INCLUDE_DIR)
45
+
46
+if(JXL_PKGCONF_VERSION VERSION_LESS JXL_FIND_VERSION)
47
+  set(JXL_FOUND false)
48
+endif()
49
+
50
+if(JXL_FOUND)
51
+  set(JXL_LIBRARIES ${JXL_LIBRARY} ${JXL_THREADS_LIBRARY})
52
+  set(JXL_INCLUDE_DIRS ${JXL_INCLUDE_DIR})
53
+endif(JXL_FOUND)
54
--- cmake/windows-macros.cmake.orig	2022-02-09 10:21:58 UTC
55
+++ cmake/windows-macros.cmake
56
@@ -157,6 +157,15 @@ if (WIN32 AND NOT BUILD_MSYS2_INSTALL)
57
     )
58
     list(APPEND CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ${TMP_SYSTEM_RUNTIME_LIBS})
59
   endif()
60
+  
61
+  if(JXL_FOUND)
62
+    file(GLOB TMP_SYSTEM_RUNTIME_LIBS
63
+      #LIBJXL
64
+      ${MINGW_PATH}/libjxl.dll
65
+      ${MINGW_PATH}/libjxl_threads.dll
66
+    )
67
+    list(APPEND CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS ${TMP_SYSTEM_RUNTIME_LIBS})
68
+  endif()
69
 
70
   if(WebP_FOUND)
71
     file(GLOB TMP_SYSTEM_RUNTIME_LIBS
72
--- data/darktableconfig.xml.in.orig	2022-02-09 10:21:58 UTC
73
+++ data/darktableconfig.xml.in
74
@@ -2407,6 +2407,41 @@
75
     <longdescription/>
76
   </dtconfig>
77
   <dtconfig>
78
+    <name>plugins/imageio/format/jxl/bpp</name>
79
+    <type min="0" max="5">int</type>
80
+    <default>0</default>
81
+    <shortdescription/>
82
+    <longdescription/>
83
+  </dtconfig>
84
+  <dtconfig>
85
+    <name>plugins/imageio/format/jxl/quality</name>
86
+    <type min="0" max="100">int</type>
87
+    <default>95</default>
88
+    <shortdescription/>
89
+    <longdescription/>
90
+  </dtconfig>
91
+  <dtconfig>
92
+    <name>plugins/imageio/format/jxl/original</name>
93
+    <type>bool</type>
94
+    <default>true</default>
95
+    <shortdescription/>
96
+    <longdescription/>
97
+  </dtconfig>
98
+  <dtconfig>
99
+    <name>plugins/imageio/format/jxl/effort</name>
100
+    <type min="1" max="9">int</type>
101
+    <default>7</default>
102
+    <shortdescription/>
103
+    <longdescription/>
104
+  </dtconfig>
105
+  <dtconfig>
106
+    <name>plugins/imageio/format/jxl/tier</name>
107
+    <type min="0" max="4">int</type>
108
+    <default>0</default>
109
+    <shortdescription/>
110
+    <longdescription/>
111
+  </dtconfig>
112
+  <dtconfig>
113
     <name>plugins/imageio/format/webp/comp_type</name>
114
     <type min="0" max="1">int</type>
115
     <default>0</default>
116
--- src/CMakeLists.txt.orig	2022-02-09 10:21:58 UTC
117
+++ src/CMakeLists.txt
118
@@ -340,6 +340,15 @@ endif(USE_OPENEXR)
119
   endif(OpenEXR_FOUND)
120
 endif(USE_OPENEXR)
121
 
122
+if(USE_JXL)
123
+  find_package(JXL 0.7.0)
124
+  if(JXL_FOUND)
125
+    include_directories(SYSTEM ${JXL_INCLUDE_DIRS})
126
+    list(APPEND LIBS ${JXL_LIBRARIES})
127
+    add_definitions(${JXL_DEFINITIONS})
128
+  endif(JXL_FOUND)
129
+endif(USE_JXL)
130
+
131
 if(USE_WEBP)
132
   find_package(WebP 0.3.0)
133
   if(WebP_FOUND)
134
--- src/imageio/format/CMakeLists.txt.orig	2022-02-09 10:21:58 UTC
135
+++ src/imageio/format/CMakeLists.txt
136
@@ -17,6 +17,16 @@ add_library(tiff MODULE "tiff.c")
137
 add_library(pfm MODULE "pfm.c")
138
 add_library(tiff MODULE "tiff.c")
139
 
140
+if(JXL_FOUND)
141
+  list(APPEND MODULES "jxl_format")
142
+  add_library(jxl_format MODULE "jxl.c")
143
+  # At least on Windows, have to link to libjxl explicitly here as symbols
144
+  # from libdarktable get stripped (until read support is added). As a
145
+  # consequence, have to also change the module name to not be libjxl.
146
+  target_link_libraries(jxl_format PUBLIC ${JXL_LIBRARIES})
147
+  set_target_properties(jxl_format PROPERTIES OUTPUT_NAME jpegxl)
148
+endif(JXL_FOUND)
149
+
150
 if(WebP_FOUND)
151
   list(APPEND MODULES "webp")
152
   add_library(webp MODULE "webp.c")
153
--- src/imageio/format/jxl.c.orig	2022-08-31 08:07:07 UTC
154
+++ src/imageio/format/jxl.c
155
@@ -0,0 +1,668 @@
156
+/*
157
+    This file is part of darktable,
158
+    Copyright (C) 2021-2022 darktable developers.
159
+
160
+    darktable is free software: you can redistribute it and/or modify
161
+    it under the terms of the GNU General Public License as published by
162
+    the Free Software Foundation, either version 3 of the License, or
163
+    (at your option) any later version.
164
+
165
+    darktable is distributed in the hope that it will be useful,
166
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
167
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
168
+    GNU General Public License for more details.
169
+
170
+    You should have received a copy of the GNU General Public License
171
+    along with darktable.  If not, see <http://www.gnu.org/licenses/>.
172
+*/
173
+
174
+#include "bauhaus/bauhaus.h"
175
+#include "common/colorspaces.h"
176
+#include "common/darktable.h"
177
+#include "common/exif.h"
178
+#include "common/imageio.h"
179
+#include "control/conf.h"
180
+#include "imageio/format/imageio_format_api.h"
181
+
182
+#include "jxl/encode.h"
183
+#include "jxl/resizable_parallel_runner.h"
184
+
185
+DT_MODULE(1)
186
+
187
+typedef struct dt_imageio_jxl_t
188
+{
189
+  dt_imageio_module_data_t global;
190
+  int bpp;
191
+  int quality;
192
+  int original;
193
+  int effort;
194
+  int tier;
195
+} dt_imageio_jxl_t;
196
+
197
+typedef struct dt_imageio_jxl_gui_data_t
198
+{
199
+  // Int (0:8b, 1:10b, 2:12b, 3:16b, 4:half, 5:float)
200
+  GtkWidget *bpp;
201
+  // Int (0-100): the quality of the image, roughly corresponding to JPEG quality (100 is lossless)
202
+  GtkWidget *quality;
203
+  // Bool: whether to encode using the original color profile or the internal XYB one
204
+  GtkWidget *original;
205
+  // Int (1-9): effort with which to encode output; higher is slower (default is 7)
206
+  GtkWidget *effort;
207
+  // Int (0-4): higher value favors decoding speed vs quality (default is 0)
208
+  GtkWidget *tier;
209
+} dt_imageio_jxl_gui_data_t;
210
+
211
+
212
+void init(dt_imageio_module_format_t *self)
213
+{
214
+#ifdef USE_LUA
215
+  dt_lua_register_module_member(darktable.lua_state.state, self, dt_imageio_jxl_t, bpp, int);
216
+
217
+  dt_lua_register_module_member(darktable.lua_state.state, self, dt_imageio_jxl_t, quality, int);
218
+
219
+  dt_lua_register_module_member(darktable.lua_state.state, self, dt_imageio_jxl_t, original, int);
220
+
221
+  dt_lua_register_module_member(darktable.lua_state.state, self, dt_imageio_jxl_t, effort, int);
222
+
223
+  dt_lua_register_module_member(darktable.lua_state.state, self, dt_imageio_jxl_t, tier, int);
224
+#endif
225
+}
226
+
227
+void cleanup(dt_imageio_module_format_t *self)
228
+{
229
+}
230
+
231
+
232
+const char *mime(dt_imageio_module_data_t *data)
233
+{
234
+  return "image/jxl";
235
+}
236
+
237
+const char *extension(dt_imageio_module_data_t *data)
238
+{
239
+  return "jxl";
240
+}
241
+
242
+int dimension(struct dt_imageio_module_format_t *self, struct dt_imageio_module_data_t *data, uint32_t *width,
243
+              uint32_t *height)
244
+{
245
+  // The maximum dimensions supported by jxl images
246
+  *width = 1073741823U;
247
+  *height = 1073741823U;
248
+  return 1;
249
+}
250
+
251
+int bpp(dt_imageio_module_data_t *data)
252
+{
253
+  return 32; /* always request float */
254
+}
255
+
256
+
257
+int write_image(struct dt_imageio_module_data_t *data, const char *filename, const void *in_tmp,
258
+                dt_colorspaces_color_profile_type_t over_type, const char *over_filename, void *exif, int exif_len,
259
+                int imgid, int num, int total, struct dt_dev_pixelpipe_t *pipe, const gboolean export_masks)
260
+{
261
+  // Return error code by default
262
+  int ret = 1;
263
+
264
+  float *pixels = NULL;
265
+  uint8_t *out_buf = NULL;
266
+  FILE *out_file = NULL;
267
+  uint8_t *icc_buf = NULL;
268
+  uint8_t *exif_buf = NULL;
269
+  char *xmp_string = NULL;
270
+
271
+#define LIBJXL_ASSERT(code)                                                                                       \
272
+  {                                                                                                               \
273
+    if((JxlEncoderStatus)code != JXL_ENC_SUCCESS)                                                                 \
274
+    {                                                                                                             \
275
+      JxlEncoderError err = JxlEncoderGetError(encoder);                                                          \
276
+      dt_print(DT_DEBUG_IMAGEIO, "[jxl] libjxl call failed with err %d (src/imageio/format/jxl.c#L%d)\n", err,    \
277
+               __LINE__);                                                                                         \
278
+      goto end;                                                                                                   \
279
+    }                                                                                                             \
280
+  }
281
+
282
+#define JXL_FAIL(msg, ...)                                                                                        \
283
+  {                                                                                                               \
284
+    dt_print(DT_DEBUG_IMAGEIO, "[jxl] " msg "\n", ##__VA_ARGS__);                                                 \
285
+    goto end;                                                                                                     \
286
+  }
287
+
288
+  const dt_imageio_jxl_t *params = (dt_imageio_jxl_t *)data;
289
+  const uint32_t width = (uint32_t)params->global.width;
290
+  const uint32_t height = (uint32_t)params->global.height;
291
+
292
+  JxlEncoder *encoder = JxlEncoderCreate(NULL);
293
+
294
+  const uint32_t num_threads = JxlResizableParallelRunnerSuggestThreads(width, height);
295
+  void *runner = JxlResizableParallelRunnerCreate(NULL);
296
+  if(!runner) JXL_FAIL("could not create resizable parallel runner");
297
+  JxlResizableParallelRunnerSetThreads(runner, num_threads);
298
+  LIBJXL_ASSERT(JxlEncoderSetParallelRunner(encoder, JxlResizableParallelRunner, runner));
299
+
300
+  // Automatically freed when we destroy the encoder
301
+  JxlEncoderFrameSettings *frame_settings = JxlEncoderFrameSettingsCreate(encoder, NULL);
302
+
303
+  // Set encoder basic info
304
+  JxlBasicInfo basic_info;
305
+  JxlEncoderInitBasicInfo(&basic_info);
306
+  basic_info.xsize = width;
307
+  basic_info.ysize = height;
308
+  switch(params->bpp)
309
+  {
310
+    case 0:
311
+      basic_info.bits_per_sample = 8;
312
+      basic_info.exponent_bits_per_sample = 0;
313
+      break;
314
+    case 1:
315
+      basic_info.bits_per_sample = 10;
316
+      basic_info.exponent_bits_per_sample = 0;
317
+      break;
318
+    case 2:
319
+      basic_info.bits_per_sample = 12;
320
+      basic_info.exponent_bits_per_sample = 0;
321
+      break;
322
+    case 3:
323
+      basic_info.bits_per_sample = 16;
324
+      basic_info.exponent_bits_per_sample = 0;
325
+      break;
326
+    case 4:
327
+      basic_info.bits_per_sample = 16;
328
+      basic_info.exponent_bits_per_sample = 5;
329
+      break;
330
+    default:
331
+      basic_info.bits_per_sample = 32;
332
+      basic_info.exponent_bits_per_sample = 8;
333
+      break;
334
+  }
335
+  // Lossless only makes sense for integer modes
336
+  if(basic_info.exponent_bits_per_sample == 0 && params->quality == 100)
337
+  {
338
+    // Must preserve original profile for lossless mode
339
+    basic_info.uses_original_profile = JXL_TRUE;
340
+    LIBJXL_ASSERT(JxlEncoderSetFrameDistance(frame_settings, 0.0f));
341
+    LIBJXL_ASSERT(JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE));
342
+  }
343
+  else
344
+  {
345
+    basic_info.uses_original_profile = params->original == FALSE ? JXL_FALSE : JXL_TRUE;
346
+    float distance = params->quality >= 30 ? 0.1f + (100 - params->quality) * 0.09f
347
+                                           : 6.4f + powf(2.5f, (30 - params->quality) / 5.0f) / 6.25f;
348
+    LIBJXL_ASSERT(JxlEncoderSetFrameDistance(frame_settings, distance));
349
+  }
350
+
351
+  LIBJXL_ASSERT(JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, params->effort));
352
+
353
+  LIBJXL_ASSERT(
354
+      JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_DECODING_SPEED, params->tier));
355
+
356
+  // Codestream level should be chosen automatically given the settings
357
+  LIBJXL_ASSERT(JxlEncoderSetBasicInfo(encoder, &basic_info));
358
+
359
+  // Determine and set the encoder color space
360
+  const dt_colorspaces_color_profile_t *output_profile
361
+      = dt_colorspaces_get_output_profile(imgid, over_type, over_filename);
362
+  const cmsHPROFILE out_profile = output_profile->profile;
363
+  // Previous call will give us a more accurate color profile type
364
+  // (not what the user requested in the export menu but what the image is actually using)
365
+  over_type = output_profile->type;
366
+
367
+  // If possible we want libjxl to save the color encoding in its own format, rather
368
+  // than as an ICC binary blob which is possible.
369
+  // If we are unable to find the required color encoding data for libjxl we will
370
+  // just fallback to providing an ICC blob (and hope we can at least do that!).
371
+  bool write_color_natively = true;
372
+
373
+  JxlColorEncoding color_encoding;
374
+  color_encoding.color_space = JXL_COLOR_SPACE_RGB;
375
+  // If not explicitly set in the export menu, use the intent of the actual output profile
376
+  if(pipe->icc_intent >= DT_INTENT_PERCEPTUAL && pipe->icc_intent < DT_INTENT_LAST)
377
+    color_encoding.rendering_intent = (JxlRenderingIntent)pipe->icc_intent;
378
+  else
379
+    color_encoding.rendering_intent = (JxlRenderingIntent)cmsGetHeaderRenderingIntent(out_profile);
380
+
381
+  // Attempt to find and set the known white point, primaries and transfer function.
382
+  // If we can't find any of these we fall back to an ICC binary blob.
383
+  switch(over_type)
384
+  {
385
+    case DT_COLORSPACE_SRGB:
386
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
387
+      color_encoding.primaries = JXL_PRIMARIES_SRGB;
388
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
389
+      break;
390
+    case DT_COLORSPACE_LIN_REC709:
391
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
392
+      color_encoding.primaries = JXL_PRIMARIES_SRGB;
393
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
394
+      break;
395
+    case DT_COLORSPACE_LIN_REC2020:
396
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
397
+      color_encoding.primaries = JXL_PRIMARIES_2100;
398
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
399
+      break;
400
+    // TODO: enable when JXL_PRIMARIES_XYZ are added to libjxl
401
+    //  case DT_COLORSPACE_XYZ:
402
+    //    color_encoding.white_point = JXL_WHITE_POINT_E;
403
+    //    color_encoding.primaries = JXL_PRIMARIES_XYZ;
404
+    //    color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
405
+    //    break;
406
+    case DT_COLORSPACE_REC709:
407
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
408
+      color_encoding.primaries = JXL_PRIMARIES_SRGB;
409
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_709;
410
+      break;
411
+    case DT_COLORSPACE_PQ_REC2020:
412
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
413
+      color_encoding.primaries = JXL_PRIMARIES_2100;
414
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_PQ;
415
+      break;
416
+    case DT_COLORSPACE_HLG_REC2020:
417
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
418
+      color_encoding.primaries = JXL_PRIMARIES_2100;
419
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_HLG;
420
+      break;
421
+    case DT_COLORSPACE_PQ_P3:
422
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
423
+      color_encoding.primaries = JXL_PRIMARIES_P3;
424
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_PQ;
425
+      break;
426
+    case DT_COLORSPACE_HLG_P3:
427
+      color_encoding.white_point = JXL_WHITE_POINT_D65;
428
+      color_encoding.primaries = JXL_PRIMARIES_P3;
429
+      color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_HLG;
430
+      break;
431
+    default:
432
+      write_color_natively = false;
433
+      break;
434
+  }
435
+
436
+  if(write_color_natively)
437
+  {
438
+    JxlEncoderSetColorEncoding(encoder, &color_encoding);
439
+  }
440
+  else
441
+  {
442
+    // If we didn't manage to write the color encoding natively we need to fallback to ICC
443
+    dt_print(DT_DEBUG_IMAGEIO, "[jxl] could not generate color encoding structure, falling back to ICC\n");
444
+
445
+    cmsUInt32Number icc_size = 0;
446
+    // First find the size of the ICC buffer
447
+    if(!cmsSaveProfileToMem(out_profile, NULL, &icc_size)) JXL_FAIL("error finding ICC data length");
448
+    if(icc_size > 0) icc_buf = g_malloc(icc_size);
449
+    if(!icc_buf) JXL_FAIL("could not allocate ICC buffer of size %u", icc_size);
450
+
451
+    // Fill the ICC buffer
452
+    if(!cmsSaveProfileToMem(out_profile, icc_buf, &icc_size)) JXL_FAIL("error writing ICC data");
453
+
454
+    LIBJXL_ASSERT(JxlEncoderSetICCProfile(encoder, icc_buf, icc_size));
455
+  }
456
+
457
+  // We assume that the user wants the JXL image in a BMFF container.
458
+  // JXL images can be stored without any container so they are smaller, but
459
+  // this removes the possibility of storing extra metadata like Exif and XMP.
460
+  LIBJXL_ASSERT(JxlEncoderUseBoxes(encoder));
461
+
462
+  JxlPixelFormat pixel_format = { 3, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0 };
463
+
464
+  // Fix pixel stride
465
+  const size_t pixels_size = width * height * 3 * sizeof(float);
466
+  pixels = g_malloc(pixels_size);
467
+  if(!pixels) JXL_FAIL("could not allocate output pixel buffer of size %zu", pixels_size);
468
+#ifdef _OPENMP
469
+#pragma omp parallel for simd default(none) dt_omp_firstprivate(in_tmp, pixels, width, height) schedule(simd      \
470
+                                                                                                        : static) \
471
+    collapse(2)
472
+#endif
473
+  for(uint32_t y = 0; y < height; ++y)
474
+  {
475
+    for(uint32_t x = 0; x < width; ++x)
476
+    {
477
+      const float *in_pixel = (const float *)in_tmp + 4 * ((y * width) + x);
478
+      float *out_pixel = pixels + 3 * ((y * width) + x);
479
+
480
+      out_pixel[0] = in_pixel[0];
481
+      out_pixel[1] = in_pixel[1];
482
+      out_pixel[2] = in_pixel[2];
483
+    }
484
+  }
485
+
486
+  LIBJXL_ASSERT(JxlEncoderAddImageFrame(frame_settings, &pixel_format, pixels, pixels_size));
487
+
488
+  /* TODO: workaround; remove when exiv2 implements JXL BMFF write support and use dt_exif_write_blob() after
489
+   * closing file instead */
490
+  if(exif && exif_len > 0)
491
+  {
492
+    // Prepend the 4 byte (zero) offset to the blob before writing
493
+    // (as required in the equivalent HEIF/JPEG XS Exif box specs)
494
+    exif_buf = g_malloc0(exif_len + 4);
495
+    if(!exif_buf) JXL_FAIL("could not allocate Exif buffer of size %zu", (size_t)(exif_len + 4));
496
+    memmove(exif_buf + 4, exif, exif_len);
497
+    // Exiv2 doesn't support Brotli compressed boxes yet
498
+    LIBJXL_ASSERT(JxlEncoderAddBox(encoder, "Exif", exif_buf, exif_len + 4, JXL_FALSE));
499
+  }
500
+
501
+  /* TODO: workaround; remove when exiv2 implements JXL BMFF write support and update flags() */
502
+  xmp_string = dt_exif_xmp_read_string(imgid);
503
+  size_t xmp_len;
504
+  if(xmp_string && (xmp_len = strlen(xmp_string)) > 0)
505
+  {
506
+    // Exiv2 doesn't support Brotli compressed boxes
507
+    LIBJXL_ASSERT(JxlEncoderAddBox(encoder, "xml ", (const uint8_t *)xmp_string, xmp_len, JXL_FALSE));
508
+  }
509
+
510
+  // No more image frames nor metadata boxes to add
511
+  JxlEncoderCloseInput(encoder);
512
+
513
+  // Write the image codestream to a buffer, starting with a chunk of 64 KiB.
514
+  // TODO: Can we better estimate what the optimal size of chunks is for this image?
515
+  size_t chunk_size = 1 << 16;
516
+  size_t out_len = chunk_size;
517
+  out_buf = g_malloc(out_len);
518
+  if(!out_buf) JXL_FAIL("could not allocate codestream buffer of size %zu", out_len);
519
+  uint8_t *out_cur = out_buf;
520
+  size_t out_avail = out_len;
521
+
522
+  JxlEncoderStatus out_status = JXL_ENC_NEED_MORE_OUTPUT;
523
+  while(out_status == JXL_ENC_NEED_MORE_OUTPUT)
524
+  {
525
+    out_status = JxlEncoderProcessOutput(encoder, &out_cur, &out_avail);
526
+
527
+    if(out_status == JXL_ENC_NEED_MORE_OUTPUT)
528
+    {
529
+      const size_t offset = out_cur - out_buf;
530
+      if(chunk_size < 1 << 20) chunk_size *= 2;
531
+      out_len += chunk_size;
532
+      out_buf = g_realloc(out_buf, out_len);
533
+      if(!out_buf)
534
+      {
535
+        JXL_FAIL("could not reallocate codestream buffer to size %zu", out_len);
536
+        goto end;
537
+      }
538
+      out_cur = out_buf + offset;
539
+      out_avail = out_len - offset;
540
+    }
541
+  }
542
+  LIBJXL_ASSERT(out_status);
543
+  // Update actual length of codestream written
544
+  out_len = out_cur - out_buf;
545
+
546
+  // Write codestream contents to file
547
+  out_file = g_fopen(filename, "wb");
548
+  if(!out_file) JXL_FAIL("could not open output file `%s'", filename);
549
+
550
+  if(fwrite(out_buf, sizeof(uint8_t), out_len, out_file) != out_len)
551
+    JXL_FAIL("could not write bytes to `%s'", filename);
552
+
553
+  // Finally, successful write: set to success code
554
+  ret = 0;
555
+
556
+end:
557
+  if(runner) JxlResizableParallelRunnerDestroy(runner);
558
+  if(encoder) JxlEncoderDestroy(encoder);
559
+  if(out_file) fclose(out_file);
560
+  g_free(pixels);
561
+  g_free(icc_buf);
562
+  g_free(exif_buf);
563
+  g_free(xmp_string);
564
+  g_free(out_buf);
565
+
566
+  return ret;
567
+}
568
+
569
+int levels(dt_imageio_module_data_t *data)
570
+{
571
+  return IMAGEIO_RGB | IMAGEIO_FLOAT;
572
+}
573
+
574
+int flags(dt_imageio_module_data_t *data)
575
+{
576
+  /*
577
+   * As of exiv2 0.27.5 there is no write support for the JXL BMFF format,
578
+   * so we do not return the XMP supported flag currently.
579
+   * Once exiv2 write support is there, the flag can be returned, and the
580
+   * direct XMP embedding workaround using JxlEncoderAddBox("xml ") above
581
+   * can be removed.
582
+   */
583
+  return 0; /* FORMAT_FLAGS_SUPPORT_XMP; */
584
+}
585
+
586
+
587
+size_t params_size(dt_imageio_module_format_t *self)
588
+{
589
+  return sizeof(dt_imageio_jxl_t);
590
+}
591
+
592
+void *get_params(dt_imageio_module_format_t *self)
593
+{
594
+  dt_imageio_jxl_t *d = g_malloc0(sizeof(dt_imageio_jxl_t));
595
+
596
+  if(!d) return NULL;
597
+
598
+  d->bpp = dt_conf_get_int("plugins/imageio/format/jxl/bpp");
599
+
600
+  d->quality = dt_conf_get_int("plugins/imageio/format/jxl/quality");
601
+
602
+  d->original = dt_conf_get_bool("plugins/imageio/format/jxl/original") & 1;
603
+
604
+  d->effort = dt_conf_get_int("plugins/imageio/format/jxl/effort");
605
+
606
+  d->tier = dt_conf_get_int("plugins/imageio/format/jxl/tier");
607
+
608
+  return d;
609
+}
610
+
611
+void free_params(dt_imageio_module_format_t *self, dt_imageio_module_data_t *params)
612
+{
613
+  g_free(params);
614
+}
615
+
616
+int set_params(dt_imageio_module_format_t *self, const void *params, const int size)
617
+{
618
+  if(size != self->params_size(self)) return 1;
619
+
620
+  const dt_imageio_jxl_t *d = (dt_imageio_jxl_t *)params;
621
+  dt_imageio_jxl_gui_data_t *g = (dt_imageio_jxl_gui_data_t *)self->gui_data;
622
+
623
+  int bpp = d->bpp;
624
+  if(bpp < 0)
625
+    bpp = 0;
626
+  else if(bpp > 5)
627
+    bpp = 5;
628
+  dt_bauhaus_combobox_set(g->bpp, bpp);
629
+
630
+  int quality = d->quality;
631
+  if(quality < 0)
632
+    quality = 0;
633
+  else if(quality > 100)
634
+    quality = 100;
635
+  dt_bauhaus_slider_set(g->quality, quality);
636
+
637
+  int original = d->original;
638
+  dt_bauhaus_combobox_set(g->original, original & 1);
639
+
640
+  int effort = d->effort;
641
+  if(effort < 1)
642
+    effort = 1;
643
+  else if(effort > 9)
644
+    effort = 9;
645
+  dt_bauhaus_slider_set(g->effort, effort);
646
+
647
+  int tier = d->tier;
648
+  if(tier < 0)
649
+    tier = 0;
650
+  else if(tier > 4)
651
+    tier = 4;
652
+  dt_bauhaus_slider_set(g->tier, tier);
653
+
654
+  return 0;
655
+}
656
+
657
+
658
+const char *name()
659
+{
660
+  return _("JPEG XL");
661
+}
662
+
663
+static void bpp_changed(GtkWidget *bpp, dt_imageio_module_format_t *self)
664
+{
665
+  const int bpp_enum = dt_bauhaus_combobox_get(bpp);
666
+  dt_conf_set_int("plugins/imageio/format/jxl/bpp", bpp_enum);
667
+
668
+  dt_imageio_jxl_gui_data_t *g = (dt_imageio_jxl_gui_data_t *)self->gui_data;
669
+  const int quality_val = (int)dt_bauhaus_slider_get(g->quality);
670
+
671
+  if(bpp_enum < 4 && quality_val == 100)
672
+  {
673
+    dt_bauhaus_combobox_set(g->original, 1);
674
+    gtk_widget_set_sensitive(g->original, FALSE);
675
+  }
676
+  else
677
+    gtk_widget_set_sensitive(g->original, TRUE);
678
+}
679
+
680
+static void quality_changed(GtkWidget *quality, dt_imageio_module_format_t *self)
681
+{
682
+  const int quality_val = (int)dt_bauhaus_slider_get(quality);
683
+  dt_conf_set_int("plugins/imageio/format/jxl/quality", quality_val);
684
+
685
+  dt_imageio_jxl_gui_data_t *g = (dt_imageio_jxl_gui_data_t *)self->gui_data;
686
+  const int bpp_enum = dt_bauhaus_combobox_get(g->bpp);
687
+
688
+  if(bpp_enum < 4 && quality_val == 100)
689
+  {
690
+    dt_bauhaus_combobox_set(g->original, 1);
691
+    gtk_widget_set_sensitive(g->original, FALSE);
692
+  }
693
+  else
694
+    gtk_widget_set_sensitive(g->original, TRUE);
695
+}
696
+
697
+static void original_changed(GtkWidget *original, dt_imageio_module_format_t *self)
698
+{
699
+  dt_conf_set_bool("plugins/imageio/format/jxl/original", dt_bauhaus_combobox_get(original));
700
+}
701
+
702
+static void effort_changed(GtkWidget *effort, dt_imageio_module_format_t *self)
703
+{
704
+  dt_conf_set_int("plugins/imageio/format/jxl/effort", (int)dt_bauhaus_slider_get(effort));
705
+}
706
+
707
+static void tier_changed(GtkWidget *tier, dt_imageio_module_format_t *self)
708
+{
709
+  dt_conf_set_int("plugins/imageio/format/jxl/tier", (int)dt_bauhaus_slider_get(tier));
710
+}
711
+
712
+void gui_init(dt_imageio_module_format_t *self)
713
+{
714
+  dt_imageio_jxl_gui_data_t *gui = g_malloc0(sizeof(dt_imageio_jxl_gui_data_t));
715
+  if(!gui) return;
716
+  self->gui_data = gui;
717
+
718
+  GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
719
+  self->widget = box;
720
+
721
+  // bits per sample combobox
722
+  GtkWidget *bpp = dt_bauhaus_combobox_new(NULL);
723
+  dt_bauhaus_combobox_add(bpp, _("8 bit"));
724
+  dt_bauhaus_combobox_add(bpp, _("10 bit"));
725
+  dt_bauhaus_combobox_add(bpp, _("12 bit"));
726
+  dt_bauhaus_combobox_add(bpp, _("16 bit"));
727
+  dt_bauhaus_combobox_add(bpp, _("16 bit (half)"));
728
+  dt_bauhaus_combobox_add(bpp, _("32 bit (float)"));
729
+  const int bpp_enum = dt_conf_get_int("plugins/imageio/format/jxl/bpp");
730
+  dt_bauhaus_combobox_set(bpp, bpp_enum);
731
+  dt_bauhaus_widget_set_label(bpp, NULL, N_("bit depth"));
732
+  g_signal_connect(G_OBJECT(bpp), "value-changed", G_CALLBACK(bpp_changed), self);
733
+  gtk_box_pack_start(GTK_BOX(box), bpp, TRUE, TRUE, 0);
734
+  gui->bpp = bpp;
735
+
736
+  // quality slider
737
+  GtkWidget *quality
738
+      = dt_bauhaus_slider_new_with_range(NULL, dt_confgen_get_int("plugins/imageio/format/jxl/quality", DT_MIN),
739
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/quality", DT_MAX), 1,
740
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/quality", DT_DEFAULT), 0);
741
+  const int quality_val = dt_conf_get_int("plugins/imageio/format/jxl/quality");
742
+  dt_bauhaus_slider_set(quality, quality_val);
743
+  dt_bauhaus_widget_set_label(quality, NULL, _("quality"));
744
+  gtk_widget_set_tooltip_text(quality, _("the quality of the output image\n0-29 = very lossy\n30-99 = JPEG "
745
+                                         "quality comparable\n100 = lossless (integer bith depth only)"));
746
+  g_signal_connect(G_OBJECT(quality), "value-changed", G_CALLBACK(quality_changed), self);
747
+  gtk_box_pack_start(GTK_BOX(box), quality, TRUE, TRUE, 0);
748
+  gui->quality = quality;
749
+
750
+  // encoding color profile combobox
751
+  GtkWidget *original = dt_bauhaus_combobox_new(NULL);
752
+  dt_bauhaus_combobox_add(original, _("internal"));
753
+  dt_bauhaus_combobox_add(original, _("original"));
754
+  dt_bauhaus_combobox_set_default(original,
755
+                                  dt_confgen_get_bool("plugins/imageio/format/jxl/original", DT_DEFAULT) & 1);
756
+  if(bpp_enum < 4 && quality_val == 100)
757
+  {
758
+    dt_bauhaus_combobox_set(original, 1);
759
+    gtk_widget_set_sensitive(original, FALSE);
760
+  }
761
+  else
762
+    dt_bauhaus_combobox_set(original, dt_conf_get_bool("plugins/imageio/format/jxl/original") & 1);
763
+  dt_bauhaus_widget_set_label(original, NULL, N_("encoding color profile"));
764
+  g_signal_connect(G_OBJECT(original), "value-changed", G_CALLBACK(original_changed), self);
765
+  gtk_box_pack_start(GTK_BOX(box), original, TRUE, TRUE, 0);
766
+  gui->original = original;
767
+
768
+  // encoding effort slider
769
+  GtkWidget *effort
770
+      = dt_bauhaus_slider_new_with_range(NULL, dt_confgen_get_int("plugins/imageio/format/jxl/effort", DT_MIN),
771
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/effort", DT_MAX), 1,
772
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/effort", DT_DEFAULT), 0);
773
+  dt_bauhaus_slider_set(effort, dt_conf_get_int("plugins/imageio/format/jxl/effort"));
774
+  dt_bauhaus_widget_set_label(effort, NULL, _("encoding effort"));
775
+  gtk_widget_set_tooltip_text(effort, _("the effort used to encode the image, higher efforts will have "
776
+                                        "better results at the expense of longer encode times"));
777
+  g_signal_connect(G_OBJECT(effort), "value-changed", G_CALLBACK(effort_changed), self);
778
+  gtk_box_pack_start(GTK_BOX(box), effort, TRUE, TRUE, 0);
779
+  gui->effort = effort;
780
+
781
+  // decoding speed (tier) slider
782
+  GtkWidget *tier
783
+      = dt_bauhaus_slider_new_with_range(NULL, dt_confgen_get_int("plugins/imageio/format/jxl/tier", DT_MIN),
784
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/tier", DT_MAX), 1,
785
+                                         dt_confgen_get_int("plugins/imageio/format/jxl/tier", DT_DEFAULT), 0);
786
+  dt_bauhaus_slider_set(tier, dt_conf_get_int("plugins/imageio/format/jxl/tier"));
787
+  dt_bauhaus_widget_set_label(tier, NULL, _("decoding speed"));
788
+  gtk_widget_set_tooltip_text(tier, _("the preffered decoding speed with some sacrifice of quality"));
789
+  g_signal_connect(G_OBJECT(tier), "value-changed", G_CALLBACK(tier_changed), self);
790
+  gtk_box_pack_start(GTK_BOX(box), tier, TRUE, TRUE, 0);
791
+  gui->tier = tier;
792
+}
793
+
794
+void gui_cleanup(dt_imageio_module_format_t *self)
795
+{
796
+  g_free(self->gui_data);
797
+}
798
+
799
+void gui_reset(dt_imageio_module_format_t *self)
800
+{
801
+  dt_imageio_jxl_gui_data_t *gui = (dt_imageio_jxl_gui_data_t *)self->gui_data;
802
+
803
+  const int bpp = dt_confgen_get_int("plugins/imageio/format/jxl/bpp", DT_DEFAULT);
804
+  dt_bauhaus_combobox_set(gui->bpp, bpp);
805
+
806
+  const int quality = dt_confgen_get_int("plugins/imageio/format/jxl/quality", DT_DEFAULT);
807
+  dt_bauhaus_slider_set(gui->quality, quality);
808
+
809
+  const int original = dt_confgen_get_bool("plugins/imageio/format/jxl/original", DT_DEFAULT);
810
+  dt_bauhaus_combobox_set(gui->original, original & 1);
811
+
812
+  const int effort = dt_confgen_get_int("plugins/imageio/format/jxl/effort", DT_DEFAULT);
813
+  dt_bauhaus_slider_set(gui->effort, effort);
814
+
815
+  const int tier = dt_confgen_get_int("plugins/imageio/format/jxl/tier", DT_DEFAULT);
816
+  dt_bauhaus_slider_set(gui->tier, tier);
817
+}
818
+
819
+// clang-format off
820
+// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
821
+// vim: shiftwidth=2 expandtab tabstop=2 cindent
822
+// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
823
+// clang-format on
824
--- src/imageio/format/tiff.c.orig	2022-02-09 10:21:58 UTC
825
+++ src/imageio/format/tiff.c
826
@@ -823,6 +823,8 @@ void gui_init(dt_imageio_module_format_t *self)
827
   dt_bauhaus_combobox_add(gui->compress, _("uncompressed"));
828
   dt_bauhaus_combobox_add(gui->compress, _("deflate"));
829
   dt_bauhaus_combobox_add(gui->compress, _("deflate with predictor"));
830
+  dt_bauhaus_combobox_set_default(gui->compress,
831
+                                  dt_confgen_get_int("plugins/imageio/format/tiff/compress", DT_DEFAULT));
832
   dt_bauhaus_combobox_set(gui->compress, compress);
833
   gtk_box_pack_start(GTK_BOX(self->widget), gui->compress, TRUE, TRUE, 0);
834
 
835
@@ -862,6 +864,7 @@ void gui_reset(dt_imageio_module_format_t *self)
836
 {
837
   dt_imageio_tiff_gui_t *gui = (dt_imageio_tiff_gui_t *)self->gui_data;
838
   dt_bauhaus_combobox_set(gui->bpp, 0); //8bpp
839
+  dt_bauhaus_combobox_set(gui->compress, dt_confgen_get_int("plugins/imageio/format/tiff/compress", DT_DEFAULT));
840
   dt_bauhaus_slider_set(gui->compresslevel, dt_confgen_get_int("plugins/imageio/format/tiff/compresslevel", DT_DEFAULT));
841
   dt_bauhaus_combobox_set(gui->shortfiles, dt_confgen_get_int("plugins/imageio/format/tiff/shortfile", DT_DEFAULT));
842
 }
(-)b/graphics/darktable40/pkg-plist (+1 lines)
Lines 11-16 lib/darktable/plugins/imageio/format/libcopy.so Link Here
11
%%OPENEXR%%lib/darktable/plugins/imageio/format/libexr.so
11
%%OPENEXR%%lib/darktable/plugins/imageio/format/libexr.so
12
%%OPENJPEG%%lib/darktable/plugins/imageio/format/libj2k.so
12
%%OPENJPEG%%lib/darktable/plugins/imageio/format/libj2k.so
13
lib/darktable/plugins/imageio/format/libjpeg.so
13
lib/darktable/plugins/imageio/format/libjpeg.so
14
%%JXL%%lib/darktable/plugins/imageio/format/libjpegxl.so
14
lib/darktable/plugins/imageio/format/libpdf.so
15
lib/darktable/plugins/imageio/format/libpdf.so
15
lib/darktable/plugins/imageio/format/libpfm.so
16
lib/darktable/plugins/imageio/format/libpfm.so
16
lib/darktable/plugins/imageio/format/libpng.so
17
lib/darktable/plugins/imageio/format/libpng.so

Return to bug 266125