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