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 1163 : 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 1163 : if (VSIFReadL(psDGN->abyElem, 1, 4, psDGN->fp) != 4)
80 51 : return FALSE;
81 :
82 : /* Is this an 0xFFFF endof file marker? */
83 1112 : if (psDGN->abyElem[0] == 0xff && psDGN->abyElem[1] == 0xff)
84 30 : return FALSE;
85 :
86 1082 : int nWords = psDGN->abyElem[2] + psDGN->abyElem[3] * 256;
87 1082 : int nType = psDGN->abyElem[1] & 0x7f;
88 1082 : int nLevel = psDGN->abyElem[0] & 0x3f;
89 :
90 : /* -------------------------------------------------------------------- */
91 : /* Read the rest of the element data into the working buffer. */
92 : /* -------------------------------------------------------------------- */
93 1082 : if (nWords * 2 + 4 >= (int)sizeof(psDGN->abyElem))
94 0 : return FALSE;
95 :
96 : /* coverity[tainted_data] */
97 1082 : if ((int)VSIFReadL(psDGN->abyElem + 4, 2, nWords, psDGN->fp) != nWords)
98 0 : return FALSE;
99 1082 : psDGN->abyElem[4 + 2 * nWords] = 0;
100 1082 : psDGN->abyElem[sizeof(psDGN->abyElem) - 1] = 0;
101 :
102 1082 : psDGN->nElemBytes = nWords * 2 + 4;
103 :
104 1082 : psDGN->next_element_id++;
105 :
106 : /* -------------------------------------------------------------------- */
107 : /* Return requested info. */
108 : /* -------------------------------------------------------------------- */
109 1082 : if (pnType != nullptr)
110 1082 : *pnType = nType;
111 :
112 1082 : if (pnLevel != nullptr)
113 1082 : *pnLevel = nLevel;
114 :
115 1082 : 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 184 : 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 184 : if (pabyRawData == nullptr)
135 182 : pabyRawData = psDGN->abyElem + 0;
136 :
137 184 : 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 144 : default:
167 144 : 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 600 : static DGNElemCore *DGNProcessElement(DGNInfo *psDGN, int nType, int nLevel)
259 :
260 : {
261 600 : DGNElemCore *psElement = nullptr;
262 :
263 : /* -------------------------------------------------------------------- */
264 : /* Handle based on element type. */
265 : /* -------------------------------------------------------------------- */
266 600 : 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 = (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 = (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 = (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 = (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 = (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 = (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 = (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 = (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 101 : case DGNT_TCB:
808 101 : psElement = DGNParseTCB(psDGN);
809 101 : 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 = (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 = (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 = (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 = (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 = (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 = (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 = (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 0 : 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 0 : int attr_bytes =
1062 0 : psDGN->nElemBytes -
1063 0 : (psDGN->abyElem[30] + psDGN->abyElem[31] * 256) * 2 - 32;
1064 0 : int numelems = (psDGN->nElemBytes - 36 - attr_bytes) / 4;
1065 0 : if (numelems < 1)
1066 : {
1067 0 : CPLError(CE_Failure, CPLE_AssertionFailed, "numelems < 1");
1068 0 : return nullptr;
1069 : }
1070 : DGNElemKnotWeight *psArray =
1071 0 : static_cast<DGNElemKnotWeight *>(CPLCalloc(
1072 0 : sizeof(DGNElemKnotWeight) + (numelems - 1) * sizeof(float),
1073 : 1));
1074 :
1075 0 : psElement = (DGNElemCore *)psArray;
1076 0 : psElement->stype = DGNST_KNOT_WEIGHT;
1077 0 : DGNParseCore(psDGN, psElement);
1078 :
1079 : // Read array
1080 0 : for (int i = 0; i < numelems; i++)
1081 : {
1082 0 : psArray->array[i] = static_cast<float>(
1083 0 : 1.0 * DGN_INT32(psDGN->abyElem + 36 + i * 4) /
1084 : ((1UL << 31) - 1));
1085 : }
1086 : }
1087 0 : break;
1088 0 : case DGNT_SHARED_CELL_DEFN:
1089 : {
1090 : DGNElemSharedCellDefn *psShared =
1091 : static_cast<DGNElemSharedCellDefn *>(
1092 0 : CPLCalloc(sizeof(DGNElemSharedCellDefn), 1));
1093 0 : psElement = (DGNElemCore *)psShared;
1094 0 : psElement->stype = DGNST_SHARED_CELL_DEFN;
1095 0 : DGNParseCore(psDGN, psElement);
1096 :
1097 0 : psShared->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
1098 : }
1099 0 : break;
1100 120 : default:
1101 : {
1102 : psElement =
1103 120 : static_cast<DGNElemCore *>(CPLCalloc(sizeof(DGNElemCore), 1));
1104 120 : psElement->stype = DGNST_CORE;
1105 120 : DGNParseCore(psDGN, psElement);
1106 : }
1107 120 : break;
1108 : }
1109 :
1110 : /* -------------------------------------------------------------------- */
1111 : /* If the element structure type is "core" or if we are running */
1112 : /* in "capture all" mode, record the complete binary image of */
1113 : /* the element. */
1114 : /* -------------------------------------------------------------------- */
1115 600 : if (psElement->stype == DGNST_CORE ||
1116 143 : (psDGN->options & DGNO_CAPTURE_RAW_DATA))
1117 : {
1118 491 : psElement->raw_bytes = psDGN->nElemBytes;
1119 491 : psElement->raw_data =
1120 491 : static_cast<unsigned char *>(CPLMalloc(psElement->raw_bytes));
1121 :
1122 491 : memcpy(psElement->raw_data, psDGN->abyElem, psElement->raw_bytes);
1123 : }
1124 :
1125 : /* -------------------------------------------------------------------- */
1126 : /* Collect some additional generic information. */
1127 : /* -------------------------------------------------------------------- */
1128 600 : psElement->element_id = psDGN->next_element_id - 1;
1129 :
1130 600 : psElement->offset =
1131 600 : static_cast<int>(VSIFTellL(psDGN->fp)) - psDGN->nElemBytes;
1132 600 : psElement->size = psDGN->nElemBytes;
1133 :
1134 600 : return psElement;
1135 : }
1136 :
1137 : /************************************************************************/
1138 : /* DGNReadElement() */
1139 : /************************************************************************/
1140 :
1141 : /**
1142 : * Read a DGN element.
1143 : *
1144 : * This function will return the next element in the file, starting with the
1145 : * first. It is affected by DGNGotoElement() calls.
1146 : *
1147 : * The element is read into a structure which includes the DGNElemCore
1148 : * structure. It is expected that applications will inspect the stype
1149 : * field of the returned DGNElemCore and use it to cast the pointer to the
1150 : * appropriate element structure type such as DGNElemMultiPoint.
1151 : *
1152 : * @param hDGN the handle of the file to read from.
1153 : *
1154 : * @return pointer to element structure, or NULL on EOF or processing error.
1155 : * The structure should be freed with DGNFreeElement() when no longer needed.
1156 : */
1157 :
1158 637 : DGNElemCore *DGNReadElement(DGNHandle hDGN)
1159 :
1160 : {
1161 637 : DGNInfo *psDGN = (DGNInfo *)hDGN;
1162 637 : int nType = 0;
1163 637 : int nLevel = 0;
1164 637 : bool bInsideFilter = false;
1165 :
1166 : /* -------------------------------------------------------------------- */
1167 : /* Load the element data into the current buffer. If a spatial */
1168 : /* filter is in effect, loop until we get something within our */
1169 : /* spatial constraints. */
1170 : /* -------------------------------------------------------------------- */
1171 3 : do
1172 : {
1173 640 : bInsideFilter = true;
1174 :
1175 640 : if (!DGNLoadRawElement(psDGN, &nType, &nLevel))
1176 37 : return nullptr;
1177 :
1178 603 : if (psDGN->has_spatial_filter)
1179 : {
1180 15 : if (!psDGN->sf_converted_to_uor)
1181 1 : DGNSpatialFilterToUOR(psDGN);
1182 :
1183 15 : GUInt32 nXMin = 0;
1184 15 : GUInt32 nXMax = 0;
1185 15 : GUInt32 nYMin = 0;
1186 15 : GUInt32 nYMax = 0;
1187 15 : if (!DGNGetRawExtents(psDGN, nType, nullptr, &nXMin, &nYMin,
1188 : nullptr, &nXMax, &nYMax, nullptr))
1189 : {
1190 : /* If we don't have spatial characteristics for the element
1191 : we will pass it through. */
1192 11 : bInsideFilter = true;
1193 : }
1194 4 : else if (nXMin > psDGN->sf_max_x || nYMin > psDGN->sf_max_y ||
1195 2 : nXMax < psDGN->sf_min_x || nYMax < psDGN->sf_min_y)
1196 : {
1197 3 : bInsideFilter = false;
1198 : }
1199 :
1200 : /*
1201 : ** We want to select complex elements based on the extents of
1202 : ** the header, not the individual elements.
1203 : */
1204 15 : if (nType == DGNT_COMPLEX_CHAIN_HEADER ||
1205 15 : nType == DGNT_COMPLEX_SHAPE_HEADER)
1206 : {
1207 0 : psDGN->in_complex_group = true;
1208 0 : psDGN->select_complex_group = bInsideFilter;
1209 : }
1210 15 : else if (psDGN->abyElem[0] & 0x80 /* complex flag set */)
1211 : {
1212 0 : if (psDGN->in_complex_group)
1213 0 : bInsideFilter = psDGN->select_complex_group;
1214 : }
1215 : else
1216 : {
1217 15 : psDGN->in_complex_group = false;
1218 : }
1219 : }
1220 603 : } while (!bInsideFilter);
1221 :
1222 : /* -------------------------------------------------------------------- */
1223 : /* Convert into an element structure. */
1224 : /* -------------------------------------------------------------------- */
1225 600 : DGNElemCore *psElement = DGNProcessElement(psDGN, nType, nLevel);
1226 :
1227 600 : return psElement;
1228 : }
1229 :
1230 : /************************************************************************/
1231 : /* DGNElemTypeHasDispHdr() */
1232 : /************************************************************************/
1233 :
1234 : /**
1235 : * Does element type have display header.
1236 : *
1237 : * @param nElemType element type (0-63) to test.
1238 : *
1239 : * @return TRUE if elements of passed in type have a display header after the
1240 : * core element header, or FALSE otherwise.
1241 : */
1242 :
1243 790 : int DGNElemTypeHasDispHdr(int nElemType)
1244 :
1245 : {
1246 790 : switch (nElemType)
1247 : {
1248 212 : case 0:
1249 : case DGNT_TCB:
1250 : case DGNT_CELL_LIBRARY:
1251 : case DGNT_LEVEL_SYMBOLOGY:
1252 : case 32:
1253 : case 44:
1254 : case 48:
1255 : case 49:
1256 : case 50:
1257 : case 51:
1258 : case 57:
1259 : case 60:
1260 : case 61:
1261 : case 62:
1262 : case 63:
1263 212 : return FALSE;
1264 :
1265 578 : default:
1266 578 : return TRUE;
1267 : }
1268 : }
1269 :
1270 : /************************************************************************/
1271 : /* DGNParseCore() */
1272 : /************************************************************************/
1273 :
1274 651 : int DGNParseCore(DGNInfo *psDGN, DGNElemCore *psElement)
1275 :
1276 : {
1277 651 : GByte *psData = psDGN->abyElem + 0;
1278 :
1279 651 : psElement->level = psData[0] & 0x3f;
1280 651 : psElement->complex = psData[0] & 0x80;
1281 651 : psElement->deleted = psData[1] & 0x80;
1282 651 : psElement->type = psData[1] & 0x7f;
1283 :
1284 651 : if (psDGN->nElemBytes >= 36 && DGNElemTypeHasDispHdr(psElement->type))
1285 : {
1286 439 : psElement->graphic_group = psData[28] + psData[29] * 256;
1287 439 : psElement->properties = psData[32] + psData[33] * 256;
1288 439 : psElement->style = psData[34] & 0x7;
1289 439 : psElement->weight = (psData[34] & 0xf8) >> 3;
1290 439 : psElement->color = psData[35];
1291 : }
1292 : else
1293 : {
1294 212 : psElement->graphic_group = 0;
1295 212 : psElement->properties = 0;
1296 212 : psElement->style = 0;
1297 212 : psElement->weight = 0;
1298 212 : psElement->color = 0;
1299 : }
1300 :
1301 651 : if (psElement->properties & DGNPF_ATTRIBUTES)
1302 : {
1303 25 : const int nAttIndex = psData[30] + psData[31] * 256;
1304 :
1305 25 : psElement->attr_bytes = psDGN->nElemBytes - nAttIndex * 2 - 32;
1306 25 : if (psElement->attr_bytes > 0)
1307 : {
1308 25 : psElement->attr_data =
1309 25 : static_cast<unsigned char *>(CPLMalloc(psElement->attr_bytes));
1310 25 : memcpy(psElement->attr_data, psData + nAttIndex * 2 + 32,
1311 25 : psElement->attr_bytes);
1312 : }
1313 : else
1314 : {
1315 0 : CPLError(CE_Warning, CPLE_AppDefined,
1316 : "Computed %d bytes for attribute info on element,\n"
1317 : "perhaps this element type doesn't really have a disphdr?",
1318 : psElement->attr_bytes);
1319 0 : psElement->attr_bytes = 0;
1320 : }
1321 : }
1322 :
1323 651 : return TRUE;
1324 : }
1325 :
1326 : /************************************************************************/
1327 : /* DGNParseColorTable() */
1328 : /************************************************************************/
1329 :
1330 0 : static DGNElemCore *DGNParseColorTable(DGNInfo *psDGN)
1331 :
1332 : {
1333 : DGNElemColorTable *psColorTable = static_cast<DGNElemColorTable *>(
1334 0 : CPLCalloc(sizeof(DGNElemColorTable), 1));
1335 0 : DGNElemCore *psElement = (DGNElemCore *)psColorTable;
1336 0 : psElement->stype = DGNST_COLORTABLE;
1337 :
1338 0 : DGNParseCore(psDGN, psElement);
1339 :
1340 0 : psColorTable->screen_flag = psDGN->abyElem[36] + psDGN->abyElem[37] * 256;
1341 :
1342 0 : memcpy(psColorTable->color_info[255], psDGN->abyElem + 38, 3);
1343 0 : memcpy(psColorTable->color_info, psDGN->abyElem + 41, 765);
1344 :
1345 : // We used to only install a color table as the default color
1346 : // table if it was the first in the file. But apparently we should
1347 : // really be using the last one. This doesn't necessarily accomplish
1348 : // that either if the elements are being read out of order but it will
1349 : // usually do better at least.
1350 0 : memcpy(psDGN->color_table, psColorTable->color_info, 768);
1351 0 : psDGN->got_color_table = 1;
1352 :
1353 0 : return psElement;
1354 : }
1355 :
1356 : /************************************************************************/
1357 : /* DGNParseTagSet() */
1358 : /************************************************************************/
1359 :
1360 0 : static DGNElemCore *DGNParseTagSet(DGNInfo *psDGN)
1361 :
1362 : {
1363 : DGNElemTagSet *psTagSet =
1364 0 : static_cast<DGNElemTagSet *>(CPLCalloc(sizeof(DGNElemTagSet), 1));
1365 0 : DGNElemCore *psElement = (DGNElemCore *)psTagSet;
1366 0 : psElement->stype = DGNST_TAG_SET;
1367 :
1368 0 : DGNParseCore(psDGN, psElement);
1369 :
1370 : /* -------------------------------------------------------------------- */
1371 : /* Parse the overall information. */
1372 : /* -------------------------------------------------------------------- */
1373 0 : psTagSet->tagCount = psDGN->abyElem[44] + psDGN->abyElem[45] * 256;
1374 0 : psTagSet->flags = psDGN->abyElem[46] + psDGN->abyElem[47] * 256;
1375 0 : psTagSet->tagSetName = CPLStrdup((const char *)(psDGN->abyElem + 48));
1376 :
1377 : /* -------------------------------------------------------------------- */
1378 : /* Get the tag set number out of the attributes, if available. */
1379 : /* -------------------------------------------------------------------- */
1380 0 : psTagSet->tagSet = -1;
1381 :
1382 0 : if (psElement->attr_bytes >= 8 && psElement->attr_data[0] == 0x03 &&
1383 0 : psElement->attr_data[1] == 0x10 && psElement->attr_data[2] == 0x2f &&
1384 0 : psElement->attr_data[3] == 0x7d)
1385 0 : psTagSet->tagSet =
1386 0 : psElement->attr_data[4] + psElement->attr_data[5] * 256;
1387 :
1388 : /* -------------------------------------------------------------------- */
1389 : /* Parse each of the tag definitions. */
1390 : /* -------------------------------------------------------------------- */
1391 0 : psTagSet->tagList = static_cast<DGNTagDef *>(
1392 0 : CPLCalloc(sizeof(DGNTagDef), psTagSet->tagCount));
1393 :
1394 0 : size_t nDataOffset = 48 + strlen(psTagSet->tagSetName) + 1 + 1;
1395 :
1396 0 : for (int iTag = 0; iTag < psTagSet->tagCount; iTag++)
1397 : {
1398 0 : DGNTagDef *tagDef = psTagSet->tagList + iTag;
1399 :
1400 : // Check the buffer is large enough to read all tagDef components
1401 0 : size_t nDataOffsetEnd = nDataOffset;
1402 0 : if (nDataOffsetEnd <= static_cast<size_t>(psDGN->nElemBytes))
1403 : {
1404 0 : nDataOffsetEnd +=
1405 0 : strlen((char *)psDGN->abyElem + nDataOffsetEnd) + 1 + 2;
1406 : }
1407 0 : if (nDataOffsetEnd <= static_cast<size_t>(psDGN->nElemBytes))
1408 : {
1409 0 : nDataOffsetEnd +=
1410 0 : strlen((char *)psDGN->abyElem + nDataOffsetEnd) + 1 + 2 + 5;
1411 0 : if (tagDef->type == 1)
1412 : {
1413 0 : nDataOffsetEnd += strlen(tagDef->defaultValue.string) + 1;
1414 : }
1415 0 : else if (tagDef->type == 3 || tagDef->type == 5)
1416 : {
1417 0 : nDataOffsetEnd += 4;
1418 : }
1419 0 : else if (tagDef->type == 4)
1420 : {
1421 0 : nDataOffsetEnd += 8;
1422 : }
1423 : else
1424 : {
1425 0 : nDataOffsetEnd += 4;
1426 : }
1427 : }
1428 0 : if (nDataOffsetEnd > static_cast<size_t>(psDGN->nElemBytes))
1429 : {
1430 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1431 : "nDataOffset >= static_cast<size_t>(psDGN->nElemBytes)");
1432 0 : DGNFreeElement(psDGN, psElement);
1433 0 : return nullptr;
1434 : }
1435 :
1436 : /* collect tag name. */
1437 0 : tagDef->name = CPLStrdup((char *)psDGN->abyElem + nDataOffset);
1438 0 : nDataOffset += strlen(tagDef->name) + 1;
1439 :
1440 : /* Get tag id */
1441 0 : tagDef->id =
1442 0 : psDGN->abyElem[nDataOffset] + psDGN->abyElem[nDataOffset + 1] * 256;
1443 0 : nDataOffset += 2;
1444 :
1445 : /* Get User Prompt */
1446 0 : tagDef->prompt = CPLStrdup((char *)psDGN->abyElem + nDataOffset);
1447 0 : nDataOffset += strlen(tagDef->prompt) + 1;
1448 :
1449 : /* Get type */
1450 0 : tagDef->type =
1451 0 : psDGN->abyElem[nDataOffset] + psDGN->abyElem[nDataOffset + 1] * 256;
1452 0 : nDataOffset += 2;
1453 :
1454 : /* skip five zeros */
1455 0 : nDataOffset += 5;
1456 :
1457 : /* Get the default */
1458 0 : if (tagDef->type == 1)
1459 : {
1460 0 : tagDef->defaultValue.string =
1461 0 : CPLStrdup((char *)psDGN->abyElem + nDataOffset);
1462 0 : nDataOffset += strlen(tagDef->defaultValue.string) + 1;
1463 : }
1464 0 : else if (tagDef->type == 3 || tagDef->type == 5)
1465 : {
1466 0 : memcpy(&(tagDef->defaultValue.integer),
1467 0 : psDGN->abyElem + nDataOffset, 4);
1468 0 : CPL_LSBPTR32(&(tagDef->defaultValue.integer));
1469 0 : nDataOffset += 4;
1470 : }
1471 0 : else if (tagDef->type == 4)
1472 : {
1473 0 : memcpy(&(tagDef->defaultValue.real), psDGN->abyElem + nDataOffset,
1474 : 8);
1475 0 : DGN2IEEEDouble(&(tagDef->defaultValue.real));
1476 0 : nDataOffset += 8;
1477 : }
1478 : else
1479 0 : nDataOffset += 4;
1480 : }
1481 0 : return psElement;
1482 : }
1483 :
1484 : /************************************************************************/
1485 : /* DGNParseTCB() */
1486 : /************************************************************************/
1487 :
1488 152 : static DGNElemCore *DGNParseTCB(DGNInfo *psDGN)
1489 :
1490 : {
1491 : DGNElemTCB *psTCB =
1492 152 : static_cast<DGNElemTCB *>(CPLCalloc(sizeof(DGNElemTCB), 1));
1493 152 : DGNElemCore *psElement = (DGNElemCore *)psTCB;
1494 152 : psElement->stype = DGNST_TCB;
1495 152 : DGNParseCore(psDGN, psElement);
1496 :
1497 152 : if (psDGN->abyElem[1214] & 0x40)
1498 49 : psTCB->dimension = 3;
1499 : else
1500 103 : psTCB->dimension = 2;
1501 :
1502 152 : psTCB->subunits_per_master =
1503 152 : static_cast<long>(DGN_INT32(psDGN->abyElem + 1112));
1504 :
1505 152 : psTCB->master_units[0] = (char)psDGN->abyElem[1120];
1506 152 : psTCB->master_units[1] = (char)psDGN->abyElem[1121];
1507 152 : psTCB->master_units[2] = '\0';
1508 :
1509 152 : psTCB->uor_per_subunit =
1510 152 : static_cast<long>(DGN_INT32(psDGN->abyElem + 1116));
1511 :
1512 152 : psTCB->sub_units[0] = (char)psDGN->abyElem[1122];
1513 152 : psTCB->sub_units[1] = (char)psDGN->abyElem[1123];
1514 152 : psTCB->sub_units[2] = '\0';
1515 :
1516 : /* Get global origin */
1517 152 : memcpy(&(psTCB->origin_x), psDGN->abyElem + 1240, 8);
1518 152 : memcpy(&(psTCB->origin_y), psDGN->abyElem + 1248, 8);
1519 152 : memcpy(&(psTCB->origin_z), psDGN->abyElem + 1256, 8);
1520 :
1521 : /* Transform to IEEE */
1522 152 : DGN2IEEEDouble(&(psTCB->origin_x));
1523 152 : DGN2IEEEDouble(&(psTCB->origin_y));
1524 152 : DGN2IEEEDouble(&(psTCB->origin_z));
1525 :
1526 : /* Convert from UORs to master units. */
1527 152 : if (psTCB->uor_per_subunit != 0 && psTCB->subunits_per_master != 0)
1528 : {
1529 138 : psTCB->origin_x = psTCB->origin_x /
1530 138 : (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1531 138 : psTCB->origin_y = psTCB->origin_y /
1532 138 : (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1533 138 : psTCB->origin_z = psTCB->origin_z /
1534 138 : (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1535 : }
1536 :
1537 152 : if (!psDGN->got_tcb)
1538 : {
1539 78 : psDGN->got_tcb = true;
1540 78 : psDGN->dimension = psTCB->dimension;
1541 78 : psDGN->origin_x = psTCB->origin_x;
1542 78 : psDGN->origin_y = psTCB->origin_y;
1543 78 : psDGN->origin_z = psTCB->origin_z;
1544 :
1545 78 : if (psTCB->uor_per_subunit != 0 && psTCB->subunits_per_master != 0)
1546 78 : psDGN->scale =
1547 78 : 1.0 / (psTCB->uor_per_subunit * psTCB->subunits_per_master);
1548 : }
1549 :
1550 : /* Collect views */
1551 1368 : for (int iView = 0; iView < 8; iView++)
1552 : {
1553 1216 : unsigned char *pabyRawView = psDGN->abyElem + 46 + iView * 118;
1554 1216 : DGNViewInfo *psView = psTCB->views + iView;
1555 :
1556 1216 : psView->flags = pabyRawView[0] + pabyRawView[1] * 256;
1557 1216 : memcpy(psView->levels, pabyRawView + 2, 8);
1558 :
1559 1216 : psView->origin.x = DGN_INT32(pabyRawView + 10);
1560 1216 : psView->origin.y = DGN_INT32(pabyRawView + 14);
1561 1216 : psView->origin.z = DGN_INT32(pabyRawView + 18);
1562 :
1563 1216 : DGNTransformPoint(psDGN, &(psView->origin));
1564 :
1565 1216 : psView->delta.x = DGN_INT32(pabyRawView + 22);
1566 1216 : psView->delta.y = DGN_INT32(pabyRawView + 26);
1567 1216 : psView->delta.z = DGN_INT32(pabyRawView + 30);
1568 :
1569 1216 : psView->delta.x *= psDGN->scale;
1570 1216 : psView->delta.y *= psDGN->scale;
1571 1216 : psView->delta.z *= psDGN->scale;
1572 :
1573 1216 : memcpy(psView->transmatrx, pabyRawView + 34, sizeof(double) * 9);
1574 12160 : for (int i = 0; i < 9; i++)
1575 10944 : DGN2IEEEDouble(psView->transmatrx + i);
1576 :
1577 1216 : memcpy(&(psView->conversion), pabyRawView + 106, sizeof(double));
1578 1216 : DGN2IEEEDouble(&(psView->conversion));
1579 :
1580 1216 : psView->activez =
1581 1216 : static_cast<unsigned long>(DGN_INT32(pabyRawView + 114));
1582 : }
1583 :
1584 152 : return psElement;
1585 : }
1586 :
1587 : /************************************************************************/
1588 : /* DGNFreeElement() */
1589 : /************************************************************************/
1590 :
1591 : /**
1592 : * Free an element structure.
1593 : *
1594 : * This function will deallocate all resources associated with any element
1595 : * structure returned by DGNReadElement().
1596 : *
1597 : * @param hDGN handle to file from which the element was read.
1598 : * @param psElement the element structure returned by DGNReadElement().
1599 : */
1600 :
1601 946 : void DGNFreeElement(CPL_UNUSED DGNHandle hDGN, DGNElemCore *psElement)
1602 : {
1603 946 : if (psElement->attr_data != nullptr)
1604 70 : VSIFree(psElement->attr_data);
1605 :
1606 946 : if (psElement->raw_data != nullptr)
1607 786 : VSIFree(psElement->raw_data);
1608 :
1609 946 : if (psElement->stype == DGNST_TAG_SET)
1610 : {
1611 0 : DGNElemTagSet *psTagSet = (DGNElemTagSet *)psElement;
1612 0 : CPLFree(psTagSet->tagSetName);
1613 :
1614 0 : for (int iTag = 0; iTag < psTagSet->tagCount; iTag++)
1615 : {
1616 0 : CPLFree(psTagSet->tagList[iTag].name);
1617 0 : CPLFree(psTagSet->tagList[iTag].prompt);
1618 :
1619 0 : if (psTagSet->tagList[iTag].type == 1)
1620 0 : CPLFree(psTagSet->tagList[iTag].defaultValue.string);
1621 : }
1622 0 : CPLFree(psTagSet->tagList);
1623 : }
1624 946 : else if (psElement->stype == DGNST_TAG_VALUE)
1625 : {
1626 0 : if (((DGNElemTagValue *)psElement)->tagType == 1)
1627 0 : CPLFree(((DGNElemTagValue *)psElement)->tagValue.string);
1628 : }
1629 :
1630 946 : CPLFree(psElement);
1631 946 : }
1632 :
1633 : /************************************************************************/
1634 : /* DGNRewind() */
1635 : /************************************************************************/
1636 :
1637 : /**
1638 : * Rewind element reading.
1639 : *
1640 : * Rewind the indicated DGN file, so the next element read with
1641 : * DGNReadElement() will be the first. Does not require indexing like
1642 : * the more general DGNReadElement() function.
1643 : *
1644 : * @param hDGN handle to file.
1645 : */
1646 :
1647 112 : void DGNRewind(DGNHandle hDGN)
1648 :
1649 : {
1650 112 : DGNInfo *psDGN = (DGNInfo *)hDGN;
1651 :
1652 112 : VSIRewindL(psDGN->fp);
1653 :
1654 112 : psDGN->next_element_id = 0;
1655 112 : psDGN->in_complex_group = false;
1656 112 : }
1657 :
1658 : /************************************************************************/
1659 : /* DGNTransformPoint() */
1660 : /************************************************************************/
1661 :
1662 1384 : void DGNTransformPoint(DGNInfo *psDGN, DGNPoint *psPoint)
1663 :
1664 : {
1665 1384 : psPoint->x = psPoint->x * psDGN->scale - psDGN->origin_x;
1666 1384 : psPoint->y = psPoint->y * psDGN->scale - psDGN->origin_y;
1667 1384 : psPoint->z = psPoint->z * psDGN->scale - psDGN->origin_z;
1668 1384 : }
1669 :
1670 : /************************************************************************/
1671 : /* DGNInverseTransformPoint() */
1672 : /************************************************************************/
1673 :
1674 2 : void DGNInverseTransformPoint(DGNInfo *psDGN, DGNPoint *psPoint)
1675 :
1676 : {
1677 2 : psPoint->x = (psPoint->x + psDGN->origin_x) / psDGN->scale;
1678 2 : psPoint->y = (psPoint->y + psDGN->origin_y) / psDGN->scale;
1679 2 : psPoint->z = (psPoint->z + psDGN->origin_z) / psDGN->scale;
1680 :
1681 2 : psPoint->x = std::max(-2147483647.0, std::min(2147483647.0, psPoint->x));
1682 2 : psPoint->y = std::max(-2147483647.0, std::min(2147483647.0, psPoint->y));
1683 2 : psPoint->z = std::max(-2147483647.0, std::min(2147483647.0, psPoint->z));
1684 2 : }
1685 :
1686 : /************************************************************************/
1687 : /* DGNInverseTransformPointToInt() */
1688 : /************************************************************************/
1689 :
1690 293 : void DGNInverseTransformPointToInt(DGNInfo *psDGN, DGNPoint *psPoint,
1691 : unsigned char *pabyTarget)
1692 :
1693 : {
1694 293 : double adfCT[3] = {(psPoint->x + psDGN->origin_x) / psDGN->scale,
1695 293 : (psPoint->y + psDGN->origin_y) / psDGN->scale,
1696 293 : (psPoint->z + psDGN->origin_z) / psDGN->scale};
1697 :
1698 293 : const int nIter = std::min(3, psDGN->dimension);
1699 969 : for (int i = 0; i < nIter; i++)
1700 : {
1701 676 : GInt32 nCTI = static_cast<GInt32>(
1702 676 : std::max(-2147483647.0, std::min(2147483647.0, adfCT[i])));
1703 : unsigned char abyCTI[4];
1704 676 : memcpy(abyCTI, &nCTI, sizeof(GInt32));
1705 :
1706 : #ifdef WORDS_BIGENDIAN
1707 : pabyTarget[i * 4 + 0] = abyCTI[1];
1708 : pabyTarget[i * 4 + 1] = abyCTI[0];
1709 : pabyTarget[i * 4 + 2] = abyCTI[3];
1710 : pabyTarget[i * 4 + 3] = abyCTI[2];
1711 : #else
1712 676 : pabyTarget[i * 4 + 3] = abyCTI[1];
1713 676 : pabyTarget[i * 4 + 2] = abyCTI[0];
1714 676 : pabyTarget[i * 4 + 1] = abyCTI[3];
1715 676 : pabyTarget[i * 4 + 0] = abyCTI[2];
1716 : #endif
1717 : }
1718 293 : }
1719 :
1720 : /************************************************************************/
1721 : /* DGNLoadTCB() */
1722 : /************************************************************************/
1723 :
1724 : /**
1725 : * Load TCB if not already loaded.
1726 : *
1727 : * This function will load the TCB element if it is not already loaded.
1728 : * It is used primarily to ensure the TCB is loaded before doing any operations
1729 : * that require TCB values (like creating new elements).
1730 : *
1731 : * @return FALSE on failure or TRUE on success.
1732 : */
1733 :
1734 296 : int DGNLoadTCB(DGNHandle hDGN)
1735 :
1736 : {
1737 296 : DGNInfo *psDGN = (DGNInfo *)hDGN;
1738 :
1739 296 : if (psDGN->got_tcb)
1740 262 : return TRUE;
1741 :
1742 68 : while (!psDGN->got_tcb)
1743 : {
1744 34 : DGNElemCore *psElem = DGNReadElement(hDGN);
1745 34 : if (psElem == nullptr)
1746 : {
1747 0 : CPLError(CE_Failure, CPLE_AppDefined,
1748 : "DGNLoadTCB() - unable to find TCB in file.");
1749 0 : return FALSE;
1750 : }
1751 34 : DGNFreeElement(hDGN, psElem);
1752 : }
1753 :
1754 34 : return TRUE;
1755 : }
1756 :
1757 : /************************************************************************/
1758 : /* DGNGetElementIndex() */
1759 : /************************************************************************/
1760 :
1761 : /**
1762 : * Fetch element index.
1763 : *
1764 : * This function will return an array with brief information about every
1765 : * element in a DGN file. It requires one pass through the entire file to
1766 : * generate (this is not repeated on subsequent calls).
1767 : *
1768 : * The returned array of DGNElementInfo structures contain the level, type,
1769 : * stype, and other flags for each element in the file. This can facilitate
1770 : * application level code representing the number of elements of various types
1771 : * efficiently.
1772 : *
1773 : * Note that while building the index requires one pass through the whole file,
1774 : * it does not generally request much processing for each element.
1775 : *
1776 : * @param hDGN the file to get an index for.
1777 : * @param pnElementCount the integer to put the total element count into.
1778 : *
1779 : * @return a pointer to an internal array of DGNElementInfo structures (there
1780 : * will be *pnElementCount entries in the array), or NULL on failure. The
1781 : * returned array should not be modified or freed, and will last only as long
1782 : * as the DGN file remains open.
1783 : */
1784 :
1785 37 : const DGNElementInfo *DGNGetElementIndex(DGNHandle hDGN, int *pnElementCount)
1786 :
1787 : {
1788 37 : DGNInfo *psDGN = (DGNInfo *)hDGN;
1789 :
1790 37 : DGNBuildIndex(psDGN);
1791 :
1792 37 : if (pnElementCount != nullptr)
1793 0 : *pnElementCount = psDGN->element_count;
1794 :
1795 37 : return psDGN->element_index;
1796 : }
1797 :
1798 : /************************************************************************/
1799 : /* DGNGetExtents() */
1800 : /************************************************************************/
1801 :
1802 : /**
1803 : * Fetch overall file extents.
1804 : *
1805 : * The extents are collected for each element while building an index, so
1806 : * if an index has not already been built, it will be built when
1807 : * DGNGetExtents() is called.
1808 : *
1809 : * The Z min/max values are generally meaningless (0 and 0xffffffff in uor
1810 : * space).
1811 : *
1812 : * @param hDGN the file to get extents for.
1813 : * @param padfExtents pointer to an array of six doubles into which are loaded
1814 : * the values xmin, ymin, zmin, xmax, ymax, and zmax.
1815 : *
1816 : * @return TRUE on success or FALSE on failure.
1817 : */
1818 :
1819 0 : int DGNGetExtents(DGNHandle hDGN, double *padfExtents)
1820 :
1821 : {
1822 0 : DGNInfo *psDGN = (DGNInfo *)hDGN;
1823 :
1824 0 : DGNBuildIndex(psDGN);
1825 :
1826 0 : if (!psDGN->got_bounds)
1827 0 : return FALSE;
1828 :
1829 0 : DGNPoint sMin = {psDGN->min_x - 2147483648.0, psDGN->min_y - 2147483648.0,
1830 0 : psDGN->min_z - 2147483648.0};
1831 :
1832 0 : DGNTransformPoint(psDGN, &sMin);
1833 :
1834 0 : padfExtents[0] = sMin.x;
1835 0 : padfExtents[1] = sMin.y;
1836 0 : padfExtents[2] = sMin.z;
1837 :
1838 0 : DGNPoint sMax = {psDGN->max_x - 2147483648.0, psDGN->max_y - 2147483648.0,
1839 0 : psDGN->max_z - 2147483648.0};
1840 :
1841 0 : DGNTransformPoint(psDGN, &sMax);
1842 :
1843 0 : padfExtents[3] = sMax.x;
1844 0 : padfExtents[4] = sMax.y;
1845 0 : padfExtents[5] = sMax.z;
1846 :
1847 0 : return TRUE;
1848 : }
1849 :
1850 : /************************************************************************/
1851 : /* DGNBuildIndex() */
1852 : /************************************************************************/
1853 :
1854 366 : void DGNBuildIndex(DGNInfo *psDGN)
1855 :
1856 : {
1857 366 : if (psDGN->index_built)
1858 322 : return;
1859 :
1860 44 : int nType = 0;
1861 44 : int nLevel = 0;
1862 44 : GUInt32 anRegion[6] = {};
1863 :
1864 44 : psDGN->index_built = true;
1865 :
1866 44 : DGNRewind(psDGN);
1867 :
1868 44 : int nMaxElements = 0;
1869 :
1870 44 : vsi_l_offset nLastOffset = VSIFTellL(psDGN->fp);
1871 228 : while (DGNLoadRawElement(psDGN, &nType, &nLevel))
1872 : {
1873 184 : if (psDGN->element_count == nMaxElements)
1874 : {
1875 44 : nMaxElements = (int)(nMaxElements * 1.5) + 500;
1876 :
1877 44 : psDGN->element_index = (DGNElementInfo *)CPLRealloc(
1878 44 : psDGN->element_index, nMaxElements * sizeof(DGNElementInfo));
1879 : }
1880 :
1881 184 : DGNElementInfo *psEI = psDGN->element_index + psDGN->element_count;
1882 184 : psEI->level = (unsigned char)nLevel;
1883 184 : psEI->type = (unsigned char)nType;
1884 184 : psEI->flags = 0;
1885 184 : psEI->offset = nLastOffset;
1886 :
1887 184 : if (psDGN->abyElem[0] & 0x80)
1888 17 : psEI->flags |= DGNEIF_COMPLEX;
1889 :
1890 184 : if (psDGN->abyElem[1] & 0x80)
1891 0 : psEI->flags |= DGNEIF_DELETED;
1892 :
1893 184 : if (nType == DGNT_LINE || nType == DGNT_LINE_STRING ||
1894 173 : nType == DGNT_SHAPE || nType == DGNT_CURVE ||
1895 165 : nType == DGNT_BSPLINE_POLE)
1896 19 : psEI->stype = DGNST_MULTIPOINT;
1897 :
1898 165 : else if (nType == DGNT_GROUP_DATA && nLevel == DGN_GDL_COLOR_TABLE)
1899 : {
1900 0 : DGNElemCore *psCT = DGNParseColorTable(psDGN);
1901 0 : DGNFreeElement((DGNHandle)psDGN, psCT);
1902 0 : psEI->stype = DGNST_COLORTABLE;
1903 : }
1904 165 : else if (nType == DGNT_ELLIPSE || nType == DGNT_ARC)
1905 7 : psEI->stype = DGNST_ARC;
1906 :
1907 158 : else if (nType == DGNT_COMPLEX_SHAPE_HEADER ||
1908 158 : nType == DGNT_COMPLEX_CHAIN_HEADER ||
1909 157 : nType == DGNT_3DSURFACE_HEADER || nType == DGNT_3DSOLID_HEADER)
1910 1 : psEI->stype = DGNST_COMPLEX_HEADER;
1911 :
1912 157 : else if (nType == DGNT_TEXT)
1913 10 : psEI->stype = DGNST_TEXT;
1914 :
1915 147 : else if (nType == DGNT_TAG_VALUE)
1916 0 : psEI->stype = DGNST_TAG_VALUE;
1917 :
1918 147 : else if (nType == DGNT_APPLICATION_ELEM)
1919 : {
1920 69 : if (nLevel == 24)
1921 0 : psEI->stype = DGNST_TAG_SET;
1922 : else
1923 69 : psEI->stype = DGNST_CORE;
1924 : }
1925 78 : else if (nType == DGNT_TCB)
1926 : {
1927 51 : DGNElemCore *psTCB = DGNParseTCB(psDGN);
1928 51 : DGNFreeElement((DGNHandle)psDGN, psTCB);
1929 51 : psEI->stype = DGNST_TCB;
1930 : }
1931 27 : else if (nType == DGNT_CONE)
1932 0 : psEI->stype = DGNST_CONE;
1933 : else
1934 27 : psEI->stype = DGNST_CORE;
1935 :
1936 552 : if (!(psEI->flags & DGNEIF_DELETED) &&
1937 351 : !(psEI->flags & DGNEIF_COMPLEX) &&
1938 167 : DGNGetRawExtents(psDGN, nType, nullptr, anRegion + 0, anRegion + 1,
1939 : anRegion + 2, anRegion + 3, anRegion + 4,
1940 : anRegion + 5))
1941 : {
1942 : #ifdef notdef
1943 : printf("panRegion[%d]=%.1f,%.1f,%.1f,%.1f,%.1f,%.1f\n", /*ok*/
1944 : psDGN->element_count, anRegion[0] - 2147483648.0,
1945 : anRegion[1] - 2147483648.0, anRegion[2] - 2147483648.0,
1946 : anRegion[3] - 2147483648.0, anRegion[4] - 2147483648.0,
1947 : anRegion[5] - 2147483648.0);
1948 : #endif
1949 34 : if (psDGN->got_bounds)
1950 : {
1951 24 : psDGN->min_x = std::min(psDGN->min_x, anRegion[0]);
1952 24 : psDGN->min_y = std::min(psDGN->min_y, anRegion[1]);
1953 24 : psDGN->min_z = std::min(psDGN->min_z, anRegion[2]);
1954 24 : psDGN->max_x = std::max(psDGN->max_x, anRegion[3]);
1955 24 : psDGN->max_y = std::max(psDGN->max_y, anRegion[4]);
1956 24 : psDGN->max_z = std::max(psDGN->max_z, anRegion[5]);
1957 : }
1958 : else
1959 : {
1960 10 : psDGN->min_x = anRegion[0];
1961 10 : psDGN->min_y = anRegion[1];
1962 10 : psDGN->min_z = anRegion[2];
1963 10 : psDGN->max_x = anRegion[3];
1964 10 : psDGN->max_y = anRegion[4];
1965 10 : psDGN->max_z = anRegion[5];
1966 10 : psDGN->got_bounds = true;
1967 : }
1968 : }
1969 :
1970 184 : psDGN->element_count++;
1971 :
1972 184 : nLastOffset = VSIFTellL(psDGN->fp);
1973 : }
1974 :
1975 44 : DGNRewind(psDGN);
1976 :
1977 44 : psDGN->max_element_count = nMaxElements;
1978 : }
|