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