File indexing completed on 2024-04-28 04:52:23
0001 /* 0002 SPDX-FileCopyrightText: 2010 Simon Andreas Eugster <simon.eu@gmail.com> 0003 This file is part of kdenlive. See www.kdenlive.org. 0004 0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "waveformgenerator.h" 0009 0010 #include <cmath> 0011 0012 #include <QDebug> 0013 #include <QElapsedTimer> 0014 #include <QImage> 0015 #include <QPainter> 0016 #include <QSize> 0017 #include <vector> 0018 0019 #define CHOP255(a) int((255) < (a) ? (255) : (a)) 0020 0021 WaveformGenerator::WaveformGenerator() = default; 0022 0023 WaveformGenerator::~WaveformGenerator() = default; 0024 0025 QImage WaveformGenerator::calculateWaveform(const QSize &waveformSize, const QImage &image, WaveformGenerator::PaintMode paintMode, bool drawAxis, ITURec rec, 0026 uint accelFactor) 0027 { 0028 Q_ASSERT(accelFactor >= 1); 0029 0030 // QTime time; 0031 // time.start(); 0032 0033 QImage wave(waveformSize, QImage::Format_ARGB32); 0034 0035 if (waveformSize.width() <= 0 || waveformSize.height() <= 0 || image.width() <= 0 || image.height() <= 0) { 0036 return QImage(); 0037 } 0038 0039 // Fill with transparent color 0040 wave.fill(qRgba(0, 0, 0, 0)); 0041 0042 const uint ww = uint(waveformSize.width()); 0043 const uint wh = uint(waveformSize.height()); 0044 const uint iw = uint(image.width()); 0045 const auto totalPixels = image.width() * image.height(); 0046 0047 std::vector<std::vector<uint>> waveValues(size_t(waveformSize.width()), std::vector<uint>(size_t(waveformSize.height()), 0)); 0048 0049 // Number of input pixels that will fall on one scope pixel. 0050 // Must be a float because the acceleration factor can be high, leading to <1 expected px per px. 0051 const float pixelDepth = float(totalPixels / accelFactor) / (ww * wh); 0052 const float gain = 255.f / (8 * pixelDepth); 0053 // qCDebug(KDENLIVE_LOG) << "Pixel depth: expected " << pixelDepth << "; Gain: using " << gain << " (acceleration: " << accelFactor << "x)"; 0054 0055 // Subtract 1 from sizes because we start counting from 0. 0056 // Not doing it would result in attempts to paint outside of the image. 0057 const float hPrediv = (wh - 1) / 255.f; 0058 const float wPrediv = (ww - 1) / float(iw - 1); 0059 0060 for (int i = 0; i < totalPixels; i += accelFactor) { 0061 const int x = i % image.width(); 0062 const QRgb pixel = image.pixel(x, i / image.width()); 0063 0064 float dY, dx, dy; 0065 0066 if (rec == ITURec::Rec_601) { 0067 // CIE 601 Luminance 0068 dY = REC_601_R * qRed(pixel) + REC_601_G * qGreen(pixel) + REC_601_B * qBlue(pixel); 0069 } else { 0070 // CIE 709 Luminance 0071 dY = REC_709_R * qRed(pixel) + REC_709_G * qGreen(pixel) + REC_709_B * qBlue(pixel); 0072 } 0073 // dY is on [0,255] now. 0074 0075 dy = dY * hPrediv; 0076 dx = x * wPrediv; 0077 waveValues[size_t(dx)][size_t(dy)]++; 0078 } 0079 0080 switch (paintMode) { 0081 case PaintMode_Green: 0082 for (int i = 0; i < waveformSize.width(); ++i) { 0083 for (int j = 0; j < waveformSize.height(); ++j) { 0084 // Logarithmic scale. Needs fine tuning by hand, but looks great. 0085 wave.setPixel(i, waveformSize.height() - j - 1, 0086 qRgba(CHOP255(52 * logf(0.1f * gain * float(waveValues[size_t(i)][size_t(j)]))), 0087 CHOP255(52 * logf(gain * float(waveValues[size_t(i)][size_t(j)]))), 0088 CHOP255(52 * logf(.25f * gain * float(waveValues[size_t(i)][size_t(j)]))), 0089 CHOP255(64 * logf(gain * float(waveValues[size_t(i)][size_t(j)]))))); 0090 } 0091 } 0092 break; 0093 case PaintMode_Yellow: 0094 for (int i = 0; i < waveformSize.width(); ++i) { 0095 for (int j = 0; j < waveformSize.height(); ++j) { 0096 wave.setPixel(i, waveformSize.height() - j - 1, qRgba(255, 242, 0, CHOP255(gain * float(waveValues[size_t(i)][size_t(j)])))); 0097 } 0098 } 0099 break; 0100 default: 0101 for (int i = 0; i < waveformSize.width(); ++i) { 0102 for (int j = 0; j < waveformSize.height(); ++j) { 0103 wave.setPixel(i, waveformSize.height() - j - 1, qRgba(255, 255, 255, CHOP255(2.f * gain * float(waveValues[size_t(i)][size_t(j)])))); 0104 } 0105 } 0106 break; 0107 } 0108 0109 if (drawAxis) { 0110 QPainter davinci; 0111 bool ok = davinci.begin(&wave); 0112 if (!ok) { 0113 qDebug() << "Could not initialise QPainter for Waveform."; 0114 return wave; 0115 } 0116 0117 QRgb opx; 0118 davinci.setPen(qRgba(150, 255, 200, 32)); 0119 davinci.setCompositionMode(QPainter::CompositionMode_Overlay); 0120 for (int i = 0; i <= 10; ++i) { 0121 int dy = int(i / 10.f * (wh - 1)); 0122 for (int x = 0; x < int(ww); ++x) { 0123 opx = wave.pixel(x, dy); 0124 wave.setPixel(x, dy, qRgba(CHOP255(150 + qRed(opx)), 255, CHOP255(200 + qBlue(opx)), CHOP255(32 + qAlpha(opx)))); 0125 } 0126 } 0127 } 0128 0129 // uint diff = time.elapsed(); 0130 // Q_EMIT signalCalculationFinished(wave, diff); 0131 0132 return wave; 0133 } 0134 #undef CHOP255