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