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