/* * kis_previewwidget.cc - part of Krita * * Copyright (c) 2001 John Califf * Copyright (c) 2004 Bart Coppens * Copyright (c) 2005 Cyrille Berger * Copyright (c) 2007 Ben Schleimer * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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(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"