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