#include "pch.h" #include "log.h" #include "node_panel_brush.h" #include "asset.h" #include "texture.h" #ifdef __APPLE__ #include #elif __WEB__ void webgl_sync(); #endif #include "canvas.h" #include "app.h" #include "abr.h" Node* NodeButtonBrush::clone_instantiate() const { return new NodeButtonBrush(); } void NodeButtonBrush::init() { init_template_file("data/dialogs/panel-brushes.xml", "tpl-brush-icon"); color_hover = glm::vec4(.7, .7, .7, 1); color_normal = glm::vec4(.3, .3, .3, 1); m_color = color_normal; img = (NodeImage*)m_children[0].get(); } void NodeButtonBrush::set_icon(const char* path) { img->m_path = path; img->m_tex_id = const_hash(img->m_path.c_str()); img->m_use_mipmaps = true; img->create(); } void NodeButtonBrush::draw() { m_color = m_mouse_inside ? color_hover : color_normal; m_color = m_selected ? glm::vec4(.9, 0, 0, 1) : m_color; NodeButtonCustom::draw(); } bool NodeButtonBrush::read(BinaryStreamReader& r) { Serializer::Descriptor d; r >> d; d.value("brush_name", brush_name); d.value("high_path", high_path); d.value("thumb_path", thumb_path); d.value("m_user_brush", m_user_brush); high_path = str_replace(high_path, "{data_path}", App::I->data_path); thumb_path = str_replace(thumb_path, "{data_path}", App::I->data_path); return true; } void NodeButtonBrush::write(BinaryStreamWriter& w) const { Serializer::Descriptor d; d.class_id = "brush"; d.name = L"Brush class"; d.props["brush_name"] = std::make_shared(brush_name); d.props["high_path"] = std::make_shared( str_replace(high_path, App::I->data_path, "{data_path}")); d.props["thumb_path"] = std::make_shared( str_replace(thumb_path, App::I->data_path, "{data_path}")); d.props["m_user_brush"] = std::make_shared(m_user_brush); w << d; } Node* NodePanelBrush::clone_instantiate() const { return new NodePanelBrush(); } void NodePanelBrush::init() { init_template_file("data/dialogs/panel-brushes.xml", "tpl-panel-brushes"); m_btn_add = find("btn-add"); m_btn_add->on_click = [this](Node*) { App::I->pick_file({ "JPG", "PNG" }, [this](std::string path) { std::string name, base, ext; std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)"); std::smatch m; if (!std::regex_search(path, m, r)) return; base = m[1].str(); name = m[2].str(); ext = m[3].str(); Image img; if (!m_dir_name.empty() && img.load_file(path)) { std::string path_high = App::I->data_path + "/" + m_dir_name + "/" + name + ".png"; std::string path_thumb = App::I->data_path + "/" + m_dir_name + "/thumbs/" + name + ".png"; //img = img.resize_squared(glm::u8vec4(255)); if (m_dir_name == "brushes") img.gayscale_alpha(); auto thumb = img.resize(64, 64).resize_squared(glm::u8vec4(255)); thumb.save_png(path_thumb); //auto po2 = img.resize_power2(); img.save_png(path_high); NodeButtonBrush* brush = new NodeButtonBrush; m_container->add_child(brush); brush->init(); brush->create(); brush->loaded(); brush->set_icon(path_thumb.c_str()); brush->thumb_path = path_thumb; brush->high_path = path_high; brush->brush_name = name; brush->m_user_brush = true; brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1); save(); } }); }; m_btn_remove = find("btn-remove"); m_btn_remove->on_click = [this](Node*) { if (m_current) { int idx = m_container->get_child_index(m_current); if (m_current->m_user_brush) { // only delete user brushes Asset::delete_file(m_current->thumb_path); Asset::delete_file(m_current->high_path); } m_container->remove_child(m_current); if (m_container->m_children.size() > 0) { idx = std::max(0, std::min(idx, (int)m_container->m_children.size() - 1)); m_current = (NodeButtonBrush*)m_container->m_children[idx].get(); m_current->m_selected = true; if (on_brush_changed) on_brush_changed(this, idx); } else { m_current = nullptr; } save(); } }; m_btn_up = find("btn-up"); m_btn_up->on_click = [this](Node*) { if (m_current) { int idx = m_container->get_child_index(m_current); idx = std::max(0, std::min(idx - 1, (int)m_container->m_children.size() - 1)); m_container->move_child(m_current, idx); save(); } }; m_btn_down = find("btn-down"); m_btn_down->on_click = [this](Node*) { if (m_current) { int idx = m_container->get_child_index(m_current); idx = std::max(0, std::min(idx + 1, (int)m_container->m_children.size() - 1)); m_container->move_child(m_current, idx); save(); } }; m_container = find("brushes"); if (Asset::exist(App::I->data_path + "/settings/" + m_dir_name + ".bin") && !restore()) { auto mb = App::I->message_box("Brushes", "Could not read brush textures file, it will be deleted.", true); mb->btn_ok->on_click = [this, mb](Node*) { Asset::delete_file(App::I->data_path + "/settings/" + m_dir_name + ".bin"); mb->destroy(); }; mb->btn_cancel->on_click = [mb](Node*) { mb->destroy(); }; } if (m_container->m_children.empty() && !m_dir_name.empty()) scan(); save(); } kEventResult NodePanelBrush::handle_event(Event* e) { switch (e->m_type) { case kEventType::MouseLeave: if (!m_interacted) break; // else fall through case kEventType::MouseUpL: if (!m_mouse_inside) { mouse_release(); m_parent->remove_child(this); if (on_popup_close) on_popup_close(this); } break; default: return kEventResult::Available; break; } return kEventResult::Consumed; } void NodePanelBrush::handle_click(Node* target) { if (target == m_current) return; if (m_current) m_current->m_selected = false; m_current = (NodeButtonBrush*)target; m_current->m_selected = true; if (on_brush_changed) on_brush_changed(this, m_container->get_child_index(target)); m_interacted = true; } int NodePanelBrush::find_brush(const std::string & name) const { for (int i = 0; i < m_container->m_children.size(); i++) { NodeButtonBrush* b = (NodeButtonBrush*)m_container->m_children[i].get(); if (b->brush_name.find(name) != std::string::npos) return i; } return -1; } std::string NodePanelBrush::get_texture_path(int index) const { if (index < 0 || index >= m_container->m_children.size()) return ""; return ((NodeButtonBrush*)m_container->m_children[index].get())->high_path; } std::string NodePanelBrush::get_thumb_path(int index) const { if (index < 0 || index >= m_container->m_children.size()) return ""; return ((NodeButtonBrush*)m_container->m_children[index].get())->thumb_path; } bool NodePanelBrush::save() { std::ofstream f(App::I->data_path + "/settings/" + m_dir_name + ".bin", std::ios::binary); if (f.good()) { BinaryStreamWriter sw; sw.init(); sw.wstring_raw("PPVR"); // magic code sw.wu16(0); // version major sw.wu16(1); // minor sw.wu32((int)m_container->m_children.size()); // number of items for (const auto& child : m_container->m_children) { auto b = std::static_pointer_cast(child); sw << *b; } f.write((char*)sw.m_data.data(), sw.m_data.size()); f.close(); #if __WEB__ webgl_sync(); #endif return true; } return false; } bool NodePanelBrush::restore() { Asset f; auto path = App::I->data_path + "/settings/" + m_dir_name + ".bin"; if (f.open(path.c_str())) { f.read_all(); if (f.m_len == 0) return false; BinaryStreamReader sr; sr.init(f.m_data, f.m_len); // sanity checks if (sr.rstring(4) != "PPVR") { LOG("PPVR tag not found") return false; } auto vmaj = sr.ru16(); auto vmin = sr.ru16(); if (vmaj != 0 && vmin != 1) { LOG("unrecognised version %d.%d", vmaj, vmin); return false; } auto count = sr.ru32(); for (int k = 0; k < count; k++) { auto b = std::make_shared(); if (!b->read(sr)) { LOG("error deserializing the button brush"); return false; } if (Asset::exist(b->high_path)) { m_container->add_child(b); b->init(); b->create(); b->loaded(); b->set_icon(b->thumb_path.c_str()); b->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1); } } return true; } return false; } void NodePanelBrush::clear() { m_container->remove_all_children(); } void NodePanelBrush::scan() { auto icons = Asset::list_files("data/" + m_dir_name, ".*\\.png$"); for (auto& i : icons) { std::string path = "data/" + m_dir_name + "/thumbs/" + i; std::string path_hi = "data/" + m_dir_name + "/" + i; NodeButtonBrush* brush = new NodeButtonBrush; m_container->add_child(brush); brush->init(); brush->create(); brush->loaded(); brush->set_icon(path.c_str()); brush->thumb_path = path; brush->high_path = path_hi; brush->brush_name = i; brush->m_user_brush = false; // system brush, cannot be deleted from file brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1); } auto custom_icons = Asset::list_files(App::I->data_path + "/" + m_dir_name, ".*\\.png$"); for (auto& i : custom_icons) { std::string path_thumb = App::I->data_path + "/" + m_dir_name + "/thumbs/" + i; std::string path_high = App::I->data_path + "/" + m_dir_name + "/" + i; NodeButtonBrush* brush = new NodeButtonBrush; m_container->add_child(brush); brush->init(); brush->create(); brush->loaded(); brush->set_icon(path_thumb.c_str()); brush->thumb_path = path_thumb; brush->high_path = path_high; brush->brush_name = i; brush->m_user_brush = true; brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1); } } void NodePanelBrush::reload() { clear(); scan(); save(); } void NodePanelBrush::added(Node* parent) { m_interacted = false; } // ----------------------------------------------------------------------- std::vector NodePanelBrushPreset::s_panels; Node* NodeBrushPresetItem::clone_instantiate() const { return new NodeBrushPresetItem(); } void NodeBrushPresetItem::init() { init_template_file("data/dialogs/panel-brushes.xml", "tpl-brush-preset"); color_hover = glm::vec4(.7, .7, .7, 1); color_normal = glm::vec4(.3, .3, .3, 1); m_color = color_normal; m_thumb = find("thumb"); m_caption_size = find("caption-size"); m_preview = find("canvas"); m_preview->m_min_flow = 1.f; } void NodeBrushPresetItem::draw() { m_color = m_mouse_inside ? color_hover : color_normal; m_color = m_selected ? glm::vec4(.9, 0, 0, 1) : m_color; NodeButtonCustom::draw(); } NodePanelBrushPreset::NodePanelBrushPreset() { s_panels.push_back(this); } NodePanelBrushPreset::~NodePanelBrushPreset() { s_panels.erase(std::remove(s_panels.begin(), s_panels.end(), this)); } //--- Node* NodePanelBrushPreset::clone_instantiate() const { return new NodePanelBrushPreset(); } void NodePanelBrushPreset::init() { init_template_file("data/dialogs/panel-brushes.xml", "tpl-panel-brush-preset"); m_container = find("brushes"); m_btn_add = find("btn-add"); m_btn_add->on_click = [this] (Node*) { for (auto p : s_panels) p->add_brush(std::make_shared(*Canvas::I->m_current_brush)); save(); }; m_btn_up = find("btn-up"); m_btn_up->on_click = [this](Node*) { if (m_current) { int idx = m_container->get_child_index(m_current); int to = std::max(idx - 1, 0); for (auto p : s_panels) p->m_container->move_child(p->m_container->m_children[idx].get(), to); save(); } }; m_btn_down = find("btn-down"); m_btn_down->on_click = [this](Node*) { if (m_current) { int idx = m_container->get_child_index(m_current); int to = std::min(idx + 1, (int)m_container->m_children.size() - 1); for (auto p : s_panels) p->m_container->move_child(p->m_container->m_children[idx].get(), to); save(); } }; /* m_btn_save = find("btn-save"); m_btn_save->on_click = [this](Node*) { if (m_current) { *m_current->m_brush = *Canvas::I->m_current_brush; m_current->m_preview->draw_stroke(); m_current->m_thumb->set_image(m_current->m_brush->m_brush_thumb_path); save(); } }; */ m_btn_delete = find("btn-remove"); m_btn_delete->on_click = [this](Node*) { if (!m_current) return; int index = m_container->get_child_index(m_current); for (auto p : s_panels) { bool new_current = !p->m_current || p->m_container->get_child_index(p->m_current) == index; p->m_container->m_children[index]->destroy(); if (p->m_container->m_children.empty()) { p->m_current = nullptr; } else if (new_current) { int next = std::min((int)p->m_container->m_children.size() - 1, index); p->m_current = (NodeBrushPresetItem*)p->m_container->m_children[next].get(); p->m_current->m_selected = true; } p->m_notification->SetVisibility(p->m_container->m_children.size() == 0); } save(); }; m_btn_menu = find("btn-menu"); m_btn_menu->on_click = [this](Node* b) { auto popup = add_child_file("data/dialogs/panel-brushes.xml", "tpl-brush-popup"); popup->SetPosition(b->m_pos.x + b->m_size.x, b->m_pos.y); root()->add_child(popup); root()->update(); auto bounds = root()->GetSize() - zw(popup->get_children_rect()); popup->SetPosition(glm::clamp(popup->m_pos, { 0, 0 }, bounds)); popup->on_select = [this, popup] (Node* target, int index) { switch (index) { case 0: // import file App::I->pick_file({"abr", "ppbr"}, [this] (std::string path) { std::thread([this, path] { BT_SetTerminate(); import_brush(path); for (auto p : s_panels) p->m_notification->SetVisibility(p->m_container->m_children.size() == 0); }).detach(); }); break; case 1: // export file App::I->dialog_ppbr_export(); break; case 2: // download break; case 3: // upload break; case 4: // clear presets { auto mb = App::I->message_box("Clear Presets", "Do you want to remove all the Brush Presets?", true); mb->btn_ok->m_text->set_text("Yes"); mb->btn_cancel->m_text->set_text("No"); mb->btn_ok->on_click = mb->on_submit = [this, mb](Node*) { App::I->presets->clear_brushes(); App::I->presets->save(); mb->destroy(); }; break; } } popup->destroy(); }; }; m_btn_import = find("import"); m_btn_import->on_click = [this] (Node*) { App::I->pick_file({ "abr", "ppbr" }, [this](std::string path) { std::thread([this, path] { BT_SetTerminate(); import_brush(path); for (auto p : s_panels) p->m_notification->SetVisibility(p->m_container->m_children.size() == 0); }).detach(); }); }; m_btn_download = find("download"); m_btn_download->on_click = [this] (Node*) { App::I->dialog_preset_download(); }; m_notification = find("notification"); if (Asset::exist(App::I->data_path + "/settings/presets.bin") && !restore()) { auto mb = App::I->message_box("Presets", "Could not read brush presets file, it will be deleted.", true); mb->btn_ok->on_click = [mb](Node*) { Asset::delete_file(App::I->data_path + "/settings/presets.bin"); mb->destroy(); }; mb->btn_cancel->on_click = [mb](Node*) { mb->destroy(); }; } m_notification->SetVisibility(m_container->m_children.size() == 0); } kEventResult NodePanelBrushPreset::handle_event(Event* e) { switch (e->m_type) { case kEventType::MouseLeave: if (!m_interacted) break; // else fall through case kEventType::MouseUpL: if (!m_mouse_inside) { mouse_release(); m_parent->remove_child(this); if (on_popup_close) on_popup_close(this); } break; default: return kEventResult::Available; break; } return kEventResult::Consumed; } void NodePanelBrushPreset::handle_click(Node* target) { int idx = m_container->get_child_index(target); for (auto p : s_panels) { if (p->m_current) p->m_current->m_selected = false; p->m_current = (NodeBrushPresetItem*)p->m_container->get_child_at(idx); p->m_current->m_selected = true; p->m_interacted = true; } if (on_brush_changed) on_brush_changed(this, m_current->m_brush); } bool NodePanelBrushPreset::save() { auto path = App::I->data_path + "/settings/presets.bin"; std::ofstream f(path, std::ios::binary); if (f.good()) { BinaryStreamWriter sw; sw.init(); sw.wstring_raw("PPVR"); sw.wu16(0); sw.wu16(1); sw.wu32((int)m_container->m_children.size()); for (int ci = 0; ci < m_container->m_children.size(); ci++) { auto bpi = static_cast(m_container->get_child_at(ci)); sw << *bpi->m_brush; } f.write((char*)sw.m_data.data(), sw.m_data.size()); return true; } return false; } bool NodePanelBrushPreset::restore() { auto path = App::I->data_path + "/settings/presets.bin"; Asset f; if (f.open(path.c_str())) { f.read_all(); BinaryStreamReader sr; sr.init(f.m_data, f.m_len); // sanity checks auto magic = sr.rstring(4); if (magic != "PPVR") { LOG("PPVR tag not found") return false; } auto vmaj = sr.ru16(); auto vmin = sr.ru16(); if (vmaj != 0 && vmin != 1) { LOG("unrecognised version %d.%d", vmaj, vmin); return false; } auto count = sr.ru32(); for (int k = 0; k < count; k++) { auto b = std::make_shared(); if (!b->read(sr)) { LOG("error deserializing the brush"); return false; } if (b->valid()) { add_brush(b); } } m_notification->SetVisibility(m_container->m_children.size() == 0); return true; } return false; } void NodePanelBrushPreset::add_brush(std::shared_ptr brush) { NodeBrushPresetItem* b = new NodeBrushPresetItem; m_container->add_child(b); b->init(); b->create(); b->loaded(); b->thumb_path = brush->m_brush_thumb_path; b->high_path = brush->m_brush_path; b->m_brush = brush; b->m_preview->m_brush = brush; b->m_preview->draw_stroke(); b->m_thumb->m_use_mipmaps = true; b->m_thumb->set_image(brush->m_brush_thumb_path); b->m_caption_size->set_text_format("%d", (int)brush->m_tip_size); b->on_click = std::bind(&NodePanelBrushPreset::handle_click, this, std::placeholders::_1); m_notification->SetVisibility(m_container->m_children.size() == 0); } bool NodePanelBrushPreset::export_ppbr(const std::string& path_in, const PPBRInfo& info_data) { std::string path = path_in; if (path_in.find(".ppbr") == std::string::npos) path += ".ppbr"; LOG("export ppbr to: %s", path.c_str()); std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)?$)"); std::smatch m; if (!std::regex_search(path, m, r)) return false; auto base = m[1].str(); auto name = m[2].str(); auto ext = m[3].str(); #if __OSX__ std::string out_path = info_data.dest_path + "/" + name + "_data"; #else std::string out_path = base + "/" + name + "_data"; #endif bool path_created = info_data.export_data && !out_path.empty() ? Asset::create_dir(out_path) : false; std::ofstream f(path, std::ios::binary); if (f.good()) { BinaryStreamWriter sw; sw.init(BinaryStream::ByteOrder::LittleEndian); sw.wstring_raw("PPBR"); sw.wu16(0); sw.wu16(1); // count list of images std::set img_brushes; std::set img_patterns; for (auto& c : m_container->m_children) { auto bpi = std::static_pointer_cast(c); if (!bpi->m_brush->m_brush_path.empty() && !Asset::is_asset(bpi->m_brush->m_brush_path)) img_brushes.insert(bpi->m_brush->m_brush_path); if (!bpi->m_brush->m_dual_path.empty() && !Asset::is_asset(bpi->m_brush->m_dual_path)) img_brushes.insert(bpi->m_brush->m_dual_path); if (!bpi->m_brush->m_pattern_path.empty() && !Asset::is_asset(bpi->m_brush->m_pattern_path)) img_patterns.insert(bpi->m_brush->m_pattern_path); } Serializer::Descriptor info; info.class_id = "ppbr_info"; info.name = L"info header"; bool has_header_image = info_data.header_image != nullptr; info.props["author"] = std::make_shared(info_data.author); info.props["email"] = std::make_shared(info_data.email); info.props["url"] = std::make_shared(info_data.url); info.props["descr"] = std::make_shared(info_data.descr); info.props["has_header_image"] = std::make_shared(has_header_image); info.props["num_brush_tips"] = std::make_shared(img_brushes.size()); info.props["num_brush_patt"] = std::make_shared(img_patterns.size()); info.props["num_brushes"] = std::make_shared(m_container->m_children.size()); auto pb = App::I->show_progress("Exporting PPBR", 1 + img_brushes.size() + img_patterns.size() + m_container->m_children.size() * 2); sw << info; // header image if (has_header_image) { sw << *info_data.header_image; if (path_created) info_data.header_image->save_jpg(out_path + "/header.jpg", 75); } pb->increment(); // create previews sw.wu32((int)m_container->m_children.size()); auto pr = std::make_unique(); pr->m_preview_size = pr->m_size = { 256, 128 }; int thumb_counter = 0; for (auto& c : m_container->m_children) { auto bpi = std::static_pointer_cast(c); pr->m_brush = std::make_shared(*bpi->m_brush); // create copy pr->m_brush->load(); Image img = pr->render_to_image(); img.file_name = pr->m_brush->m_name; sw << img; if (path_created) img.save_jpg(fmt::format(out_path + "/thumb-{:04d}.jpg", thumb_counter), 75); thumb_counter++; pb->increment(); } // write brushes sw.wu32((int)img_brushes.size()); for (std::string image_path : img_brushes) { Image img; if (!img.load(image_path)) LOG("export_ppbr failed to load image: %s", image_path.c_str()); sw << img; pb->increment(); } // write patterns sw.wu32((int)img_patterns.size()); for (std::string image_path : img_patterns) { Image img; if (!img.load(image_path)) LOG("export_ppbr failed to load image: %s", image_path.c_str()); sw << img; pb->increment(); } // write brush settings sw.wu32((int)m_container->m_children.size()); for (auto& c : m_container->m_children) { auto bpi = std::static_pointer_cast(c); sw << *bpi->m_brush; pb->increment(); } f.write((char*)sw.m_data.data(), sw.m_data.size()); pb->destroy(); return true; } LOG("export failed file creation"); return false; } bool NodePanelBrushPreset::import_ppbr(const std::string& path) { Asset f; if (f.open(path.c_str())) { f.read_all(); BinaryStreamReader sr; sr.init(f.m_data, f.m_len, BinaryStream::ByteOrder::LittleEndian); // sanity checks auto magic = sr.rstring(4); if (magic != "PPBR") { LOG("PPBR tag not found") return false; } auto vmaj = sr.ru16(); auto vmin = sr.ru16(); if (vmaj != 0 && vmin != 1) { LOG("unrecognised version %d.%d", vmaj, vmin); return false; } Serializer::Descriptor info; sr >> info; int num_brush_tips = info.value("num_brush_tips"); int num_brush_patt = info.value("num_brush_patt"); int num_brushes = info.value("num_brushes"); std::string info_dump = info.str(0, "Info"); LOG("%s", info_dump.c_str()); auto pb = App::I->show_progress("Importing PPBR", 1 + num_brush_patt + num_brush_tips + num_brushes * 2); // header image Image header_image; if (info.value("has_header_image")) sr >> header_image; pb->increment(); // stroke previews auto previews_count = sr.ru32(); for (int i = 0; i < previews_count; i++) { Image img; sr >> img; pb->increment(); } // list of images std::set img_brushes; std::set img_patterns; // brush tips auto tips_count = sr.ru32(); for (int i = 0; i < tips_count; i++) { Image img; sr >> img; std::string path = App::I->data_path + "/brushes/" + img.file_name + "." + img.file_ext; std::string path_thumb = App::I->data_path + "/brushes/thumbs/" + img.file_name + "." + img.file_ext; if (!Asset::exist(path)) { img.save_png(path); auto thumb = img.resize(64, 64); thumb.save_png(path_thumb); } else { LOG("import_ppbr: brush image already exists in %s", path.c_str()); } img_brushes.insert(path); pb->increment(); } // brush patterns auto patt_count = sr.ru32(); for (int i = 0; i < patt_count; i++) { Image img; sr >> img; std::string path = App::I->data_path + "/patterns/" + img.file_name + "." + img.file_ext; std::string path_thumb = App::I->data_path + "/patterns/thumbs/" + img.file_name + "." + img.file_ext; if (!Asset::exist(path)) { img.save_png(path); auto thumb = img.resize(64, 64); thumb.save_png(path_thumb); } else { LOG("import_ppbr: brush image already exists in %s", path.c_str()); } pb->increment(); img_patterns.insert(path); } // brush settings auto brushes_count = sr.ru32(); for (int i = 0; i < brushes_count; i++) { auto b = std::make_shared(); sr >> *b; b->relocate_paths(App::I->data_path); LOG("import_ppbr brush name %s", b->m_name.c_str()); if (b->valid()) { for (auto p : s_panels) p->add_brush(b); } pb->increment(); } save(); App::I->stroke->m_brush_popup->reload(); pb->destroy(); return true; } return false; } bool NodePanelBrushPreset::import_abr(const std::string& path) { BT_SetTerminate(); ABR abr; LOG("ABR detected"); std::string name, base, ext; std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)"); std::smatch m; if (!std::regex_search(path, m, r)) return false; base = m[1].str(); name = m[2].str(); ext = m[3].str(); if (!str_iequals(ext, "abr") || !Asset::exist(path)) return false; abr.open(path); auto pb = App::I->show_progress("Importing ABR", abr.m_samples.size() + abr.m_patterns.size() + abr.m_presets.size()); parallel_for(abr.m_samples.size(), [&](size_t i) //for (const auto& samp : abr.m_samples) { auto ii = abr.m_samples.begin(); std::advance(ii, i); const auto& samp = *ii; std::string path_high = App::I->data_path + "/brushes/" + samp.first + ".png"; std::string path_thumb = App::I->data_path + "/brushes/thumbs/" + samp.first + ".png"; auto padded = samp.second->resize_squared(glm::u8vec4(255)); //auto high = padded.resize_power2(); //high.save(path_high); samp.second->save_png(path_high); auto thumb = padded.resize(64, 64); thumb.save_png(path_thumb); pb->increment(); }); parallel_for(abr.m_patterns.size(), [&](size_t i) //for (const auto& patt : abr.m_patterns) { auto ii = abr.m_patterns.begin(); std::advance(ii, i); const auto& patt = *ii; std::string path_high = App::I->data_path + "/patterns/" + patt.first + ".png"; std::string path_thumb = App::I->data_path + "/patterns/thumbs/" + patt.first + ".png"; patt.second->save_png(path_high); auto thumb = patt.second->resize(64, 64); thumb.save_png(path_thumb); pb->increment(); }); auto brushes = abr.compute_brushes(App::I->data_path); App::I->ui_task([&]{ for (const auto& b : brushes) { if (b->valid()) { LOG("add preset %s", b->m_name.c_str()); for (auto p : s_panels) p->add_brush(b); } pb->increment(); } }); save(); App::I->stroke->m_brush_popup->reload(); pb->destroy(); return true; } bool NodePanelBrushPreset::import_brush(const std::string& path) { std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)"); std::smatch m; if (!std::regex_search(path, m, r)) return false; std::string base = m[1].str(); std::string name = m[2].str(); std::string ext = m[3].str(); return ext == "ppbr" ? import_ppbr(path) : import_abr(path); } void NodePanelBrushPreset::clear_brushes() { for (auto p : s_panels) { p->m_container->remove_all_children(); p->m_notification->SetVisibility(p->m_container->m_children.size() == 0); } } void NodePanelBrushPreset::added(Node* parent) { m_interacted = false; m_notification->SetVisibility(m_container->m_children.size() == 0); }