#include "pch.h" #include "log.h" #include "canvas.h" #include "legacy_canvas_render_shell_services.h" #include "legacy_ui_gl_dispatch.h" #include "legacy_canvas_stroke_erase_services.h" #include "legacy_gl_renderbuffer_dispatch.h" #include "legacy_canvas_stroke_commit_services.h" #include "legacy_canvas_stroke_composite_services.h" #include "legacy_canvas_stroke_edge_services.h" #include "legacy_canvas_stroke_execution_services.h" #include "legacy_canvas_stroke_runtime_services.h" #include "legacy_canvas_stroke_shader_services.h" #include "legacy_canvas_object_draw_services.h" #include "legacy_canvas_projection_services.h" #include "legacy_canvas_stroke_services.h" #include "legacy_ui_overlay_services.h" #include "app_core/document_canvas.h" #include "texture.h" #include "node_progress_bar.h" #include "paint_renderer/compositor.h" #include "renderer_gl/opengl_capabilities.h" #include "util.h" #include #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #endif namespace { GLint current_canvas_stroke_internal_format() { const auto renderer_features = ShaderManager::render_device_features(); if (renderer_features.float32_linear_filtering) return static_cast(pp::renderer::gl::rgba32f_internal_format()); if (renderer_features.float16_render_targets) return static_cast(pp::renderer::gl::rgba16f_internal_format()); return static_cast(pp::renderer::gl::rgba8_internal_format()); } GLint rgba8_internal_format() { return static_cast(pp::renderer::gl::rgba8_internal_format()); } pp::renderer::RenderDeviceFeatures canvas_render_device_features() noexcept { return ShaderManager::render_device_features(); } pp::paint_renderer::CanvasStrokeRasterizationPlan canvas_stroke_rasterization_plan( int width, int height) noexcept { return pp::panopainter::plan_legacy_canvas_stroke_rasterization( canvas_render_device_features(), width, height); } pp::paint_renderer::CanvasStrokeFeedbackPlan canvas_destination_feedback_plan( int width, int height) noexcept { return canvas_stroke_rasterization_plan(width, height).feedback; } pp::paint_renderer::CanvasStrokeMaterialPlan canvas_stroke_material_plan( const Brush& brush, bool destination_feedback_needed) noexcept { return pp::panopainter::plan_legacy_canvas_stroke_material( pp::paint_renderer::CanvasStrokeMaterialRequest { .destination_feedback_needed = destination_feedback_needed, .pattern_enabled = brush.m_pattern_enabled, .pattern_eachsample = brush.m_pattern_eachsample, .wet_blend = brush.m_tip_wet > 0.F, .mix_blend = brush.m_tip_mix > 0.F, .noise_enabled = brush.m_tip_noise > 0.F, .dual_brush_enabled = brush.m_dual_enabled, .dual_blend_mode = brush.m_dual_blend_mode, .pattern_blend_mode = brush.m_pattern_blend_mode, .dual_alpha = brush.m_dual_opacity, }); } pp::renderer::Extent2D canvas_stroke_extent(int width, int height) noexcept { return pp::renderer::Extent2D { .width = static_cast(std::max(width, 0)), .height = static_cast(std::max(height, 0)), }; } pp::paint_renderer::CanvasBlendGatePlan draw_merge_blend_gate_plan( int width, int height, const std::vector>& layers, const Brush* brush) noexcept { std::vector layer_blend_modes; layer_blend_modes.reserve(layers.size()); for (const auto& layer : layers) { if (!layer) { continue; } layer_blend_modes.push_back(layer->m_blend_mode); } const auto plan = pp::paint_renderer::plan_canvas_blend_gate( canvas_render_device_features(), pp::paint_renderer::CanvasBlendGateRequest { .extent = pp::renderer::Extent2D { .width = static_cast(std::max(width, 0)), .height = static_cast(std::max(height, 0)), }, .layer_blend_modes = layer_blend_modes, .has_stroke_blend_mode = brush != nullptr, .stroke_blend_mode = brush ? brush->m_blend_mode : 0, }); if (plan) { return plan.value(); } pp::paint_renderer::CanvasBlendGatePlan fallback; fallback.shader_blend = true; fallback.complex_blend = true; fallback.compatibility_fallback = true; return fallback; } GLenum depth_test_state() { return static_cast(pp::renderer::gl::depth_test_state()); } GLenum scissor_test_state() { return static_cast(pp::renderer::gl::scissor_test_state()); } GLenum blend_state() { return static_cast(pp::renderer::gl::blend_state()); } GLint texture_filter_linear() { return static_cast(pp::renderer::gl::linear_texture_filter()); } GLint texture_filter_linear_mipmap_linear() { return static_cast(pp::renderer::gl::linear_mipmap_linear_texture_filter()); } GLint texture_filter_nearest() { return static_cast(pp::renderer::gl::nearest_texture_filter()); } GLint texture_wrap_repeat() { return static_cast(pp::renderer::gl::repeat_texture_wrap()); } GLint texture_wrap_clamp_to_border() { return static_cast(pp::renderer::gl::clamp_to_border_texture_wrap()); } pp::renderer::gl::OpenGlPixelFormat texture_format_for_image_channels(int channel_count) { return pp::renderer::gl::texture_format_for_channel_count(static_cast(channel_count)); } void set_active_texture_unit(std::uint32_t unit_index) { pp::legacy::ui_gl::activate_texture_unit(unit_index, "Canvas"); } void unbind_texture_2d() { pp::legacy::ui_gl::unbind_texture_2d("Canvas"); } void apply_canvas_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) { pp::legacy::ui_gl::apply_viewport(x, y, width, height, "Canvas"); } pp::renderer::gl::OpenGlViewportRect query_canvas_viewport() { return pp::legacy::ui_gl::query_viewport_rect("Canvas"); } std::array query_canvas_clear_color() { return pp::legacy::ui_gl::query_clear_color("Canvas"); } void apply_canvas_clear_color(std::array color) { pp::legacy::ui_gl::set_clear_color(color, "Canvas"); } void apply_canvas_scissor(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) { pp::legacy::ui_gl::apply_scissor_rect(x, y, width, height, "Canvas"); } void apply_canvas_capability(std::uint32_t state, bool enabled) { pp::legacy::ui_gl::set_capability(state, enabled, "Canvas"); } bool query_canvas_capability(std::uint32_t state) { return pp::legacy::ui_gl::query_capability(state, "Canvas"); } GLuint allocate_canvas_depth_renderbuffer(int width, int height) { return static_cast(pp::legacy::gl_renderbuffer::allocate_depth_renderbuffer( width, height, "OpenGL canvas depth renderbuffer allocation")); } void attach_canvas_depth_renderbuffer(GLuint renderbuffer) { pp::legacy::gl_renderbuffer::attach_depth_renderbuffer( static_cast(renderbuffer), "OpenGL canvas depth renderbuffer attachment"); } void delete_canvas_renderbuffer(GLuint renderbuffer) { pp::legacy::gl_renderbuffer::delete_renderbuffer( static_cast(renderbuffer), "OpenGL canvas renderbuffer delete"); } } Canvas* Canvas::I; std::vector Canvas::modes[] = { { new CanvasModePen, new CanvasModeBasicCamera }, // brush { new CanvasModePen, new CanvasModeBasicCamera }, // eraser { new CanvasModeLine, new CanvasModeBasicCamera }, // line { new CanvasModeCamera, new CanvasModeBasicCamera }, // parallax { new CanvasModeGrid, new CanvasModeBasicCamera }, // grids { new CanvasModeTransform, new CanvasModeBasicCamera }, // import { new CanvasModeTransform, new CanvasModeBasicCamera }, // cut { new CanvasModeTransform, new CanvasModeBasicCamera }, // copy { new CanvasModeFill, new CanvasModeBasicCamera }, // fill { new CanvasModeMaskFree, new CanvasModeBasicCamera }, // mask-free { new CanvasModeMaskLine, new CanvasModeBasicCamera }, // mask-poly { new CanvasModeFloodFill, new CanvasModeBasicCamera }, // flood-fill }; void Canvas::stroke_end() { pp::panopainter::legacy_canvas_stroke_end(*this); } void Canvas::stroke_cancel() { pp::panopainter::legacy_canvas_stroke_cancel(*this); } void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) { pp::panopainter::legacy_canvas_stroke_draw_mix(*this, bb_min, bb_sz); } std::array, 6> Canvas::stroke_draw_project(std::array& B, bool project_3d /*= false*/, glm::mat4 mv /*= glm::mat4(1)*/) const { return pp::panopainter::legacy_canvas_stroke_draw_project(*this, B, project_3d, mv); } void Canvas::draw_merge_temporary_paint_branch( int layer_index, int plane_index, std::shared_ptr layer, const Brush& brush, const glm::mat4& ortho) { pp::panopainter::legacy_canvas_draw_merge_temporary_paint_branch( *this, layer_index, plane_index, std::move(layer), brush, ortho); } void Canvas::draw_merge_branch_orchestration( int plane_index, int layer_index, const std::shared_ptr& layer, const Brush& brush, const glm::mat4& ortho, bool use_blend, bool copy_blend_destination) { pp::panopainter::legacy_canvas_draw_merge_branch_orchestration( *this, plane_index, layer_index, layer, brush, ortho, use_blend, copy_blend_destination); } void Canvas::draw_merge_final_plane_composite( const glm::mat4& ortho, bool draw_checkerboard) { pp::panopainter::legacy_canvas_draw_merge_final_plane_composite( *this, ortho, draw_checkerboard); } void Canvas::stroke_draw() { stroke_draw_live(); } bool Canvas::point_trace(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, glm::vec3& hit_pos, glm::vec2& fb_pos, glm::vec3& hit_normal, int& out_plane_id) { return pp::panopainter::legacy_canvas_point_trace(*this, loc, ray_origin, ray_dir, hit_pos, fb_pos, hit_normal, out_plane_id); } /* bool Canvas::point_trace_plane(glm::vec2 loc, glm::vec3& hit_pos, glm::vec2& hit_fb_pos, int plane_id) { auto ln = (loc / zw(m_box)) * 2.f - 1.f; auto p = m_plane_unproject[plane_id] * glm::vec4(ln, 1, 1); if (p.w <= 0) { return true; } return false; } */ bool Canvas::point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, glm::vec3& hit_pos, glm::vec3& hit_normal, glm::vec2& hit_fb_pos, int plane_id) { return pp::panopainter::legacy_canvas_point_trace_plane(*this, loc, ray_origin, ray_dir, hit_pos, hit_normal, hit_fb_pos, plane_id); } void Canvas::point_unproject(glm::vec2 loc, glm::vec4 vp, glm::mat4 camera, glm::mat4 proj, glm::vec3& out_origin, glm::vec3& out_dir) { pp::panopainter::legacy_canvas_point_unproject(loc, vp, camera, proj, out_origin, out_dir); } void Canvas::point_unproject(glm::vec2 loc, glm::vec3& out_origin, glm::vec3& out_dir) { pp::panopainter::legacy_canvas_point_unproject(*this, loc, out_origin, out_dir); } glm::vec3 Canvas::point_trace(glm::vec2 loc) { return pp::panopainter::legacy_canvas_point_trace(*this, loc); } void Canvas::stroke_commit_timelapse() { pp::panopainter::legacy_canvas_stroke_commit_timelapse(*this); } void Canvas::draw_merge(bool draw_checkerboard, std::array faces /*= SIXPLETTE(true)*/) { pp::panopainter::legacy_canvas_draw_merge(*this, draw_checkerboard, faces); } void Canvas::stroke_update(glm::vec3 point, float pressure) { pp::panopainter::legacy_canvas_stroke_update(*this, point, pressure); } void Canvas::stroke_start(glm::vec3 point, float pressure) { pp::panopainter::legacy_canvas_stroke_start(*this, point, pressure); } void Canvas::destroy() { pp::panopainter::legacy_canvas_destroy(*this); } bool Canvas::create(int width, int height) { return pp::panopainter::legacy_canvas_create(*this, width, height); } void Canvas::clear_context() { pp::panopainter::legacy_canvas_clear_context(*this); } void Canvas::draw_objects_direct(std::function observer, Layer& layer, int frame) { pp::panopainter::legacy_canvas_draw_objects_direct(*this, std::move(observer), layer, frame); } void Canvas::draw_objects(std::function observer, Layer& layer, int frame, bool save_history) { pp::panopainter::legacy_canvas_draw_objects(*this, std::move(observer), layer, frame, save_history); } void Canvas::draw_objects(std::function observer, int frame, bool save_history) { draw_objects(observer, layer(), frame, save_history); } void Canvas::project2Dpoints(std::vector& vertices) { pp::panopainter::legacy_canvas_project_2d_points(*this, vertices); } glm::vec3 Canvas::project2Dpoint(glm::vec2 pt) { return pp::panopainter::legacy_canvas_project_2d_point(*this, pt); }