/src/gdal/frmts/hfa/hfaband.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: Erdas Imagine (.img) Translator |
4 | | * Purpose: Implementation of the HFABand, for accessing one Eimg_Layer. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 1999, Intergraph Corporation |
9 | | * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | #include "hfa_p.h" |
16 | | |
17 | | #include <cerrno> |
18 | | #include <climits> |
19 | | #include <cstddef> |
20 | | #include <cstdio> |
21 | | #include <cstring> |
22 | | #include <algorithm> |
23 | | |
24 | | #include "cpl_conv.h" |
25 | | #include "cpl_error.h" |
26 | | #include "cpl_string.h" |
27 | | #include "cpl_vsi.h" |
28 | | #include "hfa.h" |
29 | | #include "gdal_priv.h" |
30 | | |
31 | | /************************************************************************/ |
32 | | /* HFABand() */ |
33 | | /************************************************************************/ |
34 | | |
35 | | HFABand::HFABand(HFAInfo_t *psInfoIn, HFAEntry *poNodeIn) |
36 | 40.7k | : nBlocks(0), panBlockStart(nullptr), panBlockSize(nullptr), |
37 | 40.7k | panBlockFlag(nullptr), nBlockStart(0), nBlockSize(0), nLayerStackCount(0), |
38 | 40.7k | nLayerStackIndex(0), nPCTColors(-1), padfPCTBins(nullptr), |
39 | 40.7k | psInfo(psInfoIn), fpExternal(nullptr), |
40 | 40.7k | eDataType(static_cast<EPTType>(poNodeIn->GetIntField("pixelType"))), |
41 | 40.7k | poNode(poNodeIn), nBlockXSize(poNodeIn->GetIntField("blockWidth")), |
42 | 40.7k | nBlockYSize(poNodeIn->GetIntField("blockHeight")), |
43 | 40.7k | nWidth(poNodeIn->GetIntField("width")), |
44 | 40.7k | nHeight(poNodeIn->GetIntField("height")), nBlocksPerRow(0), |
45 | 40.7k | nBlocksPerColumn(0), bNoDataSet(false), dfNoData(0.0), |
46 | 40.7k | bOverviewsPending(true), nOverviews(0), papoOverviews(nullptr) |
47 | 40.7k | { |
48 | 40.7k | const int nDataType = poNodeIn->GetIntField("pixelType"); |
49 | | |
50 | 40.7k | apadfPCT[0] = nullptr; |
51 | 40.7k | apadfPCT[1] = nullptr; |
52 | 40.7k | apadfPCT[2] = nullptr; |
53 | 40.7k | apadfPCT[3] = nullptr; |
54 | | |
55 | 40.7k | if (nWidth <= 0 || nHeight <= 0 || nBlockXSize <= 0 || nBlockYSize <= 0) |
56 | 305 | { |
57 | 305 | nWidth = 0; |
58 | 305 | nHeight = 0; |
59 | 305 | CPLError(CE_Failure, CPLE_AppDefined, |
60 | 305 | "HFABand::HFABand : (nWidth <= 0 || nHeight <= 0 || " |
61 | 305 | "nBlockXSize <= 0 || nBlockYSize <= 0)"); |
62 | 305 | return; |
63 | 305 | } |
64 | 40.4k | if (nDataType < EPT_MIN || nDataType > EPT_MAX) |
65 | 13 | { |
66 | 13 | nWidth = 0; |
67 | 13 | nHeight = 0; |
68 | 13 | CPLError(CE_Failure, CPLE_AppDefined, |
69 | 13 | "HFABand::HFABand : nDataType=%d unhandled", nDataType); |
70 | 13 | return; |
71 | 13 | } |
72 | | |
73 | | // TODO(schwehr): Move to initializer list. |
74 | 40.4k | nBlocksPerRow = DIV_ROUND_UP(nWidth, nBlockXSize); |
75 | 40.4k | nBlocksPerColumn = DIV_ROUND_UP(nHeight, nBlockYSize); |
76 | | |
77 | 40.4k | if (nBlocksPerRow > INT_MAX / nBlocksPerColumn) |
78 | 16 | { |
79 | 16 | nWidth = 0; |
80 | 16 | nHeight = 0; |
81 | 16 | CPLError(CE_Failure, CPLE_AppDefined, |
82 | 16 | "HFABand::HFABand : too big dimensions / block size"); |
83 | 16 | return; |
84 | 16 | } |
85 | 40.4k | nBlocks = nBlocksPerRow * nBlocksPerColumn; |
86 | | |
87 | | // Check for nodata. This is really an RDO (ESRI Raster Data Objects?), |
88 | | // not used by Imagine itself. |
89 | 40.4k | HFAEntry *poNDNode = poNode->GetNamedChild("Eimg_NonInitializedValue"); |
90 | | |
91 | 40.4k | if (poNDNode != nullptr) |
92 | 173 | { |
93 | 173 | bNoDataSet = true; |
94 | 173 | dfNoData = poNDNode->GetDoubleField("valueBD"); |
95 | 173 | } |
96 | 40.4k | } |
97 | | |
98 | | /************************************************************************/ |
99 | | /* ~HFABand() */ |
100 | | /************************************************************************/ |
101 | | |
102 | | HFABand::~HFABand() |
103 | | |
104 | 40.7k | { |
105 | 47.3k | for (int iOverview = 0; iOverview < nOverviews; iOverview++) |
106 | 6.57k | delete papoOverviews[iOverview]; |
107 | | |
108 | 40.7k | if (nOverviews > 0) |
109 | 178 | CPLFree(papoOverviews); |
110 | | |
111 | 40.7k | CPLFree(panBlockStart); |
112 | 40.7k | CPLFree(panBlockSize); |
113 | 40.7k | CPLFree(panBlockFlag); |
114 | | |
115 | 40.7k | CPLFree(apadfPCT[0]); |
116 | 40.7k | CPLFree(apadfPCT[1]); |
117 | 40.7k | CPLFree(apadfPCT[2]); |
118 | 40.7k | CPLFree(apadfPCT[3]); |
119 | 40.7k | CPLFree(padfPCTBins); |
120 | | |
121 | 40.7k | if (fpExternal != nullptr) |
122 | 14 | CPL_IGNORE_RET_VAL(VSIFCloseL(fpExternal)); |
123 | 40.7k | } |
124 | | |
125 | | /************************************************************************/ |
126 | | /* LoadOverviews() */ |
127 | | /************************************************************************/ |
128 | | |
129 | | CPLErr HFABand::LoadOverviews() |
130 | | |
131 | 14.1k | { |
132 | 14.1k | if (!bOverviewsPending) |
133 | 6.57k | return CE_None; |
134 | | |
135 | 7.59k | bOverviewsPending = false; |
136 | | |
137 | | // Does this band have overviews? Try to find them. |
138 | 7.59k | HFAEntry *poRRDNames = poNode->GetNamedChild("RRDNamesList"); |
139 | | |
140 | 7.59k | if (poRRDNames != nullptr) |
141 | 462 | { |
142 | | // Limit to 1000 to avoid infinite loop as in |
143 | | // https://oss-fuzz.com/v2/testcase-detail/6206784937132032 |
144 | 98.4k | for (int iName = 0; iName < 1000; iName++) |
145 | 98.4k | { |
146 | 98.4k | char szField[128] = {}; |
147 | 98.4k | snprintf(szField, sizeof(szField), "nameList[%d].string", iName); |
148 | | |
149 | 98.4k | CPLErr eErr = CE_None; |
150 | 98.4k | const char *pszName = poRRDNames->GetStringField(szField, &eErr); |
151 | 98.4k | if (pszName == nullptr || eErr != CE_None) |
152 | 459 | break; |
153 | | |
154 | 97.9k | char *pszFilename = CPLStrdup(pszName); |
155 | 97.9k | char *pszEnd = strstr(pszFilename, "(:"); |
156 | 97.9k | if (pszEnd == nullptr) |
157 | 69.4k | { |
158 | 69.4k | CPLFree(pszFilename); |
159 | 69.4k | continue; |
160 | 69.4k | } |
161 | | |
162 | 28.4k | pszEnd[0] = '\0'; |
163 | | |
164 | 28.4k | char *pszJustFilename = CPLStrdup(CPLGetFilename(pszFilename)); |
165 | 28.4k | HFAInfo_t *psHFA = HFAGetDependent(psInfo, pszJustFilename); |
166 | 28.4k | CPLFree(pszJustFilename); |
167 | | |
168 | | // Try finding the dependent file as this file with the |
169 | | // extension .rrd. This is intended to address problems |
170 | | // with users changing the names of their files. |
171 | 28.4k | if (psHFA == nullptr) |
172 | 19.5k | { |
173 | 19.5k | char *pszBasename = |
174 | 19.5k | CPLStrdup(CPLGetBasenameSafe(psInfo->pszFilename).c_str()); |
175 | | |
176 | 19.5k | pszJustFilename = CPLStrdup( |
177 | 19.5k | CPLFormFilenameSafe(nullptr, pszBasename, "rrd").c_str()); |
178 | 19.5k | CPLDebug("HFA", |
179 | 19.5k | "Failed to find overview file with " |
180 | 19.5k | "expected name, try %s instead.", |
181 | 19.5k | pszJustFilename); |
182 | 19.5k | psHFA = HFAGetDependent(psInfo, pszJustFilename); |
183 | 19.5k | CPLFree(pszJustFilename); |
184 | 19.5k | CPLFree(pszBasename); |
185 | 19.5k | } |
186 | | |
187 | 28.4k | if (psHFA == nullptr) |
188 | 19.5k | { |
189 | 19.5k | CPLFree(pszFilename); |
190 | 19.5k | continue; |
191 | 19.5k | } |
192 | | |
193 | 8.92k | char *pszPath = pszEnd + 2; |
194 | 8.92k | { |
195 | 8.92k | const int nPathLen = static_cast<int>(strlen(pszPath)); |
196 | 8.92k | if (pszPath[nPathLen - 1] == ')') |
197 | 81 | pszPath[nPathLen - 1] = '\0'; |
198 | 8.92k | } |
199 | | |
200 | 71.4k | for (int i = 0; pszPath[i] != '\0'; i++) |
201 | 62.5k | { |
202 | 62.5k | if (pszPath[i] == ':') |
203 | 2.12k | pszPath[i] = '.'; |
204 | 62.5k | } |
205 | | |
206 | 8.92k | HFAEntry *poOvEntry = psHFA->poRoot->GetNamedChild(pszPath); |
207 | 8.92k | CPLFree(pszFilename); |
208 | | |
209 | 8.92k | if (poOvEntry == nullptr) |
210 | 2.41k | continue; |
211 | | |
212 | | // We have an overview node. Instantiate a HFABand from it, and |
213 | | // add to the list. |
214 | 6.50k | papoOverviews = static_cast<HFABand **>( |
215 | 6.50k | CPLRealloc(papoOverviews, sizeof(void *) * ++nOverviews)); |
216 | 6.50k | papoOverviews[nOverviews - 1] = new HFABand(psHFA, poOvEntry); |
217 | 6.50k | if (papoOverviews[nOverviews - 1]->nWidth == 0) |
218 | 2 | { |
219 | 2 | nWidth = 0; |
220 | 2 | nHeight = 0; |
221 | 2 | delete papoOverviews[nOverviews - 1]; |
222 | 2 | papoOverviews[nOverviews - 1] = nullptr; |
223 | 2 | return CE_None; |
224 | 2 | } |
225 | 6.50k | } |
226 | 462 | } |
227 | | |
228 | | // If there are no overviews mentioned in this file, probe for |
229 | | // an .rrd file anyways. |
230 | 7.59k | HFAEntry *poBandProxyNode = poNode; |
231 | 7.59k | HFAInfo_t *psOvHFA = psInfo; |
232 | | |
233 | 7.59k | if (nOverviews == 0 && |
234 | 7.48k | EQUAL(CPLGetExtensionSafe(psInfo->pszFilename).c_str(), "aux")) |
235 | 0 | { |
236 | 0 | const CPLString osRRDFilename = |
237 | 0 | CPLResetExtensionSafe(psInfo->pszFilename, "rrd"); |
238 | 0 | const CPLString osFullRRD = |
239 | 0 | CPLFormFilenameSafe(psInfo->pszPath, osRRDFilename, nullptr); |
240 | 0 | VSIStatBufL sStatBuf; |
241 | |
|
242 | 0 | if (VSIStatL(osFullRRD, &sStatBuf) == 0) |
243 | 0 | { |
244 | 0 | psOvHFA = HFAGetDependent(psInfo, osRRDFilename); |
245 | 0 | if (psOvHFA) |
246 | 0 | poBandProxyNode = |
247 | 0 | psOvHFA->poRoot->GetNamedChild(poNode->GetName()); |
248 | 0 | else |
249 | 0 | psOvHFA = psInfo; |
250 | 0 | } |
251 | 0 | } |
252 | | |
253 | | // If there are no named overviews, try looking for unnamed |
254 | | // overviews within the same layer, as occurs in floodplain.img |
255 | | // for instance, or in the not-referenced rrd mentioned in #3463. |
256 | 7.59k | if (nOverviews == 0 && poBandProxyNode != nullptr) |
257 | 7.48k | { |
258 | 7.48k | for (HFAEntry *poChild = poBandProxyNode->GetChild(); |
259 | 40.4k | poChild != nullptr; poChild = poChild->GetNext()) |
260 | 33.0k | { |
261 | 33.0k | if (EQUAL(poChild->GetType(), "Eimg_Layer_SubSample")) |
262 | 73 | { |
263 | 73 | papoOverviews = static_cast<HFABand **>( |
264 | 73 | CPLRealloc(papoOverviews, sizeof(void *) * ++nOverviews)); |
265 | 73 | papoOverviews[nOverviews - 1] = new HFABand(psOvHFA, poChild); |
266 | 73 | if (papoOverviews[nOverviews - 1]->nWidth == 0) |
267 | 8 | { |
268 | 8 | nWidth = 0; |
269 | 8 | nHeight = 0; |
270 | 8 | delete papoOverviews[nOverviews - 1]; |
271 | 8 | papoOverviews[nOverviews - 1] = nullptr; |
272 | 8 | return CE_None; |
273 | 8 | } |
274 | 73 | } |
275 | 33.0k | } |
276 | | |
277 | | // TODO(schwehr): Can this use std::sort? |
278 | | // Bubble sort into biggest to smallest order. |
279 | 7.54k | for (int i1 = 0; i1 < nOverviews; i1++) |
280 | 65 | { |
281 | 65 | for (int i2 = 0; i2 < nOverviews - 1; i2++) |
282 | 0 | { |
283 | 0 | if (papoOverviews[i2]->nWidth < papoOverviews[i2 + 1]->nWidth) |
284 | 0 | { |
285 | | // TODO(schwehr): Use std::swap. |
286 | 0 | HFABand *poTemp = papoOverviews[i2 + 1]; |
287 | 0 | papoOverviews[i2 + 1] = papoOverviews[i2]; |
288 | 0 | papoOverviews[i2] = poTemp; |
289 | 0 | } |
290 | 0 | } |
291 | 65 | } |
292 | 7.47k | } |
293 | 7.58k | return CE_None; |
294 | 7.59k | } |
295 | | |
296 | | /************************************************************************/ |
297 | | /* LoadBlockInfo() */ |
298 | | /************************************************************************/ |
299 | | |
300 | | CPLErr HFABand::LoadBlockInfo() |
301 | | |
302 | 32.4k | { |
303 | 32.4k | if (panBlockFlag != nullptr) |
304 | 17.9k | return CE_None; |
305 | | |
306 | 14.4k | HFAEntry *poDMS = poNode->GetNamedChild("RasterDMS"); |
307 | 14.4k | if (poDMS == nullptr) |
308 | 1.51k | { |
309 | 1.51k | if (poNode->GetNamedChild("ExternalRasterDMS") != nullptr) |
310 | 120 | return LoadExternalBlockInfo(); |
311 | | |
312 | 1.39k | CPLError(CE_Failure, CPLE_AppDefined, |
313 | 1.39k | "Can't find RasterDMS field in Eimg_Layer with block list."); |
314 | | |
315 | 1.39k | return CE_Failure; |
316 | 1.51k | } |
317 | | |
318 | 12.9k | if (sizeof(vsi_l_offset) + 2 * sizeof(int) > |
319 | 12.9k | (~(size_t)0) / static_cast<unsigned int>(nBlocks)) |
320 | 0 | { |
321 | 0 | CPLError(CE_Failure, CPLE_OutOfMemory, "Too many blocks"); |
322 | 0 | return CE_Failure; |
323 | 0 | } |
324 | 12.9k | const int MAX_INITIAL_BLOCKS = 1000 * 1000; |
325 | 12.9k | const int nInitBlocks = std::min(nBlocks, MAX_INITIAL_BLOCKS); |
326 | 12.9k | panBlockStart = static_cast<vsi_l_offset *>( |
327 | 12.9k | VSI_MALLOC2_VERBOSE(sizeof(vsi_l_offset), nInitBlocks)); |
328 | 12.9k | panBlockSize = |
329 | 12.9k | static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nInitBlocks)); |
330 | 12.9k | panBlockFlag = |
331 | 12.9k | static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nInitBlocks)); |
332 | | |
333 | 12.9k | if (panBlockStart == nullptr || panBlockSize == nullptr || |
334 | 12.9k | panBlockFlag == nullptr) |
335 | 0 | { |
336 | 0 | CPLFree(panBlockStart); |
337 | 0 | CPLFree(panBlockSize); |
338 | 0 | CPLFree(panBlockFlag); |
339 | 0 | panBlockStart = nullptr; |
340 | 0 | panBlockSize = nullptr; |
341 | 0 | panBlockFlag = nullptr; |
342 | 0 | return CE_Failure; |
343 | 0 | } |
344 | | |
345 | 97.6k | for (int iBlock = 0; iBlock < nBlocks; iBlock++) |
346 | 84.8k | { |
347 | 84.8k | CPLErr eErr = CE_None; |
348 | | |
349 | 84.8k | if (iBlock == MAX_INITIAL_BLOCKS) |
350 | 0 | { |
351 | 0 | vsi_l_offset *panBlockStartNew = |
352 | 0 | static_cast<vsi_l_offset *>(VSI_REALLOC_VERBOSE( |
353 | 0 | panBlockStart, sizeof(vsi_l_offset) * nBlocks)); |
354 | 0 | if (panBlockStartNew == nullptr) |
355 | 0 | { |
356 | 0 | CPLFree(panBlockStart); |
357 | 0 | CPLFree(panBlockSize); |
358 | 0 | CPLFree(panBlockFlag); |
359 | 0 | panBlockStart = nullptr; |
360 | 0 | panBlockSize = nullptr; |
361 | 0 | panBlockFlag = nullptr; |
362 | 0 | return CE_Failure; |
363 | 0 | } |
364 | 0 | panBlockStart = panBlockStartNew; |
365 | |
|
366 | 0 | int *panBlockSizeNew = static_cast<int *>( |
367 | 0 | VSI_REALLOC_VERBOSE(panBlockSize, sizeof(int) * nBlocks)); |
368 | 0 | if (panBlockSizeNew == nullptr) |
369 | 0 | { |
370 | 0 | CPLFree(panBlockStart); |
371 | 0 | CPLFree(panBlockSize); |
372 | 0 | CPLFree(panBlockFlag); |
373 | 0 | panBlockStart = nullptr; |
374 | 0 | panBlockSize = nullptr; |
375 | 0 | panBlockFlag = nullptr; |
376 | 0 | return CE_Failure; |
377 | 0 | } |
378 | 0 | panBlockSize = panBlockSizeNew; |
379 | |
|
380 | 0 | int *panBlockFlagNew = static_cast<int *>( |
381 | 0 | VSI_REALLOC_VERBOSE(panBlockFlag, sizeof(int) * nBlocks)); |
382 | 0 | if (panBlockFlagNew == nullptr) |
383 | 0 | { |
384 | 0 | CPLFree(panBlockStart); |
385 | 0 | CPLFree(panBlockSize); |
386 | 0 | CPLFree(panBlockFlag); |
387 | 0 | panBlockStart = nullptr; |
388 | 0 | panBlockSize = nullptr; |
389 | 0 | panBlockFlag = nullptr; |
390 | 0 | return CE_Failure; |
391 | 0 | } |
392 | 0 | panBlockFlag = panBlockFlagNew; |
393 | 0 | } |
394 | | |
395 | 84.8k | char szVarName[64] = {}; |
396 | 84.8k | snprintf(szVarName, sizeof(szVarName), "blockinfo[%d].offset", iBlock); |
397 | 84.8k | panBlockStart[iBlock] = |
398 | 84.8k | static_cast<GUInt32>(poDMS->GetIntField(szVarName, &eErr)); |
399 | 84.8k | if (eErr == CE_Failure) |
400 | 180 | { |
401 | 180 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName); |
402 | 180 | return eErr; |
403 | 180 | } |
404 | | |
405 | 84.7k | snprintf(szVarName, sizeof(szVarName), "blockinfo[%d].size", iBlock); |
406 | 84.7k | panBlockSize[iBlock] = poDMS->GetIntField(szVarName, &eErr); |
407 | 84.7k | if (eErr == CE_Failure) |
408 | 8 | { |
409 | 8 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName); |
410 | 8 | return eErr; |
411 | 8 | } |
412 | 84.6k | if (panBlockSize[iBlock] < 0) |
413 | 27 | { |
414 | 27 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid block size"); |
415 | 27 | return CE_Failure; |
416 | 27 | } |
417 | | |
418 | 84.6k | snprintf(szVarName, sizeof(szVarName), "blockinfo[%d].logvalid", |
419 | 84.6k | iBlock); |
420 | 84.6k | const int nLogvalid = poDMS->GetIntField(szVarName, &eErr); |
421 | 84.6k | if (eErr == CE_Failure) |
422 | 16 | { |
423 | 16 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName); |
424 | 16 | return eErr; |
425 | 16 | } |
426 | | |
427 | 84.6k | snprintf(szVarName, sizeof(szVarName), "blockinfo[%d].compressionType", |
428 | 84.6k | iBlock); |
429 | 84.6k | const int nCompressType = poDMS->GetIntField(szVarName, &eErr); |
430 | 84.6k | if (eErr == CE_Failure) |
431 | 11 | { |
432 | 11 | CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName); |
433 | 11 | return eErr; |
434 | 11 | } |
435 | | |
436 | 84.6k | panBlockFlag[iBlock] = 0; |
437 | 84.6k | if (nLogvalid) |
438 | 58.1k | panBlockFlag[iBlock] |= BFLG_VALID; |
439 | 84.6k | if (nCompressType != 0) |
440 | 58.2k | panBlockFlag[iBlock] |= BFLG_COMPRESSED; |
441 | 84.6k | } |
442 | | |
443 | 12.7k | return CE_None; |
444 | 12.9k | } |
445 | | |
446 | | /************************************************************************/ |
447 | | /* LoadExternalBlockInfo() */ |
448 | | /************************************************************************/ |
449 | | |
450 | | CPLErr HFABand::LoadExternalBlockInfo() |
451 | | |
452 | 120 | { |
453 | 120 | if (panBlockFlag != nullptr) |
454 | 0 | return CE_None; |
455 | | |
456 | | // Get the info structure. |
457 | 120 | HFAEntry *poDMS = poNode->GetNamedChild("ExternalRasterDMS"); |
458 | 120 | CPLAssert(poDMS != nullptr); |
459 | | |
460 | 120 | nLayerStackCount = poDMS->GetIntField("layerStackCount"); |
461 | 120 | nLayerStackIndex = poDMS->GetIntField("layerStackIndex"); |
462 | | |
463 | | // Open raw data file. |
464 | 120 | const std::string osFullFilename = HFAGetIGEFilename(psInfo); |
465 | 120 | if (osFullFilename.empty()) |
466 | 51 | { |
467 | 51 | CPLError(CE_Failure, CPLE_OpenFailed, |
468 | 51 | "Cannot find external data file name"); |
469 | 51 | return CE_Failure; |
470 | 51 | } |
471 | | |
472 | 69 | if (psInfo->eAccess == HFA_ReadOnly) |
473 | 69 | fpExternal = VSIFOpenL(osFullFilename.c_str(), "rb"); |
474 | 0 | else |
475 | 0 | fpExternal = VSIFOpenL(osFullFilename.c_str(), "r+b"); |
476 | 69 | if (fpExternal == nullptr) |
477 | 55 | { |
478 | 55 | CPLError(CE_Failure, CPLE_OpenFailed, |
479 | 55 | "Unable to open external data file: %s", |
480 | 55 | osFullFilename.c_str()); |
481 | 55 | return CE_Failure; |
482 | 55 | } |
483 | | |
484 | | // Verify header. |
485 | 14 | char szHeader[49] = {}; |
486 | | |
487 | 14 | if (VSIFReadL(szHeader, sizeof(szHeader), 1, fpExternal) != 1 || |
488 | 14 | !STARTS_WITH(szHeader, "ERDAS_IMG_EXTERNAL_RASTER")) |
489 | 14 | { |
490 | 14 | CPLError(CE_Failure, CPLE_AppDefined, |
491 | 14 | "Raw data file %s appears to be corrupt.", |
492 | 14 | osFullFilename.c_str()); |
493 | 14 | return CE_Failure; |
494 | 14 | } |
495 | | |
496 | | // Allocate blockmap. |
497 | 0 | panBlockFlag = |
498 | 0 | static_cast<int *>(VSI_MALLOC2_VERBOSE(sizeof(int), nBlocks)); |
499 | 0 | if (panBlockFlag == nullptr) |
500 | 0 | { |
501 | 0 | return CE_Failure; |
502 | 0 | } |
503 | | |
504 | | // Load the validity bitmap. |
505 | 0 | const int nBytesPerRow = (nBlocksPerRow + 7) / 8; |
506 | 0 | unsigned char *pabyBlockMap = static_cast<unsigned char *>( |
507 | 0 | VSI_MALLOC_VERBOSE(nBytesPerRow * nBlocksPerColumn + 20)); |
508 | 0 | if (pabyBlockMap == nullptr) |
509 | 0 | { |
510 | 0 | return CE_Failure; |
511 | 0 | } |
512 | | |
513 | 0 | if (VSIFSeekL(fpExternal, |
514 | 0 | static_cast<vsi_l_offset>( |
515 | 0 | poDMS->GetBigIntField("layerStackValidFlagsOffset")), |
516 | 0 | SEEK_SET) < 0 || |
517 | 0 | VSIFReadL(pabyBlockMap, nBytesPerRow * nBlocksPerColumn + 20, 1, |
518 | 0 | fpExternal) != 1) |
519 | 0 | { |
520 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Failed to read block validity map."); |
521 | 0 | return CE_Failure; |
522 | 0 | } |
523 | | |
524 | | // Establish block information. Block position is computed |
525 | | // from data base address. Blocks are never compressed. |
526 | | // Validity is determined from the validity bitmap. |
527 | | |
528 | 0 | nBlockStart = poDMS->GetBigIntField("layerStackDataOffset"); |
529 | 0 | nBlockSize = (nBlockXSize * static_cast<vsi_l_offset>(nBlockYSize) * |
530 | 0 | HFAGetDataTypeBits(eDataType) + |
531 | 0 | 7) / |
532 | 0 | 8; |
533 | |
|
534 | 0 | for (int iBlock = 0; iBlock < nBlocks; iBlock++) |
535 | 0 | { |
536 | 0 | const int nColumn = iBlock % nBlocksPerRow; |
537 | 0 | const int nRow = iBlock / nBlocksPerRow; |
538 | 0 | const int nBit = nRow * nBytesPerRow * 8 + nColumn + 20 * 8; |
539 | |
|
540 | 0 | if ((pabyBlockMap[nBit >> 3] >> (nBit & 7)) & 0x1) |
541 | 0 | panBlockFlag[iBlock] = BFLG_VALID; |
542 | 0 | else |
543 | 0 | panBlockFlag[iBlock] = 0; |
544 | 0 | } |
545 | |
|
546 | 0 | CPLFree(pabyBlockMap); |
547 | |
|
548 | 0 | return CE_None; |
549 | 0 | } |
550 | | |
551 | | /************************************************************************/ |
552 | | /* UncompressBlock() */ |
553 | | /* */ |
554 | | /* Uncompress ESRI Grid compression format block. */ |
555 | | /************************************************************************/ |
556 | | |
557 | | // TODO(schwehr): Get rid of this macro without a goto. |
558 | | #define CHECK_ENOUGH_BYTES(n) \ |
559 | 1.26M | if (nSrcBytes < (n)) \ |
560 | 1.26M | { \ |
561 | 294 | CPLError(CE_Failure, CPLE_AppDefined, \ |
562 | 294 | "Not enough bytes in compressed block"); \ |
563 | 294 | return CE_Failure; \ |
564 | 294 | } |
565 | | |
566 | | static CPLErr UncompressBlock(GByte *pabyCData, int nSrcBytes, GByte *pabyDest, |
567 | | int nMaxPixels, EPTType eDataType) |
568 | | |
569 | 7.47k | { |
570 | 7.47k | CHECK_ENOUGH_BYTES(13); |
571 | | |
572 | 7.45k | const GUInt32 nDataMin = CPL_LSBUINT32PTR(pabyCData); |
573 | 7.45k | const GInt32 nNumRuns = CPL_LSBSINT32PTR(pabyCData + 4); |
574 | 7.45k | const GInt32 nDataOffset = CPL_LSBSINT32PTR(pabyCData + 8); |
575 | | |
576 | 7.45k | const int nNumBits = pabyCData[12]; |
577 | | |
578 | | // If this is not run length encoded, but just reduced |
579 | | // precision, handle it now. |
580 | | |
581 | 7.45k | int nPixelsOutput = 0; |
582 | 7.45k | GByte *pabyValues = nullptr; |
583 | 7.45k | int nValueBitOffset = 0; |
584 | | |
585 | 7.45k | if (nNumRuns == -1) |
586 | 253 | { |
587 | 253 | pabyValues = pabyCData + 13; |
588 | 253 | nValueBitOffset = 0; |
589 | | |
590 | 253 | if (nNumBits > INT_MAX / nMaxPixels || |
591 | 253 | nNumBits * nMaxPixels > INT_MAX - 7 || |
592 | 253 | (nNumBits * nMaxPixels + 7) / 8 > INT_MAX - 13) |
593 | 0 | { |
594 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
595 | 0 | "Integer overflow : nNumBits * nMaxPixels + 7"); |
596 | 0 | return CE_Failure; |
597 | 0 | } |
598 | 253 | CHECK_ENOUGH_BYTES(13 + (nNumBits * nMaxPixels + 7) / 8); |
599 | | |
600 | | // Loop over block pixels. |
601 | 11.2M | for (nPixelsOutput = 0; nPixelsOutput < nMaxPixels; nPixelsOutput++) |
602 | 11.2M | { |
603 | | // Extract the data value in a way that depends on the number |
604 | | // of bits in it. |
605 | | |
606 | 11.2M | int nRawValue = 0; |
607 | | |
608 | 11.2M | if (nNumBits == 0) |
609 | 10.9M | { |
610 | | // nRawValue = 0; |
611 | 10.9M | } |
612 | 363k | else if (nNumBits == 1) |
613 | 20.6k | { |
614 | 20.6k | nRawValue = (pabyValues[nValueBitOffset >> 3] >> |
615 | 20.6k | (nValueBitOffset & 7)) & |
616 | 20.6k | 0x1; |
617 | 20.6k | nValueBitOffset++; |
618 | 20.6k | } |
619 | 343k | else if (nNumBits == 2) |
620 | 646 | { |
621 | 646 | nRawValue = (pabyValues[nValueBitOffset >> 3] >> |
622 | 646 | (nValueBitOffset & 7)) & |
623 | 646 | 0x3; |
624 | 646 | nValueBitOffset += 2; |
625 | 646 | } |
626 | 342k | else if (nNumBits == 4) |
627 | 1.29k | { |
628 | 1.29k | nRawValue = (pabyValues[nValueBitOffset >> 3] >> |
629 | 1.29k | (nValueBitOffset & 7)) & |
630 | 1.29k | 0xf; |
631 | 1.29k | nValueBitOffset += 4; |
632 | 1.29k | } |
633 | 341k | else if (nNumBits == 8) |
634 | 152k | { |
635 | 152k | nRawValue = *pabyValues; |
636 | 152k | pabyValues++; |
637 | 152k | } |
638 | 188k | else if (nNumBits == 16) |
639 | 188k | { |
640 | 188k | nRawValue = 256 * *(pabyValues++); |
641 | 188k | nRawValue += *(pabyValues++); |
642 | 188k | } |
643 | 75 | else if (nNumBits == 32) |
644 | 65 | { |
645 | 65 | memcpy(&nRawValue, pabyValues, 4); |
646 | 65 | CPL_MSBPTR32(&nRawValue); |
647 | 65 | pabyValues += 4; |
648 | 65 | } |
649 | 10 | else |
650 | 10 | { |
651 | 10 | CPLError(CE_Failure, CPLE_NotSupported, |
652 | 10 | "Unsupported nNumBits value: %d", nNumBits); |
653 | 10 | return CE_Failure; |
654 | 10 | } |
655 | | |
656 | | // Offset by the minimum value. |
657 | 11.2M | const int nDataValue = CPLUnsanitizedAdd<int>(nRawValue, nDataMin); |
658 | | |
659 | | // Now apply to the output buffer in a type specific way. |
660 | 11.2M | if (eDataType == EPT_u8) |
661 | 175k | { |
662 | 175k | pabyDest[nPixelsOutput] = static_cast<GByte>(nDataValue); |
663 | 175k | } |
664 | 11.1M | else if (eDataType == EPT_u1) |
665 | 28.8k | { |
666 | 28.8k | if (nDataValue == 1) |
667 | 6.23k | pabyDest[nPixelsOutput >> 3] |= |
668 | 6.23k | (1 << (nPixelsOutput & 0x7)); |
669 | 22.6k | else |
670 | 22.6k | pabyDest[nPixelsOutput >> 3] &= |
671 | 22.6k | ~(1 << (nPixelsOutput & 0x7)); |
672 | 28.8k | } |
673 | 11.0M | else if (eDataType == EPT_u2) |
674 | 9.49M | { |
675 | | // nDataValue & 0x3 is just to avoid UBSAN warning on shifting |
676 | | // negative values |
677 | 9.49M | if ((nPixelsOutput & 0x3) == 0) |
678 | 2.37M | pabyDest[nPixelsOutput >> 2] = |
679 | 2.37M | static_cast<GByte>(nDataValue); |
680 | 7.11M | else if ((nPixelsOutput & 0x3) == 1) |
681 | 2.37M | pabyDest[nPixelsOutput >> 2] |= |
682 | 2.37M | static_cast<GByte>((nDataValue & 0x3) << 2); |
683 | 4.74M | else if ((nPixelsOutput & 0x3) == 2) |
684 | 2.37M | pabyDest[nPixelsOutput >> 2] |= |
685 | 2.37M | static_cast<GByte>((nDataValue & 0x3) << 4); |
686 | 2.37M | else |
687 | 2.37M | pabyDest[nPixelsOutput >> 2] |= |
688 | 2.37M | static_cast<GByte>((nDataValue & 0x3) << 6); |
689 | 9.49M | } |
690 | 1.59M | else if (eDataType == EPT_u4) |
691 | 9.70k | { |
692 | | // nDataValue & 0xF is just to avoid UBSAN warning on shifting |
693 | | // negative values |
694 | 9.70k | if ((nPixelsOutput & 0x1) == 0) |
695 | 4.85k | pabyDest[nPixelsOutput >> 1] = |
696 | 4.85k | static_cast<GByte>(nDataValue); |
697 | 4.84k | else |
698 | 4.84k | pabyDest[nPixelsOutput >> 1] |= |
699 | 4.84k | static_cast<GByte>((nDataValue & 0xF) << 4); |
700 | 9.70k | } |
701 | 1.58M | else if (eDataType == EPT_s8) |
702 | 331 | { |
703 | 331 | reinterpret_cast<GInt8 *>(pabyDest)[nPixelsOutput] = |
704 | 331 | static_cast<GInt8>(nDataValue); |
705 | 331 | } |
706 | 1.58M | else if (eDataType == EPT_u16) |
707 | 358 | { |
708 | 358 | reinterpret_cast<GUInt16 *>(pabyDest)[nPixelsOutput] = |
709 | 358 | static_cast<GUInt16>(nDataValue); |
710 | 358 | } |
711 | 1.58M | else if (eDataType == EPT_s16) |
712 | 450 | { |
713 | 450 | reinterpret_cast<GInt16 *>(pabyDest)[nPixelsOutput] = |
714 | 450 | static_cast<GInt16>(nDataValue); |
715 | 450 | } |
716 | 1.58M | else if (eDataType == EPT_s32) |
717 | 151k | { |
718 | 151k | reinterpret_cast<GInt32 *>(pabyDest)[nPixelsOutput] = |
719 | 151k | nDataValue; |
720 | 151k | } |
721 | 1.43M | else if (eDataType == EPT_u32) |
722 | 786k | { |
723 | 786k | reinterpret_cast<GUInt32 *>(pabyDest)[nPixelsOutput] = |
724 | 786k | nDataValue; |
725 | 786k | } |
726 | 650k | else if (eDataType == EPT_f32) |
727 | 650k | { |
728 | | // Note, floating point values are handled as if they were |
729 | | // signed 32-bit integers (bug #1000). |
730 | 650k | memcpy(&(reinterpret_cast<float *>(pabyDest)[nPixelsOutput]), |
731 | 650k | &nDataValue, sizeof(float)); |
732 | 650k | } |
733 | 10 | else |
734 | 10 | { |
735 | 10 | CPLError( |
736 | 10 | CE_Failure, CPLE_AppDefined, |
737 | 10 | "Attempt to uncompress an unsupported pixel data type."); |
738 | 10 | return CE_Failure; |
739 | 10 | } |
740 | 11.2M | } |
741 | | |
742 | 212 | return CE_None; |
743 | 232 | } |
744 | | |
745 | | // Establish data pointers for runs. |
746 | 7.19k | if (nNumRuns < 0 || nDataOffset < 0) |
747 | 64 | { |
748 | 64 | CPLError(CE_Failure, CPLE_AppDefined, "nNumRuns=%d, nDataOffset=%d", |
749 | 64 | nNumRuns, nDataOffset); |
750 | 64 | return CE_Failure; |
751 | 64 | } |
752 | | |
753 | 7.13k | if (nNumRuns != 0 && |
754 | 5.76k | (nNumBits > INT_MAX / nNumRuns || nNumBits * nNumRuns > INT_MAX - 7 || |
755 | 5.68k | (nNumBits * nNumRuns + 7) / 8 > INT_MAX - nDataOffset)) |
756 | 83 | { |
757 | 83 | CPLError(CE_Failure, CPLE_AppDefined, |
758 | 83 | "Integer overflow: nDataOffset + (nNumBits * nNumRuns + 7)/8"); |
759 | 83 | return CE_Failure; |
760 | 83 | } |
761 | 7.05k | CHECK_ENOUGH_BYTES(nDataOffset + (nNumBits * nNumRuns + 7) / 8); |
762 | | |
763 | 6.96k | GByte *pabyCounter = pabyCData + 13; |
764 | 6.96k | int nCounterOffset = 13; |
765 | 6.96k | pabyValues = pabyCData + nDataOffset; |
766 | 6.96k | nValueBitOffset = 0; |
767 | | |
768 | | // Loop over runs. |
769 | 1.00M | for (int iRun = 0; iRun < nNumRuns; iRun++) |
770 | 999k | { |
771 | 999k | int nRepeatCount = 0; |
772 | | |
773 | | // Get the repeat count. This can be stored as one, two, three |
774 | | // or four bytes depending on the low order two bits of the |
775 | | // first byte. |
776 | 999k | CHECK_ENOUGH_BYTES(nCounterOffset + 1); |
777 | 998k | if ((*pabyCounter & 0xc0) == 0x00) |
778 | 750k | { |
779 | 750k | nRepeatCount = (*(pabyCounter++)) & 0x3f; |
780 | 750k | nCounterOffset++; |
781 | 750k | } |
782 | 248k | else if (((*pabyCounter) & 0xc0) == 0x40) |
783 | 190k | { |
784 | 190k | CHECK_ENOUGH_BYTES(nCounterOffset + 2); |
785 | 190k | nRepeatCount = (*(pabyCounter++)) & 0x3f; |
786 | 190k | nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); |
787 | 190k | nCounterOffset += 2; |
788 | 190k | } |
789 | 58.0k | else if (((*pabyCounter) & 0xc0) == 0x80) |
790 | 33.8k | { |
791 | 33.8k | CHECK_ENOUGH_BYTES(nCounterOffset + 3); |
792 | 33.8k | nRepeatCount = (*(pabyCounter++)) & 0x3f; |
793 | 33.8k | nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); |
794 | 33.8k | nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); |
795 | 33.8k | nCounterOffset += 3; |
796 | 33.8k | } |
797 | 24.2k | else if (((*pabyCounter) & 0xc0) == 0xc0) |
798 | 24.2k | { |
799 | 24.2k | CHECK_ENOUGH_BYTES(nCounterOffset + 4); |
800 | 24.1k | nRepeatCount = (*(pabyCounter++)) & 0x3f; |
801 | 24.1k | nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); |
802 | 24.1k | nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); |
803 | 24.1k | nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++)); |
804 | 24.1k | nCounterOffset += 4; |
805 | 24.1k | } |
806 | | |
807 | | // Extract the data value in a way that depends on the number |
808 | | // of bits in it. |
809 | 998k | int nDataValue = 0; |
810 | | |
811 | 998k | if (nNumBits == 0) |
812 | 405k | { |
813 | | // nDataValue = 0; |
814 | 405k | } |
815 | 593k | else if (nNumBits == 1) |
816 | 66.2k | { |
817 | 66.2k | nDataValue = |
818 | 66.2k | (pabyValues[nValueBitOffset >> 3] >> (nValueBitOffset & 7)) & |
819 | 66.2k | 0x1; |
820 | 66.2k | nValueBitOffset++; |
821 | 66.2k | } |
822 | 527k | else if (nNumBits == 2) |
823 | 27.3k | { |
824 | 27.3k | nDataValue = |
825 | 27.3k | (pabyValues[nValueBitOffset >> 3] >> (nValueBitOffset & 7)) & |
826 | 27.3k | 0x3; |
827 | 27.3k | nValueBitOffset += 2; |
828 | 27.3k | } |
829 | 499k | else if (nNumBits == 4) |
830 | 19.2k | { |
831 | 19.2k | nDataValue = |
832 | 19.2k | (pabyValues[nValueBitOffset >> 3] >> (nValueBitOffset & 7)) & |
833 | 19.2k | 0xf; |
834 | 19.2k | nValueBitOffset += 4; |
835 | 19.2k | } |
836 | 480k | else if (nNumBits == 8) |
837 | 176k | { |
838 | 176k | nDataValue = *pabyValues; |
839 | 176k | pabyValues++; |
840 | 176k | } |
841 | 304k | else if (nNumBits == 16) |
842 | 125k | { |
843 | 125k | nDataValue = 256 * *(pabyValues++); |
844 | 125k | nDataValue += *(pabyValues++); |
845 | 125k | } |
846 | 179k | else if (nNumBits == 32) |
847 | 178k | { |
848 | 178k | memcpy(&nDataValue, pabyValues, 4); |
849 | 178k | CPL_MSBPTR32(&nDataValue); |
850 | 178k | pabyValues += 4; |
851 | 178k | } |
852 | 11 | else |
853 | 11 | { |
854 | 11 | CPLError(CE_Failure, CPLE_NotSupported, "nNumBits = %d", nNumBits); |
855 | 11 | return CE_Failure; |
856 | 11 | } |
857 | | |
858 | | // Offset by the minimum value. |
859 | 998k | nDataValue = CPLUnsanitizedAdd<int>(nDataValue, nDataMin); |
860 | | |
861 | | // Now apply to the output buffer in a type specific way. |
862 | 998k | if (nRepeatCount > INT_MAX - nPixelsOutput || |
863 | 998k | nPixelsOutput + nRepeatCount > nMaxPixels) |
864 | 400k | { |
865 | 400k | CPLDebug("HFA", "Repeat count too big: %d", nRepeatCount); |
866 | 400k | nRepeatCount = nMaxPixels - nPixelsOutput; |
867 | 400k | } |
868 | | |
869 | 998k | if (eDataType == EPT_u8) |
870 | 261k | { |
871 | 18.5M | for (int i = 0; i < nRepeatCount; i++) |
872 | 18.3M | { |
873 | | #if DEBUG_VERBOSE |
874 | | // TODO(schwehr): Do something smarter with out-of-range data. |
875 | | // Bad data can trigger this assert. r23498 |
876 | | CPLAssert(nDataValue < 256); |
877 | | #endif |
878 | 18.3M | pabyDest[nPixelsOutput++] = static_cast<GByte>(nDataValue); |
879 | 18.3M | } |
880 | 261k | } |
881 | 737k | else if (eDataType == EPT_u16) |
882 | 14.9k | { |
883 | 28.1M | for (int i = 0; i < nRepeatCount; i++) |
884 | 28.1M | { |
885 | | #if DEBUG_VERBOSE |
886 | | CPLAssert(nDataValue >= 0); |
887 | | CPLAssert(nDataValue < 65536); |
888 | | #endif |
889 | 28.1M | reinterpret_cast<GUInt16 *>(pabyDest)[nPixelsOutput++] = |
890 | 28.1M | static_cast<GUInt16>(nDataValue); |
891 | 28.1M | } |
892 | 14.9k | } |
893 | 722k | else if (eDataType == EPT_s8) |
894 | 18.9k | { |
895 | 17.0M | for (int i = 0; i < nRepeatCount; i++) |
896 | 16.9M | { |
897 | | #if DEBUG_VERBOSE |
898 | | // TODO(schwehr): Do something smarter with out-of-range data. |
899 | | // Bad data can trigger this assert. r23498 |
900 | | CPLAssert(nDataValue >= -127); |
901 | | CPLAssert(nDataValue < 128); |
902 | | #endif |
903 | 16.9M | ((GByte *)pabyDest)[nPixelsOutput++] = |
904 | 16.9M | static_cast<GByte>(nDataValue); |
905 | 16.9M | } |
906 | 18.9k | } |
907 | 703k | else if (eDataType == EPT_s16) |
908 | 25.2k | { |
909 | 20.0M | for (int i = 0; i < nRepeatCount; i++) |
910 | 20.0M | { |
911 | | #if DEBUG_VERBOSE |
912 | | // TODO(schwehr): Do something smarter with out-of-range data. |
913 | | // Bad data can trigger this assert. r23498 |
914 | | CPLAssert(nDataValue >= -32768); |
915 | | CPLAssert(nDataValue < 32768); |
916 | | #endif |
917 | 20.0M | reinterpret_cast<GInt16 *>(pabyDest)[nPixelsOutput++] = |
918 | 20.0M | static_cast<GInt16>(nDataValue); |
919 | 20.0M | } |
920 | 25.2k | } |
921 | 678k | else if (eDataType == EPT_u32) |
922 | 18.0k | { |
923 | 14.9M | for (int i = 0; i < nRepeatCount; i++) |
924 | 14.9M | { |
925 | | #if DEBUG_VERBOSE |
926 | | // TODO(schwehr): Do something smarter with out-of-range data. |
927 | | // Bad data can trigger this assert. r23498 |
928 | | CPLAssert(nDataValue >= 0); |
929 | | #endif |
930 | 14.9M | reinterpret_cast<GUInt32 *>(pabyDest)[nPixelsOutput++] = |
931 | 14.9M | static_cast<GUInt32>(nDataValue); |
932 | 14.9M | } |
933 | 18.0k | } |
934 | 660k | else if (eDataType == EPT_s32) |
935 | 147k | { |
936 | 17.3M | for (int i = 0; i < nRepeatCount; i++) |
937 | 17.2M | { |
938 | 17.2M | reinterpret_cast<GInt32 *>(pabyDest)[nPixelsOutput++] = |
939 | 17.2M | static_cast<GInt32>(nDataValue); |
940 | 17.2M | } |
941 | 147k | } |
942 | 512k | else if (eDataType == EPT_f32) |
943 | 210k | { |
944 | 210k | float fDataValue = 0.0f; |
945 | | |
946 | 210k | memcpy(&fDataValue, &nDataValue, 4); |
947 | 1.59M | for (int i = 0; i < nRepeatCount; i++) |
948 | 1.38M | { |
949 | 1.38M | reinterpret_cast<float *>(pabyDest)[nPixelsOutput++] = |
950 | 1.38M | fDataValue; |
951 | 1.38M | } |
952 | 210k | } |
953 | 301k | else if (eDataType == EPT_u1) |
954 | 235k | { |
955 | | #ifdef DEBUG_VERBOSE |
956 | | CPLAssert(nDataValue == 0 || nDataValue == 1); |
957 | | #endif |
958 | 235k | if (nDataValue == 1) |
959 | 57.4k | { |
960 | 1.94M | for (int i = 0; i < nRepeatCount; i++) |
961 | 1.88M | { |
962 | 1.88M | pabyDest[nPixelsOutput >> 3] |= |
963 | 1.88M | (1 << (nPixelsOutput & 0x7)); |
964 | 1.88M | nPixelsOutput++; |
965 | 1.88M | } |
966 | 57.4k | } |
967 | 177k | else |
968 | 177k | { |
969 | 80.8M | for (int i = 0; i < nRepeatCount; i++) |
970 | 80.7M | { |
971 | 80.7M | pabyDest[nPixelsOutput >> 3] &= |
972 | 80.7M | ~(1 << (nPixelsOutput & 0x7)); |
973 | 80.7M | nPixelsOutput++; |
974 | 80.7M | } |
975 | 177k | } |
976 | 235k | } |
977 | 66.4k | else if (eDataType == EPT_u2) |
978 | 29.3k | { |
979 | | #ifdef DEBUG_VERBOSE |
980 | | CPLAssert(nDataValue >= 0 && nDataValue < 4); |
981 | | #endif |
982 | 24.7M | for (int i = 0; i < nRepeatCount; i++) |
983 | 24.7M | { |
984 | 24.7M | if ((nPixelsOutput & 0x3) == 0) |
985 | 6.18M | pabyDest[nPixelsOutput >> 2] = |
986 | 6.18M | static_cast<GByte>(nDataValue); |
987 | 18.5M | else if ((nPixelsOutput & 0x3) == 1) |
988 | 6.18M | pabyDest[nPixelsOutput >> 2] |= |
989 | 6.18M | static_cast<GByte>((nDataValue & 0x3) << 2); |
990 | 12.3M | else if ((nPixelsOutput & 0x3) == 2) |
991 | 6.18M | pabyDest[nPixelsOutput >> 2] |= |
992 | 6.18M | static_cast<GByte>((nDataValue & 0x3) << 4); |
993 | 6.18M | else |
994 | 6.18M | pabyDest[nPixelsOutput >> 2] |= |
995 | 6.18M | static_cast<GByte>((nDataValue & 0x3) << 6); |
996 | 24.7M | nPixelsOutput++; |
997 | 24.7M | } |
998 | 29.3k | } |
999 | 37.1k | else if (eDataType == EPT_u4) |
1000 | 37.1k | { |
1001 | | #ifdef DEBUG_VERBOSE |
1002 | | CPLAssert(nDataValue >= 0 && nDataValue < 16); |
1003 | | #endif |
1004 | 99.4M | for (int i = 0; i < nRepeatCount; i++) |
1005 | 99.3M | { |
1006 | 99.3M | if ((nPixelsOutput & 0x1) == 0) |
1007 | 49.6M | pabyDest[nPixelsOutput >> 1] = |
1008 | 49.6M | static_cast<GByte>(nDataValue); |
1009 | 49.6M | else |
1010 | 49.6M | pabyDest[nPixelsOutput >> 1] |= |
1011 | 49.6M | static_cast<GByte>((nDataValue & 0xF) << 4); |
1012 | | |
1013 | 99.3M | nPixelsOutput++; |
1014 | 99.3M | } |
1015 | 37.1k | } |
1016 | 21 | else |
1017 | 21 | { |
1018 | 21 | CPLError(CE_Failure, CPLE_AppDefined, |
1019 | 21 | "Attempt to uncompress an unsupported pixel data type."); |
1020 | 21 | return CE_Failure; |
1021 | 21 | } |
1022 | 998k | } |
1023 | | |
1024 | 6.76k | return CE_None; |
1025 | 6.96k | } |
1026 | | |
1027 | | /************************************************************************/ |
1028 | | /* NullBlock() */ |
1029 | | /* */ |
1030 | | /* Set the block buffer to zero or the nodata value as */ |
1031 | | /* appropriate. */ |
1032 | | /************************************************************************/ |
1033 | | |
1034 | | void HFABand::NullBlock(void *pData) |
1035 | | |
1036 | 1.65k | { |
1037 | 1.65k | const int nChunkSize = std::max(1, HFAGetDataTypeBits(eDataType) / 8); |
1038 | 1.65k | int nWords = nBlockXSize * nBlockYSize; |
1039 | | |
1040 | 1.65k | if (!bNoDataSet) |
1041 | 872 | { |
1042 | | #ifdef ESRI_BUILD |
1043 | | // We want special defaulting for 1 bit data in ArcGIS. |
1044 | | if (eDataType >= EPT_u2) |
1045 | | memset(pData, 0, static_cast<size_t>(nChunkSize) * nWords); |
1046 | | else |
1047 | | memset(pData, 255, static_cast<size_t>(nChunkSize) * nWords); |
1048 | | #else |
1049 | 872 | memset(pData, 0, static_cast<size_t>(nChunkSize) * nWords); |
1050 | 872 | #endif |
1051 | 872 | } |
1052 | 785 | else |
1053 | 785 | { |
1054 | 785 | GByte abyTmp[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
1055 | | |
1056 | 785 | switch (eDataType) |
1057 | 785 | { |
1058 | 169 | case EPT_u1: |
1059 | 169 | { |
1060 | 169 | nWords = (nWords + 7) / 8; |
1061 | 169 | if (dfNoData != 0.0) |
1062 | 88 | ((unsigned char *)abyTmp)[0] = 0xff; |
1063 | 81 | else |
1064 | 81 | ((unsigned char *)abyTmp)[0] = 0x00; |
1065 | 169 | } |
1066 | 169 | break; |
1067 | | |
1068 | 49 | case EPT_u2: |
1069 | 49 | { |
1070 | 49 | nWords = (nWords + 3) / 4; |
1071 | 49 | if (dfNoData == 0.0) |
1072 | 19 | ((unsigned char *)abyTmp)[0] = 0x00; |
1073 | 30 | else if (dfNoData == 1.0) |
1074 | 25 | ((unsigned char *)abyTmp)[0] = 0x55; |
1075 | 5 | else if (dfNoData == 2.0) |
1076 | 0 | ((unsigned char *)abyTmp)[0] = 0xaa; |
1077 | 5 | else |
1078 | 5 | ((unsigned char *)abyTmp)[0] = 0xff; |
1079 | 49 | } |
1080 | 49 | break; |
1081 | | |
1082 | 207 | case EPT_u4: |
1083 | 207 | { |
1084 | 207 | const unsigned char byVal = static_cast<unsigned char>( |
1085 | 207 | std::max(0, std::min(15, static_cast<int>(dfNoData)))); |
1086 | | |
1087 | 207 | nWords = (nWords + 1) / 2; |
1088 | | |
1089 | 207 | ((unsigned char *)abyTmp)[0] = byVal + (byVal << 4); |
1090 | 207 | } |
1091 | 207 | break; |
1092 | | |
1093 | 69 | case EPT_u8: |
1094 | 69 | ((unsigned char *)abyTmp)[0] = static_cast<unsigned char>( |
1095 | 69 | std::max(0, std::min(255, static_cast<int>(dfNoData)))); |
1096 | 69 | break; |
1097 | | |
1098 | 0 | case EPT_s8: |
1099 | 0 | ((signed char *)abyTmp)[0] = static_cast<signed char>( |
1100 | 0 | std::max(-128, std::min(127, static_cast<int>(dfNoData)))); |
1101 | 0 | break; |
1102 | | |
1103 | 62 | case EPT_u16: |
1104 | 62 | { |
1105 | 62 | GUInt16 nTmp = static_cast<GUInt16>(dfNoData); |
1106 | 62 | memcpy(abyTmp, &nTmp, sizeof(nTmp)); |
1107 | 62 | break; |
1108 | 0 | } |
1109 | | |
1110 | 44 | case EPT_s16: |
1111 | 44 | { |
1112 | 44 | GInt16 nTmp = static_cast<GInt16>(dfNoData); |
1113 | 44 | memcpy(abyTmp, &nTmp, sizeof(nTmp)); |
1114 | 44 | break; |
1115 | 0 | } |
1116 | | |
1117 | 31 | case EPT_u32: |
1118 | 31 | { |
1119 | 31 | GUInt32 nTmp = static_cast<GUInt32>(dfNoData); |
1120 | 31 | memcpy(abyTmp, &nTmp, sizeof(nTmp)); |
1121 | 31 | break; |
1122 | 0 | } |
1123 | | |
1124 | 0 | case EPT_s32: |
1125 | 0 | { |
1126 | 0 | GInt32 nTmp = static_cast<GInt32>(dfNoData); |
1127 | 0 | memcpy(abyTmp, &nTmp, sizeof(nTmp)); |
1128 | 0 | break; |
1129 | 0 | } |
1130 | | |
1131 | 65 | case EPT_f32: |
1132 | 65 | { |
1133 | 65 | float fTmp = static_cast<float>(dfNoData); |
1134 | 65 | memcpy(abyTmp, &fTmp, sizeof(fTmp)); |
1135 | 65 | break; |
1136 | 0 | } |
1137 | | |
1138 | 10 | case EPT_f64: |
1139 | 10 | { |
1140 | 10 | memcpy(abyTmp, &dfNoData, sizeof(dfNoData)); |
1141 | 10 | break; |
1142 | 0 | } |
1143 | | |
1144 | 79 | case EPT_c64: |
1145 | 79 | { |
1146 | 79 | float fTmp = static_cast<float>(dfNoData); |
1147 | 79 | memcpy(abyTmp, &fTmp, sizeof(fTmp)); |
1148 | 79 | memset(abyTmp + 4, 0, sizeof(float)); |
1149 | 79 | break; |
1150 | 0 | } |
1151 | | |
1152 | 0 | case EPT_c128: |
1153 | 0 | { |
1154 | 0 | memcpy(abyTmp, &dfNoData, sizeof(dfNoData)); |
1155 | 0 | memset(abyTmp + 8, 0, sizeof(double)); |
1156 | 0 | break; |
1157 | 0 | } |
1158 | 785 | } |
1159 | | |
1160 | 3.21M | for (int i = 0; i < nWords; i++) |
1161 | 3.20M | memcpy(((GByte *)pData) + nChunkSize * i, abyTmp, nChunkSize); |
1162 | 785 | } |
1163 | 1.65k | } |
1164 | | |
1165 | | /************************************************************************/ |
1166 | | /* GetRasterBlock() */ |
1167 | | /************************************************************************/ |
1168 | | |
1169 | | CPLErr HFABand::GetRasterBlock(int nXBlock, int nYBlock, void *pData, |
1170 | | int nDataSize) |
1171 | | |
1172 | 12.3k | { |
1173 | 12.3k | if (LoadBlockInfo() != CE_None) |
1174 | 1.75k | return CE_Failure; |
1175 | | |
1176 | 10.5k | const int iBlock = nXBlock + nYBlock * nBlocksPerRow; |
1177 | 10.5k | const int nDataTypeSizeBytes = |
1178 | 10.5k | std::max(1, HFAGetDataTypeBits(eDataType) / 8); |
1179 | 10.5k | const int nGDALBlockSize = nDataTypeSizeBytes * nBlockXSize * nBlockYSize; |
1180 | | |
1181 | | // If the block isn't valid, we just return all zeros, and an |
1182 | | // indication of success. |
1183 | 10.5k | if ((panBlockFlag[iBlock] & BFLG_VALID) == 0) |
1184 | 1.65k | { |
1185 | 1.65k | NullBlock(pData); |
1186 | 1.65k | return CE_None; |
1187 | 1.65k | } |
1188 | | |
1189 | | // Otherwise we really read the data. |
1190 | 8.94k | vsi_l_offset nBlockOffset = 0; |
1191 | 8.94k | VSILFILE *fpData = nullptr; |
1192 | | |
1193 | | // Calculate block offset in case we have spill file. Use predefined |
1194 | | // block map otherwise. |
1195 | 8.94k | if (fpExternal) |
1196 | 0 | { |
1197 | 0 | fpData = fpExternal; |
1198 | 0 | nBlockOffset = nBlockStart + nBlockSize * iBlock * nLayerStackCount + |
1199 | 0 | nLayerStackIndex * nBlockSize; |
1200 | 0 | } |
1201 | 8.94k | else |
1202 | 8.94k | { |
1203 | 8.94k | fpData = psInfo->fp; |
1204 | 8.94k | nBlockOffset = panBlockStart[iBlock]; |
1205 | 8.94k | nBlockSize = panBlockSize[iBlock]; |
1206 | 8.94k | } |
1207 | | |
1208 | 8.94k | if (VSIFSeekL(fpData, nBlockOffset, SEEK_SET) != 0) |
1209 | 0 | { |
1210 | | // XXX: We will not report error here, because file just may be |
1211 | | // in update state and data for this block will be available later. |
1212 | 0 | if (psInfo->eAccess == HFA_Update) |
1213 | 0 | { |
1214 | 0 | memset(pData, 0, nGDALBlockSize); |
1215 | 0 | return CE_None; |
1216 | 0 | } |
1217 | 0 | else |
1218 | 0 | { |
1219 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
1220 | 0 | "Seek to %x:%08x on %p failed\n%s", |
1221 | 0 | static_cast<int>(nBlockOffset >> 32), |
1222 | 0 | static_cast<int>(nBlockOffset & 0xffffffff), fpData, |
1223 | 0 | VSIStrerror(errno)); |
1224 | 0 | return CE_Failure; |
1225 | 0 | } |
1226 | 0 | } |
1227 | | |
1228 | | // If the block is compressed, read into an intermediate buffer |
1229 | | // and convert. |
1230 | 8.94k | if (panBlockFlag[iBlock] & BFLG_COMPRESSED) |
1231 | 7.60k | { |
1232 | 7.60k | GByte *pabyCData = static_cast<GByte *>( |
1233 | 7.60k | VSI_MALLOC_VERBOSE(static_cast<size_t>(nBlockSize))); |
1234 | 7.60k | if (pabyCData == nullptr) |
1235 | 0 | { |
1236 | 0 | return CE_Failure; |
1237 | 0 | } |
1238 | | |
1239 | 7.60k | if (VSIFReadL(pabyCData, static_cast<size_t>(nBlockSize), 1, fpData) != |
1240 | 7.60k | 1) |
1241 | 136 | { |
1242 | 136 | CPLFree(pabyCData); |
1243 | | |
1244 | | // XXX: Suppose that file in update state |
1245 | 136 | if (psInfo->eAccess == HFA_Update) |
1246 | 0 | { |
1247 | 0 | memset(pData, 0, nGDALBlockSize); |
1248 | 0 | return CE_None; |
1249 | 0 | } |
1250 | 136 | else |
1251 | 136 | { |
1252 | 136 | CPLError(CE_Failure, CPLE_FileIO, |
1253 | 136 | "Read of %d bytes at %x:%08x on %p failed.\n%s", |
1254 | 136 | static_cast<int>(nBlockSize), |
1255 | 136 | static_cast<int>(nBlockOffset >> 32), |
1256 | 136 | static_cast<int>(nBlockOffset & 0xffffffff), fpData, |
1257 | 136 | VSIStrerror(errno)); |
1258 | 136 | return CE_Failure; |
1259 | 136 | } |
1260 | 136 | } |
1261 | | |
1262 | 7.47k | CPLErr eErr = UncompressBlock(pabyCData, static_cast<int>(nBlockSize), |
1263 | 7.47k | static_cast<GByte *>(pData), |
1264 | 7.47k | nBlockXSize * nBlockYSize, eDataType); |
1265 | | |
1266 | 7.47k | CPLFree(pabyCData); |
1267 | | |
1268 | 7.47k | return eErr; |
1269 | 7.60k | } |
1270 | | |
1271 | | // Read uncompressed data directly into the return buffer. |
1272 | 1.33k | if (nDataSize != -1 && |
1273 | 1.33k | (nBlockSize > INT_MAX || static_cast<int>(nBlockSize) > nDataSize)) |
1274 | 35 | { |
1275 | 35 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid block size: %d", |
1276 | 35 | static_cast<int>(nBlockSize)); |
1277 | 35 | return CE_Failure; |
1278 | 35 | } |
1279 | | |
1280 | 1.30k | if (VSIFReadL(pData, static_cast<size_t>(nBlockSize), 1, fpData) != 1) |
1281 | 676 | { |
1282 | 676 | memset(pData, 0, nGDALBlockSize); |
1283 | | |
1284 | 676 | if (fpData != fpExternal) |
1285 | 676 | CPLDebug("HFABand", "Read of %x:%08x bytes at %d on %p failed.\n%s", |
1286 | 676 | static_cast<int>(nBlockSize), |
1287 | 676 | static_cast<int>(nBlockOffset >> 32), |
1288 | 676 | static_cast<int>(nBlockOffset & 0xffffffff), fpData, |
1289 | 676 | VSIStrerror(errno)); |
1290 | | |
1291 | 676 | return CE_None; |
1292 | 676 | } |
1293 | | |
1294 | | // Byte swap to local byte order if required. It appears that |
1295 | | // raster data is always stored in Intel byte order in Imagine |
1296 | | // files. |
1297 | | |
1298 | | #ifdef CPL_MSB |
1299 | | if (HFAGetDataTypeBits(eDataType) == 16) |
1300 | | { |
1301 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize; ii++) |
1302 | | CPL_SWAP16PTR(((unsigned char *)pData) + ii * 2); |
1303 | | } |
1304 | | else if (HFAGetDataTypeBits(eDataType) == 32) |
1305 | | { |
1306 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize; ii++) |
1307 | | CPL_SWAP32PTR(((unsigned char *)pData) + ii * 4); |
1308 | | } |
1309 | | else if (eDataType == EPT_f64) |
1310 | | { |
1311 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize; ii++) |
1312 | | CPL_SWAP64PTR(((unsigned char *)pData) + ii * 8); |
1313 | | } |
1314 | | else if (eDataType == EPT_c64) |
1315 | | { |
1316 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize * 2; ii++) |
1317 | | CPL_SWAP32PTR(((unsigned char *)pData) + ii * 4); |
1318 | | } |
1319 | | else if (eDataType == EPT_c128) |
1320 | | { |
1321 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize * 2; ii++) |
1322 | | CPL_SWAP64PTR(((unsigned char *)pData) + ii * 8); |
1323 | | } |
1324 | | #endif // def CPL_MSB |
1325 | | |
1326 | 625 | return CE_None; |
1327 | 1.30k | } |
1328 | | |
1329 | | /************************************************************************/ |
1330 | | /* ReAllocBlock() */ |
1331 | | /************************************************************************/ |
1332 | | |
1333 | | void HFABand::ReAllocBlock(int iBlock, int nSize) |
1334 | 1.60k | { |
1335 | | // For compressed files - need to realloc the space for the block. |
1336 | | |
1337 | | // TODO: Should check to see if panBlockStart[iBlock] is not zero then do a |
1338 | | // HFAFreeSpace() but that doesn't exist yet. |
1339 | | // Instead as in interim measure it will reuse the existing block if |
1340 | | // the new data will fit in. |
1341 | 1.60k | if ((panBlockStart[iBlock] != 0) && (nSize <= panBlockSize[iBlock])) |
1342 | 0 | { |
1343 | 0 | panBlockSize[iBlock] = nSize; |
1344 | | // fprintf( stderr, "Reusing block %d\n", iBlock ); |
1345 | 0 | return; |
1346 | 0 | } |
1347 | | |
1348 | 1.60k | panBlockStart[iBlock] = HFAAllocateSpace(psInfo, nSize); |
1349 | | |
1350 | 1.60k | panBlockSize[iBlock] = nSize; |
1351 | | |
1352 | | // Need to rewrite this info to the RasterDMS node. |
1353 | 1.60k | HFAEntry *poDMS = poNode->GetNamedChild("RasterDMS"); |
1354 | | |
1355 | 1.60k | if (!poDMS) |
1356 | 0 | { |
1357 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Unable to load RasterDMS"); |
1358 | 0 | return; |
1359 | 0 | } |
1360 | | |
1361 | 1.60k | char szVarName[64]; |
1362 | 1.60k | snprintf(szVarName, sizeof(szVarName), "blockinfo[%d].offset", iBlock); |
1363 | 1.60k | poDMS->SetIntField(szVarName, static_cast<int>(panBlockStart[iBlock])); |
1364 | | |
1365 | 1.60k | snprintf(szVarName, sizeof(szVarName), "blockinfo[%d].size", iBlock); |
1366 | 1.60k | poDMS->SetIntField(szVarName, panBlockSize[iBlock]); |
1367 | 1.60k | } |
1368 | | |
1369 | | /************************************************************************/ |
1370 | | /* SetRasterBlock() */ |
1371 | | /************************************************************************/ |
1372 | | |
1373 | | CPLErr HFABand::SetRasterBlock(int nXBlock, int nYBlock, void *pData) |
1374 | | |
1375 | 20.1k | { |
1376 | 20.1k | if (psInfo->eAccess == HFA_ReadOnly) |
1377 | 0 | { |
1378 | 0 | CPLError(CE_Failure, CPLE_NoWriteAccess, |
1379 | 0 | "Attempt to write block to read-only HFA file failed."); |
1380 | 0 | return CE_Failure; |
1381 | 0 | } |
1382 | | |
1383 | 20.1k | if (LoadBlockInfo() != CE_None) |
1384 | 0 | return CE_Failure; |
1385 | | |
1386 | 20.1k | const int iBlock = nXBlock + nYBlock * nBlocksPerRow; |
1387 | | |
1388 | | // For now we don't support write invalid uncompressed blocks. |
1389 | | // To do so we will need logic to make space at the end of the |
1390 | | // file in the right size. |
1391 | 20.1k | if ((panBlockFlag[iBlock] & BFLG_VALID) == 0 && |
1392 | 20.1k | !(panBlockFlag[iBlock] & BFLG_COMPRESSED) && panBlockStart[iBlock] == 0) |
1393 | 0 | { |
1394 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1395 | 0 | "Attempt to write to invalid tile with number %d " |
1396 | 0 | "(X position %d, Y position %d). This operation is " |
1397 | 0 | "currently unsupported by HFABand::SetRasterBlock().", |
1398 | 0 | iBlock, nXBlock, nYBlock); |
1399 | |
|
1400 | 0 | return CE_Failure; |
1401 | 0 | } |
1402 | | |
1403 | | // Move to the location that the data sits. |
1404 | 20.1k | VSILFILE *fpData = nullptr; |
1405 | 20.1k | vsi_l_offset nBlockOffset = 0; |
1406 | | |
1407 | | // Calculate block offset in case we have spill file. Use predefined |
1408 | | // block map otherwise. |
1409 | 20.1k | if (fpExternal) |
1410 | 0 | { |
1411 | 0 | fpData = fpExternal; |
1412 | 0 | nBlockOffset = nBlockStart + nBlockSize * iBlock * nLayerStackCount + |
1413 | 0 | nLayerStackIndex * nBlockSize; |
1414 | 0 | } |
1415 | 20.1k | else |
1416 | 20.1k | { |
1417 | 20.1k | fpData = psInfo->fp; |
1418 | 20.1k | nBlockOffset = panBlockStart[iBlock]; |
1419 | 20.1k | nBlockSize = panBlockSize[iBlock]; |
1420 | 20.1k | } |
1421 | | |
1422 | | // Compressed Tile Handling. |
1423 | 20.1k | if (panBlockFlag[iBlock] & BFLG_COMPRESSED) |
1424 | 1.60k | { |
1425 | | // Write compressed data. |
1426 | 1.60k | int nInBlockSize = static_cast<int>( |
1427 | 1.60k | (static_cast<GIntBig>(nBlockXSize) * nBlockYSize * |
1428 | 1.60k | static_cast<GIntBig>(HFAGetDataTypeBits(eDataType)) + |
1429 | 1.60k | 7) / |
1430 | 1.60k | 8); |
1431 | | |
1432 | | // Create the compressor object. |
1433 | 1.60k | HFACompress compress(pData, nInBlockSize, eDataType); |
1434 | 1.60k | if (compress.getCounts() == nullptr || compress.getValues() == nullptr) |
1435 | 0 | { |
1436 | 0 | return CE_Failure; |
1437 | 0 | } |
1438 | | |
1439 | | // Compress the data. |
1440 | 1.60k | if (compress.compressBlock()) |
1441 | 1.60k | { |
1442 | | // Get the data out of the object. |
1443 | 1.60k | GByte *pCounts = compress.getCounts(); |
1444 | 1.60k | GUInt32 nSizeCount = compress.getCountSize(); |
1445 | 1.60k | GByte *pValues = compress.getValues(); |
1446 | 1.60k | GUInt32 nSizeValues = compress.getValueSize(); |
1447 | 1.60k | GUInt32 nMin = compress.getMin(); |
1448 | 1.60k | GUInt32 nNumRuns = compress.getNumRuns(); |
1449 | 1.60k | GByte nNumBits = compress.getNumBits(); |
1450 | | |
1451 | | // Compensate for the header info. |
1452 | 1.60k | GUInt32 nDataOffset = nSizeCount + 13; |
1453 | 1.60k | int nTotalSize = nSizeCount + nSizeValues + 13; |
1454 | | |
1455 | | // Allocate space for the compressed block and seek to it. |
1456 | 1.60k | ReAllocBlock(iBlock, nTotalSize); |
1457 | | |
1458 | 1.60k | nBlockOffset = panBlockStart[iBlock]; |
1459 | 1.60k | nBlockSize = panBlockSize[iBlock]; |
1460 | | |
1461 | | // Seek to offset. |
1462 | 1.60k | if (VSIFSeekL(fpData, nBlockOffset, SEEK_SET) != 0) |
1463 | 0 | { |
1464 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
1465 | 0 | "Seek to %x:%08x on %p failed\n%s", |
1466 | 0 | static_cast<int>(nBlockOffset >> 32), |
1467 | 0 | static_cast<int>(nBlockOffset & 0xffffffff), fpData, |
1468 | 0 | VSIStrerror(errno)); |
1469 | 0 | return CE_Failure; |
1470 | 0 | } |
1471 | | |
1472 | | // Byte swap to local byte order if required. It appears that |
1473 | | // raster data is always stored in Intel byte order in Imagine |
1474 | | // files. |
1475 | | |
1476 | | #ifdef CPL_MSB |
1477 | | CPL_SWAP32PTR(&nMin); |
1478 | | CPL_SWAP32PTR(&nNumRuns); |
1479 | | CPL_SWAP32PTR(&nDataOffset); |
1480 | | #endif // def CPL_MSB |
1481 | | |
1482 | | // Write out the Minimum value. |
1483 | 1.60k | bool bRet = VSIFWriteL(&nMin, sizeof(nMin), 1, fpData) > 0; |
1484 | | |
1485 | | // The number of runs. |
1486 | 1.60k | bRet &= VSIFWriteL(&nNumRuns, sizeof(nNumRuns), 1, fpData) > 0; |
1487 | | |
1488 | | // The offset to the data. |
1489 | 1.60k | bRet &= |
1490 | 1.60k | VSIFWriteL(&nDataOffset, sizeof(nDataOffset), 1, fpData) > 0; |
1491 | | |
1492 | | // The number of bits. |
1493 | 1.60k | bRet &= VSIFWriteL(&nNumBits, sizeof(nNumBits), 1, fpData) > 0; |
1494 | | |
1495 | | // The counters - MSB stuff handled in HFACompress. |
1496 | 1.60k | bRet &= VSIFWriteL(pCounts, nSizeCount, 1, fpData) > 0; |
1497 | | |
1498 | | // The values - MSB stuff handled in HFACompress. |
1499 | 1.60k | bRet &= VSIFWriteL(pValues, nSizeValues, 1, fpData) > 0; |
1500 | | |
1501 | 1.60k | if (!bRet) |
1502 | 0 | return CE_Failure; |
1503 | | |
1504 | | // Compressed data is freed in the HFACompress destructor. |
1505 | 1.60k | } |
1506 | 0 | else |
1507 | 0 | { |
1508 | | // If we have actually made the block bigger - i.e. does not |
1509 | | // compress well. |
1510 | 0 | panBlockFlag[iBlock] ^= BFLG_COMPRESSED; |
1511 | | // Alloc more space for the uncompressed block. |
1512 | 0 | ReAllocBlock(iBlock, nInBlockSize); |
1513 | |
|
1514 | 0 | nBlockOffset = panBlockStart[iBlock]; |
1515 | 0 | nBlockSize = panBlockSize[iBlock]; |
1516 | | |
1517 | | // Need to change the RasterDMS entry. |
1518 | 0 | HFAEntry *poDMS = poNode->GetNamedChild("RasterDMS"); |
1519 | |
|
1520 | 0 | if (!poDMS) |
1521 | 0 | { |
1522 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Unable to load RasterDMS"); |
1523 | 0 | return CE_Failure; |
1524 | 0 | } |
1525 | | |
1526 | 0 | char szVarName[64] = {}; |
1527 | 0 | snprintf(szVarName, sizeof(szVarName), |
1528 | 0 | "blockinfo[%d].compressionType", iBlock); |
1529 | 0 | poDMS->SetIntField(szVarName, 0); |
1530 | 0 | } |
1531 | | |
1532 | | // If the block was previously invalid, mark it as valid now. |
1533 | 1.60k | if ((panBlockFlag[iBlock] & BFLG_VALID) == 0) |
1534 | 1.60k | { |
1535 | 1.60k | char szVarName[64]; |
1536 | 1.60k | HFAEntry *poDMS = poNode->GetNamedChild("RasterDMS"); |
1537 | | |
1538 | 1.60k | if (!poDMS) |
1539 | 0 | { |
1540 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Unable to load RasterDMS"); |
1541 | 0 | return CE_Failure; |
1542 | 0 | } |
1543 | | |
1544 | 1.60k | snprintf(szVarName, sizeof(szVarName), "blockinfo[%d].logvalid", |
1545 | 1.60k | iBlock); |
1546 | 1.60k | poDMS->SetStringField(szVarName, "true"); |
1547 | | |
1548 | 1.60k | panBlockFlag[iBlock] |= BFLG_VALID; |
1549 | 1.60k | } |
1550 | 1.60k | } |
1551 | | |
1552 | | // Uncompressed TILE handling. |
1553 | 20.1k | if ((panBlockFlag[iBlock] & BFLG_COMPRESSED) == 0) |
1554 | 18.5k | { |
1555 | | |
1556 | 18.5k | if (VSIFSeekL(fpData, nBlockOffset, SEEK_SET) != 0) |
1557 | 0 | { |
1558 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
1559 | 0 | "Seek to %x:%08x on %p failed\n%s", |
1560 | 0 | static_cast<int>(nBlockOffset >> 32), |
1561 | 0 | static_cast<int>(nBlockOffset & 0xffffffff), fpData, |
1562 | 0 | VSIStrerror(errno)); |
1563 | 0 | return CE_Failure; |
1564 | 0 | } |
1565 | | |
1566 | | // Byte swap to local byte order if required. It appears that |
1567 | | // raster data is always stored in Intel byte order in Imagine |
1568 | | // files. |
1569 | | |
1570 | | #ifdef CPL_MSB |
1571 | | if (HFAGetDataTypeBits(eDataType) == 16) |
1572 | | { |
1573 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize; ii++) |
1574 | | CPL_SWAP16PTR(((unsigned char *)pData) + ii * 2); |
1575 | | } |
1576 | | else if (HFAGetDataTypeBits(eDataType) == 32) |
1577 | | { |
1578 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize; ii++) |
1579 | | CPL_SWAP32PTR(((unsigned char *)pData) + ii * 4); |
1580 | | } |
1581 | | else if (eDataType == EPT_f64) |
1582 | | { |
1583 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize; ii++) |
1584 | | CPL_SWAP64PTR(((unsigned char *)pData) + ii * 8); |
1585 | | } |
1586 | | else if (eDataType == EPT_c64) |
1587 | | { |
1588 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize * 2; ii++) |
1589 | | CPL_SWAP32PTR(((unsigned char *)pData) + ii * 4); |
1590 | | } |
1591 | | else if (eDataType == EPT_c128) |
1592 | | { |
1593 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize * 2; ii++) |
1594 | | CPL_SWAP64PTR(((unsigned char *)pData) + ii * 8); |
1595 | | } |
1596 | | #endif // def CPL_MSB |
1597 | | |
1598 | | // Write uncompressed data. |
1599 | 18.5k | if (VSIFWriteL(pData, static_cast<size_t>(nBlockSize), 1, fpData) != 1) |
1600 | 0 | { |
1601 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
1602 | 0 | "Write of %d bytes at %x:%08x on %p failed.\n%s", |
1603 | 0 | static_cast<int>(nBlockSize), |
1604 | 0 | static_cast<int>(nBlockOffset >> 32), |
1605 | 0 | static_cast<int>(nBlockOffset & 0xffffffff), fpData, |
1606 | 0 | VSIStrerror(errno)); |
1607 | 0 | return CE_Failure; |
1608 | 0 | } |
1609 | | |
1610 | | // If the block was previously invalid, mark it as valid now. |
1611 | 18.5k | if ((panBlockFlag[iBlock] & BFLG_VALID) == 0) |
1612 | 18.5k | { |
1613 | 18.5k | char szVarName[64]; |
1614 | 18.5k | HFAEntry *poDMS = poNode->GetNamedChild("RasterDMS"); |
1615 | 18.5k | if (poDMS == nullptr) |
1616 | 0 | { |
1617 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1618 | 0 | "Unable to get RasterDMS when trying to mark " |
1619 | 0 | "block valid."); |
1620 | 0 | return CE_Failure; |
1621 | 0 | } |
1622 | 18.5k | snprintf(szVarName, sizeof(szVarName), "blockinfo[%d].logvalid", |
1623 | 18.5k | iBlock); |
1624 | 18.5k | poDMS->SetStringField(szVarName, "true"); |
1625 | | |
1626 | 18.5k | panBlockFlag[iBlock] |= BFLG_VALID; |
1627 | 18.5k | } |
1628 | 18.5k | } |
1629 | | // Swap back, since we don't really have permission to change |
1630 | | // the callers buffer. |
1631 | | |
1632 | | #ifdef CPL_MSB |
1633 | | if (HFAGetDataTypeBits(eDataType) == 16) |
1634 | | { |
1635 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize; ii++) |
1636 | | CPL_SWAP16PTR(((unsigned char *)pData) + ii * 2); |
1637 | | } |
1638 | | else if (HFAGetDataTypeBits(eDataType) == 32) |
1639 | | { |
1640 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize; ii++) |
1641 | | CPL_SWAP32PTR(((unsigned char *)pData) + ii * 4); |
1642 | | } |
1643 | | else if (eDataType == EPT_f64) |
1644 | | { |
1645 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize; ii++) |
1646 | | CPL_SWAP64PTR(((unsigned char *)pData) + ii * 8); |
1647 | | } |
1648 | | else if (eDataType == EPT_c64) |
1649 | | { |
1650 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize * 2; ii++) |
1651 | | CPL_SWAP32PTR(((unsigned char *)pData) + ii * 4); |
1652 | | } |
1653 | | else if (eDataType == EPT_c128) |
1654 | | { |
1655 | | for (int ii = 0; ii < nBlockXSize * nBlockYSize * 2; ii++) |
1656 | | CPL_SWAP64PTR(((unsigned char *)pData) + ii * 8); |
1657 | | } |
1658 | | #endif // def CPL_MSB |
1659 | | |
1660 | 20.1k | return CE_None; |
1661 | 20.1k | } |
1662 | | |
1663 | | /************************************************************************/ |
1664 | | /* GetBandName() */ |
1665 | | /* */ |
1666 | | /* Return the Layer Name */ |
1667 | | /************************************************************************/ |
1668 | | |
1669 | | const char *HFABand::GetBandName() |
1670 | 27.6k | { |
1671 | 27.6k | if (strlen(poNode->GetName()) > 0) |
1672 | 27.6k | return poNode->GetName(); |
1673 | | |
1674 | 0 | for (int iBand = 0; iBand < psInfo->nBands; iBand++) |
1675 | 0 | { |
1676 | 0 | if (psInfo->papoBand[iBand] == this) |
1677 | 0 | { |
1678 | 0 | osOverName.Printf("Layer_%d", iBand + 1); |
1679 | 0 | return osOverName; |
1680 | 0 | } |
1681 | 0 | } |
1682 | | |
1683 | 0 | osOverName.Printf("Layer_%x", poNode->GetFilePos()); |
1684 | 0 | return osOverName; |
1685 | 0 | } |
1686 | | |
1687 | | /************************************************************************/ |
1688 | | /* SetBandName() */ |
1689 | | /* */ |
1690 | | /* Set the Layer Name */ |
1691 | | /************************************************************************/ |
1692 | | |
1693 | | void HFABand::SetBandName(const char *pszName) |
1694 | 2.43k | { |
1695 | 2.43k | if (psInfo->eAccess == HFA_Update) |
1696 | 2.43k | { |
1697 | 2.43k | poNode->SetName(pszName); |
1698 | 2.43k | } |
1699 | 2.43k | } |
1700 | | |
1701 | | /************************************************************************/ |
1702 | | /* SetNoDataValue() */ |
1703 | | /* */ |
1704 | | /* Set the band no-data value */ |
1705 | | /************************************************************************/ |
1706 | | |
1707 | | CPLErr HFABand::SetNoDataValue(double dfValue) |
1708 | 7.37k | { |
1709 | 7.37k | if (psInfo->eAccess != HFA_Update) |
1710 | 0 | return CE_Failure; |
1711 | | |
1712 | 7.37k | HFAEntry *poNDNode = poNode->GetNamedChild("Eimg_NonInitializedValue"); |
1713 | | |
1714 | 7.37k | if (poNDNode == nullptr) |
1715 | 7.37k | { |
1716 | 7.37k | poNDNode = HFAEntry::New(psInfo, "Eimg_NonInitializedValue", |
1717 | 7.37k | "Eimg_NonInitializedValue", poNode); |
1718 | 7.37k | } |
1719 | | |
1720 | 7.37k | poNDNode->MakeData(8 + 12 + 8); |
1721 | 7.37k | poNDNode->SetPosition(); |
1722 | | |
1723 | 7.37k | poNDNode->SetIntField("valueBD[-3]", EPT_f64); |
1724 | 7.37k | poNDNode->SetIntField("valueBD[-2]", 1); |
1725 | 7.37k | poNDNode->SetIntField("valueBD[-1]", 1); |
1726 | | |
1727 | 7.37k | if (poNDNode->SetDoubleField("valueBD[0]", dfValue) == CE_Failure) |
1728 | 0 | return CE_Failure; |
1729 | | |
1730 | 7.37k | bNoDataSet = true; |
1731 | 7.37k | dfNoData = dfValue; |
1732 | 7.37k | return CE_None; |
1733 | 7.37k | } |
1734 | | |
1735 | | /************************************************************************/ |
1736 | | /* HFAReadBFUniqueBins() */ |
1737 | | /* */ |
1738 | | /* Attempt to read the bins used for a PCT or RAT from a */ |
1739 | | /* BinFunction node. On failure just return NULL. */ |
1740 | | /************************************************************************/ |
1741 | | |
1742 | | double *HFAReadBFUniqueBins(HFAEntry *poBinFunc, int nPCTColors) |
1743 | | |
1744 | 133 | { |
1745 | | // First confirm this is a "BFUnique" bin function. We don't |
1746 | | // know what to do with any other types. |
1747 | 133 | const char *pszBinFunctionType = |
1748 | 133 | poBinFunc->GetStringField("binFunction.type.string"); |
1749 | | |
1750 | 133 | if (pszBinFunctionType == nullptr || !EQUAL(pszBinFunctionType, "BFUnique")) |
1751 | 11 | return nullptr; |
1752 | | |
1753 | | // Process dictionary. |
1754 | 122 | const char *pszDict = |
1755 | 122 | poBinFunc->GetStringField("binFunction.MIFDictionary.string"); |
1756 | 122 | if (pszDict == nullptr) |
1757 | 3 | pszDict = poBinFunc->GetStringField("binFunction.MIFDictionary"); |
1758 | 122 | if (pszDict == nullptr) |
1759 | 3 | return nullptr; |
1760 | | |
1761 | 119 | HFADictionary oMiniDict(pszDict); |
1762 | | |
1763 | 119 | HFAType *poBFUnique = oMiniDict.FindType("BFUnique"); |
1764 | 119 | if (poBFUnique == nullptr) |
1765 | 34 | return nullptr; |
1766 | | |
1767 | | // Field the MIFObject raw data pointer. |
1768 | 85 | int nMIFObjectSize = 0; |
1769 | 85 | const GByte *pabyMIFObject = |
1770 | 85 | reinterpret_cast<const GByte *>(poBinFunc->GetStringField( |
1771 | 85 | "binFunction.MIFObject", nullptr, &nMIFObjectSize)); |
1772 | | |
1773 | 85 | if (pabyMIFObject == nullptr || |
1774 | 81 | nMIFObjectSize < 24 + static_cast<int>(sizeof(double)) * nPCTColors) |
1775 | 7 | return nullptr; |
1776 | | |
1777 | | // Confirm that this is a 64bit floating point basearray. |
1778 | 78 | if (pabyMIFObject[20] != 0x0a || pabyMIFObject[21] != 0x00) |
1779 | 6 | { |
1780 | 6 | CPLDebug("HFA", "HFAReadPCTBins(): " |
1781 | 6 | "The basedata does not appear to be EGDA_TYPE_F64."); |
1782 | 6 | return nullptr; |
1783 | 6 | } |
1784 | | |
1785 | | // Decode bins. |
1786 | 72 | double *padfBins = |
1787 | 72 | static_cast<double *>(CPLCalloc(sizeof(double), nPCTColors)); |
1788 | | |
1789 | 72 | memcpy(padfBins, pabyMIFObject + 24, sizeof(double) * nPCTColors); |
1790 | | |
1791 | 6.35k | for (int i = 0; i < nPCTColors; i++) |
1792 | 6.28k | { |
1793 | 6.28k | HFAStandard(8, padfBins + i); |
1794 | | #if DEBUG_VERBOSE |
1795 | | CPLDebug("HFA", "Bin[%d] = %g", i, padfBins[i]); |
1796 | | #endif |
1797 | 6.28k | } |
1798 | | |
1799 | 72 | return padfBins; |
1800 | 78 | } |
1801 | | |
1802 | | /************************************************************************/ |
1803 | | /* GetPCT() */ |
1804 | | /* */ |
1805 | | /* Return PCT information, if any exists. */ |
1806 | | /************************************************************************/ |
1807 | | |
1808 | | CPLErr HFABand::GetPCT(int *pnColors, double **ppadfRed, double **ppadfGreen, |
1809 | | double **ppadfBlue, double **ppadfAlpha, |
1810 | | double **ppadfBins) |
1811 | | |
1812 | 21.1k | { |
1813 | 21.1k | *pnColors = 0; |
1814 | 21.1k | *ppadfRed = nullptr; |
1815 | 21.1k | *ppadfGreen = nullptr; |
1816 | 21.1k | *ppadfBlue = nullptr; |
1817 | 21.1k | *ppadfAlpha = nullptr; |
1818 | 21.1k | *ppadfBins = nullptr; |
1819 | | |
1820 | | // If we haven't already tried to load the colors, do so now. |
1821 | 21.1k | if (nPCTColors == -1) |
1822 | 21.1k | { |
1823 | | |
1824 | 21.1k | nPCTColors = 0; |
1825 | | |
1826 | 21.1k | HFAEntry *poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Red"); |
1827 | 21.1k | if (poColumnEntry == nullptr) |
1828 | 20.8k | return CE_Failure; |
1829 | | |
1830 | 262 | nPCTColors = poColumnEntry->GetIntField("numRows"); |
1831 | 262 | if (nPCTColors < 0 || nPCTColors > 65536) |
1832 | 39 | { |
1833 | 39 | CPLError(CE_Failure, CPLE_AppDefined, |
1834 | 39 | "Invalid number of colors: %d", nPCTColors); |
1835 | 39 | return CE_Failure; |
1836 | 39 | } |
1837 | | |
1838 | 792 | for (int iColumn = 0; iColumn < 4; iColumn++) |
1839 | 651 | { |
1840 | 651 | apadfPCT[iColumn] = static_cast<double *>( |
1841 | 651 | VSI_MALLOC2_VERBOSE(sizeof(double), nPCTColors)); |
1842 | 651 | if (apadfPCT[iColumn] == nullptr) |
1843 | 46 | { |
1844 | 46 | return CE_Failure; |
1845 | 46 | } |
1846 | | |
1847 | 605 | if (iColumn == 0) |
1848 | 177 | { |
1849 | 177 | poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Red"); |
1850 | 177 | } |
1851 | 428 | else if (iColumn == 1) |
1852 | 145 | { |
1853 | 145 | poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Green"); |
1854 | 145 | } |
1855 | 283 | else if (iColumn == 2) |
1856 | 142 | { |
1857 | 142 | poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Blue"); |
1858 | 142 | } |
1859 | 141 | else if (iColumn == 3) |
1860 | 141 | { |
1861 | 141 | poColumnEntry = |
1862 | 141 | poNode->GetNamedChild("Descriptor_Table.Opacity"); |
1863 | 141 | } |
1864 | | |
1865 | 605 | if (poColumnEntry == nullptr) |
1866 | 186 | { |
1867 | 186 | double *pdCol = apadfPCT[iColumn]; |
1868 | 193k | for (int i = 0; i < nPCTColors; i++) |
1869 | 193k | pdCol[i] = 1.0; |
1870 | 186 | } |
1871 | 419 | else |
1872 | 419 | { |
1873 | 419 | if (VSIFSeekL(psInfo->fp, |
1874 | 419 | poColumnEntry->GetIntField("columnDataPtr"), |
1875 | 419 | SEEK_SET) < 0) |
1876 | 1 | { |
1877 | 1 | CPLError(CE_Failure, CPLE_FileIO, |
1878 | 1 | "VSIFSeekL() failed in HFABand::GetPCT()."); |
1879 | 1 | return CE_Failure; |
1880 | 1 | } |
1881 | 418 | if (VSIFReadL(apadfPCT[iColumn], sizeof(double), nPCTColors, |
1882 | 418 | psInfo->fp) != static_cast<size_t>(nPCTColors)) |
1883 | 35 | { |
1884 | 35 | CPLError(CE_Failure, CPLE_FileIO, |
1885 | 35 | "VSIFReadL() failed in HFABand::GetPCT()."); |
1886 | 35 | return CE_Failure; |
1887 | 35 | } |
1888 | | |
1889 | 177k | for (int i = 0; i < nPCTColors; i++) |
1890 | 177k | HFAStandard(8, apadfPCT[iColumn] + i); |
1891 | 383 | } |
1892 | 605 | } |
1893 | | |
1894 | | // Do we have a custom binning function? If so, try reading it. |
1895 | 141 | HFAEntry *poBinFunc = |
1896 | 141 | poNode->GetNamedChild("Descriptor_Table.#Bin_Function840#"); |
1897 | | |
1898 | 141 | if (poBinFunc != nullptr) |
1899 | 64 | { |
1900 | 64 | padfPCTBins = HFAReadBFUniqueBins(poBinFunc, nPCTColors); |
1901 | 64 | } |
1902 | 141 | } |
1903 | | |
1904 | | // Return the values. |
1905 | 141 | if (nPCTColors == 0) |
1906 | 0 | return CE_Failure; |
1907 | | |
1908 | 141 | *pnColors = nPCTColors; |
1909 | 141 | *ppadfRed = apadfPCT[0]; |
1910 | 141 | *ppadfGreen = apadfPCT[1]; |
1911 | 141 | *ppadfBlue = apadfPCT[2]; |
1912 | 141 | *ppadfAlpha = apadfPCT[3]; |
1913 | 141 | *ppadfBins = padfPCTBins; |
1914 | | |
1915 | 141 | return CE_None; |
1916 | 141 | } |
1917 | | |
1918 | | /************************************************************************/ |
1919 | | /* SetPCT() */ |
1920 | | /* */ |
1921 | | /* Set the PCT information for this band. */ |
1922 | | /************************************************************************/ |
1923 | | |
1924 | | CPLErr HFABand::SetPCT(int nColors, const double *padfRed, |
1925 | | const double *padfGreen, const double *padfBlue, |
1926 | | const double *padfAlpha) |
1927 | | |
1928 | 263 | { |
1929 | 263 | static const char *const apszColNames[4] = {"Red", "Green", "Blue", |
1930 | 263 | "Opacity"}; |
1931 | 263 | const double *const apadfValues[] = {padfRed, padfGreen, padfBlue, |
1932 | 263 | padfAlpha}; |
1933 | 263 | HFAEntry *poEdsc_Table; |
1934 | | |
1935 | | // Do we need to try and clear any existing color table? |
1936 | 263 | if (nColors == 0) |
1937 | 0 | { |
1938 | 0 | poEdsc_Table = poNode->GetNamedChild("Descriptor_Table"); |
1939 | 0 | if (poEdsc_Table == nullptr) |
1940 | 0 | return CE_None; |
1941 | | |
1942 | 0 | for (int iColumn = 0; iColumn < 4; iColumn++) |
1943 | 0 | { |
1944 | 0 | HFAEntry *poEdsc_Column = |
1945 | 0 | poEdsc_Table->GetNamedChild(apszColNames[iColumn]); |
1946 | 0 | if (poEdsc_Column) |
1947 | 0 | poEdsc_Column->RemoveAndDestroy(); |
1948 | 0 | } |
1949 | |
|
1950 | 0 | return CE_None; |
1951 | 0 | } |
1952 | | |
1953 | | // Create the Descriptor table. |
1954 | 263 | poEdsc_Table = poNode->GetNamedChild("Descriptor_Table"); |
1955 | 263 | if (poEdsc_Table == nullptr || |
1956 | 0 | !EQUAL(poEdsc_Table->GetType(), "Edsc_Table")) |
1957 | 263 | poEdsc_Table = |
1958 | 263 | HFAEntry::New(psInfo, "Descriptor_Table", "Edsc_Table", poNode); |
1959 | | |
1960 | 263 | poEdsc_Table->SetIntField("numrows", nColors); |
1961 | | |
1962 | | // Create the Binning function node. I am not sure that we |
1963 | | // really need this though. |
1964 | 263 | HFAEntry *poEdsc_BinFunction = |
1965 | 263 | poEdsc_Table->GetNamedChild("#Bin_Function#"); |
1966 | 263 | if (poEdsc_BinFunction == nullptr || |
1967 | 0 | !EQUAL(poEdsc_BinFunction->GetType(), "Edsc_BinFunction")) |
1968 | 263 | poEdsc_BinFunction = HFAEntry::New(psInfo, "#Bin_Function#", |
1969 | 263 | "Edsc_BinFunction", poEdsc_Table); |
1970 | | |
1971 | | // Because of the BaseData we have to hardcode the size. |
1972 | 263 | poEdsc_BinFunction->MakeData(30); |
1973 | | |
1974 | 263 | poEdsc_BinFunction->SetIntField("numBins", nColors); |
1975 | 263 | poEdsc_BinFunction->SetStringField("binFunction", "direct"); |
1976 | 263 | poEdsc_BinFunction->SetDoubleField("minLimit", 0.0); |
1977 | 263 | poEdsc_BinFunction->SetDoubleField("maxLimit", nColors - 1.0); |
1978 | | |
1979 | | // Process each color component. |
1980 | 1.31k | for (int iColumn = 0; iColumn < 4; iColumn++) |
1981 | 1.05k | { |
1982 | 1.05k | const double *padfValues = apadfValues[iColumn]; |
1983 | 1.05k | const char *pszName = apszColNames[iColumn]; |
1984 | | |
1985 | | // Create the Edsc_Column. |
1986 | 1.05k | HFAEntry *poEdsc_Column = poEdsc_Table->GetNamedChild(pszName); |
1987 | 1.05k | if (poEdsc_Column == nullptr || |
1988 | 0 | !EQUAL(poEdsc_Column->GetType(), "Edsc_Column")) |
1989 | 1.05k | poEdsc_Column = |
1990 | 1.05k | HFAEntry::New(psInfo, pszName, "Edsc_Column", poEdsc_Table); |
1991 | | |
1992 | 1.05k | poEdsc_Column->SetIntField("numRows", nColors); |
1993 | 1.05k | poEdsc_Column->SetStringField("dataType", "real"); |
1994 | 1.05k | poEdsc_Column->SetIntField("maxNumChars", 0); |
1995 | | |
1996 | | // Write the data out. |
1997 | 1.05k | const auto nOffset = HFAAllocateSpace(psInfo, 8 * nColors); |
1998 | 1.05k | if (nOffset > static_cast<unsigned>(INT_MAX)) |
1999 | 0 | return CE_Failure; |
2000 | | |
2001 | 1.05k | poEdsc_Column->SetIntField("columnDataPtr", static_cast<int>(nOffset)); |
2002 | | |
2003 | 1.05k | double *padfFileData = |
2004 | 1.05k | static_cast<double *>(CPLMalloc(nColors * sizeof(double))); |
2005 | 3.17k | for (int iColor = 0; iColor < nColors; iColor++) |
2006 | 2.12k | { |
2007 | 2.12k | padfFileData[iColor] = padfValues[iColor]; |
2008 | 2.12k | HFAStandard(8, padfFileData + iColor); |
2009 | 2.12k | } |
2010 | 1.05k | const bool bRet = VSIFSeekL(psInfo->fp, nOffset, SEEK_SET) >= 0 && |
2011 | 1.05k | VSIFWriteL(padfFileData, 8, nColors, psInfo->fp) == |
2012 | 1.05k | static_cast<size_t>(nColors); |
2013 | 1.05k | CPLFree(padfFileData); |
2014 | 1.05k | if (!bRet) |
2015 | 0 | return CE_Failure; |
2016 | 1.05k | } |
2017 | | |
2018 | | // Update the layer type to be thematic. |
2019 | 263 | poNode->SetStringField("layerType", "thematic"); |
2020 | | |
2021 | 263 | return CE_None; |
2022 | 263 | } |
2023 | | |
2024 | | /************************************************************************/ |
2025 | | /* HFAGetOverviewBlockSize() */ |
2026 | | /************************************************************************/ |
2027 | | |
2028 | | static int HFAGetOverviewBlockSize() |
2029 | 0 | { |
2030 | 0 | const char *pszVal = CPLGetConfigOption("GDAL_HFA_OVR_BLOCKSIZE", "64"); |
2031 | 0 | int nOvrBlockSize = atoi(pszVal); |
2032 | 0 | if (nOvrBlockSize < 32 || nOvrBlockSize > 2048 || |
2033 | 0 | !CPLIsPowerOfTwo(nOvrBlockSize)) |
2034 | 0 | { |
2035 | 0 | CPLErrorOnce(CE_Warning, CPLE_NotSupported, |
2036 | 0 | "Wrong value for GDAL_HFA_OVR_BLOCKSIZE : %s. " |
2037 | 0 | "Should be a power of 2 between 32 and 2048. " |
2038 | 0 | "Defaulting to 64", |
2039 | 0 | pszVal); |
2040 | 0 | nOvrBlockSize = 64; |
2041 | 0 | } |
2042 | |
|
2043 | 0 | return nOvrBlockSize; |
2044 | 0 | } |
2045 | | |
2046 | | /************************************************************************/ |
2047 | | /* CreateOverview() */ |
2048 | | /************************************************************************/ |
2049 | | |
2050 | | int HFABand::CreateOverview(int nOverviewLevel, const char *pszResampling) |
2051 | | |
2052 | 0 | { |
2053 | 0 | const int nOXSize = DIV_ROUND_UP(psInfo->nXSize, nOverviewLevel); |
2054 | 0 | const int nOYSize = DIV_ROUND_UP(psInfo->nYSize, nOverviewLevel); |
2055 | | |
2056 | | // Do we want to use a dependent file (.rrd) for the overviews? |
2057 | | // Or just create them directly in this file? |
2058 | 0 | HFAInfo_t *psRRDInfo = psInfo; |
2059 | 0 | HFAEntry *poParent = poNode; |
2060 | |
|
2061 | 0 | if (CPLTestBool(CPLGetConfigOption("HFA_USE_RRD", "NO"))) |
2062 | 0 | { |
2063 | 0 | psRRDInfo = HFACreateDependent(psInfo); |
2064 | 0 | if (psRRDInfo == nullptr) |
2065 | 0 | return -1; |
2066 | | |
2067 | 0 | poParent = psRRDInfo->poRoot->GetNamedChild(GetBandName()); |
2068 | | |
2069 | | // Need to create layer object. |
2070 | 0 | if (poParent == nullptr) |
2071 | 0 | { |
2072 | 0 | poParent = HFAEntry::New(psRRDInfo, GetBandName(), "Eimg_Layer", |
2073 | 0 | psRRDInfo->poRoot); |
2074 | 0 | } |
2075 | 0 | } |
2076 | | |
2077 | | // What pixel type should we use for the overview. Usually |
2078 | | // this is the same as the base layer, but when |
2079 | | // AVERAGE_BIT2GRAYSCALE is in effect we force it to u8 from u1. |
2080 | 0 | EPTType eOverviewDataType = eDataType; |
2081 | |
|
2082 | 0 | if (STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2GR")) |
2083 | 0 | eOverviewDataType = EPT_u8; |
2084 | | |
2085 | | // Eventually we need to decide on the whether to use the spill |
2086 | | // file, primarily on the basis of whether the new overview |
2087 | | // will drive our .img file size near 4GB. For now, just base |
2088 | | // it on the config options. |
2089 | 0 | bool bCreateLargeRaster = |
2090 | 0 | CPLTestBool(CPLGetConfigOption("USE_SPILL", "NO")); |
2091 | 0 | GIntBig nValidFlagsOffset = 0; |
2092 | 0 | GIntBig nDataOffset = 0; |
2093 | 0 | int nOverviewBlockSize = HFAGetOverviewBlockSize(); |
2094 | |
|
2095 | 0 | if ((psRRDInfo->nEndOfFile + |
2096 | 0 | (nOXSize * static_cast<double>(nOYSize)) * |
2097 | 0 | (HFAGetDataTypeBits(eOverviewDataType) / 8)) > 2000000000.0) |
2098 | 0 | bCreateLargeRaster = true; |
2099 | |
|
2100 | 0 | if (bCreateLargeRaster) |
2101 | 0 | { |
2102 | 0 | if (!HFACreateSpillStack(psRRDInfo, nOXSize, nOYSize, 1, |
2103 | 0 | nOverviewBlockSize, eOverviewDataType, |
2104 | 0 | &nValidFlagsOffset, &nDataOffset)) |
2105 | 0 | { |
2106 | 0 | return -1; |
2107 | 0 | } |
2108 | 0 | } |
2109 | | |
2110 | | // Are we compressed? If so, overview should be too (unless |
2111 | | // HFA_COMPRESS_OVR is defined). |
2112 | | // Check RasterDMS like HFAGetBandInfo. |
2113 | 0 | bool bCompressionType = false; |
2114 | 0 | const char *pszCompressOvr = |
2115 | 0 | CPLGetConfigOption("HFA_COMPRESS_OVR", nullptr); |
2116 | 0 | if (pszCompressOvr != nullptr) |
2117 | 0 | { |
2118 | 0 | bCompressionType = CPLTestBool(pszCompressOvr); |
2119 | 0 | } |
2120 | 0 | else |
2121 | 0 | { |
2122 | 0 | HFAEntry *poDMS = poNode->GetNamedChild("RasterDMS"); |
2123 | |
|
2124 | 0 | if (poDMS != nullptr) |
2125 | 0 | bCompressionType = poDMS->GetIntField("compressionType") != 0; |
2126 | 0 | } |
2127 | | |
2128 | | // Create the layer. |
2129 | 0 | CPLString osLayerName; |
2130 | 0 | osLayerName.Printf("_ss_%d_", nOverviewLevel); |
2131 | |
|
2132 | 0 | if (!HFACreateLayer( |
2133 | 0 | psRRDInfo, poParent, osLayerName, TRUE, nOverviewBlockSize, |
2134 | 0 | bCompressionType, bCreateLargeRaster, FALSE, nOXSize, nOYSize, |
2135 | 0 | eOverviewDataType, nullptr, nValidFlagsOffset, nDataOffset, 1, 0)) |
2136 | 0 | return -1; |
2137 | | |
2138 | 0 | HFAEntry *poOverLayer = poParent->GetNamedChild(osLayerName); |
2139 | 0 | if (poOverLayer == nullptr) |
2140 | 0 | return -1; |
2141 | | |
2142 | | // Create RRDNamesList list if it does not yet exist. |
2143 | 0 | HFAEntry *poRRDNamesList = poNode->GetNamedChild("RRDNamesList"); |
2144 | 0 | if (poRRDNamesList == nullptr) |
2145 | 0 | { |
2146 | 0 | poRRDNamesList = |
2147 | 0 | HFAEntry::New(psInfo, "RRDNamesList", "Eimg_RRDNamesList", poNode); |
2148 | 0 | poRRDNamesList->MakeData(23 + 16 + 8 + 3000); // Hack for growth room. |
2149 | | |
2150 | | // We need to hardcode file offset into the data, so locate it now. |
2151 | 0 | poRRDNamesList->SetPosition(); |
2152 | |
|
2153 | 0 | poRRDNamesList->SetStringField("algorithm.string", |
2154 | 0 | "IMAGINE 2X2 Resampling"); |
2155 | 0 | } |
2156 | | |
2157 | | // Add new overview layer to RRDNamesList. |
2158 | 0 | int iNextName = poRRDNamesList->GetFieldCount("nameList"); |
2159 | 0 | char szName[50]; |
2160 | 0 | CPLString osNodeName; |
2161 | |
|
2162 | 0 | snprintf(szName, sizeof(szName), "nameList[%d].string", iNextName); |
2163 | |
|
2164 | 0 | osLayerName.Printf("%s(:%s:_ss_%d_)", psRRDInfo->pszFilename, GetBandName(), |
2165 | 0 | nOverviewLevel); |
2166 | | |
2167 | | // TODO: Need to add to end of array (that is pretty hard). |
2168 | 0 | if (poRRDNamesList->SetStringField(szName, osLayerName) != CE_None) |
2169 | 0 | { |
2170 | 0 | poRRDNamesList->MakeData(poRRDNamesList->GetDataSize() + 3000); |
2171 | 0 | if (poRRDNamesList->SetStringField(szName, osLayerName) != CE_None) |
2172 | 0 | return -1; |
2173 | 0 | } |
2174 | | |
2175 | | // Add to the list of overviews for this band. |
2176 | 0 | papoOverviews = static_cast<HFABand **>( |
2177 | 0 | CPLRealloc(papoOverviews, sizeof(void *) * ++nOverviews)); |
2178 | 0 | papoOverviews[nOverviews - 1] = new HFABand(psRRDInfo, poOverLayer); |
2179 | | |
2180 | | // If there is a nodata value, copy it to the overview band. |
2181 | 0 | if (bNoDataSet) |
2182 | 0 | papoOverviews[nOverviews - 1]->SetNoDataValue(dfNoData); |
2183 | |
|
2184 | 0 | return nOverviews - 1; |
2185 | 0 | } |