/src/gdal/ogr/ogrsf_frmts/dgn/dgnread.cpp
Line | Count | Source |
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 | | static short int DGN_INT16(const GByte *p) |
26 | 1.55M | { |
27 | 1.55M | return static_cast<short>(p[0] | (p[1] << 8)); |
28 | 1.55M | } |
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 | | int DGNGotoElement(DGNHandle hDGN, int element_id) |
49 | | |
50 | 33 | { |
51 | 33 | DGNInfo *psDGN = (DGNInfo *)hDGN; |
52 | | |
53 | 33 | DGNBuildIndex(psDGN); |
54 | | |
55 | 33 | if (element_id < 0 || element_id >= psDGN->element_count) |
56 | 0 | return FALSE; |
57 | | |
58 | 33 | if (VSIFSeekL(psDGN->fp, psDGN->element_index[element_id].offset, |
59 | 33 | SEEK_SET) != 0) |
60 | 0 | return FALSE; |
61 | | |
62 | 33 | psDGN->next_element_id = element_id; |
63 | 33 | psDGN->in_complex_group = false; |
64 | | |
65 | 33 | return TRUE; |
66 | 33 | } |
67 | | |
68 | | /************************************************************************/ |
69 | | /* DGNLoadRawElement() */ |
70 | | /************************************************************************/ |
71 | | |
72 | | int DGNLoadRawElement(DGNInfo *psDGN, int *pnType, int *pnLevel) |
73 | | |
74 | 300M | { |
75 | | /* -------------------------------------------------------------------- */ |
76 | | /* Read the first four bytes to get the level, type, and word */ |
77 | | /* count. */ |
78 | | /* -------------------------------------------------------------------- */ |
79 | 300M | if (VSIFReadL(psDGN->abyElem, 1, 4, psDGN->fp) != 4) |
80 | 291M | return FALSE; |
81 | | |
82 | | /* Is this an 0xFFFF endof file marker? */ |
83 | 8.48M | if (psDGN->abyElem[0] == 0xff && psDGN->abyElem[1] == 0xff) |
84 | 4.28k | return FALSE; |
85 | | |
86 | 8.47M | int nWords = psDGN->abyElem[2] + psDGN->abyElem[3] * 256; |
87 | 8.47M | int nType = psDGN->abyElem[1] & 0x7f; |
88 | 8.47M | int nLevel = psDGN->abyElem[0] & 0x3f; |
89 | | |
90 | | /* -------------------------------------------------------------------- */ |
91 | | /* Read the rest of the element data into the working buffer. */ |
92 | | /* -------------------------------------------------------------------- */ |
93 | 8.47M | if (nWords * 2 + 4 >= (int)sizeof(psDGN->abyElem)) |
94 | 0 | return FALSE; |
95 | | |
96 | | /* coverity[tainted_data] */ |
97 | 8.47M | if ((int)VSIFReadL(psDGN->abyElem + 4, 2, nWords, psDGN->fp) != nWords) |
98 | 7.43k | return FALSE; |
99 | 8.47M | psDGN->abyElem[4 + 2 * nWords] = 0; |
100 | 8.47M | psDGN->abyElem[sizeof(psDGN->abyElem) - 1] = 0; |
101 | | |
102 | 8.47M | psDGN->nElemBytes = nWords * 2 + 4; |
103 | | |
104 | 8.47M | psDGN->next_element_id++; |
105 | | |
106 | | /* -------------------------------------------------------------------- */ |
107 | | /* Return requested info. */ |
108 | | /* -------------------------------------------------------------------- */ |
109 | 8.47M | if (pnType != nullptr) |
110 | 8.47M | *pnType = nType; |
111 | | |
112 | 8.47M | if (pnLevel != nullptr) |
113 | 8.47M | *pnLevel = nLevel; |
114 | | |
115 | 8.47M | return TRUE; |
116 | 8.47M | } |
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 | | 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 | 3.95M | { |
134 | 3.95M | if (pabyRawData == nullptr) |
135 | 3.95M | pabyRawData = psDGN->abyElem + 0; |
136 | | |
137 | 3.95M | switch (nType) |
138 | 3.95M | { |
139 | 233k | case DGNT_LINE: |
140 | 268k | case DGNT_LINE_STRING: |
141 | 271k | case DGNT_SHAPE: |
142 | 280k | case DGNT_CURVE: |
143 | 296k | case DGNT_BSPLINE_POLE: |
144 | 297k | case DGNT_BSPLINE_SURFACE_HEADER: |
145 | 299k | case DGNT_BSPLINE_CURVE_HEADER: |
146 | 354k | case DGNT_ELLIPSE: |
147 | 531k | case DGNT_ARC: |
148 | 584k | case DGNT_TEXT: |
149 | 599k | case DGNT_TEXT_NODE: |
150 | 732k | case DGNT_COMPLEX_CHAIN_HEADER: |
151 | 887k | case DGNT_COMPLEX_SHAPE_HEADER: |
152 | 891k | case DGNT_CONE: |
153 | 892k | case DGNT_3DSURFACE_HEADER: |
154 | 945k | case DGNT_3DSOLID_HEADER: |
155 | 945k | *pnXMin = DGN_INT32(pabyRawData + 4); |
156 | 945k | *pnYMin = DGN_INT32(pabyRawData + 8); |
157 | 945k | if (pnZMin != nullptr) |
158 | 945k | *pnZMin = DGN_INT32(pabyRawData + 12); |
159 | | |
160 | 945k | *pnXMax = DGN_INT32(pabyRawData + 16); |
161 | 945k | *pnYMax = DGN_INT32(pabyRawData + 20); |
162 | 945k | if (pnZMax != nullptr) |
163 | 945k | *pnZMax = DGN_INT32(pabyRawData + 24); |
164 | 945k | return true; |
165 | | |
166 | 3.00M | default: |
167 | 3.00M | return false; |
168 | 3.95M | } |
169 | 3.95M | } |
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 | | int DGNGetElementExtents(DGNHandle hDGN, DGNElemCore *psElement, |
201 | | DGNPoint *psMin, DGNPoint *psMax) |
202 | | |
203 | 0 | { |
204 | 0 | DGNInfo *psDGN = (DGNInfo *)hDGN; |
205 | 0 | bool bResult = false; |
206 | |
|
207 | 0 | GUInt32 anMin[3] = {0, 0, 0}; |
208 | 0 | 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 | 0 | if (psElement->raw_data != nullptr) |
215 | 0 | bResult = DGNGetRawExtents(psDGN, psElement->type, psElement->raw_data, |
216 | 0 | anMin + 0, anMin + 1, anMin + 2, anMax + 0, |
217 | 0 | 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 | 0 | anMin + 0, anMin + 1, anMin + 2, anMax + 0, |
221 | 0 | anMax + 1, anMax + 2); |
222 | 0 | else |
223 | 0 | { |
224 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
225 | 0 | "DGNGetElementExtents() fails because the requested element " |
226 | 0 | "does not have raw data available."); |
227 | 0 | return FALSE; |
228 | 0 | } |
229 | | |
230 | 0 | 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 | 0 | psMin->x = anMin[0] - 2147483648.0; |
238 | 0 | psMin->y = anMin[1] - 2147483648.0; |
239 | 0 | psMin->z = anMin[2] - 2147483648.0; |
240 | |
|
241 | 0 | psMax->x = anMax[0] - 2147483648.0; |
242 | 0 | psMax->y = anMax[1] - 2147483648.0; |
243 | 0 | psMax->z = anMax[2] - 2147483648.0; |
244 | |
|
245 | 0 | DGNTransformPoint(psDGN, psMin); |
246 | 0 | DGNTransformPoint(psDGN, psMax); |
247 | |
|
248 | 0 | return TRUE; |
249 | 0 | } |
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 | | static DGNElemCore *DGNProcessElement(DGNInfo *psDGN, int nType, int nLevel) |
259 | | |
260 | 4.40M | { |
261 | 4.40M | DGNElemCore *psElement = nullptr; |
262 | | |
263 | | /* -------------------------------------------------------------------- */ |
264 | | /* Handle based on element type. */ |
265 | | /* -------------------------------------------------------------------- */ |
266 | 4.40M | switch (nType) |
267 | 4.40M | { |
268 | 90.7k | case DGNT_CELL_HEADER: |
269 | 90.7k | { |
270 | 90.7k | DGNElemCellHeader *psCell = static_cast<DGNElemCellHeader *>( |
271 | 90.7k | CPLCalloc(sizeof(DGNElemCellHeader), 1)); |
272 | 90.7k | psElement = reinterpret_cast<DGNElemCore *>(psCell); |
273 | 90.7k | psElement->stype = DGNST_CELL_HEADER; |
274 | 90.7k | DGNParseCore(psDGN, psElement); |
275 | | |
276 | 90.7k | psCell->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; |
277 | | |
278 | 90.7k | DGNRad50ToAscii(psDGN->abyElem[38] + psDGN->abyElem[39] * 256, |
279 | 90.7k | psCell->name + 0); |
280 | 90.7k | DGNRad50ToAscii(psDGN->abyElem[40] + psDGN->abyElem[41] * 256, |
281 | 90.7k | psCell->name + 3); |
282 | | |
283 | 90.7k | psCell->cclass = psDGN->abyElem[42] + psDGN->abyElem[43] * 256; |
284 | 90.7k | psCell->levels[0] = psDGN->abyElem[44] + psDGN->abyElem[45] * 256; |
285 | 90.7k | psCell->levels[1] = psDGN->abyElem[46] + psDGN->abyElem[47] * 256; |
286 | 90.7k | psCell->levels[2] = psDGN->abyElem[48] + psDGN->abyElem[49] * 256; |
287 | 90.7k | psCell->levels[3] = psDGN->abyElem[50] + psDGN->abyElem[51] * 256; |
288 | | |
289 | 90.7k | if (psDGN->dimension == 2) |
290 | 73.2k | { |
291 | 73.2k | psCell->rnglow.x = DGN_INT32(psDGN->abyElem + 52); |
292 | 73.2k | psCell->rnglow.y = DGN_INT32(psDGN->abyElem + 56); |
293 | 73.2k | psCell->rnghigh.x = DGN_INT32(psDGN->abyElem + 60); |
294 | 73.2k | psCell->rnghigh.y = DGN_INT32(psDGN->abyElem + 64); |
295 | | |
296 | 73.2k | psCell->trans[0] = |
297 | 73.2k | 1.0 * DGN_INT32(psDGN->abyElem + 68) / (1U << 31); |
298 | 73.2k | psCell->trans[1] = |
299 | 73.2k | 1.0 * DGN_INT32(psDGN->abyElem + 72) / (1U << 31); |
300 | 73.2k | psCell->trans[2] = |
301 | 73.2k | 1.0 * DGN_INT32(psDGN->abyElem + 76) / (1U << 31); |
302 | 73.2k | psCell->trans[3] = |
303 | 73.2k | 1.0 * DGN_INT32(psDGN->abyElem + 80) / (1U << 31); |
304 | | |
305 | 73.2k | psCell->origin.x = DGN_INT32(psDGN->abyElem + 84); |
306 | 73.2k | psCell->origin.y = DGN_INT32(psDGN->abyElem + 88); |
307 | | |
308 | 73.2k | { |
309 | 73.2k | const double a = DGN_INT32(psDGN->abyElem + 68); |
310 | 73.2k | const double b = DGN_INT32(psDGN->abyElem + 72); |
311 | 73.2k | const double c = DGN_INT32(psDGN->abyElem + 76); |
312 | 73.2k | const double d = DGN_INT32(psDGN->abyElem + 80); |
313 | 73.2k | const double a2 = a * a; |
314 | 73.2k | const double c2 = c * c; |
315 | | |
316 | 73.2k | psCell->xscale = sqrt(a2 + c2) / 214748; |
317 | 73.2k | psCell->yscale = sqrt(b * b + d * d) / 214748; |
318 | 73.2k | if ((a2 + c2) <= 0.0) |
319 | 2.16k | psCell->rotation = 0.0; |
320 | 71.1k | else |
321 | 71.1k | psCell->rotation = acos(a / sqrt(a2 + c2)); |
322 | | |
323 | 73.2k | if (b <= 0) |
324 | 4.35k | psCell->rotation = psCell->rotation * 180 / M_PI; |
325 | 68.9k | else |
326 | 68.9k | psCell->rotation = 360 - psCell->rotation * 180 / M_PI; |
327 | 73.2k | } |
328 | 73.2k | } |
329 | 17.4k | else |
330 | 17.4k | { |
331 | 17.4k | psCell->rnglow.x = DGN_INT32(psDGN->abyElem + 52); |
332 | 17.4k | psCell->rnglow.y = DGN_INT32(psDGN->abyElem + 56); |
333 | 17.4k | psCell->rnglow.z = DGN_INT32(psDGN->abyElem + 60); |
334 | 17.4k | psCell->rnghigh.x = DGN_INT32(psDGN->abyElem + 64); |
335 | 17.4k | psCell->rnghigh.y = DGN_INT32(psDGN->abyElem + 68); |
336 | 17.4k | psCell->rnghigh.z = DGN_INT32(psDGN->abyElem + 72); |
337 | | |
338 | 17.4k | psCell->trans[0] = |
339 | 17.4k | 1.0 * DGN_INT32(psDGN->abyElem + 76) / (1U << 31); |
340 | 17.4k | psCell->trans[1] = |
341 | 17.4k | 1.0 * DGN_INT32(psDGN->abyElem + 80) / (1U << 31); |
342 | 17.4k | psCell->trans[2] = |
343 | 17.4k | 1.0 * DGN_INT32(psDGN->abyElem + 84) / (1U << 31); |
344 | 17.4k | psCell->trans[3] = |
345 | 17.4k | 1.0 * DGN_INT32(psDGN->abyElem + 88) / (1U << 31); |
346 | 17.4k | psCell->trans[4] = |
347 | 17.4k | 1.0 * DGN_INT32(psDGN->abyElem + 92) / (1U << 31); |
348 | 17.4k | psCell->trans[5] = |
349 | 17.4k | 1.0 * DGN_INT32(psDGN->abyElem + 96) / (1U << 31); |
350 | 17.4k | psCell->trans[6] = |
351 | 17.4k | 1.0 * DGN_INT32(psDGN->abyElem + 100) / (1U << 31); |
352 | 17.4k | psCell->trans[7] = |
353 | 17.4k | 1.0 * DGN_INT32(psDGN->abyElem + 104) / (1U << 31); |
354 | 17.4k | psCell->trans[8] = |
355 | 17.4k | 1.0 * DGN_INT32(psDGN->abyElem + 108) / (1U << 31); |
356 | | |
357 | 17.4k | psCell->origin.x = DGN_INT32(psDGN->abyElem + 112); |
358 | 17.4k | psCell->origin.y = DGN_INT32(psDGN->abyElem + 116); |
359 | 17.4k | psCell->origin.z = DGN_INT32(psDGN->abyElem + 120); |
360 | 17.4k | } |
361 | | |
362 | 90.7k | DGNTransformPoint(psDGN, &(psCell->rnglow)); |
363 | 90.7k | DGNTransformPoint(psDGN, &(psCell->rnghigh)); |
364 | 90.7k | DGNTransformPoint(psDGN, &(psCell->origin)); |
365 | 90.7k | } |
366 | 90.7k | break; |
367 | | |
368 | 260k | case DGNT_CELL_LIBRARY: |
369 | 260k | { |
370 | 260k | DGNElemCellLibrary *psCell = static_cast<DGNElemCellLibrary *>( |
371 | 260k | CPLCalloc(sizeof(DGNElemCellLibrary), 1)); |
372 | 260k | psElement = reinterpret_cast<DGNElemCore *>(psCell); |
373 | 260k | psElement->stype = DGNST_CELL_LIBRARY; |
374 | 260k | DGNParseCore(psDGN, psElement); |
375 | | |
376 | 260k | DGNRad50ToAscii(psDGN->abyElem[32] + psDGN->abyElem[33] * 256, |
377 | 260k | psCell->name + 0); |
378 | 260k | DGNRad50ToAscii(psDGN->abyElem[34] + psDGN->abyElem[35] * 256, |
379 | 260k | psCell->name + 3); |
380 | | |
381 | 260k | psElement->properties = |
382 | 260k | psDGN->abyElem[38] + psDGN->abyElem[39] * 256; |
383 | | |
384 | 260k | psCell->dispsymb = psDGN->abyElem[40] + psDGN->abyElem[41] * 256; |
385 | | |
386 | 260k | psCell->cclass = psDGN->abyElem[42] + psDGN->abyElem[43] * 256; |
387 | 260k | psCell->levels[0] = psDGN->abyElem[44] + psDGN->abyElem[45] * 256; |
388 | 260k | psCell->levels[1] = psDGN->abyElem[46] + psDGN->abyElem[47] * 256; |
389 | 260k | psCell->levels[2] = psDGN->abyElem[48] + psDGN->abyElem[49] * 256; |
390 | 260k | psCell->levels[3] = psDGN->abyElem[50] + psDGN->abyElem[51] * 256; |
391 | | |
392 | 260k | psCell->numwords = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; |
393 | | |
394 | 260k | memset(psCell->description, 0, sizeof(psCell->description)); |
395 | | |
396 | 2.60M | for (int iWord = 0; iWord < 9; iWord++) |
397 | 2.34M | { |
398 | 2.34M | int iOffset = 52 + iWord * 2; |
399 | | |
400 | 2.34M | DGNRad50ToAscii(psDGN->abyElem[iOffset] + |
401 | 2.34M | psDGN->abyElem[iOffset + 1] * 256, |
402 | 2.34M | psCell->description + iWord * 3); |
403 | 2.34M | } |
404 | 260k | } |
405 | 260k | break; |
406 | | |
407 | 247k | case DGNT_LINE: |
408 | 247k | { |
409 | 247k | DGNElemMultiPoint *psLine = static_cast<DGNElemMultiPoint *>( |
410 | 247k | CPLCalloc(sizeof(DGNElemMultiPoint) + sizeof(DGNPoint), 1)); |
411 | 247k | psElement = reinterpret_cast<DGNElemCore *>(psLine); |
412 | 247k | psElement->stype = DGNST_MULTIPOINT; |
413 | 247k | DGNParseCore(psDGN, psElement); |
414 | | |
415 | 247k | int deltaLength = 0, deltaStart = 0; |
416 | 247k | if (psLine->core.properties & DGNPF_ATTRIBUTES) |
417 | 804 | { |
418 | 2.64M | for (int iAttr = 0; iAttr < psLine->core.attr_bytes - 3; |
419 | 2.64M | iAttr++) |
420 | 2.64M | { |
421 | 2.64M | if (psLine->core.attr_data[iAttr] == 0xA9 && |
422 | 2.57k | psLine->core.attr_data[iAttr + 1] == 0x51) |
423 | 16 | { |
424 | 16 | deltaLength = |
425 | 16 | (psLine->core.attr_data[iAttr + 2] + |
426 | 16 | psLine->core.attr_data[iAttr + 3] * 256) * |
427 | 16 | 2; |
428 | 16 | deltaStart = iAttr + 6; |
429 | 16 | break; |
430 | 16 | } |
431 | 2.64M | } |
432 | 804 | } |
433 | | |
434 | 247k | psLine->num_vertices = 2; |
435 | 247k | if (psDGN->dimension == 2) |
436 | 201k | { |
437 | 201k | psLine->vertices[0].x = DGN_INT32(psDGN->abyElem + 36); |
438 | 201k | psLine->vertices[0].y = DGN_INT32(psDGN->abyElem + 40); |
439 | 201k | psLine->vertices[1].x = DGN_INT32(psDGN->abyElem + 44); |
440 | 201k | psLine->vertices[1].y = DGN_INT32(psDGN->abyElem + 48); |
441 | 201k | } |
442 | 46.4k | else |
443 | 46.4k | { |
444 | 46.4k | psLine->vertices[0].x = DGN_INT32(psDGN->abyElem + 36); |
445 | 46.4k | psLine->vertices[0].y = DGN_INT32(psDGN->abyElem + 40); |
446 | 46.4k | psLine->vertices[0].z = DGN_INT32(psDGN->abyElem + 44); |
447 | 46.4k | psLine->vertices[1].x = DGN_INT32(psDGN->abyElem + 48); |
448 | 46.4k | psLine->vertices[1].y = DGN_INT32(psDGN->abyElem + 52); |
449 | 46.4k | psLine->vertices[1].z = DGN_INT32(psDGN->abyElem + 56); |
450 | 46.4k | } |
451 | | |
452 | 247k | if (deltaStart && deltaLength && |
453 | 16 | deltaStart + 1 * 4 + 2 + 2 <= psLine->core.attr_bytes) |
454 | 15 | { |
455 | 45 | for (int i = 0; i < 2; i++) |
456 | 30 | { |
457 | 30 | int dx = |
458 | 30 | DGN_INT16(psLine->core.attr_data + deltaStart + i * 4); |
459 | 30 | int dy = DGN_INT16(psLine->core.attr_data + deltaStart + |
460 | 30 | i * 4 + 2); |
461 | 30 | psLine->vertices[i].x += dx / 32767.0; |
462 | 30 | psLine->vertices[i].y += dy / 32767.0; |
463 | 30 | } |
464 | 15 | } |
465 | | |
466 | 247k | DGNTransformPoint(psDGN, psLine->vertices + 0); |
467 | 247k | DGNTransformPoint(psDGN, psLine->vertices + 1); |
468 | 247k | } |
469 | 247k | break; |
470 | | |
471 | 44.4k | case DGNT_LINE_STRING: |
472 | 46.7k | case DGNT_SHAPE: |
473 | 61.6k | case DGNT_CURVE: |
474 | 85.5k | case DGNT_BSPLINE_POLE: |
475 | 85.5k | { |
476 | 85.5k | int pntsize = psDGN->dimension * 4; |
477 | | |
478 | 85.5k | int count = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; |
479 | 85.5k | if (count < 2) |
480 | 32.7k | { |
481 | 32.7k | CPLError(CE_Failure, CPLE_AssertionFailed, "count < 2"); |
482 | 32.7k | return nullptr; |
483 | 32.7k | } |
484 | 52.8k | DGNElemMultiPoint *psLine = |
485 | 52.8k | static_cast<DGNElemMultiPoint *>(VSI_CALLOC_VERBOSE( |
486 | 52.8k | sizeof(DGNElemMultiPoint) + (count - 1) * sizeof(DGNPoint), |
487 | 52.8k | 1)); |
488 | 52.8k | if (psLine == nullptr) |
489 | 0 | return nullptr; |
490 | 52.8k | psElement = reinterpret_cast<DGNElemCore *>(psLine); |
491 | 52.8k | psElement->stype = DGNST_MULTIPOINT; |
492 | 52.8k | DGNParseCore(psDGN, psElement); |
493 | | |
494 | 52.8k | if (psDGN->nElemBytes < 38 + count * pntsize) |
495 | 52.6k | { |
496 | 52.6k | int new_count = (psDGN->nElemBytes - 38) / pntsize; |
497 | 52.6k | if (new_count < 0) |
498 | 34.9k | { |
499 | 34.9k | CPLError(CE_Failure, CPLE_AssertionFailed, "new_count < 2"); |
500 | 34.9k | DGNFreeElement(psDGN, psElement); |
501 | 34.9k | return nullptr; |
502 | 34.9k | } |
503 | 17.6k | CPLError(CE_Warning, CPLE_AppDefined, |
504 | 17.6k | "Trimming multipoint vertices to %d from %d because\n" |
505 | 17.6k | "element is short.\n", |
506 | 17.6k | new_count, count); |
507 | 17.6k | count = new_count; |
508 | 17.6k | } |
509 | 17.9k | int deltaLength = 0, deltaStart = 0; |
510 | 17.9k | if (psLine->core.properties & DGNPF_ATTRIBUTES) |
511 | 12.0k | { |
512 | 4.70M | for (int iAttr = 0; iAttr < psLine->core.attr_bytes - 3; |
513 | 4.69M | iAttr++) |
514 | 4.69M | { |
515 | 4.69M | if (psLine->core.attr_data[iAttr] == 0xA9 && |
516 | 8.10k | psLine->core.attr_data[iAttr + 1] == 0x51) |
517 | 95 | { |
518 | 95 | deltaLength = |
519 | 95 | (psLine->core.attr_data[iAttr + 2] + |
520 | 95 | psLine->core.attr_data[iAttr + 3] * 256) * |
521 | 95 | 2; |
522 | 95 | deltaStart = iAttr + 6; |
523 | 95 | break; |
524 | 95 | } |
525 | 4.69M | } |
526 | 12.0k | } |
527 | 7.81M | for (int i = 0; i < count && ((psDGN->dimension == 3) ? 46 : 42) + |
528 | 7.79M | i * pntsize + 4 <= |
529 | 7.79M | psDGN->nElemBytes; |
530 | 7.79M | i++) |
531 | 7.79M | { |
532 | 7.79M | psLine->vertices[i].x = |
533 | 7.79M | DGN_INT32(psDGN->abyElem + 38 + i * pntsize); |
534 | 7.79M | psLine->vertices[i].y = |
535 | 7.79M | DGN_INT32(psDGN->abyElem + 42 + i * pntsize); |
536 | 7.79M | if (psDGN->dimension == 3) |
537 | 367k | psLine->vertices[i].z = |
538 | 367k | DGN_INT32(psDGN->abyElem + 46 + i * pntsize); |
539 | 7.79M | if (deltaStart && deltaLength && |
540 | 1.01M | deltaStart + i * 4 + 2 + 2 <= psLine->core.attr_bytes) |
541 | 315k | { |
542 | 315k | int dx = |
543 | 315k | DGN_INT16(psLine->core.attr_data + deltaStart + i * 4); |
544 | 315k | int dy = DGN_INT16(psLine->core.attr_data + deltaStart + |
545 | 315k | i * 4 + 2); |
546 | 315k | psLine->vertices[i].x += dx / 32767.0; |
547 | 315k | psLine->vertices[i].y += dy / 32767.0; |
548 | 315k | } |
549 | 7.79M | DGNTransformPoint(psDGN, psLine->vertices + i); |
550 | 7.79M | psLine->num_vertices = i + 1; |
551 | 7.79M | } |
552 | 17.9k | } |
553 | 0 | break; |
554 | | |
555 | 16.2k | case DGNT_TEXT_NODE: |
556 | 16.2k | { |
557 | 16.2k | DGNElemTextNode *psNode = static_cast<DGNElemTextNode *>( |
558 | 16.2k | CPLCalloc(sizeof(DGNElemTextNode), 1)); |
559 | 16.2k | psElement = reinterpret_cast<DGNElemCore *>(psNode); |
560 | 16.2k | psElement->stype = DGNST_TEXT_NODE; |
561 | 16.2k | DGNParseCore(psDGN, psElement); |
562 | | |
563 | 16.2k | psNode->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; |
564 | 16.2k | psNode->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256; |
565 | | |
566 | 16.2k | psNode->node_number = psDGN->abyElem[40] + psDGN->abyElem[41] * 256; |
567 | 16.2k | psNode->max_length = psDGN->abyElem[42]; |
568 | 16.2k | psNode->max_used = psDGN->abyElem[43]; |
569 | 16.2k | psNode->font_id = psDGN->abyElem[44]; |
570 | 16.2k | psNode->justification = psDGN->abyElem[45]; |
571 | 16.2k | psNode->length_mult = |
572 | 16.2k | (DGN_INT32(psDGN->abyElem + 50)) * psDGN->scale * 6.0 / 1000.0; |
573 | 16.2k | psNode->height_mult = |
574 | 16.2k | (DGN_INT32(psDGN->abyElem + 54)) * psDGN->scale * 6.0 / 1000.0; |
575 | | |
576 | 16.2k | if (psDGN->dimension == 2) |
577 | 11.7k | { |
578 | 11.7k | psNode->rotation = DGN_INT32(psDGN->abyElem + 58) / 360000.0; |
579 | | |
580 | 11.7k | psNode->origin.x = DGN_INT32(psDGN->abyElem + 62); |
581 | 11.7k | psNode->origin.y = DGN_INT32(psDGN->abyElem + 66); |
582 | 11.7k | } |
583 | 4.44k | else |
584 | 4.44k | { |
585 | | /* leave quaternion for later */ |
586 | | |
587 | 4.44k | psNode->origin.x = DGN_INT32(psDGN->abyElem + 74); |
588 | 4.44k | psNode->origin.y = DGN_INT32(psDGN->abyElem + 78); |
589 | 4.44k | psNode->origin.z = DGN_INT32(psDGN->abyElem + 82); |
590 | 4.44k | } |
591 | 16.2k | DGNTransformPoint(psDGN, &(psNode->origin)); |
592 | 16.2k | } |
593 | 16.2k | break; |
594 | | |
595 | 15.0k | case DGNT_GROUP_DATA: |
596 | 15.0k | if (nLevel == DGN_GDL_COLOR_TABLE) |
597 | 8.85k | { |
598 | 8.85k | psElement = DGNParseColorTable(psDGN); |
599 | 8.85k | } |
600 | 6.18k | else |
601 | 6.18k | { |
602 | 6.18k | psElement = static_cast<DGNElemCore *>( |
603 | 6.18k | CPLCalloc(sizeof(DGNElemCore), 1)); |
604 | 6.18k | psElement->stype = DGNST_CORE; |
605 | 6.18k | DGNParseCore(psDGN, psElement); |
606 | 6.18k | } |
607 | 15.0k | break; |
608 | | |
609 | 59.3k | case DGNT_ELLIPSE: |
610 | 59.3k | { |
611 | 59.3k | DGNElemArc *psEllipse = |
612 | 59.3k | static_cast<DGNElemArc *>(CPLCalloc(sizeof(DGNElemArc), 1)); |
613 | 59.3k | psElement = reinterpret_cast<DGNElemCore *>(psEllipse); |
614 | 59.3k | psElement->stype = DGNST_ARC; |
615 | 59.3k | DGNParseCore(psDGN, psElement); |
616 | | |
617 | 59.3k | memcpy(&(psEllipse->primary_axis), psDGN->abyElem + 36, 8); |
618 | 59.3k | DGN2IEEEDouble(&(psEllipse->primary_axis)); |
619 | 59.3k | psEllipse->primary_axis *= psDGN->scale; |
620 | | |
621 | 59.3k | memcpy(&(psEllipse->secondary_axis), psDGN->abyElem + 44, 8); |
622 | 59.3k | DGN2IEEEDouble(&(psEllipse->secondary_axis)); |
623 | 59.3k | psEllipse->secondary_axis *= psDGN->scale; |
624 | | |
625 | 59.3k | if (psDGN->dimension == 2) |
626 | 48.9k | { |
627 | 48.9k | psEllipse->rotation = DGN_INT32(psDGN->abyElem + 52); |
628 | 48.9k | psEllipse->rotation = psEllipse->rotation / 360000.0; |
629 | | |
630 | 48.9k | memcpy(&(psEllipse->origin.x), psDGN->abyElem + 56, 8); |
631 | 48.9k | DGN2IEEEDouble(&(psEllipse->origin.x)); |
632 | | |
633 | 48.9k | memcpy(&(psEllipse->origin.y), psDGN->abyElem + 64, 8); |
634 | 48.9k | DGN2IEEEDouble(&(psEllipse->origin.y)); |
635 | 48.9k | } |
636 | 10.3k | else |
637 | 10.3k | { |
638 | | /* leave quaternion for later */ |
639 | | |
640 | 10.3k | memcpy(&(psEllipse->origin.x), psDGN->abyElem + 68, 8); |
641 | 10.3k | DGN2IEEEDouble(&(psEllipse->origin.x)); |
642 | | |
643 | 10.3k | memcpy(&(psEllipse->origin.y), psDGN->abyElem + 76, 8); |
644 | 10.3k | DGN2IEEEDouble(&(psEllipse->origin.y)); |
645 | | |
646 | 10.3k | memcpy(&(psEllipse->origin.z), psDGN->abyElem + 84, 8); |
647 | 10.3k | DGN2IEEEDouble(&(psEllipse->origin.z)); |
648 | | |
649 | 10.3k | psEllipse->quat[0] = DGN_INT32(psDGN->abyElem + 52); |
650 | 10.3k | psEllipse->quat[1] = DGN_INT32(psDGN->abyElem + 56); |
651 | 10.3k | psEllipse->quat[2] = DGN_INT32(psDGN->abyElem + 60); |
652 | 10.3k | psEllipse->quat[3] = DGN_INT32(psDGN->abyElem + 64); |
653 | 10.3k | } |
654 | | |
655 | 59.3k | DGNTransformPoint(psDGN, &(psEllipse->origin)); |
656 | | |
657 | 59.3k | psEllipse->startang = 0.0; |
658 | 59.3k | psEllipse->sweepang = 360.0; |
659 | 59.3k | } |
660 | 59.3k | break; |
661 | | |
662 | 186k | case DGNT_ARC: |
663 | 186k | { |
664 | 186k | GInt32 nSweepVal = 0; |
665 | | |
666 | 186k | DGNElemArc *psEllipse = |
667 | 186k | static_cast<DGNElemArc *>(CPLCalloc(sizeof(DGNElemArc), 1)); |
668 | 186k | psElement = reinterpret_cast<DGNElemCore *>(psEllipse); |
669 | 186k | psElement->stype = DGNST_ARC; |
670 | 186k | DGNParseCore(psDGN, psElement); |
671 | | |
672 | 186k | psEllipse->startang = DGN_INT32(psDGN->abyElem + 36); |
673 | 186k | psEllipse->startang = psEllipse->startang / 360000.0; |
674 | 186k | if (psDGN->abyElem[41] & 0x80) |
675 | 1.25k | { |
676 | 1.25k | psDGN->abyElem[41] &= 0x7f; |
677 | 1.25k | nSweepVal = -1 * DGN_INT32(psDGN->abyElem + 40); |
678 | 1.25k | } |
679 | 185k | else |
680 | 185k | nSweepVal = DGN_INT32(psDGN->abyElem + 40); |
681 | | |
682 | 186k | if (nSweepVal == 0) |
683 | 51.5k | psEllipse->sweepang = 360.0; |
684 | 135k | else |
685 | 135k | psEllipse->sweepang = nSweepVal / 360000.0; |
686 | | |
687 | 186k | memcpy(&(psEllipse->primary_axis), psDGN->abyElem + 44, 8); |
688 | 186k | DGN2IEEEDouble(&(psEllipse->primary_axis)); |
689 | 186k | psEllipse->primary_axis *= psDGN->scale; |
690 | | |
691 | 186k | memcpy(&(psEllipse->secondary_axis), psDGN->abyElem + 52, 8); |
692 | 186k | DGN2IEEEDouble(&(psEllipse->secondary_axis)); |
693 | 186k | psEllipse->secondary_axis *= psDGN->scale; |
694 | | |
695 | 186k | if (psDGN->dimension == 2) |
696 | 164k | { |
697 | 164k | psEllipse->rotation = DGN_INT32(psDGN->abyElem + 60); |
698 | 164k | psEllipse->rotation = psEllipse->rotation / 360000.0; |
699 | | |
700 | 164k | memcpy(&(psEllipse->origin.x), psDGN->abyElem + 64, 8); |
701 | 164k | DGN2IEEEDouble(&(psEllipse->origin.x)); |
702 | | |
703 | 164k | memcpy(&(psEllipse->origin.y), psDGN->abyElem + 72, 8); |
704 | 164k | DGN2IEEEDouble(&(psEllipse->origin.y)); |
705 | 164k | } |
706 | 22.1k | else |
707 | 22.1k | { |
708 | | /* for now we don't try to handle quaternion */ |
709 | 22.1k | psEllipse->rotation = 0; |
710 | | |
711 | 22.1k | memcpy(&(psEllipse->origin.x), psDGN->abyElem + 76, 8); |
712 | 22.1k | DGN2IEEEDouble(&(psEllipse->origin.x)); |
713 | | |
714 | 22.1k | memcpy(&(psEllipse->origin.y), psDGN->abyElem + 84, 8); |
715 | 22.1k | DGN2IEEEDouble(&(psEllipse->origin.y)); |
716 | | |
717 | 22.1k | memcpy(&(psEllipse->origin.z), psDGN->abyElem + 92, 8); |
718 | 22.1k | DGN2IEEEDouble(&(psEllipse->origin.z)); |
719 | | |
720 | 22.1k | psEllipse->quat[0] = DGN_INT32(psDGN->abyElem + 60); |
721 | 22.1k | psEllipse->quat[1] = DGN_INT32(psDGN->abyElem + 64); |
722 | 22.1k | psEllipse->quat[2] = DGN_INT32(psDGN->abyElem + 68); |
723 | 22.1k | psEllipse->quat[3] = DGN_INT32(psDGN->abyElem + 72); |
724 | 22.1k | } |
725 | | |
726 | 186k | DGNTransformPoint(psDGN, &(psEllipse->origin)); |
727 | 186k | } |
728 | 186k | break; |
729 | | |
730 | 55.5k | case DGNT_TEXT: |
731 | 55.5k | { |
732 | 55.5k | int num_chars = 0; |
733 | 55.5k | int text_off = 0; |
734 | | |
735 | 55.5k | if (psDGN->dimension == 2) |
736 | 52.2k | num_chars = psDGN->abyElem[58]; |
737 | 3.33k | else |
738 | 3.33k | num_chars = psDGN->abyElem[74]; |
739 | | |
740 | 55.5k | DGNElemText *psText = static_cast<DGNElemText *>( |
741 | 55.5k | CPLCalloc(sizeof(DGNElemText) + num_chars, 1)); |
742 | 55.5k | psElement = reinterpret_cast<DGNElemCore *>(psText); |
743 | 55.5k | psElement->stype = DGNST_TEXT; |
744 | 55.5k | DGNParseCore(psDGN, psElement); |
745 | | |
746 | 55.5k | psText->font_id = psDGN->abyElem[36]; |
747 | 55.5k | psText->justification = psDGN->abyElem[37]; |
748 | 55.5k | psText->length_mult = |
749 | 55.5k | (DGN_INT32(psDGN->abyElem + 38)) * psDGN->scale * 6.0 / 1000.0; |
750 | 55.5k | psText->height_mult = |
751 | 55.5k | (DGN_INT32(psDGN->abyElem + 42)) * psDGN->scale * 6.0 / 1000.0; |
752 | | |
753 | 55.5k | if (psDGN->dimension == 2) |
754 | 52.2k | { |
755 | 52.2k | psText->rotation = DGN_INT32(psDGN->abyElem + 46); |
756 | 52.2k | psText->rotation = psText->rotation / 360000.0; |
757 | | |
758 | 52.2k | psText->origin.x = DGN_INT32(psDGN->abyElem + 50); |
759 | 52.2k | psText->origin.y = DGN_INT32(psDGN->abyElem + 54); |
760 | 52.2k | text_off = 60; |
761 | 52.2k | } |
762 | 3.33k | else |
763 | 3.33k | { |
764 | | /* leave quaternion for later */ |
765 | | |
766 | 3.33k | psText->origin.x = DGN_INT32(psDGN->abyElem + 62); |
767 | 3.33k | psText->origin.y = DGN_INT32(psDGN->abyElem + 66); |
768 | 3.33k | psText->origin.z = DGN_INT32(psDGN->abyElem + 70); |
769 | 3.33k | text_off = 76; |
770 | 3.33k | } |
771 | | |
772 | 55.5k | DGNTransformPoint(psDGN, &(psText->origin)); |
773 | | |
774 | | /* experimental multibyte support from Ason Kang |
775 | | * (hiska@netian.com)*/ |
776 | 55.5k | if (*(psDGN->abyElem + text_off) == 0xFF && |
777 | 9.49k | *(psDGN->abyElem + text_off + 1) == 0xFD) |
778 | 229 | { |
779 | 229 | int n = 0; |
780 | 17.8k | for (int i = 0; i < num_chars / 2 - 1; i++) |
781 | 17.5k | { |
782 | 17.5k | unsigned short w = 0; |
783 | 17.5k | memcpy(&w, psDGN->abyElem + text_off + 2 + i * 2, 2); |
784 | 17.5k | CPL_LSBPTR16(&w); |
785 | 17.5k | if (w < 256) |
786 | 10.4k | { // if alpa-numeric code area : Normal character |
787 | 10.4k | *(psText->string + n) = (char)(w & 0xFF); |
788 | 10.4k | n++; // skip 1 byte; |
789 | 10.4k | } |
790 | 7.12k | else |
791 | 7.12k | { // if extend code area : 2 byte Korean character |
792 | 7.12k | *(psText->string + n) = (char)(w >> 8); // hi |
793 | 7.12k | *(psText->string + n + 1) = (char)(w & 0xFF); // lo |
794 | 7.12k | n += 2; // 2 byte |
795 | 7.12k | } |
796 | 17.5k | } |
797 | 229 | psText->string[n] = '\0'; // terminate C string |
798 | 229 | } |
799 | 55.3k | else |
800 | 55.3k | { |
801 | 55.3k | memcpy(psText->string, psDGN->abyElem + text_off, num_chars); |
802 | 55.3k | psText->string[num_chars] = '\0'; |
803 | 55.3k | } |
804 | 55.5k | } |
805 | 55.5k | break; |
806 | | |
807 | 52.8k | case DGNT_TCB: |
808 | 52.8k | psElement = DGNParseTCB(psDGN); |
809 | 52.8k | break; |
810 | | |
811 | 138k | case DGNT_COMPLEX_CHAIN_HEADER: |
812 | 313k | case DGNT_COMPLEX_SHAPE_HEADER: |
813 | 313k | { |
814 | 313k | DGNElemComplexHeader *psHdr = static_cast<DGNElemComplexHeader *>( |
815 | 313k | CPLCalloc(sizeof(DGNElemComplexHeader), 1)); |
816 | 313k | psElement = reinterpret_cast<DGNElemCore *>(psHdr); |
817 | 313k | psElement->stype = DGNST_COMPLEX_HEADER; |
818 | 313k | DGNParseCore(psDGN, psElement); |
819 | | |
820 | 313k | psHdr->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; |
821 | 313k | psHdr->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256; |
822 | 313k | } |
823 | 313k | break; |
824 | | |
825 | 12.2k | case DGNT_TAG_VALUE: |
826 | 12.2k | { |
827 | 12.2k | DGNElemTagValue *psTag = static_cast<DGNElemTagValue *>( |
828 | 12.2k | CPLCalloc(sizeof(DGNElemTagValue), 1)); |
829 | 12.2k | psElement = reinterpret_cast<DGNElemCore *>(psTag); |
830 | 12.2k | psElement->stype = DGNST_TAG_VALUE; |
831 | 12.2k | DGNParseCore(psDGN, psElement); |
832 | | |
833 | 12.2k | psTag->tagType = psDGN->abyElem[74] + psDGN->abyElem[75] * 256; |
834 | 12.2k | memcpy(&(psTag->tagSet), psDGN->abyElem + 68, 4); |
835 | 12.2k | CPL_LSBPTR32(&(psTag->tagSet)); |
836 | 12.2k | psTag->tagIndex = psDGN->abyElem[72] + psDGN->abyElem[73] * 256; |
837 | 12.2k | psTag->tagLength = psDGN->abyElem[150] + psDGN->abyElem[151] * 256; |
838 | | |
839 | 12.2k | if (psTag->tagType == 1) |
840 | 520 | { |
841 | 520 | psTag->tagValue.string = |
842 | 520 | CPLStrdup((char *)psDGN->abyElem + 154); |
843 | 520 | } |
844 | 11.7k | else if (psTag->tagType == 3) |
845 | 278 | { |
846 | 278 | memcpy(&(psTag->tagValue.integer), psDGN->abyElem + 154, 4); |
847 | 278 | CPL_LSBPTR32(&(psTag->tagValue.integer)); |
848 | 278 | } |
849 | 11.4k | else if (psTag->tagType == 4) |
850 | 25 | { |
851 | 25 | memcpy(&(psTag->tagValue.real), psDGN->abyElem + 154, 8); |
852 | 25 | DGN2IEEEDouble(&(psTag->tagValue.real)); |
853 | 25 | } |
854 | 12.2k | } |
855 | 12.2k | break; |
856 | | |
857 | 9.00k | case DGNT_APPLICATION_ELEM: |
858 | 9.00k | if (nLevel == 24) |
859 | 1.68k | { |
860 | 1.68k | psElement = DGNParseTagSet(psDGN); |
861 | 1.68k | if (psElement == nullptr) |
862 | 728 | return nullptr; |
863 | 1.68k | } |
864 | 7.31k | else |
865 | 7.31k | { |
866 | 7.31k | psElement = static_cast<DGNElemCore *>( |
867 | 7.31k | CPLCalloc(sizeof(DGNElemCore), 1)); |
868 | 7.31k | psElement->stype = DGNST_CORE; |
869 | 7.31k | DGNParseCore(psDGN, psElement); |
870 | 7.31k | } |
871 | 8.27k | break; |
872 | | |
873 | 28.3k | case DGNT_CONE: |
874 | 28.3k | { |
875 | 28.3k | if (psDGN->dimension != 3) |
876 | 22.3k | { |
877 | 22.3k | CPLError(CE_Failure, CPLE_AssertionFailed, |
878 | 22.3k | "psDGN->dimension != 3"); |
879 | 22.3k | return nullptr; |
880 | 22.3k | } |
881 | | |
882 | 6.02k | DGNElemCone *psCone = |
883 | 6.02k | static_cast<DGNElemCone *>(CPLCalloc(sizeof(DGNElemCone), 1)); |
884 | 6.02k | psElement = reinterpret_cast<DGNElemCore *>(psCone); |
885 | 6.02k | psElement->stype = DGNST_CONE; |
886 | 6.02k | DGNParseCore(psDGN, psElement); |
887 | | |
888 | 6.02k | psCone->unknown = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; |
889 | 6.02k | psCone->quat[0] = DGN_INT32(psDGN->abyElem + 38); |
890 | 6.02k | psCone->quat[1] = DGN_INT32(psDGN->abyElem + 42); |
891 | 6.02k | psCone->quat[2] = DGN_INT32(psDGN->abyElem + 46); |
892 | 6.02k | psCone->quat[3] = DGN_INT32(psDGN->abyElem + 50); |
893 | | |
894 | 6.02k | memcpy(&(psCone->center_1.x), psDGN->abyElem + 54, 8); |
895 | 6.02k | DGN2IEEEDouble(&(psCone->center_1.x)); |
896 | 6.02k | memcpy(&(psCone->center_1.y), psDGN->abyElem + 62, 8); |
897 | 6.02k | DGN2IEEEDouble(&(psCone->center_1.y)); |
898 | 6.02k | memcpy(&(psCone->center_1.z), psDGN->abyElem + 70, 8); |
899 | 6.02k | DGN2IEEEDouble(&(psCone->center_1.z)); |
900 | 6.02k | memcpy(&(psCone->radius_1), psDGN->abyElem + 78, 8); |
901 | 6.02k | DGN2IEEEDouble(&(psCone->radius_1)); |
902 | | |
903 | 6.02k | memcpy(&(psCone->center_2.x), psDGN->abyElem + 86, 8); |
904 | 6.02k | DGN2IEEEDouble(&(psCone->center_2.x)); |
905 | 6.02k | memcpy(&(psCone->center_2.y), psDGN->abyElem + 94, 8); |
906 | 6.02k | DGN2IEEEDouble(&(psCone->center_2.y)); |
907 | 6.02k | memcpy(&(psCone->center_2.z), psDGN->abyElem + 102, 8); |
908 | 6.02k | DGN2IEEEDouble(&(psCone->center_2.z)); |
909 | 6.02k | memcpy(&(psCone->radius_2), psDGN->abyElem + 110, 8); |
910 | 6.02k | DGN2IEEEDouble(&(psCone->radius_2)); |
911 | | |
912 | 6.02k | psCone->radius_1 *= psDGN->scale; |
913 | 6.02k | psCone->radius_2 *= psDGN->scale; |
914 | 6.02k | DGNTransformPoint(psDGN, &psCone->center_1); |
915 | 6.02k | DGNTransformPoint(psDGN, &psCone->center_2); |
916 | 6.02k | } |
917 | 0 | break; |
918 | | |
919 | 1.62k | case DGNT_3DSURFACE_HEADER: |
920 | 57.4k | case DGNT_3DSOLID_HEADER: |
921 | 57.4k | { |
922 | 57.4k | DGNElemComplexHeader *psShape = static_cast<DGNElemComplexHeader *>( |
923 | 57.4k | CPLCalloc(sizeof(DGNElemComplexHeader), 1)); |
924 | 57.4k | psElement = reinterpret_cast<DGNElemCore *>(psShape); |
925 | 57.4k | psElement->stype = DGNST_COMPLEX_HEADER; |
926 | 57.4k | DGNParseCore(psDGN, psElement); |
927 | | |
928 | | // Read complex header |
929 | 57.4k | psShape->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; |
930 | 57.4k | psShape->numelems = psDGN->abyElem[38] + psDGN->abyElem[39] * 256; |
931 | 57.4k | psShape->surftype = psDGN->abyElem[40]; |
932 | 57.4k | psShape->boundelms = psDGN->abyElem[41] + 1; |
933 | 57.4k | } |
934 | 57.4k | break; |
935 | 10.3k | case DGNT_BSPLINE_SURFACE_HEADER: |
936 | 10.3k | { |
937 | 10.3k | DGNElemBSplineSurfaceHeader *psSpline = |
938 | 10.3k | static_cast<DGNElemBSplineSurfaceHeader *>( |
939 | 10.3k | CPLCalloc(sizeof(DGNElemBSplineSurfaceHeader), 1)); |
940 | 10.3k | psElement = reinterpret_cast<DGNElemCore *>(psSpline); |
941 | 10.3k | psElement->stype = DGNST_BSPLINE_SURFACE_HEADER; |
942 | 10.3k | DGNParseCore(psDGN, psElement); |
943 | | |
944 | | // Read B-Spline surface header |
945 | 10.3k | psSpline->desc_words = |
946 | 10.3k | static_cast<long>(DGN_INT32(psDGN->abyElem + 36)); |
947 | 10.3k | psSpline->curve_type = psDGN->abyElem[41]; |
948 | | |
949 | | // U |
950 | 10.3k | psSpline->u_order = (psDGN->abyElem[40] & 0x0f) + 2; |
951 | 10.3k | psSpline->u_properties = psDGN->abyElem[40] & 0xf0; |
952 | 10.3k | psSpline->num_poles_u = |
953 | 10.3k | psDGN->abyElem[42] + psDGN->abyElem[43] * 256; |
954 | 10.3k | psSpline->num_knots_u = |
955 | 10.3k | psDGN->abyElem[44] + psDGN->abyElem[45] * 256; |
956 | 10.3k | psSpline->rule_lines_u = |
957 | 10.3k | psDGN->abyElem[46] + psDGN->abyElem[47] * 256; |
958 | | |
959 | | // V |
960 | 10.3k | psSpline->v_order = (psDGN->abyElem[48] & 0x0f) + 2; |
961 | 10.3k | psSpline->v_properties = psDGN->abyElem[48] & 0xf0; |
962 | 10.3k | psSpline->num_poles_v = |
963 | 10.3k | psDGN->abyElem[50] + psDGN->abyElem[51] * 256; |
964 | 10.3k | psSpline->num_knots_v = |
965 | 10.3k | psDGN->abyElem[52] + psDGN->abyElem[53] * 256; |
966 | 10.3k | psSpline->rule_lines_v = |
967 | 10.3k | psDGN->abyElem[54] + psDGN->abyElem[55] * 256; |
968 | | |
969 | 10.3k | psSpline->num_bounds = |
970 | 10.3k | psDGN->abyElem[56] + psDGN->abyElem[57] * 556; |
971 | 10.3k | } |
972 | 10.3k | break; |
973 | 2.26k | case DGNT_BSPLINE_CURVE_HEADER: |
974 | 2.26k | { |
975 | 2.26k | DGNElemBSplineCurveHeader *psSpline = |
976 | 2.26k | static_cast<DGNElemBSplineCurveHeader *>( |
977 | 2.26k | CPLCalloc(sizeof(DGNElemBSplineCurveHeader), 1)); |
978 | 2.26k | psElement = reinterpret_cast<DGNElemCore *>(psSpline); |
979 | 2.26k | psElement->stype = DGNST_BSPLINE_CURVE_HEADER; |
980 | 2.26k | DGNParseCore(psDGN, psElement); |
981 | | |
982 | | // Read B-Spline curve header |
983 | 2.26k | psSpline->desc_words = |
984 | 2.26k | static_cast<long>(DGN_INT32(psDGN->abyElem + 36)); |
985 | | |
986 | | // flags |
987 | 2.26k | psSpline->order = (psDGN->abyElem[40] & 0x0f) + 2; |
988 | 2.26k | psSpline->properties = psDGN->abyElem[40] & 0xf0; |
989 | 2.26k | psSpline->curve_type = psDGN->abyElem[41]; |
990 | | |
991 | 2.26k | psSpline->num_poles = psDGN->abyElem[42] + psDGN->abyElem[43] * 256; |
992 | 2.26k | psSpline->num_knots = psDGN->abyElem[44] + psDGN->abyElem[45] * 256; |
993 | 2.26k | } |
994 | 2.26k | break; |
995 | 19.8k | case DGNT_BSPLINE_SURFACE_BOUNDARY: |
996 | 19.8k | { |
997 | 19.8k | short numverts = psDGN->abyElem[38] + psDGN->abyElem[39] * 256; |
998 | 19.8k | if (numverts <= 0) |
999 | 17.0k | { |
1000 | 17.0k | CPLError(CE_Failure, CPLE_AssertionFailed, "numverts <= 0"); |
1001 | 17.0k | return nullptr; |
1002 | 17.0k | } |
1003 | | |
1004 | 2.80k | DGNElemBSplineSurfaceBoundary *psBounds = |
1005 | 2.80k | static_cast<DGNElemBSplineSurfaceBoundary *>( |
1006 | 2.80k | CPLCalloc(sizeof(DGNElemBSplineSurfaceBoundary) + |
1007 | 2.80k | (numverts - 1) * sizeof(DGNPoint), |
1008 | 2.80k | 1)); |
1009 | 2.80k | psElement = reinterpret_cast<DGNElemCore *>(psBounds); |
1010 | 2.80k | psElement->stype = DGNST_BSPLINE_SURFACE_BOUNDARY; |
1011 | 2.80k | DGNParseCore(psDGN, psElement); |
1012 | | |
1013 | 2.80k | int deltaLength = 0, deltaStart = 0; |
1014 | 2.80k | if (psBounds->core.properties & DGNPF_ATTRIBUTES) |
1015 | 695 | { |
1016 | 1.16M | for (int iAttr = 0; iAttr < psBounds->core.attr_bytes - 3; |
1017 | 1.16M | iAttr++) |
1018 | 1.16M | { |
1019 | 1.16M | if (psBounds->core.attr_data[iAttr] == 0xA9 && |
1020 | 3.90k | psBounds->core.attr_data[iAttr + 1] == 0x51) |
1021 | 246 | { |
1022 | 246 | deltaLength = |
1023 | 246 | (psBounds->core.attr_data[iAttr + 2] + |
1024 | 246 | psBounds->core.attr_data[iAttr + 3] * 256) * |
1025 | 246 | 2; |
1026 | 246 | deltaStart = iAttr + 6; |
1027 | 246 | break; |
1028 | 246 | } |
1029 | 1.16M | } |
1030 | 695 | } |
1031 | | // Read B-Spline surface boundary |
1032 | 2.80k | psBounds->number = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; |
1033 | | |
1034 | 1.26M | for (int i = 0; i < numverts && 44 + i * 8 + 4 <= psDGN->nElemBytes; |
1035 | 1.26M | i++) |
1036 | 1.26M | { |
1037 | 1.26M | psBounds->vertices[i].x = |
1038 | 1.26M | DGN_INT32(psDGN->abyElem + 40 + i * 8); |
1039 | 1.26M | psBounds->vertices[i].y = |
1040 | 1.26M | DGN_INT32(psDGN->abyElem + 44 + i * 8); |
1041 | 1.26M | psBounds->vertices[i].z = 0; |
1042 | 1.26M | if (deltaStart && deltaLength && |
1043 | 880k | deltaStart + i * 4 + 2 + 2 <= psBounds->core.attr_bytes) |
1044 | 462k | { |
1045 | 462k | int dx = DGN_INT16(psBounds->core.attr_data + deltaStart + |
1046 | 462k | i * 4); |
1047 | 462k | int dy = DGN_INT16(psBounds->core.attr_data + deltaStart + |
1048 | 462k | i * 4 + 2); |
1049 | 462k | psBounds->vertices[i].x += dx / 32767.0; |
1050 | 462k | psBounds->vertices[i].y += dy / 32767.0; |
1051 | 462k | } |
1052 | 1.26M | psBounds->numverts = static_cast<short>(i + 1); |
1053 | 1.26M | } |
1054 | 2.80k | } |
1055 | 0 | break; |
1056 | 430 | case DGNT_BSPLINE_KNOT: |
1057 | 76.4k | case DGNT_BSPLINE_WEIGHT_FACTOR: |
1058 | 76.4k | { |
1059 | | // FIXME: Is it OK to assume that the # of elements corresponds |
1060 | | // directly to the element size? kintel 20051215. |
1061 | 76.4k | int attr_bytes = |
1062 | 76.4k | psDGN->nElemBytes - |
1063 | 76.4k | (psDGN->abyElem[30] + psDGN->abyElem[31] * 256) * 2 - 32; |
1064 | 76.4k | if (attr_bytes < 0) |
1065 | 75.6k | { |
1066 | 75.6k | CPLError(CE_Failure, CPLE_AssertionFailed, "attr_bytes < 0"); |
1067 | 75.6k | return nullptr; |
1068 | 75.6k | } |
1069 | 786 | int numelems = (psDGN->nElemBytes - 36 - attr_bytes) / 4; |
1070 | 786 | if (numelems < 1) |
1071 | 753 | { |
1072 | 753 | CPLError(CE_Failure, CPLE_AssertionFailed, "numelems < 1"); |
1073 | 753 | return nullptr; |
1074 | 753 | } |
1075 | 33 | DGNElemKnotWeight *psArray = |
1076 | 33 | static_cast<DGNElemKnotWeight *>(CPLCalloc( |
1077 | 33 | sizeof(DGNElemKnotWeight) + (numelems - 1) * sizeof(float), |
1078 | 33 | 1)); |
1079 | | |
1080 | 33 | psElement = reinterpret_cast<DGNElemCore *>(psArray); |
1081 | 33 | psElement->stype = DGNST_KNOT_WEIGHT; |
1082 | 33 | DGNParseCore(psDGN, psElement); |
1083 | | |
1084 | | // Read array |
1085 | 59.9k | for (int i = 0; i < numelems; i++) |
1086 | 59.9k | { |
1087 | 59.9k | psArray->array[i] = static_cast<float>( |
1088 | 59.9k | 1.0 * DGN_INT32(psDGN->abyElem + 36 + i * 4) / |
1089 | 59.9k | ((1UL << 31) - 1)); |
1090 | 59.9k | } |
1091 | 33 | } |
1092 | 0 | break; |
1093 | 15.1k | case DGNT_SHARED_CELL_DEFN: |
1094 | 15.1k | { |
1095 | 15.1k | DGNElemSharedCellDefn *psShared = |
1096 | 15.1k | static_cast<DGNElemSharedCellDefn *>( |
1097 | 15.1k | CPLCalloc(sizeof(DGNElemSharedCellDefn), 1)); |
1098 | 15.1k | psElement = reinterpret_cast<DGNElemCore *>(psShared); |
1099 | 15.1k | psElement->stype = DGNST_SHARED_CELL_DEFN; |
1100 | 15.1k | DGNParseCore(psDGN, psElement); |
1101 | | |
1102 | 15.1k | psShared->totlength = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; |
1103 | 15.1k | } |
1104 | 15.1k | break; |
1105 | 2.79M | default: |
1106 | 2.79M | { |
1107 | 2.79M | psElement = |
1108 | 2.79M | static_cast<DGNElemCore *>(CPLCalloc(sizeof(DGNElemCore), 1)); |
1109 | 2.79M | psElement->stype = DGNST_CORE; |
1110 | 2.79M | DGNParseCore(psDGN, psElement); |
1111 | 2.79M | } |
1112 | 2.79M | break; |
1113 | 4.40M | } |
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 | 4.22M | if (psElement->stype == DGNST_CORE || |
1121 | 1.41M | (psDGN->options & DGNO_CAPTURE_RAW_DATA)) |
1122 | 2.80M | { |
1123 | 2.80M | psElement->raw_bytes = psDGN->nElemBytes; |
1124 | 2.80M | psElement->raw_data = |
1125 | 2.80M | static_cast<unsigned char *>(CPLMalloc(psElement->raw_bytes)); |
1126 | | |
1127 | 2.80M | memcpy(psElement->raw_data, psDGN->abyElem, psElement->raw_bytes); |
1128 | 2.80M | } |
1129 | | |
1130 | | /* -------------------------------------------------------------------- */ |
1131 | | /* Collect some additional generic information. */ |
1132 | | /* -------------------------------------------------------------------- */ |
1133 | 4.22M | psElement->element_id = psDGN->next_element_id - 1; |
1134 | | |
1135 | 4.22M | psElement->offset = |
1136 | 4.22M | static_cast<int>(VSIFTellL(psDGN->fp)) - psDGN->nElemBytes; |
1137 | 4.22M | psElement->size = psDGN->nElemBytes; |
1138 | | |
1139 | 4.22M | return psElement; |
1140 | 4.40M | } |
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 | | DGNElemCore *DGNReadElement(DGNHandle hDGN) |
1164 | | |
1165 | 296M | { |
1166 | 296M | DGNInfo *psDGN = (DGNInfo *)hDGN; |
1167 | 296M | int nType = 0; |
1168 | 296M | int nLevel = 0; |
1169 | 296M | 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 | 296M | do |
1177 | 296M | { |
1178 | 296M | bInsideFilter = true; |
1179 | | |
1180 | 296M | if (!DGNLoadRawElement(psDGN, &nType, &nLevel)) |
1181 | 291M | return nullptr; |
1182 | | |
1183 | 4.40M | if (psDGN->has_spatial_filter) |
1184 | 0 | { |
1185 | 0 | if (!psDGN->sf_converted_to_uor) |
1186 | 0 | DGNSpatialFilterToUOR(psDGN); |
1187 | |
|
1188 | 0 | GUInt32 nXMin = 0; |
1189 | 0 | GUInt32 nXMax = 0; |
1190 | 0 | GUInt32 nYMin = 0; |
1191 | 0 | GUInt32 nYMax = 0; |
1192 | 0 | if (!DGNGetRawExtents(psDGN, nType, nullptr, &nXMin, &nYMin, |
1193 | 0 | nullptr, &nXMax, &nYMax, nullptr)) |
1194 | 0 | { |
1195 | | /* If we don't have spatial characteristics for the element |
1196 | | we will pass it through. */ |
1197 | 0 | bInsideFilter = true; |
1198 | 0 | } |
1199 | 0 | else if (nXMin > psDGN->sf_max_x || nYMin > psDGN->sf_max_y || |
1200 | 0 | nXMax < psDGN->sf_min_x || nYMax < psDGN->sf_min_y) |
1201 | 0 | { |
1202 | 0 | bInsideFilter = false; |
1203 | 0 | } |
1204 | | |
1205 | | /* |
1206 | | ** We want to select complex elements based on the extents of |
1207 | | ** the header, not the individual elements. |
1208 | | */ |
1209 | 0 | if (nType == DGNT_COMPLEX_CHAIN_HEADER || |
1210 | 0 | nType == DGNT_COMPLEX_SHAPE_HEADER) |
1211 | 0 | { |
1212 | 0 | psDGN->in_complex_group = true; |
1213 | 0 | psDGN->select_complex_group = bInsideFilter; |
1214 | 0 | } |
1215 | 0 | else if (psDGN->abyElem[0] & 0x80 /* complex flag set */) |
1216 | 0 | { |
1217 | 0 | if (psDGN->in_complex_group) |
1218 | 0 | bInsideFilter = psDGN->select_complex_group; |
1219 | 0 | } |
1220 | 0 | else |
1221 | 0 | { |
1222 | 0 | psDGN->in_complex_group = false; |
1223 | 0 | } |
1224 | 0 | } |
1225 | 4.40M | } while (!bInsideFilter); |
1226 | | |
1227 | | /* -------------------------------------------------------------------- */ |
1228 | | /* Convert into an element structure. */ |
1229 | | /* -------------------------------------------------------------------- */ |
1230 | 4.40M | DGNElemCore *psElement = DGNProcessElement(psDGN, nType, nLevel); |
1231 | | |
1232 | 4.40M | return psElement; |
1233 | 296M | } |
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 | | int DGNElemTypeHasDispHdr(int nElemType) |
1249 | | |
1250 | 113k | { |
1251 | 113k | switch (nElemType) |
1252 | 113k | { |
1253 | 58.6k | case 0: |
1254 | 59.3k | case DGNT_TCB: |
1255 | 62.3k | case DGNT_CELL_LIBRARY: |
1256 | 62.6k | case DGNT_LEVEL_SYMBOLOGY: |
1257 | 62.7k | case 32: |
1258 | 62.7k | case 44: |
1259 | 62.8k | case 48: |
1260 | 62.8k | case 49: |
1261 | 62.9k | case 50: |
1262 | 63.0k | case 51: |
1263 | 63.0k | case 57: |
1264 | 63.0k | case 60: |
1265 | 64.2k | case 61: |
1266 | 64.2k | case 62: |
1267 | 64.2k | case 63: |
1268 | 64.2k | return FALSE; |
1269 | | |
1270 | 49.7k | default: |
1271 | 49.7k | return TRUE; |
1272 | 113k | } |
1273 | 113k | } |
1274 | | |
1275 | | /************************************************************************/ |
1276 | | /* DGNParseCore() */ |
1277 | | /************************************************************************/ |
1278 | | |
1279 | | int DGNParseCore(DGNInfo *psDGN, DGNElemCore *psElement) |
1280 | | |
1281 | 4.30M | { |
1282 | 4.30M | GByte *psData = psDGN->abyElem + 0; |
1283 | | |
1284 | 4.30M | psElement->level = psData[0] & 0x3f; |
1285 | 4.30M | psElement->complex = psData[0] & 0x80; |
1286 | 4.30M | psElement->deleted = psData[1] & 0x80; |
1287 | 4.30M | psElement->type = psData[1] & 0x7f; |
1288 | | |
1289 | 4.30M | if (psDGN->nElemBytes >= 36 && DGNElemTypeHasDispHdr(psElement->type)) |
1290 | 49.7k | { |
1291 | 49.7k | psElement->graphic_group = psData[28] + psData[29] * 256; |
1292 | 49.7k | psElement->properties = psData[32] + psData[33] * 256; |
1293 | 49.7k | psElement->style = psData[34] & 0x7; |
1294 | 49.7k | psElement->weight = (psData[34] & 0xf8) >> 3; |
1295 | 49.7k | psElement->color = psData[35]; |
1296 | 49.7k | } |
1297 | 4.25M | else |
1298 | 4.25M | { |
1299 | 4.25M | psElement->graphic_group = 0; |
1300 | 4.25M | psElement->properties = 0; |
1301 | 4.25M | psElement->style = 0; |
1302 | 4.25M | psElement->weight = 0; |
1303 | 4.25M | psElement->color = 0; |
1304 | 4.25M | } |
1305 | | |
1306 | 4.30M | if (psElement->properties & DGNPF_ATTRIBUTES) |
1307 | 19.9k | { |
1308 | 19.9k | const int nAttIndex = psData[30] + psData[31] * 256; |
1309 | | |
1310 | 19.9k | psElement->attr_bytes = psDGN->nElemBytes - nAttIndex * 2 - 32; |
1311 | 19.9k | if (psElement->attr_bytes > 0) |
1312 | 7.30k | { |
1313 | 7.30k | psElement->attr_data = |
1314 | 7.30k | static_cast<unsigned char *>(CPLMalloc(psElement->attr_bytes)); |
1315 | 7.30k | memcpy(psElement->attr_data, psData + nAttIndex * 2 + 32, |
1316 | 7.30k | psElement->attr_bytes); |
1317 | 7.30k | } |
1318 | 12.6k | else |
1319 | 12.6k | { |
1320 | 12.6k | CPLError(CE_Warning, CPLE_AppDefined, |
1321 | 12.6k | "Computed %d bytes for attribute info on element,\n" |
1322 | 12.6k | "perhaps this element type doesn't really have a disphdr?", |
1323 | 12.6k | psElement->attr_bytes); |
1324 | 12.6k | psElement->attr_bytes = 0; |
1325 | 12.6k | } |
1326 | 19.9k | } |
1327 | | |
1328 | 4.30M | return TRUE; |
1329 | 4.30M | } |
1330 | | |
1331 | | /************************************************************************/ |
1332 | | /* DGNParseColorTable() */ |
1333 | | /************************************************************************/ |
1334 | | |
1335 | | static DGNElemCore *DGNParseColorTable(DGNInfo *psDGN) |
1336 | | |
1337 | 15.7k | { |
1338 | 15.7k | DGNElemColorTable *psColorTable = static_cast<DGNElemColorTable *>( |
1339 | 15.7k | CPLCalloc(sizeof(DGNElemColorTable), 1)); |
1340 | 15.7k | DGNElemCore *psElement = reinterpret_cast<DGNElemCore *>(psColorTable); |
1341 | 15.7k | psElement->stype = DGNST_COLORTABLE; |
1342 | | |
1343 | 15.7k | DGNParseCore(psDGN, psElement); |
1344 | | |
1345 | 15.7k | psColorTable->screen_flag = psDGN->abyElem[36] + psDGN->abyElem[37] * 256; |
1346 | | |
1347 | 15.7k | memcpy(psColorTable->color_info[255], psDGN->abyElem + 38, 3); |
1348 | 15.7k | 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 | 15.7k | memcpy(psDGN->color_table, psColorTable->color_info, 768); |
1356 | 15.7k | psDGN->got_color_table = 1; |
1357 | | |
1358 | 15.7k | return psElement; |
1359 | 15.7k | } |
1360 | | |
1361 | | /************************************************************************/ |
1362 | | /* DGNParseTagSet() */ |
1363 | | /************************************************************************/ |
1364 | | |
1365 | | static DGNElemCore *DGNParseTagSet(DGNInfo *psDGN) |
1366 | | |
1367 | 1.68k | { |
1368 | 1.68k | DGNElemTagSet *psTagSet = |
1369 | 1.68k | static_cast<DGNElemTagSet *>(CPLCalloc(sizeof(DGNElemTagSet), 1)); |
1370 | 1.68k | DGNElemCore *psElement = reinterpret_cast<DGNElemCore *>(psTagSet); |
1371 | 1.68k | psElement->stype = DGNST_TAG_SET; |
1372 | | |
1373 | 1.68k | DGNParseCore(psDGN, psElement); |
1374 | | |
1375 | | /* -------------------------------------------------------------------- */ |
1376 | | /* Parse the overall information. */ |
1377 | | /* -------------------------------------------------------------------- */ |
1378 | 1.68k | psTagSet->tagCount = psDGN->abyElem[44] + psDGN->abyElem[45] * 256; |
1379 | 1.68k | psTagSet->flags = psDGN->abyElem[46] + psDGN->abyElem[47] * 256; |
1380 | 1.68k | psTagSet->tagSetName = CPLStrdup((const char *)(psDGN->abyElem + 48)); |
1381 | | |
1382 | | /* -------------------------------------------------------------------- */ |
1383 | | /* Get the tag set number out of the attributes, if available. */ |
1384 | | /* -------------------------------------------------------------------- */ |
1385 | 1.68k | psTagSet->tagSet = -1; |
1386 | | |
1387 | 1.68k | 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 | 1.68k | psTagSet->tagList = static_cast<DGNTagDef *>( |
1397 | 1.68k | CPLCalloc(sizeof(DGNTagDef), psTagSet->tagCount)); |
1398 | | |
1399 | 1.68k | size_t nDataOffset = 48 + strlen(psTagSet->tagSetName) + 1 + 1; |
1400 | | |
1401 | 109k | for (int iTag = 0; iTag < psTagSet->tagCount; iTag++) |
1402 | 108k | { |
1403 | 108k | DGNTagDef *tagDef = psTagSet->tagList + iTag; |
1404 | | |
1405 | | // Check the buffer is large enough to read all tagDef components |
1406 | 108k | size_t nDataOffsetEnd = nDataOffset; |
1407 | 108k | if (nDataOffsetEnd <= static_cast<size_t>(psDGN->nElemBytes)) |
1408 | 107k | { |
1409 | 107k | nDataOffsetEnd += |
1410 | 107k | strlen((char *)psDGN->abyElem + nDataOffsetEnd) + 1 + 2; |
1411 | 107k | } |
1412 | 108k | if (nDataOffsetEnd <= static_cast<size_t>(psDGN->nElemBytes)) |
1413 | 107k | { |
1414 | 107k | nDataOffsetEnd += |
1415 | 107k | strlen((char *)psDGN->abyElem + nDataOffsetEnd) + 1 + 2 + 5; |
1416 | 107k | if (tagDef->type == 1) |
1417 | 0 | { |
1418 | 0 | nDataOffsetEnd += strlen(tagDef->defaultValue.string) + 1; |
1419 | 0 | } |
1420 | 107k | else if (tagDef->type == 3 || tagDef->type == 5) |
1421 | 0 | { |
1422 | 0 | nDataOffsetEnd += 4; |
1423 | 0 | } |
1424 | 107k | else if (tagDef->type == 4) |
1425 | 0 | { |
1426 | 0 | nDataOffsetEnd += 8; |
1427 | 0 | } |
1428 | 107k | else |
1429 | 107k | { |
1430 | 107k | nDataOffsetEnd += 4; |
1431 | 107k | } |
1432 | 107k | } |
1433 | 108k | if (nDataOffsetEnd > static_cast<size_t>(psDGN->nElemBytes)) |
1434 | 728 | { |
1435 | 728 | CPLError(CE_Failure, CPLE_AssertionFailed, |
1436 | 728 | "nDataOffset >= static_cast<size_t>(psDGN->nElemBytes)"); |
1437 | 728 | DGNFreeElement(psDGN, psElement); |
1438 | 728 | return nullptr; |
1439 | 728 | } |
1440 | | |
1441 | | /* collect tag name. */ |
1442 | 107k | tagDef->name = CPLStrdup((char *)psDGN->abyElem + nDataOffset); |
1443 | 107k | nDataOffset += strlen(tagDef->name) + 1; |
1444 | | |
1445 | | /* Get tag id */ |
1446 | 107k | tagDef->id = |
1447 | 107k | psDGN->abyElem[nDataOffset] + psDGN->abyElem[nDataOffset + 1] * 256; |
1448 | 107k | nDataOffset += 2; |
1449 | | |
1450 | | /* Get User Prompt */ |
1451 | 107k | tagDef->prompt = CPLStrdup((char *)psDGN->abyElem + nDataOffset); |
1452 | 107k | nDataOffset += strlen(tagDef->prompt) + 1; |
1453 | | |
1454 | | /* Get type */ |
1455 | 107k | tagDef->type = |
1456 | 107k | psDGN->abyElem[nDataOffset] + psDGN->abyElem[nDataOffset + 1] * 256; |
1457 | 107k | nDataOffset += 2; |
1458 | | |
1459 | | /* skip five zeros */ |
1460 | 107k | nDataOffset += 5; |
1461 | | |
1462 | | /* Get the default */ |
1463 | 107k | if (tagDef->type == 1) |
1464 | 180 | { |
1465 | 180 | tagDef->defaultValue.string = |
1466 | 180 | CPLStrdup((char *)psDGN->abyElem + nDataOffset); |
1467 | 180 | nDataOffset += strlen(tagDef->defaultValue.string) + 1; |
1468 | 180 | } |
1469 | 107k | else if (tagDef->type == 3 || tagDef->type == 5) |
1470 | 652 | { |
1471 | 652 | memcpy(&(tagDef->defaultValue.integer), |
1472 | 652 | psDGN->abyElem + nDataOffset, 4); |
1473 | 652 | CPL_LSBPTR32(&(tagDef->defaultValue.integer)); |
1474 | 652 | nDataOffset += 4; |
1475 | 652 | } |
1476 | 106k | else if (tagDef->type == 4) |
1477 | 113 | { |
1478 | 113 | memcpy(&(tagDef->defaultValue.real), psDGN->abyElem + nDataOffset, |
1479 | 113 | 8); |
1480 | 113 | DGN2IEEEDouble(&(tagDef->defaultValue.real)); |
1481 | 113 | nDataOffset += 8; |
1482 | 113 | } |
1483 | 106k | else |
1484 | 106k | nDataOffset += 4; |
1485 | 107k | } |
1486 | 959 | return psElement; |
1487 | 1.68k | } |
1488 | | |
1489 | | /************************************************************************/ |
1490 | | /* DGNParseTCB() */ |
1491 | | /************************************************************************/ |
1492 | | |
1493 | | static DGNElemCore *DGNParseTCB(DGNInfo *psDGN) |
1494 | | |
1495 | 97.1k | { |
1496 | 97.1k | DGNElemTCB *psTCB = |
1497 | 97.1k | static_cast<DGNElemTCB *>(CPLCalloc(sizeof(DGNElemTCB), 1)); |
1498 | 97.1k | DGNElemCore *psElement = reinterpret_cast<DGNElemCore *>(psTCB); |
1499 | 97.1k | psElement->stype = DGNST_TCB; |
1500 | 97.1k | DGNParseCore(psDGN, psElement); |
1501 | | |
1502 | 97.1k | if (psDGN->abyElem[1214] & 0x40) |
1503 | 6.31k | psTCB->dimension = 3; |
1504 | 90.8k | else |
1505 | 90.8k | psTCB->dimension = 2; |
1506 | | |
1507 | 97.1k | psTCB->subunits_per_master = |
1508 | 97.1k | static_cast<long>(DGN_INT32(psDGN->abyElem + 1112)); |
1509 | | |
1510 | 97.1k | psTCB->master_units[0] = (char)psDGN->abyElem[1120]; |
1511 | 97.1k | psTCB->master_units[1] = (char)psDGN->abyElem[1121]; |
1512 | 97.1k | psTCB->master_units[2] = '\0'; |
1513 | | |
1514 | 97.1k | psTCB->uor_per_subunit = |
1515 | 97.1k | static_cast<long>(DGN_INT32(psDGN->abyElem + 1116)); |
1516 | | |
1517 | 97.1k | psTCB->sub_units[0] = (char)psDGN->abyElem[1122]; |
1518 | 97.1k | psTCB->sub_units[1] = (char)psDGN->abyElem[1123]; |
1519 | 97.1k | psTCB->sub_units[2] = '\0'; |
1520 | | |
1521 | | /* Get global origin */ |
1522 | 97.1k | memcpy(&(psTCB->origin_x), psDGN->abyElem + 1240, 8); |
1523 | 97.1k | memcpy(&(psTCB->origin_y), psDGN->abyElem + 1248, 8); |
1524 | 97.1k | memcpy(&(psTCB->origin_z), psDGN->abyElem + 1256, 8); |
1525 | | |
1526 | | /* Transform to IEEE */ |
1527 | 97.1k | DGN2IEEEDouble(&(psTCB->origin_x)); |
1528 | 97.1k | DGN2IEEEDouble(&(psTCB->origin_y)); |
1529 | 97.1k | DGN2IEEEDouble(&(psTCB->origin_z)); |
1530 | | |
1531 | | /* Convert from UORs to master units. */ |
1532 | 97.1k | if (psTCB->uor_per_subunit != 0 && psTCB->subunits_per_master != 0) |
1533 | 49.7k | { |
1534 | 49.7k | psTCB->origin_x = psTCB->origin_x / |
1535 | 49.7k | (psTCB->uor_per_subunit * psTCB->subunits_per_master); |
1536 | 49.7k | psTCB->origin_y = psTCB->origin_y / |
1537 | 49.7k | (psTCB->uor_per_subunit * psTCB->subunits_per_master); |
1538 | 49.7k | psTCB->origin_z = psTCB->origin_z / |
1539 | 49.7k | (psTCB->uor_per_subunit * psTCB->subunits_per_master); |
1540 | 49.7k | } |
1541 | | |
1542 | 97.1k | if (!psDGN->got_tcb) |
1543 | 2.23k | { |
1544 | 2.23k | psDGN->got_tcb = true; |
1545 | 2.23k | psDGN->dimension = psTCB->dimension; |
1546 | 2.23k | psDGN->origin_x = psTCB->origin_x; |
1547 | 2.23k | psDGN->origin_y = psTCB->origin_y; |
1548 | 2.23k | psDGN->origin_z = psTCB->origin_z; |
1549 | | |
1550 | 2.23k | if (psTCB->uor_per_subunit != 0 && psTCB->subunits_per_master != 0) |
1551 | 666 | psDGN->scale = |
1552 | 666 | 1.0 / (psTCB->uor_per_subunit * psTCB->subunits_per_master); |
1553 | 2.23k | } |
1554 | | |
1555 | | /* Collect views */ |
1556 | 874k | for (int iView = 0; iView < 8; iView++) |
1557 | 777k | { |
1558 | 777k | unsigned char *pabyRawView = psDGN->abyElem + 46 + iView * 118; |
1559 | 777k | DGNViewInfo *psView = psTCB->views + iView; |
1560 | | |
1561 | 777k | psView->flags = pabyRawView[0] + pabyRawView[1] * 256; |
1562 | 777k | memcpy(psView->levels, pabyRawView + 2, 8); |
1563 | | |
1564 | 777k | psView->origin.x = DGN_INT32(pabyRawView + 10); |
1565 | 777k | psView->origin.y = DGN_INT32(pabyRawView + 14); |
1566 | 777k | psView->origin.z = DGN_INT32(pabyRawView + 18); |
1567 | | |
1568 | 777k | DGNTransformPoint(psDGN, &(psView->origin)); |
1569 | | |
1570 | 777k | psView->delta.x = DGN_INT32(pabyRawView + 22); |
1571 | 777k | psView->delta.y = DGN_INT32(pabyRawView + 26); |
1572 | 777k | psView->delta.z = DGN_INT32(pabyRawView + 30); |
1573 | | |
1574 | 777k | psView->delta.x *= psDGN->scale; |
1575 | 777k | psView->delta.y *= psDGN->scale; |
1576 | 777k | psView->delta.z *= psDGN->scale; |
1577 | | |
1578 | 777k | memcpy(psView->transmatrx, pabyRawView + 34, sizeof(double) * 9); |
1579 | 7.77M | for (int i = 0; i < 9; i++) |
1580 | 6.99M | DGN2IEEEDouble(psView->transmatrx + i); |
1581 | | |
1582 | 777k | memcpy(&(psView->conversion), pabyRawView + 106, sizeof(double)); |
1583 | 777k | DGN2IEEEDouble(&(psView->conversion)); |
1584 | | |
1585 | 777k | psView->activez = |
1586 | 777k | static_cast<unsigned long>(DGN_INT32(pabyRawView + 114)); |
1587 | 777k | } |
1588 | | |
1589 | 97.1k | return psElement; |
1590 | 97.1k | } |
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 | | void DGNFreeElement(CPL_UNUSED DGNHandle hDGN, DGNElemCore *psElement) |
1607 | 4.30M | { |
1608 | 4.30M | if (psElement->attr_data != nullptr) |
1609 | 7.30k | VSIFree(psElement->attr_data); |
1610 | | |
1611 | 4.30M | if (psElement->raw_data != nullptr) |
1612 | 2.80M | VSIFree(psElement->raw_data); |
1613 | | |
1614 | 4.30M | if (psElement->stype == DGNST_TAG_SET) |
1615 | 1.68k | { |
1616 | 1.68k | DGNElemTagSet *psTagSet = reinterpret_cast<DGNElemTagSet *>(psElement); |
1617 | 1.68k | CPLFree(psTagSet->tagSetName); |
1618 | | |
1619 | 6.98M | for (int iTag = 0; iTag < psTagSet->tagCount; iTag++) |
1620 | 6.97M | { |
1621 | 6.97M | CPLFree(psTagSet->tagList[iTag].name); |
1622 | 6.97M | CPLFree(psTagSet->tagList[iTag].prompt); |
1623 | | |
1624 | 6.97M | if (psTagSet->tagList[iTag].type == 1) |
1625 | 180 | CPLFree(psTagSet->tagList[iTag].defaultValue.string); |
1626 | 6.97M | } |
1627 | 1.68k | CPLFree(psTagSet->tagList); |
1628 | 1.68k | } |
1629 | 4.30M | else if (psElement->stype == DGNST_TAG_VALUE) |
1630 | 12.2k | { |
1631 | 12.2k | DGNElemTagValue *psTagValue = |
1632 | 12.2k | reinterpret_cast<DGNElemTagValue *>(psElement); |
1633 | 12.2k | if (psTagValue->tagType == 1) |
1634 | 520 | CPLFree(psTagValue->tagValue.string); |
1635 | 12.2k | } |
1636 | | |
1637 | 4.30M | CPLFree(psElement); |
1638 | 4.30M | } |
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 | | void DGNRewind(DGNHandle hDGN) |
1655 | | |
1656 | 8.34k | { |
1657 | 8.34k | DGNInfo *psDGN = (DGNInfo *)hDGN; |
1658 | | |
1659 | 8.34k | VSIRewindL(psDGN->fp); |
1660 | | |
1661 | 8.34k | psDGN->next_element_id = 0; |
1662 | 8.34k | psDGN->in_complex_group = false; |
1663 | 8.34k | } |
1664 | | |
1665 | | /************************************************************************/ |
1666 | | /* DGNTransformPoint() */ |
1667 | | /************************************************************************/ |
1668 | | |
1669 | | void DGNTransformPoint(DGNInfo *psDGN, DGNPoint *psPoint) |
1670 | | |
1671 | 9.66M | { |
1672 | 9.66M | psPoint->x = psPoint->x * psDGN->scale - psDGN->origin_x; |
1673 | 9.66M | psPoint->y = psPoint->y * psDGN->scale - psDGN->origin_y; |
1674 | 9.66M | psPoint->z = psPoint->z * psDGN->scale - psDGN->origin_z; |
1675 | 9.66M | } |
1676 | | |
1677 | | /************************************************************************/ |
1678 | | /* DGNInverseTransformPoint() */ |
1679 | | /************************************************************************/ |
1680 | | |
1681 | | void DGNInverseTransformPoint(DGNInfo *psDGN, DGNPoint *psPoint) |
1682 | | |
1683 | 0 | { |
1684 | 0 | psPoint->x = (psPoint->x + psDGN->origin_x) / psDGN->scale; |
1685 | 0 | psPoint->y = (psPoint->y + psDGN->origin_y) / psDGN->scale; |
1686 | 0 | psPoint->z = (psPoint->z + psDGN->origin_z) / psDGN->scale; |
1687 | |
|
1688 | 0 | psPoint->x = std::max(-2147483647.0, std::min(2147483647.0, psPoint->x)); |
1689 | 0 | psPoint->y = std::max(-2147483647.0, std::min(2147483647.0, psPoint->y)); |
1690 | 0 | psPoint->z = std::max(-2147483647.0, std::min(2147483647.0, psPoint->z)); |
1691 | 0 | } |
1692 | | |
1693 | | /************************************************************************/ |
1694 | | /* DGNInverseTransformPointToInt() */ |
1695 | | /************************************************************************/ |
1696 | | |
1697 | | void DGNInverseTransformPointToInt(DGNInfo *psDGN, DGNPoint *psPoint, |
1698 | | unsigned char *pabyTarget) |
1699 | | |
1700 | 0 | { |
1701 | 0 | double adfCT[3] = {(psPoint->x + psDGN->origin_x) / psDGN->scale, |
1702 | 0 | (psPoint->y + psDGN->origin_y) / psDGN->scale, |
1703 | 0 | (psPoint->z + psDGN->origin_z) / psDGN->scale}; |
1704 | |
|
1705 | 0 | const int nIter = std::min(3, psDGN->dimension); |
1706 | 0 | for (int i = 0; i < nIter; i++) |
1707 | 0 | { |
1708 | 0 | GInt32 nCTI = static_cast<GInt32>( |
1709 | 0 | std::max(-2147483647.0, std::min(2147483647.0, adfCT[i]))); |
1710 | 0 | unsigned char abyCTI[4]; |
1711 | 0 | 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 | 0 | pabyTarget[i * 4 + 3] = abyCTI[1]; |
1720 | 0 | pabyTarget[i * 4 + 2] = abyCTI[0]; |
1721 | 0 | pabyTarget[i * 4 + 1] = abyCTI[3]; |
1722 | 0 | pabyTarget[i * 4 + 0] = abyCTI[2]; |
1723 | 0 | #endif |
1724 | 0 | } |
1725 | 0 | } |
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 | | int DGNLoadTCB(DGNHandle hDGN) |
1742 | | |
1743 | 33 | { |
1744 | 33 | DGNInfo *psDGN = (DGNInfo *)hDGN; |
1745 | | |
1746 | 33 | if (psDGN->got_tcb) |
1747 | 30 | return TRUE; |
1748 | | |
1749 | 6 | while (!psDGN->got_tcb) |
1750 | 3 | { |
1751 | 3 | DGNElemCore *psElem = DGNReadElement(hDGN); |
1752 | 3 | if (psElem == nullptr) |
1753 | 0 | { |
1754 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1755 | 0 | "DGNLoadTCB() - unable to find TCB in file."); |
1756 | 0 | return FALSE; |
1757 | 0 | } |
1758 | 3 | DGNFreeElement(hDGN, psElem); |
1759 | 3 | } |
1760 | | |
1761 | 3 | return TRUE; |
1762 | 3 | } |
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 | | const DGNElementInfo *DGNGetElementIndex(DGNHandle hDGN, int *pnElementCount) |
1793 | | |
1794 | 20.4k | { |
1795 | 20.4k | DGNInfo *psDGN = (DGNInfo *)hDGN; |
1796 | | |
1797 | 20.4k | DGNBuildIndex(psDGN); |
1798 | | |
1799 | 20.4k | if (pnElementCount != nullptr) |
1800 | 0 | *pnElementCount = psDGN->element_count; |
1801 | | |
1802 | 20.4k | return psDGN->element_index; |
1803 | 20.4k | } |
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 | | int DGNGetExtents(DGNHandle hDGN, double *padfExtents) |
1827 | | |
1828 | 0 | { |
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 | 0 | } |
1856 | | |
1857 | | /************************************************************************/ |
1858 | | /* DGNBuildIndex() */ |
1859 | | /************************************************************************/ |
1860 | | |
1861 | | void DGNBuildIndex(DGNInfo *psDGN) |
1862 | | |
1863 | 20.4k | { |
1864 | 20.4k | if (psDGN->index_built) |
1865 | 16.3k | return; |
1866 | | |
1867 | 4.17k | int nType = 0; |
1868 | 4.17k | int nLevel = 0; |
1869 | 4.17k | GUInt32 anRegion[6] = {}; |
1870 | | |
1871 | 4.17k | psDGN->index_built = true; |
1872 | | |
1873 | 4.17k | DGNRewind(psDGN); |
1874 | | |
1875 | 4.17k | int nMaxElements = 0; |
1876 | | |
1877 | 4.17k | vsi_l_offset nLastOffset = VSIFTellL(psDGN->fp); |
1878 | 4.07M | while (DGNLoadRawElement(psDGN, &nType, &nLevel)) |
1879 | 4.06M | { |
1880 | 4.06M | if (psDGN->element_count == nMaxElements) |
1881 | 6.71k | { |
1882 | 6.71k | nMaxElements = (int)(nMaxElements * 1.5) + 500; |
1883 | | |
1884 | 6.71k | psDGN->element_index = (DGNElementInfo *)CPLRealloc( |
1885 | 6.71k | psDGN->element_index, nMaxElements * sizeof(DGNElementInfo)); |
1886 | 6.71k | } |
1887 | | |
1888 | 4.06M | DGNElementInfo *psEI = psDGN->element_index + psDGN->element_count; |
1889 | 4.06M | psEI->level = (unsigned char)nLevel; |
1890 | 4.06M | psEI->type = (unsigned char)nType; |
1891 | 4.06M | psEI->flags = 0; |
1892 | 4.06M | psEI->offset = nLastOffset; |
1893 | | |
1894 | 4.06M | if (psDGN->abyElem[0] & 0x80) |
1895 | 32.0k | psEI->flags |= DGNEIF_COMPLEX; |
1896 | | |
1897 | 4.06M | if (psDGN->abyElem[1] & 0x80) |
1898 | 84.1k | psEI->flags |= DGNEIF_DELETED; |
1899 | | |
1900 | 4.06M | if (nType == DGNT_LINE || nType == DGNT_LINE_STRING || |
1901 | 3.79M | nType == DGNT_SHAPE || nType == DGNT_CURVE || |
1902 | 3.77M | nType == DGNT_BSPLINE_POLE) |
1903 | 305k | psEI->stype = DGNST_MULTIPOINT; |
1904 | | |
1905 | 3.76M | else if (nType == DGNT_GROUP_DATA && nLevel == DGN_GDL_COLOR_TABLE) |
1906 | 6.93k | { |
1907 | 6.93k | DGNElemCore *psCT = DGNParseColorTable(psDGN); |
1908 | 6.93k | DGNFreeElement((DGNHandle)psDGN, psCT); |
1909 | 6.93k | psEI->stype = DGNST_COLORTABLE; |
1910 | 6.93k | } |
1911 | 3.75M | else if (nType == DGNT_ELLIPSE || nType == DGNT_ARC) |
1912 | 231k | psEI->stype = DGNST_ARC; |
1913 | | |
1914 | 3.52M | else if (nType == DGNT_COMPLEX_SHAPE_HEADER || |
1915 | 3.36M | nType == DGNT_COMPLEX_CHAIN_HEADER || |
1916 | 3.23M | nType == DGNT_3DSURFACE_HEADER || nType == DGNT_3DSOLID_HEADER) |
1917 | 343k | psEI->stype = DGNST_COMPLEX_HEADER; |
1918 | | |
1919 | 3.17M | else if (nType == DGNT_TEXT) |
1920 | 53.5k | psEI->stype = DGNST_TEXT; |
1921 | | |
1922 | 3.12M | else if (nType == DGNT_TAG_VALUE) |
1923 | 12.0k | psEI->stype = DGNST_TAG_VALUE; |
1924 | | |
1925 | 3.11M | else if (nType == DGNT_APPLICATION_ELEM) |
1926 | 8.66k | { |
1927 | 8.66k | if (nLevel == 24) |
1928 | 1.35k | psEI->stype = DGNST_TAG_SET; |
1929 | 7.31k | else |
1930 | 7.31k | psEI->stype = DGNST_CORE; |
1931 | 8.66k | } |
1932 | 3.10M | else if (nType == DGNT_TCB) |
1933 | 44.2k | { |
1934 | 44.2k | DGNElemCore *psTCB = DGNParseTCB(psDGN); |
1935 | 44.2k | DGNFreeElement((DGNHandle)psDGN, psTCB); |
1936 | 44.2k | psEI->stype = DGNST_TCB; |
1937 | 44.2k | } |
1938 | 3.06M | else if (nType == DGNT_CONE) |
1939 | 27.0k | psEI->stype = DGNST_CONE; |
1940 | 3.03M | else |
1941 | 3.03M | psEI->stype = DGNST_CORE; |
1942 | | |
1943 | 4.06M | if (!(psEI->flags & DGNEIF_DELETED) && |
1944 | 3.98M | !(psEI->flags & DGNEIF_COMPLEX) && |
1945 | 3.95M | DGNGetRawExtents(psDGN, nType, nullptr, anRegion + 0, anRegion + 1, |
1946 | 3.95M | anRegion + 2, anRegion + 3, anRegion + 4, |
1947 | 3.95M | anRegion + 5)) |
1948 | 945k | { |
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 | 945k | if (psDGN->got_bounds) |
1957 | 942k | { |
1958 | 942k | psDGN->min_x = std::min(psDGN->min_x, anRegion[0]); |
1959 | 942k | psDGN->min_y = std::min(psDGN->min_y, anRegion[1]); |
1960 | 942k | psDGN->min_z = std::min(psDGN->min_z, anRegion[2]); |
1961 | 942k | psDGN->max_x = std::max(psDGN->max_x, anRegion[3]); |
1962 | 942k | psDGN->max_y = std::max(psDGN->max_y, anRegion[4]); |
1963 | 942k | psDGN->max_z = std::max(psDGN->max_z, anRegion[5]); |
1964 | 942k | } |
1965 | 3.85k | else |
1966 | 3.85k | { |
1967 | 3.85k | psDGN->min_x = anRegion[0]; |
1968 | 3.85k | psDGN->min_y = anRegion[1]; |
1969 | 3.85k | psDGN->min_z = anRegion[2]; |
1970 | 3.85k | psDGN->max_x = anRegion[3]; |
1971 | 3.85k | psDGN->max_y = anRegion[4]; |
1972 | 3.85k | psDGN->max_z = anRegion[5]; |
1973 | 3.85k | psDGN->got_bounds = true; |
1974 | 3.85k | } |
1975 | 945k | } |
1976 | | |
1977 | 4.06M | psDGN->element_count++; |
1978 | | |
1979 | 4.06M | nLastOffset = VSIFTellL(psDGN->fp); |
1980 | 4.06M | } |
1981 | | |
1982 | 4.17k | DGNRewind(psDGN); |
1983 | | |
1984 | 4.17k | psDGN->max_element_count = nMaxElements; |
1985 | 4.17k | } |