chalk

kis_previewwidget.cpp
1/*
2 * kis_previewwidget.cpp - part of Chalk
3 *
4 * Copyright (c) 2001 John Califf <jwcaliff@compuzone.net>
5 * Copyright (c) 2004 Bart Coppens <kde@bartcoppens.be>
6 * Copyright (c) 2005 Cyrille Berger <cberger@cberger.net>
7 * Copyright (c) 2007 Ben Schleimer <bensch128@yahoo.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23
24#include <tqcheckbox.h>
25#include <tqradiobutton.h>
26#include <tqpainter.h>
27#include <tqpoint.h>
28#include <tqpushbutton.h>
29#include <tqlayout.h>
30#include <tqlabel.h>
31#include <tqapplication.h>
32#include <tqcolor.h>
33#include <tqgroupbox.h>
34#include <tqcursor.h>
35#include <tqtimer.h>
36
37#include <kdebug.h>
38#include <kiconloader.h>
39#include <kpushbutton.h>
40
41#include <kis_cursor.h>
42#include <kis_colorspace.h>
43#include <kis_colorspace_factory_registry.h>
44#include <kis_config.h>
45#include <kis_filter_strategy.h>
46#include <kis_global.h>
47#include <kis_image.h>
48#include <kis_layer.h>
49#include <kis_paint_layer.h>
50#include <kis_group_layer.h>
51#include <kis_meta_registry.h>
52#include <kis_painter.h>
53#include <kis_profile.h>
54#include <kis_types.h>
55#include <kis_undo_adapter.h>
56#include <kis_label_progress.h>
57#include <kis_selection.h>
58#include <kis_transform_worker.h>
59
60#include "kis_previewwidgetbase.h"
61#include "kis_previewwidget.h"
62#include "imageviewer.h"
63
64static const int ZOOM_PAUSE = 100;
65static const int FILTER_PAUSE = 500;
66static const double ZOOM_FACTOR = 1.1;
67
68KisPreviewWidget::KisPreviewWidget( TQWidget* parent, const char* name )
69 : PreviewWidgetBase( parent, name )
70 , m_autoupdate(true)
71 , m_previewIsDisplayed(true)
72 , m_scaledOriginal()
73 , m_dirtyOriginal(true)
74 , m_origDevice(new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "temp"))
75 , m_scaledPreview()
76 , m_dirtyPreview(true)
77 , m_previewDevice(new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "temp"))
78 , m_scaledImage(NULL)
79 , m_filterZoom(1.0)
80 , m_zoom(-1.0)
81 , m_profile(NULL)
82 , m_progress( 0 )
83 , m_zoomTimer(new TQTimer(this))
84 , m_filterTimer(new TQTimer(this))
85 , m_firstFilter(true)
86 , m_firstZoom(true)
87{
88 btnZoomIn->setIconSet(TDEGlobal::instance()->iconLoader()->loadIconSet( "zoom-in", TDEIcon::MainToolbar, 16 ));
89 connect(btnZoomIn, TQ_SIGNAL(clicked()), this, TQ_SLOT(zoomIn()));
90 btnZoomOut->setIconSet(TDEGlobal::instance()->iconLoader()->loadIconSet( "zoom-out", TDEIcon::MainToolbar, 16 ));
91 connect(btnZoomOut, TQ_SIGNAL(clicked()), this, TQ_SLOT(zoomOut()));
92 btnUpdate->setIconSet(TDEGlobal::instance()->iconLoader()->loadIconSet( "reload", TDEIcon::MainToolbar, 16 ));
93 connect(btnUpdate, TQ_SIGNAL(clicked()), this, TQ_SLOT(forceUpdate()));
94
95 connect(radioBtnPreview, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(setPreviewDisplayed(bool)));
96
97 connect(checkBoxAutoUpdate, TQ_SIGNAL(toggled(bool)), this, TQ_SLOT(slotSetAutoUpdate(bool)));
98 btnZoomOneToOne->setIconSet(TDEGlobal::instance()->iconLoader()->loadIconSet( "zoom-original", TDEIcon::MainToolbar, 16 ));
99 connect(btnZoomOneToOne, TQ_SIGNAL(clicked()), this, TQ_SLOT(zoomOneToOne()));
100
101 m_progress = new KisLabelProgress(frmProgress);
102 m_progress->setMaximumHeight(fontMetrics().height() );
103 TQVBoxLayout *vbox = new TQVBoxLayout( frmProgress );
104 vbox->addWidget(m_progress);
105 m_progress->hide();
106
107 connect(m_zoomTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(updateZoom()));
108 connect(m_filterTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(runFilterHelper()));
109
110/* kToolBar1->insertLineSeparator();
111 kToolBar1->insertButton("reload",2, true, i18n("Update"));
112 connect(kToolBar1->getButton(2),TQ_SIGNAL(clicked()),this,TQ_SLOT(forceUpdate()));
113
114 kToolBar1->insertButton("",3, true, i18n("Auto Update"));
115 connect(kToolBar1->getButton(3),TQ_SIGNAL(clicked()),this,TQ_SLOT(toggleAutoUpdate()));
116
117 kToolBar1->insertButton("",4, true, i18n("Switch"));
118 connect(kToolBar1->getButton(4),TQ_SIGNAL(clicked()),this,TQ_SLOT(toggleImageDisplayed()));*/
119// these currently don't yet work, reenable when they do work :) (TZ-12-2005)
120// TODO reenable these
121// kToolBar1->insertButton("",5, true, i18n("Popup Original and Preview"));
122}
123
124KisPreviewWidget::~KisPreviewWidget() { }
125
126void KisPreviewWidget::forceUpdate()
127{
128 if(m_previewIsDisplayed)
129 {
130 m_groupBox->setTitle(m_origDevice->name());
131 emit updated();
132 }
133}
134
135void KisPreviewWidget::slotSetDevice(KisPaintDeviceSP dev)
136{
137 Q_ASSERT( dev );
138
139 if (!dev) return;
140
141 m_origDevice = dev;
142 m_previewDevice = dev;
143 m_filterZoom = 1.0;
144
145 KisConfig cfg;
146 TQString monitorProfileName = cfg.monitorProfile();
147 m_profile = KisMetaRegistry::instance()->csRegistry()->getProfileByName(monitorProfileName);
148
149 TQRect r = dev->exactBounds();
150
151 m_groupBox->setTitle(i18n("Preview: ") + dev->name());
152 m_previewIsDisplayed = true;
153
154 m_zoom = -1.0;
155 zoomChanged(double(m_preview->width()) / double(r.width()) );
156}
157
158void KisPreviewWidget::updateZoom()
159{
160 TQApplication::setOverrideCursor(KisCursor::waitCursor());
161
162 if(m_previewIsDisplayed)
163 {
164 if(m_dirtyPreview)
165 {
166 TQSize r = m_previewDevice->extent().size();
167 int w = r.width(), h = r.height();
168 int sw = int(ceil(m_zoom * w / m_filterZoom));
169 int sh = int(ceil(m_zoom * h / m_filterZoom));
170 m_dirtyPreview = false;
171 m_scaledPreview = m_previewDevice->convertToTQImage(m_profile, 0, 0, w, h);
172 m_scaledPreview = m_scaledPreview.scale(sw,sh, TQImage::ScaleMax); // Use scale instead of smoothScale for speed up
173 }
174 m_preview->setImage(m_scaledPreview);
175 } else
176 {
177 if(m_dirtyOriginal)
178 {
179 TQSize r = m_origDevice->extent().size();
180 int w = r.width(), h = r.height();
181 int sw = int(ceil(m_zoom * w));
182 int sh = int(ceil(m_zoom * h));
183 m_dirtyOriginal = false;
184 m_scaledOriginal = m_origDevice->convertToTQImage(m_profile, 0, 0, w, h);
185 m_scaledOriginal = m_scaledOriginal.scale(sw,sh, TQImage::ScaleMax); // Use scale instead of smoothScale for speed up
186 }
187 m_preview->setImage(m_scaledOriginal);
188 }
189
190 TQApplication::restoreOverrideCursor();
191}
192
194 m_autoupdate = set;
195}
196
197void KisPreviewWidget::wheelEvent(TQWheelEvent * e)
198{
199 if (e->delta() > 0) {
200 zoomIn();
201 } else {
202 zoomOut();
203 }
204 e->accept();
205}
206
208{
209 if(v != m_previewIsDisplayed)
210 {
211 m_previewIsDisplayed = v;
212 if(m_previewIsDisplayed) {
213 m_groupBox->setTitle(i18n("Preview: ") + m_origDevice->name());
214 } else {
215 m_groupBox->setTitle(i18n("Original: ") + m_origDevice->name());
216 }
217 // Call directly without any pause because there is no scaling
218 updateZoom();
219 }
220}
221
223{
224 if(m_previewIsDisplayed)
225 m_groupBox->setTitle(i18n("Preview (needs update)"));
226}
227
229 return m_autoupdate;
230}
231
232void KisPreviewWidget::zoomChanged(const double zoom)
233{
234 // constrain the zoom
235 double tZoom = zoom;
236 if(zoom <= 1./8.) { tZoom = 1./8.; }
237 if(zoom > 8.) { tZoom = 8.; }
238
239 if(tZoom != m_zoom)
240 {
241 m_zoom = tZoom;
242 m_dirtyOriginal = true;
243 m_dirtyPreview = true;
244
245 if(m_firstZoom) {
246 m_firstZoom = false;
247 updateZoom();
248 } else {
249 m_zoomTimer->start(ZOOM_PAUSE, true);
250 }
251 }
252}
253
254void KisPreviewWidget::zoomIn() {
255 zoomChanged(m_zoom * ZOOM_FACTOR);
256}
257
258void KisPreviewWidget::zoomOut() {
259 zoomChanged(m_zoom / ZOOM_FACTOR);
260}
261
262void KisPreviewWidget::zoomOneToOne() {
263 zoomChanged(1.0);
264}
265
266static inline void cropDevice(KisPaintDevice * device, const double & zoom) {
267 TQRect r = device->exactBounds();
268 r.setX(int(zoom * r.x()) );
269 r.setY(int(zoom * r.y()) );
270 r.setWidth(int(zoom * r.width()) );
271 r.setHeight(int(zoom * r.height()) );
272 device->crop(r);
273}
274
275class MyCropVisitor : public KisLayerVisitor {
276 const double m_zoom;
277
278public:
279 MyCropVisitor(const double & z) : m_zoom(z) { }
280 virtual ~MyCropVisitor() { }
281
282 virtual bool visit(KisPaintLayer *layer) {
283 KisPaintDeviceSP device = layer->paintDevice();
284 ::cropDevice(device.data(), m_zoom);
285 // Make sure we have a tight fit for the selection
286 if(device->hasSelection()) {
287 ::cropDevice(device->selection().data(), m_zoom);
288 }
289
290 return true;
291 }
292 virtual bool visit(KisGroupLayer *layer) {
293 for(KisLayerSP l = layer->firstChild(); l; l = l->nextSibling()) {
294 l->accept(*this);
295 }
296 return true;
297 }
298 virtual bool visit(KisPartLayer *) { return true; }
299 virtual bool visit(KisAdjustmentLayer *) { return true; }
300};
301
303 if(!filter) return;
304 if(!config) return;
305
306 m_filter = filter;
307 m_config = config;
308
309 if(m_firstFilter) {
310 m_firstFilter = false;
311 runFilterHelper();
312 } else {
313 m_filterTimer->start(FILTER_PAUSE, true);
314 }
315}
316
321void KisPreviewWidget::runFilterHelper() {
322
323 m_filterZoom = m_zoom;
324 // Dont scale more then 1.0 so we don't waste time in preview widget for large scaling.
325 if(m_filterZoom > 1.0) {
326 m_filterZoom = 1.0;
327 }
328
329 KisPaintDeviceSP scaledDevice;
330 KisHermiteFilterStrategy strategy;
331
332 // Copy the image and scale
333 if (m_origDevice->image())
334 {
335 m_scaledImage = new KisImage(*m_origDevice->image());
336 if(!m_origDevice->parentLayer()) return;
337 TQString layerName = m_origDevice->parentLayer()->name();
338 KisPaintLayerSP pl = ::tqt_cast<KisPaintLayer*>(m_scaledImage->findLayer(layerName));
339 if(!pl) return;
340 scaledDevice = pl->paintDevice();
341
342 KisSelectionSP select;
343 if(scaledDevice->hasSelection())
344 {
345 select = new KisSelection(*scaledDevice->selection());
346 scaledDevice->deselect();
347 }
348 // Scale
349 m_scaledImage->setUndoAdapter(NULL);
350 m_scaledImage->scale(m_filterZoom, m_filterZoom, NULL, &strategy);
351 // Scale the selection
352 if(select)
353 {
354 KisPaintDeviceSP t = select.data();
355 KisTransformWorker tw(t, m_filterZoom, m_filterZoom,
356 0.0, 0.0, 0.0, 0, 0, NULL, &strategy);
357 tw.run();
358 scaledDevice->setSelection(select);
359 select->setParentLayer(scaledDevice->parentLayer());
360 }
361
362 // Crop by the zoom value instead of cropping by rectangle. It gives better results
363 MyCropVisitor v(m_filterZoom);
364 m_scaledImage->rootLayer()->accept(v);
365 } else
366 {
367 scaledDevice = new KisPaintDevice(*m_origDevice);
368 KisSelectionSP select;
369 if(scaledDevice->hasSelection())
370 {
371 select = new KisSelection(*scaledDevice->selection());
372 scaledDevice->deselect();
373 }
374 KisTransformWorker tw(scaledDevice, m_filterZoom, m_filterZoom,
375 0.0, 0.0, 0.0, 0, 0, NULL, &strategy);
376 tw.run();
377 // Scale the selection
378 if(select)
379 {
380 KisPaintDeviceSP t = select.data();
381 KisTransformWorker tw(t, m_filterZoom, m_filterZoom,
382 0.0, 0.0, 0.0, 0, 0, NULL, &strategy);
383 tw.run();
384 scaledDevice->setSelection(select);
385 ::cropDevice(select.data(), m_filterZoom);
386 }
387 ::cropDevice(scaledDevice.data(), m_filterZoom);
388 }
389
390 m_previewDevice = new KisPaintDevice(*scaledDevice);
391
392 // Setup the progress display
393 m_filter->enableProgress();
394 m_progress->setSubject(m_filter, true, true);
395 m_filter->setProgressDisplay(m_progress);
396 m_filter->process(scaledDevice, m_previewDevice, m_config, scaledDevice->exactBounds());
397 m_filter->disableProgress();
398
399 m_dirtyPreview = true;
400
401 if(m_firstZoom) {
402 m_firstZoom = false;
403 updateZoom();
404 } else {
405 m_zoomTimer->start(ZOOM_PAUSE, true);
406 }
407}
408
409#include "kis_previewwidget.moc"
Class that contains a KisFilter and optionally a KisSelection.
KisProfile * getProfileByName(const TQString &name)
Return the profile associated with the given product name, or 0.
A KisFilterConfiguration is the serializable representation of the filter parameters.
Basic interface of a Chalk filter.
Definition kis_filter.h:46
A KisLayer that bundles child layers into a single layer.
virtual KisLayerSP firstChild() const
Returns the first child layer of this layer (if it supports that).
A single singleton that provides access to several registries.
A paint device contains the actual pixel data and offers methods to read and write pixels.
void crop(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h)
Cut the paint device down to the specified rect.
virtual void exactBounds(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const
Get the exact bounds of this paint device.
This layer is of a type that can be painted on.
An interface for the Part Layer so that we can use it in core/, but can implement it in ui/.
void updated()
This is emitted when the position or zoom factor of the widget has changed.
void slotSetDevice(KisPaintDeviceSP dev)
Sets the preview to use the layer specified as argument.
void runFilter(KisFilter *filter, KisFilterConfiguration *config)
Instructs the KisPreviewWidget to eventually update the preview.
bool getAutoUpdate() const
returns if the preview is automatically updated
KisPreviewWidget(TQWidget *parent=0, const char *name=0)
Constructs the widget.
void setPreviewDisplayed(bool v)
Toggle between display preview and display original.
void needUpdate()
use to indicate that the preview need to be updated.
void slotSetAutoUpdate(bool set)
Enables or disables the automatically updating of the preview.
KisSelection contains a byte-map representation of a layer, where the value of a byte signifies wheth...