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 11874 : static void SetMaxMemoryToUse(struct jpeg_decompress_struct *psDInfo)
121 : {
122 : // This is to address bug related in ticket #1795.
123 11874 : 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 11874 : const long nMinMemory = 500 * 1024 * 1024;
128 11874 : psDInfo->mem->max_memory_to_use =
129 11874 : std::max(psDInfo->mem->max_memory_to_use, nMinMemory);
130 : }
131 11875 : }
132 :
133 : #if !defined(JPGDataset)
134 :
135 : /************************************************************************/
136 : /* ReadImageStructureMetadata() */
137 : /************************************************************************/
138 :
139 5 : void JPGDatasetCommon::ReadImageStructureMetadata()
140 : {
141 5 : if (bHasReadImageStructureMetadata)
142 0 : return;
143 :
144 5 : bHasReadImageStructureMetadata = true;
145 5 : if (GetDataPrecision() != 8)
146 0 : return; // quality guessing not implemented for 12-bit JPEG for now
147 :
148 : // Save current position to avoid disturbing JPEG stream decoding.
149 5 : const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
150 :
151 : GByte abyChunkHeader[4];
152 5 : int nChunkLoc = 2;
153 5 : constexpr GByte MARKER_QUANT_TABLE = 0xDB;
154 : struct CPLMD5Context context;
155 5 : CPLMD5Init(&context);
156 :
157 : while (true)
158 : {
159 20 : if (VSIFSeekL(m_fpImage, nChunkLoc, SEEK_SET) != 0)
160 0 : break;
161 :
162 20 : if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, m_fpImage) !=
163 : 1)
164 0 : break;
165 :
166 20 : const int nChunkLength = abyChunkHeader[2] * 256 + abyChunkHeader[3];
167 20 : if (abyChunkHeader[0] == 0xFF &&
168 20 : abyChunkHeader[1] == MARKER_QUANT_TABLE && nChunkLength > 2)
169 : {
170 14 : std::vector<GByte> abyTable(nChunkLength);
171 7 : abyTable[0] = abyChunkHeader[2];
172 7 : abyTable[1] = abyChunkHeader[3];
173 7 : if (VSIFReadL(&abyTable[2], nChunkLength - 2, 1, m_fpImage) == 1)
174 : {
175 7 : CPLMD5Update(&context, &abyTable[0], nChunkLength);
176 7 : }
177 : }
178 : else
179 : {
180 13 : if (abyChunkHeader[0] != 0xFF || (abyChunkHeader[1] & 0xf0) != 0xe0)
181 : break; // Not an APP chunk.
182 : }
183 :
184 15 : nChunkLoc += 2 + nChunkLength;
185 15 : }
186 :
187 5 : VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
188 :
189 : GByte digest[16];
190 5 : CPLMD5Final(digest, &context);
191 :
192 5 : const bool bIsYCbCr = nBands == 3 && GetJPEGColorSpace() == JCS_YCbCr;
193 410 : for (int i = 0; i < 100; i++)
194 : {
195 410 : if ((bIsYCbCr &&
196 160 : (memcmp(md5JPEGQuantTable_3_YCBCR_8bit[i], digest, 16) == 0 ||
197 158 : memcmp(md5JPEGQuantTable_3_YCBCR_8bit_jpeg9e[i], digest, 16) ==
198 408 : 0)) ||
199 408 : (!bIsYCbCr &&
200 250 : memcmp(md5JPEGQuantTable_generic_8bit[i], digest, 16) == 0))
201 : {
202 5 : GDALDataset::SetMetadataItem(
203 : "JPEG_QUALITY", CPLSPrintf("%d", i + 1), "IMAGE_STRUCTURE");
204 5 : break;
205 : }
206 : }
207 : }
208 :
209 : /************************************************************************/
210 : /* ReadEXIFMetadata() */
211 : /************************************************************************/
212 116 : void JPGDatasetCommon::ReadEXIFMetadata()
213 : {
214 116 : if (bHasReadEXIFMetadata)
215 0 : return;
216 :
217 116 : CPLAssert(papszMetadata == nullptr);
218 :
219 : // Save current position to avoid disturbing JPEG stream decoding.
220 116 : const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
221 :
222 116 : if (EXIFInit(m_fpImage))
223 : {
224 47 : EXIFExtractMetadata(papszMetadata, m_fpImage, nTiffDirStart, bSwabflag,
225 47 : nTIFFHEADER, nExifOffset, nInterOffset, nGPSOffset);
226 :
227 47 : if (nExifOffset > 0)
228 : {
229 36 : EXIFExtractMetadata(papszMetadata, m_fpImage, nExifOffset,
230 36 : bSwabflag, nTIFFHEADER, nExifOffset,
231 36 : nInterOffset, nGPSOffset);
232 : }
233 47 : if (nInterOffset > 0)
234 : {
235 3 : EXIFExtractMetadata(papszMetadata, m_fpImage, nInterOffset,
236 3 : bSwabflag, nTIFFHEADER, nExifOffset,
237 3 : nInterOffset, nGPSOffset);
238 : }
239 47 : if (nGPSOffset > 0)
240 : {
241 21 : EXIFExtractMetadata(papszMetadata, m_fpImage, nGPSOffset, bSwabflag,
242 21 : nTIFFHEADER, nExifOffset, nInterOffset,
243 21 : nGPSOffset);
244 : }
245 :
246 : // Pix4D Mapper files have both DNG_CameraSerialNumber and EXIF_BodySerialNumber
247 : // set at the same value. Only expose the later in that situation.
248 47 : if (const char *pszDNG_CameraSerialNumber =
249 47 : CSLFetchNameValue(papszMetadata, "DNG_CameraSerialNumber"))
250 : {
251 : const char *pszEXIF_BodySerialNumber =
252 2 : CSLFetchNameValue(papszMetadata, "EXIF_BodySerialNumber");
253 2 : if (pszEXIF_BodySerialNumber &&
254 1 : EQUAL(pszDNG_CameraSerialNumber, pszEXIF_BodySerialNumber))
255 : {
256 1 : CPLDebug("JPEG", "Unsetting DNG_CameraSerialNumber as it has "
257 : "the same value as EXIF_BodySerialNumber");
258 1 : papszMetadata = CSLSetNameValue(
259 : papszMetadata, "DNG_CameraSerialNumber", nullptr);
260 : }
261 : }
262 :
263 : // Pix4D Mapper files have both DNG_UniqueCameraModel and EXIF_Model
264 : // set at the same value. Only expose the later in that situation.
265 47 : if (const char *pszDNG_UniqueCameraModel =
266 47 : CSLFetchNameValue(papszMetadata, "DNG_UniqueCameraModel"))
267 : {
268 : const char *pszEXIF_Model =
269 2 : CSLFetchNameValue(papszMetadata, "EXIF_Model");
270 2 : if (pszEXIF_Model && EQUAL(pszDNG_UniqueCameraModel, pszEXIF_Model))
271 : {
272 1 : CPLDebug("JPEG", "Unsetting DNG_UniqueCameraModel as it has "
273 : "the same value as EXIF_Model");
274 1 : papszMetadata = CSLSetNameValue(
275 : papszMetadata, "DNG_UniqueCameraModel", nullptr);
276 : }
277 : }
278 :
279 : // Avoid setting the PAM dirty bit just for that.
280 47 : const int nOldPamFlags = nPamFlags;
281 :
282 : // Append metadata from PAM after EXIF metadata.
283 47 : papszMetadata = CSLMerge(papszMetadata, GDALPamDataset::GetMetadata());
284 :
285 : // Expose XMP in EXIF in xml:XMP metadata domain
286 47 : if (GDALDataset::GetMetadata("xml:XMP") == nullptr)
287 : {
288 : const char *pszXMP =
289 46 : CSLFetchNameValue(papszMetadata, "EXIF_XmlPacket");
290 46 : if (pszXMP)
291 : {
292 0 : CPLDebug("JPEG", "Read XMP metadata from EXIF tag");
293 0 : const char *const apszMDList[2] = {pszXMP, nullptr};
294 0 : SetMetadata(const_cast<char **>(apszMDList), "xml:XMP");
295 :
296 0 : papszMetadata =
297 0 : CSLSetNameValue(papszMetadata, "EXIF_XmlPacket", nullptr);
298 : }
299 : }
300 :
301 47 : if (papszMetadata)
302 47 : SetMetadata(papszMetadata);
303 :
304 47 : nPamFlags = nOldPamFlags;
305 : }
306 :
307 116 : VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
308 :
309 116 : bHasReadEXIFMetadata = true;
310 : }
311 :
312 : /************************************************************************/
313 : /* ReadXMPMetadata() */
314 : /************************************************************************/
315 :
316 : // See ยง2.1.3 of
317 : // http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMPSpecificationPart3.pdf
318 :
319 70 : void JPGDatasetCommon::ReadXMPMetadata()
320 : {
321 70 : if (bHasReadXMPMetadata)
322 2 : return;
323 :
324 : // Save current position to avoid disturbing JPEG stream decoding.
325 68 : const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
326 :
327 : // Search for APP1 chunk.
328 68 : constexpr int APP1_BYTE = 0xe1;
329 68 : constexpr int JFIF_MARKER_SIZE = 2 + 2; // ID + size
330 68 : constexpr const char APP1_XMP_SIGNATURE[] = "http://ns.adobe.com/xap/1.0/";
331 68 : constexpr int APP1_XMP_SIGNATURE_LEN =
332 : static_cast<int>(sizeof(APP1_XMP_SIGNATURE));
333 68 : GByte abyChunkHeader[JFIF_MARKER_SIZE + APP1_XMP_SIGNATURE_LEN] = {};
334 68 : int nChunkLoc = 2;
335 68 : bool bFoundXMP = false;
336 :
337 : while (true)
338 : {
339 449 : if (VSIFSeekL(m_fpImage, nChunkLoc, SEEK_SET) != 0)
340 0 : break;
341 :
342 449 : if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, m_fpImage) !=
343 : 1)
344 7 : break;
345 :
346 442 : nChunkLoc += 2 + abyChunkHeader[2] * 256 + abyChunkHeader[3];
347 :
348 : // Not a marker
349 442 : if (abyChunkHeader[0] != 0xFF)
350 0 : break;
351 :
352 : // Stop on Start of Scan
353 442 : if (abyChunkHeader[1] == 0xDA)
354 48 : break;
355 :
356 394 : if (abyChunkHeader[1] == APP1_BYTE &&
357 32 : memcmp(reinterpret_cast<char *>(abyChunkHeader) + JFIF_MARKER_SIZE,
358 : APP1_XMP_SIGNATURE, APP1_XMP_SIGNATURE_LEN) == 0)
359 : {
360 13 : bFoundXMP = true;
361 13 : break; // APP1 - XMP.
362 : }
363 : }
364 :
365 68 : if (bFoundXMP)
366 : {
367 13 : const int nXMPLength = abyChunkHeader[2] * 256 + abyChunkHeader[3] - 2 -
368 : APP1_XMP_SIGNATURE_LEN;
369 13 : if (nXMPLength > 0)
370 : {
371 13 : char *pszXMP = static_cast<char *>(VSIMalloc(nXMPLength + 1));
372 13 : if (pszXMP)
373 : {
374 13 : if (VSIFReadL(pszXMP, nXMPLength, 1, m_fpImage) == 1)
375 : {
376 13 : pszXMP[nXMPLength] = '\0';
377 :
378 : // Avoid setting the PAM dirty bit just for that.
379 13 : const int nOldPamFlags = nPamFlags;
380 :
381 13 : char *apszMDList[2] = {pszXMP, nullptr};
382 13 : SetMetadata(apszMDList, "xml:XMP");
383 :
384 : // cppcheck-suppress redundantAssignment
385 13 : nPamFlags = nOldPamFlags;
386 : }
387 13 : VSIFree(pszXMP);
388 : }
389 : }
390 : }
391 :
392 68 : VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
393 :
394 68 : bHasReadXMPMetadata = true;
395 : }
396 :
397 : /************************************************************************/
398 : /* ReadThermalMetadata() */
399 : /************************************************************************/
400 17 : void JPGDatasetCommon::ReadThermalMetadata()
401 : {
402 17 : ReadFLIRMetadata();
403 17 : ReadDJIMetadata();
404 17 : }
405 :
406 : /************************************************************************/
407 : /* ReadDJIMetadata() */
408 : /************************************************************************/
409 :
410 17 : void JPGDatasetCommon::ReadDJIMetadata()
411 : {
412 17 : if (bHasReadDJIMetadata)
413 14 : return;
414 15 : bHasReadDJIMetadata = true;
415 :
416 15 : if (!m_abyRawThermalImage.empty())
417 : {
418 : // If there is already FLIR data, do not overwrite with DJI.
419 : // That combination is not expected, but just in case.
420 5 : return;
421 : }
422 :
423 10 : const auto make = GetMetadataItem("EXIF_Make");
424 10 : const bool bMakerDJI = make && STRCASECMP(make, "DJI") == 0;
425 10 : if (!bMakerDJI)
426 7 : return;
427 :
428 3 : 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 17 : void JPGDatasetCommon::ReadFLIRMetadata()
547 : {
548 17 : if (bHasReadFLIRMetadata)
549 12 : return;
550 15 : bHasReadFLIRMetadata = true;
551 :
552 : // Save current position to avoid disturbing JPEG stream decoding.
553 15 : const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
554 :
555 15 : int nChunkLoc = 2;
556 : // size of APP1 segment marker + size of "FLIR\0"
557 : GByte abyChunkHeader[4 + 5];
558 15 : std::vector<GByte> abyFLIR;
559 :
560 : while (true)
561 : {
562 175 : if (VSIFSeekL(m_fpImage, nChunkLoc, SEEK_SET) != 0)
563 0 : break;
564 :
565 175 : if (VSIFReadL(abyChunkHeader, sizeof(abyChunkHeader), 1, m_fpImage) !=
566 : 1)
567 0 : break;
568 :
569 175 : const int nMarkerLength =
570 175 : abyChunkHeader[2] * 256 + abyChunkHeader[3] - 2;
571 175 : nChunkLoc += 4 + nMarkerLength;
572 :
573 : // Not a marker
574 175 : if (abyChunkHeader[0] != 0xFF)
575 0 : break;
576 :
577 : // Stop on Start of Scan
578 175 : if (abyChunkHeader[1] == 0xDA)
579 15 : break;
580 :
581 160 : if (abyChunkHeader[1] == 0xe1 &&
582 19 : memcmp(abyChunkHeader + 4, "FLIR\0", 5) == 0)
583 : {
584 : // Somewhat arbitrary limit
585 5 : 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 5 : if (nMarkerLength < 8)
595 : {
596 0 : abyFLIR.clear();
597 0 : break;
598 : }
599 5 : size_t nOldSize = abyFLIR.size();
600 5 : abyFLIR.resize(nOldSize + nMarkerLength - 8);
601 : GByte abyIgnored[3]; // skip '\1' + chunk_idx + chunk_count
602 10 : if (VSIFReadL(abyIgnored, 3, 1, m_fpImage) != 1 ||
603 5 : VSIFReadL(&abyFLIR[nOldSize], nMarkerLength - 8, 1,
604 : m_fpImage) != 1)
605 : {
606 0 : abyFLIR.clear();
607 0 : break;
608 : }
609 : }
610 160 : }
611 : // Restore file pointer
612 15 : VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
613 :
614 15 : constexpr size_t FLIR_HEADER_SIZE = 64;
615 15 : if (abyFLIR.size() < FLIR_HEADER_SIZE)
616 10 : return;
617 5 : if (memcmp(&abyFLIR[0], "FFF\0", 4) != 0)
618 0 : return;
619 :
620 130 : const auto ReadString = [&abyFLIR](size_t nOffset, size_t nLen)
621 : {
622 : std::string osStr(
623 65 : reinterpret_cast<const char *>(abyFLIR.data()) + nOffset, nLen);
624 65 : osStr.resize(strlen(osStr.c_str()));
625 65 : return osStr;
626 5 : };
627 :
628 5 : bool bLittleEndian = false;
629 :
630 240 : const auto ReadUInt16 = [&abyFLIR, &bLittleEndian](size_t nOffset)
631 : {
632 : std::uint16_t nVal;
633 120 : memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
634 120 : if (bLittleEndian)
635 40 : CPL_LSBPTR16(&nVal);
636 : else
637 80 : CPL_MSBPTR16(&nVal);
638 120 : return nVal;
639 5 : };
640 :
641 10 : const auto ReadInt16 = [&abyFLIR, &bLittleEndian](size_t nOffset)
642 : {
643 : std::int16_t nVal;
644 5 : memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
645 5 : if (bLittleEndian)
646 5 : CPL_LSBPTR16(&nVal);
647 : else
648 0 : CPL_MSBPTR16(&nVal);
649 5 : return nVal;
650 5 : };
651 :
652 330 : const auto ReadUInt32 = [&abyFLIR, &bLittleEndian](size_t nOffset)
653 : {
654 : std::uint32_t nVal;
655 165 : memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
656 165 : if (bLittleEndian)
657 10 : CPL_LSBPTR32(&nVal);
658 : else
659 155 : CPL_MSBPTR32(&nVal);
660 165 : return nVal;
661 5 : };
662 :
663 10 : const auto ReadInt32 = [&abyFLIR, &bLittleEndian](size_t nOffset)
664 : {
665 : std::int32_t nVal;
666 5 : memcpy(&nVal, &abyFLIR[nOffset], sizeof(nVal));
667 5 : if (bLittleEndian)
668 5 : CPL_LSBPTR32(&nVal);
669 : else
670 0 : CPL_MSBPTR32(&nVal);
671 5 : return nVal;
672 5 : };
673 :
674 260 : const auto ReadFloat32 = [&abyFLIR, &bLittleEndian](size_t nOffset)
675 : {
676 : float fVal;
677 130 : memcpy(&fVal, &abyFLIR[nOffset], sizeof(fVal));
678 130 : if (bLittleEndian)
679 130 : CPL_LSBPTR32(&fVal);
680 : else
681 0 : CPL_MSBPTR32(&fVal);
682 130 : return fVal;
683 5 : };
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 5 : };
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 5 : explicit PamFlagKeeper(int &nPamFlagsRef)
703 5 : : m_nPamFlagsRef(nPamFlagsRef), m_nOldPamFlags(nPamFlagsRef)
704 : {
705 5 : }
706 :
707 5 : ~PamFlagKeeper()
708 5 : {
709 5 : m_nPamFlagsRef = m_nOldPamFlags;
710 5 : }
711 : };
712 :
713 5 : PamFlagKeeper oKeeper(nPamFlags);
714 :
715 : const auto SetStringIfNotEmpty =
716 65 : [&](const char *pszItemName, int nOffset, int nLength)
717 : {
718 130 : const auto str = ReadString(nOffset, nLength);
719 65 : if (!str.empty())
720 35 : SetMetadataItem(pszItemName, str.c_str(), "FLIR");
721 65 : };
722 5 : SetStringIfNotEmpty("CreatorSoftware", 4, 16);
723 :
724 : // Check file format version (big endian most of the time)
725 5 : const auto nFileFormatVersion = ReadUInt32(20);
726 5 : 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 5 : const auto nOffsetRecordDirectory = ReadUInt32(24);
740 5 : const auto nEntryCountRecordDirectory = ReadUInt32(28);
741 :
742 5 : CPLDebugOnly("JPEG", "FLIR: record offset %u, entry count %u",
743 : nOffsetRecordDirectory, nEntryCountRecordDirectory);
744 5 : constexpr size_t SIZE_RECORD_DIRECTORY = 32;
745 10 : if (nOffsetRecordDirectory < FLIR_HEADER_SIZE ||
746 5 : nOffsetRecordDirectory +
747 5 : SIZE_RECORD_DIRECTORY * nEntryCountRecordDirectory >
748 5 : 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 5 : [&](std::uint32_t nRecOffset, std::uint32_t nRecLength)
757 : {
758 5 : if (!(nRecLength >= 32 && nRecOffset + nRecLength <= abyFLIR.size()))
759 0 : return;
760 :
761 5 : const int nByteOrder = ReadUInt16(nRecOffset);
762 5 : if (nByteOrder == 512)
763 5 : bLittleEndian = !bLittleEndian;
764 0 : else if (nByteOrder != 2)
765 0 : return;
766 5 : const auto nImageWidth = ReadUInt16(nRecOffset + 2);
767 5 : SetMetadataItem("RawThermalImageWidth", CPLSPrintf("%d", nImageWidth),
768 5 : "FLIR");
769 5 : const auto nImageHeight = ReadUInt16(nRecOffset + 4);
770 5 : SetMetadataItem("RawThermalImageHeight", CPLSPrintf("%d", nImageHeight),
771 5 : "FLIR");
772 5 : m_bRawThermalLittleEndian = bLittleEndian;
773 5 : m_nRawThermalImageWidth = nImageWidth;
774 5 : m_nRawThermalImageHeight = nImageHeight;
775 5 : m_abyRawThermalImage.clear();
776 5 : m_abyRawThermalImage.insert(m_abyRawThermalImage.end(),
777 5 : abyFLIR.begin() + nRecOffset + 32,
778 15 : abyFLIR.begin() + nRecOffset + nRecLength);
779 :
780 5 : if (!STARTS_WITH(GetDescription(), "JPEG:"))
781 : {
782 5 : m_nSubdatasetCount++;
783 5 : SetMetadataItem(
784 : CPLSPrintf("SUBDATASET_%d_NAME", m_nSubdatasetCount),
785 : CPLSPrintf("JPEG:\"%s\":FLIR_RAW_THERMAL_IMAGE",
786 5 : GetDescription()),
787 5 : "SUBDATASETS");
788 5 : SetMetadataItem(
789 : CPLSPrintf("SUBDATASET_%d_DESC", m_nSubdatasetCount),
790 5 : "FLIR raw thermal image", "SUBDATASETS");
791 : }
792 5 : };
793 :
794 : // Read the Camera Info record
795 : const auto ReadCameraInfo =
796 5 : [&](std::uint32_t nRecOffset, std::uint32_t nRecLength)
797 : {
798 5 : if (!(nRecLength >= 1126 && nRecOffset + nRecLength <= abyFLIR.size()))
799 0 : return;
800 :
801 5 : const int nByteOrder = ReadUInt16(nRecOffset);
802 5 : if (nByteOrder == 512)
803 5 : bLittleEndian = !bLittleEndian;
804 0 : else if (nByteOrder != 2)
805 0 : return;
806 :
807 55 : const auto ReadFloat32FromKelvin = [=](std::uint32_t nOffset)
808 : {
809 55 : constexpr float ZERO_CELCIUS_IN_KELVIN = 273.15f;
810 55 : return ReadFloat32(nOffset) - ZERO_CELCIUS_IN_KELVIN;
811 5 : };
812 5 : SetMetadataItem("Emissivity",
813 5 : CPLSPrintf("%f", ReadFloat32(nRecOffset + 32)), "FLIR");
814 5 : SetMetadataItem("ObjectDistance",
815 5 : CPLSPrintf("%f m", ReadFloat32(nRecOffset + 36)),
816 5 : "FLIR");
817 5 : SetMetadataItem(
818 : "ReflectedApparentTemperature",
819 5 : CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 40)), "FLIR");
820 5 : SetMetadataItem(
821 : "AtmosphericTemperature",
822 5 : CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 44)), "FLIR");
823 5 : SetMetadataItem(
824 : "IRWindowTemperature",
825 5 : CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 48)), "FLIR");
826 5 : SetMetadataItem("IRWindowTransmission",
827 5 : CPLSPrintf("%f", ReadFloat32(nRecOffset + 52)), "FLIR");
828 5 : auto fRelativeHumidity = ReadFloat32(nRecOffset + 60);
829 5 : if (fRelativeHumidity > 2)
830 0 : fRelativeHumidity /= 100.0f; // Sometimes expressed in percentage
831 5 : SetMetadataItem("RelativeHumidity",
832 5 : CPLSPrintf("%f %%", 100.0f * fRelativeHumidity),
833 5 : "FLIR");
834 5 : SetMetadataItem("PlanckR1",
835 5 : CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 88)),
836 5 : "FLIR");
837 5 : SetMetadataItem("PlanckB",
838 5 : CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 92)),
839 5 : "FLIR");
840 5 : SetMetadataItem("PlanckF",
841 5 : CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 96)),
842 5 : "FLIR");
843 5 : SetMetadataItem("AtmosphericTransAlpha1",
844 5 : CPLSPrintf("%f", ReadFloat32(nRecOffset + 112)),
845 5 : "FLIR");
846 5 : SetMetadataItem("AtmosphericTransAlpha2",
847 5 : CPLSPrintf("%f", ReadFloat32(nRecOffset + 116)),
848 5 : "FLIR");
849 5 : SetMetadataItem("AtmosphericTransBeta1",
850 5 : CPLSPrintf("%f", ReadFloat32(nRecOffset + 120)),
851 5 : "FLIR");
852 5 : SetMetadataItem("AtmosphericTransBeta2",
853 5 : CPLSPrintf("%f", ReadFloat32(nRecOffset + 124)),
854 5 : "FLIR");
855 5 : SetMetadataItem("AtmosphericTransX",
856 5 : CPLSPrintf("%f", ReadFloat32(nRecOffset + 128)),
857 5 : "FLIR");
858 5 : SetMetadataItem(
859 : "CameraTemperatureRangeMax",
860 5 : CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 144)),
861 5 : "FLIR");
862 5 : SetMetadataItem(
863 : "CameraTemperatureRangeMin",
864 5 : CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 148)),
865 5 : "FLIR");
866 5 : SetMetadataItem(
867 : "CameraTemperatureMaxClip",
868 5 : CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 152)),
869 5 : "FLIR");
870 5 : SetMetadataItem(
871 : "CameraTemperatureMinClip",
872 5 : CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 156)),
873 5 : "FLIR");
874 5 : SetMetadataItem(
875 : "CameraTemperatureMaxWarn",
876 5 : CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 160)),
877 5 : "FLIR");
878 5 : SetMetadataItem(
879 : "CameraTemperatureMinWarn",
880 5 : CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 164)),
881 5 : "FLIR");
882 5 : SetMetadataItem(
883 : "CameraTemperatureMaxSaturated",
884 5 : CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 168)),
885 5 : "FLIR");
886 5 : SetMetadataItem(
887 : "CameraTemperatureMinSaturated",
888 5 : CPLSPrintf("%f C", ReadFloat32FromKelvin(nRecOffset + 172)),
889 5 : "FLIR");
890 :
891 5 : SetStringIfNotEmpty("CameraModel", nRecOffset + 212, 32);
892 5 : SetStringIfNotEmpty("CameraPartNumber", nRecOffset + 244, 16);
893 5 : SetStringIfNotEmpty("CameraSerialNumber", nRecOffset + 260, 16);
894 5 : SetStringIfNotEmpty("CameraSoftware", nRecOffset + 276, 16);
895 5 : SetStringIfNotEmpty("LensModel", nRecOffset + 368, 32);
896 5 : SetStringIfNotEmpty("LensPartNumber", nRecOffset + 400, 16);
897 5 : SetStringIfNotEmpty("LensSerialNumber", nRecOffset + 416, 16);
898 5 : SetMetadataItem("FieldOfView",
899 5 : CPLSPrintf("%f deg", ReadFloat32(nRecOffset + 436)),
900 5 : "FLIR");
901 5 : SetStringIfNotEmpty("FilterModel", nRecOffset + 492, 16);
902 5 : SetStringIfNotEmpty("FilterPartNumber", nRecOffset + 508, 32);
903 5 : SetStringIfNotEmpty("FilterSerialNumber", nRecOffset + 540, 32);
904 5 : SetMetadataItem("PlanckO",
905 5 : CPLSPrintf("%d", ReadInt32(nRecOffset + 776)), "FLIR");
906 5 : SetMetadataItem("PlanckR2",
907 5 : CPLSPrintf("%.8g", ReadFloat32(nRecOffset + 780)),
908 5 : "FLIR");
909 5 : SetMetadataItem("RawValueRangeMin",
910 5 : CPLSPrintf("%d", ReadUInt16(nRecOffset + 784)), "FLIR");
911 5 : SetMetadataItem("RawValueRangeMax",
912 5 : CPLSPrintf("%d", ReadUInt16(nRecOffset + 786)), "FLIR");
913 5 : SetMetadataItem("RawValueMedian",
914 5 : CPLSPrintf("%d", ReadUInt16(nRecOffset + 824)), "FLIR");
915 5 : SetMetadataItem("RawValueRange",
916 5 : CPLSPrintf("%d", ReadUInt16(nRecOffset + 828)), "FLIR");
917 5 : const auto nUnixTime = ReadUInt32(nRecOffset + 900);
918 5 : const auto nSS = ReadUInt32(nRecOffset + 904) & 0xffff;
919 5 : const auto nTZ = ReadInt16(nRecOffset + 908);
920 : struct tm brokenDown;
921 5 : CPLUnixTimeToYMDHMS(static_cast<GIntBig>(nUnixTime) - nTZ * 60,
922 : &brokenDown);
923 : std::string osDateTime(CPLSPrintf(
924 5 : "%04d-%02d-%02dT%02d:%02d:%02d.%03d", brokenDown.tm_year + 1900,
925 5 : brokenDown.tm_mon + 1, brokenDown.tm_mday, brokenDown.tm_hour,
926 10 : brokenDown.tm_min, brokenDown.tm_sec, nSS));
927 5 : if (nTZ <= 0)
928 5 : osDateTime += CPLSPrintf("+%02d:%02d", (-nTZ) / 60, (-nTZ) % 60);
929 : else
930 0 : osDateTime += CPLSPrintf("-%02d:%02d", nTZ / 60, nTZ % 60);
931 5 : SetMetadataItem("DateTimeOriginal", osDateTime.c_str(), "FLIR");
932 5 : SetMetadataItem("FocusStepCount",
933 5 : CPLSPrintf("%d", ReadUInt16(nRecOffset + 912)), "FLIR");
934 5 : SetMetadataItem("FocusDistance",
935 5 : CPLSPrintf("%f m", ReadFloat32(nRecOffset + 1116)),
936 5 : "FLIR");
937 5 : SetMetadataItem("FrameRate",
938 5 : CPLSPrintf("%d", ReadUInt16(nRecOffset + 1124)),
939 5 : "FLIR");
940 5 : };
941 :
942 : // Read the Palette Info record
943 : const auto ReadPaletteInfo =
944 5 : [&](std::uint32_t nRecOffset, std::uint32_t nRecLength)
945 : {
946 5 : if (!(nRecLength >= 112 && nRecOffset + nRecLength <= abyFLIR.size()))
947 0 : return;
948 5 : const int nPaletteColors = abyFLIR[nRecOffset];
949 5 : SetMetadataItem("PaletteColors", CPLSPrintf("%d", nPaletteColors),
950 5 : "FLIR");
951 :
952 : const auto SetColorItem =
953 90 : [this, &abyFLIR](const char *pszItem, std::uint32_t nOffset)
954 : {
955 30 : SetMetadataItem(pszItem,
956 30 : CPLSPrintf("%d %d %d", abyFLIR[nOffset],
957 30 : abyFLIR[nOffset + 1],
958 30 : abyFLIR[nOffset + 2]),
959 30 : "FLIR");
960 35 : };
961 5 : SetColorItem("AboveColor", nRecOffset + 6);
962 5 : SetColorItem("BelowColor", nRecOffset + 9);
963 5 : SetColorItem("OverflowColor", nRecOffset + 12);
964 5 : SetColorItem("UnderflowColor", nRecOffset + 15);
965 5 : SetColorItem("Isotherm1Color", nRecOffset + 18);
966 5 : SetColorItem("Isotherm2Color", nRecOffset + 21);
967 5 : SetMetadataItem("PaletteMethod",
968 5 : CPLSPrintf("%d", abyFLIR[nRecOffset + 26]), "FLIR");
969 5 : SetMetadataItem("PaletteStretch",
970 5 : CPLSPrintf("%d", abyFLIR[nRecOffset + 27]), "FLIR");
971 5 : SetStringIfNotEmpty("PaletteFileName", nRecOffset + 48, 32);
972 5 : SetStringIfNotEmpty("PaletteName", nRecOffset + 80, 32);
973 5 : if (nRecLength < static_cast<std::uint32_t>(112 + nPaletteColors * 3))
974 0 : return;
975 10 : std::string osPalette;
976 1125 : for (int i = 0; i < nPaletteColors; i++)
977 : {
978 1120 : if (!osPalette.empty())
979 1115 : osPalette += ", ";
980 : osPalette +=
981 1120 : CPLSPrintf("(%d %d %d)", abyFLIR[nRecOffset + 112 + 3 * i + 0],
982 1120 : abyFLIR[nRecOffset + 112 + 3 * i + 1],
983 1120 : abyFLIR[nRecOffset + 112 + 3 * i + 2]);
984 : }
985 5 : SetMetadataItem("Palette", osPalette.c_str(), "FLIR");
986 5 : };
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 5 : };
1028 :
1029 5 : 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 75 : for (std::uint32_t iRec = 0; iRec < nEntryCountRecordDirectory; iRec++)
1042 : {
1043 70 : const auto nRecType = ReadUInt16(nOffsetDirEntry);
1044 70 : const auto nRecOffset = ReadUInt32(nOffsetDirEntry + 12);
1045 70 : const auto nRecLength = ReadUInt32(nOffsetDirEntry + 16);
1046 70 : nOffsetDirEntry += SIZE_RECORD_DIRECTORY;
1047 :
1048 70 : if (nRecType == FLIR_REC_FREE && nRecLength == 0)
1049 50 : continue; // silently keep empty records of type 0
1050 20 : CPLDebugOnly("JPEG", "FLIR: record %u, type %u, offset %u, length %u",
1051 : iRec, nRecType, nRecOffset, nRecLength);
1052 20 : 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 20 : switch (nRecType)
1062 : {
1063 5 : case FLIR_REC_RAWDATA:
1064 : {
1065 5 : const auto bLittleEndianBackup = bLittleEndian;
1066 5 : ReadRawData(nRecOffset, nRecLength);
1067 5 : bLittleEndian = bLittleEndianBackup;
1068 5 : break;
1069 : }
1070 5 : case FLIR_REC_CAMERA_INFO:
1071 : {
1072 5 : const auto bLittleEndianBackup = bLittleEndian;
1073 5 : ReadCameraInfo(nRecOffset, nRecLength);
1074 5 : bLittleEndian = bLittleEndianBackup;
1075 5 : break;
1076 : }
1077 5 : case FLIR_REC_PALETTE_INFO:
1078 : {
1079 5 : ReadPaletteInfo(nRecOffset, nRecLength);
1080 5 : 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 5 : default:
1090 : {
1091 5 : CPLDebugOnly("JPEG", "FLIR record ignored");
1092 5 : break;
1093 : }
1094 : }
1095 : }
1096 :
1097 5 : 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 25231 : JPGRasterBand::JPGRasterBand(JPGDatasetCommon *poDSIn, int nBandIn)
1569 25231 : : poGDS(poDSIn)
1570 : {
1571 25230 : poDS = poDSIn;
1572 :
1573 25230 : nBand = nBandIn;
1574 25230 : if (poDSIn->GetDataPrecision() == 12)
1575 381 : eDataType = GDT_UInt16;
1576 : else
1577 24848 : eDataType = GDT_Byte;
1578 :
1579 25229 : nBlockXSize = poDSIn->nRasterXSize;
1580 25229 : nBlockYSize = 1;
1581 :
1582 25229 : GDALMajorObject::SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
1583 25230 : if (eDataType == GDT_UInt16)
1584 381 : GDALMajorObject::SetMetadataItem("NBITS", "12", "IMAGE_STRUCTURE");
1585 25230 : }
1586 :
1587 : /************************************************************************/
1588 : /* JPGCreateBand() */
1589 : /************************************************************************/
1590 :
1591 25228 : GDALRasterBand *JPGCreateBand(JPGDatasetCommon *poDS, int nBand)
1592 : {
1593 25228 : return new JPGRasterBand(poDS, nBand);
1594 : }
1595 :
1596 : /************************************************************************/
1597 : /* IReadBlock() */
1598 : /************************************************************************/
1599 :
1600 199356 : CPLErr JPGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
1601 :
1602 : {
1603 199356 : CPLAssert(nBlockXOff == 0);
1604 :
1605 199356 : const int nXSize = GetXSize();
1606 199355 : const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
1607 199356 : 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 199286 : CPLErr eErr = poGDS->LoadScanline(nBlockYOff);
1615 199286 : if (eErr != CE_None)
1616 5 : return eErr;
1617 :
1618 : // Transfer between the working buffer the callers buffer.
1619 199281 : 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 33229 : memcpy(pImage, poGDS->m_pabyScanline,
1626 33230 : 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 199281 : if (nBand == 1)
1678 : {
1679 188899 : 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 199281 : 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 11250 : JPGDatasetCommon::~JPGDatasetCommon()
1834 :
1835 : {
1836 11248 : JPGDatasetCommon::Close();
1837 :
1838 11249 : if (m_pabyScanline != nullptr)
1839 3169 : CPLFree(m_pabyScanline);
1840 11250 : if (papszMetadata != nullptr)
1841 47 : CSLDestroy(papszMetadata);
1842 :
1843 11250 : CPLFree(pabyBitMask);
1844 11250 : CPLFree(pabyCMask);
1845 11250 : delete poMaskBand;
1846 11247 : }
1847 :
1848 : /************************************************************************/
1849 : /* Close() */
1850 : /************************************************************************/
1851 :
1852 11664 : CPLErr JPGDatasetCommon::Close()
1853 : {
1854 11664 : CPLErr eErr = CE_None;
1855 :
1856 11664 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
1857 : {
1858 11250 : JPGDatasetCommon::CloseDependentDatasets();
1859 :
1860 11248 : if (m_fpImage != nullptr && VSIFCloseL(m_fpImage) != 0)
1861 0 : eErr = CE_Failure;
1862 11249 : m_fpImage = nullptr;
1863 :
1864 11249 : eErr = GDAL::Combine(eErr, GDALPamDataset::Close());
1865 : }
1866 11665 : return eErr;
1867 : }
1868 :
1869 : /************************************************************************/
1870 : /* CloseDependentDatasets() */
1871 : /************************************************************************/
1872 :
1873 11249 : int JPGDatasetCommon::CloseDependentDatasets()
1874 : {
1875 11249 : int bRet = GDALPamDataset::CloseDependentDatasets();
1876 11248 : 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 11248 : CPLFree(papoInternalOverviews);
1884 11248 : papoInternalOverviews = nullptr;
1885 :
1886 11248 : 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 11250 : JPGDataset::JPGDataset()
2184 : {
2185 11250 : memset(&sDInfo, 0, sizeof(sDInfo));
2186 11250 : sDInfo.data_precision = 8;
2187 :
2188 11250 : memset(&sJErr, 0, sizeof(sJErr));
2189 11250 : memset(&sJProgress, 0, sizeof(sJProgress));
2190 11250 : }
2191 :
2192 : /************************************************************************/
2193 : /* ~JPGDataset() */
2194 : /************************************************************************/
2195 :
2196 22498 : JPGDataset::~JPGDataset()
2197 :
2198 : {
2199 11250 : GDALPamDataset::FlushCache(true);
2200 11249 : JPGDataset::StopDecompress();
2201 22498 : }
2202 :
2203 : /************************************************************************/
2204 : /* StopDecompress() */
2205 : /************************************************************************/
2206 :
2207 11946 : void JPGDataset::StopDecompress()
2208 : {
2209 11946 : if (bHasDoneJpegStartDecompress)
2210 : {
2211 3589 : jpeg_abort_decompress(&sDInfo);
2212 3589 : bHasDoneJpegStartDecompress = false;
2213 : }
2214 11946 : if (bHasDoneJpegCreateDecompress)
2215 : {
2216 11875 : jpeg_destroy_decompress(&sDInfo);
2217 11874 : bHasDoneJpegCreateDecompress = false;
2218 : }
2219 11945 : nLoadedScanline = INT_MAX;
2220 11945 : if (ppoActiveDS)
2221 10193 : *ppoActiveDS = nullptr;
2222 11945 : }
2223 :
2224 : /************************************************************************/
2225 : /* ErrorOutOnNonFatalError() */
2226 : /************************************************************************/
2227 :
2228 113471 : bool JPGDataset::ErrorOutOnNonFatalError()
2229 : {
2230 113471 : if (sUserData.bNonFatalErrorEncountered)
2231 : {
2232 4 : sUserData.bNonFatalErrorEncountered = false;
2233 4 : return true;
2234 : }
2235 113467 : return false;
2236 : }
2237 :
2238 : /************************************************************************/
2239 : /* StartDecompress() */
2240 : /************************************************************************/
2241 :
2242 3591 : 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 3591 : 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 3590 : sDInfo.progress = &sJProgress;
2309 3590 : sJProgress.progress_monitor = JPGDataset::ProgressMonitor;
2310 3590 : jpeg_start_decompress(&sDInfo);
2311 3588 : bHasDoneJpegStartDecompress = true;
2312 :
2313 3588 : return CE_None;
2314 : }
2315 :
2316 : /************************************************************************/
2317 : /* LoadScanline() */
2318 : /************************************************************************/
2319 :
2320 215461 : CPLErr JPGDataset::LoadScanline(int iLine, GByte *outBuffer)
2321 :
2322 : {
2323 215461 : 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 111437 : if (!bHasDoneJpegCreateDecompress && Restart() != CE_None)
2329 0 : return CE_Failure;
2330 :
2331 : // setup to trap a fatal error.
2332 111437 : if (setjmp(sUserData.setjmp_buffer))
2333 1 : return CE_Failure;
2334 :
2335 111437 : if (!bHasDoneJpegStartDecompress && StartDecompress() != CE_None)
2336 1 : return CE_Failure;
2337 :
2338 111434 : if (outBuffer == nullptr && m_pabyScanline == nullptr)
2339 : {
2340 3170 : int nJPEGBands = 0;
2341 3170 : switch (sDInfo.out_color_space)
2342 : {
2343 1143 : case JCS_GRAYSCALE:
2344 1143 : nJPEGBands = 1;
2345 1143 : 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 3170 : CPLMalloc(cpl::fits_on<int>(nJPEGBands * GetRasterXSize() * 2)));
2361 : }
2362 :
2363 111433 : if (iLine < nLoadedScanline)
2364 : {
2365 307 : if (Restart() != CE_None)
2366 0 : return CE_Failure;
2367 : }
2368 :
2369 224902 : while (nLoadedScanline < iLine)
2370 : {
2371 113471 : GDAL_JSAMPLE *ppSamples = reinterpret_cast<GDAL_JSAMPLE *>(
2372 82025 : 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 113471 : jpeg_read_scanlines(&sDInfo, &ppSamples, 1);
2377 : #endif
2378 113471 : if (ErrorOutOnNonFatalError())
2379 3 : return CE_Failure;
2380 113469 : nLoadedScanline++;
2381 : }
2382 :
2383 111431 : 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 46950 : void JPGDataset::LoadDefaultTables(int n)
2469 : {
2470 46950 : if (nQLevel < 1)
2471 46951 : 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 11737 : void JPGDataset::SetScaleNumAndDenom()
2548 : {
2549 : #if JPEG_LIB_VERSION > 62
2550 11600 : sDInfo.scale_num = 8 / nScaleFactor;
2551 11600 : sDInfo.scale_denom = 8;
2552 : #else
2553 137 : sDInfo.scale_num = 1;
2554 137 : sDInfo.scale_denom = nScaleFactor;
2555 : #endif
2556 11737 : }
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 813 : 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 813 : if (panBandMap == nullptr)
2828 : {
2829 0 : return CE_Failure;
2830 : }
2831 :
2832 : #ifndef JPEG_LIB_MK1
2833 813 : 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 1626 : 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 429 : return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2897 : pData, nBufXSize, nBufYSize, eBufType,
2898 : nBandCount, panBandMap, nPixelSpace,
2899 429 : nLineSpace, nBandSpace, psExtraArg);
2900 : }
2901 :
2902 : /************************************************************************/
2903 : /* Open() */
2904 : /************************************************************************/
2905 :
2906 3452 : 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 3452 : if (!JPEGDriverIdentify(poOpenInfo))
2912 0 : return nullptr;
2913 : #endif
2914 :
2915 3452 : if (poOpenInfo->eAccess == GA_Update)
2916 : {
2917 0 : ReportUpdateNotSupportedByDriver("JPEG");
2918 0 : return nullptr;
2919 : }
2920 :
2921 6902 : CPLString osFilename(poOpenInfo->pszFilename);
2922 3452 : bool bFLIRRawThermalImage = false;
2923 3452 : bool bDJIRawThermalImage = false;
2924 3452 : if (STARTS_WITH(poOpenInfo->pszFilename, "JPEG:"))
2925 : {
2926 8 : CPLStringList aosTokens(CSLTokenizeString2(poOpenInfo->pszFilename, ":",
2927 8 : CSLT_HONOURSTRINGS));
2928 8 : if (aosTokens.size() != 3)
2929 1 : return nullptr;
2930 :
2931 7 : osFilename = aosTokens[1];
2932 7 : if (std::string(aosTokens[2]) == "FLIR_RAW_THERMAL_IMAGE")
2933 4 : 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 3450 : VSILFILE *fpL = poOpenInfo->fpL;
2941 3450 : poOpenInfo->fpL = nullptr;
2942 :
2943 3450 : JPGDatasetOpenArgs sArgs;
2944 3450 : sArgs.pszFilename = osFilename.c_str();
2945 3450 : sArgs.fpLin = fpL;
2946 3450 : sArgs.papszSiblingFiles = poOpenInfo->GetSiblingFiles();
2947 3450 : sArgs.bDoPAMInitialize = true;
2948 3450 : sArgs.bUseInternalOverviews = CPLFetchBool(poOpenInfo->papszOpenOptions,
2949 : "USE_INTERNAL_OVERVIEWS", true);
2950 : #ifdef D_LOSSLESS_SUPPORTED
2951 : sArgs.bIsLossless = JPEGDatasetIsJPEGLS(poOpenInfo);
2952 : #endif
2953 :
2954 3450 : auto poJPG_DS = JPGDataset::Open(&sArgs);
2955 6895 : auto poDS = std::unique_ptr<GDALDataset>(poJPG_DS);
2956 3444 : if (poDS == nullptr)
2957 : {
2958 1 : return nullptr;
2959 : }
2960 3443 : if (bFLIRRawThermalImage || bDJIRawThermalImage)
2961 : {
2962 5 : poDS.reset(poJPG_DS->OpenRawThermalImage());
2963 : }
2964 :
2965 6894 : if (poDS &&
2966 6893 : 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 3445 : return poDS.release();
2983 : }
2984 :
2985 : /************************************************************************/
2986 : /* OpenRawThermalImage() */
2987 : /************************************************************************/
2988 :
2989 6 : GDALDataset *JPGDatasetCommon::OpenRawThermalImage()
2990 : {
2991 6 : ReadThermalMetadata();
2992 6 : if (m_abyRawThermalImage.empty())
2993 : {
2994 1 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find raw thermal image");
2995 1 : return nullptr;
2996 : }
2997 :
2998 : GByte *pabyData =
2999 5 : static_cast<GByte *>(CPLMalloc(m_abyRawThermalImage.size()));
3000 : const std::string osTmpFilename(
3001 10 : VSIMemGenerateHiddenFilename("jpeg_thermal_raw"));
3002 5 : memcpy(pabyData, m_abyRawThermalImage.data(), m_abyRawThermalImage.size());
3003 5 : VSILFILE *fpRaw = VSIFileFromMemBuffer(osTmpFilename.c_str(), pabyData,
3004 5 : m_abyRawThermalImage.size(), true);
3005 :
3006 : // Termal image as uncompressed data
3007 5 : if (m_nRawThermalImageWidth * m_nRawThermalImageHeight * 2 ==
3008 5 : static_cast<int>(m_abyRawThermalImage.size()))
3009 : {
3010 3 : CPLDebug("JPEG", "Raw thermal image");
3011 :
3012 : class JPEGRawDataset final : public RawDataset
3013 : {
3014 : public:
3015 3 : JPEGRawDataset(int nXSizeIn, int nYSizeIn)
3016 3 : {
3017 3 : nRasterXSize = nXSizeIn;
3018 3 : nRasterYSize = nYSizeIn;
3019 3 : }
3020 :
3021 6 : CPLErr Close() override
3022 : {
3023 6 : return GDALPamDataset::Close();
3024 : }
3025 :
3026 6 : ~JPEGRawDataset() override
3027 3 : {
3028 3 : JPEGRawDataset::Close();
3029 6 : }
3030 :
3031 3 : void SetBand(int nBand, std::unique_ptr<GDALRasterBand> &&poBand)
3032 : {
3033 3 : RawDataset::SetBand(nBand, std::move(poBand));
3034 3 : }
3035 : };
3036 :
3037 : auto poBand = RawRasterBand::Create(
3038 : fpRaw,
3039 : 0, // image offset
3040 : 2, // pixel offset
3041 3 : 2 * m_nRawThermalImageWidth, // line offset
3042 : GDT_UInt16,
3043 3 : m_bRawThermalLittleEndian
3044 : ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
3045 : : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
3046 : m_nRawThermalImageWidth, m_nRawThermalImageHeight,
3047 6 : RawRasterBand::OwnFP::YES);
3048 3 : if (!poBand)
3049 0 : return nullptr;
3050 :
3051 : auto poRawDS = new JPEGRawDataset(m_nRawThermalImageWidth,
3052 3 : m_nRawThermalImageHeight);
3053 3 : poRawDS->SetDescription(osTmpFilename.c_str());
3054 3 : poRawDS->SetBand(1, std::move(poBand));
3055 3 : poRawDS->MarkSuppressOnClose();
3056 3 : return poRawDS;
3057 : }
3058 :
3059 2 : VSIFCloseL(fpRaw);
3060 :
3061 : // Termal image as PNG
3062 4 : if (m_abyRawThermalImage.size() > 4 &&
3063 2 : memcmp(m_abyRawThermalImage.data(), "\x89PNG", 4) == 0)
3064 : {
3065 : // FLIR 16-bit PNG have a wrong endianness.
3066 : // Cf https://exiftool.org/TagNames/FLIR.html: "Note that most FLIR
3067 : // cameras using the PNG format seem to write the 16-bit raw image data
3068 : // in the wrong byte order."
3069 2 : const char *const apszPNGOpenOptions[] = {
3070 : "@BYTE_ORDER_LITTLE_ENDIAN=YES", nullptr};
3071 2 : auto poRawDS = GDALDataset::Open(osTmpFilename.c_str(), GDAL_OF_RASTER,
3072 : nullptr, apszPNGOpenOptions, nullptr);
3073 2 : if (poRawDS == nullptr)
3074 : {
3075 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid raw thermal image");
3076 0 : VSIUnlink(osTmpFilename.c_str());
3077 0 : return nullptr;
3078 : }
3079 2 : poRawDS->MarkSuppressOnClose();
3080 2 : return poRawDS;
3081 : }
3082 :
3083 0 : CPLError(CE_Failure, CPLE_AppDefined,
3084 : "Unrecognized format for raw thermal image");
3085 0 : VSIUnlink(osTmpFilename.c_str());
3086 0 : return nullptr;
3087 : }
3088 :
3089 : #endif // !defined(JPGDataset)
3090 :
3091 : /************************************************************************/
3092 : /* Open() */
3093 : /************************************************************************/
3094 :
3095 11184 : JPGDatasetCommon *JPGDataset::Open(JPGDatasetOpenArgs *psArgs)
3096 :
3097 : {
3098 11184 : JPGDataset *poDS = new JPGDataset();
3099 22364 : return OpenStage2(psArgs, poDS);
3100 : }
3101 :
3102 11184 : JPGDatasetCommon *JPGDataset::OpenStage2(JPGDatasetOpenArgs *psArgs,
3103 : JPGDataset *&poDS)
3104 : {
3105 : // Will detect mismatch between compile-time and run-time libjpeg versions.
3106 11184 : if (setjmp(poDS->sUserData.setjmp_buffer))
3107 : {
3108 : #if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
3109 :
3110 137 : if (poDS->sDInfo.data_precision == 12 && poDS->m_fpImage != nullptr)
3111 : {
3112 136 : VSILFILE *fpImage = poDS->m_fpImage;
3113 136 : poDS->m_fpImage = nullptr;
3114 136 : delete poDS;
3115 136 : psArgs->fpLin = fpImage;
3116 136 : return JPEGDataset12Open(psArgs);
3117 : }
3118 : #endif
3119 1 : delete poDS;
3120 1 : return nullptr;
3121 : }
3122 :
3123 11184 : const char *pszFilename = psArgs->pszFilename;
3124 11184 : VSILFILE *fpLin = psArgs->fpLin;
3125 11184 : CSLConstList papszSiblingFiles = psArgs->papszSiblingFiles;
3126 11184 : const int nScaleFactor = psArgs->nScaleFactor;
3127 11184 : const bool bDoPAMInitialize = psArgs->bDoPAMInitialize;
3128 11184 : const bool bUseInternalOverviews = psArgs->bUseInternalOverviews;
3129 :
3130 : // If it is a subfile, read the JPEG header.
3131 11184 : bool bIsSubfile = false;
3132 11184 : GUIntBig subfile_offset = 0;
3133 11184 : GUIntBig subfile_size = 0;
3134 11184 : const char *real_filename = pszFilename;
3135 11184 : int nQLevel = -1;
3136 :
3137 11184 : if (STARTS_WITH_CI(pszFilename, "JPEG_SUBFILE:"))
3138 : {
3139 277 : bool bScan = false;
3140 :
3141 277 : if (STARTS_WITH_CI(pszFilename, "JPEG_SUBFILE:Q"))
3142 : {
3143 270 : char **papszTokens = CSLTokenizeString2(pszFilename + 14, ",", 0);
3144 270 : if (CSLCount(papszTokens) >= 3)
3145 : {
3146 270 : nQLevel = atoi(papszTokens[0]);
3147 540 : subfile_offset = CPLScanUIntBig(
3148 270 : papszTokens[1], static_cast<int>(strlen(papszTokens[1])));
3149 540 : subfile_size = CPLScanUIntBig(
3150 270 : papszTokens[2], static_cast<int>(strlen(papszTokens[2])));
3151 270 : bScan = true;
3152 : }
3153 270 : CSLDestroy(papszTokens);
3154 : }
3155 : else
3156 : {
3157 7 : char **papszTokens = CSLTokenizeString2(pszFilename + 13, ",", 0);
3158 7 : if (CSLCount(papszTokens) >= 2)
3159 : {
3160 14 : subfile_offset = CPLScanUIntBig(
3161 7 : papszTokens[0], static_cast<int>(strlen(papszTokens[0])));
3162 14 : subfile_size = CPLScanUIntBig(
3163 7 : papszTokens[1], static_cast<int>(strlen(papszTokens[1])));
3164 7 : bScan = true;
3165 : }
3166 7 : CSLDestroy(papszTokens);
3167 : }
3168 :
3169 277 : if (!bScan)
3170 : {
3171 0 : CPLError(CE_Failure, CPLE_OpenFailed,
3172 : "Corrupt subfile definition: %s", pszFilename);
3173 0 : delete poDS;
3174 0 : return nullptr;
3175 : }
3176 :
3177 277 : real_filename = strstr(pszFilename, ",");
3178 277 : if (real_filename != nullptr)
3179 277 : real_filename = strstr(real_filename + 1, ",");
3180 277 : if (real_filename != nullptr && nQLevel != -1)
3181 270 : real_filename = strstr(real_filename + 1, ",");
3182 277 : if (real_filename != nullptr)
3183 277 : real_filename++;
3184 : else
3185 : {
3186 0 : CPLError(CE_Failure, CPLE_OpenFailed,
3187 : "Could not find filename in subfile definition.");
3188 0 : delete poDS;
3189 0 : return nullptr;
3190 : }
3191 :
3192 277 : CPLDebug("JPG",
3193 : "real_filename %s, offset=" CPL_FRMT_GUIB
3194 : ", size=" CPL_FRMT_GUIB "\n",
3195 : real_filename, subfile_offset, subfile_size);
3196 :
3197 277 : bIsSubfile = true;
3198 : }
3199 :
3200 : // Open the file using the large file api if necessary.
3201 11184 : VSILFILE *fpImage = fpLin;
3202 :
3203 11184 : if (!fpImage)
3204 : {
3205 7750 : fpImage = VSIFOpenL(real_filename, "rb");
3206 :
3207 7750 : if (fpImage == nullptr)
3208 : {
3209 2 : CPLError(CE_Failure, CPLE_OpenFailed,
3210 : "VSIFOpenL(%s) failed unexpectedly in jpgdataset.cpp",
3211 : real_filename);
3212 2 : delete poDS;
3213 2 : return nullptr;
3214 : }
3215 : }
3216 :
3217 : // Create a corresponding GDALDataset.
3218 11182 : poDS->nQLevel = nQLevel;
3219 11182 : poDS->m_fpImage = fpImage;
3220 :
3221 : // Move to the start of jpeg data.
3222 11182 : poDS->nSubfileOffset = subfile_offset;
3223 11182 : VSIFSeekL(poDS->m_fpImage, poDS->nSubfileOffset, SEEK_SET);
3224 :
3225 11182 : poDS->eAccess = GA_ReadOnly;
3226 :
3227 11182 : poDS->sDInfo.err = jpeg_std_error(&poDS->sJErr);
3228 11181 : poDS->sJErr.error_exit = JPGDataset::ErrorExit;
3229 11181 : poDS->sJErr.output_message = JPGDataset::OutputMessage;
3230 11181 : poDS->sUserData.p_previous_emit_message = poDS->sJErr.emit_message;
3231 11181 : poDS->sJErr.emit_message = JPGDataset::EmitMessage;
3232 11181 : poDS->sDInfo.client_data = &poDS->sUserData;
3233 :
3234 : #if defined(__GNUC__)
3235 : #pragma GCC diagnostic push
3236 : #pragma GCC diagnostic ignored "-Wold-style-cast"
3237 : #endif
3238 11181 : jpeg_create_decompress(&poDS->sDInfo);
3239 : #if defined(__GNUC__)
3240 : #pragma GCC diagnostic pop
3241 : #endif
3242 :
3243 11182 : poDS->bHasDoneJpegCreateDecompress = true;
3244 :
3245 11182 : SetMaxMemoryToUse(&poDS->sDInfo);
3246 :
3247 : #if !defined(JPGDataset)
3248 : // Preload default NITF JPEG quantization tables.
3249 11045 : poDS->LoadDefaultTables(0);
3250 11044 : poDS->LoadDefaultTables(1);
3251 11045 : poDS->LoadDefaultTables(2);
3252 11044 : poDS->LoadDefaultTables(3);
3253 : #endif // !defined(JPGDataset)
3254 :
3255 : // Read pre-image data after ensuring the file is rewound.
3256 11182 : VSIFSeekL(poDS->m_fpImage, poDS->nSubfileOffset, SEEK_SET);
3257 :
3258 11182 : jpeg_vsiio_src(&poDS->sDInfo, poDS->m_fpImage);
3259 11182 : jpeg_read_header(&poDS->sDInfo, TRUE);
3260 :
3261 11045 : if (poDS->sDInfo.data_precision != 8 && poDS->sDInfo.data_precision != 12)
3262 : {
3263 0 : CPLError(CE_Failure, CPLE_NotSupported,
3264 : "GDAL JPEG Driver doesn't support files with precision of "
3265 : "other than 8 or 12 bits.");
3266 0 : delete poDS;
3267 0 : return nullptr;
3268 : }
3269 :
3270 : #if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
3271 10908 : if (poDS->sDInfo.data_precision == 12 && poDS->m_fpImage != nullptr)
3272 : {
3273 0 : poDS->m_fpImage = nullptr;
3274 0 : delete poDS;
3275 0 : psArgs->fpLin = fpImage;
3276 0 : return JPEGDataset12Open(psArgs);
3277 : }
3278 : #endif
3279 :
3280 : // Capture some information from the file that is of interest.
3281 :
3282 11045 : poDS->nScaleFactor = nScaleFactor;
3283 11045 : poDS->SetScaleNumAndDenom();
3284 11044 : poDS->nRasterXSize = DIV_ROUND_UP(poDS->sDInfo.image_width, nScaleFactor);
3285 11044 : poDS->nRasterYSize = DIV_ROUND_UP(poDS->sDInfo.image_height, nScaleFactor);
3286 :
3287 11044 : poDS->sDInfo.out_color_space = poDS->sDInfo.jpeg_color_space;
3288 11044 : poDS->eGDALColorSpace = poDS->sDInfo.jpeg_color_space;
3289 :
3290 11044 : if (poDS->sDInfo.jpeg_color_space == JCS_GRAYSCALE)
3291 : {
3292 4173 : poDS->nBands = 1;
3293 : }
3294 6871 : else if (poDS->sDInfo.jpeg_color_space == JCS_RGB)
3295 : {
3296 3102 : poDS->nBands = 3;
3297 : }
3298 3769 : else if (poDS->sDInfo.jpeg_color_space == JCS_YCbCr)
3299 : {
3300 3473 : poDS->nBands = 3;
3301 3473 : if (CPLTestBool(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES")))
3302 : {
3303 3473 : poDS->sDInfo.out_color_space = JCS_RGB;
3304 3473 : poDS->eGDALColorSpace = JCS_RGB;
3305 3473 : poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCr",
3306 3473 : "IMAGE_STRUCTURE");
3307 : }
3308 : }
3309 296 : else if (poDS->sDInfo.jpeg_color_space == JCS_CMYK)
3310 : {
3311 594 : if (poDS->sDInfo.data_precision == 8 &&
3312 297 : CPLTestBool(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES")))
3313 : {
3314 5 : poDS->eGDALColorSpace = JCS_RGB;
3315 5 : poDS->nBands = 3;
3316 5 : poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "CMYK",
3317 5 : "IMAGE_STRUCTURE");
3318 : }
3319 : else
3320 : {
3321 292 : poDS->nBands = 4;
3322 : }
3323 : }
3324 0 : else if (poDS->sDInfo.jpeg_color_space == JCS_YCCK)
3325 : {
3326 0 : if (poDS->sDInfo.data_precision == 8 &&
3327 0 : CPLTestBool(CPLGetConfigOption("GDAL_JPEG_TO_RGB", "YES")))
3328 : {
3329 0 : poDS->eGDALColorSpace = JCS_RGB;
3330 0 : poDS->nBands = 3;
3331 0 : poDS->SetMetadataItem("SOURCE_COLOR_SPACE", "YCbCrK",
3332 0 : "IMAGE_STRUCTURE");
3333 :
3334 : // libjpeg does the translation from YCrCbK -> CMYK internally
3335 : // and we'll do the translation to RGB in IReadBlock().
3336 0 : poDS->sDInfo.out_color_space = JCS_CMYK;
3337 : }
3338 : else
3339 : {
3340 0 : poDS->nBands = 4;
3341 : }
3342 : }
3343 : else
3344 : {
3345 0 : CPLError(CE_Failure, CPLE_NotSupported,
3346 : "Unrecognized jpeg_color_space value of %d.\n",
3347 0 : poDS->sDInfo.jpeg_color_space);
3348 0 : delete poDS;
3349 0 : return nullptr;
3350 : }
3351 :
3352 : // Create band information objects.
3353 36121 : for (int iBand = 0; iBand < poDS->nBands; iBand++)
3354 25079 : poDS->SetBand(iBand + 1, JPGCreateBand(poDS, iBand + 1));
3355 :
3356 : // More metadata.
3357 11042 : if (poDS->nBands > 1)
3358 : {
3359 6872 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
3360 6872 : poDS->SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
3361 : }
3362 :
3363 11042 : if (psArgs->bIsLossless)
3364 : {
3365 0 : poDS->SetMetadataItem("COMPRESSION_REVERSIBILITY", "LOSSLESS",
3366 0 : "IMAGE_STRUCTURE");
3367 : }
3368 :
3369 : // Initialize any PAM information.
3370 11042 : poDS->SetDescription(pszFilename);
3371 :
3372 11044 : if (nScaleFactor == 1 && bDoPAMInitialize)
3373 : {
3374 3629 : if (!bIsSubfile)
3375 3483 : poDS->TryLoadXML(papszSiblingFiles);
3376 : else
3377 146 : poDS->nPamFlags |= GPF_NOSAVE;
3378 :
3379 : // Open (external) overviews.
3380 3624 : poDS->oOvManager.Initialize(poDS, real_filename, papszSiblingFiles);
3381 :
3382 3629 : if (!bUseInternalOverviews)
3383 0 : poDS->bHasInitInternalOverviews = true;
3384 :
3385 : // In the case of a file downloaded through the HTTP driver, this one
3386 : // will unlink the temporary /vsimem file just after GDALOpen(), so
3387 : // later VSIFOpenL() when reading internal overviews would fail.
3388 : // Initialize them now.
3389 3629 : if (STARTS_WITH(real_filename, "/vsimem/") &&
3390 3344 : strstr(real_filename, "_gdal_http_"))
3391 : {
3392 0 : poDS->InitInternalOverviews();
3393 : }
3394 : }
3395 : else
3396 : {
3397 7415 : poDS->nPamFlags |= GPF_NOSAVE;
3398 : }
3399 :
3400 11042 : poDS->bIsSubfile = bIsSubfile;
3401 :
3402 11042 : return poDS;
3403 : }
3404 :
3405 : #if !defined(JPGDataset)
3406 :
3407 : /************************************************************************/
3408 : /* LoadWorldFileOrTab() */
3409 : /************************************************************************/
3410 :
3411 377 : void JPGDatasetCommon::LoadWorldFileOrTab()
3412 : {
3413 377 : if (bIsSubfile)
3414 234 : return;
3415 377 : if (bHasTriedLoadWorldFileOrTab)
3416 234 : return;
3417 143 : bHasTriedLoadWorldFileOrTab = true;
3418 :
3419 143 : char *pszWldFilename = nullptr;
3420 :
3421 : // TIROS3 JPEG files have a .wld extension, so don't look for .wld as
3422 : // as worldfile.
3423 : const bool bEndsWithWld =
3424 286 : strlen(GetDescription()) > 4 &&
3425 143 : EQUAL(GetDescription() + strlen(GetDescription()) - 4, ".wld");
3426 143 : bGeoTransformValid =
3427 143 : GDALReadWorldFile2(GetDescription(), nullptr, m_gt,
3428 143 : oOvManager.GetSiblingFiles(), &pszWldFilename) ||
3429 143 : GDALReadWorldFile2(GetDescription(), ".jpw", m_gt,
3430 286 : oOvManager.GetSiblingFiles(), &pszWldFilename) ||
3431 143 : (!bEndsWithWld &&
3432 143 : GDALReadWorldFile2(GetDescription(), ".wld", m_gt,
3433 : oOvManager.GetSiblingFiles(), &pszWldFilename));
3434 :
3435 143 : if (!bGeoTransformValid)
3436 : {
3437 139 : char *pszProjection = nullptr;
3438 139 : int nGCPCount = 0;
3439 139 : GDAL_GCP *pasGCPList = nullptr;
3440 278 : const bool bTabFileOK = CPL_TO_BOOL(GDALReadTabFile2(
3441 139 : GetDescription(), m_gt.data(), &pszProjection, &nGCPCount,
3442 : &pasGCPList, oOvManager.GetSiblingFiles(), &pszWldFilename));
3443 139 : if (pszProjection)
3444 0 : m_oSRS.importFromWkt(pszProjection);
3445 139 : CPLFree(pszProjection);
3446 139 : m_aoGCPs = gdal::GCP::fromC(pasGCPList, nGCPCount);
3447 139 : GDALDeinitGCPs(nGCPCount, pasGCPList);
3448 139 : CPLFree(pasGCPList);
3449 :
3450 139 : if (bTabFileOK && nGCPCount == 0)
3451 0 : bGeoTransformValid = true;
3452 : }
3453 :
3454 143 : if (pszWldFilename)
3455 : {
3456 4 : osWldFilename = pszWldFilename;
3457 4 : CPLFree(pszWldFilename);
3458 : }
3459 : }
3460 :
3461 : /************************************************************************/
3462 : /* GetFileList() */
3463 : /************************************************************************/
3464 :
3465 74 : char **JPGDatasetCommon::GetFileList()
3466 :
3467 : {
3468 74 : char **papszFileList = GDALPamDataset::GetFileList();
3469 :
3470 74 : LoadWorldFileOrTab();
3471 :
3472 76 : if (!osWldFilename.empty() &&
3473 2 : CSLFindString(papszFileList, osWldFilename) == -1)
3474 : {
3475 2 : papszFileList = CSLAddString(papszFileList, osWldFilename);
3476 : }
3477 :
3478 74 : return papszFileList;
3479 : }
3480 :
3481 : /************************************************************************/
3482 : /* CheckForMask() */
3483 : /************************************************************************/
3484 :
3485 65 : void JPGDatasetCommon::CheckForMask()
3486 :
3487 : {
3488 : // Save current position to avoid disturbing JPEG stream decoding.
3489 65 : const vsi_l_offset nCurOffset = VSIFTellL(m_fpImage);
3490 :
3491 : // Go to the end of the file, pull off four bytes, and see if
3492 : // it is plausibly the size of the real image data.
3493 65 : VSIFSeekL(m_fpImage, 0, SEEK_END);
3494 65 : GIntBig nFileSize = VSIFTellL(m_fpImage);
3495 65 : VSIFSeekL(m_fpImage, nFileSize - 4, SEEK_SET);
3496 :
3497 65 : GUInt32 nImageSize = 0;
3498 65 : VSIFReadL(&nImageSize, 4, 1, m_fpImage);
3499 65 : CPL_LSBPTR32(&nImageSize);
3500 :
3501 65 : GByte abyEOD[2] = {0, 0};
3502 :
3503 65 : if (nImageSize >= 2 && nImageSize >= nFileSize / 2 &&
3504 65 : nImageSize <= nFileSize - 4)
3505 : {
3506 : // If that seems okay, seek back, and verify that just preceding
3507 : // the bitmask is an apparent end-of-jpeg-data marker.
3508 15 : VSIFSeekL(m_fpImage, nImageSize - 2, SEEK_SET);
3509 15 : VSIFReadL(abyEOD, 2, 1, m_fpImage);
3510 15 : if (abyEOD[0] == 0xff && abyEOD[1] == 0xd9)
3511 : {
3512 : // We seem to have a mask. Read it in.
3513 15 : nCMaskSize = static_cast<int>(nFileSize - nImageSize - 4);
3514 15 : pabyCMask = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nCMaskSize));
3515 15 : if (pabyCMask)
3516 : {
3517 15 : VSIFReadL(pabyCMask, nCMaskSize, 1, m_fpImage);
3518 :
3519 15 : CPLDebug("JPEG", "Got %d byte compressed bitmask.", nCMaskSize);
3520 : }
3521 : }
3522 : }
3523 :
3524 65 : VSIFSeekL(m_fpImage, nCurOffset, SEEK_SET);
3525 65 : }
3526 :
3527 : /************************************************************************/
3528 : /* DecompressMask() */
3529 : /************************************************************************/
3530 :
3531 4261 : void JPGDatasetCommon::DecompressMask()
3532 :
3533 : {
3534 4261 : if (pabyCMask == nullptr || pabyBitMask != nullptr)
3535 4246 : return;
3536 :
3537 : // Allocate 1bit buffer - may be slightly larger than needed.
3538 15 : const int nBufSize = nRasterYSize * ((nRasterXSize + 7) / 8);
3539 15 : pabyBitMask = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBufSize));
3540 15 : if (pabyBitMask == nullptr)
3541 : {
3542 0 : CPLFree(pabyCMask);
3543 0 : pabyCMask = nullptr;
3544 0 : return;
3545 : }
3546 :
3547 : // Decompress.
3548 : void *pOut =
3549 15 : CPLZLibInflate(pabyCMask, nCMaskSize, pabyBitMask, nBufSize, nullptr);
3550 :
3551 : // Cleanup if an error occurs.
3552 15 : if (pOut == nullptr)
3553 : {
3554 0 : CPLError(CE_Failure, CPLE_AppDefined,
3555 : "Failure decoding JPEG validity bitmask.");
3556 0 : CPLFree(pabyCMask);
3557 0 : pabyCMask = nullptr;
3558 :
3559 0 : CPLFree(pabyBitMask);
3560 0 : pabyBitMask = nullptr;
3561 :
3562 0 : return;
3563 : }
3564 :
3565 : const char *pszJPEGMaskBitOrder =
3566 15 : CPLGetConfigOption("JPEG_MASK_BIT_ORDER", "AUTO");
3567 15 : if (EQUAL(pszJPEGMaskBitOrder, "LSB"))
3568 0 : bMaskLSBOrder = true;
3569 15 : else if (EQUAL(pszJPEGMaskBitOrder, "MSB"))
3570 0 : bMaskLSBOrder = false;
3571 15 : else if (nRasterXSize > 8 && nRasterYSize > 1)
3572 : {
3573 : // Test MSB ordering hypothesis in a very restrictive case where it is
3574 : // *obviously* ordered as MSB ! (unless someone coded something
3575 : // specifically to defeat the below logic)
3576 : // The case considered here is dop_465_6100.jpg from #5102.
3577 : // The mask is identical for each line, starting with 1's and ending
3578 : // with 0's (or starting with 0's and ending with 1's), and no other
3579 : // intermediate change.
3580 : // We can detect the MSB ordering since the lsb bits at the end of the
3581 : // first line will be set with the 1's of the beginning of the second
3582 : // line.
3583 : // We can only be sure of this heuristics if the change of value occurs
3584 : // in the middle of a byte, or if the raster width is not a multiple of
3585 : // 8.
3586 : //
3587 : // TODO(schwehr): Check logic in this section that was added in r26063.
3588 15 : int nPrevValBit = 0;
3589 15 : int nChangedValBit = 0;
3590 15 : int iX = 0; // Used after for.
3591 2101 : for (; iX < nRasterXSize; iX++)
3592 : {
3593 2098 : const int nValBit =
3594 2098 : (pabyBitMask[iX >> 3] & (0x1 << (7 - (iX & 7)))) != 0;
3595 2098 : if (iX == 0)
3596 15 : nPrevValBit = nValBit;
3597 2083 : else if (nValBit != nPrevValBit)
3598 : {
3599 13 : nPrevValBit = nValBit;
3600 13 : nChangedValBit++;
3601 13 : if (nChangedValBit == 1)
3602 : {
3603 13 : const bool bValChangedOnByteBoundary = (iX % 8) == 0;
3604 13 : if (bValChangedOnByteBoundary && (nRasterXSize % 8) == 0)
3605 11 : break;
3606 : }
3607 : else
3608 : {
3609 0 : break;
3610 : }
3611 : }
3612 2087 : const int iNextLineX = iX + nRasterXSize;
3613 2087 : const int nNextLineValBit = (pabyBitMask[iNextLineX >> 3] &
3614 2087 : (0x1 << (7 - (iNextLineX & 7)))) != 0;
3615 2087 : if (nValBit != nNextLineValBit)
3616 1 : break;
3617 : }
3618 :
3619 15 : if (iX == nRasterXSize && nChangedValBit == 1)
3620 : {
3621 2 : CPLDebug("JPEG",
3622 : "Bit ordering in mask is guessed to be msb (unusual)");
3623 2 : bMaskLSBOrder = false;
3624 : }
3625 : else
3626 : {
3627 13 : bMaskLSBOrder = true;
3628 15 : }
3629 : }
3630 : else
3631 : {
3632 0 : bMaskLSBOrder = true;
3633 : }
3634 : }
3635 :
3636 : /************************************************************************/
3637 : /* GetCompressionFormats() */
3638 : /************************************************************************/
3639 :
3640 1 : CPLStringList JPGDatasetCommon::GetCompressionFormats(int nXOff, int nYOff,
3641 : int nXSize, int nYSize,
3642 : int nBandCount,
3643 : const int *panBandList)
3644 : {
3645 1 : CPLStringList aosRet;
3646 1 : if (m_fpImage && nXOff == 0 && nYOff == 0 && nXSize == nRasterXSize &&
3647 2 : nYSize == nRasterYSize && IsAllBands(nBandCount, panBandList))
3648 : {
3649 1 : aosRet.AddString(GDALGetCompressionFormatForJPEG(m_fpImage).c_str());
3650 : }
3651 1 : return aosRet;
3652 : }
3653 :
3654 : /************************************************************************/
3655 : /* ReadCompressedData() */
3656 : /************************************************************************/
3657 :
3658 20 : CPLErr JPGDatasetCommon::ReadCompressedData(
3659 : const char *pszFormat, int nXOff, int nYOff, int nXSize, int nYSize,
3660 : int nBandCount, const int *panBandList, void **ppBuffer,
3661 : size_t *pnBufferSize, char **ppszDetailedFormat)
3662 : {
3663 20 : if (m_fpImage && nXOff == 0 && nYOff == 0 && nXSize == nRasterXSize &&
3664 40 : nYSize == nRasterYSize && IsAllBands(nBandCount, panBandList))
3665 : {
3666 20 : const CPLStringList aosTokens(CSLTokenizeString2(pszFormat, ";", 0));
3667 20 : if (aosTokens.size() != 1)
3668 0 : return CE_Failure;
3669 :
3670 20 : if (EQUAL(aosTokens[0], "JPEG"))
3671 : {
3672 15 : if (ppszDetailedFormat)
3673 6 : *ppszDetailedFormat = VSIStrdup(
3674 12 : GDALGetCompressionFormatForJPEG(m_fpImage).c_str());
3675 :
3676 15 : const auto nSavedPos = VSIFTellL(m_fpImage);
3677 15 : VSIFSeekL(m_fpImage, 0, SEEK_END);
3678 15 : auto nFileSize = VSIFTellL(m_fpImage);
3679 15 : if (nFileSize > std::numeric_limits<size_t>::max() / 2)
3680 0 : return CE_Failure;
3681 15 : if (nFileSize > 4)
3682 : {
3683 15 : VSIFSeekL(m_fpImage, nFileSize - 4, SEEK_SET);
3684 : // Detect zlib compress mask band at end of file
3685 : // and remove it if found
3686 15 : uint32_t nImageSize = 0;
3687 15 : VSIFReadL(&nImageSize, 4, 1, m_fpImage);
3688 15 : CPL_LSBPTR32(&nImageSize);
3689 15 : if (nImageSize > 2 && nImageSize >= nFileSize / 2 &&
3690 15 : nImageSize < nFileSize - 4)
3691 : {
3692 2 : VSIFSeekL(m_fpImage, nImageSize - 2, SEEK_SET);
3693 : GByte abyTwoBytes[2];
3694 2 : if (VSIFReadL(abyTwoBytes, 2, 1, m_fpImage) == 1 &&
3695 2 : abyTwoBytes[0] == 0xFF && abyTwoBytes[1] == 0xD9)
3696 : {
3697 2 : nFileSize = nImageSize;
3698 : }
3699 : }
3700 : }
3701 15 : auto nSize = static_cast<size_t>(nFileSize);
3702 15 : if (ppBuffer)
3703 : {
3704 14 : if (pnBufferSize == nullptr)
3705 : {
3706 1 : VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
3707 2 : return CE_Failure;
3708 : }
3709 13 : bool bFreeOnError = false;
3710 13 : if (*ppBuffer)
3711 : {
3712 3 : if (*pnBufferSize < nSize)
3713 : {
3714 1 : VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
3715 1 : return CE_Failure;
3716 : }
3717 : }
3718 : else
3719 : {
3720 10 : *ppBuffer = VSI_MALLOC_VERBOSE(nSize);
3721 10 : if (*ppBuffer == nullptr)
3722 : {
3723 0 : VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
3724 0 : return CE_Failure;
3725 : }
3726 10 : bFreeOnError = true;
3727 : }
3728 12 : VSIFSeekL(m_fpImage, 0, SEEK_SET);
3729 12 : if (VSIFReadL(*ppBuffer, nSize, 1, m_fpImage) != 1)
3730 : {
3731 0 : if (bFreeOnError)
3732 : {
3733 0 : VSIFree(*ppBuffer);
3734 0 : *ppBuffer = nullptr;
3735 : }
3736 0 : VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
3737 0 : return CE_Failure;
3738 : }
3739 :
3740 12 : constexpr GByte EXIF_SIGNATURE[] = {'E', 'x', 'i',
3741 : 'f', '\0', '\0'};
3742 12 : constexpr char APP1_XMP_SIGNATURE[] =
3743 : "http://ns.adobe.com/xap/1.0/";
3744 12 : size_t nChunkLoc = 2;
3745 12 : GByte *pabyJPEG = static_cast<GByte *>(*ppBuffer);
3746 104 : while (nChunkLoc + 4 <= nSize)
3747 : {
3748 104 : if (pabyJPEG[nChunkLoc + 0] != 0xFF)
3749 0 : break;
3750 104 : if (pabyJPEG[nChunkLoc + 1] == 0xDA)
3751 12 : break;
3752 92 : const int nChunkLength =
3753 92 : pabyJPEG[nChunkLoc + 2] * 256 + pabyJPEG[nChunkLoc + 3];
3754 92 : if (nChunkLength < 2 || static_cast<size_t>(nChunkLength) >
3755 92 : nSize - (nChunkLoc + 2))
3756 : break;
3757 92 : if (pabyJPEG[nChunkLoc + 1] == 0xE1 &&
3758 7 : nChunkLoc + 4 + sizeof(EXIF_SIGNATURE) <= nSize &&
3759 7 : memcmp(pabyJPEG + nChunkLoc + 4, EXIF_SIGNATURE,
3760 : sizeof(EXIF_SIGNATURE)) == 0)
3761 : {
3762 6 : CPLDebug("JPEG", "Remove existing EXIF from "
3763 : "source compressed data");
3764 6 : memmove(pabyJPEG + nChunkLoc,
3765 6 : pabyJPEG + nChunkLoc + 2 + nChunkLength,
3766 6 : nSize - (nChunkLoc + 2 + nChunkLength));
3767 6 : nSize -= 2 + nChunkLength;
3768 6 : continue;
3769 : }
3770 86 : else if (pabyJPEG[nChunkLoc + 1] == 0xE1 &&
3771 1 : nChunkLoc + 4 + sizeof(APP1_XMP_SIGNATURE) <=
3772 2 : nSize &&
3773 1 : memcmp(pabyJPEG + nChunkLoc + 4,
3774 : APP1_XMP_SIGNATURE,
3775 : sizeof(APP1_XMP_SIGNATURE)) == 0)
3776 : {
3777 1 : CPLDebug("JPEG", "Remove existing XMP from "
3778 : "source compressed data");
3779 1 : memmove(pabyJPEG + nChunkLoc,
3780 1 : pabyJPEG + nChunkLoc + 2 + nChunkLength,
3781 1 : nSize - (nChunkLoc + 2 + nChunkLength));
3782 1 : nSize -= 2 + nChunkLength;
3783 1 : continue;
3784 : }
3785 85 : nChunkLoc += 2 + nChunkLength;
3786 : }
3787 : }
3788 13 : VSIFSeekL(m_fpImage, nSavedPos, SEEK_SET);
3789 13 : if (pnBufferSize)
3790 13 : *pnBufferSize = nSize;
3791 13 : return CE_None;
3792 : }
3793 : }
3794 5 : return CE_Failure;
3795 : }
3796 :
3797 : #endif // !defined(JPGDataset)
3798 :
3799 : /************************************************************************/
3800 : /* ErrorExit() */
3801 : /************************************************************************/
3802 :
3803 148 : void JPGDataset::ErrorExit(j_common_ptr cinfo)
3804 : {
3805 148 : GDALJPEGUserData *psUserData =
3806 : static_cast<GDALJPEGUserData *>(cinfo->client_data);
3807 148 : char buffer[JMSG_LENGTH_MAX] = {};
3808 :
3809 : // Create the message.
3810 148 : (*cinfo->err->format_message)(cinfo, buffer);
3811 :
3812 : // Avoid error for a 12bit JPEG if reading from the 8bit JPEG driver and
3813 : // we have JPEG_DUAL_MODE_8_12 support, as we'll try again with 12bit JPEG
3814 : // driver.
3815 : #if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
3816 148 : if (strstr(buffer, "Unsupported JPEG data precision 12") == nullptr)
3817 : #endif
3818 12 : CPLError(CE_Failure, CPLE_AppDefined, "libjpeg: %s", buffer);
3819 :
3820 : // Return control to the setjmp point.
3821 148 : longjmp(psUserData->setjmp_buffer, 1);
3822 : }
3823 :
3824 : /************************************************************************/
3825 : /* OutputMessage() */
3826 : /************************************************************************/
3827 :
3828 0 : void JPGDataset::OutputMessage(j_common_ptr cinfo)
3829 : {
3830 0 : char buffer[JMSG_LENGTH_MAX] = {};
3831 :
3832 : // Create the message.
3833 0 : (*cinfo->err->format_message)(cinfo, buffer);
3834 :
3835 0 : CPLDebug("JPEG", "libjpeg: %s", buffer);
3836 0 : }
3837 :
3838 : /************************************************************************/
3839 : /* EmitMessage() */
3840 : /************************************************************************/
3841 :
3842 220015 : void JPGDataset::EmitMessage(j_common_ptr cinfo, int msg_level)
3843 : {
3844 220015 : GDALJPEGUserData *psUserData =
3845 : static_cast<GDALJPEGUserData *>(cinfo->client_data);
3846 220015 : if (msg_level >= 0) // Trace message.
3847 : {
3848 220007 : if (psUserData->p_previous_emit_message != nullptr)
3849 220007 : psUserData->p_previous_emit_message(cinfo, msg_level);
3850 : }
3851 : else
3852 : {
3853 : // Warning : libjpeg will try to recover but the image will be likely
3854 : // corrupted.
3855 :
3856 8 : struct jpeg_error_mgr *err = cinfo->err;
3857 :
3858 : // It's a warning message. Since corrupt files may generate many
3859 : // warnings, the policy implemented here is to show only the first
3860 : // warning, unless trace_level >= 3.
3861 8 : if (err->num_warnings == 0 || err->trace_level >= 3)
3862 : {
3863 4 : char buffer[JMSG_LENGTH_MAX] = {};
3864 :
3865 : // Create the message.
3866 4 : (*cinfo->err->format_message)(cinfo, buffer);
3867 :
3868 : const char *pszVal =
3869 9 : CPLGetConfigOption("GDAL_ERROR_ON_LIBJPEG_WARNING", nullptr);
3870 9 : if (strstr(buffer, "Premature end of JPEG file"))
3871 : {
3872 : // Consider this an error by default
3873 6 : if (pszVal == nullptr || CPLTestBool(pszVal))
3874 : {
3875 5 : psUserData->bNonFatalErrorEncountered = true;
3876 5 : if (pszVal == nullptr)
3877 : {
3878 3 : CPLError(CE_Failure, CPLE_AppDefined,
3879 : "libjpeg: %s (this error can be turned as a "
3880 : "warning "
3881 : "by setting GDAL_ERROR_ON_LIBJPEG_WARNING to "
3882 : "FALSE)",
3883 : buffer);
3884 : }
3885 : else
3886 : {
3887 2 : CPLError(CE_Failure, CPLE_AppDefined, "libjpeg: %s",
3888 : buffer);
3889 : }
3890 : }
3891 : else
3892 : {
3893 1 : CPLError(CE_Warning, CPLE_AppDefined, "libjpeg: %s",
3894 : buffer);
3895 : }
3896 : }
3897 3 : else if (pszVal == nullptr || !CPLTestBool(pszVal))
3898 : {
3899 2 : if (pszVal == nullptr)
3900 : {
3901 1 : CPLError(
3902 : CE_Warning, CPLE_AppDefined,
3903 : "libjpeg: %s (this warning can be turned as an error "
3904 : "by setting GDAL_ERROR_ON_LIBJPEG_WARNING to TRUE)",
3905 : buffer);
3906 : }
3907 : else
3908 : {
3909 1 : CPLError(CE_Warning, CPLE_AppDefined, "libjpeg: %s",
3910 : buffer);
3911 : }
3912 : }
3913 : else
3914 : {
3915 1 : psUserData->bNonFatalErrorEncountered = true;
3916 1 : CPLError(CE_Failure, CPLE_AppDefined, "libjpeg: %s", buffer);
3917 : }
3918 : }
3919 :
3920 : // Always count warnings in num_warnings.
3921 13 : err->num_warnings++;
3922 : }
3923 220021 : }
3924 :
3925 : /************************************************************************/
3926 : /* ProgressMonitor() */
3927 : /************************************************************************/
3928 :
3929 : /* Avoid the risk of denial-of-service on crafted JPEGs with an insane */
3930 : /* number of scans. */
3931 : /* See
3932 : * http://www.libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf
3933 : */
3934 218775 : void JPGDataset::ProgressMonitor(j_common_ptr cinfo)
3935 : {
3936 218775 : if (cinfo->is_decompressor)
3937 : {
3938 218776 : GDALJPEGUserData *psUserData =
3939 : static_cast<GDALJPEGUserData *>(cinfo->client_data);
3940 218776 : const int scan_no =
3941 : reinterpret_cast<j_decompress_ptr>(cinfo)->input_scan_number;
3942 218776 : if (scan_no >= psUserData->nMaxScans)
3943 : {
3944 1 : CPLError(CE_Failure, CPLE_AppDefined,
3945 : "Scan number %d exceeds maximum scans (%d)", scan_no,
3946 : psUserData->nMaxScans);
3947 :
3948 : // Return control to the setjmp point.
3949 1 : longjmp(psUserData->setjmp_buffer, 1);
3950 : }
3951 : }
3952 218774 : }
3953 :
3954 : #if !defined(JPGDataset)
3955 :
3956 : /************************************************************************/
3957 : /* JPGAddICCProfile() */
3958 : /* */
3959 : /* This function adds an ICC profile to a JPEG file. */
3960 : /************************************************************************/
3961 :
3962 3 : void JPGAddICCProfile(void *pInfo, const char *pszICCProfile,
3963 : my_jpeg_write_m_header p_jpeg_write_m_header,
3964 : my_jpeg_write_m_byte p_jpeg_write_m_byte)
3965 : {
3966 3 : if (pszICCProfile == nullptr)
3967 0 : return;
3968 :
3969 : // Write out each segment of the ICC profile.
3970 3 : char *pEmbedBuffer = CPLStrdup(pszICCProfile);
3971 : GInt32 nEmbedLen =
3972 3 : CPLBase64DecodeInPlace(reinterpret_cast<GByte *>(pEmbedBuffer));
3973 3 : char *pEmbedPtr = pEmbedBuffer;
3974 3 : char const *const paHeader = "ICC_PROFILE";
3975 3 : int nSegments = (nEmbedLen + 65518) / 65519;
3976 3 : int nSegmentID = 1;
3977 :
3978 10 : while (nEmbedLen != 0)
3979 : {
3980 : // 65535 - 16 bytes for header = 65519
3981 7 : const int nChunkLen = (nEmbedLen > 65519) ? 65519 : nEmbedLen;
3982 7 : nEmbedLen -= nChunkLen;
3983 :
3984 : // Write marker and length.
3985 7 : p_jpeg_write_m_header(pInfo, JPEG_APP0 + 2,
3986 7 : static_cast<unsigned int>(nChunkLen + 14));
3987 :
3988 : // Write identifier.
3989 91 : for (int i = 0; i < 12; i++)
3990 84 : p_jpeg_write_m_byte(pInfo, paHeader[i]);
3991 :
3992 : // Write ID and max ID.
3993 7 : p_jpeg_write_m_byte(pInfo, nSegmentID);
3994 7 : p_jpeg_write_m_byte(pInfo, nSegments);
3995 :
3996 : // Write ICC Profile.
3997 304295 : for (int i = 0; i < nChunkLen; i++)
3998 304288 : p_jpeg_write_m_byte(pInfo, pEmbedPtr[i]);
3999 :
4000 7 : nSegmentID++;
4001 :
4002 7 : pEmbedPtr += nChunkLen;
4003 : }
4004 :
4005 3 : CPLFree(pEmbedBuffer);
4006 : }
4007 :
4008 : /************************************************************************/
4009 : /* JPGAppendMask() */
4010 : /* */
4011 : /* This function appends a zlib compressed bitmask to a JPEG */
4012 : /* file (or really any file) pulled from an existing mask band. */
4013 : /************************************************************************/
4014 :
4015 : // MSVC does not know that memset() has initialized sStream.
4016 : #ifdef _MSC_VER
4017 : #pragma warning(disable : 4701)
4018 : #endif
4019 :
4020 8 : CPLErr JPGAppendMask(const char *pszJPGFilename, GDALRasterBand *poMask,
4021 : GDALProgressFunc pfnProgress, void *pProgressData)
4022 :
4023 : {
4024 8 : const int nXSize = poMask->GetXSize();
4025 8 : const int nYSize = poMask->GetYSize();
4026 8 : const int nBitBufSize = nYSize * ((nXSize + 7) / 8);
4027 8 : CPLErr eErr = CE_None;
4028 :
4029 : // Allocate uncompressed bit buffer.
4030 : GByte *pabyBitBuf =
4031 8 : static_cast<GByte *>(VSI_CALLOC_VERBOSE(1, nBitBufSize));
4032 :
4033 8 : GByte *pabyMaskLine = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nXSize));
4034 8 : if (pabyBitBuf == nullptr || pabyMaskLine == nullptr)
4035 : {
4036 0 : eErr = CE_Failure;
4037 : }
4038 :
4039 : // No reason to set it to MSB, unless for debugging purposes
4040 : // to be able to generate a unusual LSB ordered mask (#5102).
4041 : const char *pszJPEGMaskBitOrder =
4042 8 : CPLGetConfigOption("JPEG_WRITE_MASK_BIT_ORDER", "LSB");
4043 8 : const bool bMaskLSBOrder = EQUAL(pszJPEGMaskBitOrder, "LSB");
4044 :
4045 : // Set bit buffer from mask band, scanline by scanline.
4046 8 : GUInt32 iBit = 0;
4047 688 : for (int iY = 0; eErr == CE_None && iY < nYSize; iY++)
4048 : {
4049 680 : eErr = poMask->RasterIO(GF_Read, 0, iY, nXSize, 1, pabyMaskLine, nXSize,
4050 : 1, GDT_Byte, 0, 0, nullptr);
4051 680 : if (eErr != CE_None)
4052 0 : break;
4053 :
4054 680 : if (bMaskLSBOrder)
4055 : {
4056 265051 : for (int iX = 0; iX < nXSize; iX++)
4057 : {
4058 264453 : if (pabyMaskLine[iX] != 0)
4059 198418 : pabyBitBuf[iBit >> 3] |= (0x1 << (iBit & 7));
4060 :
4061 264453 : iBit++;
4062 : }
4063 : }
4064 : else
4065 : {
4066 2331 : for (int iX = 0; iX < nXSize; iX++)
4067 : {
4068 2249 : if (pabyMaskLine[iX] != 0)
4069 784 : pabyBitBuf[iBit >> 3] |= (0x1 << (7 - (iBit & 7)));
4070 :
4071 2249 : iBit++;
4072 : }
4073 : }
4074 :
4075 848 : if (pfnProgress != nullptr &&
4076 168 : !pfnProgress((iY + 1) / static_cast<double>(nYSize), nullptr,
4077 : pProgressData))
4078 : {
4079 0 : eErr = CE_Failure;
4080 0 : CPLError(CE_Failure, CPLE_UserInterrupt,
4081 : "User terminated JPGAppendMask()");
4082 : }
4083 : }
4084 :
4085 8 : CPLFree(pabyMaskLine);
4086 :
4087 : // Compress.
4088 8 : GByte *pabyCMask = nullptr;
4089 :
4090 8 : if (eErr == CE_None)
4091 : {
4092 8 : pabyCMask = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBitBufSize + 30));
4093 8 : if (pabyCMask == nullptr)
4094 : {
4095 0 : eErr = CE_Failure;
4096 : }
4097 : }
4098 :
4099 8 : size_t nTotalOut = 0;
4100 8 : if (eErr == CE_None)
4101 : {
4102 16 : if (CPLZLibDeflate(pabyBitBuf, nBitBufSize, -1, pabyCMask,
4103 8 : nBitBufSize + 30, &nTotalOut) == nullptr)
4104 : {
4105 0 : CPLError(CE_Failure, CPLE_AppDefined,
4106 : "Deflate compression of jpeg bit mask failed.");
4107 0 : eErr = CE_Failure;
4108 : }
4109 : }
4110 :
4111 : // Write to disk, along with image file size.
4112 8 : if (eErr == CE_None)
4113 : {
4114 8 : VSILFILE *fpOut = VSIFOpenL(pszJPGFilename, "r+");
4115 8 : if (fpOut == nullptr)
4116 : {
4117 0 : CPLError(CE_Failure, CPLE_AppDefined,
4118 : "Failed to open jpeg to append bitmask.");
4119 0 : eErr = CE_Failure;
4120 : }
4121 : else
4122 : {
4123 8 : VSIFSeekL(fpOut, 0, SEEK_END);
4124 :
4125 8 : GUInt32 nImageSize = static_cast<GUInt32>(VSIFTellL(fpOut));
4126 8 : CPL_LSBPTR32(&nImageSize);
4127 :
4128 8 : if (VSIFWriteL(pabyCMask, 1, nTotalOut, fpOut) != nTotalOut)
4129 : {
4130 0 : CPLError(CE_Failure, CPLE_FileIO,
4131 : "Failure writing compressed bitmask.\n%s",
4132 0 : VSIStrerror(errno));
4133 0 : eErr = CE_Failure;
4134 : }
4135 : else
4136 : {
4137 8 : VSIFWriteL(&nImageSize, 4, 1, fpOut);
4138 : }
4139 :
4140 8 : VSIFCloseL(fpOut);
4141 : }
4142 : }
4143 :
4144 8 : CPLFree(pabyBitBuf);
4145 8 : CPLFree(pabyCMask);
4146 :
4147 8 : return eErr;
4148 : }
4149 :
4150 : /************************************************************************/
4151 : /* JPGAddEXIF() */
4152 : /************************************************************************/
4153 :
4154 248 : void JPGAddEXIF(GDALDataType eWorkDT, GDALDataset *poSrcDS, char **papszOptions,
4155 : void *cinfo, my_jpeg_write_m_header p_jpeg_write_m_header,
4156 : my_jpeg_write_m_byte p_jpeg_write_m_byte,
4157 : GDALDataset *(pCreateCopy)(const char *, GDALDataset *, int,
4158 : char **,
4159 : GDALProgressFunc pfnProgress,
4160 : void *pProgressData))
4161 : {
4162 248 : const int nBands = poSrcDS->GetRasterCount();
4163 248 : const int nXSize = poSrcDS->GetRasterXSize();
4164 248 : const int nYSize = poSrcDS->GetRasterYSize();
4165 :
4166 : bool bGenerateEXIFThumbnail =
4167 248 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "EXIF_THUMBNAIL", "NO"));
4168 : const char *pszThumbnailWidth =
4169 248 : CSLFetchNameValue(papszOptions, "THUMBNAIL_WIDTH");
4170 : const char *pszThumbnailHeight =
4171 248 : CSLFetchNameValue(papszOptions, "THUMBNAIL_HEIGHT");
4172 248 : int nOvrWidth = 0;
4173 248 : int nOvrHeight = 0;
4174 248 : if (pszThumbnailWidth == nullptr && pszThumbnailHeight == nullptr)
4175 : {
4176 244 : if (nXSize >= nYSize)
4177 : {
4178 237 : nOvrWidth = 128;
4179 : }
4180 : else
4181 : {
4182 7 : nOvrHeight = 128;
4183 : }
4184 : }
4185 248 : if (pszThumbnailWidth != nullptr)
4186 : {
4187 3 : nOvrWidth = atoi(pszThumbnailWidth);
4188 3 : if (nOvrWidth < 32)
4189 0 : nOvrWidth = 32;
4190 3 : if (nOvrWidth > 1024)
4191 0 : nOvrWidth = 1024;
4192 : }
4193 248 : if (pszThumbnailHeight != nullptr)
4194 : {
4195 3 : nOvrHeight = atoi(pszThumbnailHeight);
4196 3 : if (nOvrHeight < 32)
4197 0 : nOvrHeight = 32;
4198 3 : if (nOvrHeight > 1024)
4199 0 : nOvrHeight = 1024;
4200 : }
4201 248 : if (nOvrWidth == 0)
4202 : {
4203 8 : nOvrWidth = static_cast<int>(static_cast<GIntBig>(nOvrHeight) * nXSize /
4204 8 : nYSize);
4205 8 : if (nOvrWidth == 0)
4206 0 : nOvrWidth = 1;
4207 : }
4208 240 : else if (nOvrHeight == 0)
4209 : {
4210 238 : nOvrHeight =
4211 238 : static_cast<int>(static_cast<GIntBig>(nOvrWidth) * nYSize / nXSize);
4212 238 : if (nOvrHeight == 0)
4213 0 : nOvrHeight = 1;
4214 : }
4215 :
4216 248 : vsi_l_offset nJPEGIfByteCount = 0;
4217 248 : GByte *pabyOvr = nullptr;
4218 :
4219 248 : if (bGenerateEXIFThumbnail && nXSize > nOvrWidth && nYSize > nOvrHeight)
4220 : {
4221 6 : GDALDataset *poMemDS = MEMDataset::Create("", nOvrWidth, nOvrHeight,
4222 : nBands, eWorkDT, nullptr);
4223 : GDALRasterBand **papoSrcBands = static_cast<GDALRasterBand **>(
4224 6 : CPLMalloc(nBands * sizeof(GDALRasterBand *)));
4225 : GDALRasterBand ***papapoOverviewBands = static_cast<GDALRasterBand ***>(
4226 6 : CPLMalloc(nBands * sizeof(GDALRasterBand **)));
4227 14 : for (int i = 0; i < nBands; i++)
4228 : {
4229 8 : papoSrcBands[i] = poSrcDS->GetRasterBand(i + 1);
4230 16 : papapoOverviewBands[i] = static_cast<GDALRasterBand **>(
4231 8 : CPLMalloc(sizeof(GDALRasterBand *)));
4232 8 : papapoOverviewBands[i][0] = poMemDS->GetRasterBand(i + 1);
4233 : }
4234 6 : CPLErr eErr = GDALRegenerateOverviewsMultiBand(
4235 : nBands, papoSrcBands, 1, papapoOverviewBands, "AVERAGE", nullptr,
4236 : nullptr,
4237 : /* papszOptions = */ nullptr);
4238 6 : CPLFree(papoSrcBands);
4239 14 : for (int i = 0; i < nBands; i++)
4240 : {
4241 8 : CPLFree(papapoOverviewBands[i]);
4242 : }
4243 6 : CPLFree(papapoOverviewBands);
4244 :
4245 6 : if (eErr != CE_None)
4246 : {
4247 0 : GDALClose(poMemDS);
4248 0 : return;
4249 : }
4250 :
4251 12 : const CPLString osTmpFile(VSIMemGenerateHiddenFilename("ovrjpg"));
4252 6 : GDALDataset *poOutDS = pCreateCopy(osTmpFile, poMemDS, 0, nullptr,
4253 : GDALDummyProgress, nullptr);
4254 6 : const bool bExifOverviewSuccess = poOutDS != nullptr;
4255 6 : delete poOutDS;
4256 6 : poOutDS = nullptr;
4257 6 : GDALClose(poMemDS);
4258 6 : if (bExifOverviewSuccess)
4259 6 : pabyOvr = VSIGetMemFileBuffer(osTmpFile, &nJPEGIfByteCount, TRUE);
4260 6 : VSIUnlink(osTmpFile);
4261 :
4262 : // cppcheck-suppress knownConditionTrueFalse
4263 6 : if (pabyOvr == nullptr)
4264 : {
4265 0 : nJPEGIfByteCount = 0;
4266 0 : CPLError(CE_Warning, CPLE_AppDefined,
4267 : "Could not generate EXIF overview");
4268 : }
4269 : }
4270 :
4271 : GUInt32 nMarkerSize;
4272 : const bool bWriteExifMetadata =
4273 248 : CPLFetchBool(papszOptions, "WRITE_EXIF_METADATA", true);
4274 :
4275 : GByte *pabyEXIF =
4276 248 : EXIFCreate(bWriteExifMetadata ? poSrcDS->GetMetadata() : nullptr,
4277 : pabyOvr, static_cast<GUInt32>(nJPEGIfByteCount), nOvrWidth,
4278 : nOvrHeight, &nMarkerSize);
4279 248 : if (pabyEXIF)
4280 : {
4281 22 : p_jpeg_write_m_header(cinfo, JPEG_APP0 + 1, nMarkerSize);
4282 4783 : for (GUInt32 i = 0; i < nMarkerSize; i++)
4283 : {
4284 4761 : p_jpeg_write_m_byte(cinfo, pabyEXIF[i]);
4285 : }
4286 22 : VSIFree(pabyEXIF);
4287 : }
4288 248 : CPLFree(pabyOvr);
4289 : }
4290 :
4291 : #endif // !defined(JPGDataset)
4292 :
4293 : /************************************************************************/
4294 : /* CreateCopy() */
4295 : /************************************************************************/
4296 :
4297 275 : GDALDataset *JPGDataset::CreateCopy(const char *pszFilename,
4298 : GDALDataset *poSrcDS, int bStrict,
4299 : char **papszOptions,
4300 : GDALProgressFunc pfnProgress,
4301 : void *pProgressData)
4302 :
4303 : {
4304 275 : const int nBands = poSrcDS->GetRasterCount();
4305 :
4306 : const char *pszLossLessCopy =
4307 275 : CSLFetchNameValueDef(papszOptions, "LOSSLESS_COPY", "AUTO");
4308 275 : if (EQUAL(pszLossLessCopy, "AUTO") || CPLTestBool(pszLossLessCopy))
4309 : {
4310 275 : void *pJPEGContent = nullptr;
4311 275 : size_t nJPEGContent = 0;
4312 275 : if (poSrcDS->ReadCompressedData("JPEG", 0, 0, poSrcDS->GetRasterXSize(),
4313 : poSrcDS->GetRasterYSize(), nBands,
4314 : nullptr, &pJPEGContent, &nJPEGContent,
4315 559 : nullptr) == CE_None &&
4316 284 : GDALGetCompressionFormatForJPEG(pJPEGContent, nJPEGContent)
4317 9 : .find(";colorspace=RGBA") == std::string::npos)
4318 : {
4319 9 : if (!pfnProgress(0.0, nullptr, pProgressData))
4320 9 : return nullptr;
4321 :
4322 9 : CPLDebug("JPEG", "Lossless copy from source dataset");
4323 9 : std::vector<GByte> abyJPEG;
4324 : try
4325 : {
4326 9 : abyJPEG.assign(static_cast<const GByte *>(pJPEGContent),
4327 9 : static_cast<const GByte *>(pJPEGContent) +
4328 9 : nJPEGContent);
4329 :
4330 : const bool bWriteExifMetadata =
4331 9 : CPLFetchBool(papszOptions, "WRITE_EXIF_METADATA", true);
4332 9 : if (bWriteExifMetadata)
4333 : {
4334 9 : char **papszEXIF_MD = poSrcDS->GetMetadata("EXIF");
4335 9 : if (papszEXIF_MD == nullptr)
4336 : {
4337 9 : papszEXIF_MD = poSrcDS->GetMetadata();
4338 : }
4339 9 : GUInt32 nEXIFContentSize = 0;
4340 9 : GByte *pabyEXIF = EXIFCreate(papszEXIF_MD, nullptr, 0, 0, 0,
4341 : &nEXIFContentSize);
4342 9 : if (nEXIFContentSize > 0 && nEXIFContentSize + 2 <= 65535U)
4343 : {
4344 1 : size_t nChunkLoc = 2;
4345 1 : size_t nInsertPos = 0;
4346 1 : constexpr GByte JFIF_SIGNATURE[] = {'J', 'F', 'I', 'F',
4347 : '\0'};
4348 1 : constexpr GByte EXIF_SIGNATURE[] = {'E', 'x', 'i',
4349 : 'f', '\0', '\0'};
4350 9 : while (nChunkLoc + 4 <= abyJPEG.size())
4351 : {
4352 9 : if (abyJPEG[nChunkLoc + 0] != 0xFF)
4353 0 : break;
4354 9 : if (abyJPEG[nChunkLoc + 1] == 0xDA)
4355 : {
4356 1 : if (nInsertPos == 0)
4357 0 : nInsertPos = nChunkLoc;
4358 1 : break;
4359 : }
4360 : const int nChunkLength =
4361 8 : abyJPEG[nChunkLoc + 2] * 256 +
4362 8 : abyJPEG[nChunkLoc + 3];
4363 8 : if (nChunkLength < 2)
4364 0 : break;
4365 9 : if (abyJPEG[nChunkLoc + 1] == 0xE0 &&
4366 1 : nChunkLoc + 4 + sizeof(JFIF_SIGNATURE) <=
4367 9 : abyJPEG.size() &&
4368 1 : memcmp(abyJPEG.data() + nChunkLoc + 4,
4369 : JFIF_SIGNATURE,
4370 : sizeof(JFIF_SIGNATURE)) == 0)
4371 : {
4372 1 : if (nInsertPos == 0)
4373 1 : nInsertPos = nChunkLoc + 2 + nChunkLength;
4374 : }
4375 7 : else if (abyJPEG[nChunkLoc + 1] == 0xE1 &&
4376 0 : nChunkLoc + 4 + sizeof(EXIF_SIGNATURE) <=
4377 7 : abyJPEG.size() &&
4378 0 : memcmp(abyJPEG.data() + nChunkLoc + 4,
4379 : EXIF_SIGNATURE,
4380 : sizeof(EXIF_SIGNATURE)) == 0)
4381 : {
4382 0 : CPLDebug("JPEG",
4383 : "Remove existing EXIF from source "
4384 : "compressed data");
4385 0 : abyJPEG.erase(abyJPEG.begin() + nChunkLoc,
4386 0 : abyJPEG.begin() + nChunkLoc + 2 +
4387 0 : nChunkLength);
4388 0 : continue;
4389 : }
4390 8 : nChunkLoc += 2 + nChunkLength;
4391 : }
4392 1 : if (nInsertPos > 0)
4393 : {
4394 2 : std::vector<GByte> abyNew;
4395 1 : const size_t nMarkerSize = 2 + nEXIFContentSize;
4396 1 : abyNew.reserve(abyJPEG.size() + 2 + nMarkerSize);
4397 1 : abyNew.insert(abyNew.end(), abyJPEG.data(),
4398 2 : abyJPEG.data() + nInsertPos);
4399 0 : abyNew.insert(abyNew.end(),
4400 1 : static_cast<GByte>(0xFF));
4401 0 : abyNew.insert(abyNew.end(),
4402 1 : static_cast<GByte>(0xE1));
4403 0 : abyNew.insert(abyNew.end(),
4404 1 : static_cast<GByte>(nMarkerSize >> 8));
4405 : abyNew.insert(
4406 0 : abyNew.end(),
4407 1 : static_cast<GByte>(nMarkerSize & 0xFF));
4408 0 : abyNew.insert(abyNew.end(), pabyEXIF,
4409 1 : pabyEXIF + nEXIFContentSize);
4410 0 : abyNew.insert(abyNew.end(),
4411 1 : abyJPEG.data() + nInsertPos,
4412 1 : abyJPEG.data() + abyJPEG.size());
4413 1 : abyJPEG = std::move(abyNew);
4414 : }
4415 : }
4416 9 : VSIFree(pabyEXIF);
4417 : }
4418 :
4419 : const bool bWriteXMP =
4420 9 : CPLFetchBool(papszOptions, "WRITE_XMP", true);
4421 : char **papszXMP =
4422 9 : bWriteXMP ? poSrcDS->GetMetadata("xml:XMP") : nullptr;
4423 9 : if (papszXMP && papszXMP[0])
4424 : {
4425 1 : size_t nChunkLoc = 2;
4426 1 : size_t nInsertPos = 0;
4427 1 : constexpr GByte JFIF_SIGNATURE[] = {'J', 'F', 'I', 'F',
4428 : '\0'};
4429 1 : constexpr const char APP1_XMP_SIGNATURE[] =
4430 : "http://ns.adobe.com/xap/1.0/";
4431 6 : while (nChunkLoc + 4 <= abyJPEG.size())
4432 : {
4433 6 : if (abyJPEG[nChunkLoc + 0] != 0xFF)
4434 0 : break;
4435 6 : if (abyJPEG[nChunkLoc + 1] == 0xDA)
4436 : {
4437 1 : if (nInsertPos == 0)
4438 0 : nInsertPos = nChunkLoc;
4439 1 : break;
4440 : }
4441 5 : const int nChunkLength = abyJPEG[nChunkLoc + 2] * 256 +
4442 5 : abyJPEG[nChunkLoc + 3];
4443 5 : if (nChunkLength < 2)
4444 0 : break;
4445 6 : if (abyJPEG[nChunkLoc + 1] == 0xE0 &&
4446 1 : nChunkLoc + 4 + sizeof(JFIF_SIGNATURE) <=
4447 6 : abyJPEG.size() &&
4448 1 : memcmp(abyJPEG.data() + nChunkLoc + 4,
4449 : JFIF_SIGNATURE, sizeof(JFIF_SIGNATURE)) == 0)
4450 : {
4451 1 : if (nInsertPos == 0)
4452 1 : nInsertPos = nChunkLoc + 2 + nChunkLength;
4453 : }
4454 4 : else if (abyJPEG[nChunkLoc + 1] == 0xE1 &&
4455 0 : nChunkLoc + 4 + sizeof(APP1_XMP_SIGNATURE) <=
4456 4 : abyJPEG.size() &&
4457 0 : memcmp(abyJPEG.data() + nChunkLoc + 4,
4458 : APP1_XMP_SIGNATURE,
4459 : sizeof(APP1_XMP_SIGNATURE)) == 0)
4460 : {
4461 0 : CPLDebug("JPEG", "Remove existing XMP from source "
4462 : "compressed data");
4463 0 : abyJPEG.erase(abyJPEG.begin() + nChunkLoc,
4464 0 : abyJPEG.begin() + nChunkLoc + 2 +
4465 0 : nChunkLength);
4466 0 : continue;
4467 : }
4468 5 : nChunkLoc += 2 + nChunkLength;
4469 : }
4470 1 : const size_t nMarkerSize =
4471 1 : 2 + sizeof(APP1_XMP_SIGNATURE) + strlen(papszXMP[0]);
4472 1 : if (nInsertPos > 0 && nMarkerSize <= 65535U)
4473 : {
4474 2 : std::vector<GByte> abyNew;
4475 1 : abyNew.reserve(abyJPEG.size() + 2 + nMarkerSize);
4476 1 : abyNew.insert(abyNew.end(), abyJPEG.data(),
4477 2 : abyJPEG.data() + nInsertPos);
4478 1 : abyNew.insert(abyNew.end(), static_cast<GByte>(0xFF));
4479 1 : abyNew.insert(abyNew.end(), static_cast<GByte>(0xE1));
4480 0 : abyNew.insert(abyNew.end(),
4481 1 : static_cast<GByte>(nMarkerSize >> 8));
4482 0 : abyNew.insert(abyNew.end(),
4483 1 : static_cast<GByte>(nMarkerSize & 0xFF));
4484 0 : abyNew.insert(abyNew.end(), APP1_XMP_SIGNATURE,
4485 : APP1_XMP_SIGNATURE +
4486 1 : sizeof(APP1_XMP_SIGNATURE));
4487 0 : abyNew.insert(abyNew.end(), papszXMP[0],
4488 1 : papszXMP[0] + strlen(papszXMP[0]));
4489 0 : abyNew.insert(abyNew.end(), abyJPEG.data() + nInsertPos,
4490 1 : abyJPEG.data() + abyJPEG.size());
4491 1 : abyJPEG = std::move(abyNew);
4492 : }
4493 : }
4494 : }
4495 0 : catch (const std::exception &)
4496 : {
4497 0 : abyJPEG.clear();
4498 : }
4499 9 : VSIFree(pJPEGContent);
4500 :
4501 9 : if (!abyJPEG.empty())
4502 : {
4503 : auto fpImage(
4504 9 : CPLTestBool(CSLFetchNameValueDef(
4505 : papszOptions, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME",
4506 : "NO"))
4507 0 : ? VSIFileManager::GetHandler(pszFilename)
4508 : ->CreateOnlyVisibleAtCloseTime(pszFilename, true,
4509 0 : nullptr)
4510 18 : : VSIFilesystemHandler::OpenStatic(pszFilename, "wb"));
4511 9 : if (fpImage == nullptr)
4512 : {
4513 0 : CPLError(CE_Failure, CPLE_OpenFailed,
4514 : "Unable to create jpeg file %s.", pszFilename);
4515 :
4516 0 : return nullptr;
4517 : }
4518 18 : if (fpImage->Write(abyJPEG.data(), 1, abyJPEG.size()) !=
4519 9 : abyJPEG.size())
4520 : {
4521 0 : CPLError(CE_Failure, CPLE_FileIO,
4522 0 : "Failure writing data: %s", VSIStrerror(errno));
4523 0 : fpImage->CancelCreation();
4524 0 : return nullptr;
4525 : }
4526 :
4527 9 : if (fpImage->Close() != 0)
4528 : {
4529 0 : CPLError(CE_Failure, CPLE_FileIO,
4530 : "Error at file closing of '%s': %s", pszFilename,
4531 0 : VSIStrerror(errno));
4532 0 : return nullptr;
4533 : }
4534 :
4535 9 : pfnProgress(1.0, nullptr, pProgressData);
4536 :
4537 : // Append masks to the jpeg file if necessary.
4538 9 : const auto poLastSrcBand = poSrcDS->GetRasterBand(nBands);
4539 : const bool bAppendMask =
4540 9 : poLastSrcBand != nullptr &&
4541 10 : poLastSrcBand->GetColorInterpretation() == GCI_AlphaBand &&
4542 1 : CPLFetchBool(papszOptions, "INTERNAL_MASK", true);
4543 :
4544 9 : if (bAppendMask)
4545 : {
4546 1 : CPLDebug("JPEG", "Appending Mask Bitmap");
4547 :
4548 1 : CPLErr eErr = JPGAppendMask(pszFilename, poLastSrcBand,
4549 : nullptr, nullptr);
4550 :
4551 1 : if (eErr != CE_None)
4552 : {
4553 0 : VSIUnlink(pszFilename);
4554 0 : return nullptr;
4555 : }
4556 : }
4557 :
4558 : // Do we need a world file?
4559 9 : if (CPLFetchBool(papszOptions, "WORLDFILE", false))
4560 : {
4561 0 : GDALGeoTransform gt;
4562 0 : poSrcDS->GetGeoTransform(gt);
4563 0 : GDALWriteWorldFile(pszFilename, "wld", gt.data());
4564 : }
4565 :
4566 : // Re-open dataset, and copy any auxiliary pam information.
4567 :
4568 : // If writing to stdout, we can't reopen it, so return
4569 : // a fake dataset to make the caller happy.
4570 9 : if (CPLTestBool(
4571 : CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", "YES")))
4572 : {
4573 9 : CPLPushErrorHandler(CPLQuietErrorHandler);
4574 :
4575 9 : JPGDatasetOpenArgs sArgs;
4576 9 : sArgs.pszFilename = pszFilename;
4577 9 : sArgs.bDoPAMInitialize = true;
4578 9 : sArgs.bUseInternalOverviews = true;
4579 :
4580 9 : auto poDS = Open(&sArgs);
4581 9 : CPLPopErrorHandler();
4582 9 : if (poDS)
4583 : {
4584 8 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
4585 8 : return poDS;
4586 : }
4587 :
4588 1 : CPLErrorReset();
4589 : }
4590 :
4591 1 : JPGDataset *poJPG_DS = new JPGDataset();
4592 1 : poJPG_DS->nRasterXSize = poSrcDS->GetRasterXSize();
4593 1 : poJPG_DS->nRasterYSize = poSrcDS->GetRasterYSize();
4594 2 : for (int i = 0; i < nBands; i++)
4595 1 : poJPG_DS->SetBand(i + 1, JPGCreateBand(poJPG_DS, i + 1));
4596 1 : return poJPG_DS;
4597 : }
4598 : }
4599 : }
4600 :
4601 266 : if (!EQUAL(pszLossLessCopy, "AUTO") && CPLTestBool(pszLossLessCopy))
4602 : {
4603 0 : CPLError(CE_Failure, CPLE_AppDefined,
4604 : "LOSSLESS_COPY=YES requested but not possible");
4605 0 : return nullptr;
4606 : }
4607 :
4608 : // Some some rudimentary checks.
4609 266 : if (nBands != 1 && nBands != 3 && nBands != 4)
4610 : {
4611 3 : CPLError(CE_Failure, CPLE_NotSupported,
4612 : "JPEG driver doesn't support %d bands. Must be 1 (grey), "
4613 : "3 (RGB) or 4 bands (CMYK).\n",
4614 : nBands);
4615 :
4616 3 : return nullptr;
4617 : }
4618 :
4619 263 : if (nBands == 1 && poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
4620 : {
4621 0 : CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported,
4622 : "JPEG driver ignores color table. "
4623 : "The source raster band will be considered as grey level.\n"
4624 : "Consider using color table expansion "
4625 : "(-expand option in gdal_translate)");
4626 0 : if (bStrict)
4627 0 : return nullptr;
4628 : }
4629 :
4630 265 : if (nBands == 4 &&
4631 2 : poSrcDS->GetRasterBand(1)->GetColorInterpretation() != GCI_CyanBand)
4632 : {
4633 2 : CPLError(CE_Warning, CPLE_AppDefined,
4634 : "4-band JPEGs will be interpreted on reading as in CMYK "
4635 : "colorspace");
4636 : }
4637 :
4638 263 : GDALJPEGUserData sUserData;
4639 263 : sUserData.bNonFatalErrorEncountered = false;
4640 263 : GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
4641 :
4642 : #if defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12)
4643 263 : if (eDT != GDT_Byte && eDT != GDT_UInt16)
4644 : {
4645 9 : CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported,
4646 : "JPEG driver doesn't support data type %s. "
4647 : "Only eight and twelve bit bands supported.",
4648 : GDALGetDataTypeName(
4649 : poSrcDS->GetRasterBand(1)->GetRasterDataType()));
4650 :
4651 9 : if (bStrict)
4652 9 : return nullptr;
4653 : }
4654 :
4655 254 : if (eDT == GDT_UInt16 || eDT == GDT_Int16)
4656 : {
4657 : #if defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
4658 2 : return JPEGDataset12CreateCopy(pszFilename, poSrcDS, bStrict,
4659 : papszOptions, pfnProgress,
4660 2 : pProgressData);
4661 : #else
4662 2 : eDT = GDT_UInt16;
4663 : #endif // defined(JPEG_DUAL_MODE_8_12) && !defined(JPGDataset)
4664 : }
4665 : else
4666 : {
4667 250 : eDT = GDT_Byte;
4668 : }
4669 :
4670 : #else // !(defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12))
4671 : if (eDT != GDT_Byte)
4672 : {
4673 : CPLError(bStrict ? CE_Failure : CE_Warning, CPLE_NotSupported,
4674 : "JPEG driver doesn't support data type %s. "
4675 : "Only eight bit byte bands supported.\n",
4676 : GDALGetDataTypeName(
4677 : poSrcDS->GetRasterBand(1)->GetRasterDataType()));
4678 :
4679 : if (bStrict)
4680 : return nullptr;
4681 : }
4682 :
4683 : eDT = GDT_Byte; // force to 8bit.
4684 : #endif // !(defined(JPEG_LIB_MK1_OR_12BIT) || defined(JPEG_DUAL_MODE_8_12))
4685 :
4686 : // What options has the caller selected?
4687 252 : int nQuality = 75;
4688 252 : const char *pszQuality = CSLFetchNameValue(papszOptions, "QUALITY");
4689 252 : if (pszQuality)
4690 : {
4691 113 : nQuality = atoi(pszQuality);
4692 113 : if (nQuality < 1 || nQuality > 100)
4693 : {
4694 0 : CPLError(CE_Failure, CPLE_IllegalArg,
4695 : "QUALITY=%s is not a legal value in the range 1-100.",
4696 : pszQuality);
4697 0 : return nullptr;
4698 : }
4699 : }
4700 :
4701 : // Create the dataset.
4702 : auto fpImage(
4703 252 : CPLTestBool(CSLFetchNameValueDef(
4704 : papszOptions, "@CREATE_ONLY_VISIBLE_AT_CLOSE_TIME", "NO"))
4705 2 : ? VSIFileManager::GetHandler(pszFilename)
4706 2 : ->CreateOnlyVisibleAtCloseTime(pszFilename, true, nullptr)
4707 506 : : VSIFilesystemHandler::OpenStatic(pszFilename, "wb"));
4708 252 : if (fpImage == nullptr)
4709 : {
4710 3 : CPLError(CE_Failure, CPLE_OpenFailed,
4711 : "Unable to create jpeg file %s.\n", pszFilename);
4712 3 : return nullptr;
4713 : }
4714 :
4715 : struct jpeg_compress_struct sCInfo;
4716 : struct jpeg_error_mgr sJErr;
4717 : GByte *pabyScanline;
4718 :
4719 : // Does the source have a mask? If so, we will append it to the
4720 : // jpeg file after the imagery.
4721 249 : const int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags();
4722 7 : const bool bAppendMask = !(nMaskFlags & GMF_ALL_VALID) &&
4723 257 : (nBands == 1 || (nMaskFlags & GMF_PER_DATASET)) &&
4724 7 : CPLFetchBool(papszOptions, "INTERNAL_MASK", true);
4725 :
4726 : // Nasty trick to avoid variable clobbering issues with setjmp/longjmp.
4727 498 : return CreateCopyStage2(pszFilename, poSrcDS, papszOptions, pfnProgress,
4728 249 : pProgressData, std::move(fpImage), eDT, nQuality,
4729 : bAppendMask, sUserData, sCInfo, sJErr,
4730 249 : pabyScanline);
4731 : }
4732 :
4733 249 : GDALDataset *JPGDataset::CreateCopyStage2(
4734 : const char *pszFilename, GDALDataset *poSrcDS, char **papszOptions,
4735 : GDALProgressFunc pfnProgress, void *pProgressData,
4736 : VSIVirtualHandleUniquePtr fpImage, GDALDataType eDT, int nQuality,
4737 : bool bAppendMask, GDALJPEGUserData &sUserData,
4738 : struct jpeg_compress_struct &sCInfo, struct jpeg_error_mgr &sJErr,
4739 : GByte *&pabyScanline)
4740 :
4741 : {
4742 249 : if (setjmp(sUserData.setjmp_buffer))
4743 : {
4744 0 : if (fpImage)
4745 0 : fpImage->CancelCreation();
4746 0 : return nullptr;
4747 : }
4748 :
4749 249 : if (!pfnProgress(0.0, nullptr, pProgressData))
4750 0 : return nullptr;
4751 :
4752 : // Initialize JPG access to the file.
4753 249 : sCInfo.err = jpeg_std_error(&sJErr);
4754 249 : sJErr.error_exit = JPGDataset::ErrorExit;
4755 249 : sJErr.output_message = JPGDataset::OutputMessage;
4756 249 : sUserData.p_previous_emit_message = sJErr.emit_message;
4757 249 : sJErr.emit_message = JPGDataset::EmitMessage;
4758 249 : sCInfo.client_data = &sUserData;
4759 :
4760 : #if defined(__GNUC__)
4761 : #pragma GCC diagnostic push
4762 : #pragma GCC diagnostic ignored "-Wold-style-cast"
4763 : #endif
4764 249 : jpeg_create_compress(&sCInfo);
4765 : #if defined(__GNUC__)
4766 : #pragma GCC diagnostic pop
4767 : #endif
4768 :
4769 249 : if (setjmp(sUserData.setjmp_buffer))
4770 : {
4771 1 : if (fpImage)
4772 1 : fpImage->CancelCreation();
4773 1 : jpeg_destroy_compress(&sCInfo);
4774 1 : return nullptr;
4775 : }
4776 :
4777 249 : jpeg_vsiio_dest(&sCInfo, fpImage.get());
4778 :
4779 249 : const int nXSize = poSrcDS->GetRasterXSize();
4780 249 : const int nYSize = poSrcDS->GetRasterYSize();
4781 249 : const int nBands = poSrcDS->GetRasterCount();
4782 249 : sCInfo.image_width = nXSize;
4783 249 : sCInfo.image_height = nYSize;
4784 249 : sCInfo.input_components = nBands;
4785 :
4786 249 : if (nBands == 3)
4787 156 : sCInfo.in_color_space = JCS_RGB;
4788 93 : else if (nBands == 1)
4789 91 : sCInfo.in_color_space = JCS_GRAYSCALE;
4790 : else
4791 2 : sCInfo.in_color_space = JCS_UNKNOWN;
4792 :
4793 249 : jpeg_set_defaults(&sCInfo);
4794 :
4795 : // libjpeg turbo 1.5.2 honours max_memory_to_use, but has no backing
4796 : // store implementation, so better not set max_memory_to_use ourselves.
4797 : // See https://github.com/libjpeg-turbo/libjpeg-turbo/issues/162
4798 249 : if (sCInfo.mem->max_memory_to_use > 0)
4799 : {
4800 : // This is to address bug related in ticket #1795.
4801 0 : if (CPLGetConfigOption("JPEGMEM", nullptr) == nullptr)
4802 : {
4803 : // If the user doesn't provide a value for JPEGMEM, we want to be
4804 : // sure that at least 500 MB will be used before creating the
4805 : // temporary file.
4806 0 : const long nMinMemory = 500 * 1024 * 1024;
4807 0 : sCInfo.mem->max_memory_to_use =
4808 0 : std::max(sCInfo.mem->max_memory_to_use, nMinMemory);
4809 : }
4810 : }
4811 :
4812 249 : if (eDT == GDT_UInt16)
4813 : {
4814 2 : sCInfo.data_precision = 12;
4815 : }
4816 : else
4817 : {
4818 247 : sCInfo.data_precision = 8;
4819 : }
4820 :
4821 249 : const char *pszVal = CSLFetchNameValue(papszOptions, "ARITHMETIC");
4822 249 : if (pszVal)
4823 1 : sCInfo.arith_code = CPLTestBool(pszVal);
4824 :
4825 : // Optimized Huffman coding. Supposedly slower according to libjpeg doc
4826 : // but no longer significant with today computer standards.
4827 249 : if (!sCInfo.arith_code)
4828 248 : sCInfo.optimize_coding = TRUE;
4829 :
4830 : #if JPEG_LIB_VERSION_MAJOR >= 8 && \
4831 : (JPEG_LIB_VERSION_MAJOR > 8 || JPEG_LIB_VERSION_MINOR >= 3)
4832 : pszVal = CSLFetchNameValue(papszOptions, "BLOCK");
4833 : if (pszVal)
4834 : sCInfo.block_size = atoi(pszVal);
4835 : #endif
4836 :
4837 : #if JPEG_LIB_VERSION_MAJOR >= 9
4838 : pszVal = CSLFetchNameValue(papszOptions, "COLOR_TRANSFORM");
4839 : if (pszVal)
4840 : {
4841 : sCInfo.color_transform =
4842 : EQUAL(pszVal, "RGB1") ? JCT_SUBTRACT_GREEN : JCT_NONE;
4843 : jpeg_set_colorspace(&sCInfo, JCS_RGB);
4844 : }
4845 : else
4846 : #endif
4847 :
4848 : // Mostly for debugging purposes.
4849 405 : if (nBands == 3 &&
4850 156 : CPLTestBool(CPLGetConfigOption("JPEG_WRITE_RGB", "NO")))
4851 : {
4852 0 : jpeg_set_colorspace(&sCInfo, JCS_RGB);
4853 : }
4854 :
4855 : #ifdef JPEG_LIB_MK1
4856 : sCInfo.bits_in_jsample = sCInfo.data_precision;
4857 : // Always force to 16 bit for JPEG_LIB_MK1
4858 : const GDALDataType eWorkDT = GDT_UInt16;
4859 : #else
4860 249 : const GDALDataType eWorkDT = eDT;
4861 : #endif
4862 :
4863 249 : jpeg_set_quality(&sCInfo, nQuality, TRUE);
4864 :
4865 249 : const bool bProgressive = CPLFetchBool(papszOptions, "PROGRESSIVE", false);
4866 249 : if (bProgressive)
4867 3 : jpeg_simple_progression(&sCInfo);
4868 :
4869 249 : jpeg_start_compress(&sCInfo, TRUE);
4870 :
4871 : struct Adapter
4872 : {
4873 29 : static void my_jpeg_write_m_header_adapter(void *cinfo, int marker,
4874 : unsigned int datalen)
4875 : {
4876 29 : jpeg_write_m_header(static_cast<jpeg_compress_struct *>(cinfo),
4877 : marker, datalen);
4878 29 : }
4879 :
4880 309147 : static void my_jpeg_write_m_byte_adapter(void *cinfo, int val)
4881 : {
4882 309147 : jpeg_write_m_byte(static_cast<jpeg_compress_struct *>(cinfo), val);
4883 309147 : }
4884 : };
4885 :
4886 248 : JPGAddEXIF(eWorkDT, poSrcDS, papszOptions, &sCInfo,
4887 : Adapter::my_jpeg_write_m_header_adapter,
4888 : Adapter::my_jpeg_write_m_byte_adapter, CreateCopy);
4889 :
4890 : // Add comment if available.
4891 248 : const char *pszComment = CSLFetchNameValue(papszOptions, "COMMENT");
4892 248 : if (pszComment)
4893 3 : jpeg_write_marker(&sCInfo, JPEG_COM,
4894 : reinterpret_cast<const JOCTET *>(pszComment),
4895 3 : static_cast<unsigned int>(strlen(pszComment)));
4896 :
4897 : // Save ICC profile if available.
4898 : const char *pszICCProfile =
4899 248 : CSLFetchNameValue(papszOptions, "SOURCE_ICC_PROFILE");
4900 248 : if (pszICCProfile == nullptr)
4901 : pszICCProfile =
4902 247 : poSrcDS->GetMetadataItem("SOURCE_ICC_PROFILE", "COLOR_PROFILE");
4903 :
4904 248 : if (pszICCProfile != nullptr)
4905 3 : JPGAddICCProfile(&sCInfo, pszICCProfile,
4906 : Adapter::my_jpeg_write_m_header_adapter,
4907 : Adapter::my_jpeg_write_m_byte_adapter);
4908 :
4909 : // Loop over image, copying image data.
4910 248 : const int nWorkDTSize = GDALGetDataTypeSizeBytes(eWorkDT);
4911 248 : pabyScanline = static_cast<GByte *>(
4912 248 : CPLMalloc(cpl::fits_on<int>(nBands * nXSize * nWorkDTSize)));
4913 :
4914 248 : if (setjmp(sUserData.setjmp_buffer))
4915 : {
4916 10 : fpImage->CancelCreation();
4917 10 : CPLFree(pabyScanline);
4918 10 : jpeg_destroy_compress(&sCInfo);
4919 10 : return nullptr;
4920 : }
4921 :
4922 248 : CPLErr eErr = CE_None;
4923 248 : bool bClipWarn = false;
4924 36563 : for (int iLine = 0; iLine < nYSize && eErr == CE_None; iLine++)
4925 : {
4926 145260 : eErr = poSrcDS->RasterIO(
4927 : GF_Read, 0, iLine, nXSize, 1, pabyScanline, nXSize, 1, eWorkDT,
4928 36315 : nBands, nullptr, cpl::fits_on<int>(nBands * nWorkDTSize),
4929 36315 : cpl::fits_on<int>(nBands * nXSize * nWorkDTSize), nWorkDTSize,
4930 : nullptr);
4931 :
4932 : // Clamp 16bit values to 12bit.
4933 36315 : if (nWorkDTSize == 2)
4934 : {
4935 266 : GUInt16 *panScanline = reinterpret_cast<GUInt16 *>(pabyScanline);
4936 :
4937 65902 : for (int iPixel = 0; iPixel < nXSize * nBands; iPixel++)
4938 : {
4939 65636 : if (panScanline[iPixel] > 4095)
4940 : {
4941 0 : panScanline[iPixel] = 4095;
4942 0 : if (!bClipWarn)
4943 : {
4944 0 : bClipWarn = true;
4945 0 : CPLError(CE_Warning, CPLE_AppDefined,
4946 : "One or more pixels clipped to fit "
4947 : "12bit domain for jpeg output.");
4948 : }
4949 : }
4950 : }
4951 : }
4952 :
4953 36315 : GDAL_JSAMPLE *ppSamples =
4954 36315 : reinterpret_cast<GDAL_JSAMPLE *>(pabyScanline);
4955 :
4956 36315 : if (eErr == CE_None)
4957 : {
4958 : #if defined(HAVE_JPEGTURBO_DUAL_MODE_8_12) && BITS_IN_JSAMPLE == 12
4959 : jpeg12_write_scanlines(&sCInfo, &ppSamples, 1);
4960 : #else
4961 36315 : jpeg_write_scanlines(&sCInfo, &ppSamples, 1);
4962 : #endif
4963 : }
4964 72630 : if (eErr == CE_None &&
4965 72630 : !pfnProgress((iLine + 1) / ((bAppendMask ? 2 : 1) *
4966 36315 : static_cast<double>(nYSize)),
4967 : nullptr, pProgressData))
4968 : {
4969 1 : eErr = CE_Failure;
4970 1 : CPLError(CE_Failure, CPLE_UserInterrupt,
4971 : "User terminated CreateCopy()");
4972 : }
4973 : }
4974 :
4975 : // Cleanup and close.
4976 248 : if (eErr == CE_None)
4977 247 : jpeg_finish_compress(&sCInfo);
4978 238 : jpeg_destroy_compress(&sCInfo);
4979 :
4980 : // Free scanline and image after jpeg_finish_compress since this could
4981 : // cause a longjmp to occur.
4982 238 : CPLFree(pabyScanline);
4983 :
4984 238 : if (eErr == CE_None)
4985 : {
4986 237 : if (fpImage->Close() != 0)
4987 : {
4988 0 : CPLError(CE_Failure, CPLE_FileIO,
4989 : "Error at file closing of '%s': %s", pszFilename,
4990 0 : VSIStrerror(errno));
4991 0 : eErr = CE_Failure;
4992 : }
4993 : }
4994 : else
4995 : {
4996 1 : fpImage->CancelCreation();
4997 1 : fpImage.reset();
4998 : }
4999 :
5000 238 : if (eErr != CE_None)
5001 : {
5002 1 : VSIUnlink(pszFilename);
5003 1 : return nullptr;
5004 : }
5005 :
5006 : // Append masks to the jpeg file if necessary.
5007 237 : int nCloneFlags = GCIF_PAM_DEFAULT & ~GCIF_METADATA;
5008 237 : if (bAppendMask)
5009 : {
5010 7 : CPLDebug("JPEG", "Appending Mask Bitmap");
5011 :
5012 : void *pScaledData =
5013 7 : GDALCreateScaledProgress(0.5, 1, pfnProgress, pProgressData);
5014 : eErr =
5015 7 : JPGAppendMask(pszFilename, poSrcDS->GetRasterBand(1)->GetMaskBand(),
5016 : GDALScaledProgress, pScaledData);
5017 7 : GDALDestroyScaledProgress(pScaledData);
5018 7 : nCloneFlags &= (~GCIF_MASK);
5019 :
5020 7 : if (eErr != CE_None)
5021 : {
5022 0 : VSIUnlink(pszFilename);
5023 0 : return nullptr;
5024 : }
5025 : }
5026 :
5027 : // Do we need a world file?
5028 237 : if (CPLFetchBool(papszOptions, "WORLDFILE", false))
5029 : {
5030 1 : GDALGeoTransform gt;
5031 1 : poSrcDS->GetGeoTransform(gt);
5032 1 : GDALWriteWorldFile(pszFilename, "wld", gt.data());
5033 : }
5034 :
5035 : // Re-open dataset, and copy any auxiliary pam information.
5036 :
5037 : // If writing to stdout, we can't reopen it, so return
5038 : // a fake dataset to make the caller happy.
5039 237 : if (CPLTestBool(CPLGetConfigOption("GDAL_OPEN_AFTER_COPY", "YES")))
5040 : {
5041 173 : CPLPushErrorHandler(CPLQuietErrorHandler);
5042 :
5043 173 : JPGDatasetOpenArgs sArgs;
5044 173 : sArgs.pszFilename = pszFilename;
5045 173 : sArgs.bDoPAMInitialize = true;
5046 173 : sArgs.bUseInternalOverviews = true;
5047 :
5048 173 : auto poDS = Open(&sArgs);
5049 173 : CPLPopErrorHandler();
5050 173 : if (poDS)
5051 : {
5052 172 : poDS->CloneInfo(poSrcDS, nCloneFlags);
5053 :
5054 : char **papszExcludedDomains =
5055 172 : CSLAddString(nullptr, "COLOR_PROFILE");
5056 172 : char **papszMD = poSrcDS->GetMetadata();
5057 172 : bool bOnlyEXIF = true;
5058 209 : for (char **papszIter = papszMD; papszIter && *papszIter;
5059 : ++papszIter)
5060 : {
5061 54 : if (!STARTS_WITH_CI(*papszIter, "EXIF_"))
5062 : {
5063 17 : bOnlyEXIF = false;
5064 17 : break;
5065 : }
5066 : }
5067 172 : if (bOnlyEXIF)
5068 155 : papszExcludedDomains = CSLAddString(papszExcludedDomains, "");
5069 172 : GDALDriver::DefaultCopyMetadata(poSrcDS, poDS, papszOptions,
5070 : papszExcludedDomains);
5071 172 : CSLDestroy(papszExcludedDomains);
5072 :
5073 172 : return poDS;
5074 : }
5075 :
5076 1 : CPLErrorReset();
5077 : }
5078 :
5079 65 : JPGDataset *poJPG_DS = new JPGDataset();
5080 65 : poJPG_DS->nRasterXSize = nXSize;
5081 65 : poJPG_DS->nRasterYSize = nYSize;
5082 214 : for (int i = 0; i < nBands; i++)
5083 149 : poJPG_DS->SetBand(i + 1, JPGCreateBand(poJPG_DS, i + 1));
5084 65 : return poJPG_DS;
5085 : }
5086 :
5087 : /************************************************************************/
5088 : /* GDALRegister_JPEG() */
5089 : /************************************************************************/
5090 :
5091 : #if !defined(JPGDataset)
5092 :
5093 442 : char **GDALJPGDriver::GetMetadata(const char *pszDomain)
5094 : {
5095 884 : std::lock_guard oLock(m_oMutex);
5096 442 : InitializeMetadata();
5097 884 : return GDALDriver::GetMetadata(pszDomain);
5098 : }
5099 :
5100 67126 : const char *GDALJPGDriver::GetMetadataItem(const char *pszName,
5101 : const char *pszDomain)
5102 : {
5103 134253 : std::lock_guard oLock(m_oMutex);
5104 :
5105 67127 : if (pszName != nullptr && EQUAL(pszName, GDAL_DMD_CREATIONOPTIONLIST) &&
5106 633 : (pszDomain == nullptr || EQUAL(pszDomain, "")))
5107 : {
5108 633 : InitializeMetadata();
5109 : }
5110 134254 : return GDALDriver::GetMetadataItem(pszName, pszDomain);
5111 : }
5112 :
5113 : // C_ARITH_CODING_SUPPORTED is defined in libjpeg-turbo's jconfig.h
5114 : #ifndef C_ARITH_CODING_SUPPORTED
5115 : static void GDALJPEGIsArithmeticCodingAvailableErrorExit(j_common_ptr cinfo)
5116 : {
5117 : jmp_buf *p_setjmp_buffer = static_cast<jmp_buf *>(cinfo->client_data);
5118 : // Return control to the setjmp point.
5119 : longjmp(*p_setjmp_buffer, 1);
5120 : }
5121 :
5122 : // Runtime check if arithmetic coding is available.
5123 : static bool GDALJPEGIsArithmeticCodingAvailable()
5124 : {
5125 : struct jpeg_compress_struct sCInfo;
5126 : struct jpeg_error_mgr sJErr;
5127 : jmp_buf setjmp_buffer;
5128 : if (setjmp(setjmp_buffer))
5129 : {
5130 : jpeg_destroy_compress(&sCInfo);
5131 : return false;
5132 : }
5133 : sCInfo.err = jpeg_std_error(&sJErr);
5134 : sJErr.error_exit = GDALJPEGIsArithmeticCodingAvailableErrorExit;
5135 : sCInfo.client_data = &setjmp_buffer;
5136 : #if defined(__GNUC__)
5137 : #pragma GCC diagnostic push
5138 : #pragma GCC diagnostic ignored "-Wold-style-cast"
5139 : #endif
5140 : jpeg_create_compress(&sCInfo);
5141 : #if defined(__GNUC__)
5142 : #pragma GCC diagnostic pop
5143 : #endif
5144 : // Hopefully nothing will be written.
5145 : jpeg_stdio_dest(&sCInfo, stderr);
5146 : sCInfo.image_width = 1;
5147 : sCInfo.image_height = 1;
5148 : sCInfo.input_components = 1;
5149 : sCInfo.in_color_space = JCS_UNKNOWN;
5150 : jpeg_set_defaults(&sCInfo);
5151 : sCInfo.arith_code = TRUE;
5152 : jpeg_start_compress(&sCInfo, FALSE);
5153 : jpeg_abort_compress(&sCInfo);
5154 : jpeg_destroy_compress(&sCInfo);
5155 :
5156 : return true;
5157 : }
5158 : #endif
5159 :
5160 1075 : void GDALJPGDriver::InitializeMetadata()
5161 : {
5162 1075 : if (m_bMetadataInitialized)
5163 863 : return;
5164 212 : m_bMetadataInitialized = true;
5165 :
5166 : {
5167 : CPLString osCreationOptions =
5168 : "<CreationOptionList>\n"
5169 : " <Option name='PROGRESSIVE' type='boolean' description='whether "
5170 : "to generate a progressive JPEG' default='NO'/>\n"
5171 : " <Option name='QUALITY' type='int' description='good=100, "
5172 : "bad=1, default=75'/>\n"
5173 : " <Option name='LOSSLESS_COPY' type='string-select' "
5174 : "description='Whether conversion should be lossless' "
5175 : "default='AUTO'>"
5176 : " <Value>AUTO</Value>"
5177 : " <Value>YES</Value>"
5178 : " <Value>NO</Value>"
5179 : " </Option>"
5180 : " <Option name='WORLDFILE' type='boolean' description='whether "
5181 : "to generate a worldfile' default='NO'/>\n"
5182 : " <Option name='INTERNAL_MASK' type='boolean' "
5183 : "description='whether to generate a validity mask' "
5184 424 : "default='YES'/>\n";
5185 : #ifndef C_ARITH_CODING_SUPPORTED
5186 : if (GDALJPEGIsArithmeticCodingAvailable())
5187 : #endif
5188 : {
5189 : osCreationOptions += " <Option name='ARITHMETIC' type='boolean' "
5190 : "description='whether to use arithmetic "
5191 212 : "encoding' default='NO'/>\n";
5192 : }
5193 : osCreationOptions +=
5194 : #if JPEG_LIB_VERSION_MAJOR >= 8 && \
5195 : (JPEG_LIB_VERSION_MAJOR > 8 || JPEG_LIB_VERSION_MINOR >= 3)
5196 : " <Option name='BLOCK' type='int' description='between 1 and "
5197 : "16'/>\n"
5198 : #endif
5199 : #if JPEG_LIB_VERSION_MAJOR >= 9
5200 : " <Option name='COLOR_TRANSFORM' type='string-select'>\n"
5201 : " <Value>RGB</Value>"
5202 : " <Value>RGB1</Value>"
5203 : " </Option>"
5204 : #endif
5205 : " <Option name='COMMENT' description='Comment' type='string'/>\n"
5206 : " <Option name='SOURCE_ICC_PROFILE' description='ICC profile "
5207 : "encoded in Base64' type='string'/>\n"
5208 : " <Option name='EXIF_THUMBNAIL' type='boolean' "
5209 : "description='whether to generate an EXIF thumbnail(overview). By "
5210 : "default its max dimension will be 128' default='NO'/>\n"
5211 : " <Option name='THUMBNAIL_WIDTH' type='int' description='Forced "
5212 : "thumbnail width' min='32' max='512'/>\n"
5213 : " <Option name='THUMBNAIL_HEIGHT' type='int' description='Forced "
5214 : "thumbnail height' min='32' max='512'/>\n"
5215 : " <Option name='WRITE_EXIF_METADATA' type='boolean' "
5216 : "description='whether to write EXIF_ metadata in a EXIF segment' "
5217 : "default='YES'/>"
5218 212 : "</CreationOptionList>\n";
5219 212 : SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, osCreationOptions);
5220 : }
5221 : }
5222 :
5223 2033 : void GDALRegister_JPEG()
5224 :
5225 : {
5226 2033 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
5227 283 : return;
5228 :
5229 1750 : GDALDriver *poDriver = new GDALJPGDriver();
5230 1750 : JPEGDriverSetCommonMetadata(poDriver);
5231 :
5232 1750 : poDriver->pfnOpen = JPGDatasetCommon::Open;
5233 1750 : poDriver->pfnCreateCopy = JPGDataset::CreateCopy;
5234 :
5235 1750 : GetGDALDriverManager()->RegisterDriver(poDriver);
5236 : }
5237 : #endif
|