You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
410 lines
13 KiB
410 lines
13 KiB
/*
|
|
* kis_previewwidget.cc - part of Krita
|
|
*
|
|
* Copyright (c) 2001 John Califf <jwcaliff@compuzone.net>
|
|
* Copyright (c) 2004 Bart Coppens <kde@bartcoppens.be>
|
|
* Copyright (c) 2005 Cyrille Berger <cberger@cberger.net>
|
|
* Copyright (c) 2007 Ben Schleimer <bensch128@yahoo.com>
|
|
*
|
|
* 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 for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <qcheckbox.h>
|
|
#include <qradiobutton.h>
|
|
#include <qpainter.h>
|
|
#include <qpoint.h>
|
|
#include <qpushbutton.h>
|
|
#include <qlayout.h>
|
|
#include <qlabel.h>
|
|
#include <qapplication.h>
|
|
#include <qcolor.h>
|
|
#include <qgroupbox.h>
|
|
#include <qcursor.h>
|
|
#include <qtimer.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <kiconloader.h>
|
|
#include <kpushbutton.h>
|
|
|
|
#include <kis_cursor.h>
|
|
#include <kis_colorspace.h>
|
|
#include <kis_colorspace_factory_registry.h>
|
|
#include <kis_config.h>
|
|
#include <kis_filter_strategy.h>
|
|
#include <kis_global.h>
|
|
#include <kis_image.h>
|
|
#include <kis_layer.h>
|
|
#include <kis_paint_layer.h>
|
|
#include <kis_group_layer.h>
|
|
#include <kis_meta_registry.h>
|
|
#include <kis_painter.h>
|
|
#include <kis_profile.h>
|
|
#include <kis_types.h>
|
|
#include <kis_undo_adapter.h>
|
|
#include <kis_label_progress.h>
|
|
#include <kis_selection.h>
|
|
#include <kis_transform_worker.h>
|
|
|
|
#include "kis_previewwidgetbase.h"
|
|
#include "kis_previewwidget.h"
|
|
#include "imageviewer.h"
|
|
|
|
static const int ZOOM_PAUSE = 100;
|
|
static const int FILTER_PAUSE = 500;
|
|
static const double ZOOM_FACTOR = 1.1;
|
|
|
|
KisPreviewWidget::KisPreviewWidget( QWidget* parent, const char* name )
|
|
: PreviewWidgetBase( parent, name )
|
|
, m_autoupdate(true)
|
|
, m_previewIsDisplayed(true)
|
|
, m_scaledOriginal()
|
|
, m_dirtyOriginal(true)
|
|
, m_origDevice(new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "temp"))
|
|
, m_scaledPreview()
|
|
, m_dirtyPreview(true)
|
|
, m_previewDevice(new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "temp"))
|
|
, m_scaledImage(NULL)
|
|
, m_filterZoom(1.0)
|
|
, m_zoom(-1.0)
|
|
, m_profile(NULL)
|
|
, m_progress( 0 )
|
|
, m_zoomTimer(new QTimer(this))
|
|
, m_filterTimer(new QTimer(this))
|
|
, m_firstFilter(true)
|
|
, m_firstZoom(true)
|
|
{
|
|
btnZoomIn->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "viewmag+", KIcon::MainToolbar, 16 ));
|
|
connect(btnZoomIn, SIGNAL(clicked()), this, SLOT(zoomIn()));
|
|
btnZoomOut->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "viewmag-", KIcon::MainToolbar, 16 ));
|
|
connect(btnZoomOut, SIGNAL(clicked()), this, SLOT(zoomOut()));
|
|
btnUpdate->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "reload", KIcon::MainToolbar, 16 ));
|
|
connect(btnUpdate, SIGNAL(clicked()), this, SLOT(forceUpdate()));
|
|
|
|
connect(radioBtnPreview, SIGNAL(toggled(bool)), this, SLOT(setPreviewDisplayed(bool)));
|
|
|
|
connect(checkBoxAutoUpdate, SIGNAL(toggled(bool)), this, SLOT(slotSetAutoUpdate(bool)));
|
|
btnZoomOneToOne->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "viewmag1", KIcon::MainToolbar, 16 ));
|
|
connect(btnZoomOneToOne, SIGNAL(clicked()), this, SLOT(zoomOneToOne()));
|
|
|
|
m_progress = new KisLabelProgress(frmProgress);
|
|
m_progress->setMaximumHeight(fontMetrics().height() );
|
|
QVBoxLayout *vbox = new QVBoxLayout( frmProgress );
|
|
vbox->addWidget(m_progress);
|
|
m_progress->hide();
|
|
|
|
connect(m_zoomTimer, SIGNAL(timeout()), this, SLOT(updateZoom()));
|
|
connect(m_filterTimer, SIGNAL(timeout()), this, SLOT(runFilterHelper()));
|
|
|
|
/* kToolBar1->insertLineSeparator();
|
|
kToolBar1->insertButton("reload",2, true, i18n("Update"));
|
|
connect(kToolBar1->getButton(2),SIGNAL(clicked()),this,SLOT(forceUpdate()));
|
|
|
|
kToolBar1->insertButton("",3, true, i18n("Auto Update"));
|
|
connect(kToolBar1->getButton(3),SIGNAL(clicked()),this,SLOT(toggleAutoUpdate()));
|
|
|
|
kToolBar1->insertButton("",4, true, i18n("Switch"));
|
|
connect(kToolBar1->getButton(4),SIGNAL(clicked()),this,SLOT(toggleImageDisplayed()));*/
|
|
// these currently don't yet work, reenable when they do work :) (TZ-12-2005)
|
|
// TODO reenable these
|
|
// kToolBar1->insertButton("",5, true, i18n("Popup Original and Preview"));
|
|
}
|
|
|
|
KisPreviewWidget::~KisPreviewWidget() { }
|
|
|
|
void KisPreviewWidget::forceUpdate()
|
|
{
|
|
if(m_previewIsDisplayed)
|
|
{
|
|
m_groupBox->setTitle(m_origDevice->name());
|
|
emit updated();
|
|
}
|
|
}
|
|
|
|
void KisPreviewWidget::slotSetDevice(KisPaintDeviceSP dev)
|
|
{
|
|
Q_ASSERT( dev );
|
|
|
|
if (!dev) return;
|
|
|
|
m_origDevice = dev;
|
|
m_previewDevice = dev;
|
|
m_filterZoom = 1.0;
|
|
|
|
KisConfig cfg;
|
|
QString monitorProfileName = cfg.monitorProfile();
|
|
m_profile = KisMetaRegistry::instance()->csRegistry()->getProfileByName(monitorProfileName);
|
|
|
|
QRect r = dev->exactBounds();
|
|
|
|
m_groupBox->setTitle(i18n("Preview: ") + dev->name());
|
|
m_previewIsDisplayed = true;
|
|
|
|
m_zoom = -1.0;
|
|
zoomChanged(double(m_preview->width()) / double(r.width()) );
|
|
}
|
|
|
|
void KisPreviewWidget::updateZoom()
|
|
{
|
|
QApplication::setOverrideCursor(KisCursor::waitCursor());
|
|
|
|
if(m_previewIsDisplayed)
|
|
{
|
|
if(m_dirtyPreview)
|
|
{
|
|
QSize r = m_previewDevice->extent().size();
|
|
int w = r.width(), h = r.height();
|
|
int sw = int(ceil(m_zoom * w / m_filterZoom));
|
|
int sh = int(ceil(m_zoom * h / m_filterZoom));
|
|
m_dirtyPreview = false;
|
|
m_scaledPreview = m_previewDevice->convertToQImage(m_profile, 0, 0, w, h);
|
|
m_scaledPreview = m_scaledPreview.scale(sw,sh, QImage::ScaleMax); // Use scale instead of smoothScale for speed up
|
|
}
|
|
m_preview->setImage(m_scaledPreview);
|
|
} else
|
|
{
|
|
if(m_dirtyOriginal)
|
|
{
|
|
QSize r = m_origDevice->extent().size();
|
|
int w = r.width(), h = r.height();
|
|
int sw = int(ceil(m_zoom * w));
|
|
int sh = int(ceil(m_zoom * h));
|
|
m_dirtyOriginal = false;
|
|
m_scaledOriginal = m_origDevice->convertToQImage(m_profile, 0, 0, w, h);
|
|
m_scaledOriginal = m_scaledOriginal.scale(sw,sh, QImage::ScaleMax); // Use scale instead of smoothScale for speed up
|
|
}
|
|
m_preview->setImage(m_scaledOriginal);
|
|
}
|
|
|
|
QApplication::restoreOverrideCursor();
|
|
}
|
|
|
|
void KisPreviewWidget::slotSetAutoUpdate(bool set) {
|
|
m_autoupdate = set;
|
|
}
|
|
|
|
void KisPreviewWidget::wheelEvent(QWheelEvent * e)
|
|
{
|
|
if (e->delta() > 0) {
|
|
zoomIn();
|
|
} else {
|
|
zoomOut();
|
|
}
|
|
e->accept();
|
|
}
|
|
|
|
void KisPreviewWidget::setPreviewDisplayed(bool v)
|
|
{
|
|
if(v != m_previewIsDisplayed)
|
|
{
|
|
m_previewIsDisplayed = v;
|
|
if(m_previewIsDisplayed) {
|
|
m_groupBox->setTitle(i18n("Preview: ") + m_origDevice->name());
|
|
} else {
|
|
m_groupBox->setTitle(i18n("Original: ") + m_origDevice->name());
|
|
}
|
|
// Call directly without any pause because there is no scaling
|
|
updateZoom();
|
|
}
|
|
}
|
|
|
|
void KisPreviewWidget::needUpdate()
|
|
{
|
|
if(m_previewIsDisplayed)
|
|
m_groupBox->setTitle(i18n("Preview (needs update)"));
|
|
}
|
|
|
|
bool KisPreviewWidget::getAutoUpdate() const {
|
|
return m_autoupdate;
|
|
}
|
|
|
|
void KisPreviewWidget::zoomChanged(const double zoom)
|
|
{
|
|
// constrain the zoom
|
|
double tZoom = zoom;
|
|
if(zoom <= 1./8.) { tZoom = 1./8.; }
|
|
if(zoom > 8.) { tZoom = 8.; }
|
|
|
|
if(tZoom != m_zoom)
|
|
{
|
|
m_zoom = tZoom;
|
|
m_dirtyOriginal = true;
|
|
m_dirtyPreview = true;
|
|
|
|
if(m_firstZoom) {
|
|
m_firstZoom = false;
|
|
updateZoom();
|
|
} else {
|
|
m_zoomTimer->start(ZOOM_PAUSE, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void KisPreviewWidget::zoomIn() {
|
|
zoomChanged(m_zoom * ZOOM_FACTOR);
|
|
}
|
|
|
|
void KisPreviewWidget::zoomOut() {
|
|
zoomChanged(m_zoom / ZOOM_FACTOR);
|
|
}
|
|
|
|
void KisPreviewWidget::zoomOneToOne() {
|
|
zoomChanged(1.0);
|
|
}
|
|
|
|
static inline void cropDevice(KisPaintDevice * device, const double & zoom) {
|
|
QRect r = device->exactBounds();
|
|
r.setX(int(zoom * r.x()) );
|
|
r.setY(int(zoom * r.y()) );
|
|
r.setWidth(int(zoom * r.width()) );
|
|
r.setHeight(int(zoom * r.height()) );
|
|
device->crop(r);
|
|
}
|
|
|
|
class MyCropVisitor : public KisLayerVisitor {
|
|
const double m_zoom;
|
|
|
|
public:
|
|
MyCropVisitor(const double & z) : m_zoom(z) { }
|
|
virtual ~MyCropVisitor() { }
|
|
|
|
virtual bool visit(KisPaintLayer *layer) {
|
|
KisPaintDeviceSP device = layer->paintDevice();
|
|
::cropDevice(device.data(), m_zoom);
|
|
// Make sure we have a tight fit for the selection
|
|
if(device->hasSelection()) {
|
|
::cropDevice(device->selection().data(), m_zoom);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
virtual bool visit(KisGroupLayer *layer) {
|
|
for(KisLayerSP l = layer->firstChild(); l; l = l->nextSibling()) {
|
|
l->accept(*this);
|
|
}
|
|
return true;
|
|
}
|
|
virtual bool visit(KisPartLayer *) { return true; }
|
|
virtual bool visit(KisAdjustmentLayer *) { return true; }
|
|
};
|
|
|
|
void KisPreviewWidget::runFilter(KisFilter * filter, KisFilterConfiguration * config) {
|
|
if(!filter) return;
|
|
if(!config) return;
|
|
|
|
m_filter = filter;
|
|
m_config = config;
|
|
|
|
if(m_firstFilter) {
|
|
m_firstFilter = false;
|
|
runFilterHelper();
|
|
} else {
|
|
m_filterTimer->start(FILTER_PAUSE, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* XXX: Fix the situations which m_origDevice is NOT associated with a image.
|
|
* If it comes from a adjustment layer or projection or thumbnail. Currently, nothing happens
|
|
*/
|
|
void KisPreviewWidget::runFilterHelper() {
|
|
|
|
m_filterZoom = m_zoom;
|
|
// Dont scale more then 1.0 so we don't waste time in preview widget for large scaling.
|
|
if(m_filterZoom > 1.0) {
|
|
m_filterZoom = 1.0;
|
|
}
|
|
|
|
KisPaintDeviceSP scaledDevice;
|
|
KisHermiteFilterStrategy strategy;
|
|
|
|
// Copy the image and scale
|
|
if (m_origDevice->image())
|
|
{
|
|
m_scaledImage = new KisImage(*m_origDevice->image());
|
|
if(!m_origDevice->parentLayer()) return;
|
|
QString layerName = m_origDevice->parentLayer()->name();
|
|
KisPaintLayerSP pl = ::qt_cast<KisPaintLayer*>(m_scaledImage->findLayer(layerName));
|
|
if(!pl) return;
|
|
scaledDevice = pl->paintDevice();
|
|
|
|
KisSelectionSP select;
|
|
if(scaledDevice->hasSelection())
|
|
{
|
|
select = new KisSelection(*scaledDevice->selection());
|
|
scaledDevice->deselect();
|
|
}
|
|
// Scale
|
|
m_scaledImage->setUndoAdapter(NULL);
|
|
m_scaledImage->scale(m_filterZoom, m_filterZoom, NULL, &strategy);
|
|
// Scale the selection
|
|
if(select)
|
|
{
|
|
KisPaintDeviceSP t = select.data();
|
|
KisTransformWorker tw(t, m_filterZoom, m_filterZoom,
|
|
0.0, 0.0, 0.0, 0, 0, NULL, &strategy);
|
|
tw.run();
|
|
scaledDevice->setSelection(select);
|
|
select->setParentLayer(scaledDevice->parentLayer());
|
|
}
|
|
|
|
// Crop by the zoom value instead of cropping by rectangle. It gives better results
|
|
MyCropVisitor v(m_filterZoom);
|
|
m_scaledImage->rootLayer()->accept(v);
|
|
} else
|
|
{
|
|
scaledDevice = new KisPaintDevice(*m_origDevice);
|
|
KisSelectionSP select;
|
|
if(scaledDevice->hasSelection())
|
|
{
|
|
select = new KisSelection(*scaledDevice->selection());
|
|
scaledDevice->deselect();
|
|
}
|
|
KisTransformWorker tw(scaledDevice, m_filterZoom, m_filterZoom,
|
|
0.0, 0.0, 0.0, 0, 0, NULL, &strategy);
|
|
tw.run();
|
|
// Scale the selection
|
|
if(select)
|
|
{
|
|
KisPaintDeviceSP t = select.data();
|
|
KisTransformWorker tw(t, m_filterZoom, m_filterZoom,
|
|
0.0, 0.0, 0.0, 0, 0, NULL, &strategy);
|
|
tw.run();
|
|
scaledDevice->setSelection(select);
|
|
::cropDevice(select.data(), m_filterZoom);
|
|
}
|
|
::cropDevice(scaledDevice.data(), m_filterZoom);
|
|
}
|
|
|
|
m_previewDevice = new KisPaintDevice(*scaledDevice);
|
|
|
|
// Setup the progress display
|
|
m_filter->enableProgress();
|
|
m_progress->setSubject(m_filter, true, true);
|
|
m_filter->setProgressDisplay(m_progress);
|
|
m_filter->process(scaledDevice, m_previewDevice, m_config, scaledDevice->exactBounds());
|
|
m_filter->disableProgress();
|
|
|
|
m_dirtyPreview = true;
|
|
|
|
if(m_firstZoom) {
|
|
m_firstZoom = false;
|
|
updateZoom();
|
|
} else {
|
|
m_zoomTimer->start(ZOOM_PAUSE, true);
|
|
}
|
|
}
|
|
|
|
#include "kis_previewwidget.moc"
|