Chris@0
|
1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 A waveform viewer and audio annotation editor.
|
Chris@5
|
5 Chris Cannam, Queen Mary University of London, 2005-2006
|
Chris@0
|
6
|
Chris@0
|
7 This is experimental software. Not for distribution.
|
Chris@0
|
8 */
|
Chris@0
|
9
|
Chris@0
|
10 #include "Colour3DPlotLayer.h"
|
Chris@0
|
11
|
Chris@0
|
12 #include "base/View.h"
|
Chris@0
|
13 #include "base/Profiler.h"
|
Chris@0
|
14
|
Chris@0
|
15 #include <QPainter>
|
Chris@0
|
16 #include <QImage>
|
Chris@0
|
17 #include <QRect>
|
Chris@0
|
18
|
Chris@0
|
19 #include <iostream>
|
Chris@0
|
20
|
Chris@0
|
21 #include <cassert>
|
Chris@0
|
22
|
Chris@0
|
23
|
Chris@0
|
24 Colour3DPlotLayer::Colour3DPlotLayer(View *w) :
|
Chris@0
|
25 Layer(w),
|
Chris@0
|
26 m_model(0),
|
Chris@0
|
27 m_cache(0)
|
Chris@0
|
28 {
|
Chris@0
|
29 m_view->addLayer(this);
|
Chris@0
|
30 }
|
Chris@0
|
31
|
Chris@0
|
32 Colour3DPlotLayer::~Colour3DPlotLayer()
|
Chris@0
|
33 {
|
Chris@0
|
34 }
|
Chris@0
|
35
|
Chris@0
|
36 void
|
Chris@0
|
37 Colour3DPlotLayer::setModel(const DenseThreeDimensionalModel *model)
|
Chris@0
|
38 {
|
Chris@0
|
39 m_model = model;
|
Chris@0
|
40 if (!m_model || !m_model->isOK()) return;
|
Chris@0
|
41
|
Chris@0
|
42 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
|
Chris@0
|
43 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
|
Chris@0
|
44 this, SIGNAL(modelChanged(size_t, size_t)));
|
Chris@0
|
45
|
Chris@0
|
46 connect(m_model, SIGNAL(completionChanged()),
|
Chris@0
|
47 this, SIGNAL(modelCompletionChanged()));
|
Chris@0
|
48
|
Chris@0
|
49 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
|
Chris@0
|
50 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
|
Chris@0
|
51 this, SLOT(cacheInvalid(size_t, size_t)));
|
Chris@0
|
52
|
Chris@0
|
53 emit modelReplaced();
|
Chris@0
|
54 }
|
Chris@0
|
55
|
Chris@0
|
56 void
|
Chris@0
|
57 Colour3DPlotLayer::cacheInvalid()
|
Chris@0
|
58 {
|
Chris@0
|
59 delete m_cache;
|
Chris@0
|
60 m_cache = 0;
|
Chris@0
|
61 }
|
Chris@0
|
62
|
Chris@0
|
63 void
|
Chris@0
|
64 Colour3DPlotLayer::cacheInvalid(size_t, size_t)
|
Chris@0
|
65 {
|
Chris@0
|
66 cacheInvalid();
|
Chris@0
|
67 }
|
Chris@0
|
68
|
Chris@0
|
69 void
|
Chris@0
|
70 Colour3DPlotLayer::paint(QPainter &paint, QRect rect) const
|
Chris@0
|
71 {
|
Chris@0
|
72 // Profiler profiler("Colour3DPlotLayer::paint");
|
Chris@0
|
73 // std::cerr << "Colour3DPlotLayer::paint(): m_model is " << m_model << ", zoom level is " << m_view->getZoomLevel() << std::endl;
|
Chris@0
|
74
|
Chris@0
|
75 //!!! This doesn't yet accommodate the fact that the model may
|
Chris@0
|
76 //have a different sample rate from an underlying model. At the
|
Chris@0
|
77 //moment our paint mechanism assumes all models have the same
|
Chris@0
|
78 //sample rate. If that isn't the case, they won't align and the
|
Chris@0
|
79 //time ruler will match whichever model was used to construct it.
|
Chris@0
|
80 //Obviously it is not going to be the case in general that models
|
Chris@0
|
81 //will have the same samplerate, so we need a pane samplerate as
|
Chris@0
|
82 //well which we trivially realign to. (We can probably require
|
Chris@0
|
83 //the waveform and spectrogram layers to display at the pane
|
Chris@0
|
84 //samplerate.)
|
Chris@0
|
85
|
Chris@0
|
86 int completion = 0;
|
Chris@0
|
87 if (!m_model || !m_model->isOK() || !m_model->isReady(&completion)) {
|
Chris@0
|
88 if (completion > 0) {
|
Chris@0
|
89 paint.fillRect(0, 10, m_view->width() * completion / 100,
|
Chris@0
|
90 10, QColor(120, 120, 120));
|
Chris@0
|
91 }
|
Chris@0
|
92 return;
|
Chris@0
|
93 }
|
Chris@0
|
94
|
Chris@0
|
95 long startFrame = m_view->getStartFrame();
|
Chris@0
|
96 int zoomLevel = m_view->getZoomLevel();
|
Chris@0
|
97
|
Chris@0
|
98 size_t modelStart = m_model->getStartFrame();
|
Chris@0
|
99 size_t modelEnd = m_model->getEndFrame();
|
Chris@0
|
100 size_t modelWindow = m_model->getWindowSize();
|
Chris@0
|
101
|
Chris@12
|
102 size_t cacheWidth = (modelEnd - modelStart) / modelWindow + 1;
|
Chris@12
|
103 size_t cacheHeight = m_model->getYBinCount();
|
Chris@12
|
104
|
Chris@12
|
105 if (m_cache &&
|
Chris@12
|
106 (m_cache->width() != cacheWidth ||
|
Chris@12
|
107 m_cache->height() != cacheHeight)) {
|
Chris@12
|
108
|
Chris@12
|
109 delete m_cache;
|
Chris@12
|
110 m_cache = 0;
|
Chris@12
|
111 }
|
Chris@12
|
112
|
Chris@0
|
113 if (!m_cache) {
|
Chris@0
|
114
|
Chris@12
|
115 m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8);
|
Chris@0
|
116
|
Chris@0
|
117 m_cache->setNumColors(256);
|
Chris@0
|
118 DenseThreeDimensionalModel::BinValueSet values;
|
Chris@0
|
119 /*
|
Chris@0
|
120 for (int pixel = 0; pixel < 256; ++pixel) {
|
Chris@0
|
121 int hue = 256 - pixel;
|
Chris@0
|
122 // int hue = 220 - pixel;
|
Chris@0
|
123 // if (hue < 0) hue += 360;
|
Chris@0
|
124 QColor color = QColor::fromHsv(hue, pixel/2 + 128, pixel);
|
Chris@0
|
125 m_cache->setColor(pixel, qRgb(color.red(), color.green(), color.blue()));
|
Chris@0
|
126 }
|
Chris@0
|
127 */
|
Chris@0
|
128
|
Chris@0
|
129 float min = m_model->getMinimumLevel();
|
Chris@0
|
130 float max = m_model->getMaximumLevel();
|
Chris@0
|
131
|
Chris@0
|
132 if (max == min) max = min + 1.0;
|
Chris@0
|
133
|
Chris@0
|
134 // int min = lrintf(m_model->getMinimumLevel());
|
Chris@0
|
135 // int max = lrintf(m_model->getMaximumLevel());
|
Chris@0
|
136 for (int value = 0; value < 256; ++value) {
|
Chris@0
|
137 // int spread = ((value - min) * 256) / (max - min);
|
Chris@0
|
138 // int hue = 256 - spread;
|
Chris@0
|
139 // QColor color = QColor::fromHsv(hue, spread/2 + 128, spread);
|
Chris@0
|
140 int hue = 256 - value;
|
Chris@0
|
141 QColor color = QColor::fromHsv(hue, value/2 + 128, value);
|
Chris@0
|
142 m_cache->setColor(value, qRgba(color.red(), color.green(), color.blue(), 80));
|
Chris@0
|
143 // std::cerr << "Colour3DPlotLayer: Index " << value << ": hue " << hue << std::endl;
|
Chris@0
|
144 }
|
Chris@0
|
145
|
Chris@0
|
146 m_cache->fill(min);
|
Chris@0
|
147
|
Chris@0
|
148 for (size_t f = modelStart; f <= modelEnd; f += modelWindow) {
|
Chris@0
|
149
|
Chris@0
|
150 values.clear();
|
Chris@0
|
151 m_model->getBinValues(f, values);
|
Chris@0
|
152
|
Chris@0
|
153 for (size_t y = 0; y < m_model->getYBinCount(); ++y) {
|
Chris@0
|
154
|
Chris@0
|
155 float value = min;
|
Chris@0
|
156 if (y < values.size()) value = values[y];
|
Chris@0
|
157
|
Chris@0
|
158 //!!! divide-by-zero!
|
Chris@0
|
159 int pixel = int(((value - min) * 256) / (max - min));
|
Chris@0
|
160
|
Chris@0
|
161 m_cache->setPixel(f / modelWindow, y, pixel);
|
Chris@0
|
162 }
|
Chris@0
|
163 }
|
Chris@0
|
164 }
|
Chris@0
|
165
|
Chris@0
|
166 int x0 = rect.left();
|
Chris@0
|
167 int x1 = rect.right() + 1;
|
Chris@0
|
168
|
Chris@0
|
169 // int y0 = rect.top();
|
Chris@0
|
170 // int y1 = rect.bottom();
|
Chris@0
|
171 int w = x1 - x0;
|
Chris@0
|
172 int h = m_view->height();
|
Chris@0
|
173
|
Chris@0
|
174 // The cache is from the model's start frame to the model's end
|
Chris@0
|
175 // frame at the model's window increment frames per pixel. We
|
Chris@0
|
176 // want to draw from our start frame + x0 * zoomLevel to our start
|
Chris@0
|
177 // frame + x1 * zoomLevel at zoomLevel frames per pixel.
|
Chris@0
|
178
|
Chris@0
|
179 //!!! Strictly speaking we want quite different paint mechanisms
|
Chris@0
|
180 //for models that have more than one bin per pixel in either
|
Chris@0
|
181 //direction. This one is only really appropriate for models with
|
Chris@0
|
182 //far fewer bins in both directions.
|
Chris@0
|
183
|
Chris@0
|
184 int sx0 = ((startFrame + x0 * zoomLevel) - int(modelStart)) /
|
Chris@0
|
185 int(modelWindow);
|
Chris@0
|
186 int sx1 = ((startFrame + x1 * zoomLevel) - int(modelStart)) /
|
Chris@0
|
187 int(modelWindow);
|
Chris@0
|
188 int sw = sx1 - sx0;
|
Chris@0
|
189 int sh = m_model->getYBinCount();
|
Chris@0
|
190
|
Chris@0
|
191 /*
|
Chris@0
|
192 std::cerr << "Colour3DPlotLayer::paint: w " << w << ", h " << h << ", sx0 " << sx0 << ", sx1 " << sx1 << ", sw " << sw << ", sh " << sh << std::endl;
|
Chris@0
|
193 std::cerr << "Colour3DPlotLayer: sample rate is " << m_model->getSampleRate() << ", window size " << m_model->getWindowSize() << std::endl;
|
Chris@0
|
194 */
|
Chris@0
|
195
|
Chris@0
|
196 for (int sx = sx0 - 1; sx <= sx1; ++sx) {
|
Chris@0
|
197
|
Chris@0
|
198 int fx = sx * int(modelWindow);
|
Chris@0
|
199
|
Chris@0
|
200 if (fx + modelWindow < int(modelStart) ||
|
Chris@0
|
201 fx > int(modelEnd)) continue;
|
Chris@0
|
202
|
Chris@0
|
203 for (int sy = 0; sy < sh; ++sy) {
|
Chris@0
|
204
|
Chris@0
|
205 int rx0 = ((fx + int(modelStart))
|
Chris@0
|
206 - int(startFrame)) / zoomLevel;
|
Chris@0
|
207 int rx1 = ((fx + int(modelWindow) + int(modelStart))
|
Chris@0
|
208 - int(startFrame)) / zoomLevel;
|
Chris@0
|
209
|
Chris@0
|
210 int ry0 = h - (sy * h) / sh - 1;
|
Chris@0
|
211 int ry1 = h - ((sy + 1) * h) / sh - 2;
|
Chris@0
|
212 QRgb pixel = qRgb(255, 255, 255);
|
Chris@0
|
213 if (sx >= 0 && sx < m_cache->width() &&
|
Chris@0
|
214 sy >= 0 && sy < m_cache->height()) {
|
Chris@0
|
215 pixel = m_cache->pixel(sx, sy);
|
Chris@0
|
216 }
|
Chris@0
|
217
|
Chris@0
|
218 QColor pen(255, 255, 255, 80);
|
Chris@0
|
219 // QColor pen(pixel);
|
Chris@0
|
220 QColor brush(pixel);
|
Chris@0
|
221 brush.setAlpha(160);
|
Chris@0
|
222 // paint.setPen(pen);
|
Chris@0
|
223 paint.setPen(Qt::NoPen);
|
Chris@0
|
224 paint.setBrush(brush);
|
Chris@0
|
225
|
Chris@0
|
226 int w = rx1 - rx0;
|
Chris@0
|
227 if (w < 1) w = 1;
|
Chris@0
|
228 paint.drawRect(rx0, ry0 - h / sh - 1, w, h / sh + 1);
|
Chris@0
|
229
|
Chris@0
|
230 if (sx >= 0 && sx < m_cache->width() &&
|
Chris@0
|
231 sy >= 0 && sy < m_cache->height()) {
|
Chris@0
|
232 int dv = m_cache->pixelIndex(sx, sy);
|
Chris@0
|
233 if (dv != 0 && paint.fontMetrics().height() < (h / sh)) {
|
Chris@0
|
234 QString text = QString("%1").arg(dv);
|
Chris@0
|
235 if (paint.fontMetrics().width(text) < w - 3) {
|
Chris@0
|
236 paint.setPen(Qt::white);
|
Chris@0
|
237 paint.drawText(rx0 + 2,
|
Chris@0
|
238 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
|
Chris@0
|
239 QString("%1").arg(dv));
|
Chris@0
|
240 }
|
Chris@0
|
241 }
|
Chris@0
|
242 }
|
Chris@0
|
243 }
|
Chris@0
|
244 }
|
Chris@0
|
245
|
Chris@0
|
246 /*
|
Chris@0
|
247 QRect targetRect(x0, 0, w, h);
|
Chris@0
|
248 QRect sourceRect(sx0, 0, sw, sh);
|
Chris@0
|
249
|
Chris@0
|
250 QImage scaled(w, h, QImage::Format_RGB32);
|
Chris@0
|
251
|
Chris@0
|
252 for (int x = 0; x < w; ++x) {
|
Chris@0
|
253 for (int y = 0; y < h; ++y) {
|
Chris@0
|
254
|
Chris@0
|
255
|
Chris@0
|
256
|
Chris@0
|
257 int sx = sx0 + (x * sw) / w;
|
Chris@0
|
258 int sy = sh - (y * sh) / h - 1;
|
Chris@0
|
259 // std::cerr << "Colour3DPlotLayer::paint: sx " << sx << ", sy " << sy << ", cache w " << m_cache->width() << ", cache h " << m_cache->height() << std::endl;
|
Chris@0
|
260 if (sx >= 0 && sy >= 0 &&
|
Chris@0
|
261 sx < m_cache->width() && sy < m_cache->height()) {
|
Chris@0
|
262 scaled.setPixel(x, y, m_cache->pixel(sx, sy));
|
Chris@0
|
263 } else {
|
Chris@0
|
264 scaled.setPixel(x, y, qRgba(255, 255, 255, 80));
|
Chris@0
|
265 }
|
Chris@0
|
266 }
|
Chris@0
|
267 }
|
Chris@0
|
268
|
Chris@0
|
269 paint.drawImage(x0, 0, scaled);
|
Chris@0
|
270 */
|
Chris@0
|
271 }
|
Chris@0
|
272
|
Chris@0
|
273 #ifdef INCLUDE_MOCFILES
|
Chris@0
|
274 #include "Colour3DPlotLayer.moc.cpp"
|
Chris@0
|
275 #endif
|
Chris@0
|
276
|
Chris@0
|
277
|