LCOV - code coverage report
Current view: top level - frmts/jpeg - jpgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2150 2585 83.2 %
Date: 2026-02-10 10:01:39 Functions: 113 126 89.7 %

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

Generated by: LCOV version 1.14