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