Coverage Report

Created: 2026-03-30 09:00

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