File indexing completed on 2024-04-28 04:52:22
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 "histogramgenerator.h" 0009 0010 #include "klocalizedstring.h" 0011 #include <QDebug> 0012 #include <QImage> 0013 #include <QPainter> 0014 #include <algorithm> 0015 #include <cmath> 0016 0017 HistogramGenerator::HistogramGenerator() = default; 0018 0019 QImage HistogramGenerator::calculateHistogram(const QSize ¶deSize, const QImage &image, const int &components, ITURec rec, bool unscaled, bool logScale, 0020 uint accelFactor) const 0021 { 0022 if (paradeSize.height() <= 0 || paradeSize.width() <= 0 || image.width() <= 0 || image.height() <= 0) { 0023 return QImage(); 0024 } 0025 0026 bool drawY = (components & HistogramGenerator::ComponentY) != 0; 0027 bool drawR = (components & HistogramGenerator::ComponentR) != 0; 0028 bool drawG = (components & HistogramGenerator::ComponentG) != 0; 0029 bool drawB = (components & HistogramGenerator::ComponentB) != 0; 0030 bool drawSum = (components & HistogramGenerator::ComponentSum) != 0; 0031 0032 int r[256], g[256], b[256], y[256], s[766]; 0033 // Initialize the values to zero 0034 std::fill(r, r + 256, 0); 0035 std::fill(g, g + 256, 0); 0036 std::fill(b, b + 256, 0); 0037 std::fill(y, y + 256, 0); 0038 std::fill(s, s + 766, 0); 0039 0040 const int ww = paradeSize.width(); 0041 const int wh = paradeSize.height(); 0042 0043 // Read the stats from the input image 0044 for (int Y = 0; Y < image.height(); ++Y) { 0045 for (int X = 0; X < image.width(); X += accelFactor) { 0046 0047 QRgb col = image.pixel(X, Y); 0048 r[qRed(col)]++; 0049 g[qGreen(col)]++; 0050 b[qBlue(col)]++; 0051 0052 if (drawY) { 0053 // Use if branch to avoid expensive multiplication if Y disabled 0054 if (rec == ITURec::Rec_601) { 0055 y[int(REC_601_R * qRed(col) + REC_601_G * qGreen(col) + REC_601_B * qBlue(col))]++; 0056 } else { 0057 y[int(REC_709_R * qRed(col) + REC_709_G * qGreen(col) + REC_709_B * qBlue(col))]++; 0058 } 0059 } 0060 0061 if (drawSum) { 0062 // Use an if branch here because the sum takes more operations than rgb 0063 s[qRed(col)]++; 0064 s[qGreen(col)]++; 0065 s[qBlue(col)]++; 0066 } 0067 } 0068 } 0069 0070 const int nParts = (drawY ? 1 : 0) + (drawR ? 1 : 0) + (drawG ? 1 : 0) + (drawB ? 1 : 0) + (drawSum ? 1 : 0); 0071 if (nParts == 0) { 0072 // Nothing to draw 0073 return QImage(); 0074 } 0075 0076 // Distance for text 0077 const int d = 20; 0078 0079 // Height of a single histogram box without text 0080 const int partH = (wh - nParts * d) / nParts; 0081 0082 // Total number of bytes of the image 0083 const int byteCount = int(image.sizeInBytes()); 0084 0085 // Factor for scaling the measured value to the histogram. 0086 // This factor is used for linear scaling and does not depend 0087 // on the measured histogram values. Very large values, 0088 // e.g. in an image with a lot of white, are clipped. 0089 // Otherwise, the relatively low height of the histogram 0090 // would show all other values close to 0 when one bin is very high. 0091 float scaling = 0; 0092 int div = byteCount >> 7; 0093 if (div > 0) { 0094 scaling = partH / float(byteCount >> 7); 0095 } 0096 const int dist = 40; 0097 0098 QImage histogram(paradeSize, QImage::Format_ARGB32); 0099 QPainter davinci; 0100 bool ok = davinci.begin(&histogram); 0101 if (!ok) { 0102 qDebug() << "Could not initialise QPainter for Histogram."; 0103 return histogram; 0104 } 0105 davinci.setPen(QColor(220, 220, 220, 255)); 0106 histogram.fill(qRgba(0, 0, 0, 0)); 0107 0108 QColor neutralColor(220, 220, 210, 255); 0109 QColor redColor(255, 128, 0, 255); 0110 QColor greenColor(128, 255, 0, 255); 0111 QColor blueColor(0, 128, 255, 255); 0112 0113 int wy = 0; // Drawing position 0114 0115 if (drawY) { 0116 drawComponentFull(&davinci, y, scaling, QRect(0, wy, ww, partH + dist), neutralColor, dist, unscaled, logScale, 256); 0117 wy += partH + d; 0118 } 0119 0120 if (drawSum) { 0121 drawComponentFull(&davinci, s, scaling / 3, QRect(0, wy, ww, partH + dist), neutralColor, dist, unscaled, logScale, 256); 0122 wy += partH + d; 0123 } 0124 0125 if (drawR) { 0126 drawComponentFull(&davinci, r, scaling, QRect(0, wy, ww, partH + dist), redColor, dist, unscaled, logScale, 256); 0127 wy += partH + d; 0128 } 0129 0130 if (drawG) { 0131 drawComponentFull(&davinci, g, scaling, QRect(0, wy, ww, partH + dist), greenColor, dist, unscaled, logScale, 256); 0132 wy += partH + d; 0133 } 0134 0135 if (drawB) { 0136 drawComponentFull(&davinci, b, scaling, QRect(0, wy, ww, partH + dist), blueColor, dist, unscaled, logScale, 256); 0137 } 0138 0139 return histogram; 0140 } 0141 0142 QImage HistogramGenerator::drawComponent(const int *y, const QSize &size, const float &scaling, const QColor &color, bool unscaled, bool logScale, int max) 0143 { 0144 QImage component(max, size.height(), QImage::Format_ARGB32); 0145 component.fill(qRgba(0, 0, 0, 255)); 0146 Q_ASSERT(scaling != INFINITY); 0147 0148 const int partH = size.height(); 0149 0150 const int maxBinSize = *std::max_element(&y[0], &y[max - 1]); 0151 const float logScaling = float(size.height()) / log10f(float(maxBinSize + 1)); 0152 0153 for (int x = 0; x < max; ++x) { 0154 0155 // Calculate the height of the curve at position x 0156 int partY; 0157 if (logScale) { 0158 partY = int(logScaling * log10f(float(y[x] + 1))); 0159 } else { 0160 partY = int(scaling * y[x]); 0161 } 0162 0163 // Invert the y axis 0164 if (partY > partH - 1) { 0165 partY = partH - 1; 0166 } 0167 partY = partH - 1 - partY; 0168 0169 for (int k = partH - 1; k >= partY; --k) { 0170 component.setPixel(x, k, color.rgba()); 0171 } 0172 } 0173 if (unscaled && size.width() >= component.width()) { 0174 return component; 0175 } 0176 return component.scaled(size, Qt::IgnoreAspectRatio, Qt::FastTransformation); 0177 } 0178 0179 void HistogramGenerator::drawComponentFull(QPainter *davinci, const int *y, const float &scaling, const QRect &rect, const QColor &color, int textSpace, 0180 bool unscaled, bool logScale, int max) 0181 { 0182 QImage component = drawComponent(y, rect.size() - QSize(0, textSpace), scaling, color, unscaled, logScale, max); 0183 davinci->drawImage(rect.topLeft(), component); 0184 0185 int min = 0; 0186 for (int x = 0; x < max; ++x) { 0187 min = x; 0188 if (y[x] > 0) { 0189 break; 0190 } 0191 } 0192 int maxVal = max - 1; 0193 for (int x = max - 1; x >= 0; --x) { 0194 maxVal = x; 0195 if (y[x] > 0) { 0196 break; 0197 } 0198 } 0199 0200 const int textY = rect.bottom() - textSpace + 15; 0201 const int dist = 40; 0202 const int cw = component.width(); 0203 0204 davinci->drawText(0, textY, i18n("min")); 0205 davinci->drawText(dist, textY, QString::number(min, 'f', 0)); 0206 0207 davinci->drawText(cw - dist - 30, textY, i18n("max")); 0208 davinci->drawText(cw - 30, textY, QString::number(maxVal, 'f', 0)); 0209 }