summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLe Juez Victor <[email protected]>2024-06-23 22:51:24 +0200
committerGitHub <[email protected]>2024-06-23 22:51:24 +0200
commit7e50270d496762e7202b61e26ba1c42c47161c4e (patch)
tree3b66d46b310c9e27d7a34ed759da559077442e70
parent77732540225eabb0ff659710167315265401c772 (diff)
downloadraylib-7e50270d496762e7202b61e26ba1c42c47161c4e.tar.gz
raylib-7e50270d496762e7202b61e26ba1c42c47161c4e.zip
[rtextures] Adding triangle drawing function for images (#4094)
* adding triangle drawing function for images * remove unnecessary check * fix an iteration mistake
-rw-r--r--src/raylib.h5
-rw-r--r--src/rtextures.c188
2 files changed, 193 insertions, 0 deletions
diff --git a/src/raylib.h b/src/raylib.h
index 9f06462f..7195fd55 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -1381,6 +1381,11 @@ RLAPI void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int hei
RLAPI void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color); // Draw rectangle within an image (Vector version)
RLAPI void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color); // Draw rectangle within an image
RLAPI void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color); // Draw rectangle lines within an image
+RLAPI void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle within an image
+RLAPI void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3); // Draw triangle with interpolated colors within an image
+RLAPI void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color); // Draw triangle outline within an image
+RLAPI void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle fan defined by points within an image (first vertex is the center)
+RLAPI void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color); // Draw a triangle strip defined by points within an image
RLAPI void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint); // Draw a source image within a destination image (tint applied to source)
RLAPI void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) within an image (destination)
RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text (custom sprite font) within an image (destination)
diff --git a/src/rtextures.c b/src/rtextures.c
index 2f02afec..39c0f78f 100644
--- a/src/rtextures.c
+++ b/src/rtextures.c
@@ -3627,6 +3627,194 @@ void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color)
ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + rec.height - thick), (int)rec.width, thick, color);
}
+// Draw triangle within an image
+void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color)
+{
+ // Calculate the 2D bounding box of the triangle
+ // Determine the minimum and maximum x and y coordinates of the triangle vertices
+ int xMin = (v1.x < v2.x)? ((v1.x < v3.x)? v1.x : v3.x) : ((v2.x < v3.x)? v2.x : v3.x);
+ int yMin = (v1.y < v2.y)? ((v1.y < v3.y)? v1.y : v3.y) : ((v2.y < v3.y)? v2.y : v3.y);
+ int xMax = (v1.x > v2.x)? ((v1.x > v3.x)? v1.x : v3.x) : ((v2.x > v3.x)? v2.x : v3.x);
+ int yMax = (v1.y > v2.y)? ((v1.y > v3.y)? v1.y : v3.y) : ((v2.y > v3.y)? v2.y : v3.y);
+
+ // Clamp the bounding box to the image dimensions
+ if (xMin < 0) xMin = 0;
+ if (yMin < 0) yMin = 0;
+ if (xMax > dst->width) xMax = dst->width;
+ if (yMax > dst->height) yMax = dst->height;
+
+ // Check the order of the vertices to determine if it's a front or back face
+ // NOTE: if signedArea is equal to 0, the face is degenerate
+ float signedArea = (v2.x - v1.x)*(v3.y - v1.y) - (v3.x - v1.x)*(v2.y - v1.y);
+ bool isBackFace = (signedArea > 0);
+
+ // Barycentric interpolation setup
+ // Calculate the step increments for the barycentric coordinates
+ int w1XStep = v3.y - v2.y, w1YStep = v2.x - v3.x;
+ int w2XStep = v1.y - v3.y, w2YStep = v3.x - v1.x;
+ int w3XStep = v2.y - v1.y, w3YStep = v1.x - v2.x;
+
+ // If the triangle is a back face, invert the steps
+ if (isBackFace)
+ {
+ w1XStep = -w1XStep, w1YStep = -w1YStep;
+ w2XStep = -w2XStep, w2YStep = -w2YStep;
+ w3XStep = -w3XStep, w3YStep = -w3YStep;
+ }
+
+ // Calculate the initial barycentric coordinates for the top-left point of the bounding box
+ int w1Row = (xMin - v2.x)*w1XStep + w1YStep*(yMin - v2.y);
+ int w2Row = (xMin - v3.x)*w2XStep + w2YStep*(yMin - v3.y);
+ int w3Row = (xMin - v1.x)*w3XStep + w3YStep*(yMin - v1.y);
+
+ // Rasterization loop
+ // Iterate through each pixel in the bounding box
+ for (int y = yMin; y <= yMax; y++)
+ {
+ int w1 = w1Row;
+ int w2 = w2Row;
+ int w3 = w3Row;
+
+ for (int x = xMin; x <= xMax; x++)
+ {
+ // Check if the pixel is inside the triangle using barycentric coordinates
+ // If it is then we can draw the pixel with the given color
+ if ((w1 | w2 | w3) >= 0) ImageDrawPixel(dst, x, y, color);
+
+ // Increment the barycentric coordinates for the next pixel
+ w1 += w1XStep;
+ w2 += w2XStep;
+ w3 += w3XStep;
+ }
+
+ // Move to the next row in the bounding box
+ w1Row += w1YStep;
+ w2Row += w2YStep;
+ w3Row += w3YStep;
+ }
+}
+
+// Draw triangle with interpolated colors within an image
+void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3)
+{
+ // Calculate the 2D bounding box of the triangle
+ // Determine the minimum and maximum x and y coordinates of the triangle vertices
+ int xMin = (v1.x < v2.x)? ((v1.x < v3.x)? v1.x : v3.x) : ((v2.x < v3.x)? v2.x : v3.x);
+ int yMin = (v1.y < v2.y)? ((v1.y < v3.y)? v1.y : v3.y) : ((v2.y < v3.y)? v2.y : v3.y);
+ int xMax = (v1.x > v2.x)? ((v1.x > v3.x)? v1.x : v3.x) : ((v2.x > v3.x)? v2.x : v3.x);
+ int yMax = (v1.y > v2.y)? ((v1.y > v3.y)? v1.y : v3.y) : ((v2.y > v3.y)? v2.y : v3.y);
+
+ // Clamp the bounding box to the image dimensions
+ if (xMin < 0) xMin = 0;
+ if (yMin < 0) yMin = 0;
+ if (xMax > dst->width) xMax = dst->width;
+ if (yMax > dst->height) yMax = dst->height;
+
+ // Check the order of the vertices to determine if it's a front or back face
+ // NOTE: if signedArea is equal to 0, the face is degenerate
+ float signedArea = (v2.x - v1.x)*(v3.y - v1.y) - (v3.x - v1.x)*(v2.y - v1.y);
+ bool isBackFace = (signedArea > 0);
+
+ // Barycentric interpolation setup
+ // Calculate the step increments for the barycentric coordinates
+ int w1XStep = v3.y - v2.y, w1YStep = v2.x - v3.x;
+ int w2XStep = v1.y - v3.y, w2YStep = v3.x - v1.x;
+ int w3XStep = v2.y - v1.y, w3YStep = v1.x - v2.x;
+
+ // If the triangle is a back face, invert the steps
+ if (isBackFace)
+ {
+ w1XStep = -w1XStep, w1YStep = -w1YStep;
+ w2XStep = -w2XStep, w2YStep = -w2YStep;
+ w3XStep = -w3XStep, w3YStep = -w3YStep;
+ }
+
+ // Calculate the initial barycentric coordinates for the top-left point of the bounding box
+ int w1Row = (xMin - v2.x)*w1XStep + w1YStep*(yMin - v2.y);
+ int w2Row = (xMin - v3.x)*w2XStep + w2YStep*(yMin - v3.y);
+ int w3Row = (xMin - v1.x)*w3XStep + w3YStep*(yMin - v1.y);
+
+ // Calculate the inverse of the sum of the barycentric coordinates for normalization
+ // NOTE 1: Here, we act as if we multiply by 255 the reciprocal, which avoids additional
+ // calculations in the loop. This is acceptable because we are only interpolating colors.
+ // NOTE 2: This sum remains constant throughout the triangle
+ float wInvSum = 255.0f/(w1Row + w2Row + w3Row);
+
+ // Rasterization loop
+ // Iterate through each pixel in the bounding box
+ for (int y = yMin; y <= yMax; y++)
+ {
+ int w1 = w1Row;
+ int w2 = w2Row;
+ int w3 = w3Row;
+
+ for (int x = xMin; x <= xMax; x++)
+ {
+ // Check if the pixel is inside the triangle using barycentric coordinates
+ if ((w1 | w2 | w3) >= 0)
+ {
+ // Compute the normalized barycentric coordinates
+ unsigned char aW1 = (unsigned char)((float)w1*wInvSum);
+ unsigned char aW2 = (unsigned char)((float)w2*wInvSum);
+ unsigned char aW3 = (unsigned char)((float)w3*wInvSum);
+
+ // Interpolate the color using the barycentric coordinates
+ Color finalColor = { 0 };
+ finalColor.r = (c1.r*aW1 + c2.r*aW2 + c3.r*aW3)/255;
+ finalColor.g = (c1.g*aW1 + c2.g*aW2 + c3.g*aW3)/255;
+ finalColor.b = (c1.b*aW1 + c2.b*aW2 + c3.b*aW3)/255;
+ finalColor.a = (c1.a*aW1 + c2.a*aW2 + c3.a*aW3)/255;
+
+ // Draw the pixel with the interpolated color
+ ImageDrawPixel(dst, x, y, finalColor);
+ }
+
+ // Increment the barycentric coordinates for the next pixel
+ w1 += w1XStep;
+ w2 += w2XStep;
+ w3 += w3XStep;
+ }
+
+ // Move to the next row in the bounding box
+ w1Row += w1YStep;
+ w2Row += w2YStep;
+ w3Row += w3YStep;
+ }
+}
+
+// Draw triangle outline within an image
+void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color)
+{
+ ImageDrawLine(dst, v1.x, v1.y, v2.x, v2.y, color);
+ ImageDrawLine(dst, v2.x, v2.y, v3.x, v3.y, color);
+ ImageDrawLine(dst, v3.x, v3.y, v1.x, v1.y, color);
+}
+
+// Draw a triangle fan defined by points within an image (first vertex is the center)
+void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color)
+{
+ if (pointCount >= 3)
+ {
+ for (int i = 1; i < pointCount - 1; i++)
+ {
+ ImageDrawTriangle(dst, points[0], points[i], points[i + 1], color);
+ }
+ }
+}
+
+// Draw a triangle strip defined by points within an image
+void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color)
+{
+ if (pointCount >= 3)
+ {
+ for (int i = 2; i < pointCount; i++)
+ {
+ if ((i%2) == 0) ImageDrawTriangle(dst, points[i], points[i - 2], points[i - 1], color);
+ else ImageDrawTriangle(dst, points[i], points[i - 1], points[i - 2], color);
+ }
+ }
+}
+
// Draw an image (source) within an image (destination)
// NOTE: Color tint is applied to source image
void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint)