Coverage Report

Created: 2025-06-09 08:44

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