compress file when saving, using bbox, dirty flags and PNG compression
This commit is contained in:
@@ -307,21 +307,9 @@
|
|||||||
<!--layers menu-->
|
<!--layers menu-->
|
||||||
<layout id="layers-menu">
|
<layout id="layers-menu">
|
||||||
<popup-menu positioning="absolute" position="100 100" width="150" thickness="1" border-color=".1" color=".4 .4 .4 .8" dir="col">
|
<popup-menu positioning="absolute" position="100 100" width="150" thickness="1" border-color=".1" color=".4 .4 .4 .8" dir="col">
|
||||||
<button-custom text="Menu" height="30" align="center" color=".2" pad="0 0 0 10" dir="row">
|
<button-custom id="clear-grids" height="30" align="center" color=".2" pad="0 0 0 10" dir="row">
|
||||||
<icon icon="page_add" width="20"/>
|
|
||||||
<text text="New Panodoc" margin="0 0 0 5" font-face="arial" font-size="11"/>
|
|
||||||
</button-custom>
|
|
||||||
<button-custom text="Menu" height="30" align="center" color=".2" pad="0 0 0 10" dir="row">
|
|
||||||
<icon icon="disk" width="20"/>
|
|
||||||
<text text="Save" margin="0 0 0 5" font-face="arial" font-size="11"/>
|
|
||||||
</button-custom>
|
|
||||||
<button-custom text="Menu" height="30" align="center" color=".2" pad="0 0 0 10" dir="row">
|
|
||||||
<icon icon="disk_multiple" width="20"/>
|
|
||||||
<text text="Save as.." margin="0 0 0 5" font-face="arial" font-size="11"/>
|
|
||||||
</button-custom>
|
|
||||||
<button-custom text="Menu" height="30" align="center" color=".2" pad="0 0 0 10" dir="row">
|
|
||||||
<icon icon="add" width="20"/>
|
<icon icon="add" width="20"/>
|
||||||
<text text="Add reference" margin="0 0 0 5" font-face="arial" font-size="11"/>
|
<text text="Clear grids" margin="0 0 0 5" font-face="arial" font-size="11"/>
|
||||||
</button-custom>
|
</button-custom>
|
||||||
</popup-menu>
|
</popup-menu>
|
||||||
</layout>
|
</layout>
|
||||||
|
|||||||
@@ -580,7 +580,7 @@ void App::initLayout()
|
|||||||
button->on_click = [this,button](Node*) {
|
button->on_click = [this,button](Node*) {
|
||||||
if (canvas)
|
if (canvas)
|
||||||
{
|
{
|
||||||
canvas->m_canvas->save(data_path);
|
canvas->m_canvas->export_equirectangular(data_path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,6 +334,13 @@ void ui::Canvas::stroke_commit()
|
|||||||
glReadPixels(m_dirty_box[i].x, m_dirty_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, action->m_image[i].get());
|
glReadPixels(m_dirty_box[i].x, m_dirty_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, action->m_image[i].get());
|
||||||
|
|
||||||
action->m_box[i] = m_dirty_box[i];
|
action->m_box[i] = m_dirty_box[i];
|
||||||
|
action->m_old_box[i] = m_layers[m_current_layer_idx].m_dirty_box[i];
|
||||||
|
action->m_old_dirty[i] = m_layers[m_current_layer_idx].m_dirty_face[i];
|
||||||
|
|
||||||
|
auto& lbox = m_layers[m_current_layer_idx].m_dirty_box[i];
|
||||||
|
lbox.xy = glm::min(m_dirty_box[i].xy(), lbox.xy());
|
||||||
|
lbox.zw = glm::max(m_dirty_box[i].zw(), lbox.zw());
|
||||||
|
m_layers[m_current_layer_idx].m_dirty_face[i] = true;
|
||||||
|
|
||||||
// copy to tmp2 for layer blending
|
// copy to tmp2 for layer blending
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
@@ -495,7 +502,7 @@ void ui::Canvas::clear_context()
|
|||||||
m_latlong.destroy();
|
m_latlong.destroy();
|
||||||
};
|
};
|
||||||
|
|
||||||
void ui::Canvas::save(std::string data_path)
|
void ui::Canvas::export_equirectangular(std::string data_path)
|
||||||
{
|
{
|
||||||
// save viewport and clear color states
|
// save viewport and clear color states
|
||||||
GLint vp[4];
|
GLint vp[4];
|
||||||
@@ -632,7 +639,28 @@ void ui::Canvas::project_save(std::string data_path)
|
|||||||
auto snap = m_layers[i].snapshot(data_path);
|
auto snap = m_layers[i].snapshot(data_path);
|
||||||
for (int plane_index = 0; plane_index < 6; plane_index++)
|
for (int plane_index = 0; plane_index < 6; plane_index++)
|
||||||
{
|
{
|
||||||
fwrite(snap.image[plane_index].get(), 1, m_width * m_height * 4, fp);
|
int has_data = snap.m_dirty_face[plane_index] ? 1 : 0;
|
||||||
|
fwrite(&has_data, sizeof(int), 1, fp);
|
||||||
|
if (has_data)
|
||||||
|
{
|
||||||
|
glm::ivec4 b = snap.m_dirty_box[plane_index];
|
||||||
|
glm::vec2 sz = b.zw() - b.xy();
|
||||||
|
int box[4] = { b.x, b.y, b.z, b.w };
|
||||||
|
fwrite(&box, sizeof(box), 1, fp);
|
||||||
|
|
||||||
|
std::vector<uint8_t> compressed;
|
||||||
|
auto callback = [](void *context, void *data, int size)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t>* buffer = static_cast<std::vector<uint8_t>*>(context);
|
||||||
|
buffer->insert(buffer->end(), (uint8_t*)data, (uint8_t*)data + size);
|
||||||
|
};
|
||||||
|
int ret = stbi_write_png_to_func(callback, &compressed, sz.x, sz.y, 4, snap.image[plane_index].get(), sz.x * 4);
|
||||||
|
|
||||||
|
int data_size = compressed.size();
|
||||||
|
fwrite(&data_size, sizeof(int), 1, fp);
|
||||||
|
|
||||||
|
fwrite(compressed.data(), 1, compressed.size(), fp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@@ -665,7 +693,7 @@ void ui::Canvas::project_open(std::string data_path)
|
|||||||
|
|
||||||
const int bytes = m_width * m_height * 4;
|
const int bytes = m_width * m_height * 4;
|
||||||
Layer::Snapshot snap;
|
Layer::Snapshot snap;
|
||||||
snap.create(m_width, m_height);
|
snap.create(m_width, m_height); // allocate single data, no box should be bigger
|
||||||
|
|
||||||
m_layers.clear();
|
m_layers.clear();
|
||||||
m_order.clear();
|
m_order.clear();
|
||||||
@@ -681,7 +709,26 @@ void ui::Canvas::project_open(std::string data_path)
|
|||||||
fread((char*)name.data(), name_len, 1, fp);
|
fread((char*)name.data(), name_len, 1, fp);
|
||||||
for (int plane_index = 0; plane_index < 6; plane_index++)
|
for (int plane_index = 0; plane_index < 6; plane_index++)
|
||||||
{
|
{
|
||||||
fread(snap.image[plane_index].get(), 1, bytes, fp);
|
int has_data;
|
||||||
|
fread(&has_data, sizeof(int), 1, fp);
|
||||||
|
snap.m_dirty_face[plane_index] = has_data;
|
||||||
|
if (has_data)
|
||||||
|
{
|
||||||
|
int b[4];
|
||||||
|
fread(&b, sizeof(b), 1, fp);
|
||||||
|
snap.m_dirty_box[plane_index] = glm::vec4(b[0], b[1], b[2], b[3]);
|
||||||
|
glm::vec2 sz = snap.m_dirty_box[plane_index].zw() - snap.m_dirty_box[plane_index].xy();
|
||||||
|
|
||||||
|
int data_size;
|
||||||
|
fread(&data_size, sizeof(int), 1, fp);
|
||||||
|
std::vector<uint8_t> compressed(data_size);
|
||||||
|
|
||||||
|
fread(compressed.data(), 1, data_size, fp);
|
||||||
|
int imgw, imgh, imgc;
|
||||||
|
uint8_t* rgba = stbi_load_from_memory(compressed.data(), data_size, &imgw, &imgh, &imgc, 4);
|
||||||
|
std::copy(rgba, rgba + (imgw*imgh * 4), snap.image[plane_index].get());
|
||||||
|
delete rgba;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_layers.emplace_back();
|
m_layers.emplace_back();
|
||||||
m_layers.back().create(m_width, m_height, name.c_str());
|
m_layers.back().create(m_width, m_height, name.c_str());
|
||||||
@@ -814,15 +861,21 @@ void ui::Layer::destroy()
|
|||||||
|
|
||||||
void ui::Layer::restore(const Snapshot& snap)
|
void ui::Layer::restore(const Snapshot& snap)
|
||||||
{
|
{
|
||||||
|
//clear({ 0, 0, 0, 0 });
|
||||||
for (int i = 0; i < 6; i++)
|
for (int i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
if (!snap.image[i])
|
if (snap.image[i] == nullptr || snap.m_dirty_face[i] == false)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
m_dirty_box[i] = snap.m_dirty_box[i];
|
||||||
|
m_dirty_face[i] = snap.m_dirty_face[i];
|
||||||
|
|
||||||
m_rtt[i].recreate(); // TODO: this should not be recreated here! Sorry I messed up with this, just quick fix DON'T SHIP!!
|
m_rtt[i].recreate(); // TODO: this should not be recreated here! Sorry I messed up with this, just quick fix DON'T SHIP!!
|
||||||
m_rtt[i].bindTexture();
|
m_rtt[i].bindTexture();
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_rtt[i].getWidth(), m_rtt[i].getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, snap.image[i].get());
|
glm::vec2 box_sz = m_dirty_box[i].zw() - m_dirty_box[i].xy();
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, m_dirty_box[i].x, m_dirty_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, snap.image[i].get());
|
||||||
m_rtt[i].unbindTexture();
|
m_rtt[i].unbindTexture();
|
||||||
LOG("restore face %d - %d bytes (%dx%d)", i, m_rtt[i].bytes(), m_rtt[i].getWidth(), m_rtt[i].getHeight());
|
LOG("restore face %d - %d bytes (%dx%d)", i, (int)box_sz.x * (int)box_sz.y * 4, (int)box_sz.x, (int)box_sz.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -835,9 +888,22 @@ ui::Layer::Snapshot ui::Layer::snapshot(std::string data_path)
|
|||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
for (int i = 0; i < 6; i++)
|
for (int i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
|
snap.m_dirty_box[i] = m_dirty_box[i];
|
||||||
|
snap.m_dirty_face[i] = m_dirty_face[i];
|
||||||
|
|
||||||
|
if (!m_dirty_face[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
snap.image[i] = std::make_unique<uint8_t[]>(m_rtt[i].bytes());
|
snap.image[i] = std::make_unique<uint8_t[]>(m_rtt[i].bytes());
|
||||||
m_rtt[i].readTextureData(snap.image[i].get());
|
|
||||||
LOG("snapshot face %d - %d bytes (%dx%d)", i, m_rtt[i].bytes(), m_rtt[i].getWidth(), m_rtt[i].getHeight());
|
glReadBuffer(GL_BACK);
|
||||||
|
m_rtt[i].bindFramebuffer();
|
||||||
|
glm::vec2 box_sz = m_dirty_box[i].zw() - m_dirty_box[i].xy();
|
||||||
|
glReadPixels(m_dirty_box[i].x, m_dirty_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, snap.image[i].get());
|
||||||
|
m_rtt[i].unbindFramebuffer();
|
||||||
|
glReadBuffer(GL_NONE);
|
||||||
|
|
||||||
|
LOG("snapshot face %d - %d bytes (%dx%d)", i, (int)box_sz.x * (int)box_sz.y * 4, (int)box_sz.x, (int)box_sz.y);
|
||||||
static char name[128];
|
static char name[128];
|
||||||
sprintf(name, "%s/Layer%d-%d.png", data_path.c_str(), counter, i);
|
sprintf(name, "%s/Layer%d-%d.png", data_path.c_str(), counter, i);
|
||||||
//int ret = stbi_write_png(name, m_rtt[i].getWidth(), m_rtt[i].getHeight(), 4, snap.image[i].get(), m_rtt[i].stride());
|
//int ret = stbi_write_png(name, m_rtt[i].getWidth(), m_rtt[i].getHeight(), 4, snap.image[i].get(), m_rtt[i].stride());
|
||||||
@@ -858,6 +924,9 @@ void ui::Layer::clear(const glm::vec4& c)
|
|||||||
m_rtt[i].bindFramebuffer();
|
m_rtt[i].bindFramebuffer();
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
m_rtt[i].unbindFramebuffer();
|
m_rtt[i].unbindFramebuffer();
|
||||||
|
|
||||||
|
m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box
|
||||||
|
m_dirty_face[i] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore clear color state
|
// restore clear color state
|
||||||
@@ -867,12 +936,16 @@ void ui::Layer::clear(const glm::vec4& c)
|
|||||||
bool ui::Layer::create(int width, int height, std::string name)
|
bool ui::Layer::create(int width, int height, std::string name)
|
||||||
{
|
{
|
||||||
m_name = name;
|
m_name = name;
|
||||||
|
w = width;
|
||||||
|
h = height;
|
||||||
for (int i = 0; i < 6; i++)
|
for (int i = 0; i < 6; i++)
|
||||||
{
|
{
|
||||||
m_rtt[i].create(width, height);
|
m_rtt[i].create(width, height);
|
||||||
m_rtt[i].bindFramebuffer();
|
m_rtt[i].bindFramebuffer();
|
||||||
m_rtt[i].clear();
|
m_rtt[i].clear();
|
||||||
m_rtt[i].unbindFramebuffer();
|
m_rtt[i].unbindFramebuffer();
|
||||||
|
m_dirty_box[i] = glm::vec4(w, h, 0, 0); // reset bounding box
|
||||||
|
m_dirty_face[i] = false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,18 @@ class Layer
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RTT m_rtt[6];
|
RTT m_rtt[6];
|
||||||
|
glm::vec4 m_dirty_box[6];
|
||||||
|
bool m_dirty_face[6];
|
||||||
bool m_visible = true;
|
bool m_visible = true;
|
||||||
bool m_alpha_locked = false;
|
bool m_alpha_locked = false;
|
||||||
float m_opacity = 1.f;
|
float m_opacity = 1.f;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
int w, h;
|
||||||
struct Snapshot
|
struct Snapshot
|
||||||
{
|
{
|
||||||
std::unique_ptr<uint8_t[]> image[6];
|
std::unique_ptr<uint8_t[]> image[6];
|
||||||
|
glm::vec4 m_dirty_box[6];
|
||||||
|
bool m_dirty_face[6];
|
||||||
void create(int w, int h)
|
void create(int w, int h)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 6; i++)
|
for (int i = 0; i < 6; i++)
|
||||||
@@ -103,7 +108,7 @@ public:
|
|||||||
void snapshot_save(std::string data_path);
|
void snapshot_save(std::string data_path);
|
||||||
void snapshot_restore();
|
void snapshot_restore();
|
||||||
void clear_context();
|
void clear_context();
|
||||||
void save(std::string data_path);
|
void export_equirectangular(std::string data_path);
|
||||||
void project_save(std::string data_path);
|
void project_save(std::string data_path);
|
||||||
void project_open(std::string data_path);
|
void project_open(std::string data_path);
|
||||||
ui::Image thumbnail_generate(int w, int h);
|
ui::Image thumbnail_generate(int w, int h);
|
||||||
@@ -125,6 +130,8 @@ class ActionStroke : public Action
|
|||||||
public:
|
public:
|
||||||
std::unique_ptr<Stroke> m_stroke;
|
std::unique_ptr<Stroke> m_stroke;
|
||||||
std::unique_ptr<uint8_t[]> m_image[6];
|
std::unique_ptr<uint8_t[]> m_image[6];
|
||||||
|
glm::ivec4 m_old_box[6];
|
||||||
|
bool m_old_dirty[6];
|
||||||
glm::ivec4 m_box[6];
|
glm::ivec4 m_box[6];
|
||||||
bool m_dirty[6];
|
bool m_dirty[6];
|
||||||
int m_layer_idx;
|
int m_layer_idx;
|
||||||
@@ -141,6 +148,9 @@ public:
|
|||||||
if (!m_image[i])
|
if (!m_image[i])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
m_canvas->m_layers[m_layer_idx].m_dirty_box[i] = m_old_box[i];
|
||||||
|
m_canvas->m_layers[m_layer_idx].m_dirty_face[i] = m_old_dirty[i];
|
||||||
|
|
||||||
m_canvas->m_layers[m_layer_idx].m_rtt[i].bindTexture();
|
m_canvas->m_layers[m_layer_idx].m_rtt[i].bindTexture();
|
||||||
glm::vec2 box_sz = m_box[i].zw() - m_box[i].xy();
|
glm::vec2 box_sz = m_box[i].zw() - m_box[i].xy();
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, m_box[i].x, m_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, m_image[i].get());
|
glTexSubImage2D(GL_TEXTURE_2D, 0, m_box[i].x, m_box[i].y, box_sz.x, box_sz.y, GL_RGBA, GL_UNSIGNED_BYTE, m_image[i].get());
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ void CanvasModeCamera::on_MouseEvent(MouseEvent* me, glm::vec2& loc)
|
|||||||
case kEventType::MouseUpL:
|
case kEventType::MouseUpL:
|
||||||
m_dragging = false;
|
m_dragging = false;
|
||||||
node->mouse_release();
|
node->mouse_release();
|
||||||
canvas->m_cam_pos.xy = m_pos_start;
|
canvas->m_cam_pos = { 0, 0, 0 };
|
||||||
break;
|
break;
|
||||||
case kEventType::MouseMove:
|
case kEventType::MouseMove:
|
||||||
if (m_dragging)
|
if (m_dragging)
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
#define STB_TRUETYPE_IMPLEMENTATION
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
#include <stb/stb_truetype.h>
|
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <stb/stb_image.h>
|
|
||||||
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include <stb/stb_truetype.h>
|
||||||
|
#include <stb/stb_image.h>
|
||||||
#include <stb/stb_image_write.h>
|
#include <stb/stb_image_write.h>
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
#define _USE_MATH_DEFINES
|
#define _USE_MATH_DEFINES
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#define _SCL_SECURE_NO_WARNINGS
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <windowsx.h>
|
#include <windowsx.h>
|
||||||
#include <gl\glew.h>
|
#include <gl\glew.h>
|
||||||
|
|||||||
Reference in New Issue
Block a user