1 /*
2  * Copyright (c) 2020-2022 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 "draw/draw_line.h"
17 #include "draw/draw_utils.h"
18 #include "gfx_utils/graphic_math.h"
19 
20 namespace OHOS {
21 #define INCREASE_ACC(acc, accTemp, adj, step, dir)           \
22     do {                                                     \
23         (accTemp) = (acc);                                   \
24         (acc) += (adj);                                      \
25         if ((acc) <= (accTemp)) {                            \
26             (step) += (dir);                                 \
27         }                                                    \
28     } while (0)
29 
30 #define SWAP_START_END(sx, sy, ex, ey, dx, dy, dir)          \
31     do {                                                     \
32         if ((dy) >= (dx)) {                                  \
33             if ((sy) > (ey)) {                               \
34                 SWAP_POINTS((sx), (ex), (sy), (ey));         \
35             }                                                \
36             if ((ex) < (sx)) {                               \
37                 (dir) = -1;                                  \
38             }                                                \
39         } else {                                             \
40             if ((sx) < (ex)) {                               \
41                 SWAP_POINTS((sx), (ex), (sy), (ey));         \
42             }                                                \
43             if ((ey) < (sy)) {                               \
44                 (dir) = -1;                                  \
45             }                                                \
46         }                                                    \
47     } while (0)
48 
49 #define SWAP_IF_Y_LARGER(x1, x2, y1, y2)                     \
50     if ((y1) > (y2)) {                                       \
51         SWAP_POINTS((x1), (x2), (y1), (y2));                 \
52     }
53 
54 #define SWAP_IF_X_SMALLER(x1, x2, y1, y2)                    \
55     if ((x1) < (x2)) {                                       \
56         SWAP_POINTS((x1), (x2), (y1), (y2));                 \
57     }
58 
Draw(BufferInfo & gfxDstBuffer,const Point & start,const Point & end,const Rect & mask,int16_t width,const ColorType & color,OpacityType opacity)59 void DrawLine::Draw(BufferInfo& gfxDstBuffer,
60                     const Point& start,
61                     const Point& end,
62                     const Rect& mask,
63                     int16_t width,
64                     const ColorType& color,
65                     OpacityType opacity)
66 {
67     if ((width == 0) || (opacity == OPA_TRANSPARENT)) {
68         return;
69     }
70 
71     int16_t yTop;
72     int16_t yBottom;
73 
74     if (start.y < end.y) {
75         yTop = start.y - width / 2;  // 2: half
76         yBottom = end.y + width / 2; // 2: half
77     } else {
78         yTop = end.y - width / 2;      // 2: half
79         yBottom = start.y + width / 2; // 2: half
80     }
81 
82     if ((yBottom < mask.GetTop()) || (yTop > mask.GetBottom())) {
83         return;
84     }
85 
86     if (start.y == end.y) {
87         DrawHorizontalLine(gfxDstBuffer, start, end, mask, width, color, opacity);
88     } else if (start.x == end.x) {
89         DrawVerticalLine(gfxDstBuffer, start, end, mask, width, color, opacity);
90     } else {
91         DrawWuLine(gfxDstBuffer, start, end, mask, width, color, opacity);
92     }
93 }
94 
DrawVerticalLine(BufferInfo & gfxDstBuffer,const Point & start,const Point & end,const Rect & mask,int16_t width,const ColorType & color,OpacityType opacity)95 void DrawLine::DrawVerticalLine(BufferInfo& gfxDstBuffer,
96                                 const Point& start,
97                                 const Point& end,
98                                 const Rect& mask,
99                                 int16_t width,
100                                 const ColorType& color,
101                                 OpacityType opacity)
102 {
103     Rect rect;
104 
105     if (start.y < end.y) {
106         rect.SetX(start.x - width / 2); // 2: half
107         rect.SetY(start.y);
108         rect.SetWidth(width);
109         rect.SetHeight(end.y - start.y + 1);
110     } else {
111         rect.SetX(end.x - width / 2); // 2: half
112         rect.SetY(end.y);
113         rect.SetWidth(width);
114         rect.SetHeight(start.y - end.y + 1);
115     }
116 
117     DrawUtils::GetInstance()->DrawColorArea(gfxDstBuffer, rect, mask, color, opacity);
118 }
119 
DrawHorizontalLine(BufferInfo & gfxDstBuffer,const Point & start,const Point & end,const Rect & mask,int16_t width,const ColorType & color,OpacityType opacity)120 void DrawLine::DrawHorizontalLine(BufferInfo& gfxDstBuffer, const Point& start,
121                                   const Point& end,
122                                   const Rect& mask,
123                                   int16_t width,
124                                   const ColorType& color,
125                                   OpacityType opacity)
126 {
127     Rect rect;
128 
129     if (start.x < end.x) {
130         rect.SetX(start.x);
131         rect.SetY(start.y - width / 2); // 2: half
132         rect.SetWidth(end.x - start.x + 1);
133         rect.SetHeight(width);
134     } else {
135         rect.SetX(end.x);
136         rect.SetY(end.y - width / 2); // 2: half
137         rect.SetWidth(start.x - end.x + 1);
138         rect.SetHeight(width);
139     }
140 
141     DrawUtils::GetInstance()->DrawColorArea(gfxDstBuffer, rect, mask, color, opacity);
142 }
143 
DrawWuLine(BufferInfo & gfxDstBuffer,const Point & start,const Point & end,const Rect & mask,int16_t width,const ColorType & color,OpacityType opacity)144 void DrawLine::DrawWuLine(BufferInfo& gfxDstBuffer, const Point& start, const Point& end,
145     const Rect& mask, int16_t width, const ColorType& color, OpacityType opacity)
146 {
147     if (width <= 2) { // 2 : thin line width
148         DrawThinWuLine(gfxDstBuffer, start, end, mask, width, color, opacity);
149         return;
150     }
151 
152     int16_t sx = start.x;
153     int16_t sy = start.y;
154     int16_t ex = end.x;
155     int16_t ey = end.y;
156     uint16_t dx = MATH_ABS(ex - sx);
157     uint16_t dy = MATH_ABS(ey - sy);
158     int8_t dir = 1;
159     SWAP_START_END(sx, sy, ex, ey, dx, dy, dir);
160 
161     // calculate four vertex ordered according to dy and dx
162     float plot = -static_cast<float>(ex - sx) / static_cast<float>(ey - sy);
163     float offset = 1 / (1 + plot * plot);
164     offset = Sqrt(offset) * width / 2; // 2: half
165     float x0 = sx + offset;
166     float y0 = sy + (x0 - sx) * plot;
167     float x1 = sx - offset;
168     float y1 = sy + (x1 - sx) * plot;
169     float x2 = ex + offset;
170     float y2 = ey + (x2 - ex) * plot;
171     float x3 = ex - offset;
172     float y3 = ey + (x3 - ex) * plot;
173     int16_t x0Int = MATH_ROUND(x0);
174     int16_t y0Int = MATH_ROUND(y0);
175     int16_t x1Int = MATH_ROUND(x1);
176     int16_t y1Int = MATH_ROUND(y1);
177     int16_t x2Int = MATH_ROUND(x2);
178     int16_t y2Int = MATH_ROUND(y2);
179     int16_t x3Int = MATH_ROUND(x3);
180     int16_t y3Int = MATH_ROUND(y3);
181     // width is longer than distance between start point and end point, need swap direction of line.
182     if (dx * dx + dy * dy < width * width) {
183         if ((dx == 1) && (dy == 1)) {
184             DrawThinWuLine(gfxDstBuffer, { x0Int, y0Int }, { x3Int, y3Int }, mask, 2, color, opacity); // 2 : line width
185             return;
186         }
187         dx = MATH_ABS(x0Int - x1Int);
188         dy = MATH_ABS(y0Int - y1Int);
189         if (dy == dx) {
190             dir = -dir;
191         }
192     }
193     if (dy >= dx) {
194         SWAP_IF_Y_LARGER(x0Int, x1Int, y0Int, y1Int);
195         SWAP_IF_Y_LARGER(x1Int, x2Int, y1Int, y2Int);
196         SWAP_IF_Y_LARGER(x2Int, x3Int, y2Int, y3Int);
197         SWAP_IF_Y_LARGER(x0Int, x1Int, y0Int, y1Int);
198         SWAP_IF_Y_LARGER(x1Int, x2Int, y1Int, y2Int);
199         SWAP_IF_Y_LARGER(x0Int, x1Int, y0Int, y1Int);
200         if (dir == -1) {
201             SWAP_IF_X_SMALLER(x1Int, x0Int, y1Int, y0Int);
202             SWAP_IF_X_SMALLER(x3Int, x2Int, y3Int, y2Int);
203         } else {
204             SWAP_IF_X_SMALLER(x0Int, x1Int, y0Int, y1Int);
205             SWAP_IF_X_SMALLER(x2Int, x3Int, y2Int, y3Int);
206         }
207     } else {
208         SWAP_IF_X_SMALLER(x0Int, x1Int, y0Int, y1Int);
209         SWAP_IF_X_SMALLER(x1Int, x2Int, y1Int, y2Int);
210         SWAP_IF_X_SMALLER(x2Int, x3Int, y2Int, y3Int);
211         SWAP_IF_X_SMALLER(x0Int, x1Int, y0Int, y1Int);
212         SWAP_IF_X_SMALLER(x1Int, x2Int, y1Int, y2Int);
213         SWAP_IF_X_SMALLER(x0Int, x1Int, y0Int, y1Int);
214         if (dir == 1) {
215             SWAP_IF_Y_LARGER(x1Int, x0Int, y1Int, y0Int);
216             SWAP_IF_Y_LARGER(x3Int, x2Int, y3Int, y2Int);
217         } else {
218             SWAP_IF_Y_LARGER(x0Int, x1Int, y0Int, y1Int);
219             SWAP_IF_Y_LARGER(x2Int, x3Int, y2Int, y3Int);
220         }
221     }
222 
223     uint64_t adj0;
224     uint16_t accTemp0;
225     uint16_t acc0 = 0;
226     uint64_t adj1;
227     uint16_t accTemp1;
228     uint16_t acc1 = 0;
229     uint16_t accTemp2;
230     uint16_t acc2 = 0;
231 
232     int16_t endPoints0[MAX_LINE_WIDTH] = { 0 };
233     int16_t endPoints1[MAX_LINE_WIDTH] = { 0 };
234     int16_t temp0 = 0;
235     int16_t temp1 = 0;
236     int16_t edge0 = 0;
237     int16_t edge1 = 0;
238     Rect rect;
239     DrawUtils* drawUtils = DrawUtils::GetInstance();
240     // sort points
241     if (dy >= dx) {
242         adj0 = static_cast<uint64_t>(dx << SHIFT_16) / static_cast<uint64_t>(dy);
243         adj1 = static_cast<uint64_t>(MATH_ABS(y1Int - y0Int) << SHIFT_16) /
244             static_cast<uint64_t>(MATH_ABS(x1Int - x0Int));
245         if (adj1 != 0) {
246             // draw top line
247             dx = MATH_ABS(x1Int - x0Int);
248             sx = x0Int;
249             sy = y0Int;
250             drawUtils->DrawPixel(gfxDstBuffer, x0Int, y0Int, mask, color, opacity);
251             while (--dx) {
252                 accTemp1 = acc1;
253                 acc1 += adj1;
254                 if (acc1 <= accTemp1) {
255                     if (sy - y0Int < MAX_LINE_WIDTH) {
256                         endPoints0[sy - y0Int] = sx;
257                     }
258                     sy++;
259                 }
260                 sx -= dir;
261                 drawUtils->DrawPixelInLine(gfxDstBuffer, sx, sy, mask, color, opacity,
262                     (acc1 >> SHIFT_8) ^ OPA_OPAQUE);
263             }
264             if (sy - y0Int < MAX_LINE_WIDTH) {
265                 endPoints0[sy - y0Int] = sx - dir;
266             }
267 
268             // draw botttom line
269             acc1 = 0;
270             dx = MATH_ABS(x3Int - x2Int);
271             sy = y3Int;
272             sx = x3Int;
273             drawUtils->DrawPixel(gfxDstBuffer, x3Int, y3Int, mask, color, opacity);
274             while (--dx) {
275                 accTemp1 = acc1;
276                 acc1 += adj1;
277                 if (acc1 <= accTemp1) {
278                     if (temp1 < MAX_LINE_WIDTH) {
279                         endPoints1[temp1++] = sx;
280                     }
281                     sy--;
282                 }
283                 sx += dir;
284                 drawUtils->DrawPixelInLine(gfxDstBuffer, sx, sy, mask, color, opacity,
285                                            (acc1 >> SHIFT_8) ^ OPA_OPAQUE);
286             }
287             if (temp1 < MAX_LINE_WIDTH) {
288                 endPoints1[temp1++] = sx + dir;
289             }
290         } else {
291             /* If y0 is equal to y1, draw two horizontal lines as the top line and bottom line. */
292             rect.SetRect(MATH_MIN(x0Int, x1Int), y0Int, MATH_MAX(x0Int, x1Int), y1Int);
293             drawUtils->DrawColorArea(gfxDstBuffer, rect, mask, color, opacity);
294             rect.SetRect(MATH_MIN(x2Int, x3Int), y3Int, MATH_MAX(x2Int, x3Int), y2Int);
295             drawUtils->DrawColorArea(gfxDstBuffer, rect, mask, color, opacity);
296         }
297 
298         sx = x0Int;
299         sy = y0Int + 1;
300         dy = MATH_ABS(y3Int - y0Int);
301         if (dy == 0) {
302             return;
303         }
304         int16_t sxTemp = x1Int;
305         while (--dy) {
306             if (sy <= y1Int) {
307                 INCREASE_ACC(acc0, accTemp0, adj0, sx, dir);
308                 drawUtils->DrawPixelInLine(gfxDstBuffer, sx + dir, sy, mask,
309                                            color, opacity, acc0 >> SHIFT_8);
310                 if (temp0 < MAX_LINE_WIDTH) {
311                     edge0 = endPoints0[temp0++];
312                 }
313                 edge1 = sx;
314             } else if (sy < y2Int) {
315                 INCREASE_ACC(acc0, accTemp0, adj0, sx, dir);
316                 INCREASE_ACC(acc2, accTemp2, adj0, sxTemp, dir);
317                 drawUtils->DrawPixelInLine(gfxDstBuffer, sx + dir, sy, mask,
318                                            color, opacity, acc0 >> SHIFT_8);
319                 drawUtils->DrawPixelInLine(gfxDstBuffer, sxTemp, sy, mask, color, opacity,
320                                            (acc2 >> SHIFT_8) ^ OPA_OPAQUE);
321                 edge0 = sxTemp + dir;
322                 edge1 = sx;
323             } else if (sy < y3Int) {
324                 INCREASE_ACC(acc2, accTemp2, adj0, sxTemp, dir);
325                 drawUtils->DrawPixelInLine(gfxDstBuffer, sxTemp, sy, mask, color, opacity,
326                                            (acc2 >> SHIFT_8) ^ OPA_OPAQUE);
327                 edge0 = sxTemp + dir;
328                 if (temp1 > 0) {
329                     edge1 = endPoints1[--temp1];
330                 }
331             }
332             if ((dir < 0) && (edge0 > edge1)) {
333                 SWAP_INT16(edge0, edge1);
334             }
335             rect.SetRect(edge0, sy, edge1, sy);
336             drawUtils->DrawColorArea(gfxDstBuffer, rect, mask, color, opacity);
337             sy++;
338         }
339     } else {
340         adj0 = static_cast<uint64_t>(dy << SHIFT_16) / static_cast<uint64_t>(dx);
341         adj1 = static_cast<uint64_t>(MATH_ABS(x1Int - x0Int) << SHIFT_16) /
342             static_cast<uint64_t>(MATH_ABS(y1Int - y0Int));
343         if (adj1 != 0) {
344             // draw top line
345             dy = MATH_ABS(y1Int - y0Int);
346             sx = x0Int;
347             sy = y0Int;
348             drawUtils->DrawPixel(gfxDstBuffer, sx, sy, mask, color, opacity);
349             while (--dy) {
350                 accTemp1 = acc1;
351                 acc1 += adj1;
352                 if (acc1 <= accTemp1) {
353                     if (x0Int - sx < MAX_LINE_WIDTH) {
354                         endPoints0[x0Int - sx] = sy;
355                     }
356                     sx--;
357                 }
358                 sy -= dir;
359                 drawUtils->DrawPixelInLine(gfxDstBuffer, sx, sy, mask, color, opacity,
360                                            (acc1 >> SHIFT_8) ^ OPA_OPAQUE);
361             }
362             if (x0Int - sx < MAX_LINE_WIDTH) {
363                 endPoints0[x0Int - sx] = sy - dir;
364             }
365 
366             // draw botttom line
367             acc1 = 0;
368             dy = MATH_ABS(y3Int - y2Int);
369             sy = y3Int;
370             sx = x3Int;
371             while (--dy) {
372                 accTemp1 = acc1;
373                 acc1 += adj1;
374                 if (acc1 <= accTemp1) {
375                     if (temp1 < MAX_LINE_WIDTH) {
376                         endPoints1[temp1++] = sy;
377                     }
378                     sx++;
379                 }
380                 sy += dir;
381                 drawUtils->DrawPixelInLine(gfxDstBuffer, sx, sy, mask, color, opacity,
382                                            (acc1 >> SHIFT_8) ^ OPA_OPAQUE);
383             }
384             drawUtils->DrawPixel(gfxDstBuffer, x3Int, y3Int, mask, color, opacity);
385             if (temp1 < MAX_LINE_WIDTH) {
386                 endPoints1[temp1++] = sy + dir;
387             }
388         } else {
389             /* If x0 is equal to x1, draw two vertical lines as the top line and bottom line. */
390             rect.SetRect(x1Int, MATH_MIN(y0Int, y1Int), x0Int, MATH_MAX(y0Int, y1Int));
391             drawUtils->DrawColorArea(gfxDstBuffer, rect, mask, color, opacity);
392             rect.SetRect(x3Int, MATH_MIN(y2Int, y3Int), x2Int, MATH_MAX(y2Int, y3Int));
393             drawUtils->DrawColorArea(gfxDstBuffer, rect, mask, color, opacity);
394         }
395 
396         sx = x0Int - 1;
397         sy = y0Int;
398         dx = MATH_ABS(x3Int - x0Int);
399         int16_t syTemp = y1Int;
400         if (dx == 0) {
401             return;
402         }
403         while (--dx) {
404             if (sx >= x1Int) {
405                 INCREASE_ACC(acc0, accTemp0, adj0, sy, dir);
406                 drawUtils->DrawPixelInLine(gfxDstBuffer, sx, sy + dir, mask,
407                                            color, opacity, acc0 >> SHIFT_8);
408                 if (temp0 < MAX_LINE_WIDTH) {
409                     edge0 = endPoints0[temp0++];
410                 }
411                 edge1 = sy;
412             } else if (sx > x2Int) {
413                 INCREASE_ACC(acc0, accTemp0, adj0, sy, dir);
414                 INCREASE_ACC(acc2, accTemp2, adj0, syTemp, dir);
415                 drawUtils->DrawPixelInLine(gfxDstBuffer, sx, sy + dir, mask,
416                                            color, opacity, acc0 >> SHIFT_8);
417                 drawUtils->DrawPixelInLine(gfxDstBuffer, sx, syTemp, mask, color,
418                                            opacity, (acc2 >> SHIFT_8) ^ OPA_OPAQUE);
419                 edge0 = syTemp + dir;
420                 edge1 = sy;
421             } else if (sx > x3Int) {
422                 INCREASE_ACC(acc2, accTemp2, adj0, syTemp, dir);
423                 drawUtils->DrawPixelInLine(gfxDstBuffer, sx, syTemp, mask, color, opacity,
424                                            (acc2 >> SHIFT_8) ^ OPA_OPAQUE);
425                 edge0 = syTemp + dir;
426                 if (temp1 > 0) {
427                     edge1 = endPoints1[--temp1];
428                 }
429             }
430             if ((dir < 0) && (edge0 > edge1)) {
431                 SWAP_INT16(edge0, edge1);
432             }
433             rect.SetRect(sx, edge0, sx, edge1);
434             drawUtils->DrawColorArea(gfxDstBuffer, rect, mask, color, opacity);
435             sx--;
436         }
437     }
438 }
439 
DrawThinWuLine(BufferInfo & gfxDstBuffer,const Point & start,const Point & end,const Rect & mask,int16_t width,const ColorType & color,OpacityType opacity)440 void DrawLine::DrawThinWuLine(BufferInfo& gfxDstBuffer, const Point& start, const Point& end,
441     const Rect& mask, int16_t width, const ColorType& color, OpacityType opacity)
442 {
443     int16_t sx = start.x;
444     int16_t sy = start.y;
445     int16_t ex = end.x;
446     int16_t ey = end.y;
447     uint16_t dx = MATH_ABS(ex - sx);
448     uint16_t dy = MATH_ABS(ey - sy);
449     uint64_t adj;
450     uint16_t accTemp;
451     uint16_t acc = 0;
452     int8_t dir = 1;
453     SWAP_START_END(sx, sy, ex, ey, dx, dy, dir);
454     DrawUtils* drawUtils = DrawUtils::GetInstance();
455     if (dy >= dx) {
456         adj = static_cast<uint64_t>(dx << SHIFT_16) / static_cast<uint64_t>(dy);
457         while (dy--) {
458             INCREASE_ACC(acc, accTemp, adj, sx, dir);
459             sy++;
460             if (width == 1) {
461                 drawUtils->DrawAdjPixelInLine(gfxDstBuffer, sx, sy, sx + dir, sy, mask,
462                                               color, opacity, acc >> SHIFT_8);
463             } else {
464                 drawUtils->DrawVerPixelInLine(gfxDstBuffer, sx, sy, dir, mask,
465                                               color, opacity, acc >> SHIFT_8);
466             }
467         }
468     } else {
469         adj = static_cast<uint64_t>(dy << SHIFT_16) / static_cast<uint64_t>(dx);
470         while (dx--) {
471             INCREASE_ACC(acc, accTemp, adj, sy, dir);
472             sx--;
473             if (width == 1) {
474                 drawUtils->DrawAdjPixelInLine(gfxDstBuffer, sx, sy, sx, sy + dir, mask,
475                                               color, opacity, acc >> SHIFT_8);
476             } else {
477                 drawUtils->DrawHorPixelInLine(gfxDstBuffer, sx, sy, dir, mask,
478                                               color, opacity, acc >> SHIFT_8);
479             }
480         }
481     }
482 }
483 } // namespace OHOS
484