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