• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 };