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