diff --git a/data/layout.xml b/data/layout.xml index aceb00e..0be3147 100644 --- a/data/layout.xml +++ b/data/layout.xml @@ -1128,7 +1128,7 @@ Here's a list of what's available in this release. - + diff --git a/src/canvas.cpp b/src/canvas.cpp index b55441c..93ff608 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -1090,6 +1090,7 @@ bool Canvas::create(int width, int height) } m_sampler.create(GL_NEAREST); m_sampler.create(GL_LINEAR); + m_sampler_nearest.create(GL_NEAREST); m_sampler_brush.create(GL_LINEAR, GL_CLAMP_TO_BORDER); m_sampler_brush.set_filter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); m_sampler_brush.set_border({ 1, 1, 1, 1 }); @@ -1097,6 +1098,7 @@ bool Canvas::create(int width, int height) m_sampler_mask.create(GL_LINEAR); m_sampler_stencil.create(GL_LINEAR, GL_REPEAT); m_sampler_mix.create(GL_NEAREST, GL_REPEAT); + m_sampler_linear.create(); m_plane.create<1>(1, 1); m_plane_brush.create<1>(1, 1); m_brush_shape.create(); @@ -2212,22 +2214,58 @@ void Canvas::draw_objects(std::functionwas_saved = !m_unsaved; + glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f); for (int i = 0; i < 6; i++) { glm::mat4 plane_camera = glm::lookAt(glm::vec3(0), m_plane_origin[i], m_plane_tangent[i]); - layer.m_rtt[i].bindFramebuffer(); + m_tmp[i].bindFramebuffer(); + m_tmp[i].clear({ 1, 1, 1, 0 }); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboID); - observer(plane_camera, proj, i); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); + m_tmp[i].unbindFramebuffer(); + + glm::vec4 bounds = m_tmp[i].calc_bounds(); + glm::vec2 box_sz = zw(bounds) - xy(bounds); + if (box_sz.x <= 0 || box_sz.y <= 0) + continue; + + layer.m_rtt[i].bindFramebuffer(); + + // save image before commit + action->m_image[i] = std::make_unique(box_sz.x * box_sz.y * 4); + glReadPixels(bounds.x, bounds.y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, action->m_image[i].get()); + + action->m_box[i] = bounds; + action->m_old_box[i] = layer.m_dirty_box[i]; + action->m_old_dirty[i] = layer.m_dirty_face[i]; + + // draw the tmp layer into the actual layer + ShaderManager::use(kShader::Texture); + ShaderManager::u_int(kShaderUniform::Tex, 0); + ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-0.5f, 0.5f, -0.5f, 0.5f)); + glActiveTexture(GL_TEXTURE0); + m_sampler_nearest.bind(0); + m_tmp[i].bindTexture(); + m_plane.draw_fill(); + m_tmp[i].unbindTexture(); + layer.m_rtt[i].unbindFramebuffer(); layer.m_dirty_face[i] = true; - layer.m_dirty_box[i] = { 0, 0, layer.w, layer.h }; + layer.m_dirty_box[i] = rect_union(layer.m_dirty_box[i], bounds); } + // save history + action->m_layer_idx = m_current_layer_idx; + action->m_canvas = this; + action->m_stroke = std::move(m_current_stroke); + ActionManager::add(action); + glDeleteRenderbuffers(1, &rboID); // restore viewport and clear color states @@ -2515,6 +2553,49 @@ void Layer::destroy() m_rtt[i].destroy(); } +void Layer::optimize() +{ + int saved_bytes = 0; + glBindTexture(GL_TEXTURE_2D, 0); + for (int i = 0; i < 6; i++) + { + if (!m_dirty_face[i]) + continue; + + auto data = std::unique_ptr(reinterpret_cast(m_rtt[i].readTextureData())); + glm::ivec2 bbmin(w,h); + glm::ivec2 bbmax(0); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + if (data[x + y * w].a > 0) + { + bbmin = glm::min(bbmin, { x, y }); + bbmax = glm::max(bbmax, { x + 1, y + 1 }); + } + } + } + glm::vec2 bbsz = bbmax - bbmin; + glm::vec2 old_size = zw(m_dirty_box[i]) - xy(m_dirty_box[i]); + glm::vec2 diff; + if (bbsz.x <= 0 || bbmax.y <= 0) + { + m_dirty_face[i] = false; + m_dirty_box[i] = glm::vec4(0); + diff = old_size; + } + else + { + m_dirty_box[i] = { bbmin, bbmax }; + + diff = old_size - bbsz; + } + saved_bytes += (int)(diff.x * diff.y * 4); + } + LOG("optimized %d bytes", saved_bytes); +} + void Layer::restore(const Snapshot& snap) { //clear({ 0, 0, 0, 0 }); diff --git a/src/canvas.h b/src/canvas.h index 126279e..f0a46ac 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -48,6 +48,29 @@ public: std::fill_n(image[i].get(), width*height*4, 0); } } + void optimize() + { + for (int i = 0; i < 6; i++) + { + if (!m_dirty_face[i] || !image[i]) + continue; + auto data = reinterpret_cast(image[i].get()); + glm::ivec2 bbmin(width, height); + glm::ivec2 bbmax(0); + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + if (data[x + y * width].a > 0) + { + bbmin = glm::min(bbmin, { x, y }); + bbmax = glm::max(bbmax, { x + 1, y + 1 }); + } + } + } + glm::vec2 bbsz = bbmax - bbmin; + } + } }; void resize(int width, int height); bool create(int width, int height, std::string name); @@ -55,6 +78,7 @@ public: Snapshot snapshot(); void restore(const Snapshot& snap); void destroy(); + void optimize(); }; struct PPIThumb @@ -155,6 +179,7 @@ public: static glm::mat4 m_plane_transform[6]; glm::vec2 stencil_offset; Sampler m_sampler; + Sampler m_sampler_nearest; Sampler m_sampler_linear; Sampler m_sampler_brush; Sampler m_sampler_bg; diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index d993db6..6c47d2e 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -75,7 +75,7 @@ void NodeCanvas::draw() //m_canvas->m_cam_rot = m_pan * 0.003f; glm::mat4 ortho_proj = glm::ortho(0.f, box.z, 0.f, box.w, -1000.f, 1000.f); - glm::mat4 proj = glm::perspective(glm::radians(m_canvas->m_cam_fov), box.z / box.w, 0.01f, 1000.f); + glm::mat4 proj = glm::perspective(glm::radians(m_canvas->m_cam_fov), box.z / box.w, 0.001f, 1000.f); glm::mat4 camera = m_canvas->m_cam_rot * glm::translate(m_canvas->m_cam_pos); m_canvas->m_mv = camera; diff --git a/src/rtt.cpp b/src/rtt.cpp index 2f50a72..b5ce8bf 100644 --- a/src/rtt.cpp +++ b/src/rtt.cpp @@ -70,6 +70,19 @@ void RTT::destroy() // h = 0; } +void RTT::copy(const RTT & source) +{ + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDFboID); + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldRFboID); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboID); + glBindFramebuffer(GL_READ_FRAMEBUFFER, source.fboID); + glBlitFramebuffer(0, 0, source.w, source.h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDFboID); + glBindFramebuffer(GL_READ_FRAMEBUFFER, oldRFboID); +} + bool RTT::create(int width, int height, int tex/* = -1*/, GLint internal_format) { // Destroy any previously created object @@ -182,6 +195,25 @@ void RTT::clear(glm::vec4 color) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } +glm::ivec4 RTT::calc_bounds() +{ + auto data = std::unique_ptr(reinterpret_cast(readTextureData())); + glm::ivec2 bbmin(w, h); + glm::ivec2 bbmax(0); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + if (data[x + y * w].a > 0) + { + bbmin = glm::min(bbmin, { x, y }); + bbmax = glm::max(bbmax, { x + 1, y + 1 }); + } + } + } + return { bbmin, bbmax }; +} + uint8_t* RTT::readTextureData(uint8_t* buffer) { if (!buffer) diff --git a/src/rtt.h b/src/rtt.h index fbc4759..b5a5b82 100644 --- a/src/rtt.h +++ b/src/rtt.h @@ -17,10 +17,12 @@ public: ~RTT(); void destroy(); + void copy(const RTT& source); void resize(int width, int height); bool create(int width, int height, int tex = -1, GLint internal_format = GL_RGBA8); bool recreate() { return create(w, h); } void clear(glm::vec4 color = glm::vec4(0)); + glm::ivec4 calc_bounds(); uint8_t* readTextureData(uint8_t* buffer = nullptr); float* readTextureDataFloat(float* buffer = nullptr); uint8_t* createBuffer();