1 /*
2  *  Copyright (c) 2017 Victor Wåhlström <victor.wahlstrom@initiali.se>
3  *
4  *  This library is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU Lesser General Public License as published by
6  *  the Free Software Foundation; either version 2.1 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include "kis_wdg_options_heightmap.h"
21 
22 #include <QtMath>
23 #include <QToolTip>
24 
25 #include <kis_assert.h>
26 #include <kis_paint_layer.h>
27 
nextPow2(quint32 n)28 static constexpr quint32 nextPow2(quint32 n)
29 {
30     return ((((((n - 1) | n >> 1) | n >> 2) | n >> 4) | n >> 8) | n >> 16) + 1 + (quint32)(n == 0);
31 }
32 
prevPow2(quint32 n)33 static constexpr quint32 prevPow2(quint32 n)
34 {
35     return nextPow2(n) >> 1;
36 }
37 
showErrorTooltip(const QString & msg)38 static void showErrorTooltip(const QString &msg)
39 {
40     QToolTip::showText(QCursor::pos(), i18n("Error: ") + msg);
41 }
42 
KisWdgOptionsHeightmap(QWidget * parent)43 KisWdgOptionsHeightmap::KisWdgOptionsHeightmap(QWidget *parent)
44     : KisWdgOptionsHeightmap(parent, false)
45 {
46 }
47 
KisWdgOptionsHeightmap(QWidget * parent,bool export_mode)48 KisWdgOptionsHeightmap::KisWdgOptionsHeightmap(QWidget *parent, bool export_mode)
49     : KisConfigWidget(parent)
50     , m_exportMode(export_mode)
51 {
52     setupUi(this);
53 
54     if (m_exportMode) {
55         dimensionsGroupBox->setVisible(false);
56         fileSizeDescLabel->setVisible(false);
57         fileSizeLabel->setVisible(false);
58         bppDescLabel->setVisible(false);
59         bppLabel->setVisible(false);
60     }
61     else {
62         connect(guessButton, SIGNAL(clicked(bool)), this, SLOT(guessDimensions()));
63         connect(widthInput, SIGNAL(valueChanged(int)), this, SLOT(widthChanged(int)));
64         connect(heightInput, SIGNAL(valueChanged(int)), this, SLOT(heightChanged(int)));
65     }
66 }
67 
setConfiguration(const KisPropertiesConfigurationSP cfg)68 void KisWdgOptionsHeightmap::setConfiguration(const KisPropertiesConfigurationSP cfg)
69 {
70     int endianness = cfg->getInt("endianness", 1);
71     if (endianness == 0) {
72         radioBig->setChecked(true);
73     }
74     else {
75         radioLittle->setChecked(true);
76     }
77 }
78 
configuration() const79 KisPropertiesConfigurationSP KisWdgOptionsHeightmap::configuration() const
80 {
81     KisPropertiesConfigurationSP cfg = new KisPropertiesConfiguration();
82     if (radioBig->isChecked()) {
83         cfg->setProperty("endianness", 0);
84     }
85     else {
86         cfg->setProperty("endianness", 1);
87     }
88     return cfg;
89 }
90 
showEvent(QShowEvent * event)91 void KisWdgOptionsHeightmap::showEvent(QShowEvent *event)
92 {
93     updateStatus();
94     QWidget::showEvent(event);
95 }
96 
updateStatus()97 void KisWdgOptionsHeightmap::updateStatus()
98 {
99     if (m_exportMode)
100         return;
101     bool ok;
102     int fileSize = fileSizeLabel->text().toInt(&ok);
103     KIS_ASSERT_RECOVER_RETURN(ok);
104     KIS_ASSERT_RECOVER_RETURN(fileSize > 0);
105     int w = widthInput->value();
106     int h = heightInput->value();
107 
108     quint32 depth = bppLabel->text().toUInt(&ok);
109     KIS_ASSERT_RECOVER_RETURN(ok);
110 
111     QString old_status = statusLabel->text();
112 
113     int bytesPerPixel = depth / 8;
114     int dataSize = w * h * bytesPerPixel;
115 
116     bool status_ok = false;
117 
118     QString fileMismatch = i18n("Input does not match file size");
119     if (w == 0 && h == 0) {
120         statusLabel->setText(i18n("Please specify width and height"));
121     }
122     else if (w == 0) {
123         statusLabel->setText(i18n("Please specify width"));
124     }
125     else if (h == 0) {
126         statusLabel->setText(i18n("Please specify height"));
127     }
128     else if (dataSize != fileSize) {
129         statusLabel->setText(fileMismatch);
130     }
131     else {
132         statusLabel->setText("");
133         status_ok = true;
134     }
135 
136     if (old_status.compare(statusLabel->text()) != 0) {
137         emit statusUpdated(status_ok);
138     }
139 }
140 
widthChanged(int i)141 void KisWdgOptionsHeightmap::widthChanged(int i)
142 {
143     Q_UNUSED(i);
144     updateStatus();
145 }
146 
heightChanged(int i)147 void KisWdgOptionsHeightmap::heightChanged(int i)
148 {
149     Q_UNUSED(i);
150     updateStatus();
151 }
152 
guessDimensions()153 void KisWdgOptionsHeightmap::guessDimensions()
154 {
155     quint32 w = widthInput->value();
156     quint32 h = heightInput->value();
157 
158     bool ok;
159     quint32 fileSize = fileSizeLabel->text().toUInt(&ok);
160     KIS_ASSERT_RECOVER_RETURN(ok);
161 
162     quint32 depth = bppLabel->text().toUInt(&ok);
163     KIS_ASSERT_RECOVER_RETURN(ok);
164 
165     quint32 bytesPerPixel = depth / 8;
166 
167     w = widthInput->text().toUInt(&ok);
168     KIS_ASSERT_RECOVER_RETURN(ok);
169 
170     h = heightInput->text().toUInt(&ok);
171     KIS_ASSERT_RECOVER_RETURN(ok);
172 
173     quint32 dimensions = fileSize / bytesPerPixel;
174 
175     if (w > 0 && h > 0) {
176         if (w * h == dimensions) {
177             // toggle landscape/portrait orientation
178             widthInput->setValue(h);
179             heightInput->setValue(w);
180         }
181     }
182     else if (w == 0 && h == 0) {
183         quint32 r = (quint32)(qFloor(qSqrt(dimensions) + 0.5));
184 
185         // First guess, square image?
186         if (r*r == dimensions) {
187             widthInput->setValue(r);
188             heightInput->setValue(r);
189         }
190         else {
191             // second guess, power of two?
192             w = prevPow2(r);
193             h = dimensions / w + (dimensions % w);
194             if (w * h != dimensions) {
195                 showErrorTooltip(i18n("Too many possible combinations. Input a width or height and try again."));
196                 return;
197             }
198 
199             // prefer landscape orientation
200             widthInput->setValue(w > h ? w : h);
201             heightInput->setValue(w > h ? h : w);
202 
203             // TODO: cycle through other pow2 combinations if called multiple times?
204         }
205     }
206     else if (w > 0) {
207         if (w > dimensions) {
208             showErrorTooltip(i18n("Width exceeds available pixels."));
209             return;
210         }
211         h = dimensions / w + (dimensions % w);
212         if (w * h != dimensions) {
213             showErrorTooltip(i18n("Unable to calculate an appropriate height. File does not contain enough pixels to form a rectangle."));
214             return;
215         }
216         heightInput->setValue(h);
217     }
218     else {
219         if (h > dimensions) {
220             showErrorTooltip(i18n("Height exceeds available pixels."));
221             return;
222         }
223         w = dimensions / h + (dimensions % h);
224         if (w * h != dimensions) {
225             showErrorTooltip(i18n("Unable to calculate an appropriate width. File does not contain enough pixels to form a rectangle."));
226             return;
227         }
228         widthInput->setValue(w);
229     }
230 }
231