* Check line clipping by using a linear equation and draw the visible part of * the line given by x/y and x2/y2. * @param video Destination pointer to draw into. * @param x X coordinate of first point. * @param y Y coordinate of first point. * @param x2 X coordinate of second point. * @param y2 Y coordinate of second point. * @param screen_width With of the screen to check clipping against.
| 315 | * @param dash Length of dashes for dashed lines. 0 means solid line. |
| 316 | */ |
| 317 | static inline void GfxDoDrawLine(void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, PixelColour colour, int width, int dash = 0) |
| 318 | { |
| 319 | Blitter *blitter = BlitterFactory::GetCurrentBlitter(); |
| 320 | |
| 321 | assert(width > 0); |
| 322 | |
| 323 | if (y2 == y || x2 == x) { |
| 324 | /* Special case: horizontal/vertical line. All checks already done in GfxPreprocessLine. */ |
| 325 | blitter->DrawLine(video, x, y, x2, y2, screen_width, screen_height, colour, width, dash); |
| 326 | return; |
| 327 | } |
| 328 | |
| 329 | int grade_y = y2 - y; |
| 330 | int grade_x = x2 - x; |
| 331 | |
| 332 | /* Clipping rectangle. Slightly extended so we can ignore the width of the line. */ |
| 333 | int extra = (int)CeilDiv(3 * width, 4); // not less then "width * sqrt(2) / 2" |
| 334 | Rect clip = { -extra, -extra, screen_width - 1 + extra, screen_height - 1 + extra }; |
| 335 | |
| 336 | /* prevent integer overflows. */ |
| 337 | int margin = 1; |
| 338 | while (INT_MAX / abs(grade_y) < std::max(abs(clip.left - x), abs(clip.right - x))) { |
| 339 | grade_y /= 2; |
| 340 | grade_x /= 2; |
| 341 | margin *= 2; // account for rounding errors |
| 342 | } |
| 343 | |
| 344 | /* Prevent division by zero. */ |
| 345 | if (grade_x == 0) grade_x = 1; |
| 346 | |
| 347 | /* Imagine that the line is infinitely long and it intersects with |
| 348 | * infinitely long left and right edges of the clipping rectangle. |
| 349 | * If both intersection points are outside the clipping rectangle |
| 350 | * and both on the same side of it, we don't need to draw anything. */ |
| 351 | int left_isec_y = y + (clip.left - x) * grade_y / grade_x; |
| 352 | int right_isec_y = y + (clip.right - x) * grade_y / grade_x; |
| 353 | if ((left_isec_y > clip.bottom + margin && right_isec_y > clip.bottom + margin) || |
| 354 | (left_isec_y < clip.top - margin && right_isec_y < clip.top - margin)) { |
| 355 | return; |
| 356 | } |
| 357 | |
| 358 | /* It is possible to use the line equation to further reduce the amount of |
| 359 | * work the blitter has to do by shortening the effective line segment. |
| 360 | * However, in order to get that right and prevent the flickering effects |
| 361 | * of rounding errors so much additional code has to be run here that in |
| 362 | * the general case the effect is not noticeable. */ |
| 363 | |
| 364 | blitter->DrawLine(video, x, y, x2, y2, screen_width, screen_height, colour, width, dash); |
| 365 | } |
| 366 | |
| 367 | /** |
| 368 | * Align parameters of a line to the given DPI and check simple clipping. |
no test coverage detected