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 20namespace 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 59void 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 95void 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 120void 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 144void 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 440void 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