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