1 /****************************************************************************
2 * MeshLab                                                           o o     *
3 * An extendible mesh processor                                    o     o   *
4 *                                                                _   O  _   *
5 * Copyright(C) 2005, 2009                                          \/)\/    *
6 * Visual Computing Lab                                            /\/|      *
7 * ISTI - Italian National Research Council                           |      *
8 *                                                                    \      *
9 * All rights reserved.                                                      *
10 *                                                                           *
11 * This program is free software; you can redistribute it and/or modify      *
12 * it under the terms of the GNU General Public License as published by      *
13 * the Free Software Foundation; either version 2 of the License, or         *
14 * (at your option) any later version.                                       *
15 *                                                                           *
16 * This program is distributed in the hope that it will be useful,           *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
20 * for more details.                                                         *
21 *                                                                           *
22 ****************************************************************************/
23 
24 #include "maskImageWidget.h"
25 #include "maskRenderWidget.h"
26 #include "fillImage.h"
27 #include <QtGui/QPen>
28 #include <QtGui/QBrush>
29 #include <QtGui/QPolygon>
30 #include <QtGui/QPixmap>
31 #include <QtGui/QImage>
32 #include <QtGui/QPainter>
33 #include <QtGui/QPalette>
34 #include <QtGui/QMouseEvent>
35 #include <QtGui/QPaintEvent>
36 #include <QtGui/QApplication>
37 #include <QtGui/QMessageBox>
38 #include <QtGui/QAction>
39 #include <QtGui/QVBoxLayout>
40 #include <QtGui/QToolBar>
41 #include <QtGui/QSpinBox>
42 #include <QtGui/QFileDialog>
43 #include <QtGui/QDesktopWidget>
44 
45 #include <stack>
46 #include <cmath>
47 #include <sstream>
48 #include <cassert>
49 #include <iostream>
50 
51 #ifdef WIN32
52 #undef min
53 #undef max
54 #endif
55 
56 
57 namespace ui
58 {
59 
60   struct maskImageWidget::Impl
61   {
62 		enum DrawMode { Pen, Eraser } mode_;
63 
64 		maskRenderWidget *render_area_;
65 		int threshold_gradient_, threshold_fixed_;
66 		int realwidth_, realheight_;
67     Impl();
68   };
69 
70 
Impl()71   maskImageWidget::Impl::Impl()
72   {
73 		mode_ = Pen;
74 		threshold_gradient_ = 100;
75 		threshold_fixed_ = 30;
76   }
77 
78 
maskImageWidget(const QImage & image,QWidget * parent)79   maskImageWidget::maskImageWidget(const QImage& image, QWidget *parent) : QDialog(parent), pimpl_(new Impl)
80   {
81 		init(image);
82   }
83 
84 
~maskImageWidget()85   maskImageWidget::~maskImageWidget() throw()
86   {
87 		delete pimpl_;
88   }
89 
getMask() const90 	QImage maskImageWidget::getMask() const
91 	{
92 		return pimpl_->render_area_->getMask(pimpl_->realwidth_, pimpl_->realheight_);
93 	}
94 
loadMask(const QString & filename)95 	void maskImageWidget::loadMask(const QString& filename)
96 	{
97 		pimpl_->render_area_->load(filename);
98 	}
99 
init(const QImage & image)100 	void maskImageWidget::init(const QImage& image)
101 	{
102 		setWindowTitle(tr("Mask Editor"));
103 
104 		QPixmap load("coral_open32x32.png");
105 		QPixmap save("coral_save32x32.png");
106 		QPixmap undo("coral_undo32x32.png");
107 		QPixmap redo("coral_redo32x32.png");
108 		QPixmap pen("coral_pencil32x32.png");
109 		QPixmap eraser("coral_eraser32x32.png");
110 
111 		QAction *canvasloadmask = new QAction(this);
112 		canvasloadmask->setIcon(load);
113 		canvasloadmask->setText(tr("&Load Mask"));
114 		QAction *canvassavemask = new QAction(this);
115 		canvassavemask->setIcon(QIcon(save));
116 		canvassavemask->setText(tr("&Save Mask"));
117 		QAction *canvasundo = new QAction(this);
118 		canvasundo->setIcon(QIcon(undo));
119 		canvasundo->setText(tr("&Undo"));
120 		canvasundo->setShortcut(QKeySequence("Ctrl+Z"));
121 		QAction *canvasredo = new QAction(this);
122 		canvasredo->setIcon(QIcon(redo));
123 		canvasredo->setText(tr("&Redo"));
124 		canvasredo->setShortcut(QKeySequence("Ctrl+Shift+Z"));
125 		QAction *canvasclear = new QAction(tr("&Clear"), this);
126 		canvasclear->setShortcut(QKeySequence("Ctrl+C"));
127 
128 		QAction *canvaspen = new QAction(this);
129 		canvaspen->setIcon(QIcon(pen));
130 		canvaspen->setText(tr("&Pen"));
131 		QAction *canvaseraser = new QAction(this);
132 		canvaseraser->setIcon(QIcon(eraser));
133 		canvaseraser->setText(tr("&Eraser"));
134 
135 		QActionGroup *actions(new QActionGroup(this));
136 		actions->addAction(canvaspen);
137 		actions->addAction(canvaseraser);
138 		canvaspen->setCheckable(true);
139 		canvaseraser->setCheckable(true);
140 		canvaspen->setChecked(true);
141 		actions->setExclusive(true);
142 
143 		QAction *canvasOK = new QAction(this);
144 		canvasOK->setText("OK");
145 		QAction *canvasCancel = new QAction(this);
146 		canvasCancel->setText("Cancel");
147 
148 		QBoxLayout *layout(new QVBoxLayout(this));
149 
150 		// We don't want a real-size image. We will downscale it!
151 		QImage image_to_use = image;
152 		pimpl_->realwidth_ = image.width();
153 		pimpl_->realheight_ = image.height();
154 		qDebug("maskImageWidget::Init real wxh %i x%i",pimpl_->realwidth_,pimpl_->realheight_);
155 		QDesktopWidget *desktop(QApplication::desktop());
156 		if (image.width() > (desktop->width() * .8) ||
157 				image.height() > (desktop->height() * .8))
158 		{
159 			int width(desktop->width()), height(desktop->height());
160 			image_to_use = image.scaled((int)std::floor(width * .75),
161 																	(int)std::floor(height * .75), Qt::KeepAspectRatio);
162 		}
163 		pimpl_->render_area_ = new maskRenderWidget(image_to_use, this);
164 
165 		QToolBar *canvas_toolbar(new QToolBar(this));
166 		canvas_toolbar->addSeparator();
167 		canvas_toolbar->addAction(canvasloadmask);
168 		canvas_toolbar->addAction(canvassavemask);
169 		canvas_toolbar->addSeparator();
170 
171 		canvas_toolbar->addAction(canvasundo);
172 		canvas_toolbar->addAction(canvasredo);
173 		canvas_toolbar->addSeparator();
174 
175 		QSpinBox *pen_width(new QSpinBox(canvas_toolbar));
176 		pen_width->setToolTip(tr("Pen Width"));
177 		pen_width->setRange(0, 80);
178 		pen_width->setSingleStep(2);
179 		pen_width->setValue(16);
180 		connect(pen_width, SIGNAL(valueChanged(int)), SLOT(setCanvasPenWidth(int)));
181 		canvas_toolbar->addWidget(pen_width);
182 		canvas_toolbar->addAction(canvaspen);
183 		canvas_toolbar->addAction(canvaseraser);
184 		canvas_toolbar->addSeparator();
185 
186 		QSpinBox *gradient(new QSpinBox(canvas_toolbar));
187 		gradient->setToolTip("Gradient Threshold");
188 		gradient->setRange(0, 255);
189 		gradient->setValue(pimpl_->threshold_gradient_);
190 		connect(gradient, SIGNAL(valueChanged(int)), SLOT(setGradientThreshold(int)));
191 
192 		QSpinBox *fixed(new QSpinBox(canvas_toolbar));
193 		fixed->setToolTip("Fixed Threshold");
194 		fixed->setRange(0, 255);
195 		fixed->setValue(pimpl_->threshold_fixed_);
196 		connect(fixed, SIGNAL(valueChanged(int)), SLOT(setFixedThreshold(int)));
197 
198 		canvas_toolbar->addWidget(gradient);
199 		canvas_toolbar->addWidget(fixed);
200 		canvas_toolbar->addSeparator();
201 
202 		canvas_toolbar->addAction(canvasOK);
203 		canvas_toolbar->addAction(canvasCancel);
204 
205 		layout->addWidget(canvas_toolbar);
206 		layout->addWidget(pimpl_->render_area_);
207 		layout->setSizeConstraint(QLayout::SetFixedSize);
208 
209 		connect(canvasloadmask, SIGNAL(activated()), SLOT(loadMask()));
210 		connect(canvassavemask, SIGNAL(activated()), SLOT(saveMask()));
211 		connect(canvasundo, SIGNAL(activated()), pimpl_->render_area_, SLOT(undo()));
212 		connect(canvasredo, SIGNAL(activated()), pimpl_->render_area_, SLOT(redo()));
213 		connect(canvasclear, SIGNAL(activated()), pimpl_->render_area_, SLOT(clear()));
214 		connect(canvaspen, SIGNAL(activated()), SLOT(setCanvasPen()));
215 		connect(canvaseraser, SIGNAL(activated()), SLOT(setCanvasEraser()));
216 
217 		connect(pimpl_->render_area_, SIGNAL(pointSelected(const QPoint &)), SLOT(automaticMask(const QPoint &)));
218 
219 		connect(canvasOK, SIGNAL(activated()), SLOT(accept()));
220 		connect(canvasCancel, SIGNAL(activated()), SLOT(reject()));
221 	}
222 
setCanvasPenWidth(int width)223 	void maskImageWidget::setCanvasPenWidth(int width)
224 	{
225 		QPen pen(pimpl_->render_area_->pen());
226 		pen.setWidth(width);
227 		pimpl_->render_area_->setPen(pen);
228 	}
229 
230 
setCanvasPen()231 	void maskImageWidget::setCanvasPen()
232 	{
233 		QPen pen(pimpl_->render_area_->pen());
234 		pen.setColor(QColor(Qt::black));
235 		pen.setJoinStyle(Qt::RoundJoin);
236 		pimpl_->render_area_->setPen(pen);
237 	}
238 
239 
setCanvasEraser()240 	void maskImageWidget::setCanvasEraser()
241 	{
242 		QPen pen(pimpl_->render_area_->pen());
243 		pen.setColor(QColor(Qt::transparent));
244 		pen.setJoinStyle(Qt::RoundJoin);
245 		pimpl_->render_area_->setPen(pen);
246 	}
247 
248 
setGradientThreshold(int threshold_gradient)249 	void maskImageWidget::setGradientThreshold(int threshold_gradient)
250 	{
251 		pimpl_->threshold_gradient_ = threshold_gradient;
252 	}
253 
254 
setFixedThreshold(int threshold_fixed)255 	void maskImageWidget::setFixedThreshold(int threshold_fixed)
256 	{
257 		pimpl_->threshold_fixed_ = threshold_fixed;
258 	}
259 
loadMask()260 	void maskImageWidget::loadMask()
261 	{
262 		try
263 		{
264 			QString filename(QFileDialog::getOpenFileName(this, QString("Open mask file"), QString(), QString("*.png")));
265 			if (QString::null != filename)
266 				pimpl_->render_area_->load(filename);
267 		}
268 		catch (std::exception &e)
269 		{
270 			QMessageBox::warning(this, tr("Problem"), e.what());
271 		}
272 	}
273 
274 	namespace
275 	{
check_extension(QString & filename,const QString & ext)276 		bool check_extension(QString &filename, const QString &ext)
277 		{
278 			bool ret(false);
279 			if (ext != filename.section('.', -1))
280 			{
281 				int index(filename.lastIndexOf('.'));
282 				if (-1 == index)
283 				{
284 					filename += '.';
285 					index += filename.size();
286 				}
287 				filename.replace(index + 1, ext.size(), ext);
288 				filename.resize(index + 1 + ext.size());
289 				ret = true;
290 			}
291 			return ret;
292 		}
293 	};
294 
saveMask()295 	void maskImageWidget::saveMask()
296 	{
297 		try
298 		{
299 			QString filename(QFileDialog::getSaveFileName(this, QString("Save mask file"), QString(), QString("*.png")));
300 			if (QString::null != filename)
301 			{
302 				check_extension(filename, QString("png"));
303 				pimpl_->render_area_->save(filename, pimpl_->realwidth_, pimpl_->realheight_);
304 			}
305 		}
306 		catch (std::exception &e)
307 		{
308 			QMessageBox::warning(this, tr("Epoch 3D Webservice"), e.what());
309 		}
310 	}
311 
312 
automaticMask(const QPoint & p)313 	void maskImageWidget::automaticMask(const QPoint &p)
314 	{
315 		QImage image = (pimpl_->render_area_->palette().base().texture()).toImage();
316 		QImage out;
317 		fillImage fi;
318 		fi.Compute(image, p.x(), p.y(), pimpl_->threshold_gradient_, pimpl_->threshold_fixed_, out);
319 
320 		const size_t width(image.width()), height(image.height());
321 		QImage temp(pimpl_->render_area_->alphaMask());
322 		for (size_t i = 0; i < width; ++i)
323 		{
324 			for (size_t j = 0; j < height; ++j)
325 			{
326 				if (out.pixelIndex(i, j) > 0)
327 					temp.setPixel(i, j, QColor(Qt::black).rgba());
328 			}
329 		}
330 		//temp.save("temp.jpg","jpg");
331 		pimpl_->render_area_->setAlphaMask(temp);
332 	}
333 };
334