From 4f44de2a24cb574bfe318f42f9ce6af5eb2fe4b3 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 14 Aug 2016 01:17:30 +0200 Subject: [PATCH] Make the VU meters less ugly at lower sizes, by antialiasing the edges. --- lrameter.cpp | 10 ++++----- vu_common.cpp | 59 ++++++++++++++++++++++++++++++++++++--------------- vu_common.h | 2 +- vumeter.cpp | 2 +- 4 files changed, 49 insertions(+), 24 deletions(-) diff --git a/lrameter.cpp b/lrameter.cpp index 9ca7b01..f4395be 100644 --- a/lrameter.cpp +++ b/lrameter.cpp @@ -46,8 +46,8 @@ void LRAMeter::paintEvent(QPaintEvent *event) float level_lu = level_lufs + 23.0f; float range_low_lu = range_low_lufs + 23.0f; float range_high_lu = range_high_lufs + 23.0f; - int range_low_pos = lufs_to_pos(range_low_lu, height()); - int range_high_pos = lufs_to_pos(range_high_lu, height()); + int range_low_pos = lrint(lufs_to_pos(range_low_lu, height())); + int range_high_pos = lrint(lufs_to_pos(range_high_lu, height())); QRect top_off_rect(0, 0, width(), range_high_pos); QRect on_rect(0, range_high_pos, width(), range_low_pos - range_high_pos); @@ -59,8 +59,8 @@ void LRAMeter::paintEvent(QPaintEvent *event) // Draw the target area (+/-1 LU is allowed EBU range). // It turns green when we're within. - int min_y = lufs_to_pos(1.0f, height()); - int max_y = lufs_to_pos(-1.0f, height()); + int min_y = lrint(lufs_to_pos(1.0f, height())); + int max_y = lrint(lufs_to_pos(-1.0f, height())); // FIXME: This outlining isn't so pretty. { @@ -82,7 +82,7 @@ void LRAMeter::paintEvent(QPaintEvent *event) } // Draw the integrated loudness meter, in the same color as the target area. - int y = lufs_to_pos(level_lu, height()); + int y = lrint(lufs_to_pos(level_lu, height())); { QPen pen(Qt::black); pen.setWidth(5); diff --git a/vu_common.cpp b/vu_common.cpp index 5024969..f1a0619 100644 --- a/vu_common.cpp +++ b/vu_common.cpp @@ -9,7 +9,7 @@ using namespace std; -int lufs_to_pos(float level_lu, int height) +double lufs_to_pos(float level_lu, int height) { const float min_level = 9.0f; // y=0 is top of screen, so “min” is the loudest level. const float max_level = -18.0f; @@ -19,9 +19,17 @@ int lufs_to_pos(float level_lu, int height) return height - 1; } - int y = lrintf(height * (level_lu - min_level) / (max_level - min_level)); - y = max(y, 0); - y = min(y, height - 1); + double y = height * (level_lu - min_level) / (max_level - min_level); + y = max(y, 0); + y = min(y, height - 1); + + // If we are big enough, snap to pixel grid instead of antialiasing + // the edges; the unevenness will be less noticeable than the blurriness. + double height_per_level = height / (min_level - max_level) - 2.0; + if (height_per_level >= 10.0) { + y = round(y); + } + return y; } @@ -29,22 +37,39 @@ void draw_vu_meter(QPainter &painter, int width, int height, int margin, bool is { painter.fillRect(margin, 0, width - 2 * margin, height, Qt::black); - // TODO: QLinearGradient is not gamma-correct; we might want to correct for that. - QLinearGradient on(0, 0, 0, height); - on.setColorAt(0.0f, QColor(255, 0, 0)); - on.setColorAt(0.5f, QColor(255, 255, 0)); - on.setColorAt(1.0f, QColor(0, 255, 0)); - QColor off(80, 80, 80); - - // Draw bars colored up until the level, then gray from there. - for (int level = -18; level < 9; ++level) { - int min_y = lufs_to_pos(level + 1.0f, height) + 1; - int max_y = lufs_to_pos(level, height) - 1; + for (int y = 0; y < height; ++y) { + // Find coverage of “on” rectangles in this pixel row. + double coverage = 0.0; + for (int level = -18; level < 9; ++level) { + double min_y = lufs_to_pos(level + 1.0, height) + 1.0; + double max_y = lufs_to_pos(level, height) - 1.0; + min_y = std::max(min_y, y); + min_y = std::min(min_y, y + 1); + max_y = std::max(max_y, y); + max_y = std::min(max_y, y + 1); + coverage += max_y - min_y; + } + double on_r, on_g, on_b; if (is_on) { - painter.fillRect(margin, min_y, width - 2 * margin, max_y - min_y, on); + double t = double(y) / height; + if (t <= 0.5) { + on_r = 1.0; + on_g = 2.0 * t; + on_b = 0.0; + } else { + on_r = 1.0 - 2.0 * (t - 0.5); + on_g = 1.0; + on_b = 0.0; + } } else { - painter.fillRect(margin, min_y, width - 2 * margin, max_y - min_y, off); + on_r = on_g = on_b = 0.05; } + + // Correct for coverage and do a simple gamma correction. + int r = lrintf(255 * pow(on_r * coverage, 1.0 / 2.2)); + int g = lrintf(255 * pow(on_g * coverage, 1.0 / 2.2)); + int b = lrintf(255 * pow(on_b * coverage, 1.0 / 2.2)); + painter.fillRect(margin, y, width - 2 * margin, 1, QColor(r, g, b)); } } diff --git a/vu_common.h b/vu_common.h index 5df149e..118eed8 100644 --- a/vu_common.h +++ b/vu_common.h @@ -3,7 +3,7 @@ #include -int lufs_to_pos(float level_lu, int height); +double lufs_to_pos(float level_lu, int height); void draw_vu_meter(QPainter &painter, int width, int height, int margin, bool is_on); diff --git a/vumeter.cpp b/vumeter.cpp index 2e2d1c4..15240b4 100644 --- a/vumeter.cpp +++ b/vumeter.cpp @@ -32,7 +32,7 @@ void VUMeter::paintEvent(QPaintEvent *event) } float level_lu = level_lufs + 23.0f; - int on_pos = lufs_to_pos(level_lu, height()); + int on_pos = lrint(lufs_to_pos(level_lu, height())); QRect off_rect(0, 0, width(), on_pos); QRect on_rect(0, on_pos, width(), height() - on_pos); -- 2.39.2