wip(refactor): Cairo drawing

This commit is contained in:
Michael Carlberg 2017-01-19 15:05:26 +01:00
parent 452afcdc68
commit a59e115622
7 changed files with 183 additions and 77 deletions

View File

@ -11,6 +11,7 @@
#include "cairo/utils.hpp" #include "cairo/utils.hpp"
#include "common.hpp" #include "common.hpp"
#include "components/logger.hpp" #include "components/logger.hpp"
#include "components/types.hpp"
#include "errors.hpp" #include "errors.hpp"
#include "utils/color.hpp" #include "utils/color.hpp"
#include "utils/string.hpp" #include "utils/string.hpp"
@ -63,6 +64,9 @@ namespace cairo {
} }
context& operator<<(const abspos& p) { context& operator<<(const abspos& p) {
if (p.clear) {
cairo_new_path(m_c);
}
cairo_move_to(m_c, p.x, p.y); cairo_move_to(m_c, p.x, p.y);
return *this; return *this;
} }
@ -117,8 +121,24 @@ namespace cairo {
return *this; return *this;
} }
context& operator<<(const rounded_corners& c) {
double radius = c.radius / 1.0;
double d = M_PI / 180.0;
cairo_new_sub_path(m_c);
cairo_arc(m_c, c.x + c.w - radius, c.y + radius, radius, -90 * d, 0 * d);
cairo_arc(m_c, c.x + c.w - radius, c.y + c.h - radius, radius, 0 * d, 90 * d);
cairo_arc(m_c, c.x + radius, c.y + c.h - radius, radius, 90 * d, 180 * d);
cairo_arc(m_c, c.x + radius, c.y + radius, radius, 180 * d, 270 * d);
cairo_close_path(m_c);
return *this;
}
context& operator<<(const textblock& t) { context& operator<<(const textblock& t) {
// Sort the fontlist so that the preferred font is tested first double x, y;
position(&x, &y);
// Sort the fontlist so that the
// preferred font gets prioritized
auto& fns = m_fonts; auto& fns = m_fonts;
std::sort(fns.begin(), fns.end(), [&](const unique_ptr<font>& a, const unique_ptr<font>&) { std::sort(fns.begin(), fns.end(), [&](const unique_ptr<font>& a, const unique_ptr<font>&) {
if (t.fontindex > 0 && std::distance(fns.begin(), std::find(fns.begin(), fns.end(), a)) == t.fontindex - 1) { if (t.fontindex > 0 && std::distance(fns.begin(), std::find(fns.begin(), fns.end(), a)) == t.fontindex - 1) {
@ -147,17 +167,55 @@ namespace cairo {
end++; end++;
} }
f->use(m_c); // encapsulate the vert. centering
save();
{
*this << abspos{x, y};
f->use();
cairo_text_extents_t extents; cairo_text_extents_t extents;
f->textwidth(utf8, &extents); f->textwidth(utf8, &extents);
double dx{0.0}, dy{0.0};
save(); // encapsulate the y pos update if (t.align == alignment::CENTER) {
*this << relpos{0.0, extents.height / 2.0 - f->extents().descent + f->offset()}; dx -= extents.x_advance / 2.0;
f->render(subset);
save();
{
cairo_rectangle(m_c, x, y, extents.x_advance / 2.0, extents.height);
auto pattern = cairo_pattern_create_rgba(1.0, 0.0, 0.0, 0.0);
cairo_set_source(m_c, pattern);
cairo_translate(m_c, dx, 0.0);
cairo_set_operator(m_c, CAIRO_OPERATOR_DEST);
cairo_fill(m_c);
cairo_pattern_destroy(pattern);
}
restore(); restore();
} else if (t.align == alignment::RIGHT) {
dx -= extents.x_advance;
*this << relpos{extents.width, 0.0}; save();
{
cairo_rectangle(m_c, x, y, extents.x_advance, extents.height);
auto pattern = cairo_pattern_create_rgba(0.0, 0.0, 0.0, 0.0);
cairo_set_source(m_c, pattern);
cairo_set_operator(m_c, CAIRO_OPERATOR_DEST);
cairo_translate(m_c, dx, 0.0);
cairo_fill(m_c);
cairo_pattern_destroy(pattern);
}
restore();
}
auto fontextents = f->extents();
f->render(subset, x + dx,
y + dy - (extents.height / 2.0 + extents.y_bearing + fontextents.descent) + f->offset());
position(&x, nullptr);
x += dx;
y += dy;
}
restore();
chars.erase(chars.begin(), end); chars.erase(chars.begin(), end);
break; break;
@ -179,6 +237,8 @@ namespace cairo {
chars.erase(chars.begin(), ++chars.begin()); chars.erase(chars.begin(), ++chars.begin());
} }
*this << abspos{x, y};
return *this; return *this;
} }
@ -197,11 +257,11 @@ namespace cairo {
} }
context& restore(bool restore_point = true) { context& restore(bool restore_point = true) {
cairo_restore(m_c);
if (restore_point) { if (restore_point) {
*this << abspos{m_points.front().first, m_points.front().first}; *this << abspos{m_points.front().first, m_points.front().first};
m_points.pop_front(); m_points.pop_front();
} }
cairo_restore(m_c);
return *this; return *this;
} }
@ -215,8 +275,12 @@ namespace cairo {
return *this; return *this;
} }
context& fill() { context& fill(bool preserve = false) {
if (preserve) {
cairo_fill_preserve(m_c);
} else {
cairo_fill(m_c); cairo_fill(m_c);
}
return *this; return *this;
} }
@ -231,10 +295,11 @@ namespace cairo {
return *this; return *this;
} }
context& position(double* x, double* y) { context& position(double* x, double* y = nullptr) {
*x = 0.0;
*y = 0.0;
if (cairo_has_current_point(m_c)) { if (cairo_has_current_point(m_c)) {
double x_, y_;
x = x != nullptr ? x : &x_;
y = y != nullptr ? y : &y_;
cairo_get_current_point(m_c, x, y); cairo_get_current_point(m_c, x, y);
} }
return *this; return *this;

View File

@ -63,25 +63,23 @@ namespace cairo {
class font { class font {
public: public:
explicit font(cairo_t* cairo, double offset) : m_cairo(cairo), m_offset(offset) {} explicit font(cairo_t* cairo, double offset) : m_cairo(cairo), m_offset(offset) {}
virtual ~font() {}; virtual ~font(){};
virtual string name() const = 0; virtual string name() const = 0;
virtual string file() const = 0; virtual string file() const = 0;
virtual double offset() const = 0; virtual double offset() const = 0;
virtual double size() const = 0; virtual double size() const = 0;
virtual void use(cairo_t* c) { virtual cairo_font_extents_t extents() = 0;
cairo_set_font_face(c, cairo_font_face_reference(m_font_face));
virtual void use() {
cairo_set_font_face(m_cairo, cairo_font_face_reference(m_font_face));
} }
virtual size_t match(unicode_charlist& charlist) = 0; virtual size_t match(unicode_charlist& charlist) = 0;
virtual size_t render(const string& text) = 0; virtual size_t render(const string& text, double x = 0.0, double y = 0.0, bool reverse = false) = 0;
virtual void textwidth(const string& text, cairo_text_extents_t* extents) = 0; virtual void textwidth(const string& text, cairo_text_extents_t* extents) = 0;
cairo_font_extents_t extents() const {
return m_extents;
}
protected: protected:
cairo_t* m_cairo; cairo_t* m_cairo;
cairo_font_face_t* m_font_face{nullptr}; cairo_font_face_t* m_font_face{nullptr};
@ -111,7 +109,7 @@ namespace cairo {
throw application_error(sstream() << "cairo_scaled_font_create(): " << cairo_status_to_string(status)); throw application_error(sstream() << "cairo_scaled_font_create(): " << cairo_status_to_string(status));
} }
auto lock = make_unique<details::ft_face_lock>(cairo_scaled_font_reference(m_scaled)); auto lock = make_unique<details::ft_face_lock>(m_scaled);
auto face = static_cast<FT_Face>(*lock); auto face = static_cast<FT_Face>(*lock);
if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) == FT_Err_Ok) { if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) == FT_Err_Ok) {
@ -121,6 +119,8 @@ namespace cairo {
} else if (FT_Select_Charmap(face, FT_ENCODING_SJIS) == FT_Err_Ok) { } else if (FT_Select_Charmap(face, FT_ENCODING_SJIS) == FT_Err_Ok) {
return; return;
} }
lock.reset();
} }
~font_fc() override { ~font_fc() override {
@ -132,6 +132,11 @@ namespace cairo {
} }
} }
cairo_font_extents_t extents() override {
cairo_scaled_font_extents(m_scaled, &m_extents);
return m_extents;
}
string name() const override { string name() const override {
return property("family"); return property("family");
} }
@ -157,12 +162,12 @@ namespace cairo {
return px; return px;
} }
void use(cairo_t* c) override { void use() override {
cairo_set_scaled_font(c, cairo_scaled_font_reference(m_scaled)); cairo_set_scaled_font(m_cairo, m_scaled);
} }
size_t match(unicode_charlist& charlist) override { size_t match(unicode_charlist& charlist) override {
auto lock = make_unique<details::ft_face_lock>(cairo_scaled_font_reference(m_scaled)); auto lock = make_unique<details::ft_face_lock>(m_scaled);
auto face = static_cast<FT_Face>(*lock); auto face = static_cast<FT_Face>(*lock);
size_t available_chars = 0; size_t available_chars = 0;
for (auto&& c : charlist) { for (auto&& c : charlist) {
@ -176,20 +181,22 @@ namespace cairo {
return available_chars; return available_chars;
} }
size_t render(const string& text) override { size_t render(const string& text, double x = 0.0, double y = 0.0, bool reverse = false) override {
double x, y;
cairo_get_current_point(m_cairo, &x, &y);
cairo_glyph_t* glyphs{nullptr}; cairo_glyph_t* glyphs{nullptr};
cairo_text_cluster_t* clusters{nullptr}; cairo_text_cluster_t* clusters{nullptr};
cairo_text_cluster_flags_t cf{}; cairo_text_cluster_flags_t cf{};
int nglyphs = 0, nclusters = 0; int nglyphs = 0, nclusters = 0;
if (reverse) {
cf = CAIRO_TEXT_CLUSTER_FLAG_BACKWARD;
}
string utf8 = string(text); string utf8 = string(text);
auto status = cairo_scaled_font_text_to_glyphs(cairo_scaled_font_reference(m_scaled), x, y, utf8.c_str(), auto status = cairo_scaled_font_text_to_glyphs(
utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf); m_scaled, x, y, utf8.c_str(), utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf);
if (status != CAIRO_STATUS_SUCCESS) { if (status != CAIRO_STATUS_SUCCESS) {
throw application_error(sstream() << "cairo_scaled_font_status()" << cairo_status_to_string(status)); throw application_error(sstream() << "cairo_scaled_font_text_to_glyphs()" << cairo_status_to_string(status));
} }
size_t bytes = 0; size_t bytes = 0;
@ -201,18 +208,36 @@ namespace cairo {
} }
} }
if (bytes) { if (bytes && bytes < text.size()) {
cairo_glyph_free(glyphs);
cairo_text_cluster_free(clusters);
utf8 = text.substr(0, bytes); utf8 = text.substr(0, bytes);
cairo_scaled_font_text_to_glyphs( auto status = cairo_scaled_font_text_to_glyphs(
m_scaled, x, y, utf8.c_str(), utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf); m_scaled, x, y, utf8.c_str(), utf8.size(), &glyphs, &nglyphs, &clusters, &nclusters, &cf);
cairo_show_text_glyphs(m_cairo, utf8.c_str(), utf8.size(), glyphs, nglyphs, clusters, nclusters, cf);
if (status != CAIRO_STATUS_SUCCESS) {
throw application_error(sstream() << "cairo_scaled_font_text_to_glyphs()" << cairo_status_to_string(status));
} }
}
if (bytes) {
cairo_text_extents_t extents{};
cairo_scaled_font_glyph_extents(m_scaled, glyphs, nglyphs, &extents);
cairo_show_text_glyphs(m_cairo, utf8.c_str(), utf8.size(), glyphs, nglyphs, clusters, nclusters, cf);
cairo_rel_move_to(m_cairo, extents.x_advance, 0.0);
// cairo_glyph_path(m_cairo, glyphs, nglyphs);
cairo_fill_preserve(m_cairo);
}
cairo_glyph_free(glyphs);
cairo_text_cluster_free(clusters);
return bytes; return bytes;
} }
void textwidth(const string& text, cairo_text_extents_t* extents) override { void textwidth(const string& text, cairo_text_extents_t* extents) override {
cairo_scaled_font_text_extents(cairo_scaled_font_reference(m_scaled), text.c_str(), extents); cairo_scaled_font_text_extents(m_scaled, text.c_str(), extents);
} }
protected: protected:

View File

@ -4,10 +4,13 @@
POLYBAR_NS POLYBAR_NS
enum class alignment;
namespace cairo { namespace cairo {
struct abspos { struct abspos {
double x; double x;
double y; double y;
bool clear{true};
}; };
struct relpos { struct relpos {
double x; double x;
@ -37,9 +40,18 @@ namespace cairo {
vector<unsigned int> steps; vector<unsigned int> steps;
}; };
struct rounded_corners {
double x;
double y;
double w;
double h;
double radius;
};
struct textblock { struct textblock {
alignment align;
string contents; string contents;
unsigned char fontindex; int fontindex;
}; };
} }

View File

@ -145,6 +145,7 @@ struct bar_settings {
std::unordered_map<edge, border_settings, enum_hash> borders{}; std::unordered_map<edge, border_settings, enum_hash> borders{};
double radius{0.0};
int spacing{0}; int spacing{0};
string separator{}; string separator{};

View File

@ -136,6 +136,7 @@ bar::bar(connection& conn, signal_emitter& emitter, const config& config, const
m_opts.spacing = m_conf.get(bs, "spacing", m_opts.spacing); m_opts.spacing = m_conf.get(bs, "spacing", m_opts.spacing);
m_opts.separator = m_conf.get(bs, "separator", ""s); m_opts.separator = m_conf.get(bs, "separator", ""s);
m_opts.locale = m_conf.get(bs, "locale", ""s); m_opts.locale = m_conf.get(bs, "locale", ""s);
m_opts.radius = m_conf.get(bs, "radius", m_opts.radius);
try { try {
auto padding = m_conf.get<decltype(m_opts.padding.left)>(bs, "module-padding"); auto padding = m_conf.get<decltype(m_opts.padding.left)>(bs, "module-padding");

View File

@ -256,8 +256,8 @@ void renderer::flush() {
/** /**
* Reserve space at given edge * Reserve space at given edge
*/ */
void renderer::reserve_space(edge side, unsigned short int w) { void renderer::reserve_space(edge side, unsigned int w) {
m_log.trace_x("renderer: reserve_space(%i, %i)", static_cast<unsigned char>(side), w); m_log.trace_x("renderer: reserve_space(%i, %i)", static_cast<int>(side), w);
m_cleararea.side = side; m_cleararea.side = side;
m_cleararea.size = w; m_cleararea.size = w;
@ -295,6 +295,16 @@ void renderer::fill_background() {
m_context->save(); m_context->save();
*m_context << m_compositing_background; *m_context << m_compositing_background;
if (m_bar.radius != 0.0) {
// clang-format off
*m_context << cairo::rounded_corners{
static_cast<double>(m_rect.x),
static_cast<double>(m_rect.y),
static_cast<double>(m_rect.width),
static_cast<double>(m_rect.height), m_bar.radius};
// clang-format on
}
if (!m_bar.background_steps.empty()) { if (!m_bar.background_steps.empty()) {
m_log.trace_x("renderer: gradient background (steps=%lu)", m_bar.background_steps.size()); m_log.trace_x("renderer: gradient background (steps=%lu)", m_bar.background_steps.size());
*m_context << cairo::linear_gradient{0.0, 0.0 + m_rect.y, 0.0, 0.0 + m_rect.height, m_bar.background_steps}; *m_context << cairo::linear_gradient{0.0, 0.0 + m_rect.y, 0.0, 0.0 + m_rect.height, m_bar.background_steps};
@ -303,7 +313,12 @@ void renderer::fill_background() {
*m_context << m_bar.background; *m_context << m_bar.background;
} }
if (m_bar.radius != 0.0) {
m_context->fill();
} else {
m_context->paint(); m_context->paint();
}
m_context->restore(); m_context->restore();
} }
@ -311,8 +326,8 @@ void renderer::fill_background() {
* Fill overline color * Fill overline color
*/ */
void renderer::fill_overline(double x, double w) { void renderer::fill_overline(double x, double w) {
if (m_bar.overline.size && m_attributes.test(static_cast<unsigned char>(attribute::OVERLINE))) { if (m_bar.overline.size && m_attributes.test(static_cast<int>(attribute::OVERLINE))) {
m_log.trace_x("renderer: overline(x=%i, w=%i)", x, w); m_log.trace_x("renderer: overline(x=%f, w=%f)", x, w);
m_context->save(); m_context->save();
*m_context << m_compositing_overline; *m_context << m_compositing_overline;
*m_context << m_color_overline; *m_context << m_color_overline;
@ -326,8 +341,8 @@ void renderer::fill_overline(double x, double w) {
* Fill underline color * Fill underline color
*/ */
void renderer::fill_underline(double x, double w) { void renderer::fill_underline(double x, double w) {
if (m_bar.underline.size && m_attributes.test(static_cast<unsigned char>(attribute::UNDERLINE))) { if (m_bar.underline.size && m_attributes.test(static_cast<int>(attribute::UNDERLINE))) {
m_log.trace_x("renderer: underline(x=%i, w=%i)", x, w); m_log.trace_x("renderer: underline(x=%f, w=%f)", x, w);
m_context->save(); m_context->save();
*m_context << m_compositing_underline; *m_context << m_compositing_underline;
*m_context << m_color_underline; *m_context << m_color_underline;
@ -382,21 +397,15 @@ void renderer::fill_borders() {
void renderer::draw_text(const string& contents) { void renderer::draw_text(const string& contents) {
m_log.trace_x("renderer: text(%s)", contents.c_str()); m_log.trace_x("renderer: text(%s)", contents.c_str());
cairo_text_extents_t extents;
cairo_text_extents(*m_context, contents.c_str(), &extents);
if (!extents.width) {
return;
}
cairo::abspos origin{static_cast<double>(m_rect.x), static_cast<double>(m_rect.y)}; cairo::abspos origin{static_cast<double>(m_rect.x), static_cast<double>(m_rect.y)};
origin.y += m_rect.height / 2.0;
if (m_alignment == alignment::CENTER) { if (m_alignment == alignment::CENTER) {
origin.x += m_rect.width / 2.0 - extents.width / 2.0; origin.x += m_rect.width / 2.0;
adjust_clickable_areas(extents.width / 2.0); // adjust_clickable_areas(extents.width / 2.0);
} else if (m_alignment == alignment::RIGHT) { } else if (m_alignment == alignment::RIGHT) {
origin.x += m_rect.width - extents.width; origin.x += m_rect.width;
adjust_clickable_areas(extents.width); // adjust_clickable_areas(extents.width);
} else { } else {
origin.x += m_x; origin.x += m_x;
} }
@ -410,24 +419,16 @@ void renderer::draw_text(const string& contents) {
// m_context->restore(); // m_context->restore();
// } // }
origin.y += m_rect.height / 2.0;
m_context->save(); m_context->save();
*m_context << origin; *m_context << origin;
*m_context << m_compositing_foreground; *m_context << m_compositing_foreground;
*m_context << m_color_foreground; *m_context << m_color_foreground;
*m_context << cairo::textblock{contents, m_fontindex}; *m_context << cairo::textblock{m_alignment, contents, m_fontindex};
m_context->position(&m_x, &m_y); m_context->position(&m_x, &m_y);
m_context->restore(); m_context->restore();
// if (m_alignment == alignment::CENTER) { fill_underline(origin.x, m_x - origin.x);
// m_x += extents.width / 2.0; fill_overline(origin.x, m_x - origin.x);
// } else {
// m_x += extents.width;
// }
// fill_underline(origin.x, m_x - origin.x);
// fill_overline(origin.x, m_x - origin.x);
} }
/** /**
@ -450,7 +451,7 @@ void renderer::highlight_clickable_areas() {
map<alignment, int> hint_num{}; map<alignment, int> hint_num{};
for (auto&& action : m_actions) { for (auto&& action : m_actions) {
if (!action.active) { if (!action.active) {
unsigned char n = hint_num.find(action.align)->second++; int n = hint_num.find(action.align)->second++;
double x = action.start_x + n * DEBUG_HINTS_OFFSET_X; double x = action.start_x + n * DEBUG_HINTS_OFFSET_X;
double y = m_bar.pos.y + m_rect.y + n * DEBUG_HINTS_OFFSET_Y; double y = m_bar.pos.y + m_rect.y + n * DEBUG_HINTS_OFFSET_Y;
double w = action.width(); double w = action.width();
@ -513,27 +514,28 @@ bool renderer::on(const signals::parser::change_alignment& evt) {
} }
bool renderer::on(const signals::parser::offset_pixel& evt) { bool renderer::on(const signals::parser::offset_pixel& evt) {
m_x += evt.cast(); (void)evt;
// m_x += evt.cast();
return true; return true;
} }
bool renderer::on(const signals::parser::attribute_set& evt) { bool renderer::on(const signals::parser::attribute_set& evt) {
m_attributes.set(static_cast<unsigned char>(evt.cast()), true); m_attributes.set(static_cast<int>(evt.cast()), true);
return true; return true;
} }
bool renderer::on(const signals::parser::attribute_unset& evt) { bool renderer::on(const signals::parser::attribute_unset& evt) {
m_attributes.set(static_cast<unsigned char>(evt.cast()), false); m_attributes.set(static_cast<int>(evt.cast()), false);
return true; return true;
} }
bool renderer::on(const signals::parser::attribute_toggle& evt) { bool renderer::on(const signals::parser::attribute_toggle& evt) {
m_attributes.flip(static_cast<unsigned char>(evt.cast())); m_attributes.flip(static_cast<int>(evt.cast()));
return true; return true;
} }
bool renderer::on(const signals::parser::action_begin& evt) { bool renderer::on(const signals::parser::action_begin& evt) {
(void) evt; (void)evt;
// auto a = evt.cast(); // auto a = evt.cast();
// action_block action{}; // action_block action{};
// action.button = a.button == mousebtn::NONE ? mousebtn::LEFT : a.button; // action.button = a.button == mousebtn::NONE ? mousebtn::LEFT : a.button;
@ -546,9 +548,9 @@ bool renderer::on(const signals::parser::action_begin& evt) {
} }
bool renderer::on(const signals::parser::action_end& evt) { bool renderer::on(const signals::parser::action_end& evt) {
(void) evt; (void)evt;
// auto btn = evt.cast(); // auto btn = evt.cast();
// short int clickable_width = 0; // int clickable_width = 0;
// for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) { // for (auto action = m_actions.rbegin(); action != m_actions.rend(); action++) {
// if (action->active && action->align == m_alignment && action->button == btn) { // if (action->active && action->align == m_alignment && action->button == btn) {
// switch (action->align) { // switch (action->align) {

View File

@ -101,7 +101,7 @@ void tray_client::reconfigure(int x, int y) const {
/** /**
* Respond to client resize requests * Respond to client resize requests
*/ */
void tray_client::configure_notify(short int x, short int y) const { void tray_client::configure_notify(int x, int y) const {
auto notify = memory_util::make_malloc_ptr<xcb_configure_notify_event_t, 32_z>(); auto notify = memory_util::make_malloc_ptr<xcb_configure_notify_event_t, 32_z>();
notify->response_type = XCB_CONFIGURE_NOTIFY; notify->response_type = XCB_CONFIGURE_NOTIFY;
notify->event = m_window; notify->event = m_window;