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