Skip to content

Commit

Permalink
[rtextures] Adding ImageDrawLineEx function (#4097)
Browse files Browse the repository at this point in the history
* adding `ImageDrawLineEx` function
also review other functions for drawing lines in images

* fix `ImageDrawLineV`
  • Loading branch information
Bigfoot71 committed Jun 24, 2024
1 parent c7bda3d commit c2df169
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 72 deletions.
1 change: 1 addition & 0 deletions src/raylib.h
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,7 @@ RLAPI void ImageDrawPixel(Image *dst, int posX, int posY, Color color);
RLAPI void ImageDrawPixelV(Image *dst, Vector2 position, Color color); // Draw pixel within an image (Vector version)
RLAPI void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw line within an image
RLAPI void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color); // Draw line within an image (Vector version)
RLAPI void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color color); // Draw a line defining thickness within an image
RLAPI void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color); // Draw a filled circle within an image
RLAPI void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color); // Draw a filled circle within an image (Vector version)
RLAPI void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color); // Draw circle outline within an image
Expand Down
158 changes: 86 additions & 72 deletions src/rtextures.c
Original file line number Diff line number Diff line change
Expand Up @@ -3403,98 +3403,112 @@ void ImageDrawPixelV(Image *dst, Vector2 position, Color color)
// Draw line within an image
void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color)
{
// Using Bresenham's algorithm as described in
// Drawing Lines with Pixels - Joshua Scott - March 2012
// https://classic.csunplugged.org/wp-content/uploads/2014/12/Lines.pdf
// Calculate differences in coordinates
int shortLen = endPosY - startPosY;
int longLen = endPosX - startPosX;
bool yLonger = false;

int changeInX = (endPosX - startPosX);
int absChangeInX = (changeInX < 0)? -changeInX : changeInX;
int changeInY = (endPosY - startPosY);
int absChangeInY = (changeInY < 0)? -changeInY : changeInY;
// Determine if the line is more vertical than horizontal
if (abs(shortLen) > abs(longLen))
{
// Swap the lengths if the line is more vertical
int temp = shortLen;
shortLen = longLen;
longLen = temp;
yLonger = true;
}

int startU, startV, endU, stepV; // Substitutions, either U = X, V = Y or vice versa. See loop at end of function
//int endV; // Not needed but left for better understanding, check code below
int A, B, P; // See linked paper above, explained down in the main loop
int reversedXY = (absChangeInY < absChangeInX);
// Initialize variables for drawing loop
int endVal = longLen;
int sgnInc = 1;

if (reversedXY)
// Adjust direction increment based on longLen sign
if (longLen < 0)
{
A = 2*absChangeInY;
B = A - 2*absChangeInX;
P = A - absChangeInX;
longLen = -longLen;
sgnInc = -1;
}

if (changeInX > 0)
{
startU = startPosX;
startV = startPosY;
endU = endPosX;
//endV = endPosY;
}
else
// Calculate fixed-point increment for shorter length
int decInc = (longLen == 0)? 0 : (shortLen<<16) / longLen;

// Draw the line pixel by pixel
if (yLonger)
{
// If line is more vertical, iterate over y-axis
for (int i = 0, j = 0; i != endVal; i += sgnInc, j += decInc)
{
startU = endPosX;
startV = endPosY;
endU = startPosX;
//endV = startPosY;

// Since start and end are reversed
changeInX = -changeInX;
changeInY = -changeInY;
// Calculate pixel position and draw it
ImageDrawPixel(dst, startPosX + (j>>16), startPosY + i, color);
}

stepV = (changeInY < 0)? -1 : 1;

ImageDrawPixel(dst, startU, startV, color); // At this point they are correctly ordered...
}
else
{
A = 2*absChangeInX;
B = A - 2*absChangeInY;
P = A - absChangeInY;

if (changeInY > 0)
{
startU = startPosY;
startV = startPosX;
endU = endPosY;
//endV = endPosX;
}
else
// If line is more horizontal, iterate over x-axis
for (int i = 0, j = 0; i != endVal; i += sgnInc, j += decInc)
{
startU = endPosY;
startV = endPosX;
endU = startPosY;
//endV = startPosX;

// Since start and end are reversed
changeInX = -changeInX;
changeInY = -changeInY;
// Calculate pixel position and draw it
ImageDrawPixel(dst, startPosX + i, startPosY + (j>>16), color);
}
}
}

// Draw line within an image (Vector version)
void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color)
{
// Round start and end positions to nearest integer coordinates
int x1 = (int)(start.x + 0.5f);
int y1 = (int)(start.y + 0.5f);
int x2 = (int)(end.x + 0.5f);
int y2 = (int)(end.y + 0.5f);

// Draw a vertical line using ImageDrawLine function
ImageDrawLine(dst, x1, y1, x2, y2, color);
}

stepV = (changeInX < 0)? -1 : 1;
// Draw a line defining thickness within an image
void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color color)
{
// Round start and end positions to nearest integer coordinates
int x1 = (int)(start.x + 0.5f);
int y1 = (int)(start.y + 0.5f);
int x2 = (int)(end.x + 0.5f);
int y2 = (int)(end.y + 0.5f);

ImageDrawPixel(dst, startV, startU, color); // ... but need to be reversed here. Repeated in the main loop below
}
// Calculate differences in x and y coordinates
int dx = x2 - x1;
int dy = y2 - y1;

// Draw the main line between (x1, y1) and (x2, y2)
ImageDrawLine(dst, x1, y1, x2, y2, color);

// We already drew the start point. If we started at startU + 0, the line would be crooked and too short
for (int u = startU + 1, v = startV; u <= endU; u++)
// Determine if the line is more horizontal or vertical
if (dx != 0 && abs(dy/dx) < 1)
{
if (P >= 0)
// Line is more horizontal
// Calculate half the width of the line
int wy = (thick - 1)*sqrtf(dx*dx + dy*dy)/(2*abs(dx));

// Draw additional lines above and below the main line
for (int i = 1; i <= wy; i++)
{
v += stepV; // Adjusts whenever we stray too far from the direct line. Details in the linked paper above
P += B; // Remembers that we corrected our path
ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color); // Draw above the main line
ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color); // Draw below the main line
}
else P += A; // Remembers how far we are from the direct line

if (reversedXY) ImageDrawPixel(dst, u, v, color);
else ImageDrawPixel(dst, v, u, color);
}
}
else if (dy != 0)
{
// Line is more vertical or perfectly horizontal
// Calculate half the width of the line
int wx = (thick - 1)*sqrtf(dx*dx + dy*dy)/(2*abs(dy));

// Draw line within an image (Vector version)
void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color)
{
ImageDrawLine(dst, (int)start.x, (int)start.y, (int)end.x, (int)end.y, color);
// Draw additional lines to the left and right of the main line
for (int i = 1; i <= wx; i++)
{
ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color); // Draw left of the main line
ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color); // Draw right of the main line
}
}
}

// Draw circle within an image
Expand Down

0 comments on commit c2df169

Please sign in to comment.