LCOV - code coverage report
Current view: top level - frmts/gtiff - tif_jxl.c (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 487 638 76.3 %
Date: 2024-11-21 22:18:42 Functions: 15 15 100.0 %

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

Generated by: LCOV version 1.14