1 /******************************************************************************
2 *
3 * Project: Microstation DGN Access Library
4 * Purpose: DGN Access Library element reading code.
5 * Author: Frank Warmerdam, warmerdam@pobox.com
6 *
7 ******************************************************************************
8 * Copyright (c) 2000, Avenza Systems Inc, http://www.avenza.com/
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 ****************************************************************************/
28
29 #include "dgnlibp.h"
30
31 #include <algorithm>
32
33 CPL_CVSID("$Id: dgnread.cpp 7b0ca41c54ccdcaaf512a7b11cad4dc93ca71e66 2020-11-06 22:14:23 +0100 Even Rouault $")
34
35 static DGNElemCore *DGNParseTCB( DGNInfo * );
36 static DGNElemCore *DGNParseColorTable( DGNInfo * );
37 static DGNElemCore *DGNParseTagSet( DGNInfo * );
38
39
40 /************************************************************************/
41 /* DGN_INT16() */
42 /************************************************************************/
43
DGN_INT16(const GByte * p)44 static short int DGN_INT16(const GByte *p)
45 {
46 return static_cast<short>(p[0] | (p[1] << 8));
47 }
48
49 /************************************************************************/
50 /* DGNGotoElement() */
51 /************************************************************************/
52
53 /**
54 * Seek to indicated element.
55 *
56 * Changes what element will be read on the next call to DGNReadElement().
57 * Note that this function requires and index, and one will be built if
58 * not already available.
59 *
60 * @param hDGN the file to affect.
61 * @param element_id the element to seek to. These values are sequentially
62 * ordered starting at zero for the first element.
63 *
64 * @return returns TRUE on success or FALSE on failure.
65 */
66
DGNGotoElement(DGNHandle hDGN,int element_id)67 int DGNGotoElement( DGNHandle hDGN, int element_id )
68
69 {
70 DGNInfo *psDGN = (DGNInfo *) hDGN;
71
72 DGNBuildIndex( psDGN );
73
74 if( element_id < 0 || element_id >= psDGN->element_count )
75 return FALSE;
76
77 if( VSIFSeekL( psDGN->fp, psDGN->element_index[element_id].offset,
78 SEEK_SET ) != 0 )
79 return FALSE;
80
81 psDGN->next_element_id = element_id;
82 psDGN->in_complex_group = false;
83
84 return TRUE;
85 }
86
87 /************************************************************************/
88 /* DGNLoadRawElement() */
89 /************************************************************************/
90
DGNLoadRawElement(DGNInfo * psDGN,int * pnType,int * pnLevel)91 int DGNLoadRawElement( DGNInfo *psDGN, int *pnType, int *pnLevel )
92
93 {
94 /* -------------------------------------------------------------------- */
95 /* Read the first four bytes to get the level, type, and word */
96 /* count. */
97 /* -------------------------------------------------------------------- */
98 if( VSIFReadL( psDGN->abyElem, 1, 4, psDGN->fp ) != 4 )
99 return FALSE;
100
101 /* Is this an 0xFFFF endof file marker? */
102 if( psDGN->abyElem[0] == 0xff && psDGN->abyElem[1] == 0xff )
103 return FALSE;
104
105 int nWords = psDGN->abyElem[2] + psDGN->abyElem[3]*256;
106 int nType = psDGN->abyElem[1] & 0x7f;
107 int nLevel = psDGN->abyElem[0] & 0x3f;
108
109 /* -------------------------------------------------------------------- */
110 /* Read the rest of the element data into the working buffer. */
111 /* -------------------------------------------------------------------- */
112 if( nWords * 2 + 4 >= (int) sizeof(psDGN->abyElem) )
113 return FALSE;
114
115 /* coverity[tainted_data] */
116 if( (int) VSIFReadL( psDGN->abyElem + 4, 2, nWords, psDGN->fp ) != nWords )
117 return FALSE;
118 psDGN->abyElem[4 + 2 * nWords] = 0;
119 psDGN->abyElem[sizeof(psDGN->abyElem)-1] = 0;
120
121 psDGN->nElemBytes = nWords * 2 + 4;
122
123 psDGN->next_element_id++;
124
125 /* -------------------------------------------------------------------- */
126 /* Return requested info. */
127 /* -------------------------------------------------------------------- */
128 if( pnType != nullptr )
129 *pnType = nType;
130
131 if( pnLevel != nullptr )
132 *pnLevel = nLevel;
133
134 return TRUE;
135 }
136
137 /************************************************************************/
138 /* DGNGetRawExtents() */
139 /* */
140 /* Returns false if the element type does not have recognizable */
141 /* element extents, other true and the extents will be updated. */
142 /* */
143 /* It is assumed the raw element data has been loaded into the */
144 /* working area by DGNLoadRawElement(). */
145 /************************************************************************/
146
147 static bool
DGNGetRawExtents(DGNInfo * psDGN,int nType,unsigned char * pabyRawData,GUInt32 * pnXMin,GUInt32 * pnYMin,GUInt32 * pnZMin,GUInt32 * pnXMax,GUInt32 * pnYMax,GUInt32 * pnZMax)148 DGNGetRawExtents( DGNInfo *psDGN, int nType, unsigned char *pabyRawData,
149 GUInt32 *pnXMin, GUInt32 *pnYMin, GUInt32 *pnZMin,
150 GUInt32 *pnXMax, GUInt32 *pnYMax, GUInt32 *pnZMax )
151
152 {
153 if( pabyRawData == nullptr )
154 pabyRawData = psDGN->abyElem + 0;
155
156 switch( nType )
157 {
158 case DGNT_LINE:
159 case DGNT_LINE_STRING:
160 case DGNT_SHAPE:
161 case DGNT_CURVE:
162 case DGNT_BSPLINE_POLE:
163 case DGNT_BSPLINE_SURFACE_HEADER:
164 case DGNT_BSPLINE_CURVE_HEADER:
165 case DGNT_ELLIPSE:
166 case DGNT_ARC:
167 case DGNT_TEXT:
168 case DGNT_TEXT_NODE:
169 case DGNT_COMPLEX_CHAIN_HEADER:
170 case DGNT_COMPLEX_SHAPE_HEADER:
171 case DGNT_CONE:
172 case DGNT_3DSURFACE_HEADER:
173 case DGNT_3DSOLID_HEADER:
174 *pnXMin = DGN_INT32( pabyRawData + 4 );
175 *pnYMin = DGN_INT32( pabyRawData + 8 );
176 if( pnZMin != nullptr )
177 *pnZMin = DGN_INT32( pabyRawData + 12 );
178
179 *pnXMax = DGN_INT32( pabyRawData + 16 );
180 *pnYMax = DGN_INT32( pabyRawData + 20 );
181 if( pnZMax != nullptr )
182 *pnZMax = DGN_INT32( pabyRawData + 24 );
183 return true;
184
185 default:
186 return false;
187 }
188 }
189
190 /************************************************************************/
191 /* DGNGetElementExtents() */
192 /************************************************************************/
193
194 /**
195 * Fetch extents of an element.
196 *
197 * This function will return the extents of the passed element if possible.
198 * The extents are extracted from the element header if it contains them,
199 * and transformed into master georeferenced format. Some element types
200 * do not have extents at all and will fail.
201 *
202 * This call will also fail if the extents raw data for the element is not
203 * available. This will occur if it was not the most recently read element,
204 * and if the raw_data field is not loaded.
205 *
206 * @param hDGN the handle of the file to read from.
207 *
208 * @param psElement the element to extract extents from.
209 *
210 * @param psMin structure loaded with X, Y and Z minimum values for the
211 * extent.
212 *
213 * @param psMax structure loaded with X, Y and Z maximum values for the
214 * extent.
215 *
216 * @return TRUE on success of FALSE if extracting extents fails.
217 */
218
DGNGetElementExtents(DGNHandle hDGN,DGNElemCore * psElement,DGNPoint * psMin,DGNPoint * psMax)219 int DGNGetElementExtents( DGNHandle hDGN, DGNElemCore *psElement,
220 DGNPoint *psMin, DGNPoint *psMax )
221
222 {
223 DGNInfo *psDGN = (DGNInfo *) hDGN;
224 bool bResult = false;
225
226 GUInt32 anMin[3] = { 0, 0, 0 };
227 GUInt32 anMax[3] = { 0, 0, 0 };
228
229 /* -------------------------------------------------------------------- */
230 /* Get the extents if we have raw data in the element, or */
231 /* loaded in the file buffer. */
232 /* -------------------------------------------------------------------- */
233 if( psElement->raw_data != nullptr )
234 bResult = DGNGetRawExtents( psDGN, psElement->type,
235 psElement->raw_data,
236 anMin + 0, anMin + 1, anMin + 2,
237 anMax + 0, anMax + 1, anMax + 2 );
238 else if( psElement->element_id == psDGN->next_element_id - 1 )
239 bResult = DGNGetRawExtents( psDGN, psElement->type,
240 psDGN->abyElem + 0,
241 anMin + 0, anMin + 1, anMin + 2,
242 anMax + 0, anMax + 1, anMax + 2 );
243 else
244 {
245 CPLError(CE_Warning, CPLE_AppDefined,
246 "DGNGetElementExtents() fails because the requested element "
247 "does not have raw data available." );
248 return FALSE;
249 }
250
251 if( !bResult )
252 return FALSE;
253
254 /* -------------------------------------------------------------------- */
255 /* Transform to user coordinate system and return. The offset */
256 /* is to convert from "binary offset" form to twos complement. */
257 /* -------------------------------------------------------------------- */
258 psMin->x = anMin[0] - 2147483648.0;
259 psMin->y = anMin[1] - 2147483648.0;
260 psMin->z = anMin[2] - 2147483648.0;
261
262 psMax->x = anMax[0] - 2147483648.0;
263 psMax->y = anMax[1] - 2147483648.0;
264 psMax->z = anMax[2] - 2147483648.0;
265
266 DGNTransformPoint( psDGN, psMin );
267 DGNTransformPoint( psDGN, psMax );
268
269 return TRUE;
270 }
271
272 /************************************************************************/
273 /* DGNProcessElement() */
274 /* */
275 /* Assumes the raw element data has already been loaded, and */
276 /* tries to convert it into an element structure. */
277 /************************************************************************/
278
DGNProcessElement(DGNInfo * psDGN,int nType,int nLevel)279 static DGNElemCore *DGNProcessElement( DGNInfo *psDGN, int nType, int nLevel )
280
281 {
282 DGNElemCore *psElement = nullptr;
283
284 /* -------------------------------------------------------------------- */
285 /* Handle based on element type. */
286 /* -------------------------------------------------------------------- */
287 switch( nType )
288 {
289 case DGNT_CELL_HEADER:
290 {
291 DGNElemCellHeader *psCell = static_cast<DGNElemCellHeader *>(
292 CPLCalloc(sizeof(DGNElemCellHeader), 1));
293 psElement = (DGNElemCore *) psCell;
294 psElement->stype = DGNST_CELL_HEADER;
295 DGNParseCore( psDGN, psElement );
296
297 psCell->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
298
299 DGNRad50ToAscii( psDGN->abyElem[38] + psDGN->abyElem[39] * 256,
300 psCell->name + 0 );
301 DGNRad50ToAscii( psDGN->abyElem[40] + psDGN->abyElem[41] * 256,
302 psCell->name + 3 );
303
304 psCell->cclass = psDGN->abyElem[42] + psDGN->abyElem[43] * 256;
305 psCell->levels[0] = psDGN->abyElem[44] + psDGN->abyElem[45] * 256;
306 psCell->levels[1] = psDGN->abyElem[46] + psDGN->abyElem[47] * 256;
307 psCell->levels[2] = psDGN->abyElem[48] + psDGN->abyElem[49] * 256;
308 psCell->levels[3] = psDGN->abyElem[50] + psDGN->abyElem[51] * 256;
309
310 if( psDGN->dimension == 2 )
311 {
312 psCell->rnglow.x = DGN_INT32( psDGN->abyElem + 52 );
313 psCell->rnglow.y = DGN_INT32( psDGN->abyElem + 56 );
314 psCell->rnghigh.x = DGN_INT32( psDGN->abyElem + 60 );
315 psCell->rnghigh.y = DGN_INT32( psDGN->abyElem + 64 );
316
317 psCell->trans[0] =
318 1.0 * DGN_INT32( psDGN->abyElem + 68 ) / (1<<31);
319 psCell->trans[1] =
320 1.0 * DGN_INT32( psDGN->abyElem + 72 ) / (1<<31);
321 psCell->trans[2] =
322 1.0 * DGN_INT32( psDGN->abyElem + 76 ) / (1<<31);
323 psCell->trans[3] =
324 1.0 * DGN_INT32( psDGN->abyElem + 80 ) / (1<<31);
325
326 psCell->origin.x = DGN_INT32( psDGN->abyElem + 84 );
327 psCell->origin.y = DGN_INT32( psDGN->abyElem + 88 );
328
329 {
330 const double a = DGN_INT32( psDGN->abyElem + 68 );
331 const double b = DGN_INT32( psDGN->abyElem + 72 );
332 const double c = DGN_INT32( psDGN->abyElem + 76 );
333 const double d = DGN_INT32( psDGN->abyElem + 80 );
334 const double a2 = a * a;
335 const double c2 = c * c;
336
337 psCell->xscale = sqrt(a2 + c2) / 214748;
338 psCell->yscale = sqrt(b*b + d*d) / 214748;
339 if( (a2 + c2) <= 0.0 )
340 psCell->rotation = 0.0;
341 else
342 psCell->rotation = acos(a / sqrt(a2 + c2));
343
344 if (b <= 0)
345 psCell->rotation = psCell->rotation * 180 / M_PI;
346 else
347 psCell->rotation = 360 - psCell->rotation * 180 / M_PI;
348 }
349 }
350 else
351 {
352 psCell->rnglow.x = DGN_INT32( psDGN->abyElem + 52 );
353 psCell->rnglow.y = DGN_INT32( psDGN->abyElem + 56 );
354 psCell->rnglow.z = DGN_INT32( psDGN->abyElem + 60 );
355 psCell->rnghigh.x = DGN_INT32( psDGN->abyElem + 64 );
356 psCell->rnghigh.y = DGN_INT32( psDGN->abyElem + 68 );
357 psCell->rnghigh.z = DGN_INT32( psDGN->abyElem + 72 );
358
359 psCell->trans[0] =
360 1.0 * DGN_INT32( psDGN->abyElem + 76 ) / (1<<31);
361 psCell->trans[1] =
362 1.0 * DGN_INT32( psDGN->abyElem + 80 ) / (1<<31);
363 psCell->trans[2] =
364 1.0 * DGN_INT32( psDGN->abyElem + 84 ) / (1<<31);
365 psCell->trans[3] =
366 1.0 * DGN_INT32( psDGN->abyElem + 88 ) / (1<<31);
367 psCell->trans[4] =
368 1.0 * DGN_INT32( psDGN->abyElem + 92 ) / (1<<31);
369 psCell->trans[5] =
370 1.0 * DGN_INT32( psDGN->abyElem + 96 ) / (1<<31);
371 psCell->trans[6] =
372 1.0 * DGN_INT32( psDGN->abyElem + 100 ) / (1<<31);
373 psCell->trans[7] =
374 1.0 * DGN_INT32( psDGN->abyElem + 104 ) / (1<<31);
375 psCell->trans[8] =
376 1.0 * DGN_INT32( psDGN->abyElem + 108 ) / (1<<31);
377
378 psCell->origin.x = DGN_INT32( psDGN->abyElem + 112 );
379 psCell->origin.y = DGN_INT32( psDGN->abyElem + 116 );
380 psCell->origin.z = DGN_INT32( psDGN->abyElem + 120 );
381 }
382
383 DGNTransformPoint( psDGN, &(psCell->rnglow) );
384 DGNTransformPoint( psDGN, &(psCell->rnghigh) );
385 DGNTransformPoint( psDGN, &(psCell->origin) );
386 }
387 break;
388
389 case DGNT_CELL_LIBRARY:
390 {
391 DGNElemCellLibrary *psCell = static_cast<DGNElemCellLibrary *>(
392 CPLCalloc(sizeof(DGNElemCellLibrary), 1));
393 psElement = (DGNElemCore *) psCell;
394 psElement->stype = DGNST_CELL_LIBRARY;
395 DGNParseCore( psDGN, psElement );
396
397 DGNRad50ToAscii( psDGN->abyElem[32] + psDGN->abyElem[33] * 256,
398 psCell->name + 0 );
399 DGNRad50ToAscii( psDGN->abyElem[34] + psDGN->abyElem[35] * 256,
400 psCell->name + 3 );
401
402 psElement->properties = psDGN->abyElem[38]
403 + psDGN->abyElem[39] * 256;
404
405 psCell->dispsymb = psDGN->abyElem[40] + psDGN->abyElem[41] * 256;
406
407 psCell->cclass = psDGN->abyElem[42] + psDGN->abyElem[43] * 256;
408 psCell->levels[0] = psDGN->abyElem[44] + psDGN->abyElem[45] * 256;
409 psCell->levels[1] = psDGN->abyElem[46] + psDGN->abyElem[47] * 256;
410 psCell->levels[2] = psDGN->abyElem[48] + psDGN->abyElem[49] * 256;
411 psCell->levels[3] = psDGN->abyElem[50] + psDGN->abyElem[51] * 256;
412
413 psCell->numwords = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
414
415 memset( psCell->description, 0, sizeof(psCell->description) );
416
417 for( int iWord = 0; iWord < 9; iWord++ )
418 {
419 int iOffset = 52 + iWord * 2;
420
421 DGNRad50ToAscii( psDGN->abyElem[iOffset]
422 + psDGN->abyElem[iOffset+1] * 256,
423 psCell->description + iWord * 3 );
424 }
425 }
426 break;
427
428 case DGNT_LINE:
429 {
430 DGNElemMultiPoint *psLine = static_cast<DGNElemMultiPoint *>(
431 CPLCalloc(sizeof(DGNElemMultiPoint) + sizeof(DGNPoint), 1));
432 psElement = (DGNElemCore *) psLine;
433 psElement->stype = DGNST_MULTIPOINT;
434 DGNParseCore( psDGN, psElement );
435
436 int deltaLength = 0, deltaStart = 0;
437 if (psLine->core.properties & DGNPF_ATTRIBUTES)
438 {
439 for (int iAttr = 0; iAttr<psLine->core.attr_bytes - 3; iAttr++)
440 {
441 if (psLine->core.attr_data[iAttr] == 0xA9 &&
442 psLine->core.attr_data[iAttr + 1] == 0x51)
443 {
444 deltaLength = (psLine->core.attr_data[iAttr + 2] +
445 psLine->core.attr_data[iAttr + 3] * 256) * 2;
446 deltaStart = iAttr + 6;
447 break;
448 }
449 }
450 }
451
452 psLine->num_vertices = 2;
453 if( psDGN->dimension == 2 )
454 {
455 psLine->vertices[0].x = DGN_INT32( psDGN->abyElem + 36 );
456 psLine->vertices[0].y = DGN_INT32( psDGN->abyElem + 40 );
457 psLine->vertices[1].x = DGN_INT32( psDGN->abyElem + 44 );
458 psLine->vertices[1].y = DGN_INT32( psDGN->abyElem + 48 );
459 }
460 else
461 {
462 psLine->vertices[0].x = DGN_INT32( psDGN->abyElem + 36 );
463 psLine->vertices[0].y = DGN_INT32( psDGN->abyElem + 40 );
464 psLine->vertices[0].z = DGN_INT32( psDGN->abyElem + 44 );
465 psLine->vertices[1].x = DGN_INT32( psDGN->abyElem + 48 );
466 psLine->vertices[1].y = DGN_INT32( psDGN->abyElem + 52 );
467 psLine->vertices[1].z = DGN_INT32( psDGN->abyElem + 56 );
468 }
469
470 if (deltaStart && deltaLength &&
471 deltaStart + 1 * 4 + 2 + 2 <= psLine->core.attr_bytes)
472 {
473 for (int i=0; i<2; i++)
474 {
475 int dx = DGN_INT16(psLine->core.attr_data + deltaStart + i * 4);
476 int dy = DGN_INT16(psLine->core.attr_data + deltaStart + i * 4 + 2);
477 psLine->vertices[i].x += dx / 32767.0;
478 psLine->vertices[i].y += dy / 32767.0;
479 }
480 }
481
482 DGNTransformPoint( psDGN, psLine->vertices + 0 );
483 DGNTransformPoint( psDGN, psLine->vertices + 1 );
484 }
485 break;
486
487 case DGNT_LINE_STRING:
488 case DGNT_SHAPE:
489 case DGNT_CURVE:
490 case DGNT_BSPLINE_POLE:
491 {
492 int pntsize = psDGN->dimension * 4;
493
494 int count = psDGN->abyElem[36] + psDGN->abyElem[37]*256;
495 if( count < 2 )
496 {
497 CPLError(CE_Failure, CPLE_AssertionFailed, "count < 2");
498 return nullptr;
499 }
500 DGNElemMultiPoint *psLine = static_cast<DGNElemMultiPoint *>(
501 VSI_CALLOC_VERBOSE(sizeof(DGNElemMultiPoint)+(count-1)*sizeof(DGNPoint),
502 1));
503 if( psLine == nullptr )
504 return nullptr;
505 psElement = (DGNElemCore *) psLine;
506 psElement->stype = DGNST_MULTIPOINT;
507 DGNParseCore( psDGN, psElement );
508
509 if( psDGN->nElemBytes < 38 + count * pntsize )
510 {
511 int new_count = (psDGN->nElemBytes - 38) / pntsize;
512 if( new_count < 0 )
513 {
514 CPLError(CE_Failure, CPLE_AssertionFailed, "new_count < 2");
515 DGNFreeElement(psDGN, psElement);
516 return nullptr;
517 }
518 CPLError( CE_Warning, CPLE_AppDefined,
519 "Trimming multipoint vertices to %d from %d because\n"
520 "element is short.\n",
521 new_count,
522 count );
523 count = new_count;
524 }
525 int deltaLength=0,deltaStart=0;
526 if (psLine->core.properties & DGNPF_ATTRIBUTES)
527 {
528 for (int iAttr=0; iAttr<psLine->core.attr_bytes-3; iAttr++)
529 {
530 if (psLine->core.attr_data[iAttr] == 0xA9 &&
531 psLine->core.attr_data[iAttr+1] == 0x51)
532 {
533 deltaLength = (psLine->core.attr_data[iAttr + 2] +
534 psLine->core.attr_data[iAttr + 3] * 256) * 2;
535 deltaStart = iAttr + 6;
536 break;
537 }
538 }
539 }
540 for( int i = 0; i < count &&
541 (( psDGN->dimension == 3 ) ? 46 : 42) +
542 i*pntsize + 4 <= psDGN->nElemBytes; i++ )
543 {
544 psLine->vertices[i].x =
545 DGN_INT32( psDGN->abyElem + 38 + i*pntsize );
546 psLine->vertices[i].y =
547 DGN_INT32( psDGN->abyElem + 42 + i*pntsize );
548 if( psDGN->dimension == 3 )
549 psLine->vertices[i].z =
550 DGN_INT32( psDGN->abyElem + 46 + i*pntsize );
551 if (deltaStart && deltaLength &&
552 deltaStart + i * 4 + 2 + 2 <= psLine->core.attr_bytes)
553 {
554 int dx = DGN_INT16(psLine->core.attr_data + deltaStart + i * 4);
555 int dy = DGN_INT16(psLine->core.attr_data + deltaStart + i * 4 + 2);
556 psLine->vertices[i].x += dx / 32767.0;
557 psLine->vertices[i].y += dy / 32767.0;
558 }
559 DGNTransformPoint( psDGN, psLine->vertices + i );
560 psLine->num_vertices = i+1;
561 }
562 }
563 break;
564
565 case DGNT_TEXT_NODE:
566 {
567 DGNElemTextNode *psNode = static_cast<DGNElemTextNode *>(
568 CPLCalloc(sizeof(DGNElemTextNode), 1));
569 psElement = (DGNElemCore *) psNode;
570 psElement->stype = DGNST_TEXT_NODE;
571 DGNParseCore( psDGN, psElement );
572
573 psNode->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
574 psNode->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256;
575
576 psNode->node_number = psDGN->abyElem[40] + psDGN->abyElem[41] * 256;
577 psNode->max_length = psDGN->abyElem[42];
578 psNode->max_used = psDGN->abyElem[43];
579 psNode->font_id = psDGN->abyElem[44];
580 psNode->justification = psDGN->abyElem[45];
581 psNode->length_mult = (DGN_INT32( psDGN->abyElem + 50 ))
582 * psDGN->scale * 6.0 / 1000.0;
583 psNode->height_mult = (DGN_INT32( psDGN->abyElem + 54 ))
584 * psDGN->scale * 6.0 / 1000.0;
585
586 if( psDGN->dimension == 2 )
587 {
588 psNode->rotation = DGN_INT32( psDGN->abyElem + 58 ) / 360000.0;
589
590 psNode->origin.x = DGN_INT32( psDGN->abyElem + 62 );
591 psNode->origin.y = DGN_INT32( psDGN->abyElem + 66 );
592 }
593 else
594 {
595 /* leave quaternion for later */
596
597 psNode->origin.x = DGN_INT32( psDGN->abyElem + 74 );
598 psNode->origin.y = DGN_INT32( psDGN->abyElem + 78 );
599 psNode->origin.z = DGN_INT32( psDGN->abyElem + 82 );
600 }
601 DGNTransformPoint( psDGN, &(psNode->origin) );
602 }
603 break;
604
605 case DGNT_GROUP_DATA:
606 if( nLevel == DGN_GDL_COLOR_TABLE )
607 {
608 psElement = DGNParseColorTable( psDGN );
609 }
610 else
611 {
612 psElement = static_cast<DGNElemCore *>(
613 CPLCalloc(sizeof(DGNElemCore), 1));
614 psElement->stype = DGNST_CORE;
615 DGNParseCore( psDGN, psElement );
616 }
617 break;
618
619 case DGNT_ELLIPSE:
620 {
621 DGNElemArc *psEllipse =
622 static_cast<DGNElemArc *>(CPLCalloc(sizeof(DGNElemArc), 1));
623 psElement = (DGNElemCore *) psEllipse;
624 psElement->stype = DGNST_ARC;
625 DGNParseCore( psDGN, psElement );
626
627 memcpy( &(psEllipse->primary_axis), psDGN->abyElem + 36, 8 );
628 DGN2IEEEDouble( &(psEllipse->primary_axis) );
629 psEllipse->primary_axis *= psDGN->scale;
630
631 memcpy( &(psEllipse->secondary_axis), psDGN->abyElem + 44, 8 );
632 DGN2IEEEDouble( &(psEllipse->secondary_axis) );
633 psEllipse->secondary_axis *= psDGN->scale;
634
635 if( psDGN->dimension == 2 )
636 {
637 psEllipse->rotation = DGN_INT32( psDGN->abyElem + 52 );
638 psEllipse->rotation = psEllipse->rotation / 360000.0;
639
640 memcpy( &(psEllipse->origin.x), psDGN->abyElem + 56, 8 );
641 DGN2IEEEDouble( &(psEllipse->origin.x) );
642
643 memcpy( &(psEllipse->origin.y), psDGN->abyElem + 64, 8 );
644 DGN2IEEEDouble( &(psEllipse->origin.y) );
645 }
646 else
647 {
648 /* leave quaternion for later */
649
650 memcpy( &(psEllipse->origin.x), psDGN->abyElem + 68, 8 );
651 DGN2IEEEDouble( &(psEllipse->origin.x) );
652
653 memcpy( &(psEllipse->origin.y), psDGN->abyElem + 76, 8 );
654 DGN2IEEEDouble( &(psEllipse->origin.y) );
655
656 memcpy( &(psEllipse->origin.z), psDGN->abyElem + 84, 8 );
657 DGN2IEEEDouble( &(psEllipse->origin.z) );
658
659 psEllipse->quat[0] = DGN_INT32( psDGN->abyElem + 52 );
660 psEllipse->quat[1] = DGN_INT32( psDGN->abyElem + 56 );
661 psEllipse->quat[2] = DGN_INT32( psDGN->abyElem + 60 );
662 psEllipse->quat[3] = DGN_INT32( psDGN->abyElem + 64 );
663 }
664
665 DGNTransformPoint( psDGN, &(psEllipse->origin) );
666
667 psEllipse->startang = 0.0;
668 psEllipse->sweepang = 360.0;
669 }
670 break;
671
672 case DGNT_ARC:
673 {
674 GInt32 nSweepVal = 0;
675
676 DGNElemArc *psEllipse =
677 static_cast<DGNElemArc *>(CPLCalloc(sizeof(DGNElemArc), 1));
678 psElement = (DGNElemCore *) psEllipse;
679 psElement->stype = DGNST_ARC;
680 DGNParseCore( psDGN, psElement );
681
682 psEllipse->startang = DGN_INT32( psDGN->abyElem + 36 );
683 psEllipse->startang = psEllipse->startang / 360000.0;
684 if( psDGN->abyElem[41] & 0x80 )
685 {
686 psDGN->abyElem[41] &= 0x7f;
687 nSweepVal = -1 * DGN_INT32( psDGN->abyElem + 40 );
688 }
689 else
690 nSweepVal = DGN_INT32( psDGN->abyElem + 40 );
691
692 if( nSweepVal == 0 )
693 psEllipse->sweepang = 360.0;
694 else
695 psEllipse->sweepang = nSweepVal / 360000.0;
696
697 memcpy( &(psEllipse->primary_axis), psDGN->abyElem + 44, 8 );
698 DGN2IEEEDouble( &(psEllipse->primary_axis) );
699 psEllipse->primary_axis *= psDGN->scale;
700
701 memcpy( &(psEllipse->secondary_axis), psDGN->abyElem + 52, 8 );
702 DGN2IEEEDouble( &(psEllipse->secondary_axis) );
703 psEllipse->secondary_axis *= psDGN->scale;
704
705 if( psDGN->dimension == 2 )
706 {
707 psEllipse->rotation = DGN_INT32( psDGN->abyElem + 60 );
708 psEllipse->rotation = psEllipse->rotation / 360000.0;
709
710 memcpy( &(psEllipse->origin.x), psDGN->abyElem + 64, 8 );
711 DGN2IEEEDouble( &(psEllipse->origin.x) );
712
713 memcpy( &(psEllipse->origin.y), psDGN->abyElem + 72, 8 );
714 DGN2IEEEDouble( &(psEllipse->origin.y) );
715 }
716 else
717 {
718 /* for now we don't try to handle quaternion */
719 psEllipse->rotation = 0;
720
721 memcpy( &(psEllipse->origin.x), psDGN->abyElem + 76, 8 );
722 DGN2IEEEDouble( &(psEllipse->origin.x) );
723
724 memcpy( &(psEllipse->origin.y), psDGN->abyElem + 84, 8 );
725 DGN2IEEEDouble( &(psEllipse->origin.y) );
726
727 memcpy( &(psEllipse->origin.z), psDGN->abyElem + 92, 8 );
728 DGN2IEEEDouble( &(psEllipse->origin.z) );
729
730 psEllipse->quat[0] = DGN_INT32( psDGN->abyElem + 60 );
731 psEllipse->quat[1] = DGN_INT32( psDGN->abyElem + 64 );
732 psEllipse->quat[2] = DGN_INT32( psDGN->abyElem + 68 );
733 psEllipse->quat[3] = DGN_INT32( psDGN->abyElem + 72 );
734 }
735
736 DGNTransformPoint( psDGN, &(psEllipse->origin) );
737 }
738 break;
739
740 case DGNT_TEXT:
741 {
742 int num_chars = 0;
743 int text_off = 0;
744
745 if( psDGN->dimension == 2 )
746 num_chars = psDGN->abyElem[58];
747 else
748 num_chars = psDGN->abyElem[74];
749
750 DGNElemText *psText = static_cast<DGNElemText *>(
751 CPLCalloc(sizeof(DGNElemText)+num_chars, 1));
752 psElement = (DGNElemCore *) psText;
753 psElement->stype = DGNST_TEXT;
754 DGNParseCore( psDGN, psElement );
755
756 psText->font_id = psDGN->abyElem[36];
757 psText->justification = psDGN->abyElem[37];
758 psText->length_mult = (DGN_INT32( psDGN->abyElem + 38 ))
759 * psDGN->scale * 6.0 / 1000.0;
760 psText->height_mult = (DGN_INT32( psDGN->abyElem + 42 ))
761 * psDGN->scale * 6.0 / 1000.0;
762
763 if( psDGN->dimension == 2 )
764 {
765 psText->rotation = DGN_INT32( psDGN->abyElem + 46 );
766 psText->rotation = psText->rotation / 360000.0;
767
768 psText->origin.x = DGN_INT32( psDGN->abyElem + 50 );
769 psText->origin.y = DGN_INT32( psDGN->abyElem + 54 );
770 text_off = 60;
771 }
772 else
773 {
774 /* leave quaternion for later */
775
776 psText->origin.x = DGN_INT32( psDGN->abyElem + 62 );
777 psText->origin.y = DGN_INT32( psDGN->abyElem + 66 );
778 psText->origin.z = DGN_INT32( psDGN->abyElem + 70 );
779 text_off = 76;
780 }
781
782 DGNTransformPoint( psDGN, &(psText->origin) );
783
784 /* experimental multibyte support from Ason Kang (hiska@netian.com)*/
785 if (*(psDGN->abyElem + text_off) == 0xFF
786 && *(psDGN->abyElem + text_off + 1) == 0xFD)
787 {
788 int n=0;
789 for( int i = 0; i < num_chars/2 - 1; i++ )
790 {
791 unsigned short w = 0;
792 memcpy(&w, psDGN->abyElem + text_off + 2 + i*2, 2);
793 CPL_LSBPTR16(&w);
794 if (w<256) { // if alpa-numeric code area : Normal character
795 *(psText->string + n) = (char) (w & 0xFF);
796 n++; // skip 1 byte;
797 }
798 else { // if extend code area : 2 byte Korean character
799 *(psText->string + n) = (char) (w >> 8); // hi
800 *(psText->string + n + 1) = (char) (w & 0xFF); // lo
801 n+=2; // 2 byte
802 }
803 }
804 psText->string[n] = '\0'; // terminate C string
805 }
806 else
807 {
808 memcpy( psText->string, psDGN->abyElem + text_off, num_chars );
809 psText->string[num_chars] = '\0';
810 }
811 }
812 break;
813
814 case DGNT_TCB:
815 psElement = DGNParseTCB( psDGN );
816 break;
817
818 case DGNT_COMPLEX_CHAIN_HEADER:
819 case DGNT_COMPLEX_SHAPE_HEADER:
820 {
821 DGNElemComplexHeader *psHdr = static_cast<DGNElemComplexHeader *>(
822 CPLCalloc(sizeof(DGNElemComplexHeader), 1));
823 psElement = (DGNElemCore *) psHdr;
824 psElement->stype = DGNST_COMPLEX_HEADER;
825 DGNParseCore( psDGN, psElement );
826
827 psHdr->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
828 psHdr->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256;
829 }
830 break;
831
832 case DGNT_TAG_VALUE:
833 {
834 DGNElemTagValue *psTag = static_cast<DGNElemTagValue *>(
835 CPLCalloc(sizeof(DGNElemTagValue), 1));
836 psElement = (DGNElemCore *) psTag;
837 psElement->stype = DGNST_TAG_VALUE;
838 DGNParseCore( psDGN, psElement );
839
840 psTag->tagType = psDGN->abyElem[74] + psDGN->abyElem[75] * 256;
841 memcpy( &(psTag->tagSet), psDGN->abyElem + 68, 4 );
842 CPL_LSBPTR32( &(psTag->tagSet) );
843 psTag->tagIndex = psDGN->abyElem[72] + psDGN->abyElem[73] * 256;
844 psTag->tagLength = psDGN->abyElem[150] + psDGN->abyElem[151] * 256;
845
846 if( psTag->tagType == 1 )
847 {
848 psTag->tagValue.string =
849 CPLStrdup( (char *) psDGN->abyElem + 154 );
850 }
851 else if( psTag->tagType == 3 )
852 {
853 memcpy( &(psTag->tagValue.integer),
854 psDGN->abyElem + 154, 4 );
855 CPL_LSBPTR32( &(psTag->tagValue.integer) );
856 }
857 else if( psTag->tagType == 4 )
858 {
859 memcpy( &(psTag->tagValue.real),
860 psDGN->abyElem + 154, 8 );
861 DGN2IEEEDouble( &(psTag->tagValue.real) );
862 }
863 }
864 break;
865
866 case DGNT_APPLICATION_ELEM:
867 if( nLevel == 24 )
868 {
869 psElement = DGNParseTagSet( psDGN );
870 if( psElement == nullptr )
871 return nullptr;
872 }
873 else
874 {
875 psElement = static_cast<DGNElemCore *>(
876 CPLCalloc(sizeof(DGNElemCore), 1));
877 psElement->stype = DGNST_CORE;
878 DGNParseCore( psDGN, psElement );
879 }
880 break;
881
882 case DGNT_CONE:
883 {
884 if( psDGN->dimension != 3 )
885 {
886 CPLError(CE_Failure, CPLE_AssertionFailed, "psDGN->dimension != 3");
887 return nullptr;
888 }
889
890 DGNElemCone *psCone =
891 static_cast<DGNElemCone *>(CPLCalloc(sizeof(DGNElemCone), 1));
892 psElement = (DGNElemCore *) psCone;
893 psElement->stype = DGNST_CONE;
894 DGNParseCore( psDGN, psElement );
895
896 psCone->unknown = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
897 psCone->quat[0] = DGN_INT32( psDGN->abyElem + 38 );
898 psCone->quat[1] = DGN_INT32( psDGN->abyElem + 42 );
899 psCone->quat[2] = DGN_INT32( psDGN->abyElem + 46 );
900 psCone->quat[3] = DGN_INT32( psDGN->abyElem + 50 );
901
902 memcpy( &(psCone->center_1.x), psDGN->abyElem + 54, 8 );
903 DGN2IEEEDouble( &(psCone->center_1.x) );
904 memcpy( &(psCone->center_1.y), psDGN->abyElem + 62, 8 );
905 DGN2IEEEDouble( &(psCone->center_1.y) );
906 memcpy( &(psCone->center_1.z), psDGN->abyElem + 70, 8 );
907 DGN2IEEEDouble( &(psCone->center_1.z) );
908 memcpy( &(psCone->radius_1), psDGN->abyElem + 78, 8 );
909 DGN2IEEEDouble( &(psCone->radius_1) );
910
911 memcpy( &(psCone->center_2.x), psDGN->abyElem + 86, 8 );
912 DGN2IEEEDouble( &(psCone->center_2.x) );
913 memcpy( &(psCone->center_2.y), psDGN->abyElem + 94, 8 );
914 DGN2IEEEDouble( &(psCone->center_2.y) );
915 memcpy( &(psCone->center_2.z), psDGN->abyElem + 102, 8 );
916 DGN2IEEEDouble( &(psCone->center_2.z) );
917 memcpy( &(psCone->radius_2), psDGN->abyElem + 110, 8 );
918 DGN2IEEEDouble( &(psCone->radius_2) );
919
920 psCone->radius_1 *= psDGN->scale;
921 psCone->radius_2 *= psDGN->scale;
922 DGNTransformPoint( psDGN, &psCone->center_1 );
923 DGNTransformPoint( psDGN, &psCone->center_2 );
924 }
925 break;
926
927 case DGNT_3DSURFACE_HEADER:
928 case DGNT_3DSOLID_HEADER:
929 {
930 DGNElemComplexHeader *psShape = static_cast<DGNElemComplexHeader *>(
931 CPLCalloc(sizeof(DGNElemComplexHeader), 1));
932 psElement = (DGNElemCore *) psShape;
933 psElement->stype = DGNST_COMPLEX_HEADER;
934 DGNParseCore( psDGN, psElement );
935
936 // Read complex header
937 psShape->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
938 psShape->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256;
939 psShape->surftype = psDGN->abyElem[40];
940 psShape->boundelms = psDGN->abyElem[41] + 1;
941 }
942 break;
943 case DGNT_BSPLINE_SURFACE_HEADER:
944 {
945 DGNElemBSplineSurfaceHeader *psSpline =
946 static_cast<DGNElemBSplineSurfaceHeader *>(
947 CPLCalloc(sizeof(DGNElemBSplineSurfaceHeader), 1));
948 psElement = (DGNElemCore *) psSpline;
949 psElement->stype = DGNST_BSPLINE_SURFACE_HEADER;
950 DGNParseCore( psDGN, psElement );
951
952 // Read B-Spline surface header
953 psSpline->desc_words = static_cast<long>(DGN_INT32(psDGN->abyElem + 36));
954 psSpline->curve_type = psDGN->abyElem[41];
955
956 // U
957 psSpline->u_order = (psDGN->abyElem[40] & 0x0f) + 2;
958 psSpline->u_properties = psDGN->abyElem[40] & 0xf0;
959 psSpline->num_poles_u = psDGN->abyElem[42] + psDGN->abyElem[43]*256;
960 psSpline->num_knots_u = psDGN->abyElem[44] + psDGN->abyElem[45]*256;
961 psSpline->rule_lines_u = psDGN->abyElem[46] + psDGN->abyElem[47]*256;
962
963 // V
964 psSpline->v_order = (psDGN->abyElem[48] & 0x0f) + 2;
965 psSpline->v_properties = psDGN->abyElem[48] & 0xf0;
966 psSpline->num_poles_v = psDGN->abyElem[50] + psDGN->abyElem[51]*256;
967 psSpline->num_knots_v = psDGN->abyElem[52] + psDGN->abyElem[53]*256;
968 psSpline->rule_lines_v = psDGN->abyElem[54] + psDGN->abyElem[55]*256;
969
970 psSpline->num_bounds = psDGN->abyElem[56] + psDGN->abyElem[57]*556;
971 }
972 break;
973 case DGNT_BSPLINE_CURVE_HEADER:
974 {
975 DGNElemBSplineCurveHeader *psSpline =
976 static_cast<DGNElemBSplineCurveHeader *>(
977 CPLCalloc(sizeof(DGNElemBSplineCurveHeader), 1));
978 psElement = (DGNElemCore *) psSpline;
979 psElement->stype = DGNST_BSPLINE_CURVE_HEADER;
980 DGNParseCore( psDGN, psElement );
981
982 // Read B-Spline curve header
983 psSpline->desc_words = static_cast<long>(DGN_INT32(psDGN->abyElem + 36));
984
985 // flags
986 psSpline->order = (psDGN->abyElem[40] & 0x0f) + 2;
987 psSpline->properties = psDGN->abyElem[40] & 0xf0;
988 psSpline->curve_type = psDGN->abyElem[41];
989
990 psSpline->num_poles = psDGN->abyElem[42] + psDGN->abyElem[43]*256;
991 psSpline->num_knots = psDGN->abyElem[44] + psDGN->abyElem[45]*256;
992 }
993 break;
994 case DGNT_BSPLINE_SURFACE_BOUNDARY:
995 {
996 short numverts = psDGN->abyElem[38] + psDGN->abyElem[39]*256;
997 if( numverts <= 0 )
998 {
999 CPLError(CE_Failure, CPLE_AssertionFailed, "numverts <= 0");
1000 return nullptr;
1001 }
1002
1003 DGNElemBSplineSurfaceBoundary *psBounds =
1004 static_cast<DGNElemBSplineSurfaceBoundary *>(
1005 CPLCalloc(sizeof(DGNElemBSplineSurfaceBoundary) +
1006 (numverts-1)*sizeof(DGNPoint), 1));
1007 psElement = (DGNElemCore *) psBounds;
1008 psElement->stype = DGNST_BSPLINE_SURFACE_BOUNDARY;
1009 DGNParseCore( psDGN, psElement );
1010
1011 int deltaLength=0,deltaStart=0;
1012 if (psBounds->core.properties & DGNPF_ATTRIBUTES)
1013 {
1014 for (int iAttr=0; iAttr<psBounds->core.attr_bytes-3; iAttr++)
1015 {
1016 if (psBounds->core.attr_data[iAttr] == 0xA9 &&
1017 psBounds->core.attr_data[iAttr+1] == 0x51)
1018 {
1019 deltaLength = (psBounds->core.attr_data[iAttr + 2] +
1020 psBounds->core.attr_data[iAttr + 3] * 256) * 2;
1021 deltaStart = iAttr + 6;
1022 break;
1023 }
1024 }
1025 }
1026 // Read B-Spline surface boundary
1027 psBounds->number = psDGN->abyElem[36] + psDGN->abyElem[37]*256;
1028
1029 for (int i=0;i<numverts &&
1030 44 + i * 8 + 4 <= psDGN->nElemBytes;i++) {
1031 psBounds->vertices[i].x = DGN_INT32( psDGN->abyElem + 40 + i*8 );
1032 psBounds->vertices[i].y = DGN_INT32( psDGN->abyElem + 44 + i*8 );
1033 psBounds->vertices[i].z = 0;
1034 if (deltaStart && deltaLength &&
1035 deltaStart + i * 4 + 2 + 2 <= psBounds->core.attr_bytes)
1036 {
1037 int dx = DGN_INT16(psBounds->core.attr_data + deltaStart + i * 4);
1038 int dy = DGN_INT16(psBounds->core.attr_data + deltaStart + i * 4 + 2);
1039 psBounds->vertices[i].x += dx / 32767.0;
1040 psBounds->vertices[i].y += dy / 32767.0;
1041 }
1042 psBounds->numverts = static_cast<short>(i+1);
1043 }
1044 }
1045 break;
1046 case DGNT_BSPLINE_KNOT:
1047 case DGNT_BSPLINE_WEIGHT_FACTOR:
1048 {
1049 // FIXME: Is it OK to assume that the # of elements corresponds
1050 // directly to the element size? kintel 20051215.
1051 int attr_bytes = psDGN->nElemBytes -
1052 (psDGN->abyElem[30] + psDGN->abyElem[31]*256)*2 - 32;
1053 int numelems = (psDGN->nElemBytes - 36 - attr_bytes)/4;
1054 if( numelems < 1 )
1055 {
1056 CPLError(CE_Failure, CPLE_AssertionFailed, "numelems < 1");
1057 return nullptr;
1058 }
1059 DGNElemKnotWeight *psArray = static_cast<DGNElemKnotWeight *>(
1060 CPLCalloc(sizeof(DGNElemKnotWeight) + (numelems-1)*sizeof(float),
1061 1));
1062
1063 psElement = (DGNElemCore *) psArray;
1064 psElement->stype = DGNST_KNOT_WEIGHT;
1065 DGNParseCore( psDGN, psElement );
1066
1067 // Read array
1068 for (int i=0;i<numelems;i++) {
1069 psArray->array[i] =
1070 static_cast<float>(1.0 * DGN_INT32(psDGN->abyElem + 36 + i*4) / ((1UL << 31) - 1));
1071 }
1072 }
1073 break;
1074 case DGNT_SHARED_CELL_DEFN:
1075 {
1076 DGNElemSharedCellDefn *psShared =
1077 static_cast<DGNElemSharedCellDefn *>(
1078 CPLCalloc(sizeof(DGNElemSharedCellDefn), 1));
1079 psElement = (DGNElemCore *) psShared;
1080 psElement->stype = DGNST_SHARED_CELL_DEFN;
1081 DGNParseCore( psDGN, psElement );
1082
1083 psShared->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
1084 }
1085 break;
1086 default:
1087 {
1088 psElement = static_cast<DGNElemCore *>(
1089 CPLCalloc(sizeof(DGNElemCore), 1));
1090 psElement->stype = DGNST_CORE;
1091 DGNParseCore( psDGN, psElement );
1092 }
1093 break;
1094 }
1095
1096 /* -------------------------------------------------------------------- */
1097 /* If the element structure type is "core" or if we are running */
1098 /* in "capture all" mode, record the complete binary image of */
1099 /* the element. */
1100 /* -------------------------------------------------------------------- */
1101 if( psElement->stype == DGNST_CORE
1102 || (psDGN->options & DGNO_CAPTURE_RAW_DATA) )
1103 {
1104 psElement->raw_bytes = psDGN->nElemBytes;
1105 psElement->raw_data = static_cast<unsigned char *>(
1106 CPLMalloc(psElement->raw_bytes));
1107
1108 memcpy( psElement->raw_data, psDGN->abyElem, psElement->raw_bytes );
1109 }
1110
1111 /* -------------------------------------------------------------------- */
1112 /* Collect some additional generic information. */
1113 /* -------------------------------------------------------------------- */
1114 psElement->element_id = psDGN->next_element_id - 1;
1115
1116 psElement->offset =
1117 static_cast<int>(VSIFTellL( psDGN->fp )) - psDGN->nElemBytes;
1118 psElement->size = psDGN->nElemBytes;
1119
1120 return psElement;
1121 }
1122
1123 /************************************************************************/
1124 /* DGNReadElement() */
1125 /************************************************************************/
1126
1127 /**
1128 * Read a DGN element.
1129 *
1130 * This function will return the next element in the file, starting with the
1131 * first. It is affected by DGNGotoElement() calls.
1132 *
1133 * The element is read into a structure which includes the DGNElemCore
1134 * structure. It is expected that applications will inspect the stype
1135 * field of the returned DGNElemCore and use it to cast the pointer to the
1136 * appropriate element structure type such as DGNElemMultiPoint.
1137 *
1138 * @param hDGN the handle of the file to read from.
1139 *
1140 * @return pointer to element structure, or NULL on EOF or processing error.
1141 * The structure should be freed with DGNFreeElement() when no longer needed.
1142 */
1143
DGNReadElement(DGNHandle hDGN)1144 DGNElemCore *DGNReadElement( DGNHandle hDGN )
1145
1146 {
1147 DGNInfo *psDGN = (DGNInfo *) hDGN;
1148 int nType = 0;
1149 int nLevel = 0;
1150 bool bInsideFilter = false;
1151
1152 /* -------------------------------------------------------------------- */
1153 /* Load the element data into the current buffer. If a spatial */
1154 /* filter is in effect, loop until we get something within our */
1155 /* spatial constraints. */
1156 /* -------------------------------------------------------------------- */
1157 do {
1158 bInsideFilter = true;
1159
1160 if( !DGNLoadRawElement( psDGN, &nType, &nLevel ) )
1161 return nullptr;
1162
1163 if( psDGN->has_spatial_filter )
1164 {
1165 if( !psDGN->sf_converted_to_uor )
1166 DGNSpatialFilterToUOR( psDGN );
1167
1168 GUInt32 nXMin = 0;
1169 GUInt32 nXMax = 0;
1170 GUInt32 nYMin = 0;
1171 GUInt32 nYMax = 0;
1172 if( !DGNGetRawExtents( psDGN, nType, nullptr,
1173 &nXMin, &nYMin, nullptr,
1174 &nXMax, &nYMax, nullptr ) )
1175 {
1176 /* If we don't have spatial characteristics for the element
1177 we will pass it through. */
1178 bInsideFilter = true;
1179 }
1180 else if( nXMin > psDGN->sf_max_x
1181 || nYMin > psDGN->sf_max_y
1182 || nXMax < psDGN->sf_min_x
1183 || nYMax < psDGN->sf_min_y )
1184 {
1185 bInsideFilter = false;
1186 }
1187
1188 /*
1189 ** We want to select complex elements based on the extents of
1190 ** the header, not the individual elements.
1191 */
1192 if( nType == DGNT_COMPLEX_CHAIN_HEADER
1193 || nType == DGNT_COMPLEX_SHAPE_HEADER )
1194 {
1195 psDGN->in_complex_group = true;
1196 psDGN->select_complex_group = bInsideFilter;
1197 }
1198 else if( psDGN->abyElem[0] & 0x80 /* complex flag set */ )
1199 {
1200 if( psDGN->in_complex_group )
1201 bInsideFilter = psDGN->select_complex_group;
1202 }
1203 else
1204 {
1205 psDGN->in_complex_group = false;
1206 }
1207 }
1208 } while( !bInsideFilter );
1209
1210 /* -------------------------------------------------------------------- */
1211 /* Convert into an element structure. */
1212 /* -------------------------------------------------------------------- */
1213 DGNElemCore *psElement = DGNProcessElement( psDGN, nType, nLevel );
1214
1215 return psElement;
1216 }
1217
1218 /************************************************************************/
1219 /* DGNElemTypeHasDispHdr() */
1220 /************************************************************************/
1221
1222 /**
1223 * Does element type have display header.
1224 *
1225 * @param nElemType element type (0-63) to test.
1226 *
1227 * @return TRUE if elements of passed in type have a display header after the
1228 * core element header, or FALSE otherwise.
1229 */
1230
DGNElemTypeHasDispHdr(int nElemType)1231 int DGNElemTypeHasDispHdr( int nElemType )
1232
1233 {
1234 switch( nElemType )
1235 {
1236 case 0:
1237 case DGNT_TCB:
1238 case DGNT_CELL_LIBRARY:
1239 case DGNT_LEVEL_SYMBOLOGY:
1240 case 32:
1241 case 44:
1242 case 48:
1243 case 49:
1244 case 50:
1245 case 51:
1246 case 57:
1247 case 60:
1248 case 61:
1249 case 62:
1250 case 63:
1251 return FALSE;
1252
1253 default:
1254 return TRUE;
1255 }
1256 }
1257
1258 /************************************************************************/
1259 /* DGNParseCore() */
1260 /************************************************************************/
1261
DGNParseCore(DGNInfo * psDGN,DGNElemCore * psElement)1262 int DGNParseCore( DGNInfo *psDGN, DGNElemCore *psElement )
1263
1264 {
1265 GByte *psData = psDGN->abyElem+0;
1266
1267 psElement->level = psData[0] & 0x3f;
1268 psElement->complex = psData[0] & 0x80;
1269 psElement->deleted = psData[1] & 0x80;
1270 psElement->type = psData[1] & 0x7f;
1271
1272 if( psDGN->nElemBytes >= 36 && DGNElemTypeHasDispHdr( psElement->type ) )
1273 {
1274 psElement->graphic_group = psData[28] + psData[29] * 256;
1275 psElement->properties = psData[32] + psData[33] * 256;
1276 psElement->style = psData[34] & 0x7;
1277 psElement->weight = (psData[34] & 0xf8) >> 3;
1278 psElement->color = psData[35];
1279 }
1280 else
1281 {
1282 psElement->graphic_group = 0;
1283 psElement->properties = 0;
1284 psElement->style = 0;
1285 psElement->weight = 0;
1286 psElement->color = 0;
1287 }
1288
1289 if( psElement->properties & DGNPF_ATTRIBUTES )
1290 {
1291 const int nAttIndex = psData[30] + psData[31] * 256;
1292
1293 psElement->attr_bytes = psDGN->nElemBytes - nAttIndex*2 - 32;
1294 if( psElement->attr_bytes > 0 )
1295 {
1296 psElement->attr_data = static_cast<unsigned char *>(
1297 CPLMalloc(psElement->attr_bytes));
1298 memcpy( psElement->attr_data, psData + nAttIndex * 2 + 32,
1299 psElement->attr_bytes );
1300 }
1301 else
1302 {
1303 CPLError(
1304 CE_Warning, CPLE_AppDefined,
1305 "Computed %d bytes for attribute info on element,\n"
1306 "perhaps this element type doesn't really have a disphdr?",
1307 psElement->attr_bytes );
1308 psElement->attr_bytes = 0;
1309 }
1310 }
1311
1312 return TRUE;
1313 }
1314
1315 /************************************************************************/
1316 /* DGNParseColorTable() */
1317 /************************************************************************/
1318
DGNParseColorTable(DGNInfo * psDGN)1319 static DGNElemCore *DGNParseColorTable( DGNInfo * psDGN )
1320
1321 {
1322 DGNElemColorTable *psColorTable = static_cast<DGNElemColorTable *>(
1323 CPLCalloc(sizeof(DGNElemColorTable), 1));
1324 DGNElemCore *psElement = (DGNElemCore *) psColorTable;
1325 psElement->stype = DGNST_COLORTABLE;
1326
1327 DGNParseCore( psDGN, psElement );
1328
1329 psColorTable->screen_flag =
1330 psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
1331
1332 memcpy( psColorTable->color_info[255], psDGN->abyElem+38, 3 );
1333 memcpy( psColorTable->color_info, psDGN->abyElem+41, 765 );
1334
1335 // We used to only install a color table as the default color
1336 // table if it was the first in the file. But apparently we should
1337 // really be using the last one. This doesn't necessarily accomplish
1338 // that either if the elements are being read out of order but it will
1339 // usually do better at least.
1340 memcpy( psDGN->color_table, psColorTable->color_info, 768 );
1341 psDGN->got_color_table = 1;
1342
1343 return psElement;
1344 }
1345
1346 /************************************************************************/
1347 /* DGNParseTagSet() */
1348 /************************************************************************/
1349
DGNParseTagSet(DGNInfo * psDGN)1350 static DGNElemCore *DGNParseTagSet( DGNInfo * psDGN )
1351
1352 {
1353 DGNElemTagSet *psTagSet =
1354 static_cast<DGNElemTagSet *>(CPLCalloc(sizeof(DGNElemTagSet), 1));
1355 DGNElemCore *psElement = (DGNElemCore *) psTagSet;
1356 psElement->stype = DGNST_TAG_SET;
1357
1358 DGNParseCore( psDGN, psElement );
1359
1360 /* -------------------------------------------------------------------- */
1361 /* Parse the overall information. */
1362 /* -------------------------------------------------------------------- */
1363 psTagSet->tagCount =
1364 psDGN->abyElem[44] + psDGN->abyElem[45] * 256;
1365 psTagSet->flags =
1366 psDGN->abyElem[46] + psDGN->abyElem[47] * 256;
1367 psTagSet->tagSetName = CPLStrdup( (const char *) (psDGN->abyElem + 48) );
1368
1369 /* -------------------------------------------------------------------- */
1370 /* Get the tag set number out of the attributes, if available. */
1371 /* -------------------------------------------------------------------- */
1372 psTagSet->tagSet = -1;
1373
1374 if( psElement->attr_bytes >= 8
1375 && psElement->attr_data[0] == 0x03
1376 && psElement->attr_data[1] == 0x10
1377 && psElement->attr_data[2] == 0x2f
1378 && psElement->attr_data[3] == 0x7d )
1379 psTagSet->tagSet = psElement->attr_data[4]
1380 + psElement->attr_data[5] * 256;
1381
1382 /* -------------------------------------------------------------------- */
1383 /* Parse each of the tag definitions. */
1384 /* -------------------------------------------------------------------- */
1385 psTagSet->tagList = static_cast<DGNTagDef *>(
1386 CPLCalloc(sizeof(DGNTagDef), psTagSet->tagCount));
1387
1388 size_t nDataOffset = 48 + strlen(psTagSet->tagSetName) + 1 + 1;
1389
1390 for( int iTag = 0; iTag < psTagSet->tagCount; iTag++ )
1391 {
1392 DGNTagDef *tagDef = psTagSet->tagList + iTag;
1393
1394 // Check the buffer is large enough to read all tagDef components
1395 size_t nDataOffsetEnd = nDataOffset;
1396 if( nDataOffsetEnd <= static_cast<size_t>(psDGN->nElemBytes) )
1397 {
1398 nDataOffsetEnd += strlen((char *)psDGN->abyElem + nDataOffsetEnd)+1 +
1399 2;
1400 }
1401 if( nDataOffsetEnd <= static_cast<size_t>(psDGN->nElemBytes) )
1402 {
1403 nDataOffsetEnd += strlen((char *)psDGN->abyElem + nDataOffsetEnd)+1 +
1404 2 + 5;
1405 if( tagDef->type == 1 )
1406 {
1407 nDataOffsetEnd += strlen(tagDef->defaultValue.string)+1;
1408 }
1409 else if( tagDef->type == 3 || tagDef->type == 5 )
1410 {
1411 nDataOffsetEnd += 4;
1412 }
1413 else if( tagDef->type == 4 )
1414 {
1415 nDataOffsetEnd += 8;
1416 }
1417 else
1418 {
1419 nDataOffsetEnd += 4;
1420 }
1421 }
1422 if( nDataOffsetEnd > static_cast<size_t>(psDGN->nElemBytes) )
1423 {
1424 CPLError(CE_Failure, CPLE_AssertionFailed,
1425 "nDataOffset >= static_cast<size_t>(psDGN->nElemBytes)");
1426 DGNFreeElement(psDGN, psElement);
1427 return nullptr;
1428 }
1429
1430 /* collect tag name. */
1431 tagDef->name = CPLStrdup( (char *) psDGN->abyElem + nDataOffset );
1432 nDataOffset += strlen(tagDef->name)+1;
1433
1434 /* Get tag id */
1435 tagDef->id = psDGN->abyElem[nDataOffset]
1436 + psDGN->abyElem[nDataOffset+1] * 256;
1437 nDataOffset += 2;
1438
1439 /* Get User Prompt */
1440 tagDef->prompt = CPLStrdup( (char *) psDGN->abyElem + nDataOffset );
1441 nDataOffset += strlen(tagDef->prompt)+1;
1442
1443 /* Get type */
1444 tagDef->type = psDGN->abyElem[nDataOffset]
1445 + psDGN->abyElem[nDataOffset+1] * 256;
1446 nDataOffset += 2;
1447
1448 /* skip five zeros */
1449 nDataOffset += 5;
1450
1451 /* Get the default */
1452 if( tagDef->type == 1 )
1453 {
1454 tagDef->defaultValue.string =
1455 CPLStrdup( (char *) psDGN->abyElem + nDataOffset );
1456 nDataOffset += strlen(tagDef->defaultValue.string)+1;
1457 }
1458 else if( tagDef->type == 3 || tagDef->type == 5 )
1459 {
1460 memcpy( &(tagDef->defaultValue.integer),
1461 psDGN->abyElem + nDataOffset, 4 );
1462 CPL_LSBPTR32( &(tagDef->defaultValue.integer) );
1463 nDataOffset += 4;
1464 }
1465 else if( tagDef->type == 4 )
1466 {
1467 memcpy( &(tagDef->defaultValue.real),
1468 psDGN->abyElem + nDataOffset, 8 );
1469 DGN2IEEEDouble( &(tagDef->defaultValue.real) );
1470 nDataOffset += 8;
1471 }
1472 else
1473 nDataOffset += 4;
1474 }
1475 return psElement;
1476 }
1477
1478 /************************************************************************/
1479 /* DGNParseTCB() */
1480 /************************************************************************/
1481
DGNParseTCB(DGNInfo * psDGN)1482 static DGNElemCore *DGNParseTCB( DGNInfo * psDGN )
1483
1484 {
1485 DGNElemTCB *psTCB = static_cast<DGNElemTCB *>(
1486 CPLCalloc(sizeof(DGNElemTCB), 1));
1487 DGNElemCore *psElement = (DGNElemCore *) psTCB;
1488 psElement->stype = DGNST_TCB;
1489 DGNParseCore( psDGN, psElement );
1490
1491 if( psDGN->abyElem[1214] & 0x40 )
1492 psTCB->dimension = 3;
1493 else
1494 psTCB->dimension = 2;
1495
1496 psTCB->subunits_per_master = static_cast<long>(DGN_INT32( psDGN->abyElem + 1112 ));
1497
1498 psTCB->master_units[0] = (char) psDGN->abyElem[1120];
1499 psTCB->master_units[1] = (char) psDGN->abyElem[1121];
1500 psTCB->master_units[2] = '\0';
1501
1502 psTCB->uor_per_subunit = static_cast<long>(DGN_INT32( psDGN->abyElem + 1116 ));
1503
1504 psTCB->sub_units[0] = (char) psDGN->abyElem[1122];
1505 psTCB->sub_units[1] = (char) psDGN->abyElem[1123];
1506 psTCB->sub_units[2] = '\0';
1507
1508 /* Get global origin */
1509 memcpy( &(psTCB->origin_x), psDGN->abyElem+1240, 8 );
1510 memcpy( &(psTCB->origin_y), psDGN->abyElem+1248, 8 );
1511 memcpy( &(psTCB->origin_z), psDGN->abyElem+1256, 8 );
1512
1513 /* Transform to IEEE */
1514 DGN2IEEEDouble( &(psTCB->origin_x) );
1515 DGN2IEEEDouble( &(psTCB->origin_y) );
1516 DGN2IEEEDouble( &(psTCB->origin_z) );
1517
1518 /* Convert from UORs to master units. */
1519 if( psTCB->uor_per_subunit != 0
1520 && psTCB->subunits_per_master != 0 )
1521 {
1522 psTCB->origin_x = psTCB->origin_x /
1523 (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1524 psTCB->origin_y = psTCB->origin_y /
1525 (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1526 psTCB->origin_z = psTCB->origin_z /
1527 (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1528 }
1529
1530 if( !psDGN->got_tcb )
1531 {
1532 psDGN->got_tcb = true;
1533 psDGN->dimension = psTCB->dimension;
1534 psDGN->origin_x = psTCB->origin_x;
1535 psDGN->origin_y = psTCB->origin_y;
1536 psDGN->origin_z = psTCB->origin_z;
1537
1538 if( psTCB->uor_per_subunit != 0
1539 && psTCB->subunits_per_master != 0 )
1540 psDGN->scale = 1.0
1541 / (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1542 }
1543
1544 /* Collect views */
1545 for( int iView = 0; iView < 8; iView++ )
1546 {
1547 unsigned char *pabyRawView = psDGN->abyElem + 46 + iView*118;
1548 DGNViewInfo *psView = psTCB->views + iView;
1549
1550 psView->flags = pabyRawView[0] + pabyRawView[1] * 256;
1551 memcpy( psView->levels, pabyRawView + 2, 8 );
1552
1553 psView->origin.x = DGN_INT32( pabyRawView + 10 );
1554 psView->origin.y = DGN_INT32( pabyRawView + 14 );
1555 psView->origin.z = DGN_INT32( pabyRawView + 18 );
1556
1557 DGNTransformPoint( psDGN, &(psView->origin) );
1558
1559 psView->delta.x = DGN_INT32( pabyRawView + 22 );
1560 psView->delta.y = DGN_INT32( pabyRawView + 26 );
1561 psView->delta.z = DGN_INT32( pabyRawView + 30 );
1562
1563 psView->delta.x *= psDGN->scale;
1564 psView->delta.y *= psDGN->scale;
1565 psView->delta.z *= psDGN->scale;
1566
1567 memcpy( psView->transmatrx, pabyRawView + 34, sizeof(double) * 9 );
1568 for( int i = 0; i < 9; i++ )
1569 DGN2IEEEDouble( psView->transmatrx + i );
1570
1571 memcpy( &(psView->conversion), pabyRawView + 106, sizeof(double) );
1572 DGN2IEEEDouble( &(psView->conversion) );
1573
1574 psView->activez = static_cast<unsigned long>(DGN_INT32( pabyRawView + 114 ));
1575 }
1576
1577 return psElement;
1578 }
1579
1580 /************************************************************************/
1581 /* DGNFreeElement() */
1582 /************************************************************************/
1583
1584 /**
1585 * Free an element structure.
1586 *
1587 * This function will deallocate all resources associated with any element
1588 * structure returned by DGNReadElement().
1589 *
1590 * @param hDGN handle to file from which the element was read.
1591 * @param psElement the element structure returned by DGNReadElement().
1592 */
1593
DGNFreeElement(CPL_UNUSED DGNHandle hDGN,DGNElemCore * psElement)1594 void DGNFreeElement( CPL_UNUSED DGNHandle hDGN, DGNElemCore *psElement )
1595 {
1596 if( psElement->attr_data != nullptr )
1597 VSIFree( psElement->attr_data );
1598
1599 if( psElement->raw_data != nullptr )
1600 VSIFree( psElement->raw_data );
1601
1602 if( psElement->stype == DGNST_TAG_SET )
1603 {
1604 DGNElemTagSet *psTagSet = (DGNElemTagSet *) psElement;
1605 CPLFree( psTagSet->tagSetName );
1606
1607 for( int iTag = 0; iTag < psTagSet->tagCount; iTag++ )
1608 {
1609 CPLFree( psTagSet->tagList[iTag].name );
1610 CPLFree( psTagSet->tagList[iTag].prompt );
1611
1612 if( psTagSet->tagList[iTag].type == 1 )
1613 CPLFree( psTagSet->tagList[iTag].defaultValue.string );
1614 }
1615 CPLFree( psTagSet->tagList );
1616 }
1617 else if( psElement->stype == DGNST_TAG_VALUE )
1618 {
1619 if( ((DGNElemTagValue *) psElement)->tagType == 1 )
1620 CPLFree( ((DGNElemTagValue *) psElement)->tagValue.string );
1621 }
1622
1623 CPLFree( psElement );
1624 }
1625
1626 /************************************************************************/
1627 /* DGNRewind() */
1628 /************************************************************************/
1629
1630 /**
1631 * Rewind element reading.
1632 *
1633 * Rewind the indicated DGN file, so the next element read with
1634 * DGNReadElement() will be the first. Does not require indexing like
1635 * the more general DGNReadElement() function.
1636 *
1637 * @param hDGN handle to file.
1638 */
1639
DGNRewind(DGNHandle hDGN)1640 void DGNRewind( DGNHandle hDGN )
1641
1642 {
1643 DGNInfo *psDGN = (DGNInfo *) hDGN;
1644
1645 VSIRewindL( psDGN->fp );
1646
1647 psDGN->next_element_id = 0;
1648 psDGN->in_complex_group = false;
1649 }
1650
1651 /************************************************************************/
1652 /* DGNTransformPoint() */
1653 /************************************************************************/
1654
DGNTransformPoint(DGNInfo * psDGN,DGNPoint * psPoint)1655 void DGNTransformPoint( DGNInfo *psDGN, DGNPoint *psPoint )
1656
1657 {
1658 psPoint->x = psPoint->x * psDGN->scale - psDGN->origin_x;
1659 psPoint->y = psPoint->y * psDGN->scale - psDGN->origin_y;
1660 psPoint->z = psPoint->z * psDGN->scale - psDGN->origin_z;
1661 }
1662
1663 /************************************************************************/
1664 /* DGNInverseTransformPoint() */
1665 /************************************************************************/
1666
DGNInverseTransformPoint(DGNInfo * psDGN,DGNPoint * psPoint)1667 void DGNInverseTransformPoint( DGNInfo *psDGN, DGNPoint *psPoint )
1668
1669 {
1670 psPoint->x = (psPoint->x + psDGN->origin_x) / psDGN->scale;
1671 psPoint->y = (psPoint->y + psDGN->origin_y) / psDGN->scale;
1672 psPoint->z = (psPoint->z + psDGN->origin_z) / psDGN->scale;
1673
1674 psPoint->x = std::max(-2147483647.0, std::min(2147483647.0, psPoint->x));
1675 psPoint->y = std::max(-2147483647.0, std::min(2147483647.0, psPoint->y));
1676 psPoint->z = std::max(-2147483647.0, std::min(2147483647.0, psPoint->z));
1677 }
1678
1679 /************************************************************************/
1680 /* DGNInverseTransformPointToInt() */
1681 /************************************************************************/
1682
DGNInverseTransformPointToInt(DGNInfo * psDGN,DGNPoint * psPoint,unsigned char * pabyTarget)1683 void DGNInverseTransformPointToInt( DGNInfo *psDGN, DGNPoint *psPoint,
1684 unsigned char *pabyTarget )
1685
1686 {
1687 double adfCT[3] = {
1688 (psPoint->x + psDGN->origin_x) / psDGN->scale,
1689 (psPoint->y + psDGN->origin_y) / psDGN->scale,
1690 (psPoint->z + psDGN->origin_z) / psDGN->scale
1691 };
1692
1693 const int nIter = std::min(3, psDGN->dimension);
1694 for( int i = 0; i < nIter; i++ )
1695 {
1696 GInt32 nCTI = static_cast<GInt32>(
1697 std::max(-2147483647.0, std::min(2147483647.0, adfCT[i])));
1698 unsigned char abyCTI[4];
1699 memcpy(abyCTI, &nCTI, sizeof(GInt32));
1700
1701 #ifdef WORDS_BIGENDIAN
1702 pabyTarget[i*4+0] = abyCTI[1];
1703 pabyTarget[i*4+1] = abyCTI[0];
1704 pabyTarget[i*4+2] = abyCTI[3];
1705 pabyTarget[i*4+3] = abyCTI[2];
1706 #else
1707 pabyTarget[i*4+3] = abyCTI[1];
1708 pabyTarget[i*4+2] = abyCTI[0];
1709 pabyTarget[i*4+1] = abyCTI[3];
1710 pabyTarget[i*4+0] = abyCTI[2];
1711 #endif
1712 }
1713 }
1714
1715 /************************************************************************/
1716 /* DGNLoadTCB() */
1717 /************************************************************************/
1718
1719 /**
1720 * Load TCB if not already loaded.
1721 *
1722 * This function will load the TCB element if it is not already loaded.
1723 * It is used primarily to ensure the TCB is loaded before doing any operations
1724 * that require TCB values (like creating new elements).
1725 *
1726 * @return FALSE on failure or TRUE on success.
1727 */
1728
DGNLoadTCB(DGNHandle hDGN)1729 int DGNLoadTCB( DGNHandle hDGN )
1730
1731 {
1732 DGNInfo *psDGN = (DGNInfo *) hDGN;
1733
1734 if( psDGN->got_tcb )
1735 return TRUE;
1736
1737 while( !psDGN->got_tcb )
1738 {
1739 DGNElemCore *psElem = DGNReadElement( hDGN );
1740 if( psElem == nullptr )
1741 {
1742 CPLError( CE_Failure, CPLE_AppDefined,
1743 "DGNLoadTCB() - unable to find TCB in file." );
1744 return FALSE;
1745 }
1746 DGNFreeElement( hDGN, psElem );
1747 }
1748
1749 return TRUE;
1750 }
1751
1752 /************************************************************************/
1753 /* DGNGetElementIndex() */
1754 /************************************************************************/
1755
1756 /**
1757 * Fetch element index.
1758 *
1759 * This function will return an array with brief information about every
1760 * element in a DGN file. It requires one pass through the entire file to
1761 * generate (this is not repeated on subsequent calls).
1762 *
1763 * The returned array of DGNElementInfo structures contain the level, type,
1764 * stype, and other flags for each element in the file. This can facilitate
1765 * application level code representing the number of elements of various types
1766 * efficiently.
1767 *
1768 * Note that while building the index requires one pass through the whole file,
1769 * it does not generally request much processing for each element.
1770 *
1771 * @param hDGN the file to get an index for.
1772 * @param pnElementCount the integer to put the total element count into.
1773 *
1774 * @return a pointer to an internal array of DGNElementInfo structures (there
1775 * will be *pnElementCount entries in the array), or NULL on failure. The
1776 * returned array should not be modified or freed, and will last only as long
1777 * as the DGN file remains open.
1778 */
1779
DGNGetElementIndex(DGNHandle hDGN,int * pnElementCount)1780 const DGNElementInfo *DGNGetElementIndex( DGNHandle hDGN, int *pnElementCount )
1781
1782 {
1783 DGNInfo *psDGN = (DGNInfo *) hDGN;
1784
1785 DGNBuildIndex( psDGN );
1786
1787 if( pnElementCount != nullptr )
1788 *pnElementCount = psDGN->element_count;
1789
1790 return psDGN->element_index;
1791 }
1792
1793 /************************************************************************/
1794 /* DGNGetExtents() */
1795 /************************************************************************/
1796
1797 /**
1798 * Fetch overall file extents.
1799 *
1800 * The extents are collected for each element while building an index, so
1801 * if an index has not already been built, it will be built when
1802 * DGNGetExtents() is called.
1803 *
1804 * The Z min/max values are generally meaningless (0 and 0xffffffff in uor
1805 * space).
1806 *
1807 * @param hDGN the file to get extents for.
1808 * @param padfExtents pointer to an array of six doubles into which are loaded
1809 * the values xmin, ymin, zmin, xmax, ymax, and zmax.
1810 *
1811 * @return TRUE on success or FALSE on failure.
1812 */
1813
DGNGetExtents(DGNHandle hDGN,double * padfExtents)1814 int DGNGetExtents( DGNHandle hDGN, double * padfExtents )
1815
1816 {
1817 DGNInfo *psDGN = (DGNInfo *) hDGN;
1818
1819 DGNBuildIndex( psDGN );
1820
1821 if( !psDGN->got_bounds )
1822 return FALSE;
1823
1824 DGNPoint sMin = {
1825 psDGN->min_x - 2147483648.0,
1826 psDGN->min_y - 2147483648.0,
1827 psDGN->min_z - 2147483648.0
1828 };
1829
1830 DGNTransformPoint( psDGN, &sMin );
1831
1832 padfExtents[0] = sMin.x;
1833 padfExtents[1] = sMin.y;
1834 padfExtents[2] = sMin.z;
1835
1836 DGNPoint sMax = {
1837 psDGN->max_x - 2147483648.0,
1838 psDGN->max_y - 2147483648.0,
1839 psDGN->max_z - 2147483648.0
1840 };
1841
1842 DGNTransformPoint( psDGN, &sMax );
1843
1844 padfExtents[3] = sMax.x;
1845 padfExtents[4] = sMax.y;
1846 padfExtents[5] = sMax.z;
1847
1848 return TRUE;
1849 }
1850
1851 /************************************************************************/
1852 /* DGNBuildIndex() */
1853 /************************************************************************/
1854
DGNBuildIndex(DGNInfo * psDGN)1855 void DGNBuildIndex( DGNInfo *psDGN )
1856
1857 {
1858 if( psDGN->index_built )
1859 return;
1860
1861 int nType = 0;
1862 int nLevel = 0;
1863 GUInt32 anRegion[6] = {};
1864
1865 psDGN->index_built = true;
1866
1867 DGNRewind( psDGN );
1868
1869 int nMaxElements = 0;
1870
1871 vsi_l_offset nLastOffset = VSIFTellL( psDGN->fp );
1872 while( DGNLoadRawElement( psDGN, &nType, &nLevel ) )
1873 {
1874 if( psDGN->element_count == nMaxElements )
1875 {
1876 nMaxElements = (int) (nMaxElements * 1.5) + 500;
1877
1878 psDGN->element_index = (DGNElementInfo *)
1879 CPLRealloc( psDGN->element_index,
1880 nMaxElements * sizeof(DGNElementInfo) );
1881 }
1882
1883 DGNElementInfo *psEI = psDGN->element_index + psDGN->element_count;
1884 psEI->level = (unsigned char) nLevel;
1885 psEI->type = (unsigned char) nType;
1886 psEI->flags = 0;
1887 psEI->offset = nLastOffset;
1888
1889 if( psDGN->abyElem[0] & 0x80 )
1890 psEI->flags |= DGNEIF_COMPLEX;
1891
1892 if( psDGN->abyElem[1] & 0x80 )
1893 psEI->flags |= DGNEIF_DELETED;
1894
1895 if( nType == DGNT_LINE || nType == DGNT_LINE_STRING
1896 || nType == DGNT_SHAPE || nType == DGNT_CURVE
1897 || nType == DGNT_BSPLINE_POLE )
1898 psEI->stype = DGNST_MULTIPOINT;
1899
1900 else if( nType == DGNT_GROUP_DATA && nLevel == DGN_GDL_COLOR_TABLE )
1901 {
1902 DGNElemCore *psCT = DGNParseColorTable( psDGN );
1903 DGNFreeElement( (DGNHandle) psDGN, psCT );
1904 psEI->stype = DGNST_COLORTABLE;
1905 }
1906 else if( nType == DGNT_ELLIPSE || nType == DGNT_ARC )
1907 psEI->stype = DGNST_ARC;
1908
1909 else if( nType == DGNT_COMPLEX_SHAPE_HEADER
1910 || nType == DGNT_COMPLEX_CHAIN_HEADER
1911 || nType == DGNT_3DSURFACE_HEADER
1912 || nType == DGNT_3DSOLID_HEADER)
1913 psEI->stype = DGNST_COMPLEX_HEADER;
1914
1915 else if( nType == DGNT_TEXT )
1916 psEI->stype = DGNST_TEXT;
1917
1918 else if( nType == DGNT_TAG_VALUE )
1919 psEI->stype = DGNST_TAG_VALUE;
1920
1921 else if( nType == DGNT_APPLICATION_ELEM )
1922 {
1923 if( nLevel == 24 )
1924 psEI->stype = DGNST_TAG_SET;
1925 else
1926 psEI->stype = DGNST_CORE;
1927 }
1928 else if( nType == DGNT_TCB )
1929 {
1930 DGNElemCore *psTCB = DGNParseTCB( psDGN );
1931 DGNFreeElement( (DGNHandle) psDGN, psTCB );
1932 psEI->stype = DGNST_TCB;
1933 }
1934 else if( nType == DGNT_CONE )
1935 psEI->stype = DGNST_CONE;
1936 else
1937 psEI->stype = DGNST_CORE;
1938
1939 if( !(psEI->flags & DGNEIF_DELETED)
1940 && !(psEI->flags & DGNEIF_COMPLEX)
1941 && DGNGetRawExtents( psDGN, nType, nullptr,
1942 anRegion+0, anRegion+1, anRegion+2,
1943 anRegion+3, anRegion+4, anRegion+5 ) )
1944 {
1945 #ifdef notdef
1946 printf( "panRegion[%d]=%.1f,%.1f,%.1f,%.1f,%.1f,%.1f\n",/*ok*/
1947 psDGN->element_count,
1948 anRegion[0] - 2147483648.0,
1949 anRegion[1] - 2147483648.0,
1950 anRegion[2] - 2147483648.0,
1951 anRegion[3] - 2147483648.0,
1952 anRegion[4] - 2147483648.0,
1953 anRegion[5] - 2147483648.0 );
1954 #endif
1955 if( psDGN->got_bounds )
1956 {
1957 psDGN->min_x = std::min(psDGN->min_x, anRegion[0]);
1958 psDGN->min_y = std::min(psDGN->min_y, anRegion[1]);
1959 psDGN->min_z = std::min(psDGN->min_z, anRegion[2]);
1960 psDGN->max_x = std::max(psDGN->max_x, anRegion[3]);
1961 psDGN->max_y = std::max(psDGN->max_y, anRegion[4]);
1962 psDGN->max_z = std::max(psDGN->max_z, anRegion[5]);
1963 }
1964 else
1965 {
1966 psDGN->min_x = anRegion[0];
1967 psDGN->min_y = anRegion[1];
1968 psDGN->min_z = anRegion[2];
1969 psDGN->max_x = anRegion[3];
1970 psDGN->max_y = anRegion[4];
1971 psDGN->max_z = anRegion[5];
1972 psDGN->got_bounds = true;
1973 }
1974 }
1975
1976 psDGN->element_count++;
1977
1978 nLastOffset = VSIFTellL( psDGN->fp );
1979 }
1980
1981 DGNRewind( psDGN );
1982
1983 psDGN->max_element_count = nMaxElements;
1984 }
1985