Coverage Report

Created: 2025-06-13 06:29

/src/gdal/frmts/gtiff/gtiffjpegoverviewds.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GeoTIFF Driver
4
 * Purpose:  GDAL GeoTIFF support.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1998, 2002, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys dot com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "gtiffjpegoverviewds.h"
15
16
#include "gtiffdataset.h"
17
18
#include "tifvsi.h"
19
20
/************************************************************************/
21
/* ==================================================================== */
22
/*                     GTiffJPEGOverviewBand                            */
23
/* ==================================================================== */
24
/************************************************************************/
25
26
class GTiffJPEGOverviewBand final : public GDALRasterBand
27
{
28
  public:
29
    GTiffJPEGOverviewBand(GTiffJPEGOverviewDS *poDS, int nBand);
30
31
    virtual ~GTiffJPEGOverviewBand()
32
0
    {
33
0
    }
34
35
    virtual CPLErr IReadBlock(int, int, void *) override;
36
37
    GDALColorInterp GetColorInterpretation() override
38
0
    {
39
0
        return cpl::down_cast<GTiffJPEGOverviewDS *>(poDS)
40
0
            ->m_poParentDS->GetRasterBand(nBand)
41
0
            ->GetColorInterpretation();
42
0
    }
43
};
44
45
/************************************************************************/
46
/*                        GTiffJPEGOverviewDS()                         */
47
/************************************************************************/
48
49
GTiffJPEGOverviewDS::GTiffJPEGOverviewDS(GTiffDataset *poParentDSIn,
50
                                         int nOverviewLevelIn,
51
                                         const void *pJPEGTable,
52
                                         int nJPEGTableSizeIn)
53
0
    : m_poParentDS(poParentDSIn), m_nOverviewLevel(nOverviewLevelIn),
54
0
      m_nJPEGTableSize(nJPEGTableSizeIn)
55
0
{
56
0
    ShareLockWithParentDataset(poParentDSIn);
57
58
0
    m_osTmpFilenameJPEGTable = VSIMemGenerateHiddenFilename("jpegtable");
59
60
0
    const GByte abyAdobeAPP14RGB[] = {0xFF, 0xEE, 0x00, 0x0E, 0x41, 0x64,
61
0
                                      0x6F, 0x62, 0x65, 0x00, 0x64, 0x00,
62
0
                                      0x00, 0x00, 0x00, 0x00};
63
0
    const bool bAddAdobe =
64
0
        m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
65
0
        m_poParentDS->m_nPhotometric != PHOTOMETRIC_YCBCR &&
66
0
        m_poParentDS->nBands == 3;
67
0
    m_pabyJPEGTable = static_cast<GByte *>(CPLMalloc(
68
0
        m_nJPEGTableSize + (bAddAdobe ? sizeof(abyAdobeAPP14RGB) : 0)));
69
0
    memcpy(m_pabyJPEGTable, pJPEGTable, m_nJPEGTableSize);
70
0
    if (bAddAdobe)
71
0
    {
72
0
        memcpy(m_pabyJPEGTable + m_nJPEGTableSize, abyAdobeAPP14RGB,
73
0
               sizeof(abyAdobeAPP14RGB));
74
0
        m_nJPEGTableSize += sizeof(abyAdobeAPP14RGB);
75
0
    }
76
0
    CPL_IGNORE_RET_VAL(VSIFCloseL(VSIFileFromMemBuffer(
77
0
        m_osTmpFilenameJPEGTable, m_pabyJPEGTable, m_nJPEGTableSize, TRUE)));
78
79
0
    const int nScaleFactor = 1 << m_nOverviewLevel;
80
0
    nRasterXSize = DIV_ROUND_UP(m_poParentDS->nRasterXSize, nScaleFactor);
81
0
    nRasterYSize = DIV_ROUND_UP(m_poParentDS->nRasterYSize, nScaleFactor);
82
83
0
    for (int i = 1; i <= m_poParentDS->nBands; ++i)
84
0
        SetBand(i, new GTiffJPEGOverviewBand(this, i));
85
86
0
    SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
87
0
    if (m_poParentDS->m_nPhotometric == PHOTOMETRIC_YCBCR)
88
0
        SetMetadataItem("COMPRESSION", "YCbCr JPEG", "IMAGE_STRUCTURE");
89
0
    else
90
0
        SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
91
0
}
92
93
/************************************************************************/
94
/*                       ~GTiffJPEGOverviewDS()                         */
95
/************************************************************************/
96
97
GTiffJPEGOverviewDS::~GTiffJPEGOverviewDS()
98
0
{
99
0
    m_poJPEGDS.reset();
100
0
    VSIUnlink(m_osTmpFilenameJPEGTable);
101
0
    if (!m_osTmpFilename.empty())
102
0
        VSIUnlink(m_osTmpFilename);
103
0
}
104
105
/************************************************************************/
106
/*                            IRasterIO()                               */
107
/************************************************************************/
108
109
CPLErr GTiffJPEGOverviewDS::IRasterIO(
110
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
111
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
112
    int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
113
    GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
114
115
0
{
116
    // For non-single strip JPEG-IN-TIFF, the block based strategy will
117
    // be the most efficient one, to avoid decompressing the JPEG content
118
    // for each requested band.
119
0
    if (nBandCount > 1 &&
120
0
        m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
121
0
        (m_poParentDS->m_nBlockXSize < m_poParentDS->nRasterXSize ||
122
0
         m_poParentDS->m_nBlockYSize > 1))
123
0
    {
124
0
        return BlockBasedRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
125
0
                                  nBufXSize, nBufYSize, eBufType, nBandCount,
126
0
                                  panBandMap, nPixelSpace, nLineSpace,
127
0
                                  nBandSpace, psExtraArg);
128
0
    }
129
130
0
    return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
131
0
                                  nBufXSize, nBufYSize, eBufType, nBandCount,
132
0
                                  panBandMap, nPixelSpace, nLineSpace,
133
0
                                  nBandSpace, psExtraArg);
134
0
}
135
136
/************************************************************************/
137
/*                        GTiffJPEGOverviewBand()                       */
138
/************************************************************************/
139
140
GTiffJPEGOverviewBand::GTiffJPEGOverviewBand(GTiffJPEGOverviewDS *poDSIn,
141
                                             int nBandIn)
142
0
{
143
0
    poDS = poDSIn;
144
0
    nBand = nBandIn;
145
0
    eDataType =
146
0
        poDSIn->m_poParentDS->GetRasterBand(nBandIn)->GetRasterDataType();
147
0
    poDSIn->m_poParentDS->GetRasterBand(nBandIn)->GetBlockSize(&nBlockXSize,
148
0
                                                               &nBlockYSize);
149
0
    const int nScaleFactor = 1 << poDSIn->m_nOverviewLevel;
150
0
    nBlockXSize = DIV_ROUND_UP(nBlockXSize, nScaleFactor);
151
0
    nBlockYSize = DIV_ROUND_UP(nBlockYSize, nScaleFactor);
152
0
}
153
154
/************************************************************************/
155
/*                          IReadBlock()                                */
156
/************************************************************************/
157
158
CPLErr GTiffJPEGOverviewBand::IReadBlock(int nBlockXOff, int nBlockYOff,
159
                                         void *pImage)
160
0
{
161
0
    GTiffJPEGOverviewDS *m_poGDS = cpl::down_cast<GTiffJPEGOverviewDS *>(poDS);
162
163
    // Compute the source block ID.
164
0
    int nBlockId = 0;
165
0
    int nParentBlockXSize, nParentBlockYSize;
166
0
    m_poGDS->m_poParentDS->GetRasterBand(1)->GetBlockSize(&nParentBlockXSize,
167
0
                                                          &nParentBlockYSize);
168
0
    const bool bIsSingleStripAsSplit =
169
0
        (nParentBlockYSize == 1 &&
170
0
         m_poGDS->m_poParentDS->m_nBlockYSize != nParentBlockYSize);
171
0
    if (!bIsSingleStripAsSplit)
172
0
    {
173
0
        nBlockId =
174
0
            nBlockYOff * m_poGDS->m_poParentDS->m_nBlocksPerRow + nBlockXOff;
175
0
    }
176
0
    if (m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
177
0
    {
178
0
        nBlockId += (nBand - 1) * m_poGDS->m_poParentDS->m_nBlocksPerBand;
179
0
    }
180
181
    // Make sure it is available.
182
0
    const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
183
0
    vsi_l_offset nOffset = 0;
184
0
    vsi_l_offset nByteCount = 0;
185
0
    bool bErrOccurred = false;
186
0
    if (!m_poGDS->m_poParentDS->IsBlockAvailable(nBlockId, &nOffset,
187
0
                                                 &nByteCount, &bErrOccurred))
188
0
    {
189
0
        memset(pImage, 0,
190
0
               static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
191
0
                   nDataTypeSize);
192
0
        if (bErrOccurred)
193
0
            return CE_Failure;
194
0
        return CE_None;
195
0
    }
196
197
0
    const int nScaleFactor = 1 << m_poGDS->m_nOverviewLevel;
198
0
    if (m_poGDS->m_poJPEGDS == nullptr || nBlockId != m_poGDS->m_nBlockId)
199
0
    {
200
0
        if (nByteCount < 2)
201
0
            return CE_Failure;
202
0
        nOffset += 2;  // Skip leading 0xFF 0xF8.
203
0
        nByteCount -= 2;
204
205
0
        CPLString osFileToOpen;
206
0
        m_poGDS->m_osTmpFilename = VSIMemGenerateHiddenFilename("sparse");
207
0
        VSILFILE *fp = VSIFOpenL(m_poGDS->m_osTmpFilename, "wb+");
208
209
        // If the size of the JPEG strip/tile is small enough, we will
210
        // read it from the TIFF file and forge a in-memory JPEG file with
211
        // the JPEG table followed by the JPEG data.
212
0
        const bool bInMemoryJPEGFile = nByteCount < 256 * 256;
213
0
        if (bInMemoryJPEGFile)
214
0
        {
215
0
            osFileToOpen = m_poGDS->m_osTmpFilename;
216
217
0
            bool bError = false;
218
0
            if (VSIFSeekL(fp, m_poGDS->m_nJPEGTableSize + nByteCount - 1,
219
0
                          SEEK_SET) != 0)
220
0
                bError = true;
221
0
            char ch = 0;
222
0
            if (!bError && VSIFWriteL(&ch, 1, 1, fp) != 1)
223
0
                bError = true;
224
0
            GByte *pabyBuffer =
225
0
                VSIGetMemFileBuffer(m_poGDS->m_osTmpFilename, nullptr, FALSE);
226
0
            memcpy(pabyBuffer, m_poGDS->m_pabyJPEGTable,
227
0
                   m_poGDS->m_nJPEGTableSize);
228
0
            TIFF *hTIFF = m_poGDS->m_poParentDS->m_hTIFF;
229
0
            VSILFILE *fpTIF = VSI_TIFFGetVSILFile(TIFFClientdata(hTIFF));
230
0
            if (!bError && VSIFSeekL(fpTIF, nOffset, SEEK_SET) != 0)
231
0
                bError = true;
232
0
            if (VSIFReadL(pabyBuffer + m_poGDS->m_nJPEGTableSize,
233
0
                          static_cast<size_t>(nByteCount), 1, fpTIF) != 1)
234
0
                bError = true;
235
0
            if (bError)
236
0
            {
237
0
                CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
238
0
                return CE_Failure;
239
0
            }
240
0
        }
241
0
        else
242
0
        {
243
            // If the JPEG strip/tile is too big (e.g. a single-strip
244
            // JPEG-in-TIFF), we will use /vsisparse mechanism to make a
245
            // fake JPEG file.
246
247
0
            osFileToOpen =
248
0
                CPLSPrintf("/vsisparse/%s", m_poGDS->m_osTmpFilename.c_str());
249
250
0
            if (VSIFPrintfL(fp,
251
0
                            "<VSISparseFile><SubfileRegion>"
252
0
                            "<Filename relative='0'>%s</Filename>"
253
0
                            "<DestinationOffset>0</DestinationOffset>"
254
0
                            "<SourceOffset>0</SourceOffset>"
255
0
                            "<RegionLength>%d</RegionLength>"
256
0
                            "</SubfileRegion>"
257
0
                            "<SubfileRegion>"
258
0
                            "<Filename relative='0'>%s</Filename>"
259
0
                            "<DestinationOffset>%d</DestinationOffset>"
260
0
                            "<SourceOffset>" CPL_FRMT_GUIB "</SourceOffset>"
261
0
                            "<RegionLength>" CPL_FRMT_GUIB "</RegionLength>"
262
0
                            "</SubfileRegion></VSISparseFile>",
263
0
                            m_poGDS->m_osTmpFilenameJPEGTable.c_str(),
264
0
                            static_cast<int>(m_poGDS->m_nJPEGTableSize),
265
0
                            m_poGDS->m_poParentDS->GetDescription(),
266
0
                            static_cast<int>(m_poGDS->m_nJPEGTableSize),
267
0
                            nOffset, nByteCount) < 0)
268
0
            {
269
0
                CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
270
0
                return CE_Failure;
271
0
            }
272
0
        }
273
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
274
275
0
        const char *const apszDrivers[] = {"JPEG", nullptr};
276
277
0
        CPLConfigOptionSetter oJPEGtoRGBSetter(
278
0
            "GDAL_JPEG_TO_RGB",
279
0
            m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_CONTIG &&
280
0
                    m_poGDS->nBands == 4
281
0
                ? "NO"
282
0
                : "YES",
283
0
            false);
284
285
0
        m_poGDS->m_poJPEGDS.reset(
286
0
            GDALDataset::Open(osFileToOpen, GDAL_OF_RASTER | GDAL_OF_INTERNAL,
287
0
                              apszDrivers, nullptr, nullptr));
288
289
0
        if (m_poGDS->m_poJPEGDS != nullptr)
290
0
        {
291
            // Force all implicit overviews to be available, even for
292
            // small tiles.
293
0
            CPLConfigOptionSetter oInternalOverviewsSetter(
294
0
                "JPEG_FORCE_INTERNAL_OVERVIEWS", "YES", false);
295
0
            GDALGetOverviewCount(
296
0
                GDALGetRasterBand(m_poGDS->m_poJPEGDS.get(), 1));
297
298
0
            m_poGDS->m_nBlockId = nBlockId;
299
0
        }
300
0
    }
301
302
0
    CPLErr eErr = CE_Failure;
303
0
    if (m_poGDS->m_poJPEGDS)
304
0
    {
305
0
        GDALDataset *l_poDS = m_poGDS->m_poJPEGDS.get();
306
307
0
        int nReqXOff = 0;
308
0
        int nReqYOff = 0;
309
0
        int nReqXSize = 0;
310
0
        int nReqYSize = 0;
311
0
        if (bIsSingleStripAsSplit)
312
0
        {
313
0
            nReqYOff = nBlockYOff * nScaleFactor;
314
0
            nReqXSize = l_poDS->GetRasterXSize();
315
0
            nReqYSize = nScaleFactor;
316
0
        }
317
0
        else
318
0
        {
319
0
            if (nBlockXSize == m_poGDS->GetRasterXSize())
320
0
            {
321
0
                nReqXSize = l_poDS->GetRasterXSize();
322
0
            }
323
0
            else
324
0
            {
325
0
                nReqXSize = nBlockXSize * nScaleFactor;
326
0
            }
327
0
            nReqYSize = nBlockYSize * nScaleFactor;
328
0
        }
329
0
        int nBufXSize = nBlockXSize;
330
0
        int nBufYSize = nBlockYSize;
331
0
        if (nBlockXOff == m_poGDS->m_poParentDS->m_nBlocksPerRow - 1)
332
0
        {
333
0
            nReqXSize = m_poGDS->m_poParentDS->nRasterXSize -
334
0
                        nBlockXOff * m_poGDS->m_poParentDS->m_nBlockXSize;
335
0
        }
336
0
        if (nReqXOff + nReqXSize > l_poDS->GetRasterXSize())
337
0
        {
338
0
            nReqXSize = l_poDS->GetRasterXSize() - nReqXOff;
339
0
        }
340
0
        if (!bIsSingleStripAsSplit &&
341
0
            nBlockYOff == m_poGDS->m_poParentDS->m_nBlocksPerColumn - 1)
342
0
        {
343
0
            nReqYSize = m_poGDS->m_poParentDS->nRasterYSize -
344
0
                        nBlockYOff * m_poGDS->m_poParentDS->m_nBlockYSize;
345
0
        }
346
0
        if (nReqYOff + nReqYSize > l_poDS->GetRasterYSize())
347
0
        {
348
0
            nReqYSize = l_poDS->GetRasterYSize() - nReqYOff;
349
0
        }
350
0
        if (nBlockXOff * nBlockXSize > m_poGDS->GetRasterXSize() - nBufXSize)
351
0
        {
352
0
            memset(pImage, 0,
353
0
                   static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
354
0
                       nDataTypeSize);
355
0
            nBufXSize = m_poGDS->GetRasterXSize() - nBlockXOff * nBlockXSize;
356
0
        }
357
0
        if (nBlockYOff * nBlockYSize > m_poGDS->GetRasterYSize() - nBufYSize)
358
0
        {
359
0
            memset(pImage, 0,
360
0
                   static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize *
361
0
                       nDataTypeSize);
362
0
            nBufYSize = m_poGDS->GetRasterYSize() - nBlockYOff * nBlockYSize;
363
0
        }
364
365
0
        const int nSrcBand =
366
0
            m_poGDS->m_poParentDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE
367
0
                ? 1
368
0
                : nBand;
369
0
        if (nSrcBand <= l_poDS->GetRasterCount())
370
0
        {
371
0
            eErr = l_poDS->GetRasterBand(nSrcBand)->RasterIO(
372
0
                GF_Read, nReqXOff, nReqYOff, nReqXSize, nReqYSize, pImage,
373
0
                nBufXSize, nBufYSize, eDataType, 0,
374
0
                static_cast<GPtrDiff_t>(nBlockXSize) * nDataTypeSize, nullptr);
375
0
        }
376
0
    }
377
378
0
    return eErr;
379
0
}