add ABR brushes and presets with properties and patterns
This commit is contained in:
@@ -74,6 +74,7 @@ public class MainActivity extends NativeActivity {
|
|||||||
Log.v("PanoPainterJava", "create path failed: " + frames.getAbsolutePath());
|
Log.v("PanoPainterJava", "create path failed: " + frames.getAbsolutePath());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// brushes
|
||||||
File brushes = new File(pano_dir.getAbsolutePath(), "brushes");
|
File brushes = new File(pano_dir.getAbsolutePath(), "brushes");
|
||||||
if (!brushes.exists())
|
if (!brushes.exists())
|
||||||
{
|
{
|
||||||
@@ -92,6 +93,26 @@ public class MainActivity extends NativeActivity {
|
|||||||
Log.v("PanoPainterJava", "create path failed: " + brush_thumbs.getAbsolutePath());
|
Log.v("PanoPainterJava", "create path failed: " + brush_thumbs.getAbsolutePath());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// patterns
|
||||||
|
File patterns = new File(pano_dir.getAbsolutePath(), "brushes");
|
||||||
|
if (!patterns.exists())
|
||||||
|
{
|
||||||
|
if (patterns.mkdirs())
|
||||||
|
Log.v("PanoPainterJava", "create path " + patterns.getAbsolutePath());
|
||||||
|
else
|
||||||
|
Log.v("PanoPainterJava", "create path failed: " + patterns.getAbsolutePath());
|
||||||
|
|
||||||
|
}
|
||||||
|
File patterns_thumbs = new File(patterns.getAbsolutePath(), "thumbs");
|
||||||
|
if (!patterns_thumbs.exists())
|
||||||
|
{
|
||||||
|
if (patterns_thumbs.mkdirs())
|
||||||
|
Log.v("PanoPainterJava", "create path " + patterns_thumbs.getAbsolutePath());
|
||||||
|
else
|
||||||
|
Log.v("PanoPainterJava", "create path failed: " + patterns_thumbs.getAbsolutePath());
|
||||||
|
|
||||||
|
}
|
||||||
|
// settings
|
||||||
File settings = new File(pano_dir.getAbsolutePath(), "settings");
|
File settings = new File(pano_dir.getAbsolutePath(), "settings");
|
||||||
if (!settings.exists())
|
if (!settings.exists())
|
||||||
{
|
{
|
||||||
|
|||||||
149
src/abr.cpp
149
src/abr.cpp
@@ -8,11 +8,20 @@ bool ABR::section_desc()
|
|||||||
auto s = rwstring(); // maybe a string
|
auto s = rwstring(); // maybe a string
|
||||||
auto null = rkey_or_string();
|
auto null = rkey_or_string();
|
||||||
auto null_val = ri32(); // integer following the null
|
auto null_val = ri32(); // integer following the null
|
||||||
auto name = rkey_or_string();
|
|
||||||
auto list = rstring(4);
|
// presets list
|
||||||
auto list_val = call(list);
|
auto name = rkey_or_string(); // "Brsh"
|
||||||
auto out = list_val->str(0, "");
|
auto list = rstring(4); // "VlLs"
|
||||||
std::cout << out;
|
if (auto presets = std::dynamic_pointer_cast<List>(call(list)))
|
||||||
|
{
|
||||||
|
for (auto const& pr : presets->items)
|
||||||
|
{
|
||||||
|
if (auto desc = std::dynamic_pointer_cast<Descriptor>(pr))
|
||||||
|
m_presets.push_back(desc);
|
||||||
|
}
|
||||||
|
auto out = presets->str(0, "");
|
||||||
|
std::cout << out << '\n';
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,30 +34,16 @@ bool ABR::section_samp()
|
|||||||
{
|
{
|
||||||
auto samp_size = ru32();
|
auto samp_size = ru32();
|
||||||
auto uid = rpascal();
|
auto uid = rpascal();
|
||||||
printf("sample brush %s\n", uid.c_str());
|
//printf("sample brush %s\n", uid.c_str());
|
||||||
skip(4);
|
skip(4); // unknown bytes usually 00 01 00 00
|
||||||
auto image = parse_vmem();
|
auto vm = parse_vmem();
|
||||||
if (image->channels.size() >= 3)
|
if (vm)
|
||||||
{
|
{
|
||||||
std::vector<glm::u8vec3> out((image->channels[0].depth >> 3) * image->rect.area());
|
if (auto img = vm->image(true))
|
||||||
for (int ch = 0; ch < 3; ch++)
|
|
||||||
{
|
{
|
||||||
auto const& raw = image->channels[ch].data;
|
// TODO: check if uid already exists in map
|
||||||
for (int i = 0; i < raw.size(); i++)
|
m_samples[uid] = img;
|
||||||
out[i][ch] = raw[i];
|
|
||||||
}
|
}
|
||||||
stbi_write_png(fmt::format("x64/out/{}.png", uid).c_str(),
|
|
||||||
image->rect.width(), image->rect.height(), 3, out.data(), 0);
|
|
||||||
}
|
|
||||||
else if (image->channels.size() == 1)
|
|
||||||
{
|
|
||||||
stbi_write_png(fmt::format("x64/out/{}.png", uid).c_str(),
|
|
||||||
image->rect.width(), image->rect.height(), 1,
|
|
||||||
std::data(image->channels[0].data), 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("Error cannot read image of %d channels\n", image->channels.size());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -68,7 +63,7 @@ bool ABR::section_patt()
|
|||||||
auto image_mode = ru32();
|
auto image_mode = ru32();
|
||||||
if (!(image_mode == 1 || image_mode == 3))
|
if (!(image_mode == 1 || image_mode == 3))
|
||||||
{
|
{
|
||||||
printf("skip image mode %d\n", image_mode);
|
printf("PATT: skip image mode %d\n", image_mode);
|
||||||
skip(patt_length - 8);
|
skip(patt_length - 8);
|
||||||
snap();
|
snap();
|
||||||
continue;
|
continue;
|
||||||
@@ -82,48 +77,94 @@ bool ABR::section_patt()
|
|||||||
// read(...);
|
// read(...);
|
||||||
// no worries indexed is skipped anyway
|
// no worries indexed is skipped anyway
|
||||||
|
|
||||||
auto image = parse_vmem();
|
auto vm = parse_vmem();
|
||||||
if (image_mode == 3)
|
if (vm)
|
||||||
{
|
{
|
||||||
if (image->channels.size() >= 3)
|
int nc = std::min((int)vm->channels.size(), 3);
|
||||||
|
if (nc != image_mode)
|
||||||
{
|
{
|
||||||
std::vector<glm::u8vec3> out((image->channels[0].depth >> 3) * image->rect.area());
|
printf("PATT: image_mode (%d) and number of channels (%d) not matching\n",
|
||||||
for (int ch = 0; ch < 3; ch++)
|
image_mode, vm->channels.size());
|
||||||
{
|
|
||||||
auto const& raw = image->channels[ch].data;
|
|
||||||
for (int i = 0; i < raw.size(); i++)
|
|
||||||
out[i][ch] = raw[i];
|
|
||||||
}
|
}
|
||||||
stbi_write_png(fmt::format("x64/out/{}.png", uid).c_str(),
|
if (auto img = vm->image(true))
|
||||||
image->rect.width(), image->rect.height(), 3, out.data(), 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
printf("Error image mode is 3 but channels are only %d\n", image->channels.size());
|
// TODO: check if uid already exists in map
|
||||||
|
m_patterns[uid] = img;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (image_mode == 1)
|
|
||||||
{
|
|
||||||
stbi_write_png(fmt::format("x64/out/{}.png", uid).c_str(),
|
|
||||||
image->rect.width(), image->rect.height(), 1,
|
|
||||||
std::data(image->channels[0].data), 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<ABR::Image> ABR::parse_vmem()
|
std::vector<std::shared_ptr<Brush>> ABR::compute_brushes(const std::string& path)
|
||||||
|
{
|
||||||
|
std::vector<std::shared_ptr<Brush>> ret;
|
||||||
|
for (auto const& p : m_presets)
|
||||||
|
{
|
||||||
|
auto samp = p->get<Descriptor>("Brsh");
|
||||||
|
if (samp->class_id != "sampledBrush")
|
||||||
|
continue;
|
||||||
|
auto b = std::make_shared<Brush>();
|
||||||
|
b->m_name = wstr2str(p->value<String>("Nm "));
|
||||||
|
//b->m_tip_color = i.m_tip_color;
|
||||||
|
b->m_tip_size = samp->value<UnitFloat>("Dmtr") / (800.f * 4.f);
|
||||||
|
b->m_tip_spacing = samp->value<UnitFloat>("Spcn") * 0.01f;
|
||||||
|
b->m_tip_flow = .25f;
|
||||||
|
b->m_tip_opacity = 1.f;
|
||||||
|
b->m_tip_angle = glm::radians(samp->value<UnitFloat>("Angl"));
|
||||||
|
//b->m_tip_mix = i.m_tip_mix;
|
||||||
|
//b->m_tip_stencil = i.m_tip_stencil;
|
||||||
|
b->m_tip_wet = p->value<UnitFloat>("Wtdg");
|
||||||
|
b->m_tip_noise = (float)samp->value<UnitFloat>("Nose");
|
||||||
|
//b->m_tip_hue = i.m_tip_hue;
|
||||||
|
//b->m_tip_sat = i.m_tip_sat;
|
||||||
|
//b->m_tip_val = i.m_tip_val;
|
||||||
|
//b->m_tip_angle_follow = i.m_tip_angle_follow;
|
||||||
|
//b->m_tip_flow_pressure = i.m_tip_flow_pressure;
|
||||||
|
//b->m_tip_size_pressure = i.m_tip_size_pressure;
|
||||||
|
//b->m_tip_hue_pressure = i.m_tip_hue_pressure;
|
||||||
|
//b->m_tip_sat_pressure = i.m_tip_sat_pressure;
|
||||||
|
//b->m_tip_val_pressure = i.m_tip_val_pressure;
|
||||||
|
//b->m_jitter_scale = i.m_jitter_scale;
|
||||||
|
//b->m_jitter_angle = i.m_jitter_angle;
|
||||||
|
//b->m_jitter_spread = i.m_jitter_spread;
|
||||||
|
//b->m_jitter_flow = i.m_jitter_flow;
|
||||||
|
//b->m_jitter_hue = i.m_jitter_hue;
|
||||||
|
//b->m_jitter_sat = i.m_jitter_sat;
|
||||||
|
//b->m_jitter_val = i.m_jitter_val;
|
||||||
|
//b->m_blend_mode = i.m_blend_mode;
|
||||||
|
//b->m_name.resize(i.m_name_len);
|
||||||
|
//b->m_stencil_path.resize(i.m_stencil_path_len);
|
||||||
|
auto& tip_uid = wstr2str(samp->value<String>("sampledData"));
|
||||||
|
b->m_brush_path = path + "/brushes/" + tip_uid + ".png";
|
||||||
|
b->m_brush_thumb_path = path + "/brushes/thumbs/" + tip_uid + ".png";
|
||||||
|
if (auto patt = p->get<Descriptor>("Txtr"))
|
||||||
|
{
|
||||||
|
auto& patt_uid = wstr2str(patt->value<String>("Idnt"));
|
||||||
|
b->m_stencil_path = path + "/patterns/" + patt_uid + ".png";
|
||||||
|
//b->m_brush_thumb_path = path + "/patterns/thumbs/" + patt_uid + ".png";
|
||||||
|
b->m_tip_stencil = p->value<UnitFloat>("textureDepth") * 0.01f;
|
||||||
|
}
|
||||||
|
ret.push_back(b);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<ABR::VMArray> ABR::parse_vmem()
|
||||||
{
|
{
|
||||||
// Virtual Memory Array List
|
// Virtual Memory Array List
|
||||||
auto vmem_version = ru32(); // = 3
|
auto vmem_version = ru32(); // = 3
|
||||||
|
assert(vmem_version == 3);
|
||||||
auto vmem_length = ru32();
|
auto vmem_length = ru32();
|
||||||
|
// TODO: check if at the end there's good data
|
||||||
|
// check if the bounds are within the parent's size
|
||||||
auto vmem_rect = rrect();
|
auto vmem_rect = rrect();
|
||||||
auto vmem_channels = ru32();
|
auto vmem_channels = ru32();
|
||||||
// The following is a virtual memory array,
|
// The following is a virtual memory array,
|
||||||
// repeated for the number of channels
|
// repeated for the number of channels
|
||||||
// + one for a user mask + one for a sheet mask.
|
// + one for a user mask + one for a sheet mask.
|
||||||
vmem_channels += 2; // user and sheet mask
|
vmem_channels += 2; // user and sheet mask
|
||||||
auto ret = std::make_shared<Image>(vmem_version, vmem_rect);
|
auto ret = std::make_shared<VMArray>(vmem_version, vmem_rect);
|
||||||
for (int ch = 0; ch < vmem_channels; ch++)
|
for (int ch = 0; ch < vmem_channels; ch++)
|
||||||
{
|
{
|
||||||
auto array_written = ru32(); // skip if 0
|
auto array_written = ru32(); // skip if 0
|
||||||
@@ -200,14 +241,6 @@ std::shared_ptr<ABR::String> ABR::parse_text()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
//ABR::Type::Ref ABR::parse_prop()
|
|
||||||
//{
|
|
||||||
// auto name = rstring(4);
|
|
||||||
// //printf("prop type %s\n", t.c_str());
|
|
||||||
// if (!call(name))
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
std::shared_ptr<ABR::Descriptor> ABR::parse_objc()
|
std::shared_ptr<ABR::Descriptor> ABR::parse_objc()
|
||||||
{
|
{
|
||||||
auto ret = std::make_shared<Descriptor>();
|
auto ret = std::make_shared<Descriptor>();
|
||||||
@@ -297,7 +330,6 @@ ABR::ABR()
|
|||||||
|
|
||||||
bool ABR::open(const std::string& path)
|
bool ABR::open(const std::string& path)
|
||||||
{
|
{
|
||||||
m_parser_table[""] = std::bind(&ABR::parse_bool, this);
|
|
||||||
Asset asset;
|
Asset asset;
|
||||||
if (asset.open(path.c_str()))
|
if (asset.open(path.c_str()))
|
||||||
{
|
{
|
||||||
@@ -328,6 +360,7 @@ bool ABR::open(const std::string& path)
|
|||||||
skip(align4(ru32()));
|
skip(align4(ru32()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
132
src/abr.h
132
src/abr.h
@@ -3,6 +3,8 @@
|
|||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "image.h"
|
||||||
|
#include "brush.h"
|
||||||
|
|
||||||
class BinaryStream
|
class BinaryStream
|
||||||
{
|
{
|
||||||
@@ -66,7 +68,11 @@ public:
|
|||||||
auto ptr = advance<char>(len * 2);
|
auto ptr = advance<char>(len * 2);
|
||||||
for (int i = 0; i < len; i++)
|
for (int i = 0; i < len; i++)
|
||||||
std::swap(ptr[i * 2], ptr[i * 2 + 1]);
|
std::swap(ptr[i * 2], ptr[i * 2 + 1]);
|
||||||
return std::wstring((wchar_t*)ptr, len);
|
// right trim trailing zeroes
|
||||||
|
auto wptr = (wchar_t*)ptr;
|
||||||
|
for (int i = len - 1; i >= 0; i--)
|
||||||
|
if (wptr[i] == 0) len--;
|
||||||
|
return std::wstring(wptr, len);
|
||||||
}
|
}
|
||||||
std::string rpascal()
|
std::string rpascal()
|
||||||
{
|
{
|
||||||
@@ -194,7 +200,7 @@ private:
|
|||||||
ByteOrder m_byte_order = ByteOrder::Host;
|
ByteOrder m_byte_order = ByteOrder::Host;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ABR : public BinaryStream
|
class ABR : private BinaryStream
|
||||||
{
|
{
|
||||||
struct Type
|
struct Type
|
||||||
{
|
{
|
||||||
@@ -222,12 +228,14 @@ class ABR : public BinaryStream
|
|||||||
};
|
};
|
||||||
struct Double : public Type
|
struct Double : public Type
|
||||||
{
|
{
|
||||||
|
using native_type = double;
|
||||||
double value;
|
double value;
|
||||||
virtual std::string str(int indent, const std::string& prefix) const override
|
virtual std::string str(int indent, const std::string& prefix) const override
|
||||||
{ return std::string(indent, '-') + prefix + fmt::format("double: {}", value); }
|
{ return std::string(indent, '-') + prefix + fmt::format("double: {}", value); }
|
||||||
};
|
};
|
||||||
struct UnitFloat : public Type
|
struct UnitFloat : public Type
|
||||||
{
|
{
|
||||||
|
using native_type = double;
|
||||||
std::string unit;
|
std::string unit;
|
||||||
double value;
|
double value;
|
||||||
virtual std::string str(int indent, const std::string& prefix) const override
|
virtual std::string str(int indent, const std::string& prefix) const override
|
||||||
@@ -235,6 +243,7 @@ class ABR : public BinaryStream
|
|||||||
};
|
};
|
||||||
struct String : public Type
|
struct String : public Type
|
||||||
{
|
{
|
||||||
|
using native_type = std::wstring;
|
||||||
std::wstring value;
|
std::wstring value;
|
||||||
virtual std::string str(int indent, const std::string& prefix) const override
|
virtual std::string str(int indent, const std::string& prefix) const override
|
||||||
{ return std::string(indent, '-') + prefix + fmt::format("string: {}", wstr2str(value)); }
|
{ return std::string(indent, '-') + prefix + fmt::format("string: {}", wstr2str(value)); }
|
||||||
@@ -253,6 +262,7 @@ class ABR : public BinaryStream
|
|||||||
struct Name : public Type { };
|
struct Name : public Type { };
|
||||||
struct Integer : public Type
|
struct Integer : public Type
|
||||||
{
|
{
|
||||||
|
using native_type = int32_t;
|
||||||
int32_t value;
|
int32_t value;
|
||||||
virtual std::string str(int indent, const std::string& prefix) const override
|
virtual std::string str(int indent, const std::string& prefix) const override
|
||||||
{ return std::string(indent, '-') + prefix + fmt::format("int: {}", value); }
|
{ return std::string(indent, '-') + prefix + fmt::format("int: {}", value); }
|
||||||
@@ -260,6 +270,7 @@ class ABR : public BinaryStream
|
|||||||
struct LargeInteger : public Type { };
|
struct LargeInteger : public Type { };
|
||||||
struct Boolean : public Type
|
struct Boolean : public Type
|
||||||
{
|
{
|
||||||
|
using native_type = bool;
|
||||||
bool value;
|
bool value;
|
||||||
virtual std::string str(int indent, const std::string& prefix) const override
|
virtual std::string str(int indent, const std::string& prefix) const override
|
||||||
{ return std::string(indent, '-') + prefix + fmt::format("bool: {}", value); }
|
{ return std::string(indent, '-') + prefix + fmt::format("bool: {}", value); }
|
||||||
@@ -278,12 +289,37 @@ class ABR : public BinaryStream
|
|||||||
Type::Map props;
|
Type::Map props;
|
||||||
virtual std::string str(int indent, const std::string& prefix) const override
|
virtual std::string str(int indent, const std::string& prefix) const override
|
||||||
{
|
{
|
||||||
auto ret = std::string(indent, '-') +
|
auto ret = std::string(indent, '-') + prefix +
|
||||||
fmt::format("objc {} ({}): {} props:", wstr2str(name), class_id, props.size());
|
fmt::format("objc {} ({}): {} props:", wstr2str(name), class_id, props.size());
|
||||||
for (const auto& p : props)
|
for (const auto& p : props)
|
||||||
ret += "\n" + p.second->str(indent + 1, fmt::format("'{}' ", p.first));
|
ret += "\n" + p.second->str(indent + 1, fmt::format("'{}' ", p.first));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
bool has(const std::string& key) const
|
||||||
|
{
|
||||||
|
return props.find(key) != props.end();
|
||||||
|
}
|
||||||
|
template<typename T> std::shared_ptr<T> get(const std::string& key) const
|
||||||
|
{
|
||||||
|
return has(key) ? std::dynamic_pointer_cast<T>(props.at(key)) : nullptr;
|
||||||
|
}
|
||||||
|
template<typename T> auto value(const std::string& key) const
|
||||||
|
{
|
||||||
|
if (auto v = get<T>(key))
|
||||||
|
return v->value;
|
||||||
|
return decltype(T::value){};
|
||||||
|
}
|
||||||
|
template<typename T, typename D> auto value_or(const std::string& key, const D val) const
|
||||||
|
{
|
||||||
|
if (auto v = get<T>(key))
|
||||||
|
return v->value;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
template<typename T, typename D> void value(const std::string& key, D& dest) const
|
||||||
|
{
|
||||||
|
if (auto v = get<T>(key))
|
||||||
|
dest = static_cast<D>(v->value);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
struct Rectangle : public Type
|
struct Rectangle : public Type
|
||||||
{
|
{
|
||||||
@@ -314,20 +350,53 @@ class ABR : public BinaryStream
|
|||||||
Channel(uint32_t depth, const Rectangle& rect, uint8_t compression, std::vector<uint8_t>& data) :
|
Channel(uint32_t depth, const Rectangle& rect, uint8_t compression, std::vector<uint8_t>& data) :
|
||||||
depth(depth), rect(rect), compression(compression), data(std::move(data)) { }
|
depth(depth), rect(rect), compression(compression), data(std::move(data)) { }
|
||||||
};
|
};
|
||||||
struct Image : public Type
|
struct VMArray : public Type
|
||||||
{
|
{
|
||||||
uint32_t version; // = 3
|
uint32_t version; // = 3
|
||||||
Rectangle rect;
|
Rectangle rect;
|
||||||
std::vector<Channel> channels;
|
std::vector<Channel> channels;
|
||||||
Image() = default;
|
VMArray() = default;
|
||||||
Image(uint32_t version, const Rectangle& rect) :
|
VMArray(uint32_t version, const Rectangle& rect) : version(version), rect(rect) { }
|
||||||
version(version), rect(rect) { }
|
std::shared_ptr<Image> image(bool grayscale) const
|
||||||
|
{
|
||||||
|
int nc = channels.size();
|
||||||
|
auto pixels = (channels[0].depth >> 3) * rect.area();
|
||||||
|
if (nc == 1 || nc >= 3)
|
||||||
|
{
|
||||||
|
auto img = std::make_shared<Image>();
|
||||||
|
img->comp = 4;
|
||||||
|
img->width = rect.width();
|
||||||
|
img->height = rect.height();
|
||||||
|
img->m_data = std::make_unique<uint8_t[]>(pixels * 4);
|
||||||
|
auto out = reinterpret_cast<glm::u8vec4*>(img->m_data.get());
|
||||||
|
if (grayscale)
|
||||||
|
{
|
||||||
|
auto const& raw = channels[0].data;
|
||||||
|
for (int i = 0; i < raw.size(); i++)
|
||||||
|
out[i] = glm::u8vec4(glm::u8vec3(255 - raw[i]), 255);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::fill_n(out, pixels, glm::u8vec4(255));
|
||||||
|
for (int ch = 0; ch < std::min(nc, 3); ch++)
|
||||||
|
{
|
||||||
|
auto const& raw = channels[ch].data;
|
||||||
|
for (int i = 0; i < raw.size(); i++)
|
||||||
|
out[i][ch] = 255 - raw[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return img;
|
||||||
|
//stbi_write_png(fmt::format("x64/out/{}.png", uid).c_str(),
|
||||||
|
// image->rect.width(), image->rect.height(), 4, out.data(), 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Error image with %d channels\n", channels.size());
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
|
||||||
ABR();
|
|
||||||
bool open(const std::string& path);
|
|
||||||
private:
|
|
||||||
std::string rkey_or_string()
|
std::string rkey_or_string()
|
||||||
{
|
{
|
||||||
auto len = ru32();
|
auto len = ru32();
|
||||||
@@ -351,11 +420,22 @@ private:
|
|||||||
ret.y = ru16();
|
ret.y = ru16();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type::Ref call(std::string t)
|
||||||
|
{
|
||||||
|
if (m_parser_table.find(t) != m_parser_table.end())
|
||||||
|
{
|
||||||
|
auto& method = m_parser_table[t];
|
||||||
|
return method();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool section_desc();
|
bool section_desc();
|
||||||
bool section_samp();
|
bool section_samp();
|
||||||
bool section_patt();
|
bool section_patt();
|
||||||
// Parse Virtual Memory Array List
|
|
||||||
std::shared_ptr<Image> parse_vmem();
|
std::shared_ptr<VMArray> parse_vmem(); // Parse Virtual Memory Array List
|
||||||
std::shared_ptr<List> parse_vlls();
|
std::shared_ptr<List> parse_vlls();
|
||||||
std::shared_ptr<String> parse_text();
|
std::shared_ptr<String> parse_text();
|
||||||
std::shared_ptr<Descriptor> parse_objc();
|
std::shared_ptr<Descriptor> parse_objc();
|
||||||
@@ -365,20 +445,16 @@ private:
|
|||||||
std::shared_ptr<Double> parse_doub();
|
std::shared_ptr<Double> parse_doub();
|
||||||
std::shared_ptr<Enum> parse_enum();
|
std::shared_ptr<Enum> parse_enum();
|
||||||
std::shared_ptr<RawData> parse_tdta();
|
std::shared_ptr<RawData> parse_tdta();
|
||||||
Type::Ref call(std::string t)
|
|
||||||
{
|
std::map<std::string /*key*/, std::function<Type::Ref()>> m_parser_table;
|
||||||
if (m_parser_table.find(t) != m_parser_table.end())
|
std::vector<std::shared_ptr<Descriptor>> m_presets;
|
||||||
{
|
|
||||||
auto& method = m_parser_table[t];
|
public:
|
||||||
return method();
|
std::map<std::string /*uid*/, std::shared_ptr<Image>> m_patterns;
|
||||||
}
|
std::map<std::string /*uid*/, std::shared_ptr<Image>> m_samples;
|
||||||
//else if (m_parser_table.find(pick(4)) != m_parser_table.end())
|
|
||||||
//{
|
ABR();
|
||||||
// auto& method = m_parser_table[rstring(4)];
|
bool open(const std::string& path);
|
||||||
// return method();
|
std::vector<std::shared_ptr<Brush>> compute_brushes(const std::string& path);
|
||||||
//}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
std::map<std::string, std::function<Type::Ref()>> m_parser_table;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
34
src/app.cpp
34
src/app.cpp
@@ -134,15 +134,25 @@ void App::initLog()
|
|||||||
{
|
{
|
||||||
LOG("error creating rec path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
LOG("error creating rec path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
}
|
}
|
||||||
|
// brushes
|
||||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/brushes"] withIntermediateDirectories:YES attributes:nil error:&err])
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/brushes"] withIntermediateDirectories:YES attributes:nil error:&err])
|
||||||
{
|
{
|
||||||
LOG("error creating brushes path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
LOG("error creating brushes path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
}
|
}
|
||||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/brushes/thumbs"] withIntermediateDirectories:YES attributes:nil error:&err])
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/brushes/thumbs"] withIntermediateDirectories:YES attributes:nil error:&err])
|
||||||
{
|
{
|
||||||
LOG("error creating thumbs path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
LOG("error creating brushes thumbs path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
}
|
}
|
||||||
|
// patterns
|
||||||
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/patterns"] withIntermediateDirectories:YES attributes:nil error:&err])
|
||||||
|
{
|
||||||
|
LOG("error creating patterns path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
|
}
|
||||||
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/patterns /thumbs"] withIntermediateDirectories:YES attributes:nil error:&err])
|
||||||
|
{
|
||||||
|
LOG("error creating patterns thumbs path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
|
}
|
||||||
|
// settings
|
||||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/settings"] withIntermediateDirectories:YES attributes:nil error:&err])
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/settings"] withIntermediateDirectories:YES attributes:nil error:&err])
|
||||||
{
|
{
|
||||||
LOG("error creating settings path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
LOG("error creating settings path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
@@ -165,15 +175,25 @@ void App::initLog()
|
|||||||
{
|
{
|
||||||
LOG("error creating rec path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
LOG("error creating rec path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
}
|
}
|
||||||
|
// brushes
|
||||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/brushes"] withIntermediateDirectories:YES attributes:nil error:&err])
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/brushes"] withIntermediateDirectories:YES attributes:nil error:&err])
|
||||||
{
|
{
|
||||||
LOG("error creating brushes path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
LOG("error creating brushes path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
}
|
}
|
||||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/brushes/thumbs"] withIntermediateDirectories:YES attributes:nil error:&err])
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/brushes/thumbs"] withIntermediateDirectories:YES attributes:nil error:&err])
|
||||||
{
|
{
|
||||||
LOG("error creating thumbs path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
LOG("error creating brushes thumbs path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
}
|
}
|
||||||
|
// patterns
|
||||||
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/patterns"] withIntermediateDirectories:YES attributes:nil error:&err])
|
||||||
|
{
|
||||||
|
LOG("error creating brushes path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
|
}
|
||||||
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/patterns/thumbs"] withIntermediateDirectories:YES attributes:nil error:&err])
|
||||||
|
{
|
||||||
|
LOG("error creating patterns thumbs path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
|
}
|
||||||
|
// settings
|
||||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/settings"] withIntermediateDirectories:YES attributes:nil error:&err])
|
if (![[NSFileManager defaultManager] createDirectoryAtPath:[docpath stringByAppendingString:@"/settings"] withIntermediateDirectories:YES attributes:nil error:&err])
|
||||||
{
|
{
|
||||||
LOG("error creating settings path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
LOG("error creating settings path: %s", [[err localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||||
@@ -214,10 +234,14 @@ void App::initLog()
|
|||||||
|
|
||||||
if (!PathFileExistsA((data_path + "\\brushes").c_str()))
|
if (!PathFileExistsA((data_path + "\\brushes").c_str()))
|
||||||
CreateDirectoryA((data_path + "\\brushes").c_str(), NULL);
|
CreateDirectoryA((data_path + "\\brushes").c_str(), NULL);
|
||||||
|
|
||||||
if (!PathFileExistsA((data_path + "\\brushes\\thumbs").c_str()))
|
if (!PathFileExistsA((data_path + "\\brushes\\thumbs").c_str()))
|
||||||
CreateDirectoryA((data_path + "\\brushes\\thumbs").c_str(), NULL);
|
CreateDirectoryA((data_path + "\\brushes\\thumbs").c_str(), NULL);
|
||||||
|
|
||||||
|
if (!PathFileExistsA((data_path + "\\patterns").c_str()))
|
||||||
|
CreateDirectoryA((data_path + "\\patterns").c_str(), NULL);
|
||||||
|
if (!PathFileExistsA((data_path + "\\patterns\\thumbs").c_str()))
|
||||||
|
CreateDirectoryA((data_path + "\\patterns\\thumbs").c_str(), NULL);
|
||||||
|
|
||||||
if (!PathFileExistsA((data_path + "\\settings").c_str()))
|
if (!PathFileExistsA((data_path + "\\settings").c_str()))
|
||||||
CreateDirectoryA((data_path + "\\settings").c_str(), NULL);
|
CreateDirectoryA((data_path + "\\settings").c_str(), NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -100,20 +100,22 @@ void App::pick_file(std::vector<std::string> types, std::function<void (std::str
|
|||||||
#elif __ANDROID__
|
#elif __ANDROID__
|
||||||
//android_pick_file(and_app, callback);
|
//android_pick_file(and_app, callback);
|
||||||
#elif _WIN32
|
#elif _WIN32
|
||||||
std::string filter = "Image Files (";
|
std::string filter = "Supported Files (";
|
||||||
bool first_type = true;
|
bool first_type = true;
|
||||||
for (auto& t : types)
|
for (auto& t : types)
|
||||||
{
|
{
|
||||||
filter += std::string(first_type ? "" : " ,") + "*." + t;
|
filter.append(std::string(first_type ? "" : " ,") + "*." + t);
|
||||||
first_type = false;
|
first_type = false;
|
||||||
}
|
}
|
||||||
filter += ")\0";
|
filter.append(")");
|
||||||
|
filter.push_back(0);
|
||||||
first_type = true;
|
first_type = true;
|
||||||
for (auto& t : types)
|
for (auto& t : types)
|
||||||
{
|
{
|
||||||
filter += std::string(first_type ? "" : ";") + "*." + t;
|
filter.append(std::string(first_type ? "" : ";") + "*." + t);
|
||||||
first_type = false;
|
first_type = false;
|
||||||
}
|
}
|
||||||
|
filter.push_back(0);
|
||||||
std::string path = win32_open_file(filter.c_str());
|
std::string path = win32_open_file(filter.c_str());
|
||||||
if (!path.empty())
|
if (!path.empty())
|
||||||
callback(path);
|
callback(path);
|
||||||
|
|||||||
@@ -279,3 +279,24 @@ bool Brush::load_stencil(const std::string& path)
|
|||||||
m_stencil_path = path;
|
m_stencil_path = path;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Brush::load()
|
||||||
|
{
|
||||||
|
if (!m_brush_path.empty())
|
||||||
|
{
|
||||||
|
m_tip_texture = std::make_shared<Texture2D>();
|
||||||
|
if (!m_tip_texture->load(m_brush_path))
|
||||||
|
return false;
|
||||||
|
m_tip_texture->create_mipmaps();
|
||||||
|
m_tip_texture->auto_destroy = true;
|
||||||
|
}
|
||||||
|
if (!m_stencil_path.empty())
|
||||||
|
{
|
||||||
|
m_stencil_texture = std::make_shared<Texture2D>();
|
||||||
|
if (!m_stencil_texture->load(m_stencil_path))
|
||||||
|
return false;
|
||||||
|
m_stencil_texture->create_mipmaps();
|
||||||
|
m_stencil_texture->auto_destroy = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public:
|
|||||||
int m_blend_mode = 0;
|
int m_blend_mode = 0;
|
||||||
bool load_texture(const std::string& path, const std::string& thumb);
|
bool load_texture(const std::string& path, const std::string& thumb);
|
||||||
bool load_stencil(const std::string& path);
|
bool load_stencil(const std::string& path);
|
||||||
|
bool load();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StrokeSample
|
struct StrokeSample
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
#include "abr.h"
|
||||||
|
|
||||||
Node* NodeButtonBrush::clone_instantiate() const
|
Node* NodeButtonBrush::clone_instantiate() const
|
||||||
{
|
{
|
||||||
@@ -50,19 +51,63 @@ void NodePanelBrush::init()
|
|||||||
|
|
||||||
m_btn_add = find<NodeButtonCustom>("btn-add");
|
m_btn_add = find<NodeButtonCustom>("btn-add");
|
||||||
m_btn_add->on_click = [this](Node*) {
|
m_btn_add->on_click = [this](Node*) {
|
||||||
App::I.pick_image([this](std::string path) {
|
App::I.pick_file({ "JPG", "PNG", "ABR" }, [this](std::string path) {
|
||||||
Image img;
|
|
||||||
if (img.load_file(path))
|
|
||||||
{
|
|
||||||
std::string name, base, ext;
|
std::string name, base, ext;
|
||||||
std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)");
|
std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)");
|
||||||
std::smatch m;
|
std::smatch m;
|
||||||
if (std::regex_search(path, m, r))
|
if (!std::regex_search(path, m, r))
|
||||||
{
|
return;
|
||||||
base = m[1].str();
|
base = m[1].str();
|
||||||
name = m[2].str();
|
name = m[2].str();
|
||||||
ext = m[3].str();
|
ext = m[3].str();
|
||||||
|
Image img;
|
||||||
|
|
||||||
|
if (str_iequals(ext, "abr"))
|
||||||
|
{
|
||||||
|
ABR abr;
|
||||||
|
abr.open(path);
|
||||||
|
for (const auto& samp : abr.m_samples)
|
||||||
|
{
|
||||||
|
std::string path_high = App::I.data_path + "/brushes/" + samp.first + ".png";
|
||||||
|
std::string path_thumb = App::I.data_path + "/brushes/thumbs/" + samp.first + ".png";
|
||||||
|
samp.second->save(path_high);
|
||||||
|
auto thumb = samp.second->resize(64, 64);
|
||||||
|
thumb.save(path_thumb);
|
||||||
|
|
||||||
|
async_start();
|
||||||
|
NodeButtonBrush* brush = new NodeButtonBrush;
|
||||||
|
m_container->add_child(brush);
|
||||||
|
brush->init();
|
||||||
|
brush->create();
|
||||||
|
brush->loaded();
|
||||||
|
brush->set_icon(path_thumb.c_str());
|
||||||
|
brush->thumb_path = path_thumb;
|
||||||
|
brush->high_path = path_high;
|
||||||
|
brush->brush_name = name;
|
||||||
|
brush->m_user_brush = true;
|
||||||
|
brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1);
|
||||||
|
app_redraw();
|
||||||
|
async_end();
|
||||||
|
}
|
||||||
|
for (const auto& patt : abr.m_patterns)
|
||||||
|
{
|
||||||
|
std::string path_high = App::I.data_path + "/patterns/" + patt.first + ".png";
|
||||||
|
std::string path_thumb = App::I.data_path + "/patterns/thumbs/" + patt.first + ".png";
|
||||||
|
patt.second->save(path_high);
|
||||||
|
auto thumb = patt.second->resize(64, 64);
|
||||||
|
thumb.save(path_thumb);
|
||||||
|
}
|
||||||
|
auto brushes = abr.compute_brushes(App::I.data_path);
|
||||||
|
for (const auto& pr : brushes)
|
||||||
|
{
|
||||||
|
auto presets = App::I.stroke->m_presets_popup;
|
||||||
|
if (pr->load())
|
||||||
|
presets->add_brush(pr);
|
||||||
|
}
|
||||||
|
//save();
|
||||||
|
}
|
||||||
|
else if (img.load_file(path))
|
||||||
|
{
|
||||||
std::string path_high = App::I.data_path + "/brushes/" + name + ".png";
|
std::string path_high = App::I.data_path + "/brushes/" + name + ".png";
|
||||||
std::string path_thumb = App::I.data_path + "/brushes/thumbs/" + name + ".png";
|
std::string path_thumb = App::I.data_path + "/brushes/thumbs/" + name + ".png";
|
||||||
|
|
||||||
@@ -86,12 +131,10 @@ void NodePanelBrush::init()
|
|||||||
brush->brush_name = name;
|
brush->brush_name = name;
|
||||||
brush->m_user_brush = true;
|
brush->m_user_brush = true;
|
||||||
brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1);
|
brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1);
|
||||||
|
|
||||||
app_redraw();
|
app_redraw();
|
||||||
async_end();
|
async_end();
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -317,7 +360,7 @@ bool NodePanelBrush::restore()
|
|||||||
brush->thumb_path = path_thumb;
|
brush->thumb_path = path_thumb;
|
||||||
brush->high_path = path_high;
|
brush->high_path = path_high;
|
||||||
brush->brush_name = name;
|
brush->brush_name = name;
|
||||||
brush->m_user_brush = true;
|
brush->m_user_brush = i.m_user_brush;
|
||||||
brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1);
|
brush->on_click = std::bind(&NodePanelBrush::handle_click, this, std::placeholders::_1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -602,3 +645,21 @@ bool NodePanelBrushPreset::restore()
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NodePanelBrushPreset::add_brush(std::shared_ptr<Brush> brush)
|
||||||
|
{
|
||||||
|
NodeBrushPresetItem* b = new NodeBrushPresetItem;
|
||||||
|
m_container->add_child(b);
|
||||||
|
b->init();
|
||||||
|
b->create();
|
||||||
|
b->loaded();
|
||||||
|
b->thumb_path = brush->m_brush_thumb_path;
|
||||||
|
b->high_path = brush->m_brush_path;
|
||||||
|
b->m_brush = brush;
|
||||||
|
//brush->m_brush->m_tip_size = .05f;
|
||||||
|
b->m_preview->m_brush = brush;
|
||||||
|
b->m_preview->draw_stroke();
|
||||||
|
b->m_thumb->m_use_mipmaps = true;
|
||||||
|
b->m_thumb->set_image(brush->m_brush_thumb_path);
|
||||||
|
b->on_click = std::bind(&NodePanelBrushPreset::handle_click, this, std::placeholders::_1);
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ class NodePanelBrush : public Node
|
|||||||
int m_high_len = 0;
|
int m_high_len = 0;
|
||||||
int m_thumb_len = 0;
|
int m_thumb_len = 0;
|
||||||
bool m_deleted = false;
|
bool m_deleted = false;
|
||||||
|
bool m_user_brush = false;
|
||||||
};
|
};
|
||||||
public:
|
public:
|
||||||
std::function<void(Node* target, int index)> on_brush_changed;
|
std::function<void(Node* target, int index)> on_brush_changed;
|
||||||
@@ -131,5 +132,6 @@ public:
|
|||||||
void handle_click(Node* target);
|
void handle_click(Node* target);
|
||||||
bool save();
|
bool save();
|
||||||
bool restore();
|
bool restore();
|
||||||
|
void add_brush(std::shared_ptr<Brush> brush);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ void NodePanelStroke::init_controls()
|
|||||||
|
|
||||||
auto b = std::make_shared<Brush>();
|
auto b = std::make_shared<Brush>();
|
||||||
int br_idx = m_brush_popup->find_brush("Round-Hard");
|
int br_idx = m_brush_popup->find_brush("Round-Hard");
|
||||||
|
if (br_idx == -1)
|
||||||
|
br_idx = 0;
|
||||||
b->load_texture(m_brush_popup->get_texture_path(br_idx), m_brush_popup->get_thumb_path(br_idx));
|
b->load_texture(m_brush_popup->get_texture_path(br_idx), m_brush_popup->get_thumb_path(br_idx));
|
||||||
//b->load_stencil("data/paper.jpg");
|
//b->load_stencil("data/paper.jpg");
|
||||||
b->m_tip_size = .1f;
|
b->m_tip_size = .1f;
|
||||||
|
|||||||
11
src/util.cpp
11
src/util.cpp
@@ -313,6 +313,17 @@ std::string wstr2str(const std::wstring & wstr)
|
|||||||
return converted;
|
return converted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool str_iequals(const std::string& a, const std::string& b)
|
||||||
|
{
|
||||||
|
unsigned int sz = a.size();
|
||||||
|
if (b.size() != sz)
|
||||||
|
return false;
|
||||||
|
for (unsigned int i = 0; i < sz; ++i)
|
||||||
|
if (std::tolower(a[i]) != std::tolower(b[i]))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static const char* gl2str(GLenum err)
|
static const char* gl2str(GLenum err)
|
||||||
{
|
{
|
||||||
switch (err)
|
switch (err)
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ std::vector<std::string> split(const std::string& subject, char d, int max_split
|
|||||||
std::string unescape(const std::string& s);
|
std::string unescape(const std::string& s);
|
||||||
std::wstring str2wstr(const std::string& str);
|
std::wstring str2wstr(const std::string& str);
|
||||||
std::string wstr2str(const std::wstring& wstr);
|
std::string wstr2str(const std::wstring& wstr);
|
||||||
|
bool str_iequals(const std::string& a, const std::string& b);
|
||||||
|
|
||||||
size_t curl_data_handler(void *contents, size_t size, size_t nmemb, void *userp);
|
size_t curl_data_handler(void *contents, size_t size, size_t nmemb, void *userp);
|
||||||
size_t curl_data_write(void *ptr, size_t size, size_t nmemb, FILE *stream);
|
size_t curl_data_write(void *ptr, size_t size, size_t nmemb, FILE *stream);
|
||||||
|
|||||||
Reference in New Issue
Block a user