Coverage Report

Created: 2025-06-09 07:07

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