void WaveformRendererRGB::draw(QPainter* painter, QPaintEvent* /*event*/) { const TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); if (!trackInfo) { return; } const Waveform* waveform = trackInfo->getWaveform(); if (waveform == NULL) { return; } const int dataSize = waveform->getDataSize(); if (dataSize <= 1) { return; } const WaveformData* data = waveform->data(); if (data == NULL) { return; } painter->save(); painter->setRenderHints(QPainter::Antialiasing, false); painter->setRenderHints(QPainter::HighQualityAntialiasing, false); painter->setRenderHints(QPainter::SmoothPixmapTransform, false); painter->setWorldMatrixEnabled(false); painter->resetTransform(); const double firstVisualIndex = m_waveformRenderer->getFirstDisplayedPosition() * dataSize; const double lastVisualIndex = m_waveformRenderer->getLastDisplayedPosition() * dataSize; const double offset = firstVisualIndex; // Represents the # of waveform data points per horizontal pixel. const double gain = (lastVisualIndex - firstVisualIndex) / (double)m_waveformRenderer->getWidth(); float allGain(1.0); getGains(&allGain, NULL, NULL, NULL); QColor color; const float halfHeight = (float)m_waveformRenderer->getHeight()/2.0; const float heightFactor = allGain*halfHeight/255.0; // Draw reference line painter->setPen(m_pColors->getAxesColor()); painter->drawLine(0,halfHeight,m_waveformRenderer->getWidth(),halfHeight); for (int x = 0; x < m_waveformRenderer->getWidth(); ++x) { // Width of the x position in visual indices. const double xSampleWidth = gain * x; // Effective visual index of x const double xVisualSampleIndex = xSampleWidth + offset; // Our current pixel (x) corresponds to a number of visual samples // (visualSamplerPerPixel) in our waveform object. We take the max of // all the data points on either side of xVisualSampleIndex within a // window of 'maxSamplingRange' visual samples to measure the maximum // data point contained by this pixel. double maxSamplingRange = gain / 2.0; // Since xVisualSampleIndex is in visual-samples (e.g. R,L,R,L) we want // to check +/- maxSamplingRange frames, not samples. To do this, divide // xVisualSampleIndex by 2. Since frames indices are integers, we round // to the nearest integer by adding 0.5 before casting to int. int visualFrameStart = int(xVisualSampleIndex / 2.0 - maxSamplingRange + 0.5); int visualFrameStop = int(xVisualSampleIndex / 2.0 + maxSamplingRange + 0.5); const int lastVisualFrame = dataSize / 2 - 1; // We now know that some subset of [visualFrameStart, visualFrameStop] // lies within the valid range of visual frames. Clamp // visualFrameStart/Stop to within [0, lastVisualFrame]. visualFrameStart = math_clamp(visualFrameStart, 0, lastVisualFrame); visualFrameStop = math_clamp(visualFrameStop, 0, lastVisualFrame); int visualIndexStart = visualFrameStart * 2; int visualIndexStop = visualFrameStop * 2; unsigned char maxLow = 0; unsigned char maxMid = 0; unsigned char maxHigh = 0; unsigned char maxAllA = 0; unsigned char maxAllB = 0; for (int i = visualIndexStart; i >= 0 && i + 1 < dataSize && i + 1 <= visualIndexStop; i += 2) { const WaveformData& waveformData = *(data + i); const WaveformData& waveformDataNext = *(data + i + 1); maxLow = math_max3(maxLow, waveformData.filtered.low, waveformDataNext.filtered.low); maxMid = math_max3(maxMid, waveformData.filtered.mid, waveformDataNext.filtered.mid); maxHigh = math_max3(maxHigh, waveformData.filtered.high, waveformDataNext.filtered.high); maxAllA = math_max(maxAllA, waveformData.filtered.all); maxAllB = math_max(maxAllB, waveformDataNext.filtered.all); } int red = maxLow * m_lowColor.red() + maxMid * m_midColor.red() + maxHigh * m_highColor.red(); int green = maxLow * m_lowColor.green() + maxMid * m_midColor.green() + maxHigh * m_highColor.green(); int blue = maxLow * m_lowColor.blue() + maxMid * m_midColor.blue() + maxHigh * m_highColor.blue(); // Compute maximum (needed for value normalization) float max = (float) math_max3(red, green, blue); // Prevent division by zero if (max > 0.0f) { // Set color color.setRgbF(red / max, green / max, blue / max); painter->setPen(color); switch (m_alignment) { case Qt::AlignBottom : painter->drawLine( x, m_waveformRenderer->getHeight(), x, m_waveformRenderer->getHeight() - (int)(heightFactor*(float)math_max(maxAllA,maxAllB))); break; case Qt::AlignTop : painter->drawLine( x, 0, x, (int)(heightFactor*(float)math_max(maxAllA,maxAllB))); break; default : painter->drawLine( x, (int)(halfHeight-heightFactor*(float)maxAllA), x, (int)(halfHeight+heightFactor*(float)maxAllB)); } } } painter->restore(); }
bool WOverviewRGB::drawNextPixmapPart() { ScopedTimer t("WOverviewRGB::drawNextPixmapPart"); //qDebug() << "WOverview::drawNextPixmapPart() - m_waveform" << m_waveform; int currentCompletion; ConstWaveformPointer pWaveform = getWaveform(); if (!pWaveform) { return false; } const int dataSize = pWaveform->getDataSize(); if (dataSize == 0) { return false; } if (!m_pWaveformSourceImage) { // Waveform pixmap twice the height of the viewport to be scalable // by total_gain // We keep full range waveform data to scale it on paint m_pWaveformSourceImage = new QImage(dataSize / 2, 2 * 255, QImage::Format_ARGB32_Premultiplied); m_pWaveformSourceImage->fill(QColor(0,0,0,0).value()); } // Always multiple of 2 const int waveformCompletion = pWaveform->getCompletion(); // Test if there is some new to draw (at least of pixel width) const int completionIncrement = waveformCompletion - m_actualCompletion; int visiblePixelIncrement = completionIncrement * width() / dataSize; if (completionIncrement < 2 || visiblePixelIncrement == 0) { return false; } const int nextCompletion = m_actualCompletion + completionIncrement; //qDebug() << "WOverview::drawNextPixmapPart() - nextCompletion:" // << nextCompletion // << "m_actualCompletion:" << m_actualCompletion // << "waveformCompletion:" << waveformCompletion // << "completionIncrement:" << completionIncrement; QPainter painter(m_pWaveformSourceImage); painter.translate(0.0,static_cast<double>(m_pWaveformSourceImage->height())/2.0); QColor color; qreal lowColor_r, lowColor_g, lowColor_b; m_signalColors.getRgbLowColor().getRgbF(&lowColor_r, &lowColor_g, &lowColor_b); qreal midColor_r, midColor_g, midColor_b; m_signalColors.getRgbMidColor().getRgbF(&midColor_r, &midColor_g, &midColor_b); qreal highColor_r, highColor_g, highColor_b; m_signalColors.getRgbHighColor().getRgbF(&highColor_r, &highColor_g, &highColor_b); for (currentCompletion = m_actualCompletion; currentCompletion < nextCompletion; currentCompletion += 2) { unsigned char left = pWaveform->getAll(currentCompletion); unsigned char right = pWaveform->getAll(currentCompletion + 1); // Retrieve "raw" LMH values from waveform qreal low = static_cast<qreal>(pWaveform->getLow(currentCompletion)); qreal mid = static_cast<qreal>(pWaveform->getMid(currentCompletion)); qreal high = static_cast<qreal>(pWaveform->getHigh(currentCompletion)); // Do matrix multiplication qreal red = low * lowColor_r + mid * midColor_r + high * highColor_r; qreal green = low * lowColor_g + mid * midColor_g + high * highColor_g; qreal blue = low * lowColor_b + mid * midColor_b + high * highColor_b; // Normalize and draw qreal max = math_max3(red, green, blue); if (max > 0.0) { color.setRgbF(red / max, green / max, blue / max); painter.setPen(color); painter.drawLine(currentCompletion / 2, -left, currentCompletion / 2, 0); } // Retrieve "raw" LMH values from waveform low = static_cast<qreal>(pWaveform->getLow(currentCompletion + 1)); mid = static_cast<qreal>(pWaveform->getMid(currentCompletion + 1)); high = static_cast<qreal>(pWaveform->getHigh(currentCompletion + 1)); // Do matrix multiplication red = low * lowColor_r + mid * midColor_r + high * highColor_r; green = low * lowColor_g + mid * midColor_g + high * highColor_g; blue = low * lowColor_b + mid * midColor_b + high * highColor_b; // Normalize and draw max = math_max3(red, green, blue); if (max > 0.0) { color.setRgbF(red / max, green / max, blue / max); painter.setPen(color); painter.drawLine(currentCompletion / 2, 0, currentCompletion / 2, right); } } // Evaluate waveform ratio peak for (currentCompletion = m_actualCompletion; currentCompletion < nextCompletion; currentCompletion += 2) { m_waveformPeak = math_max3( m_waveformPeak, static_cast<float>(pWaveform->getAll(currentCompletion)), static_cast<float>(pWaveform->getAll(currentCompletion + 1))); } m_actualCompletion = nextCompletion; m_waveformImageScaled = QImage(); m_diffGain = 0; // Test if the complete waveform is done if (m_actualCompletion >= dataSize - 2) { m_pixmapDone = true; //qDebug() << "m_waveformPeakRatio" << m_waveformPeak; } return true; }
void GLWaveformRendererRGB::draw(QPainter* painter, QPaintEvent* /*event*/) { TrackPointer pTrack = m_waveformRenderer->getTrackInfo(); if (!pTrack) { return; } const Waveform* waveform = pTrack->getWaveform(); if (waveform == NULL) { return; } const int dataSize = waveform->getDataSize(); if (dataSize <= 1) { return; } const WaveformData* data = waveform->data(); if (data == NULL) { return; } double firstVisualIndex = m_waveformRenderer->getFirstDisplayedPosition() * dataSize; double lastVisualIndex = m_waveformRenderer->getLastDisplayedPosition() * dataSize; const int firstIndex = int(firstVisualIndex + 0.5); firstVisualIndex = firstIndex - firstIndex % 2; const int lastIndex = int(lastVisualIndex + 0.5); lastVisualIndex = lastIndex + lastIndex % 2; // Reset device for native painting painter->beginNativePainting(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Per-band gain from the EQ knobs. float allGain(1.0), lowGain(1.0), midGain(1.0), highGain(1.0); getGains(&allGain, &lowGain, &midGain, &highGain); if (m_alignment == Qt::AlignCenter) { glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(firstVisualIndex, lastVisualIndex, -255.0, 255.0, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glScalef(1.0f, allGain, 1.0f); glLineWidth(1.2); glDisable(GL_LINE_SMOOTH); // Draw reference line glBegin(GL_LINES); { glColor4f(m_axesColor_r, m_axesColor_g, m_axesColor_b, m_axesColor_a); glVertex2f(firstVisualIndex, 0); glVertex2f(lastVisualIndex, 0); } glEnd(); glLineWidth(1.2); glEnable(GL_LINE_SMOOTH); glBegin(GL_LINES); { for( int visualIndex = firstVisualIndex; visualIndex < lastVisualIndex; visualIndex += 2) { if( visualIndex < 0) continue; if( visualIndex > dataSize - 1) break; float left_low = lowGain * (float) data[visualIndex].filtered.low; float left_mid = midGain * (float) data[visualIndex].filtered.mid; float left_high = highGain * (float) data[visualIndex].filtered.high; float left_all = sqrtf(left_low * left_low + left_mid * left_mid + left_high * left_high); float left_red = left_low * m_lowColor.red() + left_mid * m_midColor.red() + left_high * m_highColor.red(); float left_green = left_low * m_lowColor.green() + left_mid * m_midColor.green() + left_high * m_highColor.green(); float left_blue = left_low * m_lowColor.blue() + left_mid * m_midColor.blue() + left_high * m_highColor.blue(); float left_max = math_max3(left_red, left_green, left_blue); if (left_max > 0.0f) { // Prevent division by zero glColor4f(left_red / left_max, left_green / left_max, left_blue / left_max, 0.8f); glVertex2f(visualIndex, 0.0f); glVertex2f(visualIndex, left_all); } float right_low = lowGain * (float) data[visualIndex+1].filtered.low; float right_mid = midGain * (float) data[visualIndex+1].filtered.mid; float right_high = highGain * (float) data[visualIndex+1].filtered.high; float right_all = sqrtf(right_low * right_low + right_mid * right_mid + right_high * right_high); float right_red = right_low * m_lowColor.red() + right_mid * m_midColor.red() + right_high * m_highColor.red(); float right_green = right_low * m_lowColor.green() + right_mid * m_midColor.green() + right_high * m_highColor.green(); float right_blue = right_low * m_lowColor.blue() + right_mid * m_midColor.blue() + right_high * m_highColor.blue(); float right_max = math_max3(right_red, right_green, right_blue); if (right_max > 0.0f) { // Prevent division by zero glColor4f(right_red / right_max, right_green / right_max, right_blue / right_max, 0.8f); glVertex2f(visualIndex, 0.0f); glVertex2f(visualIndex, -1.0f * right_all); } } } glEnd(); } else { // top || bottom glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); if( m_alignment == Qt::AlignBottom) glOrtho(firstVisualIndex, lastVisualIndex, 0.0, 255.0, -10.0, 10.0); else glOrtho(firstVisualIndex, lastVisualIndex, 255.0, 0.0, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glScalef(1.0f, allGain, 1.0f); glLineWidth(1.2); glEnable(GL_LINE_SMOOTH); glBegin(GL_LINES); { for( int visualIndex = firstVisualIndex; visualIndex < lastVisualIndex; visualIndex += 2) { if( visualIndex < 0) continue; if( visualIndex > dataSize - 1) break; float low = lowGain * (float) math_max(data[visualIndex].filtered.low, data[visualIndex+1].filtered.low); float mid = midGain * (float) math_max(data[visualIndex].filtered.mid, data[visualIndex+1].filtered.mid); float high = highGain * (float) math_max(data[visualIndex].filtered.high, data[visualIndex+1].filtered.high); float all = sqrtf(low * low + mid * mid + high * high); float red = low * m_lowColor.red() + mid * m_midColor.red() + high * m_highColor.red(); float green = low * m_lowColor.green() + mid * m_midColor.green() + high * m_highColor.green(); float blue = low * m_lowColor.blue() + mid * m_midColor.blue() + high * m_highColor.blue(); float max = math_max3(red, green, blue); if (max > 0.0f) { // Prevent division by zero glColor4f(red / max, green / max, blue / max, 0.9f); glVertex2f(float(visualIndex), 0.0f); glVertex2f(float(visualIndex), all); } } } glEnd(); } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); painter->endNativePainting(); }
bool WOverviewHSV::drawNextPixmapPart() { ScopedTimer t("WOverviewHSV::drawNextPixmapPart"); //qDebug() << "WOverview::drawNextPixmapPart() - m_waveform" << m_waveform; int currentCompletion; ConstWaveformPointer pWaveform = getWaveform(); if (!pWaveform) { return false; } const int dataSize = pWaveform->getDataSize(); if (dataSize == 0) { return false; } if (!m_pWaveformSourceImage) { // Waveform pixmap twice the height of the viewport to be scalable // by total_gain // We keep full range waveform data to scale it on paint m_pWaveformSourceImage = new QImage(dataSize / 2, 2 * 255, QImage::Format_ARGB32_Premultiplied); m_pWaveformSourceImage->fill(QColor(0,0,0,0).value()); } // Always multiple of 2 const int waveformCompletion = pWaveform->getCompletion(); // Test if there is some new to draw (at least of pixel width) const int completionIncrement = waveformCompletion - m_actualCompletion; int visiblePixelIncrement = completionIncrement * width() / dataSize; if (completionIncrement < 2 || visiblePixelIncrement == 0) { return false; } const int nextCompletion = m_actualCompletion + completionIncrement; //qDebug() << "WOverview::drawNextPixmapPart() - nextCompletion:" // << nextCompletion // << "m_actualCompletion:" << m_actualCompletion // << "waveformCompletion:" << waveformCompletion // << "completionIncrement:" << completionIncrement; QPainter painter(m_pWaveformSourceImage); painter.translate(0.0,static_cast<double>(m_pWaveformSourceImage->height())/2.0); // Get HSV of low color. NOTE(rryan): On ARM, qreal is float so it's // important we use qreal here and not double or float or else we will get // build failures on ARM. qreal h, s, v; m_signalColors.getLowColor().getHsvF(&h, &s, &v); QColor color; float lo, hi, total; unsigned char maxLow[2] = {0, 0}; unsigned char maxHigh[2] = {0, 0}; unsigned char maxMid[2] = {0, 0}; unsigned char maxAll[2] = {0, 0}; for (currentCompletion = m_actualCompletion; currentCompletion < nextCompletion; currentCompletion += 2) { maxAll[0] = pWaveform->getAll(currentCompletion); maxAll[1] = pWaveform->getAll(currentCompletion+1); if (maxAll[0] || maxAll[1]) { maxLow[0] = pWaveform->getLow(currentCompletion); maxLow[1] = pWaveform->getLow(currentCompletion+1); maxMid[0] = pWaveform->getMid(currentCompletion); maxMid[1] = pWaveform->getMid(currentCompletion+1); maxHigh[0] = pWaveform->getHigh(currentCompletion); maxHigh[1] = pWaveform->getHigh(currentCompletion+1); total = (maxLow[0] + maxLow[1] + maxMid[0] + maxMid[1] + maxHigh[0] + maxHigh[1]) * 1.2; // Prevent division by zero if (total > 0) { // Normalize low and high // (mid not need, because it not change the color) lo = (maxLow[0] + maxLow[1]) / total; hi = (maxHigh[0] + maxHigh[1]) / total; } else { lo = hi = 0.0; } // Set color color.setHsvF(h, 1.0-hi, 1.0-lo); painter.setPen(color); painter.drawLine(QPoint(currentCompletion / 2, -maxAll[0]), QPoint(currentCompletion / 2, maxAll[1])); } } // Evaluate waveform ratio peak for (currentCompletion = m_actualCompletion; currentCompletion < nextCompletion; currentCompletion += 2) { m_waveformPeak = math_max3( m_waveformPeak, static_cast<float>(pWaveform->getAll(currentCompletion)), static_cast<float>(pWaveform->getAll(currentCompletion + 1))); } m_actualCompletion = nextCompletion; m_waveformImageScaled = QImage(); m_diffGain = 0; // Test if the complete waveform is done if (m_actualCompletion >= dataSize - 2) { m_pixmapDone = true; //qDebug() << "m_waveformPeakRatio" << m_waveformPeak; } return true; }