Line data Source code
1 : /*
2 : * Copyright (c) 2021, Airbus DS Intelligence
3 : * Author: <even.rouault at spatialys.com>
4 : *
5 : * Permission to use, copy, modify, distribute, and sell this software and
6 : * its documentation for any purpose is hereby granted without fee, provided
7 : * that (i) the above copyright notices and this permission notice appear in
8 : * all copies of the software and related documentation, and (ii) the names of
9 : * Sam Leffler and Silicon Graphics may not be used in any advertising or
10 : * publicity relating to the software without the specific, prior written
11 : * permission of Sam Leffler and Silicon Graphics.
12 : *
13 : * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
14 : * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
15 : * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
16 : *
17 : * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
18 : * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
19 : * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20 : * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
21 : * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22 : * OF THIS SOFTWARE.
23 : */
24 :
25 : #include "tiffiop.h"
26 : #include "tif_jxl.h"
27 :
28 : #include <jxl/decode.h>
29 : #include <jxl/encode.h>
30 :
31 : #include <stdint.h>
32 :
33 : #include <assert.h>
34 :
35 : #define LSTATE_INIT_DECODE 0x01
36 : #define LSTATE_INIT_ENCODE 0x02
37 :
38 : /*
39 : * State block for each open TIFF file using JXL compression/decompression.
40 : */
41 : typedef struct
42 : {
43 : int state; /* state flags */
44 :
45 : int lossless; /* TRUE (default) or FALSE */
46 : int effort; /* 3 to 9. default: 7 */
47 : float distance; /* 0 to 15. default: 1.0 */
48 : float alpha_distance; /* 0 to 15. default: -1.0 (same as distance) */
49 :
50 : uint32_t segment_width;
51 : uint32_t segment_height;
52 :
53 : unsigned int uncompressed_size;
54 : unsigned int uncompressed_alloc;
55 : uint8_t *uncompressed_buffer;
56 : unsigned int uncompressed_offset;
57 :
58 : JxlDecoder *decoder;
59 :
60 : TIFFVGetMethod vgetparent; /* super-class method */
61 : TIFFVSetMethod vsetparent; /* super-class method */
62 : } JXLState;
63 :
64 : #define LState(tif) ((JXLState *)(tif)->tif_data)
65 : #define DecoderState(tif) LState(tif)
66 : #define EncoderState(tif) LState(tif)
67 :
68 : static int JXLEncode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s);
69 : static int JXLDecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s);
70 :
71 923 : static int GetJXLDataType(TIFF *tif)
72 : {
73 923 : TIFFDirectory *td = &tif->tif_dir;
74 : static const char module[] = "GetJXLDataType";
75 :
76 923 : if (td->td_sampleformat == SAMPLEFORMAT_UINT && td->td_bitspersample == 8)
77 : {
78 519 : return JXL_TYPE_UINT8;
79 : }
80 :
81 404 : if (td->td_sampleformat == SAMPLEFORMAT_UINT && td->td_bitspersample == 16)
82 : {
83 203 : return JXL_TYPE_UINT16;
84 : }
85 :
86 : /* 20210903: Not supported yet by libjxl*/
87 : /*
88 : if( td->td_sampleformat == SAMPLEFORMAT_INT &&
89 : td->td_bitspersample == 32 )
90 : {
91 : return JXL_TYPE_UINT32;
92 : }
93 : */
94 :
95 201 : if (td->td_sampleformat == SAMPLEFORMAT_IEEEFP &&
96 201 : td->td_bitspersample == 32)
97 : {
98 196 : return JXL_TYPE_FLOAT;
99 : }
100 :
101 5 : if (td->td_sampleformat == SAMPLEFORMAT_IEEEFP &&
102 5 : td->td_bitspersample == 16)
103 : {
104 5 : return JXL_TYPE_FLOAT16;
105 : }
106 :
107 0 : TIFFErrorExtR(
108 : tif, module,
109 : "Unsupported combination of SampleFormat(=%d) and BitsPerSample(=%d)",
110 0 : td->td_sampleformat, td->td_bitspersample);
111 0 : return -1;
112 : }
113 :
114 812 : static int GetJXLDataTypeSize(JxlDataType dtype)
115 : {
116 812 : switch (dtype)
117 : {
118 450 : case JXL_TYPE_UINT8:
119 450 : return 1;
120 182 : case JXL_TYPE_UINT16:
121 182 : return 2;
122 176 : case JXL_TYPE_FLOAT:
123 176 : return 4;
124 4 : case JXL_TYPE_FLOAT16:
125 4 : return 2;
126 0 : default:
127 0 : return 0;
128 : }
129 : }
130 :
131 383 : static int JXLFixupTags(TIFF *tif)
132 : {
133 : (void)tif;
134 383 : return 1;
135 : }
136 :
137 115 : static int JXLSetupDecode(TIFF *tif)
138 : {
139 115 : JXLState *sp = DecoderState(tif);
140 :
141 115 : assert(sp != NULL);
142 :
143 : /* if we were last encoding, terminate this mode */
144 115 : if (sp->state & LSTATE_INIT_ENCODE)
145 : {
146 2 : sp->state = 0;
147 : }
148 :
149 115 : sp->state |= LSTATE_INIT_DECODE;
150 115 : return 1;
151 : }
152 :
153 406 : static int SetupUncompressedBuffer(TIFF *tif, JXLState *sp, const char *module)
154 : {
155 406 : TIFFDirectory *td = &tif->tif_dir;
156 : uint64_t new_size_64;
157 : uint64_t new_alloc_64;
158 : unsigned int new_size;
159 : unsigned int new_alloc;
160 :
161 406 : sp->uncompressed_offset = 0;
162 :
163 406 : if (isTiled(tif))
164 : {
165 288 : sp->segment_width = td->td_tilewidth;
166 288 : sp->segment_height = td->td_tilelength;
167 : }
168 : else
169 : {
170 118 : sp->segment_width = td->td_imagewidth;
171 118 : sp->segment_height = td->td_imagelength - tif->tif_row;
172 118 : if (sp->segment_height > td->td_rowsperstrip)
173 29 : sp->segment_height = td->td_rowsperstrip;
174 : }
175 :
176 406 : JxlDataType dtype = GetJXLDataType(tif);
177 : if (dtype < 0)
178 : {
179 : _TIFFfreeExt(tif, sp->uncompressed_buffer);
180 : sp->uncompressed_buffer = 0;
181 : sp->uncompressed_alloc = 0;
182 : return 0;
183 : }
184 406 : int nBytesPerSample = GetJXLDataTypeSize(dtype);
185 406 : new_size_64 =
186 406 : (uint64_t)sp->segment_width * sp->segment_height * nBytesPerSample;
187 406 : if (td->td_planarconfig == PLANARCONFIG_CONTIG)
188 : {
189 202 : new_size_64 *= td->td_samplesperpixel;
190 : }
191 :
192 406 : new_size = (unsigned int)new_size_64;
193 406 : sp->uncompressed_size = new_size;
194 :
195 : /* add some margin */
196 406 : new_alloc_64 = 100 + new_size_64 + new_size_64 / 3;
197 406 : new_alloc = (unsigned int)new_alloc_64;
198 406 : if (new_alloc != new_alloc_64)
199 : {
200 0 : TIFFErrorExtR(tif, module, "Too large uncompressed strip/tile");
201 0 : _TIFFfreeExt(tif, sp->uncompressed_buffer);
202 0 : sp->uncompressed_buffer = 0;
203 0 : sp->uncompressed_alloc = 0;
204 0 : return 0;
205 : }
206 :
207 406 : if (sp->uncompressed_alloc < new_alloc)
208 : {
209 224 : _TIFFfreeExt(tif, sp->uncompressed_buffer);
210 224 : sp->uncompressed_buffer = _TIFFmallocExt(tif, new_alloc);
211 224 : if (!sp->uncompressed_buffer)
212 : {
213 0 : TIFFErrorExtR(tif, module, "Cannot allocate buffer");
214 0 : _TIFFfreeExt(tif, sp->uncompressed_buffer);
215 0 : sp->uncompressed_buffer = 0;
216 0 : sp->uncompressed_alloc = 0;
217 0 : return 0;
218 : }
219 224 : sp->uncompressed_alloc = new_alloc;
220 : }
221 :
222 406 : return 1;
223 : }
224 :
225 : /*
226 : * Setup state for decoding a strip.
227 : */
228 204 : static int JXLPreDecode(TIFF *tif, uint16_t s)
229 : {
230 : static const char module[] = "JXLPreDecode";
231 204 : JXLState *sp = DecoderState(tif);
232 204 : TIFFDirectory *td = &tif->tif_dir;
233 :
234 : (void)s;
235 204 : assert(sp != NULL);
236 204 : if (sp->state != LSTATE_INIT_DECODE)
237 2 : tif->tif_setupdecode(tif);
238 :
239 204 : const int jxlDataType = GetJXLDataType(tif);
240 204 : if (jxlDataType < 0)
241 0 : return 0;
242 :
243 204 : if (!SetupUncompressedBuffer(tif, sp, module))
244 0 : return 0;
245 :
246 204 : if (sp->decoder == NULL)
247 : {
248 115 : sp->decoder = JxlDecoderCreate(NULL);
249 115 : if (sp->decoder == NULL)
250 : {
251 0 : TIFFErrorExtR(tif, module, "JxlDecoderCreate() failed");
252 0 : return 0;
253 : }
254 : }
255 : else
256 : {
257 89 : JxlDecoderReset(sp->decoder);
258 : }
259 :
260 : JxlDecoderStatus status;
261 204 : status = JxlDecoderSubscribeEvents(sp->decoder,
262 : JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE);
263 204 : if (status != JXL_DEC_SUCCESS)
264 : {
265 0 : TIFFErrorExtR(tif, module, "JxlDecoderSubscribeEvents() failed");
266 0 : return 0;
267 : }
268 :
269 204 : status = JxlDecoderSetInput(sp->decoder, (const uint8_t *)tif->tif_rawcp,
270 204 : (size_t)tif->tif_rawcc);
271 204 : if (status != JXL_DEC_SUCCESS)
272 : {
273 0 : TIFFErrorExtR(tif, module, "JxlDecoderSetInput() failed with %d",
274 : status);
275 0 : return 0;
276 : }
277 :
278 204 : status = JxlDecoderProcessInput(sp->decoder);
279 204 : if (status != JXL_DEC_BASIC_INFO)
280 : {
281 0 : TIFFErrorExtR(tif, module, "JxlDecoderProcessInput() failed with %d",
282 : status);
283 0 : JxlDecoderReleaseInput(sp->decoder);
284 0 : return 0;
285 : }
286 :
287 : JxlBasicInfo info;
288 204 : status = JxlDecoderGetBasicInfo(sp->decoder, &info);
289 204 : if (status != JXL_DEC_SUCCESS)
290 : {
291 0 : TIFFErrorExtR(tif, module, "JxlDecoderGetBasicInfo() failed with %d",
292 : status);
293 0 : JxlDecoderReleaseInput(sp->decoder);
294 0 : return 0;
295 : }
296 :
297 204 : if (sp->segment_width != info.xsize)
298 : {
299 0 : TIFFErrorExtR(tif, module,
300 : "JXL basic info xsize = %d, whereas %u was expected",
301 : info.xsize, sp->segment_width);
302 0 : JxlDecoderReleaseInput(sp->decoder);
303 0 : return 0;
304 : }
305 :
306 204 : if (sp->segment_height != info.ysize)
307 : {
308 0 : TIFFErrorExtR(tif, module,
309 : "JXL basic info ysize = %d, whereas %u was expected",
310 : info.ysize, sp->segment_height);
311 0 : JxlDecoderReleaseInput(sp->decoder);
312 0 : return 0;
313 : }
314 :
315 204 : if (td->td_bitspersample != info.bits_per_sample)
316 : {
317 0 : TIFFErrorExtR(
318 : tif, module,
319 : "JXL basic info bits_per_sample = %d, whereas %d was expected",
320 0 : info.bits_per_sample, td->td_bitspersample);
321 0 : JxlDecoderReleaseInput(sp->decoder);
322 0 : return 0;
323 : }
324 :
325 204 : if (td->td_planarconfig == PLANARCONFIG_CONTIG)
326 : {
327 102 : if (info.num_color_channels + info.num_extra_channels !=
328 102 : td->td_samplesperpixel)
329 : {
330 0 : TIFFErrorExtR(tif, module,
331 : "JXL basic info invalid number of channels");
332 0 : JxlDecoderReleaseInput(sp->decoder);
333 0 : return 0;
334 : }
335 : }
336 : else
337 : {
338 102 : if (info.num_color_channels != 1 || info.alpha_bits > 0 ||
339 102 : info.num_extra_channels > 0)
340 : {
341 0 : TIFFErrorExtR(tif, module,
342 : "JXL basic info invalid number of channels");
343 0 : JxlDecoderReleaseInput(sp->decoder);
344 0 : return 0;
345 : }
346 : }
347 :
348 204 : JxlPixelFormat format = {0};
349 204 : format.data_type = jxlDataType;
350 204 : format.endianness = JXL_NATIVE_ENDIAN;
351 204 : format.align = 0;
352 : // alpha_bits is set even for a gray, gray, Alpha, gray, gray
353 : // or for R, G, B, undefined, Alpha
354 : // Probably a defect of libjxl: https://github.com/libjxl/libjxl/issues/1773
355 : // So for num_color_channels==3, num_color_channels > 1 and
356 : // alpha_bits != 0, get information of the first extra channel to
357 : // check if it is alpha, to detect R, G, B, Alpha, undefined.
358 : // Note: there's no difference in the codestream if writing RGBAU
359 : // as num_channels == 3 with 2 extra channels the first one being
360 : // explicitly set to alpha, or with num_channels == 4.
361 204 : int bAlphaEmbedded = 0;
362 204 : if (info.alpha_bits != 0)
363 : {
364 37 : if ((info.num_color_channels == 3 || info.num_color_channels == 1) &&
365 37 : (info.num_extra_channels == 1))
366 : {
367 28 : bAlphaEmbedded = 1;
368 : }
369 9 : else if (info.num_color_channels == 3 && info.num_extra_channels > 1)
370 : {
371 : JxlExtraChannelInfo extra_channel_info;
372 6 : memset(&extra_channel_info, 0, sizeof(extra_channel_info));
373 6 : if (JxlDecoderGetExtraChannelInfo(
374 6 : sp->decoder, 0, &extra_channel_info) == JXL_DEC_SUCCESS &&
375 6 : extra_channel_info.type == JXL_CHANNEL_ALPHA)
376 : {
377 3 : bAlphaEmbedded = 1;
378 : }
379 : }
380 : }
381 204 : uint32_t nFirstExtraChannel = (bAlphaEmbedded) ? 1 : 0;
382 204 : unsigned int main_buffer_size = sp->uncompressed_size;
383 204 : unsigned int channel_size = main_buffer_size / td->td_samplesperpixel;
384 204 : uint8_t *extra_channel_buffer = NULL;
385 :
386 204 : int nBytesPerSample = GetJXLDataTypeSize(format.data_type);
387 :
388 204 : if (nFirstExtraChannel < info.num_extra_channels)
389 : {
390 17 : int nExtraChannelsToExtract =
391 17 : info.num_extra_channels - nFirstExtraChannel;
392 17 : format.num_channels = 1;
393 17 : main_buffer_size =
394 17 : channel_size * (info.num_color_channels + (bAlphaEmbedded ? 1 : 0));
395 : extra_channel_buffer =
396 17 : _TIFFmallocExt(tif, channel_size * nExtraChannelsToExtract);
397 17 : if (extra_channel_buffer == NULL)
398 0 : return 0;
399 55 : for (int i = 0; i < nExtraChannelsToExtract; ++i)
400 : {
401 : size_t buffer_size;
402 38 : const int iCorrectedIdx = i + nFirstExtraChannel;
403 :
404 38 : if (JxlDecoderExtraChannelBufferSize(sp->decoder, &format,
405 : &buffer_size, iCorrectedIdx) !=
406 : JXL_DEC_SUCCESS)
407 : {
408 0 : TIFFErrorExtR(tif, module,
409 : "JxlDecoderExtraChannelBufferSize failed()");
410 0 : _TIFFfreeExt(tif, extra_channel_buffer);
411 0 : return 0;
412 : }
413 38 : if (buffer_size != channel_size)
414 : {
415 0 : TIFFErrorExtR(tif, module,
416 : "JxlDecoderExtraChannelBufferSize returned %ld, "
417 : "expecting %u",
418 : buffer_size, channel_size);
419 0 : _TIFFfreeExt(tif, extra_channel_buffer);
420 0 : return 0;
421 : }
422 :
423 : #if 0
424 : // Check consistency of JXL codestream header regarding
425 : // extra alpha channels and TIFF ExtraSamples tag
426 : JxlExtraChannelInfo extra_channel_info;
427 : memset(&extra_channel_info, 0, sizeof(extra_channel_info));
428 : if( JxlDecoderGetExtraChannelInfo(sp->decoder, iCorrectedIdx, &extra_channel_info) == JXL_DEC_SUCCESS )
429 : {
430 : if( extra_channel_info.type == JXL_CHANNEL_ALPHA &&
431 : !extra_channel_info.alpha_premultiplied )
432 : {
433 : if( iCorrectedIdx < td->td_extrasamples &&
434 : td->td_sampleinfo[iCorrectedIdx] == EXTRASAMPLE_UNASSALPHA )
435 : {
436 : // ok
437 : }
438 : else
439 : {
440 : TIFFWarningExtR(tif, module,
441 : "Unpremultiplied alpha channel expected from JXL codestream "
442 : "in extra channel %d, but other value found in ExtraSamples tag", iCorrectedIdx);
443 : }
444 : }
445 : else if( extra_channel_info.type == JXL_CHANNEL_ALPHA &&
446 : extra_channel_info.alpha_premultiplied )
447 : {
448 : if( iCorrectedIdx < td->td_extrasamples &&
449 : td->td_sampleinfo[iCorrectedIdx] == EXTRASAMPLE_ASSOCALPHA )
450 : {
451 : // ok
452 : }
453 : else
454 : {
455 : TIFFWarningExtR(tif, module,
456 : "Premultiplied alpha channel expected from JXL codestream "
457 : "in extra channel %d, but other value found in ExtraSamples tag", iCorrectedIdx);
458 : }
459 : }
460 : else if( iCorrectedIdx < td->td_extrasamples &&
461 : td->td_sampleinfo[iCorrectedIdx] == EXTRASAMPLE_UNASSALPHA )
462 : {
463 : TIFFWarningExtR(tif, module,
464 : "Unpremultiplied alpha channel expected from ExtraSamples tag "
465 : "in extra channel %d, but other value found in JXL codestream", iCorrectedIdx);
466 : }
467 : else if( iCorrectedIdx < td->td_extrasamples &&
468 : td->td_sampleinfo[iCorrectedIdx] == EXTRASAMPLE_ASSOCALPHA )
469 : {
470 : TIFFWarningExtR(tif, module,
471 : "Premultiplied alpha channel expected from ExtraSamples tag "
472 : "in extra channel %d, but other value found in JXL codestream", iCorrectedIdx);
473 : }
474 : }
475 : #endif
476 38 : if (JxlDecoderSetExtraChannelBuffer(
477 : sp->decoder, &format,
478 38 : extra_channel_buffer + i * channel_size, channel_size,
479 : i + nFirstExtraChannel) != JXL_DEC_SUCCESS)
480 : {
481 0 : TIFFErrorExtR(tif, module,
482 : "JxlDecoderSetExtraChannelBuffer failed()");
483 0 : _TIFFfreeExt(tif, extra_channel_buffer);
484 0 : return 0;
485 : }
486 : }
487 : }
488 :
489 204 : format.num_channels = info.num_color_channels;
490 204 : if (bAlphaEmbedded)
491 31 : format.num_channels++;
492 :
493 204 : status = JxlDecoderProcessInput(sp->decoder);
494 204 : if (status != JXL_DEC_NEED_IMAGE_OUT_BUFFER)
495 : {
496 0 : TIFFErrorExtR(tif, module,
497 : "JxlDecoderProcessInput() (second call) failed with %d",
498 : status);
499 0 : JxlDecoderReleaseInput(sp->decoder);
500 0 : _TIFFfreeExt(tif, extra_channel_buffer);
501 0 : return 0;
502 : }
503 :
504 204 : status = JxlDecoderSetImageOutBuffer(
505 204 : sp->decoder, &format, sp->uncompressed_buffer, main_buffer_size);
506 204 : if (status != JXL_DEC_SUCCESS)
507 : {
508 0 : TIFFErrorExtR(tif, module,
509 : "JxlDecoderSetImageOutBuffer() failed with %d", status);
510 0 : JxlDecoderReleaseInput(sp->decoder);
511 0 : _TIFFfreeExt(tif, extra_channel_buffer);
512 0 : return 0;
513 : }
514 :
515 204 : status = JxlDecoderProcessInput(sp->decoder);
516 204 : if (status != JXL_DEC_FULL_IMAGE)
517 : {
518 0 : TIFFErrorExtR(tif, module,
519 : "JxlDecoderProcessInput() (third call) failed with %d",
520 : status);
521 0 : JxlDecoderReleaseInput(sp->decoder);
522 0 : _TIFFfreeExt(tif, extra_channel_buffer);
523 0 : return 0;
524 : }
525 204 : if (nFirstExtraChannel < info.num_extra_channels)
526 : {
527 : // first reorder the main buffer
528 20 : const int nMainChannels = bAlphaEmbedded ? info.num_color_channels + 1
529 17 : : info.num_color_channels;
530 17 : const unsigned int mainPixSize = nMainChannels * nBytesPerSample;
531 17 : const unsigned int fullPixSize =
532 17 : td->td_samplesperpixel * nBytesPerSample;
533 17 : assert(fullPixSize > mainPixSize);
534 :
535 : /* Find min value of k such that k * fullPixSize >= (k + 1) * mainPixSize:
536 : * ==> k = ceil(mainPixSize / (fullPixSize - mainPixSize))
537 : * ==> k = (mainPixSize + (fullPixSize - mainPixSize) - 1) / (fullPixSize - mainPixSize)
538 : * ==> k = (fullPixSize - 1) / (fullPixSize - mainPixSize)
539 : */
540 17 : const unsigned int nNumPixels = info.xsize * info.ysize;
541 17 : unsigned int outOff = sp->uncompressed_size - fullPixSize;
542 17 : unsigned int inOff = main_buffer_size - mainPixSize;
543 17 : const unsigned int kThreshold =
544 17 : (fullPixSize - 1) / (fullPixSize - mainPixSize);
545 17 : if (mainPixSize == 1)
546 : {
547 131872 : for (unsigned int k = kThreshold; k < nNumPixels; ++k)
548 : {
549 131868 : memcpy(sp->uncompressed_buffer + outOff,
550 131868 : sp->uncompressed_buffer + inOff, /*mainPixSize=*/1);
551 131868 : inOff -= /*mainPixSize=*/1;
552 131868 : outOff -= fullPixSize;
553 : }
554 : }
555 13 : else if (mainPixSize == 2)
556 : {
557 131072 : for (unsigned int k = kThreshold; k < nNumPixels; ++k)
558 : {
559 131070 : memcpy(sp->uncompressed_buffer + outOff,
560 131070 : sp->uncompressed_buffer + inOff, /*mainPixSize=*/2);
561 131070 : inOff -= /*mainPixSize=*/2;
562 131070 : outOff -= fullPixSize;
563 : }
564 : }
565 11 : else if (mainPixSize == 3)
566 : {
567 131070 : for (unsigned int k = kThreshold; k < nNumPixels; ++k)
568 : {
569 131068 : memcpy(sp->uncompressed_buffer + outOff,
570 131068 : sp->uncompressed_buffer + inOff, /*mainPixSize=*/3);
571 131068 : inOff -= /*mainPixSize=*/3;
572 131068 : outOff -= fullPixSize;
573 : }
574 : }
575 9 : else if (mainPixSize == 4)
576 : {
577 196605 : for (unsigned int k = kThreshold; k < nNumPixels; ++k)
578 : {
579 196602 : memcpy(sp->uncompressed_buffer + outOff,
580 196602 : sp->uncompressed_buffer + inOff, /*mainPixSize=*/4);
581 196602 : inOff -= /*mainPixSize=*/4;
582 196602 : outOff -= fullPixSize;
583 : }
584 : }
585 6 : else if (mainPixSize == 3 * 2)
586 : {
587 131070 : for (unsigned int k = kThreshold; k < nNumPixels; ++k)
588 : {
589 131068 : memcpy(sp->uncompressed_buffer + outOff,
590 131068 : sp->uncompressed_buffer + inOff, /*mainPixSize=*/3 * 2);
591 131068 : inOff -= /*mainPixSize=*/3 * 2;
592 131068 : outOff -= fullPixSize;
593 : }
594 : }
595 4 : else if (mainPixSize == 4 * 2)
596 : {
597 65533 : for (unsigned int k = kThreshold; k < nNumPixels; ++k)
598 : {
599 65532 : memcpy(sp->uncompressed_buffer + outOff,
600 65532 : sp->uncompressed_buffer + inOff, /*mainPixSize=*/4 * 2);
601 65532 : inOff -= /*mainPixSize=*/4 * 2;
602 65532 : outOff -= fullPixSize;
603 : }
604 : }
605 : else
606 : {
607 196603 : for (unsigned int k = kThreshold; k < nNumPixels; ++k)
608 : {
609 196600 : memcpy(sp->uncompressed_buffer + outOff,
610 196600 : sp->uncompressed_buffer + inOff, mainPixSize);
611 196600 : inOff -= mainPixSize;
612 196600 : outOff -= fullPixSize;
613 : }
614 : }
615 : /* Last iterations need memmove() because of overlapping between */
616 : /* source and target regions. */
617 32 : for (unsigned int k = kThreshold; k > 1;)
618 : {
619 15 : --k;
620 15 : memmove(sp->uncompressed_buffer + outOff,
621 15 : sp->uncompressed_buffer + inOff, mainPixSize);
622 15 : inOff -= mainPixSize;
623 15 : outOff -= fullPixSize;
624 : }
625 : // then copy over the data from the extra_channel_buffer
626 17 : const int nExtraChannelsToExtract =
627 17 : info.num_extra_channels - nFirstExtraChannel;
628 55 : for (int i = 0; i < nExtraChannelsToExtract; ++i)
629 : {
630 38 : outOff = (i + nMainChannels) * nBytesPerSample;
631 38 : uint8_t *channel_buffer = extra_channel_buffer + i * channel_size;
632 38 : if (nBytesPerSample == 1)
633 : {
634 658578 : for (; outOff < sp->uncompressed_size;
635 658560 : outOff += fullPixSize,
636 658560 : channel_buffer += /*nBytesPerSample=*/1)
637 : {
638 658560 : memcpy(sp->uncompressed_buffer + outOff, channel_buffer,
639 : /*nBytesPerSample=*/1);
640 : }
641 : }
642 20 : else if (nBytesPerSample == 2)
643 : {
644 655370 : for (; outOff < sp->uncompressed_size;
645 655360 : outOff += fullPixSize,
646 655360 : channel_buffer += /*nBytesPerSample=*/2)
647 : {
648 655360 : memcpy(sp->uncompressed_buffer + outOff, channel_buffer,
649 : /*nBytesPerSample=*/2);
650 : }
651 : }
652 : else
653 : {
654 10 : assert(nBytesPerSample == 4);
655 655370 : for (; outOff < sp->uncompressed_size;
656 655360 : outOff += fullPixSize, channel_buffer += nBytesPerSample)
657 : {
658 655360 : memcpy(sp->uncompressed_buffer + outOff, channel_buffer,
659 : nBytesPerSample);
660 : }
661 : }
662 : }
663 17 : _TIFFfreeExt(tif, extra_channel_buffer);
664 : }
665 :
666 204 : /*const size_t nRemaining = */ JxlDecoderReleaseInput(sp->decoder);
667 : /*if( nRemaining != 0 )
668 : {
669 : TIFFErrorExtR(tif, module,
670 : "JxlDecoderReleaseInput(): %u input bytes remaining",
671 : (unsigned)nRemaining);
672 : }*/
673 :
674 204 : return 1;
675 : }
676 :
677 : /*
678 : * Decode a strip, tile or scanline.
679 : */
680 204 : static int JXLDecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s)
681 : {
682 : static const char module[] = "JXLDecode";
683 204 : JXLState *sp = DecoderState(tif);
684 :
685 : (void)s;
686 204 : assert(sp != NULL);
687 204 : assert(sp->state == LSTATE_INIT_DECODE);
688 :
689 204 : if (sp->uncompressed_buffer == 0)
690 : {
691 0 : TIFFErrorExtR(tif, module, "Uncompressed buffer not allocated");
692 0 : return 0;
693 : }
694 :
695 204 : if ((uint64_t)sp->uncompressed_offset + (uint64_t)occ >
696 204 : sp->uncompressed_size)
697 : {
698 0 : TIFFErrorExtR(tif, module, "Too many bytes read");
699 0 : return 0;
700 : }
701 :
702 204 : memcpy(op, sp->uncompressed_buffer + sp->uncompressed_offset, occ);
703 204 : sp->uncompressed_offset += (unsigned)occ;
704 :
705 204 : return 1;
706 : }
707 :
708 111 : static int JXLSetupEncode(TIFF *tif)
709 : {
710 111 : JXLState *sp = EncoderState(tif);
711 :
712 111 : assert(sp != NULL);
713 111 : if (sp->state & LSTATE_INIT_DECODE)
714 : {
715 0 : sp->state = 0;
716 : }
717 :
718 111 : if (GetJXLDataType(tif) < 0)
719 0 : return 0;
720 :
721 111 : sp->state |= LSTATE_INIT_ENCODE;
722 :
723 111 : return 1;
724 : }
725 :
726 : /*
727 : * Reset encoding state at the start of a strip.
728 : */
729 202 : static int JXLPreEncode(TIFF *tif, uint16_t s)
730 : {
731 : static const char module[] = "JXLPreEncode";
732 202 : JXLState *sp = EncoderState(tif);
733 :
734 : (void)s;
735 202 : assert(sp != NULL);
736 202 : if (sp->state != LSTATE_INIT_ENCODE)
737 0 : tif->tif_setupencode(tif);
738 :
739 202 : if (!SetupUncompressedBuffer(tif, sp, module))
740 0 : return 0;
741 :
742 202 : return 1;
743 : }
744 :
745 : /*
746 : * Encode a chunk of pixels.
747 : */
748 202 : static int JXLEncode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
749 : {
750 : static const char module[] = "JXLEncode";
751 202 : JXLState *sp = EncoderState(tif);
752 :
753 : (void)s;
754 202 : assert(sp != NULL);
755 202 : assert(sp->state == LSTATE_INIT_ENCODE);
756 :
757 202 : if ((uint64_t)sp->uncompressed_offset + (uint64_t)cc >
758 202 : sp->uncompressed_size)
759 : {
760 0 : TIFFErrorExtR(tif, module, "Too many bytes written");
761 0 : return 0;
762 : }
763 :
764 202 : memcpy(sp->uncompressed_buffer + sp->uncompressed_offset, bp, cc);
765 202 : sp->uncompressed_offset += (unsigned)cc;
766 :
767 202 : return 1;
768 : }
769 :
770 : /*
771 : * Finish off an encoded strip by flushing it.
772 : */
773 202 : static int JXLPostEncode(TIFF *tif)
774 : {
775 : static const char module[] = "JXLPostEncode";
776 202 : JXLState *sp = EncoderState(tif);
777 202 : TIFFDirectory *td = &tif->tif_dir;
778 :
779 202 : if (sp->uncompressed_offset != sp->uncompressed_size)
780 : {
781 0 : TIFFErrorExtR(tif, module, "Unexpected number of bytes in the buffer");
782 0 : return 0;
783 : }
784 :
785 202 : JxlEncoder *enc = JxlEncoderCreate(NULL);
786 202 : if (enc == NULL)
787 : {
788 0 : TIFFErrorExtR(tif, module, "JxlEncoderCreate() failed");
789 0 : return 0;
790 : }
791 202 : JxlEncoderUseContainer(enc, JXL_FALSE);
792 :
793 : #ifdef HAVE_JxlEncoderFrameSettingsCreate
794 202 : JxlEncoderFrameSettings *opts = JxlEncoderFrameSettingsCreate(enc, NULL);
795 : #else
796 : JxlEncoderOptions *opts = JxlEncoderOptionsCreate(enc, NULL);
797 : #endif
798 202 : if (opts == NULL)
799 : {
800 0 : TIFFErrorExtR(tif, module, "JxlEncoderFrameSettingsCreate() failed");
801 0 : JxlEncoderDestroy(enc);
802 0 : return 0;
803 : }
804 :
805 202 : JxlPixelFormat format = {0};
806 202 : format.data_type = GetJXLDataType(tif);
807 202 : format.endianness = JXL_NATIVE_ENDIAN;
808 202 : format.align = 0;
809 :
810 : #ifdef HAVE_JxlEncoderSetCodestreamLevel
811 202 : if (td->td_bitspersample > 12)
812 : {
813 90 : JxlEncoderSetCodestreamLevel(enc, 10);
814 : }
815 : #endif
816 202 : JxlBasicInfo basic_info = {0};
817 202 : JxlEncoderInitBasicInfo(&basic_info);
818 202 : basic_info.xsize = sp->segment_width;
819 202 : basic_info.ysize = sp->segment_height;
820 202 : basic_info.bits_per_sample = td->td_bitspersample;
821 202 : basic_info.orientation = JXL_ORIENT_IDENTITY;
822 202 : if (td->td_sampleformat == SAMPLEFORMAT_IEEEFP)
823 : {
824 45 : if (td->td_bitspersample == 32)
825 44 : basic_info.exponent_bits_per_sample = 8;
826 : else
827 1 : basic_info.exponent_bits_per_sample = 5;
828 : }
829 : else
830 : {
831 157 : basic_info.exponent_bits_per_sample = 0;
832 : }
833 :
834 202 : int bAlphaEmbedded = 0;
835 202 : const int bAlphaDistanceSameAsMainChannel =
836 217 : (sp->alpha_distance < 0.0f) ||
837 15 : ((sp->lossless && sp->alpha_distance == 0.0f) ||
838 15 : (!sp->lossless && sp->alpha_distance == sp->distance));
839 : #ifndef HAVE_JxlEncoderSetExtraChannelDistance
840 : if (!bAlphaDistanceSameAsMainChannel)
841 : {
842 : TIFFWarningExtR(tif, module,
843 : "AlphaDistance ignored due to "
844 : "JxlEncoderSetExtraChannelDistance() not being "
845 : "available. Please upgrade libjxl to > 0.8.1");
846 : }
847 : #endif
848 :
849 202 : if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
850 : {
851 102 : format.num_channels = 1;
852 102 : basic_info.num_color_channels = 1;
853 102 : basic_info.num_extra_channels = 0;
854 102 : basic_info.alpha_bits = 0;
855 102 : basic_info.alpha_exponent_bits = 0;
856 : }
857 : else
858 : {
859 100 : if (td->td_photometric == PHOTOMETRIC_MINISBLACK &&
860 28 : td->td_extrasamples > 0 &&
861 14 : td->td_extrasamples == td->td_samplesperpixel - 1 &&
862 14 : td->td_sampleinfo[0] == EXTRASAMPLE_UNASSALPHA &&
863 : bAlphaDistanceSameAsMainChannel)
864 : { // gray with alpha
865 6 : format.num_channels = 2;
866 6 : basic_info.num_color_channels = 1;
867 6 : basic_info.num_extra_channels = td->td_extrasamples;
868 6 : basic_info.alpha_bits = td->td_bitspersample;
869 6 : basic_info.alpha_exponent_bits =
870 6 : basic_info.exponent_bits_per_sample;
871 6 : bAlphaEmbedded = 1;
872 : }
873 94 : else if (td->td_photometric == PHOTOMETRIC_RGB &&
874 72 : td->td_extrasamples > 0 &&
875 30 : td->td_extrasamples == td->td_samplesperpixel - 3 &&
876 30 : td->td_sampleinfo[0] == EXTRASAMPLE_UNASSALPHA &&
877 : bAlphaDistanceSameAsMainChannel)
878 : { // rgb with alpha, and same distance for alpha vs non-alpha channels
879 9 : format.num_channels = 4;
880 9 : basic_info.num_color_channels = 3;
881 9 : basic_info.num_extra_channels = td->td_samplesperpixel - 3;
882 9 : basic_info.alpha_bits = td->td_bitspersample;
883 9 : basic_info.alpha_exponent_bits =
884 9 : basic_info.exponent_bits_per_sample;
885 9 : bAlphaEmbedded = 1;
886 : }
887 85 : else if (td->td_photometric == PHOTOMETRIC_RGB &&
888 63 : ((td->td_extrasamples == 0) ||
889 21 : (td->td_extrasamples > 0 &&
890 21 : td->td_extrasamples == td->td_samplesperpixel - 3 &&
891 21 : (td->td_sampleinfo[0] != EXTRASAMPLE_UNASSALPHA ||
892 : !bAlphaDistanceSameAsMainChannel))))
893 : { // rgb without alpha, or differente distance for alpha vs non-alpha
894 : // channels
895 63 : format.num_channels = 3;
896 63 : basic_info.num_color_channels = 3;
897 63 : basic_info.num_extra_channels = td->td_samplesperpixel - 3;
898 63 : basic_info.alpha_bits = 0;
899 63 : basic_info.alpha_exponent_bits = 0;
900 : }
901 : else
902 : { // fallback to gray without alpha and with eventual extra channels
903 22 : format.num_channels = 1;
904 22 : basic_info.num_color_channels = 1;
905 22 : basic_info.num_extra_channels = td->td_samplesperpixel - 1;
906 22 : basic_info.alpha_bits = 0;
907 22 : basic_info.alpha_exponent_bits = 0;
908 : }
909 : #ifndef HAVE_JxlExtraChannels
910 : if (basic_info.num_extra_channels > 1 ||
911 : (basic_info.num_extra_channels == 1 && !bAlphaEmbedded))
912 : {
913 : TIFFErrorExtR(
914 : tif, module,
915 : "JXL: INTERLEAVE=PIXEL does not support this combination of "
916 : "bands. Please upgrade libjxl to 0.8+");
917 : return 0;
918 : }
919 : #endif
920 : }
921 :
922 202 : if (sp->lossless)
923 : {
924 : #ifdef HAVE_JxlEncoderSetFrameLossless
925 166 : JxlEncoderSetFrameLossless(opts, TRUE);
926 : #else
927 : JxlEncoderOptionsSetLossless(opts, TRUE);
928 : #endif
929 : #ifdef HAVE_JxlEncoderSetFrameDistance
930 166 : JxlEncoderSetFrameDistance(opts, 0);
931 : #else
932 : JxlEncoderOptionsSetDistance(opts, 0);
933 : #endif
934 166 : basic_info.uses_original_profile = JXL_TRUE;
935 : }
936 : else
937 : {
938 : #ifdef HAVE_JxlEncoderSetFrameDistance
939 36 : if (JxlEncoderSetFrameDistance(opts, sp->distance) != JXL_ENC_SUCCESS)
940 : #else
941 : if (JxlEncoderOptionsSetDistance(opts, sp->distance) != JXL_ENC_SUCCESS)
942 : #endif
943 : {
944 0 : TIFFErrorExtR(tif, module, "JxlEncoderSetFrameDistance() failed");
945 0 : JxlEncoderDestroy(enc);
946 0 : return 0;
947 : }
948 : }
949 : #ifdef HAVE_JxlEncoderFrameSettingsSetOption
950 202 : if (JxlEncoderFrameSettingsSetOption(opts, JXL_ENC_FRAME_SETTING_EFFORT,
951 202 : sp->effort) != JXL_ENC_SUCCESS)
952 : #else
953 : if (JxlEncoderOptionsSetEffort(opts, sp->effort) != JXL_ENC_SUCCESS)
954 : #endif
955 : {
956 0 : TIFFErrorExtR(tif, module, "JxlEncoderFrameSettingsSetOption() failed");
957 0 : JxlEncoderDestroy(enc);
958 0 : return 0;
959 : }
960 :
961 202 : if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc, &basic_info))
962 : {
963 0 : TIFFErrorExtR(tif, module, "JxlEncoderSetBasicInfo() failed");
964 0 : JxlEncoderDestroy(enc);
965 0 : return 0;
966 : }
967 :
968 202 : JxlColorEncoding color_encoding = {0};
969 202 : JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray*/
970 202 : (td->td_planarconfig == PLANARCONFIG_SEPARATE ||
971 100 : basic_info.num_color_channels == 1));
972 202 : if (JXL_ENC_SUCCESS != JxlEncoderSetColorEncoding(enc, &color_encoding))
973 : {
974 0 : TIFFErrorExtR(tif, module, "JxlEncoderSetColorEncoding() failed");
975 0 : JxlEncoderDestroy(enc);
976 0 : return 0;
977 : }
978 :
979 202 : uint8_t *main_buffer = sp->uncompressed_buffer;
980 202 : unsigned int main_size = sp->uncompressed_size;
981 :
982 : #ifdef HAVE_JxlExtraChannels
983 202 : int nBytesPerSample = GetJXLDataTypeSize(format.data_type);
984 202 : if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
985 100 : (basic_info.num_extra_channels > 1 ||
986 86 : (basic_info.num_extra_channels == 1 && !bAlphaEmbedded)))
987 : {
988 32 : main_size = (sp->uncompressed_size / td->td_samplesperpixel);
989 32 : int nMainChannels = basic_info.num_color_channels;
990 32 : if (bAlphaEmbedded)
991 3 : nMainChannels++;
992 32 : main_size *= nMainChannels;
993 32 : main_buffer = _TIFFmallocExt(tif, main_size);
994 32 : if (main_buffer == NULL)
995 0 : return 0;
996 32 : int outChunkSize = nBytesPerSample * nMainChannels;
997 32 : int inStep = nBytesPerSample * td->td_samplesperpixel;
998 32 : uint8_t *cur_outbuffer = main_buffer;
999 32 : uint8_t *cur_inbuffer = sp->uncompressed_buffer;
1000 1286700 : for (; cur_outbuffer - main_buffer < main_size;
1001 1286670 : cur_outbuffer += outChunkSize, cur_inbuffer += inStep)
1002 : {
1003 1286670 : memcpy(cur_outbuffer, cur_inbuffer, outChunkSize);
1004 : }
1005 85 : for (int iChannel = nMainChannels; iChannel < td->td_samplesperpixel;
1006 53 : iChannel++)
1007 : {
1008 : JxlExtraChannelInfo extra_channel_info;
1009 53 : int channelType = JXL_CHANNEL_OPTIONAL;
1010 53 : const int iExtraChannel = iChannel - nMainChannels + bAlphaEmbedded;
1011 53 : if (iExtraChannel < td->td_extrasamples &&
1012 53 : (td->td_sampleinfo[iExtraChannel] == EXTRASAMPLE_UNASSALPHA ||
1013 32 : td->td_sampleinfo[iExtraChannel] == EXTRASAMPLE_ASSOCALPHA))
1014 : {
1015 21 : channelType = JXL_CHANNEL_ALPHA;
1016 : }
1017 53 : JxlEncoderInitExtraChannelInfo(channelType, &extra_channel_info);
1018 53 : extra_channel_info.bits_per_sample = basic_info.bits_per_sample;
1019 53 : extra_channel_info.exponent_bits_per_sample =
1020 53 : basic_info.exponent_bits_per_sample;
1021 53 : if (iExtraChannel < td->td_extrasamples &&
1022 53 : td->td_sampleinfo[iExtraChannel] == EXTRASAMPLE_ASSOCALPHA)
1023 : {
1024 0 : extra_channel_info.alpha_premultiplied = JXL_TRUE;
1025 : }
1026 :
1027 53 : if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelInfo(
1028 : enc, iExtraChannel, &extra_channel_info))
1029 : {
1030 0 : TIFFErrorExtR(tif, module,
1031 : "JxlEncoderSetExtraChannelInfo(%d) failed",
1032 : iChannel);
1033 0 : JxlEncoderDestroy(enc);
1034 0 : _TIFFfreeExt(tif, main_buffer);
1035 0 : return 0;
1036 : }
1037 : #if HAVE_JxlEncoderSetExtraChannelDistance
1038 53 : if (channelType == JXL_CHANNEL_ALPHA && sp->alpha_distance >= 0.0f)
1039 : {
1040 15 : if (JXL_ENC_SUCCESS !=
1041 15 : JxlEncoderSetExtraChannelDistance(opts, iExtraChannel,
1042 : sp->alpha_distance))
1043 : {
1044 0 : TIFFErrorExtR(
1045 : tif, module,
1046 : "JxlEncoderSetExtraChannelDistance(%d) failed",
1047 : iChannel);
1048 0 : JxlEncoderDestroy(enc);
1049 0 : _TIFFfreeExt(tif, main_buffer);
1050 0 : return 0;
1051 : }
1052 : }
1053 38 : else if (!(sp->lossless))
1054 : {
1055 : // By default libjxl applies lossless encoding for extra channels
1056 4 : if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelDistance(
1057 : opts, iExtraChannel, sp->distance))
1058 : {
1059 0 : TIFFErrorExtR(
1060 : tif, module,
1061 : "JxlEncoderSetExtraChannelDistance(%d) failed",
1062 : iChannel);
1063 0 : JxlEncoderDestroy(enc);
1064 0 : _TIFFfreeExt(tif, main_buffer);
1065 0 : return 0;
1066 : }
1067 : }
1068 : #endif
1069 : }
1070 : }
1071 : #endif
1072 :
1073 202 : int retCode =
1074 202 : JxlEncoderAddImageFrame(opts, &format, main_buffer, main_size);
1075 : // cleanup now
1076 202 : if (main_buffer != sp->uncompressed_buffer)
1077 : {
1078 32 : _TIFFfreeExt(tif, main_buffer);
1079 : }
1080 202 : if (retCode != JXL_ENC_SUCCESS)
1081 : {
1082 0 : TIFFErrorExtR(tif, module, "JxlEncoderAddImageFrame() failed");
1083 0 : JxlEncoderDestroy(enc);
1084 0 : return 0;
1085 : }
1086 :
1087 : #ifdef HAVE_JxlExtraChannels
1088 202 : if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
1089 100 : (basic_info.num_extra_channels > 1 ||
1090 86 : (basic_info.num_extra_channels == 1 && !bAlphaEmbedded)))
1091 : {
1092 32 : int nMainChannels = basic_info.num_color_channels;
1093 32 : if (bAlphaEmbedded)
1094 3 : nMainChannels++;
1095 32 : int extra_channel_size =
1096 32 : (sp->uncompressed_size / td->td_samplesperpixel);
1097 32 : uint8_t *extra_channel_buffer = _TIFFmallocExt(tif, extra_channel_size);
1098 32 : if (extra_channel_buffer == NULL)
1099 0 : return 0;
1100 32 : int inStep = nBytesPerSample * td->td_samplesperpixel;
1101 32 : int outStep = nBytesPerSample;
1102 85 : for (int iChannel = nMainChannels; iChannel < td->td_samplesperpixel;
1103 53 : iChannel++)
1104 : {
1105 53 : uint8_t *cur_outbuffer = extra_channel_buffer;
1106 53 : uint8_t *cur_inbuffer =
1107 53 : sp->uncompressed_buffer + iChannel * outStep;
1108 2272160 : for (; cur_outbuffer - extra_channel_buffer < extra_channel_size;
1109 2272110 : cur_outbuffer += outStep, cur_inbuffer += inStep)
1110 : {
1111 2272110 : memcpy(cur_outbuffer, cur_inbuffer, outStep);
1112 : }
1113 106 : if (JxlEncoderSetExtraChannelBuffer(
1114 : opts, &format, extra_channel_buffer, extra_channel_size,
1115 : (bAlphaEmbedded)
1116 3 : ? iChannel - nMainChannels + 1
1117 50 : : iChannel - nMainChannels) != JXL_ENC_SUCCESS)
1118 : {
1119 0 : TIFFErrorExtR(tif, module,
1120 : "JxlEncoderSetExtraChannelBuffer() failed");
1121 0 : _TIFFfreeExt(tif, extra_channel_buffer);
1122 0 : JxlEncoderDestroy(enc);
1123 0 : return 0;
1124 : }
1125 : }
1126 32 : _TIFFfreeExt(tif, extra_channel_buffer);
1127 : }
1128 : #endif
1129 :
1130 202 : JxlEncoderCloseInput(enc);
1131 :
1132 : while (TRUE)
1133 0 : {
1134 202 : size_t len = (size_t)tif->tif_rawdatasize;
1135 202 : uint8_t *buf = (uint8_t *)tif->tif_rawdata;
1136 : JxlEncoderStatus process_result =
1137 202 : JxlEncoderProcessOutput(enc, &buf, &len);
1138 202 : if (process_result == JXL_ENC_ERROR)
1139 : {
1140 0 : TIFFErrorExtR(tif, module, "JxlEncoderProcessOutput() failed");
1141 0 : JxlEncoderDestroy(enc);
1142 0 : return 0;
1143 : }
1144 202 : tif->tif_rawcc = tif->tif_rawdatasize - len;
1145 202 : if (!TIFFFlushData1(tif))
1146 : {
1147 0 : JxlEncoderDestroy(enc);
1148 0 : return 0;
1149 : }
1150 202 : if (process_result != JXL_ENC_NEED_MORE_OUTPUT)
1151 202 : break;
1152 : }
1153 :
1154 202 : JxlEncoderDestroy(enc);
1155 202 : return 1;
1156 : }
1157 :
1158 539 : static void JXLCleanup(TIFF *tif)
1159 : {
1160 539 : JXLState *sp = LState(tif);
1161 :
1162 539 : assert(sp != 0);
1163 :
1164 539 : tif->tif_tagmethods.vgetfield = sp->vgetparent;
1165 539 : tif->tif_tagmethods.vsetfield = sp->vsetparent;
1166 :
1167 539 : _TIFFfreeExt(tif, sp->uncompressed_buffer);
1168 :
1169 539 : if (sp->decoder)
1170 115 : JxlDecoderDestroy(sp->decoder);
1171 :
1172 539 : _TIFFfreeExt(tif, sp);
1173 539 : tif->tif_data = NULL;
1174 :
1175 539 : _TIFFSetDefaultCompressionState(tif);
1176 539 : }
1177 :
1178 : static const TIFFField JXLFields[] = {
1179 : {TIFFTAG_JXL_LOSSYNESS, 0, 0, TIFF_ANY, 0, TIFF_SETGET_UINT32,
1180 : TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "Lossyness", NULL},
1181 : {TIFFTAG_JXL_EFFORT, 0, 0, TIFF_ANY, 0, TIFF_SETGET_UINT32,
1182 : TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "Effort", NULL},
1183 : {TIFFTAG_JXL_DISTANCE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_FLOAT,
1184 : TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "Distance", NULL},
1185 : {TIFFTAG_JXL_ALPHA_DISTANCE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_FLOAT,
1186 : TIFF_SETGET_UNDEFINED, FIELD_PSEUDO, FALSE, FALSE, "AlphaDistance", NULL},
1187 : };
1188 :
1189 5723 : static int JXLVSetField(TIFF *tif, uint32_t tag, va_list ap)
1190 : {
1191 : static const char module[] = "JXLVSetField";
1192 5723 : JXLState *sp = LState(tif);
1193 :
1194 5723 : switch (tag)
1195 : {
1196 270 : case TIFFTAG_JXL_LOSSYNESS:
1197 : {
1198 270 : uint32_t lossyness = va_arg(ap, uint32_t);
1199 270 : if (lossyness == JXL_LOSSLESS)
1200 209 : sp->lossless = TRUE;
1201 61 : else if (lossyness == JXL_LOSSY)
1202 61 : sp->lossless = FALSE;
1203 : else
1204 : {
1205 0 : TIFFErrorExtR(tif, module, "Invalid value for Lossyness: %u",
1206 : lossyness);
1207 0 : return 0;
1208 : }
1209 270 : return 1;
1210 : }
1211 :
1212 265 : case TIFFTAG_JXL_EFFORT:
1213 : {
1214 265 : uint32_t effort = va_arg(ap, uint32_t);
1215 265 : if (effort < 1 || effort > 9)
1216 : {
1217 0 : TIFFErrorExtR(tif, module, "Invalid value for Effort: %u",
1218 : effort);
1219 0 : return 0;
1220 : }
1221 265 : sp->effort = effort;
1222 265 : return 1;
1223 : }
1224 :
1225 266 : case TIFFTAG_JXL_DISTANCE:
1226 : {
1227 266 : float distance = (float)va_arg(ap, double);
1228 266 : if (distance < 0 || distance > 15)
1229 : {
1230 0 : TIFFErrorExtR(tif, module, "Invalid value for Distance: %f",
1231 : distance);
1232 0 : return 0;
1233 : }
1234 266 : sp->distance = distance;
1235 266 : return 1;
1236 : }
1237 :
1238 266 : case TIFFTAG_JXL_ALPHA_DISTANCE:
1239 : {
1240 266 : float alpha_distance = (float)va_arg(ap, double);
1241 266 : if (alpha_distance != -1 &&
1242 6 : (alpha_distance < 0 || alpha_distance > 15))
1243 : {
1244 0 : TIFFErrorExtR(tif, module,
1245 : "Invalid value for AlphaDistance: %f",
1246 : alpha_distance);
1247 0 : return 0;
1248 : }
1249 266 : sp->alpha_distance = alpha_distance;
1250 266 : return 1;
1251 : }
1252 :
1253 4656 : default:
1254 : {
1255 4656 : return (*sp->vsetparent)(tif, tag, ap);
1256 : }
1257 : }
1258 : /*NOTREACHED*/
1259 : }
1260 :
1261 8185 : static int JXLVGetField(TIFF *tif, uint32_t tag, va_list ap)
1262 : {
1263 8185 : JXLState *sp = LState(tif);
1264 :
1265 8185 : switch (tag)
1266 : {
1267 0 : case TIFFTAG_JXL_LOSSYNESS:
1268 0 : *va_arg(ap, uint32_t *) = sp->lossless ? JXL_LOSSLESS : JXL_LOSSY;
1269 0 : break;
1270 0 : case TIFFTAG_JXL_EFFORT:
1271 0 : *va_arg(ap, uint32_t *) = sp->effort;
1272 0 : break;
1273 0 : case TIFFTAG_JXL_DISTANCE:
1274 0 : *va_arg(ap, float *) = sp->distance;
1275 0 : break;
1276 0 : case TIFFTAG_JXL_ALPHA_DISTANCE:
1277 0 : *va_arg(ap, float *) = sp->alpha_distance;
1278 0 : break;
1279 8185 : default:
1280 8185 : return (*sp->vgetparent)(tif, tag, ap);
1281 : }
1282 0 : return 1;
1283 : }
1284 :
1285 539 : int TIFFInitJXL(TIFF *tif, int scheme)
1286 : {
1287 : static const char module[] = "TIFFInitJXL";
1288 : JXLState *sp;
1289 :
1290 : (void)scheme;
1291 539 : assert(scheme == COMPRESSION_JXL || scheme == COMPRESSION_JXL_DNG_1_7);
1292 :
1293 : /*
1294 : * Merge codec-specific tag information.
1295 : */
1296 539 : if (!_TIFFMergeFields(tif, JXLFields, TIFFArrayCount(JXLFields)))
1297 : {
1298 0 : TIFFErrorExtR(tif, module, "Merging JXL codec-specific tags failed");
1299 0 : return 0;
1300 : }
1301 :
1302 : /*
1303 : * Allocate state block so tag methods have storage to record values.
1304 : */
1305 539 : tif->tif_data = (uint8_t *)_TIFFcallocExt(tif, 1, sizeof(JXLState));
1306 539 : if (tif->tif_data == NULL)
1307 0 : goto bad;
1308 539 : sp = LState(tif);
1309 :
1310 : /*
1311 : * Override parent get/set field methods.
1312 : */
1313 539 : sp->vgetparent = tif->tif_tagmethods.vgetfield;
1314 539 : tif->tif_tagmethods.vgetfield = JXLVGetField; /* hook for codec tags */
1315 539 : sp->vsetparent = tif->tif_tagmethods.vsetfield;
1316 539 : tif->tif_tagmethods.vsetfield = JXLVSetField; /* hook for codec tags */
1317 :
1318 : /*
1319 : * Install codec methods.
1320 : */
1321 539 : tif->tif_fixuptags = JXLFixupTags;
1322 539 : tif->tif_setupdecode = JXLSetupDecode;
1323 539 : tif->tif_predecode = JXLPreDecode;
1324 539 : tif->tif_decoderow = JXLDecode;
1325 539 : tif->tif_decodestrip = JXLDecode;
1326 539 : tif->tif_decodetile = JXLDecode;
1327 539 : tif->tif_setupencode = JXLSetupEncode;
1328 539 : tif->tif_preencode = JXLPreEncode;
1329 539 : tif->tif_postencode = JXLPostEncode;
1330 539 : tif->tif_encoderow = JXLEncode;
1331 539 : tif->tif_encodestrip = JXLEncode;
1332 539 : tif->tif_encodetile = JXLEncode;
1333 539 : tif->tif_cleanup = JXLCleanup;
1334 :
1335 : /* Default values for codec-specific fields */
1336 539 : sp->decoder = NULL;
1337 :
1338 539 : sp->state = 0;
1339 539 : sp->lossless = TRUE;
1340 539 : sp->effort = 5;
1341 539 : sp->distance = 1.0;
1342 539 : sp->alpha_distance = -1.0;
1343 :
1344 539 : return 1;
1345 0 : bad:
1346 0 : TIFFErrorExtR(tif, module, "No space for JXL state block");
1347 0 : return 0;
1348 : }
|