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