Skip to content

Commit

Permalink
[gradient] Improve color accuracy when zoomed with large gradients by…
Browse files Browse the repository at this point in the history
… only drawing a subset
  • Loading branch information
jcelerier committed Aug 29, 2023
1 parent 507aeed commit bfcf66a
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ static ossia::hunter_lab to_ossia_color(QColor c)
static auto to_ossia_gradient(const Gradient::ProcessModel::gradient_colors& c)
{
gradient::grad_type g;

for(auto& e : c)
{
g.insert(std::make_pair(e.first, to_ossia_color(e.second)));
Expand Down
82 changes: 75 additions & 7 deletions src/plugins/score-plugin-automation/Color/GradientView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,93 @@ static const QPainterPath& triangle{[] {

void View::paint_impl(QPainter* p) const
{
const auto rect = boundingRect();
const auto W = rect.width();
switch(m_colors.size())
{
case 0:
p->fillRect(boundingRect(), Qt::black);
p->fillRect(rect, Qt::black);
return;
case 1:
p->fillRect(boundingRect(), m_colors.begin()->second);
p->fillRect(rect, m_colors.begin()->second);
return;
default:
break;
}
bool drawFull = false;

QLinearGradient g{QPointF{0, 0}, QPointF{m_dataWidth, 0.}};
for(const auto& col : m_colors)
if(auto v = ::getView(*this))
{
g.setColorAt(col.first, col.second);
const auto view_left = v->mapToScene(QPoint(0., 0.)).x();
const auto view_right = v->mapToScene(QPoint(v->width(), 0.)).x();

const auto item_left = this->mapToScene(QPointF(0., 0.)).x();
const auto item_right = this->mapToScene(QPointF(W, 0.)).x();

if(item_left >= view_left && item_right <= view_right)
{
drawFull = true;
}
else
{
// Find the index of the first gradient before view_left
double view_left_percent = (view_left - item_left) / W;
double view_right_percent = (view_right - item_left) / W;

double left_start = 0.;
double right_end = 1.;
auto left_it = this->m_colors.lower_bound(view_left_percent);
if(left_it != this->m_colors.begin())
{
--left_it;
left_start = left_it->first;
}

auto right_it = this->m_colors.upper_bound(view_right_percent);
if(right_it != this->m_colors.end())
right_end = right_it->first;

const double left_item_pos = left_start * W;
const double right_item_pos = right_end * W;

constexpr auto map = [](double input, double input_start, double input_end) {
constexpr auto output_start = 0.;
constexpr auto output_end = 1.;
return output_start
+ ((output_end - output_start) / (input_end - input_start))
* (input - input_start);
};

// Only fill between the gradient stops visible on screen
QLinearGradient g{QPointF{0, 0}, QPointF{1, 0.}};
g.setCoordinateMode(QGradient::CoordinateMode::ObjectMode);
for(const auto& [pos, col] : m_colors)
{
if(pos >= left_start && pos <= right_end)
{
const double mapped_pos = map(pos, left_start, right_end);
g.setColorAt(mapped_pos, col);
}
}

p->fillRect(
QRectF(left_item_pos, 0, right_item_pos - left_item_pos, rect.height()), g);
}
}
else
{
drawFull = true;
}

if(drawFull)
{
QLinearGradient g{QPointF{0, 0}, QPointF{m_dataWidth, 0.}};
for(const auto& col : m_colors)
{
g.setColorAt(col.first, col.second);
}
p->fillRect(rect, g);
}
p->fillRect(boundingRect(), g);

QPen pen(QColor::fromRgba(qRgba(200, 200, 200, 150)), 1);
QBrush br(Qt::gray);
Expand All @@ -96,7 +165,6 @@ void View::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
if(std::abs(e.first * m_dataWidth - pos.x()) < 2.)
{

if(event->button() == Qt::LeftButton)
{
if(pos.y() < (side / 1.5))
Expand Down

0 comments on commit bfcf66a

Please sign in to comment.