8#include "gammaoption.h"
10#include <QVarLengthArray>
12#include <ksanecore_debug.h>
19GammaOption::GammaOption(
const SANE_Handle handle,
const int index)
20 : BaseOption(handle, index)
22 m_optionType = Option::TypeGamma;
25bool GammaOption::setValue(
const QVariant &value)
27 if (state() == Option::StateHidden) {
39 if (gammaValues.
size() != 3) {
42 brightness = gammaValues.
at(0).toInt(&ok);
44 contrast = gammaValues.
at(1).toInt(&ok);
47 gamma = gammaValues.
at(2).toInt(&ok);
50 if (ok && (m_brightness != brightness || m_contrast != contrast || m_gamma != gamma) ) {
51 m_brightness = brightness;
52 m_contrast = contrast;
54 calculateGTwriteData();
63 if (m_brightness !=
copy.at(0).toInt() || m_contrast !=
copy.at(1).toInt() || m_gamma !=
copy.at(2).toInt() ) {
64 m_brightness =
copy.at(0).toInt();
65 m_contrast =
copy.at(1).toInt();
66 m_gamma =
copy.at(2).toInt();
67 calculateGTwriteData();
74void GammaOption::readValue()
76 if (state() == Option::StateHidden) {
83 status = sane_control_option(m_handle, m_index, SANE_ACTION_GET_VALUE, data.data(), &res);
84 if (
status != SANE_STATUS_GOOD) {
89 gammaTable.
reserve(data.size() /
sizeof(
int));
90 for (
int i = 0; i < data.size(); i +=
sizeof(SANE_Word)) gammaTable.
append(toSANE_Word(&data[i]));
92 if (gammaTable != m_gammaTable) {
93 m_gammaTable = gammaTable;
95 m_gammaTableMax = m_optDesc->constraint.range->max;
97 calculateBCGwriteData();
103 if (state() == Option::StateHidden) {
106 return QVariantList{ m_brightness, m_contrast, m_gamma };
109int GammaOption::valueSize()
const
114QVariant GammaOption::maximumValue()
const
118 value =
static_cast<float>(m_optDesc->constraint.range->max);
124QString GammaOption::valueAsString()
const
126 if (state() == Option::StateHidden) {
133void GammaOption::calculateGTwriteData()
135 double maxValue = m_optDesc->constraint.range->max;
136 double gamma = 100.0 / m_gamma;
137 double contrast = (200.0 / (100.0 - m_contrast)) - 1;
138 double halfMax = maxValue / 2.0;
140 double brightness = m_brightness * maxValue / 100.0;
143 for (
int i = 0; i < m_gammaTable.size(); i++) {
145 x = std::pow(
static_cast<double>(i) / m_gammaTable.size(), gamma) * maxValue;
148 x = (contrast * (x - halfMax)) + halfMax;
151 x += brightness + 0.5;
161 m_gammaTable[i] =
static_cast<int>(x);
164 writeData(m_gammaTable.data());
165 QVariantList values = { m_brightness, m_contrast, m_gamma };
166 Q_EMIT valueChanged(values);
169void GammaOption::calculateBCGwriteData() {
171 int endIndex = m_gammaTable.size() - 1;
173 while (beginIndex < endIndex && m_gammaTable[beginIndex] == m_gammaTable[0])
175 while (endIndex > beginIndex && m_gammaTable[endIndex] == m_gammaTable[m_gammaTable.size()-1])
178 float gamma = 0, contrast = 0, brightness = 0;
180 const int &gammaTableMax = m_gammaTableMax;
182 auto guessGamma = [&gammaTable, &gamma](
int i1,
int i2,
int step) {
183 int diff1 = gammaTable[i1 + step] - gammaTable[i1 - step];
184 int diff2 = gammaTable[i2 + step] - gammaTable[i2 - step];
185 if (diff1 == 0 || diff2 == 0)
187 float stepProportion =
static_cast<float>(i2) / i1;
188 float diffProportion =
static_cast<float>(diff2) / diff1;
189 float guessedGamma = log(stepProportion * diffProportion) / log(stepProportion);
190 gamma += guessedGamma;
193 auto guessContrast = [&gammaTable, &gammaTableMax, &gamma, &contrast](
int i1,
int i2, int) {
194 int prevVal = gammaTable[i1], nextVal = gammaTable[i2];
195 float scaledDiff =
static_cast<float>(nextVal - prevVal) / gammaTableMax;
196 float scaledPrevIndex =
static_cast<float>(i1) / gammaTable.size();
197 float scaledNextIndex =
static_cast<float>(i2) / gammaTable.size();
198 float guessedContrast = scaledDiff / (pow(scaledNextIndex, gamma) - pow(scaledPrevIndex, gamma));
199 contrast += guessedContrast;
202 auto guessBrightness = [&gammaTable, &gammaTableMax, &gamma, &contrast, &brightness](
int i, int, int) {
203 float scaledThisVal =
static_cast<float>(gammaTable[i]) / gammaTableMax;
204 float scaledIndex =
static_cast<float>(i) / gammaTable.size();
205 float guessedBrightness = scaledThisVal - ((pow(scaledIndex, gamma) - 0.5) * contrast + 0.5);
206 brightness += guessedBrightness;
209 const int numberOfApproximations = 16;
211 auto passValuePairsAndSteps = [&beginIndex, &endIndex](
auto func) {
212 const int step = (endIndex - beginIndex) / 8;
213 for (
int i = 0; i < numberOfApproximations;) {
215 int i1 = rand() % (endIndex - beginIndex - 2 * step - 2) + beginIndex + step + 1;
216 int i2 = rand() % (endIndex - beginIndex - 2 * step - 2) + beginIndex + step + 1;
217 if (i2 - i1 >= 4 * step) {
224 if (endIndex == beginIndex) {
225 qCDebug(KSANECORE_LOG()) <<
"Ignoring gamma table: horizontal line at" << m_gammaTable[0];
226 setValue(QVariantList{0, 0, 100});
230 if (endIndex - beginIndex <= 32) {
231 if (endIndex - beginIndex > 4) {
232 guessGamma(beginIndex + 2, endIndex - 2, 2);
236 guessContrast(beginIndex, endIndex, 0);
237 guessBrightness((beginIndex + endIndex) / 2, 0, 0);
239 passValuePairsAndSteps(guessGamma);
240 gamma /= numberOfApproximations;
242 passValuePairsAndSteps(guessContrast);
243 contrast /= numberOfApproximations;
245 passValuePairsAndSteps(guessBrightness);
246 brightness /= numberOfApproximations;
249 int newGamma = 100.0 / gamma;
250 int newContrast = 100.0 - 200.0 / (contrast + 1.0);
251 int newBrightness = brightness * 100.0;
253 if (m_gamma != newGamma || m_contrast != newContrast || m_brightness != newBrightness) {
255 m_contrast = newContrast;
256 m_brightness = newBrightness;
258 QVariantList values = { m_brightness, m_contrast, m_gamma };
259 Q_EMIT valueChanged(values);
265#include "moc_gammaoption.cpp"
Q_SCRIPTABLE CaptureState status()
const QList< QKeySequence > & copy()
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
void reserve(qsizetype size)
qsizetype size() const const
QString asprintf(const char *cformat,...)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool canConvert() const const
QList< QVariant > toList() const const
QString toString() const const
int userType() const const