• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15797738239

21 Jun 2025 04:51PM UTC coverage: 71.076% (+0.001%) from 71.075%
15797738239

Pull #12622

github

web-flow
Merge 19559fd15 into 645a00347
Pull Request #12622: VSI Win32: implement OpenDir()

574181 of 807840 relevant lines covered (71.08%)

249831.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.33
/frmts/gtiff/tif_jxl.c
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
static int GetJXLDataType(TIFF *tif)
927✔
72
{
73
    TIFFDirectory *td = &tif->tif_dir;
927✔
74
    static const char module[] = "GetJXLDataType";
75

76
    if (td->td_sampleformat == SAMPLEFORMAT_UINT && td->td_bitspersample == 8)
927✔
77
    {
78
        return JXL_TYPE_UINT8;
523✔
79
    }
80

81
    if (td->td_sampleformat == SAMPLEFORMAT_UINT && td->td_bitspersample == 16)
404✔
82
    {
83
        return JXL_TYPE_UINT16;
203✔
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
    if (td->td_sampleformat == SAMPLEFORMAT_IEEEFP &&
201✔
96
        td->td_bitspersample == 32)
201✔
97
    {
98
        return JXL_TYPE_FLOAT;
196✔
99
    }
100

101
    if (td->td_sampleformat == SAMPLEFORMAT_IEEEFP &&
5✔
102
        td->td_bitspersample == 16)
5✔
103
    {
104
        return JXL_TYPE_FLOAT16;
5✔
105
    }
106

107
    TIFFErrorExtR(
×
108
        tif, module,
109
        "Unsupported combination of SampleFormat(=%d) and BitsPerSample(=%d)",
110
        td->td_sampleformat, td->td_bitspersample);
×
111
    return -1;
×
112
}
113

114
static int GetJXLDataTypeSize(JxlDataType dtype)
816✔
115
{
116
    switch (dtype)
816✔
117
    {
118
        case JXL_TYPE_UINT8:
454✔
119
            return 1;
454✔
120
        case JXL_TYPE_UINT16:
182✔
121
            return 2;
182✔
122
        case JXL_TYPE_FLOAT:
176✔
123
            return 4;
176✔
124
        case JXL_TYPE_FLOAT16:
4✔
125
            return 2;
4✔
126
        default:
×
127
            return 0;
×
128
    }
129
}
130

131
static int JXLFixupTags(TIFF *tif)
385✔
132
{
133
    (void)tif;
134
    return 1;
385✔
135
}
136

137
static int JXLSetupDecode(TIFF *tif)
117✔
138
{
139
    JXLState *sp = DecoderState(tif);
117✔
140

141
    assert(sp != NULL);
117✔
142

143
    /* if we were last encoding, terminate this mode */
144
    if (sp->state & LSTATE_INIT_ENCODE)
117✔
145
    {
146
        sp->state = 0;
2✔
147
    }
148

149
    sp->state |= LSTATE_INIT_DECODE;
117✔
150
    return 1;
117✔
151
}
152

153
static int SetupUncompressedBuffer(TIFF *tif, JXLState *sp, const char *module)
408✔
154
{
155
    TIFFDirectory *td = &tif->tif_dir;
408✔
156
    uint64_t new_size_64;
157
    uint64_t new_alloc_64;
158
    unsigned int new_size;
159
    unsigned int new_alloc;
160

161
    sp->uncompressed_offset = 0;
408✔
162

163
    if (isTiled(tif))
408✔
164
    {
165
        sp->segment_width = td->td_tilewidth;
288✔
166
        sp->segment_height = td->td_tilelength;
288✔
167
    }
168
    else
169
    {
170
        sp->segment_width = td->td_imagewidth;
120✔
171
        sp->segment_height = td->td_imagelength - tif->tif_row;
120✔
172
        if (sp->segment_height > td->td_rowsperstrip)
120✔
173
            sp->segment_height = td->td_rowsperstrip;
29✔
174
    }
175

176
    JxlDataType dtype = GetJXLDataType(tif);
408✔
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
    int nBytesPerSample = GetJXLDataTypeSize(dtype);
408✔
185
    new_size_64 =
408✔
186
        (uint64_t)sp->segment_width * sp->segment_height * nBytesPerSample;
408✔
187
    if (td->td_planarconfig == PLANARCONFIG_CONTIG)
408✔
188
    {
189
        new_size_64 *= td->td_samplesperpixel;
204✔
190
    }
191

192
    new_size = (unsigned int)new_size_64;
408✔
193
    sp->uncompressed_size = new_size;
408✔
194

195
    /* add some margin */
196
    new_alloc_64 = 100 + new_size_64 + new_size_64 / 3;
408✔
197
    new_alloc = (unsigned int)new_alloc_64;
408✔
198
    if (new_alloc != new_alloc_64)
408✔
199
    {
200
        TIFFErrorExtR(tif, module, "Too large uncompressed strip/tile");
×
201
        _TIFFfreeExt(tif, sp->uncompressed_buffer);
×
202
        sp->uncompressed_buffer = 0;
×
203
        sp->uncompressed_alloc = 0;
×
204
        return 0;
×
205
    }
206

207
    if (sp->uncompressed_alloc < new_alloc)
408✔
208
    {
209
        _TIFFfreeExt(tif, sp->uncompressed_buffer);
226✔
210
        sp->uncompressed_buffer = _TIFFmallocExt(tif, new_alloc);
226✔
211
        if (!sp->uncompressed_buffer)
226✔
212
        {
213
            TIFFErrorExtR(tif, module, "Cannot allocate buffer");
×
214
            _TIFFfreeExt(tif, sp->uncompressed_buffer);
×
215
            sp->uncompressed_buffer = 0;
×
216
            sp->uncompressed_alloc = 0;
×
217
            return 0;
×
218
        }
219
        sp->uncompressed_alloc = new_alloc;
226✔
220
    }
221

222
    return 1;
408✔
223
}
224

225
/*
226
 * Setup state for decoding a strip.
227
 */
228
static int JXLPreDecode(TIFF *tif, uint16_t s)
206✔
229
{
230
    static const char module[] = "JXLPreDecode";
231
    JXLState *sp = DecoderState(tif);
206✔
232
    TIFFDirectory *td = &tif->tif_dir;
206✔
233

234
    (void)s;
235
    assert(sp != NULL);
206✔
236
    if (sp->state != LSTATE_INIT_DECODE)
206✔
237
        tif->tif_setupdecode(tif);
2✔
238

239
    const int jxlDataType = GetJXLDataType(tif);
206✔
240
    if (jxlDataType < 0)
206✔
241
        return 0;
×
242

243
    if (!SetupUncompressedBuffer(tif, sp, module))
206✔
244
        return 0;
×
245

246
    if (sp->decoder == NULL)
206✔
247
    {
248
        sp->decoder = JxlDecoderCreate(NULL);
117✔
249
        if (sp->decoder == NULL)
117✔
250
        {
251
            TIFFErrorExtR(tif, module, "JxlDecoderCreate() failed");
×
252
            return 0;
×
253
        }
254
    }
255
    else
256
    {
257
        JxlDecoderReset(sp->decoder);
89✔
258
    }
259

260
    JxlDecoderStatus status;
261
    status = JxlDecoderSubscribeEvents(sp->decoder,
206✔
262
                                       JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE);
263
    if (status != JXL_DEC_SUCCESS)
206✔
264
    {
265
        TIFFErrorExtR(tif, module, "JxlDecoderSubscribeEvents() failed");
×
266
        return 0;
×
267
    }
268

269
    status = JxlDecoderSetInput(sp->decoder, (const uint8_t *)tif->tif_rawcp,
206✔
270
                                (size_t)tif->tif_rawcc);
206✔
271
    if (status != JXL_DEC_SUCCESS)
206✔
272
    {
273
        TIFFErrorExtR(tif, module, "JxlDecoderSetInput() failed with %d",
×
274
                      status);
275
        return 0;
×
276
    }
277

278
    status = JxlDecoderProcessInput(sp->decoder);
206✔
279
    if (status != JXL_DEC_BASIC_INFO)
206✔
280
    {
281
        TIFFErrorExtR(tif, module, "JxlDecoderProcessInput() failed with %d",
×
282
                      status);
283
        JxlDecoderReleaseInput(sp->decoder);
×
284
        return 0;
×
285
    }
286

287
    JxlBasicInfo info;
288
    status = JxlDecoderGetBasicInfo(sp->decoder, &info);
206✔
289
    if (status != JXL_DEC_SUCCESS)
206✔
290
    {
291
        TIFFErrorExtR(tif, module, "JxlDecoderGetBasicInfo() failed with %d",
×
292
                      status);
293
        JxlDecoderReleaseInput(sp->decoder);
×
294
        return 0;
×
295
    }
296

297
    if (sp->segment_width != info.xsize)
206✔
298
    {
299
        TIFFErrorExtR(tif, module,
×
300
                      "JXL basic info xsize = %d, whereas %u was expected",
301
                      info.xsize, sp->segment_width);
302
        JxlDecoderReleaseInput(sp->decoder);
×
303
        return 0;
×
304
    }
305

306
    if (sp->segment_height != info.ysize)
206✔
307
    {
308
        TIFFErrorExtR(tif, module,
×
309
                      "JXL basic info ysize = %d, whereas %u was expected",
310
                      info.ysize, sp->segment_height);
311
        JxlDecoderReleaseInput(sp->decoder);
×
312
        return 0;
×
313
    }
314

315
    if (td->td_bitspersample != info.bits_per_sample)
206✔
316
    {
317
        TIFFErrorExtR(
×
318
            tif, module,
319
            "JXL basic info bits_per_sample = %d, whereas %d was expected",
320
            info.bits_per_sample, td->td_bitspersample);
×
321
        JxlDecoderReleaseInput(sp->decoder);
×
322
        return 0;
×
323
    }
324

325
    if (td->td_planarconfig == PLANARCONFIG_CONTIG)
206✔
326
    {
327
        if (info.num_color_channels + info.num_extra_channels !=
104✔
328
            td->td_samplesperpixel)
104✔
329
        {
330
            TIFFErrorExtR(tif, module,
×
331
                          "JXL basic info invalid number of channels");
332
            JxlDecoderReleaseInput(sp->decoder);
×
333
            return 0;
×
334
        }
335
    }
336
    else
337
    {
338
        if (info.num_color_channels != 1 || info.alpha_bits > 0 ||
102✔
339
            info.num_extra_channels > 0)
102✔
340
        {
341
            TIFFErrorExtR(tif, module,
×
342
                          "JXL basic info invalid number of channels");
343
            JxlDecoderReleaseInput(sp->decoder);
×
344
            return 0;
×
345
        }
346
    }
347

348
    JxlPixelFormat format = {0};
206✔
349
    format.data_type = jxlDataType;
206✔
350
    format.endianness = JXL_NATIVE_ENDIAN;
206✔
351
    format.align = 0;
206✔
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
    int bAlphaEmbedded = 0;
206✔
362
    if (info.alpha_bits != 0)
206✔
363
    {
364
        if ((info.num_color_channels == 3 || info.num_color_channels == 1) &&
37✔
365
            (info.num_extra_channels == 1))
37✔
366
        {
367
            bAlphaEmbedded = 1;
28✔
368
        }
369
        else if (info.num_color_channels == 3 && info.num_extra_channels > 1)
9✔
370
        {
371
            JxlExtraChannelInfo extra_channel_info;
372
            memset(&extra_channel_info, 0, sizeof(extra_channel_info));
6✔
373
            if (JxlDecoderGetExtraChannelInfo(
6✔
374
                    sp->decoder, 0, &extra_channel_info) == JXL_DEC_SUCCESS &&
6✔
375
                extra_channel_info.type == JXL_CHANNEL_ALPHA)
6✔
376
            {
377
                bAlphaEmbedded = 1;
3✔
378
            }
379
        }
380
    }
381
    uint32_t nFirstExtraChannel = (bAlphaEmbedded) ? 1 : 0;
206✔
382
    unsigned int main_buffer_size = sp->uncompressed_size;
206✔
383
    unsigned int channel_size = main_buffer_size / td->td_samplesperpixel;
206✔
384
    uint8_t *extra_channel_buffer = NULL;
206✔
385

386
    int nBytesPerSample = GetJXLDataTypeSize(format.data_type);
206✔
387

388
    if (nFirstExtraChannel < info.num_extra_channels)
206✔
389
    {
390
        int nExtraChannelsToExtract =
17✔
391
            info.num_extra_channels - nFirstExtraChannel;
17✔
392
        format.num_channels = 1;
17✔
393
        main_buffer_size =
17✔
394
            channel_size * (info.num_color_channels + (bAlphaEmbedded ? 1 : 0));
17✔
395
        extra_channel_buffer =
396
            _TIFFmallocExt(tif, channel_size * nExtraChannelsToExtract);
17✔
397
        if (extra_channel_buffer == NULL)
17✔
398
            return 0;
×
399
        for (int i = 0; i < nExtraChannelsToExtract; ++i)
55✔
400
        {
401
            size_t buffer_size;
402
            const int iCorrectedIdx = i + nFirstExtraChannel;
38✔
403

404
            if (JxlDecoderExtraChannelBufferSize(sp->decoder, &format,
38✔
405
                                                 &buffer_size, iCorrectedIdx) !=
406
                JXL_DEC_SUCCESS)
407
            {
408
                TIFFErrorExtR(tif, module,
×
409
                              "JxlDecoderExtraChannelBufferSize failed()");
410
                _TIFFfreeExt(tif, extra_channel_buffer);
×
411
                return 0;
×
412
            }
413
            if (buffer_size != channel_size)
38✔
414
            {
415
                TIFFErrorExtR(tif, module,
×
416
                              "JxlDecoderExtraChannelBufferSize returned "
417
                              "%" TIFF_SIZE_FORMAT ", expecting %u",
418
                              buffer_size, channel_size);
419
                _TIFFfreeExt(tif, extra_channel_buffer);
×
420
                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
            if (JxlDecoderSetExtraChannelBuffer(
38✔
477
                    sp->decoder, &format,
478
                    extra_channel_buffer + i * channel_size, channel_size,
38✔
479
                    i + nFirstExtraChannel) != JXL_DEC_SUCCESS)
480
            {
481
                TIFFErrorExtR(tif, module,
×
482
                              "JxlDecoderSetExtraChannelBuffer failed()");
483
                _TIFFfreeExt(tif, extra_channel_buffer);
×
484
                return 0;
×
485
            }
486
        }
487
    }
488

489
    format.num_channels = info.num_color_channels;
206✔
490
    if (bAlphaEmbedded)
206✔
491
        format.num_channels++;
31✔
492

493
    status = JxlDecoderProcessInput(sp->decoder);
206✔
494
    if (status != JXL_DEC_NEED_IMAGE_OUT_BUFFER)
206✔
495
    {
496
        TIFFErrorExtR(tif, module,
×
497
                      "JxlDecoderProcessInput() (second call) failed with %d",
498
                      status);
499
        JxlDecoderReleaseInput(sp->decoder);
×
500
        _TIFFfreeExt(tif, extra_channel_buffer);
×
501
        return 0;
×
502
    }
503

504
    status = JxlDecoderSetImageOutBuffer(
206✔
505
        sp->decoder, &format, sp->uncompressed_buffer, main_buffer_size);
206✔
506
    if (status != JXL_DEC_SUCCESS)
206✔
507
    {
508
        TIFFErrorExtR(tif, module,
×
509
                      "JxlDecoderSetImageOutBuffer() failed with %d", status);
510
        JxlDecoderReleaseInput(sp->decoder);
×
511
        _TIFFfreeExt(tif, extra_channel_buffer);
×
512
        return 0;
×
513
    }
514

515
    status = JxlDecoderProcessInput(sp->decoder);
206✔
516
    if (status != JXL_DEC_FULL_IMAGE)
205✔
517
    {
518
        TIFFErrorExtR(tif, module,
×
519
                      "JxlDecoderProcessInput() (third call) failed with %d",
520
                      status);
521
        JxlDecoderReleaseInput(sp->decoder);
×
522
        _TIFFfreeExt(tif, extra_channel_buffer);
×
523
        return 0;
×
524
    }
525
    if (nFirstExtraChannel < info.num_extra_channels)
205✔
526
    {
527
        // first reorder the main buffer
528
        const int nMainChannels = bAlphaEmbedded ? info.num_color_channels + 1
20✔
529
                                                 : info.num_color_channels;
17✔
530
        const unsigned int mainPixSize = nMainChannels * nBytesPerSample;
17✔
531
        const unsigned int fullPixSize =
17✔
532
            td->td_samplesperpixel * nBytesPerSample;
17✔
533
        assert(fullPixSize > mainPixSize);
17✔
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
        const unsigned int nNumPixels = info.xsize * info.ysize;
17✔
541
        unsigned int outOff = sp->uncompressed_size - fullPixSize;
17✔
542
        unsigned int inOff = main_buffer_size - mainPixSize;
17✔
543
        const unsigned int kThreshold =
17✔
544
            (fullPixSize - 1) / (fullPixSize - mainPixSize);
17✔
545
        if (mainPixSize == 1)
17✔
546
        {
547
            for (unsigned int k = kThreshold; k < nNumPixels; ++k)
131,872✔
548
            {
549
                memcpy(sp->uncompressed_buffer + outOff,
131,868✔
550
                       sp->uncompressed_buffer + inOff, /*mainPixSize=*/1);
131,868✔
551
                inOff -= /*mainPixSize=*/1;
131,868✔
552
                outOff -= fullPixSize;
131,868✔
553
            }
554
        }
555
        else if (mainPixSize == 2)
13✔
556
        {
557
            for (unsigned int k = kThreshold; k < nNumPixels; ++k)
131,072✔
558
            {
559
                memcpy(sp->uncompressed_buffer + outOff,
131,070✔
560
                       sp->uncompressed_buffer + inOff, /*mainPixSize=*/2);
131,070✔
561
                inOff -= /*mainPixSize=*/2;
131,070✔
562
                outOff -= fullPixSize;
131,070✔
563
            }
564
        }
565
        else if (mainPixSize == 3)
11✔
566
        {
567
            for (unsigned int k = kThreshold; k < nNumPixels; ++k)
131,070✔
568
            {
569
                memcpy(sp->uncompressed_buffer + outOff,
131,068✔
570
                       sp->uncompressed_buffer + inOff, /*mainPixSize=*/3);
131,068✔
571
                inOff -= /*mainPixSize=*/3;
131,068✔
572
                outOff -= fullPixSize;
131,068✔
573
            }
574
        }
575
        else if (mainPixSize == 4)
9✔
576
        {
577
            for (unsigned int k = kThreshold; k < nNumPixels; ++k)
196,605✔
578
            {
579
                memcpy(sp->uncompressed_buffer + outOff,
196,602✔
580
                       sp->uncompressed_buffer + inOff, /*mainPixSize=*/4);
196,602✔
581
                inOff -= /*mainPixSize=*/4;
196,602✔
582
                outOff -= fullPixSize;
196,602✔
583
            }
584
        }
585
        else if (mainPixSize == 3 * 2)
6✔
586
        {
587
            for (unsigned int k = kThreshold; k < nNumPixels; ++k)
131,070✔
588
            {
589
                memcpy(sp->uncompressed_buffer + outOff,
131,068✔
590
                       sp->uncompressed_buffer + inOff, /*mainPixSize=*/3 * 2);
131,068✔
591
                inOff -= /*mainPixSize=*/3 * 2;
131,068✔
592
                outOff -= fullPixSize;
131,068✔
593
            }
594
        }
595
        else if (mainPixSize == 4 * 2)
4✔
596
        {
597
            for (unsigned int k = kThreshold; k < nNumPixels; ++k)
65,533✔
598
            {
599
                memcpy(sp->uncompressed_buffer + outOff,
65,532✔
600
                       sp->uncompressed_buffer + inOff, /*mainPixSize=*/4 * 2);
65,532✔
601
                inOff -= /*mainPixSize=*/4 * 2;
65,532✔
602
                outOff -= fullPixSize;
65,532✔
603
            }
604
        }
605
        else
606
        {
607
            for (unsigned int k = kThreshold; k < nNumPixels; ++k)
196,603✔
608
            {
609
                memcpy(sp->uncompressed_buffer + outOff,
196,600✔
610
                       sp->uncompressed_buffer + inOff, mainPixSize);
196,600✔
611
                inOff -= mainPixSize;
196,600✔
612
                outOff -= fullPixSize;
196,600✔
613
            }
614
        }
615
        /* Last iterations need memmove() because of overlapping between */
616
        /* source and target regions. */
617
        for (unsigned int k = kThreshold; k > 1;)
32✔
618
        {
619
            --k;
15✔
620
            memmove(sp->uncompressed_buffer + outOff,
15✔
621
                    sp->uncompressed_buffer + inOff, mainPixSize);
15✔
622
            inOff -= mainPixSize;
15✔
623
            outOff -= fullPixSize;
15✔
624
        }
625
        // then copy over the data from the extra_channel_buffer
626
        const int nExtraChannelsToExtract =
17✔
627
            info.num_extra_channels - nFirstExtraChannel;
17✔
628
        for (int i = 0; i < nExtraChannelsToExtract; ++i)
55✔
629
        {
630
            outOff = (i + nMainChannels) * nBytesPerSample;
38✔
631
            uint8_t *channel_buffer = extra_channel_buffer + i * channel_size;
38✔
632
            if (nBytesPerSample == 1)
38✔
633
            {
634
                for (; outOff < sp->uncompressed_size;
658,578✔
635
                     outOff += fullPixSize,
658,560✔
636
                     channel_buffer += /*nBytesPerSample=*/1)
658,560✔
637
                {
638
                    memcpy(sp->uncompressed_buffer + outOff, channel_buffer,
658,560✔
639
                           /*nBytesPerSample=*/1);
640
                }
641
            }
642
            else if (nBytesPerSample == 2)
20✔
643
            {
644
                for (; outOff < sp->uncompressed_size;
655,370✔
645
                     outOff += fullPixSize,
655,360✔
646
                     channel_buffer += /*nBytesPerSample=*/2)
655,360✔
647
                {
648
                    memcpy(sp->uncompressed_buffer + outOff, channel_buffer,
655,360✔
649
                           /*nBytesPerSample=*/2);
650
                }
651
            }
652
            else
653
            {
654
                assert(nBytesPerSample == 4);
10✔
655
                for (; outOff < sp->uncompressed_size;
655,370✔
656
                     outOff += fullPixSize, channel_buffer += nBytesPerSample)
655,360✔
657
                {
658
                    memcpy(sp->uncompressed_buffer + outOff, channel_buffer,
655,360✔
659
                           nBytesPerSample);
660
                }
661
            }
662
        }
663
        _TIFFfreeExt(tif, extra_channel_buffer);
17✔
664
    }
665

666
    /*const size_t nRemaining = */ JxlDecoderReleaseInput(sp->decoder);
205✔
667
    /*if( nRemaining != 0 )
668
    {
669
        TIFFErrorExtR(tif, module,
670
                     "JxlDecoderReleaseInput(): %u input bytes remaining",
671
                     (unsigned)nRemaining);
672
    }*/
673

674
    return 1;
206✔
675
}
676

677
/*
678
 * Decode a strip, tile or scanline.
679
 */
680
static int JXLDecode(TIFF *tif, uint8_t *op, tmsize_t occ, uint16_t s)
206✔
681
{
682
    static const char module[] = "JXLDecode";
683
    JXLState *sp = DecoderState(tif);
206✔
684

685
    (void)s;
686
    assert(sp != NULL);
206✔
687
    assert(sp->state == LSTATE_INIT_DECODE);
206✔
688

689
    if (sp->uncompressed_buffer == 0)
206✔
690
    {
691
        TIFFErrorExtR(tif, module, "Uncompressed buffer not allocated");
×
692
        return 0;
×
693
    }
694

695
    if ((uint64_t)sp->uncompressed_offset + (uint64_t)occ >
206✔
696
        sp->uncompressed_size)
206✔
697
    {
698
        TIFFErrorExtR(tif, module, "Too many bytes read");
×
699
        return 0;
×
700
    }
701

702
    memcpy(op, sp->uncompressed_buffer + sp->uncompressed_offset, occ);
206✔
703
    sp->uncompressed_offset += (unsigned)occ;
206✔
704

705
    return 1;
206✔
706
}
707

708
static int JXLSetupEncode(TIFF *tif)
111✔
709
{
710
    JXLState *sp = EncoderState(tif);
111✔
711

712
    assert(sp != NULL);
111✔
713
    if (sp->state & LSTATE_INIT_DECODE)
111✔
714
    {
715
        sp->state = 0;
×
716
    }
717

718
    if (GetJXLDataType(tif) < 0)
111✔
719
        return 0;
×
720

721
    sp->state |= LSTATE_INIT_ENCODE;
111✔
722

723
    return 1;
111✔
724
}
725

726
/*
727
 * Reset encoding state at the start of a strip.
728
 */
729
static int JXLPreEncode(TIFF *tif, uint16_t s)
202✔
730
{
731
    static const char module[] = "JXLPreEncode";
732
    JXLState *sp = EncoderState(tif);
202✔
733

734
    (void)s;
735
    assert(sp != NULL);
202✔
736
    if (sp->state != LSTATE_INIT_ENCODE)
202✔
737
        tif->tif_setupencode(tif);
×
738

739
    if (!SetupUncompressedBuffer(tif, sp, module))
202✔
740
        return 0;
×
741

742
    return 1;
202✔
743
}
744

745
/*
746
 * Encode a chunk of pixels.
747
 */
748
static int JXLEncode(TIFF *tif, uint8_t *bp, tmsize_t cc, uint16_t s)
202✔
749
{
750
    static const char module[] = "JXLEncode";
751
    JXLState *sp = EncoderState(tif);
202✔
752

753
    (void)s;
754
    assert(sp != NULL);
202✔
755
    assert(sp->state == LSTATE_INIT_ENCODE);
202✔
756

757
    if ((uint64_t)sp->uncompressed_offset + (uint64_t)cc >
202✔
758
        sp->uncompressed_size)
202✔
759
    {
760
        TIFFErrorExtR(tif, module, "Too many bytes written");
×
761
        return 0;
×
762
    }
763

764
    memcpy(sp->uncompressed_buffer + sp->uncompressed_offset, bp, cc);
202✔
765
    sp->uncompressed_offset += (unsigned)cc;
202✔
766

767
    return 1;
202✔
768
}
769

770
/*
771
 * Finish off an encoded strip by flushing it.
772
 */
773
static int JXLPostEncode(TIFF *tif)
202✔
774
{
775
    static const char module[] = "JXLPostEncode";
776
    JXLState *sp = EncoderState(tif);
202✔
777
    TIFFDirectory *td = &tif->tif_dir;
202✔
778

779
    if (sp->uncompressed_offset != sp->uncompressed_size)
202✔
780
    {
781
        TIFFErrorExtR(tif, module, "Unexpected number of bytes in the buffer");
×
782
        return 0;
×
783
    }
784

785
    JxlEncoder *enc = JxlEncoderCreate(NULL);
202✔
786
    if (enc == NULL)
202✔
787
    {
788
        TIFFErrorExtR(tif, module, "JxlEncoderCreate() failed");
×
789
        return 0;
×
790
    }
791
    JxlEncoderUseContainer(enc, JXL_FALSE);
202✔
792

793
#ifdef HAVE_JxlEncoderFrameSettingsCreate
794
    JxlEncoderFrameSettings *opts = JxlEncoderFrameSettingsCreate(enc, NULL);
202✔
795
#else
796
    JxlEncoderOptions *opts = JxlEncoderOptionsCreate(enc, NULL);
797
#endif
798
    if (opts == NULL)
202✔
799
    {
800
        TIFFErrorExtR(tif, module, "JxlEncoderFrameSettingsCreate() failed");
×
801
        JxlEncoderDestroy(enc);
×
802
        return 0;
×
803
    }
804

805
    JxlPixelFormat format = {0};
202✔
806
    format.data_type = GetJXLDataType(tif);
202✔
807
    format.endianness = JXL_NATIVE_ENDIAN;
202✔
808
    format.align = 0;
202✔
809

810
#ifdef HAVE_JxlEncoderSetCodestreamLevel
811
    if (td->td_bitspersample > 12)
202✔
812
    {
813
        JxlEncoderSetCodestreamLevel(enc, 10);
90✔
814
    }
815
#endif
816
    JxlBasicInfo basic_info = {0};
202✔
817
    JxlEncoderInitBasicInfo(&basic_info);
202✔
818
    basic_info.xsize = sp->segment_width;
202✔
819
    basic_info.ysize = sp->segment_height;
202✔
820
    basic_info.bits_per_sample = td->td_bitspersample;
202✔
821
    basic_info.orientation = JXL_ORIENT_IDENTITY;
202✔
822
    if (td->td_sampleformat == SAMPLEFORMAT_IEEEFP)
202✔
823
    {
824
        if (td->td_bitspersample == 32)
45✔
825
            basic_info.exponent_bits_per_sample = 8;
44✔
826
        else
827
            basic_info.exponent_bits_per_sample = 5;
1✔
828
    }
829
    else
830
    {
831
        basic_info.exponent_bits_per_sample = 0;
157✔
832
    }
833

834
    int bAlphaEmbedded = 0;
202✔
835
    const int bAlphaDistanceSameAsMainChannel =
202✔
836
        (sp->alpha_distance < 0.0f) ||
217✔
837
        ((sp->lossless && sp->alpha_distance == 0.0f) ||
15✔
838
         (!sp->lossless && sp->alpha_distance == sp->distance));
15✔
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
    if (td->td_planarconfig == PLANARCONFIG_SEPARATE)
202✔
850
    {
851
        format.num_channels = 1;
102✔
852
        basic_info.num_color_channels = 1;
102✔
853
        basic_info.num_extra_channels = 0;
102✔
854
        basic_info.alpha_bits = 0;
102✔
855
        basic_info.alpha_exponent_bits = 0;
102✔
856
    }
857
    else
858
    {
859
        if (td->td_photometric == PHOTOMETRIC_MINISBLACK &&
100✔
860
            td->td_extrasamples > 0 &&
28✔
861
            td->td_extrasamples == td->td_samplesperpixel - 1 &&
14✔
862
            td->td_sampleinfo[0] == EXTRASAMPLE_UNASSALPHA &&
14✔
863
            bAlphaDistanceSameAsMainChannel)
864
        {  // gray with alpha
865
            format.num_channels = 2;
6✔
866
            basic_info.num_color_channels = 1;
6✔
867
            basic_info.num_extra_channels = td->td_extrasamples;
6✔
868
            basic_info.alpha_bits = td->td_bitspersample;
6✔
869
            basic_info.alpha_exponent_bits =
6✔
870
                basic_info.exponent_bits_per_sample;
6✔
871
            bAlphaEmbedded = 1;
6✔
872
        }
873
        else if (td->td_photometric == PHOTOMETRIC_RGB &&
94✔
874
                 td->td_extrasamples > 0 &&
72✔
875
                 td->td_extrasamples == td->td_samplesperpixel - 3 &&
30✔
876
                 td->td_sampleinfo[0] == EXTRASAMPLE_UNASSALPHA &&
30✔
877
                 bAlphaDistanceSameAsMainChannel)
878
        {  // rgb with alpha, and same distance for alpha vs non-alpha channels
879
            format.num_channels = 4;
9✔
880
            basic_info.num_color_channels = 3;
9✔
881
            basic_info.num_extra_channels = td->td_samplesperpixel - 3;
9✔
882
            basic_info.alpha_bits = td->td_bitspersample;
9✔
883
            basic_info.alpha_exponent_bits =
9✔
884
                basic_info.exponent_bits_per_sample;
9✔
885
            bAlphaEmbedded = 1;
9✔
886
        }
887
        else if (td->td_photometric == PHOTOMETRIC_RGB &&
85✔
888
                 ((td->td_extrasamples == 0) ||
63✔
889
                  (td->td_extrasamples > 0 &&
21✔
890
                   td->td_extrasamples == td->td_samplesperpixel - 3 &&
21✔
891
                   (td->td_sampleinfo[0] != EXTRASAMPLE_UNASSALPHA ||
21✔
892
                    !bAlphaDistanceSameAsMainChannel))))
893
        {  // rgb without alpha, or differente distance for alpha vs non-alpha
894
            // channels
895
            format.num_channels = 3;
63✔
896
            basic_info.num_color_channels = 3;
63✔
897
            basic_info.num_extra_channels = td->td_samplesperpixel - 3;
63✔
898
            basic_info.alpha_bits = 0;
63✔
899
            basic_info.alpha_exponent_bits = 0;
63✔
900
        }
901
        else
902
        {  // fallback to gray without alpha and with eventual extra channels
903
            format.num_channels = 1;
22✔
904
            basic_info.num_color_channels = 1;
22✔
905
            basic_info.num_extra_channels = td->td_samplesperpixel - 1;
22✔
906
            basic_info.alpha_bits = 0;
22✔
907
            basic_info.alpha_exponent_bits = 0;
22✔
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
    if (sp->lossless)
202✔
923
    {
924
#ifdef HAVE_JxlEncoderSetFrameLossless
925
        JxlEncoderSetFrameLossless(opts, TRUE);
166✔
926
#else
927
        JxlEncoderOptionsSetLossless(opts, TRUE);
928
#endif
929
#ifdef HAVE_JxlEncoderSetFrameDistance
930
        JxlEncoderSetFrameDistance(opts, 0);
166✔
931
#else
932
        JxlEncoderOptionsSetDistance(opts, 0);
933
#endif
934
        basic_info.uses_original_profile = JXL_TRUE;
166✔
935
    }
936
    else
937
    {
938
#ifdef HAVE_JxlEncoderSetFrameDistance
939
        if (JxlEncoderSetFrameDistance(opts, sp->distance) != JXL_ENC_SUCCESS)
36✔
940
#else
941
        if (JxlEncoderOptionsSetDistance(opts, sp->distance) != JXL_ENC_SUCCESS)
942
#endif
943
        {
944
            TIFFErrorExtR(tif, module, "JxlEncoderSetFrameDistance() failed");
×
945
            JxlEncoderDestroy(enc);
×
946
            return 0;
×
947
        }
948
    }
949
#ifdef HAVE_JxlEncoderFrameSettingsSetOption
950
    if (JxlEncoderFrameSettingsSetOption(opts, JXL_ENC_FRAME_SETTING_EFFORT,
202✔
951
                                         sp->effort) != JXL_ENC_SUCCESS)
202✔
952
#else
953
    if (JxlEncoderOptionsSetEffort(opts, sp->effort) != JXL_ENC_SUCCESS)
954
#endif
955
    {
956
        TIFFErrorExtR(tif, module, "JxlEncoderFrameSettingsSetOption() failed");
×
957
        JxlEncoderDestroy(enc);
×
958
        return 0;
×
959
    }
960

961
    if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc, &basic_info))
202✔
962
    {
963
        TIFFErrorExtR(tif, module, "JxlEncoderSetBasicInfo() failed");
×
964
        JxlEncoderDestroy(enc);
×
965
        return 0;
×
966
    }
967

968
    JxlColorEncoding color_encoding = {0};
202✔
969
    JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray*/
202✔
970
                              (td->td_planarconfig == PLANARCONFIG_SEPARATE ||
202✔
971
                               basic_info.num_color_channels == 1));
100✔
972
    if (JXL_ENC_SUCCESS != JxlEncoderSetColorEncoding(enc, &color_encoding))
202✔
973
    {
974
        TIFFErrorExtR(tif, module, "JxlEncoderSetColorEncoding() failed");
×
975
        JxlEncoderDestroy(enc);
×
976
        return 0;
×
977
    }
978

979
    uint8_t *main_buffer = sp->uncompressed_buffer;
202✔
980
    unsigned int main_size = sp->uncompressed_size;
202✔
981

982
#ifdef HAVE_JxlExtraChannels
983
    int nBytesPerSample = GetJXLDataTypeSize(format.data_type);
202✔
984
    if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
202✔
985
        (basic_info.num_extra_channels > 1 ||
100✔
986
         (basic_info.num_extra_channels == 1 && !bAlphaEmbedded)))
86✔
987
    {
988
        main_size = (sp->uncompressed_size / td->td_samplesperpixel);
32✔
989
        int nMainChannels = basic_info.num_color_channels;
32✔
990
        if (bAlphaEmbedded)
32✔
991
            nMainChannels++;
3✔
992
        main_size *= nMainChannels;
32✔
993
        main_buffer = _TIFFmallocExt(tif, main_size);
32✔
994
        if (main_buffer == NULL)
32✔
995
            return 0;
×
996
        int outChunkSize = nBytesPerSample * nMainChannels;
32✔
997
        int inStep = nBytesPerSample * td->td_samplesperpixel;
32✔
998
        uint8_t *cur_outbuffer = main_buffer;
32✔
999
        uint8_t *cur_inbuffer = sp->uncompressed_buffer;
32✔
1000
        for (; (size_t)(cur_outbuffer - main_buffer) < main_size;
1,286,700✔
1001
             cur_outbuffer += outChunkSize, cur_inbuffer += inStep)
1,286,670✔
1002
        {
1003
            memcpy(cur_outbuffer, cur_inbuffer, outChunkSize);
1,286,670✔
1004
        }
1005
        for (int iChannel = nMainChannels; iChannel < td->td_samplesperpixel;
85✔
1006
             iChannel++)
53✔
1007
        {
1008
            JxlExtraChannelInfo extra_channel_info;
1009
            int channelType = JXL_CHANNEL_OPTIONAL;
53✔
1010
            const int iExtraChannel = iChannel - nMainChannels + bAlphaEmbedded;
53✔
1011
            if (iExtraChannel < td->td_extrasamples &&
53✔
1012
                (td->td_sampleinfo[iExtraChannel] == EXTRASAMPLE_UNASSALPHA ||
53✔
1013
                 td->td_sampleinfo[iExtraChannel] == EXTRASAMPLE_ASSOCALPHA))
32✔
1014
            {
1015
                channelType = JXL_CHANNEL_ALPHA;
21✔
1016
            }
1017
            JxlEncoderInitExtraChannelInfo(channelType, &extra_channel_info);
53✔
1018
            extra_channel_info.bits_per_sample = basic_info.bits_per_sample;
53✔
1019
            extra_channel_info.exponent_bits_per_sample =
53✔
1020
                basic_info.exponent_bits_per_sample;
53✔
1021
            if (iExtraChannel < td->td_extrasamples &&
53✔
1022
                td->td_sampleinfo[iExtraChannel] == EXTRASAMPLE_ASSOCALPHA)
53✔
1023
            {
1024
                extra_channel_info.alpha_premultiplied = JXL_TRUE;
×
1025
            }
1026

1027
            if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelInfo(
53✔
1028
                                       enc, iExtraChannel, &extra_channel_info))
1029
            {
1030
                TIFFErrorExtR(tif, module,
×
1031
                              "JxlEncoderSetExtraChannelInfo(%d) failed",
1032
                              iChannel);
1033
                JxlEncoderDestroy(enc);
×
1034
                _TIFFfreeExt(tif, main_buffer);
×
1035
                return 0;
×
1036
            }
1037
#if HAVE_JxlEncoderSetExtraChannelDistance
1038
            if (channelType == JXL_CHANNEL_ALPHA && sp->alpha_distance >= 0.0f)
53✔
1039
            {
1040
                if (JXL_ENC_SUCCESS !=
15✔
1041
                    JxlEncoderSetExtraChannelDistance(opts, iExtraChannel,
15✔
1042
                                                      sp->alpha_distance))
1043
                {
1044
                    TIFFErrorExtR(
×
1045
                        tif, module,
1046
                        "JxlEncoderSetExtraChannelDistance(%d) failed",
1047
                        iChannel);
1048
                    JxlEncoderDestroy(enc);
×
1049
                    _TIFFfreeExt(tif, main_buffer);
×
1050
                    return 0;
×
1051
                }
1052
            }
1053
            else if (!(sp->lossless))
38✔
1054
            {
1055
                // By default libjxl applies lossless encoding for extra channels
1056
                if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelDistance(
4✔
1057
                                           opts, iExtraChannel, sp->distance))
1058
                {
1059
                    TIFFErrorExtR(
×
1060
                        tif, module,
1061
                        "JxlEncoderSetExtraChannelDistance(%d) failed",
1062
                        iChannel);
1063
                    JxlEncoderDestroy(enc);
×
1064
                    _TIFFfreeExt(tif, main_buffer);
×
1065
                    return 0;
×
1066
                }
1067
            }
1068
#endif
1069
        }
1070
    }
1071
#endif
1072

1073
    int retCode =
202✔
1074
        JxlEncoderAddImageFrame(opts, &format, main_buffer, main_size);
202✔
1075
    // cleanup now
1076
    if (main_buffer != sp->uncompressed_buffer)
202✔
1077
    {
1078
        _TIFFfreeExt(tif, main_buffer);
32✔
1079
    }
1080
    if (retCode != JXL_ENC_SUCCESS)
202✔
1081
    {
1082
        TIFFErrorExtR(tif, module, "JxlEncoderAddImageFrame() failed");
×
1083
        JxlEncoderDestroy(enc);
×
1084
        return 0;
×
1085
    }
1086

1087
#ifdef HAVE_JxlExtraChannels
1088
    if (td->td_planarconfig == PLANARCONFIG_CONTIG &&
202✔
1089
        (basic_info.num_extra_channels > 1 ||
100✔
1090
         (basic_info.num_extra_channels == 1 && !bAlphaEmbedded)))
86✔
1091
    {
1092
        int nMainChannels = basic_info.num_color_channels;
32✔
1093
        if (bAlphaEmbedded)
32✔
1094
            nMainChannels++;
3✔
1095
        int extra_channel_size =
32✔
1096
            (sp->uncompressed_size / td->td_samplesperpixel);
32✔
1097
        uint8_t *extra_channel_buffer = _TIFFmallocExt(tif, extra_channel_size);
32✔
1098
        if (extra_channel_buffer == NULL)
32✔
1099
            return 0;
×
1100
        int inStep = nBytesPerSample * td->td_samplesperpixel;
32✔
1101
        int outStep = nBytesPerSample;
32✔
1102
        for (int iChannel = nMainChannels; iChannel < td->td_samplesperpixel;
85✔
1103
             iChannel++)
53✔
1104
        {
1105
            uint8_t *cur_outbuffer = extra_channel_buffer;
53✔
1106
            uint8_t *cur_inbuffer =
53✔
1107
                sp->uncompressed_buffer + iChannel * outStep;
53✔
1108
            for (; cur_outbuffer - extra_channel_buffer < extra_channel_size;
2,272,160✔
1109
                 cur_outbuffer += outStep, cur_inbuffer += inStep)
2,272,110✔
1110
            {
1111
                memcpy(cur_outbuffer, cur_inbuffer, outStep);
2,272,110✔
1112
            }
1113
            if (JxlEncoderSetExtraChannelBuffer(
106✔
1114
                    opts, &format, extra_channel_buffer, extra_channel_size,
1115
                    (bAlphaEmbedded)
1116
                        ? iChannel - nMainChannels + 1
3✔
1117
                        : iChannel - nMainChannels) != JXL_ENC_SUCCESS)
50✔
1118
            {
1119
                TIFFErrorExtR(tif, module,
×
1120
                              "JxlEncoderSetExtraChannelBuffer() failed");
1121
                _TIFFfreeExt(tif, extra_channel_buffer);
×
1122
                JxlEncoderDestroy(enc);
×
1123
                return 0;
×
1124
            }
1125
        }
1126
        _TIFFfreeExt(tif, extra_channel_buffer);
32✔
1127
    }
1128
#endif
1129

1130
    JxlEncoderCloseInput(enc);
202✔
1131

1132
    while (TRUE)
1133
    {
×
1134
        size_t len = (size_t)tif->tif_rawdatasize;
202✔
1135
        uint8_t *buf = (uint8_t *)tif->tif_rawdata;
202✔
1136
        JxlEncoderStatus process_result =
1137
            JxlEncoderProcessOutput(enc, &buf, &len);
202✔
1138
        if (process_result == JXL_ENC_ERROR)
201✔
1139
        {
1140
            TIFFErrorExtR(tif, module, "JxlEncoderProcessOutput() failed");
×
1141
            JxlEncoderDestroy(enc);
×
1142
            return 0;
×
1143
        }
1144
        tif->tif_rawcc = tif->tif_rawdatasize - len;
201✔
1145
        if (!TIFFFlushData1(tif))
201✔
1146
        {
1147
            JxlEncoderDestroy(enc);
×
1148
            return 0;
×
1149
        }
1150
        if (process_result != JXL_ENC_NEED_MORE_OUTPUT)
200✔
1151
            break;
200✔
1152
    }
1153

1154
    JxlEncoderDestroy(enc);
200✔
1155
    return 1;
201✔
1156
}
1157

1158
static void JXLCleanup(TIFF *tif)
540✔
1159
{
1160
    JXLState *sp = LState(tif);
540✔
1161

1162
    assert(sp != 0);
540✔
1163

1164
    tif->tif_tagmethods.vgetfield = sp->vgetparent;
540✔
1165
    tif->tif_tagmethods.vsetfield = sp->vsetparent;
540✔
1166

1167
    _TIFFfreeExt(tif, sp->uncompressed_buffer);
540✔
1168

1169
    if (sp->decoder)
540✔
1170
        JxlDecoderDestroy(sp->decoder);
117✔
1171

1172
    _TIFFfreeExt(tif, sp);
539✔
1173
    tif->tif_data = NULL;
539✔
1174

1175
    _TIFFSetDefaultCompressionState(tif);
539✔
1176
}
540✔
1177

1178
static const TIFFField JXLFields[] = {
1179
    {TIFFTAG_JXL_LOSSYNESS, 0, 0, TIFF_ANY, 0, TIFF_SETGET_UINT32, FIELD_PSEUDO,
1180
     FALSE, FALSE, "Lossyness", NULL},
1181
    {TIFFTAG_JXL_EFFORT, 0, 0, TIFF_ANY, 0, TIFF_SETGET_UINT32, FIELD_PSEUDO,
1182
     FALSE, FALSE, "Effort", NULL},
1183
    {TIFFTAG_JXL_DISTANCE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_FLOAT, FIELD_PSEUDO,
1184
     FALSE, FALSE, "Distance", NULL},
1185
    {TIFFTAG_JXL_ALPHA_DISTANCE, 0, 0, TIFF_ANY, 0, TIFF_SETGET_FLOAT,
1186
     FIELD_PSEUDO, FALSE, FALSE, "AlphaDistance", NULL},
1187
};
1188

1189
static int JXLVSetField(TIFF *tif, uint32_t tag, va_list ap)
5,747✔
1190
{
1191
    static const char module[] = "JXLVSetField";
1192
    JXLState *sp = LState(tif);
5,747✔
1193

1194
    switch (tag)
5,747✔
1195
    {
1196
        case TIFFTAG_JXL_LOSSYNESS:
270✔
1197
        {
1198
            uint32_t lossyness = va_arg(ap, uint32_t);
270✔
1199
            if (lossyness == JXL_LOSSLESS)
270✔
1200
                sp->lossless = TRUE;
209✔
1201
            else if (lossyness == JXL_LOSSY)
61✔
1202
                sp->lossless = FALSE;
61✔
1203
            else
1204
            {
1205
                TIFFErrorExtR(tif, module, "Invalid value for Lossyness: %u",
×
1206
                              lossyness);
1207
                return 0;
×
1208
            }
1209
            return 1;
270✔
1210
        }
1211

1212
        case TIFFTAG_JXL_EFFORT:
265✔
1213
        {
1214
            uint32_t effort = va_arg(ap, uint32_t);
265✔
1215
            if (effort < 1 || effort > 9)
265✔
1216
            {
1217
                TIFFErrorExtR(tif, module, "Invalid value for Effort: %u",
×
1218
                              effort);
1219
                return 0;
×
1220
            }
1221
            sp->effort = effort;
265✔
1222
            return 1;
265✔
1223
        }
1224

1225
        case TIFFTAG_JXL_DISTANCE:
266✔
1226
        {
1227
            float distance = (float)va_arg(ap, double);
266✔
1228
            if (distance < 0 || distance > 15)
266✔
1229
            {
1230
                TIFFErrorExtR(tif, module, "Invalid value for Distance: %f",
×
1231
                              distance);
1232
                return 0;
×
1233
            }
1234
            sp->distance = distance;
266✔
1235
            return 1;
266✔
1236
        }
1237

1238
        case TIFFTAG_JXL_ALPHA_DISTANCE:
266✔
1239
        {
1240
            float alpha_distance = (float)va_arg(ap, double);
266✔
1241
            if (alpha_distance != -1 &&
266✔
1242
                (alpha_distance < 0 || alpha_distance > 15))
6✔
1243
            {
1244
                TIFFErrorExtR(tif, module,
×
1245
                              "Invalid value for AlphaDistance: %f",
1246
                              alpha_distance);
1247
                return 0;
×
1248
            }
1249
            sp->alpha_distance = alpha_distance;
266✔
1250
            return 1;
266✔
1251
        }
1252

1253
        default:
4,680✔
1254
        {
1255
            return (*sp->vsetparent)(tif, tag, ap);
4,680✔
1256
        }
1257
    }
1258
    /*NOTREACHED*/
1259
}
1260

1261
static int JXLVGetField(TIFF *tif, uint32_t tag, va_list ap)
8,231✔
1262
{
1263
    JXLState *sp = LState(tif);
8,231✔
1264

1265
    switch (tag)
8,231✔
1266
    {
1267
        case TIFFTAG_JXL_LOSSYNESS:
×
1268
            *va_arg(ap, uint32_t *) = sp->lossless ? JXL_LOSSLESS : JXL_LOSSY;
×
1269
            break;
×
1270
        case TIFFTAG_JXL_EFFORT:
×
1271
            *va_arg(ap, uint32_t *) = sp->effort;
×
1272
            break;
×
1273
        case TIFFTAG_JXL_DISTANCE:
×
1274
            *va_arg(ap, float *) = sp->distance;
×
1275
            break;
×
1276
        case TIFFTAG_JXL_ALPHA_DISTANCE:
×
1277
            *va_arg(ap, float *) = sp->alpha_distance;
×
1278
            break;
×
1279
        default:
8,231✔
1280
            return (*sp->vgetparent)(tif, tag, ap);
8,231✔
1281
    }
1282
    return 1;
×
1283
}
1284

1285
int TIFFInitJXL(TIFF *tif, int scheme)
541✔
1286
{
1287
    static const char module[] = "TIFFInitJXL";
1288
    JXLState *sp;
1289

1290
    (void)scheme;
1291
    assert(scheme == COMPRESSION_JXL || scheme == COMPRESSION_JXL_DNG_1_7);
541✔
1292

1293
    /*
1294
     * Merge codec-specific tag information.
1295
     */
1296
    if (!_TIFFMergeFields(tif, JXLFields, TIFFArrayCount(JXLFields)))
541✔
1297
    {
1298
        TIFFErrorExtR(tif, module, "Merging JXL codec-specific tags failed");
×
1299
        return 0;
×
1300
    }
1301

1302
    /*
1303
     * Allocate state block so tag methods have storage to record values.
1304
     */
1305
    tif->tif_data = (uint8_t *)_TIFFcallocExt(tif, 1, sizeof(JXLState));
541✔
1306
    if (tif->tif_data == NULL)
541✔
1307
        goto bad;
×
1308
    sp = LState(tif);
541✔
1309

1310
    /*
1311
     * Override parent get/set field methods.
1312
     */
1313
    sp->vgetparent = tif->tif_tagmethods.vgetfield;
541✔
1314
    tif->tif_tagmethods.vgetfield = JXLVGetField; /* hook for codec tags */
541✔
1315
    sp->vsetparent = tif->tif_tagmethods.vsetfield;
541✔
1316
    tif->tif_tagmethods.vsetfield = JXLVSetField; /* hook for codec tags */
541✔
1317

1318
    /*
1319
     * Install codec methods.
1320
     */
1321
    tif->tif_fixuptags = JXLFixupTags;
541✔
1322
    tif->tif_setupdecode = JXLSetupDecode;
541✔
1323
    tif->tif_predecode = JXLPreDecode;
541✔
1324
    tif->tif_decoderow = JXLDecode;
541✔
1325
    tif->tif_decodestrip = JXLDecode;
541✔
1326
    tif->tif_decodetile = JXLDecode;
541✔
1327
    tif->tif_setupencode = JXLSetupEncode;
541✔
1328
    tif->tif_preencode = JXLPreEncode;
541✔
1329
    tif->tif_postencode = JXLPostEncode;
541✔
1330
    tif->tif_encoderow = JXLEncode;
541✔
1331
    tif->tif_encodestrip = JXLEncode;
541✔
1332
    tif->tif_encodetile = JXLEncode;
541✔
1333
    tif->tif_cleanup = JXLCleanup;
541✔
1334

1335
    /* Default values for codec-specific fields */
1336
    sp->decoder = NULL;
541✔
1337

1338
    sp->state = 0;
541✔
1339
    sp->lossless = TRUE;
541✔
1340
    sp->effort = 5;
541✔
1341
    sp->distance = 1.0;
541✔
1342
    sp->alpha_distance = -1.0;
541✔
1343

1344
    return 1;
541✔
1345
bad:
×
1346
    TIFFErrorExtR(tif, module, "No space for JXL state block");
×
1347
    return 0;
×
1348
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc