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/button.h>
19 namespace teeui {
20 
makeSquareAtOffset(const PxPoint & offset,const pxs & sideLength)21 ConvexObject<4> makeSquareAtOffset(const PxPoint& offset, const pxs& sideLength) {
22     return ConvexObject<4>({offset, offset + PxPoint{sideLength, .0},
23                             offset + PxPoint{sideLength, sideLength},
24                             offset + PxPoint{.0, sideLength}});
25 }
26 
draw(const PixelDrawer & drawPixel,const Box<pxs> & bounds,const ConvexObjectInfo * coBegin,const ConvexObjectInfo * coEnd)27 Error ButtonImpl::draw(const PixelDrawer& drawPixel, const Box<pxs>& bounds,
28                        const ConvexObjectInfo* coBegin, const ConvexObjectInfo* coEnd) {
29 
30     using intpxs = Coordinate<px, int64_t>;
31     Box<intpxs> intBounds(bounds);
32 
33     auto drawPixelBoundsEnforced = [&](uint32_t x, uint32_t y, Color color) -> Error {
34         if (!intBounds.contains(Point<intpxs>(x, y))) {
35             TEEUI_LOG << "Bounds: " << bounds << " Pixel: " << Point<pxs>(x, y) << ENDL;
36             return Error::OutOfBoundsDrawing;
37         }
38         return drawPixel(x, y, color);
39     };
40 
41     auto drawBox = [&](const Box<intpxs>& box, Color c) -> Error {
42         for (int y = 0; y < box.h().count(); ++y) {
43             for (int x = 0; x < box.w().count(); ++x) {
44                 if (auto error = drawPixel(box.x().count() + x, box.y().count() + y, c)) {
45                     return error;
46                 }
47             }
48         }
49         return Error::OK;
50     };
51 
52 #ifdef DRAW_DEBUG_MARKERS
53     auto drawDebugBox = [&](const Box<pxs>& box, Color c) {
54         drawBox(box, (c & 0xffffff) | 0x40000000);
55     };
56 
57     drawDebugBox(intBounds, 0xff);
58 #endif
59 
60     intpxs intRadius = radius_.count();
61 
62     TEEUI_LOG << intBounds << ENDL;
63 
64     auto drawCorner = [&, this](intpxs right, intpxs bottom) -> Error {
65         Box<intpxs> cBounds(intBounds.x(), intBounds.y(), intRadius, intRadius);
66         cBounds.translateSelf(Point<intpxs>(right * (intBounds.w() - intRadius),
67                                             bottom * (intBounds.h() - intRadius)));
68         auto center = Point<pxs>((intpxs(1) - right) * intRadius, (intpxs(1) - bottom) * intRadius);
69         center += cBounds.topLeft();
70         center -= Point<pxs>(.5, .5);
71         TEEUI_LOG << "Radius: " << intRadius << " cBounds: " << cBounds << " center: " << center
72                   << ENDL;
73         for (int y = 0; y < cBounds.h().count(); ++y) {
74             for (int x = 0; x < cBounds.w().count(); ++x) {
75                 auto pos = Point<pxs>(cBounds.x().count() + x, cBounds.y().count() + y);
76                 auto color = drawCirclePoint(center, intRadius, pos, color_);
77                 if (auto error = drawPixelBoundsEnforced(pos.x().count(), pos.y().count(), color)) {
78                     return error;
79                 }
80             }
81         }
82         return Error::OK;
83     };
84 
85     if (roundTopLeft_) {
86         if (auto error = drawCorner(0, 0)) return error;
87     } else {
88         if (auto error = drawBox(
89                 Box<intpxs>(0, 0, intRadius, intRadius).translate(intBounds.topLeft()), color_)) {
90             return error;
91         }
92     }
93 
94     if (roundTopRight_) {
95         if (auto error = drawCorner(1, 0)) return error;
96     } else {
97         if (auto error = drawBox(Box<intpxs>(intBounds.w() - intRadius, 0, intRadius, intRadius)
98                                      .translate(intBounds.topLeft()),
99                                  color_)) {
100             return error;
101         }
102     }
103 
104     if (roundBottomLeft_) {
105         if (auto error = drawCorner(0, 1)) return error;
106     } else {
107         if (auto error = drawBox(Box<intpxs>(0, intBounds.h() - intRadius, intRadius, intRadius)
108                                      .translate(intBounds.topLeft()),
109                                  color_)) {
110             return error;
111         }
112     }
113 
114     if (roundBottomRight_) {
115         if (auto error = drawCorner(1, 1)) return error;
116     } else {
117         if (auto error = drawBox(Box<intpxs>(intBounds.w() - intRadius, intBounds.h() - intRadius,
118                                              intRadius, intRadius)
119                                      .translate(intBounds.topLeft()),
120                                  color_)) {
121             return error;
122         }
123     }
124 
125     auto centerbox = Box<intpxs>(intRadius, intRadius, intBounds.w() - intRadius - intRadius,
126                                  intBounds.h() - intRadius - intRadius)
127                          .translate(intBounds.topLeft());
128 
129     if (auto error = drawBox(centerbox, color_)) return error;
130 
131     if (auto error =
132             drawBox(Box<intpxs>(0, intRadius, intRadius, intBounds.h() - intRadius - intRadius)
133                         .translate(intBounds.topLeft()),
134                     color_)) {
135         return error;
136     }
137     if (auto error =
138             drawBox(Box<intpxs>(intRadius, 0, intBounds.w() - intRadius - intRadius, intRadius)
139                         .translate(intBounds.topLeft()),
140                     color_)) {
141         return error;
142     }
143     if (auto error = drawBox(Box<intpxs>(intBounds.w() - intRadius, intRadius, intRadius,
144                                          intBounds.h() - intRadius - intRadius)
145                                  .translate(intBounds.topLeft()),
146                              color_)) {
147         return error;
148     }
149     if (auto error = drawBox(Box<intpxs>(intRadius, intBounds.h() - intRadius,
150                                          intBounds.w() - intRadius - intRadius, intRadius)
151                                  .translate(intBounds.topLeft()),
152                              color_)) {
153         return error;
154     }
155 
156     bool hasCOs = coBegin != coEnd;
157     if (hasCOs) {
158         Box<pxs> coBBox = Box<pxs>::boundingBox(coBegin->begin, coBegin->end);
159         for (const auto& co : makeRange(coBegin + 1, coEnd)) {
160             coBBox = coBBox.merge(co.begin, co.end);
161         }
162 
163         auto start = PxPoint{bounds.w() - coBBox.w(), bounds.h() - coBBox.h()} / pxs(2.0);
164         start += bounds.topLeft();
165 
166         Box<intpxs> intBBox(start.x().floor(), start.y().floor(), 0, 0);
167         intBBox = intBBox.merge(
168             Point<intpxs>{(start.x() + coBBox.w()).ceil(), (start.y() + coBBox.h()).ceil()});
169 
170         TEEUI_LOG << "coBBox: " << coBBox << ENDL;
171 
172         TEEUI_LOG << "intBBox: " << intBBox << ENDL;
173         for (int64_t y = 0; y < intBBox.h().count(); ++y) {
174             for (int64_t x = 0; x < intBBox.w().count(); ++x) {
175                 PxPoint offset = coBBox.topLeft() + PxPoint{x, y};
176                 // The pixel is a square of width and height 1.0
177                 auto thePixel = makeSquareAtOffset(offset, 1.0);
178                 TEEUI_LOG << thePixel << ENDL;
179                 pxs areaCovered = 0.0;
180                 for (const auto& co : makeRange(coBegin, coEnd)) {
181                     auto coveredRegion = thePixel.intersect<10>(co.begin, co.end);
182                     if (coveredRegion) areaCovered += coveredRegion->area();
183                     TEEUI_LOG << " region: " << (bool)coveredRegion << " area: " << areaCovered;
184                 }
185                 TEEUI_LOG << ENDL;
186                 if (areaCovered > 1.0) areaCovered = 1.0;
187                 TEEUI_LOG << "x: " << x << " y: " << y << " area: " << areaCovered << ENDL;
188                 uint32_t intensity = 0xff * areaCovered.count();
189                 if (auto error =
190                         drawPixelBoundsEnforced(intBBox.x().count() + x, intBBox.y().count() + y,
191                                                 intensity << 24 | (0xffffff & convexObjectColor_)))
192                     return error;
193             }
194         }
195     }
196 
197     return Error::OK;
198 }
199 
200 }  // namespace teeui
201