MauiKit Image Tools

imagedocument.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2020 Carl Schwan <carl@carlschwan.eu>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6
7#include "imagedocument.h"
8
9#include "commands/cropcommand.h"
10#include "commands/mirrorcommand.h"
11#include "commands/resizecommand.h"
12#include "commands/rotatecommand.h"
13#include "commands/transformcommand.h"
14
15#include <QDebug>
16
17ImageDocument::ImageDocument(QObject *parent)
18 : QObject(parent)
19{
20 m_changesApplied = true;
21
22 connect(this, &ImageDocument::pathChanged, this, [this](const QUrl &url) {
23 m_image = QImage(url.isLocalFile() ? url.toLocalFile() : url.toString());
24 m_originalImage = m_image;
25 m_edited = false;
26 Q_EMIT editedChanged();
27 Q_EMIT imageChanged();
28 });
29}
30
32{
33 while (!m_undos.empty()) {
34 const auto command = m_undos.pop();
35 if(m_undos.isEmpty())
36 m_image = command->undo(m_image);
37 delete command;
38 }
39
40 resetValues();
41 setEdited(false);
42 Q_EMIT imageChanged();
43}
44
45QImage ImageDocument::image() const
46{
47 return m_image;
48}
49
50bool ImageDocument::edited() const
51{
52 return m_edited;
53}
54
56{
57 if(m_undos.empty())
58 {
59 qDebug() << "No more commands to undo";
60 return;
61 }
62
63 const auto command = m_undos.pop();
64 m_image = command->undo();
65 m_originalImage = m_image;
66 delete command;
67 Q_EMIT imageChanged();
68 if (m_undos.empty()) {
69 setEdited(false);
70 }
71}
72
73void ImageDocument::crop(int x, int y, int width, int height)
74{
75 const auto command = new CropCommand(QRect(x, y, width, height));
76 m_image = command->redo(m_image);
77 m_undos.append(command);
78 setEdited(true);
79 Q_EMIT imageChanged();
80}
81
82void ImageDocument::resize(int width, int height)
83{
84 const auto command = new ResizeCommand(QSize(width, height));
85 m_image = command->redo(m_image);
86 m_undos.append(command);
87 setEdited(true);
88 Q_EMIT imageChanged();
89}
90
91void ImageDocument::mirror(bool horizontal, bool vertical)
92{
93 const auto command = new MirrorCommand(horizontal, vertical);
94 m_image = command->redo(m_image);
95
96 m_undos.append(command);
97 setEdited(true);
98 Q_EMIT imageChanged();
99}
100
102{
103 QTransform transform;
104 transform.rotate(angle);
105 const auto command = new RotateCommand(transform);
106 m_image = command->redo(m_image);
107 m_undos.append(command);
108 setEdited(true);
109 Q_EMIT imageChanged();
110}
111
113{
114 m_changesApplied = !value;
115 Q_EMIT changesAppliedChanged();
116
117 if (m_edited == value) {
118 return;
119 }
120 m_edited = value;
121 Q_EMIT editedChanged();
122}
123
125{
126 applyChanges();
127 return m_originalImage.save(m_path.isLocalFile() ? m_path.toLocalFile() : m_path.toString());
128}
129
130bool ImageDocument::saveAs(const QUrl &location)
131{
132 return m_originalImage.save(location.isLocalFile() ? location.toLocalFile() : location.toString());
133}
134
135void ImageDocument::adjustBrightness(int value)
136{
137 if(value == m_brightness)
138 return;
139
140 auto oldValue = m_brightness;
141 m_brightness = value;
142
143 auto transformation = [val = m_brightness](QImage &ref) -> QImage
144 {
145 return Trans::adjustBrightness(ref, val);
146 };
147
148 auto undoCallback = [this, oldValue]()
149 {
150 this->m_brightness = oldValue;
151 Q_EMIT brightnessChanged();
152 };
153
154 const auto command = new TransformCommand(m_image, transformation, undoCallback);
155
156 m_image = command->redo(m_originalImage);
157 m_undos.append(command);
158 Q_EMIT brightnessChanged();
159 setEdited(true);
160 Q_EMIT imageChanged();
161}
162
163void ImageDocument::adjustContrast(int value)
164{
165 if(value == m_contrast)
166 return;
167
168 auto oldValue = m_contrast;
169 m_contrast = value;
170
171 auto transformation = [val = m_contrast](QImage &ref) -> QImage
172 {
173 return Trans::adjustContrast(ref, val);
174 };
175
176 auto undoCallback = [this, oldValue]()
177 {
178 this->m_contrast = oldValue;
179 Q_EMIT contrastChanged();
180 };
181
182 const auto command = new TransformCommand(m_image, transformation, undoCallback);
183
184 m_image = command->redo(m_originalImage);
185 m_undos.append(command);
186 setEdited(true);
187 Q_EMIT contrastChanged();
188 Q_EMIT imageChanged();
189}
190
191void ImageDocument::adjustSaturation(int value)
192{
193 if(m_image.isGrayscale())
194 return;
195
196 if(value == m_saturation)
197 return;
198
199 auto oldValue = m_saturation;
200 m_saturation = value;
201
202 auto transformation = [val = m_saturation](QImage &ref) -> QImage
203 {
204 return Trans::adjustSaturation(ref, val);
205 };
206
207 auto undoCallback = [this, oldValue]()
208 {
209 this->m_saturation = oldValue;
210 Q_EMIT saturationChanged();
211 };
212
213 const auto command = new TransformCommand(m_image, transformation, undoCallback);
214
215 m_image = command->redo(m_originalImage);
216 m_undos.append(command);
217 setEdited(true);
218 Q_EMIT imageChanged();
219 Q_EMIT saturationChanged();
220}
221
222void ImageDocument::adjustHue(int value)
223{
224 qDebug() << "adjust HUE DOCUMENT" << value;
225 if(value == m_hue)
226 return;
227
228 if(m_image.isGrayscale())
229 return;
230
231 auto oldValue = m_hue;
232 m_hue = value;
233
234 auto transformation = [val = m_hue](QImage &ref) -> QImage
235 {
236 return Trans::adjustHue(ref, val);
237 };
238
239 auto undoCallback = [this, oldValue]()
240 {
241 this->m_hue = oldValue;
242 Q_EMIT hueChanged();
243 };
244
245 const auto command = new TransformCommand(m_image, transformation, undoCallback);
246
247 m_image = command->redo(m_originalImage);
248 m_undos.append(command);
249 setEdited(true);
250 Q_EMIT imageChanged();
251 Q_EMIT hueChanged();
252}
253
254void ImageDocument::adjustGamma(int value)
255{
256 qDebug() << "adjust GAMMA DOCUMENT" << value;
257 // if(m_image.isGrayscale())
258 // return;
259
260 if(value == m_gamma)
261 return;
262
263 auto oldValue = m_gamma;
264 m_gamma = value;
265
266 auto transformation = [val = m_gamma](QImage &ref) -> QImage
267 {
268 return Trans::adjustGamma(ref, val);
269 };
270
271 auto undoCallback = [this, oldValue]()
272 {
273 this->m_gamma = oldValue;
274 Q_EMIT gammaChanged();
275 };
276
277 const auto command = new TransformCommand(m_image, transformation, undoCallback);
278
279 m_image = command->redo(m_originalImage);
280 m_undos.append(command);
281 setEdited(true);
282 Q_EMIT imageChanged();
283 Q_EMIT gammaChanged();
284}
285
286void ImageDocument::adjustSharpness(int value)
287{
288 qDebug() << "adjust SHARPNESS DOCUMENT" << value;
289 // if(m_image.isGrayscale())
290 // return;
291
292 if(value == m_sharpness)
293 return;
294
295 auto oldValue = m_sharpness;
296 m_sharpness = value;
297
298 auto transformation = [val = m_sharpness](QImage &ref) -> QImage
299 {
300 return Trans::adjustSharpness(ref, val);
301 };
302
303 auto undoCallback = [this, oldValue]()
304 {
305 this->m_sharpness = oldValue;
306 Q_EMIT sharpnessChanged();
307 };
308
309 const auto command = new TransformCommand(m_image, transformation, undoCallback);
310
311 m_image = command->redo(m_originalImage);
312 m_undos.append(command);
313 setEdited(true);
314 Q_EMIT imageChanged();
315 Q_EMIT sharpnessChanged();
316}
317
318void ImageDocument::adjustThreshold(int value)
319{
320 qDebug() << "adjust threshold DOCUMENT" << value;
321 // if(m_image.isGrayscale())
322 // return;
323
324 if(value == m_threshold)
325 return;
326
327 auto oldValue = m_threshold;
328 m_threshold = value;
329
330 auto transformation = [val = m_threshold](QImage &ref) -> QImage
331 {
332 return Trans::adjustThreshold(ref, val);
333 };
334
335 auto undoCallback = [this, oldValue]()
336 {
337 this->m_threshold = oldValue;
338 Q_EMIT thresholdChanged();
339 };
340
341 const auto command = new TransformCommand(m_image, transformation, undoCallback);
342
343 m_image = command->redo(m_originalImage);
344 m_undos.append(command);
345 setEdited(true);
346 Q_EMIT imageChanged();
347 Q_EMIT thresholdChanged();
348}
349
350void ImageDocument::adjustGaussianBlur(int value)
351{
352 if(value == m_gaussianBlur)
353 return;
354
355 auto oldValue = m_gaussianBlur;
356 m_gaussianBlur = value;
357 auto transformation = [val = m_gaussianBlur](QImage &ref) -> QImage
358 {
359 qDebug() << "SXetting gaussian blur" << val;
360 return Trans::adjustGaussianBlur(ref, val);
361 };
362
363 auto undoCallback = [this, oldValue]()
364 {
365 this->m_gaussianBlur = oldValue;
366 Q_EMIT gaussianBlurChanged();
367 };
368
369 const auto command = new TransformCommand(m_image, transformation, undoCallback);
370 m_image = command->redo(m_originalImage);
371 m_undos.append(command);
372 setEdited(true);
373 Q_EMIT imageChanged();
374 Q_EMIT gaussianBlurChanged();
375}
376
377void ImageDocument::toGray()
378{
379 const auto command = new TransformCommand(m_image, &Trans::toGray, nullptr);
380
381 m_image = command->redo(m_originalImage);
382 m_undos.append(command);
383 setEdited(true);
384 Q_EMIT imageChanged();
385}
386
387void ImageDocument::toBW()
388{
389 const auto command = new TransformCommand(m_image, &Trans::toBlackAndWhite, nullptr);
390
391 m_image = command->redo(m_originalImage);
392 m_undos.append(command);
393 setEdited(true);
394 Q_EMIT imageChanged();
395}
396
397void ImageDocument::toSketch()
398{
399 const auto command = new TransformCommand(m_image, &Trans::sketch, nullptr);
400
401 m_image = command->redo(m_originalImage);
402 m_undos.append(command);
403 setEdited(true);
404 Q_EMIT imageChanged();
405}
406
407void ImageDocument::addVignette()
408{
409 const auto command = new TransformCommand(m_image, &Trans::vignette, nullptr);
410
411 m_image = command->redo(m_originalImage);
412 m_undos.append(command);
413 setEdited(true);
414 Q_EMIT imageChanged();
415}
416
417auto borderTrans(int thickness, const QColor &color, QImage &ref)
418{
419 return Trans::addBorder(ref, thickness, color);
420}
421
422void ImageDocument::addBorder(int thickness, const QColor &color)
423{
424 qDebug() << "SXetting add border blur" << thickness << color << color.red() << color.green()<<color.blue();
425 auto transformation = [&](QImage &ref) -> QImage
426 {
427 qDebug() << "SXetting add border blur2" << thickness << color;
428 return Trans::addBorder(ref, thickness, color);
429 };
430
431 const auto command = new TransformCommand(m_image, transformation, nullptr);
432
433 m_image = command->redo(m_originalImage);
434 m_undos.append(command);
435 setEdited(true);
436 Q_EMIT imageChanged();
437}
438
439void ImageDocument::applyChanges()
440{
441 resetValues();
442
443 m_originalImage = m_image;
444 m_changesApplied = true;
445 Q_EMIT changesAppliedChanged();
446}
447
448int ImageDocument::brightness() const
449{
450 return m_brightness;
451}
452
453int ImageDocument::contrast() const
454{
455 return m_contrast;
456}
457
458int ImageDocument::saturation() const
459{
460 return m_saturation;
461}
462
463int ImageDocument::hue() const
464{
465 return m_hue;
466}
467
468int ImageDocument::gamma() const
469{
470 return m_gamma;
471}
472
473int ImageDocument::sharpness() const
474{
475 return m_sharpness;
476}
477
478int ImageDocument::threshold() const
479{
480 return m_threshold;
481}
482
483int ImageDocument::gaussianBlur() const
484{
485 return m_gaussianBlur;
486}
487
488QUrl ImageDocument::path() const
489{
490 return m_path;
491}
492
493void ImageDocument::setPath(const QUrl &path)
494{
495 m_path = path;
496 Q_EMIT pathChanged(path);
497}
498
499QRectF ImageDocument::area() const
500{
501 return m_area;
502}
503
504void ImageDocument::setArea(const QRectF &newArea)
505{
506 if (m_area == newArea)
507 return;
508 m_area = newArea;
509 Q_EMIT areaChanged();
510}
511
512void ImageDocument::resetArea()
513{
514 setArea({}); // TODO: Adapt to use your actual default value
515}
516
517void ImageDocument::resetValues()
518{
519 m_contrast = 0;
520 m_brightness = 0;
521 m_saturation = 0;
522 m_hue = 0;
523 m_gamma = 0;
524 m_sharpness = 0;
525 m_threshold = 0;
526 Q_EMIT hueChanged();
527 Q_EMIT saturationChanged();
528 Q_EMIT brightnessChanged();
529 Q_EMIT contrastChanged();
530 Q_EMIT gammaChanged();
531 Q_EMIT thresholdChanged();
532 Q_EMIT sharpnessChanged();
533}
534
535bool ImageDocument::changesApplied() const
536{
537 return m_changesApplied;
538}
CropCommand that crop the current image.
Definition cropcommand.h:18
Q_INVOKABLE void undo()
Undo the last edit on the images.
Q_INVOKABLE void rotate(int angle)
Rotate the image.
Q_INVOKABLE bool saveAs(const QUrl &location)
Save current edited image as a new image.
Q_INVOKABLE bool save()
Save current edited image in place.
Q_INVOKABLE void mirror(bool horizontal, bool vertical)
Mirror the image.
Q_INVOKABLE void crop(int x, int y, int width, int height)
Crop the image.
Q_INVOKABLE void cancel()
Cancel all the edit.
void setEdited(bool value)
Change the edited value.
Q_INVOKABLE void resize(int width, int height)
Resize the image.
MirrorCommand that mirror an image horizontally or vertically.
ResizeCommand that resizes the current image.
RotateCommand that rotates the current image.
int blue() const const
int green() const const
int red() const const
Q_EMITQ_EMIT
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isLocalFile() const const
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri May 2 2025 11:56:08 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.