void AbstractScopeWidget::paintEvent(QPaintEvent *) { QPainter davinci(this); davinci.drawImage(m_scopeRect.topLeft(), m_imgBackground); davinci.drawImage(m_scopeRect.topLeft(), m_imgScope); davinci.drawImage(m_scopeRect.topLeft(), m_imgHUD); }
int main() { int width = 640, height = 320; QImage img(width, height, QImage::Format_ARGB32); QPainter davinci(&img); davinci.setPen(QColor(40, 80, 255, 20)); VideoOut_sV video; int ret = prepare(&video, "/tmp/ffmpegEncodedFrames.mov", "ffv1", width, height, 1000000, 1, 25); if (ret != 0) { qDebug() << "Preparing the video failed: " << video.errorMessage << "(" << ret << ")"; return ret; } eatSample(&video); for (int i = 0; i < 100; i++) { img.fill(QColor(100, 100, 100).rgb()); davinci.drawLine(0, 2*i, width-1, 2*i); davinci.fillRect(0, 0, 100, 2*i, QColor(255, 0, 0)); eatARGB(&video, img.bits()); } finish(&video); }
QImage AudioSpectrum::renderHUD(uint) { QTime start = QTime::currentTime(); if (m_innerScopeRect.height() > 0 && m_innerScopeRect.width() > 0) { // May be below 0 if widget is too small // Minimum distance between two lines const uint minDistY = 30; const uint minDistX = 40; const uint textDistX = 10; const uint textDistY = 25; const uint topDist = m_innerScopeRect.top() - m_scopeRect.top(); const uint leftDist = m_innerScopeRect.left() - m_scopeRect.left(); const uint dbDiff = ceil((float)minDistY/m_innerScopeRect.height() * (m_dBmax-m_dBmin)); const int mouseX = m_mousePos.x() - m_innerScopeRect.left(); const int mouseY = m_mousePos.y() - m_innerScopeRect.top(); QImage hud(m_scopeRect.size(), QImage::Format_ARGB32); hud.fill(qRgba(0,0,0,0)); QPainter davinci(&hud); davinci.setPen(AbstractScopeWidget::penLight); int y; for (int db = -dbDiff; db > m_dBmin; db -= dbDiff) { y = topDist + m_innerScopeRect.height() * ((float)db)/(m_dBmin - m_dBmax); if (y-topDist > m_innerScopeRect.height()-minDistY+10) { // Abort here, there is still a line left for min dB to paint which needs some room. break; } davinci.drawLine(leftDist, y, leftDist + m_innerScopeRect.width()-1, y); davinci.drawText(leftDist + m_innerScopeRect.width() + textDistX, y + 6, i18n("%1 dB", m_dBmax + db)); } davinci.drawLine(leftDist, topDist, leftDist + m_innerScopeRect.width()-1, topDist); davinci.drawText(leftDist + m_innerScopeRect.width() + textDistX, topDist+6, i18n("%1 dB", m_dBmax)); davinci.drawLine(leftDist, topDist+m_innerScopeRect.height()-1, leftDist + m_innerScopeRect.width()-1, topDist+m_innerScopeRect.height()-1); davinci.drawText(leftDist + m_innerScopeRect.width() + textDistX, topDist+m_innerScopeRect.height()+6, i18n("%1 dB", m_dBmin)); const uint hzDiff = ceil( ((float)minDistX)/m_innerScopeRect.width() * m_freqMax / 1000 ) * 1000; int x = 0; const int rightBorder = leftDist + m_innerScopeRect.width()-1; y = topDist + m_innerScopeRect.height() + textDistY; for (int hz = 0; x <= rightBorder; hz += hzDiff) { davinci.setPen(AbstractScopeWidget::penLighter); x = leftDist + m_innerScopeRect.width() * ((float)hz)/m_freqMax; if (x <= rightBorder) { davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height()+6); } if (hz < m_freqMax && x+textDistY < leftDist + m_innerScopeRect.width()) { davinci.drawText(x-4, y, QVariant(hz/1000).toString()); } else { x = leftDist + m_innerScopeRect.width(); davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height()+6); davinci.drawText(x-10, y, i18n("%1 kHz", QString("%1").arg((double)m_freqMax/1000, 0, 'f', 1))); } if (hz > 0) { // Draw finer lines between the main lines davinci.setPen(AbstractScopeWidget::penLightDots); for (uint dHz = 3; dHz > 0; dHz--) { x = leftDist + m_innerScopeRect.width() * ((float)hz - dHz * hzDiff/4.0f)/m_freqMax; if (x > rightBorder) { break; } davinci.drawLine(x, topDist, x, topDist + m_innerScopeRect.height()-1); } } } if (m_aTrackMouse->isChecked() && m_mouseWithinWidget && mouseX < m_innerScopeRect.width()-1) { davinci.setPen(AbstractScopeWidget::penThin); x = leftDist + mouseX; float db = 0; float freq = ((float) mouseX)/(m_innerScopeRect.width()-1) * m_freqMax; bool drawDb = false; m_lastFFTLock.acquire(); // We need to test whether the mouse is inside the widget // because the position could already have changed in the meantime (-> crash) if (m_lastFFT.size() > 0 && mouseX >= 0 && mouseX < m_innerScopeRect.width()) { uint right = ((float) m_freqMax)/(m_freq/2) * (m_lastFFT.size() - 1); QVector<float> dbMap = FFTTools::interpolatePeakPreserving(m_lastFFT, m_innerScopeRect.width(), 0, right, -120); db = dbMap[mouseX]; y = topDist + m_innerScopeRect.height()-1 - (dbMap[mouseX] - m_dBmin) / (m_dBmax-m_dBmin) * (m_innerScopeRect.height()-1); if (y < (int)topDist + m_innerScopeRect.height()-1) { drawDb = true; davinci.drawLine(x, y, leftDist + m_innerScopeRect.width()-1, y); } } else { y = topDist + mouseY; } m_lastFFTLock.release(); if (y > (int)topDist + mouseY) { y = topDist+ mouseY; } davinci.drawLine(x, y, x, topDist + m_innerScopeRect.height()-1); if (drawDb) { QPoint dist(20, -20); QRect rect( leftDist + mouseX + dist.x(), topDist + mouseY + dist.y(), 100, 40 ); if (rect.right() > (int)leftDist + m_innerScopeRect.width()-1) { // Mirror the rectangle at the y axis to keep it inside the widget rect = QRect( rect.topLeft() - QPoint(rect.width() + 2*dist.x(), 0), rect.size()); } QRect textRect( rect.topLeft() + QPoint(12, 4), rect.size() ); davinci.fillRect(rect, AbstractScopeWidget::penBackground.brush()); davinci.setPen(AbstractScopeWidget::penLighter); davinci.drawRect(rect); davinci.drawText(textRect, QString( i18n("%1 dB", QString("%1").arg(db, 0, 'f', 2)) + '\n' + i18n("%1 kHz", QString("%1").arg(freq/1000, 0, 'f', 2)))); } } emit signalHUDRenderingFinished(start.elapsed(), 1); return hud; } else { #ifdef DEBUG_AUDIOSPEC qDebug() << "Widget is too small for painting inside. Size of inner scope rect is " << m_innerScopeRect.width() << "x" << m_innerScopeRect.height() <<"."; #endif emit signalHUDRenderingFinished(0, 1); return QImage(); } }
QImage AudioSpectrum::renderAudioScope(uint, const QVector<int16_t> audioFrame, const int freq, const int num_channels, const int num_samples, const int) { if ( audioFrame.size() > 63 && m_innerScopeRect.width() > 0 && m_innerScopeRect.height() > 0 // <= 0 if widget is too small (resized by user) ) { if (!m_customFreq) { m_freqMax = freq / 2; } QTime start = QTime::currentTime(); #ifdef DETECT_OVERMODULATION bool overmodulated = false; int overmodulateCount = 0; for (int i = 0; i < audioFrame.size(); i++) { if ( audioFrame[i] == std::numeric_limits<int16_t>::max() || audioFrame[i] == std::numeric_limits<int16_t>::min()) { overmodulateCount++; if (overmodulateCount > 3) { overmodulated = true; break; } } } if (overmodulated) { colorizeFactor = 1; } else { if (colorizeFactor > 0) { colorizeFactor -= .08; if (colorizeFactor < 0) { colorizeFactor = 0; } } } #endif // Determine the window size to use. It should be // * not bigger than the number of samples actually available // * divisible by 2 int fftWindow = ui->windowSize->itemData(ui->windowSize->currentIndex()).toInt(); if (fftWindow > num_samples) { fftWindow = num_samples; } if ((fftWindow & 1) == 1) { fftWindow--; } // Show the window size used, for information ui->labelFFTSizeNumber->setText(QVariant(fftWindow).toString()); // Get the spectral power distribution of the input samples, // using the given window size and function float freqSpectrum[fftWindow/2]; FFTTools::WindowType windowType = (FFTTools::WindowType) ui->windowFunction->itemData(ui->windowFunction->currentIndex()).toInt(); m_fftTools.fftNormalized(audioFrame, 0, num_channels, freqSpectrum, windowType, fftWindow, 0); // Store the current FFT window (for the HUD) and run the interpolation // for easy pixel-based dB value access QVector<float> dbMap; m_lastFFTLock.acquire(); m_lastFFT = QVector<float>(fftWindow/2); memcpy(m_lastFFT.data(), &(freqSpectrum[0]), fftWindow/2 * sizeof(float)); uint right = ((float) m_freqMax)/(m_freq/2) * (m_lastFFT.size() - 1); dbMap = FFTTools::interpolatePeakPreserving(m_lastFFT, m_innerScopeRect.width(), 0, right, -180); m_lastFFTLock.release(); #ifdef DEBUG_AUDIOSPEC QTime drawTime = QTime::currentTime(); #endif // Draw the spectrum QImage spectrum(m_scopeRect.size(), QImage::Format_ARGB32); spectrum.fill(qRgba(0,0,0,0)); const uint w = m_innerScopeRect.width(); const uint h = m_innerScopeRect.height(); const uint leftDist = m_innerScopeRect.left() - m_scopeRect.left(); const uint topDist = m_innerScopeRect.top() - m_scopeRect.top(); QColor spectrumColor(AbstractScopeWidget::colDarkWhite); int yMax; #ifdef DETECT_OVERMODULATION if (colorizeFactor > 0) { QColor col = AbstractScopeWidget::colHighlightDark; QColor spec = spectrumColor; float f = std::sin(M_PI_2 * colorizeFactor); spectrumColor = QColor( (int) (f * col.red() + (1-f) * spec.red()), (int) (f * col.green() + (1-f) * spec.green()), (int) (f * col.blue() + (1-f) * spec.blue()), spec.alpha() ); // Limit the maximum colorization for non-overmodulated frames to better // recognize consecutively overmodulated frames if (colorizeFactor > MAX_OVM_COLOR) { colorizeFactor = MAX_OVM_COLOR; } } #endif #ifdef AUDIOSPEC_LINES QPainter davinci(&spectrum); davinci.setPen(QPen(QBrush(spectrumColor.rgba()), 1, Qt::SolidLine)); #endif for (uint i = 0; i < w; i++) { yMax = (dbMap[i] - m_dBmin) / (m_dBmax-m_dBmin) * (h-1); if (yMax < 0) { yMax = 0; } else if (yMax >= (int)h) { yMax = h-1; } #ifdef AUDIOSPEC_LINES davinci.drawLine(leftDist + i, topDist + h-1, leftDist + i, topDist + h-1 - yMax); #else for (int y = 0; y < yMax && y < (int)h; y++) { spectrum.setPixel(leftDist + i, topDist + h-y-1, spectrumColor.rgba()); } #endif } // Calculate the peak values. Use the new value if it is bigger, otherwise adapt to lower // values using the Moving Average formula if (m_aShowMax->isChecked()) { davinci.setPen(QPen(QBrush(AbstractScopeWidget::colHighlightLight), 2)); if (m_peaks.size() != fftWindow/2) { m_peaks = QVector<float>(m_lastFFT); } else { for (int i = 0; i < fftWindow/2; i++) { if (m_lastFFT[i] > m_peaks[i]) { m_peaks[i] = m_lastFFT[i]; } else { m_peaks[i] = ALPHA_MOVING_AVG * m_lastFFT[i] + (1-ALPHA_MOVING_AVG) * m_peaks[i]; } } } int prev = 0; m_peakMap = FFTTools::interpolatePeakPreserving(m_peaks, m_innerScopeRect.width(), 0, right, -180); for (uint i = 0; i < w; i++) { yMax = (m_peakMap[i] - m_dBmin) / (m_dBmax-m_dBmin) * (h-1); if (yMax < 0) { yMax = 0; } else if (yMax >= (int)h) { yMax = h-1; } davinci.drawLine(leftDist + i-1, topDist + h-prev-1, leftDist + i, topDist + h-yMax-1); spectrum.setPixel(leftDist + i, topDist + h-yMax-1, AbstractScopeWidget::colHighlightLight.rgba()); prev = yMax; } } #ifdef DEBUG_AUDIOSPEC m_showTotal++; m_timeTotal += drawTime.elapsed(); qDebug() << widgetName() << " took " << drawTime.elapsed() << " ms for drawing. Average: " << ((float)m_timeTotal/m_showTotal) ; #endif emit signalScopeRenderingFinished(start.elapsed(), 1); return spectrum; } else { emit signalScopeRenderingFinished(0, 1); return QImage(); } }
QImage Waveform::renderHUD(uint) { QImage hud(m_scopeRect.size(), QImage::Format_ARGB32); hud.fill(qRgba(0,0,0,0)); QPainter davinci(&hud); davinci.setPen(penLight); QMap< QString, QString > values = ProfilesDialog::getSettingsFromFile(KdenliveSettings::current_profile()); // qDebug() << values.value("width"); const int rightX = scopeRect().width()-m_textWidth.width()+3; const int x = m_mousePos.x() - scopeRect().x(); const int y = m_mousePos.y() - scopeRect().y(); if (scopeRect().height() > 0 && m_mouseWithinWidget) { int val = 255*(1-(float)y/scopeRect().height()); if (val >= 0 && val <= 255) { // Draw a horizontal line through the current mouse position // and show the value of the waveform there davinci.drawLine(0, y, scopeRect().size().width()-m_textWidth.width(), y); // Make the value stick to the line unless it is at the top/bottom of the scope int valY = y+5; const int top = 30; const int bottom = 20; if (valY < top) { valY = top; } else if (valY > scopeRect().height()-bottom) { valY = scopeRect().height()-bottom; } davinci.drawText(rightX, valY, QVariant(val).toString()); } if (scopeRect().width() > 0) { // Draw a vertical line and the x position of the source clip bool ok; const int profileWidth = values.value("width").toInt(&ok); if (ok) { const int clipX = (float)x/(scopeRect().width()-m_textWidth.width()-1)*(profileWidth-1); if (clipX >= 0 && clipX <= profileWidth) { int valX = x-15; if (valX < 0) { valX = 0; } if (valX > scopeRect().width()-55-m_textWidth.width()) { valX = scopeRect().width()-55-m_textWidth.width(); } davinci.drawLine(x, y, x, scopeRect().height()-m_paddingBottom); davinci.drawText(valX, scopeRect().height()-5, QVariant(clipX).toString() + " px"); } } } } davinci.drawText(rightX, scopeRect().height()-m_paddingBottom, "0"); davinci.drawText(rightX, 10, "255"); emit signalHUDRenderingFinished(0, 1); return hud; }
QImage HistogramGenerator::calculateHistogram(const QSize ¶deSize, const QImage &image, const int &components, HistogramGenerator::Rec rec, const bool &unscaled, const uint &accelFactor) const { if (paradeSize.height() <= 0 || paradeSize.width() <= 0 || image.width() <= 0 || image.height() <= 0) { return QImage(); } bool drawY = (components & HistogramGenerator::ComponentY) != 0; bool drawR = (components & HistogramGenerator::ComponentR) != 0; bool drawG = (components & HistogramGenerator::ComponentG) != 0; bool drawB = (components & HistogramGenerator::ComponentB) != 0; bool drawSum = (components & HistogramGenerator::ComponentSum) != 0; int r[256], g[256], b[256], y[256], s[766]; // Initialize the values to zero std::fill(r, r+256, 0); std::fill(g, g+256, 0); std::fill(b, b+256, 0); std::fill(y, y+256, 0); std::fill(s, s+766, 0); const uint iw = image.bytesPerLine(); const uint ih = image.height(); const uint ww = paradeSize.width(); const uint wh = paradeSize.height(); const uint byteCount = iw*ih; const uint stepsize = 4*accelFactor; const uchar *bits = image.bits(); QRgb *col; // Read the stats from the input image for (uint i = 0; i < byteCount; i += stepsize) { col = (QRgb *)bits; r[qRed(*col)]++; g[qGreen(*col)]++; b[qBlue(*col)]++; if (drawY) { // Use if branch to avoid expensive multiplication if Y disabled if (rec == HistogramGenerator::Rec_601) { y[(int)floor(.299*qRed(*col) + .587*qGreen(*col) + .114*qBlue(*col))]++; } else { y[(int)floor(.2125*qRed(*col) + .7154*qGreen(*col) + .0721*qBlue(*col))]++; } } if (drawSum) { // Use an if branch here because the sum takes more operations than rgb s[qRed(*col)]++; s[qGreen(*col)]++; s[qBlue(*col)]++; } bits += stepsize; } const int nParts = (drawY ? 1 : 0) + (drawR ? 1 : 0) + (drawG ? 1 : 0) + (drawB ? 1 : 0) + (drawSum ? 1 : 0); if (nParts == 0) { // Nothing to draw return QImage(); } const int d = 20; // Distance for text const int partH = (wh-nParts*d)/nParts; const float scaling = (float)partH/(byteCount >> 7); const int dist = 40; int wy = 0; // Drawing position QImage histogram(paradeSize, QImage::Format_ARGB32); QPainter davinci(&histogram); davinci.setPen(QColor(220, 220, 220, 255)); histogram.fill(qRgba(0, 0, 0, 0)); if (drawY) { drawComponentFull(&davinci, y, scaling, QRect(0, wy, ww, partH + dist), QColor(220, 220, 210, 255), dist, unscaled, 256); wy += partH + d; } if (drawSum) { drawComponentFull(&davinci, s, scaling/3, QRect(0, wy, ww, partH + dist), QColor(220, 220, 210, 255), dist, unscaled, 256); wy += partH + d; } if (drawR) { drawComponentFull(&davinci, r, scaling, QRect(0, wy, ww, partH + dist), QColor(255, 128, 0, 255), dist, unscaled, 256); wy += partH + d; } if (drawG) { drawComponentFull(&davinci, g, scaling, QRect(0, wy, ww, partH + dist), QColor(128, 255, 0, 255), dist, unscaled, 256); wy += partH + d; } if (drawB) { drawComponentFull(&davinci, b, scaling, QRect(0, wy, ww, partH + dist), QColor(0, 128, 255, 255), dist, unscaled, 256); wy += partH + d; } return histogram; }