LCOV - code coverage report
Current view: top level - frmts/jpeg - jpgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1880 2292 82.0 %
Date: 2024-05-04 12:52:34 Functions: 98 105 93.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  JPEG JFIF Driver
       4             :  * Purpose:  Implement GDAL JPEG Support based on IJG libjpeg.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2000, Frank Warmerdam
       9             :  * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Portions Copyright (c) Her majesty the Queen in right of Canada as
      12             :  * represented by the Minister of National Defence, 2006.
      13             :  *
      14             :  * Permission is hereby granted, free of charge, to any person obtaining a
      15             :  * copy of this software and associated documentation files (the "Software"),
      16             :  * to deal in the Software without restriction, including without limitation
      17             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      18             :  * and/or sell copies of the Software, and to permit persons to whom the
      19             :  * Software is furnished to do so, subject to the following conditions:
      20             :  *
      21             :  * The above copyright notice and this permission notice shall be included
      22             :  * in all copies or substantial portions of the Software.
      23             :  *
      24             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      25             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      26             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      27             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      28             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      29             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      30             :  * DEALINGS IN THE SOFTWARE.
      31             :  ****************************************************************************/
      32             : 
      33             : #include "cpl_port.h"
      34             : #include "jpgdataset.h"
      35             : 
      36             : #include <cerrno>
      37             : #include <climits>
      38             : #include <cstddef>
      39             : #include <cstdio>
      40             : #include <cstdint>
      41             : #include <cstdlib>
      42             : #include <cstring>
      43             : #if HAVE_FCNTL_H
      44             : #include <fcntl.h>
      45             : #endif
      46             : #include <limits>
      47             : #include <setjmp.h>
      48             : 
      49             : #include <algorithm>
      50             : #include <string>
      51             : 
      52             : #include "gdalorienteddataset.h"
      53             : 
      54             : #include "cpl_conv.h"
      55             : #include "cpl_error.h"
      56             : #include "cpl_md5.h"
      57             : #include "quant_table_md5sum.h"
      58             : #include "quant_table_md5sum_jpeg9e.h"
      59             : #include "cpl_progress.h"
      60             : #include "cpl_string.h"
      61             : #include "cpl_time.h"
      62             : #include "cpl_vsi.h"
      63             : #include "gdal.h"
      64             : #include "gdal_frmts.h"
      65             : #include "gdal_pam.h"
      66             : #include "gdal_priv.h"
      67             : #include "gdalexif.h"
      68             : CPL_C_START
      69             : #ifdef LIBJPEG_12_PATH
      70             : #include LIBJPEG_12_PATH
      71             : #else
      72             : #include "jpeglib.h"
      73             : #endif
      74             : CPL_C_END
      75             : #include "memdataset.h"
      76             : #include "rawdataset.h"
      77             : #include "vsidataio.h"
      78             : #include "vrt/vrtdataset.h"
      79             : #include "jpegdrivercore.h"
      80             : 
      81             : #if defined(EXPECTED_JPEG_LIB_VERSION) && !defined(LIBJPEG_12_PATH)
      82             : #if EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
      83             : #error EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
      84             : #endif
      85             : #endif
      86             : 
      87             : constexpr int TIFF_VERSION = 42;
      88             : 
      89             : constexpr int TIFF_BIGENDIAN = 0x4d4d;
      90             : constexpr int TIFF_LITTLEENDIAN = 0x4949;
      91             : 
      92             : constexpr int JPEG_TIFF_IMAGEWIDTH = 0x100;
      93             : constexpr int JPEG_TIFF_IMAGEHEIGHT = 0x101;
      94             : constexpr int JPEG_TIFF_COMPRESSION = 0x103;
      95             : constexpr int JPEG_EXIF_JPEGIFOFSET = 0x201;
      96             : constexpr int JPEG_EXIF_JPEGIFBYTECOUNT = 0x202;
      97             : 
      98             : // Ok to use setjmp().
      99             : #ifdef _MSC_VER
     100             : #pragma warning(disable : 4611)
     101             : #endif
     102             : 
     103             : // Do we want to do special processing suitable for when JSAMPLE is a
     104             : // 16bit value?
     105             : 
     106             : /* HAVE_JPEGTURBO_DUAL_MODE_8_12 is defined for libjpeg-turbo >= 2.2 which
     107             :  * adds a dual-mode 8/12 bit API in the same library.
     108             :  */
     109             : 
     110             : #if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12)
     111             : /* Start by undefining BITS_IN_JSAMPLE which is always set to 8 in libjpeg-turbo
     112             :  * >= 2.2 Cf
     113             :  * https://github.com/libjpeg-turbo/libjpeg-turbo/commit/8b9bc4b9635a2a047fb23ebe70c9acd728d3f99b
     114             :  */
     115             : #undef BITS_IN_JSAMPLE
     116             : /* libjpeg-turbo >= 2.2 adds J12xxxx datatypes for the 12-bit mode. */
     117             : #if defined(JPGDataset)
     118             : #define BITS_IN_JSAMPLE 12
     119             : #define GDAL_JSAMPLE J12SAMPLE
     120             : #else
     121             : #define BITS_IN_JSAMPLE 8
     122             : #define GDAL_JSAMPLE JSAMPLE
     123             : #endif
     124             : #else
     125             : #define GDAL_JSAMPLE JSAMPLE
     126             : #endif
     127             : 
     128             : #if defined(JPEG_LIB_MK1)
     129             : #define JPEG_LIB_MK1_OR_12BIT 1
     130             : #elif BITS_IN_JSAMPLE == 12
     131             : #define JPEG_LIB_MK1_OR_12BIT 1
     132             : #endif
     133             : 
     134             : /************************************************************************/
     135             : /*                     SetMaxMemoryToUse()                              */
     136             : /************************************************************************/
     137             : 
     138       11241 : static void SetMaxMemoryToUse(struct jpeg_decompress_struct *psDInfo)
     139             : {
     140             :     // This is to address bug related in ticket #1795.
     141       11241 :     if (CPLGetConfigOption("JPEGMEM", nullptr) == nullptr)
     142             :     {
     143             :         // If the user doesn't provide a value for JPEGMEM, we want to be sure
     144             :         // that at least 500 MB will be used before creating the temporary file.
     145       11240 :         const long nMinMemory = 500 * 1024 * 1024;
     146       11240 :         psDInfo->mem->max_memory_to_use =
     147       11240 :             std::max(psDInfo->mem->max_memory_to_use, nMinMemory);
     148             :     }
     149       11241 : }
     150             : 
     151             : #if !defined(JPGDataset)
     152             : 
     153             : /************************************************************************/
     154             : /*                     ReadImageStructureMetadata()                     */
     155             : /************************************************************************/
     156             : 
     157           5 : void JPGDatasetCommon::ReadImageStructureMetadata()
     158             : {
     159           5 :     if (bHasReadImageStructureMetadata)
     160           0 :         return;
     161             : 
     162           5 :     bHasReadImageStructureMetadata = true;
     163           5 :     if (GetDataPrecision() != 8)
     164           0 :         return;  // quality guessing not implemented for 12-bit JPEG for now
     165             : 
     166             :     // Save current position to avoid disturbing JPEG stream decoding.
     167           5 :     const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
     168             : 
     169             :     GByte abyChunkHeader[4];
     170           5 :     int nChunkLoc = 2;
     171           5 :     constexpr GByte MARKER_QUANT_TABLE = 0xDB;
     172             :     struct CPLMD5Context context;
     173           5 :     CPLMD5Init(&context);
     174             : 
     175             :     while (true)
     176             :     {
     177          20 :         if (VSIFSeekL(m_fpImage, nChunkLoc, SEEK_SET) != 0)
     178           0 :             break;
     179             : 
     180          20 :         if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, m_fpImage) !=
     181             :             1)
     182           0 :             break;
     183             : 
     184          20 :         const int nChunkLength = abyChunkHeader[2] * 256 + abyChunkHeader[3];
     185          20 :         if (abyChunkHeader[0] == 0xFF &&
     186          20 :             abyChunkHeader[1] == MARKER_QUANT_TABLE && nChunkLength > 2)
     187             :         {
     188          14 :             std::vector<GByte> abyTable(nChunkLength);
     189           7 :             abyTable[0] = abyChunkHeader[2];
     190           7 :             abyTable[1] = abyChunkHeader[3];
     191           7 :             if (VSIFReadL(&abyTable[2], nChunkLength - 2, 1, m_fpImage) == 1)
     192             :             {
     193           7 :                 CPLMD5Update(&context, &abyTable[0], nChunkLength);
     194           7 :             }
     195             :         }
     196             :         else
     197             :         {
     198          13 :             if (abyChunkHeader[0] != 0xFF || (abyChunkHeader[1] & 0xf0) != 0xe0)
     199             :                 break;  // Not an APP chunk.
     200             :         }
     201             : 
     202          15 :         nChunkLoc += 2 + nChunkLength;
     203          15 :     }
     204             : 
     205           5 :     VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
     206             : 
     207             :     GByte digest[16];
     208           5 :     CPLMD5Final(digest, &context);
     209             : 
     210           5 :     const bool bIsYCbCr = nBands == 3 && GetJPEGColorSpace() == JCS_YCbCr;
     211         410 :     for (int i = 0; i < 100; i++)
     212             :     {
     213         410 :         if ((bIsYCbCr &&
     214         160 :              (memcmp(md5JPEGQuantTable_3_YCBCR_8bit[i], digest, 16) == 0 ||
     215         158 :               memcmp(md5JPEGQuantTable_3_YCBCR_8bit_jpeg9e[i], digest, 16) ==
     216         408 :                   0)) ||
     217         408 :             (!bIsYCbCr &&
     218         250 :              memcmp(md5JPEGQuantTable_generic_8bit[i], digest, 16) == 0))
     219             :         {
     220           5 :             GDALDataset::SetMetadataItem(
     221             :                 "JPEG_QUALITY", CPLSPrintf("%d", i + 1), "IMAGE_STRUCTURE");
     222           5 :             break;
     223             :         }
     224             :     }
     225             : }
     226             : 
     227             : /************************************************************************/
     228             : /*                       ReadEXIFMetadata()                             */
     229             : /************************************************************************/
     230          97 : void JPGDatasetCommon::ReadEXIFMetadata()
     231             : {
     232          97 :     if (bHasReadEXIFMetadata)
     233           0 :         return;
     234             : 
     235          97 :     CPLAssert(papszMetadata == nullptr);
     236             : 
     237             :     // Save current position to avoid disturbing JPEG stream decoding.
     238          97 :     const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
     239             : 
     240          97 :     if (EXIFInit(m_fpImage))
     241             :     {
     242          42 :         EXIFExtractMetadata(papszMetadata, m_fpImage, nTiffDirStart, bSwabflag,
     243          42 :                             nTIFFHEADER, nExifOffset, nInterOffset, nGPSOffset);
     244             : 
     245          42 :         if (nExifOffset > 0)
     246             :         {
     247          32 :             EXIFExtractMetadata(papszMetadata, m_fpImage, nExifOffset,
     248          32 :                                 bSwabflag, nTIFFHEADER, nExifOffset,
     249          32 :                                 nInterOffset, nGPSOffset);
     250             :         }
     251          42 :         if (nInterOffset > 0)
     252             :         {
     253           0 :             EXIFExtractMetadata(papszMetadata, m_fpImage, nInterOffset,
     254           0 :                                 bSwabflag, nTIFFHEADER, nExifOffset,
     255           0 :                                 nInterOffset, nGPSOffset);
     256             :         }
     257          42 :         if (nGPSOffset > 0)
     258             :         {
     259          18 :             EXIFExtractMetadata(papszMetadata, m_fpImage, nGPSOffset, bSwabflag,
     260          18 :                                 nTIFFHEADER, nExifOffset, nInterOffset,
     261          18 :                                 nGPSOffset);
     262             :         }
     263             : 
     264             :         // Avoid setting the PAM dirty bit just for that.
     265          42 :         const int nOldPamFlags = nPamFlags;
     266             : 
     267             :         // Append metadata from PAM after EXIF metadata.
     268          42 :         papszMetadata = CSLMerge(papszMetadata, GDALPamDataset::GetMetadata());
     269             : 
     270             :         // Expose XMP in EXIF in xml:XMP metadata domain
     271          42 :         if (GDALDataset::GetMetadata("xml:XMP") == nullptr)
     272             :         {
     273             :             const char *pszXMP =
     274          42 :                 CSLFetchNameValue(papszMetadata, "EXIF_XmlPacket");
     275          42 :             if (pszXMP)
     276             :             {
     277           0 :                 CPLDebug("JPEG", "Read XMP metadata from EXIF tag");
     278           0 :                 const char *const apszMDList[2] = {pszXMP, nullptr};
     279           0 :                 SetMetadata(const_cast<char **>(apszMDList), "xml:XMP");
     280             : 
     281           0 :                 papszMetadata =
     282           0 :                     CSLSetNameValue(papszMetadata, "EXIF_XmlPacket", nullptr);
     283             :             }
     284             :         }
     285             : 
     286          42 :         SetMetadata(papszMetadata);
     287             : 
     288          42 :         nPamFlags = nOldPamFlags;
     289             :     }
     290             : 
     291          97 :     VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
     292             : 
     293          97 :     bHasReadEXIFMetadata = true;
     294             : }
     295             : 
     296             : /************************************************************************/
     297             : /*                        ReadXMPMetadata()                             */
     298             : /************************************************************************/
     299             : 
     300             : // See ยง2.1.3 of
     301             : // http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf
     302             : 
     303          46 : void JPGDatasetCommon::ReadXMPMetadata()
     304             : {
     305          46 :     if (bHasReadXMPMetadata)
     306           0 :         return;
     307             : 
     308             :     // Save current position to avoid disturbing JPEG stream decoding.
     309          46 :     const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
     310             : 
     311             :     // Search for APP1 chunk.
     312          46 :     constexpr int APP1_BYTE = 0xe1;
     313          46 :     constexpr int JFIF_MARKER_SIZE = 2 + 2;  // ID + size
     314          46 :     constexpr const char APP1_XMP_SIGNATURE[] = "http://ns.adobe.com/xap/1.0/";
     315          46 :     constexpr int APP1_XMP_SIGNATURE_LEN =
     316             :         static_cast<int>(sizeof(APP1_XMP_SIGNATURE));
     317          46 :     GByte abyChunkHeader[JFIF_MARKER_SIZE + APP1_XMP_SIGNATURE_LEN] = {};
     318          46 :     int nChunkLoc = 2;
     319          46 :     bool bFoundXMP = false;
     320             : 
     321             :     while (true)
     322             :     {
     323         324 :         if (VSIFSeekL(m_fpImage, nChunkLoc, SEEK_SET) != 0)
     324           0 :             break;
     325             : 
     326         324 :         if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, m_fpImage) !=
     327             :             1)
     328           3 :             break;
     329             : 
     330         321 :         nChunkLoc += 2 + abyChunkHeader[2] * 256 + abyChunkHeader[3];
     331             : 
     332             :         // Not a marker
     333         321 :         if (abyChunkHeader[0] != 0xFF)
     334           0 :             continue;
     335             : 
     336             :         // Stop on Start of Scan
     337         321 :         if (abyChunkHeader[1] == 0xDA)
     338          35 :             break;
     339             : 
     340         286 :         if (abyChunkHeader[1] == APP1_BYTE &&
     341          26 :             memcmp(reinterpret_cast<char *>(abyChunkHeader) + JFIF_MARKER_SIZE,
     342             :                    APP1_XMP_SIGNATURE, APP1_XMP_SIGNATURE_LEN) == 0)
     343             :         {
     344           8 :             bFoundXMP = true;
     345           8 :             break;  // APP1 - XMP.
     346             :         }
     347             :     }
     348             : 
     349          46 :     if (bFoundXMP)
     350             :     {
     351           8 :         const int nXMPLength = abyChunkHeader[2] * 256 + abyChunkHeader[3] - 2 -
     352             :                                APP1_XMP_SIGNATURE_LEN;
     353           8 :         if (nXMPLength > 0)
     354             :         {
     355           8 :             char *pszXMP = static_cast<char *>(VSIMalloc(nXMPLength + 1));
     356           8 :             if (pszXMP)
     357             :             {
     358           8 :                 if (VSIFReadL(pszXMP, nXMPLength, 1, m_fpImage) == 1)
     359             :                 {
     360           8 :                     pszXMP[nXMPLength] = '\0';
     361             : 
     362             :                     // Avoid setting the PAM dirty bit just for that.
     363           8 :                     const int nOldPamFlags = nPamFlags;
     364             : 
     365           8 :                     char *apszMDList[2] = {pszXMP, nullptr};
     366           8 :                     SetMetadata(apszMDList, "xml:XMP");
     367             : 
     368           8 :                     nPamFlags = nOldPamFlags;
     369             :                 }
     370           8 :                 VSIFree(pszXMP);
     371             :             }
     372             :         }
     373             :     }
     374             : 
     375          46 :     VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
     376             : 
     377          46 :     bHasReadXMPMetadata = true;
     378             : }
     379             : 
     380             : /************************************************************************/
     381             : /*                        ReadFLIRMetadata()                            */
     382             : /************************************************************************/
     383             : 
     384             : // See https://exiftool.org/TagNames/FLIR.html
     385             : 
     386          10 : void JPGDatasetCommon::ReadFLIRMetadata()
     387             : {
     388          10 :     if (bHasReadFLIRMetadata)
     389           6 :         return;
     390           9 :     bHasReadFLIRMetadata = true;
     391             : 
     392             :     // Save current position to avoid disturbing JPEG stream decoding.
     393           9 :     const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
     394             : 
     395           9 :     int nChunkLoc = 2;
     396             :     // size of APP1 segment marker + size of "FLIR\0"
     397             :     GByte abyChunkHeader[4 + 5];
     398           9 :     std::vector<GByte> abyFLIR;
     399             : 
     400             :     while (true)
     401             :     {
     402          71 :         if (VSIFSeekL(m_fpImage, nChunkLoc, SEEK_SET) != 0)
     403           0 :             break;
     404             : 
     405          71 :         if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, m_fpImage) !=
     406             :             1)
     407           0 :             break;
     408             : 
     409             :         // Not a marker
     410          71 :         if (abyChunkHeader[0] != 0xFF)
     411           0 :             continue;
     412             : 
     413             :         // Stop on Start of Scan
     414          71 :         if (abyChunkHeader[1] == 0xDA)
     415           9 :             break;
     416             : 
     417          62 :         int nMarkerLength = abyChunkHeader[2] * 256 + abyChunkHeader[3] - 2;
     418          62 :         nChunkLoc += 4 + nMarkerLength;
     419             : 
     420          62 :         if (abyChunkHeader[1] == 0xe1 &&
     421          10 :             memcmp(abyChunkHeader + 4, "FLIR\0", 5) == 0)
     422             :         {
     423             :             // Somewhat arbitrary limit
     424           4 :             if (abyFLIR.size() > 10 * 1024 * 1024)
     425             :             {
     426           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     427             :                          "Too large FLIR data compared to hardcoded limit");
     428           0 :                 abyFLIR.clear();
     429           0 :                 break;
     430             :             }
     431             : 
     432             :             // 8 = sizeof("FLIR\0") + '\1' + chunk_idx + chunk_count
     433           4 :             if (nMarkerLength < 8)
     434             :             {
     435           0 :                 abyFLIR.clear();
     436           0 :                 break;
     437             :             }
     438           4 :             size_t nOldSize = abyFLIR.size();
     439           4 :             abyFLIR.resize(nOldSize + nMarkerLength - 8);
     440             :             GByte abyIgnored[3];  // skip '\1' + chunk_idx + chunk_count
     441           8 :             if (VSIFReadL(abyIgnored, 3, 1, m_fpImage) != 1 ||
     442           4 :                 VSIFReadL(&abyFLIR[nOldSize], nMarkerLength - 8, 1,
     443             :                           m_fpImage) != 1)
     444             :             {
     445           0 :                 abyFLIR.clear();
     446           0 :                 break;
     447             :             }
     448             :         }
     449          62 :     }
     450             :     // Restore file pointer
     451           9 :     VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
     452             : 
     453           9 :     constexpr size_t FLIR_HEADER_SIZE = 64;
     454           9 :     if (abyFLIR.size() < FLIR_HEADER_SIZE)
     455           5 :         return;
     456           4 :     if (memcmp(&abyFLIR[0], "FFF\0", 4) != 0)
     457           0 :         return;
     458             : 
     459         104 :     const auto ReadString = [&abyFLIR](size_t nOffset, size_t nLen)
     460             :     {
     461             :         std::string osStr(
     462          52 :             reinterpret_cast<const char *>(abyFLIR.data()) + nOffset, nLen);
     463          52 :         osStr.resize(strlen(osStr.c_str()));
     464          52 :         return osStr;
     465           4 :     };
     466             : 
     467           4 :     bool bLittleEndian = false;
     468             : 
     469         192 :     const auto ReadUInt16 = [&abyFLIR, &bLittleEndian](size_t nOffset)
     470             :     {
     471             :         std::uint16_t nVal;
     472          96 :         memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
     473          96 :         if (bLittleEndian)
     474          32 :             CPL_LSBPTR16(&nVal);
     475             :         else
     476          64 :             CPL_MSBPTR16(&nVal);
     477          96 :         return nVal;
     478           4 :     };
     479             : 
     480           8 :     const auto ReadInt16 = [&abyFLIR, &bLittleEndian](size_t nOffset)
     481             :     {
     482             :         std::int16_t nVal;
     483           4 :         memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
     484           4 :         if (bLittleEndian)
     485           4 :             CPL_LSBPTR16(&nVal);
     486             :         else
     487           0 :             CPL_MSBPTR16(&nVal);
     488           4 :         return nVal;
     489           4 :     };
     490             : 
     491         264 :     const auto ReadUInt32 = [&abyFLIR, &bLittleEndian](size_t nOffset)
     492             :     {
     493             :         std::uint32_t nVal;
     494         132 :         memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
     495         132 :         if (bLittleEndian)
     496           8 :             CPL_LSBPTR32(&nVal);
     497             :         else
     498         124 :             CPL_MSBPTR32(&nVal);
     499         132 :         return nVal;
     500           4 :     };
     501             : 
     502           8 :     const auto ReadInt32 = [&abyFLIR, &bLittleEndian](size_t nOffset)
     503             :     {
     504             :         std::int32_t nVal;
     505           4 :         memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
     506           4 :         if (bLittleEndian)
     507           4 :             CPL_LSBPTR32(&nVal);
     508             :         else
     509           0 :             CPL_MSBPTR32(&nVal);
     510           4 :         return nVal;
     511           4 :     };
     512             : 
     513         208 :     const auto ReadFloat32 = [&abyFLIR, &bLittleEndian](size_t nOffset)
     514             :     {
     515             :         float fVal;
     516         104 :         memcpy(&fVal, &abyFLIR[nOffset], sizeof(fVal));
     517         104 :         if (bLittleEndian)
     518         104 :             CPL_LSBPTR32(&fVal);
     519             :         else
     520           0 :             CPL_MSBPTR32(&fVal);
     521         104 :         return fVal;
     522           4 :     };
     523             : 
     524           0 :     const auto ReadFloat64 = [&abyFLIR, &bLittleEndian](size_t nOffset)
     525             :     {
     526             :         double fVal;
     527           0 :         memcpy(&fVal, &abyFLIR[nOffset], sizeof(fVal));
     528           0 :         if (bLittleEndian)
     529           0 :             CPL_LSBPTR64(&fVal);
     530             :         else
     531           0 :             CPL_MSBPTR64(&fVal);
     532           0 :         return fVal;
     533           4 :     };
     534             : 
     535             :     // Avoid setting the PAM dirty bit just for that.
     536             :     struct PamFlagKeeper
     537             :     {
     538             :         int &m_nPamFlagsRef;
     539             :         int m_nOldPamFlags;
     540             : 
     541           4 :         explicit PamFlagKeeper(int &nPamFlagsRef)
     542           4 :             : m_nPamFlagsRef(nPamFlagsRef), m_nOldPamFlags(nPamFlagsRef)
     543             :         {
     544           4 :         }
     545             : 
     546           4 :         ~PamFlagKeeper()
     547           4 :         {
     548           4 :             m_nPamFlagsRef = m_nOldPamFlags;
     549           4 :         }
     550             :     };
     551             : 
     552           4 :     PamFlagKeeper oKeeper(nPamFlags);
     553             : 
     554             :     const auto SetStringIfNotEmpty =
     555          52 :         [&](const char *pszItemName, int nOffset, int nLength)
     556             :     {
     557         104 :         const auto str = ReadString(nOffset, nLength);
     558          52 :         if (!str.empty())
     559          28 :             SetMetadataItem(pszItemName, str.c_str(), "FLIR");
     560          52 :     };
     561           4 :     SetStringIfNotEmpty("CreatorSoftware", 4, 16);
     562             : 
     563             :     // Check file format version (big endian most of the time)
     564           4 :     const auto nFileFormatVersion = ReadUInt32(20);
     565           4 :     if (!(nFileFormatVersion >= 100 && nFileFormatVersion < 200))
     566             :     {
     567           0 :         bLittleEndian = true;  // retry with little-endian
     568           0 :         const auto nFileFormatVersionOtherEndianness = ReadUInt32(20);
     569           0 :         if (!(nFileFormatVersionOtherEndianness >= 100 &&
     570             :               nFileFormatVersionOtherEndianness < 200))
     571             :         {
     572           0 :             CPLDebug("JPEG", "FLIR: Unknown file format version: %u",
     573             :                      nFileFormatVersion);
     574           0 :             return;
     575             :         }
     576             :     }
     577             : 
     578           4 :     const auto nOffsetRecordDirectory = ReadUInt32(24);
     579           4 :     const auto nEntryCountRecordDirectory = ReadUInt32(28);
     580             : 
     581           4 :     CPLDebugOnly("JPEG", "FLIR: record offset %u, entry count %u",
     582             :                  nOffsetRecordDirectory, nEntryCountRecordDirectory);
     583           4 :     constexpr size_t SIZE_RECORD_DIRECTORY = 32;
     584           8 :     if (nOffsetRecordDirectory < FLIR_HEADER_SIZE ||
     585           4 :         nOffsetRecordDirectory +
     586           4 :                 SIZE_RECORD_DIRECTORY * nEntryCountRecordDirectory >
     587           4 :             abyFLIR.size())
     588             :     {
     589           0 :         CPLDebug("JPEG", "Invalid FLIR FFF directory");
     590           0 :         return;
     591             :     }
     592             : 
     593             :     // Read the RawData record
     594             :     const auto ReadRawData =
     595           4 :         [&](std::uint32_t nRecOffset, std::uint32_t nRecLength)
     596             :     {
     597           4 :         if (!(nRecLength >= 32 && nRecOffset + nRecLength <= abyFLIR.size()))
     598           0 :             return;
     599             : 
     600           4 :         const int nByteOrder = ReadUInt16(nRecOffset);
     601           4 :         if (nByteOrder == 512)
     602           4 :             bLittleEndian = !bLittleEndian;
     603           0 :         else if (nByteOrder != 2)
     604           0 :             return;
     605           4 :         const auto nImageWidth = ReadUInt16(nRecOffset + 2);
     606           4 :         SetMetadataItem("RawThermalImageWidth", CPLSPrintf("%d", nImageWidth),
     607           4 :                         "FLIR");
     608           4 :         const auto nImageHeight = ReadUInt16(nRecOffset + 4);
     609           4 :         SetMetadataItem("RawThermalImageHeight", CPLSPrintf("%d", nImageHeight),
     610           4 :                         "FLIR");
     611           4 :         m_bRawThermalLittleEndian = bLittleEndian;
     612           4 :         m_nRawThermalImageWidth = nImageWidth;
     613           4 :         m_nRawThermalImageHeight = nImageHeight;
     614           4 :         m_abyRawThermalImage.clear();
     615           4 :         m_abyRawThermalImage.insert(m_abyRawThermalImage.end(),
     616           4 :                                     abyFLIR.begin() + nRecOffset + 32,
     617          12 :                                     abyFLIR.begin() + nRecOffset + nRecLength);
     618             : 
     619           4 :         if (!STARTS_WITH(GetDescription(), "JPEG:"))
     620             :         {
     621           4 :             m_nSubdatasetCount++;
     622           4 :             SetMetadataItem(
     623             :                 CPLSPrintf("SUBDATASET_%d_NAME", m_nSubdatasetCount),
     624             :                 CPLSPrintf("JPEG:\"%s\":FLIR_RAW_THERMAL_IMAGE",
     625           4 :                            GetDescription()),
     626           4 :                 "SUBDATASETS");
     627           4 :             SetMetadataItem(
     628             :                 CPLSPrintf("SUBDATASET_%d_DESC", m_nSubdatasetCount),
     629           4 :                 "FLIR raw thermal image", "SUBDATASETS");
     630             :         }
     631           4 :     };
     632             : 
     633             :     // Read the Camera Info record
     634             :     const auto ReadCameraInfo =
     635           4 :         [&](std::uint32_t nRecOffset, std::uint32_t nRecLength)
     636             :     {
     637           4 :         if (!(nRecLength >= 1126 && nRecOffset + nRecLength <= abyFLIR.size()))
     638           0 :             return;
     639             : 
     640           4 :         const int nByteOrder = ReadUInt16(nRecOffset);
     641           4 :         if (nByteOrder == 512)
     642           4 :             bLittleEndian = !bLittleEndian;
     643           0 :         else if (nByteOrder != 2)
     644           0 :             return;
     645             : 
     646          44 :         const auto ReadFloat32FromKelvin = [=](std::uint32_t nOffset)
     647             :         {
     648          44 :             constexpr float ZERO_CELCIUS_IN_KELVIN = 273.15f;
     649          44 :             return ReadFloat32(nOffset) - ZERO_CELCIUS_IN_KELVIN;
     650           4 :         };
     651           4 :         SetMetadataItem("Emissivity",
     652           4 :                         CPLSPrintf("%f", ReadFloat32(nRecOffset + 32)), "FLIR");
     653           4 :         SetMetadataItem("ObjectDistance",
     654           4 :                         CPLSPrintf("%f m", ReadFloat32(nRecOffset + 36)),
     655           4 :                         "FLIR");
     656           4 :         SetMetadataItem(
     657             :             "ReflectedApparentTemperature",
     658           4 :             CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 40)), "FLIR");
     659           4 :         SetMetadataItem(
     660             :             "AtmosphericTemperature",
     661           4 :             CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 44)), "FLIR");
     662           4 :         SetMetadataItem(
     663             :             "IRWindowTemperature",
     664           4 :             CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 48)), "FLIR");
     665           4 :         SetMetadataItem("IRWindowTemperature",
     666           4 :                         CPLSPrintf("%f", ReadFloat32(nRecOffset + 52)), "FLIR");
     667           4 :         auto fRelativeHumidity = ReadFloat32(nRecOffset + 60);
     668           4 :         if (fRelativeHumidity > 2)
     669           0 :             fRelativeHumidity /= 100.0f;  // Sometimes expressed in percentage
     670           4 :         SetMetadataItem("RelativeHumidity",
     671           4 :                         CPLSPrintf("%f %%", 100.0f * fRelativeHumidity));
     672           4 :         SetMetadataItem("PlanckR1",
     673           4 :                         CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 88)),
     674           4 :                         "FLIR");
     675           4 :         SetMetadataItem("PlanckB",
     676           4 :                         CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 92)),
     677           4 :                         "FLIR");
     678           4 :         SetMetadataItem("PlanckF",
     679           4 :                         CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 96)),
     680           4 :                         "FLIR");
     681           4 :         SetMetadataItem("AtmosphericTransAlpha1",
     682           4 :                         CPLSPrintf("%f", ReadFloat32(nRecOffset + 112)),
     683           4 :                         "FLIR");
     684           4 :         SetMetadataItem("AtmosphericTransAlpha2",
     685           4 :                         CPLSPrintf("%f", ReadFloat32(nRecOffset + 116)),
     686           4 :                         "FLIR");
     687           4 :         SetMetadataItem("AtmosphericTransBeta1",
     688           4 :                         CPLSPrintf("%f", ReadFloat32(nRecOffset + 120)),
     689           4 :                         "FLIR");
     690           4 :         SetMetadataItem("AtmosphericTransBeta2",
     691           4 :                         CPLSPrintf("%f", ReadFloat32(nRecOffset + 124)),
     692           4 :                         "FLIR");
     693           4 :         SetMetadataItem("AtmosphericTransX",
     694           4 :                         CPLSPrintf("%f", ReadFloat32(nRecOffset + 128)),
     695           4 :                         "FLIR");
     696           4 :         SetMetadataItem(
     697             :             "CameraTemperatureRangeMax",
     698           4 :             CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 144)),
     699           4 :             "FLIR");
     700           4 :         SetMetadataItem(
     701             :             "CameraTemperatureRangeMin",
     702           4 :             CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 148)),
     703           4 :             "FLIR");
     704           4 :         SetMetadataItem(
     705             :             "CameraTemperatureMaxClip",
     706           4 :             CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 152)),
     707           4 :             "FLIR");
     708           4 :         SetMetadataItem(
     709             :             "CameraTemperatureMinClip",
     710           4 :             CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 156)),
     711           4 :             "FLIR");
     712           4 :         SetMetadataItem(
     713             :             "CameraTemperatureMaxWarn",
     714           4 :             CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 160)),
     715           4 :             "FLIR");
     716           4 :         SetMetadataItem(
     717             :             "CameraTemperatureMinWarn",
     718           4 :             CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 164)),
     719           4 :             "FLIR");
     720           4 :         SetMetadataItem(
     721             :             "CameraTemperatureMaxSaturated",
     722           4 :             CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 168)),
     723           4 :             "FLIR");
     724           4 :         SetMetadataItem(
     725             :             "CameraTemperatureMinSaturated",
     726           4 :             CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 172)),
     727           4 :             "FLIR");
     728             : 
     729           4 :         SetStringIfNotEmpty("CameraModel", nRecOffset + 212, 32);
     730           4 :         SetStringIfNotEmpty("CameraPartNumber", nRecOffset + 244, 16);
     731           4 :         SetStringIfNotEmpty("CameraSerialNumber", nRecOffset + 260, 16);
     732           4 :         SetStringIfNotEmpty("CameraSoftware", nRecOffset + 276, 16);
     733           4 :         SetStringIfNotEmpty("LensModel", nRecOffset + 368, 32);
     734           4 :         SetStringIfNotEmpty("LensPartNumber", nRecOffset + 400, 16);
     735           4 :         SetStringIfNotEmpty("LensSerialNumber", nRecOffset + 416, 16);
     736           4 :         SetMetadataItem("FieldOfView",
     737           4 :                         CPLSPrintf("%f deg", ReadFloat32(nRecOffset + 436)),
     738           4 :                         "FLIR");
     739           4 :         SetStringIfNotEmpty("FilterModel", nRecOffset + 492, 16);
     740           4 :         SetStringIfNotEmpty("FilterPartNumber", nRecOffset + 508, 32);
     741           4 :         SetStringIfNotEmpty("FilterSerialNumber", nRecOffset + 540, 32);
     742           4 :         SetMetadataItem("PlanckO",
     743           4 :                         CPLSPrintf("%d", ReadInt32(nRecOffset + 776)), "FLIR");
     744           4 :         SetMetadataItem("PlanckR2",
     745           4 :                         CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 780)),
     746           4 :                         "FLIR");
     747           4 :         SetMetadataItem("RawValueRangeMin",
     748           4 :                         CPLSPrintf("%d", ReadUInt16(nRecOffset + 784)), "FLIR");
     749           4 :         SetMetadataItem("RawValueRangeMax",
     750           4 :                         CPLSPrintf("%d", ReadUInt16(nRecOffset + 786)), "FLIR");
     751           4 :         SetMetadataItem("RawValueMedian",
     752           4 :                         CPLSPrintf("%d", ReadUInt16(nRecOffset + 824)), "FLIR");
     753           4 :         SetMetadataItem("RawValueRange",
     754           4 :                         CPLSPrintf("%d", ReadUInt16(nRecOffset + 828)), "FLIR");
     755           4 :         const auto nUnixTime = ReadUInt32(nRecOffset + 900);
     756           4 :         const auto nSS = ReadUInt32(nRecOffset + 904) & 0xffff;
     757           4 :         const auto nTZ = ReadInt16(nRecOffset + 908);
     758             :         struct tm brokenDown;
     759           4 :         CPLUnixTimeToYMDHMS(static_cast<GIntBig>(nUnixTime) - nTZ * 60,
     760             :                             &brokenDown);
     761             :         std::string osDateTime(CPLSPrintf(
     762           4 :             "%04d-%02d-%02dT%02d:%02d:%02d.%03d", brokenDown.tm_year + 1900,
     763           4 :             brokenDown.tm_mon + 1, brokenDown.tm_mday, brokenDown.tm_hour,
     764           8 :             brokenDown.tm_min, brokenDown.tm_sec, nSS));
     765           4 :         if (nTZ <= 0)
     766           4 :             osDateTime += CPLSPrintf("+%02d:%02d", (-nTZ) / 60, (-nTZ) % 60);
     767             :         else
     768           0 :             osDateTime += CPLSPrintf("-%02d:%02d", nTZ / 60, nTZ % 60);
     769           4 :         SetMetadataItem("DateTimeOriginal", osDateTime.c_str(), "FLIR");
     770           4 :         SetMetadataItem("FocusStepCount",
     771           4 :                         CPLSPrintf("%d", ReadUInt16(nRecOffset + 912)), "FLIR");
     772           4 :         SetMetadataItem("FocusDistance",
     773           4 :                         CPLSPrintf("%f m", ReadFloat32(nRecOffset + 1116)),
     774           4 :                         "FLIR");
     775           4 :         SetMetadataItem("FrameRate",
     776           4 :                         CPLSPrintf("%d", ReadUInt16(nRecOffset + 1124)),
     777           4 :                         "FLIR");
     778           4 :     };
     779             : 
     780             :     // Read the Palette Info record
     781             :     const auto ReadPaletteInfo =
     782           4 :         [&](std::uint32_t nRecOffset, std::uint32_t nRecLength)
     783             :     {
     784           4 :         if (!(nRecLength >= 112 && nRecOffset + nRecLength <= abyFLIR.size()))
     785           0 :             return;
     786           4 :         const int nPaletteColors = abyFLIR[nRecOffset];
     787           4 :         SetMetadataItem("PaletteColors", CPLSPrintf("%d", nPaletteColors),
     788           4 :                         "FLIR");
     789             : 
     790             :         const auto SetColorItem =
     791          72 :             [this, &abyFLIR](const char *pszItem, std::uint32_t nOffset)
     792             :         {
     793          24 :             SetMetadataItem(pszItem,
     794          24 :                             CPLSPrintf("%d %d %d", abyFLIR[nOffset],
     795          24 :                                        abyFLIR[nOffset + 1],
     796          24 :                                        abyFLIR[nOffset + 2]),
     797          24 :                             "FLIR");
     798          28 :         };
     799           4 :         SetColorItem("AboveColor", nRecOffset + 6);
     800           4 :         SetColorItem("BelowColor", nRecOffset + 9);
     801           4 :         SetColorItem("OverflowColor", nRecOffset + 12);
     802           4 :         SetColorItem("UnderflowColor", nRecOffset + 15);
     803           4 :         SetColorItem("Isotherm1Color", nRecOffset + 18);
     804           4 :         SetColorItem("Isotherm2Color", nRecOffset + 21);
     805           4 :         SetMetadataItem("PaletteMethod",
     806           4 :                         CPLSPrintf("%d", abyFLIR[nRecOffset + 26]), "FLIR");
     807           4 :         SetMetadataItem("PaletteStretch",
     808           4 :                         CPLSPrintf("%d", abyFLIR[nRecOffset + 27]), "FLIR");
     809           4 :         SetStringIfNotEmpty("PaletteFileName", nRecOffset + 48, 32);
     810           4 :         SetStringIfNotEmpty("PaletteName", nRecOffset + 80, 32);
     811           4 :         if (nRecLength < static_cast<std::uint32_t>(112 + nPaletteColors * 3))
     812           0 :             return;
     813           8 :         std::string osPalette;
     814         900 :         for (int i = 0; i < nPaletteColors; i++)
     815             :         {
     816         896 :             if (!osPalette.empty())
     817         892 :                 osPalette += ", ";
     818             :             osPalette +=
     819         896 :                 CPLSPrintf("(%d %d %d)", abyFLIR[nRecOffset + 112 + 3 * i + 0],
     820         896 :                            abyFLIR[nRecOffset + 112 + 3 * i + 1],
     821         896 :                            abyFLIR[nRecOffset + 112 + 3 * i + 2]);
     822             :         }
     823           4 :         SetMetadataItem("Palette", osPalette.c_str(), "FLIR");
     824           4 :     };
     825             : 
     826             :     // Read the GPS Info record
     827             :     const auto ReadGPSInfo =
     828           0 :         [&](std::uint32_t nRecOffset, std::uint32_t nRecLength)
     829             :     {
     830           0 :         if (!(nRecLength >= 104 && nRecOffset + nRecLength <= abyFLIR.size()))
     831           0 :             return;
     832           0 :         auto nGPSValid = ReadUInt32(nRecOffset);
     833           0 :         if (nGPSValid == 0x01000000)
     834             :         {
     835           0 :             bLittleEndian = !bLittleEndian;
     836           0 :             nGPSValid = 1;
     837             :         }
     838           0 :         if (nGPSValid != 1)
     839           0 :             return;
     840           0 :         SetMetadataItem("GPSVersionID",
     841           0 :                         CPLSPrintf("%c%c%c%c", abyFLIR[nRecOffset + 4],
     842           0 :                                    abyFLIR[nRecOffset + 5],
     843           0 :                                    abyFLIR[nRecOffset + 6],
     844           0 :                                    abyFLIR[nRecOffset + 7]),
     845           0 :                         "FLIR");
     846           0 :         SetStringIfNotEmpty("GPSLatitudeRef", nRecOffset + 8, 1);
     847           0 :         SetStringIfNotEmpty("GPSLongitudeRef", nRecOffset + 10, 1);
     848           0 :         SetMetadataItem("GPSLatitude",
     849           0 :                         CPLSPrintf("%.10f", ReadFloat64(nRecOffset + 16)),
     850           0 :                         "FLIR");
     851           0 :         SetMetadataItem("GPSLongitude",
     852           0 :                         CPLSPrintf("%.10f", ReadFloat64(nRecOffset + 24)),
     853           0 :                         "FLIR");
     854           0 :         SetMetadataItem("GPSAltitude",
     855           0 :                         CPLSPrintf("%f", ReadFloat32(nRecOffset + 32)), "FLIR");
     856           0 :         SetMetadataItem("GPSDOP",
     857           0 :                         CPLSPrintf("%f", ReadFloat32(nRecOffset + 64)), "FLIR");
     858           0 :         SetStringIfNotEmpty("GPSSpeedRef", nRecOffset + 68, 1);
     859           0 :         SetStringIfNotEmpty("GPSTrackRef", nRecOffset + 70, 1);
     860           0 :         SetMetadataItem("GPSSpeed",
     861           0 :                         CPLSPrintf("%f", ReadFloat32(nRecOffset + 76)), "FLIR");
     862           0 :         SetMetadataItem("GPSTrack",
     863           0 :                         CPLSPrintf("%f", ReadFloat32(nRecOffset + 80)), "FLIR");
     864           0 :         SetStringIfNotEmpty("GPSMapDatum", nRecOffset + 88, 16);
     865           4 :     };
     866             : 
     867           4 :     size_t nOffsetDirEntry = nOffsetRecordDirectory;
     868             : 
     869             :     enum FLIRRecordType
     870             :     {
     871             :         FLIR_REC_FREE = 0,
     872             :         FLIR_REC_RAWDATA = 1,
     873             :         FLIR_REC_CAMERA_INFO = 32,
     874             :         FLIR_REC_PALETTE_INFO = 34,
     875             :         FLIR_REC_GPS_INFO = 43,
     876             :     };
     877             : 
     878             :     // Iterate over records
     879          60 :     for (std::uint32_t iRec = 0; iRec < nEntryCountRecordDirectory; iRec++)
     880             :     {
     881          56 :         const auto nRecType = ReadUInt16(nOffsetDirEntry);
     882          56 :         const auto nRecOffset = ReadUInt32(nOffsetDirEntry + 12);
     883          56 :         const auto nRecLength = ReadUInt32(nOffsetDirEntry + 16);
     884          56 :         if (nRecType == FLIR_REC_FREE && nRecLength == 0)
     885          40 :             continue;  // silently keep empty records of type 0
     886          16 :         CPLDebugOnly("JPEG", "FLIR: record %u, type %u, offset %u, length %u",
     887             :                      iRec, nRecType, nRecOffset, nRecLength);
     888          16 :         if (nRecOffset + nRecLength > abyFLIR.size())
     889             :         {
     890           0 :             CPLDebug("JPEG",
     891             :                      "Invalid record %u, type %u, offset %u, length %u "
     892             :                      "w.r.t total FLIR segment size (%u)",
     893             :                      iRec, nRecType, nRecOffset, nRecLength,
     894           0 :                      static_cast<unsigned>(abyFLIR.size()));
     895           0 :             continue;
     896             :         }
     897          16 :         switch (nRecType)
     898             :         {
     899           4 :             case FLIR_REC_RAWDATA:
     900             :             {
     901           4 :                 const auto bLittleEndianBackup = bLittleEndian;
     902           4 :                 ReadRawData(nRecOffset, nRecLength);
     903           4 :                 bLittleEndian = bLittleEndianBackup;
     904           4 :                 break;
     905             :             }
     906           4 :             case FLIR_REC_CAMERA_INFO:
     907             :             {
     908           4 :                 const auto bLittleEndianBackup = bLittleEndian;
     909           4 :                 ReadCameraInfo(nRecOffset, nRecLength);
     910           4 :                 bLittleEndian = bLittleEndianBackup;
     911           4 :                 break;
     912             :             }
     913           4 :             case FLIR_REC_PALETTE_INFO:
     914             :             {
     915           4 :                 ReadPaletteInfo(nRecOffset, nRecLength);
     916           4 :                 break;
     917             :             }
     918           0 :             case FLIR_REC_GPS_INFO:
     919             :             {
     920           0 :                 const auto bLittleEndianBackup = bLittleEndian;
     921           0 :                 ReadGPSInfo(nRecOffset, nRecLength);
     922           0 :                 bLittleEndian = bLittleEndianBackup;
     923           0 :                 break;
     924             :             }
     925           4 :             default:
     926             :             {
     927           4 :                 CPLDebugOnly("JPEG", "FLIR record ignored");
     928           4 :                 break;
     929             :             }
     930             :         }
     931          16 :         nOffsetDirEntry += SIZE_RECORD_DIRECTORY;
     932             :     }
     933             : 
     934           4 :     CPLDebug("JPEG", "FLIR metadata read");
     935             : }
     936             : 
     937             : /************************************************************************/
     938             : /*                      GetMetadataDomainList()                         */
     939             : /************************************************************************/
     940             : 
     941           5 : char **JPGDatasetCommon::GetMetadataDomainList()
     942             : {
     943           5 :     ReadFLIRMetadata();
     944           5 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
     945             :                                    TRUE, "xml:XMP", "COLOR_PROFILE", "FLIR",
     946           5 :                                    nullptr);
     947             : }
     948             : 
     949             : /************************************************************************/
     950             : /*                        LoadForMetadataDomain()                       */
     951             : /************************************************************************/
     952        3344 : void JPGDatasetCommon::LoadForMetadataDomain(const char *pszDomain)
     953             : {
     954        3344 :     if (m_fpImage == nullptr)
     955           0 :         return;
     956        3344 :     if (eAccess == GA_ReadOnly && !bHasReadEXIFMetadata &&
     957        2791 :         (pszDomain == nullptr || EQUAL(pszDomain, "")))
     958          93 :         ReadEXIFMetadata();
     959        3344 :     if (eAccess == GA_ReadOnly && !bHasReadImageStructureMetadata &&
     960        3336 :         pszDomain != nullptr && EQUAL(pszDomain, "IMAGE_STRUCTURE"))
     961           5 :         ReadImageStructureMetadata();
     962        3344 :     if (eAccess == GA_ReadOnly && pszDomain != nullptr &&
     963        3336 :         EQUAL(pszDomain, "xml:XMP"))
     964             :     {
     965          50 :         if (!bHasReadXMPMetadata)
     966             :         {
     967          46 :             ReadXMPMetadata();
     968             :         }
     969          57 :         if (!bHasReadEXIFMetadata &&
     970           7 :             GDALPamDataset::GetMetadata("xml:XMP") == nullptr)
     971             :         {
     972             :             // XMP can sometimes be embedded in a EXIF TIFF tag
     973           4 :             ReadEXIFMetadata();
     974             :         }
     975             :     }
     976        3344 :     if (eAccess == GA_ReadOnly && !bHasReadICCMetadata &&
     977        2830 :         pszDomain != nullptr && EQUAL(pszDomain, "COLOR_PROFILE"))
     978          36 :         ReadICCProfile();
     979        3344 :     if (eAccess == GA_ReadOnly && !bHasReadFLIRMetadata &&
     980        3318 :         pszDomain != nullptr && EQUAL(pszDomain, "FLIR"))
     981           0 :         ReadFLIRMetadata();
     982        3344 :     if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
     983           2 :         ReadFLIRMetadata();
     984             : }
     985             : 
     986             : /************************************************************************/
     987             : /*                           GetMetadata()                              */
     988             : /************************************************************************/
     989         420 : char **JPGDatasetCommon::GetMetadata(const char *pszDomain)
     990             : {
     991         420 :     LoadForMetadataDomain(pszDomain);
     992         420 :     return GDALPamDataset::GetMetadata(pszDomain);
     993             : }
     994             : 
     995             : /************************************************************************/
     996             : /*                       GetMetadataItem()                              */
     997             : /************************************************************************/
     998        3065 : const char *JPGDatasetCommon::GetMetadataItem(const char *pszName,
     999             :                                               const char *pszDomain)
    1000             : {
    1001        3065 :     if (pszDomain != nullptr && EQUAL(pszDomain, "IMAGE_STRUCTURE"))
    1002             :     {
    1003         144 :         if (EQUAL(pszName, "JPEG_QUALITY"))
    1004           3 :             LoadForMetadataDomain(pszDomain);
    1005             :     }
    1006             :     else
    1007             :     {
    1008        2921 :         LoadForMetadataDomain(pszDomain);
    1009             :     }
    1010        3065 :     return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
    1011             : }
    1012             : 
    1013             : /************************************************************************/
    1014             : /*                        ReadICCProfile()                              */
    1015             : /*                                                                      */
    1016             : /*                 Read ICC Profile from APP2 data                      */
    1017             : /************************************************************************/
    1018          36 : void JPGDatasetCommon::ReadICCProfile()
    1019             : {
    1020          36 :     if (bHasReadICCMetadata)
    1021           0 :         return;
    1022          36 :     bHasReadICCMetadata = true;
    1023             : 
    1024          36 :     const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
    1025             : 
    1026          36 :     int nChunkCount = -1;
    1027          36 :     int anChunkSize[256] = {};
    1028          36 :     char *apChunk[256] = {};
    1029             : 
    1030             :     // Search for APP2 chunk.
    1031          36 :     GByte abyChunkHeader[18] = {};
    1032          36 :     int nChunkLoc = 2;
    1033          36 :     bool bOk = true;
    1034             : 
    1035             :     while (true)
    1036             :     {
    1037         351 :         if (VSIFSeekL(m_fpImage, nChunkLoc, SEEK_SET) != 0)
    1038           0 :             break;
    1039             : 
    1040         351 :         if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, m_fpImage) !=
    1041             :             1)
    1042           3 :             break;
    1043             : 
    1044         348 :         if (abyChunkHeader[0] != 0xFF)
    1045          33 :             break;  // Not a valid tag
    1046             : 
    1047         315 :         if (abyChunkHeader[1] == 0xD9)
    1048           0 :             break;  // End of image
    1049             : 
    1050         315 :         if ((abyChunkHeader[1] >= 0xD0) && (abyChunkHeader[1] <= 0xD8))
    1051             :         {
    1052             :             // Restart tags have no length
    1053           0 :             nChunkLoc += 2;
    1054           0 :             continue;
    1055             :         }
    1056             : 
    1057         315 :         const int nChunkLength = abyChunkHeader[2] * 256 + abyChunkHeader[3];
    1058             : 
    1059         315 :         if (abyChunkHeader[1] == 0xe2 &&
    1060          19 :             memcmp(reinterpret_cast<char *>(abyChunkHeader) + 4,
    1061             :                    "ICC_PROFILE\0", 12) == 0)
    1062             :         {
    1063             :             // Get length and segment ID
    1064             :             // Header:
    1065             :             // APP2 tag: 2 bytes
    1066             :             // App Length: 2 bytes
    1067             :             // ICC_PROFILE\0 tag: 12 bytes
    1068             :             // Segment index: 1 bytes
    1069             :             // Total segments: 1 bytes
    1070          19 :             const int nICCChunkLength = nChunkLength - 16;
    1071          19 :             if (nICCChunkLength < 0)
    1072             :             {
    1073           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    1074             :                          "nICCChunkLength unreasonable: %d", nICCChunkLength);
    1075           0 :                 bOk = false;
    1076           0 :                 break;
    1077             :             }
    1078          19 :             const int nICCChunkID = abyChunkHeader[16];
    1079          19 :             const int nICCMaxChunkID = abyChunkHeader[17];
    1080             : 
    1081          19 :             if (nChunkCount == -1)
    1082           7 :                 nChunkCount = nICCMaxChunkID;
    1083             : 
    1084             :             // Check that all max segment counts are the same.
    1085          19 :             if (nICCMaxChunkID != nChunkCount)
    1086             :             {
    1087           0 :                 bOk = false;
    1088           0 :                 break;
    1089             :             }
    1090             : 
    1091             :             // Check that no segment ID is larger than the total segment count.
    1092          19 :             if ((nICCChunkID > nChunkCount) || (nICCChunkID == 0) ||
    1093             :                 (nChunkCount == 0))
    1094             :             {
    1095           0 :                 bOk = false;
    1096           0 :                 break;
    1097             :             }
    1098             : 
    1099             :             // Check if ICC segment already loaded.
    1100          19 :             if (apChunk[nICCChunkID - 1] != nullptr)
    1101             :             {
    1102           0 :                 bOk = false;
    1103           0 :                 break;
    1104             :             }
    1105             : 
    1106             :             // Load it.
    1107          38 :             apChunk[nICCChunkID - 1] =
    1108          19 :                 static_cast<char *>(VSIMalloc(nICCChunkLength));
    1109          19 :             if (apChunk[nICCChunkID - 1] == nullptr)
    1110             :             {
    1111           0 :                 bOk = false;
    1112           0 :                 break;
    1113             :             }
    1114          19 :             anChunkSize[nICCChunkID - 1] = nICCChunkLength;
    1115             : 
    1116          19 :             if (VSIFReadL(apChunk[nICCChunkID - 1], nICCChunkLength, 1,
    1117          19 :                           m_fpImage) != 1)
    1118             :             {
    1119           0 :                 bOk = false;
    1120           0 :                 break;
    1121             :             }
    1122             :         }
    1123             : 
    1124         315 :         nChunkLoc += 2 + nChunkLength;
    1125         315 :     }
    1126             : 
    1127          36 :     int nTotalSize = 0;
    1128             : 
    1129             :     // Get total size and verify that there are no missing segments.
    1130          36 :     if (bOk)
    1131             :     {
    1132          55 :         for (int i = 0; i < nChunkCount; i++)
    1133             :         {
    1134          19 :             if (apChunk[i] == nullptr)
    1135             :             {
    1136             :                 // Missing segment - abort.
    1137           0 :                 bOk = false;
    1138           0 :                 break;
    1139             :             }
    1140          19 :             const int nSize = anChunkSize[i];
    1141          38 :             if (nSize < 0 ||
    1142          19 :                 nTotalSize > std::numeric_limits<int>::max() - nSize)
    1143             :             {
    1144           0 :                 CPLError(CE_Failure, CPLE_FileIO, "nTotalSize nonsensical");
    1145           0 :                 bOk = false;
    1146           0 :                 break;
    1147             :             }
    1148          19 :             nTotalSize += anChunkSize[i];
    1149             :         }
    1150             :     }
    1151             : 
    1152             :     // TODO(schwehr): Can we know what the maximum reasonable size is?
    1153          36 :     if (nTotalSize > 2 << 28)
    1154             :     {
    1155           0 :         CPLError(CE_Failure, CPLE_FileIO, "nTotalSize unreasonable: %d",
    1156             :                  nTotalSize);
    1157           0 :         bOk = false;
    1158             :     }
    1159             : 
    1160             :     // Merge all segments together and set metadata.
    1161          36 :     if (bOk && nChunkCount > 0)
    1162             :     {
    1163           7 :         char *pBuffer = static_cast<char *>(VSIMalloc(nTotalSize));
    1164           7 :         if (pBuffer == nullptr)
    1165             :         {
    1166           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    1167             :                      "ICCProfile too large.  nTotalSize: %d", nTotalSize);
    1168             :         }
    1169             :         else
    1170             :         {
    1171           7 :             char *pBufferPtr = pBuffer;
    1172          26 :             for (int i = 0; i < nChunkCount; i++)
    1173             :             {
    1174          19 :                 memcpy(pBufferPtr, apChunk[i], anChunkSize[i]);
    1175          19 :                 pBufferPtr += anChunkSize[i];
    1176             :             }
    1177             : 
    1178             :             // Escape the profile.
    1179             :             char *pszBase64Profile =
    1180           7 :                 CPLBase64Encode(nTotalSize, reinterpret_cast<GByte *>(pBuffer));
    1181             : 
    1182             :             // Avoid setting the PAM dirty bit just for that.
    1183           7 :             const int nOldPamFlags = nPamFlags;
    1184             : 
    1185             :             // Set ICC profile metadata.
    1186           7 :             SetMetadataItem("SOURCE_ICC_PROFILE", pszBase64Profile,
    1187           7 :                             "COLOR_PROFILE");
    1188             : 
    1189           7 :             nPamFlags = nOldPamFlags;
    1190             : 
    1191           7 :             VSIFree(pBuffer);
    1192           7 :             CPLFree(pszBase64Profile);
    1193             :         }
    1194             :     }
    1195             : 
    1196          55 :     for (int i = 0; i < nChunkCount; i++)
    1197             :     {
    1198          19 :         if (apChunk[i] != nullptr)
    1199          19 :             VSIFree(apChunk[i]);
    1200             :     }
    1201             : 
    1202          36 :     VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
    1203             : }
    1204             : 
    1205             : /************************************************************************/
    1206             : /*                        EXIFInit()                                    */
    1207             : /*                                                                      */
    1208             : /*           Create Metadata from Information file directory APP1       */
    1209             : /************************************************************************/
    1210        2143 : bool JPGDatasetCommon::EXIFInit(VSILFILE *fp)
    1211             : {
    1212        2143 :     if (nTiffDirStart == 0)
    1213           0 :         return false;
    1214        2143 :     if (nTiffDirStart > 0)
    1215           2 :         return true;
    1216        2141 :     nTiffDirStart = 0;
    1217             : 
    1218             : #ifdef CPL_MSB
    1219             :     constexpr bool bigendian = true;
    1220             : #else
    1221        2141 :     constexpr bool bigendian = false;
    1222             : #endif
    1223             : 
    1224             :     // Search for APP1 chunk.
    1225        2141 :     GByte abyChunkHeader[10] = {};
    1226        2141 :     int nChunkLoc = 2;
    1227             : 
    1228             :     while (true)
    1229             :     {
    1230        2306 :         if (VSIFSeekL(fp, nChunkLoc, SEEK_SET) != 0)
    1231           0 :             return false;
    1232             : 
    1233        2306 :         if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, fp) != 1)
    1234           0 :             return false;
    1235             : 
    1236        2306 :         const int nChunkLength = abyChunkHeader[2] * 256 + abyChunkHeader[3];
    1237             :         // COM marker
    1238        2306 :         if (abyChunkHeader[0] == 0xFF && abyChunkHeader[1] == 0xFE &&
    1239             :             nChunkLength >= 2)
    1240             :         {
    1241             :             char *pszComment =
    1242           3 :                 static_cast<char *>(CPLMalloc(nChunkLength - 2 + 1));
    1243           6 :             if (nChunkLength > 2 &&
    1244           6 :                 VSIFSeekL(fp, nChunkLoc + 4, SEEK_SET) == 0 &&
    1245           3 :                 VSIFReadL(pszComment, nChunkLength - 2, 1, fp) == 1)
    1246             :             {
    1247           3 :                 pszComment[nChunkLength - 2] = 0;
    1248             :                 // Avoid setting the PAM dirty bit just for that.
    1249           3 :                 const int nOldPamFlags = nPamFlags;
    1250             :                 // Set ICC profile metadata.
    1251           3 :                 SetMetadataItem("COMMENT", pszComment);
    1252           3 :                 nPamFlags = nOldPamFlags;
    1253             :             }
    1254           3 :             CPLFree(pszComment);
    1255             :         }
    1256             :         else
    1257             :         {
    1258        2303 :             if (abyChunkHeader[0] != 0xFF || (abyChunkHeader[1] & 0xf0) != 0xe0)
    1259             :                 break;  // Not an APP chunk.
    1260             : 
    1261         162 :             if (abyChunkHeader[1] == 0xe1 &&
    1262          55 :                 STARTS_WITH(reinterpret_cast<char *>(abyChunkHeader) + 4,
    1263             :                             "Exif"))
    1264             :             {
    1265          47 :                 if (nTIFFHEADER < 0)
    1266             :                 {
    1267          47 :                     nTIFFHEADER = nChunkLoc + 10;
    1268             :                 }
    1269             :                 else
    1270             :                 {
    1271           0 :                     CPLDebug(
    1272             :                         "JPEG",
    1273             :                         "Another Exif directory found at offset %u. Ignoring "
    1274             :                         "it and only taking into account the one at offset %u",
    1275           0 :                         unsigned(nChunkLoc + 10), unsigned(nTIFFHEADER));
    1276             :                 }
    1277             :             }
    1278             :         }
    1279             : 
    1280         165 :         nChunkLoc += 2 + nChunkLength;
    1281         165 :     }
    1282             : 
    1283        2141 :     if (nTIFFHEADER < 0)
    1284        2094 :         return false;
    1285             : 
    1286             :     // Read TIFF header.
    1287          47 :     TIFFHeader hdr = {0, 0, 0};
    1288             : 
    1289          47 :     VSIFSeekL(fp, nTIFFHEADER, SEEK_SET);
    1290          47 :     if (VSIFReadL(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr))
    1291             :     {
    1292           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1293             :                  "Failed to read %d byte from image header.",
    1294             :                  static_cast<int>(sizeof(hdr)));
    1295           0 :         return false;
    1296             :     }
    1297             : 
    1298          47 :     if (hdr.tiff_magic != TIFF_BIGENDIAN && hdr.tiff_magic != TIFF_LITTLEENDIAN)
    1299             :     {
    1300           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1301           0 :                  "Not a TIFF file, bad magic number %u (%#x)", hdr.tiff_magic,
    1302           0 :                  hdr.tiff_magic);
    1303           0 :         return false;
    1304             :     }
    1305             : 
    1306          47 :     if (hdr.tiff_magic == TIFF_BIGENDIAN)
    1307           2 :         bSwabflag = !bigendian;
    1308          47 :     if (hdr.tiff_magic == TIFF_LITTLEENDIAN)
    1309          45 :         bSwabflag = bigendian;
    1310             : 
    1311          47 :     if (bSwabflag)
    1312             :     {
    1313           2 :         CPL_SWAP16PTR(&hdr.tiff_version);
    1314           2 :         CPL_SWAP32PTR(&hdr.tiff_diroff);
    1315             :     }
    1316             : 
    1317          47 :     if (hdr.tiff_version != TIFF_VERSION)
    1318             :     {
    1319           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1320             :                  "Not a TIFF file, bad version number %u (%#x)",
    1321           0 :                  hdr.tiff_version, hdr.tiff_version);
    1322           0 :         return false;
    1323             :     }
    1324          47 :     nTiffDirStart = hdr.tiff_diroff;
    1325             : 
    1326          47 :     CPLDebug("JPEG", "Magic: %#x <%s-endian> Version: %#x\n", hdr.tiff_magic,
    1327          47 :              hdr.tiff_magic == TIFF_BIGENDIAN ? "big" : "little",
    1328          47 :              hdr.tiff_version);
    1329             : 
    1330          47 :     return true;
    1331             : }
    1332             : 
    1333             : /************************************************************************/
    1334             : /*                            JPGMaskBand()                             */
    1335             : /************************************************************************/
    1336             : 
    1337          15 : JPGMaskBand::JPGMaskBand(JPGDatasetCommon *poDSIn)
    1338             : 
    1339             : {
    1340          15 :     poDS = poDSIn;
    1341          15 :     nBand = 0;
    1342             : 
    1343          15 :     nRasterXSize = poDS->GetRasterXSize();
    1344          15 :     nRasterYSize = poDS->GetRasterYSize();
    1345             : 
    1346          15 :     eDataType = GDT_Byte;
    1347          15 :     nBlockXSize = nRasterXSize;
    1348          15 :     nBlockYSize = 1;
    1349          15 : }
    1350             : 
    1351             : /************************************************************************/
    1352             : /*                             IReadBlock()                             */
    1353             : /************************************************************************/
    1354             : 
    1355        4261 : CPLErr JPGMaskBand::IReadBlock(int /* nBlockX */, int nBlockY, void *pImage)
    1356             : {
    1357        4261 :     JPGDatasetCommon *poJDS = cpl::down_cast<JPGDatasetCommon *>(poDS);
    1358             : 
    1359             :     // Make sure the mask is loaded and decompressed.
    1360        4261 :     poJDS->DecompressMask();
    1361        4261 :     if (poJDS->pabyBitMask == nullptr)
    1362           0 :         return CE_Failure;
    1363             : 
    1364             :     // Set mask based on bitmask for this scanline.
    1365        4261 :     GUInt32 iBit =
    1366        4261 :         static_cast<GUInt32>(nBlockY) * static_cast<GUInt32>(nBlockXSize);
    1367             : 
    1368        4261 :     GByte *const pbyImage = static_cast<GByte *>(pImage);
    1369        4261 :     if (poJDS->bMaskLSBOrder)
    1370             :     {
    1371     2104220 :         for (int iX = 0; iX < nBlockXSize; iX++)
    1372             :         {
    1373     2100020 :             if (poJDS->pabyBitMask[iBit >> 3] & (0x1 << (iBit & 7)))
    1374     1582040 :                 pbyImage[iX] = 255;
    1375             :             else
    1376      517974 :                 pbyImage[iX] = 0;
    1377     2100020 :             iBit++;
    1378             :         }
    1379             :     }
    1380             :     else
    1381             :     {
    1382        1706 :         for (int iX = 0; iX < nBlockXSize; iX++)
    1383             :         {
    1384        1649 :             if (poJDS->pabyBitMask[iBit >> 3] & (0x1 << (7 - (iBit & 7))))
    1385         584 :                 pbyImage[iX] = 255;
    1386             :             else
    1387        1065 :                 pbyImage[iX] = 0;
    1388        1649 :             iBit++;
    1389             :         }
    1390             :     }
    1391             : 
    1392        4261 :     return CE_None;
    1393             : }
    1394             : 
    1395             : /************************************************************************/
    1396             : /*                           JPGRasterBand()                            */
    1397             : /************************************************************************/
    1398             : 
    1399       24266 : JPGRasterBand::JPGRasterBand(JPGDatasetCommon *poDSIn, int nBandIn)
    1400       24266 :     : poGDS(poDSIn)
    1401             : {
    1402       24266 :     poDS = poDSIn;
    1403             : 
    1404       24266 :     nBand = nBandIn;
    1405       24266 :     if (poDSIn->GetDataPrecision() == 12)
    1406          15 :         eDataType = GDT_UInt16;
    1407             :     else
    1408       24251 :         eDataType = GDT_Byte;
    1409             : 
    1410       24266 :     nBlockXSize = poDSIn->nRasterXSize;
    1411       24266 :     nBlockYSize = 1;
    1412             : 
    1413       24266 :     GDALMajorObject::SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
    1414       24266 :     if (eDataType == GDT_UInt16)
    1415          15 :         GDALMajorObject::SetMetadataItem("NBITS", "12", "IMAGE_STRUCTURE");
    1416       24266 : }
    1417             : 
    1418             : /************************************************************************/
    1419             : /*                           JPGCreateBand()                            */
    1420             : /************************************************************************/
    1421             : 
    1422       24266 : GDALRasterBand *JPGCreateBand(JPGDatasetCommon *poDS, int nBand)
    1423             : {
    1424       24266 :     return new JPGRasterBand(poDS, nBand);
    1425             : }
    1426             : 
    1427             : /************************************************************************/
    1428             : /*                             IReadBlock()                             */
    1429             : /************************************************************************/
    1430             : 
    1431      100973 : CPLErr JPGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
    1432             : 
    1433             : {
    1434      100973 :     CPLAssert(nBlockXOff == 0);
    1435             : 
    1436      100973 :     const int nXSize = GetXSize();
    1437      100973 :     const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
    1438      100973 :     if (poGDS->m_fpImage == nullptr)
    1439             :     {
    1440          70 :         memset(pImage, 0, cpl::fits_on<int>(nXSize * nWordSize));
    1441          70 :         return CE_None;
    1442             :     }
    1443             : 
    1444             :     // Load the desired scanline into the working buffer.
    1445      100903 :     CPLErr eErr = poGDS->LoadScanline(nBlockYOff);
    1446      100903 :     if (eErr != CE_None)
    1447           5 :         return eErr;
    1448             : 
    1449             :     // Transfer between the working buffer the callers buffer.
    1450      100898 :     if (poGDS->GetRasterCount() == 1)
    1451             :     {
    1452             : #ifdef JPEG_LIB_MK1
    1453             :         GDALCopyWords(poGDS->m_pabyScanline, GDT_UInt16, 2, pImage, eDataType,
    1454             :                       nWordSize, nXSize);
    1455             : #else
    1456       28568 :         memcpy(pImage, poGDS->m_pabyScanline,
    1457       28568 :                cpl::fits_on<int>(nXSize * nWordSize));
    1458             : #endif
    1459             :     }
    1460             :     else
    1461             :     {
    1462             : #ifdef JPEG_LIB_MK1
    1463             :         GDALCopyWords(poGDS->m_pabyScanline + (nBand - 1) * 2, GDT_UInt16, 6,
    1464             :                       pImage, eDataType, nWordSize, nXSize);
    1465             : #else
    1466      215466 :         if (poGDS->eGDALColorSpace == JCS_RGB &&
    1467       72330 :             poGDS->GetOutColorSpace() == JCS_CMYK && eDataType == GDT_Byte)
    1468             :         {
    1469         150 :             GByte *const pbyImage = static_cast<GByte *>(pImage);
    1470         150 :             if (nBand == 1)
    1471             :             {
    1472        2550 :                 for (int i = 0; i < nXSize; i++)
    1473             :                 {
    1474        2500 :                     const int C = poGDS->m_pabyScanline[i * 4 + 0];
    1475        2500 :                     const int K = poGDS->m_pabyScanline[i * 4 + 3];
    1476        2500 :                     pbyImage[i] = static_cast<GByte>((C * K) / 255);
    1477             :                 }
    1478             :             }
    1479         100 :             else if (nBand == 2)
    1480             :             {
    1481        2550 :                 for (int i = 0; i < nXSize; i++)
    1482             :                 {
    1483        2500 :                     const int M = poGDS->m_pabyScanline[i * 4 + 1];
    1484        2500 :                     const int K = poGDS->m_pabyScanline[i * 4 + 3];
    1485        2500 :                     pbyImage[i] = static_cast<GByte>((M * K) / 255);
    1486             :                 }
    1487             :             }
    1488          50 :             else if (nBand == 3)
    1489             :             {
    1490        2550 :                 for (int i = 0; i < nXSize; i++)
    1491             :                 {
    1492        2500 :                     const int Y = poGDS->m_pabyScanline[i * 4 + 2];
    1493        2500 :                     const int K = poGDS->m_pabyScanline[i * 4 + 3];
    1494        2500 :                     pbyImage[i] = static_cast<GByte>((Y * K) / 255);
    1495             :                 }
    1496             :             }
    1497             :         }
    1498             :         else
    1499             :         {
    1500      144360 :             GDALCopyWords(poGDS->m_pabyScanline + (nBand - 1) * nWordSize,
    1501       72180 :                           eDataType, nWordSize * poGDS->GetRasterCount(),
    1502             :                           pImage, eDataType, nWordSize, nXSize);
    1503             :         }
    1504             : #endif
    1505             :     }
    1506             : 
    1507             :     // Forcibly load the other bands associated with this scanline.
    1508      100898 :     if (nBand == 1)
    1509             :     {
    1510       90516 :         for (int iBand = 2; iBand <= poGDS->GetRasterCount(); iBand++)
    1511             :         {
    1512             :             GDALRasterBlock *const poBlock =
    1513       41408 :                 poGDS->GetRasterBand(iBand)->GetLockedBlockRef(nBlockXOff,
    1514             :                                                                nBlockYOff);
    1515       41408 :             if (poBlock != nullptr)
    1516       41408 :                 poBlock->DropLock();
    1517             :         }
    1518             :     }
    1519             : 
    1520      100898 :     return CE_None;
    1521             : }
    1522             : 
    1523             : /************************************************************************/
    1524             : /*                       GetColorInterpretation()                       */
    1525             : /************************************************************************/
    1526             : 
    1527         470 : GDALColorInterp JPGRasterBand::GetColorInterpretation()
    1528             : 
    1529             : {
    1530         470 :     if (poGDS->eGDALColorSpace == JCS_GRAYSCALE)
    1531          91 :         return GCI_GrayIndex;
    1532             : 
    1533         379 :     else if (poGDS->eGDALColorSpace == JCS_RGB)
    1534             :     {
    1535         255 :         if (nBand == 1)
    1536          86 :             return GCI_RedBand;
    1537         169 :         else if (nBand == 2)
    1538          80 :             return GCI_GreenBand;
    1539             :         else
    1540          89 :             return GCI_BlueBand;
    1541             :     }
    1542         124 :     else if (poGDS->eGDALColorSpace == JCS_CMYK)
    1543             :     {
    1544         124 :         if (nBand == 1)
    1545          30 :             return GCI_CyanBand;
    1546          94 :         else if (nBand == 2)
    1547          26 :             return GCI_MagentaBand;
    1548          68 :         else if (nBand == 3)
    1549          26 :             return GCI_YellowBand;
    1550             :         else
    1551          42 :             return GCI_BlackBand;
    1552             :     }
    1553           0 :     else if (poGDS->eGDALColorSpace == JCS_YCbCr ||
    1554           0 :              poGDS->eGDALColorSpace == JCS_YCCK)
    1555             :     {
    1556           0 :         if (nBand == 1)
    1557           0 :             return GCI_YCbCr_YBand;
    1558           0 :         else if (nBand == 2)
    1559           0 :             return GCI_YCbCr_CbBand;
    1560           0 :         else if (nBand == 3)
    1561           0 :             return GCI_YCbCr_CrBand;
    1562             :         else
    1563           0 :             return GCI_BlackBand;
    1564             :     }
    1565             : 
    1566           0 :     CPLAssert(false);
    1567             :     return GCI_Undefined;
    1568             : }
    1569             : 
    1570             : /************************************************************************/
    1571             : /*                            GetMaskBand()                             */
    1572             : /************************************************************************/
    1573             : 
    1574         301 : GDALRasterBand *JPGRasterBand::GetMaskBand()
    1575             : 
    1576             : {
    1577         301 :     if (poGDS->nScaleFactor > 1)
    1578           0 :         return GDALPamRasterBand::GetMaskBand();
    1579             : 
    1580         301 :     if (poGDS->m_fpImage == nullptr)
    1581           0 :         return nullptr;
    1582             : 
    1583         301 :     if (!poGDS->bHasCheckedForMask)
    1584             :     {
    1585          62 :         if (CPLTestBool(CPLGetConfigOption("JPEG_READ_MASK", "YES")))
    1586          62 :             poGDS->CheckForMask();
    1587          62 :         poGDS->bHasCheckedForMask = true;
    1588             :     }
    1589         301 :     if (poGDS->pabyCMask)
    1590             :     {
    1591          42 :         if (poGDS->poMaskBand == nullptr)
    1592          15 :             poGDS->poMaskBand = new JPGMaskBand(poGDS);
    1593             : 
    1594          42 :         return poGDS->poMaskBand;
    1595             :     }
    1596             : 
    1597         259 :     return GDALPamRasterBand::GetMaskBand();
    1598             : }
    1599             : 
    1600             : /************************************************************************/
    1601             : /*                            GetMaskFlags()                            */
    1602             : /************************************************************************/
    1603             : 
    1604         281 : int JPGRasterBand::GetMaskFlags()
    1605             : 
    1606             : {
    1607         281 :     if (poGDS->nScaleFactor > 1)
    1608           0 :         return GDALPamRasterBand::GetMaskFlags();
    1609             : 
    1610         281 :     if (poGDS->m_fpImage == nullptr)
    1611           0 :         return 0;
    1612             : 
    1613         281 :     GetMaskBand();
    1614         281 :     if (poGDS->poMaskBand != nullptr)
    1615          25 :         return GMF_PER_DATASET;
    1616             : 
    1617         256 :     return GDALPamRasterBand::GetMaskFlags();
    1618             : }
    1619             : 
    1620             : /************************************************************************/
    1621             : /*                            GetOverview()                             */
    1622             : /************************************************************************/
    1623             : 
    1624       11079 : GDALRasterBand *JPGRasterBand::GetOverview(int i)
    1625             : {
    1626       11079 :     if (i < 0 || i >= GetOverviewCount())
    1627           2 :         return nullptr;
    1628             : 
    1629       11077 :     if (poGDS->nInternalOverviewsCurrent == 0)
    1630           2 :         return GDALPamRasterBand::GetOverview(i);
    1631             : 
    1632       11075 :     return poGDS->papoInternalOverviews[i]->GetRasterBand(nBand);
    1633             : }
    1634             : 
    1635             : /************************************************************************/
    1636             : /*                         GetOverviewCount()                           */
    1637             : /************************************************************************/
    1638             : 
    1639       21572 : int JPGRasterBand::GetOverviewCount()
    1640             : {
    1641       21572 :     if (!poGDS->AreOverviewsEnabled())
    1642           0 :         return 0;
    1643             : 
    1644       21572 :     poGDS->InitInternalOverviews();
    1645             : 
    1646       21572 :     if (poGDS->nInternalOverviewsCurrent == 0)
    1647        2502 :         return GDALPamRasterBand::GetOverviewCount();
    1648             : 
    1649       19070 :     return poGDS->nInternalOverviewsCurrent;
    1650             : }
    1651             : 
    1652             : /************************************************************************/
    1653             : /* ==================================================================== */
    1654             : /*                             JPGDataset                               */
    1655             : /* ==================================================================== */
    1656             : /************************************************************************/
    1657             : 
    1658       10732 : JPGDatasetCommon::JPGDatasetCommon()
    1659             :     : nScaleFactor(1), bHasInitInternalOverviews(false),
    1660             :       nInternalOverviewsCurrent(0), nInternalOverviewsToFree(0),
    1661             :       papoInternalOverviews(nullptr), bGeoTransformValid(false),
    1662             :       m_fpImage(nullptr), nSubfileOffset(0), nLoadedScanline(-1),
    1663             :       m_pabyScanline(nullptr), bHasReadEXIFMetadata(false),
    1664             :       bHasReadXMPMetadata(false), bHasReadICCMetadata(false),
    1665             :       papszMetadata(nullptr), nExifOffset(-1), nInterOffset(-1), nGPSOffset(-1),
    1666             :       bSwabflag(false), nTiffDirStart(-1), nTIFFHEADER(-1),
    1667             :       bHasDoneJpegCreateDecompress(false), bHasDoneJpegStartDecompress(false),
    1668             :       bHasCheckedForMask(false), poMaskBand(nullptr), pabyBitMask(nullptr),
    1669             :       bMaskLSBOrder(true), pabyCMask(nullptr), nCMaskSize(0),
    1670             :       eGDALColorSpace(JCS_UNKNOWN), bIsSubfile(false),
    1671       10732 :       bHasTriedLoadWorldFileOrTab(false)
    1672             : {
    1673       10732 :     adfGeoTransform[0] = 0.0;
    1674       10732 :     adfGeoTransform[1] = 1.0;
    1675       10732 :     adfGeoTransform[2] = 0.0;
    1676       10732 :     adfGeoTransform[3] = 0.0;
    1677       10732 :     adfGeoTransform[4] = 0.0;
    1678       10732 :     adfGeoTransform[5] = 1.0;
    1679       10732 : }
    1680             : 
    1681             : /************************************************************************/
    1682             : /*                           ~JPGDataset()                              */
    1683             : /************************************************************************/
    1684             : 
    1685       10732 : JPGDatasetCommon::~JPGDatasetCommon()
    1686             : 
    1687             : {
    1688       10732 :     if (m_fpImage != nullptr)
    1689       10651 :         VSIFCloseL(m_fpImage);
    1690             : 
    1691       10732 :     if (m_pabyScanline != nullptr)
    1692        2874 :         CPLFree(m_pabyScanline);
    1693       10732 :     if (papszMetadata != nullptr)
    1694          42 :         CSLDestroy(papszMetadata);
    1695             : 
    1696       10732 :     CPLFree(pabyBitMask);
    1697       10732 :     CPLFree(pabyCMask);
    1698       10732 :     delete poMaskBand;
    1699             : 
    1700       10732 :     JPGDatasetCommon::CloseDependentDatasets();
    1701       10732 : }
    1702             : 
    1703             : /************************************************************************/
    1704             : /*                       CloseDependentDatasets()                       */
    1705             : /************************************************************************/
    1706             : 
    1707       10732 : int JPGDatasetCommon::CloseDependentDatasets()
    1708             : {
    1709       10732 :     int bRet = GDALPamDataset::CloseDependentDatasets();
    1710       10732 :     if (nInternalOverviewsToFree)
    1711             :     {
    1712        2472 :         bRet = TRUE;
    1713        9888 :         for (int i = 0; i < nInternalOverviewsToFree; i++)
    1714        7416 :             delete papoInternalOverviews[i];
    1715        2472 :         nInternalOverviewsToFree = 0;
    1716             :     }
    1717       10732 :     CPLFree(papoInternalOverviews);
    1718       10732 :     papoInternalOverviews = nullptr;
    1719             : 
    1720       10732 :     return bRet;
    1721             : }
    1722             : 
    1723             : /************************************************************************/
    1724             : /*                          InitEXIFOverview()                          */
    1725             : /************************************************************************/
    1726             : 
    1727        2046 : GDALDataset *JPGDatasetCommon::InitEXIFOverview()
    1728             : {
    1729        2046 :     if (!EXIFInit(m_fpImage))
    1730        2039 :         return nullptr;
    1731             : 
    1732             :     // Read number of entry in directory.
    1733           7 :     GUInt16 nEntryCount = 0;
    1734          21 :     if (nTiffDirStart > (INT_MAX - nTIFFHEADER) ||
    1735          14 :         VSIFSeekL(m_fpImage, nTiffDirStart + nTIFFHEADER, SEEK_SET) != 0 ||
    1736           7 :         VSIFReadL(&nEntryCount, 1, sizeof(GUInt16), m_fpImage) !=
    1737             :             sizeof(GUInt16))
    1738             :     {
    1739           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1740             :                  "Error reading EXIF Directory count at " CPL_FRMT_GUIB,
    1741           0 :                  static_cast<vsi_l_offset>(nTiffDirStart) + nTIFFHEADER);
    1742           0 :         return nullptr;
    1743             :     }
    1744             : 
    1745           7 :     if (bSwabflag)
    1746           1 :         CPL_SWAP16PTR(&nEntryCount);
    1747             : 
    1748             :     // Some files are corrupt, a large entry count is a sign of this.
    1749           7 :     if (nEntryCount > 125)
    1750             :     {
    1751           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1752             :                  "Ignoring EXIF directory with unlikely entry count (%d).",
    1753             :                  nEntryCount);
    1754           0 :         return nullptr;
    1755             :     }
    1756             : 
    1757             :     // Skip EXIF entries.
    1758           7 :     VSIFSeekL(m_fpImage, nEntryCount * sizeof(GDALEXIFTIFFDirEntry), SEEK_CUR);
    1759             : 
    1760             :     // Read offset of next directory (IFD1).
    1761           7 :     GUInt32 nNextDirOff = 0;
    1762           7 :     if (VSIFReadL(&nNextDirOff, 1, sizeof(GUInt32), m_fpImage) !=
    1763             :         sizeof(GUInt32))
    1764           0 :         return nullptr;
    1765           7 :     if (bSwabflag)
    1766           1 :         CPL_SWAP32PTR(&nNextDirOff);
    1767           7 :     if (nNextDirOff == 0 || nNextDirOff > UINT_MAX - nTIFFHEADER)
    1768           0 :         return nullptr;
    1769             : 
    1770             :     // Seek to IFD1.
    1771          14 :     if (VSIFSeekL(m_fpImage, nTIFFHEADER + nNextDirOff, SEEK_SET) != 0 ||
    1772           7 :         VSIFReadL(&nEntryCount, 1, sizeof(GUInt16), m_fpImage) !=
    1773             :             sizeof(GUInt16))
    1774             :     {
    1775           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1776             :                  "Error reading IFD1 Directory count at %d.",
    1777           0 :                  nTIFFHEADER + nNextDirOff);
    1778           0 :         return nullptr;
    1779             :     }
    1780             : 
    1781           7 :     if (bSwabflag)
    1782           1 :         CPL_SWAP16PTR(&nEntryCount);
    1783           7 :     if (nEntryCount > 125)
    1784             :     {
    1785           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1786             :                  "Ignoring IFD1 directory with unlikely entry count (%d).",
    1787             :                  nEntryCount);
    1788           0 :         return nullptr;
    1789             :     }
    1790             : #if DEBUG_VERBOSE
    1791             :     CPLDebug("JPEG", "IFD1 entry count = %d", nEntryCount);
    1792             : #endif
    1793             : 
    1794           7 :     int nImageWidth = 0;
    1795           7 :     int nImageHeight = 0;
    1796           7 :     int nCompression = 6;
    1797           7 :     GUInt32 nJpegIFOffset = 0;
    1798           7 :     GUInt32 nJpegIFByteCount = 0;
    1799          39 :     for (int i = 0; i < nEntryCount; i++)
    1800             :     {
    1801             :         GDALEXIFTIFFDirEntry sEntry;
    1802          32 :         if (VSIFReadL(&sEntry, 1, sizeof(sEntry), m_fpImage) != sizeof(sEntry))
    1803             :         {
    1804           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1805             :                      "Cannot read entry %d of IFD1", i);
    1806           0 :             return nullptr;
    1807             :         }
    1808          32 :         if (bSwabflag)
    1809             :         {
    1810           2 :             CPL_SWAP16PTR(&sEntry.tdir_tag);
    1811           2 :             CPL_SWAP16PTR(&sEntry.tdir_type);
    1812           2 :             CPL_SWAP32PTR(&sEntry.tdir_count);
    1813           2 :             CPL_SWAP32PTR(&sEntry.tdir_offset);
    1814             :         }
    1815             : 
    1816             : #ifdef DEBUG_VERBOSE
    1817             :         CPLDebug("JPEG", "tag = %d (0x%4X), type = %d, count = %d, offset = %d",
    1818             :                  sEntry.tdir_tag, sEntry.tdir_tag, sEntry.tdir_type,
    1819             :                  sEntry.tdir_count, sEntry.tdir_offset);
    1820             : #endif
    1821             : 
    1822          32 :         if ((sEntry.tdir_type == TIFF_SHORT || sEntry.tdir_type == TIFF_LONG) &&
    1823          32 :             sEntry.tdir_count == 1)
    1824             :         {
    1825          32 :             switch (sEntry.tdir_tag)
    1826             :             {
    1827           6 :                 case JPEG_TIFF_IMAGEWIDTH:
    1828           6 :                     nImageWidth = sEntry.tdir_offset;
    1829           6 :                     break;
    1830           6 :                 case JPEG_TIFF_IMAGEHEIGHT:
    1831           6 :                     nImageHeight = sEntry.tdir_offset;
    1832           6 :                     break;
    1833           6 :                 case JPEG_TIFF_COMPRESSION:
    1834           6 :                     nCompression = sEntry.tdir_offset;
    1835           6 :                     break;
    1836           7 :                 case JPEG_EXIF_JPEGIFOFSET:
    1837           7 :                     nJpegIFOffset = sEntry.tdir_offset;
    1838           7 :                     break;
    1839           7 :                 case JPEG_EXIF_JPEGIFBYTECOUNT:
    1840           7 :                     nJpegIFByteCount = sEntry.tdir_offset;
    1841           7 :                     break;
    1842           0 :                 default:
    1843           0 :                     break;
    1844             :             }
    1845             :         }
    1846             :     }
    1847           7 :     if (nCompression != 6 || nImageWidth >= nRasterXSize ||
    1848           7 :         nImageHeight >= nRasterYSize || nJpegIFOffset == 0 ||
    1849           7 :         nJpegIFOffset > UINT_MAX - nTIFFHEADER ||
    1850           7 :         static_cast<int>(nJpegIFByteCount) <= 0)
    1851             :     {
    1852           0 :         return nullptr;
    1853             :     }
    1854             : 
    1855             :     const char *pszSubfile =
    1856           7 :         CPLSPrintf("JPEG_SUBFILE:%u,%d,%s", nTIFFHEADER + nJpegIFOffset,
    1857           7 :                    nJpegIFByteCount, GetDescription());
    1858           7 :     JPGDatasetOpenArgs sArgs;
    1859           7 :     sArgs.pszFilename = pszSubfile;
    1860           7 :     return JPGDataset::Open(&sArgs);
    1861             : }
    1862             : 
    1863             : /************************************************************************/
    1864             : /*                       InitInternalOverviews()                        */
    1865             : /************************************************************************/
    1866             : 
    1867       21572 : void JPGDatasetCommon::InitInternalOverviews()
    1868             : {
    1869       21572 :     if (bHasInitInternalOverviews)
    1870       19090 :         return;
    1871        2482 :     bHasInitInternalOverviews = true;
    1872             : 
    1873             :     // Instantiate on-the-fly overviews (if no external ones).
    1874        2482 :     if (nScaleFactor == 1 && GetRasterBand(1)->GetOverviewCount() == 0)
    1875             :     {
    1876             :         // EXIF overview.
    1877        2481 :         GDALDataset *poEXIFOverview = nullptr;
    1878        2481 :         if (nRasterXSize > 512 || nRasterYSize > 512)
    1879             :         {
    1880        2046 :             const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
    1881        2046 :             poEXIFOverview = InitEXIFOverview();
    1882        2046 :             if (poEXIFOverview != nullptr)
    1883             :             {
    1884           7 :                 if (poEXIFOverview->GetRasterCount() != nBands ||
    1885          14 :                     poEXIFOverview->GetRasterXSize() >= nRasterXSize ||
    1886           7 :                     poEXIFOverview->GetRasterYSize() >= nRasterYSize)
    1887             :                 {
    1888           0 :                     GDALClose(poEXIFOverview);
    1889           0 :                     poEXIFOverview = nullptr;
    1890             :                 }
    1891             :                 else
    1892             :                 {
    1893           7 :                     CPLDebug("JPEG", "EXIF overview (%d x %d) detected",
    1894             :                              poEXIFOverview->GetRasterXSize(),
    1895             :                              poEXIFOverview->GetRasterYSize());
    1896             :                 }
    1897             :             }
    1898        2046 :             VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
    1899             :         }
    1900             : 
    1901             :         // libjpeg-6b only supports 2, 4 and 8 scale denominators.
    1902             :         // TODO: Later versions support more.
    1903             : 
    1904        2481 :         int nImplicitOverviews = 0;
    1905             : 
    1906             :         // For the needs of the implicit JPEG-in-TIFF overview mechanism.
    1907        2481 :         if (CPLTestBool(
    1908             :                 CPLGetConfigOption("JPEG_FORCE_INTERNAL_OVERVIEWS", "NO")))
    1909             :         {
    1910        2459 :             nImplicitOverviews = 3;
    1911             :         }
    1912             :         else
    1913             :         {
    1914          55 :             for (int i = 2; i >= 0; i--)
    1915             :             {
    1916          46 :                 if (nRasterXSize >= (256 << i) || nRasterYSize >= (256 << i))
    1917             :                 {
    1918          13 :                     nImplicitOverviews = i + 1;
    1919          13 :                     break;
    1920             :                 }
    1921             :             }
    1922             :         }
    1923             : 
    1924        2481 :         if (nImplicitOverviews > 0)
    1925             :         {
    1926        2472 :             ppoActiveDS = &poActiveDS;
    1927        2472 :             papoInternalOverviews = static_cast<GDALDataset **>(
    1928        2472 :                 CPLMalloc((nImplicitOverviews + (poEXIFOverview ? 1 : 0)) *
    1929             :                           sizeof(GDALDataset *)));
    1930        9881 :             for (int i = 0; i < nImplicitOverviews; i++)
    1931             :             {
    1932        7431 :                 if (poEXIFOverview != nullptr &&
    1933          21 :                     poEXIFOverview->GetRasterXSize() >= nRasterXSize >> (i + 1))
    1934             :                 {
    1935           1 :                     break;
    1936             :                 }
    1937        7409 :                 JPGDatasetOpenArgs sArgs;
    1938        7409 :                 sArgs.pszFilename = GetDescription();
    1939        7409 :                 sArgs.nScaleFactor = 1 << (i + 1);
    1940        7409 :                 JPGDatasetCommon *poImplicitOverview = JPGDataset::Open(&sArgs);
    1941        7409 :                 if (poImplicitOverview == nullptr)
    1942           0 :                     break;
    1943        7409 :                 poImplicitOverview->ppoActiveDS = &poActiveDS;
    1944        7409 :                 papoInternalOverviews[nInternalOverviewsCurrent] =
    1945             :                     poImplicitOverview;
    1946        7409 :                 nInternalOverviewsCurrent++;
    1947        7409 :                 nInternalOverviewsToFree++;
    1948             :             }
    1949        2472 :             if (poEXIFOverview != nullptr)
    1950             :             {
    1951           7 :                 papoInternalOverviews[nInternalOverviewsCurrent] =
    1952             :                     poEXIFOverview;
    1953           7 :                 nInternalOverviewsCurrent++;
    1954           7 :                 nInternalOverviewsToFree++;
    1955             :             }
    1956             :         }
    1957           9 :         else if (poEXIFOverview)
    1958             :         {
    1959           0 :             papoInternalOverviews =
    1960           0 :                 static_cast<GDALDataset **>(CPLMalloc(sizeof(GDALDataset *)));
    1961           0 :             papoInternalOverviews[0] = poEXIFOverview;
    1962           0 :             nInternalOverviewsCurrent++;
    1963           0 :             nInternalOverviewsToFree++;
    1964             :         }
    1965             :     }
    1966             : }
    1967             : 
    1968             : /************************************************************************/
    1969             : /*                          IBuildOverviews()                           */
    1970             : /************************************************************************/
    1971             : 
    1972           1 : CPLErr JPGDatasetCommon::IBuildOverviews(const char *pszResampling,
    1973             :                                          int nOverviewsListCount,
    1974             :                                          const int *panOverviewList,
    1975             :                                          int nListBands, const int *panBandList,
    1976             :                                          GDALProgressFunc pfnProgress,
    1977             :                                          void *pProgressData,
    1978             :                                          CSLConstList papszOptions)
    1979             : {
    1980           1 :     bHasInitInternalOverviews = true;
    1981           1 :     nInternalOverviewsCurrent = 0;
    1982             : 
    1983           1 :     return GDALPamDataset::IBuildOverviews(
    1984             :         pszResampling, nOverviewsListCount, panOverviewList, nListBands,
    1985           1 :         panBandList, pfnProgress, pProgressData, papszOptions);
    1986             : }
    1987             : 
    1988             : /************************************************************************/
    1989             : /*                           FlushCache()                               */
    1990             : /************************************************************************/
    1991             : 
    1992           5 : CPLErr JPGDatasetCommon::FlushCache(bool bAtClosing)
    1993             : 
    1994             : {
    1995           5 :     CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
    1996             : 
    1997           5 :     if (bHasDoneJpegStartDecompress)
    1998             :     {
    1999           0 :         Restart();
    2000             :     }
    2001             : 
    2002             :     // For the needs of the implicit JPEG-in-TIFF overview mechanism.
    2003           5 :     for (int i = 0; i < nInternalOverviewsCurrent; i++)
    2004             :     {
    2005           0 :         if (papoInternalOverviews[i]->FlushCache(bAtClosing) != CE_None)
    2006           0 :             eErr = CE_Failure;
    2007             :     }
    2008           5 :     return eErr;
    2009             : }
    2010             : 
    2011             : #endif  // !defined(JPGDataset)
    2012             : 
    2013             : /************************************************************************/
    2014             : /*                            JPGDataset()                              */
    2015             : /************************************************************************/
    2016             : 
    2017       10732 : JPGDataset::JPGDataset() : nQLevel(0)
    2018             : {
    2019       10732 :     memset(&sDInfo, 0, sizeof(sDInfo));
    2020       10732 :     sDInfo.data_precision = 8;
    2021             : 
    2022       10732 :     memset(&sJErr, 0, sizeof(sJErr));
    2023       10732 :     memset(&sJProgress, 0, sizeof(sJProgress));
    2024       10732 : }
    2025             : 
    2026             : /************************************************************************/
    2027             : /*                           ~JPGDataset()                            */
    2028             : /************************************************************************/
    2029             : 
    2030       21464 : JPGDataset::~JPGDataset()
    2031             : 
    2032             : {
    2033       10732 :     GDALPamDataset::FlushCache(true);
    2034       10732 :     JPGDataset::StopDecompress();
    2035       21464 : }
    2036             : 
    2037             : /************************************************************************/
    2038             : /*                           StopDecompress()                           */
    2039             : /************************************************************************/
    2040             : 
    2041       11312 : void JPGDataset::StopDecompress()
    2042             : {
    2043       11312 :     if (bHasDoneJpegStartDecompress)
    2044             :     {
    2045        3178 :         jpeg_abort_decompress(&sDInfo);
    2046        3178 :         bHasDoneJpegStartDecompress = false;
    2047             :     }
    2048       11312 :     if (bHasDoneJpegCreateDecompress)
    2049             :     {
    2050       11241 :         jpeg_destroy_decompress(&sDInfo);
    2051       11241 :         bHasDoneJpegCreateDecompress = false;
    2052             :     }
    2053       11312 :     nLoadedScanline = INT_MAX;
    2054       11312 :     if (ppoActiveDS)
    2055       10182 :         *ppoActiveDS = nullptr;
    2056       11312 : }
    2057             : 
    2058             : /************************************************************************/
    2059             : /*                      ErrorOutOnNonFatalError()                       */
    2060             : /************************************************************************/
    2061             : 
    2062       73456 : bool JPGDataset::ErrorOutOnNonFatalError()
    2063             : {
    2064       73456 :     if (sUserData.bNonFatalErrorEncountered)
    2065             :     {
    2066           4 :         sUserData.bNonFatalErrorEncountered = false;
    2067           4 :         return true;
    2068             :     }
    2069       73452 :     return false;
    2070             : }
    2071             : 
    2072             : /************************************************************************/
    2073             : /*                          StartDecompress()                           */
    2074             : /************************************************************************/
    2075             : 
    2076        3180 : CPLErr JPGDataset::StartDecompress()
    2077             : {
    2078             :     /* In some cases, libjpeg needs to allocate a lot of memory */
    2079             :     /* http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf
    2080             :      */
    2081        3180 :     if (jpeg_has_multiple_scans(&(sDInfo)))
    2082             :     {
    2083             :         /* In this case libjpeg will need to allocate memory or backing */
    2084             :         /* store for all coefficients */
    2085             :         /* See call to jinit_d_coef_controller() from master_selection() */
    2086             :         /* in libjpeg */
    2087             : 
    2088             :         // 1 MB for regular libjpeg usage
    2089          10 :         vsi_l_offset nRequiredMemory = 1024 * 1024;
    2090             : 
    2091          22 :         for (int ci = 0; ci < sDInfo.num_components; ci++)
    2092             :         {
    2093          12 :             const jpeg_component_info *compptr = &(sDInfo.comp_info[ci]);
    2094          12 :             if (compptr->h_samp_factor <= 0 || compptr->v_samp_factor <= 0)
    2095             :             {
    2096           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2097             :                          "Invalid sampling factor(s)");
    2098           0 :                 return CE_Failure;
    2099             :             }
    2100          12 :             nRequiredMemory +=
    2101          12 :                 static_cast<vsi_l_offset>(DIV_ROUND_UP(
    2102          12 :                     compptr->width_in_blocks, compptr->h_samp_factor)) *
    2103          12 :                 DIV_ROUND_UP(compptr->height_in_blocks,
    2104          12 :                              compptr->v_samp_factor) *
    2105             :                 sizeof(JBLOCK);
    2106             :         }
    2107             : 
    2108          10 :         if (nRequiredMemory > 10 * 1024 * 1024 && ppoActiveDS &&
    2109           4 :             *ppoActiveDS != this)
    2110             :         {
    2111             :             // If another overview was active, stop it to limit memory
    2112             :             // consumption
    2113           4 :             if (*ppoActiveDS)
    2114           1 :                 (*ppoActiveDS)->StopDecompress();
    2115           4 :             *ppoActiveDS = this;
    2116             :         }
    2117             : 
    2118          30 :         if (sDInfo.mem->max_memory_to_use > 0 &&
    2119             :             nRequiredMemory >
    2120          11 :                 static_cast<vsi_l_offset>(sDInfo.mem->max_memory_to_use) &&
    2121           1 :             CPLGetConfigOption("GDAL_ALLOW_LARGE_LIBJPEG_MEM_ALLOC", nullptr) ==
    2122             :                 nullptr)
    2123             :         {
    2124           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    2125             :                      "Reading this image would require libjpeg to allocate "
    2126             :                      "at least " CPL_FRMT_GUIB " bytes. "
    2127             :                      "This is disabled since above the " CPL_FRMT_GUIB
    2128             :                      " threshold. "
    2129             :                      "You may override this restriction by defining the "
    2130             :                      "GDAL_ALLOW_LARGE_LIBJPEG_MEM_ALLOC environment variable, "
    2131             :                      "or setting the JPEGMEM environment variable to a value "
    2132             :                      "greater "
    2133             :                      "or equal to '" CPL_FRMT_GUIB "M'",
    2134             :                      static_cast<GUIntBig>(nRequiredMemory),
    2135           1 :                      static_cast<GUIntBig>(sDInfo.mem->max_memory_to_use),
    2136           1 :                      static_cast<GUIntBig>((nRequiredMemory + 1000000 - 1) /
    2137             :                                            1000000));
    2138           1 :             return CE_Failure;
    2139             :         }
    2140             :     }
    2141             : 
    2142        3179 :     sDInfo.progress = &sJProgress;
    2143        3179 :     sJProgress.progress_monitor = JPGDataset::ProgressMonitor;
    2144        3179 :     jpeg_start_decompress(&sDInfo);
    2145        3178 :     bHasDoneJpegStartDecompress = true;
    2146             : 
    2147        3178 :     return CE_None;
    2148             : }
    2149             : 
    2150             : /************************************************************************/
    2151             : /*                            LoadScanline()                            */
    2152             : /************************************************************************/
    2153             : 
    2154      112698 : CPLErr JPGDataset::LoadScanline(int iLine, GByte *outBuffer)
    2155             : 
    2156             : {
    2157      112698 :     if (nLoadedScanline == iLine)
    2158       41360 :         return CE_None;
    2159             : 
    2160             :     // code path triggered when an active reader has been stopped by another
    2161             :     // one, in case of multiple scans datasets and overviews
    2162       71338 :     if (!bHasDoneJpegCreateDecompress && Restart() != CE_None)
    2163           0 :         return CE_Failure;
    2164             : 
    2165             :     // setup to trap a fatal error.
    2166       71338 :     if (setjmp(sUserData.setjmp_buffer))
    2167           1 :         return CE_Failure;
    2168             : 
    2169       71338 :     if (!bHasDoneJpegStartDecompress && StartDecompress() != CE_None)
    2170           1 :         return CE_Failure;
    2171             : 
    2172       71336 :     if (outBuffer == nullptr && m_pabyScanline == nullptr)
    2173             :     {
    2174        2874 :         int nJPEGBands = 0;
    2175        2874 :         switch (sDInfo.out_color_space)
    2176             :         {
    2177        1012 :             case JCS_GRAYSCALE:
    2178        1012 :                 nJPEGBands = 1;
    2179        1012 :                 break;
    2180        1795 :             case JCS_RGB:
    2181             :             case JCS_YCbCr:
    2182        1795 :                 nJPEGBands = 3;
    2183        1795 :                 break;
    2184          67 :             case JCS_CMYK:
    2185             :             case JCS_YCCK:
    2186          67 :                 nJPEGBands = 4;
    2187          67 :                 break;
    2188             : 
    2189           0 :             default:
    2190           0 :                 CPLAssert(false);
    2191             :         }
    2192             : 
    2193        2874 :         m_pabyScanline = static_cast<GByte *>(
    2194        2874 :             CPLMalloc(cpl::fits_on<int>(nJPEGBands * GetRasterXSize() * 2)));
    2195             :     }
    2196             : 
    2197       71336 :     if (iLine < nLoadedScanline)
    2198             :     {
    2199         296 :         if (Restart() != CE_None)
    2200           0 :             return CE_Failure;
    2201             :     }
    2202             : 
    2203      144788 :     while (nLoadedScanline < iLine)
    2204             :     {
    2205       73456 :         GDAL_JSAMPLE *ppSamples = reinterpret_cast<GDAL_JSAMPLE *>(
    2206       73114 :             outBuffer ? outBuffer : m_pabyScanline);
    2207             : #if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
    2208             :         jpeg12_read_scanlines(&sDInfo, &ppSamples, 1);
    2209             : #else
    2210       73456 :         jpeg_read_scanlines(&sDInfo, &ppSamples, 1);
    2211             : #endif
    2212       73456 :         if (ErrorOutOnNonFatalError())
    2213           4 :             return CE_Failure;
    2214       73452 :         nLoadedScanline++;
    2215             :     }
    2216             : 
    2217       71332 :     return CE_None;
    2218             : }
    2219             : 
    2220             : /************************************************************************/
    2221             : /*                         LoadDefaultTables()                          */
    2222             : /************************************************************************/
    2223             : 
    2224             : #if !defined(JPGDataset)
    2225             : 
    2226             : #define Q1table GDALJPEG_Q1table
    2227             : #define Q2table GDALJPEG_Q2table
    2228             : #define Q3table GDALJPEG_Q3table
    2229             : #define Q4table GDALJPEG_Q4table
    2230             : #define Q5table GDALJPEG_Q5table
    2231             : #define AC_BITS GDALJPEG_AC_BITS
    2232             : #define AC_HUFFVAL GDALJPEG_AC_HUFFVAL
    2233             : #define DC_BITS GDALJPEG_DC_BITS
    2234             : #define DC_HUFFVAL GDALJPEG_DC_HUFFVAL
    2235             : 
    2236             : constexpr GByte Q1table[64] = {
    2237             :     8,   72,  72,  72,  72,  72,  72,  72,   // 0 - 7
    2238             :     72,  72,  78,  74,  76,  74,  78,  89,   // 8 - 15
    2239             :     81,  84,  84,  81,  89,  106, 93,  94,   // 16 - 23
    2240             :     99,  94,  93,  106, 129, 111, 108, 116,  // 24 - 31
    2241             :     116, 108, 111, 129, 135, 128, 136, 145,  // 32 - 39
    2242             :     136, 128, 135, 155, 160, 177, 177, 160,  // 40 - 47
    2243             :     155, 193, 213, 228, 213, 193, 255, 255,  // 48 - 55
    2244             :     255, 255, 255, 255, 255, 255, 255, 255   // 56 - 63
    2245             : };
    2246             : 
    2247             : constexpr GByte Q2table[64] = {
    2248             :     8,   36, 36,  36,  36,  36,  36,  36,  36,  36,  39,  37,  38,
    2249             :     37,  39, 45,  41,  42,  42,  41,  45,  53,  47,  47,  50,  47,
    2250             :     47,  53, 65,  56,  54,  59,  59,  54,  56,  65,  68,  64,  69,
    2251             :     73,  69, 64,  68,  78,  81,  89,  89,  81,  78,  98,  108, 115,
    2252             :     108, 98, 130, 144, 144, 130, 178, 190, 178, 243, 243, 255};
    2253             : 
    2254             : constexpr GByte Q3table[64] = {
    2255             :     8,  10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 10, 11, 10, 11, 13,
    2256             :     11, 12, 12, 11, 13, 15, 13, 13, 14, 13, 13, 15, 18, 16, 15, 16,
    2257             :     16, 15, 16, 18, 19, 18, 19, 21, 19, 18, 19, 22, 23, 25, 25, 23,
    2258             :     22, 27, 30, 32, 30, 27, 36, 40, 40, 36, 50, 53, 50, 68, 68, 91};
    2259             : 
    2260             : constexpr GByte Q4table[64] = {
    2261             :     8,  7,  7,  7,  7,  7,  7,  7,  7,  7,  8,  7,  8,  7,  8,  9,
    2262             :     8,  8,  8,  8,  9,  11, 9,  9,  10, 9,  9,  11, 13, 11, 11, 12,
    2263             :     12, 11, 11, 13, 14, 13, 14, 15, 14, 13, 14, 16, 16, 18, 18, 16,
    2264             :     16, 20, 22, 23, 22, 20, 26, 29, 29, 26, 36, 38, 36, 49, 49, 65};
    2265             : 
    2266             : constexpr GByte Q5table[64] = {
    2267             :     4, 4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  5,
    2268             :     5, 5,  5,  5,  5,  6,  5,  5,  6,  5,  5,  6,  7,  6,  6,  6,
    2269             :     6, 6,  6,  7,  8,  7,  8,  8,  8,  7,  8,  9,  9,  10, 10, 9,
    2270             :     9, 11, 12, 13, 12, 11, 14, 16, 16, 14, 20, 21, 20, 27, 27, 36};
    2271             : 
    2272             : constexpr GByte AC_BITS[16] = {0, 2, 1, 3, 3, 2, 4, 3,
    2273             :                                5, 5, 4, 4, 0, 0, 1, 125};
    2274             : 
    2275             : constexpr GByte AC_HUFFVAL[256] = {
    2276             :     0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
    2277             :     0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08,
    2278             :     0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72,
    2279             :     0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,
    2280             :     0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45,
    2281             :     0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
    2282             :     0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75,
    2283             :     0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
    2284             :     0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3,
    2285             :     0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6,
    2286             :     0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
    2287             :     0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
    2288             :     0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4,
    2289             :     0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    2290             :     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    2291             :     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    2292             :     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    2293             :     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    2294             :     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    2295             :     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    2296             : 
    2297             : constexpr GByte DC_BITS[16] = {0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0};
    2298             : 
    2299             : constexpr GByte DC_HUFFVAL[256] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
    2300             :                                    0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B};
    2301             : 
    2302       44904 : void JPGDataset::LoadDefaultTables(int n)
    2303             : {
    2304       44904 :     if (nQLevel < 1)
    2305       44904 :         return;
    2306             : 
    2307             :     // Load quantization table.
    2308           0 :     JQUANT_TBL *quant_ptr = nullptr;
    2309           0 :     const GByte *pabyQTable = nullptr;
    2310             : 
    2311           0 :     if (nQLevel == 1)
    2312           0 :         pabyQTable = Q1table;
    2313           0 :     else if (nQLevel == 2)
    2314           0 :         pabyQTable = Q2table;
    2315           0 :     else if (nQLevel == 3)
    2316           0 :         pabyQTable = Q3table;
    2317           0 :     else if (nQLevel == 4)
    2318           0 :         pabyQTable = Q4table;
    2319           0 :     else if (nQLevel == 5)
    2320           0 :         pabyQTable = Q5table;
    2321             :     else
    2322           0 :         return;
    2323             : 
    2324           0 :     if (sDInfo.quant_tbl_ptrs[n] == nullptr)
    2325           0 :         sDInfo.quant_tbl_ptrs[n] =
    2326           0 :             jpeg_alloc_quant_table(reinterpret_cast<j_common_ptr>(&sDInfo));
    2327             : 
    2328           0 :     quant_ptr = sDInfo.quant_tbl_ptrs[n];  // quant_ptr is JQUANT_TBL.
    2329           0 :     for (int i = 0; i < 64; i++)
    2330             :     {
    2331             :         // Qtable[] is desired quantization table, in natural array order.
    2332           0 :         quant_ptr->quantval[i] = pabyQTable[i];
    2333             :     }
    2334             : 
    2335             :     // Load AC huffman table.
    2336           0 :     if (sDInfo.ac_huff_tbl_ptrs[n] == nullptr)
    2337           0 :         sDInfo.ac_huff_tbl_ptrs[n] =
    2338           0 :             jpeg_alloc_huff_table(reinterpret_cast<j_common_ptr>(&sDInfo));
    2339             : 
    2340             :     // huff_ptr is JHUFF_TBL*.
    2341           0 :     JHUFF_TBL *huff_ptr = sDInfo.ac_huff_tbl_ptrs[n];
    2342             : 
    2343           0 :     for (int i = 1; i <= 16; i++)
    2344             :     {
    2345             :         // counts[i] is number of Huffman codes of length i bits, i=1..16
    2346           0 :         huff_ptr->bits[i] = AC_BITS[i - 1];
    2347             :     }
    2348             : 
    2349           0 :     for (int i = 0; i < 256; i++)
    2350             :     {
    2351             :         // symbols[] is the list of Huffman symbols, in code-length order.
    2352           0 :         huff_ptr->huffval[i] = AC_HUFFVAL[i];
    2353             :     }
    2354             : 
    2355             :     // Load DC huffman table.
    2356             :     // TODO(schwehr): Revisit this "sideways" cast.
    2357           0 :     if (sDInfo.dc_huff_tbl_ptrs[n] == nullptr)
    2358           0 :         sDInfo.dc_huff_tbl_ptrs[n] =
    2359           0 :             jpeg_alloc_huff_table(reinterpret_cast<j_common_ptr>(&sDInfo));
    2360             : 
    2361           0 :     huff_ptr = sDInfo.dc_huff_tbl_ptrs[n];  // huff_ptr is JHUFF_TBL*
    2362             : 
    2363           0 :     for (int i = 1; i <= 16; i++)
    2364             :     {
    2365             :         // counts[i] is number of Huffman codes of length i bits, i=1..16
    2366           0 :         huff_ptr->bits[i] = DC_BITS[i - 1];
    2367             :     }
    2368             : 
    2369           0 :     for (int i = 0; i < 256; i++)
    2370             :     {
    2371             :         // symbols[] is the list of Huffman symbols, in code-length order.
    2372           0 :         huff_ptr->huffval[i] = DC_HUFFVAL[i];
    2373             :     }
    2374             : }
    2375             : #endif  // !defined(JPGDataset)
    2376             : 
    2377             : /************************************************************************/
    2378             : /*                       SetScaleNumAndDenom()                          */
    2379             : /************************************************************************/
    2380             : 
    2381       11226 : void JPGDataset::SetScaleNumAndDenom()
    2382             : {
    2383             : #if JPEG_LIB_VERSION > 62
    2384       11211 :     sDInfo.scale_num = 8 / nScaleFactor;
    2385       11211 :     sDInfo.scale_denom = 8;
    2386             : #else
    2387          15 :     sDInfo.scale_num = 1;
    2388          15 :     sDInfo.scale_denom = nScaleFactor;
    2389             : #endif
    2390       11226 : }
    2391             : 
    2392             : /************************************************************************/
    2393             : /*                              Restart()                               */
    2394             : /*                                                                      */
    2395             : /*      Restart compressor at the beginning of the file.                */
    2396             : /************************************************************************/
    2397             : 
    2398         576 : CPLErr JPGDataset::Restart()
    2399             : 
    2400             : {
    2401         576 :     if (ppoActiveDS && *ppoActiveDS != this && *ppoActiveDS != nullptr)
    2402             :     {
    2403           3 :         (*ppoActiveDS)->StopDecompress();
    2404             :     }
    2405             : 
    2406             :     // Setup to trap a fatal error.
    2407         576 :     if (setjmp(sUserData.setjmp_buffer))
    2408           0 :         return CE_Failure;
    2409             : 
    2410         576 :     J_COLOR_SPACE colorSpace = sDInfo.out_color_space;
    2411         576 :     J_COLOR_SPACE jpegColorSpace = sDInfo.jpeg_color_space;
    2412             : 
    2413         576 :     StopDecompress();
    2414         576 :     jpeg_create_decompress(&sDInfo);
    2415         576 :     bHasDoneJpegCreateDecompress = true;
    2416             : 
    2417         576 :     SetMaxMemoryToUse(&sDInfo);
    2418             : 
    2419             : #if !defined(JPGDataset)
    2420         576 :     LoadDefaultTables(0);
    2421         576 :     LoadDefaultTables(1);
    2422         576 :     LoadDefaultTables(2);
    2423         576 :     LoadDefaultTables(3);
    2424             : #endif  // !defined(JPGDataset)
    2425             : 
    2426             :     // Restart IO.
    2427         576 :     VSIFSeekL(m_fpImage, nSubfileOffset, SEEK_SET);
    2428             : 
    2429         576 :     jpeg_vsiio_src(&sDInfo, m_fpImage);
    2430         576 :     jpeg_read_header(&sDInfo, TRUE);
    2431             : 
    2432         576 :     sDInfo.out_color_space = colorSpace;
    2433         576 :     nLoadedScanline = -1;
    2434         576 :     SetScaleNumAndDenom();
    2435             : 
    2436             :     // The following errors could happen when "recycling" an existing dataset
    2437             :     // particularly when triggered by the implicit overviews of JPEG-in-TIFF
    2438             :     // with a corrupted TIFF file.
    2439         576 :     if (nRasterXSize !=
    2440         576 :             static_cast<int>(sDInfo.image_width + nScaleFactor - 1) /
    2441         576 :                 nScaleFactor ||
    2442         576 :         nRasterYSize !=
    2443         576 :             static_cast<int>(sDInfo.image_height + nScaleFactor - 1) /
    2444         576 :                 nScaleFactor)
    2445             :     {
    2446           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2447             :                  "Unexpected image dimension (%d x %d), "
    2448             :                  "where as (%d x %d) was expected",
    2449           0 :                  static_cast<int>(sDInfo.image_width + nScaleFactor - 1) /
    2450           0 :                      nScaleFactor,
    2451           0 :                  static_cast<int>(sDInfo.image_height + nScaleFactor - 1) /
    2452           0 :                      nScaleFactor,
    2453             :                  nRasterXSize, nRasterYSize);
    2454           0 :         bHasDoneJpegStartDecompress = false;
    2455             :     }
    2456         576 :     else if (jpegColorSpace != sDInfo.jpeg_color_space)
    2457             :     {
    2458           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2459           0 :                  "Unexpected jpeg color space : %d", sDInfo.jpeg_color_space);
    2460           0 :         bHasDoneJpegStartDecompress = false;
    2461             :     }
    2462             :     else
    2463             :     {
    2464         576 :         if (StartDecompress() != CE_None)
    2465           0 :             return CE_Failure;
    2466         576 :         if (ppoActiveDS)
    2467         297 :             *ppoActiveDS = this;
    2468             :     }
    2469             : 
    2470         576 :     return CE_None;
    2471             : }
    2472             : 
    2473             : #if !defined(JPGDataset)
    2474             : 
    2475             : /************************************************************************/
    2476             : /*                          GetGeoTransform()                           */
    2477             : /************************************************************************/
    2478             : 
    2479         145 : CPLErr JPGDatasetCommon::GetGeoTransform(double *padfTransform)
    2480             : 
    2481             : {
    2482         145 :     CPLErr eErr = GDALPamDataset::GetGeoTransform(padfTransform);
    2483         145 :     if (eErr != CE_Failure)
    2484           3 :         return eErr;
    2485             : 
    2486         142 :     LoadWorldFileOrTab();
    2487             : 
    2488         142 :     if (bGeoTransformValid)
    2489             :     {
    2490           3 :         memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
    2491             : 
    2492           3 :         return CE_None;
    2493             :     }
    2494             : 
    2495         139 :     return eErr;
    2496             : }
    2497             : 
    2498             : /************************************************************************/
    2499             : /*                            GetGCPCount()                             */
    2500             : /************************************************************************/
    2501             : 
    2502          78 : int JPGDatasetCommon::GetGCPCount()
    2503             : 
    2504             : {
    2505          78 :     const int nPAMGCPCount = GDALPamDataset::GetGCPCount();
    2506          78 :     if (nPAMGCPCount != 0)
    2507           6 :         return nPAMGCPCount;
    2508             : 
    2509          72 :     LoadWorldFileOrTab();
    2510             : 
    2511          72 :     return static_cast<int>(m_aoGCPs.size());
    2512             : }
    2513             : 
    2514             : /************************************************************************/
    2515             : /*                          GetGCPSpatialRef()                          */
    2516             : /************************************************************************/
    2517             : 
    2518           3 : const OGRSpatialReference *JPGDatasetCommon::GetGCPSpatialRef() const
    2519             : 
    2520             : {
    2521             :     const int nPAMGCPCount =
    2522           3 :         const_cast<JPGDatasetCommon *>(this)->GDALPamDataset::GetGCPCount();
    2523           3 :     if (nPAMGCPCount != 0)
    2524           3 :         return GDALPamDataset::GetGCPSpatialRef();
    2525             : 
    2526           0 :     const_cast<JPGDatasetCommon *>(this)->LoadWorldFileOrTab();
    2527             : 
    2528           0 :     if (!m_oSRS.IsEmpty() && !m_aoGCPs.empty())
    2529           0 :         return &m_oSRS;
    2530             : 
    2531           0 :     return nullptr;
    2532             : }
    2533             : 
    2534             : /************************************************************************/
    2535             : /*                               GetGCPs()                              */
    2536             : /************************************************************************/
    2537             : 
    2538           3 : const GDAL_GCP *JPGDatasetCommon::GetGCPs()
    2539             : 
    2540             : {
    2541           3 :     const int nPAMGCPCount = GDALPamDataset::GetGCPCount();
    2542           3 :     if (nPAMGCPCount != 0)
    2543           3 :         return GDALPamDataset::GetGCPs();
    2544             : 
    2545           0 :     LoadWorldFileOrTab();
    2546             : 
    2547           0 :     return gdal::GCP::c_ptr(m_aoGCPs);
    2548             : }
    2549             : 
    2550             : /************************************************************************/
    2551             : /*                             IRasterIO()                              */
    2552             : /*                                                                      */
    2553             : /*      Checks for what might be the most common read case              */
    2554             : /*      (reading an entire 8bit, RGB JPEG), and                         */
    2555             : /*      optimizes for that case                                         */
    2556             : /************************************************************************/
    2557             : 
    2558         408 : CPLErr JPGDatasetCommon::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    2559             :                                    int nXSize, int nYSize, void *pData,
    2560             :                                    int nBufXSize, int nBufYSize,
    2561             :                                    GDALDataType eBufType, int nBandCount,
    2562             :                                    int *panBandMap, GSpacing nPixelSpace,
    2563             :                                    GSpacing nLineSpace, GSpacing nBandSpace,
    2564             :                                    GDALRasterIOExtraArg *psExtraArg)
    2565             : 
    2566             : {
    2567             :     // Coverity says that we cannot pass a nullptr to IRasterIO.
    2568         408 :     if (panBandMap == nullptr)
    2569             :     {
    2570           0 :         return CE_Failure;
    2571             :     }
    2572             : 
    2573             : #ifndef JPEG_LIB_MK1
    2574         408 :     if ((eRWFlag == GF_Read) && (nBandCount == 3) && (nBands == 3) &&
    2575         315 :         (nXOff == 0) && (nYOff == 0) && (nXSize == nBufXSize) &&
    2576         313 :         (nXSize == nRasterXSize) && (nYSize == nBufYSize) &&
    2577         304 :         (nYSize == nRasterYSize) && (eBufType == GDT_Byte) &&
    2578         302 :         (GetDataPrecision() != 12) && (pData != nullptr) &&
    2579         302 :         (panBandMap[0] == 1) && (panBandMap[1] == 2) && (panBandMap[2] == 3) &&
    2580             :         // These color spaces need to be transformed to RGB.
    2581         816 :         GetOutColorSpace() != JCS_YCCK && GetOutColorSpace() != JCS_CMYK)
    2582             :     {
    2583         278 :         Restart();
    2584             : 
    2585             :         // Pixel interleaved case.
    2586         278 :         if (nBandSpace == 1)
    2587             :         {
    2588        1920 :             for (int y = 0; y < nYSize; ++y)
    2589             :             {
    2590        1915 :                 if (nPixelSpace == 3)
    2591             :                 {
    2592             :                     CPLErr tmpError =
    2593        1655 :                         LoadScanline(y, &(((GByte *)pData)[(y * nLineSpace)]));
    2594        1655 :                     if (tmpError != CE_None)
    2595           1 :                         return tmpError;
    2596             :                 }
    2597             :                 else
    2598             :                 {
    2599         260 :                     CPLErr tmpError = LoadScanline(y);
    2600         260 :                     if (tmpError != CE_None)
    2601           0 :                         return tmpError;
    2602             : 
    2603       94120 :                     for (int x = 0; x < nXSize; ++x)
    2604             :                     {
    2605       93860 :                         memcpy(&(((GByte *)pData)[(y * nLineSpace) +
    2606       93860 :                                                   (x * nPixelSpace)]),
    2607       93860 :                                (const GByte *)&(m_pabyScanline[x * 3]), 3);
    2608             :                     }
    2609             :                 }
    2610             :             }
    2611           5 :             nLoadedScanline = nRasterYSize;
    2612             :         }
    2613             :         else
    2614             :         {
    2615       10152 :             for (int y = 0; y < nYSize; ++y)
    2616             :             {
    2617        9880 :                 CPLErr tmpError = LoadScanline(y);
    2618        9880 :                 if (tmpError != CE_None)
    2619           0 :                     return tmpError;
    2620     1795390 :                 for (int x = 0; x < nXSize; ++x)
    2621             :                 {
    2622             :                     static_cast<GByte *>(
    2623     1785510 :                         pData)[(y * nLineSpace) + (x * nPixelSpace)] =
    2624     1785510 :                         m_pabyScanline[x * 3];
    2625     1785510 :                     ((GByte *)pData)[(y * nLineSpace) + (x * nPixelSpace) +
    2626     1785510 :                                      nBandSpace] = m_pabyScanline[x * 3 + 1];
    2627     1785510 :                     ((GByte *)pData)[(y * nLineSpace) + (x * nPixelSpace) +
    2628     1785510 :                                      2 * nBandSpace] =
    2629     1785510 :                         m_pabyScanline[x * 3 + 2];
    2630             :                 }
    2631             :             }
    2632             :         }
    2633             : 
    2634         277 :         return CE_None;
    2635             :     }
    2636             : #endif
    2637             : 
    2638         130 :     return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2639             :                                      pData, nBufXSize, nBufYSize, eBufType,
    2640             :                                      nBandCount, panBandMap, nPixelSpace,
    2641         130 :                                      nLineSpace, nBandSpace, psExtraArg);
    2642             : }
    2643             : 
    2644             : /************************************************************************/
    2645             : /*                                Open()                                */
    2646             : /************************************************************************/
    2647             : 
    2648        3057 : GDALDataset *JPGDatasetCommon::Open(GDALOpenInfo *poOpenInfo)
    2649             : 
    2650             : {
    2651             : #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    2652             :     // During fuzzing, do not use Identify to reject crazy content.
    2653        3057 :     if (!JPEGDriverIdentify(poOpenInfo))
    2654           0 :         return nullptr;
    2655             : #endif
    2656             : 
    2657        3057 :     if (poOpenInfo->eAccess == GA_Update)
    2658             :     {
    2659           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2660             :                  "The JPEG driver does not support update access to existing"
    2661             :                  " datasets.");
    2662           0 :         return nullptr;
    2663             :     }
    2664             : 
    2665        6114 :     CPLString osFilename(poOpenInfo->pszFilename);
    2666        3057 :     bool bFLIRRawThermalImage = false;
    2667        3057 :     if (STARTS_WITH(poOpenInfo->pszFilename, "JPEG:"))
    2668             :     {
    2669           5 :         CPLStringList aosTokens(CSLTokenizeString2(poOpenInfo->pszFilename, ":",
    2670           5 :                                                    CSLT_HONOURSTRINGS));
    2671           5 :         if (aosTokens.size() != 3)
    2672           1 :             return nullptr;
    2673             : 
    2674           4 :         osFilename = aosTokens[1];
    2675           4 :         if (std::string(aosTokens[2]) != "FLIR_RAW_THERMAL_IMAGE")
    2676           1 :             return nullptr;
    2677           3 :         bFLIRRawThermalImage = true;
    2678             :     }
    2679             : 
    2680        3055 :     VSILFILE *fpL = poOpenInfo->fpL;
    2681        3055 :     poOpenInfo->fpL = nullptr;
    2682             : 
    2683        3055 :     JPGDatasetOpenArgs sArgs;
    2684        3055 :     sArgs.pszFilename = osFilename.c_str();
    2685        3055 :     sArgs.fpLin = fpL;
    2686        3055 :     sArgs.papszSiblingFiles = poOpenInfo->GetSiblingFiles();
    2687        3055 :     sArgs.bDoPAMInitialize = true;
    2688        3055 :     sArgs.bUseInternalOverviews = CPLFetchBool(poOpenInfo->papszOpenOptions,
    2689             :                                                "USE_INTERNAL_OVERVIEWS", true);
    2690             : #ifdef D_LOSSLESS_SUPPORTED
    2691             :     sArgs.bIsLossless = JPEGDatasetIsJPEGLS(poOpenInfo);
    2692             : #endif
    2693             : 
    2694        3055 :     auto poJPG_DS = JPGDataset::Open(&sArgs);
    2695        6110 :     auto poDS = std::unique_ptr<GDALDataset>(poJPG_DS);
    2696        3055 :     if (poDS == nullptr)
    2697             :     {
    2698           1 :         return nullptr;
    2699             :     }
    2700        3054 :     if (bFLIRRawThermalImage)
    2701             :     {
    2702           3 :         poDS.reset(poJPG_DS->OpenFLIRRawThermalImage());
    2703             :     }
    2704             : 
    2705        6107 :     if (poDS &&
    2706        6107 :         CPLFetchBool(poOpenInfo->papszOpenOptions, "APPLY_ORIENTATION", false))
    2707             :     {
    2708           8 :         const char *pszOrientation = poDS->GetMetadataItem("EXIF_Orientation");
    2709           8 :         if (pszOrientation && !EQUAL(pszOrientation, "1"))
    2710             :         {
    2711           7 :             int nOrientation = atoi(pszOrientation);
    2712           7 :             if (nOrientation >= 2 && nOrientation <= 8)
    2713             :             {
    2714             :                 auto poOrientedDS = std::make_unique<GDALOrientedDataset>(
    2715           7 :                     std::move(poDS),
    2716          14 :                     static_cast<GDALOrientedDataset::Origin>(nOrientation));
    2717           7 :                 poDS = std::move(poOrientedDS);
    2718             :             }
    2719             :         }
    2720             :     }
    2721             : 
    2722        3054 :     return poDS.release();
    2723             : }
    2724             : 
    2725             : /************************************************************************/
    2726             : /*                       OpenFLIRRawThermalImage()                      */
    2727             : /************************************************************************/
    2728             : 
    2729           3 : GDALDataset *JPGDatasetCommon::OpenFLIRRawThermalImage()
    2730             : {
    2731           3 :     ReadFLIRMetadata();
    2732           3 :     if (m_abyRawThermalImage.empty())
    2733             :     {
    2734           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2735             :                  "Cannot find FLIR raw thermal image");
    2736           1 :         return nullptr;
    2737             :     }
    2738             : 
    2739             :     GByte *pabyData =
    2740           2 :         static_cast<GByte *>(CPLMalloc(m_abyRawThermalImage.size()));
    2741           4 :     const std::string osTmpFilename(CPLSPrintf("/vsimem/jpeg/%p", pabyData));
    2742           2 :     memcpy(pabyData, m_abyRawThermalImage.data(), m_abyRawThermalImage.size());
    2743           2 :     VSILFILE *fpRaw = VSIFileFromMemBuffer(osTmpFilename.c_str(), pabyData,
    2744           2 :                                            m_abyRawThermalImage.size(), true);
    2745             : 
    2746             :     // Termal image as uncompressed data
    2747           2 :     if (m_nRawThermalImageWidth * m_nRawThermalImageHeight * 2 ==
    2748           2 :         static_cast<int>(m_abyRawThermalImage.size()))
    2749             :     {
    2750           1 :         CPLDebug("JPEG", "Raw thermal image");
    2751             : 
    2752             :         class JPEGRawDataset : public RawDataset
    2753             :         {
    2754             :           public:
    2755           1 :             JPEGRawDataset(int nXSizeIn, int nYSizeIn)
    2756           1 :             {
    2757           1 :                 nRasterXSize = nXSizeIn;
    2758           1 :                 nRasterYSize = nYSizeIn;
    2759           1 :             }
    2760             : 
    2761           1 :             CPLErr Close() override
    2762             :             {
    2763           1 :                 return GDALPamDataset::Close();
    2764             :             }
    2765             : 
    2766           2 :             ~JPEGRawDataset() = default;
    2767             : 
    2768           1 :             void SetBand(int nBand, std::unique_ptr<GDALRasterBand> &&poBand)
    2769             :             {
    2770           1 :                 RawDataset::SetBand(nBand, std::move(poBand));
    2771           1 :             }
    2772             :         };
    2773             : 
    2774             :         auto poBand = RawRasterBand::Create(
    2775             :             fpRaw,
    2776             :             0,                            // image offset
    2777             :             2,                            // pixel offset
    2778           1 :             2 * m_nRawThermalImageWidth,  // line offset
    2779             :             GDT_UInt16,
    2780           1 :             m_bRawThermalLittleEndian
    2781             :                 ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
    2782             :                 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
    2783             :             m_nRawThermalImageWidth, m_nRawThermalImageHeight,
    2784           2 :             RawRasterBand::OwnFP::YES);
    2785           1 :         if (!poBand)
    2786           0 :             return nullptr;
    2787             : 
    2788             :         auto poRawDS = new JPEGRawDataset(m_nRawThermalImageWidth,
    2789           1 :                                           m_nRawThermalImageHeight);
    2790           1 :         poRawDS->SetDescription(osTmpFilename.c_str());
    2791           1 :         poRawDS->SetBand(1, std::move(poBand));
    2792           1 :         poRawDS->MarkSuppressOnClose();
    2793           1 :         return poRawDS;
    2794             :     }
    2795             : 
    2796           1 :     VSIFCloseL(fpRaw);
    2797             : 
    2798             :     // Termal image as PNG
    2799           2 :     if (m_abyRawThermalImage.size() > 4 &&
    2800           1 :         memcmp(m_abyRawThermalImage.data(), "\x89PNG", 4) == 0)
    2801             :     {
    2802           1 :         auto poRawDS = GDALDataset::Open(osTmpFilename.c_str());
    2803           1 :         if (poRawDS == nullptr)
    2804             :         {
    2805           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid raw thermal image");
    2806           0 :             VSIUnlink(osTmpFilename.c_str());
    2807           0 :             return nullptr;
    2808             :         }
    2809           1 :         poRawDS->MarkSuppressOnClose();
    2810           1 :         return poRawDS;
    2811             :     }
    2812             : 
    2813           0 :     CPLError(CE_Failure, CPLE_AppDefined,
    2814             :              "Unrecognized format for raw thermal image");
    2815           0 :     VSIUnlink(osTmpFilename.c_str());
    2816           0 :     return nullptr;
    2817             : }
    2818             : 
    2819             : #endif  // !defined(JPGDataset)
    2820             : 
    2821             : /************************************************************************/
    2822             : /*                                Open()                                */
    2823             : /************************************************************************/
    2824             : 
    2825       10667 : JPGDatasetCommon *JPGDataset::Open(JPGDatasetOpenArgs *psArgs)
    2826             : 
    2827             : {
    2828       10667 :     JPGDataset *poDS = new JPGDataset();
    2829       21334 :     return OpenStage2(psArgs, poDS);
    2830             : }
    2831             : 
    2832       10667 : JPGDatasetCommon *JPGDataset::OpenStage2(JPGDatasetOpenArgs *psArgs,
    2833             :                                          JPGDataset *&poDS)
    2834             : {
    2835             :     // Will detect mismatch between compile-time and run-time libjpeg versions.
    2836       10667 :     if (setjmp(poDS->sUserData.setjmp_buffer))
    2837             :     {
    2838             : #if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
    2839             : 
    2840          15 :         if (poDS->sDInfo.data_precision == 12 && poDS->m_fpImage != nullptr)
    2841             :         {
    2842          14 :             VSILFILE *fpImage = poDS->m_fpImage;
    2843          14 :             poDS->m_fpImage = nullptr;
    2844          14 :             delete poDS;
    2845          14 :             psArgs->fpLin = fpImage;
    2846          14 :             return JPEGDataset12Open(psArgs);
    2847             :         }
    2848             : #endif
    2849           1 :         delete poDS;
    2850           1 :         return nullptr;
    2851             :     }
    2852             : 
    2853       10667 :     const char *pszFilename = psArgs->pszFilename;
    2854       10667 :     VSILFILE *fpLin = psArgs->fpLin;
    2855       10667 :     char **papszSiblingFiles = psArgs->papszSiblingFiles;
    2856       10667 :     const int nScaleFactor = psArgs->nScaleFactor;
    2857       10667 :     const bool bDoPAMInitialize = psArgs->bDoPAMInitialize;
    2858       10667 :     const bool bUseInternalOverviews = psArgs->bUseInternalOverviews;
    2859             : 
    2860             :     // If it is a subfile, read the JPEG header.
    2861       10667 :     bool bIsSubfile = false;
    2862       10667 :     GUIntBig subfile_offset = 0;
    2863       10667 :     GUIntBig subfile_size = 0;
    2864       10667 :     const char *real_filename = pszFilename;
    2865       10667 :     int nQLevel = -1;
    2866             : 
    2867       10667 :     if (STARTS_WITH_CI(pszFilename, "JPEG_SUBFILE:"))
    2868             :     {
    2869          42 :         bool bScan = false;
    2870             : 
    2871          42 :         if (STARTS_WITH_CI(pszFilename, "JPEG_SUBFILE:Q"))
    2872             :         {
    2873          35 :             char **papszTokens = CSLTokenizeString2(pszFilename + 14, ",", 0);
    2874          35 :             if (CSLCount(papszTokens) >= 3)
    2875             :             {
    2876          35 :                 nQLevel = atoi(papszTokens[0]);
    2877          70 :                 subfile_offset = CPLScanUIntBig(
    2878          35 :                     papszTokens[1], static_cast<int>(strlen(papszTokens[1])));
    2879          70 :                 subfile_size = CPLScanUIntBig(
    2880          35 :                     papszTokens[2], static_cast<int>(strlen(papszTokens[2])));
    2881          35 :                 bScan = true;
    2882             :             }
    2883          35 :             CSLDestroy(papszTokens);
    2884             :         }
    2885             :         else
    2886             :         {
    2887           7 :             char **papszTokens = CSLTokenizeString2(pszFilename + 13, ",", 0);
    2888           7 :             if (CSLCount(papszTokens) >= 2)
    2889             :             {
    2890          14 :                 subfile_offset = CPLScanUIntBig(
    2891           7 :                     papszTokens[0], static_cast<int>(strlen(papszTokens[0])));
    2892          14 :                 subfile_size = CPLScanUIntBig(
    2893           7 :                     papszTokens[1], static_cast<int>(strlen(papszTokens[1])));
    2894           7 :                 bScan = true;
    2895             :             }
    2896           7 :             CSLDestroy(papszTokens);
    2897             :         }
    2898             : 
    2899          42 :         if (!bScan)
    2900             :         {
    2901           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    2902             :                      "Corrupt subfile definition: %s", pszFilename);
    2903           0 :             delete poDS;
    2904           0 :             return nullptr;
    2905             :         }
    2906             : 
    2907          42 :         real_filename = strstr(pszFilename, ",");
    2908          42 :         if (real_filename != nullptr)
    2909          42 :             real_filename = strstr(real_filename + 1, ",");
    2910          42 :         if (real_filename != nullptr && nQLevel != -1)
    2911          35 :             real_filename = strstr(real_filename + 1, ",");
    2912          42 :         if (real_filename != nullptr)
    2913          42 :             real_filename++;
    2914             :         else
    2915             :         {
    2916           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    2917             :                      "Could not find filename in subfile definition.");
    2918           0 :             delete poDS;
    2919           0 :             return nullptr;
    2920             :         }
    2921             : 
    2922          42 :         CPLDebug("JPG",
    2923             :                  "real_filename %s, offset=" CPL_FRMT_GUIB
    2924             :                  ", size=" CPL_FRMT_GUIB "\n",
    2925             :                  real_filename, subfile_offset, subfile_size);
    2926             : 
    2927          42 :         bIsSubfile = true;
    2928             :     }
    2929             : 
    2930             :     // Open the file using the large file api if necessary.
    2931       10667 :     VSILFILE *fpImage = fpLin;
    2932             : 
    2933       10667 :     if (fpImage == nullptr)
    2934             :     {
    2935        7631 :         fpImage = VSIFOpenL(real_filename, "rb");
    2936             : 
    2937        7631 :         if (fpImage == nullptr)
    2938             :         {
    2939           2 :             CPLError(CE_Failure, CPLE_OpenFailed,
    2940             :                      "VSIFOpenL(%s) failed unexpectedly in jpgdataset.cpp",
    2941             :                      real_filename);
    2942           2 :             delete poDS;
    2943           2 :             return nullptr;
    2944             :         }
    2945             :     }
    2946             :     else
    2947             :     {
    2948        3036 :         fpImage = fpLin;
    2949             :     }
    2950             : 
    2951             :     // Create a corresponding GDALDataset.
    2952       10665 :     poDS->nQLevel = nQLevel;
    2953       10665 :     poDS->m_fpImage = fpImage;
    2954             : 
    2955             :     // Move to the start of jpeg data.
    2956       10665 :     poDS->nSubfileOffset = subfile_offset;
    2957       10665 :     VSIFSeekL(poDS->m_fpImage, poDS->nSubfileOffset, SEEK_SET);
    2958             : 
    2959       10665 :     poDS->eAccess = GA_ReadOnly;
    2960             : 
    2961       10665 :     poDS->sDInfo.err = jpeg_std_error(&poDS->sJErr);
    2962       10665 :     poDS->sJErr.error_exit = JPGDataset::ErrorExit;
    2963       10665 :     poDS->sJErr.output_message = JPGDataset::OutputMessage;
    2964       10665 :     poDS->sUserData.p_previous_emit_message = poDS->sJErr.emit_message;
    2965       10665 :     poDS->sJErr.emit_message = JPGDataset::EmitMessage;
    2966       10665 :     poDS->sDInfo.client_data = &poDS->sUserData;
    2967             : 
    2968       10665 :     jpeg_create_decompress(&poDS->sDInfo);
    2969       10665 :     poDS->bHasDoneJpegCreateDecompress = true;
    2970             : 
    2971       10665 :     SetMaxMemoryToUse(&poDS->sDInfo);
    2972             : 
    2973             : #if !defined(JPGDataset)
    2974             :     // Preload default NITF JPEG quantization tables.
    2975       10650 :     poDS->LoadDefaultTables(0);
    2976       10650 :     poDS->LoadDefaultTables(1);
    2977       10650 :     poDS->LoadDefaultTables(2);
    2978       10650 :     poDS->LoadDefaultTables(3);
    2979             : #endif  // !defined(JPGDataset)
    2980             : 
    2981             :     // Read pre-image data after ensuring the file is rewound.
    2982       10665 :     VSIFSeekL(poDS->m_fpImage, poDS->nSubfileOffset, SEEK_SET);
    2983             : 
    2984       10665 :     jpeg_vsiio_src(&poDS->sDInfo, poDS->m_fpImage);
    2985       10665 :     jpeg_read_header(&poDS->sDInfo, TRUE);
    2986             : 
    2987       10650 :     if (poDS->sDInfo.data_precision != 8 && poDS->sDInfo.data_precision != 12)
    2988             :     {
    2989           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2990             :                  "GDAL JPEG Driver doesn't support files with precision of "
    2991             :                  "other than 8 or 12 bits.");
    2992           0 :         delete poDS;
    2993           0 :         return nullptr;
    2994             :     }
    2995             : 
    2996             : #if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
    2997       10635 :     if (poDS->sDInfo.data_precision == 12 && poDS->m_fpImage != nullptr)
    2998             :     {
    2999           0 :         poDS->m_fpImage = nullptr;
    3000           0 :         delete poDS;
    3001           0 :         psArgs->fpLin = fpImage;
    3002           0 :         return JPEGDataset12Open(psArgs);
    3003             :     }
    3004             : #endif
    3005             : 
    3006             :     // Capture some information from the file that is of interest.
    3007             : 
    3008       10650 :     poDS->nScaleFactor = nScaleFactor;
    3009       10650 :     poDS->SetScaleNumAndDenom();
    3010       10650 :     poDS->nRasterXSize =
    3011       10650 :         (poDS->sDInfo.image_width + nScaleFactor - 1) / nScaleFactor;
    3012       10650 :     poDS->nRasterYSize =
    3013       10650 :         (poDS->sDInfo.image_height + nScaleFactor - 1) / nScaleFactor;
    3014             : 
    3015       10650 :     poDS->sDInfo.out_color_space = poDS->sDInfo.jpeg_color_space;
    3016       10650 :     poDS->eGDALColorSpace = poDS->sDInfo.jpeg_color_space;
    3017             : 
    3018       10650 :     if (poDS->sDInfo.jpeg_color_space == JCS_GRAYSCALE)
    3019             :     {
    3020        4042 :         poDS->nBands = 1;
    3021             :     }
    3022        6608 :     else if (poDS->sDInfo.jpeg_color_space == JCS_RGB)
    3023             :     {
    3024        3004 :         poDS->nBands = 3;
    3025             :     }
    3026        3604 :     else if (poDS->sDInfo.jpeg_color_space == JCS_YCbCr)
    3027             :     {
    3028        3348 :         poDS->nBands = 3;
    3029        3348 :         if (CPLTestBool(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES")))
    3030             :         {
    3031        3348 :             poDS->sDInfo.out_color_space = JCS_RGB;
    3032        3348 :             poDS->eGDALColorSpace = JCS_RGB;
    3033        3348 :             poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr",
    3034        3348 :                                   "IMAGE_STRUCTURE");
    3035             :         }
    3036             :     }
    3037         256 :     else if (poDS->sDInfo.jpeg_color_space == JCS_CMYK)
    3038             :     {
    3039         512 :         if (poDS->sDInfo.data_precision == 8 &&
    3040         256 :             CPLTestBool(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES")))
    3041             :         {
    3042           5 :             poDS->eGDALColorSpace = JCS_RGB;
    3043           5 :             poDS->nBands = 3;
    3044           5 :             poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "CMYK",
    3045           5 :                                   "IMAGE_STRUCTURE");
    3046             :         }
    3047             :         else
    3048             :         {
    3049         251 :             poDS->nBands = 4;
    3050             :         }
    3051             :     }
    3052           0 :     else if (poDS->sDInfo.jpeg_color_space == JCS_YCCK)
    3053             :     {
    3054           0 :         if (poDS->sDInfo.data_precision == 8 &&
    3055           0 :             CPLTestBool(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES")))
    3056             :         {
    3057           0 :             poDS->eGDALColorSpace = JCS_RGB;
    3058           0 :             poDS->nBands = 3;
    3059           0 :             poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCrK",
    3060           0 :                                   "IMAGE_STRUCTURE");
    3061             : 
    3062             :             // libjpeg does the translation from YCrCbK -> CMYK internally
    3063             :             // and we'll do the translation to RGB in IReadBlock().
    3064           0 :             poDS->sDInfo.out_color_space = JCS_CMYK;
    3065             :         }
    3066             :         else
    3067             :         {
    3068           0 :             poDS->nBands = 4;
    3069             :         }
    3070             :     }
    3071             :     else
    3072             :     {
    3073           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3074             :                  "Unrecognized jpeg_color_space value of %d.\n",
    3075           0 :                  poDS->sDInfo.jpeg_color_space);
    3076           0 :         delete poDS;
    3077           0 :         return nullptr;
    3078             :     }
    3079             : 
    3080             :     // Create band information objects.
    3081       34767 :     for (int iBand = 0; iBand < poDS->nBands; iBand++)
    3082       24117 :         poDS->SetBand(iBand + 1, JPGCreateBand(poDS, iBand + 1));
    3083             : 
    3084             :     // More metadata.
    3085       10650 :     if (poDS->nBands > 1)
    3086             :     {
    3087        6608 :         poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
    3088        6608 :         poDS->SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
    3089             :     }
    3090             : 
    3091       10650 :     if (psArgs->bIsLossless)
    3092             :     {
    3093           0 :         poDS->SetMetadataItem("COMPRESSION_REVERSIBILITY", "LOSSLESS",
    3094           0 :                               "IMAGE_STRUCTURE");
    3095             :     }
    3096             : 
    3097             :     // Initialize any PAM information.
    3098       10650 :     poDS->SetDescription(pszFilename);
    3099             : 
    3100       10650 :     if (nScaleFactor == 1 && bDoPAMInitialize)
    3101             :     {
    3102        3234 :         if (!bIsSubfile)
    3103        3204 :             poDS->TryLoadXML(papszSiblingFiles);
    3104             :         else
    3105          30 :             poDS->nPamFlags |= GPF_NOSAVE;
    3106             : 
    3107             :         // Open (external) overviews.
    3108        3234 :         poDS->oOvManager.Initialize(poDS, real_filename, papszSiblingFiles);
    3109             : 
    3110        3234 :         if (!bUseInternalOverviews)
    3111           0 :             poDS->bHasInitInternalOverviews = true;
    3112             : 
    3113             :         // In the case of a file downloaded through the HTTP driver, this one
    3114             :         // will unlink the temporary /vsimem file just after GDALOpen(), so
    3115             :         // later VSIFOpenL() when reading internal overviews would fail.
    3116             :         // Initialize them now.
    3117        3234 :         if (STARTS_WITH(real_filename, "/vsimem/http_"))
    3118             :         {
    3119           0 :             poDS->InitInternalOverviews();
    3120             :         }
    3121             :     }
    3122             :     else
    3123             :     {
    3124        7416 :         poDS->nPamFlags |= GPF_NOSAVE;
    3125             :     }
    3126             : 
    3127       10650 :     poDS->bIsSubfile = bIsSubfile;
    3128             : 
    3129       10650 :     return poDS;
    3130             : }
    3131             : 
    3132             : #if !defined(JPGDataset)
    3133             : 
    3134             : /************************************************************************/
    3135             : /*                       LoadWorldFileOrTab()                           */
    3136             : /************************************************************************/
    3137             : 
    3138         288 : void JPGDatasetCommon::LoadWorldFileOrTab()
    3139             : {
    3140         288 :     if (bIsSubfile)
    3141         147 :         return;
    3142         288 :     if (bHasTriedLoadWorldFileOrTab)
    3143         147 :         return;
    3144         141 :     bHasTriedLoadWorldFileOrTab = true;
    3145             : 
    3146         141 :     char *pszWldFilename = nullptr;
    3147             : 
    3148             :     // TIROS3 JPEG files have a .wld extension, so don't look for .wld as
    3149             :     // as worldfile.
    3150             :     const bool bEndsWithWld =
    3151         282 :         strlen(GetDescription()) > 4 &&
    3152         141 :         EQUAL(GetDescription() + strlen(GetDescription()) - 4, ".wld");
    3153         141 :     bGeoTransformValid =
    3154         141 :         GDALReadWorldFile2(GetDescription(), nullptr, adfGeoTransform,
    3155         141 :                            oOvManager.GetSiblingFiles(), &pszWldFilename) ||
    3156         141 :         GDALReadWorldFile2(GetDescription(), ".jpw", adfGeoTransform,
    3157         282 :                            oOvManager.GetSiblingFiles(), &pszWldFilename) ||
    3158         141 :         (!bEndsWithWld &&
    3159         141 :          GDALReadWorldFile2(GetDescription(), ".wld", adfGeoTransform,
    3160             :                             oOvManager.GetSiblingFiles(), &pszWldFilename));
    3161             : 
    3162         141 :     if (!bGeoTransformValid)
    3163             :     {
    3164         137 :         char *pszProjection = nullptr;
    3165         137 :         int nGCPCount = 0;
    3166         137 :         GDAL_GCP *pasGCPList = nullptr;
    3167         274 :         const bool bTabFileOK = CPL_TO_BOOL(GDALReadTabFile2(
    3168         137 :             GetDescription(), adfGeoTransform, &pszProjection, &nGCPCount,
    3169             :             &pasGCPList, oOvManager.GetSiblingFiles(), &pszWldFilename));
    3170         137 :         if (pszProjection)
    3171           0 :             m_oSRS.importFromWkt(pszProjection);
    3172         137 :         CPLFree(pszProjection);
    3173         137 :         m_aoGCPs = gdal::GCP::fromC(pasGCPList, nGCPCount);
    3174         137 :         GDALDeinitGCPs(nGCPCount, pasGCPList);
    3175         137 :         CPLFree(pasGCPList);
    3176             : 
    3177         137 :         if (bTabFileOK && nGCPCount == 0)
    3178           0 :             bGeoTransformValid = true;
    3179             :     }
    3180             : 
    3181         141 :     if (pszWldFilename)
    3182             :     {
    3183           4 :         osWldFilename = pszWldFilename;
    3184           4 :         CPLFree(pszWldFilename);
    3185             :     }
    3186             : }
    3187             : 
    3188             : /************************************************************************/
    3189             : /*                            GetFileList()                             */
    3190             : /************************************************************************/
    3191             : 
    3192          74 : char **JPGDatasetCommon::GetFileList()
    3193             : 
    3194             : {
    3195          74 :     char **papszFileList = GDALPamDataset::GetFileList();
    3196             : 
    3197          74 :     LoadWorldFileOrTab();
    3198             : 
    3199          76 :     if (!osWldFilename.empty() &&
    3200           2 :         CSLFindString(papszFileList, osWldFilename) == -1)
    3201             :     {
    3202           2 :         papszFileList = CSLAddString(papszFileList, osWldFilename);
    3203             :     }
    3204             : 
    3205          74 :     return papszFileList;
    3206             : }
    3207             : 
    3208             : /************************************************************************/
    3209             : /*                            CheckForMask()                            */
    3210             : /************************************************************************/
    3211             : 
    3212          62 : void JPGDatasetCommon::CheckForMask()
    3213             : 
    3214             : {
    3215             :     // Save current position to avoid disturbing JPEG stream decoding.
    3216          62 :     const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
    3217             : 
    3218             :     // Go to the end of the file, pull off four bytes, and see if
    3219             :     // it is plausibly the size of the real image data.
    3220          62 :     VSIFSeekL(m_fpImage, 0, SEEK_END);
    3221          62 :     GIntBig nFileSize = VSIFTellL(m_fpImage);
    3222          62 :     VSIFSeekL(m_fpImage, nFileSize - 4, SEEK_SET);
    3223             : 
    3224          62 :     GUInt32 nImageSize = 0;
    3225          62 :     VSIFReadL(&nImageSize, 4, 1, m_fpImage);
    3226          62 :     CPL_LSBPTR32(&nImageSize);
    3227             : 
    3228          62 :     GByte abyEOD[2] = {0, 0};
    3229             : 
    3230          62 :     if (nImageSize < nFileSize / 2 || nImageSize > nFileSize - 4)
    3231          47 :         goto end;
    3232             : 
    3233             :     // If that seems okay, seek back, and verify that just preceding
    3234             :     // the bitmask is an apparent end-of-jpeg-data marker.
    3235          15 :     VSIFSeekL(m_fpImage, nImageSize - 2, SEEK_SET);
    3236          15 :     VSIFReadL(abyEOD, 2, 1, m_fpImage);
    3237          15 :     if (abyEOD[0] != 0xff || abyEOD[1] != 0xd9)
    3238           0 :         goto end;
    3239             : 
    3240             :     // We seem to have a mask.  Read it in.
    3241          15 :     nCMaskSize = static_cast<int>(nFileSize - nImageSize - 4);
    3242          15 :     pabyCMask = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nCMaskSize));
    3243          15 :     if (pabyCMask == nullptr)
    3244             :     {
    3245           0 :         goto end;
    3246             :     }
    3247          15 :     VSIFReadL(pabyCMask, nCMaskSize, 1, m_fpImage);
    3248             : 
    3249          15 :     CPLDebug("JPEG", "Got %d byte compressed bitmask.", nCMaskSize);
    3250             : 
    3251             :     // TODO(schwehr): Refactor to not use goto.
    3252          62 : end:
    3253          62 :     VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
    3254          62 : }
    3255             : 
    3256             : /************************************************************************/
    3257             : /*                           DecompressMask()                           */
    3258             : /************************************************************************/
    3259             : 
    3260        4261 : void JPGDatasetCommon::DecompressMask()
    3261             : 
    3262             : {
    3263        4261 :     if (pabyCMask == nullptr || pabyBitMask != nullptr)
    3264        4246 :         return;
    3265             : 
    3266             :     // Allocate 1bit buffer - may be slightly larger than needed.
    3267          15 :     const int nBufSize = nRasterYSize * ((nRasterXSize + 7) / 8);
    3268          15 :     pabyBitMask = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBufSize));
    3269          15 :     if (pabyBitMask == nullptr)
    3270             :     {
    3271           0 :         CPLFree(pabyCMask);
    3272           0 :         pabyCMask = nullptr;
    3273           0 :         return;
    3274             :     }
    3275             : 
    3276             :     // Decompress.
    3277             :     void *pOut =
    3278          15 :         CPLZLibInflate(pabyCMask, nCMaskSize, pabyBitMask, nBufSize, nullptr);
    3279             : 
    3280             :     // Cleanup if an error occurs.
    3281          15 :     if (pOut == nullptr)
    3282             :     {
    3283           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3284             :                  "Failure decoding JPEG validity bitmask.");
    3285           0 :         CPLFree(pabyCMask);
    3286           0 :         pabyCMask = nullptr;
    3287             : 
    3288           0 :         CPLFree(pabyBitMask);
    3289           0 :         pabyBitMask = nullptr;
    3290             : 
    3291           0 :         return;
    3292             :     }
    3293             : 
    3294             :     const char *pszJPEGMaskBitOrder =
    3295          15 :         CPLGetConfigOption("JPEG_MASK_BIT_ORDER", "AUTO");
    3296          15 :     if (EQUAL(pszJPEGMaskBitOrder, "LSB"))
    3297           0 :         bMaskLSBOrder = true;
    3298          15 :     else if (EQUAL(pszJPEGMaskBitOrder, "MSB"))
    3299           0 :         bMaskLSBOrder = false;
    3300          15 :     else if (nRasterXSize > 8 && nRasterYSize > 1)
    3301             :     {
    3302             :         // Test MSB ordering hypothesis in a very restrictive case where it is
    3303             :         // *obviously* ordered as MSB ! (unless someone coded something
    3304             :         // specifically to defeat the below logic)
    3305             :         // The case considered here is dop_465_6100.jpg from #5102.
    3306             :         // The mask is identical for each line, starting with 1's and ending
    3307             :         // with 0's (or starting with 0's and ending with 1's), and no other
    3308             :         // intermediate change.
    3309             :         // We can detect the MSB ordering since the lsb bits at the end of the
    3310             :         // first line will be set with the 1's of the beginning of the second
    3311             :         // line.
    3312             :         // We can only be sure of this heuristics if the change of value occurs
    3313             :         // in the middle of a byte, or if the raster width is not a multiple of
    3314             :         // 8.
    3315             :         //
    3316             :         // TODO(schwehr): Check logic in this section that was added in r26063.
    3317          15 :         int nPrevValBit = 0;
    3318          15 :         int nChangedValBit = 0;
    3319          15 :         int iX = 0;  // Used after for.
    3320        2101 :         for (; iX < nRasterXSize; iX++)
    3321             :         {
    3322        2098 :             const int nValBit =
    3323        2098 :                 (pabyBitMask[iX >> 3] & (0x1 << (7 - (iX & 7)))) != 0;
    3324        2098 :             if (iX == 0)
    3325          15 :                 nPrevValBit = nValBit;
    3326        2083 :             else if (nValBit != nPrevValBit)
    3327             :             {
    3328          13 :                 nPrevValBit = nValBit;
    3329          13 :                 nChangedValBit++;
    3330          13 :                 if (nChangedValBit == 1)
    3331             :                 {
    3332          13 :                     const bool bValChangedOnByteBoundary = (iX % 8) == 0;
    3333          13 :                     if (bValChangedOnByteBoundary && (nRasterXSize % 8) == 0)
    3334          11 :                         break;
    3335             :                 }
    3336             :                 else
    3337             :                 {
    3338           0 :                     break;
    3339             :                 }
    3340             :             }
    3341        2087 :             const int iNextLineX = iX + nRasterXSize;
    3342        2087 :             const int nNextLineValBit = (pabyBitMask[iNextLineX >> 3] &
    3343        2087 :                                          (0x1 << (7 - (iNextLineX & 7)))) != 0;
    3344        2087 :             if (nValBit != nNextLineValBit)
    3345           1 :                 break;
    3346             :         }
    3347             : 
    3348          15 :         if (iX == nRasterXSize && nChangedValBit == 1)
    3349             :         {
    3350           2 :             CPLDebug("JPEG",
    3351             :                      "Bit ordering in mask is guessed to be msb (unusual)");
    3352           2 :             bMaskLSBOrder = false;
    3353             :         }
    3354             :         else
    3355             :         {
    3356          13 :             bMaskLSBOrder = true;
    3357          15 :         }
    3358             :     }
    3359             :     else
    3360             :     {
    3361           0 :         bMaskLSBOrder = true;
    3362             :     }
    3363             : }
    3364             : 
    3365             : /************************************************************************/
    3366             : /*                       GetCompressionFormats()                        */
    3367             : /************************************************************************/
    3368             : 
    3369           1 : CPLStringList JPGDatasetCommon::GetCompressionFormats(int nXOff, int nYOff,
    3370             :                                                       int nXSize, int nYSize,
    3371             :                                                       int nBandCount,
    3372             :                                                       const int *panBandList)
    3373             : {
    3374           1 :     CPLStringList aosRet;
    3375           1 :     if (m_fpImage && nXOff == 0 && nYOff == 0 && nXSize == nRasterXSize &&
    3376           2 :         nYSize == nRasterYSize && IsAllBands(nBandCount, panBandList))
    3377             :     {
    3378           1 :         aosRet.AddString(GDALGetCompressionFormatForJPEG(m_fpImage).c_str());
    3379             :     }
    3380           1 :     return aosRet;
    3381             : }
    3382             : 
    3383             : /************************************************************************/
    3384             : /*                       ReadCompressedData()                           */
    3385             : /************************************************************************/
    3386             : 
    3387          20 : CPLErr JPGDatasetCommon::ReadCompressedData(
    3388             :     const char *pszFormat, int nXOff, int nYOff, int nXSize, int nYSize,
    3389             :     int nBandCount, const int *panBandList, void **ppBuffer,
    3390             :     size_t *pnBufferSize, char **ppszDetailedFormat)
    3391             : {
    3392          20 :     if (m_fpImage && nXOff == 0 && nYOff == 0 && nXSize == nRasterXSize &&
    3393          40 :         nYSize == nRasterYSize && IsAllBands(nBandCount, panBandList))
    3394             :     {
    3395          20 :         const CPLStringList aosTokens(CSLTokenizeString2(pszFormat, ";", 0));
    3396          20 :         if (aosTokens.size() != 1)
    3397           0 :             return CE_Failure;
    3398             : 
    3399          20 :         if (EQUAL(aosTokens[0], "JPEG"))
    3400             :         {
    3401          15 :             if (ppszDetailedFormat)
    3402           6 :                 *ppszDetailedFormat = VSIStrdup(
    3403          12 :                     GDALGetCompressionFormatForJPEG(m_fpImage).c_str());
    3404             : 
    3405          15 :             const auto nSavedPos = VSIFTellL(m_fpImage);
    3406          15 :             VSIFSeekL(m_fpImage, 0, SEEK_END);
    3407          15 :             auto nFileSize = VSIFTellL(m_fpImage);
    3408          15 :             if (nFileSize > std::numeric_limits<size_t>::max() / 2)
    3409           0 :                 return CE_Failure;
    3410          15 :             if (nFileSize > 4)
    3411             :             {
    3412          15 :                 VSIFSeekL(m_fpImage, nFileSize - 4, SEEK_SET);
    3413             :                 // Detect zlib compress mask band at end of file
    3414             :                 // and remove it if found
    3415          15 :                 uint32_t nImageSize = 0;
    3416          15 :                 VSIFReadL(&nImageSize, 4, 1, m_fpImage);
    3417          15 :                 CPL_LSBPTR32(&nImageSize);
    3418          15 :                 if (nImageSize > 2 && nImageSize >= nFileSize / 2 &&
    3419          15 :                     nImageSize < nFileSize - 4)
    3420             :                 {
    3421           2 :                     VSIFSeekL(m_fpImage, nImageSize - 2, SEEK_SET);
    3422             :                     GByte abyTwoBytes[2];
    3423           2 :                     if (VSIFReadL(abyTwoBytes, 2, 1, m_fpImage) == 1 &&
    3424           2 :                         abyTwoBytes[0] == 0xFF && abyTwoBytes[1] == 0xD9)
    3425             :                     {
    3426           2 :                         nFileSize = nImageSize;
    3427             :                     }
    3428             :                 }
    3429             :             }
    3430          15 :             auto nSize = static_cast<size_t>(nFileSize);
    3431          15 :             if (ppBuffer)
    3432             :             {
    3433          14 :                 if (pnBufferSize == nullptr)
    3434             :                 {
    3435           1 :                     VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
    3436           2 :                     return CE_Failure;
    3437             :                 }
    3438          13 :                 bool bFreeOnError = false;
    3439          13 :                 if (*ppBuffer)
    3440             :                 {
    3441           3 :                     if (*pnBufferSize < nSize)
    3442             :                     {
    3443           1 :                         VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
    3444           1 :                         return CE_Failure;
    3445             :                     }
    3446             :                 }
    3447             :                 else
    3448             :                 {
    3449          10 :                     *ppBuffer = VSI_MALLOC_VERBOSE(nSize);
    3450          10 :                     if (*ppBuffer == nullptr)
    3451             :                     {
    3452           0 :                         VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
    3453           0 :                         return CE_Failure;
    3454             :                     }
    3455          10 :                     bFreeOnError = true;
    3456             :                 }
    3457          12 :                 VSIFSeekL(m_fpImage, 0, SEEK_SET);
    3458          12 :                 if (VSIFReadL(*ppBuffer, nSize, 1, m_fpImage) != 1)
    3459             :                 {
    3460           0 :                     if (bFreeOnError)
    3461             :                     {
    3462           0 :                         VSIFree(*ppBuffer);
    3463           0 :                         *ppBuffer = nullptr;
    3464             :                     }
    3465           0 :                     VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
    3466           0 :                     return CE_Failure;
    3467             :                 }
    3468             : 
    3469          12 :                 constexpr GByte EXIF_SIGNATURE[] = {'E', 'x',  'i',
    3470             :                                                     'f', '\0', '\0'};
    3471          12 :                 constexpr char APP1_XMP_SIGNATURE[] =
    3472             :                     "http://ns.adobe.com/xap/1.0/";
    3473          12 :                 size_t nChunkLoc = 2;
    3474          12 :                 GByte *pabyJPEG = static_cast<GByte *>(*ppBuffer);
    3475         104 :                 while (nChunkLoc + 4 <= nSize)
    3476             :                 {
    3477         104 :                     if (pabyJPEG[nChunkLoc + 0] != 0xFF)
    3478           0 :                         break;
    3479         104 :                     if (pabyJPEG[nChunkLoc + 1] == 0xDA)
    3480          12 :                         break;
    3481          92 :                     const int nChunkLength =
    3482          92 :                         pabyJPEG[nChunkLoc + 2] * 256 + pabyJPEG[nChunkLoc + 3];
    3483          92 :                     if (nChunkLength < 2 || static_cast<size_t>(nChunkLength) >
    3484          92 :                                                 nSize - (nChunkLoc + 2))
    3485             :                         break;
    3486          92 :                     if (pabyJPEG[nChunkLoc + 1] == 0xE1 &&
    3487           7 :                         nChunkLoc + 4 + sizeof(EXIF_SIGNATURE) <= nSize &&
    3488           7 :                         memcmp(pabyJPEG + nChunkLoc + 4, EXIF_SIGNATURE,
    3489             :                                sizeof(EXIF_SIGNATURE)) == 0)
    3490             :                     {
    3491           6 :                         CPLDebug("JPEG", "Remove existing EXIF from "
    3492             :                                          "source compressed data");
    3493           6 :                         memmove(pabyJPEG + nChunkLoc,
    3494           6 :                                 pabyJPEG + nChunkLoc + 2 + nChunkLength,
    3495           6 :                                 nSize - (nChunkLoc + 2 + nChunkLength));
    3496           6 :                         nSize -= 2 + nChunkLength;
    3497           6 :                         continue;
    3498             :                     }
    3499          86 :                     else if (pabyJPEG[nChunkLoc + 1] == 0xE1 &&
    3500           1 :                              nChunkLoc + 4 + sizeof(APP1_XMP_SIGNATURE) <=
    3501           2 :                                  nSize &&
    3502           1 :                              memcmp(pabyJPEG + nChunkLoc + 4,
    3503             :                                     APP1_XMP_SIGNATURE,
    3504             :                                     sizeof(APP1_XMP_SIGNATURE)) == 0)
    3505             :                     {
    3506           1 :                         CPLDebug("JPEG", "Remove existing XMP from "
    3507             :                                          "source compressed data");
    3508           1 :                         memmove(pabyJPEG + nChunkLoc,
    3509           1 :                                 pabyJPEG + nChunkLoc + 2 + nChunkLength,
    3510           1 :                                 nSize - (nChunkLoc + 2 + nChunkLength));
    3511           1 :                         nSize -= 2 + nChunkLength;
    3512           1 :                         continue;
    3513             :                     }
    3514          85 :                     nChunkLoc += 2 + nChunkLength;
    3515             :                 }
    3516             :             }
    3517          13 :             VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
    3518          13 :             if (pnBufferSize)
    3519          13 :                 *pnBufferSize = nSize;
    3520          13 :             return CE_None;
    3521             :         }
    3522             :     }
    3523           5 :     return CE_Failure;
    3524             : }
    3525             : 
    3526             : #endif  // !defined(JPGDataset)
    3527             : 
    3528             : /************************************************************************/
    3529             : /*                             ErrorExit()                              */
    3530             : /************************************************************************/
    3531             : 
    3532          26 : void JPGDataset::ErrorExit(j_common_ptr cinfo)
    3533             : {
    3534          26 :     GDALJPEGUserData *psUserData =
    3535             :         static_cast<GDALJPEGUserData *>(cinfo->client_data);
    3536          26 :     char buffer[JMSG_LENGTH_MAX] = {};
    3537             : 
    3538             :     // Create the message.
    3539          26 :     (*cinfo->err->format_message)(cinfo, buffer);
    3540             : 
    3541             :     // Avoid error for a 12bit JPEG if reading from the 8bit JPEG driver and
    3542             :     // we have JPEG_DUAL_MODE_8_12 support, as we'll try again with 12bit JPEG
    3543             :     // driver.
    3544             : #if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
    3545          26 :     if (strstr(buffer, "Unsupported JPEG data precision 12") == nullptr)
    3546             : #endif
    3547          12 :         CPLError(CE_Failure, CPLE_AppDefined, "libjpeg: %s", buffer);
    3548             : 
    3549             :     // Return control to the setjmp point.
    3550          26 :     longjmp(psUserData->setjmp_buffer, 1);
    3551             : }
    3552             : 
    3553             : /************************************************************************/
    3554             : /*                          OutputMessage()                             */
    3555             : /************************************************************************/
    3556             : 
    3557           0 : void JPGDataset::OutputMessage(j_common_ptr cinfo)
    3558             : {
    3559           0 :     char buffer[JMSG_LENGTH_MAX] = {};
    3560             : 
    3561             :     // Create the message.
    3562           0 :     (*cinfo->err->format_message)(cinfo, buffer);
    3563             : 
    3564           0 :     CPLDebug("JPEG", "libjpeg: %s", buffer);
    3565           0 : }
    3566             : 
    3567             : /************************************************************************/
    3568             : /*                             EmitMessage()                            */
    3569             : /************************************************************************/
    3570             : 
    3571      206701 : void JPGDataset::EmitMessage(j_common_ptr cinfo, int msg_level)
    3572             : {
    3573      206701 :     GDALJPEGUserData *psUserData =
    3574             :         static_cast<GDALJPEGUserData *>(cinfo->client_data);
    3575      206701 :     if (msg_level >= 0)  // Trace message.
    3576             :     {
    3577      206691 :         if (psUserData->p_previous_emit_message != nullptr)
    3578      206691 :             psUserData->p_previous_emit_message(cinfo, msg_level);
    3579             :     }
    3580             :     else
    3581             :     {
    3582             :         // Warning : libjpeg will try to recover but the image will be likely
    3583             :         // corrupted.
    3584             : 
    3585          10 :         struct jpeg_error_mgr *err = cinfo->err;
    3586             : 
    3587             :         // It's a warning message.  Since corrupt files may generate many
    3588             :         // warnings, the policy implemented here is to show only the first
    3589             :         // warning, unless trace_level >= 3.
    3590          10 :         if (err->num_warnings == 0 || err->trace_level >= 3)
    3591             :         {
    3592           6 :             char buffer[JMSG_LENGTH_MAX] = {};
    3593             : 
    3594             :             // Create the message.
    3595           6 :             (*cinfo->err->format_message)(cinfo, buffer);
    3596             : 
    3597             :             const char *pszVal =
    3598           6 :                 CPLGetConfigOption("GDAL_ERROR_ON_LIBJPEG_WARNING", nullptr);
    3599           6 :             if (strstr(buffer, "Premature end of JPEG file"))
    3600             :             {
    3601             :                 // Consider this an error by default
    3602           6 :                 if (pszVal == nullptr || CPLTestBool(pszVal))
    3603             :                 {
    3604           5 :                     psUserData->bNonFatalErrorEncountered = true;
    3605           5 :                     if (pszVal == nullptr)
    3606             :                     {
    3607           3 :                         CPLError(CE_Failure, CPLE_AppDefined,
    3608             :                                  "libjpeg: %s (this error can be turned as a "
    3609             :                                  "warning "
    3610             :                                  "by setting GDAL_ERROR_ON_LIBJPEG_WARNING to "
    3611             :                                  "FALSE)",
    3612             :                                  buffer);
    3613             :                     }
    3614             :                     else
    3615             :                     {
    3616           2 :                         CPLError(CE_Failure, CPLE_AppDefined, "libjpeg: %s",
    3617             :                                  buffer);
    3618             :                     }
    3619             :                 }
    3620             :                 else
    3621             :                 {
    3622           1 :                     CPLError(CE_Warning, CPLE_AppDefined, "libjpeg: %s",
    3623             :                              buffer);
    3624             :                 }
    3625             :             }
    3626           0 :             else if (pszVal == nullptr || CPLTestBool(pszVal))
    3627             :             {
    3628           0 :                 if (pszVal == nullptr)
    3629             :                 {
    3630           0 :                     CPLError(
    3631             :                         CE_Warning, CPLE_AppDefined,
    3632             :                         "libjpeg: %s (this warning can be turned as an error "
    3633             :                         "by setting GDAL_ERROR_ON_LIBJPEG_WARNING to TRUE)",
    3634             :                         buffer);
    3635             :                 }
    3636             :                 else
    3637             :                 {
    3638           0 :                     CPLError(CE_Warning, CPLE_AppDefined, "libjpeg: %s",
    3639             :                              buffer);
    3640             :                 }
    3641             :             }
    3642             :             else
    3643             :             {
    3644           0 :                 psUserData->bNonFatalErrorEncountered = true;
    3645           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "libjpeg: %s", buffer);
    3646             :             }
    3647             :         }
    3648             : 
    3649             :         // Always count warnings in num_warnings.
    3650          10 :         err->num_warnings++;
    3651             :     }
    3652      206701 : }
    3653             : 
    3654             : /************************************************************************/
    3655             : /*                          ProgressMonitor()                           */
    3656             : /************************************************************************/
    3657             : 
    3658             : /* Avoid the risk of denial-of-service on crafted JPEGs with an insane */
    3659             : /* number of scans. */
    3660             : /* See
    3661             :  * http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf
    3662             :  */
    3663      179410 : void JPGDataset::ProgressMonitor(j_common_ptr cinfo)
    3664             : {
    3665      179410 :     if (cinfo->is_decompressor)
    3666             :     {
    3667      179410 :         GDALJPEGUserData *psUserData =
    3668             :             static_cast<GDALJPEGUserData *>(cinfo->client_data);
    3669      179410 :         const int scan_no =
    3670             :             reinterpret_cast<j_decompress_ptr>(cinfo)->input_scan_number;
    3671      179410 :         if (scan_no >= psUserData->nMaxScans)
    3672             :         {
    3673           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    3674             :                      "Scan number %d exceeds maximum scans (%d)", scan_no,
    3675             :                      psUserData->nMaxScans);
    3676             : 
    3677             :             // Return control to the setjmp point.
    3678           1 :             longjmp(psUserData->setjmp_buffer, 1);
    3679             :         }
    3680             :     }
    3681      179409 : }
    3682             : 
    3683             : #if !defined(JPGDataset)
    3684             : 
    3685             : /************************************************************************/
    3686             : /*                           JPGAddICCProfile()                         */
    3687             : /*                                                                      */
    3688             : /*      This function adds an ICC profile to a JPEG file.               */
    3689             : /************************************************************************/
    3690             : 
    3691           3 : void JPGAddICCProfile(void *pInfo, const char *pszICCProfile,
    3692             :                       my_jpeg_write_m_header p_jpeg_write_m_header,
    3693             :                       my_jpeg_write_m_byte p_jpeg_write_m_byte)
    3694             : {
    3695           3 :     if (pszICCProfile == nullptr)
    3696           0 :         return;
    3697             : 
    3698             :     // Write out each segment of the ICC profile.
    3699           3 :     char *pEmbedBuffer = CPLStrdup(pszICCProfile);
    3700           3 :     GInt32 nEmbedLen = CPLBase64DecodeInPlace((GByte *)pEmbedBuffer);
    3701           3 :     char *pEmbedPtr = pEmbedBuffer;
    3702           3 :     char const *const paHeader = "ICC_PROFILE";
    3703           3 :     int nSegments = (nEmbedLen + 65518) / 65519;
    3704           3 :     int nSegmentID = 1;
    3705             : 
    3706          10 :     while (nEmbedLen != 0)
    3707             :     {
    3708             :         // 65535 - 16 bytes for header = 65519
    3709           7 :         const int nChunkLen = (nEmbedLen > 65519) ? 65519 : nEmbedLen;
    3710           7 :         nEmbedLen -= nChunkLen;
    3711             : 
    3712             :         // Write marker and length.
    3713           7 :         p_jpeg_write_m_header(pInfo, JPEG_APP0 + 2,
    3714           7 :                               static_cast<unsigned int>(nChunkLen + 14));
    3715             : 
    3716             :         // Write identifier.
    3717          91 :         for (int i = 0; i < 12; i++)
    3718          84 :             p_jpeg_write_m_byte(pInfo, paHeader[i]);
    3719             : 
    3720             :         // Write ID and max ID.
    3721           7 :         p_jpeg_write_m_byte(pInfo, nSegmentID);
    3722           7 :         p_jpeg_write_m_byte(pInfo, nSegments);
    3723             : 
    3724             :         // Write ICC Profile.
    3725      304295 :         for (int i = 0; i < nChunkLen; i++)
    3726      304288 :             p_jpeg_write_m_byte(pInfo, pEmbedPtr[i]);
    3727             : 
    3728           7 :         nSegmentID++;
    3729             : 
    3730           7 :         pEmbedPtr += nChunkLen;
    3731             :     }
    3732             : 
    3733           3 :     CPLFree(pEmbedBuffer);
    3734             : }
    3735             : 
    3736             : /************************************************************************/
    3737             : /*                           JPGAppendMask()                            */
    3738             : /*                                                                      */
    3739             : /*      This function appends a zlib compressed bitmask to a JPEG       */
    3740             : /*      file (or really any file) pulled from an existing mask band.    */
    3741             : /************************************************************************/
    3742             : 
    3743             : // MSVC does not know that memset() has initialized sStream.
    3744             : #ifdef _MSC_VER
    3745             : #pragma warning(disable : 4701)
    3746             : #endif
    3747             : 
    3748           8 : CPLErr JPGAppendMask(const char *pszJPGFilename, GDALRasterBand *poMask,
    3749             :                      GDALProgressFunc pfnProgress, void *pProgressData)
    3750             : 
    3751             : {
    3752           8 :     const int nXSize = poMask->GetXSize();
    3753           8 :     const int nYSize = poMask->GetYSize();
    3754           8 :     const int nBitBufSize = nYSize * ((nXSize + 7) / 8);
    3755           8 :     CPLErr eErr = CE_None;
    3756             : 
    3757             :     // Allocate uncompressed bit buffer.
    3758             :     GByte *pabyBitBuf =
    3759           8 :         static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, nBitBufSize));
    3760             : 
    3761           8 :     GByte *pabyMaskLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
    3762           8 :     if (pabyBitBuf == nullptr || pabyMaskLine == nullptr)
    3763             :     {
    3764           0 :         eErr = CE_Failure;
    3765             :     }
    3766             : 
    3767             :     // No reason to set it to MSB, unless for debugging purposes
    3768             :     // to be able to generate a unusual LSB ordered mask (#5102).
    3769             :     const char *pszJPEGMaskBitOrder =
    3770           8 :         CPLGetConfigOption("JPEG_WRITE_MASK_BIT_ORDER", "LSB");
    3771           8 :     const bool bMaskLSBOrder = EQUAL(pszJPEGMaskBitOrder, "LSB");
    3772             : 
    3773             :     // Set bit buffer from mask band, scanline by scanline.
    3774           8 :     GUInt32 iBit = 0;
    3775         688 :     for (int iY = 0; eErr == CE_None && iY < nYSize; iY++)
    3776             :     {
    3777         680 :         eErr = poMask->RasterIO(GF_Read, 0, iY, nXSize, 1, pabyMaskLine, nXSize,
    3778             :                                 1, GDT_Byte, 0, 0, nullptr);
    3779         680 :         if (eErr != CE_None)
    3780           0 :             break;
    3781             : 
    3782         680 :         if (bMaskLSBOrder)
    3783             :         {
    3784      265051 :             for (int iX = 0; iX < nXSize; iX++)
    3785             :             {
    3786      264453 :                 if (pabyMaskLine[iX] != 0)
    3787      198418 :                     pabyBitBuf[iBit >> 3] |= (0x1 << (iBit & 7));
    3788             : 
    3789      264453 :                 iBit++;
    3790             :             }
    3791             :         }
    3792             :         else
    3793             :         {
    3794        2331 :             for (int iX = 0; iX < nXSize; iX++)
    3795             :             {
    3796        2249 :                 if (pabyMaskLine[iX] != 0)
    3797         784 :                     pabyBitBuf[iBit >> 3] |= (0x1 << (7 - (iBit & 7)));
    3798             : 
    3799        2249 :                 iBit++;
    3800             :             }
    3801             :         }
    3802             : 
    3803         848 :         if (pfnProgress != nullptr &&
    3804         168 :             !pfnProgress((iY + 1) / static_cast<double>(nYSize), nullptr,
    3805             :                          pProgressData))
    3806             :         {
    3807           0 :             eErr = CE_Failure;
    3808           0 :             CPLError(CE_Failure, CPLE_UserInterrupt,
    3809             :                      "User terminated JPGAppendMask()");
    3810             :         }
    3811             :     }
    3812             : 
    3813           8 :     CPLFree(pabyMaskLine);
    3814             : 
    3815             :     // Compress.
    3816           8 :     GByte *pabyCMask = nullptr;
    3817             : 
    3818           8 :     if (eErr == CE_None)
    3819             :     {
    3820           8 :         pabyCMask = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBitBufSize + 30));
    3821           8 :         if (pabyCMask == nullptr)
    3822             :         {
    3823           0 :             eErr = CE_Failure;
    3824             :         }
    3825             :     }
    3826             : 
    3827           8 :     size_t nTotalOut = 0;
    3828           8 :     if (eErr == CE_None)
    3829             :     {
    3830          16 :         if (CPLZLibDeflate(pabyBitBuf, nBitBufSize, -1, pabyCMask,
    3831           8 :                            nBitBufSize + 30, &nTotalOut) == nullptr)
    3832             :         {
    3833           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3834             :                      "Deflate compression of jpeg bit mask failed.");
    3835           0 :             eErr = CE_Failure;
    3836             :         }
    3837             :     }
    3838             : 
    3839             :     // Write to disk, along with image file size.
    3840           8 :     if (eErr == CE_None)
    3841             :     {
    3842           8 :         VSILFILE *fpOut = VSIFOpenL(pszJPGFilename, "r+");
    3843           8 :         if (fpOut == nullptr)
    3844             :         {
    3845           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    3846             :                      "Failed to open jpeg to append bitmask.");
    3847           0 :             eErr = CE_Failure;
    3848             :         }
    3849             :         else
    3850             :         {
    3851           8 :             VSIFSeekL(fpOut, 0, SEEK_END);
    3852             : 
    3853           8 :             GUInt32 nImageSize = static_cast<GUInt32>(VSIFTellL(fpOut));
    3854           8 :             CPL_LSBPTR32(&nImageSize);
    3855             : 
    3856           8 :             if (VSIFWriteL(pabyCMask, 1, nTotalOut, fpOut) != nTotalOut)
    3857             :             {
    3858           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    3859             :                          "Failure writing compressed bitmask.\n%s",
    3860           0 :                          VSIStrerror(errno));
    3861           0 :                 eErr = CE_Failure;
    3862             :             }
    3863             :             else
    3864             :             {
    3865           8 :                 VSIFWriteL(&nImageSize, 4, 1, fpOut);
    3866             :             }
    3867             : 
    3868           8 :             VSIFCloseL(fpOut);
    3869             :         }
    3870             :     }
    3871             : 
    3872           8 :     CPLFree(pabyBitBuf);
    3873           8 :     CPLFree(pabyCMask);
    3874             : 
    3875           8 :     return eErr;
    3876             : }
    3877             : 
    3878             : /************************************************************************/
    3879             : /*                             JPGAddEXIF()                             */
    3880             : /************************************************************************/
    3881             : 
    3882         247 : void JPGAddEXIF(GDALDataType eWorkDT, GDALDataset *poSrcDS, char **papszOptions,
    3883             :                 void *cinfo, my_jpeg_write_m_header p_jpeg_write_m_header,
    3884             :                 my_jpeg_write_m_byte p_jpeg_write_m_byte,
    3885             :                 GDALDataset *(pCreateCopy)(const char *, GDALDataset *, int,
    3886             :                                            char **,
    3887             :                                            GDALProgressFunc pfnProgress,
    3888             :                                            void *pProgressData))
    3889             : {
    3890         247 :     const int nBands = poSrcDS->GetRasterCount();
    3891         247 :     const int nXSize = poSrcDS->GetRasterXSize();
    3892         247 :     const int nYSize = poSrcDS->GetRasterYSize();
    3893             : 
    3894             :     bool bGenerateEXIFThumbnail =
    3895         247 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "EXIF_THUMBNAIL", "NO"));
    3896             :     const char *pszThumbnailWidth =
    3897         247 :         CSLFetchNameValue(papszOptions, "THUMBNAIL_WIDTH");
    3898             :     const char *pszThumbnailHeight =
    3899         247 :         CSLFetchNameValue(papszOptions, "THUMBNAIL_HEIGHT");
    3900         247 :     int nOvrWidth = 0;
    3901         247 :     int nOvrHeight = 0;
    3902         247 :     if (pszThumbnailWidth == nullptr && pszThumbnailHeight == nullptr)
    3903             :     {
    3904         243 :         if (nXSize >= nYSize)
    3905             :         {
    3906         236 :             nOvrWidth = 128;
    3907             :         }
    3908             :         else
    3909             :         {
    3910           7 :             nOvrHeight = 128;
    3911             :         }
    3912             :     }
    3913         247 :     if (pszThumbnailWidth != nullptr)
    3914             :     {
    3915           3 :         nOvrWidth = atoi(pszThumbnailWidth);
    3916           3 :         if (nOvrWidth < 32)
    3917           0 :             nOvrWidth = 32;
    3918           3 :         if (nOvrWidth > 1024)
    3919           0 :             nOvrWidth = 1024;
    3920             :     }
    3921         247 :     if (pszThumbnailHeight != nullptr)
    3922             :     {
    3923           3 :         nOvrHeight = atoi(pszThumbnailHeight);
    3924           3 :         if (nOvrHeight < 32)
    3925           0 :             nOvrHeight = 32;
    3926           3 :         if (nOvrHeight > 1024)
    3927           0 :             nOvrHeight = 1024;
    3928             :     }
    3929         247 :     if (nOvrWidth == 0)
    3930             :     {
    3931           8 :         nOvrWidth = static_cast<int>(static_cast<GIntBig>(nOvrHeight) * nXSize /
    3932           8 :                                      nYSize);
    3933           8 :         if (nOvrWidth == 0)
    3934           0 :             nOvrWidth = 1;
    3935             :     }
    3936         239 :     else if (nOvrHeight == 0)
    3937             :     {
    3938         237 :         nOvrHeight =
    3939         237 :             static_cast<int>(static_cast<GIntBig>(nOvrWidth) * nYSize / nXSize);
    3940         237 :         if (nOvrHeight == 0)
    3941           0 :             nOvrHeight = 1;
    3942             :     }
    3943             : 
    3944         247 :     vsi_l_offset nJPEGIfByteCount = 0;
    3945         247 :     GByte *pabyOvr = nullptr;
    3946             : 
    3947         247 :     if (bGenerateEXIFThumbnail && nXSize > nOvrWidth && nYSize > nOvrHeight)
    3948             :     {
    3949           6 :         GDALDataset *poMemDS = MEMDataset::Create("", nOvrWidth, nOvrHeight,
    3950             :                                                   nBands, eWorkDT, nullptr);
    3951             :         GDALRasterBand **papoSrcBands = static_cast<GDALRasterBand **>(
    3952           6 :             CPLMalloc(nBands * sizeof(GDALRasterBand *)));
    3953             :         GDALRasterBand ***papapoOverviewBands = static_cast<GDALRasterBand ***>(
    3954           6 :             CPLMalloc(nBands * sizeof(GDALRasterBand **)));
    3955          14 :         for (int i = 0; i < nBands; i++)
    3956             :         {
    3957           8 :             papoSrcBands[i] = poSrcDS->GetRasterBand(i + 1);
    3958          16 :             papapoOverviewBands[i] = static_cast<GDALRasterBand **>(
    3959           8 :                 CPLMalloc(sizeof(GDALRasterBand *)));
    3960           8 :             papapoOverviewBands[i][0] = poMemDS->GetRasterBand(i + 1);
    3961             :         }
    3962           6 :         CPLErr eErr = GDALRegenerateOverviewsMultiBand(
    3963             :             nBands, papoSrcBands, 1, papapoOverviewBands, "AVERAGE", nullptr,
    3964             :             nullptr,
    3965             :             /* papszOptions = */ nullptr);
    3966           6 :         CPLFree(papoSrcBands);
    3967          14 :         for (int i = 0; i < nBands; i++)
    3968             :         {
    3969           8 :             CPLFree(papapoOverviewBands[i]);
    3970             :         }
    3971           6 :         CPLFree(papapoOverviewBands);
    3972             : 
    3973           6 :         if (eErr != CE_None)
    3974             :         {
    3975           0 :             GDALClose(poMemDS);
    3976           0 :             return;
    3977             :         }
    3978             : 
    3979          12 :         CPLString osTmpFile(CPLSPrintf("/vsimem/ovrjpg%p", poMemDS));
    3980           6 :         GDALDataset *poOutDS = pCreateCopy(osTmpFile, poMemDS, 0, nullptr,
    3981             :                                            GDALDummyProgress, nullptr);
    3982           6 :         const bool bExifOverviewSuccess = poOutDS != nullptr;
    3983           6 :         delete poOutDS;
    3984           6 :         poOutDS = nullptr;
    3985           6 :         GDALClose(poMemDS);
    3986           6 :         if (bExifOverviewSuccess)
    3987           6 :             pabyOvr = VSIGetMemFileBuffer(osTmpFile, &nJPEGIfByteCount, TRUE);
    3988           6 :         VSIUnlink(osTmpFile);
    3989             : 
    3990             :         // cppcheck-suppress knownConditionTrueFalse
    3991           6 :         if (pabyOvr == nullptr)
    3992             :         {
    3993           0 :             nJPEGIfByteCount = 0;
    3994           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    3995             :                      "Could not generate EXIF overview");
    3996             :         }
    3997             :     }
    3998             : 
    3999             :     GUInt32 nMarkerSize;
    4000             :     const bool bWriteExifMetadata =
    4001         247 :         CPLFetchBool(papszOptions, "WRITE_EXIF_METADATA", true);
    4002             : 
    4003             :     GByte *pabyEXIF =
    4004         247 :         EXIFCreate(bWriteExifMetadata ? poSrcDS->GetMetadata() : nullptr,
    4005             :                    pabyOvr, static_cast<GUInt32>(nJPEGIfByteCount), nOvrWidth,
    4006             :                    nOvrHeight, &nMarkerSize);
    4007         247 :     if (pabyEXIF)
    4008             :     {
    4009          22 :         p_jpeg_write_m_header(cinfo, JPEG_APP0 + 1, nMarkerSize);
    4010        4783 :         for (GUInt32 i = 0; i < nMarkerSize; i++)
    4011             :         {
    4012        4761 :             p_jpeg_write_m_byte(cinfo, pabyEXIF[i]);
    4013             :         }
    4014          22 :         VSIFree(pabyEXIF);
    4015             :     }
    4016         247 :     CPLFree(pabyOvr);
    4017             : }
    4018             : 
    4019             : #endif  // !defined(JPGDataset)
    4020             : 
    4021             : /************************************************************************/
    4022             : /*                              CreateCopy()                            */
    4023             : /************************************************************************/
    4024             : 
    4025         273 : GDALDataset *JPGDataset::CreateCopy(const char *pszFilename,
    4026             :                                     GDALDataset *poSrcDS, int bStrict,
    4027             :                                     char **papszOptions,
    4028             :                                     GDALProgressFunc pfnProgress,
    4029             :                                     void *pProgressData)
    4030             : 
    4031             : {
    4032         273 :     const int nBands = poSrcDS->GetRasterCount();
    4033             : 
    4034             :     const char *pszLossLessCopy =
    4035         273 :         CSLFetchNameValueDef(papszOptions, "LOSSLESS_COPY", "AUTO");
    4036         273 :     if (EQUAL(pszLossLessCopy, "AUTO") || CPLTestBool(pszLossLessCopy))
    4037             :     {
    4038         273 :         void *pJPEGContent = nullptr;
    4039         273 :         size_t nJPEGContent = 0;
    4040         273 :         if (poSrcDS->ReadCompressedData("JPEG", 0, 0, poSrcDS->GetRasterXSize(),
    4041             :                                         poSrcDS->GetRasterYSize(), nBands,
    4042             :                                         nullptr, &pJPEGContent, &nJPEGContent,
    4043         555 :                                         nullptr) == CE_None &&
    4044         282 :             GDALGetCompressionFormatForJPEG(pJPEGContent, nJPEGContent)
    4045           9 :                     .find(";colorspace=RGBA") == std::string::npos)
    4046             :         {
    4047           9 :             if (!pfnProgress(0.0, nullptr, pProgressData))
    4048           9 :                 return nullptr;
    4049             : 
    4050           9 :             CPLDebug("JPEG", "Lossless copy from source dataset");
    4051           9 :             std::vector<GByte> abyJPEG;
    4052             :             try
    4053             :             {
    4054           9 :                 abyJPEG.assign(static_cast<const GByte *>(pJPEGContent),
    4055           9 :                                static_cast<const GByte *>(pJPEGContent) +
    4056           9 :                                    nJPEGContent);
    4057             : 
    4058             :                 const bool bWriteExifMetadata =
    4059           9 :                     CPLFetchBool(papszOptions, "WRITE_EXIF_METADATA", true);
    4060           9 :                 if (bWriteExifMetadata)
    4061             :                 {
    4062           9 :                     char **papszEXIF_MD = poSrcDS->GetMetadata("EXIF");
    4063           9 :                     if (papszEXIF_MD == nullptr)
    4064             :                     {
    4065           9 :                         papszEXIF_MD = poSrcDS->GetMetadata();
    4066             :                     }
    4067           9 :                     GUInt32 nEXIFContentSize = 0;
    4068           9 :                     GByte *pabyEXIF = EXIFCreate(papszEXIF_MD, nullptr, 0, 0, 0,
    4069             :                                                  &nEXIFContentSize);
    4070           9 :                     if (nEXIFContentSize > 0 && nEXIFContentSize + 2 <= 65535U)
    4071             :                     {
    4072           1 :                         size_t nChunkLoc = 2;
    4073           1 :                         size_t nInsertPos = 0;
    4074           1 :                         constexpr GByte JFIF_SIGNATURE[] = {'J', 'F', 'I', 'F',
    4075             :                                                             '\0'};
    4076           1 :                         constexpr GByte EXIF_SIGNATURE[] = {'E', 'x',  'i',
    4077             :                                                             'f', '\0', '\0'};
    4078           9 :                         while (nChunkLoc + 4 <= abyJPEG.size())
    4079             :                         {
    4080           9 :                             if (abyJPEG[nChunkLoc + 0] != 0xFF)
    4081           0 :                                 break;
    4082           9 :                             if (abyJPEG[nChunkLoc + 1] == 0xDA)
    4083             :                             {
    4084           1 :                                 if (nInsertPos == 0)
    4085           0 :                                     nInsertPos = nChunkLoc;
    4086           1 :                                 break;
    4087             :                             }
    4088             :                             const int nChunkLength =
    4089           8 :                                 abyJPEG[nChunkLoc + 2] * 256 +
    4090           8 :                                 abyJPEG[nChunkLoc + 3];
    4091           8 :                             if (nChunkLength < 2)
    4092           0 :                                 break;
    4093           9 :                             if (abyJPEG[nChunkLoc + 1] == 0xE0 &&
    4094           1 :                                 nChunkLoc + 4 + sizeof(JFIF_SIGNATURE) <=
    4095           9 :                                     abyJPEG.size() &&
    4096           1 :                                 memcmp(abyJPEG.data() + nChunkLoc + 4,
    4097             :                                        JFIF_SIGNATURE,
    4098             :                                        sizeof(JFIF_SIGNATURE)) == 0)
    4099             :                             {
    4100           1 :                                 if (nInsertPos == 0)
    4101           1 :                                     nInsertPos = nChunkLoc + 2 + nChunkLength;
    4102             :                             }
    4103           7 :                             else if (abyJPEG[nChunkLoc + 1] == 0xE1 &&
    4104           0 :                                      nChunkLoc + 4 + sizeof(EXIF_SIGNATURE) <=
    4105           7 :                                          abyJPEG.size() &&
    4106           0 :                                      memcmp(abyJPEG.data() + nChunkLoc + 4,
    4107             :                                             EXIF_SIGNATURE,
    4108             :                                             sizeof(EXIF_SIGNATURE)) == 0)
    4109             :                             {
    4110           0 :                                 CPLDebug("JPEG",
    4111             :                                          "Remove existing EXIF from source "
    4112             :                                          "compressed data");
    4113           0 :                                 abyJPEG.erase(abyJPEG.begin() + nChunkLoc,
    4114           0 :                                               abyJPEG.begin() + nChunkLoc + 2 +
    4115           0 :                                                   nChunkLength);
    4116           0 :                                 continue;
    4117             :                             }
    4118           8 :                             nChunkLoc += 2 + nChunkLength;
    4119             :                         }
    4120           1 :                         if (nInsertPos > 0)
    4121             :                         {
    4122           2 :                             std::vector<GByte> abyNew;
    4123           1 :                             const size_t nMarkerSize = 2 + nEXIFContentSize;
    4124           1 :                             abyNew.reserve(abyJPEG.size() + 2 + nMarkerSize);
    4125           1 :                             abyNew.insert(abyNew.end(), abyJPEG.data(),
    4126           2 :                                           abyJPEG.data() + nInsertPos);
    4127           0 :                             abyNew.insert(abyNew.end(),
    4128           1 :                                           static_cast<GByte>(0xFF));
    4129           0 :                             abyNew.insert(abyNew.end(),
    4130           1 :                                           static_cast<GByte>(0xE1));
    4131           0 :                             abyNew.insert(abyNew.end(),
    4132           1 :                                           static_cast<GByte>(nMarkerSize >> 8));
    4133             :                             abyNew.insert(
    4134           0 :                                 abyNew.end(),
    4135           1 :                                 static_cast<GByte>(nMarkerSize & 0xFF));
    4136           0 :                             abyNew.insert(abyNew.end(), pabyEXIF,
    4137           1 :                                           pabyEXIF + nEXIFContentSize);
    4138           0 :                             abyNew.insert(abyNew.end(),
    4139           1 :                                           abyJPEG.data() + nInsertPos,
    4140           1 :                                           abyJPEG.data() + abyJPEG.size());
    4141           1 :                             abyJPEG = std::move(abyNew);
    4142             :                         }
    4143             :                     }
    4144           9 :                     VSIFree(pabyEXIF);
    4145             :                 }
    4146             : 
    4147             :                 const bool bWriteXMP =
    4148           9 :                     CPLFetchBool(papszOptions, "WRITE_XMP", true);
    4149             :                 char **papszXMP =
    4150           9 :                     bWriteXMP ? poSrcDS->GetMetadata("xml:XMP") : nullptr;
    4151           9 :                 if (papszXMP && papszXMP[0])
    4152             :                 {
    4153           1 :                     size_t nChunkLoc = 2;
    4154           1 :                     size_t nInsertPos = 0;
    4155           1 :                     constexpr GByte JFIF_SIGNATURE[] = {'J', 'F', 'I', 'F',
    4156             :                                                         '\0'};
    4157           1 :                     constexpr const char APP1_XMP_SIGNATURE[] =
    4158             :                         "http://ns.adobe.com/xap/1.0/";
    4159           6 :                     while (nChunkLoc + 4 <= abyJPEG.size())
    4160             :                     {
    4161           6 :                         if (abyJPEG[nChunkLoc + 0] != 0xFF)
    4162           0 :                             break;
    4163           6 :                         if (abyJPEG[nChunkLoc + 1] == 0xDA)
    4164             :                         {
    4165           1 :                             if (nInsertPos == 0)
    4166           0 :                                 nInsertPos = nChunkLoc;
    4167           1 :                             break;
    4168             :                         }
    4169           5 :                         const int nChunkLength = abyJPEG[nChunkLoc + 2] * 256 +
    4170           5 :                                                  abyJPEG[nChunkLoc + 3];
    4171           5 :                         if (nChunkLength < 2)
    4172           0 :                             break;
    4173           6 :                         if (abyJPEG[nChunkLoc + 1] == 0xE0 &&
    4174           1 :                             nChunkLoc + 4 + sizeof(JFIF_SIGNATURE) <=
    4175           6 :                                 abyJPEG.size() &&
    4176           1 :                             memcmp(abyJPEG.data() + nChunkLoc + 4,
    4177             :                                    JFIF_SIGNATURE, sizeof(JFIF_SIGNATURE)) == 0)
    4178             :                         {
    4179           1 :                             if (nInsertPos == 0)
    4180           1 :                                 nInsertPos = nChunkLoc + 2 + nChunkLength;
    4181             :                         }
    4182           4 :                         else if (abyJPEG[nChunkLoc + 1] == 0xE1 &&
    4183           0 :                                  nChunkLoc + 4 + sizeof(APP1_XMP_SIGNATURE) <=
    4184           4 :                                      abyJPEG.size() &&
    4185           0 :                                  memcmp(abyJPEG.data() + nChunkLoc + 4,
    4186             :                                         APP1_XMP_SIGNATURE,
    4187             :                                         sizeof(APP1_XMP_SIGNATURE)) == 0)
    4188             :                         {
    4189           0 :                             CPLDebug("JPEG", "Remove existing XMP from source "
    4190             :                                              "compressed data");
    4191           0 :                             abyJPEG.erase(abyJPEG.begin() + nChunkLoc,
    4192           0 :                                           abyJPEG.begin() + nChunkLoc + 2 +
    4193           0 :                                               nChunkLength);
    4194           0 :                             continue;
    4195             :                         }
    4196           5 :                         nChunkLoc += 2 + nChunkLength;
    4197             :                     }
    4198           1 :                     const size_t nMarkerSize =
    4199           1 :                         2 + sizeof(APP1_XMP_SIGNATURE) + strlen(papszXMP[0]);
    4200           1 :                     if (nInsertPos > 0 && nMarkerSize <= 65535U)
    4201             :                     {
    4202           2 :                         std::vector<GByte> abyNew;
    4203           1 :                         abyNew.reserve(abyJPEG.size() + 2 + nMarkerSize);
    4204           1 :                         abyNew.insert(abyNew.end(), abyJPEG.data(),
    4205           2 :                                       abyJPEG.data() + nInsertPos);
    4206           1 :                         abyNew.insert(abyNew.end(), static_cast<GByte>(0xFF));
    4207           1 :                         abyNew.insert(abyNew.end(), static_cast<GByte>(0xE1));
    4208           0 :                         abyNew.insert(abyNew.end(),
    4209           1 :                                       static_cast<GByte>(nMarkerSize >> 8));
    4210           0 :                         abyNew.insert(abyNew.end(),
    4211           1 :                                       static_cast<GByte>(nMarkerSize & 0xFF));
    4212           0 :                         abyNew.insert(abyNew.end(), APP1_XMP_SIGNATURE,
    4213             :                                       APP1_XMP_SIGNATURE +
    4214           1 :                                           sizeof(APP1_XMP_SIGNATURE));
    4215           0 :                         abyNew.insert(abyNew.end(), papszXMP[0],
    4216           1 :                                       papszXMP[0] + strlen(papszXMP[0]));
    4217           0 :                         abyNew.insert(abyNew.end(), abyJPEG.data() + nInsertPos,
    4218           1 :                                       abyJPEG.data() + abyJPEG.size());
    4219           1 :                         abyJPEG = std::move(abyNew);
    4220             :                     }
    4221             :                 }
    4222             :             }
    4223           0 :             catch (const std::exception &)
    4224             :             {
    4225           0 :                 abyJPEG.clear();
    4226             :             }
    4227           9 :             VSIFree(pJPEGContent);
    4228             : 
    4229           9 :             if (!abyJPEG.empty())
    4230             :             {
    4231           9 :                 VSILFILE *fpImage = VSIFOpenL(pszFilename, "wb");
    4232           9 :                 if (fpImage == nullptr)
    4233             :                 {
    4234           0 :                     CPLError(CE_Failure, CPLE_OpenFailed,
    4235             :                              "Unable to create jpeg file %s.", pszFilename);
    4236             : 
    4237           0 :                     return nullptr;
    4238             :                 }
    4239          18 :                 if (VSIFWriteL(abyJPEG.data(), 1, abyJPEG.size(), fpImage) !=
    4240           9 :                     abyJPEG.size())
    4241             :                 {
    4242           0 :                     CPLError(CE_Failure, CPLE_FileIO,
    4243           0 :                              "Failure writing data: %s", VSIStrerror(errno));
    4244           0 :                     VSIFCloseL(fpImage);
    4245           0 :                     return nullptr;
    4246             :                 }
    4247           9 :                 if (VSIFCloseL(fpImage) != 0)
    4248             :                 {
    4249           0 :                     CPLError(CE_Failure, CPLE_FileIO,
    4250           0 :                              "Failure writing data: %s", VSIStrerror(errno));
    4251           0 :                     return nullptr;
    4252             :                 }
    4253             : 
    4254           9 :                 pfnProgress(1.0, nullptr, pProgressData);
    4255             : 
    4256             :                 // Append masks to the jpeg file if necessary.
    4257           9 :                 const auto poLastSrcBand = poSrcDS->GetRasterBand(nBands);
    4258             :                 const bool bAppendMask =
    4259           9 :                     poLastSrcBand != nullptr &&
    4260          10 :                     poLastSrcBand->GetColorInterpretation() == GCI_AlphaBand &&
    4261           1 :                     CPLFetchBool(papszOptions, "INTERNAL_MASK", true);
    4262             : 
    4263           9 :                 if (bAppendMask)
    4264             :                 {
    4265           1 :                     CPLDebug("JPEG", "Appending Mask Bitmap");
    4266             : 
    4267           1 :                     CPLErr eErr = JPGAppendMask(pszFilename, poLastSrcBand,
    4268             :                                                 nullptr, nullptr);
    4269             : 
    4270           1 :                     if (eErr != CE_None)
    4271             :                     {
    4272           0 :                         VSIUnlink(pszFilename);
    4273           0 :                         return nullptr;
    4274             :                     }
    4275             :                 }
    4276             : 
    4277             :                 // Do we need a world file?
    4278           9 :                 if (CPLFetchBool(papszOptions, "WORLDFILE", false))
    4279             :                 {
    4280           0 :                     double adfGeoTransform[6] = {};
    4281             : 
    4282           0 :                     poSrcDS->GetGeoTransform(adfGeoTransform);
    4283           0 :                     GDALWriteWorldFile(pszFilename, "wld", adfGeoTransform);
    4284             :                 }
    4285             : 
    4286             :                 // Re-open dataset, and copy any auxiliary pam information.
    4287             : 
    4288             :                 // If writing to stdout, we can't reopen it, so return
    4289             :                 // a fake dataset to make the caller happy.
    4290           9 :                 if (CPLTestBool(
    4291             :                         CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", "YES")))
    4292             :                 {
    4293           9 :                     CPLPushErrorHandler(CPLQuietErrorHandler);
    4294             : 
    4295           9 :                     JPGDatasetOpenArgs sArgs;
    4296           9 :                     sArgs.pszFilename = pszFilename;
    4297           9 :                     sArgs.bDoPAMInitialize = true;
    4298           9 :                     sArgs.bUseInternalOverviews = true;
    4299             : 
    4300           9 :                     auto poDS = Open(&sArgs);
    4301           9 :                     CPLPopErrorHandler();
    4302           9 :                     if (poDS)
    4303             :                     {
    4304           8 :                         poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
    4305           8 :                         return poDS;
    4306             :                     }
    4307             : 
    4308           1 :                     CPLErrorReset();
    4309             :                 }
    4310             : 
    4311           1 :                 JPGDataset *poJPG_DS = new JPGDataset();
    4312           1 :                 poJPG_DS->nRasterXSize = poSrcDS->GetRasterXSize();
    4313           1 :                 poJPG_DS->nRasterYSize = poSrcDS->GetRasterYSize();
    4314           2 :                 for (int i = 0; i < nBands; i++)
    4315           1 :                     poJPG_DS->SetBand(i + 1, JPGCreateBand(poJPG_DS, i + 1));
    4316           1 :                 return poJPG_DS;
    4317             :             }
    4318             :         }
    4319             :     }
    4320             : 
    4321         264 :     if (!EQUAL(pszLossLessCopy, "AUTO") && CPLTestBool(pszLossLessCopy))
    4322             :     {
    4323           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4324             :                  "LOSSLESS_COPY=YES requested but not possible");
    4325           0 :         return nullptr;
    4326             :     }
    4327             : 
    4328             :     // Some some rudimentary checks.
    4329         264 :     if (nBands != 1 && nBands != 3 && nBands != 4)
    4330             :     {
    4331           3 :         CPLError(CE_Failure, CPLE_NotSupported,
    4332             :                  "JPEG driver doesn't support %d bands.  Must be 1 (grey), "
    4333             :                  "3 (RGB) or 4 bands (CMYK).\n",
    4334             :                  nBands);
    4335             : 
    4336           3 :         return nullptr;
    4337             :     }
    4338             : 
    4339         261 :     if (nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
    4340             :     {
    4341           0 :         CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported,
    4342             :                  "JPEG driver ignores color table. "
    4343             :                  "The source raster band will be considered as grey level.\n"
    4344             :                  "Consider using color table expansion "
    4345             :                  "(-expand option in gdal_translate)");
    4346           0 :         if (bStrict)
    4347           0 :             return nullptr;
    4348             :     }
    4349             : 
    4350         263 :     if (nBands == 4 &&
    4351           2 :         poSrcDS->GetRasterBand(1)->GetColorInterpretation() != GCI_CyanBand)
    4352             :     {
    4353           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    4354             :                  "4-band JPEGs will be interpreted on reading as in CMYK "
    4355             :                  "colorspace");
    4356             :     }
    4357             : 
    4358         261 :     VSILFILE *fpImage = nullptr;
    4359         261 :     GDALJPEGUserData sUserData;
    4360         261 :     sUserData.bNonFatalErrorEncountered = false;
    4361         261 :     GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    4362             : 
    4363             : #if defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12)
    4364         261 :     if (eDT != GDT_Byte && eDT != GDT_UInt16)
    4365             :     {
    4366           9 :         CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported,
    4367             :                  "JPEG driver doesn't support data type %s. "
    4368             :                  "Only eight and twelve bit bands supported.",
    4369             :                  GDALGetDataTypeName(
    4370             :                      poSrcDS->GetRasterBand(1)->GetRasterDataType()));
    4371             : 
    4372           9 :         if (bStrict)
    4373           9 :             return nullptr;
    4374             :     }
    4375             : 
    4376         252 :     if (eDT == GDT_UInt16 || eDT == GDT_Int16)
    4377             :     {
    4378             : #if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
    4379           1 :         return JPEGDataset12CreateCopy(pszFilename, poSrcDS, bStrict,
    4380             :                                        papszOptions, pfnProgress,
    4381           1 :                                        pProgressData);
    4382             : #else
    4383           1 :         eDT = GDT_UInt16;
    4384             : #endif  // defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
    4385             :     }
    4386             :     else
    4387             :     {
    4388         250 :         eDT = GDT_Byte;
    4389             :     }
    4390             : 
    4391             : #else   // !(defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12))
    4392             :     if (eDT != GDT_Byte)
    4393             :     {
    4394             :         CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported,
    4395             :                  "JPEG driver doesn't support data type %s. "
    4396             :                  "Only eight bit byte bands supported.\n",
    4397             :                  GDALGetDataTypeName(
    4398             :                      poSrcDS->GetRasterBand(1)->GetRasterDataType()));
    4399             : 
    4400             :         if (bStrict)
    4401             :             return nullptr;
    4402             :     }
    4403             : 
    4404             :     eDT = GDT_Byte;  // force to 8bit.
    4405             : #endif  // !(defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12))
    4406             : 
    4407             :     // What options has the caller selected?
    4408         251 :     int nQuality = 75;
    4409         251 :     const char *pszQuality = CSLFetchNameValue(papszOptions, "QUALITY");
    4410         251 :     if (pszQuality)
    4411             :     {
    4412         113 :         nQuality = atoi(pszQuality);
    4413         113 :         if (nQuality < 1 || nQuality > 100)
    4414             :         {
    4415           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    4416             :                      "QUALITY=%s is not a legal value in the range 1-100.",
    4417             :                      pszQuality);
    4418           0 :             return nullptr;
    4419             :         }
    4420             :     }
    4421             : 
    4422             :     // Create the dataset.
    4423         251 :     fpImage = VSIFOpenL(pszFilename, "wb");
    4424         251 :     if (fpImage == nullptr)
    4425             :     {
    4426           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
    4427             :                  "Unable to create jpeg file %s.\n", pszFilename);
    4428           3 :         return nullptr;
    4429             :     }
    4430             : 
    4431             :     struct jpeg_compress_struct sCInfo;
    4432             :     struct jpeg_error_mgr sJErr;
    4433             :     GByte *pabyScanline;
    4434             : 
    4435             :     // Does the source have a mask?  If so, we will append it to the
    4436             :     // jpeg file after the imagery.
    4437         248 :     const int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags();
    4438           7 :     const bool bAppendMask = !(nMaskFlags & GMF_ALL_VALID) &&
    4439         256 :                              (nBands == 1 || (nMaskFlags & GMF_PER_DATASET)) &&
    4440           7 :                              CPLFetchBool(papszOptions, "INTERNAL_MASK", true);
    4441             : 
    4442             :     // Nasty trick to avoid variable clobbering issues with setjmp/longjmp.
    4443         248 :     return CreateCopyStage2(pszFilename, poSrcDS, papszOptions, pfnProgress,
    4444             :                             pProgressData, fpImage, eDT, nQuality, bAppendMask,
    4445         248 :                             sUserData, sCInfo, sJErr, pabyScanline);
    4446             : }
    4447             : 
    4448         248 : GDALDataset *JPGDataset::CreateCopyStage2(
    4449             :     const char *pszFilename, GDALDataset *poSrcDS, char **papszOptions,
    4450             :     GDALProgressFunc pfnProgress, void *pProgressData, VSILFILE *fpImage,
    4451             :     GDALDataType eDT, int nQuality, bool bAppendMask,
    4452             :     GDALJPEGUserData &sUserData, struct jpeg_compress_struct &sCInfo,
    4453             :     struct jpeg_error_mgr &sJErr, GByte *&pabyScanline)
    4454             : 
    4455             : {
    4456         248 :     if (setjmp(sUserData.setjmp_buffer))
    4457             :     {
    4458           0 :         if (fpImage)
    4459           0 :             VSIFCloseL(fpImage);
    4460           0 :         return nullptr;
    4461             :     }
    4462             : 
    4463         248 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    4464           0 :         return nullptr;
    4465             : 
    4466             :     // Initialize JPG access to the file.
    4467         248 :     sCInfo.err = jpeg_std_error(&sJErr);
    4468         248 :     sJErr.error_exit = JPGDataset::ErrorExit;
    4469         248 :     sJErr.output_message = JPGDataset::OutputMessage;
    4470         248 :     sUserData.p_previous_emit_message = sJErr.emit_message;
    4471         248 :     sJErr.emit_message = JPGDataset::EmitMessage;
    4472         248 :     sCInfo.client_data = &sUserData;
    4473             : 
    4474         248 :     jpeg_create_compress(&sCInfo);
    4475         248 :     if (setjmp(sUserData.setjmp_buffer))
    4476             :     {
    4477           1 :         if (fpImage)
    4478           1 :             VSIFCloseL(fpImage);
    4479           1 :         jpeg_destroy_compress(&sCInfo);
    4480           1 :         return nullptr;
    4481             :     }
    4482             : 
    4483         248 :     jpeg_vsiio_dest(&sCInfo, fpImage);
    4484             : 
    4485         248 :     const int nXSize = poSrcDS->GetRasterXSize();
    4486         248 :     const int nYSize = poSrcDS->GetRasterYSize();
    4487         248 :     const int nBands = poSrcDS->GetRasterCount();
    4488         248 :     sCInfo.image_width = nXSize;
    4489         248 :     sCInfo.image_height = nYSize;
    4490         248 :     sCInfo.input_components = nBands;
    4491             : 
    4492         248 :     if (nBands == 3)
    4493         156 :         sCInfo.in_color_space = JCS_RGB;
    4494          92 :     else if (nBands == 1)
    4495          90 :         sCInfo.in_color_space = JCS_GRAYSCALE;
    4496             :     else
    4497           2 :         sCInfo.in_color_space = JCS_UNKNOWN;
    4498             : 
    4499         248 :     jpeg_set_defaults(&sCInfo);
    4500             : 
    4501             :     // libjpeg turbo 1.5.2 honours max_memory_to_use, but has no backing
    4502             :     // store implementation, so better not set max_memory_to_use ourselves.
    4503             :     // See https://github.com/libjpeg-turbo/libjpeg-turbo/issues/162
    4504         248 :     if (sCInfo.mem->max_memory_to_use > 0)
    4505             :     {
    4506             :         // This is to address bug related in ticket #1795.
    4507           0 :         if (CPLGetConfigOption("JPEGMEM", nullptr) == nullptr)
    4508             :         {
    4509             :             // If the user doesn't provide a value for JPEGMEM, we want to be
    4510             :             // sure that at least 500 MB will be used before creating the
    4511             :             // temporary file.
    4512           0 :             const long nMinMemory = 500 * 1024 * 1024;
    4513           0 :             sCInfo.mem->max_memory_to_use =
    4514           0 :                 std::max(sCInfo.mem->max_memory_to_use, nMinMemory);
    4515             :         }
    4516             :     }
    4517             : 
    4518         248 :     if (eDT == GDT_UInt16)
    4519             :     {
    4520           1 :         sCInfo.data_precision = 12;
    4521             :     }
    4522             :     else
    4523             :     {
    4524         247 :         sCInfo.data_precision = 8;
    4525             :     }
    4526             : 
    4527         248 :     const char *pszVal = CSLFetchNameValue(papszOptions, "ARITHMETIC");
    4528         248 :     if (pszVal)
    4529           1 :         sCInfo.arith_code = CPLTestBool(pszVal);
    4530             : 
    4531             :     // Optimized Huffman coding. Supposedly slower according to libjpeg doc
    4532             :     // but no longer significant with today computer standards.
    4533         248 :     if (!sCInfo.arith_code)
    4534         247 :         sCInfo.optimize_coding = TRUE;
    4535             : 
    4536             : #if JPEG_LIB_VERSION_MAJOR >= 8 &&                                             \
    4537             :     (JPEG_LIB_VERSION_MAJOR > 8 || JPEG_LIB_VERSION_MINOR >= 3)
    4538             :     pszVal = CSLFetchNameValue(papszOptions, "BLOCK");
    4539             :     if (pszVal)
    4540             :         sCInfo.block_size = atoi(pszVal);
    4541             : #endif
    4542             : 
    4543             : #if JPEG_LIB_VERSION_MAJOR >= 9
    4544             :     pszVal = CSLFetchNameValue(papszOptions, "COLOR_TRANSFORM");
    4545             :     if (pszVal)
    4546             :     {
    4547             :         sCInfo.color_transform =
    4548             :             EQUAL(pszVal, "RGB1") ? JCT_SUBTRACT_GREEN : JCT_NONE;
    4549             :         jpeg_set_colorspace(&sCInfo, JCS_RGB);
    4550             :     }
    4551             :     else
    4552             : #endif
    4553             : 
    4554             :         // Mostly for debugging purposes.
    4555         404 :         if (nBands == 3 &&
    4556         156 :             CPLTestBool(CPLGetConfigOption("JPEG_WRITE_RGB", "NO")))
    4557             :         {
    4558           0 :             jpeg_set_colorspace(&sCInfo, JCS_RGB);
    4559             :         }
    4560             : 
    4561             : #ifdef JPEG_LIB_MK1
    4562             :     sCInfo.bits_in_jsample = sCInfo.data_precision;
    4563             :     // Always force to 16 bit for JPEG_LIB_MK1
    4564             :     const GDALDataType eWorkDT = GDT_UInt16;
    4565             : #else
    4566         248 :     const GDALDataType eWorkDT = eDT;
    4567             : #endif
    4568             : 
    4569         248 :     jpeg_set_quality(&sCInfo, nQuality, TRUE);
    4570             : 
    4571         248 :     const bool bProgressive = CPLFetchBool(papszOptions, "PROGRESSIVE", false);
    4572         248 :     if (bProgressive)
    4573           3 :         jpeg_simple_progression(&sCInfo);
    4574             : 
    4575         248 :     jpeg_start_compress(&sCInfo, TRUE);
    4576             : 
    4577         247 :     JPGAddEXIF(eWorkDT, poSrcDS, papszOptions, &sCInfo,
    4578             :                (my_jpeg_write_m_header)jpeg_write_m_header,
    4579             :                (my_jpeg_write_m_byte)jpeg_write_m_byte, CreateCopy);
    4580             : 
    4581             :     // Add comment if available.
    4582         247 :     const char *pszComment = CSLFetchNameValue(papszOptions, "COMMENT");
    4583         247 :     if (pszComment)
    4584           3 :         jpeg_write_marker(&sCInfo, JPEG_COM,
    4585             :                           reinterpret_cast<const JOCTET *>(pszComment),
    4586           3 :                           static_cast<unsigned int>(strlen(pszComment)));
    4587             : 
    4588             :     // Save ICC profile if available.
    4589             :     const char *pszICCProfile =
    4590         247 :         CSLFetchNameValue(papszOptions, "SOURCE_ICC_PROFILE");
    4591         247 :     if (pszICCProfile == nullptr)
    4592             :         pszICCProfile =
    4593         246 :             poSrcDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE");
    4594             : 
    4595         247 :     if (pszICCProfile != nullptr)
    4596           3 :         JPGAddICCProfile(&sCInfo, pszICCProfile,
    4597             :                          (my_jpeg_write_m_header)jpeg_write_m_header,
    4598             :                          (my_jpeg_write_m_byte)jpeg_write_m_byte);
    4599             : 
    4600             :     // Loop over image, copying image data.
    4601         247 :     const int nWorkDTSize = GDALGetDataTypeSizeBytes(eWorkDT);
    4602         247 :     pabyScanline = static_cast<GByte *>(
    4603         247 :         CPLMalloc(cpl::fits_on<int>(nBands * nXSize * nWorkDTSize)));
    4604             : 
    4605         247 :     if (setjmp(sUserData.setjmp_buffer))
    4606             :     {
    4607          10 :         VSIFCloseL(fpImage);
    4608          10 :         CPLFree(pabyScanline);
    4609          10 :         jpeg_destroy_compress(&sCInfo);
    4610          10 :         return nullptr;
    4611             :     }
    4612             : 
    4613         247 :     CPLErr eErr = CE_None;
    4614         247 :     bool bClipWarn = false;
    4615       35994 :     for (int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++)
    4616             :     {
    4617      142988 :         eErr = poSrcDS->RasterIO(
    4618             :             GF_Read, 0, iLine, nXSize, 1, pabyScanline, nXSize, 1, eWorkDT,
    4619       35747 :             nBands, nullptr, cpl::fits_on<int>(nBands * nWorkDTSize),
    4620       35747 :             cpl::fits_on<int>(nBands * nXSize * nWorkDTSize), nWorkDTSize,
    4621             :             nullptr);
    4622             : 
    4623             :         // Clamp 16bit values to 12bit.
    4624       35747 :         if (nWorkDTSize == 2)
    4625             :         {
    4626          10 :             GUInt16 *panScanline = reinterpret_cast<GUInt16 *>(pabyScanline);
    4627             : 
    4628         110 :             for (int iPixel = 0; iPixel < nXSize * nBands; iPixel++)
    4629             :             {
    4630         100 :                 if (panScanline[iPixel] > 4095)
    4631             :                 {
    4632           0 :                     panScanline[iPixel] = 4095;
    4633           0 :                     if (!bClipWarn)
    4634             :                     {
    4635           0 :                         bClipWarn = true;
    4636           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    4637             :                                  "One or more pixels clipped to fit "
    4638             :                                  "12bit domain for jpeg output.");
    4639             :                     }
    4640             :                 }
    4641             :             }
    4642             :         }
    4643             : 
    4644       35747 :         GDAL_JSAMPLE *ppSamples =
    4645       35747 :             reinterpret_cast<GDAL_JSAMPLE *>(pabyScanline);
    4646             : 
    4647       35747 :         if (eErr == CE_None)
    4648             :         {
    4649             : #if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
    4650             :             jpeg12_write_scanlines(&sCInfo, &ppSamples, 1);
    4651             : #else
    4652       35747 :             jpeg_write_scanlines(&sCInfo, &ppSamples, 1);
    4653             : #endif
    4654             :         }
    4655       71494 :         if (eErr == CE_None &&
    4656       71494 :             !pfnProgress((iLine + 1) / ((bAppendMask ? 2 : 1) *
    4657       35747 :                                         static_cast<double>(nYSize)),
    4658             :                          nullptr, pProgressData))
    4659             :         {
    4660           1 :             eErr = CE_Failure;
    4661           1 :             CPLError(CE_Failure, CPLE_UserInterrupt,
    4662             :                      "User terminated CreateCopy()");
    4663             :         }
    4664             :     }
    4665             : 
    4666             :     // Cleanup and close.
    4667         247 :     if (eErr == CE_None)
    4668         246 :         jpeg_finish_compress(&sCInfo);
    4669         237 :     jpeg_destroy_compress(&sCInfo);
    4670             : 
    4671             :     // Free scanline and image after jpeg_finish_compress since this could
    4672             :     // cause a longjmp to occur.
    4673         237 :     CPLFree(pabyScanline);
    4674             : 
    4675         237 :     VSIFCloseL(fpImage);
    4676             : 
    4677         237 :     if (eErr != CE_None)
    4678             :     {
    4679           1 :         VSIUnlink(pszFilename);
    4680           1 :         return nullptr;
    4681             :     }
    4682             : 
    4683             :     // Append masks to the jpeg file if necessary.
    4684         236 :     int nCloneFlags = GCIF_PAM_DEFAULT & ~GCIF_METADATA;
    4685         236 :     if (bAppendMask)
    4686             :     {
    4687           7 :         CPLDebug("JPEG", "Appending Mask Bitmap");
    4688             : 
    4689             :         void *pScaledData =
    4690           7 :             GDALCreateScaledProgress(0.5, 1, pfnProgress, pProgressData);
    4691             :         eErr =
    4692           7 :             JPGAppendMask(pszFilename, poSrcDS->GetRasterBand(1)->GetMaskBand(),
    4693             :                           GDALScaledProgress, pScaledData);
    4694           7 :         GDALDestroyScaledProgress(pScaledData);
    4695           7 :         nCloneFlags &= (~GCIF_MASK);
    4696             : 
    4697           7 :         if (eErr != CE_None)
    4698             :         {
    4699           0 :             VSIUnlink(pszFilename);
    4700           0 :             return nullptr;
    4701             :         }
    4702             :     }
    4703             : 
    4704             :     // Do we need a world file?
    4705         236 :     if (CPLFetchBool(papszOptions, "WORLDFILE", false))
    4706             :     {
    4707           1 :         double adfGeoTransform[6] = {};
    4708             : 
    4709           1 :         poSrcDS->GetGeoTransform(adfGeoTransform);
    4710           1 :         GDALWriteWorldFile(pszFilename, "wld", adfGeoTransform);
    4711             :     }
    4712             : 
    4713             :     // Re-open dataset, and copy any auxiliary pam information.
    4714             : 
    4715             :     // If writing to stdout, we can't reopen it, so return
    4716             :     // a fake dataset to make the caller happy.
    4717         236 :     if (CPLTestBool(CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", "YES")))
    4718             :     {
    4719         173 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    4720             : 
    4721         173 :         JPGDatasetOpenArgs sArgs;
    4722         173 :         sArgs.pszFilename = pszFilename;
    4723         173 :         sArgs.bDoPAMInitialize = true;
    4724         173 :         sArgs.bUseInternalOverviews = true;
    4725             : 
    4726         173 :         auto poDS = Open(&sArgs);
    4727         173 :         CPLPopErrorHandler();
    4728         173 :         if (poDS)
    4729             :         {
    4730         172 :             poDS->CloneInfo(poSrcDS, nCloneFlags);
    4731             : 
    4732             :             char **papszExcludedDomains =
    4733         172 :                 CSLAddString(nullptr, "COLOR_PROFILE");
    4734         172 :             char **papszMD = poSrcDS->GetMetadata();
    4735         172 :             bool bOnlyEXIF = true;
    4736         209 :             for (char **papszIter = papszMD; papszIter && *papszIter;
    4737             :                  ++papszIter)
    4738             :             {
    4739          55 :                 if (!STARTS_WITH_CI(*papszIter, "EXIF_"))
    4740             :                 {
    4741          18 :                     bOnlyEXIF = false;
    4742          18 :                     break;
    4743             :                 }
    4744             :             }
    4745         172 :             if (bOnlyEXIF)
    4746         154 :                 papszExcludedDomains = CSLAddString(papszExcludedDomains, "");
    4747         172 :             GDALDriver::DefaultCopyMetadata(poSrcDS, poDS, papszOptions,
    4748             :                                             papszExcludedDomains);
    4749         172 :             CSLDestroy(papszExcludedDomains);
    4750             : 
    4751         172 :             return poDS;
    4752             :         }
    4753             : 
    4754           1 :         CPLErrorReset();
    4755             :     }
    4756             : 
    4757          64 :     JPGDataset *poJPG_DS = new JPGDataset();
    4758          64 :     poJPG_DS->nRasterXSize = nXSize;
    4759          64 :     poJPG_DS->nRasterYSize = nYSize;
    4760         212 :     for (int i = 0; i < nBands; i++)
    4761         148 :         poJPG_DS->SetBand(i + 1, JPGCreateBand(poJPG_DS, i + 1));
    4762          64 :     return poJPG_DS;
    4763             : }
    4764             : 
    4765             : /************************************************************************/
    4766             : /*                         GDALRegister_JPEG()                          */
    4767             : /************************************************************************/
    4768             : 
    4769             : #if !defined(JPGDataset)
    4770             : 
    4771          32 : char **GDALJPGDriver::GetMetadata(const char *pszDomain)
    4772             : {
    4773          32 :     GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
    4774          32 :     return GDALDriver::GetMetadata(pszDomain);
    4775             : }
    4776             : 
    4777             : // C_ARITH_CODING_SUPPORTED is defined in libjpeg-turbo's jconfig.h
    4778             : #ifndef C_ARITH_CODING_SUPPORTED
    4779             : static void GDALJPEGIsArithmeticCodingAvailableErrorExit(j_common_ptr cinfo)
    4780             : {
    4781             :     jmp_buf *p_setjmp_buffer = static_cast<jmp_buf *>(cinfo->client_data);
    4782             :     // Return control to the setjmp point.
    4783             :     longjmp(*p_setjmp_buffer, 1);
    4784             : }
    4785             : 
    4786             : // Runtime check if arithmetic coding is available.
    4787             : static bool GDALJPEGIsArithmeticCodingAvailable()
    4788             : {
    4789             :     struct jpeg_compress_struct sCInfo;
    4790             :     struct jpeg_error_mgr sJErr;
    4791             :     jmp_buf setjmp_buffer;
    4792             :     if (setjmp(setjmp_buffer))
    4793             :     {
    4794             :         jpeg_destroy_compress(&sCInfo);
    4795             :         return false;
    4796             :     }
    4797             :     sCInfo.err = jpeg_std_error(&sJErr);
    4798             :     sJErr.error_exit = GDALJPEGIsArithmeticCodingAvailableErrorExit;
    4799             :     sCInfo.client_data = &setjmp_buffer;
    4800             :     jpeg_create_compress(&sCInfo);
    4801             :     // Hopefully nothing will be written.
    4802             :     jpeg_stdio_dest(&sCInfo, stderr);
    4803             :     sCInfo.image_width = 1;
    4804             :     sCInfo.image_height = 1;
    4805             :     sCInfo.input_components = 1;
    4806             :     sCInfo.in_color_space = JCS_UNKNOWN;
    4807             :     jpeg_set_defaults(&sCInfo);
    4808             :     sCInfo.arith_code = TRUE;
    4809             :     jpeg_start_compress(&sCInfo, FALSE);
    4810             :     jpeg_abort_compress(&sCInfo);
    4811             :     jpeg_destroy_compress(&sCInfo);
    4812             : 
    4813             :     return true;
    4814             : }
    4815             : #endif
    4816             : 
    4817       56223 : const char *GDALJPGDriver::GetMetadataItem(const char *pszName,
    4818             :                                            const char *pszDomain)
    4819             : {
    4820       56223 :     if (pszName != nullptr && EQUAL(pszName, GDAL_DMD_CREATIONOPTIONLIST) &&
    4821      112758 :         (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
    4822         312 :         GDALDriver::GetMetadataItem(pszName, pszDomain) == nullptr)
    4823             :     {
    4824             :         CPLString osCreationOptions =
    4825             :             "<CreationOptionList>\n"
    4826             :             "   <Option name='PROGRESSIVE' type='boolean' description='whether "
    4827             :             "to generate a progressive JPEG' default='NO'/>\n"
    4828             :             "   <Option name='QUALITY' type='int' description='good=100, "
    4829             :             "bad=1, default=75'/>\n"
    4830             :             "   <Option name='LOSSLESS_COPY' type='string-select' "
    4831             :             "description='Whether conversion should be lossless' "
    4832             :             "default='AUTO'>"
    4833             :             "     <Value>AUTO</Value>"
    4834             :             "     <Value>YES</Value>"
    4835             :             "     <Value>NO</Value>"
    4836             :             "   </Option>"
    4837             :             "   <Option name='WORLDFILE' type='boolean' description='whether "
    4838             :             "to generate a worldfile' default='NO'/>\n"
    4839             :             "   <Option name='INTERNAL_MASK' type='boolean' "
    4840             :             "description='whether to generate a validity mask' "
    4841          18 :             "default='YES'/>\n";
    4842             : #ifndef C_ARITH_CODING_SUPPORTED
    4843             :         if (GDALJPEGIsArithmeticCodingAvailable())
    4844             : #endif
    4845             :         {
    4846             :             osCreationOptions += "   <Option name='ARITHMETIC' type='boolean' "
    4847             :                                  "description='whether to use arithmetic "
    4848           9 :                                  "encoding' default='NO'/>\n";
    4849             :         }
    4850             :         osCreationOptions +=
    4851             : #if JPEG_LIB_VERSION_MAJOR >= 8 &&                                             \
    4852             :     (JPEG_LIB_VERSION_MAJOR > 8 || JPEG_LIB_VERSION_MINOR >= 3)
    4853             :             "   <Option name='BLOCK' type='int' description='between 1 and "
    4854             :             "16'/>\n"
    4855             : #endif
    4856             : #if JPEG_LIB_VERSION_MAJOR >= 9
    4857             :             "   <Option name='COLOR_TRANSFORM' type='string-select'>\n"
    4858             :             "       <Value>RGB</Value>"
    4859             :             "       <Value>RGB1</Value>"
    4860             :             "   </Option>"
    4861             : #endif
    4862             :             "   <Option name='COMMENT' description='Comment' type='string'/>\n"
    4863             :             "   <Option name='SOURCE_ICC_PROFILE' description='ICC profile "
    4864             :             "encoded in Base64' type='string'/>\n"
    4865             :             "   <Option name='EXIF_THUMBNAIL' type='boolean' "
    4866             :             "description='whether to generate an EXIF thumbnail(overview). By "
    4867             :             "default its max dimension will be 128' default='NO'/>\n"
    4868             :             "   <Option name='THUMBNAIL_WIDTH' type='int' description='Forced "
    4869             :             "thumbnail width' min='32' max='512'/>\n"
    4870             :             "   <Option name='THUMBNAIL_HEIGHT' type='int' description='Forced "
    4871             :             "thumbnail height' min='32' max='512'/>\n"
    4872             :             "   <Option name='WRITE_EXIF_METADATA' type='boolean' "
    4873             :             "description='whether to write EXIF_ metadata in a EXIF segment' "
    4874             :             "default='YES'/>"
    4875           9 :             "</CreationOptionList>\n";
    4876           9 :         SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, osCreationOptions);
    4877             :     }
    4878       56223 :     return GDALDriver::GetMetadataItem(pszName, pszDomain);
    4879             : }
    4880             : 
    4881        1520 : void GDALRegister_JPEG()
    4882             : 
    4883             : {
    4884        1520 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
    4885         301 :         return;
    4886             : 
    4887        1219 :     GDALDriver *poDriver = new GDALJPGDriver();
    4888        1219 :     JPEGDriverSetCommonMetadata(poDriver);
    4889             : 
    4890        1219 :     poDriver->pfnOpen = JPGDatasetCommon::Open;
    4891        1219 :     poDriver->pfnCreateCopy = JPGDataset::CreateCopy;
    4892             : 
    4893        1219 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    4894             : }
    4895             : #endif

Generated by: LCOV version 1.14