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