diff --git a/paint/sources/config.c b/paint/sources/config.c index 7748c33c..7198f05f 100644 --- a/paint/sources/config.c +++ b/paint/sources/config.c @@ -129,6 +129,7 @@ void config_save() { json_encode_bool("grid_snap", g_config->grid_snap); json_encode_bool("experimental", g_config->experimental); json_encode_i32("neural_backend", g_config->neural_backend); + json_encode_i32("neural_res", g_config->neural_res); json_encode_i32("render_mode", g_config->render_mode); json_encode_i32("workspace", g_config->workspace); json_encode_i32("workflow", g_config->workflow); @@ -260,6 +261,7 @@ void config_init() { g_config->view2d_grid_snap = false; g_config->experimental = false; g_config->neural_backend = NEURAL_BACKEND_VULKAN; + g_config->neural_res = 512; #if defined(IRON_ANDROID) || defined(IRON_IOS) g_config->render_mode = RENDER_MODE_FORWARD; #else diff --git a/paint/sources/functions.h b/paint/sources/functions.h index 2ea4f56e..ed243b45 100644 --- a/paint/sources/functions.h +++ b/paint/sources/functions.h @@ -784,7 +784,6 @@ void text_to_image_node_init(); void texture_mesh_node_init(); void text_to_text_node_clear(); void text_to_text_node_run(char *prompt, void (*done)(char *)); -void outpaint_image_node_init(); char *neural_node_vector(ui_node_t *node, ui_node_socket_t *socket); char *neural_node_value(ui_node_t *node, ui_node_socket_t *socket); ui_node_t *neural_from_node(ui_node_socket_t *inp, i32 socket); @@ -798,7 +797,6 @@ void edit_image_node_init(); void tile_image_node_init(); void image_to_3d_mesh_node_init(); void image_to_3d_mesh_node_button(i32 node_id); -void vary_image_node_init(); void image_to_normal_map_node_init(); void image_to_depth_node_init(); void *tex_image_node_create(ui_node_t *raw, f32_array_t *args); diff --git a/paint/sources/main.c b/paint/sources/main.c index 2f3b1147..b72dc689 100644 --- a/paint/sources/main.c +++ b/paint/sources/main.c @@ -110,7 +110,6 @@ #include "nodes_neural/inpaint_image_node.c" #include "nodes_neural/neural_node.c" #include "nodes_neural/neural_node_models.c" -#include "nodes_neural/outpaint_image_node.c" #include "nodes_neural/repeat_node.c" #include "nodes_neural/save_image_node.c" #include "nodes_neural/text_to_image_node.c" @@ -118,7 +117,6 @@ #include "nodes_neural/texture_mesh_node.c" #include "nodes_neural/tile_image_node.c" #include "nodes_neural/upscale_image_node.c" -#include "nodes_neural/vary_image_node.c" #include "render/make_bake.c" #include "render/make_blur.c" diff --git a/paint/sources/nodes_material.c b/paint/sources/nodes_material.c index 6cedab38..fbd73b81 100644 --- a/paint/sources/nodes_material.c +++ b/paint/sources/nodes_material.c @@ -94,7 +94,6 @@ void nodes_material_init() { image_to_normal_map_node_init(); image_to_pbr_node_init(); inpaint_image_node_init(); - outpaint_image_node_init(); repeat_node_init(); save_image_node_init(); text_to_image_node_init(); @@ -103,7 +102,6 @@ void nodes_material_init() { } tile_image_node_init(); upscale_image_node_init(); - vary_image_node_init(); #endif diff --git a/paint/sources/nodes_neural/edit_image_node.c b/paint/sources/nodes_neural/edit_image_node.c index 9503c72f..ae4739d5 100644 --- a/paint/sources/nodes_neural/edit_image_node.c +++ b/paint/sources/nodes_neural/edit_image_node.c @@ -1,6 +1,42 @@ #include "../global.h" +static string_array_t *edit_image_node_flux_klein_args(char *dir, char *prompt) { + string_array_t *argv = any_array_create_from_raw( + (void *[]){ + string("%s/%s", dir, neural_node_sd_bin()), + "--diffusion-model", + string("%s/flux-2-klein-4b-Q8_0.gguf", dir), + "--taesd", + string("%s/taef2.safetensors", dir), + "--llm", + string("%s/Qwen3-4B-Q8_0.gguf", dir), + "--cfg-scale", + "1.0", + "--sampling-method", + "euler", + "--diffusion-fa", + "--offload-to-cpu", + "--steps", + "4", + "-s", + "-1", + "-W", + string("%d", g_config->neural_res), + "-H", + string("%d", g_config->neural_res), + "-r", + string("%s/input.png", dir), + "-p", + prompt, + "-o", + string("%s/output.png", dir), + NULL, + }, + 28); + return argv; +} + static string_array_t *edit_image_node_qwen_args(char *dir, char *prompt) { string_array_t *argv = any_array_create_from_raw( (void *[]){ @@ -25,9 +61,9 @@ static string_array_t *edit_image_node_qwen_args(char *dir, char *prompt) { "3", "--qwen-image-zero-cond-t", "-W", - "512", + string("%d", g_config->neural_res), "-H", - "512", + string("%d", g_config->neural_res), "-p", prompt, "-r", @@ -40,43 +76,6 @@ static string_array_t *edit_image_node_qwen_args(char *dir, char *prompt) { return argv; } -static string_array_t *edit_image_node_flux_klein_args(char *dir, char *prompt) { - string_array_t *argv = any_array_create_from_raw( - (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/flux-2-klein-4b-Q8_0.gguf", dir), - "--vae", - // string("%s/full_encoder_small_decoder.safetensors", dir), - string("%s/flux_ae.safetensors", dir), - "--llm", - string("%s/Qwen3-4B-Q8_0.gguf", dir), - "--cfg-scale", - "1.0", - "--sampling-method", - "euler", - "--diffusion-fa", - "--offload-to-cpu", - "--steps", - "4", - "-s", - "-1", - "-W", - "512", - "-H", - "512", - "-r", - string("%s/input.png", dir), - "-p", - prompt, - "-o", - string("%s/output.png", dir), - NULL, - }, - 28); - return argv; -} - void edit_image_node_button(i32 node_id) { ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); ui_node_t *node = ui_get_node(canvas->nodes, node_id); @@ -107,10 +106,10 @@ void edit_image_node_button(i32 node_id) { string_array_t *argv; if (model == 0) { - argv = edit_image_node_qwen_args(dir, prompt); + argv = edit_image_node_flux_klein_args(dir, prompt); } else { - argv = edit_image_node_flux_klein_args(dir, prompt); + argv = edit_image_node_qwen_args(dir, prompt); } iron_exec_async(argv->buffer[0], argv->buffer); diff --git a/paint/sources/nodes_neural/image_to_pbr_node.c b/paint/sources/nodes_neural/image_to_pbr_node.c index 52cd3ea3..cd734548 100644 --- a/paint/sources/nodes_neural/image_to_pbr_node.c +++ b/paint/sources/nodes_neural/image_to_pbr_node.c @@ -98,15 +98,32 @@ void image_to_pbr_node_run_sd(char *model, char *prompt, void (*done)(gpu_textur } void image_to_pbr_node_all_done(void *_) { - render_target_t *occmap; { render_target_t *t = render_target_create(); t->name = "occmap"; - t->width = 2048; - t->height = 2048; + t->width = 768; + t->height = 768; t->format = "RGBA32"; render_path_create_render_target(t); - occmap = t; + } + // Ping-pong targets for the blur + { + render_target_t *t = render_target_create(); + t->name = "occmap_blur"; + t->width = 768; + t->height = 768; + t->format = "R8"; + render_path_create_render_target(t); + } + render_target_t *occmap_blurred; + { + render_target_t *t = render_target_create(); + t->name = "occmap_blurred"; + t->width = 768; + t->height = 768; + t->format = "R8"; + render_path_create_render_target(t); + occmap_blurred = t; } { render_target_t *t = render_target_create(); @@ -128,14 +145,39 @@ void image_to_pbr_node_all_done(void *_) { } render_path_load_shader("Scene/depth_to_ao_pass/depth_to_ao_pass"); + render_path_load_shader("Scene/ssao_blur_pass/ssao_blur_pass_x"); + render_path_load_shader("Scene/ssao_blur_pass/ssao_blur_pass_y"); + render_path_set_target("occmap", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); render_path_bind_target("_height_map", "height_map"); render_path_bind_target("_normal_map", "normal_map"); render_path_draw_shader("Scene/depth_to_ao_pass/depth_to_ao_pass"); + + // Blur + render_path_set_target("occmap_blur", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); + render_path_bind_target("occmap", "tex"); + render_path_bind_target("_normal_map", "gbuffer0"); + render_path_draw_shader("Scene/ssao_blur_pass/ssao_blur_pass_x"); + + render_path_set_target("occmap_blurred", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); + render_path_bind_target("occmap_blur", "tex"); + render_path_bind_target("_normal_map", "gbuffer0"); + render_path_draw_shader("Scene/ssao_blur_pass/ssao_blur_pass_y"); + + // Second blur + render_path_set_target("occmap_blur", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); + render_path_bind_target("occmap_blurred", "tex"); + render_path_bind_target("_normal_map", "gbuffer0"); + render_path_draw_shader("Scene/ssao_blur_pass/ssao_blur_pass_x"); + + render_path_set_target("occmap_blurred", NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); + render_path_bind_target("occmap_blur", "tex"); + render_path_bind_target("_normal_map", "gbuffer0"); + render_path_draw_shader("Scene/ssao_blur_pass/ssao_blur_pass_y"); + gc_unroot(image_to_pbr_node_result_occlusion); - image_to_pbr_node_result_occlusion = occmap->_image; + image_to_pbr_node_result_occlusion = occmap_blurred->_image; gc_root(image_to_pbr_node_result_occlusion); - // blur } void image_to_pbr_node_roughness_done(gpu_texture_t *tex) { @@ -147,8 +189,19 @@ void image_to_pbr_node_roughness_done(gpu_texture_t *tex) { } void image_to_pbr_node_base_done(gpu_texture_t *tex) { + // + buffer_t *pixels = gpu_get_texture_pixels(tex); + for (uint32_t i = 0; i < pixels->length; i += 4) { + for (uint32_t c = 0; c < 3; ++c) { + int v = (int)(pixels->buffer[i + c] * 2.5f); + pixels->buffer[i + c] = v > 255 ? 255 : v; + } + } + gpu_texture_t *bright = gpu_create_texture_from_bytes(pixels, tex->width, tex->height, GPU_TEXTURE_FORMAT_RGBA32); + // + gc_unroot(image_to_pbr_node_result_base); - image_to_pbr_node_result_base = tex; + image_to_pbr_node_result_base = bright; gc_root(image_to_pbr_node_result_base); image_to_pbr_node_run_sd("marigold-iid-appearance-v1-1.q8_0.gguf", "_roughness", &image_to_pbr_node_roughness_done); } diff --git a/paint/sources/nodes_neural/inpaint_image_node.c b/paint/sources/nodes_neural/inpaint_image_node.c index a6117156..d2ede25c 100644 --- a/paint/sources/nodes_neural/inpaint_image_node.c +++ b/paint/sources/nodes_neural/inpaint_image_node.c @@ -11,11 +11,6 @@ void inpaint_image_node_button_on_next_frame(ui_node_t *node) { char *node_name = parser_material_node_name(node, NULL); ui_handle_t *h = ui_handle(node_name); i32 model = ui_nest(h, 0)->i; - char *prompt = ui_nest(h, 1)->text; - - if (string_equals(prompt, "")) { - prompt = "."; - } char *dir = neural_node_dir(); if (model == 0) { @@ -28,26 +23,26 @@ void inpaint_image_node_button_on_next_frame(ui_node_t *node) { buffer_t *mask_buf = gpu_get_texture_pixels(mask); for (uint32_t i = 0; i < mask_buf->length / 4; ++i) { - if (mask_buf->buffer[i * 4] + mask_buf->buffer[i * 4 + 1] + mask_buf->buffer[i * 4 + 2] > 200) { - mask_buf->buffer[i * 4] = 0; - mask_buf->buffer[i * 4 + 1] = 0; - mask_buf->buffer[i * 4 + 2] = 0; - } - else { + if (mask_buf->buffer[i * 4] < 200 || mask_buf->buffer[i * 4 + 1] < 200 || mask_buf->buffer[i * 4 + 2] < 200) { mask_buf->buffer[i * 4] = 255; mask_buf->buffer[i * 4 + 1] = 255; mask_buf->buffer[i * 4 + 2] = 255; } + else { + mask_buf->buffer[i * 4] = 0; + mask_buf->buffer[i * 4 + 1] = 0; + mask_buf->buffer[i * 4 + 2] = 0; + } } iron_write_png(string("%s%smask.png", dir, PATH_SEP), mask_buf, mask->width, mask->height, 0); } else { - gpu_texture_t *masked = gpu_create_render_target(512, 512, GPU_TEXTURE_FORMAT_RGBA32); + gpu_texture_t *masked = gpu_create_render_target(g_config->neural_res, g_config->neural_res, GPU_TEXTURE_FORMAT_RGBA32); draw_begin(masked, false, 0); - draw_scaled_image(input, 0, 0, 512, 512); + draw_scaled_image(input, 0, 0, g_config->neural_res, g_config->neural_res); draw_set_pipeline(ui_view2d_pipe); gpu_set_int(ui_view2d_channel_loc, 6); - draw_scaled_image(mask, 0, 0, 512, 512); + draw_scaled_image(mask, 0, 0, g_config->neural_res, g_config->neural_res); draw_set_pipeline(NULL); draw_end(); iron_write_png(string("%s%sinput.png", dir, PATH_SEP), gpu_get_texture_pixels(masked), masked->width, masked->height, 0); @@ -58,22 +53,30 @@ void inpaint_image_node_button_on_next_frame(ui_node_t *node) { argv = any_array_create_from_raw( (void *[]){ string("%s/%s", dir, neural_node_sd_bin()), - "-m", - string("%s/v1-5-pruned-emaonly.safetensors", dir), + "--diffusion-model", + string("%s/flux-2-klein-4b-Q8_0.gguf", dir), + "--taesd", + string("%s/taef2.safetensors", dir), + "--llm", + string("%s/Qwen3-4B-Q8_0.gguf", dir), + "--cfg-scale", + "0.0", + "--sampling-method", + "euler", + "--diffusion-fa", + "--offload-to-cpu", "--steps", - "30", + "4", "-s", "-1", - "--cfg-scale", - "0", "--strength", "1", "-W", - "512", + string("%d", g_config->neural_res), "-H", - "512", + string("%d", g_config->neural_res), "-p", - prompt, + "", "-i", string("%s/input.png", dir), "--mask", @@ -82,7 +85,7 @@ void inpaint_image_node_button_on_next_frame(ui_node_t *node) { string("%s/output.png", dir), NULL, }, - 24); + 32); } else { argv = any_array_create_from_raw( @@ -104,11 +107,11 @@ void inpaint_image_node_button_on_next_frame(ui_node_t *node) { "-s", "-1", "-W", - "512", + string("%d", g_config->neural_res), "-H", - "512", + string("%d", g_config->neural_res), "-p", - "inpaint red area", + "remove red area", "-r", string("%s/input.png", dir), "-o", @@ -130,13 +133,11 @@ void inpaint_image_node_button(i32 node_id) { ui_handle_t *h = ui_handle(node_name); string_array_t *models = any_array_create_from_raw( (void *[]){ - "Stable Diffusion", + "FLUX 2 klein", "Qwen Image Edit", }, 2); - i32 model = ui_combo(ui_nest(h, 0), models, tr("Model"), false, UI_ALIGN_LEFT, true); - char *prompt = ui_text_area(ui_nest(h, 1), UI_ALIGN_LEFT, true, tr("prompt"), true); - node->buttons->buffer[0]->height = string_split(prompt, "\n")->length + 2; + i32 model = ui_combo(ui_nest(h, 0), models, tr("Model"), false, UI_ALIGN_LEFT, true); if (neural_node_button(node, models->buffer[model])) { sys_notify_on_next_frame(&inpaint_image_node_button_on_next_frame, node); diff --git a/paint/sources/nodes_neural/neural_node_models.c b/paint/sources/nodes_neural/neural_node_models.c index e4c982ec..a3c6e26d 100644 --- a/paint/sources/nodes_neural/neural_node_models.c +++ b/paint/sources/nodes_neural/neural_node_models.c @@ -5,18 +5,19 @@ void neural_node_models_init() { gc_unroot(neural_node_models); neural_node_models = any_array_create_from_raw( (void *[]){ - GC_ALLOC_INIT(neural_node_model_t, - {.name = "Stable Diffusion", - .memory = "4GB", - .size = "4.3GB", - .nodes = "Inpaint Image, Outpaint Image, Text to Image, Tile Image, Vary Image", - .urls = any_array_create_from_raw( - (void *[]){ - "https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.safetensors", - }, - 1), - .web = "https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5", - .license = "openrail"}), + GC_ALLOC_INIT(neural_node_model_t, {.name = "FLUX 2 klein", + .memory = "4GB", + .size = "8.2GB", + .nodes = "Text to Image, Edit Image, Inpaint Image, Tile Image", + .urls = any_array_create_from_raw( + (void *[]){ + "https://huggingface.co/leejet/FLUX.2-klein-4B-GGUF/resolve/main/flux-2-klein-4b-Q8_0.gguf", + "https://huggingface.co/madebyollin/taef2/resolve/main/taef2.safetensors", + "https://huggingface.co/unsloth/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q8_0.gguf", + }, + 3), + .web = "https://huggingface.co/leejet/FLUX.2-klein-4B-GGUF", + .license = "apache-2.0"}), GC_ALLOC_INIT(neural_node_model_t, {.name = "Z-Image-Turbo", .memory = "4GB", @@ -32,21 +33,6 @@ void neural_node_models_init() { .web = "https://huggingface.co/armory3d/z_image_turbo", .license = "apache-2.0"}), - GC_ALLOC_INIT(neural_node_model_t, - {.name = "FLUX 2 klein", - .memory = "4GB", - .size = "8.2GB", - .nodes = "Text to Image, Edit Image", - .urls = any_array_create_from_raw( - (void *[]){ - "https://huggingface.co/leejet/FLUX.2-klein-4B-GGUF/resolve/main/flux-2-klein-4b-Q8_0.gguf", - "https://huggingface.co/black-forest-labs/FLUX.2-small-decoder/resolve/main/full_encoder_small_decoder.safetensors", - "https://huggingface.co/unsloth/Qwen3-4B-GGUF/resolve/main/Qwen3-4B-Q8_0.gguf", - }, - 3), - .web = "https://huggingface.co/leejet/FLUX.2-klein-4B-GGUF", - .license = "apache-2.0"}), - GC_ALLOC_INIT(neural_node_model_t, {.name = "Qwen Image", .memory = "13GB", @@ -66,7 +52,7 @@ void neural_node_models_init() { {.name = "Qwen Image Edit", .memory = "13GB", .size = "18.3GB", - .nodes = "Edit Image, Inpaint Image, Outpaint Image, Tile Image, Vary Image", + .nodes = "Edit Image", .urls = any_array_create_from_raw( (void *[]){ "https://huggingface.co/unsloth/Qwen-Image-Edit-2511-GGUF/resolve/main/qwen-image-edit-2511-Q4_K_S.gguf", @@ -131,6 +117,6 @@ void neural_node_models_init() { .web = "https://huggingface.co/unsloth/Qwen3.6-27B-GGUF", .license = "apache-2.0"}), }, - 9); + 8); gc_root(neural_node_models); } diff --git a/paint/sources/nodes_neural/outpaint_image_node.c b/paint/sources/nodes_neural/outpaint_image_node.c deleted file mode 100644 index c89e0500..00000000 --- a/paint/sources/nodes_neural/outpaint_image_node.c +++ /dev/null @@ -1,180 +0,0 @@ - -#include "../global.h" - -void outpaint_image_node_button_on_next_frame(ui_node_t *node) { - ui_node_t *from_node = neural_from_node(node->inputs->buffer[0], 0); - gpu_texture_t *input = ui_nodes_get_node_preview_image(from_node); - if (input != NULL) { - - char *node_name = parser_material_node_name(node, NULL); - ui_handle_t *h = ui_handle(node_name); - i32 model = ui_nest(h, 0)->i; - char *prompt = ui_nest(h, 1)->text; - - if (string_equals(prompt, "")) { - prompt = "."; - } - - gpu_texture_t *mask = gpu_create_render_target(512, 512, GPU_TEXTURE_FORMAT_RGBA32); - draw_begin(mask, true, 0xffffffff); - draw_set_color(0xff000000); - draw_filled_rect(64, 64, 512 - 128, 512 - 128); - draw_end(); - - gpu_texture_t *input_scaled = gpu_create_render_target(512, 512, GPU_TEXTURE_FORMAT_RGBA32); - draw_begin(input_scaled, true, model == 0 ? 0xff808080 : 0xffff0000); - draw_scaled_image(input, 64, 64, 512 - 128, 512 - 128); - draw_end(); - - char *dir = neural_node_dir(); - iron_write_png(string("%s%sinput.png", dir, PATH_SEP), gpu_get_texture_pixels(input_scaled), input_scaled->width, input_scaled->height, 0); - iron_write_png(string("%s%smask.png", dir, PATH_SEP), gpu_get_texture_pixels(mask), mask->width, mask->height, 0); - - string_array_t *argv; - if (model == 0) { - argv = any_array_create_from_raw( - (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "-m", - string("%s/v1-5-pruned-emaonly.safetensors", dir), - "--offload-to-cpu", - "--steps", - "30", - "-s", - "-1", - "--cfg-scale", - "0", - "--strength", - "1", - "-W", - "512", - "-H", - "512", - "-p", - prompt, - "-i", - string("%s/input.png", dir), - "--mask", - string("%s/mask.png", dir), - "-o", - string("%s/output.png", dir), - NULL, - }, - 25); - } - else { - argv = any_array_create_from_raw( - (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/qwen-image-edit-2511-Q4_K_S.gguf", dir), - "--vae", - string("%s/Qwen_Image-VAE.safetensors", dir), - "--llm", - string("%s/Qwen2.5-VL-7B-Instruct-Q4_K_S.gguf", dir), - "--llm_vision", - string("%s/mmproj-F16.gguf", dir), - "--offload-to-cpu", - "--diffusion-fa", - "--qwen-image-zero-cond-t", - "--steps", - "50", - "-s", - "-1", - "-W", - "512", - "-H", - "512", - "-p", - "replace red squares by extending image contents", // prompt, - "-r", - string("%s/input.png", dir), - "-o", - string("%s/output.png", dir), - NULL, - }, - 27); - } - - iron_exec_async(argv->buffer[0], argv->buffer); - sys_notify_on_update(neural_node_check_result, node); - } -} - -void outpaint_image_node_button(i32 node_id) { - ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); - ui_node_t *node = ui_get_node(canvas->nodes, node_id); - char *node_name = parser_material_node_name(node, NULL); - ui_handle_t *h = ui_handle(node_name); - string_array_t *models = any_array_create_from_raw( - (void *[]){ - "Stable Diffusion", - "Qwen Image Edit", - }, - 2); - i32 model = ui_combo(ui_nest(h, 0), models, tr("Model"), false, UI_ALIGN_LEFT, true); - char *prompt = ui_text_area(ui_nest(h, 1), UI_ALIGN_LEFT, true, tr("prompt"), true); - node->buttons->buffer[0]->height = string_split(prompt, "\n")->length + 2; - - if (neural_node_button(node, models->buffer[model])) { - sys_notify_on_next_frame(&outpaint_image_node_button_on_next_frame, node); - } -} - -void outpaint_image_node_init() { - - ui_node_t *outpaint_image_node_def = - GC_ALLOC_INIT(ui_node_t, {.id = 0, - .name = _tr("Outpaint Image"), - .type = "NEURAL_OUTPAINT_IMAGE", - .x = 0, - .y = 0, - .color = 0xff4982a0, - .inputs = any_array_create_from_raw( - (void *[]){ - GC_ALLOC_INIT(ui_node_socket_t, {.id = 0, - .node_id = 0, - .name = _tr("Color"), - .type = "RGBA", - .color = 0xffc7c729, - .default_value = f32_array_create_xyzw(1.0, 1.0, 1.0, 1.0), - .min = 0.0, - .max = 1.0, - .precision = 100, - .display = 0}), - }, - 1), - .outputs = any_array_create_from_raw( - (void *[]){ - GC_ALLOC_INIT(ui_node_socket_t, {.id = 0, - .node_id = 0, - .name = _tr("Color"), - .type = "RGBA", - .color = 0xffc7c729, - .default_value = f32_array_create_xyzw(0.0, 0.0, 0.0, 1.0), - .min = 0.0, - .max = 1.0, - .precision = 100, - .display = 0}), - }, - 1), - .buttons = any_array_create_from_raw( - (void *[]){ - GC_ALLOC_INIT(ui_node_button_t, {.name = "outpaint_image_node_button", - .type = "CUSTOM", - .output = -1, - .default_value = f32_array_create_x(0), - .data = NULL, - .min = 0.0, - .max = 1.0, - .precision = 100, - .height = 0}), - }, - 1), - .width = 0, - .flags = 0}); - - any_array_push(nodes_material_neural, outpaint_image_node_def); - any_map_set(parser_material_node_vectors, "NEURAL_OUTPAINT_IMAGE", neural_node_vector); - any_map_set(ui_nodes_custom_buttons, "outpaint_image_node_button", outpaint_image_node_button); -} diff --git a/paint/sources/nodes_neural/text_to_image_node.c b/paint/sources/nodes_neural/text_to_image_node.c index 04b74d15..71705316 100644 --- a/paint/sources/nodes_neural/text_to_image_node.c +++ b/paint/sources/nodes_neural/text_to_image_node.c @@ -1,19 +1,28 @@ #include "../global.h" -string_array_t *text_to_image_node_sd_args(char *dir, char *prompt) { +string_array_t *text_to_image_node_flux_klein_args(char *dir, char *prompt) { string_array_t *argv = any_array_create_from_raw( (void *[]){ string("%s/%s", dir, neural_node_sd_bin()), - "-m", - string("%s/v1-5-pruned-emaonly.safetensors", dir), + "--diffusion-model", + string("%s/flux-2-klein-4b-Q8_0.gguf", dir), + "--taesd", + string("%s/taef2.safetensors", dir), + "--llm", + string("%s/Qwen3-4B-Q8_0.gguf", dir), + "--cfg-scale", + "1.0", + "--sampling-method", + "euler", + "--diffusion-fa", "--offload-to-cpu", - "-W", - "512", - "-H", - "512", "--steps", - "40", + "4", + "-W", + string("%d", g_config->neural_res), + "-H", + string("%d", g_config->neural_res), "-s", "-1", "-o", @@ -22,7 +31,7 @@ string_array_t *text_to_image_node_sd_args(char *dir, char *prompt) { string("'%s'", prompt), NULL, }, - 17); + 26); return argv; } @@ -41,9 +50,9 @@ string_array_t *text_to_image_node_zimage_args(char *dir, char *prompt) { "--cfg-scale", "1.0", "-W", - "512", + string("%d", g_config->neural_res), "-H", - "512", + string("%d", g_config->neural_res), "--steps", "40", "-s", @@ -74,9 +83,9 @@ string_array_t *text_to_image_node_qwen_args(char *dir, char *prompt) { "euler", "--offload-to-cpu", "-W", - "512", + string("%d", g_config->neural_res), "-H", - "512", + string("%d", g_config->neural_res), "--steps", "20", "-s", @@ -91,41 +100,6 @@ string_array_t *text_to_image_node_qwen_args(char *dir, char *prompt) { return argv; } -string_array_t *text_to_image_node_flux_klein_args(char *dir, char *prompt) { - string_array_t *argv = any_array_create_from_raw( - (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/flux-2-klein-4b-Q8_0.gguf", dir), - "--vae", - // string("%s/full_encoder_small_decoder.safetensors", dir), - string("%s/flux_ae.safetensors", dir), - "--llm", - string("%s/Qwen3-4B-Q8_0.gguf", dir), - "--cfg-scale", - "1.0", - "--sampling-method", - "euler", - "--diffusion-fa", - "--offload-to-cpu", - "--steps", - "4", - "-W", - "512", - "-H", - "512", - "-s", - "-1", - "-o", - string("%s/output.png", dir), - "-p", - string("'%s'", prompt), - NULL, - }, - 26); - return argv; -} - void text_to_image_node_run(ui_node_t *node, void (*callback)(ui_node_t *)) { char *node_name = parser_material_node_name(node, NULL); ui_handle_t *h = ui_handle(node_name); @@ -137,16 +111,13 @@ void text_to_image_node_run(ui_node_t *node, void (*callback)(ui_node_t *)) { } string_array_t *argv; if (model == 0) { - argv = text_to_image_node_sd_args(dir, prompt); + argv = text_to_image_node_flux_klein_args(dir, prompt); } else if (model == 1) { argv = text_to_image_node_zimage_args(dir, prompt); } - else if (model == 2) { - argv = text_to_image_node_qwen_args(dir, prompt); - } else { - argv = text_to_image_node_flux_klein_args(dir, prompt); + argv = text_to_image_node_qwen_args(dir, prompt); } if (node->buttons->buffer[1]->default_value->buffer[0] > 0.0) { array_insert(argv, argv->length - 1, "--circular"); @@ -161,12 +132,11 @@ void text_to_image_node_button(i32 node_id) { ui_handle_t *h = ui_handle(node_name); string_array_t *models = any_array_create_from_raw( (void *[]){ - "Stable Diffusion", + "FLUX 2 klein", "Z-Image-Turbo", "Qwen Image", - "FLUX 2 klein", }, - 4); + 3); i32 model = ui_combo(ui_nest(h, 0), models, tr("Model"), false, UI_ALIGN_LEFT, true); char *prompt = ui_text_area(ui_nest(h, 1), UI_ALIGN_LEFT, true, tr("prompt"), true); node->buttons->buffer[0]->height = string_split(prompt, "\n")->length + 2; diff --git a/paint/sources/nodes_neural/tile_image_node.c b/paint/sources/nodes_neural/tile_image_node.c index a2b7b753..4ea52b02 100644 --- a/paint/sources/nodes_neural/tile_image_node.c +++ b/paint/sources/nodes_neural/tile_image_node.c @@ -5,101 +5,50 @@ void tile_image_node_button_on_next_frame(ui_node_t *node) { ui_node_t *from_node = neural_from_node(node->inputs->buffer[0], 0); gpu_texture_t *input = ui_nodes_get_node_preview_image(from_node); if (input != NULL) { - char *node_name = parser_material_node_name(node, NULL); - ui_handle_t *h = ui_handle(node_name); - i32 model = ui_nest(h, 0)->i; - char *prompt = ui_nest(h, 1)->text; - gpu_texture_t *tile = gpu_create_render_target(512, 512, GPU_TEXTURE_FORMAT_RGBA32); - draw_begin(tile, false, 0); - draw_scaled_image(input, -256, -256, 512, 512); - draw_scaled_image(input, 256, -256, 512, 512); - draw_scaled_image(input, -256, 256, 512, 512); - draw_scaled_image(input, 256, 256, 512, 512); - if (model == 1) { - draw_set_color(0xffff0000); - draw_filled_triangle(0, 256, 256, 0, 512, 256); - draw_filled_triangle(0, 256, 256, 512, 512, 256); - } - draw_end(); - - // diamond - u8_array_t *u8a = u8_array_create(512 * 512); - for (i32 i = 0; i < 512 * 512; ++i) { - i32 x = i % 512; - i32 y = math_floor(i / 512.0); - i32 l = y < 256 ? y : (511 - y); - u8a->buffer[i] = (x > 256 - l && x < 256 + l) ? 255 : 0; - // u8a[i] = (x > 256 - l && x < 256 + l) ? 128 : 0; - } - gpu_texture_t *mask = gpu_create_texture_from_bytes(u8a, 512, 512, GPU_TEXTURE_FORMAT_R8); - char *dir = neural_node_dir(); - iron_write_png(string("%s%sinput.png", dir, PATH_SEP), gpu_get_texture_pixels(tile), tile->width, tile->height, 0); - iron_write_png(string("%s%smask.png", dir, PATH_SEP), gpu_get_texture_pixels(mask), mask->width, mask->height, 1); - - if (string_equals(prompt, "")) { - prompt = "."; - } + char *node_name = parser_material_node_name(node, NULL); + ui_handle_t *h = ui_handle(node_name); + i32 model = ui_nest(h, 0)->i; + char *dir = neural_node_dir(); + iron_write_png(string("%s%sinput.png", dir, PATH_SEP), gpu_get_texture_pixels(input), input->width, input->height, 0); string_array_t *argv; if (model == 0) { argv = any_array_create_from_raw( (void *[]){ string("%s/%s", dir, neural_node_sd_bin()), - "-m", - string("%s/v1-5-pruned-emaonly.safetensors", dir), + "--diffusion-model", + string("%s/flux-2-klein-4b-Q8_0.gguf", dir), + "--vae", + // string("%s/full_encoder_small_decoder.safetensors", dir), + string("%s/flux_ae.safetensors", dir), + "--llm", + string("%s/Qwen3-4B-Q8_0.gguf", dir), + "--cfg-scale", + "1.0", + "--sampling-method", + "euler", + "--diffusion-fa", "--offload-to-cpu", "--steps", - "30", + "4", "-s", "-1", "-W", - "512", + string("%d", g_config->neural_res), "-H", - "512", + string("%d", g_config->neural_res), + "--circular", + "--strength", + "0.3", "-p", - prompt, + "", "-i", string("%s/input.png", dir), - "--mask", - string("%s/mask.png", dir), "-o", string("%s/output.png", dir), NULL, }, - 21); - } - else { - argv = any_array_create_from_raw( - (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/qwen-image-edit-2511-Q4_K_S.gguf", dir), - "--vae", - string("%s/Qwen_Image-VAE.safetensors", dir), - "--llm", - string("%s/Qwen2.5-VL-7B-Instruct-Q4_K_S.gguf", dir), - "--llm_vision", - string("%s/mmproj-F16.gguf", dir), - "--offload-to-cpu", - "--diffusion-fa", - "--qwen-image-zero-cond-t", - "--steps", - "50", - "-s", - "-1", - "-W", - "512", - "-H", - "512", - "-p", - "replace red area by extending image contents. modify red area only", - "-r", - string("%s/input.png", dir), - "-o", - string("%s/output.png", dir), - NULL, - }, - 27); + 31); } iron_exec_async(argv->buffer[0], argv->buffer); @@ -114,13 +63,10 @@ void tile_image_node_button(i32 node_id) { ui_handle_t *h = ui_handle(node_name); string_array_t *models = any_array_create_from_raw( (void *[]){ - "Stable Diffusion", - "Qwen Image Edit", + "FLUX 2 klein", }, - 2); - i32 model = ui_combo(ui_nest(h, 0), models, tr("Model"), false, UI_ALIGN_LEFT, true); - char *prompt = ui_text_area(ui_nest(h, 1), UI_ALIGN_LEFT, true, tr("prompt"), true); - node->buttons->buffer[0]->height = string_split(prompt, "\n")->length + 2; + 1); + i32 model = ui_combo(ui_nest(h, 0), models, tr("Model"), false, UI_ALIGN_LEFT, true); if (neural_node_button(node, models->buffer[model])) { sys_notify_on_next_frame(&tile_image_node_button_on_next_frame, node); diff --git a/paint/sources/nodes_neural/vary_image_node.c b/paint/sources/nodes_neural/vary_image_node.c deleted file mode 100644 index 9bdc0768..00000000 --- a/paint/sources/nodes_neural/vary_image_node.c +++ /dev/null @@ -1,184 +0,0 @@ - -#include "../global.h" - -void vary_image_node_button_on_next_frame(ui_node_t *node) { - ui_node_t *from_node = neural_from_node(node->inputs->buffer[0], 0); - gpu_texture_t *input = ui_nodes_get_node_preview_image(from_node); - if (input != NULL) { - - gpu_texture_t *mask = gpu_create_render_target(512, 512, GPU_TEXTURE_FORMAT_RGBA32); - draw_begin(mask, true, 0xffffffff); - draw_end(); - -#ifdef IRON_BGRA - buffer_t *input_buf = buffer_bgra_swap(gpu_get_texture_pixels(input)); // Vulkan non-rt textures need a flip -#else - buffer_t *input_buf = gpu_get_texture_pixels(input); -#endif - - char *dir = neural_node_dir(); - iron_write_png(string("%s%sinput.png", dir, PATH_SEP), input_buf, input->width, input->height, 0); - iron_write_png(string("%s%smask.png", dir, PATH_SEP), gpu_get_texture_pixels(mask), mask->width, mask->height, 0); - - char *node_name = parser_material_node_name(node, NULL); - ui_handle_t *h = ui_handle(node_name); - i32 model = ui_nest(h, 0)->i; - char *prompt = ui_nest(h, 1)->text; - - if (string_equals(prompt, "")) { - prompt = "."; - } - - string_array_t *argv; - if (model == 0) { - argv = any_array_create_from_raw( - (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "-m", - string("%s/v1-5-pruned-emaonly.safetensors", dir), - "--offload-to-cpu", - "--steps", - "30", - "-s", - "-1", - "-W", - "512", - "-H", - "512", - "--cfg-scale", - "0.0", - "--strength", - "0.2", - "-p", - prompt, - "-i", - string("%s/input.png", dir), - "--mask", - string("%s/mask.png", dir), - "-o", - string("%s/output.png", dir), - NULL, - }, - 25); - } - else { - argv = any_array_create_from_raw( - (void *[]){ - string("%s/%s", dir, neural_node_sd_bin()), - "--diffusion-model", - string("%s/qwen-image-edit-2511-Q4_K_S.gguf", dir), - "--vae", - string("%s/Qwen_Image-VAE.safetensors", dir), - "--llm", - string("%s/Qwen2.5-VL-7B-Instruct-Q4_K_S.gguf", dir), - "--llm_vision", - string("%s/mmproj-F16.gguf", dir), - "--offload-to-cpu", - "--diffusion-fa", - "--qwen-image-zero-cond-t", - "--steps", - "50", - "-s", - "-1", - "-W", - "512", - "-H", - "512", - // "--cfg-scale", - // "1.0", - // "--strength", - // "0.2", - "-p", - "vary contents of the image", - // prompt, - "-r", - string("%s/input.png", dir), - "-o", - string("%s/output.png", dir), - NULL, - }, - 27); - } - - iron_exec_async(argv->buffer[0], argv->buffer); - sys_notify_on_update(neural_node_check_result, node); - } -} - -void vary_image_node_button(i32 node_id) { - ui_node_canvas_t *canvas = ui_nodes_get_canvas(true); - ui_node_t *node = ui_get_node(canvas->nodes, node_id); - char *node_name = parser_material_node_name(node, NULL); - ui_handle_t *h = ui_handle(node_name); - string_array_t *models = any_array_create_from_raw( - (void *[]){ - "Stable Diffusion", - "Qwen Image Edit", - }, - 2); - i32 model = ui_combo(ui_nest(h, 0), models, tr("Model"), false, UI_ALIGN_LEFT, true); - char *prompt = ui_text_area(ui_nest(h, 1), UI_ALIGN_LEFT, true, tr("prompt"), true); - node->buttons->buffer[0]->height = string_split(prompt, "\n")->length + 2; - - if (neural_node_button(node, models->buffer[model])) { - sys_notify_on_next_frame(&vary_image_node_button_on_next_frame, node); - } -} - -void vary_image_node_init() { - - ui_node_t *vary_image_node_def = - GC_ALLOC_INIT(ui_node_t, {.id = 0, - .name = _tr("Vary Image"), - .type = "NEURAL_VARY_IMAGE", - .x = 0, - .y = 0, - .color = 0xff4982a0, - .inputs = any_array_create_from_raw( - (void *[]){ - GC_ALLOC_INIT(ui_node_socket_t, {.id = 0, - .node_id = 0, - .name = _tr("Color"), - .type = "RGBA", - .color = 0xffc7c729, - .default_value = f32_array_create_xyzw(1.0, 1.0, 1.0, 1.0), - .min = 0.0, - .max = 1.0, - .precision = 100, - .display = 0}), - }, - 1), - .outputs = any_array_create_from_raw( - (void *[]){ - GC_ALLOC_INIT(ui_node_socket_t, {.id = 0, - .node_id = 0, - .name = _tr("Color"), - .type = "RGBA", - .color = 0xffc7c729, - .default_value = f32_array_create_xyzw(0.0, 0.0, 0.0, 1.0), - .min = 0.0, - .max = 1.0, - .precision = 100, - .display = 0}), - }, - 1), - .buttons = any_array_create_from_raw( - (void *[]){ - GC_ALLOC_INIT(ui_node_button_t, {.name = "vary_image_node_button", - .type = "CUSTOM", - .output = -1, - .default_value = f32_array_create_x(0), - .data = NULL, - .min = 0.0, - .max = 1.0, - .precision = 100, - .height = 0}), - }, - 1), - .width = 0, - .flags = 0}); - - any_array_push(nodes_material_neural, vary_image_node_def); - any_map_set(parser_material_node_vectors, "NEURAL_VARY_IMAGE", neural_node_vector); - any_map_set(ui_nodes_custom_buttons, "vary_image_node_button", vary_image_node_button); -} diff --git a/paint/sources/types.h b/paint/sources/types.h index ecb2e090..b105ce6e 100644 --- a/paint/sources/types.h +++ b/paint/sources/types.h @@ -133,6 +133,7 @@ typedef struct config { bool grid_snap; bool experimental; i32 neural_backend; + i32 neural_res; render_mode_t render_mode; workspace_t workspace; workflow_t workflow; diff --git a/paint/sources/ui/box_preferences.c b/paint/sources/ui/box_preferences.c index 7e8ca4f3..3676a614 100644 --- a/paint/sources/ui/box_preferences.c +++ b/paint/sources/ui/box_preferences.c @@ -882,6 +882,18 @@ void box_preferences_neural_tab() { ui_tooltip(tr("Backend for neural node processing")); } + ui_handle_t *h_neural_res = ui_handle(__ID__); + h_neural_res->i = g_config->neural_res == 2048 ? 2 : (g_config->neural_res == 1024 ? 1 : 0); + string_array_t *neural_res_combo = any_array_create_from_raw( + (void *[]){ + "512", + "1024", + "2048", + }, + 3); + i32 neural_res_sel = ui_combo(h_neural_res, neural_res_combo, tr("Resolution"), true, UI_ALIGN_LEFT, true); + g_config->neural_res = neural_res_sel == 2 ? 2048 : (neural_res_sel == 1 ? 1024 : 512); + ui_text(tr("Models"), UI_ALIGN_LEFT, 0x00000000); if (neural_node_models == NULL) { @@ -1002,7 +1014,7 @@ void box_preferences_plugins_tab_plugin_menu() { file_start(path); } if (ui_menu_button(tr("Edit in Script Tab"), "", ICON_NONE)) { - buffer_t *blob = data_get_blob(string("plugins/%s", _box_preferences_f)); + buffer_t *blob = data_get_blob(string("plugins/%s", _box_preferences_f)); tab_scripts_set(sys_buffer_to_string(blob)); data_delete_blob(string("plugins/%s", _box_preferences_f)); console_info(tr("Script opened")); diff --git a/paint/sources/ui/ui_nodes.c b/paint/sources/ui/ui_nodes.c index bcfe1519..849103dc 100644 --- a/paint/sources/ui/ui_nodes.c +++ b/paint/sources/ui/ui_nodes.c @@ -581,11 +581,11 @@ void ui_nodes_draw_menubar() { } bool full = true; - #ifdef IRON_IOS +#ifdef IRON_IOS if (config_is_iphone()) { full = false; } - #endif +#endif // Editable canvas name if (full) { diff --git a/paint/sources/ui/ui_toolbar.c b/paint/sources/ui/ui_toolbar.c index d8ef98c0..e98aacd4 100644 --- a/paint/sources/ui/ui_toolbar.c +++ b/paint/sources/ui/ui_toolbar.c @@ -121,8 +121,8 @@ i32 ui_toolbar_x() { void ui_toolbar_draw_show_3d_view() { if (g_config->touch_ui) { - i32 toolbar_w = ui_toolbar_default_w * UI_SCALE() + 14 * UI_SCALE(); - i32 y = ui_header_h + 8 * UI_SCALE(); + i32 toolbar_w = ui_toolbar_default_w * UI_SCALE() + 14 * UI_SCALE(); + i32 y = ui_header_h + 8 * UI_SCALE(); if ((ui_view2d_show || ui_nodes_show) && !g_config->touch_ui) { y += toolbar_w; diff --git a/paint/sources/ui/ui_view2d.c b/paint/sources/ui/ui_view2d.c index f9b4a8c9..a73bd179 100644 --- a/paint/sources/ui/ui_view2d.c +++ b/paint/sources/ui/ui_view2d.c @@ -546,11 +546,11 @@ void ui_view2d_update(void *_) { } bool full = true; - #ifdef IRON_IOS +#ifdef IRON_IOS if (config_is_iphone()) { full = false; } - #endif +#endif // Editable layer name if (full) {