| 528 | |
| 529 | template<typename PixelT, typename Coord, bool Overwrite> |
| 530 | inline void drawLineCore(Canvas<PixelT>& canvas, const PixelT& color, |
| 531 | Coord x0, Coord y0, Coord x1, Coord y1) { |
| 532 | PixelT* pixels = canvas.pixels; |
| 533 | int width = canvas.width; |
| 534 | int height = canvas.height; |
| 535 | |
| 536 | // Convert to 8.8 fixed-point |
| 537 | fl::i32 fx0 = detail::toFixed8(x0); |
| 538 | fl::i32 fy0 = detail::toFixed8(y0); |
| 539 | fl::i32 fx1 = detail::toFixed8(x1); |
| 540 | fl::i32 fy1 = detail::toFixed8(y1); |
| 541 | |
| 542 | bool steep = fl::abs(fy1 - fy0) > fl::abs(fx1 - fx0); |
| 543 | if (steep) { fl::swap(fx0, fy0); fl::swap(fx1, fy1); } |
| 544 | if (fx0 > fx1) { fl::swap(fx0, fx1); fl::swap(fy0, fy1); } |
| 545 | |
| 546 | fl::i32 dx8 = fx1 - fx0; |
| 547 | fl::i32 dy8 = fy1 - fy0; |
| 548 | |
| 549 | // Gradient per pixel step in 8.8: (dy * 256) / dx |
| 550 | fl::i32 gradient = (dx8 == 0) ? 0 : ((dy8 << 8) / dx8); |
| 551 | |
| 552 | // First endpoint: round x to nearest pixel boundary |
| 553 | fl::i32 xend = (fx0 + 128) & ~0xFF; |
| 554 | fl::i32 yend = fy0 + ((gradient * (xend - fx0)) >> 8); |
| 555 | fl::i32 xgap = 256 - ((fx0 + 128) & 0xFF); // rfpart(x0 + 0.5) |
| 556 | int xpxl1 = xend >> 8; |
| 557 | int ypxl1 = yend >> 8; |
| 558 | fl::u8 yfrac1 = static_cast<fl::u8>(yend & 0xFF); |
| 559 | |
| 560 | // Second endpoint |
| 561 | fl::i32 xend2 = (fx1 + 128) & ~0xFF; |
| 562 | fl::i32 yend2 = fy1 + ((gradient * (xend2 - fx1)) >> 8); |
| 563 | fl::i32 xgap2 = (fx1 + 128) & 0xFF; // fpart(x1 + 0.5) |
| 564 | int xpxl2 = xend2 >> 8; |
| 565 | int ypxl2 = yend2 >> 8; |
| 566 | fl::u8 yfrac2 = static_cast<fl::u8>(yend2 & 0xFF); |
| 567 | |
| 568 | // Draw first endpoint |
| 569 | { |
| 570 | fl::u8 b1 = static_cast<fl::u8>(((255 - yfrac1) * xgap) >> 8); |
| 571 | fl::u8 b2 = static_cast<fl::u8>((yfrac1 * xgap) >> 8); |
| 572 | if (steep) { |
| 573 | PixelT c = color; c.nscale8(b1); |
| 574 | addPixelToBuffer<PixelT, Overwrite>(pixels, width, height, ypxl1, xpxl1, c); |
| 575 | c = color; c.nscale8(b2); |
| 576 | addPixelToBuffer<PixelT, Overwrite>(pixels, width, height, ypxl1 + 1, xpxl1, c); |
| 577 | } else { |
| 578 | PixelT c = color; c.nscale8(b1); |
| 579 | addPixelToBuffer<PixelT, Overwrite>(pixels, width, height, xpxl1, ypxl1, c); |
| 580 | c = color; c.nscale8(b2); |
| 581 | addPixelToBuffer<PixelT, Overwrite>(pixels, width, height, xpxl1, ypxl1 + 1, c); |
| 582 | } |
| 583 | } |
| 584 | |
| 585 | // Draw second endpoint |
| 586 | { |
| 587 | fl::u8 b1 = static_cast<fl::u8>(((255 - yfrac2) * xgap2) >> 8); |