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