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