#include "../ImNodeFlow.h" #include #include #include #if defined(__APPLE__) #include #if defined(IMGUI_IMPL_OPENGL_ES2) #include #else #include #endif #else #include #if defined(IMGUI_IMPL_OPENGL_ES2) #include #else #include #endif #endif #include "../image_model.h" #include "../imfilebrowser.h" #include "../stb_image.h" #include "../uuid.h" #include #include #ifdef __EMSCRIPTEN__ #include "../emscripten_browser_file.h" #endif struct Texture { ImTextureID texture; ImVec2 size; }; struct LoadCallback { std::string filename; std::string mime_type; std::string_view buffer; }; class Exposure : public ImFlow::BaseNode { public: ImageBundle returnBundle; Exposure() { setTitle("Exposure"); setStyle(ImFlow::NodeStyle::green()); addIN("Image", ImageBundle()); addOUT("Image Out")->behaviour([this]() { return returnBundle; }); } ~Exposure() { stbi_image_free(returnBundle.image); } void draw() override { ImGui::SetNextItemWidth(100); ImGui::SliderInt("", &brightnessVal, -255, 255); ImageBundle val = getInVal("Image"); // If the input is valid, and out buffers are not if (val.loaded && !returnBundle.loaded) { int img_size = val.width * val.height * val.channels; uint8_t *duplicated_img = (uint8_t *)malloc(img_size); memcpy(duplicated_img, val.image, img_size); returnBundle.image = duplicated_img; returnBundle.height = val.height; returnBundle.width = val.width; returnBundle.channels = val.channels; returnBundle.loaded = true; returnBundle.fname = uuid::generate_uuid_v4(); fname = val.fname; compute(val.image, returnBundle.image, img_size, returnBundle.channels); } // If the input is invalid, and are buffers are valid if (!val.loaded && returnBundle.loaded) { stbi_image_free(returnBundle.image); returnBundle.loaded = false; } // If the input has changed if (val.fname != fname) { int img_size = val.width * val.height * val.channels; int old_img_size = returnBundle.width * returnBundle.height * returnBundle.channels; if (img_size == old_img_size) { memcpy(returnBundle.image, val.image, img_size); returnBundle.fname = uuid::generate_uuid_v4(); fname = val.fname; int img_size = returnBundle.width * returnBundle.height * returnBundle.channels; compute(val.image, returnBundle.image, img_size, returnBundle.channels); } else { stbi_image_free(returnBundle.image); uint8_t *duplicated_img = (uint8_t *)malloc(img_size); memcpy(duplicated_img, val.image, img_size); returnBundle.image = duplicated_img; returnBundle.height = val.height; returnBundle.width = val.width; returnBundle.channels = val.channels; returnBundle.fname = uuid::generate_uuid_v4(); fname = val.fname; int img_size = returnBundle.width * returnBundle.height * returnBundle.channels; compute(val.image, returnBundle.image, img_size, returnBundle.channels); } } if (returnBundle.loaded) { if (brightnessVal != prev_brightness) { int img_size = returnBundle.width * returnBundle.height * returnBundle.channels; compute(val.image, returnBundle.image, img_size, returnBundle.channels); prev_brightness = brightnessVal; returnBundle.fname = uuid::generate_uuid_v4(); } } } private: static int formula(uint8_t color, int val) { float vf = static_cast(val) / 255.0; vf = vf + 1.0; float cf = static_cast(color) / 255.0; float ret_val = (cf * vf) * 255.0f; return static_cast(ret_val); } void compute(uint8_t *input, uint8_t *output, int size, int channels) { for (int i = 0; i < size; i += channels) { uint8_t r = std::clamp(formula(input[i], brightnessVal), 0, 255); uint8_t g = std::clamp(formula(input[i + 1], brightnessVal), 0, 255); uint8_t b = std::clamp(formula(input[i + 2], brightnessVal), 0, 255); output[i] = r; output[i + 1] = g; output[i + 2] = b; } } int brightnessVal = 0; int prev_brightness = 0; std::string fname; }; class Contrast : public ImFlow::BaseNode { public: ImageBundle returnBundle; Contrast() { setTitle("Contrast"); setStyle(ImFlow::NodeStyle::green()); addIN("Image", ImageBundle()); addOUT("Image Out")->behaviour([this]() { return returnBundle; }); } ~Contrast() { stbi_image_free(returnBundle.image); } void draw() override { ImGui::SetNextItemWidth(100); ImGui::SliderInt("", &brightnessVal, -255, 255); ImageBundle val = getInVal("Image"); // If the input is valid, and out buffers are not if (val.loaded && !returnBundle.loaded) { int img_size = val.width * val.height * val.channels; uint8_t *duplicated_img = (uint8_t *)malloc(img_size); memcpy(duplicated_img, val.image, img_size); returnBundle.image = duplicated_img; returnBundle.height = val.height; returnBundle.width = val.width; returnBundle.channels = val.channels; returnBundle.loaded = true; returnBundle.fname = uuid::generate_uuid_v4(); fname = val.fname; compute(val.image, returnBundle.image, img_size, returnBundle.channels); } // If the input is invalid, and are buffers are valid if (!val.loaded && returnBundle.loaded) { stbi_image_free(returnBundle.image); returnBundle.loaded = false; } // If the input has changed if (val.fname != fname) { int img_size = val.width * val.height * val.channels; int old_img_size = returnBundle.width * returnBundle.height * returnBundle.channels; if (img_size == old_img_size) { memcpy(returnBundle.image, val.image, img_size); returnBundle.fname = uuid::generate_uuid_v4(); fname = val.fname; int img_size = returnBundle.width * returnBundle.height * returnBundle.channels; compute(val.image, returnBundle.image, img_size, returnBundle.channels); } else { stbi_image_free(returnBundle.image); uint8_t *duplicated_img = (uint8_t *)malloc(img_size); memcpy(duplicated_img, val.image, img_size); returnBundle.image = duplicated_img; returnBundle.height = val.height; returnBundle.width = val.width; returnBundle.channels = val.channels; returnBundle.fname = uuid::generate_uuid_v4(); fname = val.fname; int img_size = returnBundle.width * returnBundle.height * returnBundle.channels; compute(val.image, returnBundle.image, img_size, returnBundle.channels); } } if (returnBundle.loaded) { if (brightnessVal != prev_brightness) { int img_size = returnBundle.width * returnBundle.height * returnBundle.channels; compute(val.image, returnBundle.image, img_size, returnBundle.channels); prev_brightness = brightnessVal; returnBundle.fname = uuid::generate_uuid_v4(); } } } private: static int formula(uint8_t color, int val) { float vf = static_cast(val) / 255.0; vf = vf + 1.0; float cf = static_cast(color) / 255.0; float ret_val = (((cf - 0.5f) * vf) + 0.5f) * 255.0f; return static_cast(ret_val); } void compute(uint8_t *input, uint8_t *output, int size, int channels) { int nBval = brightnessVal + 256; for (int i = 0; i < size; i += channels) { uint8_t r = std::clamp(formula(input[i], brightnessVal), 0, 255); uint8_t g = std::clamp(formula(input[i + 1], brightnessVal), 0, 255); uint8_t b = std::clamp(formula(input[i + 2], brightnessVal), 0, 255); output[i] = r; output[i + 1] = g; output[i + 2] = b; } } int brightnessVal = 0; int prev_brightness = 0; std::string fname; }; class SimpleBrightness : public ImFlow::BaseNode { public: ImageBundle returnBundle; SimpleBrightness() { setTitle("Simple Brightness"); setStyle(ImFlow::NodeStyle::green()); addIN("Image", ImageBundle()); addOUT("Image Out")->behaviour([this]() { return returnBundle; }); } ~SimpleBrightness() { stbi_image_free(returnBundle.image); } void draw() override { ImGui::SetNextItemWidth(100); ImGui::SliderInt("", &brightnessVal, -255, 255); ImageBundle val = getInVal("Image"); // If the input is valid, and out buffers are not if (val.loaded && !returnBundle.loaded) { int img_size = val.width * val.height * val.channels; uint8_t *duplicated_img = (uint8_t *)malloc(img_size); memcpy(duplicated_img, val.image, img_size); returnBundle.image = duplicated_img; returnBundle.height = val.height; returnBundle.width = val.width; returnBundle.channels = val.channels; returnBundle.loaded = true; returnBundle.fname = uuid::generate_uuid_v4(); fname = val.fname; compute(val.image, returnBundle.image, img_size, returnBundle.channels); } // If the input is invalid, and are buffers are valid if (!val.loaded && returnBundle.loaded) { stbi_image_free(returnBundle.image); returnBundle.loaded = false; } // If the input has changed if (val.fname != fname) { int img_size = val.width * val.height * val.channels; int old_img_size = returnBundle.width * returnBundle.height * returnBundle.channels; if (img_size == old_img_size) { memcpy(returnBundle.image, val.image, img_size); returnBundle.fname = uuid::generate_uuid_v4(); fname = val.fname; int img_size = returnBundle.width * returnBundle.height * returnBundle.channels; compute(val.image, returnBundle.image, img_size, returnBundle.channels); } else { stbi_image_free(returnBundle.image); uint8_t *duplicated_img = (uint8_t *)malloc(img_size); memcpy(duplicated_img, val.image, img_size); returnBundle.image = duplicated_img; returnBundle.height = val.height; returnBundle.width = val.width; returnBundle.channels = val.channels; returnBundle.fname = uuid::generate_uuid_v4(); fname = val.fname; int img_size = returnBundle.width * returnBundle.height * returnBundle.channels; compute(val.image, returnBundle.image, img_size, returnBundle.channels); } } if (returnBundle.loaded) { if (brightnessVal != prev_brightness) { int img_size = returnBundle.width * returnBundle.height * returnBundle.channels; compute(val.image, returnBundle.image, img_size, returnBundle.channels); prev_brightness = brightnessVal; returnBundle.fname = uuid::generate_uuid_v4(); } } } private: void compute(uint8_t *input, uint8_t *output, int size, int channels) { for (int i = 0; i < size; i += channels) { uint8_t r = std::clamp(input[i] + brightnessVal, 0, 255); uint8_t g = std::clamp(input[i + 1] + brightnessVal, 0, 255); uint8_t b = std::clamp(input[i + 2] + brightnessVal, 0, 255); output[i] = r; output[i + 1] = g; output[i + 2] = b; } } int brightnessVal = 0; int prev_brightness = 0; std::string fname; }; class PreviewImage : public ImFlow::BaseNode { public: PreviewImage() { setTitle("Output"); setStyle(ImFlow::NodeStyle::cyan()); addIN("Image", ImageBundle()); } ~PreviewImage() { if (loaded) { glDeleteTextures(1, &txHandle); } } void draw() override { ImageBundle val = getInVal("Image"); if (val.loaded && !loaded) { GLuint textureHandle; glGenTextures(1, &textureHandle); glBindTexture(GL_TEXTURE_2D, textureHandle); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // This is required on WebGL for non // power-of-two textures glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Same #if defined(GL_UNPACK_ROW_LENGTH) && !defined(__EMSCRIPTEN__) glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); #endif glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, val.width, val.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, val.image); txHandle = textureHandle; id = val.fname; loaded = true; prev_h = val.height; prev_w = val.width; } if (!val.loaded && loaded) { glDeleteTextures(1, &txHandle); loaded = false; } if ((val.fname != id) && loaded) { if (prev_w != val.width || prev_h != val.height) { glDeleteTextures(1, &txHandle); GLuint textureHandle; glGenTextures(1, &textureHandle); glBindTexture(GL_TEXTURE_2D, textureHandle); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // This is required on WebGL for non // power-of-two textures glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Same #if defined(GL_UNPACK_ROW_LENGTH) && !defined(__EMSCRIPTEN__) glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); #endif glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, val.width, val.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, val.image); txHandle = textureHandle; } else { glBindTexture(GL_TEXTURE_2D, txHandle); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, val.width, val.height, GL_RGBA, GL_UNSIGNED_BYTE, val.image); glBindTexture(GL_TEXTURE_2D, 0); } prev_h = val.height; prev_w = val.width; id = val.fname; } if (loaded) { if (val.height < val.width) { ImGui::Image((void *)(intptr_t)txHandle, ImVec2(256, val.calcResizedWidth(256))); } else { ImGui::Image((void *)(intptr_t)txHandle, ImVec2(val.calcResizedHeight(256), 256)); } } } private: int prev_h = 0; int prev_w = 0; bool loaded = false; GLuint txHandle; std::string id; }; class LoadImage : public ImFlow::BaseNode { public: ImageBundle bundle; LoadImage() { setTitle("Load Image"); setStyle(ImFlow::NodeStyle::brown()); addOUT("Image")->behaviour([this]() { return bundle; }); } ~LoadImage() { if (bundle.loaded) { stbi_image_free(bundle.image); } } void draw() override { LoadCallback lc = LoadCallback(); #ifdef __EMSCRIPTEN__ if (ImGui::Button("Select File")) { emscripten_browser_file::upload(".png,.jpg,.jpeg", handle_upload_file, this); } #else if (bundle.fname == "") { handle_upload_file_local( "/Users/tanishqdubey/Downloads/Odd-eyed_cat_by_ihasb33r.jpg", this); } #endif if (bundle.fname != "") { bundle.loaded = true; ImGui::Text("File Loaded"); } else { ImGui::Text("No image loaded"); } } private: bool showPreview = false; static void handle_upload_file_local(const char *filename, void *callback) { auto lc{reinterpret_cast(callback)}; const int channelCount = 4; int imageFileChannelCount; int width, height; uint8_t *image = (uint8_t *)stbi_load(filename, &width, &height, &imageFileChannelCount, channelCount); if (image == NULL) { fprintf(stderr, "%s\nFailed to open %s\n", stbi_failure_reason(), lc->bundle.fname.c_str()); lc->bundle.fname = ""; } lc->bundle.fname = uuid::generate_uuid_v4(); lc->bundle.width = width; lc->bundle.height = height; lc->bundle.channels = channelCount; lc->bundle.image = image; lc->bundle.loaded = true; } static void handle_upload_file(std::string const &filename, std::string const &mime_type, std::string_view buffer, void *callback) { auto lc{reinterpret_cast(callback)}; const int channelCount = 4; int imageFileChannelCount; int width, height; auto d = buffer.data(); stbi_uc *image = (stbi_uc *)stbi_load_from_memory( (const stbi_uc *)buffer.data(), buffer.size(), &width, &height, &imageFileChannelCount, channelCount); if (image == NULL) { fprintf(stderr, "%s\nFailed to open %s - %s\n", stbi_failure_reason(), filename.c_str(), mime_type.c_str()); lc->bundle.fname = ""; } lc->bundle.fname = uuid::generate_uuid_v4(); lc->bundle.width = width; lc->bundle.height = height; lc->bundle.channels = channelCount; lc->bundle.image = image; lc->bundle.loaded = true; } };