Coverage Report

Created: 2026-11-12 08:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/gtiff/gt_jpeg_copy.cpp
Line
Count
Source
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
static GDALDataset *GetUnderlyingDataset(GDALDataset *poSrcDS)
35
854
{
36
    // Test if we can directly copy original JPEG content if available.
37
854
    if (auto poVRTDS = dynamic_cast<VRTDataset *>(poSrcDS))
38
707
    {
39
707
        poSrcDS = poVRTDS->GetSingleSimpleSource();
40
707
    }
41
42
854
    return poSrcDS;
43
854
}
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 = poSrcDS->GetMetadataItem(
117
        "SOURCE_COLOR_SPACE", GDAL_MDD_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_UInt8)
147
            papszCreateOptions =
148
                CSLSetNameValue(papszCreateOptions, GDALMD_NBITS, "12");
149
        else
150
            papszCreateOptions =
151
                CSLSetNameValue(papszCreateOptions, GDALMD_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("TIFF_HANDLE");
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
0
#define jpeg_vsiio_src GTIFF_jpeg_vsiio_src
226
0
#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
int GTIFF_CanCopyFromJPEG(GDALDataset *poSrcDS, char **&papszCreateOptions)
254
854
{
255
854
    poSrcDS = GetUnderlyingDataset(poSrcDS);
256
854
    if (poSrcDS == nullptr)
257
695
        return FALSE;
258
159
    if (poSrcDS->GetDriver() == nullptr)
259
0
        return FALSE;
260
159
    if (!EQUAL(GDALGetDriverShortName(poSrcDS->GetDriver()), "JPEG"))
261
159
        return FALSE;
262
263
0
    const char *pszCompress = CSLFetchNameValue(papszCreateOptions, "COMPRESS");
264
0
    if (pszCompress == nullptr || !EQUAL(pszCompress, "JPEG"))
265
0
        return FALSE;
266
267
0
    const int nBlockXSize =
268
0
        atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKXSIZE", "0"));
269
0
    const int nBlockYSize =
270
0
        atoi(CSLFetchNameValueDef(papszCreateOptions, "BLOCKYSIZE", "0"));
271
0
    int nMCUSize = 8;
272
0
    const char *pszSrcColorSpace = poSrcDS->GetMetadataItem(
273
0
        "SOURCE_COLOR_SPACE", GDAL_MDD_IMAGE_STRUCTURE);
274
0
    if (pszSrcColorSpace != nullptr && EQUAL(pszSrcColorSpace, "YCbCr"))
275
0
        nMCUSize = 16;
276
277
0
    const int nXSize = poSrcDS->GetRasterXSize();
278
0
    const int nYSize = poSrcDS->GetRasterYSize();
279
0
    const int nBands = poSrcDS->GetRasterCount();
280
281
0
    const char *pszPhotometric =
282
0
        CSLFetchNameValue(papszCreateOptions, "PHOTOMETRIC");
283
284
0
    const bool bCompatiblePhotometric =
285
0
        pszPhotometric == nullptr ||
286
0
        (nMCUSize == 16 && EQUAL(pszPhotometric, "YCbCr")) ||
287
0
        (nMCUSize == 8 && nBands == 4 &&
288
0
         poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_CyanBand &&
289
0
         poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
290
0
             GCI_MagentaBand &&
291
0
         poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
292
0
             GCI_YellowBand &&
293
0
         poSrcDS->GetRasterBand(4)->GetColorInterpretation() ==
294
0
             GCI_BlackBand) ||
295
0
        (nMCUSize == 8 && EQUAL(pszPhotometric, "RGB") && nBands == 3) ||
296
0
        (nMCUSize == 8 && EQUAL(pszPhotometric, "MINISBLACK") && nBands == 1);
297
0
    if (!bCompatiblePhotometric)
298
0
        return FALSE;
299
300
0
    if (nBands == 4 && pszPhotometric == nullptr &&
301
0
        poSrcDS->GetRasterBand(1)->GetColorInterpretation() == GCI_CyanBand &&
302
0
        poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
303
0
            GCI_MagentaBand &&
304
0
        poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_YellowBand &&
305
0
        poSrcDS->GetRasterBand(4)->GetColorInterpretation() == GCI_BlackBand)
306
0
    {
307
0
        papszCreateOptions =
308
0
            CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "CMYK");
309
0
    }
310
311
0
    const char *pszInterleave =
312
0
        CSLFetchNameValue(papszCreateOptions, GDALMD_INTERLEAVE);
313
314
0
    const bool bCompatibleInterleave =
315
0
        pszInterleave == nullptr ||
316
0
        (nBands > 1 && EQUAL(pszInterleave, "PIXEL")) || nBands == 1;
317
0
    if (!bCompatibleInterleave)
318
0
        return FALSE;
319
320
    // We don't want to apply lossy JPEG on a source using lossless JPEG !
321
0
    const char *pszReversibility = poSrcDS->GetMetadataItem(
322
0
        "COMPRESSION_REVERSIBILITY", GDAL_MDD_IMAGE_STRUCTURE);
323
0
    if (pszReversibility && EQUAL(pszReversibility, "LOSSLESS"))
324
0
        return FALSE;
325
326
0
    if ((nBlockXSize == nXSize || (nBlockXSize % nMCUSize) == 0) &&
327
0
        (nBlockYSize == nYSize || (nBlockYSize % nMCUSize) == 0) &&
328
0
        poSrcDS->GetRasterBand(1)->GetRasterDataType() == GDT_UInt8 &&
329
0
        CSLFetchNameValue(papszCreateOptions, GDALMD_NBITS) == nullptr &&
330
0
        CSLFetchNameValue(papszCreateOptions, "JPEG_QUALITY") == nullptr)
331
0
    {
332
0
        if (nMCUSize == 16 && pszPhotometric == nullptr)
333
0
            papszCreateOptions =
334
0
                CSLSetNameValue(papszCreateOptions, "PHOTOMETRIC", "YCBCR");
335
0
        return TRUE;
336
0
    }
337
338
0
    return FALSE;
339
0
}
340
341
/************************************************************************/
342
/*                        GTIFF_ErrorExitJPEG()                         */
343
/************************************************************************/
344
345
static void GTIFF_ErrorExitJPEG(j_common_ptr cinfo)
346
0
{
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
0
}
358
359
/************************************************************************/
360
/*                    GTIFF_Set_TIFFTAG_JPEGTABLES()                    */
361
/************************************************************************/
362
363
static void GTIFF_Set_TIFFTAG_JPEGTABLES(TIFF *hTIFF,
364
                                         jpeg_compress_struct &sCInfo)
365
0
{
366
0
    const std::string osTmpFilename(VSIMemGenerateHiddenFilename("tables"));
367
0
    VSILFILE *fpTABLES = VSIFOpenL(osTmpFilename.c_str(), "wb+");
368
369
0
    uint16_t nPhotometric = 0;
370
0
    TIFFGetField(hTIFF, TIFFTAG_PHOTOMETRIC, &nPhotometric);
371
372
0
    jpeg_vsiio_dest(&sCInfo, fpTABLES);
373
374
    // Avoid unnecessary tables to be emitted.
375
0
    if (nPhotometric != PHOTOMETRIC_YCBCR)
376
0
    {
377
0
        JQUANT_TBL *qtbl = sCInfo.quant_tbl_ptrs[1];
378
0
        if (qtbl != nullptr)
379
0
            qtbl->sent_table = TRUE;
380
0
        JHUFF_TBL *htbl = sCInfo.dc_huff_tbl_ptrs[1];
381
0
        if (htbl != nullptr)
382
0
            htbl->sent_table = TRUE;
383
0
        htbl = sCInfo.ac_huff_tbl_ptrs[1];
384
0
        if (htbl != nullptr)
385
0
            htbl->sent_table = TRUE;
386
0
    }
387
0
    jpeg_write_tables(&sCInfo);
388
389
0
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpTABLES));
390
391
0
    vsi_l_offset nSizeTables = 0;
392
0
    GByte *pabyJPEGTablesData =
393
0
        VSIGetMemFileBuffer(osTmpFilename.c_str(), &nSizeTables, FALSE);
394
0
    TIFFSetField(hTIFF, TIFFTAG_JPEGTABLES, static_cast<int>(nSizeTables),
395
0
                 pabyJPEGTablesData);
396
397
0
    VSIUnlink(osTmpFilename.c_str());
398
0
}
399
400
/************************************************************************/
401
/*               GTIFF_CopyFromJPEG_WriteAdditionalTags()               */
402
/************************************************************************/
403
404
CPLErr GTIFF_CopyFromJPEG_WriteAdditionalTags(TIFF *hTIFF, GDALDataset *poSrcDS)
405
0
{
406
0
    poSrcDS = GetUnderlyingDataset(poSrcDS);
407
0
    if (poSrcDS == nullptr)
408
0
        return CE_Failure;
409
410
    /* -------------------------------------------------------------------- */
411
    /*      Write TIFFTAG_JPEGTABLES                                        */
412
    /* -------------------------------------------------------------------- */
413
414
0
    VSILFILE *fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
415
0
    if (fpJPEG == nullptr)
416
0
        return CE_Failure;
417
418
0
    struct jpeg_error_mgr sJErr;
419
0
    struct jpeg_decompress_struct sDInfo;
420
0
    jmp_buf setjmp_buffer;
421
422
0
    volatile bool bCallDestroyDecompress = false;
423
0
    volatile bool bCallDestroyCompress = false;
424
425
0
    struct jpeg_compress_struct sCInfo;
426
427
0
    if (setjmp(setjmp_buffer))
428
0
    {
429
0
        if (bCallDestroyCompress)
430
0
        {
431
0
            jpeg_abort_compress(&sCInfo);
432
0
            jpeg_destroy_compress(&sCInfo);
433
0
        }
434
0
        if (bCallDestroyDecompress)
435
0
        {
436
0
            jpeg_abort_decompress(&sDInfo);
437
0
            jpeg_destroy_decompress(&sDInfo);
438
0
        }
439
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpJPEG));
440
0
        return CE_Failure;
441
0
    }
442
443
0
    sDInfo.err = jpeg_std_error(&sJErr);
444
0
    sJErr.error_exit = GTIFF_ErrorExitJPEG;
445
0
    sDInfo.client_data = &setjmp_buffer;
446
447
0
    bCallDestroyDecompress = true;
448
0
    jpeg_CreateDecompress(&sDInfo, JPEG_LIB_VERSION, sizeof(sDInfo));
449
450
0
    jpeg_vsiio_src(&sDInfo, fpJPEG);
451
0
    jpeg_read_header(&sDInfo, TRUE);
452
453
0
    sCInfo.err = jpeg_std_error(&sJErr);
454
0
    sJErr.error_exit = GTIFF_ErrorExitJPEG;
455
0
    sCInfo.client_data = &setjmp_buffer;
456
457
0
    jpeg_CreateCompress(&sCInfo, JPEG_LIB_VERSION, sizeof(sCInfo));
458
0
    bCallDestroyCompress = true;
459
0
    jpeg_copy_critical_parameters(&sDInfo, &sCInfo);
460
0
    GTIFF_Set_TIFFTAG_JPEGTABLES(hTIFF, sCInfo);
461
0
    bCallDestroyCompress = false;
462
0
    jpeg_abort_compress(&sCInfo);
463
0
    jpeg_destroy_compress(&sCInfo);
464
0
    CPL_IGNORE_RET_VAL(bCallDestroyCompress);
465
466
    /* -------------------------------------------------------------------- */
467
    /*      Write TIFFTAG_REFERENCEBLACKWHITE if needed.                    */
468
    /* -------------------------------------------------------------------- */
469
470
0
    uint16_t nPhotometric = 0;
471
0
    if (!TIFFGetField(hTIFF, TIFFTAG_PHOTOMETRIC, &(nPhotometric)))
472
0
        nPhotometric = PHOTOMETRIC_MINISBLACK;
473
474
0
    uint16_t nBitsPerSample = 0;
475
0
    if (!TIFFGetField(hTIFF, TIFFTAG_BITSPERSAMPLE, &(nBitsPerSample)))
476
0
        nBitsPerSample = 1;
477
478
0
    if (nPhotometric == PHOTOMETRIC_YCBCR)
479
0
    {
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
0
        float *ref = nullptr;
486
0
        if (!TIFFGetField(hTIFF, TIFFTAG_REFERENCEBLACKWHITE, &ref))
487
0
        {
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
0
        }
497
0
    }
498
499
    /* -------------------------------------------------------------------- */
500
    /*      Write TIFFTAG_YCBCRSUBSAMPLING if needed.                       */
501
    /* -------------------------------------------------------------------- */
502
503
0
    if (nPhotometric == PHOTOMETRIC_YCBCR && sDInfo.num_components == 3)
504
0
    {
505
0
        if ((sDInfo.comp_info[0].h_samp_factor == 1 ||
506
0
             sDInfo.comp_info[0].h_samp_factor == 2) &&
507
0
            (sDInfo.comp_info[0].v_samp_factor == 1 ||
508
0
             sDInfo.comp_info[0].v_samp_factor == 2) &&
509
0
            sDInfo.comp_info[1].h_samp_factor == 1 &&
510
0
            sDInfo.comp_info[1].v_samp_factor == 1 &&
511
0
            sDInfo.comp_info[2].h_samp_factor == 1 &&
512
0
            sDInfo.comp_info[2].v_samp_factor == 1)
513
0
        {
514
0
            TIFFSetField(hTIFF, TIFFTAG_YCBCRSUBSAMPLING,
515
0
                         sDInfo.comp_info[0].h_samp_factor,
516
0
                         sDInfo.comp_info[0].v_samp_factor);
517
0
        }
518
0
        else
519
0
        {
520
0
            CPLDebug("GTiff", "Unusual sampling factors. "
521
0
                              "TIFFTAG_YCBCRSUBSAMPLING not written.");
522
0
        }
523
0
    }
524
525
    /* -------------------------------------------------------------------- */
526
    /*      Cleanup.                                                        */
527
    /* -------------------------------------------------------------------- */
528
529
0
    bCallDestroyDecompress = false;
530
0
    jpeg_abort_decompress(&sDInfo);
531
0
    jpeg_destroy_decompress(&sDInfo);
532
0
    CPL_IGNORE_RET_VAL(bCallDestroyDecompress);
533
534
0
    if (VSIFCloseL(fpJPEG) != 0)
535
0
        return CE_Failure;
536
537
0
    return CE_None;
538
0
}
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
static CPLErr GTIFF_CopyBlockFromJPEG(GTIFF_CopyBlockFromJPEGArgs *psArgs)
561
0
{
562
0
    const CPLString osTmpFilename(
563
0
        VSIMemGenerateHiddenFilename("GTIFF_CopyBlockFromJPEG.tif"));
564
0
    VSILFILE *fpMEM = VSIFOpenL(osTmpFilename.c_str(), "wb+");
565
566
    /* -------------------------------------------------------------------- */
567
    /*      Initialization of the compressor                                */
568
    /* -------------------------------------------------------------------- */
569
0
    jmp_buf setjmp_buffer;
570
0
    if (setjmp(setjmp_buffer))
571
0
    {
572
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpMEM));
573
0
        VSIUnlink(osTmpFilename.c_str());
574
0
        return CE_Failure;
575
0
    }
576
577
0
    TIFF *hTIFF = psArgs->hTIFF;
578
0
    jpeg_decompress_struct *psDInfo = psArgs->psDInfo;
579
0
    const int iX = psArgs->iX;
580
0
    const int iY = psArgs->iY;
581
0
    const int nXBlocks = psArgs->nXBlocks;
582
0
    const int nXSize = psArgs->nXSize;
583
0
    const int nYSize = psArgs->nYSize;
584
0
    const int nBlockXSize = psArgs->nBlockXSize;
585
0
    const int nBlockYSize = psArgs->nBlockYSize;
586
0
    const int iMCU_sample_width = psArgs->iMCU_sample_width;
587
0
    const int iMCU_sample_height = psArgs->iMCU_sample_height;
588
0
    jvirt_barray_ptr *pSrcCoeffs = psArgs->pSrcCoeffs;
589
590
0
    struct jpeg_error_mgr sJErr;
591
0
    struct jpeg_compress_struct sCInfo;
592
0
    sCInfo.err = jpeg_std_error(&sJErr);
593
0
    sJErr.error_exit = GTIFF_ErrorExitJPEG;
594
0
    sCInfo.client_data = &setjmp_buffer;
595
596
    // Initialize destination compression parameters from source values.
597
0
    jpeg_CreateCompress(&sCInfo, JPEG_LIB_VERSION, sizeof(sCInfo));
598
0
    jpeg_copy_critical_parameters(psDInfo, &sCInfo);
599
600
    // Ensure libjpeg won't write any extraneous markers.
601
0
    sCInfo.write_JFIF_header = FALSE;
602
0
    sCInfo.write_Adobe_marker = FALSE;
603
604
    /* -------------------------------------------------------------------- */
605
    /*      Allocated destination coefficient array                         */
606
    /* -------------------------------------------------------------------- */
607
0
    const bool bIsTiled = CPL_TO_BOOL(TIFFIsTiled(hTIFF));
608
609
0
    int nJPEGWidth = nBlockXSize;
610
0
    int nJPEGHeight = nBlockYSize;
611
0
    if (!bIsTiled)
612
0
    {
613
0
        nJPEGWidth = std::min(nBlockXSize, nXSize - iX * nBlockXSize);
614
0
        nJPEGHeight = std::min(nBlockYSize, nYSize - iY * nBlockYSize);
615
0
    }
616
617
// Code partially derived from libjpeg transupp.c.
618
619
// Correct the destination's image dimensions as necessary.
620
0
#if JPEG_LIB_VERSION >= 70
621
0
    sCInfo.jpeg_width = nJPEGWidth;
622
0
    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
0
    const int x_crop_offset = (iX * nBlockXSize) / iMCU_sample_width;
630
0
    const int y_crop_offset = (iY * nBlockYSize) / iMCU_sample_height;
631
632
0
    jvirt_barray_ptr *pDstCoeffs =
633
0
        static_cast<jvirt_barray_ptr *>((*sCInfo.mem->alloc_small)(
634
0
            reinterpret_cast<j_common_ptr>(&sCInfo), JPOOL_IMAGE,
635
0
            sizeof(jvirt_barray_ptr) * sCInfo.num_components));
636
637
0
    for (int ci = 0; ci < sCInfo.num_components; ci++)
638
0
    {
639
0
        jpeg_component_info *compptr = sCInfo.comp_info + ci;
640
0
        int h_samp_factor, v_samp_factor;
641
0
        if (sCInfo.num_components == 1)
642
0
        {
643
            // Force samp factors to 1x1 in this case.
644
0
            h_samp_factor = 1;
645
0
            v_samp_factor = 1;
646
0
        }
647
0
        else
648
0
        {
649
0
            h_samp_factor = compptr->h_samp_factor;
650
0
            v_samp_factor = compptr->v_samp_factor;
651
0
        }
652
0
        int width_in_iMCUs = DIV_ROUND_UP(nJPEGWidth, iMCU_sample_width);
653
0
        int height_in_iMCUs = DIV_ROUND_UP(nJPEGHeight, iMCU_sample_height);
654
0
        int nWidth_in_blocks = width_in_iMCUs * h_samp_factor;
655
0
        int nHeight_in_blocks = height_in_iMCUs * v_samp_factor;
656
0
        pDstCoeffs[ci] = (*sCInfo.mem->request_virt_barray)(
657
0
            reinterpret_cast<j_common_ptr>(&sCInfo), JPOOL_IMAGE, FALSE,
658
0
            nWidth_in_blocks, nHeight_in_blocks,
659
0
            static_cast<JDIMENSION>(v_samp_factor));
660
0
    }
661
662
0
    jpeg_vsiio_dest(&sCInfo, fpMEM);
663
664
    // Start compressor (note no image data is actually written here).
665
0
    jpeg_write_coefficients(&sCInfo, pDstCoeffs);
666
667
0
    jpeg_suppress_tables(&sCInfo, TRUE);
668
669
    // Must copy the right amount of data (the destination's image size)
670
    // starting at the given X and Y offsets in the source.
671
0
    for (int ci = 0; ci < sCInfo.num_components; ci++)
672
0
    {
673
0
        jpeg_component_info *compptr = sCInfo.comp_info + ci;
674
0
        const int x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
675
0
        const int y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
676
0
        const JDIMENSION nSrcWidthInBlocks =
677
0
            psDInfo->comp_info[ci].width_in_blocks;
678
0
        const JDIMENSION nSrcHeightInBlocks =
679
0
            psDInfo->comp_info[ci].height_in_blocks;
680
681
0
        JDIMENSION nXBlocksToCopy = compptr->width_in_blocks;
682
0
        if (x_crop_blocks + compptr->width_in_blocks > nSrcWidthInBlocks)
683
0
            nXBlocksToCopy = nSrcWidthInBlocks - x_crop_blocks;
684
685
0
        for (JDIMENSION dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
686
0
             dst_blk_y += compptr->v_samp_factor)
687
0
        {
688
0
            JBLOCKARRAY dst_buffer = (*psDInfo->mem->access_virt_barray)(
689
0
                reinterpret_cast<j_common_ptr>(psDInfo), pDstCoeffs[ci],
690
0
                dst_blk_y, static_cast<JDIMENSION>(compptr->v_samp_factor),
691
0
                TRUE);
692
693
0
            int offset_y = 0;
694
0
            if (bIsTiled && dst_blk_y + y_crop_blocks + compptr->v_samp_factor >
695
0
                                nSrcHeightInBlocks)
696
0
            {
697
0
                const int nYBlocks =
698
0
                    static_cast<int>(nSrcHeightInBlocks) -
699
0
                    static_cast<int>(dst_blk_y + y_crop_blocks);
700
0
                if (nYBlocks > 0)
701
0
                {
702
0
                    JBLOCKARRAY src_buffer =
703
0
                        (*psDInfo->mem->access_virt_barray)(
704
0
                            reinterpret_cast<j_common_ptr>(psDInfo),
705
0
                            pSrcCoeffs[ci], dst_blk_y + y_crop_blocks,
706
0
                            static_cast<JDIMENSION>(1), FALSE);
707
0
                    for (; offset_y < nYBlocks; offset_y++)
708
0
                    {
709
0
                        memcpy(dst_buffer[offset_y],
710
0
                               src_buffer[offset_y] + x_crop_blocks,
711
0
                               nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF)));
712
0
                        if (nXBlocksToCopy < compptr->width_in_blocks)
713
0
                        {
714
0
                            memset(dst_buffer[offset_y] + nXBlocksToCopy, 0,
715
0
                                   (compptr->width_in_blocks - nXBlocksToCopy) *
716
0
                                       (DCTSIZE2 * sizeof(JCOEF)));
717
0
                        }
718
0
                    }
719
0
                }
720
721
0
                for (; offset_y < compptr->v_samp_factor; offset_y++)
722
0
                {
723
0
                    memset(dst_buffer[offset_y], 0,
724
0
                           compptr->width_in_blocks * DCTSIZE2 * sizeof(JCOEF));
725
0
                }
726
0
            }
727
0
            else
728
0
            {
729
0
                JBLOCKARRAY src_buffer = (*psDInfo->mem->access_virt_barray)(
730
0
                    reinterpret_cast<j_common_ptr>(psDInfo), pSrcCoeffs[ci],
731
0
                    dst_blk_y + y_crop_blocks,
732
0
                    static_cast<JDIMENSION>(compptr->v_samp_factor), FALSE);
733
0
                for (; offset_y < compptr->v_samp_factor; offset_y++)
734
0
                {
735
0
                    memcpy(dst_buffer[offset_y],
736
0
                           src_buffer[offset_y] + x_crop_blocks,
737
0
                           nXBlocksToCopy * (DCTSIZE2 * sizeof(JCOEF)));
738
0
                    if (nXBlocksToCopy < compptr->width_in_blocks)
739
0
                    {
740
0
                        memset(dst_buffer[offset_y] + nXBlocksToCopy, 0,
741
0
                               (compptr->width_in_blocks - nXBlocksToCopy) *
742
0
                                   (DCTSIZE2 * sizeof(JCOEF)));
743
0
                    }
744
0
                }
745
0
            }
746
0
        }
747
0
    }
748
749
0
    jpeg_finish_compress(&sCInfo);
750
0
    jpeg_destroy_compress(&sCInfo);
751
752
0
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpMEM));
753
754
    /* -------------------------------------------------------------------- */
755
    /*      Write the JPEG content with libtiff raw API                     */
756
    /* -------------------------------------------------------------------- */
757
0
    vsi_l_offset nSize = 0;
758
0
    GByte *pabyJPEGData =
759
0
        VSIGetMemFileBuffer(osTmpFilename.c_str(), &nSize, FALSE);
760
761
0
    CPLErr eErr = CE_None;
762
763
0
    if (bIsTiled)
764
0
    {
765
0
        if (static_cast<vsi_l_offset>(
766
0
                TIFFWriteRawTile(hTIFF, iX + iY * nXBlocks, pabyJPEGData,
767
0
                                 static_cast<tmsize_t>(nSize))) != nSize)
768
0
            eErr = CE_Failure;
769
0
    }
770
0
    else
771
0
    {
772
0
        if (static_cast<vsi_l_offset>(
773
0
                TIFFWriteRawStrip(hTIFF, iX + iY * nXBlocks, pabyJPEGData,
774
0
                                  static_cast<tmsize_t>(nSize))) != nSize)
775
0
            eErr = CE_Failure;
776
0
    }
777
778
0
    VSIUnlink(osTmpFilename.c_str());
779
780
0
    return eErr;
781
0
}
782
783
/************************************************************************/
784
/*                         GTIFF_CopyFromJPEG()                         */
785
/************************************************************************/
786
787
CPLErr GTIFF_CopyFromJPEG(GDALDataset *poDS, GDALDataset *poSrcDS,
788
                          GDALProgressFunc pfnProgress, void *pProgressData,
789
                          bool &bShouldFallbackToNormalCopyIfFail)
790
0
{
791
0
    bShouldFallbackToNormalCopyIfFail = true;
792
793
0
    poSrcDS = GetUnderlyingDataset(poSrcDS);
794
0
    if (poSrcDS == nullptr)
795
0
        return CE_Failure;
796
797
0
    VSILFILE *fpJPEG = VSIFOpenL(poSrcDS->GetDescription(), "rb");
798
0
    if (fpJPEG == nullptr)
799
0
        return CE_Failure;
800
801
0
    CPLErr eErr = CE_None;
802
803
    /* -------------------------------------------------------------------- */
804
    /*      Initialization of the decompressor                              */
805
    /* -------------------------------------------------------------------- */
806
0
    struct jpeg_error_mgr sJErr;
807
0
    struct jpeg_decompress_struct sDInfo;
808
0
    memset(&sDInfo, 0, sizeof(sDInfo));
809
0
    jmp_buf setjmp_buffer;
810
0
    if (setjmp(setjmp_buffer))
811
0
    {
812
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fpJPEG));
813
0
        jpeg_destroy_decompress(&sDInfo);
814
0
        return CE_Failure;
815
0
    }
816
817
0
    sDInfo.err = jpeg_std_error(&sJErr);
818
0
    sJErr.error_exit = GTIFF_ErrorExitJPEG;
819
0
    sDInfo.client_data = &setjmp_buffer;
820
821
0
    jpeg_CreateDecompress(&sDInfo, JPEG_LIB_VERSION, sizeof(sDInfo));
822
823
    // This is to address bug related in ticket #1795.
824
0
    if (CPLGetConfigOption("JPEGMEM", nullptr) == nullptr)
825
0
    {
826
        // If the user doesn't provide a value for JPEGMEM, be sure that at
827
        // least 500 MB will be used before creating the temporary file.
828
0
        const long nMinMemory = 500 * 1024 * 1024;
829
0
        sDInfo.mem->max_memory_to_use =
830
0
            std::max(sDInfo.mem->max_memory_to_use, nMinMemory);
831
0
    }
832
833
0
    jpeg_vsiio_src(&sDInfo, fpJPEG);
834
0
    jpeg_read_header(&sDInfo, TRUE);
835
836
0
    jvirt_barray_ptr *pSrcCoeffs = jpeg_read_coefficients(&sDInfo);
837
838
    /* -------------------------------------------------------------------- */
839
    /*      Compute MCU dimensions                                          */
840
    /* -------------------------------------------------------------------- */
841
0
    int iMCU_sample_width = 8;
842
0
    int iMCU_sample_height = 8;
843
0
    if (sDInfo.num_components != 1)
844
0
    {
845
0
        iMCU_sample_width = sDInfo.max_h_samp_factor * 8;
846
0
        iMCU_sample_height = sDInfo.max_v_samp_factor * 8;
847
0
    }
848
849
    /* -------------------------------------------------------------------- */
850
    /*      Get raster and block dimensions                                 */
851
    /* -------------------------------------------------------------------- */
852
0
    int nBlockXSize = 0;
853
0
    int nBlockYSize = 0;
854
855
0
    const int nXSize = poDS->GetRasterXSize();
856
0
    const int nYSize = poDS->GetRasterYSize();
857
    // nBands = poDS->GetRasterCount();
858
859
    // Don't use the GDAL block dimensions because of the split-band
860
    // mechanism that can expose a pseudo one-line-strip whereas the
861
    // real layout is a single big strip.
862
863
0
    TIFF *hTIFF = static_cast<TIFF *>(poDS->GetInternalHandle("TIFF_HANDLE"));
864
0
    if (TIFFIsTiled(hTIFF))
865
0
    {
866
0
        TIFFGetField(hTIFF, TIFFTAG_TILEWIDTH, &(nBlockXSize));
867
0
        TIFFGetField(hTIFF, TIFFTAG_TILELENGTH, &(nBlockYSize));
868
0
    }
869
0
    else
870
0
    {
871
0
        uint32_t nRowsPerStrip = 0;
872
0
        if (!TIFFGetField(hTIFF, TIFFTAG_ROWSPERSTRIP, &(nRowsPerStrip)))
873
0
        {
874
0
            CPLError(CE_Warning, CPLE_AppDefined,
875
0
                     "RowsPerStrip not defined ... assuming all one strip.");
876
0
            nRowsPerStrip = nYSize;  // Dummy value.
877
0
        }
878
879
        // If the rows per strip is larger than the file we will get
880
        // confused.  libtiff internally will treat the rowsperstrip as
881
        // the image height and it is best if we do too. (#4468)
882
0
        if (nRowsPerStrip > static_cast<uint32_t>(nYSize))
883
0
            nRowsPerStrip = nYSize;
884
885
0
        nBlockXSize = nXSize;
886
0
        nBlockYSize = nRowsPerStrip;
887
0
    }
888
889
0
    const int nXBlocks = DIV_ROUND_UP(nXSize, nBlockXSize);
890
0
    const int nYBlocks = DIV_ROUND_UP(nYSize, nBlockYSize);
891
892
    /* -------------------------------------------------------------------- */
893
    /*      Copy blocks.                                                    */
894
    /* -------------------------------------------------------------------- */
895
896
0
    bShouldFallbackToNormalCopyIfFail = false;
897
898
0
    for (int iY = 0; iY < nYBlocks && eErr == CE_None; iY++)
899
0
    {
900
0
        for (int iX = 0; iX < nXBlocks && eErr == CE_None; iX++)
901
0
        {
902
0
            GTIFF_CopyBlockFromJPEGArgs sArgs;
903
0
            sArgs.hTIFF = hTIFF;
904
0
            sArgs.psDInfo = &sDInfo;
905
0
            sArgs.iX = iX;
906
0
            sArgs.iY = iY;
907
0
            sArgs.nXBlocks = nXBlocks;
908
0
            sArgs.nXSize = nXSize;
909
0
            sArgs.nYSize = nYSize;
910
0
            sArgs.nBlockXSize = nBlockXSize;
911
0
            sArgs.nBlockYSize = nBlockYSize;
912
0
            sArgs.iMCU_sample_width = iMCU_sample_width;
913
0
            sArgs.iMCU_sample_height = iMCU_sample_height;
914
0
            sArgs.pSrcCoeffs = pSrcCoeffs;
915
916
0
            eErr = GTIFF_CopyBlockFromJPEG(&sArgs);
917
918
0
            if (!pfnProgress((iY * nXBlocks + iX + 1) * 1.0 /
919
0
                                 (nXBlocks * nYBlocks),
920
0
                             nullptr, pProgressData))
921
0
                eErr = CE_Failure;
922
0
        }
923
0
    }
924
925
    /* -------------------------------------------------------------------- */
926
    /*      Cleanup.                                                        */
927
    /* -------------------------------------------------------------------- */
928
929
0
    jpeg_finish_decompress(&sDInfo);
930
0
    jpeg_destroy_decompress(&sDInfo);
931
932
0
    if (VSIFCloseL(fpJPEG) != 0)
933
0
        eErr = CE_Failure;
934
935
0
    return eErr;
936
0
}
937
938
#endif  // HAVE_LIBJPEG