LCOV - code coverage report
Current view: top level - frmts/jpeg - jpgdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2033 2457 82.7 %
Date: 2025-10-27 00:14:23 Functions: 104 113 92.0 %

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

Generated by: LCOV version 1.14