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