diff --git a/examples/Makefile b/examples/Makefile
index cc9ef2395..c2ea35bf4 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -453,6 +453,7 @@ TEXTURES = \
textures/textures_image_generation \
textures/textures_image_loading \
textures/textures_image_processing \
+ textures/textures_image_rotate \
textures/textures_image_text \
textures/textures_to_image \
textures/textures_raw_data \
diff --git a/examples/textures/textures_image_rotate.c b/examples/textures/textures_image_rotate.c
new file mode 100644
index 000000000..bcff14297
--- /dev/null
+++ b/examples/textures/textures_image_rotate.c
@@ -0,0 +1,79 @@
+/*******************************************************************************************
+*
+* raylib [textures] example - Image Rotation
+*
+* Example originally created with raylib 1.0, last time updated with raylib 1.0
+*
+* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software
+*
+* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+
+#define NUM_TEXTURES 3
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture loading and drawing");
+
+ // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
+ Image image45 = LoadImage("resources/raylib_logo.png");
+ Image image90 = LoadImage("resources/raylib_logo.png");
+ Image imageNeg90 = LoadImage("resources/raylib_logo.png");
+
+ ImageRotate(&image45, 45);
+ ImageRotate(&image90, 90);
+ ImageRotate(&imageNeg90, -90);
+
+ Texture2D textures[NUM_TEXTURES] = { 0 };
+
+ textures[0] = LoadTextureFromImage(image45);
+ textures[1] = LoadTextureFromImage(image90);
+ textures[2] = LoadTextureFromImage(imageNeg90);
+
+ int currentTexture = 0;
+ //---------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) || IsKeyPressed(KEY_RIGHT))
+ {
+ currentTexture = (currentTexture + 1)%NUM_TEXTURES; // Cycle between the textures
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(RAYWHITE);
+
+ DrawTexture(textures[currentTexture], screenWidth/2 - textures[currentTexture].width/2, screenHeight/2 - textures[currentTexture].height/2, WHITE);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ for (int i = 0; i < NUM_TEXTURES; i++) UnloadTexture(textures[i]);
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
\ No newline at end of file
diff --git a/examples/textures/textures_image_rotate.png b/examples/textures/textures_image_rotate.png
new file mode 100644
index 000000000..64bbc54e4
Binary files /dev/null and b/examples/textures/textures_image_rotate.png differ
diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml
index 85b3e3e4c..ced109eaf 100644
--- a/parser/output/raylib_api.xml
+++ b/parser/output/raylib_api.xml
@@ -1685,6 +1685,10 @@
+
+
+
+
diff --git a/projects/Geany/raylib.c.tags b/projects/Geany/raylib.c.tags
index 4cc6d4605..79a192a14 100644
--- a/projects/Geany/raylib.c.tags
+++ b/projects/Geany/raylib.c.tags
@@ -202,6 +202,7 @@ ImageDrawText|void|(Image *dst, Vector2 position, const char *text, int fontSize
ImageDrawTextEx|void|(Image *dst, Vector2 position, Font font, const char *text, float fontSize, float spacing, Color color);|
ImageFlipVertical|void|(Image *image);|
ImageFlipHorizontal|void|(Image *image);|
+ImageRotate|void|(Image *image, int degrees);|
ImageRotateCW|void|(Image *image);|
ImageRotateCCW|void|(Image *image);|
ImageColorTint|void|(Image *image, Color color);|
diff --git a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml
index 7e173a936..883183cef 100644
--- a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml
+++ b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml
@@ -1570,6 +1570,12 @@
+
+
+
+
+
+
diff --git a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h
index f20f065e3..c32cdb26e 100644
--- a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h
+++ b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h
@@ -346,6 +346,7 @@ RLAPI void ImageMipmaps(Image *image);
RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
RLAPI void ImageFlipVertical(Image *image); // Flip image vertically
RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally
+RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359)
RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg
RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg
RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint
diff --git a/src/raylib.h b/src/raylib.h
index 703e70c21..b2b825831 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -1268,6 +1268,7 @@ RLAPI void ImageMipmaps(Image *image);
RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
RLAPI void ImageFlipVertical(Image *image); // Flip image vertically
RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally
+RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359)
RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg
RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg
RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint
diff --git a/src/rtextures.c b/src/rtextures.c
index eeeebba82..1134357bf 100644
--- a/src/rtextures.c
+++ b/src/rtextures.c
@@ -2118,6 +2118,65 @@ void ImageFlipHorizontal(Image *image)
}
}
+// Rotate image in degrees
+void ImageRotate(Image *image, int degrees)
+{
+ // Security check to avoid program crash
+ if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
+
+ if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
+ if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
+ else
+ {
+ double rad = degrees * PI / 180.0;
+ double sin_rad = sin(rad);
+ double cos_rad = cos(rad);
+
+ int new_width = abs(image->width * cos_rad) + abs(image->height * sin_rad);
+ int new_height = abs(image->height * cos_rad) + abs(image->width * sin_rad);
+
+ int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
+ unsigned char *rotatedData = (unsigned char *)RL_CALLOC(new_width * new_height, bytesPerPixel);
+
+ for (int y = 0; y < new_height; y++)
+ {
+ for (int x = 0; x < new_width; x++)
+ {
+ double oldX = ((x - new_width / 2.0) * cos_rad + (y - new_height / 2.0) * sin_rad) + image->width / 2.0;
+ double oldY = ((y - new_height / 2.0) * cos_rad - (x - new_width / 2.0) * sin_rad) + image->height / 2.0;
+
+ if (oldX >= 0 && oldX < image->width && oldY >= 0 && oldY < image->height)
+ {
+ int x1 = (int)floor(oldX);
+ int y1 = (int)floor(oldY);
+ int x2 = MIN(x1 + 1, image->width - 1);
+ int y2 = MIN(y1 + 1, image->height - 1);
+
+ double px = oldX - x1;
+ double py = oldY - y1;
+
+ for (int i = 0; i < bytesPerPixel; i++)
+ {
+ double f1 = ((unsigned char *)image->data)[(y1 * image->width + x1) * bytesPerPixel + i];
+ double f2 = ((unsigned char *)image->data)[(y1 * image->width + x2) * bytesPerPixel + i];
+ double f3 = ((unsigned char *)image->data)[(y2 * image->width + x1) * bytesPerPixel + i];
+ double f4 = ((unsigned char *)image->data)[(y2 * image->width + x2) * bytesPerPixel + i];
+
+ double val = f1 * (1 - px) * (1 - py) + f2 * px * (1 - py) + f3 * (1 - px) * py + f4 * px * py;
+
+ rotatedData[(y * new_width + x) * bytesPerPixel + i] = (unsigned char)val;
+ }
+ }
+ }
+ }
+
+ RL_FREE(image->data);
+ image->data = rotatedData;
+ image->width = new_width;
+ image->height = new_height;
+ }
+}
+
// Rotate image clockwise 90deg
void ImageRotateCW(Image *image)
{