krita

kis_previewwidget.cc

00001 /*
00002  *  kis_previewwidget.cc - part of Krita
00003  *
00004  *  Copyright (c) 2001 John Califf  <jwcaliff@compuzone.net>
00005  *  Copyright (c) 2004 Bart Coppens <kde@bartcoppens.be>
00006  *  Copyright (c) 2005 Cyrille Berger <cberger@cberger.net>
00007  *  Copyright (c) 2007 Ben Schleimer <bensch128@yahoo.com>
00008  *
00009  *  This program is free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with this program; if not, write to the Free Software
00021  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00022  */
00023 
00024 #include <qcheckbox.h>
00025 #include <qradiobutton.h>
00026 #include <qpainter.h>
00027 #include <qpoint.h>
00028 #include <qpushbutton.h>
00029 #include <qlayout.h>
00030 #include <qlabel.h>
00031 #include <qapplication.h>
00032 #include <qcolor.h>
00033 #include <qgroupbox.h>
00034 #include <qcursor.h>
00035 #include <qtimer.h>
00036 
00037 #include <kdebug.h>
00038 #include <kiconloader.h>
00039 #include <kpushbutton.h>
00040 
00041 #include <kis_cursor.h>
00042 #include <kis_colorspace.h>
00043 #include <kis_colorspace_factory_registry.h>
00044 #include <kis_config.h>
00045 #include <kis_filter_strategy.h>
00046 #include <kis_global.h>
00047 #include <kis_image.h>
00048 #include <kis_layer.h>
00049 #include <kis_paint_layer.h>
00050 #include <kis_group_layer.h>
00051 #include <kis_meta_registry.h>
00052 #include <kis_painter.h>
00053 #include <kis_profile.h>
00054 #include <kis_types.h>
00055 #include <kis_undo_adapter.h>
00056 #include <kis_label_progress.h>
00057 #include <kis_selection.h>
00058 #include <kis_transform_worker.h>
00059 
00060 #include "kis_previewwidgetbase.h"
00061 #include "kis_previewwidget.h"
00062 #include "imageviewer.h"
00063 
00064 static const int ZOOM_PAUSE = 100;
00065 static const int FILTER_PAUSE = 500;
00066 static const double ZOOM_FACTOR = 1.1;
00067 
00068 KisPreviewWidget::KisPreviewWidget( QWidget* parent, const char* name )
00069     : PreviewWidgetBase( parent, name )
00070     , m_autoupdate(true)
00071     , m_previewIsDisplayed(true)
00072     , m_scaledOriginal()
00073     , m_dirtyOriginal(true)
00074     , m_origDevice(new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "temp"))
00075     , m_scaledPreview()
00076     , m_dirtyPreview(true)
00077     , m_previewDevice(new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "temp"))
00078     , m_scaledImage(NULL)
00079     , m_filterZoom(1.0)
00080     , m_zoom(-1.0)
00081     , m_profile(NULL)
00082     , m_progress( 0 )
00083     , m_zoomTimer(new QTimer(this))
00084     , m_filterTimer(new QTimer(this))
00085     , m_firstFilter(true)
00086     , m_firstZoom(true)
00087 {
00088     btnZoomIn->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "viewmag+", KIcon::MainToolbar, 16 ));
00089     connect(btnZoomIn, SIGNAL(clicked()), this, SLOT(zoomIn()));
00090     btnZoomOut->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "viewmag-", KIcon::MainToolbar, 16 ));
00091     connect(btnZoomOut, SIGNAL(clicked()), this, SLOT(zoomOut()));
00092     btnUpdate->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "reload", KIcon::MainToolbar, 16 ));
00093     connect(btnUpdate, SIGNAL(clicked()), this, SLOT(forceUpdate()));
00094 
00095     connect(radioBtnPreview, SIGNAL(toggled(bool)), this, SLOT(setPreviewDisplayed(bool)));
00096 
00097     connect(checkBoxAutoUpdate, SIGNAL(toggled(bool)), this, SLOT(slotSetAutoUpdate(bool)));
00098     btnZoomOneToOne->setIconSet(KGlobal::instance()->iconLoader()->loadIconSet( "viewmag1", KIcon::MainToolbar, 16 ));
00099     connect(btnZoomOneToOne, SIGNAL(clicked()), this, SLOT(zoomOneToOne()));
00100 
00101     m_progress = new KisLabelProgress(frmProgress);
00102     m_progress->setMaximumHeight(fontMetrics().height() );
00103     QVBoxLayout *vbox = new QVBoxLayout( frmProgress );
00104     vbox->addWidget(m_progress);
00105     m_progress->hide();
00106 
00107     connect(m_zoomTimer, SIGNAL(timeout()), this, SLOT(updateZoom()));
00108     connect(m_filterTimer, SIGNAL(timeout()), this, SLOT(runFilterHelper()));
00109 
00110 /*    kToolBar1->insertLineSeparator();
00111     kToolBar1->insertButton("reload",2, true, i18n("Update"));
00112     connect(kToolBar1->getButton(2),SIGNAL(clicked()),this,SLOT(forceUpdate()));
00113 
00114     kToolBar1->insertButton("",3, true, i18n("Auto Update"));
00115     connect(kToolBar1->getButton(3),SIGNAL(clicked()),this,SLOT(toggleAutoUpdate()));
00116 
00117     kToolBar1->insertButton("",4, true, i18n("Switch"));
00118     connect(kToolBar1->getButton(4),SIGNAL(clicked()),this,SLOT(toggleImageDisplayed()));*/
00119 // these currently don't yet work, reenable when they do work :)  (TZ-12-2005)
00120 // TODO reenable these
00121 //   kToolBar1->insertButton("",5, true, i18n("Popup Original and Preview"));
00122 }
00123 
00124 KisPreviewWidget::~KisPreviewWidget() { }
00125 
00126 void KisPreviewWidget::forceUpdate()
00127 {
00128     if(m_previewIsDisplayed)
00129     {
00130         m_groupBox->setTitle(m_origDevice->name());
00131         emit updated();
00132     }
00133 }
00134 
00135 void KisPreviewWidget::slotSetDevice(KisPaintDeviceSP dev)
00136 {
00137     Q_ASSERT( dev );
00138 
00139     if (!dev) return;
00140 
00141     m_origDevice = dev;
00142     m_previewDevice = dev;
00143     m_filterZoom = 1.0;
00144 
00145     KisConfig cfg;
00146     QString monitorProfileName = cfg.monitorProfile();
00147     m_profile = KisMetaRegistry::instance()->csRegistry()->getProfileByName(monitorProfileName);
00148 
00149     QRect r = dev->exactBounds();
00150 
00151     m_groupBox->setTitle(i18n("Preview: ") + dev->name());
00152     m_previewIsDisplayed = true;
00153 
00154     m_zoom = -1.0;
00155     zoomChanged(double(m_preview->width()) / double(r.width()) );
00156 }
00157 
00158 void KisPreviewWidget::updateZoom()
00159 {
00160     QApplication::setOverrideCursor(KisCursor::waitCursor());    
00161 
00162     if(m_previewIsDisplayed)
00163     {
00164         if(m_dirtyPreview)
00165         {
00166             QSize r = m_previewDevice->extent().size();
00167             int w = r.width(), h = r.height();
00168             int sw = int(ceil(m_zoom * w / m_filterZoom));
00169             int sh = int(ceil(m_zoom * h / m_filterZoom));
00170             m_dirtyPreview = false;
00171             m_scaledPreview = m_previewDevice->convertToQImage(m_profile, 0, 0, w, h);
00172             m_scaledPreview = m_scaledPreview.scale(sw,sh, QImage::ScaleMax); // Use scale instead of smoothScale for speed up
00173         }
00174         m_preview->setImage(m_scaledPreview);
00175     } else
00176     {
00177         if(m_dirtyOriginal)
00178         {
00179             QSize r = m_origDevice->extent().size();
00180             int w = r.width(), h = r.height();
00181             int sw = int(ceil(m_zoom * w));
00182             int sh = int(ceil(m_zoom * h));
00183             m_dirtyOriginal = false;
00184             m_scaledOriginal = m_origDevice->convertToQImage(m_profile, 0, 0, w, h);
00185             m_scaledOriginal = m_scaledOriginal.scale(sw,sh, QImage::ScaleMax); // Use scale instead of smoothScale for speed up
00186         }
00187         m_preview->setImage(m_scaledOriginal);
00188     }
00189 
00190     QApplication::restoreOverrideCursor();
00191 }
00192 
00193 void KisPreviewWidget::slotSetAutoUpdate(bool set) {
00194     m_autoupdate = set;
00195 }
00196 
00197 void KisPreviewWidget::wheelEvent(QWheelEvent * e)
00198 {
00199     if (e->delta() > 0) {
00200         zoomIn();
00201     } else {
00202         zoomOut();
00203     }
00204     e->accept();
00205 }
00206 
00207 void KisPreviewWidget::setPreviewDisplayed(bool v)
00208 {
00209     if(v != m_previewIsDisplayed)
00210     {
00211         m_previewIsDisplayed = v;
00212         if(m_previewIsDisplayed) {
00213             m_groupBox->setTitle(i18n("Preview: ") + m_origDevice->name());
00214         } else {
00215             m_groupBox->setTitle(i18n("Original: ") + m_origDevice->name());
00216         }
00217         // Call directly without any pause because there is no scaling
00218         updateZoom();
00219     }
00220 }
00221 
00222 void KisPreviewWidget::needUpdate()
00223 {
00224     if(m_previewIsDisplayed)
00225         m_groupBox->setTitle(i18n("Preview (needs update)"));
00226 }
00227 
00228 bool KisPreviewWidget::getAutoUpdate()  const {
00229     return m_autoupdate;
00230 }
00231 
00232 void KisPreviewWidget::zoomChanged(const double zoom)
00233 {
00234     // constrain the zoom
00235     double tZoom = zoom;
00236     if(zoom <= 1./8.) { tZoom = 1./8.; }
00237     if(zoom > 8.) { tZoom = 8.; }
00238 
00239     if(tZoom != m_zoom) 
00240     {
00241         m_zoom = tZoom;
00242         m_dirtyOriginal = true;
00243         m_dirtyPreview = true;
00244 
00245         if(m_firstZoom) {
00246             m_firstZoom = false;
00247             updateZoom();
00248         } else {
00249             m_zoomTimer->start(ZOOM_PAUSE, true);
00250         }
00251     }
00252 }
00253 
00254 void KisPreviewWidget::zoomIn() {
00255     zoomChanged(m_zoom * ZOOM_FACTOR);
00256 }
00257 
00258 void KisPreviewWidget::zoomOut() {
00259     zoomChanged(m_zoom / ZOOM_FACTOR);
00260 }
00261 
00262 void KisPreviewWidget::zoomOneToOne() {
00263     zoomChanged(1.0);
00264 }
00265 
00266 static inline void cropDevice(KisPaintDevice * device, const double & zoom) {
00267     QRect r = device->exactBounds();
00268     r.setX(int(zoom * r.x()) );
00269     r.setY(int(zoom * r.y()) );
00270     r.setWidth(int(zoom * r.width()) );
00271     r.setHeight(int(zoom * r.height()) );
00272     device->crop(r);
00273 }
00274 
00275 class MyCropVisitor : public KisLayerVisitor {
00276     const double m_zoom;
00277 
00278 public:
00279     MyCropVisitor(const double & z) : m_zoom(z) { }
00280     virtual ~MyCropVisitor() { }
00281 
00282     virtual bool visit(KisPaintLayer *layer) {
00283         KisPaintDeviceSP device = layer->paintDevice();
00284         ::cropDevice(device.data(), m_zoom);
00285         // Make sure we have a tight fit for the selection
00286         if(device->hasSelection()) {
00287             ::cropDevice(device->selection().data(), m_zoom);
00288         }
00289 
00290         return true;
00291     }
00292     virtual bool visit(KisGroupLayer *layer) {
00293         for(KisLayerSP l = layer->firstChild(); l; l = l->nextSibling()) {
00294             l->accept(*this);
00295         }
00296         return true;
00297     }
00298     virtual bool visit(KisPartLayer *) { return true; }
00299     virtual bool visit(KisAdjustmentLayer *) { return true; }
00300 };
00301 
00302 void KisPreviewWidget::runFilter(KisFilter * filter, KisFilterConfiguration * config) {
00303     if(!filter) return;
00304     if(!config) return;
00305 
00306     m_filter = filter;
00307     m_config = config;
00308 
00309     if(m_firstFilter) {
00310         m_firstFilter = false;
00311         runFilterHelper();
00312     } else {
00313         m_filterTimer->start(FILTER_PAUSE, true);
00314     }
00315 }
00316 
00321 void KisPreviewWidget::runFilterHelper() {
00322     
00323     m_filterZoom = m_zoom;
00324     // Dont scale more then 1.0 so we don't waste time in preview widget for large scaling.
00325     if(m_filterZoom > 1.0) {
00326         m_filterZoom = 1.0;
00327     }
00328 
00329     KisPaintDeviceSP scaledDevice;
00330     KisHermiteFilterStrategy strategy;
00331 
00332     // Copy the image and scale
00333     if (m_origDevice->image())
00334     {
00335         m_scaledImage = new KisImage(*m_origDevice->image());
00336         if(!m_origDevice->parentLayer()) return;
00337         QString layerName = m_origDevice->parentLayer()->name();
00338         KisPaintLayerSP pl = ::qt_cast<KisPaintLayer*>(m_scaledImage->findLayer(layerName));
00339         if(!pl) return;
00340         scaledDevice = pl->paintDevice();
00341         
00342         KisSelectionSP select;
00343         if(scaledDevice->hasSelection())
00344         {
00345             select = new KisSelection(*scaledDevice->selection());
00346             scaledDevice->deselect();
00347         }
00348         // Scale
00349         m_scaledImage->setUndoAdapter(NULL);
00350         m_scaledImage->scale(m_filterZoom, m_filterZoom, NULL, &strategy);
00351         // Scale the selection
00352         if(select)
00353         {
00354             KisPaintDeviceSP t = select.data();        
00355             KisTransformWorker tw(t, m_filterZoom, m_filterZoom, 
00356                                   0.0, 0.0, 0.0, 0, 0, NULL, &strategy);
00357             tw.run();
00358             scaledDevice->setSelection(select);
00359             select->setParentLayer(scaledDevice->parentLayer());
00360         }
00361         
00362         // Crop by the zoom value instead of cropping by rectangle. It gives better results
00363         MyCropVisitor v(m_filterZoom);
00364         m_scaledImage->rootLayer()->accept(v);
00365     } else
00366     {
00367         scaledDevice = new KisPaintDevice(*m_origDevice);
00368         KisSelectionSP select;
00369         if(scaledDevice->hasSelection())
00370         {
00371             select = new KisSelection(*scaledDevice->selection());
00372             scaledDevice->deselect();
00373         }
00374         KisTransformWorker tw(scaledDevice, m_filterZoom, m_filterZoom, 
00375                               0.0, 0.0, 0.0, 0, 0, NULL, &strategy);
00376         tw.run();
00377         // Scale the selection
00378         if(select)
00379         {
00380             KisPaintDeviceSP t = select.data();        
00381             KisTransformWorker tw(t, m_filterZoom, m_filterZoom, 
00382                                   0.0, 0.0, 0.0, 0, 0, NULL, &strategy);
00383             tw.run();
00384             scaledDevice->setSelection(select);
00385             ::cropDevice(select.data(), m_filterZoom);
00386         }
00387         ::cropDevice(scaledDevice.data(), m_filterZoom);
00388     }
00389 
00390     m_previewDevice = new KisPaintDevice(*scaledDevice);
00391 
00392     // Setup the progress display
00393     m_filter->enableProgress();
00394     m_progress->setSubject(m_filter, true, true);
00395     m_filter->setProgressDisplay(m_progress);
00396     m_filter->process(scaledDevice, m_previewDevice, m_config, scaledDevice->exactBounds());
00397     m_filter->disableProgress();
00398 
00399     m_dirtyPreview = true;
00400 
00401     if(m_firstZoom) {
00402         m_firstZoom = false;
00403         updateZoom();
00404     } else {
00405         m_zoomTimer->start(ZOOM_PAUSE, true);
00406     }
00407 }
00408 
00409 #include "kis_previewwidget.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys