421 lines
13 KiB
C++
421 lines
13 KiB
C++
#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 <array>
|
|
#include <condition_variable>
|
|
#include <deque>
|
|
#include <mutex>
|
|
#include <stop_token>
|
|
#include <thread>
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <numeric>
|
|
|
|
#ifdef __APPLE__
|
|
#include <Foundation/Foundation.h>
|
|
#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<GLint>(pp::renderer::gl::rgba32f_internal_format());
|
|
if (renderer_features.float16_render_targets)
|
|
return static_cast<GLint>(pp::renderer::gl::rgba16f_internal_format());
|
|
return static_cast<GLint>(pp::renderer::gl::rgba8_internal_format());
|
|
}
|
|
|
|
GLint rgba8_internal_format()
|
|
{
|
|
return static_cast<GLint>(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::uint32_t>(std::max(width, 0)),
|
|
.height = static_cast<std::uint32_t>(std::max(height, 0)),
|
|
};
|
|
}
|
|
|
|
pp::paint_renderer::CanvasBlendGatePlan draw_merge_blend_gate_plan(
|
|
int width,
|
|
int height,
|
|
const std::vector<std::shared_ptr<Layer>>& layers,
|
|
const Brush* brush) noexcept
|
|
{
|
|
std::vector<int> 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::uint32_t>(std::max(width, 0)),
|
|
.height = static_cast<std::uint32_t>(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<GLenum>(pp::renderer::gl::depth_test_state());
|
|
}
|
|
|
|
GLenum scissor_test_state()
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::scissor_test_state());
|
|
}
|
|
|
|
GLenum blend_state()
|
|
{
|
|
return static_cast<GLenum>(pp::renderer::gl::blend_state());
|
|
}
|
|
|
|
GLint texture_filter_linear()
|
|
{
|
|
return static_cast<GLint>(pp::renderer::gl::linear_texture_filter());
|
|
}
|
|
|
|
GLint texture_filter_linear_mipmap_linear()
|
|
{
|
|
return static_cast<GLint>(pp::renderer::gl::linear_mipmap_linear_texture_filter());
|
|
}
|
|
|
|
GLint texture_filter_nearest()
|
|
{
|
|
return static_cast<GLint>(pp::renderer::gl::nearest_texture_filter());
|
|
}
|
|
|
|
GLint texture_wrap_repeat()
|
|
{
|
|
return static_cast<GLint>(pp::renderer::gl::repeat_texture_wrap());
|
|
}
|
|
|
|
GLint texture_wrap_clamp_to_border()
|
|
{
|
|
return static_cast<GLint>(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<std::uint32_t>(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<float, 4> query_canvas_clear_color()
|
|
{
|
|
return pp::legacy::ui_gl::query_clear_color("Canvas");
|
|
}
|
|
|
|
void apply_canvas_clear_color(std::array<float, 4> 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<GLuint>(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<std::uint32_t>(renderbuffer),
|
|
"OpenGL canvas depth renderbuffer attachment");
|
|
}
|
|
|
|
void delete_canvas_renderbuffer(GLuint renderbuffer)
|
|
{
|
|
pp::legacy::gl_renderbuffer::delete_renderbuffer(
|
|
static_cast<std::uint32_t>(renderbuffer),
|
|
"OpenGL canvas renderbuffer delete");
|
|
}
|
|
|
|
}
|
|
|
|
Canvas* Canvas::I;
|
|
std::vector<CanvasMode*> 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<std::vector<vertex_t>, 6> Canvas::stroke_draw_project(std::array<vertex_t, 4>& 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> 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>& 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<bool, 6> 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<void(const glm::mat4& camera, const glm::mat4& proj, int i)> observer, Layer& layer, int frame)
|
|
{
|
|
pp::panopainter::legacy_canvas_draw_objects_direct(*this, std::move(observer), layer, frame);
|
|
}
|
|
|
|
void Canvas::draw_objects(std::function<void(const glm::mat4& camera, const glm::mat4& proj, int i)> 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<void(const glm::mat4& camera, const glm::mat4& proj, int i)> observer, int frame, bool save_history)
|
|
{
|
|
draw_objects(observer, layer(), frame, save_history);
|
|
}
|
|
|
|
void Canvas::project2Dpoints(std::vector<vertex_t>& 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);
|
|
}
|
|
|