add export layers, export png, import brush padding

This commit is contained in:
2019-02-04 00:08:31 +01:00
parent a193666f4a
commit 24a6d0bae9
10 changed files with 266 additions and 65 deletions

View File

@@ -835,23 +835,23 @@ Here's a list of what's available in this release.
<!--popup menu-->
<layout id="popup-menu">
<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 height="30" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="camera" width="20"/>
<text text="Snapshot" 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">
<button-custom height="30" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="layers" width="20"/>
<text text="Layers" 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">
<button-custom height="30" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="page_white" width="20"/>
<text text="New Layer" 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">
<button-custom height="30" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="page_white_paste" width="20"/>
<text text="Paste" 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">
<button-custom height="30" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="page_white_stack" width="20"/>
<text text="Copy" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
@@ -875,7 +875,7 @@ Here's a list of what's available in this release.
</node>
</button-custom>
-->
<button-custom id="file-newdoc" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom id="file-newdoc" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="page_add" width="20"/>
<text text="New Pano" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
<text text="Ctrl-N" color=".4" justify="flex-end" margin="0 10 0 10" font-face="arial" font-size="11"/>
@@ -885,80 +885,97 @@ Here's a list of what's available in this release.
<text text="Import" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
<!--
<button-custom id="file-open" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom id="file-open" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="page_add" width="20"/>
<text text="Open" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
-->
<button-custom id="file-browse" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom id="file-browse" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="images" width="20"/>
<text text="Browse" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
<text text="Ctrl-Shift-O" color=".4" justify="flex-end" margin="0 10 0 10" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-open" os="osx,win" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom id="file-open" os="osx,win" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="image" width="20"/>
<text text="Open Path" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
<text text="Ctrl-O" color=".4" justify="flex-end" margin="0 10 0 10" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-save" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom id="file-save" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="disk" width="20"/>
<text text="Save" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
<text text="Ctrl-S" color=".4" justify="flex-end" margin="0 10 0 10" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-save-as" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom id="file-save-as" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="disk_multiple" width="20"/>
<text text="Save As.." grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-save-ver" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom id="file-save-ver" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="disk_multiple" width="20"/>
<text text="Save Version" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
<text text="Ctrl-Shift-S" color=".4" justify="flex-end" margin="0 10 0 10" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-export" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="picture_go" width="20"/>
<text text="Export JPG" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-share" os="osx,ios" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<node dir="row">
<button-custom id="file-export" height="40" align="center" color=".2" pad="0 0 0 10" dir="row" grow="1">
<icon icon="picture_go" width="20"/>
<text text="Export JPG" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-export-tick" height="40" width="40" align="center" justify="center" color=".2" dir="row">
<icon icon="resultset_next" width="20"/>
</button-custom>
</node>
<button-custom id="file-share" os="osx,ios" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="picture_go" width="20"/>
<text text="Share" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-resize" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom id="file-resize" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="arrow_out" width="20"/>
<text text="Resize Document" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
<!--
<button-custom id="file-export-cubes" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom id="file-export-cubes" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="page_white_stack" width="20"/>
<text text="Export Cubes" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
-->
<button-custom id="file-cloud-upload" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom id="file-cloud-upload" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="weather_clouds" width="20"/>
<text text="Cloud Publish" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-cloud-browse" text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom id="file-cloud-browse" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon icon="weather_clouds" width="20"/>
<text text="Cloud Browse" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
<!--<button-custom text="Menu" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<!--<button-custom height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<icon width="20"/>
<text text="Quit" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>-->
</popup-menu>
</layout>
<!--file submenu - export-->
<layout id="file-submenu-export">
<popup-menu positioning="absolute" position="100 100" width="150" thickness="1" border-color=".1" color=".4 .4 .4 .8" dir="col">
<button-custom id="file-submenu-export-png" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<text text="PNG" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
<button-custom id="file-submenu-export-layers" height="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<text text="Separate Layers" grow="1" margin="0 0 0 5" font-face="arial" font-size="11"/>
</button-custom>
</popup-menu>
</layout>
<!--edit menu-->
<layout id="edit-menu">
<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="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom height="40" 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="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom height="40" 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="40" align="center" color=".2" pad="0 0 0 10" dir="row">
<button-custom height="40" 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>

View File

@@ -57,6 +57,7 @@ public:
NodeMessageBox* msgbox;
NodeSettings* settings;
NodePopupMenu* popup = nullptr;
NodePopupMenu* subpopup = nullptr;
NodePopupMenu* menu_file = nullptr;
NodePopupMenu* menu_edit = nullptr;
NodePopupMenu* menu_layers = nullptr;
@@ -178,7 +179,8 @@ public:
void dialog_save_ver();
void dialog_open();
void dialog_browse();
void dialog_export();
void dialog_export(std::string ext);
void dialog_export_layers();
void dialog_export_cubes();
void dialog_layer_rename();
void dialog_resize();

View File

@@ -412,7 +412,7 @@ void App::dialog_save()
}
}
void App::dialog_export()
void App::dialog_export(std::string ext)
{
if (!check_license())
{
@@ -423,7 +423,30 @@ void App::dialog_export()
if (canvas)
{
// TODO: use picker
canvas->m_canvas->export_equirectangular(work_path + "/" + doc_name + ".jpg", [this]{
canvas->m_canvas->export_equirectangular(work_path + "/" + doc_name + ext, [this]{
#if defined(__IOS__)
message_box("Export JPG", "Image exported to Photos");
#elif defined(__OSX__)
message_box("Export JPG", "Image exported to Pictures/PanoPainter folder");
#elif defined(_WIN32)
message_box("Export JPG", "Image exported to " + work_path);
#endif
});
}
}
void App::dialog_export_layers()
{
if (!check_license())
{
message_box("License", "This function is disabled in demo mode.");
return;
}
if (canvas)
{
// TODO: use picker
canvas->m_canvas->export_layers(doc_name, [this] {
#if defined(__IOS__)
message_box("Export JPG", "Image exported to Photos");
#elif defined(__OSX__)

View File

@@ -21,7 +21,7 @@ void App::init_toolbar_main()
button->on_click = [this, button](Node*) {
if (canvas)
{
canvas->m_canvas->export_anim();
//canvas->m_canvas->export_anim();
}
};
}
@@ -463,10 +463,40 @@ void App::init_menu_file()
};
if (auto b = popup->find<NodeButtonCustom>("file-export"))
b->on_click = [this](Node*) {
dialog_export();
dialog_export(".jpg");
popup->mouse_release();
popup->destroy();
};
if (auto b = popup->find<NodeButtonCustom>("file-export-tick"))
b->on_click = [this,b](Node*) {
glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0);
subpopup = (NodePopupMenu*)layout[const_hash("file-submenu-export")]->m_children[0]->clone();
subpopup->update();
if (YGNodeStyleGetDirection(layout[main_id]->y_node) == YGDirectionRTL)
pos.x = pos.x - subpopup->m_size.x + b->m_size.x;
subpopup->SetPositioning(YGPositionTypeAbsolute);
subpopup->SetPosition(pos.x, pos.y);
layout[main_id]->add_child(subpopup);
layout[main_id]->update();
subpopup->mouse_capture();
subpopup->m_mouse_ignore = false;
subpopup->m_flood_events = true;
subpopup->m_capture_children = false;
subpopup->find<NodeButtonCustom>("file-submenu-export-png")->on_click = [this](Node*) {
dialog_export(".png");
subpopup->mouse_release();
subpopup->destroy();
popup->mouse_release();
popup->destroy();
};
subpopup->find<NodeButtonCustom>("file-submenu-export-layers")->on_click = [this](Node*) {
dialog_export_layers();
subpopup->mouse_release();
subpopup->destroy();
popup->mouse_release();
popup->destroy();
};
};
if (auto b = popup->find<NodeButtonCustom>("file-share"))
b->on_click = [this](Node*) {
share_file(doc_path);

View File

@@ -168,15 +168,15 @@ void App::initShaders()
"void main() {\n"
" mediump float stroke = 1.0 - texture(tex, uv).r;\n"
" int zero_count = 0;\n"
" if (textureOffset(tex, uv, ivec2(-1, -1)).r == 1.0) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2(-1, 0)).r == 1.0) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2(-1, +1)).r == 1.0) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2( 0, -1)).r == 1.0) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2( 0, 0)).r == 1.0) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2( 0, +1)).r == 1.0) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2(+1, -1)).r == 1.0) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2(+1, 0)).r == 1.0) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2(+1, +1)).r == 1.0) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2(-1, -1)).r > 0.99) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2(-1, 0)).r > 0.99) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2(-1, +1)).r > 0.99) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2( 0, -1)).r > 0.99) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2( 0, 0)).r > 0.99) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2( 0, +1)).r > 0.99) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2(+1, -1)).r > 0.99) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2(+1, 0)).r > 0.99) zero_count++;\n"
" if (textureOffset(tex, uv, ivec2(+1, +1)).r > 0.99) zero_count++;\n"
" mediump float edge = (zero_count > 1 && zero_count < 9) ? 0.75 : 0.0;\n"
" frag = vec4(col.rgb, edge * (1.0 - float(zero_count) / 9.f));\n"
"}\n";

View File

@@ -1334,7 +1334,7 @@ void Canvas::export_equirectangular_thread(std::string file_path)
face.bind();
// copy the framebuffer before clearing to white
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_width, m_height);
m_tmp[i].clear({ 1, 1, 1, 1 });
m_tmp[i].clear({ 1, 1, 1, 0 });
m_plane.draw_fill();
face.unbind();
@@ -1402,10 +1402,10 @@ void Canvas::export_equirectangular_thread(std::string file_path)
}
LOG("writing %s", file_path.c_str());
jpge::params params;
params.m_quality = 100;
bool saved = jpge::compress_image_to_jpeg_file(file_path.c_str(), m_latlong.getWidth(), m_latlong.getHeight(), 4, latlong_data.get(), params);
inject_xmp(file_path.c_str());
if (file_path.substr(file_path.size() - 4) == ".jpg")
stbi_write_jpg(file_path.c_str(), m_latlong.getWidth(), m_latlong.getHeight(), 4, latlong_data.get(), 100);
else if (file_path.substr(file_path.size() - 4) == ".png")
stbi_write_png(file_path.c_str(), m_latlong.getWidth(), m_latlong.getHeight(), 4, latlong_data.get(), 0);
{
progress++;
@@ -1503,10 +1503,21 @@ void Canvas::inject_xmp(std::string jpg_path)
}
void Canvas::export_anim()
void Canvas::export_layers(std::string file_name, std::function<void()> on_complete)
{
if (App::I.check_license())
{
std::thread t([=] {
export_layers_thread(file_name);
if (on_complete)
on_complete();
});
t.detach();
}
}
void Canvas::export_layers_thread(std::string file_name)
{
if (!App::I.check_license())
return;
// save viewport and clear color states
GLint vp[4];
GLfloat cc[4];
@@ -1514,6 +1525,24 @@ void Canvas::export_anim()
glGetFloatv(GL_COLOR_CLEAR_VALUE, cc);
GLboolean blend = glIsEnabled(GL_BLEND);
gl_state gl;
App::I.async_start();
std::shared_ptr<NodeProgressBar> pb;
if (App::I.layout.m_loaded)
{
pb = std::make_shared<NodeProgressBar>();
pb->m_manager = &App::I.layout;
pb->init();
pb->create();
pb->loaded();
pb->m_progress->SetWidthP(0);
pb->m_title->set_text("Export Pano Layers");
App::I.layout[App::I.main_id]->add_child(pb);
App::I.async_update();
}
int progress = 0;
int total = (m_order.size() + 1) * 6;
// prepare common states
glViewport(0, 0, m_width, m_height);
@@ -1540,20 +1569,21 @@ void Canvas::export_anim()
glViewport(0, 0, m_width, m_height);
for (int i = 0; i < 6; i++)
{
glEnable(GL_BLEND);
m_tmp[i].bindFramebuffer();
if (seq == 0)
{
m_tmp[i].clear({ 1, 1, 1, 1 });
ShaderManager::use(kShader::Checkerboard);
ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f));
m_plane.draw_fill();
glEnable(GL_BLEND);
}
else
//if (seq == 0)
//{
// m_tmp[i].clear({ 1, 1, 1, 1 });
// ShaderManager::use(kShader::Checkerboard);
// ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f));
// m_plane.draw_fill();
// glEnable(GL_BLEND);
//}
//else
{
m_tmp[i].clear({ 1, 1, 1, 0 });
glDisable(GL_BLEND);
//glDisable(GL_BLEND);
}
glActiveTexture(GL_TEXTURE0);
@@ -1575,6 +1605,18 @@ void Canvas::export_anim()
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
m_tmp[i].unbindFramebuffer();
progress++;
float p = (float)progress / total * 100.f;
LOG("progress: %f", p);
if (App::I.layout.m_loaded)
{
pb->m_progress->SetWidthP(p);
gl.save();
App::I.async_update();
gl.restore();
}
}
@@ -1596,19 +1638,37 @@ void Canvas::export_anim()
auto latlong_data = std::make_unique<uint8_t[]>(m_latlong.bytes());
m_latlong.readTextureData(latlong_data.get());
static char name[128];
sprintf(name, "%s/latlong-frame%02d.png", App::I.work_path.c_str(), seq);
sprintf(name, "%s/%s-layer-%02d.png", App::I.work_path.c_str(), file_name.c_str(), seq);
seq++;
LOG("writing %s", name);
App::I.async_end();
int ret = stbi_write_png(name, m_latlong.getWidth(), m_latlong.getHeight(), 4, latlong_data.get(), m_latlong.stride());
//jpge::params params;
//params.m_quality = 100;
//bool saved = jpge::compress_image_to_jpeg_file(name, m_latlong.getWidth(), m_latlong.getHeight(), 4, latlong_data.get(), params);
App::I.async_start();
}
progress++;
float p = (float)progress / total * 100.f;
LOG("progress: %f", p);
if (App::I.layout.m_loaded)
{
pb->m_progress->SetWidthP(p);
gl.save();
App::I.async_update();
gl.restore();
}
}
glDeleteTextures(1, &cube_id);
m_latlong.destroy();
if (App::I.layout.m_loaded)
{
pb->destroy();
App::I.async_update();
}
App::I.async_end();
// restore viewport and clear color states
blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND);
glViewport(vp[0], vp[1], vp[2], vp[3]);

View File

@@ -242,7 +242,8 @@ public:
void import_equirectangular_thread(std::string file_path);
void export_equirectangular(std::string file_path, std::function<void()> on_complete = nullptr);
void export_equirectangular_thread(std::string file_path);
void export_anim();
void export_layers(std::string file_name, std::function<void()> on_complete = nullptr);
void export_layers_thread(std::string file_name);
void export_cubes();
void project_save(std::function<void(bool)> on_complete = nullptr);
void project_save(std::string file_path, std::function<void(bool)> on_complete = nullptr);

View File

@@ -59,6 +59,18 @@ void Image::flip()
std::swap(m_data, flipped);
}
void Image::gayscale_alpha()
{
int np = width * height;
auto ptr = reinterpret_cast<glm::u8vec4*>(m_data.get());
for (int i = 0; i < np; i++)
{
auto& c = ptr[i];
c.g = c.b = c.r = 255 - c.a;
c.a = 255;
}
}
Image Image::resize(int w, int h) const
{
Image ret;
@@ -90,3 +102,50 @@ Image Image::resize(int w, int h) const
}
return ret;
}
Image Image::resize_power2() const
{
int w = pow(2, ceil(log(width) / log(2)));
int h = pow(2, ceil(log(height) / log(2)));
if (w == width && h == height)
{
Image i;
i.create(width, height);
std::copy(m_data.get(), m_data.get() + width * height * comp, i.m_data.get());
return i;
}
return resize(w, h);
}
Image Image::resize_squared() const
{
Image ret;
if (width == height)
{
ret.create(width, height);
std::copy(m_data.get(), m_data.get() + width * height * comp, ret.m_data.get());
}
else
{
int pad_x = 0;
int pad_y = 0;
int size = 0;
if (height > width)
{
size = height;
pad_x = (size - width) / 2;
}
else
{
size = width;
pad_y = (size - height) / 2;
}
ret.create(size, size);
auto ptr_src = reinterpret_cast<glm::u8vec4*>(m_data.get());
auto ptr_dst = reinterpret_cast<glm::u8vec4*>(ret.m_data.get());
std::fill_n(ptr_dst, size * size, glm::u8vec4(0));
for (int y = 0; y < height; y++)
std::copy_n(ptr_src + y * width, width, ptr_dst + pad_x + (y + pad_y) * ret.width);
}
return ret;
}

View File

@@ -31,6 +31,10 @@ public:
m_data.reset();
}
void flip();
// convert to grayscale and set opaque
void gayscale_alpha();
void create() { m_data = std::make_unique<uint8_t[]>(size()); }
Image resize(int w, int h) const;
Image resize_power2() const;
Image resize_squared() const;
};

View File

@@ -65,9 +65,14 @@ void NodePanelBrush::init()
std::string path_high = App::I.data_path + "/brushes/" + name + ".png";
std::string path_thumb = App::I.data_path + "/brushes/thumbs/" + name + ".png";
img = img.resize_squared();
img.gayscale_alpha();
auto thumb = img.resize(64, 64);
thumb.save(path_thumb);
img.save(path_high);
auto po2 = img.resize_power2();
po2.save(path_high);
async_start();
NodeButtonBrush* brush = new NodeButtonBrush;
@@ -75,7 +80,7 @@ void NodePanelBrush::init()
brush->init();
brush->create();
brush->loaded();
brush->set_icon(path.c_str());
brush->set_icon(path_thumb.c_str());
brush->thumb_path = path_thumb;
brush->high_path = path_high;
brush->brush_name = name;