Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GeoTIFF Driver
4 : * Purpose: Specialized copy of JPEG content into TIFF.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2012, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 :
15 : #include "gt_jpeg_copy.h"
16 :
17 : #include "cpl_vsi.h"
18 :
19 : #if defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG)
20 : #include "vrt/vrtdataset.h"
21 : #endif
22 :
23 : #include <algorithm>
24 :
25 : // Note: JPEG_DIRECT_COPY is not defined by default, because it is mainly
26 : // useful for debugging purposes.
27 :
28 : #if defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG)
29 :
30 : /************************************************************************/
31 : /* GetUnderlyingDataset() */
32 : /************************************************************************/
33 :
34 1899 : static GDALDataset *GetUnderlyingDataset(GDALDataset *poSrcDS)
35 : {
36 : // Test if we can directly copy original JPEG content if available.
37 1899 : if (auto poVRTDS = dynamic_cast<VRTDataset *>(poSrcDS))
38 : {
39 1144 : poSrcDS = poVRTDS->GetSingleSimpleSource();
40 : }
41 :
42 1899 : return poSrcDS;
43 : }
44 :
45 : #endif // defined(JPEG_DIRECT_COPY) || defined(HAVE_LIBJPEG)
46 :
47 : #ifdef JPEG_DIRECT_COPY
48 :
49 : /************************************************************************/
50 : /* IsBaselineDCTJPEG() */
51 : /************************************************************************/
52 :
53 : static bool IsBaselineDCTJPEG(VSILFILE *fp)
54 : {
55 : GByte abyBuf[4] = {0};
56 :
57 : if (VSIFReadL(abyBuf, 1, 2, fp) != 2 || abyBuf[0] != 0xff ||
58 : abyBuf[1] != 0xd8)
59 : {
60 : CPLError(CE_Failure, CPLE_AppDefined, "Not a valid JPEG file");
61 : return false;
62 : }
63 :
64 : int nOffset = 2;
65 : while (true)
66 : {
67 : VSIFSeekL(fp, nOffset, SEEK_SET);
68 : if (VSIFReadL(abyBuf, 1, 4, fp) != 4 || abyBuf[0] != 0xFF)
69 : {
70 : CPLError(CE_Failure, CPLE_AppDefined, "Not a valid JPEG file");
71 : return false;
72 : }
73 :
74 : const int nMarker = abyBuf[1];
75 :
76 : // Start of Frame 0 = Baseline DCT.
77 : if (nMarker == 0xC0)
78 : return true;
79 :
80 : if (nMarker == 0xD9)
81 : return false;
82 :
83 : if (nMarker == 0xF7 || // JPEG Extension 7, JPEG-LS
84 : nMarker == 0xF8 || // JPEG Extension 8, JPEG-LS Extension.
85 : // Other Start of Frames that we don't want to support.
86 : (nMarker >= 0xC1 && nMarker <= 0xCF))
87 : {
88 : CPLError(CE_Failure, CPLE_AppDefined,
89 : "Unsupported type of JPEG file for JPEG_DIRECT_COPY mode");
90 : return false;
91 : }
92 :
93 : nOffset += 2 + abyBuf[2] * 256 + abyBuf[3];
94 : }
95 : }
96 :
97 : /************************************************************************/
98 : /* GTIFF_CanDirectCopyFromJPEG() */
99 : /************************************************************************/
100 :
101 : int GTIFF_CanDirectCopyFromJPEG(GDALDataset *poSrcDS,
102 : char **&papszCreateOptions)
103 : {
104 : poSrcDS = GetUnderlyingDataset(poSrcDS);
105 : if (poSrcDS == NULL)
106 : return FALSE;
107 : if (poSrcDS->GetDriver() == NULL)
108 : return FALSE;
109 : if (!EQUAL(GDALGetDriverShortName(poSrcDS->GetDriver()), "JPEG"))
110 : return FALSE;
111 :
112 : const char *pszCompress = CSLFetchNameValue(papszCreateOptions, "COMPRESS");
113 : if (pszCompress != NULL && !EQUAL(pszCompress, "JPEG"))
114 : return FALSE;
115 :
116 : const char *pszSrcColorSpace =
117 : poSrcDS->GetMetadataItem("SOURCE_COLOR_SPACE", "IMAGE_STRUCTURE");
118 : if (pszSrcColorSpace != NULL &&
119 : (EQUAL(pszSrcColorSpace, "CMYK") || EQUAL(pszSrcColorSpace, "YCbCrK")))
120 : return FALSE;
121 :
122 : bool bJPEGDirectCopy = false;
123 :
124 : VSILFILE *fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
125 : if (fpJPEG && IsBaselineDCTJPEG(fpJPEG))
126 : {
127 : bJPEGDirectCopy = true;
128 :
129 : if (pszCompress == NULL)
130 : papszCreateOptions =
131 : CSLSetNameValue(papszCreateOptions, "COMPRESS", "JPEG");
132 :
133 : papszCreateOptions =
134 : CSLSetNameValue(papszCreateOptions, "BLOCKXSIZE", NULL);
135 : papszCreateOptions =
136 : CSLSetNameValue(papszCreateOptions, "BLOCKYSIZE",
137 : CPLSPrintf("%d", poSrcDS->GetRasterYSize()));
138 :
139 : if (pszSrcColorSpace != NULL && EQUAL(pszSrcColorSpace, "YCbCr"))
140 : papszCreateOptions =
141 : CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "YCBCR");
142 : else
143 : papszCreateOptions =
144 : CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", NULL);
145 :
146 : if (poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte)
147 : papszCreateOptions =
148 : CSLSetNameValue(papszCreateOptions, "NBITS", "12");
149 : else
150 : papszCreateOptions =
151 : CSLSetNameValue(papszCreateOptions, "NBITS", NULL);
152 :
153 : papszCreateOptions = CSLSetNameValue(papszCreateOptions, "TILED", NULL);
154 : papszCreateOptions =
155 : CSLSetNameValue(papszCreateOptions, "JPEG_QUALITY", NULL);
156 : }
157 : if (fpJPEG)
158 : {
159 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpJPEG));
160 : }
161 :
162 : return bJPEGDirectCopy;
163 : }
164 :
165 : /************************************************************************/
166 : /* GTIFF_DirectCopyFromJPEG() */
167 : /************************************************************************/
168 :
169 : CPLErr GTIFF_DirectCopyFromJPEG(GDALDataset *poDS, GDALDataset *poSrcDS,
170 : GDALProgressFunc pfnProgress,
171 : void *pProgressData,
172 : bool &bShouldFallbackToNormalCopyIfFail)
173 : {
174 : bShouldFallbackToNormalCopyIfFail = true;
175 :
176 : poSrcDS = GetUnderlyingDataset(poSrcDS);
177 : if (poSrcDS == NULL)
178 : return CE_Failure;
179 :
180 : VSILFILE *fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
181 : if (fpJPEG == NULL)
182 : return CE_Failure;
183 :
184 : CPLErr eErr = CE_None;
185 :
186 : VSIFSeekL(fpJPEG, 0, SEEK_END);
187 : tmsize_t nSize = static_cast<tmsize_t>(VSIFTellL(fpJPEG));
188 : VSIFSeekL(fpJPEG, 0, SEEK_SET);
189 :
190 : void *pabyJPEGData = VSIMalloc(nSize);
191 : if (pabyJPEGData == NULL)
192 : {
193 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpJPEG));
194 : return CE_Failure;
195 : }
196 :
197 : if (pabyJPEGData != NULL && static_cast<tmsize_t>(VSIFReadL(
198 : pabyJPEGData, 1, nSize, fpJPEG)) == nSize)
199 : {
200 : bShouldFallbackToNormalCopyIfFail = false;
201 :
202 : TIFF *hTIFF = (TIFF *)poDS->GetInternalHandle(NULL);
203 : if (TIFFWriteRawStrip(hTIFF, 0, pabyJPEGData, nSize) != nSize)
204 : eErr = CE_Failure;
205 :
206 : if (!pfnProgress(1.0, NULL, pProgressData))
207 : eErr = CE_Failure;
208 : }
209 : else
210 : {
211 : eErr = CE_Failure;
212 : }
213 :
214 : VSIFree(pabyJPEGData);
215 : if (VSIFCloseL(fpJPEG) != 0)
216 : eErr = CE_Failure;
217 :
218 : return eErr;
219 : }
220 :
221 : #endif // JPEG_DIRECT_COPY
222 :
223 : #ifdef HAVE_LIBJPEG
224 :
225 : #define jpeg_vsiio_src GTIFF_jpeg_vsiio_src
226 : #define jpeg_vsiio_dest GTIFF_jpeg_vsiio_dest
227 : #include "../jpeg/vsidataio.h"
228 : #include "../jpeg/vsidataio.cpp"
229 :
230 : #include <setjmp.h>
231 :
232 : /*
233 : * We are using width_in_blocks which is supposed to be private to
234 : * libjpeg. Unfortunately, the libjpeg delivered with Cygwin has
235 : * renamed this member to width_in_data_units. Since the header has
236 : * also renamed a define, use that unique define name in order to
237 : * detect the problem header and adjust to suit.
238 : */
239 : #if defined(D_MAX_DATA_UNITS_IN_MCU)
240 : #define width_in_blocks width_in_data_units
241 : #endif
242 :
243 : #ifdef EXPECTED_JPEG_LIB_VERSION
244 : #if EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
245 : #error EXPECTED_JPEG_LIB_VERSION != JPEG_LIB_VERSION
246 : #endif
247 : #endif
248 :
249 : /************************************************************************/
250 : /* GTIFF_CanCopyFromJPEG() */
251 : /************************************************************************/
252 :
253 1875 : int GTIFF_CanCopyFromJPEG(GDALDataset *poSrcDS, char **&papszCreateOptions)
254 : {
255 1875 : poSrcDS = GetUnderlyingDataset(poSrcDS);
256 1875 : if (poSrcDS == nullptr)
257 1044 : return FALSE;
258 831 : if (poSrcDS->GetDriver() == nullptr)
259 67 : return FALSE;
260 764 : if (!EQUAL(GDALGetDriverShortName(poSrcDS->GetDriver()), "JPEG"))
261 750 : return FALSE;
262 :
263 14 : const char *pszCompress = CSLFetchNameValue(papszCreateOptions, "COMPRESS");
264 14 : if (pszCompress == nullptr || !EQUAL(pszCompress, "JPEG"))
265 1 : return FALSE;
266 :
267 : const int nBlockXSize =
268 13 : atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKXSIZE", "0"));
269 : const int nBlockYSize =
270 13 : atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKYSIZE", "0"));
271 13 : int nMCUSize = 8;
272 : const char *pszSrcColorSpace =
273 13 : poSrcDS->GetMetadataItem("SOURCE_COLOR_SPACE", "IMAGE_STRUCTURE");
274 13 : if (pszSrcColorSpace != nullptr && EQUAL(pszSrcColorSpace, "YCbCr"))
275 5 : nMCUSize = 16;
276 :
277 13 : const int nXSize = poSrcDS->GetRasterXSize();
278 13 : const int nYSize = poSrcDS->GetRasterYSize();
279 13 : const int nBands = poSrcDS->GetRasterCount();
280 :
281 : const char *pszPhotometric =
282 13 : CSLFetchNameValue(papszCreateOptions, "PHOTOMETRIC");
283 :
284 : const bool bCompatiblePhotometric =
285 2 : pszPhotometric == nullptr ||
286 2 : (nMCUSize == 16 && EQUAL(pszPhotometric, "YCbCr")) ||
287 2 : (nMCUSize == 8 && nBands == 4 &&
288 2 : poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_CyanBand &&
289 2 : poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
290 2 : GCI_MagentaBand &&
291 2 : poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
292 2 : GCI_YellowBand &&
293 2 : poSrcDS->GetRasterBand(4)->GetColorInterpretation() ==
294 0 : GCI_BlackBand) ||
295 15 : (nMCUSize == 8 && EQUAL(pszPhotometric, "RGB") && nBands == 3) ||
296 0 : (nMCUSize == 8 && EQUAL(pszPhotometric, "MINISBLACK") && nBands == 1);
297 13 : if (!bCompatiblePhotometric)
298 0 : return FALSE;
299 :
300 3 : if (nBands == 4 && pszPhotometric == nullptr &&
301 1 : poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_CyanBand &&
302 1 : poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
303 1 : GCI_MagentaBand &&
304 17 : poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_YellowBand &&
305 1 : poSrcDS->GetRasterBand(4)->GetColorInterpretation() == GCI_BlackBand)
306 : {
307 1 : papszCreateOptions =
308 1 : CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "CMYK");
309 : }
310 :
311 : const char *pszInterleave =
312 13 : CSLFetchNameValue(papszCreateOptions, "INTERLEAVE");
313 :
314 13 : const bool bCompatibleInterleave =
315 2 : pszInterleave == nullptr ||
316 15 : (nBands > 1 && EQUAL(pszInterleave, "PIXEL")) || nBands == 1;
317 13 : if (!bCompatibleInterleave)
318 1 : return FALSE;
319 :
320 : // We don't want to apply lossy JPEG on a source using lossless JPEG !
321 24 : const char *pszReversibility = poSrcDS->GetMetadataItem(
322 12 : "COMPRESSION_REVERSIBILITY", "IMAGE_STRUCTURE");
323 12 : if (pszReversibility && EQUAL(pszReversibility, "LOSSLESS"))
324 0 : return FALSE;
325 :
326 12 : if ((nBlockXSize == nXSize || (nBlockXSize % nMCUSize) == 0) &&
327 9 : (nBlockYSize == nYSize || (nBlockYSize % nMCUSize) == 0) &&
328 12 : poSrcDS->GetRasterBand(1)->GetRasterDataType() == GDT_Byte &&
329 36 : CSLFetchNameValue(papszCreateOptions, "NBITS") == nullptr &&
330 12 : CSLFetchNameValue(papszCreateOptions, "JPEG_QUALITY") == nullptr)
331 : {
332 12 : if (nMCUSize == 16 && pszPhotometric == nullptr)
333 4 : papszCreateOptions =
334 4 : CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "YCBCR");
335 12 : return TRUE;
336 : }
337 :
338 0 : return FALSE;
339 : }
340 :
341 : /************************************************************************/
342 : /* GTIFF_ErrorExitJPEG() */
343 : /************************************************************************/
344 :
345 0 : static void GTIFF_ErrorExitJPEG(j_common_ptr cinfo)
346 : {
347 0 : jmp_buf *setjmp_buffer = static_cast<jmp_buf *>(cinfo->client_data);
348 0 : char buffer[JMSG_LENGTH_MAX] = {'\0'};
349 :
350 : // Create the message.
351 0 : (*cinfo->err->format_message)(cinfo, buffer);
352 :
353 0 : CPLError(CE_Failure, CPLE_AppDefined, "libjpeg: %s", buffer);
354 :
355 : // Return control to the setjmp point.
356 0 : longjmp(*setjmp_buffer, 1);
357 : }
358 :
359 : /************************************************************************/
360 : /* GTIFF_Set_TIFFTAG_JPEGTABLES() */
361 : /************************************************************************/
362 :
363 12 : static void GTIFF_Set_TIFFTAG_JPEGTABLES(TIFF *hTIFF,
364 : jpeg_compress_struct &sCInfo)
365 : {
366 24 : const std::string osTmpFilename(VSIMemGenerateHiddenFilename("tables"));
367 12 : VSILFILE *fpTABLES = VSIFOpenL(osTmpFilename.c_str(), "wb+");
368 :
369 12 : uint16_t nPhotometric = 0;
370 12 : TIFFGetField(hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric);
371 :
372 12 : jpeg_vsiio_dest(&sCInfo, fpTABLES);
373 :
374 : // Avoid unnecessary tables to be emitted.
375 12 : if (nPhotometric != PHOTOMETRIC_YCBCR)
376 : {
377 8 : JQUANT_TBL *qtbl = sCInfo.quant_tbl_ptrs[1];
378 8 : if (qtbl != nullptr)
379 8 : qtbl->sent_table = TRUE;
380 8 : JHUFF_TBL *htbl = sCInfo.dc_huff_tbl_ptrs[1];
381 8 : if (htbl != nullptr)
382 8 : htbl->sent_table = TRUE;
383 8 : htbl = sCInfo.ac_huff_tbl_ptrs[1];
384 8 : if (htbl != nullptr)
385 8 : htbl->sent_table = TRUE;
386 : }
387 12 : jpeg_write_tables(&sCInfo);
388 :
389 12 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpTABLES));
390 :
391 12 : vsi_l_offset nSizeTables = 0;
392 : GByte *pabyJPEGTablesData =
393 12 : VSIGetMemFileBuffer(osTmpFilename.c_str(), &nSizeTables, FALSE);
394 12 : TIFFSetField(hTIFF, TIFFTAG_JPEGTABLES, static_cast<int>(nSizeTables),
395 : pabyJPEGTablesData);
396 :
397 12 : VSIUnlink(osTmpFilename.c_str());
398 12 : }
399 :
400 : /************************************************************************/
401 : /* GTIFF_CopyFromJPEG_WriteAdditionalTags() */
402 : /************************************************************************/
403 :
404 12 : CPLErr GTIFF_CopyFromJPEG_WriteAdditionalTags(TIFF *hTIFF, GDALDataset *poSrcDS)
405 : {
406 12 : poSrcDS = GetUnderlyingDataset(poSrcDS);
407 12 : if (poSrcDS == nullptr)
408 0 : return CE_Failure;
409 :
410 : /* -------------------------------------------------------------------- */
411 : /* Write TIFFTAG_JPEGTABLES */
412 : /* -------------------------------------------------------------------- */
413 :
414 12 : VSILFILE *fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
415 12 : if (fpJPEG == nullptr)
416 0 : return CE_Failure;
417 :
418 : struct jpeg_error_mgr sJErr;
419 : struct jpeg_decompress_struct sDInfo;
420 : jmp_buf setjmp_buffer;
421 :
422 12 : volatile bool bCallDestroyDecompress = false;
423 12 : volatile bool bCallDestroyCompress = false;
424 :
425 : struct jpeg_compress_struct sCInfo;
426 :
427 12 : if (setjmp(setjmp_buffer))
428 : {
429 0 : if (bCallDestroyCompress)
430 : {
431 0 : jpeg_abort_compress(&sCInfo);
432 0 : jpeg_destroy_compress(&sCInfo);
433 : }
434 0 : if (bCallDestroyDecompress)
435 : {
436 0 : jpeg_abort_decompress(&sDInfo);
437 0 : jpeg_destroy_decompress(&sDInfo);
438 : }
439 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpJPEG));
440 0 : return CE_Failure;
441 : }
442 :
443 12 : sDInfo.err = jpeg_std_error(&sJErr);
444 12 : sJErr.error_exit = GTIFF_ErrorExitJPEG;
445 12 : sDInfo.client_data = &setjmp_buffer;
446 :
447 12 : bCallDestroyDecompress = true;
448 12 : jpeg_CreateDecompress(&sDInfo, JPEG_LIB_VERSION, sizeof(sDInfo));
449 :
450 12 : jpeg_vsiio_src(&sDInfo, fpJPEG);
451 12 : jpeg_read_header(&sDInfo, TRUE);
452 :
453 12 : sCInfo.err = jpeg_std_error(&sJErr);
454 12 : sJErr.error_exit = GTIFF_ErrorExitJPEG;
455 12 : sCInfo.client_data = &setjmp_buffer;
456 :
457 12 : jpeg_CreateCompress(&sCInfo, JPEG_LIB_VERSION, sizeof(sCInfo));
458 12 : bCallDestroyCompress = true;
459 12 : jpeg_copy_critical_parameters(&sDInfo, &sCInfo);
460 12 : GTIFF_Set_TIFFTAG_JPEGTABLES(hTIFF, sCInfo);
461 12 : bCallDestroyCompress = false;
462 12 : jpeg_abort_compress(&sCInfo);
463 12 : jpeg_destroy_compress(&sCInfo);
464 12 : CPL_IGNORE_RET_VAL(bCallDestroyCompress);
465 :
466 : /* -------------------------------------------------------------------- */
467 : /* Write TIFFTAG_REFERENCEBLACKWHITE if needed. */
468 : /* -------------------------------------------------------------------- */
469 :
470 12 : uint16_t nPhotometric = 0;
471 12 : if (!TIFFGetField(hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric)))
472 0 : nPhotometric = PHOTOMETRIC_MINISBLACK;
473 :
474 12 : uint16_t nBitsPerSample = 0;
475 12 : if (!TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &(nBitsPerSample)))
476 0 : nBitsPerSample = 1;
477 :
478 12 : if (nPhotometric == PHOTOMETRIC_YCBCR)
479 : {
480 : /*
481 : * A ReferenceBlackWhite field *must* be present since the
482 : * default value is inappropriate for YCbCr. Fill in the
483 : * proper value if application didn't set it.
484 : */
485 4 : float *ref = nullptr;
486 4 : if (!TIFFGetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE, &ref))
487 : {
488 0 : long top = 1L << nBitsPerSample;
489 0 : float refbw[6] = {0.0};
490 0 : refbw[1] = static_cast<float>(top - 1L);
491 0 : refbw[2] = static_cast<float>(top >> 1);
492 0 : refbw[3] = refbw[1];
493 0 : refbw[4] = refbw[2];
494 0 : refbw[5] = refbw[1];
495 0 : TIFFSetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE, refbw);
496 : }
497 : }
498 :
499 : /* -------------------------------------------------------------------- */
500 : /* Write TIFFTAG_YCBCRSUBSAMPLING if needed. */
501 : /* -------------------------------------------------------------------- */
502 :
503 12 : if (nPhotometric == PHOTOMETRIC_YCBCR && sDInfo.num_components == 3)
504 : {
505 4 : if ((sDInfo.comp_info[0].h_samp_factor == 1 ||
506 4 : sDInfo.comp_info[0].h_samp_factor == 2) &&
507 4 : (sDInfo.comp_info[0].v_samp_factor == 1 ||
508 4 : sDInfo.comp_info[0].v_samp_factor == 2) &&
509 4 : sDInfo.comp_info[1].h_samp_factor == 1 &&
510 4 : sDInfo.comp_info[1].v_samp_factor == 1 &&
511 4 : sDInfo.comp_info[2].h_samp_factor == 1 &&
512 4 : sDInfo.comp_info[2].v_samp_factor == 1)
513 : {
514 4 : TIFFSetField(hTIFF, TIFFTAG_YCBCRSUBSAMPLING,
515 4 : sDInfo.comp_info[0].h_samp_factor,
516 4 : sDInfo.comp_info[0].v_samp_factor);
517 : }
518 : else
519 : {
520 0 : CPLDebug("GTiff", "Unusual sampling factors. "
521 : "TIFFTAG_YCBCRSUBSAMPLING not written.");
522 : }
523 : }
524 :
525 : /* -------------------------------------------------------------------- */
526 : /* Cleanup. */
527 : /* -------------------------------------------------------------------- */
528 :
529 12 : bCallDestroyDecompress = false;
530 12 : jpeg_abort_decompress(&sDInfo);
531 12 : jpeg_destroy_decompress(&sDInfo);
532 12 : CPL_IGNORE_RET_VAL(bCallDestroyDecompress);
533 :
534 12 : if (VSIFCloseL(fpJPEG) != 0)
535 0 : return CE_Failure;
536 :
537 12 : return CE_None;
538 : }
539 :
540 : /************************************************************************/
541 : /* GTIFF_CopyBlockFromJPEG() */
542 : /************************************************************************/
543 :
544 : typedef struct
545 : {
546 : TIFF *hTIFF;
547 : jpeg_decompress_struct *psDInfo;
548 : int iX;
549 : int iY;
550 : int nXBlocks;
551 : int nXSize;
552 : int nYSize;
553 : int nBlockXSize;
554 : int nBlockYSize;
555 : int iMCU_sample_width;
556 : int iMCU_sample_height;
557 : jvirt_barray_ptr *pSrcCoeffs;
558 : } GTIFF_CopyBlockFromJPEGArgs;
559 :
560 45 : static CPLErr GTIFF_CopyBlockFromJPEG(GTIFF_CopyBlockFromJPEGArgs *psArgs)
561 : {
562 : const CPLString osTmpFilename(
563 90 : VSIMemGenerateHiddenFilename("GTIFF_CopyBlockFromJPEG.tif"));
564 45 : VSILFILE *fpMEM = VSIFOpenL(osTmpFilename.c_str(), "wb+");
565 :
566 : /* -------------------------------------------------------------------- */
567 : /* Initialization of the compressor */
568 : /* -------------------------------------------------------------------- */
569 : jmp_buf setjmp_buffer;
570 45 : if (setjmp(setjmp_buffer))
571 : {
572 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpMEM));
573 0 : VSIUnlink(osTmpFilename.c_str());
574 0 : return CE_Failure;
575 : }
576 :
577 45 : TIFF *hTIFF = psArgs->hTIFF;
578 45 : jpeg_decompress_struct *psDInfo = psArgs->psDInfo;
579 45 : const int iX = psArgs->iX;
580 45 : const int iY = psArgs->iY;
581 45 : const int nXBlocks = psArgs->nXBlocks;
582 45 : const int nXSize = psArgs->nXSize;
583 45 : const int nYSize = psArgs->nYSize;
584 45 : const int nBlockXSize = psArgs->nBlockXSize;
585 45 : const int nBlockYSize = psArgs->nBlockYSize;
586 45 : const int iMCU_sample_width = psArgs->iMCU_sample_width;
587 45 : const int iMCU_sample_height = psArgs->iMCU_sample_height;
588 45 : jvirt_barray_ptr *pSrcCoeffs = psArgs->pSrcCoeffs;
589 :
590 : struct jpeg_error_mgr sJErr;
591 : struct jpeg_compress_struct sCInfo;
592 45 : sCInfo.err = jpeg_std_error(&sJErr);
593 45 : sJErr.error_exit = GTIFF_ErrorExitJPEG;
594 45 : sCInfo.client_data = &setjmp_buffer;
595 :
596 : // Initialize destination compression parameters from source values.
597 45 : jpeg_CreateCompress(&sCInfo, JPEG_LIB_VERSION, sizeof(sCInfo));
598 45 : jpeg_copy_critical_parameters(psDInfo, &sCInfo);
599 :
600 : // Ensure libjpeg won't write any extraneous markers.
601 45 : sCInfo.write_JFIF_header = FALSE;
602 45 : sCInfo.write_Adobe_marker = FALSE;
603 :
604 : /* -------------------------------------------------------------------- */
605 : /* Allocated destination coefficient array */
606 : /* -------------------------------------------------------------------- */
607 45 : const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(hTIFF));
608 :
609 45 : int nJPEGWidth = nBlockXSize;
610 45 : int nJPEGHeight = nBlockYSize;
611 45 : if (!bIsTiled)
612 : {
613 37 : nJPEGWidth = std::min(nBlockXSize, nXSize - iX * nBlockXSize);
614 37 : nJPEGHeight = std::min(nBlockYSize, nYSize - iY * nBlockYSize);
615 : }
616 :
617 : // Code partially derived from libjpeg transupp.c.
618 :
619 : // Correct the destination's image dimensions as necessary.
620 : #if JPEG_LIB_VERSION >= 70
621 45 : sCInfo.jpeg_width = nJPEGWidth;
622 45 : sCInfo.jpeg_height = nJPEGHeight;
623 : #else
624 : sCInfo.image_width = nJPEGWidth;
625 : sCInfo.image_height = nJPEGHeight;
626 : #endif
627 :
628 : // Save x/y offsets measured in iMCUs.
629 45 : const int x_crop_offset = (iX * nBlockXSize) / iMCU_sample_width;
630 45 : const int y_crop_offset = (iY * nBlockYSize) / iMCU_sample_height;
631 :
632 : jvirt_barray_ptr *pDstCoeffs =
633 90 : static_cast<jvirt_barray_ptr *>((*sCInfo.mem->alloc_small)(
634 : reinterpret_cast<j_common_ptr>(&sCInfo), JPOOL_IMAGE,
635 45 : sizeof(jvirt_barray_ptr) * sCInfo.num_components));
636 :
637 168 : for (int ci = 0; ci < sCInfo.num_components; ci++)
638 : {
639 123 : jpeg_component_info *compptr = sCInfo.comp_info + ci;
640 : int h_samp_factor, v_samp_factor;
641 123 : if (sCInfo.num_components == 1)
642 : {
643 : // Force samp factors to 1x1 in this case.
644 9 : h_samp_factor = 1;
645 9 : v_samp_factor = 1;
646 : }
647 : else
648 : {
649 114 : h_samp_factor = compptr->h_samp_factor;
650 114 : v_samp_factor = compptr->v_samp_factor;
651 : }
652 123 : int width_in_iMCUs =
653 123 : (nJPEGWidth + iMCU_sample_width - 1) / iMCU_sample_width;
654 123 : int height_in_iMCUs =
655 123 : (nJPEGHeight + iMCU_sample_height - 1) / iMCU_sample_height;
656 123 : int nWidth_in_blocks = width_in_iMCUs * h_samp_factor;
657 123 : int nHeight_in_blocks = height_in_iMCUs * v_samp_factor;
658 123 : pDstCoeffs[ci] = (*sCInfo.mem->request_virt_barray)(
659 : reinterpret_cast<j_common_ptr>(&sCInfo), JPOOL_IMAGE, FALSE,
660 : nWidth_in_blocks, nHeight_in_blocks,
661 : static_cast<JDIMENSION>(v_samp_factor));
662 : }
663 :
664 45 : jpeg_vsiio_dest(&sCInfo, fpMEM);
665 :
666 : // Start compressor (note no image data is actually written here).
667 45 : jpeg_write_coefficients(&sCInfo, pDstCoeffs);
668 :
669 45 : jpeg_suppress_tables(&sCInfo, TRUE);
670 :
671 : // Must copy the right amount of data (the destination's image size)
672 : // starting at the given X and Y offsets in the source.
673 168 : for (int ci = 0; ci < sCInfo.num_components; ci++)
674 : {
675 123 : jpeg_component_info *compptr = sCInfo.comp_info + ci;
676 123 : const int x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
677 123 : const int y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
678 123 : const JDIMENSION nSrcWidthInBlocks =
679 123 : psDInfo->comp_info[ci].width_in_blocks;
680 123 : const JDIMENSION nSrcHeightInBlocks =
681 123 : psDInfo->comp_info[ci].height_in_blocks;
682 :
683 123 : JDIMENSION nXBlocksToCopy = compptr->width_in_blocks;
684 123 : if (x_crop_blocks + compptr->width_in_blocks > nSrcWidthInBlocks)
685 8 : nXBlocksToCopy = nSrcWidthInBlocks - x_crop_blocks;
686 :
687 590 : for (JDIMENSION dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
688 467 : dst_blk_y += compptr->v_samp_factor)
689 : {
690 934 : JBLOCKARRAY dst_buffer = (*psDInfo->mem->access_virt_barray)(
691 467 : reinterpret_cast<j_common_ptr>(psDInfo), pDstCoeffs[ci],
692 467 : dst_blk_y, static_cast<JDIMENSION>(compptr->v_samp_factor),
693 : TRUE);
694 :
695 467 : int offset_y = 0;
696 467 : if (bIsTiled && dst_blk_y + y_crop_blocks + compptr->v_samp_factor >
697 : nSrcHeightInBlocks)
698 : {
699 94 : const int nYBlocks =
700 94 : static_cast<int>(nSrcHeightInBlocks) -
701 94 : static_cast<int>(dst_blk_y + y_crop_blocks);
702 94 : if (nYBlocks > 0)
703 : {
704 : JBLOCKARRAY src_buffer =
705 2 : (*psDInfo->mem->access_virt_barray)(
706 : reinterpret_cast<j_common_ptr>(psDInfo),
707 2 : pSrcCoeffs[ci], dst_blk_y + y_crop_blocks,
708 : static_cast<JDIMENSION>(1), FALSE);
709 4 : for (; offset_y < nYBlocks; offset_y++)
710 : {
711 2 : memcpy(dst_buffer[offset_y],
712 2 : src_buffer[offset_y] + x_crop_blocks,
713 2 : nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF)));
714 2 : if (nXBlocksToCopy < compptr->width_in_blocks)
715 : {
716 1 : memset(dst_buffer[offset_y] + nXBlocksToCopy, 0,
717 1 : (compptr->width_in_blocks - nXBlocksToCopy) *
718 : (DCTSIZE2 * sizeof(JCOEF)));
719 : }
720 : }
721 : }
722 :
723 218 : for (; offset_y < compptr->v_samp_factor; offset_y++)
724 : {
725 124 : memset(dst_buffer[offset_y], 0,
726 124 : compptr->width_in_blocks * DCTSIZE2 * sizeof(JCOEF));
727 94 : }
728 : }
729 : else
730 : {
731 373 : JBLOCKARRAY src_buffer = (*psDInfo->mem->access_virt_barray)(
732 373 : reinterpret_cast<j_common_ptr>(psDInfo), pSrcCoeffs[ci],
733 373 : dst_blk_y + y_crop_blocks,
734 373 : static_cast<JDIMENSION>(compptr->v_samp_factor), FALSE);
735 829 : for (; offset_y < compptr->v_samp_factor; offset_y++)
736 : {
737 456 : memcpy(dst_buffer[offset_y],
738 456 : src_buffer[offset_y] + x_crop_blocks,
739 456 : nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF)));
740 456 : if (nXBlocksToCopy < compptr->width_in_blocks)
741 : {
742 69 : memset(dst_buffer[offset_y] + nXBlocksToCopy, 0,
743 69 : (compptr->width_in_blocks - nXBlocksToCopy) *
744 : (DCTSIZE2 * sizeof(JCOEF)));
745 : }
746 : }
747 : }
748 : }
749 : }
750 :
751 45 : jpeg_finish_compress(&sCInfo);
752 45 : jpeg_destroy_compress(&sCInfo);
753 :
754 45 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpMEM));
755 :
756 : /* -------------------------------------------------------------------- */
757 : /* Write the JPEG content with libtiff raw API */
758 : /* -------------------------------------------------------------------- */
759 45 : vsi_l_offset nSize = 0;
760 : GByte *pabyJPEGData =
761 45 : VSIGetMemFileBuffer(osTmpFilename.c_str(), &nSize, FALSE);
762 :
763 45 : CPLErr eErr = CE_None;
764 :
765 45 : if (bIsTiled)
766 : {
767 8 : if (static_cast<vsi_l_offset>(
768 8 : TIFFWriteRawTile(hTIFF, iX + iY * nXBlocks, pabyJPEGData,
769 8 : static_cast<tmsize_t>(nSize))) != nSize)
770 0 : eErr = CE_Failure;
771 : }
772 : else
773 : {
774 37 : if (static_cast<vsi_l_offset>(
775 37 : TIFFWriteRawStrip(hTIFF, iX + iY * nXBlocks, pabyJPEGData,
776 37 : static_cast<tmsize_t>(nSize))) != nSize)
777 0 : eErr = CE_Failure;
778 : }
779 :
780 45 : VSIUnlink(osTmpFilename.c_str());
781 :
782 45 : return eErr;
783 : }
784 :
785 : /************************************************************************/
786 : /* GTIFF_CopyFromJPEG() */
787 : /************************************************************************/
788 :
789 12 : CPLErr GTIFF_CopyFromJPEG(GDALDataset *poDS, GDALDataset *poSrcDS,
790 : GDALProgressFunc pfnProgress, void *pProgressData,
791 : bool &bShouldFallbackToNormalCopyIfFail)
792 : {
793 12 : bShouldFallbackToNormalCopyIfFail = true;
794 :
795 12 : poSrcDS = GetUnderlyingDataset(poSrcDS);
796 12 : if (poSrcDS == nullptr)
797 0 : return CE_Failure;
798 :
799 12 : VSILFILE *fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
800 12 : if (fpJPEG == nullptr)
801 0 : return CE_Failure;
802 :
803 12 : CPLErr eErr = CE_None;
804 :
805 : /* -------------------------------------------------------------------- */
806 : /* Initialization of the decompressor */
807 : /* -------------------------------------------------------------------- */
808 : struct jpeg_error_mgr sJErr;
809 : struct jpeg_decompress_struct sDInfo;
810 12 : memset(&sDInfo, 0, sizeof(sDInfo));
811 : jmp_buf setjmp_buffer;
812 12 : if (setjmp(setjmp_buffer))
813 : {
814 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpJPEG));
815 0 : jpeg_destroy_decompress(&sDInfo);
816 0 : return CE_Failure;
817 : }
818 :
819 12 : sDInfo.err = jpeg_std_error(&sJErr);
820 12 : sJErr.error_exit = GTIFF_ErrorExitJPEG;
821 12 : sDInfo.client_data = &setjmp_buffer;
822 :
823 12 : jpeg_CreateDecompress(&sDInfo, JPEG_LIB_VERSION, sizeof(sDInfo));
824 :
825 : // This is to address bug related in ticket #1795.
826 12 : if (CPLGetConfigOption("JPEGMEM", nullptr) == nullptr)
827 : {
828 : // If the user doesn't provide a value for JPEGMEM, be sure that at
829 : // least 500 MB will be used before creating the temporary file.
830 12 : const long nMinMemory = 500 * 1024 * 1024;
831 12 : sDInfo.mem->max_memory_to_use =
832 12 : std::max(sDInfo.mem->max_memory_to_use, nMinMemory);
833 : }
834 :
835 12 : jpeg_vsiio_src(&sDInfo, fpJPEG);
836 12 : jpeg_read_header(&sDInfo, TRUE);
837 :
838 12 : jvirt_barray_ptr *pSrcCoeffs = jpeg_read_coefficients(&sDInfo);
839 :
840 : /* -------------------------------------------------------------------- */
841 : /* Compute MCU dimensions */
842 : /* -------------------------------------------------------------------- */
843 12 : int iMCU_sample_width = 8;
844 12 : int iMCU_sample_height = 8;
845 12 : if (sDInfo.num_components != 1)
846 : {
847 8 : iMCU_sample_width = sDInfo.max_h_samp_factor * 8;
848 8 : iMCU_sample_height = sDInfo.max_v_samp_factor * 8;
849 : }
850 :
851 : /* -------------------------------------------------------------------- */
852 : /* Get raster and block dimensions */
853 : /* -------------------------------------------------------------------- */
854 12 : int nBlockXSize = 0;
855 12 : int nBlockYSize = 0;
856 :
857 12 : const int nXSize = poDS->GetRasterXSize();
858 12 : const int nYSize = poDS->GetRasterYSize();
859 : // nBands = poDS->GetRasterCount();
860 :
861 : // Don't use the GDAL block dimensions because of the split-band
862 : // mechanism that can expose a pseudo one-line-strip whereas the
863 : // real layout is a single big strip.
864 :
865 12 : TIFF *hTIFF = static_cast<TIFF *>(poDS->GetInternalHandle(nullptr));
866 12 : if (TIFFIsTiled(hTIFF))
867 : {
868 2 : TIFFGetField(hTIFF, TIFFTAG_TILEWIDTH, &(nBlockXSize));
869 2 : TIFFGetField(hTIFF, TIFFTAG_TILELENGTH, &(nBlockYSize));
870 : }
871 : else
872 : {
873 10 : uint32_t nRowsPerStrip = 0;
874 10 : if (!TIFFGetField(hTIFF, TIFFTAG_ROWSPERSTRIP, &(nRowsPerStrip)))
875 : {
876 0 : CPLError(CE_Warning, CPLE_AppDefined,
877 : "RowsPerStrip not defined ... assuming all one strip.");
878 0 : nRowsPerStrip = nYSize; // Dummy value.
879 : }
880 :
881 : // If the rows per strip is larger than the file we will get
882 : // confused. libtiff internally will treat the rowsperstrip as
883 : // the image height and it is best if we do too. (#4468)
884 10 : if (nRowsPerStrip > static_cast<uint32_t>(nYSize))
885 0 : nRowsPerStrip = nYSize;
886 :
887 10 : nBlockXSize = nXSize;
888 10 : nBlockYSize = nRowsPerStrip;
889 : }
890 :
891 12 : const int nXBlocks = (nXSize + nBlockXSize - 1) / nBlockXSize;
892 12 : const int nYBlocks = (nYSize + nBlockYSize - 1) / nBlockYSize;
893 :
894 : /* -------------------------------------------------------------------- */
895 : /* Copy blocks. */
896 : /* -------------------------------------------------------------------- */
897 :
898 12 : bShouldFallbackToNormalCopyIfFail = false;
899 :
900 53 : for (int iY = 0; iY < nYBlocks && eErr == CE_None; iY++)
901 : {
902 86 : for (int iX = 0; iX < nXBlocks && eErr == CE_None; iX++)
903 : {
904 : GTIFF_CopyBlockFromJPEGArgs sArgs;
905 45 : sArgs.hTIFF = hTIFF;
906 45 : sArgs.psDInfo = &sDInfo;
907 45 : sArgs.iX = iX;
908 45 : sArgs.iY = iY;
909 45 : sArgs.nXBlocks = nXBlocks;
910 45 : sArgs.nXSize = nXSize;
911 45 : sArgs.nYSize = nYSize;
912 45 : sArgs.nBlockXSize = nBlockXSize;
913 45 : sArgs.nBlockYSize = nBlockYSize;
914 45 : sArgs.iMCU_sample_width = iMCU_sample_width;
915 45 : sArgs.iMCU_sample_height = iMCU_sample_height;
916 45 : sArgs.pSrcCoeffs = pSrcCoeffs;
917 :
918 45 : eErr = GTIFF_CopyBlockFromJPEG(&sArgs);
919 :
920 45 : if (!pfnProgress((iY * nXBlocks + iX + 1) * 1.0 /
921 45 : (nXBlocks * nYBlocks),
922 : nullptr, pProgressData))
923 0 : eErr = CE_Failure;
924 : }
925 : }
926 :
927 : /* -------------------------------------------------------------------- */
928 : /* Cleanup. */
929 : /* -------------------------------------------------------------------- */
930 :
931 12 : jpeg_finish_decompress(&sDInfo);
932 12 : jpeg_destroy_decompress(&sDInfo);
933 :
934 12 : if (VSIFCloseL(fpJPEG) != 0)
935 0 : eErr = CE_Failure;
936 :
937 12 : return eErr;
938 : }
939 :
940 : #endif // HAVE_LIBJPEG
|