Merge lp:~artmello/gallery-app/gallery-app-remove_local_photoeditor into lp:gallery-app

Proposed by Arthur Mello
Status: Merged
Approved by: Bill Filler
Approved revision: 1256
Merged at revision: 1258
Proposed branch: lp:~artmello/gallery-app/gallery-app-remove_local_photoeditor
Merge into: lp:gallery-app
Diff against target: 3650 lines (+3/-3449)
28 files modified
rc/qml/MediaViewer/GalleryPhotoEditorPage.qml (+0/-42)
rc/qml/MediaViewer/MediaViewer.qml (+3/-4)
rc/qml/MediaViewer/PhotoEditor.qml (+0/-230)
rc/qml/MediaViewer/PhotoEditor/ActionsBar.qml (+0/-88)
rc/qml/MediaViewer/PhotoEditor/BusyIndicator.qml (+0/-56)
rc/qml/MediaViewer/PhotoEditor/CropCorner.qml (+0/-66)
rc/qml/MediaViewer/PhotoEditor/CropDragArea.qml (+0/-56)
rc/qml/MediaViewer/PhotoEditor/CropInteractor.qml (+0/-140)
rc/qml/MediaViewer/PhotoEditor/CropOverlay.qml (+0/-581)
rc/qml/MediaViewer/PhotoEditor/EditStack.qml (+0/-134)
rc/qml/MediaViewer/PhotoEditor/ExposureAdjuster.qml (+0/-104)
rc/qml/MediaViewer/PhotoEditor/GraphicsRoutines.js (+0/-108)
src/gallery-application.cpp (+0/-13)
src/gallery-application.h (+0/-2)
src/photoeditor/CMakeLists.txt (+0/-13)
src/photoeditor/file-utils.cpp (+0/-97)
src/photoeditor/file-utils.h (+0/-42)
src/photoeditor/imaging.cpp (+0/-363)
src/photoeditor/imaging.h (+0/-169)
src/photoeditor/orientation.cpp (+0/-152)
src/photoeditor/orientation.h (+0/-64)
src/photoeditor/photo-caches.cpp (+0/-183)
src/photoeditor/photo-caches.h (+0/-76)
src/photoeditor/photo-data.cpp (+0/-275)
src/photoeditor/photo-data.h (+0/-92)
src/photoeditor/photo-edit-command.h (+0/-58)
src/photoeditor/photo-edit-thread.cpp (+0/-185)
src/photoeditor/photo-edit-thread.h (+0/-56)
To merge this branch: bzr merge lp:~artmello/gallery-app/gallery-app-remove_local_photoeditor
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
Ubuntu Phablet Team Pending
Review via email: mp+280715@code.launchpad.net

Commit message

Remove local copy of PhotoEditor and keep using only the Ubuntu Extras one

Description of the change

Remove local copy of PhotoEditor and keep using only the Ubuntu Extras one

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== removed file 'rc/qml/MediaViewer/GalleryPhotoEditorPage.qml'
--- rc/qml/MediaViewer/GalleryPhotoEditorPage.qml 2015-11-05 18:10:29 +0000
+++ rc/qml/MediaViewer/GalleryPhotoEditorPage.qml 1970-01-01 00:00:00 +0000
@@ -1,42 +0,0 @@
1/*
2 * Copyright (C) 2014-2015 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19
20Page {
21 id: page
22 property string photo
23 signal done(bool photoWasModified)
24
25 title: i18n.tr("Edit Photo")
26
27 head.backAction: Action {
28 iconName: "back"
29 onTriggered: editor.close(true)
30 }
31 head.actions: editor.actions
32
33 PhotoEditor {
34 id: editor
35 anchors.fill: parent
36 onClosed: page.done(photoWasModified)
37 }
38
39 onActiveChanged: {
40 if (active) editor.open(page.photo)
41 }
42}
430
=== modified file 'rc/qml/MediaViewer/MediaViewer.qml'
--- rc/qml/MediaViewer/MediaViewer.qml 2015-11-05 18:10:29 +0000
+++ rc/qml/MediaViewer/MediaViewer.qml 2015-12-16 13:52:17 +0000
@@ -325,11 +325,10 @@
325 var editor;325 var editor;
326 try {326 try {
327 Qt.createQmlObject('import QtQuick 2.4; import Ubuntu.Components.Extras 0.2; Item {}', viewerWrapper);327 Qt.createQmlObject('import QtQuick 2.4; import Ubuntu.Components.Extras 0.2; Item {}', viewerWrapper);
328 console.log("Loading PhotoEditor Components from Extras");328 editor = overview.pushPage(Qt.resolvedUrl("PhotoEditorPage.qml"), { photo: path });
329 editor = overview.pushPage(Qt.resolvedUrl("ExtrasPhotoEditorPage.qml"), { photo: path });
330 } catch (e) {329 } catch (e) {
331 console.log("Loading PhotoEditor Components from Gallery code");330 console.log("WARNING: Unable to load PhotoEditor from Ubuntu.Components.Extras");
332 editor = overview.pushPage(Qt.resolvedUrl("GalleryPhotoEditorPage.qml"), { photo: path });331 return;
333 }332 }
334 editor.done.connect(function(photoWasModified) {333 editor.done.connect(function(photoWasModified) {
335 if (photoWasModified) galleryPhotoViewer.media.dataChanged();334 if (photoWasModified) galleryPhotoViewer.media.dataChanged();
336335
=== removed directory 'rc/qml/MediaViewer/PhotoEditor'
=== removed file 'rc/qml/MediaViewer/PhotoEditor.qml'
--- rc/qml/MediaViewer/PhotoEditor.qml 2015-11-05 19:04:22 +0000
+++ rc/qml/MediaViewer/PhotoEditor.qml 1970-01-01 00:00:00 +0000
@@ -1,230 +0,0 @@
1/*
2 * Copyright (C) 2014-2015 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19import Ubuntu.Components.Popups 1.3
20import Gallery 1.0
21import "PhotoEditor"
22
23Item {
24 id: editor
25 property string photo
26 property bool modified: stack.modified
27
28 signal closed(bool photoWasModified)
29
30 property list<Action> actions
31 actions: [stack.undoAction, stack.redoAction]
32
33 EditStack {
34 id: stack
35 data: photoData
36 actionsEnabled: !exposureSelector.visible && !cropper.visible && !photoData.busy
37 onRevertRequested: PopupUtils.open(revertPromptComponent)
38 }
39
40 property list<Action> toolActions: [
41 Action {
42 objectName: "cropButton"
43 text: i18n.tr("Crop")
44 iconSource: Qt.resolvedUrl("PhotoEditor/assets/edit_crop.png")
45 onTriggered: {
46 photoData.isLongOperation = false;
47 cropper.start("image://photo/" + photoData.path);
48 }
49 },
50 Action {
51 objectName: "rotateButton"
52 text: i18n.tr("Rotate")
53 iconSource: Qt.resolvedUrl("PhotoEditor/assets/edit_rotate_left.png")
54 onTriggered: {
55 photoData.isLongOperation = false;
56 photoData.rotateRight()
57 }
58 }
59 ]
60
61 function close(saveIfModified) {
62 stack.endEditingSession(saveIfModified);
63 editor.closed(editor.modified);
64 }
65
66 function open(photo) {
67 editor.photo = photo;
68 stack.startEditingSession(photo);
69 photoData.path = stack.currentFile;
70 image.source = "image://photo/" + photoData.path;
71 }
72
73 Rectangle {
74 color: "black"
75 anchors.fill: parent
76 }
77
78 Image {
79 id: image
80 anchors.fill: parent
81 asynchronous: true
82 cache: false
83 source: photoData.path ? "image://photo/" + photoData.path : ""
84 fillMode: Image.PreserveAspectFit
85 sourceSize {
86 width: image.width
87 height: image.height
88 }
89
90 function reload() {
91 image.asynchronous = false;
92 image.source = "";
93 image.asynchronous = true;
94 image.source = "image://photo/" + photoData.path;
95 }
96 }
97
98 GalleryPhotoData {
99 id: photoData
100 onDataChanged: image.reload()
101 property bool isLongOperation: false
102
103 onEditFinished: {
104 console.log("Edit finished")
105 // If we are editing exposure we don't need to checkpoint at every
106 // edit, and the exposure UI will checkpoint when the user confirms.
107 if (exposureSelector.opacity > 0) exposureSelector.reload()
108 else stack.checkpoint()
109 }
110 }
111
112 Loader {
113 id: cropper
114
115 anchors.fill: parent
116
117 opacity: 0.0
118 visible: opacity > 0
119 Behavior on opacity { UbuntuNumberAnimation { } }
120
121 Connections {
122 target: cropper.item
123 ignoreUnknownSignals: true
124 onCropped: {
125 var qtRect = Qt.rect(rect.x, rect.y, rect.width, rect.height);
126 photoData.crop(qtRect);
127 cropper.opacity = 0.0;
128 cropper.source = ""
129 }
130 onCanceled: {
131 cropper.opacity = 0.0;
132 cropper.source = ""
133 }
134 }
135
136 function start(target) {
137 source = "PhotoEditor/CropInteractor.qml";
138 item.targetPhoto = target;
139 }
140
141 onLoaded: opacity = 1.0
142 }
143
144 ExposureAdjuster {
145 id: exposureSelector
146 anchors.fill: parent
147 opacity: 0.0
148 enabled: !photoData.busy
149 onExposureChanged: {
150 // Restore the starting version of the image, otherwise we will
151 // accumulate compensations over the previous ones.
152 stack.restoreSnapshot(stack.level)
153 photoData.exposureCompensation(exposure)
154 }
155 onConfirm: {
156 stack.checkpoint();
157 exposureSelector.opacity = 0.0
158 }
159 onCancel: {
160 stack.restoreSnapshot(stack.level)
161 exposureSelector.opacity = 0.0
162 }
163 visible: opacity > 0
164 }
165
166 ActionsBar {
167 id: actionsBar
168 objectName: "editorActionsBar"
169 anchors.bottom: parent.bottom
170 anchors.left: parent.left
171 anchors.right: parent.right
172
173 visible: opacity > 0.0
174 opacity: (exposureSelector.opacity == 0 && cropper.opacity == 0) ? 1.0 : 0.0
175
176 enabled: !photoData.busy
177 toolActions: {
178 // This is necessary because QML does not let us declare a list with
179 // mixed component declarations and identifiers, like this:
180 // property list<Action> foo: { Action{}, someOtherAction }
181 var list = [];
182 for (var i = 0; i < editor.toolActions.length; i++)
183 list.push(editor.toolActions[i]);
184 list.push(stack.revertAction);
185 return list;
186 }
187
188 Behavior on opacity { UbuntuNumberAnimation {} }
189 }
190
191 Component {
192 id: revertPromptComponent
193 Dialog {
194 id: revertPrompt
195 objectName: "revertPromptDialog"
196 title: i18n.tr("Revert to original")
197 text: i18n.tr("This will undo all edits, including those from previous sessions.")
198
199 Row {
200 id: row
201 width: parent.width
202 spacing: units.gu(1)
203 Button {
204 objectName: "cancelRevertButton"
205 width: parent.width/2
206 text: i18n.tr("Cancel")
207 onClicked: PopupUtils.close(revertPrompt)
208 }
209 Button {
210 objectName: "confirmRevertButton"
211 width: parent.width/2
212 text: i18n.tr("Revert Photo")
213 color: UbuntuColors.green
214 onClicked: {
215 PopupUtils.close(revertPrompt)
216 stack.revertToPristine()
217 }
218 }
219 }
220 }
221 }
222
223 BusyIndicator {
224 id: busyIndicator
225 anchors.centerIn: parent
226 text: i18n.tr("Enhancing photo...")
227 running: photoData.busy
228 longOperation: photoData.isLongOperation
229 }
230}
2310
=== removed file 'rc/qml/MediaViewer/PhotoEditor/ActionsBar.qml'
--- rc/qml/MediaViewer/PhotoEditor/ActionsBar.qml 2015-11-05 19:04:22 +0000
+++ rc/qml/MediaViewer/PhotoEditor/ActionsBar.qml 1970-01-01 00:00:00 +0000
@@ -1,88 +0,0 @@
1/*
2 * Copyright (C) 2014-2015 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19import Ubuntu.Components.ListItems 1.3 as ListItem
20
21Column {
22 id: bar
23 property list<Action> toolActions
24 property list<Action> filterActions
25 property bool enabled
26
27 height: (filtersBar.visible) ? units.gu(20) : units.gu(6)
28
29 Item {
30 anchors.left: parent.left
31 anchors.right: parent.right
32 height: units.gu(6)
33
34 Rectangle {
35 anchors.fill: parent
36 color: "black"
37 opacity: 0.6
38 }
39
40 ListView {
41 id: toolsBar
42 anchors.fill: parent
43 orientation: ListView.Horizontal
44 model: toolActions
45
46 delegate: AbstractButton {
47 width: units.gu(8)
48 anchors.top: parent.top
49 anchors.bottom: parent.bottom
50 action: modelData
51 enabled: bar.enabled
52
53 Icon {
54 anchors.centerIn: parent
55 name: modelData.iconName
56 source: modelData.iconSource
57 width: units.gu(3)
58 height: units.gu(3)
59 opacity: modelData.enabled && parent.enabled ? 1.0 : 0.5
60 }
61 }
62 }
63 }
64
65 Rectangle {
66 anchors.left: parent.left
67 anchors.right: parent.right
68 height: units.gu(14)
69 color: "black"
70
71 ListView {
72 id: filtersBar
73 visible: filterActions.length > 0
74
75 orientation: ListView.Horizontal
76 model: filterActions
77
78 delegate: ListItem.Standard {
79 width: parent.height
80 anchors.top: parent.top
81 anchors.bottom: parent.bottom
82 action: modelData
83 iconFrame: false
84 enabled: bar.enabled
85 }
86 }
87 }
88}
890
=== removed file 'rc/qml/MediaViewer/PhotoEditor/BusyIndicator.qml'
--- rc/qml/MediaViewer/PhotoEditor/BusyIndicator.qml 2015-11-05 18:10:29 +0000
+++ rc/qml/MediaViewer/PhotoEditor/BusyIndicator.qml 1970-01-01 00:00:00 +0000
@@ -1,56 +0,0 @@
1/*
2 * Copyright (C) 2015 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19
20Item {
21 id: busy
22 width: childrenRect.width
23 height: childrenRect.height
24 property alias text: label.text
25 property alias running: spinner.running
26 property bool longOperation: false
27
28 visible: running
29
30 UbuntuShape {
31 color: "white"
32 anchors.centerIn: parent
33 width: parent.width + units.gu(4)
34 height: parent.height + units.gu(4)
35 opacity: longOperation ? 0.75 : 0
36 }
37
38 Column {
39 id: column
40 anchors.centerIn: parent
41 width: childrenRect.width
42 spacing: units.gu(2)
43
44 ActivityIndicator {
45 id: spinner
46 anchors.horizontalCenter: parent.horizontalCenter
47 }
48
49 Label {
50 id: label
51 anchors.horizontalCenter: parent.horizontalCenter
52 horizontalAlignment: Text.AlignHCenter
53 visible: longOperation
54 }
55 }
56}
570
=== removed file 'rc/qml/MediaViewer/PhotoEditor/CropCorner.qml'
--- rc/qml/MediaViewer/PhotoEditor/CropCorner.qml 2015-11-05 19:04:22 +0000
+++ rc/qml/MediaViewer/PhotoEditor/CropCorner.qml 1970-01-01 00:00:00 +0000
@@ -1,66 +0,0 @@
1/*
2 * Copyright (C) 2012-2015 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Lindsay <chaz@yorba.org>
18 */
19
20import QtQuick 2.4
21import Ubuntu.Components 1.3
22
23// A corner of a CropFrame.
24Item {
25 id: cropCorner
26
27 /*!
28 */
29 signal dragged(real dx, real dy)
30 /*!
31 */
32 signal dragStarted()
33 /*!
34 */
35 signal dragCompleted()
36
37 /*!
38 */
39 property bool isLeft: true
40 /*!
41 */
42 property bool isTop: true
43
44 x: isLeft ? -(width/2) : parent.width - (width/2)
45 y: isTop ? -(width/2) : parent.height - (width/2)
46 width: handle.width
47 height: handle.height
48
49 Image {
50 id: handle
51 anchors.centerIn: parent
52 source: Qt.resolvedUrl("assets/crop-handle.png")
53 }
54
55 CropDragArea {
56 anchors.centerIn: parent
57 width: handle.width + units.gu(2)
58 height: handle.height + units.gu(2)
59
60 onDragged: cropCorner.dragged(dx, dy)
61
62 onDragStarted: cropCorner.dragStarted()
63
64 onDragCompleted: cropCorner.dragCompleted()
65 }
66}
670
=== removed file 'rc/qml/MediaViewer/PhotoEditor/CropDragArea.qml'
--- rc/qml/MediaViewer/PhotoEditor/CropDragArea.qml 2015-11-05 19:04:22 +0000
+++ rc/qml/MediaViewer/PhotoEditor/CropDragArea.qml 1970-01-01 00:00:00 +0000
@@ -1,56 +0,0 @@
1/*
2 * Copyright (C) 2012-2015 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Lindsay <chaz@yorba.org>
18 */
19
20import QtQuick 2.4
21
22// A MouseArea meant to drag a corner/edge of a crop area.
23MouseArea {
24 id: cropDragArea
25
26 /*!
27 */
28 signal dragged(real dx, real dy)
29 /*!
30 */
31 signal dragStarted()
32 /*!
33 */
34 signal dragCompleted()
35
36 // Since we're usually moving this area with the mouse in response to
37 // dragging, we don't need to capture the last x/y, just where it was
38 // grabbed.
39 property real grabX: -1
40 /*!
41 */
42 property real grabY: -1
43
44 onPressed: {
45 dragStarted();
46
47 grabX = mouse.x;
48 grabY = mouse.y;
49 }
50
51 onReleased: {
52 dragCompleted();
53 }
54
55 onPositionChanged: cropDragArea.dragged(mouse.x - grabX, mouse.y - grabY)
56}
570
=== removed file 'rc/qml/MediaViewer/PhotoEditor/CropInteractor.qml'
--- rc/qml/MediaViewer/PhotoEditor/CropInteractor.qml 2015-11-05 19:04:22 +0000
+++ rc/qml/MediaViewer/PhotoEditor/CropInteractor.qml 1970-01-01 00:00:00 +0000
@@ -1,140 +0,0 @@
1/*
2 * Copyright (C) 2012-2015 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Lindsay <chaz@yorba.org>
18 * Lucas Beeler <lucas@yorba.org>
19 */
20
21import QtQuick 2.4
22import Ubuntu.Components 1.3
23import "GraphicsRoutines.js" as GraphicsRoutines
24
25/*!
26*/
27Rectangle {
28 id: cropInteractor
29 objectName: "cropInteractor"
30
31 color: "black"
32
33 property alias targetPhoto: original.source
34
35 property string matteColor: "black"
36 property real matteOpacity: 0.6
37
38 // Note: each element of the cropped rect will be in the range [0,1], since
39 // in the UI we aren't using direct photo pixel values.
40 signal cropped(variant rect)
41 signal canceled()
42
43 function computeRectSet() {
44 var actualImage = Qt.rect(
45 (original.width - original.paintedWidth) / 2.0,
46 (original.height - original.paintedHeight) / 2.0,
47 original.paintedWidth,
48 original.paintedHeight
49 );
50 var photoPreview = GraphicsRoutines.fitRect(viewport, actualImage);
51
52 var unfitCrop = Qt.rect(0, 0, photoPreview.width, photoPreview.height);
53 var cropFrame = GraphicsRoutines.fitRect(viewport, unfitCrop);
54
55 var photoExtent = Qt.rect(cropFrame.x, cropFrame.y,
56 cropFrame.scaleFactor * photoPreview.width,
57 cropFrame.scaleFactor * photoPreview.height);
58
59 return {
60 photoPreview: photoPreview,
61 cropFrame: cropFrame,
62 photoExtent: photoExtent,
63 photoExtentScale: cropFrame.scaleFactor
64 };
65 }
66
67 Item {
68 id: viewport
69
70 anchors.fill: parent
71 anchors.margins: units.gu(6)
72 z: 1
73 }
74
75 CropOverlay {
76 id: overlay
77 objectName: "cropOverlay"
78
79 property real minSize: units.gu(4)
80
81 anchors.fill: parent;
82 visible: false;
83
84 photo: original
85 viewport: viewport
86
87 matteColor: cropInteractor.matteColor
88 matteOpacity: cropInteractor.matteOpacity
89
90 z: 16
91
92 onMatteRegionPressed: {
93 cropInteractor.canceled();
94 }
95
96 onCropButtonPressed: {
97 original.visible = false;
98 overlay.visible = false;
99 original.scale = 1.0;
100 var r = overlay.getRelativeFrameRect()
101 cropInteractor.cropped(overlay.getRelativeFrameRect());
102 }
103 }
104
105 Image {
106 id: original
107
108 x: viewport.x
109 y: viewport.y
110 width: viewport.width
111 height: viewport.height
112 transformOrigin: Item.TopLeft
113 fillMode: Image.PreserveAspectFit
114 cache: false
115 sourceSize {
116 width: original.width
117 height: original.height
118 }
119
120 onStatusChanged: {
121 if (status == Image.Ready) {
122 var rects = computeRectSet();
123
124 overlay.initialFrameX = rects.cropFrame.x;
125 overlay.initialFrameY = rects.cropFrame.y;
126 overlay.initialFrameWidth = rects.cropFrame.width;
127 overlay.initialFrameHeight = rects.cropFrame.height;
128
129 overlay.resetFor(rects);
130 overlay.visible = true;
131
132 x = rects.photoExtent.x;
133 y = rects.photoExtent.y;
134 width = rects.photoPreview.width;
135 height = rects.photoPreview.height;
136 scale = rects.photoExtentScale;
137 }
138 }
139 }
140}
1410
=== removed file 'rc/qml/MediaViewer/PhotoEditor/CropOverlay.qml'
--- rc/qml/MediaViewer/PhotoEditor/CropOverlay.qml 2015-11-05 19:04:22 +0000
+++ rc/qml/MediaViewer/PhotoEditor/CropOverlay.qml 1970-01-01 00:00:00 +0000
@@ -1,581 +0,0 @@
1/*
2 * Copyright (C) 2012-2015 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Lindsay <chaz@yorba.org>
18 * Lucas Beeler <lucas@yorba.org>
19 */
20
21import QtQuick 2.4
22import Ubuntu.Components 1.3
23import "GraphicsRoutines.js" as GraphicsRoutines
24
25/* A CropOverlay is a semi-transparent surface that floats over the photo. It
26 * serves two purposes. First, it provides visual cueing as to what region of
27 * the photo's surface will be preserved when the crop operation is applied.
28 * The preserved region is the region that falls inside of the CropOverlay's
29 * frame. Second, the CropOverlay allows the user to manipulate the
30 * geometry of the crop frame, to chage its location, width, and height. The
31 * geometry of the crop frame is reinforced by a key visual cue: the region of
32 * the photo outside of the crop frame is drawn with a semi-transparent, smoked
33 * matte on top of it. This matte surrounds the crop frame.
34 */
35Item {
36 id: cropOverlay
37
38 // public properties
39 /*!
40 */
41 property Item viewport
42 /*!
43 */
44 property Item photo
45 /*!
46 */
47 property string matteColor: "red"
48 /*!
49 */
50 property real matteOpacity: 0.85
51 /*!
52 */
53 property int initialFrameX: -1
54 /*!
55 */
56 property int initialFrameY: -1
57 /*!
58 */
59 property int initialFrameWidth: -1
60 /*!
61 */
62 property int initialFrameHeight: -1
63
64 // private properties -- Frame Fit Animation parameters
65 property real interpolationFactor: 1.0
66 /*!
67 */
68 property variant startFrame
69 /*!
70 */
71 property variant endFrame
72 /*!
73 */
74 property variant startPhoto
75 /*!
76 */
77 property real referencePhotoWidth: -1
78 /*!
79 */
80 property real referencePhotoHeight: -1
81 /*!
82 */
83 property real endPhotoX
84 /*!
85 */
86 property real endPhotoY
87 /*!
88 */
89 property real endPhotoWidth
90 /*!
91 */
92 property real endPhotoHeight
93
94 /*!
95 */
96 signal userAlteredFrame()
97 /*!
98 */
99 signal runFrameFitAnimation()
100 /*!
101 */
102 signal matteRegionPressed()
103 /*!
104 */
105 signal cropButtonPressed()
106
107 /*!
108 */
109 function resetFor(rectSet) {
110 if (initialFrameX != -1 && initialFrameY != -1 && initialFrameWidth != -1 &&
111 initialFrameHeight != -1) {
112 frame.x = rectSet.cropFrame.x;
113 frame.y = rectSet.cropFrame.y;
114 frame.width = rectSet.cropFrame.width;
115 frame.height = rectSet.cropFrame.height;
116 photoExtent.x = rectSet.photoExtent.x;
117 photoExtent.y = rectSet.photoExtent.y;
118 photoExtent.width = rectSet.photoExtent.width;
119 photoExtent.height = rectSet.photoExtent.height;
120 referencePhotoWidth = rectSet.photoPreview.width;
121 referencePhotoHeight = rectSet.photoPreview.height;
122 }
123 }
124
125 /* Return the (x, y) position and the width and height of the viewport
126 */
127 function getViewportExtentRect() {
128 return GraphicsRoutines.cloneRect(viewport);
129 }
130
131 /* Return the (x, y) position and the width and height of the photoExtent.
132 * The photoExtent is the on-screen region that holds the original photo
133 * preview.
134 */
135 function getPhotoExtentRect() {
136 return GraphicsRoutines.cloneRect(photoExtent);
137 }
138
139 /*!
140 */
141 function getRelativeFrameRect() {
142 return GraphicsRoutines.getRelativeRect(frame.getExtentRect(),
143 getPhotoExtentRect());
144 }
145
146 anchors.fill: parent
147
148 Item {
149 id: photoExtent
150
151 property real panStartX
152 property real panStartY
153
154 function startPan() {
155 panStartX = x;
156 panStartY = y;
157 }
158
159 // 'deltaX' and 'deltaY' are offsets relative to the pan start point
160 function updatePan(deltaX, deltaY) {
161 var newX = panStartX + deltaX;
162 var newY = panStartY + deltaY;
163
164 x = GraphicsRoutines.clamp(newX, frame.x + frame.width -
165 photoExtent.width, frame.x);
166 y = GraphicsRoutines.clamp(newY, frame.y + frame.height -
167 photoExtent.height, frame.y);
168 }
169
170 function stopPan() {
171 }
172
173 x: initialFrameX
174 y: initialFrameY
175 width: initialFrameWidth
176 height: initialFrameHeight
177 z: 1
178
179 onXChanged: {
180 if (photo)
181 photo.x = x;
182 }
183
184 onYChanged: {
185 if (photo)
186 photo.y = y;
187 }
188
189 onWidthChanged: {
190 if (photo && referencePhotoWidth > 0)
191 photo.scale = width / referencePhotoWidth;
192 }
193
194 onHeightChanged: {
195 if (photo && referencePhotoHeight > 0)
196 photo.scale = height / referencePhotoHeight;
197 }
198 }
199
200 //
201 // The following four Rectangles are used to "matte out" the area of the photo
202 // preview that falls outside the frame. This "matting out" visual cue is
203 // accomplished by darkening the matted-out area with a translucent, smoked
204 // overlay.
205 //
206 Rectangle {
207 id: leftMatte
208
209 color: cropOverlay.matteColor
210 opacity: cropOverlay.matteOpacity
211
212 anchors.top: topMatte.bottom
213 anchors.bottom: frame.bottom
214 anchors.left: parent.left
215 anchors.right: frame.left
216
217 MouseArea {
218 anchors.fill: parent;
219
220 onPressed: cropOverlay.matteRegionPressed();
221 }
222 }
223
224 Rectangle {
225 id: topMatte
226
227 color: cropOverlay.matteColor
228 opacity: cropOverlay.matteOpacity
229
230 anchors.top: parent.top
231 anchors.bottom: frame.top
232 anchors.left: parent.left
233 anchors.right: parent.right
234
235 MouseArea {
236 anchors.fill: parent;
237
238 onPressed: cropOverlay.matteRegionPressed();
239 }
240 }
241
242 Rectangle {
243 id: rightMatte
244
245 color: cropOverlay.matteColor
246 opacity: cropOverlay.matteOpacity
247
248 anchors.top: topMatte.bottom
249 anchors.bottom: bottomMatte.top
250 anchors.left: frame.right
251 anchors.right: parent.right
252
253 MouseArea {
254 anchors.fill: parent;
255
256 onPressed: cropOverlay.matteRegionPressed();
257 }
258 }
259
260 Rectangle {
261 id: bottomMatte
262
263 color: cropOverlay.matteColor
264 opacity: cropOverlay.matteOpacity
265
266 anchors.top: frame.bottom
267 anchors.bottom: parent.bottom
268 anchors.left: parent.left
269 anchors.right: parent.right
270
271 MouseArea {
272 anchors.fill: parent;
273
274 onPressed: cropOverlay.matteRegionPressed();
275 }
276 }
277
278 //
279 // The frame is a grey rectangle with associated drag corners that
280 // frames the region of the photo that will remain when the crop operation is
281 // applied.
282 //
283 // NB: the frame can be in two states, although the QML state mechanism
284 // isn't sufficiently expressive to describe them. The frame can be
285 // in the FIT state, in which case it is optimally fit inside the
286 // frame constraint region (see getFrameConstraintRect( ) above for
287 // a description of the frame constraint region). Or, the frame can
288 // be in the USER state. In the user state, the user has the mouse button
289 // held down and is actively performing a drag operation to change the
290 // geometry of the frame.
291 //
292 Rectangle {
293 id: frame
294
295 signal resizedX(bool left, real dx)
296 signal resizedY(bool top, real dy)
297
298 property variant dragStartRect
299
300 function getExtentRect() {
301 var result = { };
302
303 result.x = x;
304 result.y = y;
305 result.width = width;
306 result.height = height;
307
308 return result;
309 }
310
311 x: cropOverlay.initialFrameX
312 y: cropOverlay.initialFrameY
313 width: cropOverlay.initialFrameWidth
314 height: cropOverlay.initialFrameHeight
315
316 color: "transparent"
317
318 border.width: units.gu(0.2)
319 border.color: "#19B6EE"
320
321 MouseArea {
322 id: panArea
323
324 property int dragStartX;
325 property int dragStartY;
326
327 anchors.fill: parent
328 anchors.margins: 2
329
330 onPressed: {
331 dragStartX = mouse.x;
332 dragStartY = mouse.y;
333
334 photoExtent.startPan();
335 }
336
337 onReleased: {
338 photoExtent.stopPan();
339 }
340
341 onPositionChanged: {
342 photoExtent.updatePan(mouse.x - dragStartX, mouse.y - dragStartY);
343 }
344 }
345
346 Button {
347 objectName: "centerCropIcon"
348 anchors.centerIn: parent
349 text: i18n.tr("Crop")
350 color: frame.border.color
351 opacity: 0.9
352 onClicked: cropOverlay.cropButtonPressed()
353 }
354
355 // Left drag bar.
356 CropDragArea {
357 x: -units.gu(2)
358 width: units.gu(3)
359 anchors.verticalCenter: parent.center
360 height: parent.height - units.gu(2)
361
362 onDragged: {
363 frame.resizedX(true, dx);
364 frame.updateOnAltered(false);
365 }
366
367 onDragStarted: frame.dragStartRect = frame.getExtentRect();
368 onDragCompleted: frame.updateOnAltered(true);
369 }
370
371 // Top drag bar.
372 CropDragArea {
373 y: -units.gu(2)
374 height: units.gu(3)
375 anchors.horizontalCenter: parent.center
376 width: parent.width - units.gu(2)
377
378 onDragged: {
379 frame.resizedY(true, dy);
380 frame.updateOnAltered(false);
381 }
382
383 onDragStarted: frame.dragStartRect = frame.getExtentRect();
384 onDragCompleted: frame.updateOnAltered(true);
385 }
386
387 // Right drag bar.
388 CropDragArea {
389 x: parent.width - units.gu(1)
390 width: units.gu(3)
391 anchors.verticalCenter: parent.center
392 height: parent.height - units.gu(2)
393
394 onDragged: {
395 frame.resizedX(false, dx);
396 frame.updateOnAltered(false);
397 }
398
399 onDragStarted: frame.dragStartRect = frame.getExtentRect();
400 onDragCompleted: frame.updateOnAltered(true);
401 }
402
403 // Bottom drag bar.
404 CropDragArea {
405 y: parent.height - units.gu(1)
406 height: units.gu(3)
407 anchors.horizontalCenter: parent.center
408 width: parent.width - units.gu(2)
409
410 onDragged: {
411 frame.resizedY(false, dy);
412 frame.updateOnAltered(false);
413 }
414
415 onDragStarted: frame.dragStartRect = frame.getExtentRect();
416 onDragCompleted: frame.updateOnAltered(true);
417 }
418
419 // Top-left corner.
420 CropCorner {
421 objectName: "topLeftCropCorner"
422 isLeft: true
423 isTop: true
424
425 onDragged: {
426 frame.resizedX(isLeft, dx);
427 frame.resizedY(isTop, dy);
428 frame.updateOnAltered(false);
429 }
430
431 onDragStarted: frame.dragStartRect = frame.getExtentRect();
432 onDragCompleted: frame.updateOnAltered(true);
433 }
434
435 // Top-right corner.
436 CropCorner {
437 objectName: "topRightCropCorner"
438 isLeft: false
439 isTop: true
440
441 onDragged: {
442 frame.resizedX(isLeft, dx);
443 frame.resizedY(isTop, dy);
444 frame.updateOnAltered(false);
445 }
446
447 onDragStarted: frame.dragStartRect = frame.getExtentRect();
448 onDragCompleted: frame.updateOnAltered(true);
449 }
450
451 // Bottom-left corner.
452 CropCorner {
453 objectName: "bottonLeftCropCorner"
454 isLeft: true
455 isTop: false
456
457 onDragged: {
458 frame.resizedX(isLeft, dx);
459 frame.resizedY(isTop, dy);
460 frame.updateOnAltered(false);
461 }
462
463 onDragStarted: frame.dragStartRect = frame.getExtentRect();
464 onDragCompleted: frame.updateOnAltered(true);
465 }
466
467 // Bottom-right corner.
468 CropCorner {
469 id: bottomRightCrop
470 objectName: "bottomRightCropCorner"
471 isLeft: false
472 isTop: false
473
474 onDragged: {
475 frame.resizedX(isLeft, dx);
476 frame.resizedY(isTop, dy);
477 frame.updateOnAltered(false);
478 }
479
480 onDragStarted: frame.dragStartRect = frame.getExtentRect();
481 onDragCompleted: frame.updateOnAltered(true);
482 }
483
484 // This handles resizing in both dimensions. first is whether we're
485 // resizing the "first" edge, e.g. left or top (in which case we
486 // adjust both position and span) vs. right or bottom (where we just
487 // adjust the span). position should be either "x" or "y", and span
488 // is either "width" or "height". This is a little complicated, and
489 // coule probably be optimized with a little more thought.
490 function resizeFrame(first, delta, position, span) {
491 var constraintRegion = cropOverlay.getPhotoExtentRect();
492
493 if (first) {
494 // Left/top side.
495 if (frame[position] + delta < constraintRegion[position])
496 delta = constraintRegion[position] - frame[position]
497
498 if (frame[span] - delta < minSize)
499 delta = frame[span] - minSize;
500
501 frame[position] += delta;
502 frame[span] -= delta;
503 } else {
504 // Right/bottom side.
505 if (frame[span] + delta < minSize)
506 delta = minSize - frame[span];
507
508 if ((frame[position] + frame[span] + delta) >
509 (constraintRegion[position] + constraintRegion[span]))
510 delta = constraintRegion[position] + constraintRegion[span] -
511 frame[position] - frame[span];
512
513 frame[span] += delta;
514 }
515 }
516
517 onResizedX: resizeFrame(left, dx, "x", "width")
518 onResizedY: resizeFrame(top, dy, "y", "height")
519
520 function updateOnAltered(finalUpdate) {
521 var start = frame.dragStartRect;
522 var end = frame.getExtentRect();
523 if (!GraphicsRoutines.areEqual(end, start)) {
524 if (finalUpdate ||
525 (end.width * end.height >= start.width * start.height)) {
526 cropOverlay.userAlteredFrame();
527 cropOverlay.runFrameFitAnimation();
528 }
529 }
530 }
531 }
532
533 /* Invoked when the user has changed the geometry of the frame by dragging
534 * one of its corners or edges. Expressed in terms of the states of the
535 * frame described above, the userAlteredFrame signal is fired
536 * when the user stops dragging. This triggers a change of the frame
537 * from the USER state to the FIT state
538 */
539 onUserAlteredFrame: {
540 // since the geometry of the frame in the FIT state depends on both
541 // how the user resized the frame when it was in the USER state as well
542 // as the size of the frame constraint region, we have to recompute the
543 // geometry of of the frame for the FIT state every time.
544
545 startFrame = GraphicsRoutines.cloneRect(frame);
546
547 endFrame = GraphicsRoutines.fitRect(getViewportExtentRect(),
548 frame.getExtentRect());
549
550 startPhoto = GraphicsRoutines.cloneRect(photoExtent);
551
552 var frameRelativeToPhoto = getRelativeFrameRect();
553 var scaleFactor = endFrame.width / frame.width;
554
555 endPhotoWidth = photoExtent.width * scaleFactor;
556 endPhotoHeight = photoExtent.height * scaleFactor;
557 endPhotoX = endFrame.x - (frameRelativeToPhoto.x * endPhotoWidth);
558 endPhotoY = endFrame.y - (frameRelativeToPhoto.y * endPhotoHeight)
559
560 photo.transformOrigin = Item.TopLeft;
561 }
562
563 onRunFrameFitAnimation: NumberAnimation { target: cropOverlay;
564 property: "interpolationFactor"; from: 0.0; to: 1.0 }
565
566 onInterpolationFactorChanged: {
567 var endPhotoRect = { };
568 endPhotoRect.x = endPhotoX;
569 endPhotoRect.y = endPhotoY;
570 endPhotoRect.width = endPhotoWidth;
571 endPhotoRect.height = endPhotoHeight;
572
573 var interpolatedRect = GraphicsRoutines.interpolateRect(startFrame,
574 endFrame, interpolationFactor);
575 GraphicsRoutines.sizeToRect(interpolatedRect, frame);
576
577 interpolatedRect = GraphicsRoutines.interpolateRect(startPhoto,
578 endPhotoRect, interpolationFactor);
579 GraphicsRoutines.sizeToRect(interpolatedRect, photoExtent);
580 }
581}
5820
=== removed file 'rc/qml/MediaViewer/PhotoEditor/EditStack.qml'
--- rc/qml/MediaViewer/PhotoEditor/EditStack.qml 2015-11-05 19:04:22 +0000
+++ rc/qml/MediaViewer/PhotoEditor/EditStack.qml 1970-01-01 00:00:00 +0000
@@ -1,134 +0,0 @@
1/*
2 * Copyright (C) 2014-2015 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19import Gallery 1.0
20
21Item {
22 property GalleryPhotoData data
23 property bool actionsEnabled: true
24 property var items: []
25 property int level: 0
26 property string editingSessionPath
27 property string currentFile
28 property string originalFile
29 property string pristineFile
30 property bool modified: level > 0 || _revertedInThisSession
31
32 property bool _revertedInThisSession
33 property bool _pristineFileExists
34
35 signal revertRequested
36
37 function startEditingSession(original) {
38 var originalFileName = GalleryFileUtils.nameFromPath(original);
39 var baseName = GalleryFileUtils.parentDirectory(original) +
40 "/.photo_editing." + originalFileName + ".";
41 editingSessionPath = GalleryFileUtils.createTemporaryDirectory(baseName);
42 if (editingSessionPath == "") return false;
43
44 originalFile = original;
45 currentFile = editingSessionPath + "/current";
46
47 pristineFile = GalleryFileUtils.parentDirectory(original) +
48 "/.original/" + originalFileName
49 _revertedInThisSession = false;
50 _pristineFileExists = GalleryFileUtils.exists(pristineFile)
51
52 GalleryFileUtils.copy(originalFile, currentFile)
53
54 items = [createSnapshot(0)];
55 level = 0;
56 return true;
57 }
58
59 function endEditingSession(saveIfModified) {
60 if (saveIfModified && modified) { // file modified
61 // if we don't have a copy of the very first original, create one
62 if (!_pristineFileExists) {
63 GalleryFileUtils.createDirectory(GalleryFileUtils.parentDirectory(pristineFile));
64 GalleryFileUtils.copy(originalFile, pristineFile);
65 } else {
66 // if we reverted to original (and made no other changes)
67 // we don't need to keep the pristine copy around
68 if (_revertedInThisSession && level <= 0) {
69 GalleryFileUtils.remove(pristineFile);
70 }
71 }
72
73 GalleryFileUtils.copy(currentFile, originalFile); // actually save
74 }
75
76 GalleryFileUtils.removeDirectory(editingSessionPath, true); // clear editing cache
77 editingSessionPath = originalFile = pristineFile = currentFile = "";
78 }
79
80 function createSnapshot(name) {
81 var snapshotFile = editingSessionPath + "/edit." + name;
82 GalleryFileUtils.copy(currentFile, snapshotFile);
83 return snapshotFile;
84 }
85
86 function restoreSnapshot(name) {
87 var snapshotFile = editingSessionPath + "/edit." + name;
88 GalleryFileUtils.copy(snapshotFile, currentFile);
89 data.refreshFromDisk();
90 }
91
92 function checkpoint() {
93 level++;
94 items = items.slice(0, level);
95 items.push(createSnapshot(items.length));
96 }
97
98 function revertToPristine() {
99 if (!GalleryFileUtils.exists(pristineFile)) {
100 restoreSnapshot(0);
101 items = items.slice(0, 1);
102 level = 0;
103 } else {
104 GalleryFileUtils.copy(pristineFile, currentFile);
105 data.refreshFromDisk();
106 items = [];
107 checkpoint();
108 level = 0;
109 _revertedInThisSession = true;
110 }
111 }
112
113 property Action undoAction: Action {
114 text: i18n.tr("Undo")
115 iconName: "undo"
116 enabled: items.length > 0 && level > 0 && actionsEnabled
117 onTriggered: restoreSnapshot(--level);
118 }
119
120 property Action redoAction: Action {
121 text: i18n.tr("Redo")
122 iconName: "redo"
123 enabled: level < items.length - 1 && actionsEnabled
124 onTriggered: restoreSnapshot(++level);
125 }
126
127 property Action revertAction: Action {
128 text: i18n.tr("Revert to Original")
129 iconSource: Qt.resolvedUrl("assets/edit_revert.png")
130 enabled: actionsEnabled &&
131 (level > 0 || (!_revertedInThisSession && _pristineFileExists))
132 onTriggered: revertRequested()
133 }
134}
1350
=== removed file 'rc/qml/MediaViewer/PhotoEditor/ExposureAdjuster.qml'
--- rc/qml/MediaViewer/PhotoEditor/ExposureAdjuster.qml 2015-11-05 19:04:22 +0000
+++ rc/qml/MediaViewer/PhotoEditor/ExposureAdjuster.qml 1970-01-01 00:00:00 +0000
@@ -1,104 +0,0 @@
1/*
2 * Copyright (C) 2014-2015 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19
20// When the photo editor uses a proper PageStack this will switch back to being
21// an Item as it will not need to cover what is below it.
22Rectangle {
23 id: adjuster
24 color:"black"
25
26 property alias exposure: exposureSelector.value
27 property bool enabled
28
29 signal confirm()
30 signal cancel()
31
32 Image {
33 id: targetImage
34 anchors.fill: parent
35 fillMode: Image.PreserveAspectFit
36 asynchronous: true
37 cache: false
38 sourceSize {
39 width: targetImage.width
40 height: targetImage.height
41 }
42 }
43
44 Column {
45 anchors.left: parent.left
46 anchors.right: parent.right
47 anchors.bottom: parent.bottom
48 anchors.margins: units.gu(2)
49 spacing: units.gu(2)
50
51 Slider {
52 id: exposureSelector
53 live: false
54 minimumValue: -1.0
55 maximumValue: +1.0
56 value: 0.0
57 enabled: adjuster.enabled
58
59 anchors.left: parent.left
60 anchors.right: parent.right
61 height: units.gu(2)
62
63 function formatValue(value) {
64 return (Math.round(value * 100) / 100).toString()
65 }
66 }
67 Row {
68 anchors.horizontalCenter: parent.horizontalCenter
69 spacing: units.gu(2)
70 Button {
71 text: i18n.tr("Done")
72 color: UbuntuColors.green
73 enabled: adjuster.enabled
74 onTriggered: {
75 targetImage.source = "";
76 confirm();
77 }
78 }
79 Button {
80 text: i18n.tr("Cancel")
81 color: UbuntuColors.red
82 enabled: adjuster.enabled
83 onTriggered: {
84 targetImage.source = "";
85 cancel();
86 }
87 }
88 }
89 }
90
91 function start(target) {
92 targetImage.source = target;
93 exposure = 0.0;
94 opacity = 1.0;
95 }
96
97 function reload() {
98 var path = targetImage.source;
99 targetImage.asynchronous = false;
100 targetImage.source = "";
101 targetImage.asynchronous = true;
102 targetImage.source = path;
103 }
104}
1050
=== removed file 'rc/qml/MediaViewer/PhotoEditor/GraphicsRoutines.js'
--- rc/qml/MediaViewer/PhotoEditor/GraphicsRoutines.js 2015-02-26 21:00:19 +0000
+++ rc/qml/MediaViewer/PhotoEditor/GraphicsRoutines.js 1970-01-01 00:00:00 +0000
@@ -1,108 +0,0 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Lucas Beeler <lucas@yorba.org>
18 */
19
20/* Given 'input', constrain the value of 'input' to range between
21 * 'lowConstraint' and 'highConstraint', inclusive. Return the constrained
22 * value without modifying 'input'.
23 */
24function clamp(input, lowConstraint, highConstraint) {
25 if (input < lowConstraint)
26 return lowConstraint;
27 else if (input > highConstraint)
28 return highConstraint;
29 else
30 return input;
31}
32
33function cloneRect(source) {
34 var ret = { };
35 ret.x = source.x;
36 ret.y = source.y;
37 ret.width = source.width;
38 ret.height = source.height;
39
40 return ret;
41}
42
43function interpolateRect(start, end, factor) {
44 var result = { };
45
46 result.x = start.x + factor * (end.x - start.x);
47 result.y = start.y + factor * (end.y - start.y);
48 result.width = start.width + factor * (end.width - start.width);
49 result.height = start.height + factor * (end.height - start.height);
50
51 return result;
52}
53
54/* Forces Geometry object 'item' to fit centered inside Geometry object
55 * 'viewport', preserving the aspect of ratio of 'item' but potentially scaling
56 * and translating it so that it snugly fits centered inside of 'viewport'.
57 * Return the new scaled-up and translated Geometry for 'item'.
58 */
59function fitRect(viewport, item) {
60 if (item.width == 0 || item.height == 0) {
61 return viewport;
62 }
63
64 var itemAspectRatio = item.width / item.height;
65 var viewportAspectRatio = viewport.width / viewport.height;
66
67 var result = { };
68 if (itemAspectRatio > viewportAspectRatio) {
69 var scaleFactor = viewport.width / item.width;
70 result.width = viewport.width;
71 result.height = item.height * scaleFactor
72 } else {
73 scaleFactor = viewport.height / item.height;
74 result.width = item.width * scaleFactor
75 result.height = viewport.height;
76 }
77
78 result.width = clamp(result.width, 0, viewport.width);
79 result.height = clamp(result.height, 0, viewport.height);
80 result.x = viewport.x + (viewport.width - result.width) / 2;
81 result.y = viewport.y + (viewport.height - result.height) / 2;
82 result.scaleFactor = scaleFactor;
83
84 return result;
85}
86
87function getRelativeRect(geom, relativeTo) {
88 var result = { };
89
90 result.x = (geom.x - relativeTo.x) / relativeTo.width;
91 result.y = (geom.y - relativeTo.y) / relativeTo.height;
92 result.width = geom.width / relativeTo.width;
93 result.height = geom.height / relativeTo.height;
94
95 return result;
96}
97
98function sizeToRect(rect, qmlItem) {
99 qmlItem.x = rect.x;
100 qmlItem.y = rect.y;
101 qmlItem.width = rect.width;
102 qmlItem.height = rect.height;
103}
104
105function areEqual(geom1, geom2) {
106 return (geom1.x === geom2.x && geom1.y === geom2.y && geom1.width ===
107 geom2.width && geom1.height === geom2.height);
108}
1090
=== removed directory 'rc/qml/MediaViewer/PhotoEditor/assets'
=== removed file 'rc/qml/MediaViewer/PhotoEditor/assets/crop-handle@20.png'
110Binary files rc/qml/MediaViewer/PhotoEditor/assets/crop-handle@20.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/crop-handle@20.png 1970-01-01 00:00:00 +0000 differ1Binary files rc/qml/MediaViewer/PhotoEditor/assets/crop-handle@20.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/crop-handle@20.png 1970-01-01 00:00:00 +0000 differ
=== removed file 'rc/qml/MediaViewer/PhotoEditor/assets/edit_autocorrect@27.png'
111Binary files rc/qml/MediaViewer/PhotoEditor/assets/edit_autocorrect@27.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/edit_autocorrect@27.png 1970-01-01 00:00:00 +0000 differ2Binary files rc/qml/MediaViewer/PhotoEditor/assets/edit_autocorrect@27.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/edit_autocorrect@27.png 1970-01-01 00:00:00 +0000 differ
=== removed file 'rc/qml/MediaViewer/PhotoEditor/assets/edit_crop@27.png'
112Binary files rc/qml/MediaViewer/PhotoEditor/assets/edit_crop@27.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/edit_crop@27.png 1970-01-01 00:00:00 +0000 differ3Binary files rc/qml/MediaViewer/PhotoEditor/assets/edit_crop@27.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/edit_crop@27.png 1970-01-01 00:00:00 +0000 differ
=== removed file 'rc/qml/MediaViewer/PhotoEditor/assets/edit_exposure@27.png'
113Binary files rc/qml/MediaViewer/PhotoEditor/assets/edit_exposure@27.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/edit_exposure@27.png 1970-01-01 00:00:00 +0000 differ4Binary files rc/qml/MediaViewer/PhotoEditor/assets/edit_exposure@27.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/edit_exposure@27.png 1970-01-01 00:00:00 +0000 differ
=== removed file 'rc/qml/MediaViewer/PhotoEditor/assets/edit_revert@27.png'
114Binary files rc/qml/MediaViewer/PhotoEditor/assets/edit_revert@27.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/edit_revert@27.png 1970-01-01 00:00:00 +0000 differ5Binary files rc/qml/MediaViewer/PhotoEditor/assets/edit_revert@27.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/edit_revert@27.png 1970-01-01 00:00:00 +0000 differ
=== removed file 'rc/qml/MediaViewer/PhotoEditor/assets/edit_rotate_left@27.png'
115Binary files rc/qml/MediaViewer/PhotoEditor/assets/edit_rotate_left@27.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/edit_rotate_left@27.png 1970-01-01 00:00:00 +0000 differ6Binary files rc/qml/MediaViewer/PhotoEditor/assets/edit_rotate_left@27.png 2015-02-26 21:00:19 +0000 and rc/qml/MediaViewer/PhotoEditor/assets/edit_rotate_left@27.png 1970-01-01 00:00:00 +0000 differ
=== renamed file 'rc/qml/MediaViewer/ExtrasPhotoEditorPage.qml' => 'rc/qml/MediaViewer/PhotoEditorPage.qml'
=== modified file 'src/gallery-application.cpp'
--- src/gallery-application.cpp 2015-04-30 22:00:38 +0000
+++ src/gallery-application.cpp 2015-12-16 13:52:17 +0000
@@ -37,8 +37,6 @@
37#include "photo.h"37#include "photo.h"
3838
39// photoeditor39// photoeditor
40#include "photo-data.h"
41#include "file-utils.h"
42#include "photo-image-provider.h"40#include "photo-image-provider.h"
4341
44// qml42// qml
@@ -175,8 +173,6 @@
175 qmlRegisterType<QmlEventCollectionModel>("Gallery", 1, 0, "EventCollectionModel");173 qmlRegisterType<QmlEventCollectionModel>("Gallery", 1, 0, "EventCollectionModel");
176 qmlRegisterType<QmlEventOverviewModel>("Gallery", 1, 0, "EventOverviewModel");174 qmlRegisterType<QmlEventOverviewModel>("Gallery", 1, 0, "EventOverviewModel");
177 qmlRegisterType<QmlMediaCollectionModel>("Gallery", 1, 0, "MediaCollectionModel");175 qmlRegisterType<QmlMediaCollectionModel>("Gallery", 1, 0, "MediaCollectionModel");
178 qmlRegisterType<PhotoData>("Gallery", 1, 0, "GalleryPhotoData");
179 qmlRegisterSingletonType<FileUtils>("Gallery", 1, 0, "GalleryFileUtils", exportFileUtilsSingleton);
180176
181 qRegisterMetaType<QList<MediaSource*> >("MediaSourceList");177 qRegisterMetaType<QList<MediaSource*> >("MediaSourceList");
182 qRegisterMetaType<QSet<DataObject*> >("QSet<DataObject*>");178 qRegisterMetaType<QSet<DataObject*> >("QSet<DataObject*>");
@@ -269,15 +265,6 @@
269 setMediaFile(m_cmdLineParser->mediaFile());265 setMediaFile(m_cmdLineParser->mediaFile());
270}266}
271267
272QObject* GalleryApplication::exportFileUtilsSingleton(QQmlEngine *engine,
273 QJSEngine *scriptEngine)
274{
275 Q_UNUSED(engine);
276 Q_UNUSED(scriptEngine);
277
278 return new FileUtils();
279}
280
281/*!268/*!
282 * \brief GalleryApplication::initCollections269 * \brief GalleryApplication::initCollections
283 */270 */
284271
=== modified file 'src/gallery-application.h'
--- src/gallery-application.h 2015-02-26 20:28:06 +0000
+++ src/gallery-application.h 2015-12-16 13:52:17 +0000
@@ -93,8 +93,6 @@
93private:93private:
94 void registerQML();94 void registerQML();
95 void createView();95 void createView();
96 static QObject* exportFileUtilsSingleton(QQmlEngine *engine,
97 QJSEngine *scriptEngine);
9896
99 QQuickView *m_view;97 QQuickView *m_view;
100 GalleryManager *m_galleryManager;98 GalleryManager *m_galleryManager;
10199
=== modified file 'src/photoeditor/CMakeLists.txt'
--- src/photoeditor/CMakeLists.txt 2015-02-26 20:34:48 +0000
+++ src/photoeditor/CMakeLists.txt 2015-12-16 13:52:17 +0000
@@ -14,24 +14,11 @@
14 )14 )
1515
16set(gallery_photoeditor_HDRS16set(gallery_photoeditor_HDRS
17 file-utils.h
18 imaging.h
19 orientation.h
20 photo-caches.h
21 photo-data.h
22 photo-edit-command.h
23 photo-edit-thread.h
24 photo-image-provider.h17 photo-image-provider.h
25 photo-metadata.h18 photo-metadata.h
26 )19 )
2720
28set(gallery_photoeditor_SRCS21set(gallery_photoeditor_SRCS
29 file-utils.cpp
30 imaging.cpp
31 orientation.cpp
32 photo-caches.cpp
33 photo-data.cpp
34 photo-edit-thread.cpp
35 photo-image-provider.cpp22 photo-image-provider.cpp
36 photo-metadata.cpp23 photo-metadata.cpp
37 )24 )
3825
=== removed file 'src/photoeditor/file-utils.cpp'
--- src/photoeditor/file-utils.cpp 2015-02-26 20:28:06 +0000
+++ src/photoeditor/file-utils.cpp 1970-01-01 00:00:00 +0000
@@ -1,97 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "file-utils.h"
18
19#include <QDebug>
20#include <QDir>
21#include <QFile>
22#include <QFileInfo>
23#include <QTemporaryDir>
24
25FileUtils::FileUtils(QObject *parent) :
26 QObject(parent)
27{
28}
29
30bool FileUtils::createDirectory(QString path) const
31{
32 if (path.isEmpty()) return false;
33
34 return QDir(path).mkpath(".");
35}
36
37QString FileUtils::createTemporaryDirectory(QString pathTemplate) const
38{
39 QTemporaryDir dir(pathTemplate);
40 if (!dir.isValid()) return QString();
41
42 dir.setAutoRemove(false);
43 return dir.path();
44}
45
46bool FileUtils::removeDirectory(QString path, bool recursive) const
47{
48 if (path.isEmpty()) return false;
49
50 QDir dir(path);
51 return (recursive) ? dir.removeRecursively() : dir.rmdir(".");
52}
53
54bool FileUtils::remove(QString path) const
55{
56 if (path.isEmpty()) return false;
57 return QFile::remove(path);
58}
59
60bool FileUtils::copy(QString sourceFile, QString destinationFile) const
61{
62 if (sourceFile.isEmpty() || destinationFile.isEmpty()) return false;
63
64 if (QFileInfo(destinationFile).exists()) QFile::remove(destinationFile);
65 return QFile::copy(sourceFile, destinationFile);
66}
67
68bool FileUtils::rename(QString sourceFile, QString destinationFile) const
69{
70 if (sourceFile.isEmpty() || destinationFile.isEmpty()) return false;
71
72 if (QFileInfo(destinationFile).exists()) QFile::remove(destinationFile);
73 return QFile::rename(sourceFile, destinationFile);
74}
75
76QString FileUtils::parentDirectory(QString path) const
77{
78 if (QFileInfo(path).isDir()) {
79 QDir dir(path);
80 dir.cdUp();
81 return dir.absolutePath();
82 } else {
83 return QFileInfo(path).dir().absolutePath();
84 }
85}
86
87QString FileUtils::nameFromPath(QString path) const
88{
89 QString name = QFileInfo(path).fileName();
90 if (name.isEmpty()) name = QDir(path).dirName();
91 return name;
92}
93
94bool FileUtils::exists(QString path) const
95{
96 return QFileInfo::exists(path);
97}
980
=== removed file 'src/photoeditor/file-utils.h'
--- src/photoeditor/file-utils.h 2015-02-26 20:28:06 +0000
+++ src/photoeditor/file-utils.h 1970-01-01 00:00:00 +0000
@@ -1,42 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef PHOTOUTILS_H
18#define PHOTOUTILS_H
19
20#include <QObject>
21
22class FileUtils : public QObject
23{
24 Q_OBJECT
25public:
26 explicit FileUtils(QObject *parent = 0);
27
28 Q_INVOKABLE bool createDirectory(QString path) const;
29 Q_INVOKABLE bool removeDirectory(QString path, bool recursive = false) const;
30 Q_INVOKABLE QString createTemporaryDirectory(QString pathTemplate) const;
31
32 Q_INVOKABLE bool remove(QString path) const;
33 Q_INVOKABLE bool copy(QString sourceFile, QString destinationFile) const;
34 Q_INVOKABLE bool rename(QString sourceFile, QString destinationFile) const;
35
36 Q_INVOKABLE QString parentDirectory(QString path) const;
37 Q_INVOKABLE QString nameFromPath(QString path) const;
38
39 Q_INVOKABLE bool exists(QString path) const;
40};
41
42#endif // PHOTOUTILS_H
430
=== removed file 'src/photoeditor/imaging.cpp'
--- src/photoeditor/imaging.cpp 2015-02-26 20:28:06 +0000
+++ src/photoeditor/imaging.cpp 1970-01-01 00:00:00 +0000
@@ -1,363 +0,0 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Lucas Beeler <lucas@yorba.org>
18 */
19
20
21#include <QApplication>
22#include <qmath.h>
23
24#include "imaging.h"
25
26/*!
27 * \brief HSVTransformation::transformPixel
28 * \param pixel_color
29 * \return
30 */
31QColor HSVTransformation::transformPixel(const QColor &pixel_color) const
32{
33 QColor result;
34
35 int h, s, v;
36 pixel_color.getHsv(&h, &s, &v);
37
38 v = remap_table_[v];
39
40 result.setHsv(h, s, v);
41
42 return result;
43}
44
45/*!
46 * \brief IntensityHistogram::IntensityHistogram
47 * \param basis_image
48 */
49IntensityHistogram::IntensityHistogram(const QImage& basis_image)
50{
51 for (int i = 0; i < 256; i++)
52 m_counts[i] = 0;
53
54 int width = basis_image.width();
55 int height = basis_image.height();
56
57 for (int j = 0; j < height; j++) {
58 QApplication::processEvents();
59
60 for (int i = 0; i < width; i++) {
61 QColor c = QColor(basis_image.pixel(i, j));
62 int intensity = c.value();
63 m_counts[intensity]++;
64 }
65 }
66
67 float pixel_count = (float)(width * height);
68 float accumulator = 0.0f;
69 for (int i = 0; i < 256; i++) {
70 m_probabilities[i] = ((float) m_counts[i]) / pixel_count;
71 accumulator += m_probabilities[i];
72 m_cumulativeProbabilities[i] = accumulator;
73 }
74}
75
76/*!
77 * \brief IntensityHistogram::getCumulativeProbability
78 * \param level
79 * \return
80 */
81float IntensityHistogram::getCumulativeProbability(int level)
82{
83 return m_cumulativeProbabilities[level];
84}
85
86
87const float ToneExpansionTransformation::DEFAULT_LOW_DISCARD_MASS = 0.02f;
88const float ToneExpansionTransformation::DEFAULT_HIGH_DISCARD_MASS = 0.98f;
89/*!
90 * \brief ToneExpansionTransformation::ToneExpansionTransformation
91 * \param h
92 * \param low_discard_mass
93 * \param high_discard_mass
94 */
95ToneExpansionTransformation::ToneExpansionTransformation(IntensityHistogram h,
96 float low_discard_mass, float high_discard_mass)
97{
98 if (low_discard_mass == -1.0f)
99 low_discard_mass = DEFAULT_LOW_DISCARD_MASS;
100 if (high_discard_mass == -1.0f)
101 high_discard_mass = DEFAULT_HIGH_DISCARD_MASS;
102
103 m_lowDiscardMass = low_discard_mass;
104 m_highDiscardMass = high_discard_mass;
105
106 m_lowKink = 0;
107 m_highKink = 255;
108
109 while (h.getCumulativeProbability(m_lowKink) < low_discard_mass)
110 m_lowKink++;
111
112 while (h.getCumulativeProbability(m_highKink) > high_discard_mass)
113 m_highKink--;
114
115 m_lowKink = clampi(m_lowKink, 0, 255);
116 m_highKink = clampi(m_highKink, 0, 255);
117
118 buildRemapTable();
119}
120
121/*!
122 * \brief ToneExpansionTransformation::isIdentity
123 * \return
124 */
125bool ToneExpansionTransformation::isIdentity() const
126{
127 return ((m_lowKink == 0) && (m_highKink == 255));
128}
129
130/*!
131 * \brief ToneExpansionTransformation::buildRemapTable
132 */
133void ToneExpansionTransformation::buildRemapTable()
134{
135 float low_kink_f = ((float) m_lowKink) / 255.0f;
136 float high_kink_f = ((float) m_highKink) / 255.0f;
137
138 float slope = 1.0f / (high_kink_f - low_kink_f);
139 float intercept = -(low_kink_f / (high_kink_f - low_kink_f));
140
141 int i = 0;
142 for ( ; i <= m_lowKink; i++)
143 remap_table_[i] = 0;
144
145 for ( ; i < m_highKink; i++)
146 remap_table_[i] = (int) ((255.0f * (slope * (((float) i) / 255.0f) +
147 intercept)) + 0.5);
148
149 for ( ; i < 256; i++)
150 remap_table_[i] = 255;
151}
152
153/*!
154 * \brief ToneExpansionTransformation::lowDiscardMass
155 * \return
156 */
157float ToneExpansionTransformation::lowDiscardMass() const
158{
159 return m_lowDiscardMass;
160}
161
162/*!
163 * \brief ToneExpansionTransformation::highDiscardMass
164 * \return
165 */
166float ToneExpansionTransformation::highDiscardMass() const
167{
168 return m_highDiscardMass;
169}
170
171
172/*!
173 * \brief HermiteGammaApproximationFunction::HermiteGammaApproximationFunction
174 * \param user_interval_upper
175 */
176HermiteGammaApproximationFunction::HermiteGammaApproximationFunction(
177 float user_interval_upper)
178{
179 m_nonzeroIntervalUpper = clampf(user_interval_upper, 0.1f, 1.0f);
180 m_xScale = 1.0f / m_nonzeroIntervalUpper;
181}
182
183/*!
184 * \brief HermiteGammaApproximationFunction::evaluate
185 * \param x
186 * \return
187 */
188float HermiteGammaApproximationFunction::evaluate(float x)
189{
190 if (x < 0.0f)
191 return 0.0f;
192 else if (x > m_nonzeroIntervalUpper)
193 return 0.0f;
194 else {
195 float indep_var = m_xScale * x;
196
197 float dep_var = 6.0f * ((indep_var * indep_var * indep_var) -
198 (2.0f * (indep_var * indep_var)) + (indep_var));
199
200 return clampf(dep_var, 0.0f, 1.0f);
201 }
202}
203
204
205const float ShadowDetailTransformation::MAX_EFFECT_SHIFT = 0.5f;
206const float ShadowDetailTransformation::MIN_TONAL_WIDTH = 0.1f;
207const float ShadowDetailTransformation::MAX_TONAL_WIDTH = 1.0f;
208const float ShadowDetailTransformation::TONAL_WIDTH = 1.0f;
209/*!
210 * \brief ShadowDetailTransformation::ShadowDetailTransformation
211 * \param intensity
212 */
213ShadowDetailTransformation::ShadowDetailTransformation(float intensity)
214{
215 m_intensity = intensity;
216 float effect_shift = MAX_EFFECT_SHIFT * intensity;
217
218 HermiteGammaApproximationFunction func =
219 HermiteGammaApproximationFunction(TONAL_WIDTH);
220
221 for (int i = 0; i < 256; i++) {
222 float x = ((float) i) / 255.0f;
223 float weight = func.evaluate(x);
224
225 int remapped = (int) ((255.0f * (weight * (x + effect_shift)) + ((1.0f -
226 weight) * x)) + 0.5f);
227 remap_table_[i] = clampi(remapped, i, 255);
228 }
229}
230
231/*!
232 * \brief ShadowDetailTransformation::isIdentity
233 * \return
234 */
235bool ShadowDetailTransformation::isIdentity() const
236{
237 return (m_intensity == 0.0f);
238}
239
240
241const int AutoEnhanceTransformation::SHADOW_DETECT_MIN_INTENSITY = 2;
242const int AutoEnhanceTransformation::SHADOW_DETECT_MAX_INTENSITY = 90;
243const int AutoEnhanceTransformation::SHADOW_DETECT_INTENSITY_RANGE =
244 AutoEnhanceTransformation::SHADOW_DETECT_MAX_INTENSITY -
245 AutoEnhanceTransformation::SHADOW_DETECT_MIN_INTENSITY;
246const int AutoEnhanceTransformation::EMPIRICAL_DARK = 40;
247const float AutoEnhanceTransformation::SHADOW_AGGRESSIVENESS_MUL = 0.45f;
248/*!
249 * \brief AutoEnhanceTransformation::AutoEnhanceTransformation
250 * \param basis
251 */
252AutoEnhanceTransformation::AutoEnhanceTransformation(const QImage& basis)
253 : m_shadowTransform(0), m_toneExpansionTransform(0)
254{
255 IntensityHistogram histogram = IntensityHistogram(basis);
256
257 /* compute the percentage of pixels in the image that fall into the
258 shadow range -- this measures "of the pixels in the image, how many of
259 them are in shadow?" */
260 float pct_in_range = 100.0f *
261 (histogram.getCumulativeProbability(SHADOW_DETECT_MAX_INTENSITY) -
262 histogram.getCumulativeProbability(SHADOW_DETECT_MIN_INTENSITY));
263
264 /* compute the mean intensity of the pixels that are in the shadow range --
265 this measures "of those pixels that are in shadow, just how dark are
266 they?" */
267 float sh_prob_mu =
268 (histogram.getCumulativeProbability(SHADOW_DETECT_MIN_INTENSITY) +
269 histogram.getCumulativeProbability(SHADOW_DETECT_MAX_INTENSITY)) * 0.5f;
270 int sh_intensity_mu = SHADOW_DETECT_MIN_INTENSITY;
271 for ( ; sh_intensity_mu <= SHADOW_DETECT_MAX_INTENSITY; sh_intensity_mu++) {
272 if (histogram.getCumulativeProbability(sh_intensity_mu) >= sh_prob_mu)
273 break;
274 }
275
276 /* if more than 30 percent of the pixels in the image are in the shadow
277 detection range, or if the mean intensity within the shadow range is less
278 than an empirically determined threshold below which pixels appear very
279 dark, regardless of the percent of pixels in it, then perform shadow
280 detail enhancement. Otherwise, skip shadow detail enhancement and perform
281 contrast expansion only */
282 if ((pct_in_range > 30.0f) || ((pct_in_range > 10.0f) &&
283 (sh_intensity_mu < EMPIRICAL_DARK))) {
284 float shadow_trans_effect_size = ((((float) SHADOW_DETECT_MAX_INTENSITY) -
285 ((float) sh_intensity_mu)) /
286 ((float) SHADOW_DETECT_INTENSITY_RANGE));
287 shadow_trans_effect_size *= SHADOW_AGGRESSIVENESS_MUL;
288
289 m_shadowTransform
290 = new ShadowDetailTransformation(shadow_trans_effect_size);
291
292 QImage shadow_corrected_image = QImage(basis);
293 // Can't write into indexed images, due to a limitation in Qt.
294 if (shadow_corrected_image.format() == QImage::Format_Indexed8)
295 shadow_corrected_image = shadow_corrected_image.convertToFormat(
296 QImage::Format_RGB32);
297
298 for (int j = 0; j < shadow_corrected_image.height(); j++) {
299 QApplication::processEvents();
300
301 for (int i = 0; i < shadow_corrected_image.width(); i++) {
302 QColor px = m_shadowTransform->transformPixel(
303 QColor(shadow_corrected_image.pixel(i, j)));
304 shadow_corrected_image.setPixel(i, j, px.rgb());
305 }
306 }
307
308 m_toneExpansionTransform = new ToneExpansionTransformation(
309 IntensityHistogram(shadow_corrected_image), 0.005f, 0.995f);
310
311 } else {
312 m_toneExpansionTransform = new ToneExpansionTransformation(
313 IntensityHistogram(basis));
314 }
315}
316
317/*!
318 * \brief AutoEnhanceTransformation::~AutoEnhanceTransformation
319 */
320AutoEnhanceTransformation::~AutoEnhanceTransformation()
321{
322 if (m_shadowTransform)
323 delete m_shadowTransform;
324 delete m_toneExpansionTransform;
325}
326
327/*!
328 * \brief AutoEnhanceTransformation::transformPixel
329 * \param pixel_color
330 * \return
331 */
332QColor AutoEnhanceTransformation::transformPixel(
333 const QColor& pixel_color) const
334{
335 QColor px = pixel_color;
336
337 if (m_shadowTransform)
338 px = m_shadowTransform->transformPixel(px);
339
340 px = m_toneExpansionTransform->transformPixel(px);
341
342 /* if tone expansion occurs, boost saturation to compensate for boosted
343 dynamic range */
344 if (!m_toneExpansionTransform->isIdentity()) {
345 int h, s, v;
346 px.getHsv(&h, &s, &v);
347
348 float compensation_multiplier =
349 (m_toneExpansionTransform->lowDiscardMass() < 0.01f) ? 1.02f : 1.10f;
350
351 s = (int) (((float) s) * compensation_multiplier);
352 s = clampi(s, 0, 255);
353
354 px.setHsv(h, s, v);
355 }
356
357 return px;
358}
359
360bool AutoEnhanceTransformation::isIdentity() const
361{
362 return false;
363}
3640
=== removed file 'src/photoeditor/imaging.h'
--- src/photoeditor/imaging.h 2015-02-26 20:28:06 +0000
+++ src/photoeditor/imaging.h 1970-01-01 00:00:00 +0000
@@ -1,169 +0,0 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Lucas Beeler <lucas@yorba.org>
18 */
19
20#ifndef GALLERY_UTIL_IMAGING_H_
21#define GALLERY_UTIL_IMAGING_H_
22
23#include <QColor>
24#include <QImage>
25#include <QVector4D>
26
27/*!
28 * \brief clampi
29 * \param i
30 * \param min
31 * \param max
32 * \return
33 */
34inline int clampi(int i, int min, int max) {
35 return (i < min) ? min : ((i > max) ? max : i);
36}
37
38/*!
39 * \brief clampf
40 * \param x
41 * \param min
42 * \param max
43 * \return
44 */
45inline float clampf(float x, float min, float max) {
46 return (x < min) ? min : ((x > max) ? max : x);
47}
48
49/*!
50 * \brief The HSVTransformation class
51 */
52class HSVTransformation
53{
54public:
55 HSVTransformation() { }
56 virtual ~HSVTransformation() { }
57
58 virtual QColor transformPixel(const QColor& pixel_color) const;
59 virtual bool isIdentity() const = 0;
60
61protected:
62 int remap_table_[256];
63};
64
65/*!
66 * \brief The IntensityHistogram class
67 */
68class IntensityHistogram
69{
70public:
71 IntensityHistogram(const QImage& basis_image);
72 virtual ~IntensityHistogram() { }
73
74 float getCumulativeProbability(int level);
75
76private:
77 int m_counts[256];
78 float m_probabilities[256];
79 float m_cumulativeProbabilities[256];
80};
81
82/*!
83 * \brief The ToneExpansionTransformation class
84 */
85class ToneExpansionTransformation : public virtual HSVTransformation
86{
87 static const float DEFAULT_LOW_DISCARD_MASS;
88 static const float DEFAULT_HIGH_DISCARD_MASS;
89
90public:
91 ToneExpansionTransformation(IntensityHistogram h, float lowDiscardMass =
92 -1.0f, float highDiscardMass = -1.0f);
93 virtual ~ToneExpansionTransformation() { }
94
95 bool isIdentity() const;
96
97 float lowDiscardMass() const;
98 float highDiscardMass() const;
99
100private:
101 void buildRemapTable();
102
103 int m_lowKink;
104 int m_highKink;
105 float m_lowDiscardMass;
106 float m_highDiscardMass;
107};
108
109/*!
110 * \brief The HermiteGammaApproximationFunction class
111 */
112class HermiteGammaApproximationFunction
113{
114public:
115 HermiteGammaApproximationFunction(float user_interval_upper);
116 virtual ~HermiteGammaApproximationFunction() { }
117
118 float evaluate(float x);
119
120private:
121 float m_xScale;
122 float m_nonzeroIntervalUpper;
123};
124
125/*!
126 * \brief The ShadowDetailTransformation class
127 */
128class ShadowDetailTransformation : public virtual HSVTransformation
129{
130 static const float MAX_EFFECT_SHIFT;
131 static const float MIN_TONAL_WIDTH;
132 static const float MAX_TONAL_WIDTH;
133 static const float TONAL_WIDTH;
134
135public:
136 ShadowDetailTransformation(float intensity);
137
138 bool isIdentity() const;
139
140private:
141 float m_intensity;
142};
143
144/*!
145 * \brief The AutoEnhanceTransformation class
146 */
147class AutoEnhanceTransformation : public virtual HSVTransformation
148{
149 static const int SHADOW_DETECT_MIN_INTENSITY;
150 static const int SHADOW_DETECT_MAX_INTENSITY;
151 static const int SHADOW_DETECT_INTENSITY_RANGE;
152 static const int EMPIRICAL_DARK;
153 static const float SHADOW_AGGRESSIVENESS_MUL;
154
155public:
156 AutoEnhanceTransformation(const QImage& basis_image);
157 virtual ~AutoEnhanceTransformation();
158
159 QColor transformPixel(const QColor& pixel_color) const;
160 bool isIdentity() const;
161
162private:
163 ShadowDetailTransformation* m_shadowTransform;
164 ToneExpansionTransformation* m_toneExpansionTransform;
165};
166
167
168#endif // GALLERY_UTIL_IMAGING_H_
169
1700
=== removed file 'src/photoeditor/orientation.cpp'
--- src/photoeditor/orientation.cpp 2015-02-26 20:28:06 +0000
+++ src/photoeditor/orientation.cpp 1970-01-01 00:00:00 +0000
@@ -1,152 +0,0 @@
1/*
2 * Copyright (C) 2011 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Lucas Beeler <lucas@yorba.org>
18 */
19
20#include <cstdio>
21
22#include "orientation.h"
23
24/*!
25 * \brief OrientationCorrection::fromOrientation
26 * \param o
27 * \return
28 */
29OrientationCorrection OrientationCorrection::fromOrientation(Orientation o)
30{
31 double rotation_angle = 0.0;
32 double horizontal_scale_factor = 1.0;
33
34 switch (o) {
35 case TOP_RIGHT_ORIGIN:
36 horizontal_scale_factor = -1.0;
37 break;
38
39 case BOTTOM_RIGHT_ORIGIN:
40 rotation_angle = 180.0;
41 break;
42
43 case BOTTOM_LEFT_ORIGIN:
44 horizontal_scale_factor = -1.0;
45 rotation_angle = 180.0;
46 break;
47
48 case LEFT_TOP_ORIGIN:
49 horizontal_scale_factor = -1.0;
50 rotation_angle = -90.0;
51 break;
52
53 case RIGHT_TOP_ORIGIN:
54 rotation_angle = 90.0;
55 break;
56
57 case RIGHT_BOTTOM_ORIGIN:
58 horizontal_scale_factor = -1.0;
59 rotation_angle = 90.0;
60 break;
61
62 case LEFT_BOTTOM_ORIGIN:
63 rotation_angle = -90.0;
64 break;
65
66 default:
67 ; // do nothing
68 break;
69 }
70
71 return OrientationCorrection(rotation_angle, horizontal_scale_factor);
72}
73
74/*!
75 * \brief OrientationCorrection::identity
76 * \return
77 */
78OrientationCorrection OrientationCorrection::identity()
79{
80 return OrientationCorrection(0.0, 1.0);
81}
82
83/*!
84 * \brief OrientationCorrection::rotateOrientation
85 * \param orientation
86 * \param left
87 * \return
88 */
89Orientation OrientationCorrection::rotateOrientation(Orientation orientation, bool left)
90{
91 QVector<Orientation> sequence_a;
92 QVector<Orientation> sequence_b;
93 sequence_a <<
94 TOP_LEFT_ORIGIN << LEFT_BOTTOM_ORIGIN << BOTTOM_RIGHT_ORIGIN << RIGHT_TOP_ORIGIN;
95 sequence_b <<
96 TOP_RIGHT_ORIGIN << RIGHT_BOTTOM_ORIGIN << BOTTOM_LEFT_ORIGIN << LEFT_TOP_ORIGIN;
97
98 const QVector<Orientation>& sequence = (
99 sequence_a.contains(orientation) ? sequence_a : sequence_b);
100
101 int current = sequence.indexOf(orientation);
102 int jump = (left ? 1 : sequence.count() - 1);
103 int next = (current + jump) % sequence.count();
104
105 return sequence[next];
106}
107
108/*!
109 * \brief OrientationCorrection::toTransform
110 * Returns the correction as a QTransform.
111 * \return Returns the correction as a QTransform.
112 */
113QTransform OrientationCorrection::toTransform() const
114{
115 QTransform result;
116 result.scale(m_horizontalScaleFactor, 1.0);
117 result.rotate(m_rotationAngle);
118
119 return result;
120}
121
122/*!
123 * \brief OrientationCorrection::isFlippedFrom
124 * Returns whether the two orientations are flipped relative to each other.
125 * Ignores rotation_angle; only checks horizontal_scale_factor_.
126 * \param other
127 * \return
128 */
129bool OrientationCorrection::isFlippedFrom(
130 const OrientationCorrection& other) const
131{
132 return (m_horizontalScaleFactor != other.m_horizontalScaleFactor);
133}
134
135/*!
136 * \brief OrientationCorrection::getNormalizedRotationDifference
137 * Returns the rotation difference in degrees (this - other), normalized to
138 * 0, 90, 180, or 270. Ignores the horizontal_scale_factor_.
139 * \param other
140 * \return
141 */
142int OrientationCorrection::getNormalizedRotationDifference(
143 const OrientationCorrection& other) const
144{
145 int degrees_rotation = (int)m_rotationAngle - (int)other.m_rotationAngle;
146 if (degrees_rotation < 0)
147 degrees_rotation += 360;
148
149 Q_ASSERT(degrees_rotation == 0 || degrees_rotation == 90 ||
150 degrees_rotation == 180 || degrees_rotation == 270);
151 return degrees_rotation;
152}
1530
=== removed file 'src/photoeditor/orientation.h'
--- src/photoeditor/orientation.h 2015-02-26 20:28:06 +0000
+++ src/photoeditor/orientation.h 1970-01-01 00:00:00 +0000
@@ -1,64 +0,0 @@
1/*
2 * Copyright (C) 2011 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Lucas Beeler <lucas@yorba.org>
18 */
19
20#ifndef GALLERY_ORIENTATION_H_
21#define GALLERY_ORIENTATION_H_
22
23#include <QTransform>
24
25enum Orientation {
26 ORIGINAL_ORIENTATION = 0,
27 MIN_ORIENTATION = 1,
28 TOP_LEFT_ORIGIN = 1,
29 TOP_RIGHT_ORIGIN = 2,
30 BOTTOM_RIGHT_ORIGIN = 3,
31 BOTTOM_LEFT_ORIGIN = 4,
32 LEFT_TOP_ORIGIN = 5,
33 RIGHT_TOP_ORIGIN = 6,
34 RIGHT_BOTTOM_ORIGIN = 7,
35 LEFT_BOTTOM_ORIGIN = 8,
36 MAX_ORIENTATION = 8
37};
38
39/*!
40 * \brief The OrientationCorrection struct
41 */
42class OrientationCorrection
43{
44public:
45 static OrientationCorrection fromOrientation(Orientation o);
46 static OrientationCorrection identity();
47 static Orientation rotateOrientation(Orientation orientation, bool left);
48
49 QTransform toTransform() const;
50
51 bool isFlippedFrom(const OrientationCorrection& other) const;
52 int getNormalizedRotationDifference(const OrientationCorrection& other) const;
53
54private:
55 OrientationCorrection(double rotation_angle, double horizontal_scale_factor)
56 : m_rotationAngle(rotation_angle),
57 m_horizontalScaleFactor(horizontal_scale_factor) { }
58
59 const double m_rotationAngle;
60 const double m_horizontalScaleFactor;
61};
62
63
64#endif // GALLERY_ORIENTATION_H_
650
=== removed file 'src/photoeditor/photo-caches.cpp'
--- src/photoeditor/photo-caches.cpp 2015-02-26 20:28:06 +0000
+++ src/photoeditor/photo-caches.cpp 1970-01-01 00:00:00 +0000
@@ -1,183 +0,0 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Lindsay <chaz@yorba.org>
18 */
19
20#include "photo-caches.h"
21
22#include <QDir>
23#include <utime.h>
24
25const QString PhotoCaches::ORIGINAL_DIR = ".original";
26const QString PhotoCaches::ENHANCED_DIR = ".enhanced";
27
28/*!
29 * \brief PhotoCaches::PhotoCaches
30 * \param file
31 */
32PhotoCaches::PhotoCaches(const QFileInfo& file) : m_file(file),
33 m_originalFile(file.dir(),
34 QString("%1/%2").arg(ORIGINAL_DIR).arg(file.fileName())),
35 m_enhancedFile(file.dir(),
36 QString("%1/%2").arg(ENHANCED_DIR).arg(file.fileName()))
37{
38 // We always want our file checks to hit the disk.
39 m_file.setCaching(false);
40 m_originalFile.setCaching(false);
41 m_enhancedFile.setCaching(false);
42}
43
44/*!
45 * \brief PhotoCaches::hasCachedOriginal
46 * \return
47 */
48bool PhotoCaches::hasCachedOriginal() const
49{
50 return m_originalFile.exists();
51}
52
53/*!
54 * \brief PhotoCaches::hasCachedEnhanced
55 * \return
56 */
57bool PhotoCaches::hasCachedEnhanced() const
58{
59 return m_enhancedFile.exists();
60}
61
62/*!
63 * \brief PhotoCaches::originalFile
64 * \return
65 */
66const QFileInfo& PhotoCaches::originalFile() const
67{
68 return m_originalFile;
69}
70
71/*!
72 * \brief PhotoCaches::enhancedFile
73 * \return
74 */
75const QFileInfo& PhotoCaches::enhancedFile() const
76{
77 return m_enhancedFile;
78}
79
80/*!
81 * \brief PhotoCaches::pristineFile
82 * Returns original_file() if it exists; otherwise, returns the file passed
83 * to the constructor.
84 * \return
85 */
86const QFileInfo& PhotoCaches::pristineFile() const
87{
88 return (hasCachedOriginal() ? m_originalFile : m_file);
89}
90
91/*!
92 * \brief PhotoCaches::cacheOriginal
93 * Moves the pristine file into .original so we don't mess it up. Note that
94 * this potentially removes the main file, so it must be followed by a copy
95 * from original (or elsewhere) back to the file.
96 * \return
97 */
98bool PhotoCaches::cacheOriginal()
99{
100 if (hasCachedOriginal()) {
101 return true;
102 }
103
104 m_file.dir().mkdir(ORIGINAL_DIR);
105
106 return rename(m_file, m_originalFile);
107}
108
109/*!
110 * \brief PhotoCaches::restoreOriginal
111 * Moves the file out of .original, overwriting the main file. Note that
112 * this removes the .original file.
113 * \return
114 */
115bool PhotoCaches::restoreOriginal()
116{
117 if (!hasCachedOriginal()) {
118 return true;
119 }
120
121 remove(m_file);
122 // touch the file so that the thumbnails will correctly regenerate
123 utime(m_originalFile.absoluteFilePath().toUtf8(), NULL);
124 return rename(m_originalFile, m_file);
125}
126
127/*!
128 * \brief PhotoCaches::cacheEnhancedFromOriginal
129 * Copies the file in .original to .enhanced so it can then be enhanced.
130 * \return
131 */
132bool PhotoCaches::cacheEnhancedFromOriginal()
133{
134 m_file.dir().mkdir(ENHANCED_DIR);
135
136 // If called subsequently, the previously cached version is replaced.
137 remove(m_enhancedFile);
138 return copy(pristineFile(), m_enhancedFile);
139}
140
141/*!
142 * \brief PhotoCaches::overwriteFromCache
143 * Tries to overwrite the file from one of its cached versions.
144 * \param preferEnhanced
145 * \return
146 */
147bool PhotoCaches::overwriteFromCache(bool preferEnhanced)
148{
149 if (preferEnhanced && hasCachedEnhanced()) {
150 remove(m_file);
151 return copy(m_enhancedFile, m_file);
152 } else if (hasCachedOriginal()) {
153 remove(m_file);
154 return copy(m_originalFile, m_file);
155 } else {
156 return true;
157 }
158}
159
160/*!
161 * \brief PhotoCaches::discardCachedOriginal
162 */
163void PhotoCaches::discardCachedOriginal()
164{
165 remove(m_originalFile);
166}
167
168/*!
169 * \brief PhotoCaches::discardCachedEnhanced
170 */
171void PhotoCaches::discardCachedEnhanced()
172{
173 remove(m_enhancedFile);
174}
175
176/*!
177 * \brief PhotoCaches::discardAll
178 */
179void PhotoCaches::discardAll()
180{
181 discardCachedOriginal();
182 discardCachedEnhanced();
183}
1840
=== removed file 'src/photoeditor/photo-caches.h'
--- src/photoeditor/photo-caches.h 2015-02-26 20:28:06 +0000
+++ src/photoeditor/photo-caches.h 1970-01-01 00:00:00 +0000
@@ -1,76 +0,0 @@
1/*
2 * Copyright (C) 2012 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Charles Lindsay <chaz@yorba.org>
18 */
19
20#ifndef GALLERY_PHOTO_CACHES_H_
21#define GALLERY_PHOTO_CACHES_H_
22
23#include <QFile>
24#include <QFileInfo>
25#include <QString>
26
27/*!
28 * \brief The PhotoCaches class
29 *
30 * An abstraction around the various files we keep in addition to the photo
31 * file itself: the original, the pristine version of the file without any
32 * applied edits; and the enhanced, a version of the original with auto-enhance
33 * applied to it (necessary because of how slow auto-enhance is).
34 */
35class PhotoCaches
36{
37public:
38 static const QString ORIGINAL_DIR;
39 static const QString ENHANCED_DIR;
40
41 PhotoCaches(); // FIXME: remove this class and only deal with a stack of temporary rollback files
42 PhotoCaches(const QFileInfo& file);
43
44 bool hasCachedOriginal() const;
45 bool hasCachedEnhanced() const;
46
47 const QFileInfo& originalFile() const;
48 const QFileInfo& enhancedFile() const;
49 const QFileInfo& pristineFile() const;
50
51 bool cacheOriginal();
52 bool restoreOriginal();
53 bool cacheEnhancedFromOriginal();
54 bool overwriteFromCache(bool preferEnhanced);
55
56 void discardCachedOriginal();
57 void discardCachedEnhanced();
58 void discardAll();
59
60private:
61 static bool remove(const QFileInfo& file) {
62 return QFile::remove(file.filePath());
63 }
64 static bool rename(const QFileInfo& oldName, const QFileInfo& newName) {
65 return QFile::rename(oldName.filePath(), newName.filePath());
66 }
67 static bool copy(const QFileInfo& oldName, const QFileInfo& newName) {
68 return QFile::copy(oldName.filePath(), newName.filePath());
69 }
70
71 QFileInfo m_file;
72 QFileInfo m_originalFile;
73 QFileInfo m_enhancedFile;
74};
75
76#endif
770
=== removed file 'src/photoeditor/photo-data.cpp'
--- src/photoeditor/photo-data.cpp 2015-02-26 20:28:06 +0000
+++ src/photoeditor/photo-data.cpp 1970-01-01 00:00:00 +0000
@@ -1,275 +0,0 @@
1/*
2 * Copyright (C) 2011-2014 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Jim Nelson <jim@yorba.org>
18 * Lucas Beeler <lucas@yorba.org>
19 * Charles Lindsay <chaz@yorba.org>
20 * Eric Gregory <eric@yorba.org>
21 * Clint Rogers <clinton@yorba.org>
22 * Ugo Riboni <ugo.riboni@canonical.com>
23 */
24
25#include "photo-data.h"
26#include "photo-edit-command.h"
27#include "photo-edit-thread.h"
28
29// medialoader
30#include "photo-metadata.h"
31
32// util
33#include "imaging.h"
34
35#include <QApplication>
36#include <QDebug>
37#include <QDir>
38#include <QFileInfo>
39#include <QImage>
40#include <QImageReader>
41#include <QImageWriter>
42#include <QStack>
43#include <QStandardPaths>
44
45/*!
46 * \brief Photo::isValid
47 * \param file
48 * \return
49 */
50bool PhotoData::isValid(const QFileInfo& file)
51{
52 QImageReader reader(file.filePath());
53 QByteArray format = reader.format();
54
55 if (QString(format).toLower() == "tiff") {
56 // QImageReader.canRead() will detect some raw files as readable TIFFs,
57 // though QImage will fail to load them.
58 QString extension = file.suffix().toLower();
59 if (extension != "tiff" && extension != "tif")
60 return false;
61 }
62
63 PhotoMetadata* tmp = PhotoMetadata::fromFile(file);
64 if (tmp == NULL)
65 return false;
66
67 delete tmp;
68 return reader.canRead() &&
69 QImageWriter::supportedImageFormats().contains(reader.format());
70}
71
72/*!
73 * \brief Photo::Photo
74 * \param file
75 */
76PhotoData::PhotoData()
77 : QObject(),
78 m_editThread(0),
79 m_busy(false),
80 m_orientation(TOP_LEFT_ORIGIN)
81{
82}
83
84void PhotoData::setPath(QString path)
85{
86 if (QFileInfo(path).absoluteFilePath() != m_file.absoluteFilePath()) {
87 QFileInfo newFile(path);
88 if (newFile.exists() && newFile.isFile()) {
89 QByteArray format = QImageReader(newFile.absoluteFilePath()).format();
90 m_fileFormat = QString(format).toLower();
91 if (m_fileFormat == "jpg") // Why does Qt expose two different names here?
92 m_fileFormat = "jpeg";
93
94 m_file = newFile;
95 Q_EMIT pathChanged();
96
97 if (fileFormatHasMetadata()) {
98 PhotoMetadata* metadata = PhotoMetadata::fromFile(newFile.absoluteFilePath());
99 m_orientation = metadata->orientation();
100 delete metadata;
101 Q_EMIT orientationChanged();
102 }
103 }
104 }
105}
106
107QString PhotoData::path() const
108{
109 return m_file.absoluteFilePath();
110}
111
112QFileInfo PhotoData::file() const
113{
114 return m_file;
115}
116
117/*!
118 * \brief Photo::~Photo
119 */
120PhotoData::~PhotoData()
121{
122 if (m_editThread) {
123 m_editThread->wait();
124 finishEditing();
125 }
126}
127
128/*!
129 * \brief Photo::orientation
130 * \return
131 */
132Orientation PhotoData::orientation() const
133{
134 return m_orientation;
135}
136
137void PhotoData::refreshFromDisk()
138{
139 if (fileFormatHasMetadata()) {
140 PhotoMetadata* metadata = PhotoMetadata::fromFile(m_file.absoluteFilePath());
141 qDebug() << "Refreshing orient." << m_orientation << "to" << metadata->orientation();
142 m_orientation = metadata->orientation();
143 delete metadata;
144 Q_EMIT orientationChanged();
145 }
146
147 Q_EMIT dataChanged();
148}
149
150/*!
151 * \brief Photo::rotateRight
152 */
153void PhotoData::rotateRight()
154{
155 Orientation current = fileFormatHasOrientation() ? orientation() :
156 TOP_LEFT_ORIGIN;
157 Orientation rotated = OrientationCorrection::rotateOrientation(current,
158 false);
159 qDebug() << " Rotate from orientation " << current << "to" << rotated;
160
161 PhotoEditCommand command;
162 command.type = EDIT_ROTATE;
163 command.orientation = rotated;
164 asyncEdit(command);
165}
166
167/*!
168 * \brief Photo::autoEnhance
169 */
170void PhotoData::autoEnhance()
171{
172 PhotoEditCommand command;
173 command.type = EDIT_ENHANCE;
174 asyncEdit(command);
175}
176
177/*!
178 * \brief Photo::exposureCompensation Changes the brightnes of the image
179 * \param value Value for the compensation. -1.0 moves the image into total black.
180 * +1.0 to total white. 0.0 leaves it as it is.
181 */
182void PhotoData::exposureCompensation(qreal value)
183{
184 PhotoEditCommand command;
185 command.type = EDIT_COMPENSATE_EXPOSURE;
186 command.exposureCompensation = value;
187 asyncEdit(command);
188}
189
190/*!
191 * \brief Photo::crop
192 * Specify all coords in [0.0, 1.0], where 1.0 is the full size of the image.
193 * They will be clamped to this range if you don't.
194 * \param vrect the rectangle specifying the region to be cropped
195 */
196void PhotoData::crop(QVariant vrect)
197{
198 PhotoEditCommand command;
199 command.type = EDIT_CROP;
200 command.crop_rectangle = vrect.toRectF();
201 asyncEdit(command);
202}
203
204/*!
205 * \brief Photo::asyncEdit does edit the photo according to the given command
206 * in a background thread.
207 * \param The command defining the edit operation to perform.
208 */
209void PhotoData::asyncEdit(const PhotoEditCommand& command)
210{
211 if (m_busy) {
212 qWarning() << "Can't start edit operation while another one is running.";
213 return;
214 }
215 m_busy = true;
216 Q_EMIT busyChanged();
217 m_editThread = new PhotoEditThread(this, command);
218 connect(m_editThread, SIGNAL(finished()), this, SLOT(finishEditing()));
219 m_editThread->start();
220}
221
222/*!
223 * \brief Photo::finishEditing do all the updates once the editing is done
224 */
225void PhotoData::finishEditing()
226{
227 if (!m_editThread || m_editThread->isRunning())
228 return;
229
230 m_editThread->deleteLater();
231 m_editThread = 0;
232 m_busy = false;
233
234 refreshFromDisk();
235
236 Q_EMIT busyChanged();
237 Q_EMIT editFinished();
238}
239
240/*!
241 * \brief Photo::fileFormat returns the file format as QString
242 * \return
243 */
244const QString &PhotoData::fileFormat() const
245{
246 return m_fileFormat;
247}
248
249/*!
250 * \brief Photo::fileFormatHasMetadata
251 * \return
252 */
253bool PhotoData::fileFormatHasMetadata() const
254{
255 return (m_fileFormat == "jpeg" || m_fileFormat == "tiff" ||
256 m_fileFormat == "png");
257}
258
259/*!
260 * \brief Photo::fileFormatHasOrientation
261 * \return
262 */
263bool PhotoData::fileFormatHasOrientation() const
264{
265 return (m_fileFormat == "jpeg");
266}
267
268/*!
269 * \brief Photo::busy return true if there is an editing operation in progress
270 * \return
271 */
272bool PhotoData::busy() const
273{
274 return m_busy;
275}
2760
=== removed file 'src/photoeditor/photo-data.h'
--- src/photoeditor/photo-data.h 2015-02-26 20:28:06 +0000
+++ src/photoeditor/photo-data.h 1970-01-01 00:00:00 +0000
@@ -1,92 +0,0 @@
1/*
2 * Copyright (C) 2011-2014 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Jim Nelson <jim@yorba.org>
18 * Lucas Beeler <lucas@yorba.org>
19 * Charles Lindsay <chaz@yorba.org>
20 * Ugo Riboni <ugo.riboni@canonical.com>
21 */
22
23#ifndef PHOTO_DATA_H_
24#define PHOTO_DATA_H_
25
26// util
27#include "orientation.h"
28
29// QT
30#include <QFileInfo>
31#include <QVariant>
32
33class PhotoEditCommand;
34class PhotoEditThread;
35
36/*!
37 * \brief The Photo class
38 */
39class PhotoData : public QObject
40{
41 Q_OBJECT
42
43 Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
44 Q_PROPERTY(int orientation READ orientation NOTIFY orientationChanged)
45 Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
46
47public:
48 explicit PhotoData();
49 virtual ~PhotoData();
50
51 static bool isValid(const QFileInfo& file);
52
53 QString path() const;
54 void setPath(QString path);
55 QFileInfo file() const;
56 bool busy() const;
57
58 virtual Orientation orientation() const;
59
60 Q_INVOKABLE void refreshFromDisk();
61 Q_INVOKABLE void rotateRight();
62 Q_INVOKABLE void autoEnhance();
63 Q_INVOKABLE void exposureCompensation(qreal value);
64 Q_INVOKABLE void crop(QVariant vrect);
65
66 const QString &fileFormat() const;
67 bool fileFormatHasMetadata() const;
68 bool fileFormatHasOrientation() const;
69
70Q_SIGNALS:
71 void pathChanged();
72 void orientationChanged();
73 void busyChanged();
74
75 void editFinished();
76 void dataChanged();
77
78private Q_SLOTS:
79 void finishEditing();
80
81private:
82 void asyncEdit(const PhotoEditCommand& state);
83
84 QString m_fileFormat;
85 PhotoEditThread *m_editThread;
86 QFileInfo m_file;
87 bool m_busy;
88
89 Orientation m_orientation;
90};
91
92#endif // PHOTO_DATA_H_
930
=== removed file 'src/photoeditor/photo-edit-command.h'
--- src/photoeditor/photo-edit-command.h 2015-02-26 20:28:06 +0000
+++ src/photoeditor/photo-edit-command.h 1970-01-01 00:00:00 +0000
@@ -1,58 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef GALLERY_PHOTO_EDIT_STATE_H_
18#define GALLERY_PHOTO_EDIT_STATE_H_
19
20// util
21#include "orientation.h"
22
23#include <QRectF>
24#include <QVector4D>
25
26enum EditType {
27 EDIT_NONE = 0,
28 EDIT_ROTATE = 1,
29 EDIT_CROP = 2,
30 EDIT_ENHANCE = 3,
31 EDIT_COMPENSATE_EXPOSURE = 4
32};
33
34/*!
35 * \brief The PhotoEditCommand class
36 *
37 * A single edit that will be applied to a photo.
38 */
39class PhotoEditCommand
40{
41public:
42 EditType type;
43 Orientation orientation;
44 QRectF crop_rectangle;
45 qreal exposureCompensation;
46 /// The color balance parameters are stored here in the order:
47 /// brightness (x), contrast(y), saturation(z), hue(w)
48 QVector4D colorBalance_;
49
50 PhotoEditCommand() :
51 type(EDIT_NONE),
52 orientation(ORIGINAL_ORIENTATION),
53 crop_rectangle(),
54 exposureCompensation(0.0) {
55 }
56};
57
58#endif
590
=== removed file 'src/photoeditor/photo-edit-thread.cpp'
--- src/photoeditor/photo-edit-thread.cpp 2015-02-26 20:28:06 +0000
+++ src/photoeditor/photo-edit-thread.cpp 1970-01-01 00:00:00 +0000
@@ -1,185 +0,0 @@
1/*
2 * Copyright (C) 2013-2014 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "photo-edit-thread.h"
18#include "photo-data.h"
19
20// medialoader
21#include "photo-metadata.h"
22
23// util
24#include "imaging.h"
25
26#include <QDebug>
27
28/*!
29 * \brief PhotoEditThread::PhotoEditThread
30 */
31PhotoEditThread::PhotoEditThread(PhotoData *photo, const PhotoEditCommand &command)
32 : QThread(),
33 m_photo(photo),
34 m_command(command)
35{
36}
37
38/*!
39 * \brief PhotoEditThread::command resturns the editing command used for this processing
40 * \return
41 */
42const PhotoEditCommand &PhotoEditThread::command() const
43{
44 return m_command;
45}
46
47/*!
48 * \brief PhotoEditThread::run \reimp
49 */
50void PhotoEditThread::run()
51{
52 // The only operation in which we don't have to work on the actual image
53 // pixels is image rotation in the case where we can simply change the
54 // metadata rotation field.
55 if (m_command.type == EDIT_ROTATE && m_photo->fileFormatHasOrientation()) {
56 handleSimpleMetadataRotation(m_command);
57 return;
58 }
59
60 // In all other cases we load the image, do the work, and save it back.
61 QImage image(m_photo->file().filePath(), m_photo->fileFormat().toStdString().c_str());
62 if (image.isNull()) {
63 qWarning() << "Error loading" << m_photo->file().filePath() << "for editing";
64 return;
65 }
66
67 // Copy all metadata from the original image so that we can save it to the
68 // new one after modifying the pixels.
69 PhotoMetadata* original = PhotoMetadata::fromFile(m_photo->file());
70
71 // If the photo was previously rotated through metadata and we are editing
72 // the actual pixels, first rotate the image to match the orientation so
73 // that the correct pixels are edited.
74 // Obviously don't do this in the case we have been asked to do a rotation
75 // operation on the pixels, as we would do it later as the operation itself.
76 if (m_photo->fileFormatHasOrientation() && m_command.type != EDIT_ROTATE) {
77 Orientation orientation = m_photo->orientation();
78 QTransform transform = OrientationCorrection::fromOrientation(orientation).toTransform();
79 image = image.transformed(transform);
80 }
81
82 if (m_command.type == EDIT_ROTATE) {
83 QTransform transform = OrientationCorrection::fromOrientation(m_command.orientation).toTransform();
84 image = image.transformed(transform);
85 } else if (m_command.type == EDIT_CROP) {
86 QRect rect;
87 rect.setX(qBound(0.0, m_command.crop_rectangle.x(), 1.0) * image.width());
88 rect.setY(qBound(0.0, m_command.crop_rectangle.y(), 1.0) * image.height());
89 rect.setWidth(qBound(0.0, m_command.crop_rectangle.width(), 1.0) * image.width());
90 rect.setHeight(qBound(0.0, m_command.crop_rectangle.height(), 1.0) * image.height());
91
92 image = image.copy(rect);
93 } else if (m_command.type == EDIT_ENHANCE) {
94 image = enhanceImage(image);
95 } else if (m_command.type == EDIT_COMPENSATE_EXPOSURE) {
96 image = compensateExposure(image, m_command.exposureCompensation);
97 } else {
98 qWarning() << "Edit thread running with unknown or no operation.";
99 return;
100 }
101
102 bool saved = image.save(m_photo->file().filePath(),
103 m_photo->fileFormat().toStdString().c_str(), 90);
104 if (!saved)
105 qWarning() << "Error saving edited" << m_photo->file().filePath();
106
107 PhotoMetadata* copy = PhotoMetadata::fromFile(m_photo->file());
108 original->copyTo(copy);
109 copy->setOrientation(TOP_LEFT_ORIGIN); // reset previous orientation
110 copy->updateThumbnail(image);
111 copy->save();
112
113 delete original;
114 delete copy;
115}
116
117/*!
118 * \brief PhotoEditThread::handleSimpleMetadataRotation
119 * Handler for the case of an image whose only change is to its
120 * orientation; used to skip re-encoding of JPEGs.
121 * \param state
122 */
123void PhotoEditThread::handleSimpleMetadataRotation(const PhotoEditCommand& state)
124{
125 PhotoMetadata* metadata = PhotoMetadata::fromFile(m_photo->file());
126 metadata->setOrientation(state.orientation);
127 metadata->save();
128 delete(metadata);
129}
130
131/*!
132 * \brief PhotoEditThread::enhanceImage
133 */
134QImage PhotoEditThread::enhanceImage(const QImage& image)
135{
136 int width = image.width();
137 int height = image.height();
138
139 QImage sample_img = (image.width() > 400) ? image.scaledToWidth(400) : image;
140
141 AutoEnhanceTransformation enhance = AutoEnhanceTransformation(sample_img);
142
143 QImage::Format dest_format = image.format();
144
145 // Can't write into indexed images, due to a limitation in Qt.
146 if (dest_format == QImage::Format_Indexed8)
147 dest_format = QImage::Format_RGB32;
148
149 QImage enhanced_image(width, height, dest_format);
150
151 for (int j = 0; j < height; j++) {
152 for (int i = 0; i < width; i++) {
153 QColor px = enhance.transformPixel(
154 QColor(image.pixel(i, j)));
155 enhanced_image.setPixel(i, j, px.rgb());
156 }
157 }
158
159 return enhanced_image;
160}
161
162/*!
163 * \brief PhotoEditThread::compensateExposure Compensates the exposure
164 * Compensating the exposure is a change in brightnes
165 * \param image Image to change the brightnes
166 * \param compansation -1.0 is total dark, +1.0 is total bright
167 * \return The image with adjusted brightnes
168 */
169QImage PhotoEditThread::compensateExposure(const QImage &image, qreal compensation)
170{
171 int shift = qBound(-255, (int)(255*compensation), 255);
172 QImage result(image.width(), image.height(), image.format());
173
174 for (int j = 0; j < image.height(); j++) {
175 for (int i = 0; i <image.width(); i++) {
176 QColor px = image.pixel(i, j);
177 int red = qBound(0, px.red() + shift, 255);
178 int green = qBound(0, px.green() + shift, 255);
179 int blue = qBound(0, px.blue() + shift, 255);
180 result.setPixel(i, j, qRgb(red, green, blue));
181 }
182 }
183
184 return result;
185}
1860
=== removed file 'src/photoeditor/photo-edit-thread.h'
--- src/photoeditor/photo-edit-thread.h 2015-02-26 20:28:06 +0000
+++ src/photoeditor/photo-edit-thread.h 1970-01-01 00:00:00 +0000
@@ -1,56 +0,0 @@
1/*
2 * Copyright (C) 2013-2014 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef GALLERY_PHOTO_EDIT_THREAD_H_
18#define GALLERY_PHOTO_EDIT_THREAD_H_
19
20#include "photo-caches.h"
21#include "photo-edit-command.h"
22
23// util
24#include "orientation.h"
25
26#include <QImage>
27#include <QThread>
28#include <QUrl>
29
30class PhotoData;
31
32/*!
33 * \brief The PhotoEditThread class
34 */
35class PhotoEditThread: public QThread
36{
37 Q_OBJECT
38public:
39 PhotoEditThread(PhotoData *photo, const PhotoEditCommand& command);
40
41 const PhotoEditCommand& command() const;
42
43protected:
44 void run() Q_DECL_OVERRIDE;
45
46private:
47 QImage enhanceImage(const QImage& image);
48 QImage compensateExposure(const QImage& image, qreal compansation);
49 QImage doColorBalance(const QImage& image, qreal brightness, qreal contrast, qreal saturation, qreal hue);
50 void handleSimpleMetadataRotation(const PhotoEditCommand& state);
51
52 PhotoData *m_photo;
53 PhotoEditCommand m_command;
54};
55
56#endif

Subscribers

People subscribed via source and target branches