1 /*
2 * Copyright (c) 2021-2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "clip_utils.h"
17
18 #include <cmath>
19 #include "draw_utils.h"
20 #include "gfx_utils/graphic_log.h"
21
22 namespace OHOS {
23 namespace {
24 constexpr float EPS = 0.01f;
25 };
26
27 #define I_PART(X) ((int)(X))
28 #define F_PART(X) (((float)(X)) - (float)I_PART(X))
29 #define RF_PART(X) (1.0 - F_PART(X))
30 #define SWAP_FLOAT(a, b) \
31 do { \
32 float tmp = a; \
33 a = b; \
34 b = tmp; \
35 } while (0)
36
37 /*
38 * Note that the square of the distance from point B to the straight line AD is dB,
39 * and the square of the distance from point C to the straight line AD is dC.
40 * When (dB <= EPS && dC <= EPS), the line AD is considered to fit the spline.
41 */
CheckoutSplineError(const Spline & spline) const42 bool ClipPath::CheckoutSplineError(const Spline& spline) const
43 {
44 float dx = spline.d.x - spline.a.x;
45 float dy = spline.d.y - spline.a.y;
46 float c = dx * spline.a.y - dy * spline.a.x;
47 float deltB = dy * spline.b.x - dx * spline.b.y + c;
48 deltB = deltB * deltB;
49 float deltC = dy * spline.c.x - dx * spline.c.y + c;
50 deltC = deltC * deltC;
51 float delt = EPS * (dx * dx + dy * dy);
52 if (deltB <= delt && deltC <= delt) {
53 return true;
54 }
55 return false;
56 }
57
MidPointOfLine(const PointF & a,const PointF & b,PointF & mid) const58 void ClipPath::MidPointOfLine(const PointF& a, const PointF& b, PointF& mid) const
59 {
60 mid.x = a.x + (b.x - a.x) / 2; // 2: half
61 mid.y = a.y + (b.y - a.y) / 2; // 2: half
62 }
63
SplitSpline(Spline & s1,Spline & s2) const64 void ClipPath::SplitSpline(Spline& s1, Spline& s2) const
65 {
66 PointF ab, bc, cd;
67 PointF abbc, bccd;
68 PointF e;
69
70 MidPointOfLine(s1.a, s1.b, ab);
71 MidPointOfLine(s1.b, s1.c, bc);
72 MidPointOfLine(s1.c, s1.d, cd);
73 MidPointOfLine(ab, bc, abbc);
74 MidPointOfLine(bc, cd, bccd);
75 MidPointOfLine(abbc, bccd, e);
76
77 s2.a = e;
78 s2.b = bccd;
79 s2.c = cd;
80 s2.d = s1.d;
81
82 s1.b = ab;
83 s1.c = abbc;
84 s1.d = e;
85 }
86
SplineDecompose(Spline & s,ClipPolygon & polygon) const87 void ClipPath::SplineDecompose(Spline& s, ClipPolygon& polygon) const
88 {
89 List<Spline> list;
90 list.PushBack(s);
91 while (!list.IsEmpty()) {
92 Spline s1 = list.Front();
93 list.PopFront();
94 if (CheckoutSplineError(s1)) {
95 polygon.AddPoint(s1.a);
96 } else {
97 Spline s2;
98 SplitSpline(s1, s2);
99 list.PushFront(s2);
100 list.PushFront(s1);
101 }
102 }
103 polygon.AddPoint(s.d);
104 }
105
GeneratePolygon(ClipPolygon & polygon) const106 void ClipPath::GeneratePolygon(ClipPolygon& polygon) const
107 {
108 ListNode<PointF>* pointIter = points_.Begin();
109 ListNode<ClipPathCmd>* iter = cmd_.Begin();
110 for (; iter != cmd_.End() && pointIter != points_.End(); iter = iter->next_) {
111 switch (iter->data_) {
112 case CMD_MOVE_TO: {
113 if (!polygon.points_.IsEmpty()) {
114 return;
115 }
116 polygon.AddPoint(startPos_);
117 break;
118 }
119 case CMD_LINE_TO: {
120 PointF end = pointIter->data_;
121 pointIter = pointIter->next_;
122 polygon.AddPoint(end);
123 break;
124 }
125 case CMD_CURVE_TO: {
126 if (polygon.points_.IsEmpty()) {
127 return;
128 }
129 Spline s;
130 s.a = polygon.points_.Back();
131 s.b = pointIter->data_;
132 pointIter = pointIter->next_;
133 if (pointIter == points_.End()) {
134 return;
135 }
136 s.c = pointIter->data_;
137 pointIter = pointIter->next_;
138 if (pointIter == points_.End()) {
139 return;
140 }
141 s.d = pointIter->data_;
142 pointIter = pointIter->next_;
143 SplineDecompose(s, polygon);
144 break;
145 }
146 default:
147 break;
148 }
149 }
150 }
151
MoveTo(const PointF & point)152 ClipPath& ClipPath::MoveTo(const PointF& point)
153 {
154 startPos_ = point;
155 /* If the previous command is also CMD_MOVE_TO, the previous command is overwritten. */
156 if ((cmd_.Size() != 0) && (cmd_.Tail()->data_ == CMD_MOVE_TO)) {
157 points_.Tail()->data_ = point;
158 return *this;
159 }
160 cmd_.PushBack(CMD_MOVE_TO);
161 return *this;
162 }
163
LineTo(const PointF & point)164 ClipPath& ClipPath::LineTo(const PointF& point)
165 {
166 if (cmd_.Size() == 0) {
167 MoveTo(point);
168 } else {
169 points_.PushBack(point);
170 cmd_.PushBack(CMD_LINE_TO);
171 }
172 return *this;
173 }
174
CurveTo(const PointF & control1,const PointF & control2,const PointF & end)175 ClipPath& ClipPath::CurveTo(const PointF& control1, const PointF& control2, const PointF& end)
176 {
177 if (cmd_.Size() == 0) {
178 MoveTo(end);
179 } else {
180 points_.PushBack(control1);
181 points_.PushBack(control2);
182 points_.PushBack(end);
183 cmd_.PushBack(CMD_CURVE_TO);
184 }
185 return *this;
186 }
187
188 /* The ArcInner function can be used to fit the arc within 90 degrees by Bezier curve */
ArcInner(const PointF & center,float radius,int16_t startAngle,int16_t endAngle)189 void ClipPath::ArcInner(const PointF& center, float radius, int16_t startAngle, int16_t endAngle)
190 {
191 if (radius > 0) {
192 float sinA = radius * Sin(startAngle);
193 float cosA = radius * Sin(QUARTER_IN_DEGREE - startAngle);
194 float sinB = radius * Sin(endAngle);
195 float cosB = radius * Sin(QUARTER_IN_DEGREE - endAngle);
196 float x0 = center.x + cosA;
197 float y0 = center.y + sinA;
198 float x3 = center.x + cosB;
199 float y3 = center.y + sinB;
200 int16_t addAngle = endAngle - startAngle;
201 // a = 4.0 * tan(angle / 4) / 3.0;
202 float a = 4.0 * Sin(addAngle / 4.0) / Sin(QUARTER_IN_DEGREE - addAngle / 4.0) / 3.0;
203 float x1 = x0 - a * (y0 - center.y);
204 float y1 = y0 + a * (x0 - center.x);
205 float x2 = x3 + a * (y3 - center.y);
206 float y2 = y3 - a * (x3 - center.x);
207
208 if (cmd_.Size() != 0) {
209 LineTo({x0, y0});
210 } else {
211 MoveTo({x0, y0});
212 }
213 CurveTo({x1, y1}, {x2, y2}, {x3, y3});
214 }
215 }
216
Arc(const PointF & center,float radius,int16_t startAngle,int16_t endAngle)217 ClipPath& ClipPath::Arc(const PointF& center, float radius, int16_t startAngle, int16_t endAngle)
218 {
219 if (radius > 0) {
220 startAngle = (startAngle % CIRCLE_IN_DEGREE + CIRCLE_IN_DEGREE) % CIRCLE_IN_DEGREE;
221 endAngle = (endAngle % CIRCLE_IN_DEGREE + CIRCLE_IN_DEGREE) % CIRCLE_IN_DEGREE;
222 if (startAngle > endAngle) {
223 endAngle += CIRCLE_IN_DEGREE;
224 }
225 int16_t tmpAngle = QUARTER_IN_DEGREE;
226 while (startAngle < endAngle) {
227 while (tmpAngle <= startAngle) {
228 tmpAngle += QUARTER_IN_DEGREE;
229 }
230 if (tmpAngle > endAngle) {
231 tmpAngle = endAngle;
232 }
233 ArcInner(center, radius, startAngle, tmpAngle);
234 startAngle = tmpAngle;
235 }
236 }
237 return *this;
238 }
239
Circle(const PointF & center,float radius)240 ClipPath& ClipPath::Circle(const PointF& center, float radius)
241 {
242 if (radius > 0) {
243 /*
244 * h(a) = (4 / 3) * (1 - cos(a / 2)) / sin(a / 2)
245 * h(90) = 0.552
246 */
247 float h = 0.552 * radius;
248 float x0 = center.x + radius;
249 float y0 = center.y;
250 float x3 = center.x;
251 float y3 = center.y + radius;
252 float x1 = x0;
253 float y1 = y0 + h;
254 float x2 = x3 + h;
255 float y2 = y3;
256 MoveTo({x0, y0});
257 CurveTo({x1, y1}, {x2, y2}, {x3, y3});
258 x0 = x3;
259 y0 = y3;
260 x3 = center.x - radius;
261 y3 = center.y;
262 x1 = x0 - h;
263 y1 = y0;
264 x2 = x3;
265 y2 = y3 + h;
266 CurveTo({x1, y1}, {x2, y2}, {x3, y3});
267 x0 = x3;
268 y0 = y3;
269 x3 = center.x;
270 y3 = center.y - radius;
271 x1 = x0;
272 y1 = y0 - h;
273 x2 = x3 - h;
274 y2 = y3;
275 CurveTo({x1, y1}, {x2, y2}, {x3, y3});
276 x0 = x3;
277 y0 = y3;
278 x3 = center.x + radius;
279 y3 = center.y;
280 x1 = x0 + h;
281 y1 = y0;
282 x2 = x3;
283 y2 = y3 - h;
284 CurveTo({x1, y1}, {x2, y2}, {x3, y3});
285 }
286 return *this;
287 }
288
DrawHorSpan(const List<Span> & span,int16_t yCur)289 void ClipImageBlitter::DrawHorSpan(const List<Span>& span, int16_t yCur)
290 {
291 if (src_ == nullptr) {
292 return;
293 }
294
295 for (int16_t y = iy_; y < yCur; y++) {
296 DrawHorLine(0, y, src_->header.width, OPA_TRANSPARENT);
297 }
298 int16_t index = 0;
299 auto iter = span.Begin();
300 while (iter != span.End()) {
301 DrawHorLine(index, yCur, iter->data_.left - index, OPA_TRANSPARENT);
302 DrawHorLine(iter->data_.left, yCur, iter->data_.right - iter->data_.left + 1, iter->data_.opa);
303 index = iter->data_.right + 1;
304 iter = iter->next_;
305 }
306 DrawHorLine(index, yCur, src_->header.width - index, OPA_TRANSPARENT);
307 iy_ = yCur + 1;
308 }
309
Finish()310 void ClipImageBlitter::Finish()
311 {
312 if (src_ == nullptr) {
313 return;
314 }
315
316 for (int16_t y = iy_; y < src_->header.height; y++) {
317 DrawHorLine(0, y, src_->header.width, OPA_TRANSPARENT);
318 }
319 }
320
DrawPixel(int16_t x,int16_t y,uint8_t opa)321 void ClipImageBlitter::DrawPixel(int16_t x, int16_t y, uint8_t opa)
322 {
323 if (x < 0 || x > src_->header.width - 1 || y < 0 || y > src_->header.height - 1) {
324 return;
325 }
326
327 int32_t offset = src_->header.width * y + x;
328 switch (src_->header.colorMode) {
329 case ARGB8888: {
330 Color32* buffer = reinterpret_cast<Color32*>(const_cast<uint8_t*>(src_->data));
331 buffer[offset].alpha = buffer[offset].alpha * opa / OPA_OPAQUE;
332 break;
333 }
334 default: {
335 GRAPHIC_LOGE("Only images in ARGB8888 format are supported!");
336 break;
337 }
338 }
339 }
340
DrawHorLine(int16_t x,int16_t y,int16_t width,uint8_t opa)341 void ClipImageBlitter::DrawHorLine(int16_t x, int16_t y, int16_t width, uint8_t opa)
342 {
343 if (width < 0 || opa == OPA_OPAQUE) {
344 return;
345 }
346
347 for (int16_t i = 0; i < width; i++) {
348 DrawPixel(x + i, y, opa);
349 }
350 }
351
CreateNewSpan(List<Span> & list,ListNode<Span> * node,int16_t left,int16_t right,uint8_t opa)352 void ClipUtils::CreateNewSpan(List<Span>& list, ListNode<Span>* node, int16_t left, int16_t right, uint8_t opa)
353 {
354 Span insert;
355 insert.left = left;
356 insert.right = right;
357 insert.opa = opa;
358 list.Insert(node, insert);
359 }
360
InsertSpan(List<Span> & curList,int16_t left,int16_t right,uint8_t opa)361 void ClipUtils::InsertSpan(List<Span>& curList, int16_t left, int16_t right, uint8_t opa)
362 {
363 if (left > right || opa == OPA_TRANSPARENT) {
364 return;
365 }
366
367 auto iter = curList.Begin();
368 auto endIt = curList.End();
369 while (iter != endIt) {
370 Span& curSpan = iter->data_;
371 if (right < curSpan.left) {
372 break;
373 }
374 if (left > curSpan.right) {
375 iter = iter->next_;
376 continue;
377 }
378
379 if (left < curSpan.left) {
380 CreateNewSpan(curList, iter, left, curSpan.left - 1, opa);
381 } else if (left > curSpan.left) {
382 CreateNewSpan(curList, iter, curSpan.left, left - 1, curSpan.opa);
383 curSpan.left = left;
384 }
385
386 if (right > curSpan.right) {
387 curSpan.opa = MATH_MAX(curSpan.opa, opa);
388 left = curSpan.right + 1;
389 iter = iter->next_;
390 continue;
391 } else if (right < curSpan.right) {
392 CreateNewSpan(curList, iter->next_, right + 1, curSpan.right, curSpan.opa);
393 curSpan.right = right;
394 }
395 curSpan.opa = MATH_MAX(curSpan.opa, opa);
396 return;
397 }
398 CreateNewSpan(curList, iter, left, right, opa);
399 }
400
MergeSpan(List<Span> & s)401 void ClipUtils::MergeSpan(List<Span>& s)
402 {
403 auto iter = s.Begin();
404 auto endIt = s.End();
405 while (iter != endIt) {
406 auto next = iter->next_;
407 while (next != endIt) {
408 if (next->data_.left - 1 == iter->data_.left && next->data_.opa == iter->data_.opa) {
409 iter->data_.right = next->data_.right;
410 next = s.Remove(next);
411 continue;
412 }
413 break;
414 }
415 iter = iter->next_;
416 }
417 }
418
ClearSpanTable()419 void ClipUtils::ClearSpanTable()
420 {
421 if (span0_ != nullptr) {
422 span0_->Clear();
423 delete span0_;
424 span0_ = nullptr;
425 }
426
427 if (span1_ != nullptr) {
428 span1_->Clear();
429 delete span1_;
430 span1_ = nullptr;
431 }
432 }
433
DrawAntiAliasedPoints(int16_t yCur)434 void ClipUtils::DrawAntiAliasedPoints(int16_t yCur)
435 {
436 auto iter = aaList_.Begin();
437 while (iter != aaList_.End()) {
438 if (iter->data_.y >= yCur + 1) {
439 break;
440 }
441 bool remove = false;
442 while (iter->data_.y < yCur + 1) {
443 uint8_t opa = MATH_ROUND(RF_PART(iter->data_.y) * OPA_OPAQUE);
444 InsertSpan(*span0_, iter->data_.x, iter->data_.x, opa);
445 InsertSpan(*span1_, iter->data_.x, iter->data_.x, OPA_OPAQUE - opa);
446 iter->data_.y += iter->data_.dy;
447 iter->data_.x += iter->data_.sx;
448 iter->data_.xIndex--;
449 if (iter->data_.xIndex <= 0) {
450 iter = aaList_.Remove(iter);
451 remove = true;
452 break;
453 }
454 }
455 if (!remove) {
456 iter = iter->next_;
457 }
458 }
459 }
460
CreateEdgeList(const ClipPath & path)461 void ClipUtils::CreateEdgeList(const ClipPath& path)
462 {
463 ClipPolygon polygon;
464 path.GeneratePolygon(polygon);
465 Graphic::Vector<PointF>& points = polygon.points_;
466 maxY_ = static_cast<int16_t>(floor(polygon.bound_.bottom));
467 minY_ = static_cast<int16_t>(ceil(polygon.bound_.top));
468
469 uint16_t size = points.Size();
470 for (int16_t i = 0; i < size; i++) {
471 float x1 = points[i].x;
472 float y1 = points[i].y;
473 float x2 = points[(i + 1) % size].x;
474 float y2 = points[(i + 1) % size].y;
475
476 if (y1 > y2) {
477 SWAP_FLOAT(x1, x2);
478 SWAP_FLOAT(y1, y2);
479 }
480
481 /* Insert the non steep edges to aaList_ */
482 bool steep = MATH_ABS(y1 - y2) > MATH_ABS(x1 - x2);
483 if (!steep) {
484 AAEdge edge;
485 float dy = (y1 - y2) / (x1 - x2);
486 edge.dy = MATH_ABS(dy);
487 if (x1 > x2) {
488 edge.x = static_cast<int16_t>(floor(x1));
489 edge.y = y1 + (x1 - edge.x) * edge.dy;
490 edge.sx = -1;
491 edge.xIndex = edge.x - static_cast<int16_t>(ceil(x2)) + 1;
492 } else {
493 edge.x = static_cast<int16_t>(ceil(x1));
494 edge.y = y1 + (edge.x - x1) * edge.dy;
495 edge.sx = 1;
496 edge.xIndex = static_cast<int16_t>(floor(x2)) - edge.x + 1;
497 }
498 if (edge.xIndex > 0) {
499 auto it = aaList_.Begin();
500 while (it != aaList_.End()) {
501 if (edge.y <= it->data_.y) {
502 break;
503 }
504 it = it->next_;
505 }
506 aaList_.Insert(it, edge);
507 }
508 }
509
510 /* Insert the edges to edgeList_ */
511 int16_t ymax = static_cast<int16_t>(floor(y2));
512 int16_t ymin = static_cast<int16_t>(floor(y1)) + 1;
513 if (ymax < ymin) {
514 continue;
515 }
516 float dx = (x1 - x2) / (y1 - y2);
517 Edge p;
518 p.ymax = ymax;
519 p.ymin = ymin;
520 p.x = x1 + dx * (ymin - y1);
521 p.dx = dx;
522 auto iter = edgeList_.Begin();
523 while (iter != edgeList_.End()) {
524 if (p.ymin <= iter->data_.ymin) {
525 break;
526 }
527 iter = iter->next_;
528 }
529 edgeList_.Insert(iter, p);
530 }
531 }
532
PerformScan(const ClipPath & path,Blitter & blitter)533 void ClipUtils::PerformScan(const ClipPath& path, Blitter& blitter)
534 {
535 CreateEdgeList(path);
536
537 span0_ = new List<Span>();
538 span1_ = new List<Span>();
539 List<Edge> activeEdgeList;
540 for (int16_t i = minY_; i <= maxY_; ++i) {
541 /* Calculate the anti aliasing point of non steep line in each row */
542 DrawAntiAliasedPoints(i);
543
544 /*
545 * When the ymin of the edge is equal to the current line, the edge is inserted into the AET.
546 * Sort by x ascending (Sort by dx ascending when x is equal)
547 */
548 auto iter1 = edgeList_.Begin();
549 while (iter1 != edgeList_.End()) {
550 if (iter1->data_.ymin == i) {
551 auto iter2 = activeEdgeList.Begin();
552 while (iter2 != activeEdgeList.End()) {
553 if (iter1->data_.x < iter2->data_.x) {
554 break;
555 }
556 if (iter1->data_.x == iter2->data_.x && iter1->data_.dx < iter2->data_.dx) {
557 break;
558 }
559 iter2 = iter2->next_;
560 }
561 activeEdgeList.Insert(iter2, iter1->data_);
562 iter1 = edgeList_.Remove(iter1);
563 continue;
564 }
565 break;
566 }
567 /* Traverse the activeEdgeList, pair the edges and insert the spans */
568 auto iter = activeEdgeList.Begin();
569 auto endIt = activeEdgeList.End();
570 while (iter != endIt && iter->next_ != endIt) {
571 int16_t xLeft = static_cast<int16_t>(ceil(iter->data_.x));
572 int16_t xRight = static_cast<int16_t>(floor(iter->next_->data_.x));
573 InsertSpan(*span0_, xLeft, xRight, OPA_OPAQUE);
574
575 /* Anti aliasing on both sides */
576 uint8_t opa = MATH_ROUND(RF_PART(iter->data_.x) * OPA_OPAQUE);
577 InsertSpan(*span0_, static_cast<int16_t>(iter->data_.x), static_cast<int16_t>(iter->data_.x), opa);
578 InsertSpan(*span0_, static_cast<int16_t>(iter->data_.x) + 1, static_cast<int16_t>(iter->data_.x) + 1,
579 OPA_OPAQUE - opa);
580 opa = MATH_ROUND(RF_PART(iter->next_->data_.x) * OPA_OPAQUE);
581 InsertSpan(*span0_, static_cast<int16_t>(iter->next_->data_.x), static_cast<int16_t>(iter->next_->data_.x),
582 opa);
583 InsertSpan(*span0_, static_cast<int16_t>(iter->next_->data_.x) + 1,
584 static_cast<int16_t>(iter->next_->data_.x) + 1, OPA_OPAQUE - opa);
585
586 iter = iter->next_->next_;
587 }
588
589 /* Remove the edge with (ymax == i) in active edge list */
590 iter = activeEdgeList.Begin();
591 while (iter != endIt) {
592 if (iter->data_.ymax == i) {
593 iter = activeEdgeList.Remove(iter);
594 } else {
595 iter->data_.x += iter->data_.dx;
596 iter = iter->next_;
597 }
598 }
599
600 /* Merge the span of current row and do blit */
601 MergeSpan(*span0_);
602 blitter.DrawHorSpan(*span0_, i);
603
604 /* Clear the span of current row, then swap span0_ and span1_ */
605 span0_->Clear();
606 List<Span>* tmp;
607 tmp = span0_;
608 span0_ = span1_;
609 span1_ = tmp;
610 }
611 blitter.Finish();
612
613 ClearSpanTable();
614 }
615 };