LCOV - code coverage report
Current view: top level - frmts/jpeg - jpgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2169 2620 82.8 %
Date: 2026-06-19 21:24:00 Functions: 114 127 89.8 %

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

Generated by: LCOV version 1.14