Line data Source code
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 1031530 : GTiffRasterBand::GTiffRasterBand(GTiffDataset *poDSIn, int nBandIn)
28 1031530 : : m_poGDS(poDSIn)
29 : {
30 1031460 : poDS = poDSIn;
31 1031460 : nBand = nBandIn;
32 :
33 : /* -------------------------------------------------------------------- */
34 : /* Get the GDAL data type. */
35 : /* -------------------------------------------------------------------- */
36 1031460 : const uint16_t nBitsPerSample = m_poGDS->m_nBitsPerSample;
37 1031460 : const uint16_t nSampleFormat = m_poGDS->m_nSampleFormat;
38 :
39 1031460 : eDataType = GDT_Unknown;
40 :
41 1031460 : if (nBitsPerSample <= 8)
42 : {
43 889336 : if (nSampleFormat == SAMPLEFORMAT_INT)
44 310 : eDataType = GDT_Int8;
45 : else
46 889026 : eDataType = GDT_Byte;
47 : }
48 142125 : else if (nBitsPerSample <= 16)
49 : {
50 134353 : if (nBitsPerSample == 16 && nSampleFormat == SAMPLEFORMAT_IEEEFP)
51 248 : eDataType = GDT_Float16;
52 134105 : else if (nSampleFormat == SAMPLEFORMAT_INT)
53 67117 : eDataType = GDT_Int16;
54 : else
55 66988 : eDataType = GDT_UInt16;
56 : }
57 7772 : else if (nBitsPerSample == 32)
58 : {
59 4665 : if (nSampleFormat == SAMPLEFORMAT_COMPLEXINT)
60 683 : eDataType = GDT_CInt16;
61 3982 : else if (nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
62 224 : eDataType = GDT_CFloat16;
63 3758 : else if (nSampleFormat == SAMPLEFORMAT_IEEEFP)
64 2537 : eDataType = GDT_Float32;
65 1221 : else if (nSampleFormat == SAMPLEFORMAT_INT)
66 644 : eDataType = GDT_Int32;
67 : else
68 577 : eDataType = GDT_UInt32;
69 : }
70 3107 : else if (nBitsPerSample == 64)
71 : {
72 2561 : if (nSampleFormat == SAMPLEFORMAT_IEEEFP)
73 1128 : eDataType = GDT_Float64;
74 1433 : else if (nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
75 460 : eDataType = GDT_CFloat32;
76 973 : else if (nSampleFormat == SAMPLEFORMAT_COMPLEXINT)
77 436 : eDataType = GDT_CInt32;
78 537 : else if (nSampleFormat == SAMPLEFORMAT_INT)
79 277 : eDataType = GDT_Int64;
80 : else
81 260 : eDataType = GDT_UInt64;
82 : }
83 546 : else if (nBitsPerSample == 128)
84 : {
85 474 : if (nSampleFormat == SAMPLEFORMAT_COMPLEXIEEEFP)
86 474 : eDataType = GDT_CFloat64;
87 : }
88 :
89 : /* -------------------------------------------------------------------- */
90 : /* Try to work out band color interpretation. */
91 : /* -------------------------------------------------------------------- */
92 1031460 : bool bLookForExtraSamples = false;
93 :
94 1031460 : if (m_poGDS->m_poColorTable != nullptr && nBand == 1)
95 : {
96 172 : m_eBandInterp = GCI_PaletteIndex;
97 : }
98 2044990 : else if (m_poGDS->m_nPhotometric == PHOTOMETRIC_RGB ||
99 1013700 : (m_poGDS->m_nPhotometric == PHOTOMETRIC_YCBCR &&
100 897 : m_poGDS->m_nCompression == COMPRESSION_JPEG &&
101 858 : CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES"))))
102 : {
103 18306 : if (nBand == 1)
104 5756 : m_eBandInterp = GCI_RedBand;
105 12550 : else if (nBand == 2)
106 5765 : m_eBandInterp = GCI_GreenBand;
107 6785 : else if (nBand == 3)
108 5765 : m_eBandInterp = GCI_BlueBand;
109 : else
110 1020 : bLookForExtraSamples = true;
111 : }
112 1012980 : else if (m_poGDS->m_nPhotometric == PHOTOMETRIC_YCBCR)
113 : {
114 39 : if (nBand == 1)
115 13 : m_eBandInterp = GCI_YCbCr_YBand;
116 26 : else if (nBand == 2)
117 13 : m_eBandInterp = GCI_YCbCr_CbBand;
118 13 : else if (nBand == 3)
119 13 : m_eBandInterp = GCI_YCbCr_CrBand;
120 : else
121 0 : bLookForExtraSamples = true;
122 : }
123 1012940 : else if (m_poGDS->m_nPhotometric == PHOTOMETRIC_SEPARATED)
124 : {
125 96 : if (nBand == 1)
126 24 : m_eBandInterp = GCI_CyanBand;
127 72 : else if (nBand == 2)
128 24 : m_eBandInterp = GCI_MagentaBand;
129 48 : else if (nBand == 3)
130 24 : m_eBandInterp = GCI_YellowBand;
131 24 : else if (nBand == 4)
132 24 : m_eBandInterp = GCI_BlackBand;
133 : else
134 0 : bLookForExtraSamples = true;
135 : }
136 1012850 : else if (m_poGDS->m_nPhotometric == PHOTOMETRIC_MINISBLACK && nBand == 1)
137 : {
138 25958 : m_eBandInterp = GCI_GrayIndex;
139 : }
140 : else
141 : {
142 986888 : bLookForExtraSamples = true;
143 : }
144 :
145 1031460 : if (bLookForExtraSamples)
146 : {
147 987773 : uint16_t *v = nullptr;
148 987773 : uint16_t count = 0;
149 :
150 987773 : if (TIFFGetField(m_poGDS->m_hTIFF, TIFFTAG_EXTRASAMPLES, &count, &v))
151 : {
152 921846 : const int nBaseSamples = m_poGDS->m_nSamplesPerPixel - count;
153 921846 : const int nExpectedBaseSamples =
154 922870 : (m_poGDS->m_nPhotometric == PHOTOMETRIC_MINISBLACK) ? 1
155 2048 : : (m_poGDS->m_nPhotometric == PHOTOMETRIC_MINISWHITE) ? 1
156 1030 : : (m_poGDS->m_nPhotometric == PHOTOMETRIC_RGB) ? 3
157 12 : : (m_poGDS->m_nPhotometric == PHOTOMETRIC_YCBCR) ? 3
158 6 : : (m_poGDS->m_nPhotometric == PHOTOMETRIC_SEPARATED) ? 4
159 : : 0;
160 :
161 921846 : if (nExpectedBaseSamples > 0 && nBand == nExpectedBaseSamples + 1 &&
162 : nBaseSamples != nExpectedBaseSamples)
163 : {
164 1 : ReportError(
165 : CE_Warning, CPLE_AppDefined,
166 : "Wrong number of ExtraSamples : %d. %d were expected",
167 1 : count, m_poGDS->m_nSamplesPerPixel - nExpectedBaseSamples);
168 : }
169 :
170 921846 : if (nBand > nBaseSamples && nBand - nBaseSamples - 1 < count &&
171 921841 : (v[nBand - nBaseSamples - 1] == EXTRASAMPLE_ASSOCALPHA ||
172 921824 : v[nBand - nBaseSamples - 1] == EXTRASAMPLE_UNASSALPHA))
173 : {
174 1113 : if (v[nBand - nBaseSamples - 1] == EXTRASAMPLE_ASSOCALPHA)
175 17 : m_oGTiffMDMD.SetMetadataItem("ALPHA", "PREMULTIPLIED",
176 : "IMAGE_STRUCTURE");
177 1113 : m_eBandInterp = GCI_AlphaBand;
178 : }
179 : else
180 920733 : m_eBandInterp = GCI_Undefined;
181 : }
182 : else
183 : {
184 65927 : m_eBandInterp = GCI_Undefined;
185 : }
186 : }
187 :
188 : /* -------------------------------------------------------------------- */
189 : /* Establish block size for strip or tiles. */
190 : /* -------------------------------------------------------------------- */
191 1031460 : nBlockXSize = m_poGDS->m_nBlockXSize;
192 1031460 : nBlockYSize = m_poGDS->m_nBlockYSize;
193 1031460 : nRasterXSize = m_poGDS->nRasterXSize;
194 1031460 : nRasterYSize = m_poGDS->nRasterYSize;
195 1031460 : nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
196 1031460 : nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
197 1031460 : }
198 :
199 : /************************************************************************/
200 : /* ~GTiffRasterBand() */
201 : /************************************************************************/
202 :
203 1931014 : GTiffRasterBand::~GTiffRasterBand()
204 : {
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 1031540 : if (!m_aSetPSelf.empty())
209 : {
210 0 : ReportError(CE_Warning, CPLE_AppDefined,
211 : "Virtual memory objects still exist at GTiffRasterBand "
212 : "destruction");
213 0 : std::set<GTiffRasterBand **>::iterator oIter = m_aSetPSelf.begin();
214 0 : for (; oIter != m_aSetPSelf.end(); ++oIter)
215 0 : *(*oIter) = nullptr;
216 : }
217 1931014 : }
218 :
219 : /************************************************************************/
220 : /* IRasterIO() */
221 : /************************************************************************/
222 :
223 5814450 : 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 : {
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 5814450 : if (nBufXSize < nXSize && nBufYSize < nYSize)
237 : {
238 362407 : int bTried = FALSE;
239 362407 : if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
240 362039 : ++m_poGDS->m_nJPEGOverviewVisibilityCounter;
241 362407 : const CPLErr eErr = TryOverviewRasterIO(
242 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
243 : eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
244 362407 : if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
245 362039 : --m_poGDS->m_nJPEGOverviewVisibilityCounter;
246 362407 : if (bTried)
247 44 : return eErr;
248 : }
249 :
250 5814410 : if (m_poGDS->m_eVirtualMemIOUsage != GTiffDataset::VirtualMemIOEnum::NO)
251 : {
252 904 : const int nErr = m_poGDS->VirtualMemIO(
253 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
254 452 : eBufType, 1, &nBand, nPixelSpace, nLineSpace, 0, psExtraArg);
255 452 : if (nErr >= 0)
256 363 : return static_cast<CPLErr>(nErr);
257 : }
258 5814050 : if (m_poGDS->m_bDirectIO)
259 : {
260 : int nErr =
261 2522 : DirectIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize,
262 : nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg);
263 2522 : if (nErr >= 0)
264 1409 : return static_cast<CPLErr>(nErr);
265 : }
266 :
267 5812640 : bool bCanUseMultiThreadedRead = false;
268 5812680 : if (m_poGDS->m_nDisableMultiThreadedRead == 0 && eRWFlag == GF_Read &&
269 3354190 : m_poGDS->m_poThreadPool != nullptr && nXSize == nBufXSize &&
270 11625300 : nYSize == nBufYSize && m_poGDS->IsMultiThreadedReadCompatible())
271 : {
272 103 : const int nBlockX1 = nXOff / nBlockXSize;
273 103 : const int nBlockY1 = nYOff / nBlockYSize;
274 103 : const int nBlockX2 = (nXOff + nXSize - 1) / nBlockXSize;
275 103 : const int nBlockY2 = (nYOff + nYSize - 1) / nBlockYSize;
276 103 : const int nXBlocks = nBlockX2 - nBlockX1 + 1;
277 103 : const int nYBlocks = nBlockY2 - nBlockY1 + 1;
278 103 : if (nXBlocks > 1 || nYBlocks > 1)
279 : {
280 100 : bCanUseMultiThreadedRead = true;
281 : }
282 : }
283 :
284 : // Cleanup data cached by below CacheMultiRange() call.
285 : struct BufferedDataFreer
286 : {
287 : void *m_pBufferedData = nullptr;
288 : TIFF *m_hTIFF = nullptr;
289 :
290 256 : void Init(void *pBufferedData, TIFF *hTIFF)
291 : {
292 256 : m_pBufferedData = pBufferedData;
293 256 : m_hTIFF = hTIFF;
294 256 : }
295 :
296 5813440 : ~BufferedDataFreer()
297 5813440 : {
298 5813440 : if (m_pBufferedData)
299 : {
300 35 : VSIFree(m_pBufferedData);
301 35 : VSI_TIFFSetCachedRanges(TIFFClientdata(m_hTIFF), 0, nullptr,
302 : nullptr, nullptr);
303 : }
304 5813440 : }
305 : };
306 :
307 : // bufferedDataFreer must be left in this scope !
308 5813680 : BufferedDataFreer bufferedDataFreer;
309 :
310 8668080 : if (m_poGDS->eAccess == GA_ReadOnly && eRWFlag == GF_Read &&
311 2854790 : m_poGDS->HasOptimizedReadMultiRange())
312 : {
313 264 : if (bCanUseMultiThreadedRead &&
314 4 : VSI_TIFFGetVSILFile(TIFFClientdata(m_poGDS->m_hTIFF))->HasPRead())
315 : {
316 : // use the multi-threaded implementation rather than the multi-range
317 : // one
318 : }
319 : else
320 : {
321 256 : bCanUseMultiThreadedRead = false;
322 256 : GTiffDataset *poDSForCache = m_poGDS;
323 256 : int nBandForCache = nBand;
324 256 : if (!m_poGDS->m_bStreamingIn && m_poGDS->m_bBlockOrderRowMajor &&
325 112 : m_poGDS->m_bLeaderSizeAsUInt4 &&
326 112 : m_poGDS->m_bMaskInterleavedWithImagery &&
327 102 : m_poGDS->m_poImageryDS)
328 : {
329 63 : poDSForCache = m_poGDS->m_poImageryDS;
330 63 : nBandForCache = 1;
331 : }
332 256 : bufferedDataFreer.Init(
333 : poDSForCache->CacheMultiRange(nXOff, nYOff, nXSize, nYSize,
334 : nBufXSize, nBufYSize,
335 : &nBandForCache, 1, psExtraArg),
336 : poDSForCache->m_hTIFF);
337 : }
338 : }
339 :
340 5813300 : if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize)
341 : {
342 2955780 : const int nBlockX1 = nXOff / nBlockXSize;
343 2955780 : const int nBlockY1 = nYOff / nBlockYSize;
344 2955780 : const int nBlockX2 = (nXOff + nXSize - 1) / nBlockXSize;
345 2955780 : const int nBlockY2 = (nYOff + nYSize - 1) / nBlockYSize;
346 2955780 : const int nXBlocks = nBlockX2 - nBlockX1 + 1;
347 2955780 : const int nYBlocks = nBlockY2 - nBlockY1 + 1;
348 :
349 2955780 : if (bCanUseMultiThreadedRead)
350 : {
351 200 : return m_poGDS->MultiThreadedRead(nXOff, nYOff, nXSize, nYSize,
352 100 : pData, eBufType, 1, &nBand,
353 100 : nPixelSpace, nLineSpace, 0);
354 : }
355 2955680 : else if (m_poGDS->nBands != 1 &&
356 735644 : m_poGDS->m_nPlanarConfig == PLANARCONFIG_CONTIG)
357 : {
358 : const GIntBig nRequiredMem =
359 675647 : static_cast<GIntBig>(m_poGDS->nBands) * nXBlocks * nYBlocks *
360 675647 : nBlockXSize * nBlockYSize * GDALGetDataTypeSizeBytes(eDataType);
361 675855 : if (nRequiredMem > GDALGetCacheMax64())
362 : {
363 9061 : if (!m_poGDS->m_bHasWarnedDisableAggressiveBandCaching)
364 : {
365 15 : CPLDebug("GTiff",
366 : "Disable aggressive band caching. "
367 : "Cache not big enough. "
368 : "At least " CPL_FRMT_GIB " bytes necessary",
369 : nRequiredMem);
370 15 : m_poGDS->m_bHasWarnedDisableAggressiveBandCaching = true;
371 : }
372 9061 : m_poGDS->m_bLoadingOtherBands = true;
373 : }
374 2956970 : }
375 : }
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 5315440 : else if (eRWFlag == GF_Write &&
382 : // Could be extended to "odd bit" case, but more work
383 2458500 : m_poGDS->m_nBitsPerSample == GDALGetDataTypeSizeBits(eDataType) &&
384 2305620 : nXSize == nBufXSize && nYSize == nBufYSize && !HasBlockCache() &&
385 48527 : !m_poGDS->m_bLoadedBlockDirty &&
386 48111 : (m_poGDS->nBands == 1 ||
387 4476 : m_poGDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE) &&
388 47001 : !m_poGDS->m_bLeaderSizeAsUInt4 && (nXOff % nBlockXSize) == 0 &&
389 46856 : (nYOff % nBlockYSize) == 0 &&
390 5363130 : (nXOff + nXSize == nRasterXSize || (nXSize % nBlockXSize) == 0) &&
391 47221 : (nYOff + nYSize == nRasterYSize || (nYSize % nBlockYSize) == 0))
392 : {
393 46475 : m_poGDS->Crystalize();
394 :
395 46716 : if (m_poGDS->m_bDebugDontWriteBlocks)
396 0 : return CE_None;
397 :
398 46716 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
399 46435 : if (nXSize == nBlockXSize && nYSize == nBlockYSize &&
400 41768 : eBufType == eDataType && nPixelSpace == nDTSize &&
401 40253 : nLineSpace == nPixelSpace * nBlockXSize)
402 : {
403 : // If writing one single block with the right data type and layout,
404 : // we don't need a temporary buffer
405 : const int nBlockId =
406 41009 : ComputeBlockId(nXOff / nBlockXSize, nYOff / nBlockYSize);
407 40910 : return m_poGDS->WriteEncodedTileOrStrip(
408 41564 : nBlockId, pData, /* bPreserveDataBuffer= */ true);
409 : }
410 :
411 : // Make sure m_poGDS->m_pabyBlockBuf is allocated.
412 : // We could actually use any temporary buffer
413 5426 : if (m_poGDS->LoadBlockBuf(/* nBlockId = */ -1,
414 5738 : /* bReadFromDisk = */ false) != CE_None)
415 : {
416 0 : return CE_Failure;
417 : }
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 5738 : const int nYBlockStart = nYOff / nBlockYSize;
425 5738 : const int nYBlockEnd = 1 + (nYOff + nYSize - 1) / nBlockYSize;
426 5738 : const int nXBlockStart = nXOff / nBlockXSize;
427 5738 : const int nXBlockEnd = 1 + (nXOff + nXSize - 1) / nBlockXSize;
428 48744 : for (int nYBlock = nYBlockStart; nYBlock < nYBlockEnd; ++nYBlock)
429 : {
430 : const int nValidY =
431 43006 : std::min(nBlockYSize, nRasterYSize - nYBlock * nBlockYSize);
432 101772 : for (int nXBlock = nXBlockStart; nXBlock < nXBlockEnd; ++nXBlock)
433 : {
434 : const int nValidX =
435 58766 : std::min(nBlockXSize, nRasterXSize - nXBlock * nBlockXSize);
436 58762 : if (nValidY < nBlockYSize || nValidX < nBlockXSize)
437 : {
438 : // Make sure padding bytes at the right/bottom of the
439 : // tile are initialized to zero.
440 2884 : memset(m_poGDS->m_pabyBlockBuf, 0,
441 2884 : static_cast<size_t>(nBlockXSize) * nBlockYSize *
442 2884 : nDTSize);
443 : }
444 58762 : const GByte *pabySrcData =
445 : static_cast<const GByte *>(pData) +
446 58762 : static_cast<size_t>(nYBlock - nYBlockStart) * nBlockYSize *
447 58762 : nLineSpace +
448 58762 : static_cast<size_t>(nXBlock - nXBlockStart) * nBlockXSize *
449 58762 : nPixelSpace;
450 3788880 : for (int iY = 0; iY < nValidY; ++iY)
451 : {
452 3730110 : GDALCopyWords64(
453 3730110 : pabySrcData + static_cast<size_t>(iY) * nLineSpace,
454 : eBufType, static_cast<int>(nPixelSpace),
455 3730110 : m_poGDS->m_pabyBlockBuf +
456 3730110 : static_cast<size_t>(iY) * nBlockXSize * nDTSize,
457 : eDataType, nDTSize, nValidX);
458 : }
459 58767 : const int nBlockId = ComputeBlockId(nXBlock, nYBlock);
460 117533 : if (m_poGDS->WriteEncodedTileOrStrip(
461 58767 : nBlockId, m_poGDS->m_pabyBlockBuf,
462 58766 : /* bPreserveDataBuffer= */ false) != CE_None)
463 : {
464 0 : return CE_Failure;
465 : }
466 : }
467 : }
468 5738 : return CE_None;
469 : }
470 :
471 5767910 : if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
472 5760930 : ++m_poGDS->m_nJPEGOverviewVisibilityCounter;
473 5767910 : const CPLErr eErr = GDALPamRasterBand::IRasterIO(
474 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
475 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
476 5766280 : if (psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
477 5763740 : --m_poGDS->m_nJPEGOverviewVisibilityCounter;
478 :
479 5766280 : m_poGDS->m_bLoadingOtherBands = false;
480 :
481 5766280 : return eErr;
482 : }
483 :
484 : /************************************************************************/
485 : /* ComputeBlockId() */
486 : /************************************************************************/
487 :
488 : /** Computes the TIFF block identifier from the tile coordinate, band
489 : * number and planar configuration.
490 : */
491 3292980 : int GTiffRasterBand::ComputeBlockId(int nBlockXOff, int nBlockYOff) const
492 : {
493 3292980 : const int nBlockId = nBlockXOff + nBlockYOff * nBlocksPerRow;
494 3292980 : if (m_poGDS->m_nPlanarConfig == PLANARCONFIG_SEPARATE)
495 : {
496 662118 : return nBlockId + (nBand - 1) * m_poGDS->m_nBlocksPerBand;
497 : }
498 2630870 : return nBlockId;
499 : }
|