summaryrefslogtreecommitdiff
path: root/vendor/github.com/golang/geo/s2/pointcompression.go
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-08-02 11:46:41 +0000
committerLibravatar GitHub <noreply@github.com>2024-08-02 12:46:41 +0100
commit94e87610c4ce9bbb1c614a61bab29c1422fed11b (patch)
tree2e06b8ce64212140e796f6077ba841b6cc678501 /vendor/github.com/golang/geo/s2/pointcompression.go
parent[feature] Allow import of following and blocks via CSV (#3150) (diff)
downloadgotosocial-94e87610c4ce9bbb1c614a61bab29c1422fed11b.tar.xz
[chore] add back exif-terminator and use only for jpeg,png,webp (#3161)
* add back exif-terminator and use only for jpeg,png,webp * fix arguments passed to terminateExif() * pull in latest exif-terminator * fix test * update processed img --------- Co-authored-by: tobi <tobi.smethurst@protonmail.com>
Diffstat (limited to 'vendor/github.com/golang/geo/s2/pointcompression.go')
-rw-r--r--vendor/github.com/golang/geo/s2/pointcompression.go319
1 files changed, 319 insertions, 0 deletions
diff --git a/vendor/github.com/golang/geo/s2/pointcompression.go b/vendor/github.com/golang/geo/s2/pointcompression.go
new file mode 100644
index 000000000..018381799
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/pointcompression.go
@@ -0,0 +1,319 @@
+// Copyright 2017 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package s2
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/golang/geo/r3"
+)
+
+// maxEncodedVertices is the maximum number of vertices, in a row, to be encoded or decoded.
+// On decode, this defends against malicious encodings that try and have us exceed RAM.
+const maxEncodedVertices = 50000000
+
+// xyzFaceSiTi represents the The XYZ and face,si,ti coordinates of a Point
+// and, if this point is equal to the center of a Cell, the level of this cell
+// (-1 otherwise). This is used for Loops and Polygons to store data in a more
+// compressed format.
+type xyzFaceSiTi struct {
+ xyz Point
+ face int
+ si, ti uint32
+ level int
+}
+
+const derivativeEncodingOrder = 2
+
+func appendFace(faces []faceRun, face int) []faceRun {
+ if len(faces) == 0 || faces[len(faces)-1].face != face {
+ return append(faces, faceRun{face, 1})
+ }
+ faces[len(faces)-1].count++
+ return faces
+}
+
+// encodePointsCompressed uses an optimized compressed format to encode the given values.
+func encodePointsCompressed(e *encoder, vertices []xyzFaceSiTi, level int) {
+ var faces []faceRun
+ for _, v := range vertices {
+ faces = appendFace(faces, v.face)
+ }
+ encodeFaces(e, faces)
+
+ type piQi struct {
+ pi, qi uint32
+ }
+ verticesPiQi := make([]piQi, len(vertices))
+ for i, v := range vertices {
+ verticesPiQi[i] = piQi{siTitoPiQi(v.si, level), siTitoPiQi(v.ti, level)}
+ }
+ piCoder, qiCoder := newNthDerivativeCoder(derivativeEncodingOrder), newNthDerivativeCoder(derivativeEncodingOrder)
+ for i, v := range verticesPiQi {
+ f := encodePointCompressed
+ if i == 0 {
+ // The first point will be just the (pi, qi) coordinates
+ // of the Point. NthDerivativeCoder will not save anything
+ // in that case, so we encode in fixed format rather than varint
+ // to avoid the varint overhead.
+ f = encodeFirstPointFixedLength
+ }
+ f(e, v.pi, v.qi, level, piCoder, qiCoder)
+ }
+
+ var offCenter []int
+ for i, v := range vertices {
+ if v.level != level {
+ offCenter = append(offCenter, i)
+ }
+ }
+ e.writeUvarint(uint64(len(offCenter)))
+ for _, idx := range offCenter {
+ e.writeUvarint(uint64(idx))
+ e.writeFloat64(vertices[idx].xyz.X)
+ e.writeFloat64(vertices[idx].xyz.Y)
+ e.writeFloat64(vertices[idx].xyz.Z)
+ }
+}
+
+func encodeFirstPointFixedLength(e *encoder, pi, qi uint32, level int, piCoder, qiCoder *nthDerivativeCoder) {
+ // Do not ZigZagEncode the first point, since it cannot be negative.
+ codedPi, codedQi := piCoder.encode(int32(pi)), qiCoder.encode(int32(qi))
+ // Interleave to reduce overhead from two partial bytes to one.
+ interleaved := interleaveUint32(uint32(codedPi), uint32(codedQi))
+
+ // Write as little endian.
+ bytesRequired := (level + 7) / 8 * 2
+ for i := 0; i < bytesRequired; i++ {
+ e.writeUint8(uint8(interleaved))
+ interleaved >>= 8
+ }
+}
+
+// encodePointCompressed encodes points into e.
+// Given a sequence of Points assumed to be the center of level-k cells,
+// compresses it into a stream using the following method:
+// - decompose the points into (face, si, ti) tuples.
+// - run-length encode the faces, combining face number and count into a
+// varint32. See the faceRun struct.
+// - right shift the (si, ti) to remove the part that's constant for all cells
+// of level-k. The result is called the (pi, qi) space.
+// - 2nd derivative encode the pi and qi sequences (linear prediction)
+// - zig-zag encode all derivative values but the first, which cannot be
+// negative
+// - interleave the zig-zag encoded values
+// - encode the first interleaved value in a fixed length encoding
+// (varint would make this value larger)
+// - encode the remaining interleaved values as varint64s, as the
+// derivative encoding should make the values small.
+// In addition, provides a lossless method to compress a sequence of points even
+// if some points are not the center of level-k cells. These points are stored
+// exactly, using 3 double precision values, after the above encoded string,
+// together with their index in the sequence (this leads to some redundancy - it
+// is expected that only a small fraction of the points are not cell centers).
+//
+// To encode leaf cells, this requires 8 bytes for the first vertex plus
+// an average of 3.8 bytes for each additional vertex, when computed on
+// Google's geographic repository.
+func encodePointCompressed(e *encoder, pi, qi uint32, level int, piCoder, qiCoder *nthDerivativeCoder) {
+ // ZigZagEncode, as varint requires the maximum number of bytes for
+ // negative numbers.
+ zzPi := zigzagEncode(piCoder.encode(int32(pi)))
+ zzQi := zigzagEncode(qiCoder.encode(int32(qi)))
+ // Interleave to reduce overhead from two partial bytes to one.
+ interleaved := interleaveUint32(zzPi, zzQi)
+ e.writeUvarint(interleaved)
+}
+
+type faceRun struct {
+ face, count int
+}
+
+func decodeFaceRun(d *decoder) faceRun {
+ faceAndCount := d.readUvarint()
+ ret := faceRun{
+ face: int(faceAndCount % numFaces),
+ count: int(faceAndCount / numFaces),
+ }
+ if ret.count <= 0 && d.err == nil {
+ d.err = errors.New("non-positive count for face run")
+ }
+ return ret
+}
+
+func decodeFaces(numVertices int, d *decoder) []faceRun {
+ var frs []faceRun
+ for nparsed := 0; nparsed < numVertices; {
+ fr := decodeFaceRun(d)
+ if d.err != nil {
+ return nil
+ }
+ frs = append(frs, fr)
+ nparsed += fr.count
+ }
+ return frs
+}
+
+// encodeFaceRun encodes each faceRun as a varint64 with value numFaces * count + face.
+func encodeFaceRun(e *encoder, fr faceRun) {
+ // It isn't necessary to encode the number of faces left for the last run,
+ // but since this would only help if there were more than 21 faces, it will
+ // be a small overall savings, much smaller than the bound encoding.
+ coded := numFaces*uint64(fr.count) + uint64(fr.face)
+ e.writeUvarint(coded)
+}
+
+func encodeFaces(e *encoder, frs []faceRun) {
+ for _, fr := range frs {
+ encodeFaceRun(e, fr)
+ }
+}
+
+type facesIterator struct {
+ faces []faceRun
+ // How often have we yet shown the current face?
+ numCurrentFaceShown int
+ curFace int
+}
+
+func (fi *facesIterator) next() (ok bool) {
+ if len(fi.faces) == 0 {
+ return false
+ }
+ fi.curFace = fi.faces[0].face
+ fi.numCurrentFaceShown++
+
+ // Advance fs if needed.
+ if fi.faces[0].count <= fi.numCurrentFaceShown {
+ fi.faces = fi.faces[1:]
+ fi.numCurrentFaceShown = 0
+ }
+
+ return true
+}
+
+func decodePointsCompressed(d *decoder, level int, target []Point) {
+ faces := decodeFaces(len(target), d)
+
+ piCoder := newNthDerivativeCoder(derivativeEncodingOrder)
+ qiCoder := newNthDerivativeCoder(derivativeEncodingOrder)
+
+ iter := facesIterator{faces: faces}
+ for i := range target {
+ decodeFn := decodePointCompressed
+ if i == 0 {
+ decodeFn = decodeFirstPointFixedLength
+ }
+ pi, qi := decodeFn(d, level, piCoder, qiCoder)
+ if ok := iter.next(); !ok && d.err == nil {
+ d.err = fmt.Errorf("ran out of faces at target %d", i)
+ return
+ }
+ target[i] = Point{facePiQitoXYZ(iter.curFace, pi, qi, level)}
+ }
+
+ numOffCenter := int(d.readUvarint())
+ if d.err != nil {
+ return
+ }
+ if numOffCenter > len(target) {
+ d.err = fmt.Errorf("numOffCenter = %d, should be at most len(target) = %d", numOffCenter, len(target))
+ return
+ }
+ for i := 0; i < numOffCenter; i++ {
+ idx := int(d.readUvarint())
+ if d.err != nil {
+ return
+ }
+ if idx >= len(target) {
+ d.err = fmt.Errorf("off center index = %d, should be < len(target) = %d", idx, len(target))
+ return
+ }
+ target[idx].X = d.readFloat64()
+ target[idx].Y = d.readFloat64()
+ target[idx].Z = d.readFloat64()
+ }
+}
+
+func decodeFirstPointFixedLength(d *decoder, level int, piCoder, qiCoder *nthDerivativeCoder) (pi, qi uint32) {
+ bytesToRead := (level + 7) / 8 * 2
+ var interleaved uint64
+ for i := 0; i < bytesToRead; i++ {
+ rr := d.readUint8()
+ interleaved |= (uint64(rr) << uint(i*8))
+ }
+
+ piCoded, qiCoded := deinterleaveUint32(interleaved)
+
+ return uint32(piCoder.decode(int32(piCoded))), uint32(qiCoder.decode(int32(qiCoded)))
+}
+
+func zigzagEncode(x int32) uint32 {
+ return (uint32(x) << 1) ^ uint32(x>>31)
+}
+
+func zigzagDecode(x uint32) int32 {
+ return int32((x >> 1) ^ uint32((int32(x&1)<<31)>>31))
+}
+
+func decodePointCompressed(d *decoder, level int, piCoder, qiCoder *nthDerivativeCoder) (pi, qi uint32) {
+ interleavedZigZagEncodedDerivPiQi := d.readUvarint()
+ piZigzag, qiZigzag := deinterleaveUint32(interleavedZigZagEncodedDerivPiQi)
+ return uint32(piCoder.decode(zigzagDecode(piZigzag))), uint32(qiCoder.decode(zigzagDecode(qiZigzag)))
+}
+
+// We introduce a new coordinate system (pi, qi), which is (si, ti)
+// with the bits that are constant for cells of that level shifted
+// off to the right.
+// si = round(s * 2^31)
+// pi = si >> (31 - level)
+// = floor(s * 2^level)
+// If the point has been snapped to the level, the bits that are
+// shifted off will be a 1 in the msb, then 0s after that, so the
+// fractional part discarded by the cast is (close to) 0.5.
+
+// stToPiQi returns the value transformed to the PiQi coordinate space.
+func stToPiQi(s float64, level uint) uint32 {
+ return uint32(s * float64(int(1)<<level))
+}
+
+// siTiToPiQi returns the value transformed into the PiQi coordinate spade.
+// encodeFirstPointFixedLength encodes the return value using level bits,
+// so we clamp si to the range [0, 2**level - 1] before trying to encode
+// it. This is okay because if si == maxSiTi, then it is not a cell center
+// anyway and will be encoded separately as an off-center point.
+func siTitoPiQi(siTi uint32, level int) uint32 {
+ s := uint(siTi)
+ const max = maxSiTi - 1
+ if s > max {
+ s = max
+ }
+
+ return uint32(s >> (maxLevel + 1 - uint(level)))
+}
+
+// piQiToST returns the value transformed to ST space.
+func piQiToST(pi uint32, level int) float64 {
+ // We want to recover the position at the center of the cell. If the point
+ // was snapped to the center of the cell, then math.Modf(s * 2^level) == 0.5.
+ // Inverting STtoPiQi gives:
+ // s = (pi + 0.5) / 2^level.
+ return (float64(pi) + 0.5) / float64(int(1)<<uint(level))
+}
+
+func facePiQitoXYZ(face int, pi, qi uint32, level int) r3.Vector {
+ return faceUVToXYZ(face, stToUV(piQiToST(pi, level)), stToUV(piQiToST(qi, level))).Normalize()
+}