Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/gtiff/gtiffrasterband.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GeoTIFF Driver
4
 * Purpose:  General methods of GTiffRasterBand
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 "gtiffrasterband.h"
15
#include "gtiffdataset.h"
16
17
#include <algorithm>
18
#include <set>
19
20
#include "gdal_priv.h"
21
#include "cpl_vsi_virtual.h"
22
#include "tifvsi.h"
23
24
/************************************************************************/
25
/*                           GTiffRasterBand()                          */
26
/************************************************************************/
27
28
GTiffRasterBand::GTiffRasterBand(GTiffDataset *poDSIn, int nBandIn)
29
0
    : m_poGDS(poDSIn)
30
0
{
31
0
    poDS = poDSIn;
32
0
    nBand = nBandIn;
33
34
    /* -------------------------------------------------------------------- */
35
    /*      Get the GDAL data type.                                         */
36
    /* -------------------------------------------------------------------- */
37
0
    const uint16_t nBitsPerSample = m_poGDS->m_nBitsPerSample;
38
0
    const uint16_t nSampleFormat = m_poGDS->m_nSampleFormat;
39
40
0
    eDataType = GDT_Unknown;
41
42
0
    if (nBitsPerSample <= 8)
43
0
    {
44
0
        if (nSampleFormat == SAMPLEFORMAT_INT)
45
0
            eDataType = GDT_Int8;
46
0
        else
47
0
            eDataType = GDT_Byte;
48
0
    }
49
0
    else if (nBitsPerSample <= 16)
50
0
    {
51
0
        if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
52
0
            eDataType = GDT_Float16;
53
0
        else if (nSampleFormat == SAMPLEFORMAT_INT)
54
0
            eDataType = GDT_Int16;
55
0
        else
56
0
            eDataType = GDT_UInt16;
57
0
    }
58
0
    else if (nBitsPerSample == 32)
59
0
    {
60
0
        if (nSampleFormat == SAMPLEFORMAT_COMPLEXINT)
61
0
            eDataType = GDT_CInt16;
62
0
        else if (nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
63
0
            eDataType = GDT_CFloat16;
64
0
        else if (nSampleFormat == SAMPLEFORMAT_IEEEFP)
65
0
            eDataType = GDT_Float32;
66
0
        else if (nSampleFormat == SAMPLEFORMAT_INT)
67
0
            eDataType = GDT_Int32;
68
0
        else
69
0
            eDataType = GDT_UInt32;
70
0
    }
71
0
    else if (nBitsPerSample == 64)
72
0
    {
73
0
        if (nSampleFormat == SAMPLEFORMAT_IEEEFP)
74
0
            eDataType = GDT_Float64;
75
0
        else if (nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
76
0
            eDataType = GDT_CFloat32;
77
0
        else if (nSampleFormat == SAMPLEFORMAT_COMPLEXINT)
78
0
            eDataType = GDT_CInt32;
79
0
        else if (nSampleFormat == SAMPLEFORMAT_INT)
80
0
            eDataType = GDT_Int64;
81
0
        else
82
0
            eDataType = GDT_UInt64;
83
0
    }
84
0
    else if (nBitsPerSample == 128)
85
0
    {
86
0
        if (nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
87
0
            eDataType = GDT_CFloat64;
88
0
    }
89
90
    /* -------------------------------------------------------------------- */
91
    /*      Try to work out band color interpretation.                      */
92
    /* -------------------------------------------------------------------- */
93
0
    bool bLookForExtraSamples = false;
94
95
0
    if (m_poGDS->m_poColorTable != nullptr && nBand == 1)
96
0
    {
97
0
        m_eBandInterp = GCI_PaletteIndex;
98
0
    }
99
0
    else if (m_poGDS->m_nPhotometric == PHOTOMETRIC_RGB ||
100
0
             (m_poGDS->m_nPhotometric == PHOTOMETRIC_YCBCR &&
101
0
              m_poGDS->m_nCompression == COMPRESSION_JPEG &&
102
0
              CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES"))))
103
0
    {
104
0
        if (nBand == 1)
105
0
            m_eBandInterp = GCI_RedBand;
106
0
        else if (nBand == 2)
107
0
            m_eBandInterp = GCI_GreenBand;
108
0
        else if (nBand == 3)
109
0
            m_eBandInterp = GCI_BlueBand;
110
0
        else
111
0
            bLookForExtraSamples = true;
112
0
    }
113
0
    else if (m_poGDS->m_nPhotometric == PHOTOMETRIC_YCBCR)
114
0
    {
115
0
        if (nBand == 1)
116
0
            m_eBandInterp = GCI_YCbCr_YBand;
117
0
        else if (nBand == 2)
118
0
            m_eBandInterp = GCI_YCbCr_CbBand;
119
0
        else if (nBand == 3)
120
0
            m_eBandInterp = GCI_YCbCr_CrBand;
121
0
        else
122
0
            bLookForExtraSamples = true;
123
0
    }
124
0
    else if (m_poGDS->m_nPhotometric == PHOTOMETRIC_SEPARATED)
125
0
    {
126
0
        if (nBand == 1)
127
0
            m_eBandInterp = GCI_CyanBand;
128
0
        else if (nBand == 2)
129
0
            m_eBandInterp = GCI_MagentaBand;
130
0
        else if (nBand == 3)
131
0
            m_eBandInterp = GCI_YellowBand;
132
0
        else if (nBand == 4)
133
0
            m_eBandInterp = GCI_BlackBand;
134
0
        else
135
0
            bLookForExtraSamples = true;
136
0
    }
137
0
    else if (m_poGDS->m_nPhotometric == PHOTOMETRIC_MINISBLACK && nBand == 1)
138
0
    {
139
0
        m_eBandInterp = GCI_GrayIndex;
140
0
    }
141
0
    else
142
0
    {
143
0
        bLookForExtraSamples = true;
144
0
    }
145
146
0
    if (bLookForExtraSamples)
147
0
    {
148
0
        uint16_t *v = nullptr;
149
0
        uint16_t count = 0;
150
151
0
        if (TIFFGetField(m_poGDS->m_hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v))
152
0
        {
153
0
            const int nBaseSamples = m_poGDS->m_nSamplesPerPixel - count;
154
0
            const int nExpectedBaseSamples =
155
0
                (m_poGDS->m_nPhotometric == PHOTOMETRIC_MINISBLACK)   ? 1
156
0
                : (m_poGDS->m_nPhotometric == PHOTOMETRIC_MINISWHITE) ? 1
157
0
                : (m_poGDS->m_nPhotometric == PHOTOMETRIC_RGB)        ? 3
158
0
                : (m_poGDS->m_nPhotometric == PHOTOMETRIC_YCBCR)      ? 3
159
0
                : (m_poGDS->m_nPhotometric == PHOTOMETRIC_SEPARATED)  ? 4
160
0
                                                                      : 0;
161
162
0
            if (nExpectedBaseSamples > 0 && nBand == nExpectedBaseSamples + 1 &&
163
0
                nBaseSamples != nExpectedBaseSamples)
164
0
            {
165
0
                ReportError(
166
0
                    CE_Warning, CPLE_AppDefined,
167
0
                    "Wrong number of ExtraSamples : %d. %d were expected",
168
0
                    count, m_poGDS->m_nSamplesPerPixel - nExpectedBaseSamples);
169
0
            }
170
171
0
            if (nBand > nBaseSamples && nBand - nBaseSamples - 1 < count &&
172
0
                (v[nBand - nBaseSamples - 1] == EXTRASAMPLE_ASSOCALPHA ||
173
0
                 v[nBand - nBaseSamples - 1] == EXTRASAMPLE_UNASSALPHA))
174
0
            {
175
0
                if (v[nBand - nBaseSamples - 1] == EXTRASAMPLE_ASSOCALPHA)
176
0
                    m_oGTiffMDMD.SetMetadataItem("ALPHA", "PREMULTIPLIED",
177
0
                                                 "IMAGE_STRUCTURE");
178
0
                m_eBandInterp = GCI_AlphaBand;
179
0
            }
180
0
            else
181
0
                m_eBandInterp = GCI_Undefined;
182
0
        }
183
0
        else
184
0
        {
185
0
            m_eBandInterp = GCI_Undefined;
186
0
        }
187
0
    }
188
189
    /* -------------------------------------------------------------------- */
190
    /*      Establish block size for strip or tiles.                        */
191
    /* -------------------------------------------------------------------- */
192
0
    nBlockXSize = m_poGDS->m_nBlockXSize;
193
0
    nBlockYSize = m_poGDS->m_nBlockYSize;
194
0
    nRasterXSize = m_poGDS->nRasterXSize;
195
0
    nRasterYSize = m_poGDS->nRasterYSize;
196
0
    nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
197
0
    nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
198
0
}
199
200
/************************************************************************/
201
/*                          ~GTiffRasterBand()                          */
202
/************************************************************************/
203
204
GTiffRasterBand::~GTiffRasterBand()
205
0
{
206
    // So that any future DropReferenceVirtualMem() will not try to access the
207
    // raster band object, but this would not conform to the advertised
208
    // contract.
209
0
    if (!m_aSetPSelf.empty())
210
0
    {
211
0
        ReportError(CE_Warning, CPLE_AppDefined,
212
0
                    "Virtual memory objects still exist at GTiffRasterBand "
213
0
                    "destruction");
214
0
        std::set<GTiffRasterBand **>::iterator oIter = m_aSetPSelf.begin();
215
0
        for (; oIter != m_aSetPSelf.end(); ++oIter)
216
0
            *(*oIter) = nullptr;
217
0
    }
218
0
}
219
220
/************************************************************************/
221
/*                            IRasterIO()                               */
222
/************************************************************************/
223
224
CPLErr GTiffRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
225
                                  int nXSize, int nYSize, void *pData,
226
                                  int nBufXSize, int nBufYSize,
227
                                  GDALDataType eBufType, GSpacing nPixelSpace,
228
                                  GSpacing nLineSpace,
229
                                  GDALRasterIOExtraArg *psExtraArg)
230
0
{
231
#if DEBUG_VERBOSE
232
    CPLDebug("GTiff", "RasterIO(%d, %d, %d, %d, %d, %d)", nXOff, nYOff, nXSize,
233
             nYSize, nBufXSize, nBufYSize);
234
#endif
235
236
    // Try to pass the request to the most appropriate overview dataset.
237
0
    if (nBufXSize < nXSize && nBufYSize < nYSize)
238
0
    {
239
0
        int bTried = FALSE;
240
0
        if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
241
0
            ++m_poGDS->m_nJPEGOverviewVisibilityCounter;
242
0
        const CPLErr eErr = TryOverviewRasterIO(
243
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
244
0
            eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
245
0
        if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
246
0
            --m_poGDS->m_nJPEGOverviewVisibilityCounter;
247
0
        if (bTried)
248
0
            return eErr;
249
0
    }
250
251
0
    if (m_poGDS->m_eVirtualMemIOUsage != GTiffDataset::VirtualMemIOEnum::NO)
252
0
    {
253
0
        const int nErr = m_poGDS->VirtualMemIO(
254
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
255
0
            eBufType, 1, &nBand, nPixelSpace, nLineSpace, 0, psExtraArg);
256
0
        if (nErr >= 0)
257
0
            return static_cast<CPLErr>(nErr);
258
0
    }
259
0
    if (m_poGDS->m_bDirectIO)
260
0
    {
261
0
        int nErr =
262
0
            DirectIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
263
0
                     nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg);
264
0
        if (nErr >= 0)
265
0
            return static_cast<CPLErr>(nErr);
266
0
    }
267
268
0
    bool bCanUseMultiThreadedRead = false;
269
0
    if (m_poGDS->m_nDisableMultiThreadedRead == 0 && eRWFlag == GF_Read &&
270
0
        m_poGDS->m_poThreadPool != nullptr && nXSize == nBufXSize &&
271
0
        nYSize == nBufYSize && m_poGDS->IsMultiThreadedReadCompatible())
272
0
    {
273
0
        const int nBlockX1 = nXOff / nBlockXSize;
274
0
        const int nBlockY1 = nYOff / nBlockYSize;
275
0
        const int nBlockX2 = (nXOff + nXSize - 1) / nBlockXSize;
276
0
        const int nBlockY2 = (nYOff + nYSize - 1) / nBlockYSize;
277
0
        const int nXBlocks = nBlockX2 - nBlockX1 + 1;
278
0
        const int nYBlocks = nBlockY2 - nBlockY1 + 1;
279
0
        if (nXBlocks > 1 || nYBlocks > 1)
280
0
        {
281
0
            bCanUseMultiThreadedRead = true;
282
0
        }
283
0
    }
284
285
    // Cleanup data cached by below CacheMultiRange() call.
286
0
    struct BufferedDataFreer
287
0
    {
288
0
        void *m_pBufferedData = nullptr;
289
0
        TIFF *m_hTIFF = nullptr;
290
291
0
        void Init(void *pBufferedData, TIFF *hTIFF)
292
0
        {
293
0
            m_pBufferedData = pBufferedData;
294
0
            m_hTIFF = hTIFF;
295
0
        }
296
297
0
        ~BufferedDataFreer()
298
0
        {
299
0
            if (m_pBufferedData)
300
0
            {
301
0
                VSIFree(m_pBufferedData);
302
0
                VSI_TIFFSetCachedRanges(TIFFClientdata(m_hTIFF), 0, nullptr,
303
0
                                        nullptr, nullptr);
304
0
            }
305
0
        }
306
0
    };
307
308
    // bufferedDataFreer must be left in this scope !
309
0
    BufferedDataFreer bufferedDataFreer;
310
311
0
    if (m_poGDS->eAccess == GA_ReadOnly && eRWFlag == GF_Read &&
312
0
        m_poGDS->HasOptimizedReadMultiRange())
313
0
    {
314
0
        if (bCanUseMultiThreadedRead &&
315
0
            VSI_TIFFGetVSILFile(TIFFClientdata(m_poGDS->m_hTIFF))->HasPRead())
316
0
        {
317
            // use the multi-threaded implementation rather than the multi-range
318
            // one
319
0
        }
320
0
        else
321
0
        {
322
0
            bCanUseMultiThreadedRead = false;
323
0
            GTiffDataset *poDSForCache = m_poGDS;
324
0
            int nBandForCache = nBand;
325
0
            if (!m_poGDS->m_bStreamingIn && m_poGDS->m_bBlockOrderRowMajor &&
326
0
                m_poGDS->m_bLeaderSizeAsUInt4 &&
327
0
                m_poGDS->m_bMaskInterleavedWithImagery &&
328
0
                m_poGDS->m_poImageryDS)
329
0
            {
330
0
                poDSForCache = m_poGDS->m_poImageryDS;
331
0
                nBandForCache = 1;
332
0
            }
333
0
            bufferedDataFreer.Init(
334
0
                poDSForCache->CacheMultiRange(nXOff, nYOff, nXSize, nYSize,
335
0
                                              nBufXSize, nBufYSize,
336
0
                                              &nBandForCache, 1, psExtraArg),
337
0
                poDSForCache->m_hTIFF);
338
0
        }
339
0
    }
340
341
0
    if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize)
342
0
    {
343
0
        const int nBlockX1 = nXOff / nBlockXSize;
344
0
        const int nBlockY1 = nYOff / nBlockYSize;
345
0
        const int nBlockX2 = (nXOff + nXSize - 1) / nBlockXSize;
346
0
        const int nBlockY2 = (nYOff + nYSize - 1) / nBlockYSize;
347
0
        const int nXBlocks = nBlockX2 - nBlockX1 + 1;
348
0
        const int nYBlocks = nBlockY2 - nBlockY1 + 1;
349
350
0
        if (bCanUseMultiThreadedRead)
351
0
        {
352
0
            return m_poGDS->MultiThreadedRead(nXOff, nYOff, nXSize, nYSize,
353
0
                                              pData, eBufType, 1, &nBand,
354
0
                                              nPixelSpace, nLineSpace, 0);
355
0
        }
356
0
        else if (m_poGDS->nBands != 1 &&
357
0
                 m_poGDS->m_nPlanarConfig == PLANARCONFIG_CONTIG)
358
0
        {
359
0
            const GIntBig nRequiredMem =
360
0
                static_cast<GIntBig>(m_poGDS->nBands) * nXBlocks * nYBlocks *
361
0
                nBlockXSize * nBlockYSize * GDALGetDataTypeSizeBytes(eDataType);
362
0
            if (nRequiredMem > GDALGetCacheMax64())
363
0
            {
364
0
                if (!m_poGDS->m_bHasWarnedDisableAggressiveBandCaching)
365
0
                {
366
0
                    CPLDebug("GTiff",
367
0
                             "Disable aggressive band caching. "
368
0
                             "Cache not big enough. "
369
0
                             "At least " CPL_FRMT_GIB " bytes necessary",
370
0
                             nRequiredMem);
371
0
                    m_poGDS->m_bHasWarnedDisableAggressiveBandCaching = true;
372
0
                }
373
0
                m_poGDS->m_bLoadingOtherBands = true;
374
0
            }
375
0
        }
376
0
    }
377
378
    // Write optimization when writing whole blocks, by-passing the block cache.
379
    // We require the block cache to be non instantiated to simplify things
380
    // (otherwise we might need to evict corresponding existing blocks from the
381
    // block cache).
382
0
    else if (eRWFlag == GF_Write &&
383
             // Could be extended to "odd bit" case, but more work
384
0
             m_poGDS->m_nBitsPerSample == GDALGetDataTypeSizeBits(eDataType) &&
385
0
             nXSize == nBufXSize && nYSize == nBufYSize && !HasBlockCache() &&
386
0
             !m_poGDS->m_bLoadedBlockDirty &&
387
0
             (m_poGDS->nBands == 1 ||
388
0
              m_poGDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE) &&
389
0
             !m_poGDS->m_bLeaderSizeAsUInt4 && (nXOff % nBlockXSize) == 0 &&
390
0
             (nYOff % nBlockYSize) == 0 &&
391
0
             (nXOff + nXSize == nRasterXSize || (nXSize % nBlockXSize) == 0) &&
392
0
             (nYOff + nYSize == nRasterYSize || (nYSize % nBlockYSize) == 0))
393
0
    {
394
0
        m_poGDS->Crystalize();
395
396
0
        if (m_poGDS->m_bDebugDontWriteBlocks)
397
0
            return CE_None;
398
399
0
        const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
400
0
        if (nXSize == nBlockXSize && nYSize == nBlockYSize &&
401
0
            eBufType == eDataType && nPixelSpace == nDTSize &&
402
0
            nLineSpace == nPixelSpace * nBlockXSize)
403
0
        {
404
            // If writing one single block with the right data type and layout,
405
            // we don't need a temporary buffer
406
0
            const int nBlockId =
407
0
                ComputeBlockId(nXOff / nBlockXSize, nYOff / nBlockYSize);
408
0
            return m_poGDS->WriteEncodedTileOrStrip(
409
0
                nBlockId, pData, /* bPreserveDataBuffer= */ true);
410
0
        }
411
412
        // Make sure m_poGDS->m_pabyBlockBuf is allocated.
413
        // We could actually use any temporary buffer
414
0
        if (m_poGDS->LoadBlockBuf(/* nBlockId = */ -1,
415
0
                                  /* bReadFromDisk = */ false) != CE_None)
416
0
        {
417
0
            return CE_Failure;
418
0
        }
419
420
        // Iterate over all blocks defined by
421
        // [nXOff, nXOff+nXSize[ * [nYOff, nYOff+nYSize[
422
        // and write their content as a nBlockXSize x nBlockYSize strile
423
        // in a temporary buffer, before calling WriteEncodedTileOrStrip()
424
        // on it
425
0
        const int nYBlockStart = nYOff / nBlockYSize;
426
0
        const int nYBlockEnd = 1 + (nYOff + nYSize - 1) / nBlockYSize;
427
0
        const int nXBlockStart = nXOff / nBlockXSize;
428
0
        const int nXBlockEnd = 1 + (nXOff + nXSize - 1) / nBlockXSize;
429
0
        for (int nYBlock = nYBlockStart; nYBlock < nYBlockEnd; ++nYBlock)
430
0
        {
431
0
            const int nValidY =
432
0
                std::min(nBlockYSize, nRasterYSize - nYBlock * nBlockYSize);
433
0
            for (int nXBlock = nXBlockStart; nXBlock < nXBlockEnd; ++nXBlock)
434
0
            {
435
0
                const int nValidX =
436
0
                    std::min(nBlockXSize, nRasterXSize - nXBlock * nBlockXSize);
437
0
                if (nValidY < nBlockYSize || nValidX < nBlockXSize)
438
0
                {
439
                    // Make sure padding bytes at the right/bottom of the
440
                    // tile are initialized to zero.
441
0
                    memset(m_poGDS->m_pabyBlockBuf, 0,
442
0
                           static_cast<size_t>(nBlockXSize) * nBlockYSize *
443
0
                               nDTSize);
444
0
                }
445
0
                const GByte *pabySrcData =
446
0
                    static_cast<const GByte *>(pData) +
447
0
                    static_cast<size_t>(nYBlock - nYBlockStart) * nBlockYSize *
448
0
                        nLineSpace +
449
0
                    static_cast<size_t>(nXBlock - nXBlockStart) * nBlockXSize *
450
0
                        nPixelSpace;
451
0
                for (int iY = 0; iY < nValidY; ++iY)
452
0
                {
453
0
                    GDALCopyWords64(
454
0
                        pabySrcData + static_cast<size_t>(iY) * nLineSpace,
455
0
                        eBufType, static_cast<int>(nPixelSpace),
456
0
                        m_poGDS->m_pabyBlockBuf +
457
0
                            static_cast<size_t>(iY) * nBlockXSize * nDTSize,
458
0
                        eDataType, nDTSize, nValidX);
459
0
                }
460
0
                const int nBlockId = ComputeBlockId(nXBlock, nYBlock);
461
0
                if (m_poGDS->WriteEncodedTileOrStrip(
462
0
                        nBlockId, m_poGDS->m_pabyBlockBuf,
463
0
                        /* bPreserveDataBuffer= */ false) != CE_None)
464
0
                {
465
0
                    return CE_Failure;
466
0
                }
467
0
            }
468
0
        }
469
0
        return CE_None;
470
0
    }
471
472
0
    if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
473
0
        ++m_poGDS->m_nJPEGOverviewVisibilityCounter;
474
0
    const CPLErr eErr = GDALPamRasterBand::IRasterIO(
475
0
        eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
476
0
        eBufType, nPixelSpace, nLineSpace, psExtraArg);
477
0
    if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
478
0
        --m_poGDS->m_nJPEGOverviewVisibilityCounter;
479
480
0
    m_poGDS->m_bLoadingOtherBands = false;
481
482
0
    return eErr;
483
0
}
484
485
/************************************************************************/
486
/*                        ComputeBlockId()                              */
487
/************************************************************************/
488
489
/** Computes the TIFF block identifier from the tile coordinate, band
490
 * number and planar configuration.
491
 */
492
int GTiffRasterBand::ComputeBlockId(int nBlockXOff, int nBlockYOff) const
493
0
{
494
0
    const int nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow;
495
0
    if (m_poGDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
496
0
    {
497
0
        return nBlockId + (nBand - 1) * m_poGDS->m_nBlocksPerBand;
498
0
    }
499
0
    return nBlockId;
500
0
}