feat: distance field approach
This commit is contained in:
124
src/distance_field_generator.c
Normal file
124
src/distance_field_generator.c
Normal file
@@ -0,0 +1,124 @@
|
||||
#include <raylib.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <omp.h>
|
||||
|
||||
bool is_inside_shape(Color pixel)
|
||||
{
|
||||
return pixel.r == 255 && pixel.g == 255 && pixel.b == 255;
|
||||
}
|
||||
|
||||
Image generate_distance_field(Image input_texture, int search_radius)
|
||||
{
|
||||
int width = input_texture.width;
|
||||
int height = input_texture.height;
|
||||
|
||||
Image distance_field = GenImageColor(width, height, (Color){128, 128, 128, 255});
|
||||
Color *input_pixels = LoadImageColors(input_texture);
|
||||
Color *output_pixels = LoadImageColors(distance_field);
|
||||
|
||||
TraceLog(LOG_INFO, "Using %d threads for parallel processing", omp_get_max_threads());
|
||||
|
||||
#pragma omp parallel for schedule(dynamic, 64)
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (omp_get_thread_num() == 0 && y % (height / 10) == 0)
|
||||
{
|
||||
TraceLog(LOG_INFO, "Progress: %d%%", (y * 100) / height);
|
||||
}
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
int index = y * width + x;
|
||||
bool is_inside = is_inside_shape(input_pixels[index]);
|
||||
|
||||
float min_distance_sq = search_radius * search_radius;
|
||||
bool found_boundary = false;
|
||||
|
||||
for (int dy = -search_radius; dy <= search_radius; dy += 1)
|
||||
{
|
||||
for (int dx = -search_radius; dx <= search_radius; dx += 1)
|
||||
{
|
||||
int sample_x = x + dx;
|
||||
int sample_y = y + dy;
|
||||
|
||||
if (sample_x < 0 || sample_x >= width || sample_y < 0 || sample_y >= height) continue;
|
||||
|
||||
int sample_index = sample_y * width + sample_x;
|
||||
bool sample_is_inside = is_inside_shape(input_pixels[sample_index]);
|
||||
|
||||
if (sample_is_inside != is_inside)
|
||||
{
|
||||
float distance_sq = dx * dx + dy * dy;
|
||||
if (distance_sq < min_distance_sq)
|
||||
{
|
||||
min_distance_sq = distance_sq;
|
||||
found_boundary = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float distance = found_boundary ? sqrtf(min_distance_sq) : (float)search_radius;
|
||||
float normalized_distance = distance / (float)search_radius;
|
||||
|
||||
unsigned char distance_value;
|
||||
if (is_inside)
|
||||
{
|
||||
distance_value = (unsigned char)(127.0f - 127.0f * normalized_distance);
|
||||
}
|
||||
else
|
||||
{
|
||||
distance_value = (unsigned char)(128.0f + 127.0f * normalized_distance);
|
||||
}
|
||||
|
||||
output_pixels[index] = (Color){distance_value, distance_value, distance_value, 255};
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
ImageDrawPixel(&distance_field, i % width, i / width, output_pixels[i]);
|
||||
}
|
||||
|
||||
UnloadImageColors(input_pixels);
|
||||
UnloadImageColors(output_pixels);
|
||||
|
||||
return distance_field;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "Usage: %s <input_image> <output_image> [search_radius]", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *input_path = argv[1];
|
||||
const char *output_path = argv[2];
|
||||
int search_radius = (argc >= 4) ? atoi(argv[3]) : 100;
|
||||
|
||||
Image input_image = LoadImage(input_path);
|
||||
if (input_image.data == NULL)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "Error: Failed to load input image '%s'", input_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Image distance_field_image = generate_distance_field(input_image, search_radius);
|
||||
if (!ExportImage(distance_field_image, output_path))
|
||||
{
|
||||
TraceLog(LOG_ERROR, "Failed to save output image '%s'", output_path);
|
||||
UnloadImage(input_image);
|
||||
UnloadImage(distance_field_image);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TraceLog(LOG_INFO, "Distance field saved to '%s'", output_path);
|
||||
|
||||
UnloadImage(input_image);
|
||||
UnloadImage(distance_field_image);
|
||||
|
||||
return 0;
|
||||
}
|
||||
92
src/main.c
92
src/main.c
@@ -1,92 +0,0 @@
|
||||
#include <raylib.h>
|
||||
|
||||
#define WIDTH 800
|
||||
#define HEIGHT 600
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Vector2 points[6];
|
||||
int numPoints;
|
||||
float influence;
|
||||
} Polygon;
|
||||
|
||||
Polygon poly = {
|
||||
{{WIDTH / 2 + 50, HEIGHT / 2 - 50},
|
||||
{WIDTH / 2 + 80, HEIGHT / 2},
|
||||
{WIDTH / 2 + 50, HEIGHT / 2 + 50},
|
||||
{WIDTH / 2 - 50, HEIGHT / 2 + 50},
|
||||
{WIDTH / 2 - 80, HEIGHT / 2},
|
||||
{WIDTH / 2 - 50, HEIGHT / 2 - 50}},
|
||||
6,
|
||||
3000.0f};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
InitWindow(WIDTH, HEIGHT, "Wallpaper");
|
||||
SetWindowState(FLAG_WINDOW_RESIZABLE);
|
||||
SetTargetFPS(60);
|
||||
|
||||
// initialize shader
|
||||
Shader shader = LoadShader("resources/shaders/basic.vs", "resources/shaders/basic.fs");
|
||||
|
||||
int resolutionLoc = GetShaderLocation(shader, "resolution");
|
||||
int mousePosLoc = GetShaderLocation(shader, "mouse_pos");
|
||||
int ballRadiusLoc = GetShaderLocation(shader, "ball_radius");
|
||||
int polygonPointsLoc = GetShaderLocation(shader, "polygon_points");
|
||||
int polygonInfluenceLoc = GetShaderLocation(shader, "polygon_influence");
|
||||
|
||||
Vector2 resolution = {WIDTH, HEIGHT};
|
||||
SetShaderValue(shader, resolutionLoc, &resolution, SHADER_UNIFORM_VEC2);
|
||||
|
||||
// send ball data
|
||||
float ballRadius = 40.0f;
|
||||
SetShaderValue(shader, ballRadiusLoc, &ballRadius, SHADER_UNIFORM_FLOAT);
|
||||
|
||||
// send polygon data
|
||||
float polygonInfluence = poly.influence;
|
||||
SetShaderValue(shader, polygonInfluenceLoc, &polygonInfluence, SHADER_UNIFORM_FLOAT);
|
||||
|
||||
float polygonPoints[12];
|
||||
for (int i = 0; i < poly.numPoints; i++)
|
||||
{
|
||||
polygonPoints[i * 2] = poly.points[i].x;
|
||||
polygonPoints[i * 2 + 1] = poly.points[i].y;
|
||||
}
|
||||
SetShaderValueV(shader, polygonPointsLoc, polygonPoints, SHADER_UNIFORM_VEC2, poly.numPoints);
|
||||
|
||||
// initializee render texture
|
||||
RenderTexture2D target = LoadRenderTexture(WIDTH, HEIGHT);
|
||||
|
||||
while (!WindowShouldClose())
|
||||
{
|
||||
int screenWidth = GetScreenWidth();
|
||||
int screenHeight = GetScreenHeight();
|
||||
|
||||
if (IsWindowResized())
|
||||
{
|
||||
UnloadRenderTexture(target);
|
||||
target = LoadRenderTexture(screenWidth, screenHeight);
|
||||
|
||||
Vector2 resolution = {screenWidth, screenHeight};
|
||||
SetShaderValue(shader, resolutionLoc, &resolution, SHADER_UNIFORM_VEC2);
|
||||
}
|
||||
|
||||
Vector2 mousePos = GetMousePosition();
|
||||
mousePos.y = screenHeight - mousePos.y;
|
||||
SetShaderValue(shader, mousePosLoc, &mousePos, SHADER_UNIFORM_VEC2);
|
||||
|
||||
BeginDrawing();
|
||||
|
||||
BeginShaderMode(shader);
|
||||
DrawTextureRec(target.texture, (Rectangle){0, 0, screenWidth, -screenHeight}, (Vector2){0, 0}, WHITE);
|
||||
EndShaderMode();
|
||||
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
UnloadRenderTexture(target);
|
||||
UnloadShader(shader);
|
||||
CloseWindow();
|
||||
|
||||
return 0;
|
||||
}
|
||||
91
src/wallpaper.c
Normal file
91
src/wallpaper.c
Normal file
@@ -0,0 +1,91 @@
|
||||
#include <raylib.h>
|
||||
#include <rlgl.h>
|
||||
|
||||
#define WIDTH 1920
|
||||
#define HEIGHT 1080
|
||||
|
||||
Texture2D load_texture_from_file(const char *file_path)
|
||||
{
|
||||
Texture2D texture = LoadTexture(file_path);
|
||||
if (texture.id == 0)
|
||||
{
|
||||
TraceLog(LOG_ERROR, "Failed to load texture from file: %s", file_path);
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
InitWindow(WIDTH, HEIGHT, "wallpaper");
|
||||
SetWindowState(FLAG_WINDOW_RESIZABLE);
|
||||
SetTargetFPS(60);
|
||||
|
||||
// initialize textures
|
||||
Texture2D distanceFieldTex = load_texture_from_file("resources/textures/background_distance_field.png");
|
||||
Texture2D backgroundTex = load_texture_from_file("resources/textures/background_transparent.png");
|
||||
RenderTexture2D target = LoadRenderTexture(WIDTH, HEIGHT);
|
||||
|
||||
// initialize shader
|
||||
Shader shader = LoadShader("resources/shaders/basic.vs", "resources/shaders/distance_field.fs");
|
||||
|
||||
int resolutionLoc = GetShaderLocation(shader, "resolution");
|
||||
int mousePosLoc = GetShaderLocation(shader, "mousePos");
|
||||
int ballRadiusLoc = GetShaderLocation(shader, "ballRadius");
|
||||
int shapeInfluenceLoc = GetShaderLocation(shader, "shapeInfluence");
|
||||
int distanceFieldTexLoc = GetShaderLocation(shader, "distanceFieldTex");
|
||||
|
||||
Vector2 resolution = {WIDTH, HEIGHT};
|
||||
SetShaderValue(shader, resolutionLoc, &resolution, SHADER_UNIFORM_VEC2);
|
||||
|
||||
float ballRadius = 100.0f;
|
||||
SetShaderValue(shader, ballRadiusLoc, &ballRadius, SHADER_UNIFORM_FLOAT);
|
||||
|
||||
float shapeInfluence = 200.0f;
|
||||
SetShaderValue(shader, shapeInfluenceLoc, &shapeInfluence, SHADER_UNIFORM_FLOAT);
|
||||
|
||||
int textureUnit = 1;
|
||||
SetShaderValue(shader, distanceFieldTexLoc, &textureUnit, SHADER_UNIFORM_INT);
|
||||
|
||||
while (!WindowShouldClose())
|
||||
{
|
||||
int screenWidth = GetScreenWidth();
|
||||
int screenHeight = GetScreenHeight();
|
||||
|
||||
if (IsWindowResized())
|
||||
{
|
||||
UnloadRenderTexture(target);
|
||||
target = LoadRenderTexture(screenWidth, screenHeight);
|
||||
|
||||
Vector2 resolution = {screenWidth, screenHeight};
|
||||
SetShaderValue(shader, resolutionLoc, &resolution, SHADER_UNIFORM_VEC2);
|
||||
}
|
||||
|
||||
Vector2 mousePos = GetMousePosition();
|
||||
mousePos.y = screenHeight - mousePos.y;
|
||||
SetShaderValue(shader, mousePosLoc, &mousePos, SHADER_UNIFORM_VEC2);
|
||||
|
||||
BeginDrawing();
|
||||
|
||||
rlActiveTextureSlot(1);
|
||||
rlEnableTexture(distanceFieldTex.id);
|
||||
rlActiveTextureSlot(0);
|
||||
|
||||
BeginShaderMode(shader);
|
||||
DrawTextureRec(target.texture, (Rectangle){0, 0, screenWidth, -screenHeight}, (Vector2){0, 0}, WHITE);
|
||||
EndShaderMode();
|
||||
|
||||
DrawTextureRec(backgroundTex, (Rectangle){0, 0, screenWidth, screenHeight}, (Vector2){0, 0}, WHITE);
|
||||
|
||||
DrawText(TextFormat("Distance Field | x: %.0f, y: %.0f | frame: %.2f ms", mousePos.x, mousePos.y, GetFrameTime() * 1000), 10, 10, 20, WHITE);
|
||||
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
UnloadRenderTexture(target);
|
||||
UnloadTexture(distanceFieldTex);
|
||||
UnloadTexture(backgroundTex);
|
||||
UnloadShader(shader);
|
||||
CloseWindow();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user