• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 13813968540

12 Mar 2025 02:33PM UTC coverage: 70.43% (-0.02%) from 70.446%
13813968540

Pull #11951

github

web-flow
Merge 0560ed8f8 into 5ab779ac6
Pull Request #11951: Doc: Build docs using CMake

553276 of 785573 relevant lines covered (70.43%)

222076.27 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.65
/frmts/jpeg/jpgdataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  JPEG JFIF Driver
4
 * Purpose:  Implement GDAL JPEG Support based on IJG libjpeg.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2000, Frank Warmerdam
9
 * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * Portions Copyright (c) Her majesty the Queen in right of Canada as
12
 * represented by the Minister of National Defence, 2006.
13
 *
14
 * SPDX-License-Identifier: MIT
15
 ****************************************************************************/
16

17
#include "cpl_port.h"
18
#include "jpgdataset.h"
19

20
#include <cerrno>
21
#include <climits>
22
#include <cstddef>
23
#include <cstdio>
24
#include <cstdint>
25
#include <cstdlib>
26
#include <cstring>
27
#if HAVE_FCNTL_H
28
#include <fcntl.h>
29
#endif
30
#include <limits>
31
#include <setjmp.h>
32

33
#include <algorithm>
34
#include <string>
35

36
#include "gdalorienteddataset.h"
37

38
#include "cpl_conv.h"
39
#include "cpl_error.h"
40
#include "cpl_md5.h"
41
#include "cpl_minixml.h"
42
#include "quant_table_md5sum.h"
43
#include "quant_table_md5sum_jpeg9e.h"
44
#include "cpl_progress.h"
45
#include "cpl_string.h"
46
#include "cpl_time.h"
47
#include "cpl_vsi.h"
48
#include "gdal.h"
49
#include "gdal_frmts.h"
50
#include "gdal_pam.h"
51
#include "gdal_priv.h"
52
#include "gdalexif.h"
53
CPL_C_START
54
#ifdef LIBJPEG_12_PATH
55
#include LIBJPEG_12_PATH
56
#else
57
#include "jpeglib.h"
58
#endif
59
CPL_C_END
60
#include "memdataset.h"
61
#include "rawdataset.h"
62
#include "vsidataio.h"
63
#include "vrt/vrtdataset.h"
64
#include "jpegdrivercore.h"
65

66
#if defined(EXPECTED_JPEG_LIB_VERSION) && !defined(LIBJPEG_12_PATH)
67
#if EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
68
#error EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
69
#endif
70
#endif
71

72
constexpr int TIFF_VERSION = 42;
73

74
constexpr int TIFF_BIGENDIAN = 0x4d4d;
75
constexpr int TIFF_LITTLEENDIAN = 0x4949;
76

77
constexpr int JPEG_TIFF_IMAGEWIDTH = 0x100;
78
constexpr int JPEG_TIFF_IMAGEHEIGHT = 0x101;
79
constexpr int JPEG_TIFF_COMPRESSION = 0x103;
80
constexpr int JPEG_EXIF_JPEGIFOFSET = 0x201;
81
constexpr int JPEG_EXIF_JPEGIFBYTECOUNT = 0x202;
82

83
// Ok to use setjmp().
84
#ifdef _MSC_VER
85
#pragma warning(disable : 4611)
86
#endif
87

88
// Do we want to do special processing suitable for when JSAMPLE is a
89
// 16bit value?
90

91
/* HAVE_JPEGTURBO_DUAL_MODE_8_12 is defined for libjpeg-turbo >= 2.2 which
92
 * adds a dual-mode 8/12 bit API in the same library.
93
 */
94

95
#if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12)
96
/* Start by undefining BITS_IN_JSAMPLE which is always set to 8 in libjpeg-turbo
97
 * >= 2.2 Cf
98
 * https://github.com/libjpeg-turbo/libjpeg-turbo/commit/8b9bc4b9635a2a047fb23ebe70c9acd728d3f99b
99
 */
100
#undef BITS_IN_JSAMPLE
101
/* libjpeg-turbo >= 2.2 adds J12xxxx datatypes for the 12-bit mode. */
102
#if defined(JPGDataset)
103
#define BITS_IN_JSAMPLE 12
104
#define GDAL_JSAMPLE J12SAMPLE
105
#else
106
#define BITS_IN_JSAMPLE 8
107
#define GDAL_JSAMPLE JSAMPLE
108
#endif
109
#else
110
#define GDAL_JSAMPLE JSAMPLE
111
#endif
112

113
#if defined(JPEG_LIB_MK1)
114
#define JPEG_LIB_MK1_OR_12BIT 1
115
#elif BITS_IN_JSAMPLE == 12
116
#define JPEG_LIB_MK1_OR_12BIT 1
117
#endif
118

119
/************************************************************************/
120
/*                     SetMaxMemoryToUse()                              */
121
/************************************************************************/
122

123
static void SetMaxMemoryToUse(struct jpeg_decompress_struct *psDInfo)
11,853✔
124
{
125
    // This is to address bug related in ticket #1795.
126
    if (CPLGetConfigOption("JPEGMEM", nullptr) == nullptr)
11,853✔
127
    {
128
        // If the user doesn't provide a value for JPEGMEM, we want to be sure
129
        // that at least 500 MB will be used before creating the temporary file.
130
        const long nMinMemory = 500 * 1024 * 1024;
11,852✔
131
        psDInfo->mem->max_memory_to_use =
11,852✔
132
            std::max(psDInfo->mem->max_memory_to_use, nMinMemory);
11,852✔
133
    }
134
}
11,853✔
135

136
#if !defined(JPGDataset)
137

138
/************************************************************************/
139
/*                     ReadImageStructureMetadata()                     */
140
/************************************************************************/
141

142
void JPGDatasetCommon::ReadImageStructureMetadata()
5✔
143
{
144
    if (bHasReadImageStructureMetadata)
5✔
145
        return;
×
146

147
    bHasReadImageStructureMetadata = true;
5✔
148
    if (GetDataPrecision() != 8)
5✔
149
        return;  // quality guessing not implemented for 12-bit JPEG for now
×
150

151
    // Save current position to avoid disturbing JPEG stream decoding.
152
    const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
5✔
153

154
    GByte abyChunkHeader[4];
155
    int nChunkLoc = 2;
5✔
156
    constexpr GByte MARKER_QUANT_TABLE = 0xDB;
5✔
157
    struct CPLMD5Context context;
158
    CPLMD5Init(&context);
5✔
159

160
    while (true)
161
    {
162
        if (VSIFSeekL(m_fpImage, nChunkLoc, SEEK_SET) != 0)
20✔
163
            break;
×
164

165
        if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, m_fpImage) !=
20✔
166
            1)
167
            break;
×
168

169
        const int nChunkLength = abyChunkHeader[2] * 256 + abyChunkHeader[3];
20✔
170
        if (abyChunkHeader[0] == 0xFF &&
20✔
171
            abyChunkHeader[1] == MARKER_QUANT_TABLE && nChunkLength > 2)
20✔
172
        {
173
            std::vector<GByte> abyTable(nChunkLength);
14✔
174
            abyTable[0] = abyChunkHeader[2];
7✔
175
            abyTable[1] = abyChunkHeader[3];
7✔
176
            if (VSIFReadL(&abyTable[2], nChunkLength - 2, 1, m_fpImage) == 1)
7✔
177
            {
178
                CPLMD5Update(&context, &abyTable[0], nChunkLength);
7✔
179
            }
7✔
180
        }
181
        else
182
        {
183
            if (abyChunkHeader[0] != 0xFF || (abyChunkHeader[1] & 0xf0) != 0xe0)
13✔
184
                break;  // Not an APP chunk.
185
        }
186

187
        nChunkLoc += 2 + nChunkLength;
15✔
188
    }
15✔
189

190
    VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
5✔
191

192
    GByte digest[16];
193
    CPLMD5Final(digest, &context);
5✔
194

195
    const bool bIsYCbCr = nBands == 3 && GetJPEGColorSpace() == JCS_YCbCr;
5✔
196
    for (int i = 0; i < 100; i++)
410✔
197
    {
198
        if ((bIsYCbCr &&
410✔
199
             (memcmp(md5JPEGQuantTable_3_YCBCR_8bit[i], digest, 16) == 0 ||
160✔
200
              memcmp(md5JPEGQuantTable_3_YCBCR_8bit_jpeg9e[i], digest, 16) ==
158✔
201
                  0)) ||
408✔
202
            (!bIsYCbCr &&
408✔
203
             memcmp(md5JPEGQuantTable_generic_8bit[i], digest, 16) == 0))
250✔
204
        {
205
            GDALDataset::SetMetadataItem(
5✔
206
                "JPEG_QUALITY", CPLSPrintf("%d", i + 1), "IMAGE_STRUCTURE");
207
            break;
5✔
208
        }
209
    }
210
}
211

212
/************************************************************************/
213
/*                       ReadEXIFMetadata()                             */
214
/************************************************************************/
215
void JPGDatasetCommon::ReadEXIFMetadata()
107✔
216
{
217
    if (bHasReadEXIFMetadata)
107✔
218
        return;
×
219

220
    CPLAssert(papszMetadata == nullptr);
107✔
221

222
    // Save current position to avoid disturbing JPEG stream decoding.
223
    const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
107✔
224

225
    if (EXIFInit(m_fpImage))
107✔
226
    {
227
        EXIFExtractMetadata(papszMetadata, m_fpImage, nTiffDirStart, bSwabflag,
44✔
228
                            nTIFFHEADER, nExifOffset, nInterOffset, nGPSOffset);
44✔
229

230
        if (nExifOffset > 0)
44✔
231
        {
232
            EXIFExtractMetadata(papszMetadata, m_fpImage, nExifOffset,
33✔
233
                                bSwabflag, nTIFFHEADER, nExifOffset,
33✔
234
                                nInterOffset, nGPSOffset);
33✔
235
        }
236
        if (nInterOffset > 0)
44✔
237
        {
238
            EXIFExtractMetadata(papszMetadata, m_fpImage, nInterOffset,
×
239
                                bSwabflag, nTIFFHEADER, nExifOffset,
×
240
                                nInterOffset, nGPSOffset);
×
241
        }
242
        if (nGPSOffset > 0)
44✔
243
        {
244
            EXIFExtractMetadata(papszMetadata, m_fpImage, nGPSOffset, bSwabflag,
18✔
245
                                nTIFFHEADER, nExifOffset, nInterOffset,
18✔
246
                                nGPSOffset);
18✔
247
        }
248

249
        // Pix4D Mapper files have both DNG_CameraSerialNumber and EXIF_BodySerialNumber
250
        // set at the same value. Only expose the later in that situation.
251
        if (const char *pszDNG_CameraSerialNumber =
44✔
252
                CSLFetchNameValue(papszMetadata, "DNG_CameraSerialNumber"))
44✔
253
        {
254
            const char *pszEXIF_BodySerialNumber =
255
                CSLFetchNameValue(papszMetadata, "EXIF_BodySerialNumber");
2✔
256
            if (pszEXIF_BodySerialNumber &&
2✔
257
                EQUAL(pszDNG_CameraSerialNumber, pszEXIF_BodySerialNumber))
1✔
258
            {
259
                CPLDebug("JPEG", "Unsetting DNG_CameraSerialNumber as it has "
1✔
260
                                 "the same value as EXIF_BodySerialNumber");
261
                papszMetadata = CSLSetNameValue(
1✔
262
                    papszMetadata, "DNG_CameraSerialNumber", nullptr);
263
            }
264
        }
265

266
        // Pix4D Mapper files have both DNG_UniqueCameraModel and EXIF_Model
267
        // set at the same value. Only expose the later in that situation.
268
        if (const char *pszDNG_UniqueCameraModel =
44✔
269
                CSLFetchNameValue(papszMetadata, "DNG_UniqueCameraModel"))
44✔
270
        {
271
            const char *pszEXIF_Model =
272
                CSLFetchNameValue(papszMetadata, "EXIF_Model");
2✔
273
            if (pszEXIF_Model && EQUAL(pszDNG_UniqueCameraModel, pszEXIF_Model))
2✔
274
            {
275
                CPLDebug("JPEG", "Unsetting DNG_UniqueCameraModel as it has "
1✔
276
                                 "the same value as EXIF_Model");
277
                papszMetadata = CSLSetNameValue(
1✔
278
                    papszMetadata, "DNG_UniqueCameraModel", nullptr);
279
            }
280
        }
281

282
        // Avoid setting the PAM dirty bit just for that.
283
        const int nOldPamFlags = nPamFlags;
44✔
284

285
        // Append metadata from PAM after EXIF metadata.
286
        papszMetadata = CSLMerge(papszMetadata, GDALPamDataset::GetMetadata());
44✔
287

288
        // Expose XMP in EXIF in xml:XMP metadata domain
289
        if (GDALDataset::GetMetadata("xml:XMP") == nullptr)
44✔
290
        {
291
            const char *pszXMP =
292
                CSLFetchNameValue(papszMetadata, "EXIF_XmlPacket");
44✔
293
            if (pszXMP)
44✔
294
            {
295
                CPLDebug("JPEG", "Read XMP metadata from EXIF tag");
×
296
                const char *const apszMDList[2] = {pszXMP, nullptr};
×
297
                SetMetadata(const_cast<char **>(apszMDList), "xml:XMP");
×
298

299
                papszMetadata =
×
300
                    CSLSetNameValue(papszMetadata, "EXIF_XmlPacket", nullptr);
×
301
            }
302
        }
303

304
        SetMetadata(papszMetadata);
44✔
305

306
        nPamFlags = nOldPamFlags;
44✔
307
    }
308

309
    VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
107✔
310

311
    bHasReadEXIFMetadata = true;
107✔
312
}
313

314
/************************************************************************/
315
/*                        ReadXMPMetadata()                             */
316
/************************************************************************/
317

318
// See §2.1.3 of
319
// http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf
320

321
void JPGDatasetCommon::ReadXMPMetadata()
69✔
322
{
323
    if (bHasReadXMPMetadata)
69✔
324
        return;
×
325

326
    // Save current position to avoid disturbing JPEG stream decoding.
327
    const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
69✔
328

329
    // Search for APP1 chunk.
330
    constexpr int APP1_BYTE = 0xe1;
69✔
331
    constexpr int JFIF_MARKER_SIZE = 2 + 2;  // ID + size
69✔
332
    constexpr const char APP1_XMP_SIGNATURE[] = "http://ns.adobe.com/xap/1.0/";
69✔
333
    constexpr int APP1_XMP_SIGNATURE_LEN =
69✔
334
        static_cast<int>(sizeof(APP1_XMP_SIGNATURE));
335
    GByte abyChunkHeader[JFIF_MARKER_SIZE + APP1_XMP_SIGNATURE_LEN] = {};
69✔
336
    int nChunkLoc = 2;
69✔
337
    bool bFoundXMP = false;
69✔
338

339
    while (true)
340
    {
341
        if (VSIFSeekL(m_fpImage, nChunkLoc, SEEK_SET) != 0)
451✔
342
            break;
×
343

344
        if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, m_fpImage) !=
451✔
345
            1)
346
            break;
7✔
347

348
        nChunkLoc += 2 + abyChunkHeader[2] * 256 + abyChunkHeader[3];
444✔
349

350
        // Not a marker
351
        if (abyChunkHeader[0] != 0xFF)
444✔
352
            break;
×
353

354
        // Stop on Start of Scan
355
        if (abyChunkHeader[1] == 0xDA)
444✔
356
            break;
48✔
357

358
        if (abyChunkHeader[1] == APP1_BYTE &&
396✔
359
            memcmp(reinterpret_cast<char *>(abyChunkHeader) + JFIF_MARKER_SIZE,
32✔
360
                   APP1_XMP_SIGNATURE, APP1_XMP_SIGNATURE_LEN) == 0)
361
        {
362
            bFoundXMP = true;
14✔
363
            break;  // APP1 - XMP.
14✔
364
        }
365
    }
366

367
    if (bFoundXMP)
69✔
368
    {
369
        const int nXMPLength = abyChunkHeader[2] * 256 + abyChunkHeader[3] - 2 -
14✔
370
                               APP1_XMP_SIGNATURE_LEN;
371
        if (nXMPLength > 0)
14✔
372
        {
373
            char *pszXMP = static_cast<char *>(VSIMalloc(nXMPLength + 1));
14✔
374
            if (pszXMP)
14✔
375
            {
376
                if (VSIFReadL(pszXMP, nXMPLength, 1, m_fpImage) == 1)
14✔
377
                {
378
                    pszXMP[nXMPLength] = '\0';
14✔
379

380
                    // Avoid setting the PAM dirty bit just for that.
381
                    const int nOldPamFlags = nPamFlags;
14✔
382

383
                    char *apszMDList[2] = {pszXMP, nullptr};
14✔
384
                    SetMetadata(apszMDList, "xml:XMP");
14✔
385

386
                    // cppcheck-suppress redundantAssignment
387
                    nPamFlags = nOldPamFlags;
14✔
388
                }
389
                VSIFree(pszXMP);
14✔
390
            }
391
        }
392
    }
393

394
    VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
69✔
395

396
    bHasReadXMPMetadata = true;
69✔
397
}
398

399
/************************************************************************/
400
/*                        ReadFLIRMetadata()                            */
401
/************************************************************************/
402

403
// See https://exiftool.org/TagNames/FLIR.html
404

405
void JPGDatasetCommon::ReadFLIRMetadata()
10✔
406
{
407
    if (bHasReadFLIRMetadata)
10✔
408
        return;
6✔
409
    bHasReadFLIRMetadata = true;
9✔
410

411
    // Save current position to avoid disturbing JPEG stream decoding.
412
    const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
9✔
413

414
    int nChunkLoc = 2;
9✔
415
    // size of APP1 segment marker + size of "FLIR\0"
416
    GByte abyChunkHeader[4 + 5];
417
    std::vector<GByte> abyFLIR;
9✔
418

419
    while (true)
420
    {
421
        if (VSIFSeekL(m_fpImage, nChunkLoc, SEEK_SET) != 0)
71✔
422
            break;
×
423

424
        if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, m_fpImage) !=
71✔
425
            1)
426
            break;
×
427

428
        const int nMarkerLength =
71✔
429
            abyChunkHeader[2] * 256 + abyChunkHeader[3] - 2;
71✔
430
        nChunkLoc += 4 + nMarkerLength;
71✔
431

432
        // Not a marker
433
        if (abyChunkHeader[0] != 0xFF)
71✔
434
            break;
×
435

436
        // Stop on Start of Scan
437
        if (abyChunkHeader[1] == 0xDA)
71✔
438
            break;
9✔
439

440
        if (abyChunkHeader[1] == 0xe1 &&
62✔
441
            memcmp(abyChunkHeader + 4, "FLIR\0", 5) == 0)
10✔
442
        {
443
            // Somewhat arbitrary limit
444
            if (abyFLIR.size() > 10 * 1024 * 1024)
4✔
445
            {
446
                CPLError(CE_Warning, CPLE_AppDefined,
×
447
                         "Too large FLIR data compared to hardcoded limit");
448
                abyFLIR.clear();
×
449
                break;
×
450
            }
451

452
            // 8 = sizeof("FLIR\0") + '\1' + chunk_idx + chunk_count
453
            if (nMarkerLength < 8)
4✔
454
            {
455
                abyFLIR.clear();
×
456
                break;
×
457
            }
458
            size_t nOldSize = abyFLIR.size();
4✔
459
            abyFLIR.resize(nOldSize + nMarkerLength - 8);
4✔
460
            GByte abyIgnored[3];  // skip '\1' + chunk_idx + chunk_count
461
            if (VSIFReadL(abyIgnored, 3, 1, m_fpImage) != 1 ||
8✔
462
                VSIFReadL(&abyFLIR[nOldSize], nMarkerLength - 8, 1,
4✔
463
                          m_fpImage) != 1)
464
            {
465
                abyFLIR.clear();
×
466
                break;
×
467
            }
468
        }
469
    }
62✔
470
    // Restore file pointer
471
    VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
9✔
472

473
    constexpr size_t FLIR_HEADER_SIZE = 64;
9✔
474
    if (abyFLIR.size() < FLIR_HEADER_SIZE)
9✔
475
        return;
5✔
476
    if (memcmp(&abyFLIR[0], "FFF\0", 4) != 0)
4✔
477
        return;
×
478

479
    const auto ReadString = [&abyFLIR](size_t nOffset, size_t nLen)
104✔
480
    {
481
        std::string osStr(
482
            reinterpret_cast<const char *>(abyFLIR.data()) + nOffset, nLen);
52✔
483
        osStr.resize(strlen(osStr.c_str()));
52✔
484
        return osStr;
52✔
485
    };
4✔
486

487
    bool bLittleEndian = false;
4✔
488

489
    const auto ReadUInt16 = [&abyFLIR, &bLittleEndian](size_t nOffset)
192✔
490
    {
491
        std::uint16_t nVal;
492
        memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
96✔
493
        if (bLittleEndian)
96✔
494
            CPL_LSBPTR16(&nVal);
32✔
495
        else
496
            CPL_MSBPTR16(&nVal);
64✔
497
        return nVal;
96✔
498
    };
4✔
499

500
    const auto ReadInt16 = [&abyFLIR, &bLittleEndian](size_t nOffset)
8✔
501
    {
502
        std::int16_t nVal;
503
        memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
4✔
504
        if (bLittleEndian)
4✔
505
            CPL_LSBPTR16(&nVal);
4✔
506
        else
507
            CPL_MSBPTR16(&nVal);
×
508
        return nVal;
4✔
509
    };
4✔
510

511
    const auto ReadUInt32 = [&abyFLIR, &bLittleEndian](size_t nOffset)
264✔
512
    {
513
        std::uint32_t nVal;
514
        memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
132✔
515
        if (bLittleEndian)
132✔
516
            CPL_LSBPTR32(&nVal);
8✔
517
        else
518
            CPL_MSBPTR32(&nVal);
124✔
519
        return nVal;
132✔
520
    };
4✔
521

522
    const auto ReadInt32 = [&abyFLIR, &bLittleEndian](size_t nOffset)
8✔
523
    {
524
        std::int32_t nVal;
525
        memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
4✔
526
        if (bLittleEndian)
4✔
527
            CPL_LSBPTR32(&nVal);
4✔
528
        else
529
            CPL_MSBPTR32(&nVal);
×
530
        return nVal;
4✔
531
    };
4✔
532

533
    const auto ReadFloat32 = [&abyFLIR, &bLittleEndian](size_t nOffset)
208✔
534
    {
535
        float fVal;
536
        memcpy(&fVal, &abyFLIR[nOffset], sizeof(fVal));
104✔
537
        if (bLittleEndian)
104✔
538
            CPL_LSBPTR32(&fVal);
104✔
539
        else
540
            CPL_MSBPTR32(&fVal);
×
541
        return fVal;
104✔
542
    };
4✔
543

544
    const auto ReadFloat64 = [&abyFLIR, &bLittleEndian](size_t nOffset)
×
545
    {
546
        double fVal;
547
        memcpy(&fVal, &abyFLIR[nOffset], sizeof(fVal));
×
548
        if (bLittleEndian)
×
549
            CPL_LSBPTR64(&fVal);
×
550
        else
551
            CPL_MSBPTR64(&fVal);
×
552
        return fVal;
×
553
    };
4✔
554

555
    // Avoid setting the PAM dirty bit just for that.
556
    struct PamFlagKeeper
557
    {
558
        int &m_nPamFlagsRef;
559
        int m_nOldPamFlags;
560

561
        explicit PamFlagKeeper(int &nPamFlagsRef)
4✔
562
            : m_nPamFlagsRef(nPamFlagsRef), m_nOldPamFlags(nPamFlagsRef)
4✔
563
        {
564
        }
4✔
565

566
        ~PamFlagKeeper()
4✔
567
        {
4✔
568
            m_nPamFlagsRef = m_nOldPamFlags;
4✔
569
        }
4✔
570
    };
571

572
    PamFlagKeeper oKeeper(nPamFlags);
4✔
573

574
    const auto SetStringIfNotEmpty =
575
        [&](const char *pszItemName, int nOffset, int nLength)
52✔
576
    {
577
        const auto str = ReadString(nOffset, nLength);
104✔
578
        if (!str.empty())
52✔
579
            SetMetadataItem(pszItemName, str.c_str(), "FLIR");
28✔
580
    };
52✔
581
    SetStringIfNotEmpty("CreatorSoftware", 4, 16);
4✔
582

583
    // Check file format version (big endian most of the time)
584
    const auto nFileFormatVersion = ReadUInt32(20);
4✔
585
    if (!(nFileFormatVersion >= 100 && nFileFormatVersion < 200))
4✔
586
    {
587
        bLittleEndian = true;  // retry with little-endian
×
588
        const auto nFileFormatVersionOtherEndianness = ReadUInt32(20);
×
589
        if (!(nFileFormatVersionOtherEndianness >= 100 &&
×
590
              nFileFormatVersionOtherEndianness < 200))
591
        {
592
            CPLDebug("JPEG", "FLIR: Unknown file format version: %u",
×
593
                     nFileFormatVersion);
594
            return;
×
595
        }
596
    }
597

598
    const auto nOffsetRecordDirectory = ReadUInt32(24);
4✔
599
    const auto nEntryCountRecordDirectory = ReadUInt32(28);
4✔
600

601
    CPLDebugOnly("JPEG", "FLIR: record offset %u, entry count %u",
4✔
602
                 nOffsetRecordDirectory, nEntryCountRecordDirectory);
603
    constexpr size_t SIZE_RECORD_DIRECTORY = 32;
4✔
604
    if (nOffsetRecordDirectory < FLIR_HEADER_SIZE ||
8✔
605
        nOffsetRecordDirectory +
4✔
606
                SIZE_RECORD_DIRECTORY * nEntryCountRecordDirectory >
4✔
607
            abyFLIR.size())
4✔
608
    {
609
        CPLDebug("JPEG", "Invalid FLIR FFF directory");
×
610
        return;
×
611
    }
612

613
    // Read the RawData record
614
    const auto ReadRawData =
615
        [&](std::uint32_t nRecOffset, std::uint32_t nRecLength)
4✔
616
    {
617
        if (!(nRecLength >= 32 && nRecOffset + nRecLength <= abyFLIR.size()))
4✔
618
            return;
×
619

620
        const int nByteOrder = ReadUInt16(nRecOffset);
4✔
621
        if (nByteOrder == 512)
4✔
622
            bLittleEndian = !bLittleEndian;
4✔
623
        else if (nByteOrder != 2)
×
624
            return;
×
625
        const auto nImageWidth = ReadUInt16(nRecOffset + 2);
4✔
626
        SetMetadataItem("RawThermalImageWidth", CPLSPrintf("%d", nImageWidth),
4✔
627
                        "FLIR");
4✔
628
        const auto nImageHeight = ReadUInt16(nRecOffset + 4);
4✔
629
        SetMetadataItem("RawThermalImageHeight", CPLSPrintf("%d", nImageHeight),
4✔
630
                        "FLIR");
4✔
631
        m_bRawThermalLittleEndian = bLittleEndian;
4✔
632
        m_nRawThermalImageWidth = nImageWidth;
4✔
633
        m_nRawThermalImageHeight = nImageHeight;
4✔
634
        m_abyRawThermalImage.clear();
4✔
635
        m_abyRawThermalImage.insert(m_abyRawThermalImage.end(),
4✔
636
                                    abyFLIR.begin() + nRecOffset + 32,
4✔
637
                                    abyFLIR.begin() + nRecOffset + nRecLength);
12✔
638

639
        if (!STARTS_WITH(GetDescription(), "JPEG:"))
4✔
640
        {
641
            m_nSubdatasetCount++;
4✔
642
            SetMetadataItem(
4✔
643
                CPLSPrintf("SUBDATASET_%d_NAME", m_nSubdatasetCount),
644
                CPLSPrintf("JPEG:\"%s\":FLIR_RAW_THERMAL_IMAGE",
645
                           GetDescription()),
4✔
646
                "SUBDATASETS");
4✔
647
            SetMetadataItem(
4✔
648
                CPLSPrintf("SUBDATASET_%d_DESC", m_nSubdatasetCount),
649
                "FLIR raw thermal image", "SUBDATASETS");
4✔
650
        }
651
    };
4✔
652

653
    // Read the Camera Info record
654
    const auto ReadCameraInfo =
655
        [&](std::uint32_t nRecOffset, std::uint32_t nRecLength)
4✔
656
    {
657
        if (!(nRecLength >= 1126 && nRecOffset + nRecLength <= abyFLIR.size()))
4✔
658
            return;
×
659

660
        const int nByteOrder = ReadUInt16(nRecOffset);
4✔
661
        if (nByteOrder == 512)
4✔
662
            bLittleEndian = !bLittleEndian;
4✔
663
        else if (nByteOrder != 2)
×
664
            return;
×
665

666
        const auto ReadFloat32FromKelvin = [=](std::uint32_t nOffset)
44✔
667
        {
668
            constexpr float ZERO_CELCIUS_IN_KELVIN = 273.15f;
44✔
669
            return ReadFloat32(nOffset) - ZERO_CELCIUS_IN_KELVIN;
44✔
670
        };
4✔
671
        SetMetadataItem("Emissivity",
4✔
672
                        CPLSPrintf("%f", ReadFloat32(nRecOffset + 32)), "FLIR");
4✔
673
        SetMetadataItem("ObjectDistance",
4✔
674
                        CPLSPrintf("%f m", ReadFloat32(nRecOffset + 36)),
4✔
675
                        "FLIR");
4✔
676
        SetMetadataItem(
4✔
677
            "ReflectedApparentTemperature",
678
            CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 40)), "FLIR");
4✔
679
        SetMetadataItem(
4✔
680
            "AtmosphericTemperature",
681
            CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 44)), "FLIR");
4✔
682
        SetMetadataItem(
4✔
683
            "IRWindowTemperature",
684
            CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 48)), "FLIR");
4✔
685
        SetMetadataItem("IRWindowTemperature",
4✔
686
                        CPLSPrintf("%f", ReadFloat32(nRecOffset + 52)), "FLIR");
4✔
687
        auto fRelativeHumidity = ReadFloat32(nRecOffset + 60);
4✔
688
        if (fRelativeHumidity > 2)
4✔
689
            fRelativeHumidity /= 100.0f;  // Sometimes expressed in percentage
×
690
        SetMetadataItem("RelativeHumidity",
4✔
691
                        CPLSPrintf("%f %%", 100.0f * fRelativeHumidity));
4✔
692
        SetMetadataItem("PlanckR1",
4✔
693
                        CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 88)),
4✔
694
                        "FLIR");
4✔
695
        SetMetadataItem("PlanckB",
4✔
696
                        CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 92)),
4✔
697
                        "FLIR");
4✔
698
        SetMetadataItem("PlanckF",
4✔
699
                        CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 96)),
4✔
700
                        "FLIR");
4✔
701
        SetMetadataItem("AtmosphericTransAlpha1",
4✔
702
                        CPLSPrintf("%f", ReadFloat32(nRecOffset + 112)),
4✔
703
                        "FLIR");
4✔
704
        SetMetadataItem("AtmosphericTransAlpha2",
4✔
705
                        CPLSPrintf("%f", ReadFloat32(nRecOffset + 116)),
4✔
706
                        "FLIR");
4✔
707
        SetMetadataItem("AtmosphericTransBeta1",
4✔
708
                        CPLSPrintf("%f", ReadFloat32(nRecOffset + 120)),
4✔
709
                        "FLIR");
4✔
710
        SetMetadataItem("AtmosphericTransBeta2",
4✔
711
                        CPLSPrintf("%f", ReadFloat32(nRecOffset + 124)),
4✔
712
                        "FLIR");
4✔
713
        SetMetadataItem("AtmosphericTransX",
4✔
714
                        CPLSPrintf("%f", ReadFloat32(nRecOffset + 128)),
4✔
715
                        "FLIR");
4✔
716
        SetMetadataItem(
4✔
717
            "CameraTemperatureRangeMax",
718
            CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 144)),
4✔
719
            "FLIR");
4✔
720
        SetMetadataItem(
4✔
721
            "CameraTemperatureRangeMin",
722
            CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 148)),
4✔
723
            "FLIR");
4✔
724
        SetMetadataItem(
4✔
725
            "CameraTemperatureMaxClip",
726
            CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 152)),
4✔
727
            "FLIR");
4✔
728
        SetMetadataItem(
4✔
729
            "CameraTemperatureMinClip",
730
            CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 156)),
4✔
731
            "FLIR");
4✔
732
        SetMetadataItem(
4✔
733
            "CameraTemperatureMaxWarn",
734
            CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 160)),
4✔
735
            "FLIR");
4✔
736
        SetMetadataItem(
4✔
737
            "CameraTemperatureMinWarn",
738
            CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 164)),
4✔
739
            "FLIR");
4✔
740
        SetMetadataItem(
4✔
741
            "CameraTemperatureMaxSaturated",
742
            CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 168)),
4✔
743
            "FLIR");
4✔
744
        SetMetadataItem(
4✔
745
            "CameraTemperatureMinSaturated",
746
            CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 172)),
4✔
747
            "FLIR");
4✔
748

749
        SetStringIfNotEmpty("CameraModel", nRecOffset + 212, 32);
4✔
750
        SetStringIfNotEmpty("CameraPartNumber", nRecOffset + 244, 16);
4✔
751
        SetStringIfNotEmpty("CameraSerialNumber", nRecOffset + 260, 16);
4✔
752
        SetStringIfNotEmpty("CameraSoftware", nRecOffset + 276, 16);
4✔
753
        SetStringIfNotEmpty("LensModel", nRecOffset + 368, 32);
4✔
754
        SetStringIfNotEmpty("LensPartNumber", nRecOffset + 400, 16);
4✔
755
        SetStringIfNotEmpty("LensSerialNumber", nRecOffset + 416, 16);
4✔
756
        SetMetadataItem("FieldOfView",
4✔
757
                        CPLSPrintf("%f deg", ReadFloat32(nRecOffset + 436)),
4✔
758
                        "FLIR");
4✔
759
        SetStringIfNotEmpty("FilterModel", nRecOffset + 492, 16);
4✔
760
        SetStringIfNotEmpty("FilterPartNumber", nRecOffset + 508, 32);
4✔
761
        SetStringIfNotEmpty("FilterSerialNumber", nRecOffset + 540, 32);
4✔
762
        SetMetadataItem("PlanckO",
4✔
763
                        CPLSPrintf("%d", ReadInt32(nRecOffset + 776)), "FLIR");
4✔
764
        SetMetadataItem("PlanckR2",
4✔
765
                        CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 780)),
4✔
766
                        "FLIR");
4✔
767
        SetMetadataItem("RawValueRangeMin",
4✔
768
                        CPLSPrintf("%d", ReadUInt16(nRecOffset + 784)), "FLIR");
4✔
769
        SetMetadataItem("RawValueRangeMax",
4✔
770
                        CPLSPrintf("%d", ReadUInt16(nRecOffset + 786)), "FLIR");
4✔
771
        SetMetadataItem("RawValueMedian",
4✔
772
                        CPLSPrintf("%d", ReadUInt16(nRecOffset + 824)), "FLIR");
4✔
773
        SetMetadataItem("RawValueRange",
4✔
774
                        CPLSPrintf("%d", ReadUInt16(nRecOffset + 828)), "FLIR");
4✔
775
        const auto nUnixTime = ReadUInt32(nRecOffset + 900);
4✔
776
        const auto nSS = ReadUInt32(nRecOffset + 904) & 0xffff;
4✔
777
        const auto nTZ = ReadInt16(nRecOffset + 908);
4✔
778
        struct tm brokenDown;
779
        CPLUnixTimeToYMDHMS(static_cast<GIntBig>(nUnixTime) - nTZ * 60,
4✔
780
                            &brokenDown);
781
        std::string osDateTime(CPLSPrintf(
782
            "%04d-%02d-%02dT%02d:%02d:%02d.%03d", brokenDown.tm_year + 1900,
4✔
783
            brokenDown.tm_mon + 1, brokenDown.tm_mday, brokenDown.tm_hour,
4✔
784
            brokenDown.tm_min, brokenDown.tm_sec, nSS));
8✔
785
        if (nTZ <= 0)
4✔
786
            osDateTime += CPLSPrintf("+%02d:%02d", (-nTZ) / 60, (-nTZ) % 60);
4✔
787
        else
788
            osDateTime += CPLSPrintf("-%02d:%02d", nTZ / 60, nTZ % 60);
×
789
        SetMetadataItem("DateTimeOriginal", osDateTime.c_str(), "FLIR");
4✔
790
        SetMetadataItem("FocusStepCount",
4✔
791
                        CPLSPrintf("%d", ReadUInt16(nRecOffset + 912)), "FLIR");
4✔
792
        SetMetadataItem("FocusDistance",
4✔
793
                        CPLSPrintf("%f m", ReadFloat32(nRecOffset + 1116)),
4✔
794
                        "FLIR");
4✔
795
        SetMetadataItem("FrameRate",
4✔
796
                        CPLSPrintf("%d", ReadUInt16(nRecOffset + 1124)),
4✔
797
                        "FLIR");
4✔
798
    };
4✔
799

800
    // Read the Palette Info record
801
    const auto ReadPaletteInfo =
802
        [&](std::uint32_t nRecOffset, std::uint32_t nRecLength)
4✔
803
    {
804
        if (!(nRecLength >= 112 && nRecOffset + nRecLength <= abyFLIR.size()))
4✔
805
            return;
×
806
        const int nPaletteColors = abyFLIR[nRecOffset];
4✔
807
        SetMetadataItem("PaletteColors", CPLSPrintf("%d", nPaletteColors),
4✔
808
                        "FLIR");
4✔
809

810
        const auto SetColorItem =
811
            [this, &abyFLIR](const char *pszItem, std::uint32_t nOffset)
72✔
812
        {
813
            SetMetadataItem(pszItem,
24✔
814
                            CPLSPrintf("%d %d %d", abyFLIR[nOffset],
24✔
815
                                       abyFLIR[nOffset + 1],
24✔
816
                                       abyFLIR[nOffset + 2]),
24✔
817
                            "FLIR");
24✔
818
        };
28✔
819
        SetColorItem("AboveColor", nRecOffset + 6);
4✔
820
        SetColorItem("BelowColor", nRecOffset + 9);
4✔
821
        SetColorItem("OverflowColor", nRecOffset + 12);
4✔
822
        SetColorItem("UnderflowColor", nRecOffset + 15);
4✔
823
        SetColorItem("Isotherm1Color", nRecOffset + 18);
4✔
824
        SetColorItem("Isotherm2Color", nRecOffset + 21);
4✔
825
        SetMetadataItem("PaletteMethod",
4✔
826
                        CPLSPrintf("%d", abyFLIR[nRecOffset + 26]), "FLIR");
4✔
827
        SetMetadataItem("PaletteStretch",
4✔
828
                        CPLSPrintf("%d", abyFLIR[nRecOffset + 27]), "FLIR");
4✔
829
        SetStringIfNotEmpty("PaletteFileName", nRecOffset + 48, 32);
4✔
830
        SetStringIfNotEmpty("PaletteName", nRecOffset + 80, 32);
4✔
831
        if (nRecLength < static_cast<std::uint32_t>(112 + nPaletteColors * 3))
4✔
832
            return;
×
833
        std::string osPalette;
8✔
834
        for (int i = 0; i < nPaletteColors; i++)
900✔
835
        {
836
            if (!osPalette.empty())
896✔
837
                osPalette += ", ";
892✔
838
            osPalette +=
839
                CPLSPrintf("(%d %d %d)", abyFLIR[nRecOffset + 112 + 3 * i + 0],
896✔
840
                           abyFLIR[nRecOffset + 112 + 3 * i + 1],
896✔
841
                           abyFLIR[nRecOffset + 112 + 3 * i + 2]);
896✔
842
        }
843
        SetMetadataItem("Palette", osPalette.c_str(), "FLIR");
4✔
844
    };
4✔
845

846
    // Read the GPS Info record
847
    const auto ReadGPSInfo =
848
        [&](std::uint32_t nRecOffset, std::uint32_t nRecLength)
×
849
    {
850
        if (!(nRecLength >= 104 && nRecOffset + nRecLength <= abyFLIR.size()))
×
851
            return;
×
852
        auto nGPSValid = ReadUInt32(nRecOffset);
×
853
        if (nGPSValid == 0x01000000)
×
854
        {
855
            bLittleEndian = !bLittleEndian;
×
856
            nGPSValid = 1;
×
857
        }
858
        if (nGPSValid != 1)
×
859
            return;
×
860
        SetMetadataItem("GPSVersionID",
×
861
                        CPLSPrintf("%c%c%c%c", abyFLIR[nRecOffset + 4],
×
862
                                   abyFLIR[nRecOffset + 5],
×
863
                                   abyFLIR[nRecOffset + 6],
×
864
                                   abyFLIR[nRecOffset + 7]),
×
865
                        "FLIR");
×
866
        SetStringIfNotEmpty("GPSLatitudeRef", nRecOffset + 8, 1);
×
867
        SetStringIfNotEmpty("GPSLongitudeRef", nRecOffset + 10, 1);
×
868
        SetMetadataItem("GPSLatitude",
×
869
                        CPLSPrintf("%.10f", ReadFloat64(nRecOffset + 16)),
×
870
                        "FLIR");
×
871
        SetMetadataItem("GPSLongitude",
×
872
                        CPLSPrintf("%.10f", ReadFloat64(nRecOffset + 24)),
×
873
                        "FLIR");
×
874
        SetMetadataItem("GPSAltitude",
×
875
                        CPLSPrintf("%f", ReadFloat32(nRecOffset + 32)), "FLIR");
×
876
        SetMetadataItem("GPSDOP",
×
877
                        CPLSPrintf("%f", ReadFloat32(nRecOffset + 64)), "FLIR");
×
878
        SetStringIfNotEmpty("GPSSpeedRef", nRecOffset + 68, 1);
×
879
        SetStringIfNotEmpty("GPSTrackRef", nRecOffset + 70, 1);
×
880
        SetMetadataItem("GPSSpeed",
×
881
                        CPLSPrintf("%f", ReadFloat32(nRecOffset + 76)), "FLIR");
×
882
        SetMetadataItem("GPSTrack",
×
883
                        CPLSPrintf("%f", ReadFloat32(nRecOffset + 80)), "FLIR");
×
884
        SetStringIfNotEmpty("GPSMapDatum", nRecOffset + 88, 16);
×
885
    };
4✔
886

887
    size_t nOffsetDirEntry = nOffsetRecordDirectory;
4✔
888

889
    enum FLIRRecordType
890
    {
891
        FLIR_REC_FREE = 0,
892
        FLIR_REC_RAWDATA = 1,
893
        FLIR_REC_CAMERA_INFO = 32,
894
        FLIR_REC_PALETTE_INFO = 34,
895
        FLIR_REC_GPS_INFO = 43,
896
    };
897

898
    // Iterate over records
899
    for (std::uint32_t iRec = 0; iRec < nEntryCountRecordDirectory; iRec++)
60✔
900
    {
901
        const auto nRecType = ReadUInt16(nOffsetDirEntry);
56✔
902
        const auto nRecOffset = ReadUInt32(nOffsetDirEntry + 12);
56✔
903
        const auto nRecLength = ReadUInt32(nOffsetDirEntry + 16);
56✔
904
        if (nRecType == FLIR_REC_FREE && nRecLength == 0)
56✔
905
            continue;  // silently keep empty records of type 0
40✔
906
        CPLDebugOnly("JPEG", "FLIR: record %u, type %u, offset %u, length %u",
16✔
907
                     iRec, nRecType, nRecOffset, nRecLength);
908
        if (nRecOffset + nRecLength > abyFLIR.size())
16✔
909
        {
910
            CPLDebug("JPEG",
×
911
                     "Invalid record %u, type %u, offset %u, length %u "
912
                     "w.r.t total FLIR segment size (%u)",
913
                     iRec, nRecType, nRecOffset, nRecLength,
914
                     static_cast<unsigned>(abyFLIR.size()));
×
915
            continue;
×
916
        }
917
        switch (nRecType)
16✔
918
        {
919
            case FLIR_REC_RAWDATA:
4✔
920
            {
921
                const auto bLittleEndianBackup = bLittleEndian;
4✔
922
                ReadRawData(nRecOffset, nRecLength);
4✔
923
                bLittleEndian = bLittleEndianBackup;
4✔
924
                break;
4✔
925
            }
926
            case FLIR_REC_CAMERA_INFO:
4✔
927
            {
928
                const auto bLittleEndianBackup = bLittleEndian;
4✔
929
                ReadCameraInfo(nRecOffset, nRecLength);
4✔
930
                bLittleEndian = bLittleEndianBackup;
4✔
931
                break;
4✔
932
            }
933
            case FLIR_REC_PALETTE_INFO:
4✔
934
            {
935
                ReadPaletteInfo(nRecOffset, nRecLength);
4✔
936
                break;
4✔
937
            }
938
            case FLIR_REC_GPS_INFO:
×
939
            {
940
                const auto bLittleEndianBackup = bLittleEndian;
×
941
                ReadGPSInfo(nRecOffset, nRecLength);
×
942
                bLittleEndian = bLittleEndianBackup;
×
943
                break;
×
944
            }
945
            default:
4✔
946
            {
947
                CPLDebugOnly("JPEG", "FLIR record ignored");
4✔
948
                break;
4✔
949
            }
950
        }
951
        nOffsetDirEntry += SIZE_RECORD_DIRECTORY;
16✔
952
    }
953

954
    CPLDebug("JPEG", "FLIR metadata read");
4✔
955
}
956

957
/************************************************************************/
958
/*                      GetMetadataDomainList()                         */
959
/************************************************************************/
960

961
char **JPGDatasetCommon::GetMetadataDomainList()
5✔
962
{
963
    ReadFLIRMetadata();
5✔
964
    return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
5✔
965
                                   TRUE, "xml:XMP", "COLOR_PROFILE", "FLIR",
966
                                   nullptr);
5✔
967
}
968

969
/************************************************************************/
970
/*                        LoadForMetadataDomain()                       */
971
/************************************************************************/
972
void JPGDatasetCommon::LoadForMetadataDomain(const char *pszDomain)
3,442✔
973
{
974
    if (m_fpImage == nullptr)
3,442✔
975
        return;
×
976
    if (eAccess == GA_ReadOnly && !bHasReadEXIFMetadata &&
3,442✔
977
        (pszDomain == nullptr || EQUAL(pszDomain, "")))
2,807✔
978
        ReadEXIFMetadata();
76✔
979
    if (eAccess == GA_ReadOnly && !bHasReadImageStructureMetadata &&
3,442✔
980
        pszDomain != nullptr && EQUAL(pszDomain, "IMAGE_STRUCTURE"))
3,434✔
981
        ReadImageStructureMetadata();
5✔
982
    if (eAccess == GA_ReadOnly && pszDomain != nullptr &&
3,442✔
983
        EQUAL(pszDomain, "xml:XMP"))
3,434✔
984
    {
985
        if (!bHasReadXMPMetadata)
144✔
986
        {
987
            ReadXMPMetadata();
17✔
988
        }
989
        if (!bHasReadEXIFMetadata &&
182✔
990
            GDALPamDataset::GetMetadata("xml:XMP") == nullptr)
38✔
991
        {
992
            // XMP can sometimes be embedded in a EXIF TIFF tag
993
            ReadEXIFMetadata();
31✔
994
        }
995
    }
996
    if (eAccess == GA_ReadOnly && !bHasReadICCMetadata &&
3,442✔
997
        pszDomain != nullptr && EQUAL(pszDomain, "COLOR_PROFILE"))
2,882✔
998
        ReadICCProfile();
36✔
999
    if (eAccess == GA_ReadOnly && !bHasReadFLIRMetadata &&
3,442✔
1000
        pszDomain != nullptr && EQUAL(pszDomain, "FLIR"))
3,416✔
1001
        ReadFLIRMetadata();
×
1002
    if (pszDomain != nullptr && EQUAL(pszDomain, "SUBDATASETS"))
3,442✔
1003
        ReadFLIRMetadata();
2✔
1004
}
1005

1006
/************************************************************************/
1007
/*                           GetMetadata()                              */
1008
/************************************************************************/
1009
char **JPGDatasetCommon::GetMetadata(const char *pszDomain)
514✔
1010
{
1011
    LoadForMetadataDomain(pszDomain);
514✔
1012
    return GDALPamDataset::GetMetadata(pszDomain);
514✔
1013
}
1014

1015
/************************************************************************/
1016
/*                       GetMetadataItem()                              */
1017
/************************************************************************/
1018
const char *JPGDatasetCommon::GetMetadataItem(const char *pszName,
3,227✔
1019
                                              const char *pszDomain)
1020
{
1021
    if (pszDomain != nullptr && EQUAL(pszDomain, "IMAGE_STRUCTURE"))
3,227✔
1022
    {
1023
        if (EQUAL(pszName, "JPEG_QUALITY"))
302✔
1024
            LoadForMetadataDomain(pszDomain);
3✔
1025
    }
1026
    else
1027
    {
1028
        LoadForMetadataDomain(pszDomain);
2,925✔
1029
    }
1030
    return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
3,227✔
1031
}
1032

1033
/************************************************************************/
1034
/*                        ReadICCProfile()                              */
1035
/*                                                                      */
1036
/*                 Read ICC Profile from APP2 data                      */
1037
/************************************************************************/
1038
void JPGDatasetCommon::ReadICCProfile()
36✔
1039
{
1040
    if (bHasReadICCMetadata)
36✔
1041
        return;
×
1042
    bHasReadICCMetadata = true;
36✔
1043

1044
    const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
36✔
1045

1046
    int nChunkCount = -1;
36✔
1047
    int anChunkSize[256] = {};
36✔
1048
    char *apChunk[256] = {};
36✔
1049

1050
    // Search for APP2 chunk.
1051
    GByte abyChunkHeader[18] = {};
36✔
1052
    int nChunkLoc = 2;
36✔
1053
    bool bOk = true;
36✔
1054

1055
    while (true)
1056
    {
1057
        if (VSIFSeekL(m_fpImage, nChunkLoc, SEEK_SET) != 0)
351✔
1058
            break;
×
1059

1060
        if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, m_fpImage) !=
351✔
1061
            1)
1062
            break;
3✔
1063

1064
        if (abyChunkHeader[0] != 0xFF)
348✔
1065
            break;  // Not a valid tag
33✔
1066

1067
        if (abyChunkHeader[1] == 0xD9)
315✔
1068
            break;  // End of image
×
1069

1070
        if ((abyChunkHeader[1] >= 0xD0) && (abyChunkHeader[1] <= 0xD8))
315✔
1071
        {
1072
            // Restart tags have no length
1073
            nChunkLoc += 2;
×
1074
            continue;
×
1075
        }
1076

1077
        const int nChunkLength = abyChunkHeader[2] * 256 + abyChunkHeader[3];
315✔
1078

1079
        if (abyChunkHeader[1] == 0xe2 &&
315✔
1080
            memcmp(reinterpret_cast<char *>(abyChunkHeader) + 4,
19✔
1081
                   "ICC_PROFILE\0", 12) == 0)
1082
        {
1083
            // Get length and segment ID
1084
            // Header:
1085
            // APP2 tag: 2 bytes
1086
            // App Length: 2 bytes
1087
            // ICC_PROFILE\0 tag: 12 bytes
1088
            // Segment index: 1 bytes
1089
            // Total segments: 1 bytes
1090
            const int nICCChunkLength = nChunkLength - 16;
19✔
1091
            if (nICCChunkLength < 0)
19✔
1092
            {
1093
                CPLError(CE_Failure, CPLE_FileIO,
×
1094
                         "nICCChunkLength unreasonable: %d", nICCChunkLength);
1095
                bOk = false;
×
1096
                break;
×
1097
            }
1098
            const int nICCChunkID = abyChunkHeader[16];
19✔
1099
            const int nICCMaxChunkID = abyChunkHeader[17];
19✔
1100

1101
            if (nChunkCount == -1)
19✔
1102
                nChunkCount = nICCMaxChunkID;
7✔
1103

1104
            // Check that all max segment counts are the same.
1105
            if (nICCMaxChunkID != nChunkCount)
19✔
1106
            {
1107
                bOk = false;
×
1108
                break;
×
1109
            }
1110

1111
            // Check that no segment ID is larger than the total segment count.
1112
            if ((nICCChunkID > nChunkCount) || (nICCChunkID == 0) ||
19✔
1113
                (nChunkCount == 0))
1114
            {
1115
                bOk = false;
×
1116
                break;
×
1117
            }
1118

1119
            // Check if ICC segment already loaded.
1120
            if (apChunk[nICCChunkID - 1] != nullptr)
19✔
1121
            {
1122
                bOk = false;
×
1123
                break;
×
1124
            }
1125

1126
            // Load it.
1127
            apChunk[nICCChunkID - 1] =
38✔
1128
                static_cast<char *>(VSIMalloc(nICCChunkLength));
19✔
1129
            if (apChunk[nICCChunkID - 1] == nullptr)
19✔
1130
            {
1131
                bOk = false;
×
1132
                break;
×
1133
            }
1134
            anChunkSize[nICCChunkID - 1] = nICCChunkLength;
19✔
1135

1136
            if (VSIFReadL(apChunk[nICCChunkID - 1], nICCChunkLength, 1,
19✔
1137
                          m_fpImage) != 1)
19✔
1138
            {
1139
                bOk = false;
×
1140
                break;
×
1141
            }
1142
        }
1143

1144
        nChunkLoc += 2 + nChunkLength;
315✔
1145
    }
315✔
1146

1147
    int nTotalSize = 0;
36✔
1148

1149
    // Get total size and verify that there are no missing segments.
1150
    if (bOk)
36✔
1151
    {
1152
        for (int i = 0; i < nChunkCount; i++)
55✔
1153
        {
1154
            if (apChunk[i] == nullptr)
19✔
1155
            {
1156
                // Missing segment - abort.
1157
                bOk = false;
×
1158
                break;
×
1159
            }
1160
            const int nSize = anChunkSize[i];
19✔
1161
            if (nSize < 0 ||
38✔
1162
                nTotalSize > std::numeric_limits<int>::max() - nSize)
19✔
1163
            {
1164
                CPLError(CE_Failure, CPLE_FileIO, "nTotalSize nonsensical");
×
1165
                bOk = false;
×
1166
                break;
×
1167
            }
1168
            nTotalSize += anChunkSize[i];
19✔
1169
        }
1170
    }
1171

1172
    // TODO(schwehr): Can we know what the maximum reasonable size is?
1173
    if (nTotalSize > 2 << 28)
36✔
1174
    {
1175
        CPLError(CE_Failure, CPLE_FileIO, "nTotalSize unreasonable: %d",
×
1176
                 nTotalSize);
1177
        bOk = false;
×
1178
    }
1179

1180
    // Merge all segments together and set metadata.
1181
    if (bOk && nChunkCount > 0)
36✔
1182
    {
1183
        char *pBuffer = static_cast<char *>(VSIMalloc(nTotalSize));
7✔
1184
        if (pBuffer == nullptr)
7✔
1185
        {
1186
            CPLError(CE_Failure, CPLE_OutOfMemory,
×
1187
                     "ICCProfile too large.  nTotalSize: %d", nTotalSize);
1188
        }
1189
        else
1190
        {
1191
            char *pBufferPtr = pBuffer;
7✔
1192
            for (int i = 0; i < nChunkCount; i++)
26✔
1193
            {
1194
                memcpy(pBufferPtr, apChunk[i], anChunkSize[i]);
19✔
1195
                pBufferPtr += anChunkSize[i];
19✔
1196
            }
1197

1198
            // Escape the profile.
1199
            char *pszBase64Profile =
1200
                CPLBase64Encode(nTotalSize, reinterpret_cast<GByte *>(pBuffer));
7✔
1201

1202
            // Avoid setting the PAM dirty bit just for that.
1203
            const int nOldPamFlags = nPamFlags;
7✔
1204

1205
            // Set ICC profile metadata.
1206
            SetMetadataItem("SOURCE_ICC_PROFILE", pszBase64Profile,
7✔
1207
                            "COLOR_PROFILE");
7✔
1208

1209
            nPamFlags = nOldPamFlags;
7✔
1210

1211
            VSIFree(pBuffer);
7✔
1212
            CPLFree(pszBase64Profile);
7✔
1213
        }
1214
    }
1215

1216
    for (int i = 0; i < nChunkCount; i++)
55✔
1217
    {
1218
        if (apChunk[i] != nullptr)
19✔
1219
            VSIFree(apChunk[i]);
19✔
1220
    }
1221

1222
    VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
36✔
1223
}
1224

1225
/************************************************************************/
1226
/*                        EXIFInit()                                    */
1227
/*                                                                      */
1228
/*           Create Metadata from Information file directory APP1       */
1229
/************************************************************************/
1230
bool JPGDatasetCommon::EXIFInit(VSILFILE *fp)
2,153✔
1231
{
1232
    if (nTiffDirStart == 0)
2,153✔
1233
        return false;
×
1234
    if (nTiffDirStart > 0)
2,153✔
1235
        return true;
2✔
1236
    nTiffDirStart = 0;
2,151✔
1237

1238
#ifdef CPL_MSB
1239
    constexpr bool bigendian = true;
1240
#else
1241
    constexpr bool bigendian = false;
2,151✔
1242
#endif
1243

1244
    // Search for APP1 chunk.
1245
    GByte abyChunkHeader[10] = {};
2,151✔
1246
    int nChunkLoc = 2;
2,151✔
1247

1248
    while (true)
1249
    {
1250
        if (VSIFSeekL(fp, nChunkLoc, SEEK_SET) != 0)
2,329✔
1251
            return false;
×
1252

1253
        if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, fp) != 1)
2,329✔
1254
            return false;
×
1255

1256
        const int nChunkLength = abyChunkHeader[2] * 256 + abyChunkHeader[3];
2,329✔
1257
        // COM marker
1258
        if (abyChunkHeader[0] == 0xFF && abyChunkHeader[1] == 0xFE &&
2,329✔
1259
            nChunkLength >= 2)
1260
        {
1261
            char *pszComment =
1262
                static_cast<char *>(CPLMalloc(nChunkLength - 2 + 1));
4✔
1263
            if (nChunkLength > 2 &&
8✔
1264
                VSIFSeekL(fp, nChunkLoc + 4, SEEK_SET) == 0 &&
8✔
1265
                VSIFReadL(pszComment, nChunkLength - 2, 1, fp) == 1)
4✔
1266
            {
1267
                pszComment[nChunkLength - 2] = 0;
4✔
1268
                // Avoid setting the PAM dirty bit just for that.
1269
                const int nOldPamFlags = nPamFlags;
4✔
1270
                // Set ICC profile metadata.
1271
                SetMetadataItem("COMMENT", pszComment);
4✔
1272
                nPamFlags = nOldPamFlags;
4✔
1273
            }
1274
            CPLFree(pszComment);
4✔
1275
        }
1276
        else
1277
        {
1278
            if (abyChunkHeader[0] != 0xFF || (abyChunkHeader[1] & 0xf0) != 0xe0)
2,325✔
1279
                break;  // Not an APP chunk.
1280

1281
            if (abyChunkHeader[1] == 0xe1 &&
174✔
1282
                STARTS_WITH(reinterpret_cast<char *>(abyChunkHeader) + 4,
57✔
1283
                            "Exif"))
1284
            {
1285
                if (nTIFFHEADER < 0)
49✔
1286
                {
1287
                    nTIFFHEADER = nChunkLoc + 10;
49✔
1288
                }
1289
                else
1290
                {
1291
                    CPLDebug(
×
1292
                        "JPEG",
1293
                        "Another Exif directory found at offset %u. Ignoring "
1294
                        "it and only taking into account the one at offset %u",
1295
                        unsigned(nChunkLoc + 10), unsigned(nTIFFHEADER));
×
1296
                }
1297
            }
1298
        }
1299

1300
        nChunkLoc += 2 + nChunkLength;
178✔
1301
    }
178✔
1302

1303
    if (nTIFFHEADER < 0)
2,151✔
1304
        return false;
2,102✔
1305

1306
    // Read TIFF header.
1307
    TIFFHeader hdr = {0, 0, 0};
49✔
1308

1309
    VSIFSeekL(fp, nTIFFHEADER, SEEK_SET);
49✔
1310
    if (VSIFReadL(&hdr, 1, sizeof(hdr), fp) != sizeof(hdr))
49✔
1311
    {
1312
        CPLError(CE_Failure, CPLE_FileIO,
×
1313
                 "Failed to read %d byte from image header.",
1314
                 static_cast<int>(sizeof(hdr)));
1315
        return false;
×
1316
    }
1317

1318
    if (hdr.tiff_magic != TIFF_BIGENDIAN && hdr.tiff_magic != TIFF_LITTLEENDIAN)
49✔
1319
    {
1320
        CPLError(CE_Failure, CPLE_AppDefined,
×
1321
                 "Not a TIFF file, bad magic number %u (%#x)", hdr.tiff_magic,
×
1322
                 hdr.tiff_magic);
×
1323
        return false;
×
1324
    }
1325

1326
    if (hdr.tiff_magic == TIFF_BIGENDIAN)
49✔
1327
        bSwabflag = !bigendian;
4✔
1328
    if (hdr.tiff_magic == TIFF_LITTLEENDIAN)
49✔
1329
        bSwabflag = bigendian;
45✔
1330

1331
    if (bSwabflag)
49✔
1332
    {
1333
        CPL_SWAP16PTR(&hdr.tiff_version);
4✔
1334
        CPL_SWAP32PTR(&hdr.tiff_diroff);
4✔
1335
    }
1336

1337
    if (hdr.tiff_version != TIFF_VERSION)
49✔
1338
    {
1339
        CPLError(CE_Failure, CPLE_AppDefined,
×
1340
                 "Not a TIFF file, bad version number %u (%#x)",
1341
                 hdr.tiff_version, hdr.tiff_version);
×
1342
        return false;
×
1343
    }
1344
    nTiffDirStart = hdr.tiff_diroff;
49✔
1345

1346
    CPLDebug("JPEG", "Magic: %#x <%s-endian> Version: %#x\n", hdr.tiff_magic,
49✔
1347
             hdr.tiff_magic == TIFF_BIGENDIAN ? "big" : "little",
49✔
1348
             hdr.tiff_version);
49✔
1349

1350
    return true;
49✔
1351
}
1352

1353
/************************************************************************/
1354
/*                            JPGMaskBand()                             */
1355
/************************************************************************/
1356

1357
JPGMaskBand::JPGMaskBand(JPGDatasetCommon *poDSIn)
15✔
1358

1359
{
1360
    poDS = poDSIn;
15✔
1361
    nBand = 0;
15✔
1362

1363
    nRasterXSize = poDS->GetRasterXSize();
15✔
1364
    nRasterYSize = poDS->GetRasterYSize();
15✔
1365

1366
    eDataType = GDT_Byte;
15✔
1367
    nBlockXSize = nRasterXSize;
15✔
1368
    nBlockYSize = 1;
15✔
1369
}
15✔
1370

1371
/************************************************************************/
1372
/*                             IReadBlock()                             */
1373
/************************************************************************/
1374

1375
CPLErr JPGMaskBand::IReadBlock(int /* nBlockX */, int nBlockY, void *pImage)
4,261✔
1376
{
1377
    JPGDatasetCommon *poJDS = cpl::down_cast<JPGDatasetCommon *>(poDS);
4,261✔
1378

1379
    // Make sure the mask is loaded and decompressed.
1380
    poJDS->DecompressMask();
4,261✔
1381
    if (poJDS->pabyBitMask == nullptr)
4,261✔
1382
        return CE_Failure;
×
1383

1384
    // Set mask based on bitmask for this scanline.
1385
    GUInt32 iBit =
4,261✔
1386
        static_cast<GUInt32>(nBlockY) * static_cast<GUInt32>(nBlockXSize);
4,261✔
1387

1388
    GByte *const pbyImage = static_cast<GByte *>(pImage);
4,261✔
1389
    if (poJDS->bMaskLSBOrder)
4,261✔
1390
    {
1391
        for (int iX = 0; iX < nBlockXSize; iX++)
2,104,220✔
1392
        {
1393
            if (poJDS->pabyBitMask[iBit >> 3] & (0x1 << (iBit & 7)))
2,100,020✔
1394
                pbyImage[iX] = 255;
1,582,040✔
1395
            else
1396
                pbyImage[iX] = 0;
517,974✔
1397
            iBit++;
2,100,020✔
1398
        }
1399
    }
1400
    else
1401
    {
1402
        for (int iX = 0; iX < nBlockXSize; iX++)
1,706✔
1403
        {
1404
            if (poJDS->pabyBitMask[iBit >> 3] & (0x1 << (7 - (iBit & 7))))
1,649✔
1405
                pbyImage[iX] = 255;
584✔
1406
            else
1407
                pbyImage[iX] = 0;
1,065✔
1408
            iBit++;
1,649✔
1409
        }
1410
    }
1411

1412
    return CE_None;
4,261✔
1413
}
1414

1415
/************************************************************************/
1416
/*                           JPGRasterBand()                            */
1417
/************************************************************************/
1418

1419
JPGRasterBand::JPGRasterBand(JPGDatasetCommon *poDSIn, int nBandIn)
25,199✔
1420
    : poGDS(poDSIn)
25,199✔
1421
{
1422
    poDS = poDSIn;
25,199✔
1423

1424
    nBand = nBandIn;
25,199✔
1425
    if (poDSIn->GetDataPrecision() == 12)
25,199✔
1426
        eDataType = GDT_UInt16;
381✔
1427
    else
1428
        eDataType = GDT_Byte;
24,818✔
1429

1430
    nBlockXSize = poDSIn->nRasterXSize;
25,199✔
1431
    nBlockYSize = 1;
25,199✔
1432

1433
    GDALMajorObject::SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
25,199✔
1434
    if (eDataType == GDT_UInt16)
25,199✔
1435
        GDALMajorObject::SetMetadataItem("NBITS", "12", "IMAGE_STRUCTURE");
381✔
1436
}
25,199✔
1437

1438
/************************************************************************/
1439
/*                           JPGCreateBand()                            */
1440
/************************************************************************/
1441

1442
GDALRasterBand *JPGCreateBand(JPGDatasetCommon *poDS, int nBand)
25,199✔
1443
{
1444
    return new JPGRasterBand(poDS, nBand);
25,199✔
1445
}
1446

1447
/************************************************************************/
1448
/*                             IReadBlock()                             */
1449
/************************************************************************/
1450

1451
CPLErr JPGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
196,281✔
1452

1453
{
1454
    CPLAssert(nBlockXOff == 0);
196,281✔
1455

1456
    const int nXSize = GetXSize();
196,281✔
1457
    const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
196,281✔
1458
    if (poGDS->m_fpImage == nullptr)
196,281✔
1459
    {
1460
        memset(pImage, 0, cpl::fits_on<int>(nXSize * nWordSize));
70✔
1461
        return CE_None;
70✔
1462
    }
1463

1464
    // Load the desired scanline into the working buffer.
1465
    CPLErr eErr = poGDS->LoadScanline(nBlockYOff);
196,211✔
1466
    if (eErr != CE_None)
196,211✔
1467
        return eErr;
5✔
1468

1469
    // Transfer between the working buffer the callers buffer.
1470
    if (poGDS->GetRasterCount() == 1)
196,206✔
1471
    {
1472
#ifdef JPEG_LIB_MK1
1473
        GDALCopyWords(poGDS->m_pabyScanline, GDT_UInt16, 2, pImage, eDataType,
1474
                      nWordSize, nXSize);
1475
#else
1476
        memcpy(pImage, poGDS->m_pabyScanline,
32,954✔
1477
               cpl::fits_on<int>(nXSize * nWordSize));
32,954✔
1478
#endif
1479
    }
1480
    else
1481
    {
1482
#ifdef JPEG_LIB_MK1
1483
        GDALCopyWords(poGDS->m_pabyScanline + (nBand - 1) * 2, GDT_UInt16, 6,
1484
                      pImage, eDataType, nWordSize, nXSize);
1485
#else
1486
        if (poGDS->eGDALColorSpace == JCS_RGB &&
485,768✔
1487
            poGDS->GetOutColorSpace() == JCS_CMYK && eDataType == GDT_Byte)
163,252✔
1488
        {
1489
            GByte *const pbyImage = static_cast<GByte *>(pImage);
150✔
1490
            if (nBand == 1)
150✔
1491
            {
1492
                for (int i = 0; i < nXSize; i++)
2,550✔
1493
                {
1494
                    const int C = poGDS->m_pabyScanline[i * 4 + 0];
2,500✔
1495
                    const int K = poGDS->m_pabyScanline[i * 4 + 3];
2,500✔
1496
                    pbyImage[i] = static_cast<GByte>((C * K) / 255);
2,500✔
1497
                }
1498
            }
1499
            else if (nBand == 2)
100✔
1500
            {
1501
                for (int i = 0; i < nXSize; i++)
2,550✔
1502
                {
1503
                    const int M = poGDS->m_pabyScanline[i * 4 + 1];
2,500✔
1504
                    const int K = poGDS->m_pabyScanline[i * 4 + 3];
2,500✔
1505
                    pbyImage[i] = static_cast<GByte>((M * K) / 255);
2,500✔
1506
                }
1507
            }
1508
            else if (nBand == 3)
50✔
1509
            {
1510
                for (int i = 0; i < nXSize; i++)
2,550✔
1511
                {
1512
                    const int Y = poGDS->m_pabyScanline[i * 4 + 2];
2,500✔
1513
                    const int K = poGDS->m_pabyScanline[i * 4 + 3];
2,500✔
1514
                    pbyImage[i] = static_cast<GByte>((Y * K) / 255);
2,500✔
1515
                }
1516
            }
1517
        }
1518
        else
1519
        {
1520
            GDALCopyWords(poGDS->m_pabyScanline + (nBand - 1) * nWordSize,
326,204✔
1521
                          eDataType, nWordSize * poGDS->GetRasterCount(),
163,102✔
1522
                          pImage, eDataType, nWordSize, nXSize);
1523
        }
1524
#endif
1525
    }
1526

1527
    // Forcibly load the other bands associated with this scanline.
1528
    if (nBand == 1)
196,206✔
1529
    {
1530
        for (int iBand = 2; iBand <= poGDS->GetRasterCount(); iBand++)
185,824✔
1531
        {
1532
            GDALRasterBlock *const poBlock =
1533
                poGDS->GetRasterBand(iBand)->GetLockedBlockRef(nBlockXOff,
102,228✔
1534
                                                               nBlockYOff);
102,228✔
1535
            if (poBlock != nullptr)
102,228✔
1536
                poBlock->DropLock();
102,228✔
1537
        }
1538
    }
1539

1540
    return CE_None;
196,206✔
1541
}
1542

1543
/************************************************************************/
1544
/*                       GetColorInterpretation()                       */
1545
/************************************************************************/
1546

1547
GDALColorInterp JPGRasterBand::GetColorInterpretation()
470✔
1548

1549
{
1550
    if (poGDS->eGDALColorSpace == JCS_GRAYSCALE)
470✔
1551
        return GCI_GrayIndex;
91✔
1552

1553
    else if (poGDS->eGDALColorSpace == JCS_RGB)
379✔
1554
    {
1555
        if (nBand == 1)
255✔
1556
            return GCI_RedBand;
86✔
1557
        else if (nBand == 2)
169✔
1558
            return GCI_GreenBand;
80✔
1559
        else
1560
            return GCI_BlueBand;
89✔
1561
    }
1562
    else if (poGDS->eGDALColorSpace == JCS_CMYK)
124✔
1563
    {
1564
        if (nBand == 1)
124✔
1565
            return GCI_CyanBand;
30✔
1566
        else if (nBand == 2)
94✔
1567
            return GCI_MagentaBand;
26✔
1568
        else if (nBand == 3)
68✔
1569
            return GCI_YellowBand;
26✔
1570
        else
1571
            return GCI_BlackBand;
42✔
1572
    }
1573
    else if (poGDS->eGDALColorSpace == JCS_YCbCr ||
×
1574
             poGDS->eGDALColorSpace == JCS_YCCK)
×
1575
    {
1576
        if (nBand == 1)
×
1577
            return GCI_YCbCr_YBand;
×
1578
        else if (nBand == 2)
×
1579
            return GCI_YCbCr_CbBand;
×
1580
        else if (nBand == 3)
×
1581
            return GCI_YCbCr_CrBand;
×
1582
        else
1583
            return GCI_BlackBand;
×
1584
    }
1585

1586
    CPLAssert(false);
×
1587
    return GCI_Undefined;
1588
}
1589

1590
/************************************************************************/
1591
/*                            GetMaskBand()                             */
1592
/************************************************************************/
1593

1594
GDALRasterBand *JPGRasterBand::GetMaskBand()
301✔
1595

1596
{
1597
    if (poGDS->nScaleFactor > 1)
301✔
1598
        return GDALPamRasterBand::GetMaskBand();
×
1599

1600
    if (poGDS->m_fpImage == nullptr)
301✔
1601
        return nullptr;
×
1602

1603
    if (!poGDS->bHasCheckedForMask)
301✔
1604
    {
1605
        if (CPLTestBool(CPLGetConfigOption("JPEG_READ_MASK", "YES")))
62✔
1606
            poGDS->CheckForMask();
62✔
1607
        poGDS->bHasCheckedForMask = true;
62✔
1608
    }
1609
    if (poGDS->pabyCMask)
301✔
1610
    {
1611
        if (poGDS->poMaskBand == nullptr)
42✔
1612
            poGDS->poMaskBand = new JPGMaskBand(poGDS);
15✔
1613

1614
        return poGDS->poMaskBand;
42✔
1615
    }
1616

1617
    return GDALPamRasterBand::GetMaskBand();
259✔
1618
}
1619

1620
/************************************************************************/
1621
/*                            GetMaskFlags()                            */
1622
/************************************************************************/
1623

1624
int JPGRasterBand::GetMaskFlags()
281✔
1625

1626
{
1627
    if (poGDS->nScaleFactor > 1)
281✔
1628
        return GDALPamRasterBand::GetMaskFlags();
×
1629

1630
    if (poGDS->m_fpImage == nullptr)
281✔
1631
        return 0;
×
1632

1633
    GetMaskBand();
281✔
1634
    if (poGDS->poMaskBand != nullptr)
281✔
1635
        return GMF_PER_DATASET;
25✔
1636

1637
    return GDALPamRasterBand::GetMaskFlags();
256✔
1638
}
1639

1640
/************************************************************************/
1641
/*                            GetOverview()                             */
1642
/************************************************************************/
1643

1644
GDALRasterBand *JPGRasterBand::GetOverview(int i)
7,934✔
1645
{
1646
    if (i < 0 || i >= GetOverviewCount())
7,934✔
1647
        return nullptr;
2✔
1648

1649
    if (poGDS->nInternalOverviewsCurrent == 0)
7,932✔
1650
        return GDALPamRasterBand::GetOverview(i);
2✔
1651

1652
    return poGDS->papoInternalOverviews[i]->GetRasterBand(nBand);
7,930✔
1653
}
1654

1655
/************************************************************************/
1656
/*                         GetOverviewCount()                           */
1657
/************************************************************************/
1658

1659
int JPGRasterBand::GetOverviewCount()
18,427✔
1660
{
1661
    if (!poGDS->AreOverviewsEnabled())
18,427✔
1662
        return 0;
×
1663

1664
    poGDS->InitInternalOverviews();
18,427✔
1665

1666
    if (poGDS->nInternalOverviewsCurrent == 0)
18,427✔
1667
        return GDALPamRasterBand::GetOverviewCount();
2,502✔
1668

1669
    return poGDS->nInternalOverviewsCurrent;
15,925✔
1670
}
1671

1672
/************************************************************************/
1673
/* ==================================================================== */
1674
/*                             JPGDataset                               */
1675
/* ==================================================================== */
1676
/************************************************************************/
1677

1678
JPGDatasetCommon::JPGDatasetCommon()
11,240✔
1679
    : nScaleFactor(1), bHasInitInternalOverviews(false),
1680
      nInternalOverviewsCurrent(0), nInternalOverviewsToFree(0),
1681
      papoInternalOverviews(nullptr), bGeoTransformValid(false),
1682
      m_fpImage(nullptr), nSubfileOffset(0), nLoadedScanline(-1),
1683
      m_pabyScanline(nullptr), bHasReadEXIFMetadata(false),
1684
      bHasReadXMPMetadata(false), bHasReadICCMetadata(false),
1685
      papszMetadata(nullptr), nExifOffset(-1), nInterOffset(-1), nGPSOffset(-1),
1686
      bSwabflag(false), nTiffDirStart(-1), nTIFFHEADER(-1),
1687
      bHasDoneJpegCreateDecompress(false), bHasDoneJpegStartDecompress(false),
1688
      bHasCheckedForMask(false), poMaskBand(nullptr), pabyBitMask(nullptr),
1689
      bMaskLSBOrder(true), pabyCMask(nullptr), nCMaskSize(0),
1690
      eGDALColorSpace(JCS_UNKNOWN), bIsSubfile(false),
1691
      bHasTriedLoadWorldFileOrTab(false)
11,240✔
1692
{
1693
    adfGeoTransform[0] = 0.0;
11,240✔
1694
    adfGeoTransform[1] = 1.0;
11,240✔
1695
    adfGeoTransform[2] = 0.0;
11,240✔
1696
    adfGeoTransform[3] = 0.0;
11,240✔
1697
    adfGeoTransform[4] = 0.0;
11,240✔
1698
    adfGeoTransform[5] = 1.0;
11,240✔
1699
}
11,240✔
1700

1701
/************************************************************************/
1702
/*                           ~JPGDataset()                              */
1703
/************************************************************************/
1704

1705
JPGDatasetCommon::~JPGDatasetCommon()
11,240✔
1706

1707
{
1708
    if (m_fpImage != nullptr)
11,240✔
1709
        VSIFCloseL(m_fpImage);
11,037✔
1710

1711
    if (m_pabyScanline != nullptr)
11,240✔
1712
        CPLFree(m_pabyScanline);
3,161✔
1713
    if (papszMetadata != nullptr)
11,240✔
1714
        CSLDestroy(papszMetadata);
44✔
1715

1716
    CPLFree(pabyBitMask);
11,240✔
1717
    CPLFree(pabyCMask);
11,240✔
1718
    delete poMaskBand;
11,240✔
1719

1720
    JPGDatasetCommon::CloseDependentDatasets();
11,240✔
1721
}
11,240✔
1722

1723
/************************************************************************/
1724
/*                       CloseDependentDatasets()                       */
1725
/************************************************************************/
1726

1727
int JPGDatasetCommon::CloseDependentDatasets()
11,240✔
1728
{
1729
    int bRet = GDALPamDataset::CloseDependentDatasets();
11,240✔
1730
    if (nInternalOverviewsToFree)
11,240✔
1731
    {
1732
        bRet = TRUE;
2,472✔
1733
        for (int i = 0; i < nInternalOverviewsToFree; i++)
9,888✔
1734
            delete papoInternalOverviews[i];
7,416✔
1735
        nInternalOverviewsToFree = 0;
2,472✔
1736
    }
1737
    CPLFree(papoInternalOverviews);
11,240✔
1738
    papoInternalOverviews = nullptr;
11,240✔
1739

1740
    return bRet;
11,240✔
1741
}
1742

1743
/************************************************************************/
1744
/*                          InitEXIFOverview()                          */
1745
/************************************************************************/
1746

1747
GDALDataset *JPGDatasetCommon::InitEXIFOverview()
2,046✔
1748
{
1749
    if (!EXIFInit(m_fpImage))
2,046✔
1750
        return nullptr;
2,039✔
1751

1752
    // Read number of entry in directory.
1753
    GUInt16 nEntryCount = 0;
7✔
1754
    if (nTiffDirStart > (INT_MAX - nTIFFHEADER) ||
21✔
1755
        VSIFSeekL(m_fpImage, nTiffDirStart + nTIFFHEADER, SEEK_SET) != 0 ||
14✔
1756
        VSIFReadL(&nEntryCount, 1, sizeof(GUInt16), m_fpImage) !=
7✔
1757
            sizeof(GUInt16))
1758
    {
1759
        CPLError(CE_Failure, CPLE_AppDefined,
×
1760
                 "Error reading EXIF Directory count at " CPL_FRMT_GUIB,
1761
                 static_cast<vsi_l_offset>(nTiffDirStart) + nTIFFHEADER);
×
1762
        return nullptr;
×
1763
    }
1764

1765
    if (bSwabflag)
7✔
1766
        CPL_SWAP16PTR(&nEntryCount);
1✔
1767

1768
    // Some files are corrupt, a large entry count is a sign of this.
1769
    if (nEntryCount > 125)
7✔
1770
    {
1771
        CPLError(CE_Warning, CPLE_AppDefined,
×
1772
                 "Ignoring EXIF directory with unlikely entry count (%d).",
1773
                 nEntryCount);
1774
        return nullptr;
×
1775
    }
1776

1777
    // Skip EXIF entries.
1778
    VSIFSeekL(m_fpImage, nEntryCount * sizeof(GDALEXIFTIFFDirEntry), SEEK_CUR);
7✔
1779

1780
    // Read offset of next directory (IFD1).
1781
    GUInt32 nNextDirOff = 0;
7✔
1782
    if (VSIFReadL(&nNextDirOff, 1, sizeof(GUInt32), m_fpImage) !=
7✔
1783
        sizeof(GUInt32))
1784
        return nullptr;
×
1785
    if (bSwabflag)
7✔
1786
        CPL_SWAP32PTR(&nNextDirOff);
1✔
1787
    if (nNextDirOff == 0 || nNextDirOff > UINT_MAX - nTIFFHEADER)
7✔
1788
        return nullptr;
×
1789

1790
    // Seek to IFD1.
1791
    if (VSIFSeekL(m_fpImage, nTIFFHEADER + nNextDirOff, SEEK_SET) != 0 ||
14✔
1792
        VSIFReadL(&nEntryCount, 1, sizeof(GUInt16), m_fpImage) !=
7✔
1793
            sizeof(GUInt16))
1794
    {
1795
        CPLError(CE_Failure, CPLE_AppDefined,
×
1796
                 "Error reading IFD1 Directory count at %d.",
1797
                 nTIFFHEADER + nNextDirOff);
×
1798
        return nullptr;
×
1799
    }
1800

1801
    if (bSwabflag)
7✔
1802
        CPL_SWAP16PTR(&nEntryCount);
1✔
1803
    if (nEntryCount > 125)
7✔
1804
    {
1805
        CPLError(CE_Warning, CPLE_AppDefined,
×
1806
                 "Ignoring IFD1 directory with unlikely entry count (%d).",
1807
                 nEntryCount);
1808
        return nullptr;
×
1809
    }
1810
#if DEBUG_VERBOSE
1811
    CPLDebug("JPEG", "IFD1 entry count = %d", nEntryCount);
1812
#endif
1813

1814
    int nImageWidth = 0;
7✔
1815
    int nImageHeight = 0;
7✔
1816
    int nCompression = 6;
7✔
1817
    GUInt32 nJpegIFOffset = 0;
7✔
1818
    GUInt32 nJpegIFByteCount = 0;
7✔
1819
    for (int i = 0; i < nEntryCount; i++)
39✔
1820
    {
1821
        GDALEXIFTIFFDirEntry sEntry;
1822
        if (VSIFReadL(&sEntry, 1, sizeof(sEntry), m_fpImage) != sizeof(sEntry))
32✔
1823
        {
1824
            CPLError(CE_Warning, CPLE_AppDefined,
×
1825
                     "Cannot read entry %d of IFD1", i);
1826
            return nullptr;
×
1827
        }
1828
        if (bSwabflag)
32✔
1829
        {
1830
            CPL_SWAP16PTR(&sEntry.tdir_tag);
2✔
1831
            CPL_SWAP16PTR(&sEntry.tdir_type);
2✔
1832
            CPL_SWAP32PTR(&sEntry.tdir_count);
2✔
1833
            CPL_SWAP32PTR(&sEntry.tdir_offset);
2✔
1834
        }
1835

1836
#ifdef DEBUG_VERBOSE
1837
        CPLDebug("JPEG", "tag = %d (0x%4X), type = %d, count = %d, offset = %d",
1838
                 sEntry.tdir_tag, sEntry.tdir_tag, sEntry.tdir_type,
1839
                 sEntry.tdir_count, sEntry.tdir_offset);
1840
#endif
1841

1842
        if ((sEntry.tdir_type == TIFF_SHORT || sEntry.tdir_type == TIFF_LONG) &&
32✔
1843
            sEntry.tdir_count == 1)
32✔
1844
        {
1845
            switch (sEntry.tdir_tag)
32✔
1846
            {
1847
                case JPEG_TIFF_IMAGEWIDTH:
6✔
1848
                    nImageWidth = sEntry.tdir_offset;
6✔
1849
                    break;
6✔
1850
                case JPEG_TIFF_IMAGEHEIGHT:
6✔
1851
                    nImageHeight = sEntry.tdir_offset;
6✔
1852
                    break;
6✔
1853
                case JPEG_TIFF_COMPRESSION:
6✔
1854
                    nCompression = sEntry.tdir_offset;
6✔
1855
                    break;
6✔
1856
                case JPEG_EXIF_JPEGIFOFSET:
7✔
1857
                    nJpegIFOffset = sEntry.tdir_offset;
7✔
1858
                    break;
7✔
1859
                case JPEG_EXIF_JPEGIFBYTECOUNT:
7✔
1860
                    nJpegIFByteCount = sEntry.tdir_offset;
7✔
1861
                    break;
7✔
1862
                default:
×
1863
                    break;
×
1864
            }
1865
        }
1866
    }
1867
    if (nCompression != 6 || nImageWidth >= nRasterXSize ||
7✔
1868
        nImageHeight >= nRasterYSize || nJpegIFOffset == 0 ||
7✔
1869
        nJpegIFOffset > UINT_MAX - nTIFFHEADER ||
7✔
1870
        static_cast<int>(nJpegIFByteCount) <= 0)
7✔
1871
    {
1872
        return nullptr;
×
1873
    }
1874

1875
    const char *pszSubfile =
1876
        CPLSPrintf("JPEG_SUBFILE:%u,%d,%s", nTIFFHEADER + nJpegIFOffset,
7✔
1877
                   nJpegIFByteCount, GetDescription());
7✔
1878
    JPGDatasetOpenArgs sArgs;
7✔
1879
    sArgs.pszFilename = pszSubfile;
7✔
1880
    return JPGDataset::Open(&sArgs);
7✔
1881
}
1882

1883
/************************************************************************/
1884
/*                       InitInternalOverviews()                        */
1885
/************************************************************************/
1886

1887
void JPGDatasetCommon::InitInternalOverviews()
18,427✔
1888
{
1889
    if (bHasInitInternalOverviews)
18,427✔
1890
        return;
15,945✔
1891
    bHasInitInternalOverviews = true;
2,482✔
1892

1893
    // Instantiate on-the-fly overviews (if no external ones).
1894
    if (nScaleFactor == 1 && GetRasterBand(1)->GetOverviewCount() == 0)
2,482✔
1895
    {
1896
        // EXIF overview.
1897
        GDALDataset *poEXIFOverview = nullptr;
2,481✔
1898
        if (nRasterXSize > 512 || nRasterYSize > 512)
2,481✔
1899
        {
1900
            const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
2,046✔
1901
            poEXIFOverview = InitEXIFOverview();
2,046✔
1902
            if (poEXIFOverview != nullptr)
2,046✔
1903
            {
1904
                if (poEXIFOverview->GetRasterCount() != nBands ||
7✔
1905
                    poEXIFOverview->GetRasterXSize() >= nRasterXSize ||
14✔
1906
                    poEXIFOverview->GetRasterYSize() >= nRasterYSize)
7✔
1907
                {
1908
                    GDALClose(poEXIFOverview);
×
1909
                    poEXIFOverview = nullptr;
×
1910
                }
1911
                else
1912
                {
1913
                    CPLDebug("JPEG", "EXIF overview (%d x %d) detected",
7✔
1914
                             poEXIFOverview->GetRasterXSize(),
1915
                             poEXIFOverview->GetRasterYSize());
1916
                }
1917
            }
1918
            VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
2,046✔
1919
        }
1920

1921
        // libjpeg-6b only supports 2, 4 and 8 scale denominators.
1922
        // TODO: Later versions support more.
1923

1924
        int nImplicitOverviews = 0;
2,481✔
1925

1926
        // For the needs of the implicit JPEG-in-TIFF overview mechanism.
1927
        if (CPLTestBool(
2,481✔
1928
                CPLGetConfigOption("JPEG_FORCE_INTERNAL_OVERVIEWS", "NO")))
1929
        {
1930
            nImplicitOverviews = 3;
2,459✔
1931
        }
1932
        else
1933
        {
1934
            for (int i = 2; i >= 0; i--)
55✔
1935
            {
1936
                if (nRasterXSize >= (256 << i) || nRasterYSize >= (256 << i))
46✔
1937
                {
1938
                    nImplicitOverviews = i + 1;
13✔
1939
                    break;
13✔
1940
                }
1941
            }
1942
        }
1943

1944
        if (nImplicitOverviews > 0)
2,481✔
1945
        {
1946
            ppoActiveDS = &poActiveDS;
2,472✔
1947
            papoInternalOverviews = static_cast<GDALDataset **>(
2,472✔
1948
                CPLMalloc((nImplicitOverviews + (poEXIFOverview ? 1 : 0)) *
2,472✔
1949
                          sizeof(GDALDataset *)));
1950
            for (int i = 0; i < nImplicitOverviews; i++)
9,881✔
1951
            {
1952
                if (poEXIFOverview != nullptr &&
7,431✔
1953
                    poEXIFOverview->GetRasterXSize() >= nRasterXSize >> (i + 1))
21✔
1954
                {
1955
                    break;
1✔
1956
                }
1957
                JPGDatasetOpenArgs sArgs;
7,409✔
1958
                sArgs.pszFilename = GetDescription();
7,409✔
1959
                sArgs.nScaleFactor = 1 << (i + 1);
7,409✔
1960
                JPGDatasetCommon *poImplicitOverview = JPGDataset::Open(&sArgs);
7,409✔
1961
                if (poImplicitOverview == nullptr)
7,409✔
1962
                    break;
×
1963
                poImplicitOverview->ppoActiveDS = &poActiveDS;
7,409✔
1964
                papoInternalOverviews[nInternalOverviewsCurrent] =
7,409✔
1965
                    poImplicitOverview;
1966
                nInternalOverviewsCurrent++;
7,409✔
1967
                nInternalOverviewsToFree++;
7,409✔
1968
            }
1969
            if (poEXIFOverview != nullptr)
2,472✔
1970
            {
1971
                papoInternalOverviews[nInternalOverviewsCurrent] =
7✔
1972
                    poEXIFOverview;
1973
                nInternalOverviewsCurrent++;
7✔
1974
                nInternalOverviewsToFree++;
7✔
1975
            }
1976
        }
1977
        else if (poEXIFOverview)
9✔
1978
        {
1979
            papoInternalOverviews =
×
1980
                static_cast<GDALDataset **>(CPLMalloc(sizeof(GDALDataset *)));
×
1981
            papoInternalOverviews[0] = poEXIFOverview;
×
1982
            nInternalOverviewsCurrent++;
×
1983
            nInternalOverviewsToFree++;
×
1984
        }
1985
    }
1986
}
1987

1988
/************************************************************************/
1989
/*                          IBuildOverviews()                           */
1990
/************************************************************************/
1991

1992
CPLErr JPGDatasetCommon::IBuildOverviews(const char *pszResampling,
1✔
1993
                                         int nOverviewsListCount,
1994
                                         const int *panOverviewList,
1995
                                         int nListBands, const int *panBandList,
1996
                                         GDALProgressFunc pfnProgress,
1997
                                         void *pProgressData,
1998
                                         CSLConstList papszOptions)
1999
{
2000
    bHasInitInternalOverviews = true;
1✔
2001
    nInternalOverviewsCurrent = 0;
1✔
2002

2003
    return GDALPamDataset::IBuildOverviews(
1✔
2004
        pszResampling, nOverviewsListCount, panOverviewList, nListBands,
2005
        panBandList, pfnProgress, pProgressData, papszOptions);
1✔
2006
}
2007

2008
/************************************************************************/
2009
/*                           FlushCache()                               */
2010
/************************************************************************/
2011

2012
CPLErr JPGDatasetCommon::FlushCache(bool bAtClosing)
5✔
2013

2014
{
2015
    CPLErr eErr = GDALPamDataset::FlushCache(bAtClosing);
5✔
2016

2017
    if (bHasDoneJpegStartDecompress)
5✔
2018
    {
2019
        Restart();
×
2020
    }
2021

2022
    // For the needs of the implicit JPEG-in-TIFF overview mechanism.
2023
    for (int i = 0; i < nInternalOverviewsCurrent; i++)
5✔
2024
    {
2025
        if (papoInternalOverviews[i]->FlushCache(bAtClosing) != CE_None)
×
2026
            eErr = CE_Failure;
×
2027
    }
2028
    return eErr;
5✔
2029
}
2030

2031
#endif  // !defined(JPGDataset)
2032

2033
/************************************************************************/
2034
/*                            JPGDataset()                              */
2035
/************************************************************************/
2036

2037
JPGDataset::JPGDataset() : nQLevel(0)
11,240✔
2038
{
2039
    memset(&sDInfo, 0, sizeof(sDInfo));
11,240✔
2040
    sDInfo.data_precision = 8;
11,240✔
2041

2042
    memset(&sJErr, 0, sizeof(sJErr));
11,240✔
2043
    memset(&sJProgress, 0, sizeof(sJProgress));
11,240✔
2044
}
11,240✔
2045

2046
/************************************************************************/
2047
/*                           ~JPGDataset()                            */
2048
/************************************************************************/
2049

2050
JPGDataset::~JPGDataset()
22,480✔
2051

2052
{
2053
    GDALPamDataset::FlushCache(true);
11,240✔
2054
    JPGDataset::StopDecompress();
11,240✔
2055
}
22,480✔
2056

2057
/************************************************************************/
2058
/*                           StopDecompress()                           */
2059
/************************************************************************/
2060

2061
void JPGDataset::StopDecompress()
11,924✔
2062
{
2063
    if (bHasDoneJpegStartDecompress)
11,924✔
2064
    {
2065
        jpeg_abort_decompress(&sDInfo);
3,569✔
2066
        bHasDoneJpegStartDecompress = false;
3,569✔
2067
    }
2068
    if (bHasDoneJpegCreateDecompress)
11,924✔
2069
    {
2070
        jpeg_destroy_decompress(&sDInfo);
11,853✔
2071
        bHasDoneJpegCreateDecompress = false;
11,853✔
2072
    }
2073
    nLoadedScanline = INT_MAX;
11,924✔
2074
    if (ppoActiveDS)
11,924✔
2075
        *ppoActiveDS = nullptr;
10,182✔
2076
}
11,924✔
2077

2078
/************************************************************************/
2079
/*                      ErrorOutOnNonFatalError()                       */
2080
/************************************************************************/
2081

2082
bool JPGDataset::ErrorOutOnNonFatalError()
111,729✔
2083
{
2084
    if (sUserData.bNonFatalErrorEncountered)
111,729✔
2085
    {
2086
        sUserData.bNonFatalErrorEncountered = false;
4✔
2087
        return true;
4✔
2088
    }
2089
    return false;
111,725✔
2090
}
2091

2092
/************************************************************************/
2093
/*                          StartDecompress()                           */
2094
/************************************************************************/
2095

2096
CPLErr JPGDataset::StartDecompress()
3,571✔
2097
{
2098
    /* In some cases, libjpeg needs to allocate a lot of memory */
2099
    /* http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf
2100
     */
2101
    if (jpeg_has_multiple_scans(&(sDInfo)))
3,571✔
2102
    {
2103
        /* In this case libjpeg will need to allocate memory or backing */
2104
        /* store for all coefficients */
2105
        /* See call to jinit_d_coef_controller() from master_selection() */
2106
        /* in libjpeg */
2107

2108
        // 1 MB for regular libjpeg usage
2109
        vsi_l_offset nRequiredMemory = 1024 * 1024;
9✔
2110

2111
        for (int ci = 0; ci < sDInfo.num_components; ci++)
18✔
2112
        {
2113
            const jpeg_component_info *compptr = &(sDInfo.comp_info[ci]);
9✔
2114
            if (compptr->h_samp_factor <= 0 || compptr->v_samp_factor <= 0)
9✔
2115
            {
2116
                CPLError(CE_Failure, CPLE_AppDefined,
×
2117
                         "Invalid sampling factor(s)");
2118
                return CE_Failure;
×
2119
            }
2120
            nRequiredMemory +=
9✔
2121
                static_cast<vsi_l_offset>(DIV_ROUND_UP(
9✔
2122
                    compptr->width_in_blocks, compptr->h_samp_factor)) *
9✔
2123
                DIV_ROUND_UP(compptr->height_in_blocks,
9✔
2124
                             compptr->v_samp_factor) *
9✔
2125
                sizeof(JBLOCK);
2126
        }
2127

2128
        if (nRequiredMemory > 10 * 1024 * 1024 && ppoActiveDS &&
9✔
2129
            *ppoActiveDS != this)
4✔
2130
        {
2131
            // If another overview was active, stop it to limit memory
2132
            // consumption
2133
            if (*ppoActiveDS)
4✔
2134
                (*ppoActiveDS)->StopDecompress();
1✔
2135
            *ppoActiveDS = this;
4✔
2136
        }
2137

2138
        if (sDInfo.mem->max_memory_to_use > 0 &&
27✔
2139
            nRequiredMemory >
2140
                static_cast<vsi_l_offset>(sDInfo.mem->max_memory_to_use) &&
10✔
2141
            CPLGetConfigOption("GDAL_ALLOW_LARGE_LIBJPEG_MEM_ALLOC", nullptr) ==
1✔
2142
                nullptr)
2143
        {
2144
            CPLError(CE_Failure, CPLE_NotSupported,
1✔
2145
                     "Reading this image would require libjpeg to allocate "
2146
                     "at least " CPL_FRMT_GUIB " bytes. "
2147
                     "This is disabled since above the " CPL_FRMT_GUIB
2148
                     " threshold. "
2149
                     "You may override this restriction by defining the "
2150
                     "GDAL_ALLOW_LARGE_LIBJPEG_MEM_ALLOC environment variable, "
2151
                     "or setting the JPEGMEM environment variable to a value "
2152
                     "greater "
2153
                     "or equal to '" CPL_FRMT_GUIB "M'",
2154
                     static_cast<GUIntBig>(nRequiredMemory),
2155
                     static_cast<GUIntBig>(sDInfo.mem->max_memory_to_use),
1✔
2156
                     static_cast<GUIntBig>((nRequiredMemory + 1000000 - 1) /
1✔
2157
                                           1000000));
2158
            return CE_Failure;
1✔
2159
        }
2160
    }
2161

2162
    sDInfo.progress = &sJProgress;
3,570✔
2163
    sJProgress.progress_monitor = JPGDataset::ProgressMonitor;
3,570✔
2164
    jpeg_start_decompress(&sDInfo);
3,570✔
2165
    bHasDoneJpegStartDecompress = true;
3,569✔
2166

2167
    return CE_None;
3,569✔
2168
}
2169

2170
/************************************************************************/
2171
/*                            LoadScanline()                            */
2172
/************************************************************************/
2173

2174
CPLErr JPGDataset::LoadScanline(int iLine, GByte *outBuffer)
211,874✔
2175

2176
{
2177
    if (nLoadedScanline == iLine)
211,874✔
2178
        return CE_None;
102,180✔
2179

2180
    // code path triggered when an active reader has been stopped by another
2181
    // one, in case of multiple scans datasets and overviews
2182
    if (!bHasDoneJpegCreateDecompress && Restart() != CE_None)
109,694✔
2183
        return CE_Failure;
×
2184

2185
    // setup to trap a fatal error.
2186
    if (setjmp(sUserData.setjmp_buffer))
109,694✔
2187
        return CE_Failure;
1✔
2188

2189
    if (!bHasDoneJpegStartDecompress && StartDecompress() != CE_None)
109,694✔
2190
        return CE_Failure;
1✔
2191

2192
    if (outBuffer == nullptr && m_pabyScanline == nullptr)
109,692✔
2193
    {
2194
        int nJPEGBands = 0;
3,161✔
2195
        switch (sDInfo.out_color_space)
3,161✔
2196
        {
2197
            case JCS_GRAYSCALE:
1,141✔
2198
                nJPEGBands = 1;
1,141✔
2199
                break;
1,141✔
2200
            case JCS_RGB:
1,912✔
2201
            case JCS_YCbCr:
2202
                nJPEGBands = 3;
1,912✔
2203
                break;
1,912✔
2204
            case JCS_CMYK:
108✔
2205
            case JCS_YCCK:
2206
                nJPEGBands = 4;
108✔
2207
                break;
108✔
2208

2209
            default:
×
2210
                CPLAssert(false);
×
2211
        }
2212

2213
        m_pabyScanline = static_cast<GByte *>(
3,161✔
2214
            CPLMalloc(cpl::fits_on<int>(nJPEGBands * GetRasterXSize() * 2)));
3,161✔
2215
    }
2216

2217
    if (iLine < nLoadedScanline)
109,692✔
2218
    {
2219
        if (Restart() != CE_None)
296✔
2220
            return CE_Failure;
×
2221
    }
2222

2223
    while (nLoadedScanline < iLine)
221,417✔
2224
    {
2225
        GDAL_JSAMPLE *ppSamples = reinterpret_cast<GDAL_JSAMPLE *>(
111,729✔
2226
            outBuffer ? outBuffer : m_pabyScanline);
80,539✔
2227
#if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
2228
        jpeg12_read_scanlines(&sDInfo, &ppSamples, 1);
2229
#else
2230
        jpeg_read_scanlines(&sDInfo, &ppSamples, 1);
111,729✔
2231
#endif
2232
        if (ErrorOutOnNonFatalError())
111,729✔
2233
            return CE_Failure;
4✔
2234
        nLoadedScanline++;
111,725✔
2235
    }
2236

2237
    return CE_None;
109,688✔
2238
}
2239

2240
/************************************************************************/
2241
/*                         LoadDefaultTables()                          */
2242
/************************************************************************/
2243

2244
#if !defined(JPGDataset)
2245

2246
#define Q1table GDALJPEG_Q1table
2247
#define Q2table GDALJPEG_Q2table
2248
#define Q3table GDALJPEG_Q3table
2249
#define Q4table GDALJPEG_Q4table
2250
#define Q5table GDALJPEG_Q5table
2251
#define AC_BITS GDALJPEG_AC_BITS
2252
#define AC_HUFFVAL GDALJPEG_AC_HUFFVAL
2253
#define DC_BITS GDALJPEG_DC_BITS
2254
#define DC_HUFFVAL GDALJPEG_DC_HUFFVAL
2255

2256
constexpr GByte Q1table[64] = {
2257
    8,   72,  72,  72,  72,  72,  72,  72,   // 0 - 7
2258
    72,  72,  78,  74,  76,  74,  78,  89,   // 8 - 15
2259
    81,  84,  84,  81,  89,  106, 93,  94,   // 16 - 23
2260
    99,  94,  93,  106, 129, 111, 108, 116,  // 24 - 31
2261
    116, 108, 111, 129, 135, 128, 136, 145,  // 32 - 39
2262
    136, 128, 135, 155, 160, 177, 177, 160,  // 40 - 47
2263
    155, 193, 213, 228, 213, 193, 255, 255,  // 48 - 55
2264
    255, 255, 255, 255, 255, 255, 255, 255   // 56 - 63
2265
};
2266

2267
constexpr GByte Q2table[64] = {
2268
    8,   36, 36,  36,  36,  36,  36,  36,  36,  36,  39,  37,  38,
2269
    37,  39, 45,  41,  42,  42,  41,  45,  53,  47,  47,  50,  47,
2270
    47,  53, 65,  56,  54,  59,  59,  54,  56,  65,  68,  64,  69,
2271
    73,  69, 64,  68,  78,  81,  89,  89,  81,  78,  98,  108, 115,
2272
    108, 98, 130, 144, 144, 130, 178, 190, 178, 243, 243, 255};
2273

2274
constexpr GByte Q3table[64] = {
2275
    8,  10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 10, 11, 10, 11, 13,
2276
    11, 12, 12, 11, 13, 15, 13, 13, 14, 13, 13, 15, 18, 16, 15, 16,
2277
    16, 15, 16, 18, 19, 18, 19, 21, 19, 18, 19, 22, 23, 25, 25, 23,
2278
    22, 27, 30, 32, 30, 27, 36, 40, 40, 36, 50, 53, 50, 68, 68, 91};
2279

2280
constexpr GByte Q4table[64] = {
2281
    8,  7,  7,  7,  7,  7,  7,  7,  7,  7,  8,  7,  8,  7,  8,  9,
2282
    8,  8,  8,  8,  9,  11, 9,  9,  10, 9,  9,  11, 13, 11, 11, 12,
2283
    12, 11, 11, 13, 14, 13, 14, 15, 14, 13, 14, 16, 16, 18, 18, 16,
2284
    16, 20, 22, 23, 22, 20, 26, 29, 29, 26, 36, 38, 36, 49, 49, 65};
2285

2286
constexpr GByte Q5table[64] = {
2287
    4, 4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  5,
2288
    5, 5,  5,  5,  5,  6,  5,  5,  6,  5,  5,  6,  7,  6,  6,  6,
2289
    6, 6,  6,  7,  8,  7,  8,  8,  8,  7,  8,  9,  9,  10, 10, 9,
2290
    9, 11, 12, 13, 12, 11, 14, 16, 16, 14, 20, 21, 20, 27, 27, 36};
2291

2292
constexpr GByte AC_BITS[16] = {0, 2, 1, 3, 3, 2, 4, 3,
2293
                               5, 5, 4, 4, 0, 0, 1, 125};
2294

2295
constexpr GByte AC_HUFFVAL[256] = {
2296
    0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
2297
    0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08,
2298
    0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72,
2299
    0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,
2300
    0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45,
2301
    0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
2302
    0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75,
2303
    0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
2304
    0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3,
2305
    0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6,
2306
    0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
2307
    0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
2308
    0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4,
2309
    0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2310
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2311
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2312
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2313
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2314
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2315
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
2316

2317
constexpr GByte DC_BITS[16] = {0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0};
2318

2319
constexpr GByte DC_HUFFVAL[256] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
2320
                                   0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B};
2321

2322
void JPGDataset::LoadDefaultTables(int n)
46,864✔
2323
{
2324
    if (nQLevel < 1)
46,864✔
2325
        return;
46,864✔
2326

2327
    // Load quantization table.
2328
    JQUANT_TBL *quant_ptr = nullptr;
×
2329
    const GByte *pabyQTable = nullptr;
×
2330

2331
    if (nQLevel == 1)
×
2332
        pabyQTable = Q1table;
×
2333
    else if (nQLevel == 2)
×
2334
        pabyQTable = Q2table;
×
2335
    else if (nQLevel == 3)
×
2336
        pabyQTable = Q3table;
×
2337
    else if (nQLevel == 4)
×
2338
        pabyQTable = Q4table;
×
2339
    else if (nQLevel == 5)
×
2340
        pabyQTable = Q5table;
×
2341
    else
2342
        return;
×
2343

2344
    if (sDInfo.quant_tbl_ptrs[n] == nullptr)
×
2345
        sDInfo.quant_tbl_ptrs[n] =
×
2346
            jpeg_alloc_quant_table(reinterpret_cast<j_common_ptr>(&sDInfo));
×
2347

2348
    quant_ptr = sDInfo.quant_tbl_ptrs[n];  // quant_ptr is JQUANT_TBL.
×
2349
    for (int i = 0; i < 64; i++)
×
2350
    {
2351
        // Qtable[] is desired quantization table, in natural array order.
2352
        quant_ptr->quantval[i] = pabyQTable[i];
×
2353
    }
2354

2355
    // Load AC huffman table.
2356
    if (sDInfo.ac_huff_tbl_ptrs[n] == nullptr)
×
2357
        sDInfo.ac_huff_tbl_ptrs[n] =
×
2358
            jpeg_alloc_huff_table(reinterpret_cast<j_common_ptr>(&sDInfo));
×
2359

2360
    // huff_ptr is JHUFF_TBL*.
2361
    JHUFF_TBL *huff_ptr = sDInfo.ac_huff_tbl_ptrs[n];
×
2362

2363
    for (int i = 1; i <= 16; i++)
×
2364
    {
2365
        // counts[i] is number of Huffman codes of length i bits, i=1..16
2366
        huff_ptr->bits[i] = AC_BITS[i - 1];
×
2367
    }
2368

2369
    for (int i = 0; i < 256; i++)
×
2370
    {
2371
        // symbols[] is the list of Huffman symbols, in code-length order.
2372
        huff_ptr->huffval[i] = AC_HUFFVAL[i];
×
2373
    }
2374

2375
    // Load DC huffman table.
2376
    // TODO(schwehr): Revisit this "sideways" cast.
2377
    if (sDInfo.dc_huff_tbl_ptrs[n] == nullptr)
×
2378
        sDInfo.dc_huff_tbl_ptrs[n] =
×
2379
            jpeg_alloc_huff_table(reinterpret_cast<j_common_ptr>(&sDInfo));
×
2380

2381
    huff_ptr = sDInfo.dc_huff_tbl_ptrs[n];  // huff_ptr is JHUFF_TBL*
×
2382

2383
    for (int i = 1; i <= 16; i++)
×
2384
    {
2385
        // counts[i] is number of Huffman codes of length i bits, i=1..16
2386
        huff_ptr->bits[i] = DC_BITS[i - 1];
×
2387
    }
2388

2389
    for (int i = 0; i < 256; i++)
×
2390
    {
2391
        // symbols[] is the list of Huffman symbols, in code-length order.
2392
        huff_ptr->huffval[i] = DC_HUFFVAL[i];
×
2393
    }
2394
}
2395
#endif  // !defined(JPGDataset)
2396

2397
/************************************************************************/
2398
/*                       SetScaleNumAndDenom()                          */
2399
/************************************************************************/
2400

2401
void JPGDataset::SetScaleNumAndDenom()
11,716✔
2402
{
2403
#if JPEG_LIB_VERSION > 62
2404
    sDInfo.scale_num = 8 / nScaleFactor;
11,579✔
2405
    sDInfo.scale_denom = 8;
11,579✔
2406
#else
2407
    sDInfo.scale_num = 1;
137✔
2408
    sDInfo.scale_denom = nScaleFactor;
137✔
2409
#endif
2410
}
11,716✔
2411

2412
/************************************************************************/
2413
/*                              Restart()                               */
2414
/*                                                                      */
2415
/*      Restart compressor at the beginning of the file.                */
2416
/************************************************************************/
2417

2418
CPLErr JPGDataset::Restart()
680✔
2419

2420
{
2421
    if (ppoActiveDS && *ppoActiveDS != this && *ppoActiveDS != nullptr)
680✔
2422
    {
2423
        (*ppoActiveDS)->StopDecompress();
3✔
2424
    }
2425

2426
    // Setup to trap a fatal error.
2427
    if (setjmp(sUserData.setjmp_buffer))
680✔
2428
        return CE_Failure;
×
2429

2430
    J_COLOR_SPACE colorSpace = sDInfo.out_color_space;
680✔
2431
    J_COLOR_SPACE jpegColorSpace = sDInfo.jpeg_color_space;
680✔
2432

2433
    StopDecompress();
680✔
2434
    jpeg_create_decompress(&sDInfo);
680✔
2435
    bHasDoneJpegCreateDecompress = true;
680✔
2436

2437
    SetMaxMemoryToUse(&sDInfo);
680✔
2438

2439
#if !defined(JPGDataset)
2440
    LoadDefaultTables(0);
680✔
2441
    LoadDefaultTables(1);
680✔
2442
    LoadDefaultTables(2);
680✔
2443
    LoadDefaultTables(3);
680✔
2444
#endif  // !defined(JPGDataset)
2445

2446
    // Restart IO.
2447
    VSIFSeekL(m_fpImage, nSubfileOffset, SEEK_SET);
680✔
2448

2449
    jpeg_vsiio_src(&sDInfo, m_fpImage);
680✔
2450
    jpeg_read_header(&sDInfo, TRUE);
680✔
2451

2452
    sDInfo.out_color_space = colorSpace;
680✔
2453
    nLoadedScanline = -1;
680✔
2454
    SetScaleNumAndDenom();
680✔
2455

2456
    // The following errors could happen when "recycling" an existing dataset
2457
    // particularly when triggered by the implicit overviews of JPEG-in-TIFF
2458
    // with a corrupted TIFF file.
2459
    if (nRasterXSize !=
680✔
2460
            static_cast<int>(sDInfo.image_width + nScaleFactor - 1) /
680✔
2461
                nScaleFactor ||
680✔
2462
        nRasterYSize !=
680✔
2463
            static_cast<int>(sDInfo.image_height + nScaleFactor - 1) /
680✔
2464
                nScaleFactor)
680✔
2465
    {
2466
        CPLError(CE_Failure, CPLE_AppDefined,
×
2467
                 "Unexpected image dimension (%d x %d), "
2468
                 "where as (%d x %d) was expected",
2469
                 static_cast<int>(sDInfo.image_width + nScaleFactor - 1) /
×
2470
                     nScaleFactor,
×
2471
                 static_cast<int>(sDInfo.image_height + nScaleFactor - 1) /
×
2472
                     nScaleFactor,
×
2473
                 nRasterXSize, nRasterYSize);
2474
        bHasDoneJpegStartDecompress = false;
×
2475
    }
2476
    else if (jpegColorSpace != sDInfo.jpeg_color_space)
680✔
2477
    {
2478
        CPLError(CE_Failure, CPLE_AppDefined,
×
2479
                 "Unexpected jpeg color space : %d", sDInfo.jpeg_color_space);
×
2480
        bHasDoneJpegStartDecompress = false;
×
2481
    }
2482
    else
2483
    {
2484
        if (StartDecompress() != CE_None)
680✔
2485
            return CE_Failure;
×
2486
        if (ppoActiveDS)
680✔
2487
            *ppoActiveDS = this;
297✔
2488
    }
2489

2490
    return CE_None;
680✔
2491
}
2492

2493
#if !defined(JPGDataset)
2494

2495
/************************************************************************/
2496
/*                          GetGeoTransform()                           */
2497
/************************************************************************/
2498

2499
CPLErr JPGDatasetCommon::GetGeoTransform(double *padfTransform)
145✔
2500

2501
{
2502
    CPLErr eErr = GDALPamDataset::GetGeoTransform(padfTransform);
145✔
2503
    if (eErr != CE_Failure)
145✔
2504
        return eErr;
×
2505

2506
    LoadWorldFileOrTab();
145✔
2507

2508
    if (bGeoTransformValid)
145✔
2509
    {
2510
        memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
3✔
2511

2512
        return CE_None;
3✔
2513
    }
2514

2515
    return eErr;
142✔
2516
}
2517

2518
/************************************************************************/
2519
/*                            GetGCPCount()                             */
2520
/************************************************************************/
2521

2522
int JPGDatasetCommon::GetGCPCount()
172✔
2523

2524
{
2525
    const int nPAMGCPCount = GDALPamDataset::GetGCPCount();
172✔
2526
    if (nPAMGCPCount != 0)
172✔
2527
        return nPAMGCPCount;
6✔
2528

2529
    LoadWorldFileOrTab();
166✔
2530

2531
    return static_cast<int>(m_aoGCPs.size());
166✔
2532
}
2533

2534
/************************************************************************/
2535
/*                          GetGCPSpatialRef()                          */
2536
/************************************************************************/
2537

2538
const OGRSpatialReference *JPGDatasetCommon::GetGCPSpatialRef() const
3✔
2539

2540
{
2541
    const int nPAMGCPCount =
2542
        const_cast<JPGDatasetCommon *>(this)->GDALPamDataset::GetGCPCount();
3✔
2543
    if (nPAMGCPCount != 0)
3✔
2544
        return GDALPamDataset::GetGCPSpatialRef();
3✔
2545

2546
    const_cast<JPGDatasetCommon *>(this)->LoadWorldFileOrTab();
×
2547

2548
    if (!m_oSRS.IsEmpty() && !m_aoGCPs.empty())
×
2549
        return &m_oSRS;
×
2550

2551
    return nullptr;
×
2552
}
2553

2554
/************************************************************************/
2555
/*                               GetGCPs()                              */
2556
/************************************************************************/
2557

2558
const GDAL_GCP *JPGDatasetCommon::GetGCPs()
3✔
2559

2560
{
2561
    const int nPAMGCPCount = GDALPamDataset::GetGCPCount();
3✔
2562
    if (nPAMGCPCount != 0)
3✔
2563
        return GDALPamDataset::GetGCPs();
3✔
2564

2565
    LoadWorldFileOrTab();
×
2566

2567
    return gdal::GCP::c_ptr(m_aoGCPs);
×
2568
}
2569

2570
/************************************************************************/
2571
/*                           GetSpatialRef()                            */
2572
/************************************************************************/
2573

2574
const OGRSpatialReference *JPGDatasetCommon::GetSpatialRef() const
94✔
2575

2576
{
2577
    const auto poSRS = GDALPamDataset::GetSpatialRef();
94✔
2578
    if (poSRS)
94✔
2579
        return poSRS;
×
2580

2581
    auto poThis = const_cast<JPGDatasetCommon *>(this);
94✔
2582
    if (poThis->GetGCPCount() == 0)
94✔
2583
    {
2584
        if (!m_oSRS.IsEmpty())
94✔
2585
            return &m_oSRS;
×
2586

2587
        if (!bHasReadXMPMetadata)
94✔
2588
            poThis->ReadXMPMetadata();
52✔
2589
        CSLConstList papszXMP = poThis->GetMetadata("xml:XMP");
94✔
2590
        if (papszXMP && papszXMP[0])
94✔
2591
        {
2592
            CPLXMLTreeCloser poXML(CPLParseXMLString(papszXMP[0]));
18✔
2593
            if (poXML)
18✔
2594
            {
2595
                const auto psRDF =
2596
                    CPLGetXMLNode(poXML.get(), "=x:xmpmeta.rdf:RDF");
18✔
2597
                if (psRDF)
18✔
2598
                {
2599
                    for (const CPLXMLNode *psIter = psRDF->psChild; psIter;
68✔
2600
                         psIter = psIter->psNext)
50✔
2601
                    {
2602
                        if (psIter->eType == CXT_Element &&
138✔
2603
                            EQUAL(psIter->pszValue, "rdf:Description") &&
86✔
2604
                            EQUAL(CPLGetXMLValue(psIter, "xmlns:Camera", ""),
34✔
2605
                                  "http://pix4d.com/camera/1.0/"))
2606
                        {
2607
                            if (const char *pszHorizCS = CPLGetXMLValue(
2✔
2608
                                    psIter, "Camera:HorizCS", nullptr))
2609
                            {
2610
                                if (m_oSRS.SetFromUserInput(
2✔
2611
                                        pszHorizCS,
2612
                                        OGRSpatialReference::
2613
                                            SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
2✔
2614
                                    OGRERR_NONE)
2615
                                {
2616
                                    if (const char *pszVertCS = CPLGetXMLValue(
2✔
2617
                                            psIter, "Camera:VertCS", nullptr))
2618
                                    {
2619
                                        if (EQUAL(pszVertCS, "ellipsoidal"))
2✔
2620
                                            m_oSRS.PromoteTo3D(nullptr);
1✔
2621
                                        else
2622
                                        {
2623
                                            OGRSpatialReference oVertCRS;
2✔
2624
                                            if (oVertCRS.SetFromUserInput(
1✔
2625
                                                    pszVertCS,
2626
                                                    OGRSpatialReference::
2627
                                                        SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
1✔
2628
                                                OGRERR_NONE)
2629
                                            {
2630
                                                OGRSpatialReference oTmpCRS;
2✔
2631
                                                oTmpCRS.SetCompoundCS(
1✔
2632
                                                    std::string(
2✔
2633
                                                        m_oSRS.GetName())
2634
                                                        .append(" + ")
1✔
2635
                                                        .append(
2636
                                                            oVertCRS.GetName())
1✔
2637
                                                        .c_str(),
2638
                                                    &m_oSRS, &oVertCRS);
1✔
2639
                                                m_oSRS = std::move(oTmpCRS);
1✔
2640
                                            }
2641
                                        }
2642
                                    }
2643
                                    m_oSRS.SetAxisMappingStrategy(
2✔
2644
                                        OAMS_TRADITIONAL_GIS_ORDER);
2645
                                    return &m_oSRS;
2✔
2646
                                }
2647
                            }
2648
                        }
2649
                    }
2650
                }
2651
            }
2652
        }
2653
    }
2654

2655
    return nullptr;
92✔
2656
}
2657

2658
/************************************************************************/
2659
/*                             IRasterIO()                              */
2660
/*                                                                      */
2661
/*      Checks for what might be the most common read case              */
2662
/*      (reading an entire 8bit, RGB JPEG), and                         */
2663
/*      optimizes for that case                                         */
2664
/************************************************************************/
2665

2666
CPLErr JPGDatasetCommon::IRasterIO(
796✔
2667
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
2668
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
2669
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
2670
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
2671

2672
{
2673
    // Coverity says that we cannot pass a nullptr to IRasterIO.
2674
    if (panBandMap == nullptr)
796✔
2675
    {
2676
        return CE_Failure;
×
2677
    }
2678

2679
#ifndef JPEG_LIB_MK1
2680
    if ((eRWFlag == GF_Read) && (nBandCount == 3) && (nBands == 3) &&
796✔
2681
        (nXOff == 0) && (nYOff == 0) && (nXSize == nBufXSize) &&
541✔
2682
        (nXSize == nRasterXSize) && (nYSize == nBufYSize) &&
539✔
2683
        (nYSize == nRasterYSize) && (eBufType == GDT_Byte) &&
530✔
2684
        (GetDataPrecision() != 12) && (pData != nullptr) &&
406✔
2685
        (panBandMap[0] == 1) && (panBandMap[1] == 2) && (panBandMap[2] == 3) &&
406✔
2686
        // These color spaces need to be transformed to RGB.
2687
        GetOutColorSpace() != JCS_YCCK && GetOutColorSpace() != JCS_CMYK)
1,592✔
2688
    {
2689
        Restart();
382✔
2690

2691
        // Pixel interleaved case.
2692
        if (nBandSpace == 1)
382✔
2693
        {
2694
            for (int y = 0; y < nYSize; ++y)
5,892✔
2695
            {
2696
                if (nPixelSpace == 3)
5,783✔
2697
                {
2698
                    CPLErr tmpError =
2699
                        LoadScanline(y, &(((GByte *)pData)[(y * nLineSpace)]));
5,523✔
2700
                    if (tmpError != CE_None)
5,523✔
2701
                        return tmpError;
1✔
2702
                }
2703
                else
2704
                {
2705
                    CPLErr tmpError = LoadScanline(y);
260✔
2706
                    if (tmpError != CE_None)
260✔
2707
                        return tmpError;
×
2708

2709
                    for (int x = 0; x < nXSize; ++x)
94,120✔
2710
                    {
2711
                        memcpy(&(((GByte *)pData)[(y * nLineSpace) +
93,860✔
2712
                                                  (x * nPixelSpace)]),
93,860✔
2713
                               (const GByte *)&(m_pabyScanline[x * 3]), 3);
93,860✔
2714
                    }
2715
                }
2716
            }
2717
            nLoadedScanline = nRasterYSize;
109✔
2718
        }
2719
        else
2720
        {
2721
            for (int y = 0; y < nYSize; ++y)
10,152✔
2722
            {
2723
                CPLErr tmpError = LoadScanline(y);
9,880✔
2724
                if (tmpError != CE_None)
9,880✔
2725
                    return tmpError;
×
2726
                for (int x = 0; x < nXSize; ++x)
1,795,390✔
2727
                {
2728
                    static_cast<GByte *>(
2729
                        pData)[(y * nLineSpace) + (x * nPixelSpace)] =
1,785,510✔
2730
                        m_pabyScanline[x * 3];
1,785,510✔
2731
                    ((GByte *)pData)[(y * nLineSpace) + (x * nPixelSpace) +
1,785,510✔
2732
                                     nBandSpace] = m_pabyScanline[x * 3 + 1];
1,785,510✔
2733
                    ((GByte *)pData)[(y * nLineSpace) + (x * nPixelSpace) +
1,785,510✔
2734
                                     2 * nBandSpace] =
1,785,510✔
2735
                        m_pabyScanline[x * 3 + 2];
1,785,510✔
2736
                }
2737
            }
2738
        }
2739

2740
        return CE_None;
381✔
2741
    }
2742
#endif
2743

2744
    return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
414✔
2745
                                     pData, nBufXSize, nBufYSize, eBufType,
2746
                                     nBandCount, panBandMap, nPixelSpace,
2747
                                     nLineSpace, nBandSpace, psExtraArg);
414✔
2748
}
2749

2750
/************************************************************************/
2751
/*                                Open()                                */
2752
/************************************************************************/
2753

2754
GDALDataset *JPGDatasetCommon::Open(GDALOpenInfo *poOpenInfo)
3,443✔
2755

2756
{
2757
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2758
    // During fuzzing, do not use Identify to reject crazy content.
2759
    if (!JPEGDriverIdentify(poOpenInfo))
3,443✔
2760
        return nullptr;
×
2761
#endif
2762

2763
    if (poOpenInfo->eAccess == GA_Update)
3,443✔
2764
    {
2765
        ReportUpdateNotSupportedByDriver("JPEG");
×
2766
        return nullptr;
×
2767
    }
2768

2769
    CPLString osFilename(poOpenInfo->pszFilename);
6,886✔
2770
    bool bFLIRRawThermalImage = false;
3,443✔
2771
    if (STARTS_WITH(poOpenInfo->pszFilename, "JPEG:"))
3,443✔
2772
    {
2773
        CPLStringList aosTokens(CSLTokenizeString2(poOpenInfo->pszFilename, ":",
5✔
2774
                                                   CSLT_HONOURSTRINGS));
5✔
2775
        if (aosTokens.size() != 3)
5✔
2776
            return nullptr;
1✔
2777

2778
        osFilename = aosTokens[1];
4✔
2779
        if (std::string(aosTokens[2]) != "FLIR_RAW_THERMAL_IMAGE")
4✔
2780
            return nullptr;
1✔
2781
        bFLIRRawThermalImage = true;
3✔
2782
    }
2783

2784
    VSILFILE *fpL = poOpenInfo->fpL;
3,441✔
2785
    poOpenInfo->fpL = nullptr;
3,441✔
2786

2787
    JPGDatasetOpenArgs sArgs;
3,441✔
2788
    sArgs.pszFilename = osFilename.c_str();
3,441✔
2789
    sArgs.fpLin = fpL;
3,441✔
2790
    sArgs.papszSiblingFiles = poOpenInfo->GetSiblingFiles();
3,441✔
2791
    sArgs.bDoPAMInitialize = true;
3,441✔
2792
    sArgs.bUseInternalOverviews = CPLFetchBool(poOpenInfo->papszOpenOptions,
3,441✔
2793
                                               "USE_INTERNAL_OVERVIEWS", true);
2794
#ifdef D_LOSSLESS_SUPPORTED
2795
    sArgs.bIsLossless = JPEGDatasetIsJPEGLS(poOpenInfo);
2796
#endif
2797

2798
    auto poJPG_DS = JPGDataset::Open(&sArgs);
3,441✔
2799
    auto poDS = std::unique_ptr<GDALDataset>(poJPG_DS);
6,882✔
2800
    if (poDS == nullptr)
3,441✔
2801
    {
2802
        return nullptr;
1✔
2803
    }
2804
    if (bFLIRRawThermalImage)
3,440✔
2805
    {
2806
        poDS.reset(poJPG_DS->OpenFLIRRawThermalImage());
3✔
2807
    }
2808

2809
    if (poDS &&
6,879✔
2810
        CPLFetchBool(poOpenInfo->papszOpenOptions, "APPLY_ORIENTATION", false))
6,879✔
2811
    {
2812
        const char *pszOrientation = poDS->GetMetadataItem("EXIF_Orientation");
8✔
2813
        if (pszOrientation && !EQUAL(pszOrientation, "1"))
8✔
2814
        {
2815
            int nOrientation = atoi(pszOrientation);
7✔
2816
            if (nOrientation >= 2 && nOrientation <= 8)
7✔
2817
            {
2818
                auto poOrientedDS = std::make_unique<GDALOrientedDataset>(
2819
                    std::move(poDS),
7✔
2820
                    static_cast<GDALOrientedDataset::Origin>(nOrientation));
14✔
2821
                poDS = std::move(poOrientedDS);
7✔
2822
            }
2823
        }
2824
    }
2825

2826
    return poDS.release();
3,440✔
2827
}
2828

2829
/************************************************************************/
2830
/*                       OpenFLIRRawThermalImage()                      */
2831
/************************************************************************/
2832

2833
GDALDataset *JPGDatasetCommon::OpenFLIRRawThermalImage()
3✔
2834
{
2835
    ReadFLIRMetadata();
3✔
2836
    if (m_abyRawThermalImage.empty())
3✔
2837
    {
2838
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
2839
                 "Cannot find FLIR raw thermal image");
2840
        return nullptr;
1✔
2841
    }
2842

2843
    GByte *pabyData =
2844
        static_cast<GByte *>(CPLMalloc(m_abyRawThermalImage.size()));
2✔
2845
    const std::string osTmpFilename(
2846
        VSIMemGenerateHiddenFilename("jpeg_flir_raw"));
4✔
2847
    memcpy(pabyData, m_abyRawThermalImage.data(), m_abyRawThermalImage.size());
2✔
2848
    VSILFILE *fpRaw = VSIFileFromMemBuffer(osTmpFilename.c_str(), pabyData,
2✔
2849
                                           m_abyRawThermalImage.size(), true);
2✔
2850

2851
    // Termal image as uncompressed data
2852
    if (m_nRawThermalImageWidth * m_nRawThermalImageHeight * 2 ==
2✔
2853
        static_cast<int>(m_abyRawThermalImage.size()))
2✔
2854
    {
2855
        CPLDebug("JPEG", "Raw thermal image");
1✔
2856

2857
        class JPEGRawDataset : public RawDataset
2858
        {
2859
          public:
2860
            JPEGRawDataset(int nXSizeIn, int nYSizeIn)
1✔
2861
            {
1✔
2862
                nRasterXSize = nXSizeIn;
1✔
2863
                nRasterYSize = nYSizeIn;
1✔
2864
            }
1✔
2865

2866
            CPLErr Close() override
1✔
2867
            {
2868
                return GDALPamDataset::Close();
1✔
2869
            }
2870

2871
            ~JPEGRawDataset() = default;
2✔
2872

2873
            void SetBand(int nBand, std::unique_ptr<GDALRasterBand> &&poBand)
1✔
2874
            {
2875
                RawDataset::SetBand(nBand, std::move(poBand));
1✔
2876
            }
1✔
2877
        };
2878

2879
        auto poBand = RawRasterBand::Create(
2880
            fpRaw,
2881
            0,                            // image offset
2882
            2,                            // pixel offset
2883
            2 * m_nRawThermalImageWidth,  // line offset
1✔
2884
            GDT_UInt16,
2885
            m_bRawThermalLittleEndian
1✔
2886
                ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
2887
                : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
2888
            m_nRawThermalImageWidth, m_nRawThermalImageHeight,
2889
            RawRasterBand::OwnFP::YES);
2✔
2890
        if (!poBand)
1✔
2891
            return nullptr;
×
2892

2893
        auto poRawDS = new JPEGRawDataset(m_nRawThermalImageWidth,
2894
                                          m_nRawThermalImageHeight);
1✔
2895
        poRawDS->SetDescription(osTmpFilename.c_str());
1✔
2896
        poRawDS->SetBand(1, std::move(poBand));
1✔
2897
        poRawDS->MarkSuppressOnClose();
1✔
2898
        return poRawDS;
1✔
2899
    }
2900

2901
    VSIFCloseL(fpRaw);
1✔
2902

2903
    // Termal image as PNG
2904
    if (m_abyRawThermalImage.size() > 4 &&
2✔
2905
        memcmp(m_abyRawThermalImage.data(), "\x89PNG", 4) == 0)
1✔
2906
    {
2907
        auto poRawDS = GDALDataset::Open(osTmpFilename.c_str());
1✔
2908
        if (poRawDS == nullptr)
1✔
2909
        {
2910
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid raw thermal image");
×
2911
            VSIUnlink(osTmpFilename.c_str());
×
2912
            return nullptr;
×
2913
        }
2914
        poRawDS->MarkSuppressOnClose();
1✔
2915
        return poRawDS;
1✔
2916
    }
2917

2918
    CPLError(CE_Failure, CPLE_AppDefined,
×
2919
             "Unrecognized format for raw thermal image");
2920
    VSIUnlink(osTmpFilename.c_str());
×
2921
    return nullptr;
×
2922
}
2923

2924
#endif  // !defined(JPGDataset)
2925

2926
/************************************************************************/
2927
/*                                Open()                                */
2928
/************************************************************************/
2929

2930
JPGDatasetCommon *JPGDataset::Open(JPGDatasetOpenArgs *psArgs)
11,175✔
2931

2932
{
2933
    JPGDataset *poDS = new JPGDataset();
11,175✔
2934
    return OpenStage2(psArgs, poDS);
22,350✔
2935
}
2936

2937
JPGDatasetCommon *JPGDataset::OpenStage2(JPGDatasetOpenArgs *psArgs,
11,175✔
2938
                                         JPGDataset *&poDS)
2939
{
2940
    // Will detect mismatch between compile-time and run-time libjpeg versions.
2941
    if (setjmp(poDS->sUserData.setjmp_buffer))
11,175✔
2942
    {
2943
#if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
2944

2945
        if (poDS->sDInfo.data_precision == 12 && poDS->m_fpImage != nullptr)
137✔
2946
        {
2947
            VSILFILE *fpImage = poDS->m_fpImage;
136✔
2948
            poDS->m_fpImage = nullptr;
136✔
2949
            delete poDS;
136✔
2950
            psArgs->fpLin = fpImage;
136✔
2951
            return JPEGDataset12Open(psArgs);
136✔
2952
        }
2953
#endif
2954
        delete poDS;
1✔
2955
        return nullptr;
1✔
2956
    }
2957

2958
    const char *pszFilename = psArgs->pszFilename;
11,175✔
2959
    VSILFILE *fpLin = psArgs->fpLin;
11,175✔
2960
    CSLConstList papszSiblingFiles = psArgs->papszSiblingFiles;
11,175✔
2961
    const int nScaleFactor = psArgs->nScaleFactor;
11,175✔
2962
    const bool bDoPAMInitialize = psArgs->bDoPAMInitialize;
11,175✔
2963
    const bool bUseInternalOverviews = psArgs->bUseInternalOverviews;
11,175✔
2964

2965
    // If it is a subfile, read the JPEG header.
2966
    bool bIsSubfile = false;
11,175✔
2967
    GUIntBig subfile_offset = 0;
11,175✔
2968
    GUIntBig subfile_size = 0;
11,175✔
2969
    const char *real_filename = pszFilename;
11,175✔
2970
    int nQLevel = -1;
11,175✔
2971

2972
    if (STARTS_WITH_CI(pszFilename, "JPEG_SUBFILE:"))
11,175✔
2973
    {
2974
        bool bScan = false;
282✔
2975

2976
        if (STARTS_WITH_CI(pszFilename, "JPEG_SUBFILE:Q"))
282✔
2977
        {
2978
            char **papszTokens = CSLTokenizeString2(pszFilename + 14, ",", 0);
275✔
2979
            if (CSLCount(papszTokens) >= 3)
275✔
2980
            {
2981
                nQLevel = atoi(papszTokens[0]);
275✔
2982
                subfile_offset = CPLScanUIntBig(
550✔
2983
                    papszTokens[1], static_cast<int>(strlen(papszTokens[1])));
275✔
2984
                subfile_size = CPLScanUIntBig(
550✔
2985
                    papszTokens[2], static_cast<int>(strlen(papszTokens[2])));
275✔
2986
                bScan = true;
275✔
2987
            }
2988
            CSLDestroy(papszTokens);
275✔
2989
        }
2990
        else
2991
        {
2992
            char **papszTokens = CSLTokenizeString2(pszFilename + 13, ",", 0);
7✔
2993
            if (CSLCount(papszTokens) >= 2)
7✔
2994
            {
2995
                subfile_offset = CPLScanUIntBig(
14✔
2996
                    papszTokens[0], static_cast<int>(strlen(papszTokens[0])));
7✔
2997
                subfile_size = CPLScanUIntBig(
14✔
2998
                    papszTokens[1], static_cast<int>(strlen(papszTokens[1])));
7✔
2999
                bScan = true;
7✔
3000
            }
3001
            CSLDestroy(papszTokens);
7✔
3002
        }
3003

3004
        if (!bScan)
282✔
3005
        {
3006
            CPLError(CE_Failure, CPLE_OpenFailed,
×
3007
                     "Corrupt subfile definition: %s", pszFilename);
3008
            delete poDS;
×
3009
            return nullptr;
×
3010
        }
3011

3012
        real_filename = strstr(pszFilename, ",");
282✔
3013
        if (real_filename != nullptr)
282✔
3014
            real_filename = strstr(real_filename + 1, ",");
282✔
3015
        if (real_filename != nullptr && nQLevel != -1)
282✔
3016
            real_filename = strstr(real_filename + 1, ",");
275✔
3017
        if (real_filename != nullptr)
282✔
3018
            real_filename++;
282✔
3019
        else
3020
        {
3021
            CPLError(CE_Failure, CPLE_OpenFailed,
×
3022
                     "Could not find filename in subfile definition.");
3023
            delete poDS;
×
3024
            return nullptr;
×
3025
        }
3026

3027
        CPLDebug("JPG",
282✔
3028
                 "real_filename %s, offset=" CPL_FRMT_GUIB
3029
                 ", size=" CPL_FRMT_GUIB "\n",
3030
                 real_filename, subfile_offset, subfile_size);
3031

3032
        bIsSubfile = true;
282✔
3033
    }
3034

3035
    // Open the file using the large file api if necessary.
3036
    VSILFILE *fpImage = fpLin;
11,175✔
3037

3038
    if (!fpImage)
11,175✔
3039
    {
3040
        fpImage = VSIFOpenL(real_filename, "rb");
7,751✔
3041

3042
        if (fpImage == nullptr)
7,751✔
3043
        {
3044
            CPLError(CE_Failure, CPLE_OpenFailed,
2✔
3045
                     "VSIFOpenL(%s) failed unexpectedly in jpgdataset.cpp",
3046
                     real_filename);
3047
            delete poDS;
2✔
3048
            return nullptr;
2✔
3049
        }
3050
    }
3051

3052
    // Create a corresponding GDALDataset.
3053
    poDS->nQLevel = nQLevel;
11,173✔
3054
    poDS->m_fpImage = fpImage;
11,173✔
3055

3056
    // Move to the start of jpeg data.
3057
    poDS->nSubfileOffset = subfile_offset;
11,173✔
3058
    VSIFSeekL(poDS->m_fpImage, poDS->nSubfileOffset, SEEK_SET);
11,173✔
3059

3060
    poDS->eAccess = GA_ReadOnly;
11,173✔
3061

3062
    poDS->sDInfo.err = jpeg_std_error(&poDS->sJErr);
11,173✔
3063
    poDS->sJErr.error_exit = JPGDataset::ErrorExit;
11,173✔
3064
    poDS->sJErr.output_message = JPGDataset::OutputMessage;
11,173✔
3065
    poDS->sUserData.p_previous_emit_message = poDS->sJErr.emit_message;
11,173✔
3066
    poDS->sJErr.emit_message = JPGDataset::EmitMessage;
11,173✔
3067
    poDS->sDInfo.client_data = &poDS->sUserData;
11,173✔
3068

3069
    jpeg_create_decompress(&poDS->sDInfo);
11,173✔
3070
    poDS->bHasDoneJpegCreateDecompress = true;
11,173✔
3071

3072
    SetMaxMemoryToUse(&poDS->sDInfo);
11,173✔
3073

3074
#if !defined(JPGDataset)
3075
    // Preload default NITF JPEG quantization tables.
3076
    poDS->LoadDefaultTables(0);
11,036✔
3077
    poDS->LoadDefaultTables(1);
11,036✔
3078
    poDS->LoadDefaultTables(2);
11,036✔
3079
    poDS->LoadDefaultTables(3);
11,036✔
3080
#endif  // !defined(JPGDataset)
3081

3082
    // Read pre-image data after ensuring the file is rewound.
3083
    VSIFSeekL(poDS->m_fpImage, poDS->nSubfileOffset, SEEK_SET);
11,173✔
3084

3085
    jpeg_vsiio_src(&poDS->sDInfo, poDS->m_fpImage);
11,173✔
3086
    jpeg_read_header(&poDS->sDInfo, TRUE);
11,173✔
3087

3088
    if (poDS->sDInfo.data_precision != 8 && poDS->sDInfo.data_precision != 12)
11,036✔
3089
    {
3090
        CPLError(CE_Failure, CPLE_NotSupported,
×
3091
                 "GDAL JPEG Driver doesn't support files with precision of "
3092
                 "other than 8 or 12 bits.");
3093
        delete poDS;
×
3094
        return nullptr;
×
3095
    }
3096

3097
#if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
3098
    if (poDS->sDInfo.data_precision == 12 && poDS->m_fpImage != nullptr)
10,899✔
3099
    {
3100
        poDS->m_fpImage = nullptr;
×
3101
        delete poDS;
×
3102
        psArgs->fpLin = fpImage;
×
3103
        return JPEGDataset12Open(psArgs);
×
3104
    }
3105
#endif
3106

3107
    // Capture some information from the file that is of interest.
3108

3109
    poDS->nScaleFactor = nScaleFactor;
11,036✔
3110
    poDS->SetScaleNumAndDenom();
11,036✔
3111
    poDS->nRasterXSize =
11,036✔
3112
        (poDS->sDInfo.image_width + nScaleFactor - 1) / nScaleFactor;
11,036✔
3113
    poDS->nRasterYSize =
11,036✔
3114
        (poDS->sDInfo.image_height + nScaleFactor - 1) / nScaleFactor;
11,036✔
3115

3116
    poDS->sDInfo.out_color_space = poDS->sDInfo.jpeg_color_space;
11,036✔
3117
    poDS->eGDALColorSpace = poDS->sDInfo.jpeg_color_space;
11,036✔
3118

3119
    if (poDS->sDInfo.jpeg_color_space == JCS_GRAYSCALE)
11,036✔
3120
    {
3121
        poDS->nBands = 1;
4,175✔
3122
    }
3123
    else if (poDS->sDInfo.jpeg_color_space == JCS_RGB)
6,861✔
3124
    {
3125
        poDS->nBands = 3;
3,102✔
3126
    }
3127
    else if (poDS->sDInfo.jpeg_color_space == JCS_YCbCr)
3,759✔
3128
    {
3129
        poDS->nBands = 3;
3,462✔
3130
        if (CPLTestBool(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES")))
3,462✔
3131
        {
3132
            poDS->sDInfo.out_color_space = JCS_RGB;
3,462✔
3133
            poDS->eGDALColorSpace = JCS_RGB;
3,462✔
3134
            poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr",
3,462✔
3135
                                  "IMAGE_STRUCTURE");
3,462✔
3136
        }
3137
    }
3138
    else if (poDS->sDInfo.jpeg_color_space == JCS_CMYK)
297✔
3139
    {
3140
        if (poDS->sDInfo.data_precision == 8 &&
594✔
3141
            CPLTestBool(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES")))
297✔
3142
        {
3143
            poDS->eGDALColorSpace = JCS_RGB;
5✔
3144
            poDS->nBands = 3;
5✔
3145
            poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "CMYK",
5✔
3146
                                  "IMAGE_STRUCTURE");
5✔
3147
        }
3148
        else
3149
        {
3150
            poDS->nBands = 4;
292✔
3151
        }
3152
    }
3153
    else if (poDS->sDInfo.jpeg_color_space == JCS_YCCK)
×
3154
    {
3155
        if (poDS->sDInfo.data_precision == 8 &&
×
3156
            CPLTestBool(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES")))
×
3157
        {
3158
            poDS->eGDALColorSpace = JCS_RGB;
×
3159
            poDS->nBands = 3;
×
3160
            poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCrK",
×
3161
                                  "IMAGE_STRUCTURE");
×
3162

3163
            // libjpeg does the translation from YCrCbK -> CMYK internally
3164
            // and we'll do the translation to RGB in IReadBlock().
3165
            poDS->sDInfo.out_color_space = JCS_CMYK;
×
3166
        }
3167
        else
3168
        {
3169
            poDS->nBands = 4;
×
3170
        }
3171
    }
3172
    else
3173
    {
3174
        CPLError(CE_Failure, CPLE_NotSupported,
×
3175
                 "Unrecognized jpeg_color_space value of %d.\n",
3176
                 poDS->sDInfo.jpeg_color_space);
×
3177
        delete poDS;
×
3178
        return nullptr;
×
3179
    }
3180

3181
    // Create band information objects.
3182
    for (int iBand = 0; iBand < poDS->nBands; iBand++)
36,086✔
3183
        poDS->SetBand(iBand + 1, JPGCreateBand(poDS, iBand + 1));
25,050✔
3184

3185
    // More metadata.
3186
    if (poDS->nBands > 1)
11,036✔
3187
    {
3188
        poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
6,861✔
3189
        poDS->SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
6,861✔
3190
    }
3191

3192
    if (psArgs->bIsLossless)
11,036✔
3193
    {
3194
        poDS->SetMetadataItem("COMPRESSION_REVERSIBILITY", "LOSSLESS",
×
3195
                              "IMAGE_STRUCTURE");
×
3196
    }
3197

3198
    // Initialize any PAM information.
3199
    poDS->SetDescription(pszFilename);
11,036✔
3200

3201
    if (nScaleFactor == 1 && bDoPAMInitialize)
11,036✔
3202
    {
3203
        if (!bIsSubfile)
3,620✔
3204
            poDS->TryLoadXML(papszSiblingFiles);
3,470✔
3205
        else
3206
            poDS->nPamFlags |= GPF_NOSAVE;
150✔
3207

3208
        // Open (external) overviews.
3209
        poDS->oOvManager.Initialize(poDS, real_filename, papszSiblingFiles);
3,620✔
3210

3211
        if (!bUseInternalOverviews)
3,620✔
3212
            poDS->bHasInitInternalOverviews = true;
×
3213

3214
        // In the case of a file downloaded through the HTTP driver, this one
3215
        // will unlink the temporary /vsimem file just after GDALOpen(), so
3216
        // later VSIFOpenL() when reading internal overviews would fail.
3217
        // Initialize them now.
3218
        if (STARTS_WITH(real_filename, "/vsimem/") &&
3,620✔
3219
            strstr(real_filename, "_gdal_http_"))
3,341✔
3220
        {
3221
            poDS->InitInternalOverviews();
×
3222
        }
3223
    }
3224
    else
3225
    {
3226
        poDS->nPamFlags |= GPF_NOSAVE;
7,416✔
3227
    }
3228

3229
    poDS->bIsSubfile = bIsSubfile;
11,036✔
3230

3231
    return poDS;
11,036✔
3232
}
3233

3234
#if !defined(JPGDataset)
3235

3236
/************************************************************************/
3237
/*                       LoadWorldFileOrTab()                           */
3238
/************************************************************************/
3239

3240
void JPGDatasetCommon::LoadWorldFileOrTab()
383✔
3241
{
3242
    if (bIsSubfile)
383✔
3243
        return;
239✔
3244
    if (bHasTriedLoadWorldFileOrTab)
383✔
3245
        return;
239✔
3246
    bHasTriedLoadWorldFileOrTab = true;
144✔
3247

3248
    char *pszWldFilename = nullptr;
144✔
3249

3250
    // TIROS3 JPEG files have a .wld extension, so don't look for .wld as
3251
    // as worldfile.
3252
    const bool bEndsWithWld =
3253
        strlen(GetDescription()) > 4 &&
288✔
3254
        EQUAL(GetDescription() + strlen(GetDescription()) - 4, ".wld");
144✔
3255
    bGeoTransformValid =
144✔
3256
        GDALReadWorldFile2(GetDescription(), nullptr, adfGeoTransform,
144✔
3257
                           oOvManager.GetSiblingFiles(), &pszWldFilename) ||
144✔
3258
        GDALReadWorldFile2(GetDescription(), ".jpw", adfGeoTransform,
144✔
3259
                           oOvManager.GetSiblingFiles(), &pszWldFilename) ||
288✔
3260
        (!bEndsWithWld &&
144✔
3261
         GDALReadWorldFile2(GetDescription(), ".wld", adfGeoTransform,
144✔
3262
                            oOvManager.GetSiblingFiles(), &pszWldFilename));
144✔
3263

3264
    if (!bGeoTransformValid)
144✔
3265
    {
3266
        char *pszProjection = nullptr;
140✔
3267
        int nGCPCount = 0;
140✔
3268
        GDAL_GCP *pasGCPList = nullptr;
140✔
3269
        const bool bTabFileOK = CPL_TO_BOOL(GDALReadTabFile2(
140✔
3270
            GetDescription(), adfGeoTransform, &pszProjection, &nGCPCount,
140✔
3271
            &pasGCPList, oOvManager.GetSiblingFiles(), &pszWldFilename));
140✔
3272
        if (pszProjection)
140✔
3273
            m_oSRS.importFromWkt(pszProjection);
×
3274
        CPLFree(pszProjection);
140✔
3275
        m_aoGCPs = gdal::GCP::fromC(pasGCPList, nGCPCount);
140✔
3276
        GDALDeinitGCPs(nGCPCount, pasGCPList);
140✔
3277
        CPLFree(pasGCPList);
140✔
3278

3279
        if (bTabFileOK && nGCPCount == 0)
140✔
3280
            bGeoTransformValid = true;
×
3281
    }
3282

3283
    if (pszWldFilename)
144✔
3284
    {
3285
        osWldFilename = pszWldFilename;
4✔
3286
        CPLFree(pszWldFilename);
4✔
3287
    }
3288
}
3289

3290
/************************************************************************/
3291
/*                            GetFileList()                             */
3292
/************************************************************************/
3293

3294
char **JPGDatasetCommon::GetFileList()
72✔
3295

3296
{
3297
    char **papszFileList = GDALPamDataset::GetFileList();
72✔
3298

3299
    LoadWorldFileOrTab();
72✔
3300

3301
    if (!osWldFilename.empty() &&
74✔
3302
        CSLFindString(papszFileList, osWldFilename) == -1)
2✔
3303
    {
3304
        papszFileList = CSLAddString(papszFileList, osWldFilename);
2✔
3305
    }
3306

3307
    return papszFileList;
72✔
3308
}
3309

3310
/************************************************************************/
3311
/*                            CheckForMask()                            */
3312
/************************************************************************/
3313

3314
void JPGDatasetCommon::CheckForMask()
62✔
3315

3316
{
3317
    // Save current position to avoid disturbing JPEG stream decoding.
3318
    const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
62✔
3319

3320
    // Go to the end of the file, pull off four bytes, and see if
3321
    // it is plausibly the size of the real image data.
3322
    VSIFSeekL(m_fpImage, 0, SEEK_END);
62✔
3323
    GIntBig nFileSize = VSIFTellL(m_fpImage);
62✔
3324
    VSIFSeekL(m_fpImage, nFileSize - 4, SEEK_SET);
62✔
3325

3326
    GUInt32 nImageSize = 0;
62✔
3327
    VSIFReadL(&nImageSize, 4, 1, m_fpImage);
62✔
3328
    CPL_LSBPTR32(&nImageSize);
62✔
3329

3330
    GByte abyEOD[2] = {0, 0};
62✔
3331

3332
    if (nImageSize >= 2 && nImageSize >= nFileSize / 2 &&
62✔
3333
        nImageSize <= nFileSize - 4)
62✔
3334
    {
3335
        // If that seems okay, seek back, and verify that just preceding
3336
        // the bitmask is an apparent end-of-jpeg-data marker.
3337
        VSIFSeekL(m_fpImage, nImageSize - 2, SEEK_SET);
15✔
3338
        VSIFReadL(abyEOD, 2, 1, m_fpImage);
15✔
3339
        if (abyEOD[0] == 0xff && abyEOD[1] == 0xd9)
15✔
3340
        {
3341
            // We seem to have a mask.  Read it in.
3342
            nCMaskSize = static_cast<int>(nFileSize - nImageSize - 4);
15✔
3343
            pabyCMask = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nCMaskSize));
15✔
3344
            if (pabyCMask)
15✔
3345
            {
3346
                VSIFReadL(pabyCMask, nCMaskSize, 1, m_fpImage);
15✔
3347

3348
                CPLDebug("JPEG", "Got %d byte compressed bitmask.", nCMaskSize);
15✔
3349
            }
3350
        }
3351
    }
3352

3353
    VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
62✔
3354
}
62✔
3355

3356
/************************************************************************/
3357
/*                           DecompressMask()                           */
3358
/************************************************************************/
3359

3360
void JPGDatasetCommon::DecompressMask()
4,261✔
3361

3362
{
3363
    if (pabyCMask == nullptr || pabyBitMask != nullptr)
4,261✔
3364
        return;
4,246✔
3365

3366
    // Allocate 1bit buffer - may be slightly larger than needed.
3367
    const int nBufSize = nRasterYSize * ((nRasterXSize + 7) / 8);
15✔
3368
    pabyBitMask = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBufSize));
15✔
3369
    if (pabyBitMask == nullptr)
15✔
3370
    {
3371
        CPLFree(pabyCMask);
×
3372
        pabyCMask = nullptr;
×
3373
        return;
×
3374
    }
3375

3376
    // Decompress.
3377
    void *pOut =
3378
        CPLZLibInflate(pabyCMask, nCMaskSize, pabyBitMask, nBufSize, nullptr);
15✔
3379

3380
    // Cleanup if an error occurs.
3381
    if (pOut == nullptr)
15✔
3382
    {
3383
        CPLError(CE_Failure, CPLE_AppDefined,
×
3384
                 "Failure decoding JPEG validity bitmask.");
3385
        CPLFree(pabyCMask);
×
3386
        pabyCMask = nullptr;
×
3387

3388
        CPLFree(pabyBitMask);
×
3389
        pabyBitMask = nullptr;
×
3390

3391
        return;
×
3392
    }
3393

3394
    const char *pszJPEGMaskBitOrder =
3395
        CPLGetConfigOption("JPEG_MASK_BIT_ORDER", "AUTO");
15✔
3396
    if (EQUAL(pszJPEGMaskBitOrder, "LSB"))
15✔
3397
        bMaskLSBOrder = true;
×
3398
    else if (EQUAL(pszJPEGMaskBitOrder, "MSB"))
15✔
3399
        bMaskLSBOrder = false;
×
3400
    else if (nRasterXSize > 8 && nRasterYSize > 1)
15✔
3401
    {
3402
        // Test MSB ordering hypothesis in a very restrictive case where it is
3403
        // *obviously* ordered as MSB ! (unless someone coded something
3404
        // specifically to defeat the below logic)
3405
        // The case considered here is dop_465_6100.jpg from #5102.
3406
        // The mask is identical for each line, starting with 1's and ending
3407
        // with 0's (or starting with 0's and ending with 1's), and no other
3408
        // intermediate change.
3409
        // We can detect the MSB ordering since the lsb bits at the end of the
3410
        // first line will be set with the 1's of the beginning of the second
3411
        // line.
3412
        // We can only be sure of this heuristics if the change of value occurs
3413
        // in the middle of a byte, or if the raster width is not a multiple of
3414
        // 8.
3415
        //
3416
        // TODO(schwehr): Check logic in this section that was added in r26063.
3417
        int nPrevValBit = 0;
15✔
3418
        int nChangedValBit = 0;
15✔
3419
        int iX = 0;  // Used after for.
15✔
3420
        for (; iX < nRasterXSize; iX++)
2,101✔
3421
        {
3422
            const int nValBit =
2,098✔
3423
                (pabyBitMask[iX >> 3] & (0x1 << (7 - (iX & 7)))) != 0;
2,098✔
3424
            if (iX == 0)
2,098✔
3425
                nPrevValBit = nValBit;
15✔
3426
            else if (nValBit != nPrevValBit)
2,083✔
3427
            {
3428
                nPrevValBit = nValBit;
13✔
3429
                nChangedValBit++;
13✔
3430
                if (nChangedValBit == 1)
13✔
3431
                {
3432
                    const bool bValChangedOnByteBoundary = (iX % 8) == 0;
13✔
3433
                    if (bValChangedOnByteBoundary && (nRasterXSize % 8) == 0)
13✔
3434
                        break;
11✔
3435
                }
3436
                else
3437
                {
3438
                    break;
×
3439
                }
3440
            }
3441
            const int iNextLineX = iX + nRasterXSize;
2,087✔
3442
            const int nNextLineValBit = (pabyBitMask[iNextLineX >> 3] &
2,087✔
3443
                                         (0x1 << (7 - (iNextLineX & 7)))) != 0;
2,087✔
3444
            if (nValBit != nNextLineValBit)
2,087✔
3445
                break;
1✔
3446
        }
3447

3448
        if (iX == nRasterXSize && nChangedValBit == 1)
15✔
3449
        {
3450
            CPLDebug("JPEG",
2✔
3451
                     "Bit ordering in mask is guessed to be msb (unusual)");
3452
            bMaskLSBOrder = false;
2✔
3453
        }
3454
        else
3455
        {
3456
            bMaskLSBOrder = true;
13✔
3457
        }
15✔
3458
    }
3459
    else
3460
    {
3461
        bMaskLSBOrder = true;
×
3462
    }
3463
}
3464

3465
/************************************************************************/
3466
/*                       GetCompressionFormats()                        */
3467
/************************************************************************/
3468

3469
CPLStringList JPGDatasetCommon::GetCompressionFormats(int nXOff, int nYOff,
1✔
3470
                                                      int nXSize, int nYSize,
3471
                                                      int nBandCount,
3472
                                                      const int *panBandList)
3473
{
3474
    CPLStringList aosRet;
1✔
3475
    if (m_fpImage && nXOff == 0 && nYOff == 0 && nXSize == nRasterXSize &&
1✔
3476
        nYSize == nRasterYSize && IsAllBands(nBandCount, panBandList))
2✔
3477
    {
3478
        aosRet.AddString(GDALGetCompressionFormatForJPEG(m_fpImage).c_str());
1✔
3479
    }
3480
    return aosRet;
1✔
3481
}
3482

3483
/************************************************************************/
3484
/*                       ReadCompressedData()                           */
3485
/************************************************************************/
3486

3487
CPLErr JPGDatasetCommon::ReadCompressedData(
20✔
3488
    const char *pszFormat, int nXOff, int nYOff, int nXSize, int nYSize,
3489
    int nBandCount, const int *panBandList, void **ppBuffer,
3490
    size_t *pnBufferSize, char **ppszDetailedFormat)
3491
{
3492
    if (m_fpImage && nXOff == 0 && nYOff == 0 && nXSize == nRasterXSize &&
20✔
3493
        nYSize == nRasterYSize && IsAllBands(nBandCount, panBandList))
40✔
3494
    {
3495
        const CPLStringList aosTokens(CSLTokenizeString2(pszFormat, ";", 0));
20✔
3496
        if (aosTokens.size() != 1)
20✔
3497
            return CE_Failure;
×
3498

3499
        if (EQUAL(aosTokens[0], "JPEG"))
20✔
3500
        {
3501
            if (ppszDetailedFormat)
15✔
3502
                *ppszDetailedFormat = VSIStrdup(
6✔
3503
                    GDALGetCompressionFormatForJPEG(m_fpImage).c_str());
12✔
3504

3505
            const auto nSavedPos = VSIFTellL(m_fpImage);
15✔
3506
            VSIFSeekL(m_fpImage, 0, SEEK_END);
15✔
3507
            auto nFileSize = VSIFTellL(m_fpImage);
15✔
3508
            if (nFileSize > std::numeric_limits<size_t>::max() / 2)
15✔
3509
                return CE_Failure;
×
3510
            if (nFileSize > 4)
15✔
3511
            {
3512
                VSIFSeekL(m_fpImage, nFileSize - 4, SEEK_SET);
15✔
3513
                // Detect zlib compress mask band at end of file
3514
                // and remove it if found
3515
                uint32_t nImageSize = 0;
15✔
3516
                VSIFReadL(&nImageSize, 4, 1, m_fpImage);
15✔
3517
                CPL_LSBPTR32(&nImageSize);
15✔
3518
                if (nImageSize > 2 && nImageSize >= nFileSize / 2 &&
15✔
3519
                    nImageSize < nFileSize - 4)
15✔
3520
                {
3521
                    VSIFSeekL(m_fpImage, nImageSize - 2, SEEK_SET);
2✔
3522
                    GByte abyTwoBytes[2];
3523
                    if (VSIFReadL(abyTwoBytes, 2, 1, m_fpImage) == 1 &&
2✔
3524
                        abyTwoBytes[0] == 0xFF && abyTwoBytes[1] == 0xD9)
2✔
3525
                    {
3526
                        nFileSize = nImageSize;
2✔
3527
                    }
3528
                }
3529
            }
3530
            auto nSize = static_cast<size_t>(nFileSize);
15✔
3531
            if (ppBuffer)
15✔
3532
            {
3533
                if (pnBufferSize == nullptr)
14✔
3534
                {
3535
                    VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
1✔
3536
                    return CE_Failure;
2✔
3537
                }
3538
                bool bFreeOnError = false;
13✔
3539
                if (*ppBuffer)
13✔
3540
                {
3541
                    if (*pnBufferSize < nSize)
3✔
3542
                    {
3543
                        VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
1✔
3544
                        return CE_Failure;
1✔
3545
                    }
3546
                }
3547
                else
3548
                {
3549
                    *ppBuffer = VSI_MALLOC_VERBOSE(nSize);
10✔
3550
                    if (*ppBuffer == nullptr)
10✔
3551
                    {
3552
                        VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
×
3553
                        return CE_Failure;
×
3554
                    }
3555
                    bFreeOnError = true;
10✔
3556
                }
3557
                VSIFSeekL(m_fpImage, 0, SEEK_SET);
12✔
3558
                if (VSIFReadL(*ppBuffer, nSize, 1, m_fpImage) != 1)
12✔
3559
                {
3560
                    if (bFreeOnError)
×
3561
                    {
3562
                        VSIFree(*ppBuffer);
×
3563
                        *ppBuffer = nullptr;
×
3564
                    }
3565
                    VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
×
3566
                    return CE_Failure;
×
3567
                }
3568

3569
                constexpr GByte EXIF_SIGNATURE[] = {'E', 'x',  'i',
12✔
3570
                                                    'f', '\0', '\0'};
3571
                constexpr char APP1_XMP_SIGNATURE[] =
12✔
3572
                    "http://ns.adobe.com/xap/1.0/";
3573
                size_t nChunkLoc = 2;
12✔
3574
                GByte *pabyJPEG = static_cast<GByte *>(*ppBuffer);
12✔
3575
                while (nChunkLoc + 4 <= nSize)
104✔
3576
                {
3577
                    if (pabyJPEG[nChunkLoc + 0] != 0xFF)
104✔
3578
                        break;
×
3579
                    if (pabyJPEG[nChunkLoc + 1] == 0xDA)
104✔
3580
                        break;
12✔
3581
                    const int nChunkLength =
92✔
3582
                        pabyJPEG[nChunkLoc + 2] * 256 + pabyJPEG[nChunkLoc + 3];
92✔
3583
                    if (nChunkLength < 2 || static_cast<size_t>(nChunkLength) >
92✔
3584
                                                nSize - (nChunkLoc + 2))
92✔
3585
                        break;
3586
                    if (pabyJPEG[nChunkLoc + 1] == 0xE1 &&
92✔
3587
                        nChunkLoc + 4 + sizeof(EXIF_SIGNATURE) <= nSize &&
7✔
3588
                        memcmp(pabyJPEG + nChunkLoc + 4, EXIF_SIGNATURE,
7✔
3589
                               sizeof(EXIF_SIGNATURE)) == 0)
3590
                    {
3591
                        CPLDebug("JPEG", "Remove existing EXIF from "
6✔
3592
                                         "source compressed data");
3593
                        memmove(pabyJPEG + nChunkLoc,
6✔
3594
                                pabyJPEG + nChunkLoc + 2 + nChunkLength,
6✔
3595
                                nSize - (nChunkLoc + 2 + nChunkLength));
6✔
3596
                        nSize -= 2 + nChunkLength;
6✔
3597
                        continue;
6✔
3598
                    }
3599
                    else if (pabyJPEG[nChunkLoc + 1] == 0xE1 &&
86✔
3600
                             nChunkLoc + 4 + sizeof(APP1_XMP_SIGNATURE) <=
1✔
3601
                                 nSize &&
2✔
3602
                             memcmp(pabyJPEG + nChunkLoc + 4,
1✔
3603
                                    APP1_XMP_SIGNATURE,
3604
                                    sizeof(APP1_XMP_SIGNATURE)) == 0)
3605
                    {
3606
                        CPLDebug("JPEG", "Remove existing XMP from "
1✔
3607
                                         "source compressed data");
3608
                        memmove(pabyJPEG + nChunkLoc,
1✔
3609
                                pabyJPEG + nChunkLoc + 2 + nChunkLength,
1✔
3610
                                nSize - (nChunkLoc + 2 + nChunkLength));
1✔
3611
                        nSize -= 2 + nChunkLength;
1✔
3612
                        continue;
1✔
3613
                    }
3614
                    nChunkLoc += 2 + nChunkLength;
85✔
3615
                }
3616
            }
3617
            VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
13✔
3618
            if (pnBufferSize)
13✔
3619
                *pnBufferSize = nSize;
13✔
3620
            return CE_None;
13✔
3621
        }
3622
    }
3623
    return CE_Failure;
5✔
3624
}
3625

3626
#endif  // !defined(JPGDataset)
3627

3628
/************************************************************************/
3629
/*                             ErrorExit()                              */
3630
/************************************************************************/
3631

3632
void JPGDataset::ErrorExit(j_common_ptr cinfo)
148✔
3633
{
3634
    GDALJPEGUserData *psUserData =
148✔
3635
        static_cast<GDALJPEGUserData *>(cinfo->client_data);
3636
    char buffer[JMSG_LENGTH_MAX] = {};
148✔
3637

3638
    // Create the message.
3639
    (*cinfo->err->format_message)(cinfo, buffer);
148✔
3640

3641
    // Avoid error for a 12bit JPEG if reading from the 8bit JPEG driver and
3642
    // we have JPEG_DUAL_MODE_8_12 support, as we'll try again with 12bit JPEG
3643
    // driver.
3644
#if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
3645
    if (strstr(buffer, "Unsupported JPEG data precision 12") == nullptr)
148✔
3646
#endif
3647
        CPLError(CE_Failure, CPLE_AppDefined, "libjpeg: %s", buffer);
12✔
3648

3649
    // Return control to the setjmp point.
3650
    longjmp(psUserData->setjmp_buffer, 1);
148✔
3651
}
3652

3653
/************************************************************************/
3654
/*                          OutputMessage()                             */
3655
/************************************************************************/
3656

3657
void JPGDataset::OutputMessage(j_common_ptr cinfo)
×
3658
{
3659
    char buffer[JMSG_LENGTH_MAX] = {};
×
3660

3661
    // Create the message.
3662
    (*cinfo->err->format_message)(cinfo, buffer);
×
3663

3664
    CPLDebug("JPEG", "libjpeg: %s", buffer);
×
3665
}
×
3666

3667
/************************************************************************/
3668
/*                             EmitMessage()                            */
3669
/************************************************************************/
3670

3671
void JPGDataset::EmitMessage(j_common_ptr cinfo, int msg_level)
219,464✔
3672
{
3673
    GDALJPEGUserData *psUserData =
219,464✔
3674
        static_cast<GDALJPEGUserData *>(cinfo->client_data);
3675
    if (msg_level >= 0)  // Trace message.
219,464✔
3676
    {
3677
        if (psUserData->p_previous_emit_message != nullptr)
219,451✔
3678
            psUserData->p_previous_emit_message(cinfo, msg_level);
219,451✔
3679
    }
3680
    else
3681
    {
3682
        // Warning : libjpeg will try to recover but the image will be likely
3683
        // corrupted.
3684

3685
        struct jpeg_error_mgr *err = cinfo->err;
13✔
3686

3687
        // It's a warning message.  Since corrupt files may generate many
3688
        // warnings, the policy implemented here is to show only the first
3689
        // warning, unless trace_level >= 3.
3690
        if (err->num_warnings == 0 || err->trace_level >= 3)
13✔
3691
        {
3692
            char buffer[JMSG_LENGTH_MAX] = {};
9✔
3693

3694
            // Create the message.
3695
            (*cinfo->err->format_message)(cinfo, buffer);
9✔
3696

3697
            const char *pszVal =
3698
                CPLGetConfigOption("GDAL_ERROR_ON_LIBJPEG_WARNING", nullptr);
9✔
3699
            if (strstr(buffer, "Premature end of JPEG file"))
9✔
3700
            {
3701
                // Consider this an error by default
3702
                if (pszVal == nullptr || CPLTestBool(pszVal))
6✔
3703
                {
3704
                    psUserData->bNonFatalErrorEncountered = true;
5✔
3705
                    if (pszVal == nullptr)
5✔
3706
                    {
3707
                        CPLError(CE_Failure, CPLE_AppDefined,
3✔
3708
                                 "libjpeg: %s (this error can be turned as a "
3709
                                 "warning "
3710
                                 "by setting GDAL_ERROR_ON_LIBJPEG_WARNING to "
3711
                                 "FALSE)",
3712
                                 buffer);
3713
                    }
3714
                    else
3715
                    {
3716
                        CPLError(CE_Failure, CPLE_AppDefined, "libjpeg: %s",
2✔
3717
                                 buffer);
3718
                    }
3719
                }
3720
                else
3721
                {
3722
                    CPLError(CE_Warning, CPLE_AppDefined, "libjpeg: %s",
1✔
3723
                             buffer);
3724
                }
3725
            }
3726
            else if (pszVal == nullptr || !CPLTestBool(pszVal))
3✔
3727
            {
3728
                if (pszVal == nullptr)
2✔
3729
                {
3730
                    CPLError(
1✔
3731
                        CE_Warning, CPLE_AppDefined,
3732
                        "libjpeg: %s (this warning can be turned as an error "
3733
                        "by setting GDAL_ERROR_ON_LIBJPEG_WARNING to TRUE)",
3734
                        buffer);
3735
                }
3736
                else
3737
                {
3738
                    CPLError(CE_Warning, CPLE_AppDefined, "libjpeg: %s",
1✔
3739
                             buffer);
3740
                }
3741
            }
3742
            else
3743
            {
3744
                psUserData->bNonFatalErrorEncountered = true;
1✔
3745
                CPLError(CE_Failure, CPLE_AppDefined, "libjpeg: %s", buffer);
1✔
3746
            }
3747
        }
3748

3749
        // Always count warnings in num_warnings.
3750
        err->num_warnings++;
13✔
3751
    }
3752
}
219,464✔
3753

3754
/************************************************************************/
3755
/*                          ProgressMonitor()                           */
3756
/************************************************************************/
3757

3758
/* Avoid the risk of denial-of-service on crafted JPEGs with an insane */
3759
/* number of scans. */
3760
/* See
3761
 * http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf
3762
 */
3763
void JPGDataset::ProgressMonitor(j_common_ptr cinfo)
217,033✔
3764
{
3765
    if (cinfo->is_decompressor)
217,033✔
3766
    {
3767
        GDALJPEGUserData *psUserData =
217,033✔
3768
            static_cast<GDALJPEGUserData *>(cinfo->client_data);
3769
        const int scan_no =
217,033✔
3770
            reinterpret_cast<j_decompress_ptr>(cinfo)->input_scan_number;
3771
        if (scan_no >= psUserData->nMaxScans)
217,033✔
3772
        {
3773
            CPLError(CE_Failure, CPLE_AppDefined,
1✔
3774
                     "Scan number %d exceeds maximum scans (%d)", scan_no,
3775
                     psUserData->nMaxScans);
3776

3777
            // Return control to the setjmp point.
3778
            longjmp(psUserData->setjmp_buffer, 1);
1✔
3779
        }
3780
    }
3781
}
217,032✔
3782

3783
#if !defined(JPGDataset)
3784

3785
/************************************************************************/
3786
/*                           JPGAddICCProfile()                         */
3787
/*                                                                      */
3788
/*      This function adds an ICC profile to a JPEG file.               */
3789
/************************************************************************/
3790

3791
void JPGAddICCProfile(void *pInfo, const char *pszICCProfile,
3✔
3792
                      my_jpeg_write_m_header p_jpeg_write_m_header,
3793
                      my_jpeg_write_m_byte p_jpeg_write_m_byte)
3794
{
3795
    if (pszICCProfile == nullptr)
3✔
3796
        return;
×
3797

3798
    // Write out each segment of the ICC profile.
3799
    char *pEmbedBuffer = CPLStrdup(pszICCProfile);
3✔
3800
    GInt32 nEmbedLen = CPLBase64DecodeInPlace((GByte *)pEmbedBuffer);
3✔
3801
    char *pEmbedPtr = pEmbedBuffer;
3✔
3802
    char const *const paHeader = "ICC_PROFILE";
3✔
3803
    int nSegments = (nEmbedLen + 65518) / 65519;
3✔
3804
    int nSegmentID = 1;
3✔
3805

3806
    while (nEmbedLen != 0)
10✔
3807
    {
3808
        // 65535 - 16 bytes for header = 65519
3809
        const int nChunkLen = (nEmbedLen > 65519) ? 65519 : nEmbedLen;
7✔
3810
        nEmbedLen -= nChunkLen;
7✔
3811

3812
        // Write marker and length.
3813
        p_jpeg_write_m_header(pInfo, JPEG_APP0 + 2,
7✔
3814
                              static_cast<unsigned int>(nChunkLen + 14));
7✔
3815

3816
        // Write identifier.
3817
        for (int i = 0; i < 12; i++)
91✔
3818
            p_jpeg_write_m_byte(pInfo, paHeader[i]);
84✔
3819

3820
        // Write ID and max ID.
3821
        p_jpeg_write_m_byte(pInfo, nSegmentID);
7✔
3822
        p_jpeg_write_m_byte(pInfo, nSegments);
7✔
3823

3824
        // Write ICC Profile.
3825
        for (int i = 0; i < nChunkLen; i++)
304,295✔
3826
            p_jpeg_write_m_byte(pInfo, pEmbedPtr[i]);
304,288✔
3827

3828
        nSegmentID++;
7✔
3829

3830
        pEmbedPtr += nChunkLen;
7✔
3831
    }
3832

3833
    CPLFree(pEmbedBuffer);
3✔
3834
}
3835

3836
/************************************************************************/
3837
/*                           JPGAppendMask()                            */
3838
/*                                                                      */
3839
/*      This function appends a zlib compressed bitmask to a JPEG       */
3840
/*      file (or really any file) pulled from an existing mask band.    */
3841
/************************************************************************/
3842

3843
// MSVC does not know that memset() has initialized sStream.
3844
#ifdef _MSC_VER
3845
#pragma warning(disable : 4701)
3846
#endif
3847

3848
CPLErr JPGAppendMask(const char *pszJPGFilename, GDALRasterBand *poMask,
8✔
3849
                     GDALProgressFunc pfnProgress, void *pProgressData)
3850

3851
{
3852
    const int nXSize = poMask->GetXSize();
8✔
3853
    const int nYSize = poMask->GetYSize();
8✔
3854
    const int nBitBufSize = nYSize * ((nXSize + 7) / 8);
8✔
3855
    CPLErr eErr = CE_None;
8✔
3856

3857
    // Allocate uncompressed bit buffer.
3858
    GByte *pabyBitBuf =
3859
        static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, nBitBufSize));
8✔
3860

3861
    GByte *pabyMaskLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
8✔
3862
    if (pabyBitBuf == nullptr || pabyMaskLine == nullptr)
8✔
3863
    {
3864
        eErr = CE_Failure;
×
3865
    }
3866

3867
    // No reason to set it to MSB, unless for debugging purposes
3868
    // to be able to generate a unusual LSB ordered mask (#5102).
3869
    const char *pszJPEGMaskBitOrder =
3870
        CPLGetConfigOption("JPEG_WRITE_MASK_BIT_ORDER", "LSB");
8✔
3871
    const bool bMaskLSBOrder = EQUAL(pszJPEGMaskBitOrder, "LSB");
8✔
3872

3873
    // Set bit buffer from mask band, scanline by scanline.
3874
    GUInt32 iBit = 0;
8✔
3875
    for (int iY = 0; eErr == CE_None && iY < nYSize; iY++)
688✔
3876
    {
3877
        eErr = poMask->RasterIO(GF_Read, 0, iY, nXSize, 1, pabyMaskLine, nXSize,
680✔
3878
                                1, GDT_Byte, 0, 0, nullptr);
3879
        if (eErr != CE_None)
680✔
3880
            break;
×
3881

3882
        if (bMaskLSBOrder)
680✔
3883
        {
3884
            for (int iX = 0; iX < nXSize; iX++)
265,051✔
3885
            {
3886
                if (pabyMaskLine[iX] != 0)
264,453✔
3887
                    pabyBitBuf[iBit >> 3] |= (0x1 << (iBit & 7));
198,418✔
3888

3889
                iBit++;
264,453✔
3890
            }
3891
        }
3892
        else
3893
        {
3894
            for (int iX = 0; iX < nXSize; iX++)
2,331✔
3895
            {
3896
                if (pabyMaskLine[iX] != 0)
2,249✔
3897
                    pabyBitBuf[iBit >> 3] |= (0x1 << (7 - (iBit & 7)));
784✔
3898

3899
                iBit++;
2,249✔
3900
            }
3901
        }
3902

3903
        if (pfnProgress != nullptr &&
848✔
3904
            !pfnProgress((iY + 1) / static_cast<double>(nYSize), nullptr,
168✔
3905
                         pProgressData))
3906
        {
3907
            eErr = CE_Failure;
×
3908
            CPLError(CE_Failure, CPLE_UserInterrupt,
×
3909
                     "User terminated JPGAppendMask()");
3910
        }
3911
    }
3912

3913
    CPLFree(pabyMaskLine);
8✔
3914

3915
    // Compress.
3916
    GByte *pabyCMask = nullptr;
8✔
3917

3918
    if (eErr == CE_None)
8✔
3919
    {
3920
        pabyCMask = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBitBufSize + 30));
8✔
3921
        if (pabyCMask == nullptr)
8✔
3922
        {
3923
            eErr = CE_Failure;
×
3924
        }
3925
    }
3926

3927
    size_t nTotalOut = 0;
8✔
3928
    if (eErr == CE_None)
8✔
3929
    {
3930
        if (CPLZLibDeflate(pabyBitBuf, nBitBufSize, -1, pabyCMask,
16✔
3931
                           nBitBufSize + 30, &nTotalOut) == nullptr)
8✔
3932
        {
3933
            CPLError(CE_Failure, CPLE_AppDefined,
×
3934
                     "Deflate compression of jpeg bit mask failed.");
3935
            eErr = CE_Failure;
×
3936
        }
3937
    }
3938

3939
    // Write to disk, along with image file size.
3940
    if (eErr == CE_None)
8✔
3941
    {
3942
        VSILFILE *fpOut = VSIFOpenL(pszJPGFilename, "r+");
8✔
3943
        if (fpOut == nullptr)
8✔
3944
        {
3945
            CPLError(CE_Failure, CPLE_AppDefined,
×
3946
                     "Failed to open jpeg to append bitmask.");
3947
            eErr = CE_Failure;
×
3948
        }
3949
        else
3950
        {
3951
            VSIFSeekL(fpOut, 0, SEEK_END);
8✔
3952

3953
            GUInt32 nImageSize = static_cast<GUInt32>(VSIFTellL(fpOut));
8✔
3954
            CPL_LSBPTR32(&nImageSize);
8✔
3955

3956
            if (VSIFWriteL(pabyCMask, 1, nTotalOut, fpOut) != nTotalOut)
8✔
3957
            {
3958
                CPLError(CE_Failure, CPLE_FileIO,
×
3959
                         "Failure writing compressed bitmask.\n%s",
3960
                         VSIStrerror(errno));
×
3961
                eErr = CE_Failure;
×
3962
            }
3963
            else
3964
            {
3965
                VSIFWriteL(&nImageSize, 4, 1, fpOut);
8✔
3966
            }
3967

3968
            VSIFCloseL(fpOut);
8✔
3969
        }
3970
    }
3971

3972
    CPLFree(pabyBitBuf);
8✔
3973
    CPLFree(pabyCMask);
8✔
3974

3975
    return eErr;
8✔
3976
}
3977

3978
/************************************************************************/
3979
/*                             JPGAddEXIF()                             */
3980
/************************************************************************/
3981

3982
void JPGAddEXIF(GDALDataType eWorkDT, GDALDataset *poSrcDS, char **papszOptions,
247✔
3983
                void *cinfo, my_jpeg_write_m_header p_jpeg_write_m_header,
3984
                my_jpeg_write_m_byte p_jpeg_write_m_byte,
3985
                GDALDataset *(pCreateCopy)(const char *, GDALDataset *, int,
3986
                                           char **,
3987
                                           GDALProgressFunc pfnProgress,
3988
                                           void *pProgressData))
3989
{
3990
    const int nBands = poSrcDS->GetRasterCount();
247✔
3991
    const int nXSize = poSrcDS->GetRasterXSize();
247✔
3992
    const int nYSize = poSrcDS->GetRasterYSize();
247✔
3993

3994
    bool bGenerateEXIFThumbnail =
3995
        CPLTestBool(CSLFetchNameValueDef(papszOptions, "EXIF_THUMBNAIL", "NO"));
247✔
3996
    const char *pszThumbnailWidth =
3997
        CSLFetchNameValue(papszOptions, "THUMBNAIL_WIDTH");
247✔
3998
    const char *pszThumbnailHeight =
3999
        CSLFetchNameValue(papszOptions, "THUMBNAIL_HEIGHT");
247✔
4000
    int nOvrWidth = 0;
247✔
4001
    int nOvrHeight = 0;
247✔
4002
    if (pszThumbnailWidth == nullptr && pszThumbnailHeight == nullptr)
247✔
4003
    {
4004
        if (nXSize >= nYSize)
243✔
4005
        {
4006
            nOvrWidth = 128;
236✔
4007
        }
4008
        else
4009
        {
4010
            nOvrHeight = 128;
7✔
4011
        }
4012
    }
4013
    if (pszThumbnailWidth != nullptr)
247✔
4014
    {
4015
        nOvrWidth = atoi(pszThumbnailWidth);
3✔
4016
        if (nOvrWidth < 32)
3✔
4017
            nOvrWidth = 32;
×
4018
        if (nOvrWidth > 1024)
3✔
4019
            nOvrWidth = 1024;
×
4020
    }
4021
    if (pszThumbnailHeight != nullptr)
247✔
4022
    {
4023
        nOvrHeight = atoi(pszThumbnailHeight);
3✔
4024
        if (nOvrHeight < 32)
3✔
4025
            nOvrHeight = 32;
×
4026
        if (nOvrHeight > 1024)
3✔
4027
            nOvrHeight = 1024;
×
4028
    }
4029
    if (nOvrWidth == 0)
247✔
4030
    {
4031
        nOvrWidth = static_cast<int>(static_cast<GIntBig>(nOvrHeight) * nXSize /
8✔
4032
                                     nYSize);
8✔
4033
        if (nOvrWidth == 0)
8✔
4034
            nOvrWidth = 1;
×
4035
    }
4036
    else if (nOvrHeight == 0)
239✔
4037
    {
4038
        nOvrHeight =
237✔
4039
            static_cast<int>(static_cast<GIntBig>(nOvrWidth) * nYSize / nXSize);
237✔
4040
        if (nOvrHeight == 0)
237✔
4041
            nOvrHeight = 1;
×
4042
    }
4043

4044
    vsi_l_offset nJPEGIfByteCount = 0;
247✔
4045
    GByte *pabyOvr = nullptr;
247✔
4046

4047
    if (bGenerateEXIFThumbnail && nXSize > nOvrWidth && nYSize > nOvrHeight)
247✔
4048
    {
4049
        GDALDataset *poMemDS = MEMDataset::Create("", nOvrWidth, nOvrHeight,
6✔
4050
                                                  nBands, eWorkDT, nullptr);
4051
        GDALRasterBand **papoSrcBands = static_cast<GDALRasterBand **>(
4052
            CPLMalloc(nBands * sizeof(GDALRasterBand *)));
6✔
4053
        GDALRasterBand ***papapoOverviewBands = static_cast<GDALRasterBand ***>(
4054
            CPLMalloc(nBands * sizeof(GDALRasterBand **)));
6✔
4055
        for (int i = 0; i < nBands; i++)
14✔
4056
        {
4057
            papoSrcBands[i] = poSrcDS->GetRasterBand(i + 1);
8✔
4058
            papapoOverviewBands[i] = static_cast<GDALRasterBand **>(
16✔
4059
                CPLMalloc(sizeof(GDALRasterBand *)));
8✔
4060
            papapoOverviewBands[i][0] = poMemDS->GetRasterBand(i + 1);
8✔
4061
        }
4062
        CPLErr eErr = GDALRegenerateOverviewsMultiBand(
6✔
4063
            nBands, papoSrcBands, 1, papapoOverviewBands, "AVERAGE", nullptr,
4064
            nullptr,
4065
            /* papszOptions = */ nullptr);
4066
        CPLFree(papoSrcBands);
6✔
4067
        for (int i = 0; i < nBands; i++)
14✔
4068
        {
4069
            CPLFree(papapoOverviewBands[i]);
8✔
4070
        }
4071
        CPLFree(papapoOverviewBands);
6✔
4072

4073
        if (eErr != CE_None)
6✔
4074
        {
4075
            GDALClose(poMemDS);
×
4076
            return;
×
4077
        }
4078

4079
        const CPLString osTmpFile(VSIMemGenerateHiddenFilename("ovrjpg"));
12✔
4080
        GDALDataset *poOutDS = pCreateCopy(osTmpFile, poMemDS, 0, nullptr,
6✔
4081
                                           GDALDummyProgress, nullptr);
4082
        const bool bExifOverviewSuccess = poOutDS != nullptr;
6✔
4083
        delete poOutDS;
6✔
4084
        poOutDS = nullptr;
6✔
4085
        GDALClose(poMemDS);
6✔
4086
        if (bExifOverviewSuccess)
6✔
4087
            pabyOvr = VSIGetMemFileBuffer(osTmpFile, &nJPEGIfByteCount, TRUE);
6✔
4088
        VSIUnlink(osTmpFile);
6✔
4089

4090
        // cppcheck-suppress knownConditionTrueFalse
4091
        if (pabyOvr == nullptr)
6✔
4092
        {
4093
            nJPEGIfByteCount = 0;
×
4094
            CPLError(CE_Warning, CPLE_AppDefined,
×
4095
                     "Could not generate EXIF overview");
4096
        }
4097
    }
4098

4099
    GUInt32 nMarkerSize;
4100
    const bool bWriteExifMetadata =
4101
        CPLFetchBool(papszOptions, "WRITE_EXIF_METADATA", true);
247✔
4102

4103
    GByte *pabyEXIF =
4104
        EXIFCreate(bWriteExifMetadata ? poSrcDS->GetMetadata() : nullptr,
247✔
4105
                   pabyOvr, static_cast<GUInt32>(nJPEGIfByteCount), nOvrWidth,
4106
                   nOvrHeight, &nMarkerSize);
4107
    if (pabyEXIF)
247✔
4108
    {
4109
        p_jpeg_write_m_header(cinfo, JPEG_APP0 + 1, nMarkerSize);
22✔
4110
        for (GUInt32 i = 0; i < nMarkerSize; i++)
4,783✔
4111
        {
4112
            p_jpeg_write_m_byte(cinfo, pabyEXIF[i]);
4,761✔
4113
        }
4114
        VSIFree(pabyEXIF);
22✔
4115
    }
4116
    CPLFree(pabyOvr);
247✔
4117
}
4118

4119
#endif  // !defined(JPGDataset)
4120

4121
/************************************************************************/
4122
/*                              CreateCopy()                            */
4123
/************************************************************************/
4124

4125
GDALDataset *JPGDataset::CreateCopy(const char *pszFilename,
273✔
4126
                                    GDALDataset *poSrcDS, int bStrict,
4127
                                    char **papszOptions,
4128
                                    GDALProgressFunc pfnProgress,
4129
                                    void *pProgressData)
4130

4131
{
4132
    const int nBands = poSrcDS->GetRasterCount();
273✔
4133

4134
    const char *pszLossLessCopy =
4135
        CSLFetchNameValueDef(papszOptions, "LOSSLESS_COPY", "AUTO");
273✔
4136
    if (EQUAL(pszLossLessCopy, "AUTO") || CPLTestBool(pszLossLessCopy))
273✔
4137
    {
4138
        void *pJPEGContent = nullptr;
273✔
4139
        size_t nJPEGContent = 0;
273✔
4140
        if (poSrcDS->ReadCompressedData("JPEG", 0, 0, poSrcDS->GetRasterXSize(),
273✔
4141
                                        poSrcDS->GetRasterYSize(), nBands,
4142
                                        nullptr, &pJPEGContent, &nJPEGContent,
4143
                                        nullptr) == CE_None &&
555✔
4144
            GDALGetCompressionFormatForJPEG(pJPEGContent, nJPEGContent)
282✔
4145
                    .find(";colorspace=RGBA") == std::string::npos)
9✔
4146
        {
4147
            if (!pfnProgress(0.0, nullptr, pProgressData))
9✔
4148
                return nullptr;
9✔
4149

4150
            CPLDebug("JPEG", "Lossless copy from source dataset");
9✔
4151
            std::vector<GByte> abyJPEG;
9✔
4152
            try
4153
            {
4154
                abyJPEG.assign(static_cast<const GByte *>(pJPEGContent),
9✔
4155
                               static_cast<const GByte *>(pJPEGContent) +
9✔
4156
                                   nJPEGContent);
9✔
4157

4158
                const bool bWriteExifMetadata =
4159
                    CPLFetchBool(papszOptions, "WRITE_EXIF_METADATA", true);
9✔
4160
                if (bWriteExifMetadata)
9✔
4161
                {
4162
                    char **papszEXIF_MD = poSrcDS->GetMetadata("EXIF");
9✔
4163
                    if (papszEXIF_MD == nullptr)
9✔
4164
                    {
4165
                        papszEXIF_MD = poSrcDS->GetMetadata();
9✔
4166
                    }
4167
                    GUInt32 nEXIFContentSize = 0;
9✔
4168
                    GByte *pabyEXIF = EXIFCreate(papszEXIF_MD, nullptr, 0, 0, 0,
9✔
4169
                                                 &nEXIFContentSize);
4170
                    if (nEXIFContentSize > 0 && nEXIFContentSize + 2 <= 65535U)
9✔
4171
                    {
4172
                        size_t nChunkLoc = 2;
1✔
4173
                        size_t nInsertPos = 0;
1✔
4174
                        constexpr GByte JFIF_SIGNATURE[] = {'J', 'F', 'I', 'F',
1✔
4175
                                                            '\0'};
4176
                        constexpr GByte EXIF_SIGNATURE[] = {'E', 'x',  'i',
1✔
4177
                                                            'f', '\0', '\0'};
4178
                        while (nChunkLoc + 4 <= abyJPEG.size())
9✔
4179
                        {
4180
                            if (abyJPEG[nChunkLoc + 0] != 0xFF)
9✔
4181
                                break;
×
4182
                            if (abyJPEG[nChunkLoc + 1] == 0xDA)
9✔
4183
                            {
4184
                                if (nInsertPos == 0)
1✔
4185
                                    nInsertPos = nChunkLoc;
×
4186
                                break;
1✔
4187
                            }
4188
                            const int nChunkLength =
4189
                                abyJPEG[nChunkLoc + 2] * 256 +
8✔
4190
                                abyJPEG[nChunkLoc + 3];
8✔
4191
                            if (nChunkLength < 2)
8✔
4192
                                break;
×
4193
                            if (abyJPEG[nChunkLoc + 1] == 0xE0 &&
9✔
4194
                                nChunkLoc + 4 + sizeof(JFIF_SIGNATURE) <=
1✔
4195
                                    abyJPEG.size() &&
9✔
4196
                                memcmp(abyJPEG.data() + nChunkLoc + 4,
1✔
4197
                                       JFIF_SIGNATURE,
4198
                                       sizeof(JFIF_SIGNATURE)) == 0)
4199
                            {
4200
                                if (nInsertPos == 0)
1✔
4201
                                    nInsertPos = nChunkLoc + 2 + nChunkLength;
1✔
4202
                            }
4203
                            else if (abyJPEG[nChunkLoc + 1] == 0xE1 &&
7✔
4204
                                     nChunkLoc + 4 + sizeof(EXIF_SIGNATURE) <=
×
4205
                                         abyJPEG.size() &&
7✔
4206
                                     memcmp(abyJPEG.data() + nChunkLoc + 4,
×
4207
                                            EXIF_SIGNATURE,
4208
                                            sizeof(EXIF_SIGNATURE)) == 0)
4209
                            {
4210
                                CPLDebug("JPEG",
×
4211
                                         "Remove existing EXIF from source "
4212
                                         "compressed data");
4213
                                abyJPEG.erase(abyJPEG.begin() + nChunkLoc,
×
4214
                                              abyJPEG.begin() + nChunkLoc + 2 +
×
4215
                                                  nChunkLength);
×
4216
                                continue;
×
4217
                            }
4218
                            nChunkLoc += 2 + nChunkLength;
8✔
4219
                        }
4220
                        if (nInsertPos > 0)
1✔
4221
                        {
4222
                            std::vector<GByte> abyNew;
2✔
4223
                            const size_t nMarkerSize = 2 + nEXIFContentSize;
1✔
4224
                            abyNew.reserve(abyJPEG.size() + 2 + nMarkerSize);
1✔
4225
                            abyNew.insert(abyNew.end(), abyJPEG.data(),
1✔
4226
                                          abyJPEG.data() + nInsertPos);
2✔
4227
                            abyNew.insert(abyNew.end(),
×
4228
                                          static_cast<GByte>(0xFF));
1✔
4229
                            abyNew.insert(abyNew.end(),
×
4230
                                          static_cast<GByte>(0xE1));
1✔
4231
                            abyNew.insert(abyNew.end(),
×
4232
                                          static_cast<GByte>(nMarkerSize >> 8));
1✔
4233
                            abyNew.insert(
4234
                                abyNew.end(),
×
4235
                                static_cast<GByte>(nMarkerSize & 0xFF));
1✔
4236
                            abyNew.insert(abyNew.end(), pabyEXIF,
×
4237
                                          pabyEXIF + nEXIFContentSize);
1✔
4238
                            abyNew.insert(abyNew.end(),
×
4239
                                          abyJPEG.data() + nInsertPos,
1✔
4240
                                          abyJPEG.data() + abyJPEG.size());
1✔
4241
                            abyJPEG = std::move(abyNew);
1✔
4242
                        }
4243
                    }
4244
                    VSIFree(pabyEXIF);
9✔
4245
                }
4246

4247
                const bool bWriteXMP =
4248
                    CPLFetchBool(papszOptions, "WRITE_XMP", true);
9✔
4249
                char **papszXMP =
4250
                    bWriteXMP ? poSrcDS->GetMetadata("xml:XMP") : nullptr;
9✔
4251
                if (papszXMP && papszXMP[0])
9✔
4252
                {
4253
                    size_t nChunkLoc = 2;
1✔
4254
                    size_t nInsertPos = 0;
1✔
4255
                    constexpr GByte JFIF_SIGNATURE[] = {'J', 'F', 'I', 'F',
1✔
4256
                                                        '\0'};
4257
                    constexpr const char APP1_XMP_SIGNATURE[] =
1✔
4258
                        "http://ns.adobe.com/xap/1.0/";
4259
                    while (nChunkLoc + 4 <= abyJPEG.size())
6✔
4260
                    {
4261
                        if (abyJPEG[nChunkLoc + 0] != 0xFF)
6✔
4262
                            break;
×
4263
                        if (abyJPEG[nChunkLoc + 1] == 0xDA)
6✔
4264
                        {
4265
                            if (nInsertPos == 0)
1✔
4266
                                nInsertPos = nChunkLoc;
×
4267
                            break;
1✔
4268
                        }
4269
                        const int nChunkLength = abyJPEG[nChunkLoc + 2] * 256 +
5✔
4270
                                                 abyJPEG[nChunkLoc + 3];
5✔
4271
                        if (nChunkLength < 2)
5✔
4272
                            break;
×
4273
                        if (abyJPEG[nChunkLoc + 1] == 0xE0 &&
6✔
4274
                            nChunkLoc + 4 + sizeof(JFIF_SIGNATURE) <=
1✔
4275
                                abyJPEG.size() &&
6✔
4276
                            memcmp(abyJPEG.data() + nChunkLoc + 4,
1✔
4277
                                   JFIF_SIGNATURE, sizeof(JFIF_SIGNATURE)) == 0)
4278
                        {
4279
                            if (nInsertPos == 0)
1✔
4280
                                nInsertPos = nChunkLoc + 2 + nChunkLength;
1✔
4281
                        }
4282
                        else if (abyJPEG[nChunkLoc + 1] == 0xE1 &&
4✔
4283
                                 nChunkLoc + 4 + sizeof(APP1_XMP_SIGNATURE) <=
×
4284
                                     abyJPEG.size() &&
4✔
4285
                                 memcmp(abyJPEG.data() + nChunkLoc + 4,
×
4286
                                        APP1_XMP_SIGNATURE,
4287
                                        sizeof(APP1_XMP_SIGNATURE)) == 0)
4288
                        {
4289
                            CPLDebug("JPEG", "Remove existing XMP from source "
×
4290
                                             "compressed data");
4291
                            abyJPEG.erase(abyJPEG.begin() + nChunkLoc,
×
4292
                                          abyJPEG.begin() + nChunkLoc + 2 +
×
4293
                                              nChunkLength);
×
4294
                            continue;
×
4295
                        }
4296
                        nChunkLoc += 2 + nChunkLength;
5✔
4297
                    }
4298
                    const size_t nMarkerSize =
1✔
4299
                        2 + sizeof(APP1_XMP_SIGNATURE) + strlen(papszXMP[0]);
1✔
4300
                    if (nInsertPos > 0 && nMarkerSize <= 65535U)
1✔
4301
                    {
4302
                        std::vector<GByte> abyNew;
2✔
4303
                        abyNew.reserve(abyJPEG.size() + 2 + nMarkerSize);
1✔
4304
                        abyNew.insert(abyNew.end(), abyJPEG.data(),
1✔
4305
                                      abyJPEG.data() + nInsertPos);
2✔
4306
                        abyNew.insert(abyNew.end(), static_cast<GByte>(0xFF));
1✔
4307
                        abyNew.insert(abyNew.end(), static_cast<GByte>(0xE1));
1✔
4308
                        abyNew.insert(abyNew.end(),
×
4309
                                      static_cast<GByte>(nMarkerSize >> 8));
1✔
4310
                        abyNew.insert(abyNew.end(),
×
4311
                                      static_cast<GByte>(nMarkerSize & 0xFF));
1✔
4312
                        abyNew.insert(abyNew.end(), APP1_XMP_SIGNATURE,
×
4313
                                      APP1_XMP_SIGNATURE +
4314
                                          sizeof(APP1_XMP_SIGNATURE));
1✔
4315
                        abyNew.insert(abyNew.end(), papszXMP[0],
×
4316
                                      papszXMP[0] + strlen(papszXMP[0]));
1✔
4317
                        abyNew.insert(abyNew.end(), abyJPEG.data() + nInsertPos,
×
4318
                                      abyJPEG.data() + abyJPEG.size());
1✔
4319
                        abyJPEG = std::move(abyNew);
1✔
4320
                    }
4321
                }
4322
            }
4323
            catch (const std::exception &)
×
4324
            {
4325
                abyJPEG.clear();
×
4326
            }
4327
            VSIFree(pJPEGContent);
9✔
4328

4329
            if (!abyJPEG.empty())
9✔
4330
            {
4331
                VSILFILE *fpImage = VSIFOpenL(pszFilename, "wb");
9✔
4332
                if (fpImage == nullptr)
9✔
4333
                {
4334
                    CPLError(CE_Failure, CPLE_OpenFailed,
×
4335
                             "Unable to create jpeg file %s.", pszFilename);
4336

4337
                    return nullptr;
×
4338
                }
4339
                if (VSIFWriteL(abyJPEG.data(), 1, abyJPEG.size(), fpImage) !=
18✔
4340
                    abyJPEG.size())
9✔
4341
                {
4342
                    CPLError(CE_Failure, CPLE_FileIO,
×
4343
                             "Failure writing data: %s", VSIStrerror(errno));
×
4344
                    VSIFCloseL(fpImage);
×
4345
                    return nullptr;
×
4346
                }
4347
                if (VSIFCloseL(fpImage) != 0)
9✔
4348
                {
4349
                    CPLError(CE_Failure, CPLE_FileIO,
×
4350
                             "Failure writing data: %s", VSIStrerror(errno));
×
4351
                    return nullptr;
×
4352
                }
4353

4354
                pfnProgress(1.0, nullptr, pProgressData);
9✔
4355

4356
                // Append masks to the jpeg file if necessary.
4357
                const auto poLastSrcBand = poSrcDS->GetRasterBand(nBands);
9✔
4358
                const bool bAppendMask =
4359
                    poLastSrcBand != nullptr &&
9✔
4360
                    poLastSrcBand->GetColorInterpretation() == GCI_AlphaBand &&
10✔
4361
                    CPLFetchBool(papszOptions, "INTERNAL_MASK", true);
1✔
4362

4363
                if (bAppendMask)
9✔
4364
                {
4365
                    CPLDebug("JPEG", "Appending Mask Bitmap");
1✔
4366

4367
                    CPLErr eErr = JPGAppendMask(pszFilename, poLastSrcBand,
1✔
4368
                                                nullptr, nullptr);
4369

4370
                    if (eErr != CE_None)
1✔
4371
                    {
4372
                        VSIUnlink(pszFilename);
×
4373
                        return nullptr;
×
4374
                    }
4375
                }
4376

4377
                // Do we need a world file?
4378
                if (CPLFetchBool(papszOptions, "WORLDFILE", false))
9✔
4379
                {
4380
                    double adfGeoTransform[6] = {};
×
4381

4382
                    poSrcDS->GetGeoTransform(adfGeoTransform);
×
4383
                    GDALWriteWorldFile(pszFilename, "wld", adfGeoTransform);
×
4384
                }
4385

4386
                // Re-open dataset, and copy any auxiliary pam information.
4387

4388
                // If writing to stdout, we can't reopen it, so return
4389
                // a fake dataset to make the caller happy.
4390
                if (CPLTestBool(
9✔
4391
                        CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", "YES")))
4392
                {
4393
                    CPLPushErrorHandler(CPLQuietErrorHandler);
9✔
4394

4395
                    JPGDatasetOpenArgs sArgs;
9✔
4396
                    sArgs.pszFilename = pszFilename;
9✔
4397
                    sArgs.bDoPAMInitialize = true;
9✔
4398
                    sArgs.bUseInternalOverviews = true;
9✔
4399

4400
                    auto poDS = Open(&sArgs);
9✔
4401
                    CPLPopErrorHandler();
9✔
4402
                    if (poDS)
9✔
4403
                    {
4404
                        poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
8✔
4405
                        return poDS;
8✔
4406
                    }
4407

4408
                    CPLErrorReset();
1✔
4409
                }
4410

4411
                JPGDataset *poJPG_DS = new JPGDataset();
1✔
4412
                poJPG_DS->nRasterXSize = poSrcDS->GetRasterXSize();
1✔
4413
                poJPG_DS->nRasterYSize = poSrcDS->GetRasterYSize();
1✔
4414
                for (int i = 0; i < nBands; i++)
2✔
4415
                    poJPG_DS->SetBand(i + 1, JPGCreateBand(poJPG_DS, i + 1));
1✔
4416
                return poJPG_DS;
1✔
4417
            }
4418
        }
4419
    }
4420

4421
    if (!EQUAL(pszLossLessCopy, "AUTO") && CPLTestBool(pszLossLessCopy))
264✔
4422
    {
4423
        CPLError(CE_Failure, CPLE_AppDefined,
×
4424
                 "LOSSLESS_COPY=YES requested but not possible");
4425
        return nullptr;
×
4426
    }
4427

4428
    // Some some rudimentary checks.
4429
    if (nBands != 1 && nBands != 3 && nBands != 4)
264✔
4430
    {
4431
        CPLError(CE_Failure, CPLE_NotSupported,
3✔
4432
                 "JPEG driver doesn't support %d bands.  Must be 1 (grey), "
4433
                 "3 (RGB) or 4 bands (CMYK).\n",
4434
                 nBands);
4435

4436
        return nullptr;
3✔
4437
    }
4438

4439
    if (nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
261✔
4440
    {
4441
        CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported,
×
4442
                 "JPEG driver ignores color table. "
4443
                 "The source raster band will be considered as grey level.\n"
4444
                 "Consider using color table expansion "
4445
                 "(-expand option in gdal_translate)");
4446
        if (bStrict)
×
4447
            return nullptr;
×
4448
    }
4449

4450
    if (nBands == 4 &&
263✔
4451
        poSrcDS->GetRasterBand(1)->GetColorInterpretation() != GCI_CyanBand)
2✔
4452
    {
4453
        CPLError(CE_Warning, CPLE_AppDefined,
2✔
4454
                 "4-band JPEGs will be interpreted on reading as in CMYK "
4455
                 "colorspace");
4456
    }
4457

4458
    VSILFILE *fpImage = nullptr;
261✔
4459
    GDALJPEGUserData sUserData;
261✔
4460
    sUserData.bNonFatalErrorEncountered = false;
261✔
4461
    GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
261✔
4462

4463
#if defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12)
4464
    if (eDT != GDT_Byte && eDT != GDT_UInt16)
261✔
4465
    {
4466
        CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported,
9✔
4467
                 "JPEG driver doesn't support data type %s. "
4468
                 "Only eight and twelve bit bands supported.",
4469
                 GDALGetDataTypeName(
4470
                     poSrcDS->GetRasterBand(1)->GetRasterDataType()));
4471

4472
        if (bStrict)
9✔
4473
            return nullptr;
9✔
4474
    }
4475

4476
    if (eDT == GDT_UInt16 || eDT == GDT_Int16)
252✔
4477
    {
4478
#if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
4479
        return JPEGDataset12CreateCopy(pszFilename, poSrcDS, bStrict,
1✔
4480
                                       papszOptions, pfnProgress,
4481
                                       pProgressData);
1✔
4482
#else
4483
        eDT = GDT_UInt16;
1✔
4484
#endif  // defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
4485
    }
4486
    else
4487
    {
4488
        eDT = GDT_Byte;
250✔
4489
    }
4490

4491
#else   // !(defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12))
4492
    if (eDT != GDT_Byte)
4493
    {
4494
        CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported,
4495
                 "JPEG driver doesn't support data type %s. "
4496
                 "Only eight bit byte bands supported.\n",
4497
                 GDALGetDataTypeName(
4498
                     poSrcDS->GetRasterBand(1)->GetRasterDataType()));
4499

4500
        if (bStrict)
4501
            return nullptr;
4502
    }
4503

4504
    eDT = GDT_Byte;  // force to 8bit.
4505
#endif  // !(defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12))
4506

4507
    // What options has the caller selected?
4508
    int nQuality = 75;
251✔
4509
    const char *pszQuality = CSLFetchNameValue(papszOptions, "QUALITY");
251✔
4510
    if (pszQuality)
251✔
4511
    {
4512
        nQuality = atoi(pszQuality);
113✔
4513
        if (nQuality < 1 || nQuality > 100)
113✔
4514
        {
4515
            CPLError(CE_Failure, CPLE_IllegalArg,
×
4516
                     "QUALITY=%s is not a legal value in the range 1-100.",
4517
                     pszQuality);
4518
            return nullptr;
×
4519
        }
4520
    }
4521

4522
    // Create the dataset.
4523
    fpImage = VSIFOpenL(pszFilename, "wb");
251✔
4524
    if (fpImage == nullptr)
251✔
4525
    {
4526
        CPLError(CE_Failure, CPLE_OpenFailed,
3✔
4527
                 "Unable to create jpeg file %s.\n", pszFilename);
4528
        return nullptr;
3✔
4529
    }
4530

4531
    struct jpeg_compress_struct sCInfo;
4532
    struct jpeg_error_mgr sJErr;
4533
    GByte *pabyScanline;
4534

4535
    // Does the source have a mask?  If so, we will append it to the
4536
    // jpeg file after the imagery.
4537
    const int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags();
248✔
4538
    const bool bAppendMask = !(nMaskFlags & GMF_ALL_VALID) &&
7✔
4539
                             (nBands == 1 || (nMaskFlags & GMF_PER_DATASET)) &&
256✔
4540
                             CPLFetchBool(papszOptions, "INTERNAL_MASK", true);
7✔
4541

4542
    // Nasty trick to avoid variable clobbering issues with setjmp/longjmp.
4543
    return CreateCopyStage2(pszFilename, poSrcDS, papszOptions, pfnProgress,
248✔
4544
                            pProgressData, fpImage, eDT, nQuality, bAppendMask,
4545
                            sUserData, sCInfo, sJErr, pabyScanline);
248✔
4546
}
4547

4548
GDALDataset *JPGDataset::CreateCopyStage2(
248✔
4549
    const char *pszFilename, GDALDataset *poSrcDS, char **papszOptions,
4550
    GDALProgressFunc pfnProgress, void *pProgressData, VSILFILE *fpImage,
4551
    GDALDataType eDT, int nQuality, bool bAppendMask,
4552
    GDALJPEGUserData &sUserData, struct jpeg_compress_struct &sCInfo,
4553
    struct jpeg_error_mgr &sJErr, GByte *&pabyScanline)
4554

4555
{
4556
    if (setjmp(sUserData.setjmp_buffer))
248✔
4557
    {
4558
        if (fpImage)
×
4559
            VSIFCloseL(fpImage);
×
4560
        return nullptr;
×
4561
    }
4562

4563
    if (!pfnProgress(0.0, nullptr, pProgressData))
248✔
4564
        return nullptr;
×
4565

4566
    // Initialize JPG access to the file.
4567
    sCInfo.err = jpeg_std_error(&sJErr);
248✔
4568
    sJErr.error_exit = JPGDataset::ErrorExit;
248✔
4569
    sJErr.output_message = JPGDataset::OutputMessage;
248✔
4570
    sUserData.p_previous_emit_message = sJErr.emit_message;
248✔
4571
    sJErr.emit_message = JPGDataset::EmitMessage;
248✔
4572
    sCInfo.client_data = &sUserData;
248✔
4573

4574
    jpeg_create_compress(&sCInfo);
248✔
4575
    if (setjmp(sUserData.setjmp_buffer))
248✔
4576
    {
4577
        if (fpImage)
1✔
4578
            VSIFCloseL(fpImage);
1✔
4579
        jpeg_destroy_compress(&sCInfo);
1✔
4580
        return nullptr;
1✔
4581
    }
4582

4583
    jpeg_vsiio_dest(&sCInfo, fpImage);
248✔
4584

4585
    const int nXSize = poSrcDS->GetRasterXSize();
248✔
4586
    const int nYSize = poSrcDS->GetRasterYSize();
248✔
4587
    const int nBands = poSrcDS->GetRasterCount();
248✔
4588
    sCInfo.image_width = nXSize;
248✔
4589
    sCInfo.image_height = nYSize;
248✔
4590
    sCInfo.input_components = nBands;
248✔
4591

4592
    if (nBands == 3)
248✔
4593
        sCInfo.in_color_space = JCS_RGB;
156✔
4594
    else if (nBands == 1)
92✔
4595
        sCInfo.in_color_space = JCS_GRAYSCALE;
90✔
4596
    else
4597
        sCInfo.in_color_space = JCS_UNKNOWN;
2✔
4598

4599
    jpeg_set_defaults(&sCInfo);
248✔
4600

4601
    // libjpeg turbo 1.5.2 honours max_memory_to_use, but has no backing
4602
    // store implementation, so better not set max_memory_to_use ourselves.
4603
    // See https://github.com/libjpeg-turbo/libjpeg-turbo/issues/162
4604
    if (sCInfo.mem->max_memory_to_use > 0)
248✔
4605
    {
4606
        // This is to address bug related in ticket #1795.
4607
        if (CPLGetConfigOption("JPEGMEM", nullptr) == nullptr)
×
4608
        {
4609
            // If the user doesn't provide a value for JPEGMEM, we want to be
4610
            // sure that at least 500 MB will be used before creating the
4611
            // temporary file.
4612
            const long nMinMemory = 500 * 1024 * 1024;
×
4613
            sCInfo.mem->max_memory_to_use =
×
4614
                std::max(sCInfo.mem->max_memory_to_use, nMinMemory);
×
4615
        }
4616
    }
4617

4618
    if (eDT == GDT_UInt16)
248✔
4619
    {
4620
        sCInfo.data_precision = 12;
1✔
4621
    }
4622
    else
4623
    {
4624
        sCInfo.data_precision = 8;
247✔
4625
    }
4626

4627
    const char *pszVal = CSLFetchNameValue(papszOptions, "ARITHMETIC");
248✔
4628
    if (pszVal)
248✔
4629
        sCInfo.arith_code = CPLTestBool(pszVal);
1✔
4630

4631
    // Optimized Huffman coding. Supposedly slower according to libjpeg doc
4632
    // but no longer significant with today computer standards.
4633
    if (!sCInfo.arith_code)
248✔
4634
        sCInfo.optimize_coding = TRUE;
247✔
4635

4636
#if JPEG_LIB_VERSION_MAJOR >= 8 &&                                             \
4637
    (JPEG_LIB_VERSION_MAJOR > 8 || JPEG_LIB_VERSION_MINOR >= 3)
4638
    pszVal = CSLFetchNameValue(papszOptions, "BLOCK");
4639
    if (pszVal)
4640
        sCInfo.block_size = atoi(pszVal);
4641
#endif
4642

4643
#if JPEG_LIB_VERSION_MAJOR >= 9
4644
    pszVal = CSLFetchNameValue(papszOptions, "COLOR_TRANSFORM");
4645
    if (pszVal)
4646
    {
4647
        sCInfo.color_transform =
4648
            EQUAL(pszVal, "RGB1") ? JCT_SUBTRACT_GREEN : JCT_NONE;
4649
        jpeg_set_colorspace(&sCInfo, JCS_RGB);
4650
    }
4651
    else
4652
#endif
4653

4654
        // Mostly for debugging purposes.
4655
        if (nBands == 3 &&
404✔
4656
            CPLTestBool(CPLGetConfigOption("JPEG_WRITE_RGB", "NO")))
156✔
4657
        {
4658
            jpeg_set_colorspace(&sCInfo, JCS_RGB);
×
4659
        }
4660

4661
#ifdef JPEG_LIB_MK1
4662
    sCInfo.bits_in_jsample = sCInfo.data_precision;
4663
    // Always force to 16 bit for JPEG_LIB_MK1
4664
    const GDALDataType eWorkDT = GDT_UInt16;
4665
#else
4666
    const GDALDataType eWorkDT = eDT;
248✔
4667
#endif
4668

4669
    jpeg_set_quality(&sCInfo, nQuality, TRUE);
248✔
4670

4671
    const bool bProgressive = CPLFetchBool(papszOptions, "PROGRESSIVE", false);
248✔
4672
    if (bProgressive)
248✔
4673
        jpeg_simple_progression(&sCInfo);
3✔
4674

4675
    jpeg_start_compress(&sCInfo, TRUE);
248✔
4676

4677
    JPGAddEXIF(eWorkDT, poSrcDS, papszOptions, &sCInfo,
247✔
4678
               (my_jpeg_write_m_header)jpeg_write_m_header,
4679
               (my_jpeg_write_m_byte)jpeg_write_m_byte, CreateCopy);
4680

4681
    // Add comment if available.
4682
    const char *pszComment = CSLFetchNameValue(papszOptions, "COMMENT");
247✔
4683
    if (pszComment)
247✔
4684
        jpeg_write_marker(&sCInfo, JPEG_COM,
3✔
4685
                          reinterpret_cast<const JOCTET *>(pszComment),
4686
                          static_cast<unsigned int>(strlen(pszComment)));
3✔
4687

4688
    // Save ICC profile if available.
4689
    const char *pszICCProfile =
4690
        CSLFetchNameValue(papszOptions, "SOURCE_ICC_PROFILE");
247✔
4691
    if (pszICCProfile == nullptr)
247✔
4692
        pszICCProfile =
4693
            poSrcDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE");
246✔
4694

4695
    if (pszICCProfile != nullptr)
247✔
4696
        JPGAddICCProfile(&sCInfo, pszICCProfile,
3✔
4697
                         (my_jpeg_write_m_header)jpeg_write_m_header,
4698
                         (my_jpeg_write_m_byte)jpeg_write_m_byte);
4699

4700
    // Loop over image, copying image data.
4701
    const int nWorkDTSize = GDALGetDataTypeSizeBytes(eWorkDT);
247✔
4702
    pabyScanline = static_cast<GByte *>(
247✔
4703
        CPLMalloc(cpl::fits_on<int>(nBands * nXSize * nWorkDTSize)));
247✔
4704

4705
    if (setjmp(sUserData.setjmp_buffer))
247✔
4706
    {
4707
        VSIFCloseL(fpImage);
10✔
4708
        CPLFree(pabyScanline);
10✔
4709
        jpeg_destroy_compress(&sCInfo);
10✔
4710
        return nullptr;
10✔
4711
    }
4712

4713
    CPLErr eErr = CE_None;
247✔
4714
    bool bClipWarn = false;
247✔
4715
    for (int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++)
35,994✔
4716
    {
4717
        eErr = poSrcDS->RasterIO(
142,988✔
4718
            GF_Read, 0, iLine, nXSize, 1, pabyScanline, nXSize, 1, eWorkDT,
4719
            nBands, nullptr, cpl::fits_on<int>(nBands * nWorkDTSize),
35,747✔
4720
            cpl::fits_on<int>(nBands * nXSize * nWorkDTSize), nWorkDTSize,
35,747✔
4721
            nullptr);
4722

4723
        // Clamp 16bit values to 12bit.
4724
        if (nWorkDTSize == 2)
35,747✔
4725
        {
4726
            GUInt16 *panScanline = reinterpret_cast<GUInt16 *>(pabyScanline);
10✔
4727

4728
            for (int iPixel = 0; iPixel < nXSize * nBands; iPixel++)
110✔
4729
            {
4730
                if (panScanline[iPixel] > 4095)
100✔
4731
                {
4732
                    panScanline[iPixel] = 4095;
×
4733
                    if (!bClipWarn)
×
4734
                    {
4735
                        bClipWarn = true;
×
4736
                        CPLError(CE_Warning, CPLE_AppDefined,
×
4737
                                 "One or more pixels clipped to fit "
4738
                                 "12bit domain for jpeg output.");
4739
                    }
4740
                }
4741
            }
4742
        }
4743

4744
        GDAL_JSAMPLE *ppSamples =
35,747✔
4745
            reinterpret_cast<GDAL_JSAMPLE *>(pabyScanline);
35,747✔
4746

4747
        if (eErr == CE_None)
35,747✔
4748
        {
4749
#if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
4750
            jpeg12_write_scanlines(&sCInfo, &ppSamples, 1);
4751
#else
4752
            jpeg_write_scanlines(&sCInfo, &ppSamples, 1);
35,747✔
4753
#endif
4754
        }
4755
        if (eErr == CE_None &&
71,494✔
4756
            !pfnProgress((iLine + 1) / ((bAppendMask ? 2 : 1) *
71,494✔
4757
                                        static_cast<double>(nYSize)),
35,747✔
4758
                         nullptr, pProgressData))
4759
        {
4760
            eErr = CE_Failure;
1✔
4761
            CPLError(CE_Failure, CPLE_UserInterrupt,
1✔
4762
                     "User terminated CreateCopy()");
4763
        }
4764
    }
4765

4766
    // Cleanup and close.
4767
    if (eErr == CE_None)
247✔
4768
        jpeg_finish_compress(&sCInfo);
246✔
4769
    jpeg_destroy_compress(&sCInfo);
237✔
4770

4771
    // Free scanline and image after jpeg_finish_compress since this could
4772
    // cause a longjmp to occur.
4773
    CPLFree(pabyScanline);
237✔
4774

4775
    VSIFCloseL(fpImage);
237✔
4776

4777
    if (eErr != CE_None)
237✔
4778
    {
4779
        VSIUnlink(pszFilename);
1✔
4780
        return nullptr;
1✔
4781
    }
4782

4783
    // Append masks to the jpeg file if necessary.
4784
    int nCloneFlags = GCIF_PAM_DEFAULT & ~GCIF_METADATA;
236✔
4785
    if (bAppendMask)
236✔
4786
    {
4787
        CPLDebug("JPEG", "Appending Mask Bitmap");
7✔
4788

4789
        void *pScaledData =
4790
            GDALCreateScaledProgress(0.5, 1, pfnProgress, pProgressData);
7✔
4791
        eErr =
4792
            JPGAppendMask(pszFilename, poSrcDS->GetRasterBand(1)->GetMaskBand(),
7✔
4793
                          GDALScaledProgress, pScaledData);
4794
        GDALDestroyScaledProgress(pScaledData);
7✔
4795
        nCloneFlags &= (~GCIF_MASK);
7✔
4796

4797
        if (eErr != CE_None)
7✔
4798
        {
4799
            VSIUnlink(pszFilename);
×
4800
            return nullptr;
×
4801
        }
4802
    }
4803

4804
    // Do we need a world file?
4805
    if (CPLFetchBool(papszOptions, "WORLDFILE", false))
236✔
4806
    {
4807
        double adfGeoTransform[6] = {};
1✔
4808

4809
        poSrcDS->GetGeoTransform(adfGeoTransform);
1✔
4810
        GDALWriteWorldFile(pszFilename, "wld", adfGeoTransform);
1✔
4811
    }
4812

4813
    // Re-open dataset, and copy any auxiliary pam information.
4814

4815
    // If writing to stdout, we can't reopen it, so return
4816
    // a fake dataset to make the caller happy.
4817
    if (CPLTestBool(CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", "YES")))
236✔
4818
    {
4819
        CPLPushErrorHandler(CPLQuietErrorHandler);
173✔
4820

4821
        JPGDatasetOpenArgs sArgs;
173✔
4822
        sArgs.pszFilename = pszFilename;
173✔
4823
        sArgs.bDoPAMInitialize = true;
173✔
4824
        sArgs.bUseInternalOverviews = true;
173✔
4825

4826
        auto poDS = Open(&sArgs);
173✔
4827
        CPLPopErrorHandler();
173✔
4828
        if (poDS)
173✔
4829
        {
4830
            poDS->CloneInfo(poSrcDS, nCloneFlags);
172✔
4831

4832
            char **papszExcludedDomains =
4833
                CSLAddString(nullptr, "COLOR_PROFILE");
172✔
4834
            char **papszMD = poSrcDS->GetMetadata();
172✔
4835
            bool bOnlyEXIF = true;
172✔
4836
            for (char **papszIter = papszMD; papszIter && *papszIter;
209✔
4837
                 ++papszIter)
4838
            {
4839
                if (!STARTS_WITH_CI(*papszIter, "EXIF_"))
55✔
4840
                {
4841
                    bOnlyEXIF = false;
18✔
4842
                    break;
18✔
4843
                }
4844
            }
4845
            if (bOnlyEXIF)
172✔
4846
                papszExcludedDomains = CSLAddString(papszExcludedDomains, "");
154✔
4847
            GDALDriver::DefaultCopyMetadata(poSrcDS, poDS, papszOptions,
172✔
4848
                                            papszExcludedDomains);
4849
            CSLDestroy(papszExcludedDomains);
172✔
4850

4851
            return poDS;
172✔
4852
        }
4853

4854
        CPLErrorReset();
1✔
4855
    }
4856

4857
    JPGDataset *poJPG_DS = new JPGDataset();
64✔
4858
    poJPG_DS->nRasterXSize = nXSize;
64✔
4859
    poJPG_DS->nRasterYSize = nYSize;
64✔
4860
    for (int i = 0; i < nBands; i++)
212✔
4861
        poJPG_DS->SetBand(i + 1, JPGCreateBand(poJPG_DS, i + 1));
148✔
4862
    return poJPG_DS;
64✔
4863
}
4864

4865
/************************************************************************/
4866
/*                         GDALRegister_JPEG()                          */
4867
/************************************************************************/
4868

4869
#if !defined(JPGDataset)
4870

4871
char **GDALJPGDriver::GetMetadata(const char *pszDomain)
36✔
4872
{
4873
    GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
36✔
4874
    return GDALDriver::GetMetadata(pszDomain);
36✔
4875
}
4876

4877
// C_ARITH_CODING_SUPPORTED is defined in libjpeg-turbo's jconfig.h
4878
#ifndef C_ARITH_CODING_SUPPORTED
4879
static void GDALJPEGIsArithmeticCodingAvailableErrorExit(j_common_ptr cinfo)
4880
{
4881
    jmp_buf *p_setjmp_buffer = static_cast<jmp_buf *>(cinfo->client_data);
4882
    // Return control to the setjmp point.
4883
    longjmp(*p_setjmp_buffer, 1);
4884
}
4885

4886
// Runtime check if arithmetic coding is available.
4887
static bool GDALJPEGIsArithmeticCodingAvailable()
4888
{
4889
    struct jpeg_compress_struct sCInfo;
4890
    struct jpeg_error_mgr sJErr;
4891
    jmp_buf setjmp_buffer;
4892
    if (setjmp(setjmp_buffer))
4893
    {
4894
        jpeg_destroy_compress(&sCInfo);
4895
        return false;
4896
    }
4897
    sCInfo.err = jpeg_std_error(&sJErr);
4898
    sJErr.error_exit = GDALJPEGIsArithmeticCodingAvailableErrorExit;
4899
    sCInfo.client_data = &setjmp_buffer;
4900
    jpeg_create_compress(&sCInfo);
4901
    // Hopefully nothing will be written.
4902
    jpeg_stdio_dest(&sCInfo, stderr);
4903
    sCInfo.image_width = 1;
4904
    sCInfo.image_height = 1;
4905
    sCInfo.input_components = 1;
4906
    sCInfo.in_color_space = JCS_UNKNOWN;
4907
    jpeg_set_defaults(&sCInfo);
4908
    sCInfo.arith_code = TRUE;
4909
    jpeg_start_compress(&sCInfo, FALSE);
4910
    jpeg_abort_compress(&sCInfo);
4911
    jpeg_destroy_compress(&sCInfo);
4912

4913
    return true;
4914
}
4915
#endif
4916

4917
const char *GDALJPGDriver::GetMetadataItem(const char *pszName,
62,686✔
4918
                                           const char *pszDomain)
4919
{
4920
    if (pszName != nullptr && EQUAL(pszName, GDAL_DMD_CREATIONOPTIONLIST) &&
62,686✔
4921
        (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
125,688✔
4922
        GDALDriver::GetMetadataItem(pszName, pszDomain) == nullptr)
320✔
4923
    {
4924
        CPLString osCreationOptions =
4925
            "<CreationOptionList>\n"
4926
            "   <Option name='PROGRESSIVE' type='boolean' description='whether "
4927
            "to generate a progressive JPEG' default='NO'/>\n"
4928
            "   <Option name='QUALITY' type='int' description='good=100, "
4929
            "bad=1, default=75'/>\n"
4930
            "   <Option name='LOSSLESS_COPY' type='string-select' "
4931
            "description='Whether conversion should be lossless' "
4932
            "default='AUTO'>"
4933
            "     <Value>AUTO</Value>"
4934
            "     <Value>YES</Value>"
4935
            "     <Value>NO</Value>"
4936
            "   </Option>"
4937
            "   <Option name='WORLDFILE' type='boolean' description='whether "
4938
            "to generate a worldfile' default='NO'/>\n"
4939
            "   <Option name='INTERNAL_MASK' type='boolean' "
4940
            "description='whether to generate a validity mask' "
4941
            "default='YES'/>\n";
22✔
4942
#ifndef C_ARITH_CODING_SUPPORTED
4943
        if (GDALJPEGIsArithmeticCodingAvailable())
4944
#endif
4945
        {
4946
            osCreationOptions += "   <Option name='ARITHMETIC' type='boolean' "
4947
                                 "description='whether to use arithmetic "
4948
                                 "encoding' default='NO'/>\n";
11✔
4949
        }
4950
        osCreationOptions +=
4951
#if JPEG_LIB_VERSION_MAJOR >= 8 &&                                             \
4952
    (JPEG_LIB_VERSION_MAJOR > 8 || JPEG_LIB_VERSION_MINOR >= 3)
4953
            "   <Option name='BLOCK' type='int' description='between 1 and "
4954
            "16'/>\n"
4955
#endif
4956
#if JPEG_LIB_VERSION_MAJOR >= 9
4957
            "   <Option name='COLOR_TRANSFORM' type='string-select'>\n"
4958
            "       <Value>RGB</Value>"
4959
            "       <Value>RGB1</Value>"
4960
            "   </Option>"
4961
#endif
4962
            "   <Option name='COMMENT' description='Comment' type='string'/>\n"
4963
            "   <Option name='SOURCE_ICC_PROFILE' description='ICC profile "
4964
            "encoded in Base64' type='string'/>\n"
4965
            "   <Option name='EXIF_THUMBNAIL' type='boolean' "
4966
            "description='whether to generate an EXIF thumbnail(overview). By "
4967
            "default its max dimension will be 128' default='NO'/>\n"
4968
            "   <Option name='THUMBNAIL_WIDTH' type='int' description='Forced "
4969
            "thumbnail width' min='32' max='512'/>\n"
4970
            "   <Option name='THUMBNAIL_HEIGHT' type='int' description='Forced "
4971
            "thumbnail height' min='32' max='512'/>\n"
4972
            "   <Option name='WRITE_EXIF_METADATA' type='boolean' "
4973
            "description='whether to write EXIF_ metadata in a EXIF segment' "
4974
            "default='YES'/>"
4975
            "</CreationOptionList>\n";
11✔
4976
        SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, osCreationOptions);
11✔
4977
    }
4978
    return GDALDriver::GetMetadataItem(pszName, pszDomain);
62,682✔
4979
}
4980

4981
void GDALRegister_JPEG()
1,646✔
4982

4983
{
4984
    if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
1,646✔
4985
        return;
281✔
4986

4987
    GDALDriver *poDriver = new GDALJPGDriver();
1,365✔
4988
    JPEGDriverSetCommonMetadata(poDriver);
1,365✔
4989

4990
    poDriver->pfnOpen = JPGDatasetCommon::Open;
1,365✔
4991
    poDriver->pfnCreateCopy = JPGDataset::CreateCopy;
1,365✔
4992

4993
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,365✔
4994
}
4995
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc