raylib java jni linux probe

发布于:2025-08-13 ⋅ 阅读:(17) ⋅ 点赞:(0)

raylib 5.5 + java jni probe

download raylib release tar package, graalvm

curl -JLO https://ghproxy.cn/https://github.com/raysan5/raylib/releases/download/5.5/raylib-5.5_linux_amd64.tar.gz
curl -JLO https://ghproxy.cn/https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-24.0.2/graalvm-community-jdk-24.0.2_linux-x64_bin.tar.gz
  • unzip by yourself

1. 先定义PaintEditor.java(usage)

public class PaintEditor {
    // 加载JNI库
    static {
        System.loadLibrary("painteditor");
    }

    // 工具类型常量
    public static final int TOOL_NONE = 0;
    public static final int TOOL_PENCIL = 1;
    public static final int TOOL_LINE = 2;
    public static final int TOOL_RECTANGLE = 3;
    public static final int TOOL_CIRCLE = 4;
    public static final int TOOL_ERASER = 5;
    public static final int TOOL_FILL = 6;

    // Native方法声明
    private native void init(int width, int height, String title);
    private native void update();
    private native boolean shouldClose();
    private native void close();
    private native void saveImage(String filePath);

    public static void main(String[] args) {
        PaintEditor editor = new PaintEditor();
        editor.init(2560, 1600, "Simple Paint Editor");
        
        while (!editor.shouldClose()) {
            editor.update();
        }
        
        editor.close();
    }
}

2. 然后生成头文件PaintEditor.h

# 生成JNI头文件
javac -h . PaintEditor.java

3. 实现定义, paint_editor_jni.c

#include "PaintEditor.h"
#include <raylib.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>  // 提供malloc和free函数声明

// 定义主键盘数字键的ASCII值
#define KEY_1 49
#define KEY_2 50
#define KEY_3 51
#define KEY_4 52
#define KEY_5 53
#define KEY_6 54
#define KEY_7 55

// 工具类型定义
typedef enum {
    TOOL_SELECTION,
    TOOL_PENCIL,
    TOOL_LINE,
    TOOL_RECTANGLE,
    TOOL_CIRCLE,
    TOOL_ERASER,
    TOOL_FILL
} ToolType;

// 全局状态变量
static ToolType currentTool = TOOL_PENCIL;
static Color currentColor = BLACK;
static int brushSize = 2;
static Vector2 startPos = {0};
static Vector2 currentPos = {0};
static bool isDrawing = false;
static RenderTexture2D canvas;
static bool showColorPicker = false;
static Color colorPalette[16] = {
    BLACK, WHITE, GRAY, LIGHTGRAY,
    RED, MAROON, ORANGE, YELLOW,
    GREEN, LIME, DARKGREEN, BLUE,
    DARKBLUE, PURPLE, PINK, BROWN
};

// 字符串转换函数
static const char* getCString(JNIEnv* env, jstring jstr) {
    if (jstr == NULL) return NULL;
    return (*env)->GetStringUTFChars(env, jstr, NULL);
}

static void releaseCString(JNIEnv* env, jstring jstr, const char* cstr) {
    if (jstr != NULL && cstr != NULL) {
        (*env)->ReleaseStringUTFChars(env, jstr, cstr);
    }
}

// 计算两点间距离
static float calculateDistance(Vector2 v1, Vector2 v2) {
    float dx = v2.x - v1.x;
    float dy = v2.y - v1.y;
    return sqrtf(dx*dx + dy*dy);
}

// 填充算法实现
static void fillArea(Vector2 pos, Color fillColor) {
    // 获取画布像素数据
    Image canvasImage = LoadImageFromTexture(canvas.texture);
    Color* pixels = (Color*)canvasImage.data;
    
    if (!pixels) {
        UnloadImage(canvasImage);
        return;
    }

    int width = canvasImage.width;
    int height = canvasImage.height;
    int x = (int)pos.x;
    int y = (int)pos.y;

    // 检查边界
    if (x < 0 || x >= width || y < 0 || y >= height) {
        UnloadImage(canvasImage);
        return;
    }

    Color targetColor = pixels[y * width + x];
    
    // 如果目标颜色与填充颜色相同,则不执行填充
    if (memcmp(&targetColor, &fillColor, sizeof(Color)) == 0) {
        UnloadImage(canvasImage);
        return;
    }

    // 栈式填充算法
    Vector2* stack = (Vector2*)malloc(width * height * sizeof(Vector2));
    int stackSize = 0;
    stack[stackSize++] = (Vector2){(float)x, (float)y};
    pixels[y * width + x] = fillColor;

    while (stackSize > 0) {
        Vector2 p = stack[--stackSize];
        int px = (int)p.x;
        int py = (int)p.y;

        // 检查上方像素
        if (py > 0 && memcmp(&pixels[(py-1)*width + px], &targetColor, sizeof(Color)) == 0) {
            pixels[(py-1)*width + px] = fillColor;
            stack[stackSize++] = (Vector2){(float)px, (float)(py-1)};
        }

        // 检查下方像素
        if (py < height-1 && memcmp(&pixels[(py+1)*width + px], &targetColor, sizeof(Color)) == 0) {
            pixels[(py+1)*width + px] = fillColor;
            stack[stackSize++] = (Vector2){(float)px, (float)(py+1)};
        }

        // 检查左侧像素
        if (px > 0 && memcmp(&pixels[py*width + (px-1)], &targetColor, sizeof(Color)) == 0) {
            pixels[py*width + (px-1)] = fillColor;
            stack[stackSize++] = (Vector2){(float)(px-1), (float)py};
        }

        // 检查右侧像素
        if (px < width-1 && memcmp(&pixels[py*width + (px+1)], &targetColor, sizeof(Color)) == 0) {
            pixels[py*width + (px+1)] = fillColor;
            stack[stackSize++] = (Vector2){(float)(px+1), (float)py};
        }
    }

    // 更新纹理
    UpdateTexture(canvas.texture, canvasImage.data);
    free(stack);
    UnloadImage(canvasImage);
}

// 初始化函数
JNIEXPORT void JNICALL Java_PaintEditor_init
(JNIEnv* env, jobject obj, jint width, jint height, jstring title) {
    const char* cTitle = getCString(env, title);
    InitWindow(width, height, cTitle);
    releaseCString(env, title, cTitle);
    
    // 创建画布
    canvas = LoadRenderTexture(width, height);
    BeginTextureMode(canvas);
        ClearBackground(WHITE);
    EndTextureMode();
    
    SetTargetFPS(60);
}

// 更新函数
JNIEXPORT void JNICALL Java_PaintEditor_update
(JNIEnv* env, jobject obj) {
    currentPos = GetMousePosition();
    Color drawColor = (currentTool == TOOL_ERASER) ? WHITE : currentColor;
    
    // 处理工具选择
    if (IsKeyPressed(KEY_ESCAPE)) CloseWindow();
    if (IsKeyPressed(KEY_1)) currentTool = TOOL_SELECTION;
    if (IsKeyPressed(KEY_2)) currentTool = TOOL_PENCIL;
    if (IsKeyPressed(KEY_3)) currentTool = TOOL_LINE;
    if (IsKeyPressed(KEY_4)) currentTool = TOOL_RECTANGLE;
    if (IsKeyPressed(KEY_5)) currentTool = TOOL_CIRCLE;
    if (IsKeyPressed(KEY_6)) currentTool = TOOL_ERASER;
    if (IsKeyPressed(KEY_7)) currentTool = TOOL_FILL;
    
    // 调整画笔大小
    if (IsKeyPressed(KEY_KP_ADD) || IsKeyPressed(KEY_EQUAL)) {
        brushSize = (brushSize < 50) ? brushSize + 1 : 50;
    }
    if (IsKeyPressed(KEY_KP_SUBTRACT) || IsKeyPressed(KEY_MINUS)) {
        brushSize = (brushSize > 1) ? brushSize - 1 : 1;
    }
    
    // 颜色选择器开关
    if (IsKeyPressed(KEY_C)) {
        showColorPicker = !showColorPicker;
    }
    
    // 清除画布
    if (IsKeyPressed(KEY_DELETE)) {
        BeginTextureMode(canvas);
            ClearBackground(WHITE);
        EndTextureMode();
    }
    
    // 保存画布
    if (IsKeyDown(KEY_LEFT_CONTROL) && IsKeyPressed(KEY_S)) {
        Image screenshot = LoadImageFromTexture(canvas.texture);
        ImageFlipVertical(&screenshot); // 修复垂直翻转问题
        ExportImage(screenshot, "drawing.png");
        UnloadImage(screenshot);
    }
    
    // 处理颜色选择
    if (showColorPicker && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
        for (int i = 0; i < 16; i++) {
            int x = 20 + (i % 4) * 30;
            int y = 40 + (i / 4) * 30;
            if (CheckCollisionPointRec(currentPos, (Rectangle){(float)x, (float)y, 25, 25})) {
                currentColor = colorPalette[i];
                showColorPicker = false;
                break;
            }
        }
    }
    
    // 处理填充工具
    if (currentTool == TOOL_FILL && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
        fillArea(currentPos, currentColor);
    }
    
    // 开始绘图
    if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && currentTool != TOOL_SELECTION && currentTool != TOOL_FILL) {
        startPos = currentPos;
        isDrawing = true;
        
        // 铅笔工具立即绘制第一个点
        if (currentTool == TOOL_PENCIL) {
            BeginTextureMode(canvas);
            DrawCircleV(currentPos, brushSize/2.0f, drawColor);
            EndTextureMode();
        }
    } 
    // 结束绘图
    else if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT) && isDrawing) {
        isDrawing = false;
        BeginTextureMode(canvas);
            if (currentTool == TOOL_LINE) {
                DrawLineEx(startPos, currentPos, brushSize, drawColor);
            }
            else if (currentTool == TOOL_RECTANGLE) {
                DrawRectangleLinesEx((Rectangle){
                    fminf(startPos.x, currentPos.x), fminf(startPos.y, currentPos.y),
                    fabsf(currentPos.x - startPos.x), fabsf(currentPos.y - startPos.y)
                }, brushSize, drawColor);
            }
            else if (currentTool == TOOL_CIRCLE) {
                float radius = calculateDistance(startPos, currentPos);
                DrawCircleLinesV(startPos, radius, drawColor);
            }
        EndTextureMode();
    }
    
    // 铅笔工具持续绘制
    if (currentTool == TOOL_PENCIL && isDrawing && IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
        BeginTextureMode(canvas);
        DrawLineEx(startPos, currentPos, brushSize, drawColor);
        EndTextureMode();
        startPos = currentPos; // 更新起点为当前位置,实现连续绘制
    }
    
    // 绘制UI
    BeginDrawing();
        ClearBackground(LIGHTGRAY);
        
        // 绘制画布
        DrawTextureRec(canvas.texture, 
            (Rectangle){0, 0, canvas.texture.width, -canvas.texture.height}, 
            (Vector2){0, 30}, WHITE);
        
        // 绘制工具栏
        DrawRectangle(0, 0, GetScreenWidth(), 30, DARKGRAY);
        DrawText("Tools: [1]Sel [2]Pencil [3]Line [4]Rect [5]Circle [6]Eraser [7]Fill", 10, 5, 12, WHITE);
        DrawText(TextFormat("Size: %d (+/-)  Color: [C]  Clear: Del  Save: Ctrl+S", brushSize), 400, 5, 12, WHITE);
        
        // 显示当前工具
        const char* toolNames[] = {"Selection", "Pencil", "Line", "Rectangle", "Circle", "Eraser", "Fill"};
        DrawText(TextFormat("Current: %s", toolNames[currentTool]), 700, 5, 12, WHITE);
        
        // 绘制颜色选择器
        if (showColorPicker) {
            DrawRectangle(10, 40, 140, 160, WHITE);
            DrawRectangleLines(10, 40, 140, 160, BLACK);
            DrawText("Select Color:", 20, 25, 12, BLACK);
            
            for (int i = 0; i < 16; i++) {
                int x = 20 + (i % 4) * 30;
                int y = 40 + (i / 4) * 30;
                DrawRectangle(x, y, 25, 25, colorPalette[i]);
                DrawRectangleLines(x, y, 25, 25, BLACK);
            }
        }
        
        // 绘制预览(正在绘制的形状)
        if (isDrawing && currentTool != TOOL_PENCIL) {
            if (currentTool == TOOL_LINE) {
                DrawLineEx(startPos, currentPos, brushSize, drawColor);
            }
            else if (currentTool == TOOL_RECTANGLE) {
                DrawRectangleLinesEx((Rectangle){
                    fminf(startPos.x, currentPos.x), fminf(startPos.y, currentPos.y),
                    fabsf(currentPos.x - startPos.x), fabsf(currentPos.y - startPos.y)
                }, brushSize, drawColor);
            }
            else if (currentTool == TOOL_CIRCLE) {
                float radius = calculateDistance(startPos, currentPos);
                DrawCircleLinesV(startPos, radius, drawColor);
            }
        }
        
        // 绘制鼠标指针(显示画笔大小)
        DrawCircleLinesV(currentPos, brushSize/2.0f, BLACK);
    EndDrawing();
}

// 检查窗口是否应关闭
JNIEXPORT jboolean JNICALL Java_PaintEditor_shouldClose
(JNIEnv* env, jobject obj) {
    return WindowShouldClose() ? JNI_TRUE : JNI_FALSE;
}

// 关闭窗口并释放资源
JNIEXPORT void JNICALL Java_PaintEditor_close
(JNIEnv* env, jobject obj) {
    UnloadRenderTexture(canvas);
    CloseWindow();
}

最后整体build.sh,自己修改你的jdk和raylib

#!/bin/bash
set -xe

# 配置路径
export JAVA_INCLUDE_DIR=/etc/alternatives/java_sdk_24
export RAYLIB_LIB_DIR=./lib       # Raylib库所在目录
export RAYLIB_INC_DIR=./include   # Raylib头文件所在目录

# 生成JNI头文件
javac -h . PaintEditor.java

# 编译共享库
gcc -fPIC -shared -Wall \
  -I${JAVA_INCLUDE_DIR}/include \
  -I${JAVA_INCLUDE_DIR}/include/linux \
  -I${RAYLIB_INC_DIR} \
  -L${RAYLIB_LIB_DIR} \
  paint_editor_jni.c \
  -o libpainteditor.so \
  -lraylib -lm -lpthread -ldl -lrt -lX11

# 运行程序
export LD_LIBRARY_PATH=${RAYLIB_LIB_DIR}:$LD_LIBRARY_PATH
java -Djava.library.path=.:${RAYLIB_LIB_DIR} --enable-native-access=ALL-UNNAMED PaintEditor

graalvm 编译成elf文件

./graalvm-community-openjdk-24.0.2+11.1/bin/native-image --enable-native-access=ALL-UNNAMED PaintEditor

ldd painteditor

./painteditor

damn, 非常慢


网站公告

今日签到

点亮在社区的每一天
去签到