LCOV - code coverage report
Current view: top level - frmts/jpeg - jpgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1940 2349 82.6 %
Date: 2024-11-25 23:50:41 Functions: 99 106 93.4 %

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

Generated by: LCOV version 1.14