1 /*
2 Copyright 2016 Esri
3 
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7 
8 http://www.apache.org/licenses/LICENSE-2.0
9 
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 
16 A local copy of the license and additional notices are located with the
17 source distribution at:
18 
19 http://github.com/Esri/lepcc/
20 
21 Contributors:  Thomas Maurer
22 */
23 
24 #include <algorithm>
25 #include <climits>
26 #include "LEPCC.h"
27 #include "BitStuffer2.h"
28 #include "Common.h"
29 #include "utl_const_array.h"
30 
31 using namespace std;
32 using namespace lepcc;
33 
34 // -------------------------------------------------------------------------- ;
35 
ComputeNumBytesNeededToEncode(uint32 nPts,const Point3D * pts,double maxXErr,double maxYErr,double maxZErr,int64 & nBytes)36 ErrCode LEPCC::ComputeNumBytesNeededToEncode(uint32 nPts, const Point3D* pts,
37   double maxXErr, double maxYErr, double maxZErr, int64& nBytes)
38 {
39   nBytes = -1;
40   m_numBytesNeeded = 0;
41 
42   if (!nPts || !pts || maxXErr <= 0 || maxYErr <= 0 || maxZErr <= 0)
43     return ErrCode::WrongParam;
44 
45   m_maxError = Point3D(maxXErr, maxYErr, maxZErr);
46 
47   m_extent3D = Compute3DExtent(nPts, pts);
48 
49   ErrCode errCode;
50   if ((errCode = Quantize(nPts, pts)) != ErrCode::Ok)
51     return errCode;
52 
53   if ((errCode = ConvertToDeltaModel()) != ErrCode::Ok)
54     return errCode;
55 
56   nBytes = HeaderSize();
57 
58   nBytes += ComputeNumBytes_CutInSegments(m_yDeltaVec, m_sectionSize);
59   nBytes += ComputeNumBytes_CutInSegments(m_numPointsPerRowVec, m_sectionSize);
60   nBytes += ComputeNumBytes_CutInSegments(m_xDeltaVec, m_sectionSize);
61   nBytes += ComputeNumBytes_CutInSegments(m_zVec, m_sectionSize);
62 
63   m_numBytesNeeded = nBytes;
64 
65   return ErrCode::Ok;
66 }
67 
68 // -------------------------------------------------------------------------- ;
69 
GetOrigPointIndexes(std::vector<uint32> & origPointIndexVec) const70 void LEPCC::GetOrigPointIndexes(std::vector<uint32>& origPointIndexVec) const
71 {
72   size_t nPts = m_cell3DVec.size();
73   origPointIndexVec.resize(nPts);
74 
75   for (size_t i = 0; i < nPts; i++)
76     origPointIndexVec[i] = m_cell3DVec[i].origPtIndex;
77 }
78 
GetOrigPointIndexes(uint32 * origPointIndexVec,int maxPtCount) const79 bool LEPCC::GetOrigPointIndexes(uint32* origPointIndexVec, int maxPtCount) const
80 {
81   size_t nPts = m_cell3DVec.size();
82   if (maxPtCount < (int)nPts)
83     return false;
84   for (size_t i = 0; i < nPts; i++)
85     origPointIndexVec[i] = m_cell3DVec[i].origPtIndex;
86   return true;
87 }
88 
89 // -------------------------------------------------------------------------- ;
90 
Encode(Byte ** ppByte,int64 bufferSize) const91 ErrCode LEPCC::Encode(Byte** ppByte, int64 bufferSize) const
92 {
93   if (!ppByte)
94     return ErrCode::WrongParam;
95 
96   if (bufferSize <= HeaderSize() || bufferSize < m_numBytesNeeded)
97     return ErrCode::BufferTooSmall;
98 
99   Byte* ptr = *ppByte;
100   Byte* ptrStart = ptr;    // keep for later
101 
102   TopHeader topHd;
103   memcpy(ptr, &topHd, sizeof(topHd));
104   ptr += sizeof(topHd);
105 
106   Header1 hd1;
107   hd1.blobSize = 0;    // overide later when done
108   hd1.extent3D = m_extent3D;
109   hd1.maxError3D = m_maxError;
110   hd1.numPoints = (uint32)m_zVec.size();
111 
112   memcpy(ptr, &hd1, sizeof(hd1));
113   ptr += sizeof(hd1);
114 
115   *ppByte = ptr;
116 
117   if (!Encode_CutInSegments(ppByte, m_yDeltaVec, m_sectionSize))
118     return ErrCode::Failed;
119   if (!Encode_CutInSegments(ppByte, m_numPointsPerRowVec, m_sectionSize))
120     return ErrCode::Failed;
121   if (!Encode_CutInSegments(ppByte, m_xDeltaVec, m_sectionSize))
122     return ErrCode::Failed;
123   if (!Encode_CutInSegments(ppByte, m_zVec, m_sectionSize))
124     return ErrCode::Failed;
125 
126   // add blob size
127   int64 numBytes = (int64)(*ppByte - ptrStart);
128   memcpy(ptrStart + sizeof(topHd), &numBytes, sizeof(numBytes));    // overide with the real num bytes
129 
130   // add check sum
131   topHd.checkSum = Common::ComputeChecksumFletcher32(ptrStart + sizeof(topHd), (numBytes - sizeof(topHd)));
132   memcpy(ptrStart, &topHd, sizeof(topHd));
133 
134   if (numBytes != m_numBytesNeeded)
135     return ErrCode::Failed;
136 
137   return ErrCode::Ok;
138 }
139 
140 // -------------------------------------------------------------------------- ;
141 
GetBlobSize(const Byte * pByte,int64 bufferSize,uint32 & blobSize)142 ErrCode LEPCC::GetBlobSize(const Byte* pByte, int64 bufferSize, uint32& blobSize)
143 {
144   blobSize = 0;
145 
146   if (!pByte)
147     return ErrCode::WrongParam;
148 
149   TopHeader refHd;
150   Header1 hd1;
151   if (bufferSize < (int64)(sizeof(refHd) + sizeof(hd1.blobSize)))
152     return ErrCode::BufferTooSmall;
153 
154   if (0 != memcmp(pByte, refHd.fileKey, refHd.FileKeyLength()))    // file key
155     return ErrCode::NotLepcc;
156 
157   // get blob size
158   pByte += sizeof(refHd);
159   int64 nBytes = 0;
160   memcpy(&nBytes, pByte, sizeof(nBytes));
161 
162   if (nBytes < bufferSize || nBytes > UINT_MAX)
163     return ErrCode::Failed;
164 
165   blobSize = (uint32)nBytes;
166   return ErrCode::Ok;
167 }
168 
169 // -------------------------------------------------------------------------- ;
170 
GetNumPointsFromHeader(const Byte * pByte,int64 bufferSize,uint32 & nPts)171 ErrCode LEPCC::GetNumPointsFromHeader(const Byte* pByte, int64 bufferSize, uint32& nPts)
172 {
173   nPts = 0;
174 
175   TopHeader topHd;
176   Header1 hd1;
177   ErrCode errCode;
178   if ((errCode = ReadHeaders(pByte, bufferSize, topHd, hd1)) != ErrCode::Ok)
179     return errCode;
180 
181   nPts = hd1.numPoints;
182   return ErrCode::Ok;
183 }
184 
185 // -------------------------------------------------------------------------- ;
186 
GetExtent3DFromHeader(const Byte * pByte,int64 bufferSize,Extent3D & ext)187 ErrCode LEPCC::GetExtent3DFromHeader(const Byte* pByte, int64 bufferSize, Extent3D& ext)
188 {
189   ext = Extent3D();
190 
191   TopHeader topHd;
192   Header1 hd1;
193   ErrCode errCode;
194   if ((errCode = ReadHeaders(pByte, bufferSize, topHd, hd1)) != ErrCode::Ok)
195     return errCode;
196 
197   ext = hd1.extent3D;
198   return ErrCode::Ok;
199 }
200 
201 // -------------------------------------------------------------------------- ;
202 
Decode(const Byte ** ppByte,int64 bufferSize,uint32 & nPtsInOut,Point3D * pts)203 ErrCode LEPCC::Decode(const Byte** ppByte, int64 bufferSize, uint32& nPtsInOut, Point3D* pts)
204 {
205   if (!ppByte || !*ppByte || !nPtsInOut || !pts)
206     return ErrCode::WrongParam;
207 
208   int headerSize = HeaderSize();
209 
210   if (bufferSize <= headerSize)
211     return ErrCode::BufferTooSmall;
212 
213   const Byte* ptr = *ppByte;
214   const Byte* ptrStart = ptr;    // keep for later
215 
216   TopHeader topHd;
217   Header1 hd1;
218   ErrCode errCode;
219   if ((errCode = ReadHeaders(ptr, bufferSize, topHd, hd1)) != ErrCode::Ok)
220     return errCode;
221 
222   ptr += headerSize;
223 
224   if (bufferSize < hd1.blobSize)
225     return ErrCode::BufferTooSmall;
226 
227   // test check sum
228   uint32 checkSum = Common::ComputeChecksumFletcher32(ptrStart + sizeof(topHd), hd1.blobSize - sizeof(topHd));
229   if (checkSum != topHd.checkSum)
230     return ErrCode::WrongCheckSum;
231 
232   int64 numBytes = hd1.blobSize;
233   m_extent3D = hd1.extent3D;
234   m_maxError = hd1.maxError3D;
235 
236   if (hd1.numPoints > nPtsInOut)
237     return ErrCode::OutArrayTooSmall;
238 
239   *ppByte = ptr;
240 
241   if (!Decode_CutInSegments(ppByte, m_yDeltaVec))
242     return ErrCode::Failed;
243   if (!Decode_CutInSegments(ppByte, m_numPointsPerRowVec))
244     return ErrCode::Failed;
245   if (!Decode_CutInSegments(ppByte, m_xDeltaVec))
246     return ErrCode::Failed;
247   if (!Decode_CutInSegments(ppByte, m_zVec))
248     return ErrCode::Failed;
249 
250   Point3D p0 = m_extent3D.lower;
251   Point3D p1 = m_extent3D.upper;
252 
253   Point3D cw(2 * m_maxError.x, 2 * m_maxError.y, 2 * m_maxError.z);
254 
255   // reconstruct the points
256   int cnt = 0;
257   int iy = 0;
258   int nRows = (int)m_yDeltaVec.size();
259 
260   for (int i = 0; i < nRows; i++)
261   {
262     iy += m_yDeltaVec[i];
263     int ix = 0;
264     int nPts = m_numPointsPerRowVec[i];
265 
266     for (int j = 0; j < nPts; j++)
267     {
268       ix += m_xDeltaVec[cnt];
269       int iz = m_zVec[cnt];
270 
271       double x = p0.x + ix * cw.x;
272       double y = p0.y + iy * cw.y;
273       double z = p0.z + iz * cw.z;
274 
275       pts[cnt] = Point3D(min(x, p1.x), min(y, p1.y), min(z, p1.z));    // ensure we don't go outside the orig extent
276       cnt++;
277     }
278   }
279 
280   nPtsInOut = hd1.numPoints;
281 
282   int64 nBytesRead = (int64)(*ppByte - ptrStart);
283   if (nBytesRead != hd1.blobSize || nBytesRead > bufferSize)
284     return ErrCode::Failed;
285 
286   return ErrCode::Ok;
287 }
288 
289 // -------------------------------------------------------------------------- ;
290 
Clear()291 void LEPCC::Clear()
292 {
293   m_cell3DVec.clear();
294   m_yDeltaVec.clear();
295   m_numPointsPerRowVec.clear();
296   m_xDeltaVec.clear();
297   m_zVec.clear();
298 }
299 
300 // -------------------------------------------------------------------------- ;
301 // -------------------------------------------------------------------------- ;
302 
HeaderSize()303 int LEPCC::HeaderSize()
304 {
305   return (int)(sizeof(TopHeader) + sizeof(Header1));
306 }
307 
308 // -------------------------------------------------------------------------- ;
309 
Compute3DExtent(uint32 nPts,const Point3D * pts) const310 Extent3D LEPCC::Compute3DExtent(uint32 nPts, const Point3D* pts) const
311 {
312   if (!nPts || !pts)
313     return Extent3D();
314 
315   Extent3D ext;
316   ext.lower = pts[0];
317   ext.upper = ext.lower;
318 
319   const_array<Point3D> pointArr(pts, nPts);    // safe wrapper
320 
321   for (uint32 i = 0; i < nPts; i++)
322   {
323     ext.lower.x = min(ext.lower.x, pointArr[i].x);
324     ext.lower.y = min(ext.lower.y, pointArr[i].y);
325     ext.lower.z = min(ext.lower.z, pointArr[i].z);
326 
327     ext.upper.x = max(ext.upper.x, pointArr[i].x);
328     ext.upper.y = max(ext.upper.y, pointArr[i].y);
329     ext.upper.z = max(ext.upper.z, pointArr[i].z);
330   }
331 
332   return ext;
333 }
334 
335 // -------------------------------------------------------------------------- ;
336 
Quantize(uint32 nPts,const Point3D * pts)337 ErrCode LEPCC::Quantize(uint32 nPts, const Point3D* pts)
338 {
339   if (!nPts || !pts)
340     return ErrCode::WrongParam;
341 
342   Point3D p0 = m_extent3D.lower;
343   Point3D p1 = m_extent3D.upper;
344 
345   Point3D cw(2 * m_maxError.x, 2 * m_maxError.y, 2 * m_maxError.z);
346 
347   int64 nx64 = (int64)((p1.x - p0.x) / cw.x + 0.5) + 1;
348   int64 ny64 = (int64)((p1.y - p0.y) / cw.y + 0.5) + 1;
349   int64 nz64 = (int64)((p1.z - p0.z) / cw.z + 0.5) + 1;
350 
351   if (nx64 <= 0 || ny64 <= 0 || nz64 <= 0 || nx64 > INT_MAX || ny64 > INT_MAX || nz64 > INT_MAX)
352     return ErrCode::QuantizeVirtualRasterTooBig;
353 
354   int nx = (int)nx64;
355   int ny = (int)ny64;
356   int nz = (int)nz64;
357 
358   m_cell3DVec.resize(0);
359   m_cell3DVec.reserve(nPts);
360 
361   const_array<Point3D> pointArr(pts, nPts);    // safe wrapper
362 
363   for (uint32 i = 0; i < nPts; i++)
364   {
365     int ix = (int)((pointArr[i].x - p0.x) / cw.x + 0.5);    // relative to (x0, y0, z0)
366     int iy = (int)((pointArr[i].y - p0.y) / cw.y + 0.5);
367     int iz = (int)((pointArr[i].z - p0.z) / cw.z + 0.5);
368 
369     if (ix >= nx || iy >= ny || iz >= nz)
370       return ErrCode::QuantizeIndexOutOfRange;
371 
372     int64 cellIndex = (int64)iy * (int64)nx + (int64)ix;
373     Cell3D cell3D = { ix, iy, iz, (int)i, cellIndex };
374 
375     m_cell3DVec.push_back(cell3D);
376   }
377 
378   return ErrCode::Ok;
379 }
380 
381 // -------------------------------------------------------------------------- ;
382 
ConvertToDeltaModel()383 ErrCode LEPCC::ConvertToDeltaModel()
384 {
385   if (m_cell3DVec.empty())
386     return ErrCode::Failed;
387 
388   int numPoints = (int)m_cell3DVec.size();
389 
390   // sort the points, top to bottom, left to right
391   std::sort(m_cell3DVec.begin(), m_cell3DVec.end(), MyLessThanOp());
392 
393   m_yDeltaVec.resize(0);
394   m_numPointsPerRowVec.resize(0);
395 
396   uint32 nPtsPerRow = 0;
397   int prevRow = 0;
398   int yCurr = m_cell3DVec[0].y;
399 
400   for (int i = 0; i < numPoints; i++)
401   {
402     int iy = m_cell3DVec[i].y;
403 
404     if (iy == yCurr)
405       nPtsPerRow++;
406     else
407     {
408       m_yDeltaVec.push_back((uint32)(yCurr - prevRow));
409       m_numPointsPerRowVec.push_back(nPtsPerRow);
410 
411       nPtsPerRow = 1;
412       prevRow = yCurr;
413       yCurr = iy;
414     }
415   }
416 
417   m_yDeltaVec.push_back((uint32)(yCurr - prevRow));
418   m_numPointsPerRowVec.push_back(nPtsPerRow);
419 
420   m_xDeltaVec.resize(0);
421   m_xDeltaVec.reserve(numPoints);
422 
423   m_zVec.resize(0);
424   m_zVec.reserve(numPoints);
425 
426   int numOccupiedRows = (int)m_yDeltaVec.size();
427   int iy = 0;
428   int cnt = 0;
429 
430   for (int i = 0; i < numOccupiedRows; i++)
431   {
432     iy += m_yDeltaVec[i];
433     int prevCol = 0;
434 
435     for (int j = 0; j < (int)m_numPointsPerRowVec[i]; j++)
436     {
437       const Cell3D& pt = m_cell3DVec[cnt++];
438       if (pt.y != iy)
439         return ErrCode::Failed;
440 
441       int xDelta = pt.x - prevCol;    // can be 0 if > 1 point per cell
442       m_xDeltaVec.push_back((uint32)xDelta);
443       prevCol = pt.x;
444 
445       m_zVec.push_back((uint32)pt.z);
446     }
447   }
448 
449   return ErrCode::Ok;
450 }
451 
452 // -------------------------------------------------------------------------- ;
453 
ComputeNumBytes_CutInSegments(const vector<uint32> & dataVec,int sectionSize) const454 int LEPCC::ComputeNumBytes_CutInSegments(const vector<uint32>& dataVec, int sectionSize) const
455 {
456   int numSections = (int)((dataVec.size() + (sectionSize - 1)) / sectionSize);
457   int lenLastSection = (int)(dataVec.size() - (numSections - 1) * sectionSize);
458 
459   int nBytes = 0;
460   uint32 totalMax = 0;
461 
462   vector<uint32> sectionMinVec;
463   sectionMinVec.reserve(numSections);
464 
465   BitStuffer2 bitStuffer2;
466 
467   for (int i = 0; i < numSections; i++)
468   {
469     int len = (i < numSections - 1) ? sectionSize : lenLastSection;
470 
471     const uint32* pData = &dataVec[i * sectionSize];
472     uint32 minElem = pData[0], maxElem = minElem;
473 
474     for (int j = 0; j < len; j++)
475     {
476       minElem = min(minElem, pData[j]);
477       maxElem = max(maxElem, pData[j]);
478     }
479 
480     sectionMinVec.push_back(minElem);
481     totalMax = max(totalMax, maxElem);
482 
483     uint32 range = maxElem - minElem;
484     nBytes += bitStuffer2.ComputeNumBytesNeededSimple((uint32)len, range);
485   }
486 
487   uint32 maxOfSectionMins = *max_element(sectionMinVec.begin(), sectionMinVec.end());
488   nBytes += bitStuffer2.ComputeNumBytesNeededSimple((uint32)numSections, maxOfSectionMins);
489 
490   //// compare to undivided and take the better
491   //int nBytesUndivided = (int)bitStuffer2.ComputeNumBytesNeededSimple((uint32)dataVec.size(), totalMax);
492   //return 1 + min(nBytes, nBytesUndivided);
493 
494   return nBytes;
495 }
496 
497 // -------------------------------------------------------------------------- ;
498 
Encode_CutInSegments(Byte ** ppByte,const std::vector<uint32> & dataVec,int sectionSize) const499 bool LEPCC::Encode_CutInSegments(Byte** ppByte, const std::vector<uint32>& dataVec, int sectionSize) const
500 {
501   if (!ppByte || dataVec.empty() || sectionSize <= 0)
502     return false;
503 
504   int numSections = (int)((dataVec.size() + (sectionSize - 1)) / sectionSize);
505   int lenLastSection = (int)(dataVec.size() - (numSections - 1) * sectionSize);
506 
507   // collect all mins, one for each section
508   vector<uint32> sectionMinVec;
509   sectionMinVec.reserve(numSections);
510   vector<uint32>::const_iterator it, it0 = dataVec.begin();
511 
512   for (int i = 0; i < numSections; i++)
513   {
514     int len = (i < numSections - 1) ? sectionSize : lenLastSection;
515     it = it0 + i * sectionSize;
516 
517     uint32 minElem = *min_element(it, it + len);
518     sectionMinVec.push_back(minElem);
519   }
520 
521   // write out these mins bit stuffed
522   BitStuffer2 bitStuffer2;
523   if (!bitStuffer2.EncodeSimple(ppByte, sectionMinVec))
524     return false;
525 
526   // now to the sections
527   vector<uint32> zeroBasedDataVec(sectionSize, 0);
528 
529   for (int i = 0; i < numSections; i++)
530   {
531     int len = (i < numSections - 1) ? sectionSize : lenLastSection;
532     const uint32* pData = &dataVec[i * sectionSize];
533 
534     zeroBasedDataVec.resize(len);
535     uint32 minElem = sectionMinVec[i];
536 
537     for (int j = 0; j < len; j++)
538       zeroBasedDataVec[j] = pData[j] - minElem;
539 
540     if (!bitStuffer2.EncodeSimple(ppByte, zeroBasedDataVec))
541       return false;
542   }
543 
544   return true;
545 }
546 
547 // -------------------------------------------------------------------------- ;
548 
Decode_CutInSegments(const Byte ** ppByte,std::vector<uint32> & dataVec) const549 bool LEPCC::Decode_CutInSegments(const Byte** ppByte, std::vector<uint32>& dataVec) const
550 {
551   if (!ppByte || !(*ppByte))
552     return false;
553 
554   dataVec.resize(0);
555 
556   // decode the section mins
557   vector<uint32> sectionMinVec, zeroBasedDataVec;
558   BitStuffer2 bitStuffer2;
559   if (!bitStuffer2.Decode(ppByte, sectionMinVec, 3))
560     return false;
561 
562   int numSections = (int)sectionMinVec.size();
563 
564   dataVec.reserve(numSections * m_sectionSize);
565 
566   for (int i = 0; i < numSections; i++)
567   {
568     if (!bitStuffer2.Decode(ppByte, zeroBasedDataVec, 3))
569       return false;
570 
571     int len = (int)zeroBasedDataVec.size();
572     uint32 minElem = sectionMinVec[i];
573 
574     for (int j = 0; j < len; j++)
575       dataVec.push_back(zeroBasedDataVec[j] + minElem);
576   }
577 
578   return true;
579 }
580 
581 // -------------------------------------------------------------------------- ;
582 
ReadHeaders(const Byte * pByte,int64 bufferSize,TopHeader & topHd,Header1 & hd1)583 ErrCode LEPCC::ReadHeaders(const Byte* pByte, int64 bufferSize, TopHeader& topHd, Header1& hd1)
584 {
585   if (!pByte)
586     return ErrCode::WrongParam;
587 
588   if (bufferSize <= HeaderSize())
589     return ErrCode::BufferTooSmall;
590 
591   TopHeader refHd;
592   if (0 != memcmp(pByte, refHd.fileKey, refHd.FileKeyLength()))    // file key
593     return ErrCode::NotLepcc;
594 
595   memcpy(&topHd, pByte, sizeof(topHd));
596   pByte += sizeof(topHd);
597 
598   if (topHd.version > kCurrVersion)    // this reader is outdated
599     return ErrCode::WrongVersion;
600 
601   memcpy(&hd1, pByte, sizeof(hd1));
602   return ErrCode::Ok;
603 }
604 
605 // -------------------------------------------------------------------------- ;
606 
607