Coverage Report

Created: 2026-09-26 08:22

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