mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-20 21:56:30 +01:00
completely rethink waveform rendering (again)
There are 3 possible components to draw at each x-axis position: the waveform "line", the zero line and an outline/clip indicator. We have to decide which of the 3 to draw at each position, pixel by pixel. This makes the rendering less efficient but it is the only way I can see to do this correctly.
This commit is contained in:
parent
9dd5e2769b
commit
87cc9f7d4f
2 changed files with 132 additions and 94 deletions
|
|
@ -192,7 +192,7 @@ private:
|
||||||
|
|
||||||
void get_image (Cairo::RefPtr<Cairo::ImageSurface>& image, framepos_t start, framepos_t end, double& image_offset) const;
|
void get_image (Cairo::RefPtr<Cairo::ImageSurface>& image, framepos_t start, framepos_t end, double& image_offset) const;
|
||||||
|
|
||||||
ArdourCanvas::Coord y_extent (double) const;
|
ArdourCanvas::Coord y_extent (double, bool) const;
|
||||||
void draw_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int) const;
|
void draw_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -319,9 +319,39 @@ WaveView::consolidate_image_cache () const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Coord
|
||||||
|
WaveView::y_extent (double s, bool round_to_lower_edge) const
|
||||||
|
{
|
||||||
|
/* it is important that this returns an integral value, so that we
|
||||||
|
can ensure correct single pixel behaviour.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Coord pos;
|
||||||
|
|
||||||
|
switch (_shape) {
|
||||||
|
case Rectified:
|
||||||
|
if (round_to_lower_edge) {
|
||||||
|
pos = ceil (_height - (s * _height));
|
||||||
|
} else {
|
||||||
|
pos = floor (_height - (s * _height));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (round_to_lower_edge) {
|
||||||
|
pos = ceil ((1.0-s) * (_height/2.0));
|
||||||
|
} else {
|
||||||
|
pos = floor ((1.0-s) * (_height/2.0));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return min (_height, (max (0.0, pos)));
|
||||||
|
}
|
||||||
|
|
||||||
struct LineTips {
|
struct LineTips {
|
||||||
double top;
|
double top;
|
||||||
double bot;
|
double bot;
|
||||||
|
double spread;
|
||||||
bool clip_max;
|
bool clip_max;
|
||||||
bool clip_min;
|
bool clip_min;
|
||||||
|
|
||||||
|
|
@ -355,8 +385,11 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
|
||||||
|
|
||||||
if (_logscaled) {
|
if (_logscaled) {
|
||||||
for (int i = 0; i < n_peaks; ++i) {
|
for (int i = 0; i < n_peaks; ++i) {
|
||||||
|
|
||||||
tips[i].bot = height();
|
tips[i].bot = height();
|
||||||
tips[i].top = y_extent (alt_log_meter (fast_coefficient_to_dB (max (fabs (_peaks[i].max), fabs (_peaks[i].min)))));
|
const double p = alt_log_meter (fast_coefficient_to_dB (max (fabs (_peaks[i].max), fabs (_peaks[i].min))));
|
||||||
|
tips[i].top = y_extent (p, false);
|
||||||
|
tips[i].spread = (1.0 - p) * _height;
|
||||||
|
|
||||||
if (fabs (_peaks[i].max) >= clip_level) {
|
if (fabs (_peaks[i].max) >= clip_level) {
|
||||||
tips[i].clip_max = true;
|
tips[i].clip_max = true;
|
||||||
|
|
@ -366,9 +399,12 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
|
||||||
tips[i].clip_min = true;
|
tips[i].clip_min = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {for (int i = 0; i < n_peaks; ++i) {
|
} else {for (int i = 0; i < n_peaks; ++i) {
|
||||||
|
|
||||||
tips[i].bot = height();
|
tips[i].bot = height();
|
||||||
tips[i].top = y_extent (max (fabs (_peaks[i].max), fabs (_peaks[i].min)));
|
tips[i].top = y_extent (max (fabs (_peaks[i].max), fabs (_peaks[i].min)), true);
|
||||||
|
tips[i].spread = (1.0 - fabs(_peaks[i].max - _peaks[i].min)) * _height;
|
||||||
|
|
||||||
if (fabs (_peaks[i].max) >= clip_level) {
|
if (fabs (_peaks[i].max) >= clip_level) {
|
||||||
tips[i].clip_max = true;
|
tips[i].clip_max = true;
|
||||||
|
|
@ -384,35 +420,36 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
|
||||||
|
|
||||||
if (_logscaled) {
|
if (_logscaled) {
|
||||||
for (int i = 0; i < n_peaks; ++i) {
|
for (int i = 0; i < n_peaks; ++i) {
|
||||||
Coord top = _peaks[i].min;
|
double top = _peaks[i].min;
|
||||||
Coord bot = _peaks[i].max;
|
double bot = _peaks[i].max;
|
||||||
|
|
||||||
if (fabs (top) >= clip_level) {
|
if (fabs (top) >= clip_level) {
|
||||||
tips[i].clip_max = true;
|
tips[i].clip_max = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fabs (bot) >= clip_level) {
|
if (fabs (top) >= clip_level) {
|
||||||
tips[i].clip_min = true;
|
tips[i].clip_min = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (top > 0.0) {
|
if (top > 0.0) {
|
||||||
top = y_extent (alt_log_meter (fast_coefficient_to_dB (top)));
|
top = alt_log_meter (fast_coefficient_to_dB (top));
|
||||||
} else if (top < 0.0) {
|
} else if (top < 0.0) {
|
||||||
top = y_extent (-alt_log_meter (fast_coefficient_to_dB (-top)));
|
top =-alt_log_meter (fast_coefficient_to_dB (-top));
|
||||||
} else {
|
} else {
|
||||||
top = y_extent (0.0);
|
top = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bot > 0.0) {
|
if (bot > 0.0) {
|
||||||
bot = y_extent (alt_log_meter (fast_coefficient_to_dB (bot)));
|
bot = alt_log_meter (fast_coefficient_to_dB (bot));
|
||||||
} else if (bot < 0.0) {
|
} else if (bot < 0.0) {
|
||||||
bot = y_extent (-alt_log_meter (fast_coefficient_to_dB (-bot)));
|
bot = -alt_log_meter (fast_coefficient_to_dB (-bot));
|
||||||
} else {
|
} else {
|
||||||
bot = y_extent (0.0);
|
bot = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tips[i].top = top;
|
tips[i].spread = fabs (((1.0 - top) * (_height/2.0)) - ((1.0 - bot) * _height/2.0));
|
||||||
tips[i].bot = bot;
|
tips[i].top = y_extent (top, false);
|
||||||
|
tips[i].bot = y_extent (bot, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -426,11 +463,11 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
|
||||||
tips[i].clip_min = true;
|
tips[i].clip_min = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
tips[i].top = y_extent (_peaks[i].min);
|
tips[i].top = y_extent (_peaks[i].min, false);
|
||||||
tips[i].bot = y_extent (_peaks[i].max);
|
tips[i].bot = y_extent (_peaks[i].max, true);
|
||||||
|
tips[i].spread = fabs (((1.0 - _peaks[i].max) * (_height/2.0)) - ((1.0 - _peaks[i].min) * (_height/2.0)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -477,72 +514,94 @@ WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peak
|
||||||
|
|
||||||
/* draw the lines */
|
/* draw the lines */
|
||||||
|
|
||||||
if (_shape == WaveView::Rectified) {
|
|
||||||
for (int i = 0; i < n_peaks; ++i) {
|
|
||||||
context->move_to (i, tips[i].top); /* down 1 pixel */
|
|
||||||
context->line_to (i, tips[i].bot);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < n_peaks; ++i) {
|
|
||||||
context->move_to (i, tips[i].top);
|
|
||||||
context->line_to (i, tips[i].bot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context->stroke ();
|
/* the height of the clip-indicator should be at most 7 pixels,
|
||||||
|
|
||||||
/* zero line goes next, so that the outline/clip is on top of it
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (show_zero_line()) {
|
|
||||||
set_source_rgba (context, _zero_color);
|
|
||||||
context->set_line_width (1.0);
|
|
||||||
context->move_to (0, y_extent (0.0) + 0.5);
|
|
||||||
context->line_to (n_peaks, y_extent (0.0) + 0.5);
|
|
||||||
context->stroke ();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* now add dots to the top and bottom of each line (this is
|
|
||||||
* modelled on pyramix, except that we also visual indicate
|
|
||||||
* clipping if it occurs).
|
|
||||||
*
|
|
||||||
* the height of the clip-indicator should be at most 7 pixels,
|
|
||||||
* or 5% of the height of the waveview item.
|
* or 5% of the height of the waveview item.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const double clip_height = min (7.0, ceil (_height * 0.05));
|
const double clip_height = min (7.0, ceil (_height * 0.05));
|
||||||
|
|
||||||
set_source_rgba (context, _outline_color);
|
if (_shape == WaveView::Rectified) {
|
||||||
|
|
||||||
for (int i = 0; i < n_peaks; ++i) {
|
for (int i = 0; i < n_peaks; ++i) {
|
||||||
|
|
||||||
|
if (tips[i].spread >= 2.0) {
|
||||||
|
context->move_to (i, tips[i].top);
|
||||||
|
context->line_to (i, tips[i].bot);
|
||||||
|
context->stroke ();
|
||||||
|
}
|
||||||
|
|
||||||
|
context->save ();
|
||||||
|
context->move_to (i, tips[i].top);
|
||||||
|
|
||||||
|
if (_global_show_waveform_clipping && (tips[i].clip_max || tips[i].clip_min)) {
|
||||||
|
/* clip-indicating upper terminal line */
|
||||||
|
set_source_rgba (context, _clip_color);
|
||||||
|
context->rel_line_to (0, clip_height);
|
||||||
|
context->stroke ();
|
||||||
|
} else {
|
||||||
|
/* normal upper terminal dot */
|
||||||
|
set_source_rgba (context, _outline_color);
|
||||||
|
context->rel_line_to (0, 1.0);
|
||||||
|
context->stroke ();
|
||||||
|
}
|
||||||
|
|
||||||
|
context->restore ();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < n_peaks; ++i) {
|
||||||
|
|
||||||
|
if (tips[i].spread >= 3.0) {
|
||||||
|
context->move_to (i, tips[i].top);
|
||||||
|
context->line_to (i, tips[i].bot);
|
||||||
|
context->stroke ();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tips[i].spread >= 3.0 && show_zero_line()) {
|
||||||
|
context->save ();
|
||||||
|
set_source_rgba (context, _zero_color);
|
||||||
|
context->move_to (0, _height/2.0);
|
||||||
|
context->rel_line_to (0, 0.5);
|
||||||
|
context->stroke ();
|
||||||
|
context->restore ();
|
||||||
|
}
|
||||||
|
|
||||||
|
context->save ();
|
||||||
|
|
||||||
context->move_to (i, tips[i].top);
|
context->move_to (i, tips[i].top);
|
||||||
if (_global_show_waveform_clipping && ((_shape == WaveView::Rectified && (tips[i].clip_max || tips[i].clip_min)) || tips[i].clip_max)) {
|
if (_global_show_waveform_clipping && ((_shape == WaveView::Rectified && (tips[i].clip_max || tips[i].clip_min)) || tips[i].clip_max)) {
|
||||||
/* clip-indicating upper terminal line */
|
/* clip-indicating upper terminal line */
|
||||||
set_source_rgba (context, _clip_color);
|
set_source_rgba (context, _clip_color);
|
||||||
context->rel_line_to (0, clip_height);
|
context->rel_line_to (0, clip_height);
|
||||||
context->stroke ();
|
context->stroke ();
|
||||||
set_source_rgba (context, _outline_color);
|
|
||||||
} else {
|
} else {
|
||||||
/* normal upper terminal dot */
|
/* normal upper terminal dot */
|
||||||
|
set_source_rgba (context, _outline_color);
|
||||||
context->rel_line_to (0, 1.0);
|
context->rel_line_to (0, 1.0);
|
||||||
context->stroke ();
|
context->stroke ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
context->restore ();
|
||||||
|
|
||||||
|
if (tips[i].spread >= 2.0) {
|
||||||
|
context->save ();
|
||||||
context->move_to (i, tips[i].bot);
|
context->move_to (i, tips[i].bot);
|
||||||
if (_global_show_waveform_clipping && _shape != WaveView::Rectified && tips[i].clip_min) {
|
if (_global_show_waveform_clipping && _shape != WaveView::Rectified && tips[i].clip_min) {
|
||||||
/* clip-indicating lower terminal line */
|
/* clip-indicating lower terminal line */
|
||||||
set_source_rgba (context, _clip_color);
|
set_source_rgba (context, _clip_color);
|
||||||
context->rel_line_to (0, -clip_height);
|
context->rel_line_to (0, -clip_height);
|
||||||
context->stroke ();
|
context->stroke ();
|
||||||
set_source_rgba (context, _outline_color);
|
|
||||||
} else {
|
} else {
|
||||||
/* normal lower terminal dot */
|
/* normal lower terminal dot */
|
||||||
|
set_source_rgba (context, _outline_color);
|
||||||
context->rel_line_to (0, -1.0);
|
context->rel_line_to (0, -1.0);
|
||||||
context->stroke ();
|
context->stroke ();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
context->restore ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -839,27 +898,6 @@ WaveView::region_resized ()
|
||||||
end_change ();
|
end_change ();
|
||||||
}
|
}
|
||||||
|
|
||||||
Coord
|
|
||||||
WaveView::y_extent (double s) const
|
|
||||||
{
|
|
||||||
/* it is important that this returns an integral value, so that we
|
|
||||||
can ensure correct single pixel behaviour.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Coord pos;
|
|
||||||
|
|
||||||
switch (_shape) {
|
|
||||||
case Rectified:
|
|
||||||
pos = floor (_height - (s * _height));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pos = floor ((1.0-s) * (_height / 2.0));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return min (_height, (max (0.0, pos)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
WaveView::set_global_gradient_depth (double depth)
|
WaveView::set_global_gradient_depth (double depth)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue