#include "pch.h" #include "legacy_brush_ui_services.h" #include "app.h" #include "asset.h" #include "canvas.h" #include "image.h" #include "log.h" #include "node_panel_brush.h" #include "node_panel_stroke.h" namespace pp::panopainter { namespace { class LegacyBrushUiServices final : public pp::app::BrushUiServices { public: LegacyBrushUiServices( App& app, bool update_quick = false, bool update_color_panel = false, const std::shared_ptr& preset_brush = nullptr) noexcept : app_(app) , update_quick_(update_quick) , update_color_panel_(update_color_panel) , preset_brush_(preset_brush) { } void set_tip_color(float r, float g, float b, float a) override { if (!Canvas::I || !Canvas::I->m_current_brush) return; Canvas::I->m_current_brush->m_tip_color = glm::vec4(r, g, b, a); if (update_quick_ && app_.quick) app_.quick->set_color(Canvas::I->m_current_brush->m_tip_color); if (update_color_panel_ && app_.color) app_.color->set_color(Canvas::I->m_current_brush->m_tip_color); } void set_texture( pp::app::BrushUiTextureSlot slot, std::string_view path, std::string_view thumbnail_path) override { if (!Canvas::I || !Canvas::I->m_current_brush) return; const std::string texture_path(path); const std::string thumbnail(thumbnail_path); switch (slot) { case pp::app::BrushUiTextureSlot::tip: Canvas::I->m_current_brush->load_tip(texture_path, thumbnail); break; case pp::app::BrushUiTextureSlot::pattern: Canvas::I->m_current_brush->load_pattern(texture_path, thumbnail); break; case pp::app::BrushUiTextureSlot::dual: Canvas::I->m_current_brush->load_dual(texture_path, thumbnail); break; } } void replace_brush_from_preset(bool preserve_existing_color, bool load_resources) override { if (!Canvas::I || !Canvas::I->m_current_brush || !preset_brush_) return; const auto color = Canvas::I->m_current_brush->m_tip_color; *Canvas::I->m_current_brush = *preset_brush_; if (preserve_existing_color) Canvas::I->m_current_brush->m_tip_color = color; if (load_resources) Canvas::I->m_current_brush->load(); } void refresh_brush_ui(bool update_color_ui, bool update_brush_ui) override { app_.brush_update(update_color_ui, update_brush_ui); } private: App& app_; bool update_quick_ = false; bool update_color_panel_ = false; std::shared_ptr preset_brush_; }; class LegacyBrushStrokeControlServices final : public pp::app::BrushStrokeControlServices { public: explicit LegacyBrushStrokeControlServices(NodePanelStroke& panel) : panel_(panel) {} void set_float_setting(pp::app::BrushStrokeFloatSetting setting, float value) override { auto& brush = *Canvas::I->m_current_brush; switch (setting) { case pp::app::BrushStrokeFloatSetting::tip_size: brush.m_tip_size = value; break; case pp::app::BrushStrokeFloatSetting::tip_spacing: brush.m_tip_spacing = value; break; case pp::app::BrushStrokeFloatSetting::tip_flow: brush.m_tip_flow = value; break; case pp::app::BrushStrokeFloatSetting::tip_opacity: brush.m_tip_opacity = value; break; case pp::app::BrushStrokeFloatSetting::tip_angle: brush.m_tip_angle = value; break; case pp::app::BrushStrokeFloatSetting::tip_angle_smooth: brush.m_tip_angle_smooth = value; break; case pp::app::BrushStrokeFloatSetting::tip_mix: brush.m_tip_mix = value; break; case pp::app::BrushStrokeFloatSetting::tip_wet: brush.m_tip_wet = value; break; case pp::app::BrushStrokeFloatSetting::tip_noise: brush.m_tip_noise = value; break; case pp::app::BrushStrokeFloatSetting::tip_hue: brush.m_tip_hue = value; break; case pp::app::BrushStrokeFloatSetting::tip_saturation: brush.m_tip_sat = value; break; case pp::app::BrushStrokeFloatSetting::tip_value: brush.m_tip_val = value; break; case pp::app::BrushStrokeFloatSetting::jitter_scale: brush.m_jitter_scale = value; break; case pp::app::BrushStrokeFloatSetting::jitter_angle: brush.m_jitter_angle = value; break; case pp::app::BrushStrokeFloatSetting::jitter_scatter: brush.m_jitter_scatter = value; break; case pp::app::BrushStrokeFloatSetting::jitter_flow: brush.m_jitter_flow = value; break; case pp::app::BrushStrokeFloatSetting::jitter_opacity: brush.m_jitter_opacity = value; break; case pp::app::BrushStrokeFloatSetting::jitter_hue: brush.m_jitter_hue = value; break; case pp::app::BrushStrokeFloatSetting::jitter_saturation: brush.m_jitter_sat = value; break; case pp::app::BrushStrokeFloatSetting::jitter_value: brush.m_jitter_val = value; break; case pp::app::BrushStrokeFloatSetting::jitter_aspect: brush.m_jitter_aspect = value; break; case pp::app::BrushStrokeFloatSetting::dual_size: brush.m_dual_size = value; break; case pp::app::BrushStrokeFloatSetting::dual_spacing: brush.m_dual_spacing = value; break; case pp::app::BrushStrokeFloatSetting::dual_scatter: brush.m_dual_scatter = value; break; case pp::app::BrushStrokeFloatSetting::tip_aspect: brush.m_tip_aspect = value; break; case pp::app::BrushStrokeFloatSetting::dual_opacity: brush.m_dual_opacity = value; break; case pp::app::BrushStrokeFloatSetting::dual_flow: brush.m_dual_flow = value; break; case pp::app::BrushStrokeFloatSetting::dual_rotate: brush.m_dual_rotate = value; break; case pp::app::BrushStrokeFloatSetting::pattern_scale: brush.m_pattern_scale = value; break; case pp::app::BrushStrokeFloatSetting::pattern_brightness: brush.m_pattern_brightness = value; break; case pp::app::BrushStrokeFloatSetting::pattern_contrast: brush.m_pattern_contrast = value; break; case pp::app::BrushStrokeFloatSetting::pattern_depth: brush.m_pattern_depth = value; break; } } void set_bool_setting(pp::app::BrushStrokeBoolSetting setting, bool value) override { auto& brush = *Canvas::I->m_current_brush; switch (setting) { case pp::app::BrushStrokeBoolSetting::tip_angle_init: brush.m_tip_angle_init = value; break; case pp::app::BrushStrokeBoolSetting::tip_angle_follow: brush.m_tip_angle_follow = value; break; case pp::app::BrushStrokeBoolSetting::tip_flow_pressure: brush.m_tip_flow_pressure = value; break; case pp::app::BrushStrokeBoolSetting::tip_opacity_pressure: brush.m_tip_opacity_pressure = value; break; case pp::app::BrushStrokeBoolSetting::tip_size_pressure: brush.m_tip_size_pressure = value; break; case pp::app::BrushStrokeBoolSetting::jitter_scatter_both_axis: brush.m_jitter_scatter_bothaxis = value; break; case pp::app::BrushStrokeBoolSetting::jitter_aspect_both_axis: brush.m_jitter_aspect_bothaxis = value; break; case pp::app::BrushStrokeBoolSetting::jitter_hsv_each_sample: brush.m_jitter_hsv_eachsample = value; break; case pp::app::BrushStrokeBoolSetting::tip_invert: brush.m_tip_invert = value; break; case pp::app::BrushStrokeBoolSetting::tip_flip_x: brush.m_tip_flipx = value; break; case pp::app::BrushStrokeBoolSetting::tip_flip_y: brush.m_tip_flipy = value; break; case pp::app::BrushStrokeBoolSetting::pattern_enabled: brush.m_pattern_enabled = value; break; case pp::app::BrushStrokeBoolSetting::dual_enabled: brush.m_dual_enabled = value; break; case pp::app::BrushStrokeBoolSetting::dual_scatter_both_axis: brush.m_dual_scatter_bothaxis = value; break; case pp::app::BrushStrokeBoolSetting::dual_invert: brush.m_dual_invert = value; break; case pp::app::BrushStrokeBoolSetting::dual_flip_x: brush.m_dual_flipx = value; break; case pp::app::BrushStrokeBoolSetting::dual_flip_y: brush.m_dual_flipy = value; break; case pp::app::BrushStrokeBoolSetting::dual_random_flip: brush.m_dual_randflip = value; break; case pp::app::BrushStrokeBoolSetting::tip_random_flip_x: brush.m_tip_randflipx = value; break; case pp::app::BrushStrokeBoolSetting::tip_random_flip_y: brush.m_tip_randflipy = value; break; case pp::app::BrushStrokeBoolSetting::pattern_each_sample: brush.m_pattern_eachsample = value; break; case pp::app::BrushStrokeBoolSetting::pattern_invert: brush.m_pattern_invert = value; break; case pp::app::BrushStrokeBoolSetting::pattern_flip_x: brush.m_pattern_flipx = value; break; case pp::app::BrushStrokeBoolSetting::pattern_flip_y: brush.m_pattern_flipy = value; break; case pp::app::BrushStrokeBoolSetting::pattern_random_offset: brush.m_pattern_rand_offset = value; break; } } void set_blend_mode(pp::app::BrushStrokeBlendSetting setting, int blend_mode) override { auto& brush = *Canvas::I->m_current_brush; switch (setting) { case pp::app::BrushStrokeBlendSetting::tip: brush.m_blend_mode = blend_mode; break; case pp::app::BrushStrokeBlendSetting::dual: brush.m_dual_blend_mode = blend_mode; break; case pp::app::BrushStrokeBlendSetting::pattern: brush.m_pattern_blend_mode = blend_mode; break; } } void reset_tip_aspect(float value) override { panel_.m_tip_aspect->set_value(value); Canvas::I->m_current_brush->m_tip_aspect = value; } void reset_default_brush() override { auto brush = std::make_shared(); brush->load_tip( panel_.m_brush_popup->get_texture_path(panel_.m_default_brush_index), panel_.m_brush_popup->get_thumb_path(panel_.m_default_brush_index)); brush->m_tip_size = 30; brush->m_tip_flow = .9f; brush->m_tip_spacing = .1f; brush->m_tip_opacity = 1.f; Canvas::I->m_current_brush = brush; } void update_stroke_controls() override { panel_.update_controls(); } void refresh_stroke_preview() override { if (panel_.m_preview) { panel_.m_preview->m_brush = Canvas::I->m_current_brush; } } void notify_stroke_changed() override { if (panel_.on_stroke_change) { panel_.on_stroke_change(&panel_); } } private: NodePanelStroke& panel_; }; } // namespace class LegacyBrushTextureListServices final : public pp::app::BrushTextureListServices { public: explicit LegacyBrushTextureListServices(NodePanelBrush& panel) noexcept : panel_(panel) { } pp::foundation::Status add_texture_from_source( std::string_view source_path, std::string_view high_path, std::string_view thumbnail_path, std::string_view brush_name, bool converts_brush_alpha) override { Image img; if (!img.load_file(std::string(source_path))) { return pp::foundation::Status::invalid_argument("brush texture source could not be loaded"); } if (converts_brush_alpha) { img.gayscale_alpha(); } auto thumbnail_image = img.resize(64, 64).resize_squared(glm::u8vec4(255)); thumbnail_image.save_png(std::string(thumbnail_path)); img.save_png(std::string(high_path)); NodeButtonBrush* brush = new NodeButtonBrush; panel_.m_container->add_child(brush); brush->init(); brush->create(); brush->loaded(); const auto thumbnail_path_string = std::string(thumbnail_path); brush->set_icon(thumbnail_path_string.c_str()); brush->thumb_path = std::string(thumbnail_path); brush->high_path = std::string(high_path); brush->brush_name = std::string(brush_name); brush->m_user_brush = true; brush->on_click = std::bind(&NodePanelBrush::handle_click, &panel_, std::placeholders::_1); return pp::foundation::Status::success(); } void remove_texture(int index, bool delete_texture_files) override { auto* brush = brush_at(index); if (!brush) { return; } if (delete_texture_files) { Asset::delete_file(brush->thumb_path); Asset::delete_file(brush->high_path); } if (panel_.m_current == brush) { panel_.m_current = nullptr; } panel_.m_container->remove_child(brush); } void move_texture(int from_index, int to_index) override { if (auto* brush = brush_at(from_index)) { panel_.m_container->move_child(brush, to_index); } } void select_texture(int index) override { if (panel_.m_current) { panel_.m_current->m_selected = false; } panel_.m_current = brush_at(index); if (!panel_.m_current) { return; } panel_.m_current->m_selected = true; if (panel_.on_brush_changed) { panel_.on_brush_changed(&panel_, index); } } void save_texture_list() override { panel_.save(); } private: NodeButtonBrush* brush_at(int index) const { if (index < 0 || index >= static_cast(panel_.m_container->m_children.size())) { return nullptr; } return static_cast(panel_.m_container->m_children[index].get()); } NodePanelBrush& panel_; }; bool apply_legacy_brush_color_plan( App& app, glm::vec4 color, bool update_quick, bool update_color_panel) { const auto plan = pp::app::plan_brush_ui_color(color.r, color.g, color.b, color.a); if (!plan) return false; LegacyBrushUiServices services(app, update_quick, update_color_panel); const auto status = pp::app::execute_brush_ui_plan(plan.value(), services); if (!status.ok()) LOG("Brush color action failed: %s", status.message); return status.ok(); } bool apply_legacy_brush_texture_plan( App& app, pp::app::BrushUiTextureSlot slot, const std::string& path, const std::string& thumb) { const auto plan = pp::app::plan_brush_ui_texture(slot, path, thumb); if (!plan) return false; LegacyBrushUiServices services(app); const auto status = pp::app::execute_brush_ui_plan(plan.value(), services); if (!status.ok()) LOG("Brush texture action failed: %s", status.message); return status.ok(); } bool apply_legacy_brush_preset_plan(App& app, const std::shared_ptr& brush) { const auto plan = pp::app::plan_brush_ui_preset_replace(static_cast(brush)); if (!plan) return false; LegacyBrushUiServices services(app, false, false, brush); const auto status = pp::app::execute_brush_ui_plan(plan.value(), services); if (!status.ok()) LOG("Brush preset action failed: %s", status.message); return status.ok(); } pp::foundation::Status execute_legacy_brush_stroke_changed_plan(App& app) { const auto plan = pp::app::plan_brush_ui_stroke_settings_changed(); LegacyBrushUiServices services(app); return pp::app::execute_brush_ui_plan(plan, services); } pp::foundation::Status execute_legacy_brush_texture_list_plan( NodePanelBrush& panel, const pp::app::BrushTextureListPlan& plan) { LegacyBrushTextureListServices services(panel); return pp::app::execute_brush_texture_list_plan(plan, services); } pp::foundation::Status execute_legacy_brush_stroke_control_plan( NodePanelStroke& panel, const pp::app::BrushStrokeControlPlan& plan) { LegacyBrushStrokeControlServices services(panel); return pp::app::execute_brush_stroke_control_plan(plan, services); } } // namespace pp::panopainter