diff --git a/examples/textures/resources/ninepatch_button.png b/examples/textures/resources/ninepatch_button.png new file mode 100644 index 000000000..d1b599707 Binary files /dev/null and b/examples/textures/resources/ninepatch_button.png differ diff --git a/examples/textures/textures_image_9patch.c b/examples/textures/textures_image_9patch.c new file mode 100644 index 000000000..74eda6ad4 --- /dev/null +++ b/examples/textures/textures_image_9patch.c @@ -0,0 +1,108 @@ +/******************************************************************************************* +* +* raylib [textures] example - 9-patch drawing +* +* NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) +* +* This example has been created using raylib 2.0 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2016 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + int screenWidth = 800; + int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - 9-patch drawing"); + + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + Texture2D nPatchTexture = LoadTexture("resources/ninepatch_button.png"); + Vector2 mousePosition; + Vector2 origin = {0.0f, 0.0f}; + + // The location and size of the n-patches. + Rectangle dstRec1 = {480.0f, 160.0f, 32.0f, 32.0f}; + Rectangle dstRec2 = {160.0f, 160.0f, 32.0f, 32.0f}; + Rectangle dstRecH = {160.0f, 93.0f, 32.0f, 32.0f}; // this rec's height is ignored + Rectangle dstRecV = {92.0f, 160.0f, 32.0f, 32.0f}; // this rec's width is ignored + + // A 9-patch (NPT_9PATCH) changes its sizes in both axis + NPatchInfo ninePatchInfo1 = {(Rectangle){0.0f, 0.0f, 64.0f, 64.0f}, 12, 40, 12, 12, NPT_9PATCH }; + NPatchInfo ninePatchInfo2 = {(Rectangle){0.0f, 128.0f, 64.0f, 64.0f}, 16, 16, 16, 16, NPT_9PATCH }; + // A horizontal 3-patch (NPT_3PATCH_HORIZONTAL) changes its sizes along the x axis only + NPatchInfo h3PatchInfo = {(Rectangle){0.0f, 64.0f, 64.0f, 64.0f}, 8, 8, 8, 8, NPT_3PATCH_HORIZONTAL }; + // A vertical 3-patch (NPT_3PATCH_VERTICAL) changes its sizes along the y axis only + NPatchInfo v3PatchInfo = {(Rectangle){0.0f, 192.0f, 64.0f, 64.0f}, 6, 6, 6, 6, NPT_3PATCH_VERTICAL }; + + SetTargetFPS(60); + //--------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + mousePosition = GetMousePosition(); + // resize the n-patches based on mouse position. + dstRec1.width = mousePosition.x - dstRec1.x; + dstRec1.height = mousePosition.y - dstRec1.y; + dstRec2.width = mousePosition.x - dstRec2.x; + dstRec2.height = mousePosition.y - dstRec2.y; + dstRecH.width = mousePosition.x - dstRecH.x; + dstRecV.height = mousePosition.y - dstRecV.y; + + // set a minimum width and/or height + if (dstRec1.width < 1.0f) dstRec1.width = 1.0f; + if (dstRec1.width > 300.0f) dstRec1.width = 300.0f; + if (dstRec1.height < 1.0f) dstRec1.height = 1.0f; + if (dstRec2.width < 1.0f) dstRec2.width = 1.0f; + if (dstRec2.width > 300.0f) dstRec2.width = 300.0f; + if (dstRec2.height < 1.0f) dstRec2.height = 1.0f; + if (dstRecH.width < 1.0f) dstRecH.width = 1.0f; + if (dstRecV.height < 1.0f) dstRecV.height = 1.0f; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // Draw the n-patches + DrawTextureNPatch(nPatchTexture, ninePatchInfo2, dstRec2, origin, 0.0f, WHITE); + DrawTextureNPatch(nPatchTexture, ninePatchInfo1, dstRec1, origin, 0.0f, WHITE); + DrawTextureNPatch(nPatchTexture, h3PatchInfo, dstRecH, origin, 0.0f, WHITE); + DrawTextureNPatch(nPatchTexture, v3PatchInfo, dstRecV, origin, 0.0f, WHITE); + + // Draw the source texture + DrawRectangleLines( 5, 88, 74, 266, BLUE); + DrawTexture(nPatchTexture, 10, 93, WHITE); + DrawText("TEXTURE", 15, 360, 10, DARKGRAY); + + DrawRectangle( 10, 10, 250, 73, Fade(SKYBLUE, 0.5)); + DrawRectangleLines( 10, 10, 250, 73, BLUE); + + DrawText("9-Patch and 3-Patch example", 20, 20, 10, BLACK); + DrawText(" Move the mouse to stretch or", 40, 40, 10, DARKGRAY); + DrawText(" shrink the n-patches.", 40, 60, 10, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(nPatchTexture); // Texture unloading + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/textures/textures_image_9patch.png b/examples/textures/textures_image_9patch.png new file mode 100644 index 000000000..5258811b4 Binary files /dev/null and b/examples/textures/textures_image_9patch.png differ diff --git a/src/raylib.h b/src/raylib.h index 8d1389f73..af31e7794 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -412,6 +412,15 @@ typedef struct RenderTexture2D { // RenderTexture type, same as RenderTexture2D typedef RenderTexture2D RenderTexture; +typedef struct NPatchInfo { + Rectangle sourceRec; // Region in the texture + int left; // left border offset + int top; // top border offset + int right; // right border offset + int bottom; // bottom border offset + int type; // layout of the n-patch: 3x3, 1x3 or 3x1 +} NPatchInfo; + // Font character info typedef struct CharInfo { int value; // Character value (Unicode) @@ -729,6 +738,13 @@ typedef enum { HMD_SONY_PSVR } VrDeviceType; +// Type of n-patch +typedef enum { + NPT_9PATCH = 0, // 3x3 + NPT_3PATCH_VERTICAL, // 1x3 + NPT_3PATCH_HORIZONTAL // 3x1 +} NPatchType; + // Callbacks to be implemented by users typedef void (*TraceLogCallback)(int msgType, const char *text, va_list args); @@ -999,6 +1015,7 @@ RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); RLAPI void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint); // Draw a Texture2D with extended parameters RLAPI void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint); // Draw a part of a texture defined by a rectangle RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, Vector2 origin, float rotation, Color tint); // Draw a part of a texture defined by a rectangle with 'pro' parameters +RLAPI void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destRec, Vector2 origin, float rotation, Color tint); // Draws a texture (or part of it) that stretches or shrinks nicely. //------------------------------------------------------------------------------------ // Font Loading and Text Drawing Functions (Module: text) diff --git a/src/textures.c b/src/textures.c index 1d0520707..700b4be94 100644 --- a/src/textures.c +++ b/src/textures.c @@ -2331,6 +2331,202 @@ void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle destRec, V } } +void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle destRec, Vector2 origin, float rotation, Color tint) +{ + if (texture.id > 0) + { + float width = (float)texture.width; + float height = (float)texture.height; + + float patchWidth = (destRec.width <= 0.0f)? 0.0f : destRec.width; + float patchHeight = (destRec.height <= 0.0f)? 0.0f : destRec.height; + + if (nPatchInfo.sourceRec.width < 0) nPatchInfo.sourceRec.x -= nPatchInfo.sourceRec.width; + if (nPatchInfo.sourceRec.height < 0) nPatchInfo.sourceRec.y -= nPatchInfo.sourceRec.height; + if (nPatchInfo.type == NPT_3PATCH_HORIZONTAL) patchHeight = nPatchInfo.sourceRec.height; + if (nPatchInfo.type == NPT_3PATCH_VERTICAL) patchWidth = nPatchInfo.sourceRec.width; + + bool drawCenter = true; + bool drawMiddle = true; + float leftBorder = (float)nPatchInfo.left; + float topBorder = (float)nPatchInfo.top; + float rightBorder = (float)nPatchInfo.right; + float bottomBorder = (float)nPatchInfo.bottom; + + // adjust the lateral (left and right) border widths in case patchWidth < texture.width + if (patchWidth <= (leftBorder + rightBorder) && nPatchInfo.type != NPT_3PATCH_VERTICAL) + { + drawCenter = false; + leftBorder = (leftBorder / (leftBorder + rightBorder)) * patchWidth; + rightBorder = patchWidth - leftBorder; + } + // adjust the lateral (top and bottom) border heights in case patchHeight < texture.height + if (patchHeight <= (topBorder + bottomBorder) && nPatchInfo.type != NPT_3PATCH_HORIZONTAL) + { + drawMiddle = false; + topBorder = (topBorder / (topBorder + bottomBorder)) * patchHeight; + bottomBorder = patchHeight - topBorder; + } + + Vector2 vertA, vertB, vertC, vertD; + vertA.x = 0.0f; // outer left + vertA.y = 0.0f; // outer top + vertB.x = leftBorder; // inner left + vertB.y = topBorder; // inner top + vertC.x = patchWidth - rightBorder; // inner right + vertC.y = patchHeight - bottomBorder; // inner bottom + vertD.x = patchWidth; // outer right + vertD.y = patchHeight; // outer bottom + + Vector2 coordA, coordB, coordC, coordD; + coordA.x = nPatchInfo.sourceRec.x / width; + coordA.y = nPatchInfo.sourceRec.y / height; + coordB.x = (nPatchInfo.sourceRec.x + leftBorder) / width; + coordB.y = (nPatchInfo.sourceRec.y + topBorder) / height; + coordC.x = (nPatchInfo.sourceRec.x + nPatchInfo.sourceRec.width - rightBorder) / width; + coordC.y = (nPatchInfo.sourceRec.y + nPatchInfo.sourceRec.height - bottomBorder) / height; + coordD.x = (nPatchInfo.sourceRec.x + nPatchInfo.sourceRec.width) / width; + coordD.y = (nPatchInfo.sourceRec.y + nPatchInfo.sourceRec.height) / height; + + rlEnableTexture(texture.id); + + rlPushMatrix(); + rlTranslatef(destRec.x, destRec.y, 0); + rlRotatef(rotation, 0, 0, 1); + rlTranslatef(-origin.x, -origin.y, 0); + + rlBegin(RL_QUADS); + rlColor4ub(tint.r, tint.g, tint.b, tint.a); + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal vector pointing towards viewer + + if (nPatchInfo.type == NPT_9PATCH) + { + // ------------------------------------------------------------ + // TOP-LEFT QUAD + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad + if (drawCenter) + { + // TOP-CENTER QUAD + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-left corner for texture and quad + } + // TOP-RIGHT QUAD + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-left corner for texture and quad + if (drawMiddle) + { + // ------------------------------------------------------------ + // MIDDLE-LEFT QUAD + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Top-left corner for texture and quad + if (drawCenter) + { + // MIDDLE-CENTER QUAD + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y); // Top-left corner for texture and quad + } + + // MIDDLE-RIGHT QUAD + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y); // Top-left corner for texture and quad + } + + // ------------------------------------------------------------ + // BOTTOM-LEFT QUAD + rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Top-left corner for texture and quad + if (drawCenter) + { + // BOTTOM-CENTER QUAD + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y); // Top-left corner for texture and quad + } + + // BOTTOM-RIGHT QUAD + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y); // Top-left corner for texture and quad + } + else if (nPatchInfo.type == NPT_3PATCH_VERTICAL) + { + // TOP QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad + if (drawCenter) + { + // MIDDLE QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y); // Top-left corner for texture and quad + } + // BOTTOM QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y); // Top-left corner for texture and quad + } + else if (nPatchInfo.type == NPT_3PATCH_HORIZONTAL) + { + // LEFT QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y); // Top-left corner for texture and quad + if (drawCenter) + { + // CENTER QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y); // Top-left corner for texture and quad + } + // RIGHT QUAD + // ----------------------------------------------------------- + // Texture coords Vertices + rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y); // Bottom-left corner for texture and quad + rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y); // Bottom-right corner for texture and quad + rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y); // Top-right corner for texture and quad + rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y); // Top-left corner for texture and quad + } + rlEnd(); + rlPopMatrix(); + + rlDisableTexture(); + + } +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //----------------------------------------------------------------------------------