2020-10-21 17:24:01 +02:00

334 lines
10 KiB
C++

/****************************************************************************
* MeshLab o o *
* An extendible mesh processor o o *
* _ O _ *
* Copyright(C) 2005, 2009 \/)\/ *
* Visual Computing Lab /\/| *
* ISTI - Italian National Research Council | *
* \ *
* All rights reserved. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
* for more details. *
* *
****************************************************************************/
#include "maskImageWidget.h"
#include "maskRenderWidget.h"
#include "fillImage.h"
#include <QPen>
#include <QBrush>
#include <QPolygon>
#include <QPixmap>
#include <QImage>
#include <QPainter>
#include <QPalette>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QApplication>
#include <QMessageBox>
#include <QAction>
#include <QVBoxLayout>
#include <QToolBar>
#include <QSpinBox>
#include <QFileDialog>
#include <QDesktopWidget>
#include <stack>
#include <cmath>
#include <sstream>
#include <cassert>
#include <iostream>
#ifdef WIN32
#undef min
#undef max
#endif
namespace ui
{
struct maskImageWidget::Impl
{
enum DrawMode { Pen, Eraser } mode_;
maskRenderWidget *render_area_;
int threshold_gradient_, threshold_fixed_;
int realwidth_, realheight_;
Impl();
};
maskImageWidget::Impl::Impl()
{
mode_ = Pen;
threshold_gradient_ = 100;
threshold_fixed_ = 30;
}
maskImageWidget::maskImageWidget(const QImage& image, QWidget *parent) : QDialog(parent), pimpl_(new Impl)
{
init(image);
}
maskImageWidget::~maskImageWidget() throw()
{
delete pimpl_;
}
QImage maskImageWidget::getMask() const
{
return pimpl_->render_area_->getMask(pimpl_->realwidth_, pimpl_->realheight_);
}
void maskImageWidget::loadMask(const QString& filename)
{
pimpl_->render_area_->load(filename);
}
void maskImageWidget::init(const QImage& image)
{
setWindowTitle(tr("Mask Editor"));
QPixmap load("coral_open32x32.png");
QPixmap save("coral_save32x32.png");
QPixmap undo("coral_undo32x32.png");
QPixmap redo("coral_redo32x32.png");
QPixmap pen("coral_pencil32x32.png");
QPixmap eraser("coral_eraser32x32.png");
QAction *canvasloadmask = new QAction(this);
canvasloadmask->setIcon(load);
canvasloadmask->setText(tr("&Load Mask"));
QAction *canvassavemask = new QAction(this);
canvassavemask->setIcon(QIcon(save));
canvassavemask->setText(tr("&Save Mask"));
QAction *canvasundo = new QAction(this);
canvasundo->setIcon(QIcon(undo));
canvasundo->setText(tr("&Undo"));
canvasundo->setShortcut(QKeySequence("Ctrl+Z"));
QAction *canvasredo = new QAction(this);
canvasredo->setIcon(QIcon(redo));
canvasredo->setText(tr("&Redo"));
canvasredo->setShortcut(QKeySequence("Ctrl+Shift+Z"));
QAction *canvasclear = new QAction(tr("&Clear"), this);
canvasclear->setShortcut(QKeySequence("Ctrl+C"));
QAction *canvaspen = new QAction(this);
canvaspen->setIcon(QIcon(pen));
canvaspen->setText(tr("&Pen"));
QAction *canvaseraser = new QAction(this);
canvaseraser->setIcon(QIcon(eraser));
canvaseraser->setText(tr("&Eraser"));
QActionGroup *actions(new QActionGroup(this));
actions->addAction(canvaspen);
actions->addAction(canvaseraser);
canvaspen->setCheckable(true);
canvaseraser->setCheckable(true);
canvaspen->setChecked(true);
actions->setExclusive(true);
QAction *canvasOK = new QAction(this);
canvasOK->setText("OK");
QAction *canvasCancel = new QAction(this);
canvasCancel->setText("Cancel");
QBoxLayout *layout(new QVBoxLayout(this));
// We don't want a real-size image. We will downscale it!
QImage image_to_use = image;
pimpl_->realwidth_ = image.width();
pimpl_->realheight_ = image.height();
qDebug("maskImageWidget::Init real wxh %i x%i",pimpl_->realwidth_,pimpl_->realheight_);
QDesktopWidget *desktop(QApplication::desktop());
if (image.width() > (desktop->width() * .8) ||
image.height() > (desktop->height() * .8))
{
int width(desktop->width()), height(desktop->height());
image_to_use = image.scaled((int)std::floor(width * .75),
(int)std::floor(height * .75), Qt::KeepAspectRatio);
}
pimpl_->render_area_ = new maskRenderWidget(image_to_use, this);
QToolBar *canvas_toolbar(new QToolBar(this));
canvas_toolbar->addSeparator();
canvas_toolbar->addAction(canvasloadmask);
canvas_toolbar->addAction(canvassavemask);
canvas_toolbar->addSeparator();
canvas_toolbar->addAction(canvasundo);
canvas_toolbar->addAction(canvasredo);
canvas_toolbar->addSeparator();
QSpinBox *pen_width(new QSpinBox(canvas_toolbar));
pen_width->setToolTip(tr("Pen Width"));
pen_width->setRange(0, 80);
pen_width->setSingleStep(2);
pen_width->setValue(16);
connect(pen_width, SIGNAL(valueChanged(int)), SLOT(setCanvasPenWidth(int)));
canvas_toolbar->addWidget(pen_width);
canvas_toolbar->addAction(canvaspen);
canvas_toolbar->addAction(canvaseraser);
canvas_toolbar->addSeparator();
QSpinBox *gradient(new QSpinBox(canvas_toolbar));
gradient->setToolTip("Gradient Threshold");
gradient->setRange(0, 255);
gradient->setValue(pimpl_->threshold_gradient_);
connect(gradient, SIGNAL(valueChanged(int)), SLOT(setGradientThreshold(int)));
QSpinBox *fixed(new QSpinBox(canvas_toolbar));
fixed->setToolTip("Fixed Threshold");
fixed->setRange(0, 255);
fixed->setValue(pimpl_->threshold_fixed_);
connect(fixed, SIGNAL(valueChanged(int)), SLOT(setFixedThreshold(int)));
canvas_toolbar->addWidget(gradient);
canvas_toolbar->addWidget(fixed);
canvas_toolbar->addSeparator();
canvas_toolbar->addAction(canvasOK);
canvas_toolbar->addAction(canvasCancel);
layout->addWidget(canvas_toolbar);
layout->addWidget(pimpl_->render_area_);
layout->setSizeConstraint(QLayout::SetFixedSize);
connect(canvasloadmask, SIGNAL(activated()), SLOT(loadMask()));
connect(canvassavemask, SIGNAL(activated()), SLOT(saveMask()));
connect(canvasundo, SIGNAL(activated()), pimpl_->render_area_, SLOT(undo()));
connect(canvasredo, SIGNAL(activated()), pimpl_->render_area_, SLOT(redo()));
connect(canvasclear, SIGNAL(activated()), pimpl_->render_area_, SLOT(clear()));
connect(canvaspen, SIGNAL(activated()), SLOT(setCanvasPen()));
connect(canvaseraser, SIGNAL(activated()), SLOT(setCanvasEraser()));
connect(pimpl_->render_area_, SIGNAL(pointSelected(const QPoint &)), SLOT(automaticMask(const QPoint &)));
connect(canvasOK, SIGNAL(activated()), SLOT(accept()));
connect(canvasCancel, SIGNAL(activated()), SLOT(reject()));
}
void maskImageWidget::setCanvasPenWidth(int width)
{
QPen pen(pimpl_->render_area_->pen());
pen.setWidth(width);
pimpl_->render_area_->setPen(pen);
}
void maskImageWidget::setCanvasPen()
{
QPen pen(pimpl_->render_area_->pen());
pen.setColor(QColor(Qt::black));
pen.setJoinStyle(Qt::RoundJoin);
pimpl_->render_area_->setPen(pen);
}
void maskImageWidget::setCanvasEraser()
{
QPen pen(pimpl_->render_area_->pen());
pen.setColor(QColor(Qt::transparent));
pen.setJoinStyle(Qt::RoundJoin);
pimpl_->render_area_->setPen(pen);
}
void maskImageWidget::setGradientThreshold(int threshold_gradient)
{
pimpl_->threshold_gradient_ = threshold_gradient;
}
void maskImageWidget::setFixedThreshold(int threshold_fixed)
{
pimpl_->threshold_fixed_ = threshold_fixed;
}
void maskImageWidget::loadMask()
{
try
{
QString filename(QFileDialog::getOpenFileName(this, QString("Open mask file"), QString(), QString("*.png")));
if (QString::null != filename)
pimpl_->render_area_->load(filename);
}
catch (std::exception &e)
{
QMessageBox::warning(this, tr("Problem"), e.what());
}
}
namespace
{
bool check_extension(QString &filename, const QString &ext)
{
bool ret(false);
if (ext != filename.section('.', -1))
{
int index(filename.lastIndexOf('.'));
if (-1 == index)
{
filename += '.';
index += filename.size();
}
filename.replace(index + 1, ext.size(), ext);
filename.resize(index + 1 + ext.size());
ret = true;
}
return ret;
}
};
void maskImageWidget::saveMask()
{
try
{
QString filename(QFileDialog::getSaveFileName(this, QString("Save mask file"), QString(), QString("*.png")));
if (QString::null != filename)
{
check_extension(filename, QString("png"));
pimpl_->render_area_->save(filename, pimpl_->realwidth_, pimpl_->realheight_);
}
}
catch (std::exception &e)
{
QMessageBox::warning(this, tr("Epoch 3D Webservice"), e.what());
}
}
void maskImageWidget::automaticMask(const QPoint &p)
{
QImage image = (pimpl_->render_area_->palette().base().texture()).toImage();
QImage out;
fillImage fi;
fi.Compute(image, p.x(), p.y(), pimpl_->threshold_gradient_, pimpl_->threshold_fixed_, out);
const size_t width(image.width()), height(image.height());
QImage temp(pimpl_->render_area_->alphaMask());
for (size_t i = 0; i < width; ++i)
{
for (size_t j = 0; j < height; ++j)
{
if (out.pixelIndex(i, j) > 0)
temp.setPixel(i, j, QColor(Qt::black).rgba());
}
}
//temp.save("temp.jpg","jpg");
pimpl_->render_area_->setAlphaMask(temp);
}
};