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