/src/gdal/frmts/northwood/northwood.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GRC/GRD Reader |
4 | | * Purpose: Northwood Format basic implementation |
5 | | * Author: Perry Casson |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2007, Waypoint Information Technology |
9 | | * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "gdal_pam.h" |
15 | | |
16 | | #include "northwood.h" |
17 | | |
18 | | #include <algorithm> |
19 | | #include <cassert> |
20 | | #include <limits> |
21 | | #include <string> |
22 | | |
23 | | int nwt_ParseHeader(NWT_GRID *pGrd, const unsigned char *nwtHeader) |
24 | 14 | { |
25 | | /* double dfTmp; */ |
26 | | |
27 | 14 | if (nwtHeader[4] == '1') |
28 | 5 | pGrd->cFormat = 0x00; // grd - surface type |
29 | 9 | else if (nwtHeader[4] == '8') |
30 | 9 | pGrd->cFormat = 0x80; // grc classified type |
31 | | |
32 | 14 | pGrd->stClassDict = nullptr; |
33 | | |
34 | 14 | memcpy(&pGrd->fVersion, &nwtHeader[5], sizeof(pGrd->fVersion)); |
35 | 14 | CPL_LSBPTR32(&pGrd->fVersion); |
36 | | |
37 | 14 | unsigned short usTmp; |
38 | 14 | memcpy(&usTmp, &nwtHeader[9], 2); |
39 | 14 | CPL_LSBPTR16(&usTmp); |
40 | 14 | pGrd->nXSide = static_cast<unsigned int>(usTmp); |
41 | 14 | if (pGrd->nXSide == 0) |
42 | 0 | { |
43 | 0 | memcpy(&pGrd->nXSide, &nwtHeader[128], sizeof(pGrd->nXSide)); |
44 | 0 | CPL_LSBPTR32(&pGrd->nXSide); |
45 | 0 | } |
46 | 14 | if (pGrd->nXSide <= 1) |
47 | 0 | return FALSE; |
48 | | |
49 | 14 | memcpy(&usTmp, &nwtHeader[11], 2); |
50 | 14 | CPL_LSBPTR16(&usTmp); |
51 | 14 | pGrd->nYSide = static_cast<unsigned int>(usTmp); |
52 | 14 | if (pGrd->nYSide == 0) |
53 | 3 | { |
54 | 3 | memcpy(&pGrd->nYSide, &nwtHeader[132], sizeof(pGrd->nYSide)); |
55 | 3 | CPL_LSBPTR32(&pGrd->nYSide); |
56 | 3 | } |
57 | | |
58 | 14 | memcpy(&pGrd->dfMinX, &nwtHeader[13], sizeof(pGrd->dfMinX)); |
59 | 14 | CPL_LSBPTR64(&pGrd->dfMinX); |
60 | 14 | memcpy(&pGrd->dfMaxX, &nwtHeader[21], sizeof(pGrd->dfMaxX)); |
61 | 14 | CPL_LSBPTR64(&pGrd->dfMaxX); |
62 | 14 | memcpy(&pGrd->dfMinY, &nwtHeader[29], sizeof(pGrd->dfMinY)); |
63 | 14 | CPL_LSBPTR64(&pGrd->dfMinY); |
64 | 14 | memcpy(&pGrd->dfMaxY, &nwtHeader[37], sizeof(pGrd->dfMaxY)); |
65 | 14 | CPL_LSBPTR64(&pGrd->dfMaxY); |
66 | | |
67 | 14 | assert(pGrd->nXSide > 1); |
68 | 14 | pGrd->dfStepSize = (pGrd->dfMaxX - pGrd->dfMinX) / (pGrd->nXSide - 1); |
69 | | /* dfTmp = (pGrd->dfMaxY - pGrd->dfMinY) / (pGrd->nYSide - 1); */ |
70 | | |
71 | 14 | memcpy(&pGrd->fZMin, &nwtHeader[45], sizeof(pGrd->fZMin)); |
72 | 14 | CPL_LSBPTR32(&pGrd->fZMin); |
73 | 14 | memcpy(&pGrd->fZMax, &nwtHeader[49], sizeof(pGrd->fZMax)); |
74 | 14 | CPL_LSBPTR32(&pGrd->fZMax); |
75 | 14 | memcpy(&pGrd->fZMinScale, &nwtHeader[53], sizeof(pGrd->fZMinScale)); |
76 | 14 | CPL_LSBPTR32(&pGrd->fZMinScale); |
77 | 14 | memcpy(&pGrd->fZMaxScale, &nwtHeader[57], sizeof(pGrd->fZMaxScale)); |
78 | 14 | CPL_LSBPTR32(&pGrd->fZMaxScale); |
79 | | |
80 | 14 | memcpy(&pGrd->cDescription, &nwtHeader[61], sizeof(pGrd->cDescription)); |
81 | 14 | memcpy(&pGrd->cZUnits, &nwtHeader[93], sizeof(pGrd->cZUnits)); |
82 | | |
83 | 14 | int i; |
84 | 14 | memcpy(&i, &nwtHeader[136], 4); |
85 | 14 | CPL_LSBPTR32(&i); |
86 | | |
87 | 14 | if (i == 1129336130) |
88 | 0 | { // BMPC |
89 | 0 | if (nwtHeader[140] & 0x01) |
90 | 0 | { |
91 | 0 | pGrd->cHillShadeBrightness = nwtHeader[144]; |
92 | 0 | pGrd->cHillShadeContrast = nwtHeader[145]; |
93 | 0 | } |
94 | 0 | } |
95 | | |
96 | 14 | memcpy(&pGrd->cMICoordSys, &nwtHeader[256], sizeof(pGrd->cMICoordSys)); |
97 | 14 | pGrd->cMICoordSys[sizeof(pGrd->cMICoordSys) - 1] = '\0'; |
98 | | |
99 | 14 | pGrd->iZUnits = nwtHeader[512]; |
100 | | |
101 | 14 | if (nwtHeader[513] & 0x80) |
102 | 4 | pGrd->bShowGradient = true; |
103 | | |
104 | 14 | if (nwtHeader[513] & 0x40) |
105 | 3 | pGrd->bShowHillShade = true; |
106 | | |
107 | 14 | if (nwtHeader[513] & 0x20) |
108 | 3 | pGrd->bHillShadeExists = true; |
109 | | |
110 | 14 | memcpy(&pGrd->iNumColorInflections, &nwtHeader[516], 2); |
111 | 14 | CPL_LSBPTR16(&pGrd->iNumColorInflections); |
112 | | |
113 | 14 | if (pGrd->iNumColorInflections > 32) |
114 | 0 | { |
115 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Corrupt header"); |
116 | 0 | pGrd->iNumColorInflections = 0; |
117 | 0 | return FALSE; |
118 | 0 | } |
119 | | |
120 | 23 | for (i = 0; i < pGrd->iNumColorInflections; i++) |
121 | 9 | { |
122 | 9 | memcpy(&pGrd->stInflection[i].zVal, &nwtHeader[518 + (7 * i)], 4); |
123 | 9 | CPL_LSBPTR32(&pGrd->stInflection[i].zVal); |
124 | 9 | memcpy(&pGrd->stInflection[i].r, &nwtHeader[522 + (7 * i)], 1); |
125 | 9 | memcpy(&pGrd->stInflection[i].g, &nwtHeader[523 + (7 * i)], 1); |
126 | 9 | memcpy(&pGrd->stInflection[i].b, &nwtHeader[524 + (7 * i)], 1); |
127 | 9 | } |
128 | | |
129 | 14 | memcpy(&pGrd->fHillShadeAzimuth, &nwtHeader[966], |
130 | 14 | sizeof(pGrd->fHillShadeAzimuth)); |
131 | 14 | CPL_LSBPTR32(&pGrd->fHillShadeAzimuth); |
132 | 14 | memcpy(&pGrd->fHillShadeAngle, &nwtHeader[970], |
133 | 14 | sizeof(pGrd->fHillShadeAngle)); |
134 | 14 | CPL_LSBPTR32(&pGrd->fHillShadeAngle); |
135 | | |
136 | 14 | pGrd->cFormat += nwtHeader[1023]; // the msb for grd/grc was already set |
137 | | |
138 | | // there are more types than this - need to build other types for testing |
139 | 14 | if (pGrd->cFormat & 0x80) |
140 | 9 | { |
141 | 9 | if (nwtHeader[1023] == 0) |
142 | 3 | pGrd->nBitsPerPixel = 16; |
143 | 6 | else |
144 | 6 | pGrd->nBitsPerPixel = nwtHeader[1023] * 4; |
145 | 9 | } |
146 | 5 | else |
147 | 5 | pGrd->nBitsPerPixel = nwtHeader[1023] * 8; |
148 | | |
149 | 14 | if (pGrd->cFormat & 0x80) // if is GRC load the Dictionary |
150 | 9 | { |
151 | 9 | vsi_l_offset nPixels = |
152 | 9 | static_cast<vsi_l_offset>(pGrd->nXSide) * pGrd->nYSide; |
153 | 9 | unsigned int nBytesPerPixel = pGrd->nBitsPerPixel / 8; |
154 | 9 | if (nPixels > 0 && |
155 | 9 | (nBytesPerPixel > |
156 | 9 | std::numeric_limits<vsi_l_offset>::max() / nPixels || |
157 | 9 | nPixels * nBytesPerPixel > |
158 | 9 | std::numeric_limits<vsi_l_offset>::max() - 1024)) |
159 | 0 | { |
160 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
161 | 0 | "Invalid file dimension / bits per pixel"); |
162 | 0 | return FALSE; |
163 | 0 | } |
164 | 9 | VSIFSeekL(pGrd->fp, 1024 + nPixels * nBytesPerPixel, SEEK_SET); |
165 | | |
166 | 9 | if (!VSIFReadL(&usTmp, 2, 1, pGrd->fp)) |
167 | 0 | { |
168 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Read failure, file short?"); |
169 | 0 | return FALSE; |
170 | 0 | } |
171 | 9 | CPL_LSBPTR16(&usTmp); |
172 | 9 | pGrd->stClassDict = reinterpret_cast<NWT_CLASSIFIED_DICT *>( |
173 | 9 | calloc(1, sizeof(NWT_CLASSIFIED_DICT))); |
174 | 9 | if (!pGrd->stClassDict) |
175 | 0 | { |
176 | 0 | return FALSE; |
177 | 0 | } |
178 | | |
179 | 9 | pGrd->stClassDict->nNumClassifiedItems = usTmp; |
180 | | |
181 | 9 | pGrd->stClassDict->stClassifiedItem = |
182 | 9 | reinterpret_cast<NWT_CLASSIFIED_ITEM **>( |
183 | 9 | calloc(pGrd->stClassDict->nNumClassifiedItems + 1, |
184 | 9 | sizeof(NWT_CLASSIFIED_ITEM *))); |
185 | 9 | if (!pGrd->stClassDict->stClassifiedItem) |
186 | 0 | { |
187 | 0 | pGrd->stClassDict->nNumClassifiedItems = 0; |
188 | 0 | return FALSE; |
189 | 0 | } |
190 | | |
191 | | // load the dictionary |
192 | 9 | for (unsigned int iItem = 0; |
193 | 16 | iItem < pGrd->stClassDict->nNumClassifiedItems; iItem++) |
194 | 10 | { |
195 | 10 | NWT_CLASSIFIED_ITEM *psItem = |
196 | 10 | pGrd->stClassDict->stClassifiedItem[iItem] = |
197 | 10 | reinterpret_cast<NWT_CLASSIFIED_ITEM *>( |
198 | 10 | calloc(1, sizeof(NWT_CLASSIFIED_ITEM))); |
199 | 10 | if (!psItem) |
200 | 0 | { |
201 | 0 | return FALSE; |
202 | 0 | } |
203 | | |
204 | 10 | unsigned char cTmp[256]; |
205 | 10 | if (!VSIFReadL(&cTmp, 9, 1, pGrd->fp)) |
206 | 0 | { |
207 | 0 | CPLError(CE_Failure, CPLE_FileIO, "Read failure, file short?"); |
208 | 0 | return FALSE; |
209 | 0 | } |
210 | 10 | memcpy(&psItem->usPixVal, &cTmp[0], 2); |
211 | 10 | CPL_LSBPTR16(&psItem->usPixVal); |
212 | 10 | memcpy(&psItem->res1, &cTmp[2], 1); |
213 | 10 | memcpy(&psItem->r, &cTmp[3], 1); |
214 | 10 | memcpy(&psItem->g, &cTmp[4], 1); |
215 | 10 | memcpy(&psItem->b, &cTmp[5], 1); |
216 | 10 | memcpy(&psItem->res2, &cTmp[6], 1); |
217 | 10 | memcpy(&psItem->usLen, &cTmp[7], 2); |
218 | 10 | CPL_LSBPTR16(&psItem->usLen); |
219 | | |
220 | 10 | if (psItem->usLen > sizeof(psItem->szClassName) - 1) |
221 | 3 | { |
222 | 3 | CPLError(CE_Failure, CPLE_AppDefined, |
223 | 3 | "Unexpected long class name, %d characters long - " |
224 | 3 | "unable to read file.", |
225 | 3 | psItem->usLen); |
226 | 3 | return FALSE; |
227 | 3 | } |
228 | | |
229 | | // 0-len class names are possible |
230 | 7 | psItem->szClassName[0] = '\0'; |
231 | 7 | if (psItem->usLen > 0 && |
232 | 7 | !VSIFReadL(&psItem->szClassName, psItem->usLen, 1, pGrd->fp)) |
233 | 0 | return FALSE; |
234 | 7 | } |
235 | 9 | } |
236 | | |
237 | 11 | return TRUE; |
238 | 14 | } |
239 | | |
240 | | // Create a color gradient ranging from ZMin to Zmax using the color |
241 | | // inflections defined in grid |
242 | | int nwt_LoadColors(NWT_RGB *pMap, int mapSize, NWT_GRID *pGrd) |
243 | 5 | { |
244 | 5 | int i; |
245 | 5 | NWT_RGB sColor; |
246 | 5 | int nWarkerMark = 0; |
247 | | |
248 | 5 | createIP(0, 255, 255, 255, pMap, &nWarkerMark); |
249 | 5 | if (pGrd->iNumColorInflections == 0) |
250 | 2 | return 0; |
251 | | |
252 | | // If Zmin is less than the 1st inflection use the 1st inflections color to |
253 | | // the start of the ramp |
254 | 3 | if (pGrd->fZMin <= pGrd->stInflection[0].zVal) |
255 | 1 | { |
256 | 1 | createIP(1, pGrd->stInflection[0].r, pGrd->stInflection[0].g, |
257 | 1 | pGrd->stInflection[0].b, pMap, &nWarkerMark); |
258 | 1 | } |
259 | | // find what inflections zmin is between |
260 | 5 | for (i = 1; i < pGrd->iNumColorInflections; i++) |
261 | 3 | { |
262 | 3 | if (pGrd->fZMin < pGrd->stInflection[i].zVal) |
263 | 1 | { |
264 | | // then we must be between i and i-1 |
265 | 1 | linearColor(&sColor, &pGrd->stInflection[i - 1], |
266 | 1 | &pGrd->stInflection[i], pGrd->fZMin); |
267 | 1 | createIP(1, sColor.r, sColor.g, sColor.b, pMap, &nWarkerMark); |
268 | 1 | break; |
269 | 1 | } |
270 | 3 | } |
271 | | // the interesting case of zmin beig higher than the max inflection value |
272 | 3 | if (i >= pGrd->iNumColorInflections) |
273 | 2 | { |
274 | 2 | createIP(1, pGrd->stInflection[pGrd->iNumColorInflections - 1].r, |
275 | 2 | pGrd->stInflection[pGrd->iNumColorInflections - 1].g, |
276 | 2 | pGrd->stInflection[pGrd->iNumColorInflections - 1].b, pMap, |
277 | 2 | &nWarkerMark); |
278 | 2 | createIP(mapSize - 1, |
279 | 2 | pGrd->stInflection[pGrd->iNumColorInflections - 1].r, |
280 | 2 | pGrd->stInflection[pGrd->iNumColorInflections - 1].g, |
281 | 2 | pGrd->stInflection[pGrd->iNumColorInflections - 1].b, pMap, |
282 | 2 | &nWarkerMark); |
283 | 2 | } |
284 | 1 | else |
285 | 1 | { |
286 | 1 | int index = 0; |
287 | 5 | for (; i < pGrd->iNumColorInflections; i++) |
288 | 4 | { |
289 | 4 | if (pGrd->fZMax < pGrd->stInflection[i].zVal) |
290 | 0 | { |
291 | | // then we must be between i and i-1 |
292 | 0 | linearColor(&sColor, &pGrd->stInflection[i - 1], |
293 | 0 | &pGrd->stInflection[i], pGrd->fZMax); |
294 | 0 | index = mapSize - 1; |
295 | 0 | createIP(index, sColor.r, sColor.g, sColor.b, pMap, |
296 | 0 | &nWarkerMark); |
297 | 0 | break; |
298 | 0 | } |
299 | | // save the inflections between zmin and zmax |
300 | 4 | index = |
301 | 4 | static_cast<int>(((pGrd->stInflection[i].zVal - pGrd->fZMin) / |
302 | 4 | (pGrd->fZMax - pGrd->fZMin)) * |
303 | 4 | mapSize); |
304 | | |
305 | 4 | if (index >= mapSize) |
306 | 1 | index = mapSize - 1; |
307 | 4 | createIP(index, pGrd->stInflection[i].r, pGrd->stInflection[i].g, |
308 | 4 | pGrd->stInflection[i].b, pMap, &nWarkerMark); |
309 | 4 | } |
310 | 1 | if (index < mapSize - 1) |
311 | 0 | createIP(mapSize - 1, |
312 | 0 | pGrd->stInflection[pGrd->iNumColorInflections - 1].r, |
313 | 0 | pGrd->stInflection[pGrd->iNumColorInflections - 1].g, |
314 | 0 | pGrd->stInflection[pGrd->iNumColorInflections - 1].b, pMap, |
315 | 0 | &nWarkerMark); |
316 | 1 | } |
317 | 3 | return 0; |
318 | 5 | } |
319 | | |
320 | | // solve for a color between pIPLow and pIPHigh |
321 | | void linearColor(NWT_RGB *pRGB, NWT_INFLECTION *pIPLow, NWT_INFLECTION *pIPHigh, |
322 | | float fMid) |
323 | 1 | { |
324 | 1 | if (fMid < pIPLow->zVal) |
325 | 0 | { |
326 | 0 | pRGB->r = pIPLow->r; |
327 | 0 | pRGB->g = pIPLow->g; |
328 | 0 | pRGB->b = pIPLow->b; |
329 | 0 | } |
330 | 1 | else if (fMid > pIPHigh->zVal) |
331 | 0 | { |
332 | 0 | pRGB->r = pIPHigh->r; |
333 | 0 | pRGB->g = pIPHigh->g; |
334 | 0 | pRGB->b = pIPHigh->b; |
335 | 0 | } |
336 | 1 | else |
337 | 1 | { |
338 | 1 | float scale = (fMid - pIPLow->zVal) / (pIPHigh->zVal - pIPLow->zVal); |
339 | 1 | pRGB->r = static_cast<unsigned char>(scale * (pIPHigh->r - pIPLow->r) + |
340 | 1 | pIPLow->r + 0.5); |
341 | 1 | pRGB->g = static_cast<unsigned char>(scale * (pIPHigh->g - pIPLow->g) + |
342 | 1 | pIPLow->g + 0.5); |
343 | 1 | pRGB->b = static_cast<unsigned char>(scale * (pIPHigh->b - pIPLow->b) + |
344 | 1 | pIPLow->b + 0.5); |
345 | 1 | } |
346 | 1 | } |
347 | | |
348 | | // insert IP's into the map filling as we go |
349 | | void createIP(int index, unsigned char r, unsigned char g, unsigned char b, |
350 | | NWT_RGB *map, int *pnWarkerMark) |
351 | 15 | { |
352 | 15 | int i; |
353 | | |
354 | 15 | if (index == 0) |
355 | 5 | { |
356 | 5 | map[0].r = r; |
357 | 5 | map[0].g = g; |
358 | 5 | map[0].b = b; |
359 | 5 | *pnWarkerMark = 0; |
360 | 5 | return; |
361 | 5 | } |
362 | | |
363 | 10 | if (index <= *pnWarkerMark) |
364 | 1 | return; |
365 | | |
366 | 9 | int wm = *pnWarkerMark; |
367 | | |
368 | 9 | float rslope = |
369 | 9 | static_cast<float>(r - map[wm].r) / static_cast<float>(index - wm); |
370 | 9 | float gslope = |
371 | 9 | static_cast<float>(g - map[wm].g) / static_cast<float>(index - wm); |
372 | 9 | float bslope = |
373 | 9 | static_cast<float>(b - map[wm].b) / static_cast<float>(index - wm); |
374 | 12.2k | for (i = wm + 1; i < index; i++) |
375 | 12.2k | { |
376 | 12.2k | map[i].r = |
377 | 12.2k | static_cast<unsigned char>(map[wm].r + ((i - wm) * rslope) + 0.5); |
378 | 12.2k | map[i].g = |
379 | 12.2k | static_cast<unsigned char>(map[wm].g + ((i - wm) * gslope) + 0.5); |
380 | 12.2k | map[i].b = |
381 | 12.2k | static_cast<unsigned char>(map[wm].b + ((i - wm) * bslope) + 0.5); |
382 | 12.2k | } |
383 | 9 | map[index].r = r; |
384 | 9 | map[index].g = g; |
385 | 9 | map[index].b = b; |
386 | 9 | *pnWarkerMark = index; |
387 | 9 | return; |
388 | 10 | } |
389 | | |
390 | | void nwt_HillShade(unsigned char *r, unsigned char *g, unsigned char *b, |
391 | | unsigned char *h) |
392 | 0 | { |
393 | 0 | HLS hls; |
394 | 0 | NWT_RGB rgb; |
395 | 0 | rgb.r = *r; |
396 | 0 | rgb.g = *g; |
397 | 0 | rgb.b = *b; |
398 | 0 | hls = RGBtoHLS(rgb); |
399 | 0 | hls.l = static_cast<short>(hls.l + (*h) * HLSMAX / 256); |
400 | 0 | rgb = HLStoRGB(hls); |
401 | |
|
402 | 0 | *r = rgb.r; |
403 | 0 | *g = rgb.g; |
404 | 0 | *b = rgb.b; |
405 | 0 | return; |
406 | 0 | } |
407 | | |
408 | | NWT_GRID *nwtOpenGrid(char *filename) |
409 | 0 | { |
410 | 0 | unsigned char nwtHeader[1024]; |
411 | 0 | VSILFILE *fp = VSIFOpenL(filename, "rb"); |
412 | |
|
413 | 0 | if (fp == nullptr) |
414 | 0 | { |
415 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, "Can't open %s", filename); |
416 | 0 | return nullptr; |
417 | 0 | } |
418 | | |
419 | 0 | if (!VSIFReadL(nwtHeader, 1024, 1, fp)) |
420 | 0 | { |
421 | 0 | VSIFCloseL(fp); |
422 | 0 | return nullptr; |
423 | 0 | } |
424 | | |
425 | 0 | if (nwtHeader[0] != 'H' || nwtHeader[1] != 'G' || nwtHeader[2] != 'P' || |
426 | 0 | nwtHeader[3] != 'C') |
427 | 0 | { |
428 | 0 | VSIFCloseL(fp); |
429 | 0 | return nullptr; |
430 | 0 | } |
431 | | |
432 | 0 | NWT_GRID *pGrd = reinterpret_cast<NWT_GRID *>(calloc(1, sizeof(NWT_GRID))); |
433 | 0 | if (!pGrd) |
434 | 0 | { |
435 | 0 | VSIFCloseL(fp); |
436 | 0 | return nullptr; |
437 | 0 | } |
438 | | |
439 | 0 | if (nwtHeader[4] == '1') |
440 | 0 | pGrd->cFormat = 0x00; // grd - surface type |
441 | 0 | else if (nwtHeader[4] == '8') |
442 | 0 | pGrd->cFormat = 0x80; // grc classified type |
443 | 0 | else |
444 | 0 | { |
445 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
446 | 0 | "Unhandled Northwood format type = %0xd", nwtHeader[4]); |
447 | 0 | VSIFCloseL(fp); |
448 | 0 | free(pGrd); |
449 | 0 | return nullptr; |
450 | 0 | } |
451 | | |
452 | 0 | strncpy(pGrd->szFileName, filename, sizeof(pGrd->szFileName)); |
453 | 0 | pGrd->szFileName[sizeof(pGrd->szFileName) - 1] = '\0'; |
454 | 0 | pGrd->fp = fp; |
455 | 0 | nwt_ParseHeader(pGrd, nwtHeader); |
456 | |
|
457 | 0 | return pGrd; |
458 | 0 | } |
459 | | |
460 | | // close the file and free the mem |
461 | | void nwtCloseGrid(NWT_GRID *pGrd) |
462 | 14 | { |
463 | 14 | if ((pGrd->cFormat & 0x80) && |
464 | 14 | pGrd->stClassDict) // if is GRC - free the Dictionary |
465 | 9 | { |
466 | 57.4k | for (unsigned int i = 0; i < pGrd->stClassDict->nNumClassifiedItems; |
467 | 57.4k | i++) |
468 | 57.4k | { |
469 | 57.4k | free(pGrd->stClassDict->stClassifiedItem[i]); |
470 | 57.4k | } |
471 | 9 | free(pGrd->stClassDict->stClassifiedItem); |
472 | 9 | free(pGrd->stClassDict); |
473 | 9 | } |
474 | 14 | if (pGrd->fp) |
475 | 0 | VSIFCloseL(pGrd->fp); |
476 | 14 | free(pGrd); |
477 | 14 | return; |
478 | 14 | } |
479 | | |
480 | | void nwtPrintGridHeader(NWT_GRID *pGrd) |
481 | 0 | { |
482 | 0 | if (pGrd->cFormat & 0x80) |
483 | 0 | { |
484 | 0 | printf("\n%s\n\nGrid type is Classified ", pGrd->szFileName); /*ok*/ |
485 | 0 | if (pGrd->cFormat == 0x81) |
486 | 0 | printf("4 bit (Less than 16 Classes)"); /*ok*/ |
487 | 0 | else if (pGrd->cFormat == 0x82) |
488 | 0 | printf("8 bit (Less than 256 Classes)"); /*ok*/ |
489 | 0 | else if (pGrd->cFormat == 0x84) |
490 | 0 | printf("16 bit (Less than 65536 Classes)"); /*ok*/ |
491 | 0 | else |
492 | 0 | { |
493 | 0 | printf("GRC - Unhandled Format or Type %d", pGrd->cFormat); /*ok*/ |
494 | 0 | return; |
495 | 0 | } |
496 | 0 | } |
497 | 0 | else |
498 | 0 | { |
499 | 0 | printf("\n%s\n\nGrid type is Numeric ", pGrd->szFileName); /*ok*/ |
500 | 0 | if (pGrd->cFormat == 0x00) |
501 | 0 | printf("16 bit (Standard Precision)"); /*ok*/ |
502 | 0 | else if (pGrd->cFormat == 0x01) |
503 | 0 | printf("32 bit (High Precision)"); /*ok*/ |
504 | 0 | else |
505 | 0 | { |
506 | 0 | printf("GRD - Unhandled Format or Type %d", pGrd->cFormat); /*ok*/ |
507 | 0 | return; |
508 | 0 | } |
509 | 0 | } |
510 | 0 | printf("\nDim (x,y) = (%u,%u)", pGrd->nXSide, pGrd->nYSide); /*ok*/ |
511 | 0 | printf("\nStep Size = %f", pGrd->dfStepSize); /*ok*/ |
512 | 0 | printf("\nBounds = (%f,%f) (%f,%f)", pGrd->dfMinX, pGrd->dfMinY, /*ok*/ |
513 | 0 | pGrd->dfMaxX, pGrd->dfMaxY); |
514 | 0 | printf("\nCoordinate System = %s", pGrd->cMICoordSys); /*ok*/ |
515 | |
|
516 | 0 | if (!(pGrd->cFormat & 0x80)) // print the numeric specific stuff |
517 | 0 | { |
518 | 0 | printf("\nMin Z = %f Max Z = %f Z Units = %d \"%s\"", /*ok*/ |
519 | 0 | pGrd->fZMin, pGrd->fZMax, pGrd->iZUnits, pGrd->cZUnits); |
520 | |
|
521 | 0 | printf("\n\nDisplay Mode ="); /*ok*/ |
522 | 0 | if (pGrd->bShowGradient) |
523 | 0 | printf(" Color Gradient"); /*ok*/ |
524 | |
|
525 | 0 | if (pGrd->bShowGradient && pGrd->bShowHillShade) |
526 | 0 | printf(" and"); /*ok*/ |
527 | |
|
528 | 0 | if (pGrd->bShowHillShade) |
529 | 0 | printf(" Hill Shading"); /*ok*/ |
530 | |
|
531 | 0 | for (int i = 0; i < pGrd->iNumColorInflections; i++) |
532 | 0 | { |
533 | 0 | printf("\nColor Inflection %d - %f (%d,%d,%d)", i + 1, /*ok*/ |
534 | 0 | pGrd->stInflection[i].zVal, pGrd->stInflection[i].r, |
535 | 0 | pGrd->stInflection[i].g, pGrd->stInflection[i].b); |
536 | 0 | } |
537 | |
|
538 | 0 | if (pGrd->bHillShadeExists) |
539 | 0 | { |
540 | 0 | printf("\n\nHill Shade Azumith = %.1f Inclination = %.1f " /*ok*/ |
541 | 0 | "Brightness = %d Contrast = %d", |
542 | 0 | pGrd->fHillShadeAzimuth, pGrd->fHillShadeAngle, |
543 | 0 | pGrd->cHillShadeBrightness, pGrd->cHillShadeContrast); |
544 | 0 | } |
545 | 0 | else |
546 | 0 | printf("\n\nNo Hill Shade Data"); /*ok*/ |
547 | 0 | } |
548 | 0 | else // print the classified specific stuff |
549 | 0 | { |
550 | 0 | printf("\nNumber of Classes defined = %u", /*ok*/ |
551 | 0 | pGrd->stClassDict->nNumClassifiedItems); |
552 | 0 | for (int i = 0; |
553 | 0 | i < static_cast<int>(pGrd->stClassDict->nNumClassifiedItems); i++) |
554 | 0 | { |
555 | 0 | printf("\n%s - (%d,%d,%d) Raw = %d %d %d", /*ok*/ |
556 | 0 | pGrd->stClassDict->stClassifiedItem[i]->szClassName, |
557 | 0 | pGrd->stClassDict->stClassifiedItem[i]->r, |
558 | 0 | pGrd->stClassDict->stClassifiedItem[i]->g, |
559 | 0 | pGrd->stClassDict->stClassifiedItem[i]->b, |
560 | 0 | pGrd->stClassDict->stClassifiedItem[i]->usPixVal, |
561 | 0 | pGrd->stClassDict->stClassifiedItem[i]->res1, |
562 | 0 | pGrd->stClassDict->stClassifiedItem[i]->res2); |
563 | 0 | } |
564 | 0 | } |
565 | 0 | } |
566 | | |
567 | | HLS RGBtoHLS(NWT_RGB rgb) |
568 | 0 | { |
569 | | /* get R, G, and B out of DWORD */ |
570 | 0 | short R = rgb.r; |
571 | 0 | short G = rgb.g; |
572 | 0 | short B = rgb.b; |
573 | | |
574 | | /* calculate lightness */ |
575 | 0 | unsigned char cMax = |
576 | 0 | static_cast<unsigned char>(std::max(std::max(R, G), B)); |
577 | 0 | unsigned char cMin = |
578 | 0 | static_cast<unsigned char>(std::min(std::min(R, G), B)); |
579 | 0 | HLS hls; |
580 | 0 | hls.l = (((cMax + cMin) * HLSMAX) + RGBMAX) / (2 * RGBMAX); |
581 | |
|
582 | 0 | short Rdelta, Gdelta, Bdelta; /* intermediate value: % of spread from max */ |
583 | 0 | if (cMax == cMin) |
584 | 0 | { /* r=g=b --> achromatic case */ |
585 | 0 | hls.s = 0; /* saturation */ |
586 | 0 | hls.h = UNDEFINED; /* hue */ |
587 | 0 | } |
588 | 0 | else |
589 | 0 | { /* chromatic case */ |
590 | | /* saturation */ |
591 | 0 | if (hls.l <= (HLSMAX / 2)) |
592 | 0 | hls.s = (((cMax - cMin) * HLSMAX) + ((cMax + cMin) / 2)) / |
593 | 0 | (cMax + cMin); |
594 | 0 | else |
595 | 0 | hls.s = |
596 | 0 | (((cMax - cMin) * HLSMAX) + ((2 * RGBMAX - cMax - cMin) / 2)) / |
597 | 0 | (2 * RGBMAX - cMax - cMin); |
598 | | |
599 | | /* hue */ |
600 | 0 | Rdelta = |
601 | 0 | (((cMax - R) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin); |
602 | 0 | Gdelta = |
603 | 0 | (((cMax - G) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin); |
604 | 0 | Bdelta = |
605 | 0 | (((cMax - B) * (HLSMAX / 6)) + ((cMax - cMin) / 2)) / (cMax - cMin); |
606 | |
|
607 | 0 | if (R == cMax) |
608 | 0 | hls.h = Bdelta - Gdelta; |
609 | 0 | else if (G == cMax) |
610 | 0 | hls.h = (HLSMAX / 3) + Rdelta - Bdelta; |
611 | 0 | else /* B == cMax */ |
612 | 0 | hls.h = ((2 * HLSMAX) / 3) + Gdelta - Rdelta; |
613 | |
|
614 | 0 | if (hls.h < 0) |
615 | 0 | hls.h += HLSMAX; |
616 | 0 | if (hls.h > HLSMAX) |
617 | 0 | hls.h -= HLSMAX; |
618 | 0 | } |
619 | 0 | return hls; |
620 | 0 | } |
621 | | |
622 | | /* utility routine for HLStoRGB */ |
623 | | static short HueToRGB(short n1, short n2, short hue) |
624 | 0 | { |
625 | | /* range check: note values passed add/subtract thirds of range */ |
626 | 0 | if (hue < 0) |
627 | 0 | hue += HLSMAX; |
628 | |
|
629 | 0 | if (hue > HLSMAX) |
630 | 0 | hue -= HLSMAX; |
631 | | |
632 | | /* return r,g, or b value from this tridrant */ |
633 | 0 | if (hue < (HLSMAX / 6)) |
634 | 0 | return n1 + (((n2 - n1) * hue + (HLSMAX / 12)) / (HLSMAX / 6)); |
635 | 0 | if (hue < (HLSMAX / 2)) |
636 | 0 | return n2; |
637 | 0 | if (hue < ((HLSMAX * 2) / 3)) |
638 | 0 | return n1 + (((n2 - n1) * (((HLSMAX * 2) / 3) - hue) + (HLSMAX / 12)) / |
639 | 0 | (HLSMAX / 6)); |
640 | 0 | else |
641 | 0 | return n1; |
642 | 0 | } |
643 | | |
644 | | NWT_RGB HLStoRGB(HLS hls) |
645 | 0 | { |
646 | 0 | NWT_RGB rgb; |
647 | |
|
648 | 0 | if (hls.s == 0) |
649 | 0 | { /* achromatic case */ |
650 | 0 | rgb.r = static_cast<unsigned char>((hls.l * RGBMAX) / HLSMAX); |
651 | 0 | rgb.g = rgb.r; |
652 | 0 | rgb.b = rgb.r; |
653 | 0 | if (hls.h != UNDEFINED) |
654 | 0 | { |
655 | | /* ERROR */ |
656 | 0 | } |
657 | 0 | } |
658 | 0 | else |
659 | 0 | { /* chromatic case */ |
660 | | /* set up magic numbers */ |
661 | 0 | short Magic1, Magic2; /* calculated magic numbers (really!) */ |
662 | 0 | if (hls.l <= (HLSMAX / 2)) |
663 | 0 | Magic2 = (hls.l * (HLSMAX + hls.s) + (HLSMAX / 2)) / HLSMAX; |
664 | 0 | else |
665 | 0 | Magic2 = hls.l + hls.s - ((hls.l * hls.s) + (HLSMAX / 2)) / HLSMAX; |
666 | 0 | Magic1 = 2 * hls.l - Magic2; |
667 | | |
668 | | /* get RGB, change units from HLSMAX to RGBMAX */ |
669 | 0 | rgb.r = static_cast<unsigned char>( |
670 | 0 | (HueToRGB(Magic1, Magic2, hls.h + (HLSMAX / 3)) * RGBMAX + |
671 | 0 | (HLSMAX / 2)) / |
672 | 0 | HLSMAX); |
673 | 0 | rgb.g = static_cast<unsigned char>( |
674 | 0 | (HueToRGB(Magic1, Magic2, hls.h) * RGBMAX + (HLSMAX / 2)) / HLSMAX); |
675 | 0 | rgb.b = static_cast<unsigned char>( |
676 | 0 | (HueToRGB(Magic1, Magic2, hls.h - (HLSMAX / 3)) * RGBMAX + |
677 | 0 | (HLSMAX / 2)) / |
678 | 0 | HLSMAX); |
679 | 0 | } |
680 | |
|
681 | 0 | return rgb; |
682 | 0 | } |