1 /*
2  *
3  * Copyright 2019, The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include <teeui/utils.h>
19 
20 namespace teeui {
21 
operator ==(const ByteBufferProxy & lhs,const ByteBufferProxy & rhs)22 bool operator==(const ByteBufferProxy& lhs, const ByteBufferProxy& rhs) {
23     if (lhs.size() == rhs.size()) {
24         auto lhsi = lhs.begin();
25         auto rhsi = rhs.begin();
26         while (lhsi != lhs.end()) {
27             if (*lhsi++ != *rhsi++) return false;
28         }
29     }
30     return true;
31 }
32 
33 constexpr const DefaultNumericType kEpsilon = 0.000001;
34 constexpr const DefaultNumericType kHalfSqrt2 = 0.70710678118;
35 
pixelLineIntersect(Point<pxs> line,pxs dist,Color c)36 Color pixelLineIntersect(Point<pxs> line, pxs dist, Color c) {
37     TEEUI_LOG << "Line: " << line << " Dist: " << dist;
38     bool more_than_half = dist < 0.0;
39     TEEUI_LOG << " " << more_than_half;
40 
41     Color intensity = 0;
42     if (dist.abs() < kEpsilon) {
43         intensity = 0x80;
44         TEEUI_LOG << " half covered";
45     } else if (dist.abs() >= kHalfSqrt2) {
46         intensity = more_than_half ? 0xff : 0;
47         TEEUI_LOG << (more_than_half ? " fully covered" : " not covered");
48     } else {
49         auto dist_vec = line * dist;
50         TEEUI_LOG << " vec " << dist_vec;
51         dist_vec = Point<pxs>(dist_vec.x().abs(), dist_vec.y().abs());
52         TEEUI_LOG << " vec " << dist_vec;
53         if (dist_vec.x() < dist_vec.y()) {
54             dist_vec = Point<pxs>(dist_vec.y(), dist_vec.x());
55         }
56         auto a0 = dist_vec.x();
57         auto a1 = -dist_vec.y();
58         pxs area(.0);
59         if (a1 > -kEpsilon) {
60             TEEUI_LOG << " X";
61             area = a0;
62         } else {
63             Point<pxs> Q(a1 * (a1 + pxs(.5)) / a0 + a0, pxs(-.5));
64             if (Q.x() >= pxs(.5)) {
65                 // line does not intersect our pixel.
66                 intensity = more_than_half ? 0xff : 0;
67                 TEEUI_LOG << (more_than_half ? " fully covered (2)" : " not covered(2)");
68             } else {
69                 TEEUI_LOG << " partially covered";
70                 Point<pxs> P(pxs(.5), a1 - a0 * (pxs(.5) - a0) / a1);
71                 TEEUI_LOG << " P: " << P << " Q: " << Q;
72                 Point<pxs> R = P - Q;
73                 TEEUI_LOG << " R: " << R;
74                 area = R.x() * R.y() * pxs(.5);
75                 if (R.y() > 1.0) {
76                     auto r = R.y() - pxs(1.0);
77                     area -= r * R.x() * ((r) / R.y()) * pxs(.5);
78                 }
79             }
80         }
81         if (more_than_half) {
82             area = pxs(1.0) - area;
83         }
84         TEEUI_LOG << " area: " << area;
85         intensity = area.count() * 0xff;
86     }
87     TEEUI_LOG << ENDL;
88     return intensity << 24 | (c & 0xffffff);
89 }
90 
drawLinePoint(Point<pxs> a,Point<pxs> b,Point<pxs> px_origin,Color c,pxs width)91 Color drawLinePoint(Point<pxs> a, Point<pxs> b, Point<pxs> px_origin, Color c, pxs width) {
92     auto line = a - b;
93     auto len = line.length();
94     auto l = line / len;
95     auto seg = l * (px_origin - b);
96     auto dist = 0_px;
97     if (seg < 0_px) {
98         //        line = px_origin - b;
99         //        dist = line.length();
100         //        line /= dist;
101         //        dist -= width;
102         return 0;
103     } else if (seg > len) {
104         //        line = px_origin - a;
105         //        dist = line.length();
106         //        line /= dist;
107         //        dist -= width;
108         return 0;
109     } else {
110         line = Point<pxs>(-line.y(), line.x()) / len;
111         dist = (line * (px_origin - a)).abs() - width + .5_px;
112     }
113 
114     return pixelLineIntersect(line, dist, c);
115 }
116 
drawCirclePoint(Point<pxs> center,pxs r,Point<pxs> px_origin,Color c)117 Color drawCirclePoint(Point<pxs> center, pxs r, Point<pxs> px_origin, Color c) {
118     auto line = px_origin - center;
119     auto dist = line.length() - r;
120 
121     return pixelLineIntersect(line.unit(), dist, c);
122 }
123 
124 /*
125  * Computes the intersection of the lines given by ax + b and cy + d.
126  * The result may be empty if there is no solution.
127  */
intersect(const PxVec & a,const PxPoint & b,const PxVec & c,const PxPoint & d)128 optional<PxPoint> intersect(const PxVec& a, const PxPoint& b, const PxVec& c, const PxPoint& d) {
129     pxs y = 0.0;
130     PxVec g = b - d;
131     if (a.x().abs() < kEpsilon) {
132         if (c.x().abs() < kEpsilon || a.y() < kEpsilon) {
133             return {};
134         } else {
135             y = g.x() / c.x();
136         }
137     } else {
138         pxs f = a.y() / a.x();
139         pxs h = f * c.x() - c.y();
140         if (h.abs() < kEpsilon) {
141             return {};
142         } else {
143             y = (f * g.x() - g.y()) / h;
144         }
145     }
146     return c * y + d;
147 }
148 
149 namespace bits {
150 
rotate90(const VectorType & in)151 template <typename VectorType> inline VectorType rotate90(const VectorType& in) {
152     return {-in.y(), in.x()};
153 }
154 
intersect(const PxPoint * oBegin,const PxPoint * oEnd,const PxPoint & lineA,const PxPoint & lineB,PxPoint * nBegin,PxPoint * nEnd)155 ssize_t intersect(const PxPoint* oBegin, const PxPoint* oEnd, const PxPoint& lineA,
156                   const PxPoint& lineB, PxPoint* nBegin, PxPoint* nEnd) {
157 
158     auto line = lineB - lineA;
159     if (oBegin == oEnd) return kIntersectEmpty;
160     auto b = oBegin;
161     auto a = b;
162     ++b;
163     auto nCur = nBegin;
164     unsigned int intersections_found = 0;
165     // inside indicates if we are inside the new convex object.
166     // If we happen to transition from inside to inside, we know that we where wrong and
167     // reset the output object. But if we were on the inside we have the full new object once
168     // we have traveled around the old object once.
169     bool inside = true;
170 
171     auto processSegment = [&](const PxVec& a, const PxVec& b) -> bool {
172         auto segment = b - a;
173         if (auto p = intersect(line, lineA, segment, a)) {
174             auto seg_len = segment.length();
175             auto aDist = (segment * (*p - a)) / seg_len;
176             if (aDist >= 0.0 && aDist < segment.length()) {
177                 ++intersections_found;
178                 // The line vector points toward the negative half plain of segment.
179                 // This means we are entering the resulting convex object.
180                 bool enter = rotate90(segment) * line < 0;
181                 if (enter && inside) {
182                     // if we are entering the object, but we thought we are already inside, we
183                     // forget all previous points, because we were wrong.
184                     if (intersections_found < 2) {
185                         // Only do after we found the first intersection. Other cases are likely
186                         // duplications due to rounding errors.
187                         nCur = nBegin;
188                     }
189                 }
190                 TEEUI_LOG << *p << " inside: " << inside << " enter: " << enter << ENDL;
191                 inside = enter;
192                 // an intersection of the new line and a segment is always part of the resulting
193                 // object.
194                 if (nCur == nEnd) {
195                     TEEUI_LOG << "error out of space 1" << ENDL;
196                     return false;
197                 }
198                 if (aDist > 0.0 || enter) {
199                     TEEUI_LOG << "add P: " << *p << ENDL;
200                     *nCur++ = *p;
201                 }
202             }
203         }
204         if (nCur == nEnd) {
205             TEEUI_LOG << "error out of space 2" << ENDL;
206             return false;
207         }
208         if (inside) {
209             TEEUI_LOG << "add B: " << b << ENDL;
210             *nCur++ = b;
211         }
212         return true;
213     };
214 
215     while (b != oEnd) {
216         if (!processSegment(*a, *b)) return kIntersectEmpty;
217         a = b++;
218     }
219     if (!processSegment(*a, *oBegin)) return kIntersectEmpty;
220 
221     TEEUI_LOG << "intersections found: " << intersections_found << ENDL;
222     // handle tangents and disjunct case
223     if (intersections_found < 2) {
224         // find a point that is not on the line
225         // if there is at most one intersection, all points of the object are on the same half
226         // plane or on the line.
227         a = oBegin;
228         pxs d;
229         do {
230             d = rotate90(line) * (*a - lineA);
231             if (++a == oEnd) {
232                 TEEUI_LOG << "error no point with distance > 0" << ENDL;
233                 return kIntersectEmpty;
234             }
235         } while (d == 0.0);
236 
237         if (d > 0) {
238             // positive half plane
239             return kIntersectAllPositive;
240         } else {
241             // negative half plane
242             TEEUI_LOG << "egative half plane" << ENDL;
243             return kIntersectEmpty;
244         }
245     }
246 
247     return nCur - nBegin;
248 }
249 
area(const PxPoint * begin,const PxPoint * end)250 pxs area(const PxPoint* begin, const PxPoint* end) {
251     if (end - begin < 3) return 0.0;
252     auto o = *begin;
253     auto a = begin;
254     ++a;
255     auto b = a;
256     ++b;
257     pxs result = 0;
258     while (b != end) {
259         auto x = *a - o;
260         auto y = *b - o;
261         result += x.x() * y.y() - x.y() * y.x();
262         a = b;
263         ++b;
264     }
265     result /= 2;
266     return result;
267 }
268 
269 }  // namespace bits
270 
271 }  // namespace teeui
272