diff --git a/src/rshapes.c b/src/rshapes.c index b6d0f0944..51bc15a60 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -311,413 +311,128 @@ void DrawLineDashed(Vector2 startPos, Vector2 endPos, int dashSize, int spaceSiz rlEnd(); } -// Draw a color-filled circle -void DrawCircle(int centerX, int centerY, float radius, Color color) +// Draw a triangle +// NOTE: Vertex must be provided in counter-clockwise order +void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { - DrawCircleV((Vector2){ (float)centerX, (float)centerY }, radius, color); + DrawTriangleGradient(v1, v2, v3, color, color, color); } -// Draw a color-filled circle (Vector version) -// NOTE: On OpenGL 3.3 and ES2 using QUADS to avoid drawing order issues -void DrawCircleV(Vector2 center, float radius, Color color) +// Draw triangle with interpolated colors (vertex in counter-clockwise order!) +void DrawTriangleGradient(Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3) { - DrawCircleSector(center, radius, 0, 360, 36, color); -} - -// Draw a gradient-filled circle -void DrawCircleGradient(Vector2 center, float radius, Color inner, Color outer) -{ - rlBegin(RL_TRIANGLES); - for (int i = 0; i < 360; i += 10) - { - rlColor4ub(inner.r, inner.g, inner.b, inner.a); - rlVertex2f(center.x, center.y); - rlColor4ub(outer.r, outer.g, outer.b, outer.a); - rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radius, center.y + sinf(DEG2RAD*(i + 10))*radius); - rlColor4ub(outer.r, outer.g, outer.b, outer.a); - rlVertex2f(center.x + cosf(DEG2RAD*i)*radius, center.y + sinf(DEG2RAD*i)*radius); - } - rlEnd(); -} - -// Draw a piece of a circle -void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color) -{ - if (startAngle == endAngle) return; - if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero - - // Function expects (endAngle > startAngle) - if (endAngle < startAngle) - { - // Swap values - float tmp = startAngle; - startAngle = endAngle; - endAngle = tmp; - } - - int minSegments = (int)ceilf((endAngle - startAngle)/90); - - if (segments < minSegments) - { - // Calculate the maximum angle between segments based on the error rate (usually 0.5f) - float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1); - segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); - - if (segments <= 0) segments = minSegments; - } - - float stepLength = (endAngle - startAngle)/(float)segments; - float angle = startAngle; - #if SUPPORT_QUADS_DRAW_MODE rlSetTexture(GetShapesTexture().id); Rectangle shapeRect = GetShapesTextureRectangle(); rlBegin(RL_QUADS); + rlNormal3f(0.0f, 0.0f, 1.0f); - // NOTE: Every QUAD actually represents two segments - for (int i = 0; i < segments/2; i++) - { - rlColor4ub(color.r, color.g, color.b, color.a); + rlColor4ub(c1.r, c1.g, c1.b, c1.a); + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(v1.x, v1.y); - rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); - rlVertex2f(center.x, center.y); + rlColor4ub(c2.r, c2.g, c2.b, c2.a); + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(v2.x, v2.y); - rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2.0f))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2.0f))*radius); - - rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); - - rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); - - angle += (stepLength*2.0f); - } - - // NOTE: In case number of segments is odd, adding one last piece to the cake - if ((((unsigned int)segments)%2) == 1) - { - rlColor4ub(color.r, color.g, color.b, color.a); - - rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); - rlVertex2f(center.x, center.y); - - rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); - - rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); - - rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); - rlVertex2f(center.x, center.y); - } + rlColor4ub(c3.r, c3.g, c3.b, c3.a); + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(v3.x, v3.y); + rlColor4ub(c3.r, c3.g, c3.b, c3.a); + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(v3.x, v3.y); rlEnd(); rlSetTexture(0); #else rlBegin(RL_TRIANGLES); - for (int i = 0; i < segments; i++) - { - rlColor4ub(color.r, color.g, color.b, color.a); - - rlVertex2f(center.x, center.y); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); - - angle += stepLength; - } + rlColor4ub(c1.r, c1.g, c1.b, c1.a); + rlVertex2f(v1.x, v1.y); + rlColor4ub(c2.r, c2.g, c2.b, c2.a); + rlVertex2f(v2.x, v2.y); + rlColor4ub(c3.r, c3.g, c3.b, c3.a); + rlVertex2f(v3.x, v3.y); rlEnd(); #endif } -// Draw a piece of a circle outlines -void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color) -{ - if (startAngle == endAngle) return; - if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero issue - - // Function expects (endAngle > startAngle) - if (endAngle < startAngle) - { - // Swap values - float tmp = startAngle; - startAngle = endAngle; - endAngle = tmp; - } - - int minSegments = (int)ceilf((endAngle - startAngle)/90); - - if (segments < minSegments) - { - // Calculate the maximum angle between segments based on the error rate (usually 0.5f) - float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1); - segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); - - if (segments <= 0) segments = minSegments; - } - - float stepLength = (endAngle - startAngle)/(float)segments; - float angle = startAngle; - bool showCapLines = true; - - rlBegin(RL_LINES); - if (showCapLines) - { - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x, center.y); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); - } - - for (int i = 0; i < segments; i++) - { - rlColor4ub(color.r, color.g, color.b, color.a); - - rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); - - angle += stepLength; - } - - if (showCapLines) - { - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x, center.y); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); - } - rlEnd(); -} - -// Draw circle outline -void DrawCircleLines(int centerX, int centerY, float radius, Color color) -{ - DrawCircleLinesV((Vector2){ (float)centerX, (float)centerY }, radius, color); -} - -// Draw circle outline (Vector version) -void DrawCircleLinesV(Vector2 center, float radius, Color color) +// Draw a triangle using lines +// NOTE: Vertex must be provided in counter-clockwise order +void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color) { rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(v1.x, v1.y); + rlVertex2f(v2.x, v2.y); - // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) - for (int i = 0; i < 360; i += 10) - { - rlVertex2f(center.x + cosf(DEG2RAD*i)*radius, center.y + sinf(DEG2RAD*i)*radius); - rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radius, center.y + sinf(DEG2RAD*(i + 10))*radius); - } + rlVertex2f(v2.x, v2.y); + rlVertex2f(v3.x, v3.y); + + rlVertex2f(v3.x, v3.y); + rlVertex2f(v1.x, v1.y); rlEnd(); } -void DrawCircleLinesEx(Vector2 center, float radius, float thick, Color color) +// Draw a triangle fan defined by points +// NOTE: First vertex provided is the center, shared by all triangles +// By default, following vertex should be provided in counter-clockwise order +void DrawTriangleFan(const Vector2 *points, int pointCount, Color color) { - DrawRing(center, radius - thick, radius, 0.0f, 360.0f, 36, color); + if (pointCount >= 3) + { + rlSetTexture(GetShapesTexture().id); + Rectangle shapeRect = GetShapesTextureRectangle(); + + rlBegin(RL_QUADS); + rlColor4ub(color.r, color.g, color.b, color.a); + + for (int i = 1; i < pointCount - 1; i++) + { + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(points[0].x, points[0].y); + + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(points[i].x, points[i].y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(points[i + 1].x, points[i + 1].y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(points[i + 1].x, points[i + 1].y); + } + rlEnd(); + rlSetTexture(0); + } } -// Draw ellipse -void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color) +// Draw a triangle strip defined by points +// NOTE: Every new vertex connects with previous two +void DrawTriangleStrip(const Vector2 *points, int pointCount, Color color) { - DrawEllipseV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color); -} - -// Draw ellipse (Vector version) -void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color) -{ - rlBegin(RL_TRIANGLES); - for (int i = 0; i < 360; i += 10) - { - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x, center.y); - rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV); - rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV); - } - rlEnd(); -} - -// Draw ellipse outline -void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color) -{ - DrawEllipseLinesV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color); -} - -// Draw ellipse outline -void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color) -{ - rlBegin(RL_LINES); - for (int i = 0; i < 360; i += 10) - { - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV); - rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV); - } - rlEnd(); -} - -// Draw ring -void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color) -{ - if (startAngle == endAngle) return; - - // Function expects (outerRadius > innerRadius) - if (outerRadius < innerRadius) + if (pointCount >= 3) { - float tmp = outerRadius; - outerRadius = innerRadius; - innerRadius = tmp; - - if (outerRadius <= 0.0f) outerRadius = 0.1f; - } - - // Function expects (endAngle > startAngle) - if (endAngle < startAngle) - { - // Swap values - float tmp = startAngle; - startAngle = endAngle; - endAngle = tmp; - } - - int minSegments = (int)ceilf((endAngle - startAngle)/90); - - if (segments < minSegments) - { - // Calculate the maximum angle between segments based on the error rate (usually 0.5f) - float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/outerRadius, 2) - 1); - segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); - - if (segments <= 0) segments = minSegments; - } - - // Not a ring - if (innerRadius <= 0.0f) - { - DrawCircleSector(center, outerRadius, startAngle, endAngle, segments, color); - return; - } - - float stepLength = (endAngle - startAngle)/(float)segments; - float angle = startAngle; - -#if SUPPORT_QUADS_DRAW_MODE - rlSetTexture(GetShapesTexture().id); - Rectangle shapeRect = GetShapesTextureRectangle(); - - rlBegin(RL_QUADS); - for (int i = 0; i < segments; i++) - { + rlBegin(RL_TRIANGLES); rlColor4ub(color.r, color.g, color.b, color.a); - rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - - rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); - - rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); - - rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); - - angle += stepLength; - } - rlEnd(); - - rlSetTexture(0); -#else - rlBegin(RL_TRIANGLES); - for (int i = 0; i < segments; i++) - { - rlColor4ub(color.r, color.g, color.b, color.a); - - rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - - angle += stepLength; - } - rlEnd(); -#endif -} - -// Draw ring outline -void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color) -{ - if (startAngle == endAngle) return; - - // Function expects (outerRadius > innerRadius) - if (outerRadius < innerRadius) - { - float tmp = outerRadius; - outerRadius = innerRadius; - innerRadius = tmp; - - if (outerRadius <= 0.0f) outerRadius = 0.1f; + for (int i = 2; i < pointCount; i++) + { + if ((i%2) == 0) + { + rlVertex2f(points[i].x, points[i].y); + rlVertex2f(points[i - 2].x, points[i - 2].y); + rlVertex2f(points[i - 1].x, points[i - 1].y); + } + else + { + rlVertex2f(points[i].x, points[i].y); + rlVertex2f(points[i - 1].x, points[i - 1].y); + rlVertex2f(points[i - 2].x, points[i - 2].y); + } + } + rlEnd(); } - - // Function expects (endAngle > startAngle) - if (endAngle < startAngle) - { - // Swap values - float tmp = startAngle; - startAngle = endAngle; - endAngle = tmp; - } - - int minSegments = (int)ceilf((endAngle - startAngle)/90); - - if (segments < minSegments) - { - // Calculate the maximum angle between segments based on the error rate (usually 0.5f) - float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/outerRadius, 2) - 1); - segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); - - if (segments <= 0) segments = minSegments; - } - - if (innerRadius <= 0.0f) - { - DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, color); - return; - } - - float stepLength = (endAngle - startAngle)/(float)segments; - float angle = startAngle; - bool showCapLines = true; - - rlBegin(RL_LINES); - if (showCapLines) - { - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); - } - - for (int i = 0; i < segments; i++) - { - rlColor4ub(color.r, color.g, color.b, color.a); - - rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); - - rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); - - angle += stepLength; - } - - if (showCapLines) - { - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); - } - rlEnd(); } // Draw a color-filled rectangle @@ -1423,130 +1138,6 @@ void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, f } } -// Draw a triangle -// NOTE: Vertex must be provided in counter-clockwise order -void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color) -{ - DrawTriangleGradient(v1, v2, v3, color, color, color); -} - -// Draw triangle with interpolated colors (vertex in counter-clockwise order!) -void DrawTriangleGradient(Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3) -{ -#if SUPPORT_QUADS_DRAW_MODE - rlSetTexture(GetShapesTexture().id); - Rectangle shapeRect = GetShapesTextureRectangle(); - - rlBegin(RL_QUADS); - rlNormal3f(0.0f, 0.0f, 1.0f); - - rlColor4ub(c1.r, c1.g, c1.b, c1.a); - rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); - rlVertex2f(v1.x, v1.y); - - rlColor4ub(c2.r, c2.g, c2.b, c2.a); - rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); - rlVertex2f(v2.x, v2.y); - - rlColor4ub(c3.r, c3.g, c3.b, c3.a); - rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); - rlVertex2f(v3.x, v3.y); - - rlColor4ub(c3.r, c3.g, c3.b, c3.a); - rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); - rlVertex2f(v3.x, v3.y); - rlEnd(); - - rlSetTexture(0); -#else - rlBegin(RL_TRIANGLES); - rlColor4ub(c1.r, c1.g, c1.b, c1.a); - rlVertex2f(v1.x, v1.y); - rlColor4ub(c2.r, c2.g, c2.b, c2.a); - rlVertex2f(v2.x, v2.y); - rlColor4ub(c3.r, c3.g, c3.b, c3.a); - rlVertex2f(v3.x, v3.y); - rlEnd(); -#endif -} - -// Draw a triangle using lines -// NOTE: Vertex must be provided in counter-clockwise order -void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color) -{ - rlBegin(RL_LINES); - rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(v1.x, v1.y); - rlVertex2f(v2.x, v2.y); - - rlVertex2f(v2.x, v2.y); - rlVertex2f(v3.x, v3.y); - - rlVertex2f(v3.x, v3.y); - rlVertex2f(v1.x, v1.y); - rlEnd(); -} - -// Draw a triangle fan defined by points -// NOTE: First vertex provided is the center, shared by all triangles -// By default, following vertex should be provided in counter-clockwise order -void DrawTriangleFan(const Vector2 *points, int pointCount, Color color) -{ - if (pointCount >= 3) - { - rlSetTexture(GetShapesTexture().id); - Rectangle shapeRect = GetShapesTextureRectangle(); - - rlBegin(RL_QUADS); - rlColor4ub(color.r, color.g, color.b, color.a); - - for (int i = 1; i < pointCount - 1; i++) - { - rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); - rlVertex2f(points[0].x, points[0].y); - - rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); - rlVertex2f(points[i].x, points[i].y); - - rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); - rlVertex2f(points[i + 1].x, points[i + 1].y); - - rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); - rlVertex2f(points[i + 1].x, points[i + 1].y); - } - rlEnd(); - rlSetTexture(0); - } -} - -// Draw a triangle strip defined by points -// NOTE: Every new vertex connects with previous two -void DrawTriangleStrip(const Vector2 *points, int pointCount, Color color) -{ - if (pointCount >= 3) - { - rlBegin(RL_TRIANGLES); - rlColor4ub(color.r, color.g, color.b, color.a); - - for (int i = 2; i < pointCount; i++) - { - if ((i%2) == 0) - { - rlVertex2f(points[i].x, points[i].y); - rlVertex2f(points[i - 2].x, points[i - 2].y); - rlVertex2f(points[i - 1].x, points[i - 1].y); - } - else - { - rlVertex2f(points[i].x, points[i].y); - rlVertex2f(points[i - 1].x, points[i - 1].y); - rlVertex2f(points[i - 2].x, points[i - 2].y); - } - } - rlEnd(); - } -} - // Draw a polygon of n sides void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color) { @@ -1670,6 +1261,415 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl #endif } +// Draw a color-filled circle +void DrawCircle(int centerX, int centerY, float radius, Color color) +{ + DrawCircleV((Vector2){ (float)centerX, (float)centerY }, radius, color); +} + +// Draw a color-filled circle (Vector version) +// NOTE: On OpenGL 3.3 and ES2 using QUADS to avoid drawing order issues +void DrawCircleV(Vector2 center, float radius, Color color) +{ + DrawCircleSector(center, radius, 0, 360, 36, color); +} + +// Draw a gradient-filled circle +void DrawCircleGradient(Vector2 center, float radius, Color inner, Color outer) +{ + rlBegin(RL_TRIANGLES); + for (int i = 0; i < 360; i += 10) + { + rlColor4ub(inner.r, inner.g, inner.b, inner.a); + rlVertex2f(center.x, center.y); + rlColor4ub(outer.r, outer.g, outer.b, outer.a); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radius, center.y + sinf(DEG2RAD*(i + 10))*radius); + rlColor4ub(outer.r, outer.g, outer.b, outer.a); + rlVertex2f(center.x + cosf(DEG2RAD*i)*radius, center.y + sinf(DEG2RAD*i)*radius); + } + rlEnd(); +} + +// Draw a piece of a circle +void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color) +{ + if (startAngle == endAngle) return; + if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + float tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + int minSegments = (int)ceilf((endAngle - startAngle)/90); + + if (segments < minSegments) + { + // Calculate the maximum angle between segments based on the error rate (usually 0.5f) + float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1); + segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); + + if (segments <= 0) segments = minSegments; + } + + float stepLength = (endAngle - startAngle)/(float)segments; + float angle = startAngle; + +#if SUPPORT_QUADS_DRAW_MODE + rlSetTexture(GetShapesTexture().id); + Rectangle shapeRect = GetShapesTextureRectangle(); + + rlBegin(RL_QUADS); + + // NOTE: Every QUAD actually represents two segments + for (int i = 0; i < segments/2; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2.0f))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2.0f))*radius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + + angle += (stepLength*2.0f); + } + + // NOTE: In case number of segments is odd, adding one last piece to the cake + if ((((unsigned int)segments)%2) == 1) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x, center.y); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x, center.y); + } + + rlEnd(); + + rlSetTexture(0); +#else + rlBegin(RL_TRIANGLES); + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + + angle += stepLength; + } + rlEnd(); +#endif +} + +// Draw a piece of a circle outlines +void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color) +{ + if (startAngle == endAngle) return; + if (radius <= 0.0f) radius = 0.1f; // Avoid div by zero issue + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + float tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + int minSegments = (int)ceilf((endAngle - startAngle)/90); + + if (segments < minSegments) + { + // Calculate the maximum angle between segments based on the error rate (usually 0.5f) + float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1); + segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); + + if (segments <= 0) segments = minSegments; + } + + float stepLength = (endAngle - startAngle)/(float)segments; + float angle = startAngle; + bool showCapLines = true; + + rlBegin(RL_LINES); + if (showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + } + + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + + angle += stepLength; + } + + if (showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + } + rlEnd(); +} + +// Draw circle outline +void DrawCircleLines(int centerX, int centerY, float radius, Color color) +{ + DrawCircleLinesV((Vector2){ (float)centerX, (float)centerY }, radius, color); +} + +// Draw circle outline (Vector version) +void DrawCircleLinesV(Vector2 center, float radius, Color color) +{ + rlBegin(RL_LINES); + rlColor4ub(color.r, color.g, color.b, color.a); + + // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) + for (int i = 0; i < 360; i += 10) + { + rlVertex2f(center.x + cosf(DEG2RAD*i)*radius, center.y + sinf(DEG2RAD*i)*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radius, center.y + sinf(DEG2RAD*(i + 10))*radius); + } + rlEnd(); +} + +void DrawCircleLinesEx(Vector2 center, float radius, float thick, Color color) +{ + DrawRing(center, radius - thick, radius, 0.0f, 360.0f, 36, color); +} + +// Draw ellipse +void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color) +{ + DrawEllipseV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color); +} + +// Draw ellipse (Vector version) +void DrawEllipseV(Vector2 center, float radiusH, float radiusV, Color color) +{ + rlBegin(RL_TRIANGLES); + for (int i = 0; i < 360; i += 10) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x, center.y); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV); + } + rlEnd(); +} + +// Draw ellipse outline +void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color) +{ + DrawEllipseLinesV((Vector2){ (float)centerX, (float)centerY }, radiusH, radiusV, color); +} + +// Draw ellipse outline +void DrawEllipseLinesV(Vector2 center, float radiusH, float radiusV, Color color) +{ + rlBegin(RL_LINES); + for (int i = 0; i < 360; i += 10) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radiusH, center.y + sinf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f(center.x + cosf(DEG2RAD*i)*radiusH, center.y + sinf(DEG2RAD*i)*radiusV); + } + rlEnd(); +} + +// Draw ring +void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color) +{ + if (startAngle == endAngle) return; + + // Function expects (outerRadius > innerRadius) + if (outerRadius < innerRadius) + { + float tmp = outerRadius; + outerRadius = innerRadius; + innerRadius = tmp; + + if (outerRadius <= 0.0f) outerRadius = 0.1f; + } + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + float tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + int minSegments = (int)ceilf((endAngle - startAngle)/90); + + if (segments < minSegments) + { + // Calculate the maximum angle between segments based on the error rate (usually 0.5f) + float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/outerRadius, 2) - 1); + segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); + + if (segments <= 0) segments = minSegments; + } + + // Not a ring + if (innerRadius <= 0.0f) + { + DrawCircleSector(center, outerRadius, startAngle, endAngle, segments, color); + return; + } + + float stepLength = (endAngle - startAngle)/(float)segments; + float angle = startAngle; + +#if SUPPORT_QUADS_DRAW_MODE + rlSetTexture(GetShapesTexture().id); + Rectangle shapeRect = GetShapesTextureRectangle(); + + rlBegin(RL_QUADS); + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlTexCoord2f(shapeRect.x/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + + rlTexCoord2f(shapeRect.x/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, shapeRect.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlTexCoord2f((shapeRect.x + shapeRect.width)/texShapes.width, (shapeRect.y + shapeRect.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); + + angle += stepLength; + } + rlEnd(); + + rlSetTexture(0); +#else + rlBegin(RL_TRIANGLES); + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + + angle += stepLength; + } + rlEnd(); +#endif +} + +// Draw ring outline +void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color) +{ + if (startAngle == endAngle) return; + + // Function expects (outerRadius > innerRadius) + if (outerRadius < innerRadius) + { + float tmp = outerRadius; + outerRadius = innerRadius; + innerRadius = tmp; + + if (outerRadius <= 0.0f) outerRadius = 0.1f; + } + + // Function expects (endAngle > startAngle) + if (endAngle < startAngle) + { + // Swap values + float tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + int minSegments = (int)ceilf((endAngle - startAngle)/90); + + if (segments < minSegments) + { + // Calculate the maximum angle between segments based on the error rate (usually 0.5f) + float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/outerRadius, 2) - 1); + segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); + + if (segments <= 0) segments = minSegments; + } + + if (innerRadius <= 0.0f) + { + DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, color); + return; + } + + float stepLength = (endAngle - startAngle)/(float)segments; + float angle = startAngle; + bool showCapLines = true; + + rlBegin(RL_LINES); + if (showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + } + + for (int i = 0; i < segments; i++) + { + rlColor4ub(color.r, color.g, color.b, color.a); + + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); + + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + + angle += stepLength; + } + + if (showCapLines) + { + rlColor4ub(color.r, color.g, color.b, color.a); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + } + rlEnd(); +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Splines functions //---------------------------------------------------------------------------------- diff --git a/src/rtextures.c b/src/rtextures.c index 103309a19..69b743a71 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3618,144 +3618,6 @@ void ImageDrawLineStrip(Image *dst, const Vector2 *points, int pointCount, Color } } -// Draw circle within an image -void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color) -{ - int x = 0; - int y = radius; - int decesionParameter = 3 - 2*radius; - - while (y >= x) - { - ImageDrawRectangle(dst, centerX - x, centerY + y, x*2, 1, color); - ImageDrawRectangle(dst, centerX - x, centerY - y, x*2, 1, color); - ImageDrawRectangle(dst, centerX - y, centerY + x, y*2, 1, color); - ImageDrawRectangle(dst, centerX - y, centerY - x, y*2, 1, color); - x++; - - if (decesionParameter > 0) - { - y--; - decesionParameter = decesionParameter + 4*(x - y) + 10; - } - else decesionParameter = decesionParameter + 4*x + 6; - } -} - -// Draw circle within an image (Vector version) -void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color) -{ - ImageDrawCircle(dst, (int)center.x, (int)center.y, radius, color); -} - -// Draw circle outline within an image -void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color) -{ - int x = 0; - int y = radius; - int decesionParameter = 3 - 2*radius; - - while (y >= x) - { - ImageDrawPixel(dst, centerX + x, centerY + y, color); - ImageDrawPixel(dst, centerX - x, centerY + y, color); - ImageDrawPixel(dst, centerX + x, centerY - y, color); - ImageDrawPixel(dst, centerX - x, centerY - y, color); - ImageDrawPixel(dst, centerX + y, centerY + x, color); - ImageDrawPixel(dst, centerX - y, centerY + x, color); - ImageDrawPixel(dst, centerX + y, centerY - x, color); - ImageDrawPixel(dst, centerX - y, centerY - x, color); - x++; - - if (decesionParameter > 0) - { - y--; - decesionParameter = decesionParameter + 4*(x - y) + 10; - } - else decesionParameter = decesionParameter + 4*x + 6; - } -} - -// Draw circle outline within an image (Vector version) -void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color) -{ - ImageDrawCircleLines(dst, (int)center.x, (int)center.y, radius, color); -} - -// Draw rectangle within an image -void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color) -{ - ImageDrawRectangleRec(dst, (Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color); -} - -// Draw rectangle within an image (Vector version) -void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color) -{ - ImageDrawRectangle(dst, (int)position.x, (int)position.y, (int)size.x, (int)size.y, color); -} - -// Draw rectangle within an image -void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) -{ - // Security check to avoid program crash - if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return; - - // Security check to avoid drawing out of bounds in case of bad user data - if (rec.x < 0) { rec.width += rec.x; rec.x = 0; } - if (rec.y < 0) { rec.height += rec.y; rec.y = 0; } - if (rec.width < 0) rec.width = 0; - if (rec.height < 0) rec.height = 0; - - // Clamp the size the the image bounds - if ((rec.x + rec.width) >= dst->width) rec.width = dst->width - rec.x; - if ((rec.y + rec.height) >= dst->height) rec.height = dst->height - rec.y; - - // Check if the rect is even inside the image - if ((rec.x >= dst->width) || (rec.y >= dst->height)) return; - if (((rec.x + rec.width) <= 0) || (rec.y + rec.height <= 0)) return; - - int sy = (int)rec.y; - int sx = (int)rec.x; - - int bytesPerPixel = GetPixelDataSize(1, 1, dst->format); - - // Fill in the first pixel of the first row based on image format - ImageDrawPixel(dst, sx, sy, color); - - int bytesOffset = ((sy*dst->width) + sx)*bytesPerPixel; - unsigned char *pSrcPixel = (unsigned char *)dst->data + bytesOffset; - - // Repeat the first pixel data throughout the row - for (int x = 1; x < (int)rec.width; x *= 2) - { - int pixelsToCopy = MIN(x, (int)rec.width - x); - memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, pixelsToCopy*bytesPerPixel); - } - - // Repeat the first row data for all other rows - int bytesPerRow = bytesPerPixel*(int)rec.width; - for (int y = 1; y < (int)rec.height; y++) - { - memcpy(pSrcPixel + (y*dst->width)*bytesPerPixel, pSrcPixel, bytesPerRow); - } -} - -// Draw rectangle lines within an image -void ImageDrawRectangleLines(Image *dst, int posX, int posY, int width, int height, Color color) -{ - Rectangle rec = { posX, posY, width, height }; - ImageDrawRectangleLinesEx(dst, rec, 1, color); -} - -// Draw rectangle lines within an image with line thickness -void ImageDrawRectangleLinesEx(Image *dst, Rectangle rec, int thick, Color color) -{ - ImageDrawRectangle(dst, (int)rec.x, (int)rec.y, (int)rec.width, thick, color); - ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + thick), thick, (int)(rec.height - thick*2), color); - ImageDrawRectangle(dst, (int)(rec.x + rec.width - thick), (int)(rec.y + thick), thick, (int)(rec.height - thick*2), 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) { @@ -3941,6 +3803,144 @@ void ImageDrawTriangleStrip(Image *dst, const Vector2 *points, int pointCount, C } } +// Draw rectangle within an image +void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color) +{ + ImageDrawRectangleRec(dst, (Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color); +} + +// Draw rectangle within an image (Vector version) +void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color) +{ + ImageDrawRectangle(dst, (int)position.x, (int)position.y, (int)size.x, (int)size.y, color); +} + +// Draw rectangle within an image +void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) +{ + // Security check to avoid program crash + if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return; + + // Security check to avoid drawing out of bounds in case of bad user data + if (rec.x < 0) { rec.width += rec.x; rec.x = 0; } + if (rec.y < 0) { rec.height += rec.y; rec.y = 0; } + if (rec.width < 0) rec.width = 0; + if (rec.height < 0) rec.height = 0; + + // Clamp the size the the image bounds + if ((rec.x + rec.width) >= dst->width) rec.width = dst->width - rec.x; + if ((rec.y + rec.height) >= dst->height) rec.height = dst->height - rec.y; + + // Check if the rect is even inside the image + if ((rec.x >= dst->width) || (rec.y >= dst->height)) return; + if (((rec.x + rec.width) <= 0) || (rec.y + rec.height <= 0)) return; + + int sy = (int)rec.y; + int sx = (int)rec.x; + + int bytesPerPixel = GetPixelDataSize(1, 1, dst->format); + + // Fill in the first pixel of the first row based on image format + ImageDrawPixel(dst, sx, sy, color); + + int bytesOffset = ((sy*dst->width) + sx)*bytesPerPixel; + unsigned char *pSrcPixel = (unsigned char *)dst->data + bytesOffset; + + // Repeat the first pixel data throughout the row + for (int x = 1; x < (int)rec.width; x *= 2) + { + int pixelsToCopy = MIN(x, (int)rec.width - x); + memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, pixelsToCopy*bytesPerPixel); + } + + // Repeat the first row data for all other rows + int bytesPerRow = bytesPerPixel*(int)rec.width; + for (int y = 1; y < (int)rec.height; y++) + { + memcpy(pSrcPixel + (y*dst->width)*bytesPerPixel, pSrcPixel, bytesPerRow); + } +} + +// Draw rectangle lines within an image +void ImageDrawRectangleLines(Image *dst, int posX, int posY, int width, int height, Color color) +{ + Rectangle rec = { posX, posY, width, height }; + ImageDrawRectangleLinesEx(dst, rec, 1, color); +} + +// Draw rectangle lines within an image with line thickness +void ImageDrawRectangleLinesEx(Image *dst, Rectangle rec, int thick, Color color) +{ + ImageDrawRectangle(dst, (int)rec.x, (int)rec.y, (int)rec.width, thick, color); + ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + thick), thick, (int)(rec.height - thick*2), color); + ImageDrawRectangle(dst, (int)(rec.x + rec.width - thick), (int)(rec.y + thick), thick, (int)(rec.height - thick*2), color); + ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + rec.height - thick), (int)rec.width, thick, color); +} + +// Draw circle within an image +void ImageDrawCircle(Image *dst, int centerX, int centerY, int radius, Color color) +{ + int x = 0; + int y = radius; + int decesionParameter = 3 - 2*radius; + + while (y >= x) + { + ImageDrawRectangle(dst, centerX - x, centerY + y, x*2, 1, color); + ImageDrawRectangle(dst, centerX - x, centerY - y, x*2, 1, color); + ImageDrawRectangle(dst, centerX - y, centerY + x, y*2, 1, color); + ImageDrawRectangle(dst, centerX - y, centerY - x, y*2, 1, color); + x++; + + if (decesionParameter > 0) + { + y--; + decesionParameter = decesionParameter + 4*(x - y) + 10; + } + else decesionParameter = decesionParameter + 4*x + 6; + } +} + +// Draw circle within an image (Vector version) +void ImageDrawCircleV(Image *dst, Vector2 center, int radius, Color color) +{ + ImageDrawCircle(dst, (int)center.x, (int)center.y, radius, color); +} + +// Draw circle outline within an image +void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color) +{ + int x = 0; + int y = radius; + int decesionParameter = 3 - 2*radius; + + while (y >= x) + { + ImageDrawPixel(dst, centerX + x, centerY + y, color); + ImageDrawPixel(dst, centerX - x, centerY + y, color); + ImageDrawPixel(dst, centerX + x, centerY - y, color); + ImageDrawPixel(dst, centerX - x, centerY - y, color); + ImageDrawPixel(dst, centerX + y, centerY + x, color); + ImageDrawPixel(dst, centerX - y, centerY + x, color); + ImageDrawPixel(dst, centerX + y, centerY - x, color); + ImageDrawPixel(dst, centerX - y, centerY - x, color); + x++; + + if (decesionParameter > 0) + { + y--; + decesionParameter = decesionParameter + 4*(x - y) + 10; + } + else decesionParameter = decesionParameter + 4*x + 6; + } +} + +// Draw circle outline within an image (Vector version) +void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color) +{ + ImageDrawCircleLines(dst, (int)center.x, (int)center.y, radius, 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)