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

Generated by: LCOV version 1.14