annotate layer/Colour3DPlotLayer.cpp @ 432:8b2b497d302c

* Fix race condition in FFTFileCache when reading from the same FFT model from multiple threads (e.g. when applying more than one plugin at once)
author Chris Cannam
date Wed, 15 Oct 2008 12:08:02 +0000
parents e1a9e478b7f2
children 5124d1494a59
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@182 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@0 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "Colour3DPlotLayer.h"
Chris@0 17
Chris@128 18 #include "view/View.h"
Chris@0 19 #include "base/Profiler.h"
Chris@197 20 #include "base/LogRange.h"
Chris@376 21 #include "ColourMapper.h"
Chris@0 22
Chris@0 23 #include <QPainter>
Chris@0 24 #include <QImage>
Chris@0 25 #include <QRect>
Chris@316 26 #include <QTextStream>
Chris@0 27
Chris@0 28 #include <iostream>
Chris@0 29
Chris@0 30 #include <cassert>
Chris@0 31
Chris@353 32 //#define DEBUG_COLOUR_3D_PLOT_LAYER_PAINT 1
Chris@125 33
Chris@0 34
Chris@44 35 Colour3DPlotLayer::Colour3DPlotLayer() :
Chris@0 36 m_model(0),
Chris@159 37 m_cache(0),
Chris@197 38 m_cacheStart(0),
Chris@197 39 m_colourScale(LinearScale),
Chris@197 40 m_colourMap(0),
Chris@197 41 m_normalizeColumns(false),
Chris@357 42 m_normalizeVisibleArea(false),
Chris@357 43 m_invertVertical(false)
Chris@0 44 {
Chris@44 45
Chris@0 46 }
Chris@0 47
Chris@0 48 Colour3DPlotLayer::~Colour3DPlotLayer()
Chris@0 49 {
Chris@0 50 }
Chris@0 51
Chris@0 52 void
Chris@0 53 Colour3DPlotLayer::setModel(const DenseThreeDimensionalModel *model)
Chris@0 54 {
Chris@193 55 if (m_model == model) return;
Chris@193 56 const DenseThreeDimensionalModel *oldModel = m_model;
Chris@0 57 m_model = model;
Chris@0 58 if (!m_model || !m_model->isOK()) return;
Chris@0 59
Chris@320 60 connectSignals(m_model);
Chris@0 61
Chris@0 62 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
Chris@0 63 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 64 this, SLOT(cacheInvalid(size_t, size_t)));
Chris@0 65
Chris@0 66 emit modelReplaced();
Chris@193 67 emit sliceableModelReplaced(oldModel, model);
Chris@0 68 }
Chris@0 69
Chris@0 70 void
Chris@0 71 Colour3DPlotLayer::cacheInvalid()
Chris@0 72 {
Chris@0 73 delete m_cache;
Chris@0 74 m_cache = 0;
Chris@0 75 }
Chris@0 76
Chris@0 77 void
Chris@0 78 Colour3DPlotLayer::cacheInvalid(size_t, size_t)
Chris@0 79 {
Chris@0 80 cacheInvalid();
Chris@0 81 }
Chris@0 82
Chris@159 83 Layer::PropertyList
Chris@159 84 Colour3DPlotLayer::getProperties() const
Chris@159 85 {
Chris@159 86 PropertyList list;
Chris@197 87 list.push_back("Colour");
Chris@159 88 list.push_back("Colour Scale");
Chris@197 89 list.push_back("Normalize Columns");
Chris@197 90 list.push_back("Normalize Visible Area");
Chris@357 91 list.push_back("Invert Vertical Scale");
Chris@159 92 return list;
Chris@159 93 }
Chris@159 94
Chris@159 95 QString
Chris@159 96 Colour3DPlotLayer::getPropertyLabel(const PropertyName &name) const
Chris@159 97 {
Chris@197 98 if (name == "Colour") return tr("Colour");
Chris@197 99 if (name == "Colour Scale") return tr("Scale");
Chris@197 100 if (name == "Normalize Columns") return tr("Normalize Columns");
Chris@197 101 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
Chris@357 102 if (name == "Invert Vertical Scale") return tr("Invert Vertical Scale");
Chris@159 103 return "";
Chris@159 104 }
Chris@159 105
Chris@346 106 QString
Chris@346 107 Colour3DPlotLayer::getPropertyIconName(const PropertyName &name) const
Chris@346 108 {
Chris@346 109 if (name == "Normalize Columns") return "normalise-columns";
Chris@346 110 if (name == "Normalize Visible Area") return "normalise";
Chris@357 111 if (name == "Invert Vertical Scale") return "invert-vertical";
Chris@346 112 return "";
Chris@346 113 }
Chris@346 114
Chris@159 115 Layer::PropertyType
Chris@159 116 Colour3DPlotLayer::getPropertyType(const PropertyName &name) const
Chris@159 117 {
Chris@197 118 if (name == "Normalize Columns") return ToggleProperty;
Chris@197 119 if (name == "Normalize Visible Area") return ToggleProperty;
Chris@357 120 if (name == "Invert Vertical Scale") return ToggleProperty;
Chris@159 121 return ValueProperty;
Chris@159 122 }
Chris@159 123
Chris@159 124 QString
Chris@159 125 Colour3DPlotLayer::getPropertyGroupName(const PropertyName &name) const
Chris@159 126 {
Chris@197 127 if (name == "Normalize Columns" ||
Chris@197 128 name == "Normalize Visible Area" ||
Chris@357 129 name == "Invert Vertical Scale" ||
Chris@197 130 name == "Colour Scale") return tr("Scale");
Chris@159 131 return QString();
Chris@159 132 }
Chris@159 133
Chris@159 134 int
Chris@159 135 Colour3DPlotLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 136 int *min, int *max, int *deflt) const
Chris@159 137 {
Chris@216 138 int val = 0;
Chris@159 139
Chris@216 140 int garbage0, garbage1, garbage2;
Chris@159 141 if (!min) min = &garbage0;
Chris@159 142 if (!max) max = &garbage1;
Chris@216 143 if (!deflt) deflt = &garbage2;
Chris@159 144
Chris@159 145 if (name == "Colour Scale") {
Chris@159 146
Chris@159 147 *min = 0;
Chris@197 148 *max = 2;
Chris@216 149 *deflt = (int)LinearScale;
Chris@159 150
Chris@216 151 val = (int)m_colourScale;
Chris@159 152
Chris@197 153 } else if (name == "Colour") {
Chris@197 154
Chris@197 155 *min = 0;
Chris@197 156 *max = ColourMapper::getColourMapCount() - 1;
Chris@216 157 *deflt = 0;
Chris@197 158
Chris@216 159 val = m_colourMap;
Chris@197 160
Chris@197 161 } else if (name == "Normalize Columns") {
Chris@197 162
Chris@216 163 *deflt = 0;
Chris@216 164 val = (m_normalizeColumns ? 1 : 0);
Chris@197 165
Chris@197 166 } else if (name == "Normalize Visible Area") {
Chris@197 167
Chris@216 168 *deflt = 0;
Chris@216 169 val = (m_normalizeVisibleArea ? 1 : 0);
Chris@197 170
Chris@357 171 } else if (name == "Invert Vertical Scale") {
Chris@357 172
Chris@357 173 *deflt = 0;
Chris@357 174 val = (m_invertVertical ? 1 : 0);
Chris@357 175
Chris@159 176 } else {
Chris@216 177 val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@159 178 }
Chris@159 179
Chris@216 180 return val;
Chris@159 181 }
Chris@159 182
Chris@159 183 QString
Chris@159 184 Colour3DPlotLayer::getPropertyValueLabel(const PropertyName &name,
Chris@159 185 int value) const
Chris@159 186 {
Chris@197 187 if (name == "Colour") {
Chris@197 188 return ColourMapper::getColourMapName(value);
Chris@197 189 }
Chris@159 190 if (name == "Colour Scale") {
Chris@159 191 switch (value) {
Chris@159 192 default:
Chris@198 193 case 0: return tr("Linear");
Chris@198 194 case 1: return tr("Log");
Chris@198 195 case 2: return tr("+/-1");
Chris@159 196 }
Chris@159 197 }
Chris@159 198 return tr("<unknown>");
Chris@159 199 }
Chris@159 200
Chris@159 201 void
Chris@159 202 Colour3DPlotLayer::setProperty(const PropertyName &name, int value)
Chris@159 203 {
Chris@159 204 if (name == "Colour Scale") {
Chris@159 205 switch (value) {
Chris@159 206 default:
Chris@159 207 case 0: setColourScale(LinearScale); break;
Chris@197 208 case 1: setColourScale(LogScale); break;
Chris@197 209 case 2: setColourScale(PlusMinusOneScale); break;
Chris@159 210 }
Chris@197 211 } else if (name == "Colour") {
Chris@197 212 setColourMap(value);
Chris@197 213 } else if (name == "Normalize Columns") {
Chris@197 214 setNormalizeColumns(value ? true : false);
Chris@197 215 } else if (name == "Normalize Visible Area") {
Chris@197 216 setNormalizeVisibleArea(value ? true : false);
Chris@357 217 } else if (name == "Invert Vertical Scale") {
Chris@357 218 setInvertVertical(value ? true : false);
Chris@159 219 }
Chris@159 220 }
Chris@159 221
Chris@159 222 void
Chris@159 223 Colour3DPlotLayer::setColourScale(ColourScale scale)
Chris@159 224 {
Chris@159 225 if (m_colourScale == scale) return;
Chris@159 226 m_colourScale = scale;
Chris@159 227 cacheInvalid();
Chris@159 228 emit layerParametersChanged();
Chris@159 229 }
Chris@159 230
Chris@197 231 void
Chris@197 232 Colour3DPlotLayer::setColourMap(int map)
Chris@197 233 {
Chris@197 234 if (m_colourMap == map) return;
Chris@197 235 m_colourMap = map;
Chris@197 236 cacheInvalid();
Chris@197 237 emit layerParametersChanged();
Chris@197 238 }
Chris@197 239
Chris@197 240 void
Chris@197 241 Colour3DPlotLayer::setNormalizeColumns(bool n)
Chris@197 242 {
Chris@197 243 if (m_normalizeColumns == n) return;
Chris@197 244 m_normalizeColumns = n;
Chris@197 245 cacheInvalid();
Chris@197 246 emit layerParametersChanged();
Chris@197 247 }
Chris@197 248
Chris@197 249 bool
Chris@197 250 Colour3DPlotLayer::getNormalizeColumns() const
Chris@197 251 {
Chris@197 252 return m_normalizeColumns;
Chris@197 253 }
Chris@197 254
Chris@197 255 void
Chris@197 256 Colour3DPlotLayer::setNormalizeVisibleArea(bool n)
Chris@197 257 {
Chris@197 258 if (m_normalizeVisibleArea == n) return;
Chris@197 259 m_normalizeVisibleArea = n;
Chris@197 260 cacheInvalid();
Chris@197 261 emit layerParametersChanged();
Chris@197 262 }
Chris@197 263
Chris@197 264 bool
Chris@197 265 Colour3DPlotLayer::getNormalizeVisibleArea() const
Chris@197 266 {
Chris@197 267 return m_normalizeVisibleArea;
Chris@197 268 }
Chris@197 269
Chris@357 270 void
Chris@357 271 Colour3DPlotLayer::setInvertVertical(bool n)
Chris@357 272 {
Chris@357 273 if (m_invertVertical == n) return;
Chris@357 274 m_invertVertical = n;
Chris@357 275 cacheInvalid();
Chris@357 276 emit layerParametersChanged();
Chris@357 277 }
Chris@357 278
Chris@357 279 bool
Chris@357 280 Colour3DPlotLayer::getInvertVertical() const
Chris@357 281 {
Chris@357 282 return m_invertVertical;
Chris@357 283 }
Chris@357 284
Chris@25 285 bool
Chris@44 286 Colour3DPlotLayer::isLayerScrollable(const View *v) const
Chris@25 287 {
Chris@197 288 if (m_normalizeVisibleArea) return false;
Chris@25 289 QPoint discard;
Chris@44 290 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@25 291 }
Chris@25 292
Chris@25 293 QString
Chris@44 294 Colour3DPlotLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 295 {
Chris@25 296 if (!m_model) return "";
Chris@25 297
Chris@25 298 int x = pos.x();
Chris@25 299 int y = pos.y();
Chris@25 300
Chris@25 301 size_t modelStart = m_model->getStartFrame();
Chris@130 302 size_t modelResolution = m_model->getResolution();
Chris@25 303
Chris@159 304 float srRatio =
Chris@159 305 float(v->getViewManager()->getMainModelSampleRate()) /
Chris@159 306 float(m_model->getSampleRate());
Chris@159 307
Chris@160 308 int sx0 = int((v->getFrameForX(x) / srRatio - long(modelStart)) /
Chris@160 309 long(modelResolution));
Chris@25 310
Chris@160 311 int f0 = sx0 * modelResolution;
Chris@160 312 int f1 = f0 + modelResolution;
Chris@160 313
Chris@160 314 float binHeight = float(v->height()) / m_model->getHeight();
Chris@248 315 int sy = int((v->height() - y) / binHeight);
Chris@25 316
Chris@357 317 if (m_invertVertical) sy = m_model->getHeight() - sy - 1;
Chris@357 318
Chris@160 319 float value = m_model->getValueAt(sx0, sy);
Chris@159 320
Chris@159 321 // std::cerr << "bin value (" << sx0 << "," << sy << ") is " << value << std::endl;
Chris@25 322
Chris@25 323 QString binName = m_model->getBinName(sy);
Chris@25 324 if (binName == "") binName = QString("[%1]").arg(sy + 1);
Chris@25 325 else binName = QString("%1 [%2]").arg(binName).arg(sy + 1);
Chris@25 326
Chris@25 327 QString text = tr("Time:\t%1 - %2\nBin:\t%3\nValue:\t%4")
Chris@160 328 .arg(RealTime::frame2RealTime(f0, m_model->getSampleRate())
Chris@25 329 .toText(true).c_str())
Chris@160 330 .arg(RealTime::frame2RealTime(f1, m_model->getSampleRate())
Chris@25 331 .toText(true).c_str())
Chris@25 332 .arg(binName)
Chris@25 333 .arg(value);
Chris@25 334
Chris@25 335 return text;
Chris@25 336 }
Chris@25 337
Chris@25 338 int
Chris@248 339 Colour3DPlotLayer::getColourScaleWidth(QPainter &) const
Chris@159 340 {
Chris@159 341 int cw = 20;
Chris@159 342 return cw;
Chris@159 343 }
Chris@159 344
Chris@159 345 int
Chris@248 346 Colour3DPlotLayer::getVerticalScaleWidth(View *, QPainter &paint) const
Chris@25 347 {
Chris@25 348 if (!m_model) return 0;
Chris@25 349
Chris@160 350 QString sampleText = QString("[%1]").arg(m_model->getHeight());
Chris@25 351 int tw = paint.fontMetrics().width(sampleText);
Chris@98 352 bool another = false;
Chris@25 353
Chris@160 354 for (size_t i = 0; i < m_model->getHeight(); ++i) {
Chris@25 355 if (m_model->getBinName(i).length() > sampleText.length()) {
Chris@25 356 sampleText = m_model->getBinName(i);
Chris@98 357 another = true;
Chris@25 358 }
Chris@25 359 }
Chris@98 360 if (another) {
Chris@25 361 tw = std::max(tw, paint.fontMetrics().width(sampleText));
Chris@25 362 }
Chris@25 363
Chris@159 364 return tw + 13 + getColourScaleWidth(paint);
Chris@25 365 }
Chris@25 366
Chris@25 367 void
Chris@44 368 Colour3DPlotLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@25 369 {
Chris@25 370 if (!m_model) return;
Chris@25 371
Chris@25 372 int h = rect.height(), w = rect.width();
Chris@160 373 float binHeight = float(v->height()) / m_model->getHeight();
Chris@25 374
Chris@159 375 int cw = getColourScaleWidth(paint);
Chris@159 376
Chris@159 377 int ch = h - 20;
Chris@159 378 if (ch > 20 && m_cache) {
Chris@159 379
Chris@287 380 paint.setPen(v->getForeground());
Chris@159 381 paint.drawRect(4, 10, cw - 8, ch - 19);
Chris@159 382
Chris@159 383 for (int y = 0; y < ch - 20; ++y) {
Chris@159 384 QRgb c = m_cache->color(((ch - 20 - y) * 255) / (ch - 20));
Chris@159 385 // std::cerr << "y = " << y << ": rgb " << qRed(c) << "," << qGreen(c) << "," << qBlue(c) << std::endl;
Chris@159 386 paint.setPen(QColor(qRed(c), qGreen(c), qBlue(c)));
Chris@159 387 paint.drawLine(5, 11 + y, cw - 5, 11 + y);
Chris@159 388 }
Chris@159 389 }
Chris@159 390
Chris@287 391 paint.setPen(v->getForeground());
Chris@159 392
Chris@98 393 int count = v->height() / paint.fontMetrics().height();
Chris@160 394 int step = m_model->getHeight() / count;
Chris@98 395 if (step == 0) step = 1;
Chris@25 396
Chris@160 397 for (size_t i = 0; i < m_model->getHeight(); ++i) {
Chris@25 398
Chris@357 399 size_t idx = i;
Chris@357 400 if (m_invertVertical) idx = m_model->getHeight() - idx - 1;
Chris@357 401
Chris@357 402 if ((idx % step) != 0) continue;
Chris@98 403
Chris@248 404 int y0 = int(v->height() - (i * binHeight) - 1);
Chris@25 405
Chris@357 406 QString text = m_model->getBinName(idx);
Chris@357 407 if (text == "") text = QString("[%1]").arg(idx + 1);
Chris@25 408
Chris@159 409 paint.drawLine(cw, y0, w, y0);
Chris@25 410
Chris@248 411 int cy = int(y0 - (step * binHeight)/2);
Chris@25 412 int ty = cy + paint.fontMetrics().ascent()/2;
Chris@25 413
Chris@159 414 paint.drawText(cw + 5, ty, text);
Chris@25 415 }
Chris@25 416 }
Chris@25 417
Chris@0 418 void
Chris@197 419 Colour3DPlotLayer::getColumn(size_t col,
Chris@197 420 DenseThreeDimensionalModel::Column &values) const
Chris@197 421 {
Chris@197 422 m_model->getColumn(col, values);
Chris@197 423
Chris@224 424 float colMax = 0.f, colMin = 0.f;
Chris@197 425
Chris@197 426 float min = 0.f, max = 0.f;
Chris@197 427 if (m_normalizeColumns) {
Chris@197 428 min = m_model->getMinimumLevel();
Chris@197 429 max = m_model->getMaximumLevel();
Chris@197 430 }
Chris@197 431
Chris@197 432 if (m_normalizeColumns) {
Chris@197 433 for (size_t y = 0; y < values.size(); ++y) {
Chris@224 434 if (y == 0 || values[y] > colMax) colMax = values[y];
Chris@224 435 if (y == 0 || values[y] < colMin) colMin = values[y];
Chris@197 436 }
Chris@224 437 if (colMin == colMax) colMax = colMin + 1;
Chris@197 438 }
Chris@197 439
Chris@197 440 for (size_t y = 0; y < values.size(); ++y) {
Chris@197 441
Chris@197 442 float value = min;
Chris@197 443
Chris@197 444 value = values[y];
Chris@197 445
Chris@197 446 if (m_normalizeColumns) {
Chris@224 447 float norm = (value - colMin) / (colMax - colMin);
Chris@224 448 value = min + (max - min) * norm;
Chris@197 449 }
Chris@197 450
Chris@197 451 values[y] = value;
Chris@197 452 }
Chris@197 453 }
Chris@197 454
Chris@197 455 void
Chris@197 456 Colour3DPlotLayer::fillCache(size_t firstBin, size_t lastBin) const
Chris@197 457 {
Chris@197 458 size_t modelStart = m_model->getStartFrame();
Chris@197 459 size_t modelEnd = m_model->getEndFrame();
Chris@197 460 size_t modelResolution = m_model->getResolution();
Chris@197 461
Chris@224 462 // std::cerr << "Colour3DPlotLayer::fillCache: " << firstBin << " -> " << lastBin << std::endl;
Chris@197 463
Chris@197 464 if (!m_normalizeVisibleArea || m_normalizeColumns) {
Chris@197 465 firstBin = modelStart / modelResolution;
Chris@197 466 lastBin = modelEnd / modelResolution;
Chris@197 467 }
Chris@197 468
Chris@197 469 size_t cacheWidth = lastBin - firstBin + 1;
Chris@197 470 size_t cacheHeight = m_model->getHeight();
Chris@197 471
Chris@197 472 if (m_cache &&
Chris@197 473 (m_cacheStart != firstBin ||
Chris@248 474 m_cache->width() != int(cacheWidth) ||
Chris@248 475 m_cache->height() != int(cacheHeight))) {
Chris@197 476
Chris@197 477 delete m_cache;
Chris@197 478 m_cache = 0;
Chris@197 479 }
Chris@197 480
Chris@197 481 if (m_cache) return;
Chris@197 482
Chris@197 483 m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8);
Chris@197 484 m_cacheStart = firstBin;
Chris@197 485
Chris@224 486 // std::cerr << "Cache size " << cacheWidth << "x" << cacheHeight << " starting " << m_cacheStart << std::endl;
Chris@197 487
Chris@197 488 m_cache->setNumColors(256);
Chris@197 489 DenseThreeDimensionalModel::Column values;
Chris@197 490
Chris@197 491 float min = m_model->getMinimumLevel();
Chris@197 492 float max = m_model->getMaximumLevel();
Chris@197 493
Chris@197 494 if (m_colourScale == LogScale) {
Chris@197 495 LogRange::mapRange(min, max);
Chris@197 496 } else if (m_colourScale == PlusMinusOneScale) {
Chris@197 497 min = -1.f;
Chris@197 498 max = 1.f;
Chris@197 499 }
Chris@197 500
Chris@197 501 if (max == min) max = min + 1.0;
Chris@197 502
Chris@197 503 ColourMapper mapper(m_colourMap, 0.f, 255.f);
Chris@197 504
Chris@197 505 for (int index = 0; index < 256; ++index) {
Chris@197 506
Chris@197 507 QColor colour = mapper.map(index);
Chris@197 508 m_cache->setColor(index, qRgb(colour.red(), colour.green(), colour.blue()));
Chris@197 509 }
Chris@197 510
Chris@197 511 m_cache->fill(0);
Chris@197 512
Chris@224 513 float visibleMax = 0.f, visibleMin = 0.f;
Chris@197 514
Chris@197 515 if (m_normalizeVisibleArea && !m_normalizeColumns) {
Chris@197 516
Chris@197 517 for (size_t c = firstBin; c <= lastBin; ++c) {
Chris@197 518
Chris@197 519 values.clear();
Chris@197 520 getColumn(c, values);
Chris@197 521
Chris@224 522 float colMax = 0.f, colMin = 0.f;
Chris@197 523
Chris@197 524 for (size_t y = 0; y < m_model->getHeight(); ++y) {
Chris@197 525 if (y >= values.size()) break;
Chris@197 526 if (y == 0 || values[y] > colMax) colMax = values[y];
Chris@224 527 if (y == 0 || values[y] < colMin) colMin = values[y];
Chris@197 528 }
Chris@197 529
Chris@197 530 if (c == firstBin || colMax > visibleMax) visibleMax = colMax;
Chris@224 531 if (c == firstBin || colMin < visibleMin) visibleMin = colMin;
Chris@197 532 }
Chris@197 533 }
Chris@197 534
Chris@224 535 if (visibleMin == visibleMax) visibleMax = visibleMin + 1;
Chris@224 536
Chris@197 537 for (size_t c = firstBin; c <= lastBin; ++c) {
Chris@197 538
Chris@197 539 values.clear();
Chris@197 540 getColumn(c, values);
Chris@197 541
Chris@197 542 for (size_t y = 0; y < m_model->getHeight(); ++y) {
Chris@197 543
Chris@197 544 float value = min;
Chris@197 545 if (y < values.size()) {
Chris@197 546 value = values[y];
Chris@197 547 }
Chris@197 548
Chris@197 549 if (m_normalizeVisibleArea && !m_normalizeColumns) {
Chris@224 550 float norm = (value - visibleMin) / (visibleMax - visibleMin);
Chris@224 551 value = min + (max - min) * norm;
Chris@224 552 }
Chris@224 553
Chris@224 554 if (m_colourScale == LogScale) {
Chris@224 555 value = LogRange::map(value);
Chris@197 556 }
Chris@197 557
Chris@197 558 int pixel = int(((value - min) * 256) / (max - min));
Chris@197 559 if (pixel < 0) pixel = 0;
Chris@197 560 if (pixel > 255) pixel = 255;
Chris@197 561
Chris@357 562 if (m_invertVertical) {
Chris@357 563 m_cache->setPixel(c - firstBin, m_model->getHeight() - y - 1,
Chris@357 564 pixel);
Chris@357 565 } else {
Chris@357 566 m_cache->setPixel(c - firstBin, y, pixel);
Chris@357 567 }
Chris@197 568 }
Chris@197 569 }
Chris@197 570 }
Chris@197 571
Chris@197 572 void
Chris@44 573 Colour3DPlotLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 574 {
Chris@0 575 // Profiler profiler("Colour3DPlotLayer::paint");
Chris@125 576 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@125 577 std::cerr << "Colour3DPlotLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << std::endl;
Chris@125 578 #endif
Chris@0 579
Chris@0 580 int completion = 0;
Chris@0 581 if (!m_model || !m_model->isOK() || !m_model->isReady(&completion)) {
Chris@0 582 if (completion > 0) {
Chris@44 583 paint.fillRect(0, 10, v->width() * completion / 100,
Chris@0 584 10, QColor(120, 120, 120));
Chris@0 585 }
Chris@0 586 return;
Chris@0 587 }
Chris@0 588
Chris@197 589 if (m_normalizeVisibleArea && !m_normalizeColumns) rect = v->rect();
Chris@197 590
Chris@0 591 size_t modelStart = m_model->getStartFrame();
Chris@0 592 size_t modelEnd = m_model->getEndFrame();
Chris@130 593 size_t modelResolution = m_model->getResolution();
Chris@0 594
Chris@197 595 // The cache is from the model's start frame to the model's end
Chris@197 596 // frame at the model's window increment frames per pixel. We
Chris@197 597 // want to draw from our start frame + x0 * zoomLevel to our start
Chris@197 598 // frame + x1 * zoomLevel at zoomLevel frames per pixel.
Chris@12 599
Chris@197 600 // We have quite different paint mechanisms for rendering "large"
Chris@197 601 // bins (more than one bin per pixel in both directions) and
Chris@197 602 // "small". This is "large"; see paintDense below for "small".
Chris@12 603
Chris@197 604 int x0 = rect.left();
Chris@197 605 int x1 = rect.right() + 1;
Chris@12 606
Chris@197 607 int h = v->height();
Chris@0 608
Chris@197 609 float srRatio =
Chris@197 610 float(v->getViewManager()->getMainModelSampleRate()) /
Chris@197 611 float(m_model->getSampleRate());
Chris@0 612
Chris@197 613 int sx0 = int((v->getFrameForX(x0) / srRatio - long(modelStart))
Chris@197 614 / long(modelResolution));
Chris@197 615 int sx1 = int((v->getFrameForX(x1) / srRatio - long(modelStart))
Chris@197 616 / long(modelResolution));
Chris@197 617 int sh = m_model->getHeight();
Chris@159 618
Chris@197 619 if (sx0 > 0) --sx0;
Chris@197 620 fillCache(sx0 < 0 ? 0 : sx0,
Chris@197 621 sx1 < 0 ? 0 : sx1);
Chris@0 622
Chris@351 623 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@351 624 std::cerr << "Colour3DPlotLayer::paint: height = "<< m_model->getHeight() << ", modelStart = " << modelStart << ", resolution = " << modelResolution << ", model rate = " << m_model->getSampleRate() << std::endl;
Chris@351 625 #endif
Chris@351 626
Chris@248 627 if (int(m_model->getHeight()) >= v->height() ||
Chris@348 628 int(modelResolution * m_model->getSampleRate()) < v->getZoomLevel() / 2) {
Chris@347 629 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@351 630 std::cerr << "calling paintDense" << std::endl;
Chris@347 631 #endif
Chris@98 632 paintDense(v, paint, rect);
Chris@98 633 return;
Chris@98 634 }
Chris@98 635
Chris@125 636 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@347 637 std::cerr << "Colour3DPlotLayer::paint: w " << x1-x0 << ", h " << h << ", sx0 " << sx0 << ", sx1 " << sx1 << ", sw " << sx1-sx0 << ", sh " << sh << std::endl;
Chris@130 638 std::cerr << "Colour3DPlotLayer: sample rate is " << m_model->getSampleRate() << ", resolution " << m_model->getResolution() << std::endl;
Chris@125 639 #endif
Chris@0 640
Chris@25 641 QPoint illuminatePos;
Chris@44 642 bool illuminate = v->shouldIlluminateLocalFeatures(this, illuminatePos);
Chris@54 643 char labelbuf[10];
Chris@25 644
Chris@197 645 for (int sx = sx0; sx <= sx1; ++sx) {
Chris@0 646
Chris@197 647 int scx = 0;
Chris@248 648 if (sx > int(m_cacheStart)) scx = sx - m_cacheStart;
Chris@197 649
Chris@130 650 int fx = sx * int(modelResolution);
Chris@0 651
Chris@351 652 if (fx + int(modelResolution) <= int(modelStart) ||
Chris@0 653 fx > int(modelEnd)) continue;
Chris@0 654
Chris@248 655 int rx0 = v->getXForFrame(int((fx + int(modelStart)) * srRatio));
Chris@248 656 int rx1 = v->getXForFrame(int((fx + int(modelStart) + int(modelResolution) + 1) * srRatio));
Chris@54 657
Chris@159 658 int rw = rx1 - rx0;
Chris@159 659 if (rw < 1) rw = 1;
Chris@54 660
Chris@159 661 bool showLabel = (rw > 10 &&
Chris@159 662 paint.fontMetrics().width("0.000000") < rw - 3 &&
Chris@54 663 paint.fontMetrics().height() < (h / sh));
Chris@98 664
Chris@0 665 for (int sy = 0; sy < sh; ++sy) {
Chris@0 666
Chris@0 667 int ry0 = h - (sy * h) / sh - 1;
Chris@0 668 QRgb pixel = qRgb(255, 255, 255);
Chris@197 669 if (scx >= 0 && scx < m_cache->width() &&
Chris@0 670 sy >= 0 && sy < m_cache->height()) {
Chris@197 671 pixel = m_cache->pixel(scx, sy);
Chris@0 672 }
Chris@0 673
Chris@159 674 QRect r(rx0, ry0 - h / sh - 1, rw, h / sh + 1);
Chris@159 675
Chris@159 676 if (rw == 1) {
Chris@159 677 paint.setPen(pixel);
Chris@159 678 paint.setBrush(Qt::NoBrush);
Chris@159 679 paint.drawLine(r.x(), r.y(), r.x(), r.y() + r.height() - 1);
Chris@159 680 continue;
Chris@159 681 }
Chris@159 682
Chris@0 683 QColor pen(255, 255, 255, 80);
Chris@0 684 QColor brush(pixel);
Chris@159 685
Chris@159 686 if (rw > 3 && r.height() > 3) {
Chris@159 687 brush.setAlpha(160);
Chris@159 688 }
Chris@159 689
Chris@0 690 paint.setPen(Qt::NoPen);
Chris@0 691 paint.setBrush(brush);
Chris@0 692
Chris@25 693 if (illuminate) {
Chris@25 694 if (r.contains(illuminatePos)) {
Chris@287 695 paint.setPen(v->getForeground());
Chris@25 696 }
Chris@25 697 }
Chris@76 698
Chris@125 699 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@159 700 std::cerr << "rect " << r.x() << "," << r.y() << " "
Chris@159 701 << r.width() << "x" << r.height() << std::endl;
Chris@125 702 #endif
Chris@25 703
Chris@25 704 paint.drawRect(r);
Chris@0 705
Chris@54 706 if (showLabel) {
Chris@197 707 if (scx >= 0 && scx < m_cache->width() &&
Chris@54 708 sy >= 0 && sy < m_cache->height()) {
Chris@197 709 float value = m_model->getValueAt(scx, sy);
Chris@54 710 sprintf(labelbuf, "%06f", value);
Chris@54 711 QString text(labelbuf);
Chris@287 712 paint.setPen(v->getBackground());
Chris@54 713 paint.drawText(rx0 + 2,
Chris@54 714 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
Chris@54 715 text);
Chris@0 716 }
Chris@0 717 }
Chris@0 718 }
Chris@0 719 }
Chris@98 720 }
Chris@0 721
Chris@98 722 void
Chris@98 723 Colour3DPlotLayer::paintDense(View *v, QPainter &paint, QRect rect) const
Chris@98 724 {
Chris@98 725 size_t modelStart = m_model->getStartFrame();
Chris@130 726 size_t modelResolution = m_model->getResolution();
Chris@0 727
Chris@159 728 float srRatio =
Chris@159 729 float(v->getViewManager()->getMainModelSampleRate()) /
Chris@159 730 float(m_model->getSampleRate());
Chris@159 731
Chris@98 732 int x0 = rect.left();
Chris@98 733 int x1 = rect.right() + 1;
Chris@0 734
Chris@98 735 int w = x1 - x0;
Chris@98 736 int h = v->height();
Chris@160 737 int sh = m_model->getHeight();
Chris@98 738
Chris@98 739 QImage img(w, h, QImage::Format_RGB32);
Chris@98 740
Chris@98 741 for (int x = x0; x < x1; ++x) {
Chris@98 742
Chris@347 743 long xf = long(v->getFrameForX(x));
Chris@105 744 if (xf < 0) {
Chris@105 745 for (int y = 0; y < h; ++y) {
Chris@105 746 img.setPixel(x - x0, y, m_cache->color(0));
Chris@105 747 }
Chris@105 748 continue;
Chris@105 749 }
Chris@105 750
Chris@347 751 xf /= srRatio;
Chris@347 752
Chris@130 753 float sx0 = (float(xf) - modelStart) / modelResolution;
Chris@159 754 float sx1 = (float(v->getFrameForX(x+1) / srRatio) - modelStart) / modelResolution;
Chris@98 755
Chris@98 756 int sx0i = int(sx0 + 0.001);
Chris@98 757 int sx1i = int(sx1);
Chris@98 758
Chris@98 759 for (int y = 0; y < h; ++y) {
Chris@98 760
Chris@98 761 float sy0 = (float(h - y - 1) * sh) / h;
Chris@98 762 float sy1 = (float(h - y) * sh) / h;
Chris@98 763
Chris@98 764 int sy0i = int(sy0 + 0.001);
Chris@98 765 int sy1i = int(sy1);
Chris@98 766
Chris@98 767 float mag = 0.0, div = 0.0;
Chris@159 768 int max = 0;
Chris@98 769
Chris@98 770 for (int sx = sx0i; sx <= sx1i; ++sx) {
Chris@98 771
Chris@197 772 int scx = 0;
Chris@248 773 if (sx > int(m_cacheStart)) scx = sx - int(m_cacheStart);
Chris@197 774
Chris@197 775 if (scx < 0 || scx >= m_cache->width()) continue;
Chris@98 776
Chris@98 777 for (int sy = sy0i; sy <= sy1i; ++sy) {
Chris@98 778
Chris@98 779 if (sy < 0 || sy >= m_cache->height()) continue;
Chris@98 780
Chris@98 781 float prop = 1.0;
Chris@98 782 if (sx == sx0i) prop *= (sx + 1) - sx0;
Chris@98 783 if (sx == sx1i) prop *= sx1 - sx;
Chris@98 784 if (sy == sy0i) prop *= (sy + 1) - sy0;
Chris@98 785 if (sy == sy1i) prop *= sy1 - sy;
Chris@98 786
Chris@197 787 mag += prop * m_cache->pixelIndex(scx, sy);
Chris@197 788 max = std::max(max, m_cache->pixelIndex(scx, sy));
Chris@98 789 div += prop;
Chris@98 790 }
Chris@98 791 }
Chris@98 792
Chris@98 793 if (div != 0) mag /= div;
Chris@98 794 if (mag < 0) mag = 0;
Chris@98 795 if (mag > 255) mag = 255;
Chris@159 796 if (max < 0) max = 0;
Chris@159 797 if (max > 255) max = 255;
Chris@98 798
Chris@98 799 img.setPixel(x - x0, y, m_cache->color(int(mag + 0.001)));
Chris@159 800 // img.setPixel(x - x0, y, m_cache->color(max));
Chris@98 801 }
Chris@0 802 }
Chris@0 803
Chris@98 804 paint.drawImage(x0, 0, img);
Chris@0 805 }
Chris@0 806
Chris@28 807 bool
Chris@44 808 Colour3DPlotLayer::snapToFeatureFrame(View *v, int &frame,
Chris@28 809 size_t &resolution,
Chris@28 810 SnapType snap) const
Chris@24 811 {
Chris@24 812 if (!m_model) {
Chris@44 813 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@24 814 }
Chris@24 815
Chris@130 816 resolution = m_model->getResolution();
Chris@28 817 int left = (frame / resolution) * resolution;
Chris@28 818 int right = left + resolution;
Chris@28 819
Chris@28 820 switch (snap) {
Chris@28 821 case SnapLeft: frame = left; break;
Chris@28 822 case SnapRight: frame = right; break;
Chris@28 823 case SnapNearest:
Chris@28 824 case SnapNeighbouring:
Chris@28 825 if (frame - left > right - frame) frame = right;
Chris@28 826 else frame = left;
Chris@28 827 break;
Chris@28 828 }
Chris@24 829
Chris@28 830 return true;
Chris@24 831 }
Chris@24 832
Chris@316 833 void
Chris@316 834 Colour3DPlotLayer::toXml(QTextStream &stream,
Chris@316 835 QString indent, QString extraAttributes) const
Chris@197 836 {
Chris@316 837 QString s = QString("scale=\"%1\" "
Chris@316 838 "colourScheme=\"%2\" "
Chris@316 839 "normalizeColumns=\"%3\" "
Chris@316 840 "normalizeVisibleArea=\"%4\"")
Chris@197 841 .arg((int)m_colourScale)
Chris@197 842 .arg(m_colourMap)
Chris@197 843 .arg(m_normalizeColumns ? "true" : "false")
Chris@197 844 .arg(m_normalizeVisibleArea ? "true" : "false");
Chris@197 845
Chris@316 846 Layer::toXml(stream, indent, extraAttributes + " " + s);
Chris@197 847 }
Chris@197 848
Chris@197 849 void
Chris@197 850 Colour3DPlotLayer::setProperties(const QXmlAttributes &attributes)
Chris@197 851 {
Chris@197 852 bool ok = false;
Chris@197 853
Chris@197 854 ColourScale scale = (ColourScale)attributes.value("scale").toInt(&ok);
Chris@197 855 if (ok) setColourScale(scale);
Chris@197 856
Chris@197 857 int colourMap = attributes.value("colourScheme").toInt(&ok);
Chris@197 858 if (ok) setColourMap(colourMap);
Chris@197 859
Chris@197 860 bool normalizeColumns =
Chris@197 861 (attributes.value("normalizeColumns").trimmed() == "true");
Chris@197 862 setNormalizeColumns(normalizeColumns);
Chris@197 863
Chris@197 864 bool normalizeVisibleArea =
Chris@197 865 (attributes.value("normalizeVisibleArea").trimmed() == "true");
Chris@197 866 setNormalizeVisibleArea(normalizeVisibleArea);
Chris@197 867 }
Chris@197 868