1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2010-11-10
7  * Description : basic filter management for DImg builtin methods
8  *
9  * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10  * Copyright (C) 2010      by Martin Klapetek <martin dot klapetek at gmail dot com>
11  *
12  * This program is free software; you can redistribute it
13  * and/or modify it under the terms of the GNU General
14  * Public License as published by the Free Software Foundation;
15  * either version 2, or (at your option)
16  * any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * ============================================================ */
24 
25 #include "dimgbuiltinfilter.h"
26 
27 // KDE includes
28 
29 #include <klocalizedstring.h>
30 
31 // Local includes
32 
33 #include "dimg.h"
34 #include "dimgthreadedfilter.h"
35 #include "filteraction.h"
36 
37 namespace Digikam
38 {
39 
DImgBuiltinFilter()40 DImgBuiltinFilter::DImgBuiltinFilter()
41     : m_type(NoOperation)
42 {
43 }
44 
DImgBuiltinFilter(const FilterAction & action)45 DImgBuiltinFilter::DImgBuiltinFilter(const FilterAction& action)
46 {
47     setAction(action);
48 }
49 
DImgBuiltinFilter(Type type,const QVariant & arg)50 DImgBuiltinFilter::DImgBuiltinFilter(Type type, const QVariant& arg)
51 {
52     setAction(type, arg);
53 }
54 
setAction(const FilterAction & action)55 void DImgBuiltinFilter::setAction(const FilterAction& action)
56 {
57     m_type = NoOperation;
58 
59     if      ((action.identifier() == QLatin1String("transform:rotate")) && (action.version() == 1))
60     {
61         int angle = action.parameter(QLatin1String("angle")).toInt();
62 
63         if      (angle == 90)
64         {
65             m_type = Rotate90;
66         }
67         else if (angle == 180)
68         {
69             m_type = Rotate180;
70         }
71         else
72         {
73             m_type = Rotate270;
74         }
75     }
76     else if ((action.identifier() == QLatin1String("transform:flip")) && (action.version() == 1))
77     {
78         QString direction = action.parameter(QLatin1String("direction")).toString();
79 
80         if (direction == QLatin1String("vertical"))
81         {
82             m_type = FlipVertically;
83         }
84         else
85         {
86             m_type = FlipHorizontally;
87         }
88     }
89     else if ((action.identifier() == QLatin1String("transform:crop")) && (action.version() == 1))
90     {
91         m_type     = Crop;
92         int x      = action.parameter(QLatin1String("x")).toInt();
93         int y      = action.parameter(QLatin1String("y")).toInt();
94         int width  = action.parameter(QLatin1String("width")).toInt();
95         int height = action.parameter(QLatin1String("height")).toInt();
96         m_arg      = QRect(x, y, width, height);
97     }
98     else if ((action.identifier() == QLatin1String("transform:resize")) && (action.version() == 1))
99     {
100         m_type     = Resize;
101         int width  = action.parameter(QLatin1String("width")).toInt();
102         int height = action.parameter(QLatin1String("height")).toInt();
103         m_arg      = QSize(width, height);
104     }
105     else if ((action.identifier() == QLatin1String("transform:convertDepth")) && (action.version() == 1))
106     {
107         int depth = action.parameter(QLatin1String("depth")).toInt();
108 
109         if (depth == 16)
110         {
111             m_type = ConvertTo16Bit;
112         }
113         else
114         {
115             m_type = ConvertTo8Bit;
116         }
117     }
118 }
119 
setAction(Type type,const QVariant & arg)120 void DImgBuiltinFilter::setAction(Type type, const QVariant& arg)
121 {
122     m_type = type;
123     m_arg  = arg;
124 }
125 
isValid() const126 bool DImgBuiltinFilter::isValid() const
127 {
128     switch (m_type)
129     {
130         case NoOperation:
131             return false;
132 
133         case Crop:
134             return m_arg.type() == QVariant::Rect;
135 
136         case Resize:
137             return m_arg.type() == QVariant::Size;
138 
139         default:
140             return true;
141     }
142 }
143 
apply(DImg & image) const144 void DImgBuiltinFilter::apply(DImg& image) const
145 {
146     switch (m_type)
147     {
148         case NoOperation:
149             break;
150 
151         case Rotate90:
152             image.rotate(DImg::ROT90);
153             break;
154 
155         case Rotate180:
156             image.rotate(DImg::ROT180);
157             break;
158 
159         case Rotate270:
160             image.rotate(DImg::ROT270);
161             break;
162 
163         case FlipHorizontally:
164             image.flip(DImg::HORIZONTAL);
165             break;
166 
167         case FlipVertically:
168             image.flip(DImg::VERTICAL);
169             break;
170 
171         case Crop:
172             image.crop(m_arg.toRect());
173             break;
174 
175         case Resize:
176         {
177             QSize s = m_arg.toSize();
178             image.resize(s.width(), s.height());
179             break;
180         }
181 
182         case ConvertTo8Bit:
183             image.convertToEightBit();
184             break;
185 
186         case ConvertTo16Bit:
187             image.convertToSixteenBit();
188             break;
189     }
190 }
191 
filterAction() const192 FilterAction DImgBuiltinFilter::filterAction() const
193 {
194     FilterAction action;
195 
196     switch (m_type)
197     {
198         case NoOperation:
199         default:
200             return action;
201 
202         case Rotate90:
203         case Rotate180:
204         case Rotate270:
205         {
206             action = FilterAction(QLatin1String("transform:rotate"), 1);
207             int angle;
208 
209             if      (m_type == Rotate90)
210             {
211                 angle = 90;
212             }
213             else if (m_type == Rotate180)
214             {
215                 angle = 180;
216             }
217             else
218             {
219                 angle = 270;
220             }
221 
222             action.addParameter(QLatin1String("angle"), angle);
223             break;
224         }
225 
226         case FlipHorizontally:
227         case FlipVertically:
228         {
229             action = FilterAction(QLatin1String("transform:flip"), 1);
230             action.addParameter(QLatin1String("direction"), (m_type == FlipHorizontally) ? QLatin1String("horizontal")
231                                                                                          : QLatin1String("vertical"));
232             break;
233         }
234 
235         case Crop:
236         {
237             action  = FilterAction(QLatin1String("transform:crop"), 1);
238             QRect r = m_arg.toRect();
239             action.addParameter(QLatin1String("x"),      r.x());
240             action.addParameter(QLatin1String("y"),      r.y());
241             action.addParameter(QLatin1String("width"),  r.width());
242             action.addParameter(QLatin1String("height"), r.height());
243             break;
244         }
245 
246         case Resize:
247         {
248             action  = FilterAction(QLatin1String("transform:resize"), 1);
249             QSize s = m_arg.toSize();
250             action.addParameter(QLatin1String("width"),  s.width());
251             action.addParameter(QLatin1String("height"), s.height());
252             break;
253         }
254 
255         case ConvertTo8Bit:
256         case ConvertTo16Bit:
257         {
258             action = FilterAction(QLatin1String("transform:convertDepth"), 1);
259             action.addParameter(QLatin1String("depth"), (m_type == ConvertTo8Bit) ? 8 : 16);
260             break;
261         }
262     }
263 
264     action.setDisplayableName(displayableName());
265     return action;
266 }
267 
reverseFilter() const268 DImgBuiltinFilter DImgBuiltinFilter::reverseFilter() const
269 {
270     switch (m_type)
271     {
272         case Rotate90:
273             return DImgBuiltinFilter(Rotate270);
274 
275         case Rotate180:
276             return DImgBuiltinFilter(Rotate180);
277 
278         case Rotate270:
279             return DImgBuiltinFilter(Rotate90);
280 
281         case FlipHorizontally:
282         case FlipVertically:
283             return DImgBuiltinFilter(m_type);
284 
285         case Crop:
286         case Resize:
287         case ConvertTo8Bit:
288         case ConvertTo16Bit:
289         case NoOperation:
290         default:
291             return DImgBuiltinFilter();
292     }
293 }
294 
isReversible() const295 bool DImgBuiltinFilter::isReversible() const
296 {
297     return reverseFilter().isValid();
298 }
299 
supportedFilters()300 QStringList DImgBuiltinFilter::supportedFilters()
301 {
302     return QStringList() << QLatin1String("transform:rotate")
303                          << QLatin1String("transform:flip")
304                          << QLatin1String("transform:crop")
305                          << QLatin1String("transform:resize")
306                          << QLatin1String("transform:convertDepth");
307 }
308 
supportedVersions(const QString & filterIdentifier)309 QList<int> DImgBuiltinFilter::supportedVersions(const QString& filterIdentifier)
310 {
311     QList<int> versions;
312 
313     // So far, all filters are at version 1
314 
315     if (isSupported(filterIdentifier))
316     {
317         versions << 1;
318     }
319 
320     return versions;
321 }
322 
i18nDisplayableName() const323 QString DImgBuiltinFilter::i18nDisplayableName() const
324 {
325     QByteArray latin1 = displayableName().toLatin1();
326 
327     return i18n(latin1.data());
328 }
329 
displayableName() const330 QString DImgBuiltinFilter::displayableName() const
331 {
332     switch (m_type)
333     {
334         case NoOperation:
335             break;
336 
337         case Rotate90:
338             return QString::fromUtf8(I18N_NOOP("Rotate Right"));
339 
340         case Rotate180:
341             return QString::fromUtf8(I18N_NOOP("Rotate 180 degrees"));
342 
343         case Rotate270:
344             return QString::fromUtf8(I18N_NOOP("Rotate Left"));
345 
346         case FlipHorizontally:
347             return QString::fromUtf8(I18N_NOOP("Flip Horizontally"));
348 
349         case FlipVertically:
350             return QString::fromUtf8(I18N_NOOP("Flip Vertically"));
351 
352         case Crop:
353             return QString::fromUtf8(I18N_NOOP("Crop"));
354 
355         case Resize:
356             return QString::fromUtf8(I18N_NOOP("Resize"));
357 
358         case ConvertTo8Bit:
359             return QString::fromUtf8(I18N_NOOP("Convert to 8 Bit"));
360 
361         case ConvertTo16Bit:
362             return QString::fromUtf8(I18N_NOOP("Convert to 16 Bit"));
363     }
364 
365     return QString();
366 }
367 
filterIcon() const368 QString DImgBuiltinFilter::filterIcon() const
369 {
370     switch (m_type)
371     {
372         case NoOperation:
373             break;
374 
375         case Rotate90:
376             return QLatin1String("object-rotate-left");
377 
378         case Rotate180:
379             return QLatin1String("transform-rotate");
380 
381         case Rotate270:
382             return QLatin1String("object-rotate-right");
383 
384         case FlipHorizontally:
385             return QLatin1String("object-flip-horizontal");
386 
387         case FlipVertically:
388             return QLatin1String("object-flip-vertical");
389 
390         case Crop:
391             return QLatin1String("transform-crop");
392 
393         case Resize:
394             return QLatin1String("transform-scale");
395 
396         case ConvertTo8Bit:
397             return QLatin1String("depth16to8");
398 
399         case ConvertTo16Bit:
400             return QLatin1String("depth8to16");
401     }
402 
403     return QString();
404 }
405 
i18nDisplayableName(const QString & filterIdentifier)406 QString DImgBuiltinFilter::i18nDisplayableName(const QString& filterIdentifier)
407 {
408     if      (filterIdentifier == QLatin1String("transform:rotate"))
409     {
410         return i18nc("Rotate image", "Rotate");
411     }
412     else if (filterIdentifier == QLatin1String("transform:flip"))
413     {
414         return i18nc("Flip image", "Flip");
415     }
416     else if (filterIdentifier == QLatin1String("transform:crop"))
417     {
418         return i18nc("Crop image", "Crop");
419     }
420     else if (filterIdentifier == QLatin1String("transform:resize"))
421     {
422         return i18nc("Resize image", "Resize");
423     }
424     else if (filterIdentifier == QLatin1String("transform:convertDepth"))
425     {
426         return i18nc("Convert image bit depth", "Convert Depth");
427     }
428 
429     return QString();
430 }
431 
filterIcon(const QString & filterIdentifier)432 QString DImgBuiltinFilter::filterIcon(const QString& filterIdentifier)
433 {
434     if      (filterIdentifier == QLatin1String("transform:rotate"))
435     {
436         return QLatin1String("transform-rotate");
437     }
438     else if (filterIdentifier == QLatin1String("transform:flip"))
439     {
440         return QLatin1String("object-flip-horizontal");
441     }
442     else if (filterIdentifier == QLatin1String("transform:crop"))
443     {
444         return QLatin1String("transform-crop");
445     }
446     else if (filterIdentifier == QLatin1String("transform:resize"))
447     {
448         return QLatin1String("transform-scale");
449     }
450     else if (filterIdentifier == QLatin1String("transform:convertDepth"))
451     {
452         return QLatin1String("fill-color");
453     }
454 
455     return QString();
456 }
457 
isSupported(const QString & filterIdentifier)458 bool DImgBuiltinFilter::isSupported(const QString& filterIdentifier)
459 {
460     return filterIdentifier.startsWith(QLatin1String("transform:")) && supportedFilters().contains(filterIdentifier);
461 }
462 
isSupported(const QString & filterIdentifier,int version)463 bool DImgBuiltinFilter::isSupported(const QString& filterIdentifier, int version)
464 {
465     if (!isSupported(filterIdentifier))
466     {
467         return false;
468     }
469 
470     // at the moment, all filters are at version 1
471 
472     return (version == 1);
473 }
474 
475 // -------------------------------------------------------------------------------------------------------------------
476 
477 class Q_DECL_HIDDEN DImgBuiltinThreadedFilter : public DImgThreadedFilter
478 {
479     Q_OBJECT
480 
481 public:
482 
DImgBuiltinThreadedFilter(const DImgBuiltinFilter & filter,DImg * const orgImage,QObject * const parent=nullptr)483     explicit DImgBuiltinThreadedFilter(const DImgBuiltinFilter& filter, DImg* const orgImage, QObject* const parent = nullptr)
484         : DImgThreadedFilter(orgImage, parent),
485           m_filter          (filter)
486     {
487     }
488 
DImgBuiltinThreadedFilter(const DImgBuiltinFilter & filter,QObject * const parent=nullptr)489     explicit DImgBuiltinThreadedFilter(const DImgBuiltinFilter& filter, QObject* const parent = nullptr)
490         : DImgThreadedFilter(parent),
491           m_filter          (filter)
492     {
493     }
494 
filterIdentifier() const495     QString filterIdentifier() const override
496     {
497         return m_filter.filterAction().identifier();
498     }
499 
filterAction()500     FilterAction filterAction() override
501     {
502         return m_filter.filterAction();
503     }
504 
readParameters(const FilterAction & action)505     void readParameters(const FilterAction& action) override
506     {
507         m_filter = DImgBuiltinFilter(action);
508     }
509 
510 protected:
511 
filterImage()512     void filterImage() override
513     {
514         m_destImage = m_orgImage;
515         m_filter.apply(m_destImage);
516     }
517 
518     DImgBuiltinFilter m_filter;
519 };
520 
createThreadedFilter(DImg * const orgImage,QObject * const parent) const521 DImgThreadedFilter* DImgBuiltinFilter::createThreadedFilter(DImg* const orgImage, QObject* const parent) const
522 {
523     return new DImgBuiltinThreadedFilter(*this, orgImage, parent);
524 }
525 
createThreadedFilter(QObject * const parent) const526 DImgThreadedFilter* DImgBuiltinFilter::createThreadedFilter(QObject* const parent) const
527 {
528     return new DImgBuiltinThreadedFilter(*this, parent);
529 }
530 
531 } // namespace Digikam
532 
533 #include "dimgbuiltinfilter.moc"
534