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