Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions examples/textures/textures_image_channel.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*******************************************************************************************
*
* raylib [textures] example - Retrive image channel (mask)
*
* 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 <raylib.h>

//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------

const int screenWidth = 800;
const int screenHeight = 450;

InitWindow(screenWidth, screenHeight, "raylib [textures] example - extract channel from image");

Image fudesumiImage = LoadImage("resources/fudesumi.png");

Image imageAlpha = ImageFromChannel(fudesumiImage, 3);
ImageAlphaMask(&imageAlpha, imageAlpha);

Image imageRed = ImageFromChannel(fudesumiImage, 0);
ImageAlphaMask(&imageRed, imageAlpha);

Image imageGreen = ImageFromChannel(fudesumiImage, 1);
ImageAlphaMask(&imageGreen, imageAlpha);

Image imageBlue = ImageFromChannel(fudesumiImage, 2);
ImageAlphaMask(&imageBlue, imageAlpha);

Image backgroundImage = GenImageChecked(screenWidth, screenHeight, screenWidth/20, screenHeight/20, ORANGE, YELLOW);

Texture2D fudesumiTexture = LoadTextureFromImage(fudesumiImage);
Texture2D textureAlpha = LoadTextureFromImage(imageAlpha);
Texture2D textureRed = LoadTextureFromImage(imageRed);
Texture2D textureGreen = LoadTextureFromImage(imageGreen);
Texture2D textureBlue = LoadTextureFromImage(imageBlue);
Texture2D backgroundTexture = LoadTextureFromImage(backgroundImage);

UnloadImage(fudesumiImage);
UnloadImage(imageAlpha);
UnloadImage(imageRed);
UnloadImage(imageGreen);
UnloadImage(imageBlue);
UnloadImage(backgroundImage);

SetTargetFPS(60); // Set our game to run at 60 frames-per-second

Rectangle fudesumiRec = {0, 0, fudesumiImage.width, fudesumiImage.height};

Rectangle fudesumiPos = {50, 10, fudesumiImage.width*0.8f, fudesumiImage.height*0.8f};
Rectangle redPos = { 410, 10, fudesumiPos.width / 2, fudesumiPos.height / 2 };
Rectangle greenPos = { 600, 10, fudesumiPos.width / 2, fudesumiPos.height / 2 };
Rectangle bluePos = { 410, 230, fudesumiPos.width / 2, fudesumiPos.height / 2 };
Rectangle alphaPos = { 600, 230, fudesumiPos.width / 2, fudesumiPos.height / 2 };

//--------------------------------------------------------------------------------------

// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();

DrawTexture(backgroundTexture, 0, 0, WHITE);
DrawTexturePro(fudesumiTexture, fudesumiRec, fudesumiPos, (Vector2) {0, 0}, 0, WHITE);

DrawTexturePro(textureRed, fudesumiRec, redPos, (Vector2) {0, 0}, 0, RED);
DrawTexturePro(textureGreen, fudesumiRec, greenPos, (Vector2) {0, 0}, 0, GREEN);
DrawTexturePro(textureBlue, fudesumiRec, bluePos, (Vector2) {0, 0}, 0, BLUE);
DrawTexturePro(textureAlpha, fudesumiRec, alphaPos, (Vector2) {0, 0}, 0, WHITE);

EndDrawing();
//----------------------------------------------------------------------------------
}

// De-Initialization
//--------------------------------------------------------------------------------------
UnloadTexture(backgroundTexture);
UnloadTexture(fudesumiTexture);
UnloadTexture(textureRed);
UnloadTexture(textureGreen);
UnloadTexture(textureBlue);
UnloadTexture(textureAlpha);
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------

return 0;
}
Binary file added examples/textures/textures_image_channel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/raylib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 ImageFromChannel(Image image, int selectedChannel); // Create an image from a selected channel of another image
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
Expand Down
202 changes: 202 additions & 0 deletions src/rtextures.c
Original file line number Diff line number Diff line change
Expand Up @@ -1630,6 +1630,208 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
return imText;
}

// Create an image from a selected channel of another image
Image ImageFromChannel(Image image, int selectedChannel)
{
Image result = { 0 };

// Security check to avoid program crash
if ((image.data == NULL) || (image.width == 0) || (image.height == 0))
return result;

// Check selected channel
if (selectedChannel < 0)
{
TRACELOG(LOG_WARNING, "Channel cannot be negative. Setting channel to 0.");
selectedChannel = 0;
}
if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE
|| image.format == PIXELFORMAT_UNCOMPRESSED_R32
|| image.format == PIXELFORMAT_UNCOMPRESSED_R16
)
{
if (selectedChannel > 0)
{
TRACELOG(LOG_WARNING, "This image has only 1 channel. Setting channel to it.");
selectedChannel = 0;
}
}
else if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA)
{
if (selectedChannel > 1)
{
TRACELOG(LOG_WARNING, "This image has only 2 channels. Setting channel to alpha.");
selectedChannel = 1;
}
}
else if (image.format == PIXELFORMAT_UNCOMPRESSED_R5G6B5
|| image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8
|| image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32
|| image.format == PIXELFORMAT_UNCOMPRESSED_R16G16B16
)
{
if (selectedChannel > 2)
{
TRACELOG(LOG_WARNING, "This image has only 3 channels. Setting channel to red.");
selectedChannel = 0;
}
}

// formats rgba
if (selectedChannel > 3)
{
TRACELOG(LOG_WARNING, "ImageFromChannel supports channels 0 to 3 (rgba). Setting channel to alpha.");
selectedChannel = 3;
}

result.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
result.height = image.height;
result.width = image.width;
result.mipmaps = 1;

unsigned char *pixels = (unsigned char *)RL_CALLOC(image.width * image.height, sizeof(unsigned char)); // values 0 to 255

if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
else
{
for (int i = 0, k = 0; i < image.width * image.height; ++i)
{
float imageValue = -1;
switch (image.format)
{
case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
{
imageValue = (float)((unsigned char *)image.data)[i + selectedChannel]/255.0f;

} break;
case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
{
imageValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f;

k += 2;
} break;
case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
{
unsigned short pixel = ((unsigned short *)image.data)[i];

if (selectedChannel == 0)
{
imageValue = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
}
else if (selectedChannel == 1)
{
imageValue = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31);
}
else if (selectedChannel == 2)
{
imageValue = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31);
}
else if (selectedChannel == 3)
{
imageValue = ((pixel & 0b0000000000000001) == 0)? 0.0f : 1.0f;
}

} break;
case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
{
unsigned short pixel = ((unsigned short *)image.data)[i];

if (selectedChannel == 0)
{
imageValue = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
}
else if (selectedChannel == 1)
{
imageValue = (float)((pixel & 0b0000011111100000) >> 5)*(1.0f/63);
}
else if (selectedChannel == 2)
{
imageValue = (float)(pixel & 0b0000000000011111)*(1.0f/31);
}

} break;
case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
{
unsigned short pixel = ((unsigned short *)image.data)[i];

if (selectedChannel == 0)
{
imageValue = (float)((pixel & 0b1111000000000000) >> 12)*(1.0f/15);
}
else if (selectedChannel == 1)
{
imageValue = (float)((pixel & 0b0000111100000000) >> 8)*(1.0f/15);
}
else if (selectedChannel == 2)
{
imageValue = (float)((pixel & 0b0000000011110000) >> 4)*(1.0f/15);
}
else if (selectedChannel == 3)
{
imageValue = (float)(pixel & 0b0000000000001111)*(1.0f/15);
}

} break;
case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
{
imageValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f;

k += 4;
} break;
case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
{
imageValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f;

k += 3;
} break;
case PIXELFORMAT_UNCOMPRESSED_R32:
{
imageValue = ((float *)image.data)[k];

k += 1;
} break;
case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
{
imageValue = ((float *)image.data)[k + selectedChannel];

k += 3;
} break;
case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
{
imageValue = ((float *)image.data)[k + selectedChannel];

k += 4;
} break;
case PIXELFORMAT_UNCOMPRESSED_R16:
{
imageValue = HalfToFloat(((unsigned short *)image.data)[k]);

k += 1;
} break;
case PIXELFORMAT_UNCOMPRESSED_R16G16B16:
{
imageValue = HalfToFloat(((unsigned short *)image.data)[k+selectedChannel]);

k += 3;
} break;
case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
{
imageValue = HalfToFloat(((unsigned short *)image.data)[k + selectedChannel]);

k += 4;
} break;
default: break;
}

pixels[i] = imageValue * 255;
}
}

result.data = pixels;

return result;
}

// Resize and image to new size using Nearest-Neighbor scaling algorithm
void ImageResizeNN(Image *image,int newWidth,int newHeight)
{
Expand Down