Coverage Report

Created: 2025-11-16 06:25

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