diff options
author | 2024-08-02 11:46:41 +0000 | |
---|---|---|
committer | 2024-08-02 12:46:41 +0100 | |
commit | 94e87610c4ce9bbb1c614a61bab29c1422fed11b (patch) | |
tree | 2e06b8ce64212140e796f6077ba841b6cc678501 /vendor/github.com/golang/geo/s2/pointcompression.go | |
parent | [feature] Allow import of following and blocks via CSV (#3150) (diff) | |
download | gotosocial-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.go | 319 |
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() +} |