From 1fc30722753917487fc5f9f5669155cfce01926b Mon Sep 17 00:00:00 2001 From: Bruno Cabral Date: Mon, 24 Jun 2024 08:50:51 -0700 Subject: [PATCH] created ImageMaskFromImage and LoadTextureMaskFromImage These functions adds the possibility to retrieve a mask from the alpha channel of an image. --- examples/textures/textures_image_mask.c | 68 ++++++++++++++++++++++ src/raylib.h | 2 + src/rtextures.c | 75 ++++++++++++++++++++----- 3 files changed, 130 insertions(+), 15 deletions(-) create mode 100644 examples/textures/textures_image_mask.c diff --git a/examples/textures/textures_image_mask.c b/examples/textures/textures_image_mask.c new file mode 100644 index 000000000..137e38d5e --- /dev/null +++ b/examples/textures/textures_image_mask.c @@ -0,0 +1,68 @@ +/******************************************************************************************* +* +* raylib [textures] example - Retrive image mask from alpha values +* +* NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) +* +* Example originally created with raylib 5.1-dev, last time updated with raylib 5.1-dev +* +* Example contributed by Bruno Cabral (github.com/brccabral) and reviewed by Ramon Santamaria (@raysan5) +* +* 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) 2024-2024 Bruno Cabral (github.com/brccabral) and Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 612; + + InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw rectangle rounded"); + + Image fudesumi_image = LoadImage("resources/fudesumi.png"); + Texture2D fudesumi_texture = LoadTextureFromImage(fudesumi_image); + Texture2D fudesumi_mask = LoadTextureMaskFromImage(fudesumi_image, RED, 0.0f); + + Image background_image = GenImageChecked(screenWidth, screenHeight, screenWidth/20, screenHeight/20, BLUE, SKYBLUE); + Texture2D background_texture = LoadTextureFromImage(background_image); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + DrawTexture(background_texture, 0, 0, WHITE); + DrawTexture(fudesumi_texture, 50, 50, WHITE); + DrawTexture(fudesumi_mask, 410, 50, WHITE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadImage(fudesumi_image); + UnloadImage(background_image); + UnloadTexture(fudesumi_texture); + UnloadTexture(fudesumi_mask); + UnloadTexture(background_texture); + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/src/raylib.h b/src/raylib.h index 7195fd55f..f1bd2f46b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1334,6 +1334,7 @@ RLAPI Image ImageCopy(Image image); RLAPI Image ImageFromImage(Image image, Rectangle rec); // Create an image from another image piece RLAPI Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) RLAPI Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint); // Create an image from text (custom sprite font) +RLAPI Image ImageMaskFromImage(Image image, Color color, float threshold); // Create an image mask from an image (alpha channel) RLAPI void ImageFormat(Image *image, int newFormat); // Convert image data to desired format RLAPI void ImageToPOT(Image *image, Color fill); // Convert image to POT (power-of-two) RLAPI void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle @@ -1394,6 +1395,7 @@ RLAPI void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 posi // NOTE: These functions require GPU access RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data +RLAPI Texture2D LoadTextureMaskFromImage(Image image, Color color, float threshold); // Load texture mask from image data (alpha channel) RLAPI TextureCubemap LoadTextureCubemap(Image image, int layout); // Load cubemap from image, multiple image cubemap layouts supported RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) RLAPI bool IsTextureReady(Texture2D texture); // Check if a texture is ready diff --git a/src/rtextures.c b/src/rtextures.c index 39c0f78f2..6fd35a44e 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1630,6 +1630,44 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co return imText; } +Image ImageMaskFromImage(const Image image, const Color color, float threshold) +{ + Image mask = { 0 }; + + // Security check to avoid program crash + if ((image.data == NULL) || (image.width == 0) || (image.height == 0)) + return mask; + + // Set threshold to at least 0.0f + if (threshold < 0.0f) { + threshold = 0.0f; + } + + Vector4 *pixels = LoadImageDataNormalized(image); + + mask.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + mask.height = image.height; + mask.width = image.width; + mask.mipmaps = 1; + + Color *new_pixels = (Color *) RL_CALLOC(image.width * image.height, sizeof(Color)); + + for (int i = 0; i < mask.width * mask.height; i++) + { + if (pixels[i].w > threshold) + new_pixels[i] = color; + else + new_pixels[i] = (Color) { 0 }; + } + + mask.data = new_pixels; + + RL_FREE(pixels); + pixels = NULL; + + return mask; +} + // Resize and image to new size using Nearest-Neighbor scaling algorithm void ImageResizeNN(Image *image,int newWidth,int newHeight) { @@ -3979,22 +4017,29 @@ Texture2D LoadTexture(const char *fileName) // Load a texture from image data // NOTE: image is not unloaded, it must be done manually -Texture2D LoadTextureFromImage(Image image) +Texture2D LoadTextureFromImage(Image image) { + Texture2D texture = {0}; + + if ((image.width != 0) && (image.height != 0)) { + texture.id = rlLoadTexture(image.data, image.width, image.height, + image.format, image.mipmaps); + } else + TRACELOG(LOG_WARNING, "IMAGE: Data is not valid to load texture"); + + texture.width = image.width; + texture.height = image.height; + texture.mipmaps = image.mipmaps; + texture.format = image.format; + + return texture; +} + +Texture2D LoadTextureMaskFromImage(Image image, Color color, float threshold) { - Texture2D texture = { 0 }; - - if ((image.width != 0) && (image.height != 0)) - { - texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps); - } - else TRACELOG(LOG_WARNING, "IMAGE: Data is not valid to load texture"); - - texture.width = image.width; - texture.height = image.height; - texture.mipmaps = image.mipmaps; - texture.format = image.format; - - return texture; + const Image mask = ImageMaskFromImage(image, color, threshold); + const Texture result = LoadTextureFromImage(mask); + UnloadImage(mask); + return result; } // Load cubemap from image, multiple image cubemap layouts supported