Coverage Report

Created: 2025-06-13 06:18

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