Merge lp:~artmello/gallery-app/gallery-app-remove_local_photoeditor into lp:gallery-app
- gallery-app-remove_local_photoeditor
- Merge into trunk
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 | ||||
Related bugs: |
|
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
1 | === removed file 'rc/qml/MediaViewer/GalleryPhotoEditorPage.qml' | |||
2 | --- rc/qml/MediaViewer/GalleryPhotoEditorPage.qml 2015-11-05 18:10:29 +0000 | |||
3 | +++ rc/qml/MediaViewer/GalleryPhotoEditorPage.qml 1970-01-01 00:00:00 +0000 | |||
4 | @@ -1,42 +0,0 @@ | |||
5 | 1 | /* | ||
6 | 2 | * Copyright (C) 2014-2015 Canonical Ltd | ||
7 | 3 | * | ||
8 | 4 | * This program is free software: you can redistribute it and/or modify | ||
9 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
10 | 6 | * published by the Free Software Foundation. | ||
11 | 7 | * | ||
12 | 8 | * This program is distributed in the hope that it will be useful, | ||
13 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | 11 | * GNU General Public License for more details. | ||
16 | 12 | * | ||
17 | 13 | * You should have received a copy of the GNU General Public License | ||
18 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | 15 | */ | ||
20 | 16 | |||
21 | 17 | import QtQuick 2.4 | ||
22 | 18 | import Ubuntu.Components 1.3 | ||
23 | 19 | |||
24 | 20 | Page { | ||
25 | 21 | id: page | ||
26 | 22 | property string photo | ||
27 | 23 | signal done(bool photoWasModified) | ||
28 | 24 | |||
29 | 25 | title: i18n.tr("Edit Photo") | ||
30 | 26 | |||
31 | 27 | head.backAction: Action { | ||
32 | 28 | iconName: "back" | ||
33 | 29 | onTriggered: editor.close(true) | ||
34 | 30 | } | ||
35 | 31 | head.actions: editor.actions | ||
36 | 32 | |||
37 | 33 | PhotoEditor { | ||
38 | 34 | id: editor | ||
39 | 35 | anchors.fill: parent | ||
40 | 36 | onClosed: page.done(photoWasModified) | ||
41 | 37 | } | ||
42 | 38 | |||
43 | 39 | onActiveChanged: { | ||
44 | 40 | if (active) editor.open(page.photo) | ||
45 | 41 | } | ||
46 | 42 | } | ||
47 | 43 | 0 | ||
48 | === modified file 'rc/qml/MediaViewer/MediaViewer.qml' | |||
49 | --- rc/qml/MediaViewer/MediaViewer.qml 2015-11-05 18:10:29 +0000 | |||
50 | +++ rc/qml/MediaViewer/MediaViewer.qml 2015-12-16 13:52:17 +0000 | |||
51 | @@ -325,11 +325,10 @@ | |||
52 | 325 | var editor; | 325 | var editor; |
53 | 326 | try { | 326 | try { |
54 | 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); |
57 | 328 | console.log("Loading PhotoEditor Components from Extras"); | 328 | editor = overview.pushPage(Qt.resolvedUrl("PhotoEditorPage.qml"), { photo: path }); |
56 | 329 | editor = overview.pushPage(Qt.resolvedUrl("ExtrasPhotoEditorPage.qml"), { photo: path }); | ||
58 | 330 | } catch (e) { | 329 | } catch (e) { |
61 | 331 | console.log("Loading PhotoEditor Components from Gallery code"); | 330 | console.log("WARNING: Unable to load PhotoEditor from Ubuntu.Components.Extras"); |
62 | 332 | editor = overview.pushPage(Qt.resolvedUrl("GalleryPhotoEditorPage.qml"), { photo: path }); | 331 | return; |
63 | 333 | } | 332 | } |
64 | 334 | editor.done.connect(function(photoWasModified) { | 333 | editor.done.connect(function(photoWasModified) { |
65 | 335 | if (photoWasModified) galleryPhotoViewer.media.dataChanged(); | 334 | if (photoWasModified) galleryPhotoViewer.media.dataChanged(); |
66 | 336 | 335 | ||
67 | === removed directory 'rc/qml/MediaViewer/PhotoEditor' | |||
68 | === removed file 'rc/qml/MediaViewer/PhotoEditor.qml' | |||
69 | --- rc/qml/MediaViewer/PhotoEditor.qml 2015-11-05 19:04:22 +0000 | |||
70 | +++ rc/qml/MediaViewer/PhotoEditor.qml 1970-01-01 00:00:00 +0000 | |||
71 | @@ -1,230 +0,0 @@ | |||
72 | 1 | /* | ||
73 | 2 | * Copyright (C) 2014-2015 Canonical Ltd | ||
74 | 3 | * | ||
75 | 4 | * This program is free software: you can redistribute it and/or modify | ||
76 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
77 | 6 | * published by the Free Software Foundation. | ||
78 | 7 | * | ||
79 | 8 | * This program is distributed in the hope that it will be useful, | ||
80 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
81 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
82 | 11 | * GNU General Public License for more details. | ||
83 | 12 | * | ||
84 | 13 | * You should have received a copy of the GNU General Public License | ||
85 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
86 | 15 | */ | ||
87 | 16 | |||
88 | 17 | import QtQuick 2.4 | ||
89 | 18 | import Ubuntu.Components 1.3 | ||
90 | 19 | import Ubuntu.Components.Popups 1.3 | ||
91 | 20 | import Gallery 1.0 | ||
92 | 21 | import "PhotoEditor" | ||
93 | 22 | |||
94 | 23 | Item { | ||
95 | 24 | id: editor | ||
96 | 25 | property string photo | ||
97 | 26 | property bool modified: stack.modified | ||
98 | 27 | |||
99 | 28 | signal closed(bool photoWasModified) | ||
100 | 29 | |||
101 | 30 | property list<Action> actions | ||
102 | 31 | actions: [stack.undoAction, stack.redoAction] | ||
103 | 32 | |||
104 | 33 | EditStack { | ||
105 | 34 | id: stack | ||
106 | 35 | data: photoData | ||
107 | 36 | actionsEnabled: !exposureSelector.visible && !cropper.visible && !photoData.busy | ||
108 | 37 | onRevertRequested: PopupUtils.open(revertPromptComponent) | ||
109 | 38 | } | ||
110 | 39 | |||
111 | 40 | property list<Action> toolActions: [ | ||
112 | 41 | Action { | ||
113 | 42 | objectName: "cropButton" | ||
114 | 43 | text: i18n.tr("Crop") | ||
115 | 44 | iconSource: Qt.resolvedUrl("PhotoEditor/assets/edit_crop.png") | ||
116 | 45 | onTriggered: { | ||
117 | 46 | photoData.isLongOperation = false; | ||
118 | 47 | cropper.start("image://photo/" + photoData.path); | ||
119 | 48 | } | ||
120 | 49 | }, | ||
121 | 50 | Action { | ||
122 | 51 | objectName: "rotateButton" | ||
123 | 52 | text: i18n.tr("Rotate") | ||
124 | 53 | iconSource: Qt.resolvedUrl("PhotoEditor/assets/edit_rotate_left.png") | ||
125 | 54 | onTriggered: { | ||
126 | 55 | photoData.isLongOperation = false; | ||
127 | 56 | photoData.rotateRight() | ||
128 | 57 | } | ||
129 | 58 | } | ||
130 | 59 | ] | ||
131 | 60 | |||
132 | 61 | function close(saveIfModified) { | ||
133 | 62 | stack.endEditingSession(saveIfModified); | ||
134 | 63 | editor.closed(editor.modified); | ||
135 | 64 | } | ||
136 | 65 | |||
137 | 66 | function open(photo) { | ||
138 | 67 | editor.photo = photo; | ||
139 | 68 | stack.startEditingSession(photo); | ||
140 | 69 | photoData.path = stack.currentFile; | ||
141 | 70 | image.source = "image://photo/" + photoData.path; | ||
142 | 71 | } | ||
143 | 72 | |||
144 | 73 | Rectangle { | ||
145 | 74 | color: "black" | ||
146 | 75 | anchors.fill: parent | ||
147 | 76 | } | ||
148 | 77 | |||
149 | 78 | Image { | ||
150 | 79 | id: image | ||
151 | 80 | anchors.fill: parent | ||
152 | 81 | asynchronous: true | ||
153 | 82 | cache: false | ||
154 | 83 | source: photoData.path ? "image://photo/" + photoData.path : "" | ||
155 | 84 | fillMode: Image.PreserveAspectFit | ||
156 | 85 | sourceSize { | ||
157 | 86 | width: image.width | ||
158 | 87 | height: image.height | ||
159 | 88 | } | ||
160 | 89 | |||
161 | 90 | function reload() { | ||
162 | 91 | image.asynchronous = false; | ||
163 | 92 | image.source = ""; | ||
164 | 93 | image.asynchronous = true; | ||
165 | 94 | image.source = "image://photo/" + photoData.path; | ||
166 | 95 | } | ||
167 | 96 | } | ||
168 | 97 | |||
169 | 98 | GalleryPhotoData { | ||
170 | 99 | id: photoData | ||
171 | 100 | onDataChanged: image.reload() | ||
172 | 101 | property bool isLongOperation: false | ||
173 | 102 | |||
174 | 103 | onEditFinished: { | ||
175 | 104 | console.log("Edit finished") | ||
176 | 105 | // If we are editing exposure we don't need to checkpoint at every | ||
177 | 106 | // edit, and the exposure UI will checkpoint when the user confirms. | ||
178 | 107 | if (exposureSelector.opacity > 0) exposureSelector.reload() | ||
179 | 108 | else stack.checkpoint() | ||
180 | 109 | } | ||
181 | 110 | } | ||
182 | 111 | |||
183 | 112 | Loader { | ||
184 | 113 | id: cropper | ||
185 | 114 | |||
186 | 115 | anchors.fill: parent | ||
187 | 116 | |||
188 | 117 | opacity: 0.0 | ||
189 | 118 | visible: opacity > 0 | ||
190 | 119 | Behavior on opacity { UbuntuNumberAnimation { } } | ||
191 | 120 | |||
192 | 121 | Connections { | ||
193 | 122 | target: cropper.item | ||
194 | 123 | ignoreUnknownSignals: true | ||
195 | 124 | onCropped: { | ||
196 | 125 | var qtRect = Qt.rect(rect.x, rect.y, rect.width, rect.height); | ||
197 | 126 | photoData.crop(qtRect); | ||
198 | 127 | cropper.opacity = 0.0; | ||
199 | 128 | cropper.source = "" | ||
200 | 129 | } | ||
201 | 130 | onCanceled: { | ||
202 | 131 | cropper.opacity = 0.0; | ||
203 | 132 | cropper.source = "" | ||
204 | 133 | } | ||
205 | 134 | } | ||
206 | 135 | |||
207 | 136 | function start(target) { | ||
208 | 137 | source = "PhotoEditor/CropInteractor.qml"; | ||
209 | 138 | item.targetPhoto = target; | ||
210 | 139 | } | ||
211 | 140 | |||
212 | 141 | onLoaded: opacity = 1.0 | ||
213 | 142 | } | ||
214 | 143 | |||
215 | 144 | ExposureAdjuster { | ||
216 | 145 | id: exposureSelector | ||
217 | 146 | anchors.fill: parent | ||
218 | 147 | opacity: 0.0 | ||
219 | 148 | enabled: !photoData.busy | ||
220 | 149 | onExposureChanged: { | ||
221 | 150 | // Restore the starting version of the image, otherwise we will | ||
222 | 151 | // accumulate compensations over the previous ones. | ||
223 | 152 | stack.restoreSnapshot(stack.level) | ||
224 | 153 | photoData.exposureCompensation(exposure) | ||
225 | 154 | } | ||
226 | 155 | onConfirm: { | ||
227 | 156 | stack.checkpoint(); | ||
228 | 157 | exposureSelector.opacity = 0.0 | ||
229 | 158 | } | ||
230 | 159 | onCancel: { | ||
231 | 160 | stack.restoreSnapshot(stack.level) | ||
232 | 161 | exposureSelector.opacity = 0.0 | ||
233 | 162 | } | ||
234 | 163 | visible: opacity > 0 | ||
235 | 164 | } | ||
236 | 165 | |||
237 | 166 | ActionsBar { | ||
238 | 167 | id: actionsBar | ||
239 | 168 | objectName: "editorActionsBar" | ||
240 | 169 | anchors.bottom: parent.bottom | ||
241 | 170 | anchors.left: parent.left | ||
242 | 171 | anchors.right: parent.right | ||
243 | 172 | |||
244 | 173 | visible: opacity > 0.0 | ||
245 | 174 | opacity: (exposureSelector.opacity == 0 && cropper.opacity == 0) ? 1.0 : 0.0 | ||
246 | 175 | |||
247 | 176 | enabled: !photoData.busy | ||
248 | 177 | toolActions: { | ||
249 | 178 | // This is necessary because QML does not let us declare a list with | ||
250 | 179 | // mixed component declarations and identifiers, like this: | ||
251 | 180 | // property list<Action> foo: { Action{}, someOtherAction } | ||
252 | 181 | var list = []; | ||
253 | 182 | for (var i = 0; i < editor.toolActions.length; i++) | ||
254 | 183 | list.push(editor.toolActions[i]); | ||
255 | 184 | list.push(stack.revertAction); | ||
256 | 185 | return list; | ||
257 | 186 | } | ||
258 | 187 | |||
259 | 188 | Behavior on opacity { UbuntuNumberAnimation {} } | ||
260 | 189 | } | ||
261 | 190 | |||
262 | 191 | Component { | ||
263 | 192 | id: revertPromptComponent | ||
264 | 193 | Dialog { | ||
265 | 194 | id: revertPrompt | ||
266 | 195 | objectName: "revertPromptDialog" | ||
267 | 196 | title: i18n.tr("Revert to original") | ||
268 | 197 | text: i18n.tr("This will undo all edits, including those from previous sessions.") | ||
269 | 198 | |||
270 | 199 | Row { | ||
271 | 200 | id: row | ||
272 | 201 | width: parent.width | ||
273 | 202 | spacing: units.gu(1) | ||
274 | 203 | Button { | ||
275 | 204 | objectName: "cancelRevertButton" | ||
276 | 205 | width: parent.width/2 | ||
277 | 206 | text: i18n.tr("Cancel") | ||
278 | 207 | onClicked: PopupUtils.close(revertPrompt) | ||
279 | 208 | } | ||
280 | 209 | Button { | ||
281 | 210 | objectName: "confirmRevertButton" | ||
282 | 211 | width: parent.width/2 | ||
283 | 212 | text: i18n.tr("Revert Photo") | ||
284 | 213 | color: UbuntuColors.green | ||
285 | 214 | onClicked: { | ||
286 | 215 | PopupUtils.close(revertPrompt) | ||
287 | 216 | stack.revertToPristine() | ||
288 | 217 | } | ||
289 | 218 | } | ||
290 | 219 | } | ||
291 | 220 | } | ||
292 | 221 | } | ||
293 | 222 | |||
294 | 223 | BusyIndicator { | ||
295 | 224 | id: busyIndicator | ||
296 | 225 | anchors.centerIn: parent | ||
297 | 226 | text: i18n.tr("Enhancing photo...") | ||
298 | 227 | running: photoData.busy | ||
299 | 228 | longOperation: photoData.isLongOperation | ||
300 | 229 | } | ||
301 | 230 | } | ||
302 | 231 | 0 | ||
303 | === removed file 'rc/qml/MediaViewer/PhotoEditor/ActionsBar.qml' | |||
304 | --- rc/qml/MediaViewer/PhotoEditor/ActionsBar.qml 2015-11-05 19:04:22 +0000 | |||
305 | +++ rc/qml/MediaViewer/PhotoEditor/ActionsBar.qml 1970-01-01 00:00:00 +0000 | |||
306 | @@ -1,88 +0,0 @@ | |||
307 | 1 | /* | ||
308 | 2 | * Copyright (C) 2014-2015 Canonical Ltd | ||
309 | 3 | * | ||
310 | 4 | * This program is free software: you can redistribute it and/or modify | ||
311 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
312 | 6 | * published by the Free Software Foundation. | ||
313 | 7 | * | ||
314 | 8 | * This program is distributed in the hope that it will be useful, | ||
315 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
316 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
317 | 11 | * GNU General Public License for more details. | ||
318 | 12 | * | ||
319 | 13 | * You should have received a copy of the GNU General Public License | ||
320 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
321 | 15 | */ | ||
322 | 16 | |||
323 | 17 | import QtQuick 2.4 | ||
324 | 18 | import Ubuntu.Components 1.3 | ||
325 | 19 | import Ubuntu.Components.ListItems 1.3 as ListItem | ||
326 | 20 | |||
327 | 21 | Column { | ||
328 | 22 | id: bar | ||
329 | 23 | property list<Action> toolActions | ||
330 | 24 | property list<Action> filterActions | ||
331 | 25 | property bool enabled | ||
332 | 26 | |||
333 | 27 | height: (filtersBar.visible) ? units.gu(20) : units.gu(6) | ||
334 | 28 | |||
335 | 29 | Item { | ||
336 | 30 | anchors.left: parent.left | ||
337 | 31 | anchors.right: parent.right | ||
338 | 32 | height: units.gu(6) | ||
339 | 33 | |||
340 | 34 | Rectangle { | ||
341 | 35 | anchors.fill: parent | ||
342 | 36 | color: "black" | ||
343 | 37 | opacity: 0.6 | ||
344 | 38 | } | ||
345 | 39 | |||
346 | 40 | ListView { | ||
347 | 41 | id: toolsBar | ||
348 | 42 | anchors.fill: parent | ||
349 | 43 | orientation: ListView.Horizontal | ||
350 | 44 | model: toolActions | ||
351 | 45 | |||
352 | 46 | delegate: AbstractButton { | ||
353 | 47 | width: units.gu(8) | ||
354 | 48 | anchors.top: parent.top | ||
355 | 49 | anchors.bottom: parent.bottom | ||
356 | 50 | action: modelData | ||
357 | 51 | enabled: bar.enabled | ||
358 | 52 | |||
359 | 53 | Icon { | ||
360 | 54 | anchors.centerIn: parent | ||
361 | 55 | name: modelData.iconName | ||
362 | 56 | source: modelData.iconSource | ||
363 | 57 | width: units.gu(3) | ||
364 | 58 | height: units.gu(3) | ||
365 | 59 | opacity: modelData.enabled && parent.enabled ? 1.0 : 0.5 | ||
366 | 60 | } | ||
367 | 61 | } | ||
368 | 62 | } | ||
369 | 63 | } | ||
370 | 64 | |||
371 | 65 | Rectangle { | ||
372 | 66 | anchors.left: parent.left | ||
373 | 67 | anchors.right: parent.right | ||
374 | 68 | height: units.gu(14) | ||
375 | 69 | color: "black" | ||
376 | 70 | |||
377 | 71 | ListView { | ||
378 | 72 | id: filtersBar | ||
379 | 73 | visible: filterActions.length > 0 | ||
380 | 74 | |||
381 | 75 | orientation: ListView.Horizontal | ||
382 | 76 | model: filterActions | ||
383 | 77 | |||
384 | 78 | delegate: ListItem.Standard { | ||
385 | 79 | width: parent.height | ||
386 | 80 | anchors.top: parent.top | ||
387 | 81 | anchors.bottom: parent.bottom | ||
388 | 82 | action: modelData | ||
389 | 83 | iconFrame: false | ||
390 | 84 | enabled: bar.enabled | ||
391 | 85 | } | ||
392 | 86 | } | ||
393 | 87 | } | ||
394 | 88 | } | ||
395 | 89 | 0 | ||
396 | === removed file 'rc/qml/MediaViewer/PhotoEditor/BusyIndicator.qml' | |||
397 | --- rc/qml/MediaViewer/PhotoEditor/BusyIndicator.qml 2015-11-05 18:10:29 +0000 | |||
398 | +++ rc/qml/MediaViewer/PhotoEditor/BusyIndicator.qml 1970-01-01 00:00:00 +0000 | |||
399 | @@ -1,56 +0,0 @@ | |||
400 | 1 | /* | ||
401 | 2 | * Copyright (C) 2015 Canonical Ltd | ||
402 | 3 | * | ||
403 | 4 | * This program is free software: you can redistribute it and/or modify | ||
404 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
405 | 6 | * published by the Free Software Foundation. | ||
406 | 7 | * | ||
407 | 8 | * This program is distributed in the hope that it will be useful, | ||
408 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
409 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
410 | 11 | * GNU General Public License for more details. | ||
411 | 12 | * | ||
412 | 13 | * You should have received a copy of the GNU General Public License | ||
413 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
414 | 15 | */ | ||
415 | 16 | |||
416 | 17 | import QtQuick 2.4 | ||
417 | 18 | import Ubuntu.Components 1.3 | ||
418 | 19 | |||
419 | 20 | Item { | ||
420 | 21 | id: busy | ||
421 | 22 | width: childrenRect.width | ||
422 | 23 | height: childrenRect.height | ||
423 | 24 | property alias text: label.text | ||
424 | 25 | property alias running: spinner.running | ||
425 | 26 | property bool longOperation: false | ||
426 | 27 | |||
427 | 28 | visible: running | ||
428 | 29 | |||
429 | 30 | UbuntuShape { | ||
430 | 31 | color: "white" | ||
431 | 32 | anchors.centerIn: parent | ||
432 | 33 | width: parent.width + units.gu(4) | ||
433 | 34 | height: parent.height + units.gu(4) | ||
434 | 35 | opacity: longOperation ? 0.75 : 0 | ||
435 | 36 | } | ||
436 | 37 | |||
437 | 38 | Column { | ||
438 | 39 | id: column | ||
439 | 40 | anchors.centerIn: parent | ||
440 | 41 | width: childrenRect.width | ||
441 | 42 | spacing: units.gu(2) | ||
442 | 43 | |||
443 | 44 | ActivityIndicator { | ||
444 | 45 | id: spinner | ||
445 | 46 | anchors.horizontalCenter: parent.horizontalCenter | ||
446 | 47 | } | ||
447 | 48 | |||
448 | 49 | Label { | ||
449 | 50 | id: label | ||
450 | 51 | anchors.horizontalCenter: parent.horizontalCenter | ||
451 | 52 | horizontalAlignment: Text.AlignHCenter | ||
452 | 53 | visible: longOperation | ||
453 | 54 | } | ||
454 | 55 | } | ||
455 | 56 | } | ||
456 | 57 | 0 | ||
457 | === removed file 'rc/qml/MediaViewer/PhotoEditor/CropCorner.qml' | |||
458 | --- rc/qml/MediaViewer/PhotoEditor/CropCorner.qml 2015-11-05 19:04:22 +0000 | |||
459 | +++ rc/qml/MediaViewer/PhotoEditor/CropCorner.qml 1970-01-01 00:00:00 +0000 | |||
460 | @@ -1,66 +0,0 @@ | |||
461 | 1 | /* | ||
462 | 2 | * Copyright (C) 2012-2015 Canonical Ltd | ||
463 | 3 | * | ||
464 | 4 | * This program is free software: you can redistribute it and/or modify | ||
465 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
466 | 6 | * published by the Free Software Foundation. | ||
467 | 7 | * | ||
468 | 8 | * This program is distributed in the hope that it will be useful, | ||
469 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
470 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
471 | 11 | * GNU General Public License for more details. | ||
472 | 12 | * | ||
473 | 13 | * You should have received a copy of the GNU General Public License | ||
474 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
475 | 15 | * | ||
476 | 16 | * Authors: | ||
477 | 17 | * Charles Lindsay <chaz@yorba.org> | ||
478 | 18 | */ | ||
479 | 19 | |||
480 | 20 | import QtQuick 2.4 | ||
481 | 21 | import Ubuntu.Components 1.3 | ||
482 | 22 | |||
483 | 23 | // A corner of a CropFrame. | ||
484 | 24 | Item { | ||
485 | 25 | id: cropCorner | ||
486 | 26 | |||
487 | 27 | /*! | ||
488 | 28 | */ | ||
489 | 29 | signal dragged(real dx, real dy) | ||
490 | 30 | /*! | ||
491 | 31 | */ | ||
492 | 32 | signal dragStarted() | ||
493 | 33 | /*! | ||
494 | 34 | */ | ||
495 | 35 | signal dragCompleted() | ||
496 | 36 | |||
497 | 37 | /*! | ||
498 | 38 | */ | ||
499 | 39 | property bool isLeft: true | ||
500 | 40 | /*! | ||
501 | 41 | */ | ||
502 | 42 | property bool isTop: true | ||
503 | 43 | |||
504 | 44 | x: isLeft ? -(width/2) : parent.width - (width/2) | ||
505 | 45 | y: isTop ? -(width/2) : parent.height - (width/2) | ||
506 | 46 | width: handle.width | ||
507 | 47 | height: handle.height | ||
508 | 48 | |||
509 | 49 | Image { | ||
510 | 50 | id: handle | ||
511 | 51 | anchors.centerIn: parent | ||
512 | 52 | source: Qt.resolvedUrl("assets/crop-handle.png") | ||
513 | 53 | } | ||
514 | 54 | |||
515 | 55 | CropDragArea { | ||
516 | 56 | anchors.centerIn: parent | ||
517 | 57 | width: handle.width + units.gu(2) | ||
518 | 58 | height: handle.height + units.gu(2) | ||
519 | 59 | |||
520 | 60 | onDragged: cropCorner.dragged(dx, dy) | ||
521 | 61 | |||
522 | 62 | onDragStarted: cropCorner.dragStarted() | ||
523 | 63 | |||
524 | 64 | onDragCompleted: cropCorner.dragCompleted() | ||
525 | 65 | } | ||
526 | 66 | } | ||
527 | 67 | 0 | ||
528 | === removed file 'rc/qml/MediaViewer/PhotoEditor/CropDragArea.qml' | |||
529 | --- rc/qml/MediaViewer/PhotoEditor/CropDragArea.qml 2015-11-05 19:04:22 +0000 | |||
530 | +++ rc/qml/MediaViewer/PhotoEditor/CropDragArea.qml 1970-01-01 00:00:00 +0000 | |||
531 | @@ -1,56 +0,0 @@ | |||
532 | 1 | /* | ||
533 | 2 | * Copyright (C) 2012-2015 Canonical Ltd | ||
534 | 3 | * | ||
535 | 4 | * This program is free software: you can redistribute it and/or modify | ||
536 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
537 | 6 | * published by the Free Software Foundation. | ||
538 | 7 | * | ||
539 | 8 | * This program is distributed in the hope that it will be useful, | ||
540 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
541 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
542 | 11 | * GNU General Public License for more details. | ||
543 | 12 | * | ||
544 | 13 | * You should have received a copy of the GNU General Public License | ||
545 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
546 | 15 | * | ||
547 | 16 | * Authors: | ||
548 | 17 | * Charles Lindsay <chaz@yorba.org> | ||
549 | 18 | */ | ||
550 | 19 | |||
551 | 20 | import QtQuick 2.4 | ||
552 | 21 | |||
553 | 22 | // A MouseArea meant to drag a corner/edge of a crop area. | ||
554 | 23 | MouseArea { | ||
555 | 24 | id: cropDragArea | ||
556 | 25 | |||
557 | 26 | /*! | ||
558 | 27 | */ | ||
559 | 28 | signal dragged(real dx, real dy) | ||
560 | 29 | /*! | ||
561 | 30 | */ | ||
562 | 31 | signal dragStarted() | ||
563 | 32 | /*! | ||
564 | 33 | */ | ||
565 | 34 | signal dragCompleted() | ||
566 | 35 | |||
567 | 36 | // Since we're usually moving this area with the mouse in response to | ||
568 | 37 | // dragging, we don't need to capture the last x/y, just where it was | ||
569 | 38 | // grabbed. | ||
570 | 39 | property real grabX: -1 | ||
571 | 40 | /*! | ||
572 | 41 | */ | ||
573 | 42 | property real grabY: -1 | ||
574 | 43 | |||
575 | 44 | onPressed: { | ||
576 | 45 | dragStarted(); | ||
577 | 46 | |||
578 | 47 | grabX = mouse.x; | ||
579 | 48 | grabY = mouse.y; | ||
580 | 49 | } | ||
581 | 50 | |||
582 | 51 | onReleased: { | ||
583 | 52 | dragCompleted(); | ||
584 | 53 | } | ||
585 | 54 | |||
586 | 55 | onPositionChanged: cropDragArea.dragged(mouse.x - grabX, mouse.y - grabY) | ||
587 | 56 | } | ||
588 | 57 | 0 | ||
589 | === removed file 'rc/qml/MediaViewer/PhotoEditor/CropInteractor.qml' | |||
590 | --- rc/qml/MediaViewer/PhotoEditor/CropInteractor.qml 2015-11-05 19:04:22 +0000 | |||
591 | +++ rc/qml/MediaViewer/PhotoEditor/CropInteractor.qml 1970-01-01 00:00:00 +0000 | |||
592 | @@ -1,140 +0,0 @@ | |||
593 | 1 | /* | ||
594 | 2 | * Copyright (C) 2012-2015 Canonical Ltd | ||
595 | 3 | * | ||
596 | 4 | * This program is free software: you can redistribute it and/or modify | ||
597 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
598 | 6 | * published by the Free Software Foundation. | ||
599 | 7 | * | ||
600 | 8 | * This program is distributed in the hope that it will be useful, | ||
601 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
602 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
603 | 11 | * GNU General Public License for more details. | ||
604 | 12 | * | ||
605 | 13 | * You should have received a copy of the GNU General Public License | ||
606 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
607 | 15 | * | ||
608 | 16 | * Authors: | ||
609 | 17 | * Charles Lindsay <chaz@yorba.org> | ||
610 | 18 | * Lucas Beeler <lucas@yorba.org> | ||
611 | 19 | */ | ||
612 | 20 | |||
613 | 21 | import QtQuick 2.4 | ||
614 | 22 | import Ubuntu.Components 1.3 | ||
615 | 23 | import "GraphicsRoutines.js" as GraphicsRoutines | ||
616 | 24 | |||
617 | 25 | /*! | ||
618 | 26 | */ | ||
619 | 27 | Rectangle { | ||
620 | 28 | id: cropInteractor | ||
621 | 29 | objectName: "cropInteractor" | ||
622 | 30 | |||
623 | 31 | color: "black" | ||
624 | 32 | |||
625 | 33 | property alias targetPhoto: original.source | ||
626 | 34 | |||
627 | 35 | property string matteColor: "black" | ||
628 | 36 | property real matteOpacity: 0.6 | ||
629 | 37 | |||
630 | 38 | // Note: each element of the cropped rect will be in the range [0,1], since | ||
631 | 39 | // in the UI we aren't using direct photo pixel values. | ||
632 | 40 | signal cropped(variant rect) | ||
633 | 41 | signal canceled() | ||
634 | 42 | |||
635 | 43 | function computeRectSet() { | ||
636 | 44 | var actualImage = Qt.rect( | ||
637 | 45 | (original.width - original.paintedWidth) / 2.0, | ||
638 | 46 | (original.height - original.paintedHeight) / 2.0, | ||
639 | 47 | original.paintedWidth, | ||
640 | 48 | original.paintedHeight | ||
641 | 49 | ); | ||
642 | 50 | var photoPreview = GraphicsRoutines.fitRect(viewport, actualImage); | ||
643 | 51 | |||
644 | 52 | var unfitCrop = Qt.rect(0, 0, photoPreview.width, photoPreview.height); | ||
645 | 53 | var cropFrame = GraphicsRoutines.fitRect(viewport, unfitCrop); | ||
646 | 54 | |||
647 | 55 | var photoExtent = Qt.rect(cropFrame.x, cropFrame.y, | ||
648 | 56 | cropFrame.scaleFactor * photoPreview.width, | ||
649 | 57 | cropFrame.scaleFactor * photoPreview.height); | ||
650 | 58 | |||
651 | 59 | return { | ||
652 | 60 | photoPreview: photoPreview, | ||
653 | 61 | cropFrame: cropFrame, | ||
654 | 62 | photoExtent: photoExtent, | ||
655 | 63 | photoExtentScale: cropFrame.scaleFactor | ||
656 | 64 | }; | ||
657 | 65 | } | ||
658 | 66 | |||
659 | 67 | Item { | ||
660 | 68 | id: viewport | ||
661 | 69 | |||
662 | 70 | anchors.fill: parent | ||
663 | 71 | anchors.margins: units.gu(6) | ||
664 | 72 | z: 1 | ||
665 | 73 | } | ||
666 | 74 | |||
667 | 75 | CropOverlay { | ||
668 | 76 | id: overlay | ||
669 | 77 | objectName: "cropOverlay" | ||
670 | 78 | |||
671 | 79 | property real minSize: units.gu(4) | ||
672 | 80 | |||
673 | 81 | anchors.fill: parent; | ||
674 | 82 | visible: false; | ||
675 | 83 | |||
676 | 84 | photo: original | ||
677 | 85 | viewport: viewport | ||
678 | 86 | |||
679 | 87 | matteColor: cropInteractor.matteColor | ||
680 | 88 | matteOpacity: cropInteractor.matteOpacity | ||
681 | 89 | |||
682 | 90 | z: 16 | ||
683 | 91 | |||
684 | 92 | onMatteRegionPressed: { | ||
685 | 93 | cropInteractor.canceled(); | ||
686 | 94 | } | ||
687 | 95 | |||
688 | 96 | onCropButtonPressed: { | ||
689 | 97 | original.visible = false; | ||
690 | 98 | overlay.visible = false; | ||
691 | 99 | original.scale = 1.0; | ||
692 | 100 | var r = overlay.getRelativeFrameRect() | ||
693 | 101 | cropInteractor.cropped(overlay.getRelativeFrameRect()); | ||
694 | 102 | } | ||
695 | 103 | } | ||
696 | 104 | |||
697 | 105 | Image { | ||
698 | 106 | id: original | ||
699 | 107 | |||
700 | 108 | x: viewport.x | ||
701 | 109 | y: viewport.y | ||
702 | 110 | width: viewport.width | ||
703 | 111 | height: viewport.height | ||
704 | 112 | transformOrigin: Item.TopLeft | ||
705 | 113 | fillMode: Image.PreserveAspectFit | ||
706 | 114 | cache: false | ||
707 | 115 | sourceSize { | ||
708 | 116 | width: original.width | ||
709 | 117 | height: original.height | ||
710 | 118 | } | ||
711 | 119 | |||
712 | 120 | onStatusChanged: { | ||
713 | 121 | if (status == Image.Ready) { | ||
714 | 122 | var rects = computeRectSet(); | ||
715 | 123 | |||
716 | 124 | overlay.initialFrameX = rects.cropFrame.x; | ||
717 | 125 | overlay.initialFrameY = rects.cropFrame.y; | ||
718 | 126 | overlay.initialFrameWidth = rects.cropFrame.width; | ||
719 | 127 | overlay.initialFrameHeight = rects.cropFrame.height; | ||
720 | 128 | |||
721 | 129 | overlay.resetFor(rects); | ||
722 | 130 | overlay.visible = true; | ||
723 | 131 | |||
724 | 132 | x = rects.photoExtent.x; | ||
725 | 133 | y = rects.photoExtent.y; | ||
726 | 134 | width = rects.photoPreview.width; | ||
727 | 135 | height = rects.photoPreview.height; | ||
728 | 136 | scale = rects.photoExtentScale; | ||
729 | 137 | } | ||
730 | 138 | } | ||
731 | 139 | } | ||
732 | 140 | } | ||
733 | 141 | 0 | ||
734 | === removed file 'rc/qml/MediaViewer/PhotoEditor/CropOverlay.qml' | |||
735 | --- rc/qml/MediaViewer/PhotoEditor/CropOverlay.qml 2015-11-05 19:04:22 +0000 | |||
736 | +++ rc/qml/MediaViewer/PhotoEditor/CropOverlay.qml 1970-01-01 00:00:00 +0000 | |||
737 | @@ -1,581 +0,0 @@ | |||
738 | 1 | /* | ||
739 | 2 | * Copyright (C) 2012-2015 Canonical Ltd | ||
740 | 3 | * | ||
741 | 4 | * This program is free software: you can redistribute it and/or modify | ||
742 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
743 | 6 | * published by the Free Software Foundation. | ||
744 | 7 | * | ||
745 | 8 | * This program is distributed in the hope that it will be useful, | ||
746 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
747 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
748 | 11 | * GNU General Public License for more details. | ||
749 | 12 | * | ||
750 | 13 | * You should have received a copy of the GNU General Public License | ||
751 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
752 | 15 | * | ||
753 | 16 | * Authors: | ||
754 | 17 | * Charles Lindsay <chaz@yorba.org> | ||
755 | 18 | * Lucas Beeler <lucas@yorba.org> | ||
756 | 19 | */ | ||
757 | 20 | |||
758 | 21 | import QtQuick 2.4 | ||
759 | 22 | import Ubuntu.Components 1.3 | ||
760 | 23 | import "GraphicsRoutines.js" as GraphicsRoutines | ||
761 | 24 | |||
762 | 25 | /* A CropOverlay is a semi-transparent surface that floats over the photo. It | ||
763 | 26 | * serves two purposes. First, it provides visual cueing as to what region of | ||
764 | 27 | * the photo's surface will be preserved when the crop operation is applied. | ||
765 | 28 | * The preserved region is the region that falls inside of the CropOverlay's | ||
766 | 29 | * frame. Second, the CropOverlay allows the user to manipulate the | ||
767 | 30 | * geometry of the crop frame, to chage its location, width, and height. The | ||
768 | 31 | * geometry of the crop frame is reinforced by a key visual cue: the region of | ||
769 | 32 | * the photo outside of the crop frame is drawn with a semi-transparent, smoked | ||
770 | 33 | * matte on top of it. This matte surrounds the crop frame. | ||
771 | 34 | */ | ||
772 | 35 | Item { | ||
773 | 36 | id: cropOverlay | ||
774 | 37 | |||
775 | 38 | // public properties | ||
776 | 39 | /*! | ||
777 | 40 | */ | ||
778 | 41 | property Item viewport | ||
779 | 42 | /*! | ||
780 | 43 | */ | ||
781 | 44 | property Item photo | ||
782 | 45 | /*! | ||
783 | 46 | */ | ||
784 | 47 | property string matteColor: "red" | ||
785 | 48 | /*! | ||
786 | 49 | */ | ||
787 | 50 | property real matteOpacity: 0.85 | ||
788 | 51 | /*! | ||
789 | 52 | */ | ||
790 | 53 | property int initialFrameX: -1 | ||
791 | 54 | /*! | ||
792 | 55 | */ | ||
793 | 56 | property int initialFrameY: -1 | ||
794 | 57 | /*! | ||
795 | 58 | */ | ||
796 | 59 | property int initialFrameWidth: -1 | ||
797 | 60 | /*! | ||
798 | 61 | */ | ||
799 | 62 | property int initialFrameHeight: -1 | ||
800 | 63 | |||
801 | 64 | // private properties -- Frame Fit Animation parameters | ||
802 | 65 | property real interpolationFactor: 1.0 | ||
803 | 66 | /*! | ||
804 | 67 | */ | ||
805 | 68 | property variant startFrame | ||
806 | 69 | /*! | ||
807 | 70 | */ | ||
808 | 71 | property variant endFrame | ||
809 | 72 | /*! | ||
810 | 73 | */ | ||
811 | 74 | property variant startPhoto | ||
812 | 75 | /*! | ||
813 | 76 | */ | ||
814 | 77 | property real referencePhotoWidth: -1 | ||
815 | 78 | /*! | ||
816 | 79 | */ | ||
817 | 80 | property real referencePhotoHeight: -1 | ||
818 | 81 | /*! | ||
819 | 82 | */ | ||
820 | 83 | property real endPhotoX | ||
821 | 84 | /*! | ||
822 | 85 | */ | ||
823 | 86 | property real endPhotoY | ||
824 | 87 | /*! | ||
825 | 88 | */ | ||
826 | 89 | property real endPhotoWidth | ||
827 | 90 | /*! | ||
828 | 91 | */ | ||
829 | 92 | property real endPhotoHeight | ||
830 | 93 | |||
831 | 94 | /*! | ||
832 | 95 | */ | ||
833 | 96 | signal userAlteredFrame() | ||
834 | 97 | /*! | ||
835 | 98 | */ | ||
836 | 99 | signal runFrameFitAnimation() | ||
837 | 100 | /*! | ||
838 | 101 | */ | ||
839 | 102 | signal matteRegionPressed() | ||
840 | 103 | /*! | ||
841 | 104 | */ | ||
842 | 105 | signal cropButtonPressed() | ||
843 | 106 | |||
844 | 107 | /*! | ||
845 | 108 | */ | ||
846 | 109 | function resetFor(rectSet) { | ||
847 | 110 | if (initialFrameX != -1 && initialFrameY != -1 && initialFrameWidth != -1 && | ||
848 | 111 | initialFrameHeight != -1) { | ||
849 | 112 | frame.x = rectSet.cropFrame.x; | ||
850 | 113 | frame.y = rectSet.cropFrame.y; | ||
851 | 114 | frame.width = rectSet.cropFrame.width; | ||
852 | 115 | frame.height = rectSet.cropFrame.height; | ||
853 | 116 | photoExtent.x = rectSet.photoExtent.x; | ||
854 | 117 | photoExtent.y = rectSet.photoExtent.y; | ||
855 | 118 | photoExtent.width = rectSet.photoExtent.width; | ||
856 | 119 | photoExtent.height = rectSet.photoExtent.height; | ||
857 | 120 | referencePhotoWidth = rectSet.photoPreview.width; | ||
858 | 121 | referencePhotoHeight = rectSet.photoPreview.height; | ||
859 | 122 | } | ||
860 | 123 | } | ||
861 | 124 | |||
862 | 125 | /* Return the (x, y) position and the width and height of the viewport | ||
863 | 126 | */ | ||
864 | 127 | function getViewportExtentRect() { | ||
865 | 128 | return GraphicsRoutines.cloneRect(viewport); | ||
866 | 129 | } | ||
867 | 130 | |||
868 | 131 | /* Return the (x, y) position and the width and height of the photoExtent. | ||
869 | 132 | * The photoExtent is the on-screen region that holds the original photo | ||
870 | 133 | * preview. | ||
871 | 134 | */ | ||
872 | 135 | function getPhotoExtentRect() { | ||
873 | 136 | return GraphicsRoutines.cloneRect(photoExtent); | ||
874 | 137 | } | ||
875 | 138 | |||
876 | 139 | /*! | ||
877 | 140 | */ | ||
878 | 141 | function getRelativeFrameRect() { | ||
879 | 142 | return GraphicsRoutines.getRelativeRect(frame.getExtentRect(), | ||
880 | 143 | getPhotoExtentRect()); | ||
881 | 144 | } | ||
882 | 145 | |||
883 | 146 | anchors.fill: parent | ||
884 | 147 | |||
885 | 148 | Item { | ||
886 | 149 | id: photoExtent | ||
887 | 150 | |||
888 | 151 | property real panStartX | ||
889 | 152 | property real panStartY | ||
890 | 153 | |||
891 | 154 | function startPan() { | ||
892 | 155 | panStartX = x; | ||
893 | 156 | panStartY = y; | ||
894 | 157 | } | ||
895 | 158 | |||
896 | 159 | // 'deltaX' and 'deltaY' are offsets relative to the pan start point | ||
897 | 160 | function updatePan(deltaX, deltaY) { | ||
898 | 161 | var newX = panStartX + deltaX; | ||
899 | 162 | var newY = panStartY + deltaY; | ||
900 | 163 | |||
901 | 164 | x = GraphicsRoutines.clamp(newX, frame.x + frame.width - | ||
902 | 165 | photoExtent.width, frame.x); | ||
903 | 166 | y = GraphicsRoutines.clamp(newY, frame.y + frame.height - | ||
904 | 167 | photoExtent.height, frame.y); | ||
905 | 168 | } | ||
906 | 169 | |||
907 | 170 | function stopPan() { | ||
908 | 171 | } | ||
909 | 172 | |||
910 | 173 | x: initialFrameX | ||
911 | 174 | y: initialFrameY | ||
912 | 175 | width: initialFrameWidth | ||
913 | 176 | height: initialFrameHeight | ||
914 | 177 | z: 1 | ||
915 | 178 | |||
916 | 179 | onXChanged: { | ||
917 | 180 | if (photo) | ||
918 | 181 | photo.x = x; | ||
919 | 182 | } | ||
920 | 183 | |||
921 | 184 | onYChanged: { | ||
922 | 185 | if (photo) | ||
923 | 186 | photo.y = y; | ||
924 | 187 | } | ||
925 | 188 | |||
926 | 189 | onWidthChanged: { | ||
927 | 190 | if (photo && referencePhotoWidth > 0) | ||
928 | 191 | photo.scale = width / referencePhotoWidth; | ||
929 | 192 | } | ||
930 | 193 | |||
931 | 194 | onHeightChanged: { | ||
932 | 195 | if (photo && referencePhotoHeight > 0) | ||
933 | 196 | photo.scale = height / referencePhotoHeight; | ||
934 | 197 | } | ||
935 | 198 | } | ||
936 | 199 | |||
937 | 200 | // | ||
938 | 201 | // The following four Rectangles are used to "matte out" the area of the photo | ||
939 | 202 | // preview that falls outside the frame. This "matting out" visual cue is | ||
940 | 203 | // accomplished by darkening the matted-out area with a translucent, smoked | ||
941 | 204 | // overlay. | ||
942 | 205 | // | ||
943 | 206 | Rectangle { | ||
944 | 207 | id: leftMatte | ||
945 | 208 | |||
946 | 209 | color: cropOverlay.matteColor | ||
947 | 210 | opacity: cropOverlay.matteOpacity | ||
948 | 211 | |||
949 | 212 | anchors.top: topMatte.bottom | ||
950 | 213 | anchors.bottom: frame.bottom | ||
951 | 214 | anchors.left: parent.left | ||
952 | 215 | anchors.right: frame.left | ||
953 | 216 | |||
954 | 217 | MouseArea { | ||
955 | 218 | anchors.fill: parent; | ||
956 | 219 | |||
957 | 220 | onPressed: cropOverlay.matteRegionPressed(); | ||
958 | 221 | } | ||
959 | 222 | } | ||
960 | 223 | |||
961 | 224 | Rectangle { | ||
962 | 225 | id: topMatte | ||
963 | 226 | |||
964 | 227 | color: cropOverlay.matteColor | ||
965 | 228 | opacity: cropOverlay.matteOpacity | ||
966 | 229 | |||
967 | 230 | anchors.top: parent.top | ||
968 | 231 | anchors.bottom: frame.top | ||
969 | 232 | anchors.left: parent.left | ||
970 | 233 | anchors.right: parent.right | ||
971 | 234 | |||
972 | 235 | MouseArea { | ||
973 | 236 | anchors.fill: parent; | ||
974 | 237 | |||
975 | 238 | onPressed: cropOverlay.matteRegionPressed(); | ||
976 | 239 | } | ||
977 | 240 | } | ||
978 | 241 | |||
979 | 242 | Rectangle { | ||
980 | 243 | id: rightMatte | ||
981 | 244 | |||
982 | 245 | color: cropOverlay.matteColor | ||
983 | 246 | opacity: cropOverlay.matteOpacity | ||
984 | 247 | |||
985 | 248 | anchors.top: topMatte.bottom | ||
986 | 249 | anchors.bottom: bottomMatte.top | ||
987 | 250 | anchors.left: frame.right | ||
988 | 251 | anchors.right: parent.right | ||
989 | 252 | |||
990 | 253 | MouseArea { | ||
991 | 254 | anchors.fill: parent; | ||
992 | 255 | |||
993 | 256 | onPressed: cropOverlay.matteRegionPressed(); | ||
994 | 257 | } | ||
995 | 258 | } | ||
996 | 259 | |||
997 | 260 | Rectangle { | ||
998 | 261 | id: bottomMatte | ||
999 | 262 | |||
1000 | 263 | color: cropOverlay.matteColor | ||
1001 | 264 | opacity: cropOverlay.matteOpacity | ||
1002 | 265 | |||
1003 | 266 | anchors.top: frame.bottom | ||
1004 | 267 | anchors.bottom: parent.bottom | ||
1005 | 268 | anchors.left: parent.left | ||
1006 | 269 | anchors.right: parent.right | ||
1007 | 270 | |||
1008 | 271 | MouseArea { | ||
1009 | 272 | anchors.fill: parent; | ||
1010 | 273 | |||
1011 | 274 | onPressed: cropOverlay.matteRegionPressed(); | ||
1012 | 275 | } | ||
1013 | 276 | } | ||
1014 | 277 | |||
1015 | 278 | // | ||
1016 | 279 | // The frame is a grey rectangle with associated drag corners that | ||
1017 | 280 | // frames the region of the photo that will remain when the crop operation is | ||
1018 | 281 | // applied. | ||
1019 | 282 | // | ||
1020 | 283 | // NB: the frame can be in two states, although the QML state mechanism | ||
1021 | 284 | // isn't sufficiently expressive to describe them. The frame can be | ||
1022 | 285 | // in the FIT state, in which case it is optimally fit inside the | ||
1023 | 286 | // frame constraint region (see getFrameConstraintRect( ) above for | ||
1024 | 287 | // a description of the frame constraint region). Or, the frame can | ||
1025 | 288 | // be in the USER state. In the user state, the user has the mouse button | ||
1026 | 289 | // held down and is actively performing a drag operation to change the | ||
1027 | 290 | // geometry of the frame. | ||
1028 | 291 | // | ||
1029 | 292 | Rectangle { | ||
1030 | 293 | id: frame | ||
1031 | 294 | |||
1032 | 295 | signal resizedX(bool left, real dx) | ||
1033 | 296 | signal resizedY(bool top, real dy) | ||
1034 | 297 | |||
1035 | 298 | property variant dragStartRect | ||
1036 | 299 | |||
1037 | 300 | function getExtentRect() { | ||
1038 | 301 | var result = { }; | ||
1039 | 302 | |||
1040 | 303 | result.x = x; | ||
1041 | 304 | result.y = y; | ||
1042 | 305 | result.width = width; | ||
1043 | 306 | result.height = height; | ||
1044 | 307 | |||
1045 | 308 | return result; | ||
1046 | 309 | } | ||
1047 | 310 | |||
1048 | 311 | x: cropOverlay.initialFrameX | ||
1049 | 312 | y: cropOverlay.initialFrameY | ||
1050 | 313 | width: cropOverlay.initialFrameWidth | ||
1051 | 314 | height: cropOverlay.initialFrameHeight | ||
1052 | 315 | |||
1053 | 316 | color: "transparent" | ||
1054 | 317 | |||
1055 | 318 | border.width: units.gu(0.2) | ||
1056 | 319 | border.color: "#19B6EE" | ||
1057 | 320 | |||
1058 | 321 | MouseArea { | ||
1059 | 322 | id: panArea | ||
1060 | 323 | |||
1061 | 324 | property int dragStartX; | ||
1062 | 325 | property int dragStartY; | ||
1063 | 326 | |||
1064 | 327 | anchors.fill: parent | ||
1065 | 328 | anchors.margins: 2 | ||
1066 | 329 | |||
1067 | 330 | onPressed: { | ||
1068 | 331 | dragStartX = mouse.x; | ||
1069 | 332 | dragStartY = mouse.y; | ||
1070 | 333 | |||
1071 | 334 | photoExtent.startPan(); | ||
1072 | 335 | } | ||
1073 | 336 | |||
1074 | 337 | onReleased: { | ||
1075 | 338 | photoExtent.stopPan(); | ||
1076 | 339 | } | ||
1077 | 340 | |||
1078 | 341 | onPositionChanged: { | ||
1079 | 342 | photoExtent.updatePan(mouse.x - dragStartX, mouse.y - dragStartY); | ||
1080 | 343 | } | ||
1081 | 344 | } | ||
1082 | 345 | |||
1083 | 346 | Button { | ||
1084 | 347 | objectName: "centerCropIcon" | ||
1085 | 348 | anchors.centerIn: parent | ||
1086 | 349 | text: i18n.tr("Crop") | ||
1087 | 350 | color: frame.border.color | ||
1088 | 351 | opacity: 0.9 | ||
1089 | 352 | onClicked: cropOverlay.cropButtonPressed() | ||
1090 | 353 | } | ||
1091 | 354 | |||
1092 | 355 | // Left drag bar. | ||
1093 | 356 | CropDragArea { | ||
1094 | 357 | x: -units.gu(2) | ||
1095 | 358 | width: units.gu(3) | ||
1096 | 359 | anchors.verticalCenter: parent.center | ||
1097 | 360 | height: parent.height - units.gu(2) | ||
1098 | 361 | |||
1099 | 362 | onDragged: { | ||
1100 | 363 | frame.resizedX(true, dx); | ||
1101 | 364 | frame.updateOnAltered(false); | ||
1102 | 365 | } | ||
1103 | 366 | |||
1104 | 367 | onDragStarted: frame.dragStartRect = frame.getExtentRect(); | ||
1105 | 368 | onDragCompleted: frame.updateOnAltered(true); | ||
1106 | 369 | } | ||
1107 | 370 | |||
1108 | 371 | // Top drag bar. | ||
1109 | 372 | CropDragArea { | ||
1110 | 373 | y: -units.gu(2) | ||
1111 | 374 | height: units.gu(3) | ||
1112 | 375 | anchors.horizontalCenter: parent.center | ||
1113 | 376 | width: parent.width - units.gu(2) | ||
1114 | 377 | |||
1115 | 378 | onDragged: { | ||
1116 | 379 | frame.resizedY(true, dy); | ||
1117 | 380 | frame.updateOnAltered(false); | ||
1118 | 381 | } | ||
1119 | 382 | |||
1120 | 383 | onDragStarted: frame.dragStartRect = frame.getExtentRect(); | ||
1121 | 384 | onDragCompleted: frame.updateOnAltered(true); | ||
1122 | 385 | } | ||
1123 | 386 | |||
1124 | 387 | // Right drag bar. | ||
1125 | 388 | CropDragArea { | ||
1126 | 389 | x: parent.width - units.gu(1) | ||
1127 | 390 | width: units.gu(3) | ||
1128 | 391 | anchors.verticalCenter: parent.center | ||
1129 | 392 | height: parent.height - units.gu(2) | ||
1130 | 393 | |||
1131 | 394 | onDragged: { | ||
1132 | 395 | frame.resizedX(false, dx); | ||
1133 | 396 | frame.updateOnAltered(false); | ||
1134 | 397 | } | ||
1135 | 398 | |||
1136 | 399 | onDragStarted: frame.dragStartRect = frame.getExtentRect(); | ||
1137 | 400 | onDragCompleted: frame.updateOnAltered(true); | ||
1138 | 401 | } | ||
1139 | 402 | |||
1140 | 403 | // Bottom drag bar. | ||
1141 | 404 | CropDragArea { | ||
1142 | 405 | y: parent.height - units.gu(1) | ||
1143 | 406 | height: units.gu(3) | ||
1144 | 407 | anchors.horizontalCenter: parent.center | ||
1145 | 408 | width: parent.width - units.gu(2) | ||
1146 | 409 | |||
1147 | 410 | onDragged: { | ||
1148 | 411 | frame.resizedY(false, dy); | ||
1149 | 412 | frame.updateOnAltered(false); | ||
1150 | 413 | } | ||
1151 | 414 | |||
1152 | 415 | onDragStarted: frame.dragStartRect = frame.getExtentRect(); | ||
1153 | 416 | onDragCompleted: frame.updateOnAltered(true); | ||
1154 | 417 | } | ||
1155 | 418 | |||
1156 | 419 | // Top-left corner. | ||
1157 | 420 | CropCorner { | ||
1158 | 421 | objectName: "topLeftCropCorner" | ||
1159 | 422 | isLeft: true | ||
1160 | 423 | isTop: true | ||
1161 | 424 | |||
1162 | 425 | onDragged: { | ||
1163 | 426 | frame.resizedX(isLeft, dx); | ||
1164 | 427 | frame.resizedY(isTop, dy); | ||
1165 | 428 | frame.updateOnAltered(false); | ||
1166 | 429 | } | ||
1167 | 430 | |||
1168 | 431 | onDragStarted: frame.dragStartRect = frame.getExtentRect(); | ||
1169 | 432 | onDragCompleted: frame.updateOnAltered(true); | ||
1170 | 433 | } | ||
1171 | 434 | |||
1172 | 435 | // Top-right corner. | ||
1173 | 436 | CropCorner { | ||
1174 | 437 | objectName: "topRightCropCorner" | ||
1175 | 438 | isLeft: false | ||
1176 | 439 | isTop: true | ||
1177 | 440 | |||
1178 | 441 | onDragged: { | ||
1179 | 442 | frame.resizedX(isLeft, dx); | ||
1180 | 443 | frame.resizedY(isTop, dy); | ||
1181 | 444 | frame.updateOnAltered(false); | ||
1182 | 445 | } | ||
1183 | 446 | |||
1184 | 447 | onDragStarted: frame.dragStartRect = frame.getExtentRect(); | ||
1185 | 448 | onDragCompleted: frame.updateOnAltered(true); | ||
1186 | 449 | } | ||
1187 | 450 | |||
1188 | 451 | // Bottom-left corner. | ||
1189 | 452 | CropCorner { | ||
1190 | 453 | objectName: "bottonLeftCropCorner" | ||
1191 | 454 | isLeft: true | ||
1192 | 455 | isTop: false | ||
1193 | 456 | |||
1194 | 457 | onDragged: { | ||
1195 | 458 | frame.resizedX(isLeft, dx); | ||
1196 | 459 | frame.resizedY(isTop, dy); | ||
1197 | 460 | frame.updateOnAltered(false); | ||
1198 | 461 | } | ||
1199 | 462 | |||
1200 | 463 | onDragStarted: frame.dragStartRect = frame.getExtentRect(); | ||
1201 | 464 | onDragCompleted: frame.updateOnAltered(true); | ||
1202 | 465 | } | ||
1203 | 466 | |||
1204 | 467 | // Bottom-right corner. | ||
1205 | 468 | CropCorner { | ||
1206 | 469 | id: bottomRightCrop | ||
1207 | 470 | objectName: "bottomRightCropCorner" | ||
1208 | 471 | isLeft: false | ||
1209 | 472 | isTop: false | ||
1210 | 473 | |||
1211 | 474 | onDragged: { | ||
1212 | 475 | frame.resizedX(isLeft, dx); | ||
1213 | 476 | frame.resizedY(isTop, dy); | ||
1214 | 477 | frame.updateOnAltered(false); | ||
1215 | 478 | } | ||
1216 | 479 | |||
1217 | 480 | onDragStarted: frame.dragStartRect = frame.getExtentRect(); | ||
1218 | 481 | onDragCompleted: frame.updateOnAltered(true); | ||
1219 | 482 | } | ||
1220 | 483 | |||
1221 | 484 | // This handles resizing in both dimensions. first is whether we're | ||
1222 | 485 | // resizing the "first" edge, e.g. left or top (in which case we | ||
1223 | 486 | // adjust both position and span) vs. right or bottom (where we just | ||
1224 | 487 | // adjust the span). position should be either "x" or "y", and span | ||
1225 | 488 | // is either "width" or "height". This is a little complicated, and | ||
1226 | 489 | // coule probably be optimized with a little more thought. | ||
1227 | 490 | function resizeFrame(first, delta, position, span) { | ||
1228 | 491 | var constraintRegion = cropOverlay.getPhotoExtentRect(); | ||
1229 | 492 | |||
1230 | 493 | if (first) { | ||
1231 | 494 | // Left/top side. | ||
1232 | 495 | if (frame[position] + delta < constraintRegion[position]) | ||
1233 | 496 | delta = constraintRegion[position] - frame[position] | ||
1234 | 497 | |||
1235 | 498 | if (frame[span] - delta < minSize) | ||
1236 | 499 | delta = frame[span] - minSize; | ||
1237 | 500 | |||
1238 | 501 | frame[position] += delta; | ||
1239 | 502 | frame[span] -= delta; | ||
1240 | 503 | } else { | ||
1241 | 504 | // Right/bottom side. | ||
1242 | 505 | if (frame[span] + delta < minSize) | ||
1243 | 506 | delta = minSize - frame[span]; | ||
1244 | 507 | |||
1245 | 508 | if ((frame[position] + frame[span] + delta) > | ||
1246 | 509 | (constraintRegion[position] + constraintRegion[span])) | ||
1247 | 510 | delta = constraintRegion[position] + constraintRegion[span] - | ||
1248 | 511 | frame[position] - frame[span]; | ||
1249 | 512 | |||
1250 | 513 | frame[span] += delta; | ||
1251 | 514 | } | ||
1252 | 515 | } | ||
1253 | 516 | |||
1254 | 517 | onResizedX: resizeFrame(left, dx, "x", "width") | ||
1255 | 518 | onResizedY: resizeFrame(top, dy, "y", "height") | ||
1256 | 519 | |||
1257 | 520 | function updateOnAltered(finalUpdate) { | ||
1258 | 521 | var start = frame.dragStartRect; | ||
1259 | 522 | var end = frame.getExtentRect(); | ||
1260 | 523 | if (!GraphicsRoutines.areEqual(end, start)) { | ||
1261 | 524 | if (finalUpdate || | ||
1262 | 525 | (end.width * end.height >= start.width * start.height)) { | ||
1263 | 526 | cropOverlay.userAlteredFrame(); | ||
1264 | 527 | cropOverlay.runFrameFitAnimation(); | ||
1265 | 528 | } | ||
1266 | 529 | } | ||
1267 | 530 | } | ||
1268 | 531 | } | ||
1269 | 532 | |||
1270 | 533 | /* Invoked when the user has changed the geometry of the frame by dragging | ||
1271 | 534 | * one of its corners or edges. Expressed in terms of the states of the | ||
1272 | 535 | * frame described above, the userAlteredFrame signal is fired | ||
1273 | 536 | * when the user stops dragging. This triggers a change of the frame | ||
1274 | 537 | * from the USER state to the FIT state | ||
1275 | 538 | */ | ||
1276 | 539 | onUserAlteredFrame: { | ||
1277 | 540 | // since the geometry of the frame in the FIT state depends on both | ||
1278 | 541 | // how the user resized the frame when it was in the USER state as well | ||
1279 | 542 | // as the size of the frame constraint region, we have to recompute the | ||
1280 | 543 | // geometry of of the frame for the FIT state every time. | ||
1281 | 544 | |||
1282 | 545 | startFrame = GraphicsRoutines.cloneRect(frame); | ||
1283 | 546 | |||
1284 | 547 | endFrame = GraphicsRoutines.fitRect(getViewportExtentRect(), | ||
1285 | 548 | frame.getExtentRect()); | ||
1286 | 549 | |||
1287 | 550 | startPhoto = GraphicsRoutines.cloneRect(photoExtent); | ||
1288 | 551 | |||
1289 | 552 | var frameRelativeToPhoto = getRelativeFrameRect(); | ||
1290 | 553 | var scaleFactor = endFrame.width / frame.width; | ||
1291 | 554 | |||
1292 | 555 | endPhotoWidth = photoExtent.width * scaleFactor; | ||
1293 | 556 | endPhotoHeight = photoExtent.height * scaleFactor; | ||
1294 | 557 | endPhotoX = endFrame.x - (frameRelativeToPhoto.x * endPhotoWidth); | ||
1295 | 558 | endPhotoY = endFrame.y - (frameRelativeToPhoto.y * endPhotoHeight) | ||
1296 | 559 | |||
1297 | 560 | photo.transformOrigin = Item.TopLeft; | ||
1298 | 561 | } | ||
1299 | 562 | |||
1300 | 563 | onRunFrameFitAnimation: NumberAnimation { target: cropOverlay; | ||
1301 | 564 | property: "interpolationFactor"; from: 0.0; to: 1.0 } | ||
1302 | 565 | |||
1303 | 566 | onInterpolationFactorChanged: { | ||
1304 | 567 | var endPhotoRect = { }; | ||
1305 | 568 | endPhotoRect.x = endPhotoX; | ||
1306 | 569 | endPhotoRect.y = endPhotoY; | ||
1307 | 570 | endPhotoRect.width = endPhotoWidth; | ||
1308 | 571 | endPhotoRect.height = endPhotoHeight; | ||
1309 | 572 | |||
1310 | 573 | var interpolatedRect = GraphicsRoutines.interpolateRect(startFrame, | ||
1311 | 574 | endFrame, interpolationFactor); | ||
1312 | 575 | GraphicsRoutines.sizeToRect(interpolatedRect, frame); | ||
1313 | 576 | |||
1314 | 577 | interpolatedRect = GraphicsRoutines.interpolateRect(startPhoto, | ||
1315 | 578 | endPhotoRect, interpolationFactor); | ||
1316 | 579 | GraphicsRoutines.sizeToRect(interpolatedRect, photoExtent); | ||
1317 | 580 | } | ||
1318 | 581 | } | ||
1319 | 582 | 0 | ||
1320 | === removed file 'rc/qml/MediaViewer/PhotoEditor/EditStack.qml' | |||
1321 | --- rc/qml/MediaViewer/PhotoEditor/EditStack.qml 2015-11-05 19:04:22 +0000 | |||
1322 | +++ rc/qml/MediaViewer/PhotoEditor/EditStack.qml 1970-01-01 00:00:00 +0000 | |||
1323 | @@ -1,134 +0,0 @@ | |||
1324 | 1 | /* | ||
1325 | 2 | * Copyright (C) 2014-2015 Canonical Ltd | ||
1326 | 3 | * | ||
1327 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1328 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1329 | 6 | * published by the Free Software Foundation. | ||
1330 | 7 | * | ||
1331 | 8 | * This program is distributed in the hope that it will be useful, | ||
1332 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1333 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1334 | 11 | * GNU General Public License for more details. | ||
1335 | 12 | * | ||
1336 | 13 | * You should have received a copy of the GNU General Public License | ||
1337 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1338 | 15 | */ | ||
1339 | 16 | |||
1340 | 17 | import QtQuick 2.4 | ||
1341 | 18 | import Ubuntu.Components 1.3 | ||
1342 | 19 | import Gallery 1.0 | ||
1343 | 20 | |||
1344 | 21 | Item { | ||
1345 | 22 | property GalleryPhotoData data | ||
1346 | 23 | property bool actionsEnabled: true | ||
1347 | 24 | property var items: [] | ||
1348 | 25 | property int level: 0 | ||
1349 | 26 | property string editingSessionPath | ||
1350 | 27 | property string currentFile | ||
1351 | 28 | property string originalFile | ||
1352 | 29 | property string pristineFile | ||
1353 | 30 | property bool modified: level > 0 || _revertedInThisSession | ||
1354 | 31 | |||
1355 | 32 | property bool _revertedInThisSession | ||
1356 | 33 | property bool _pristineFileExists | ||
1357 | 34 | |||
1358 | 35 | signal revertRequested | ||
1359 | 36 | |||
1360 | 37 | function startEditingSession(original) { | ||
1361 | 38 | var originalFileName = GalleryFileUtils.nameFromPath(original); | ||
1362 | 39 | var baseName = GalleryFileUtils.parentDirectory(original) + | ||
1363 | 40 | "/.photo_editing." + originalFileName + "."; | ||
1364 | 41 | editingSessionPath = GalleryFileUtils.createTemporaryDirectory(baseName); | ||
1365 | 42 | if (editingSessionPath == "") return false; | ||
1366 | 43 | |||
1367 | 44 | originalFile = original; | ||
1368 | 45 | currentFile = editingSessionPath + "/current"; | ||
1369 | 46 | |||
1370 | 47 | pristineFile = GalleryFileUtils.parentDirectory(original) + | ||
1371 | 48 | "/.original/" + originalFileName | ||
1372 | 49 | _revertedInThisSession = false; | ||
1373 | 50 | _pristineFileExists = GalleryFileUtils.exists(pristineFile) | ||
1374 | 51 | |||
1375 | 52 | GalleryFileUtils.copy(originalFile, currentFile) | ||
1376 | 53 | |||
1377 | 54 | items = [createSnapshot(0)]; | ||
1378 | 55 | level = 0; | ||
1379 | 56 | return true; | ||
1380 | 57 | } | ||
1381 | 58 | |||
1382 | 59 | function endEditingSession(saveIfModified) { | ||
1383 | 60 | if (saveIfModified && modified) { // file modified | ||
1384 | 61 | // if we don't have a copy of the very first original, create one | ||
1385 | 62 | if (!_pristineFileExists) { | ||
1386 | 63 | GalleryFileUtils.createDirectory(GalleryFileUtils.parentDirectory(pristineFile)); | ||
1387 | 64 | GalleryFileUtils.copy(originalFile, pristineFile); | ||
1388 | 65 | } else { | ||
1389 | 66 | // if we reverted to original (and made no other changes) | ||
1390 | 67 | // we don't need to keep the pristine copy around | ||
1391 | 68 | if (_revertedInThisSession && level <= 0) { | ||
1392 | 69 | GalleryFileUtils.remove(pristineFile); | ||
1393 | 70 | } | ||
1394 | 71 | } | ||
1395 | 72 | |||
1396 | 73 | GalleryFileUtils.copy(currentFile, originalFile); // actually save | ||
1397 | 74 | } | ||
1398 | 75 | |||
1399 | 76 | GalleryFileUtils.removeDirectory(editingSessionPath, true); // clear editing cache | ||
1400 | 77 | editingSessionPath = originalFile = pristineFile = currentFile = ""; | ||
1401 | 78 | } | ||
1402 | 79 | |||
1403 | 80 | function createSnapshot(name) { | ||
1404 | 81 | var snapshotFile = editingSessionPath + "/edit." + name; | ||
1405 | 82 | GalleryFileUtils.copy(currentFile, snapshotFile); | ||
1406 | 83 | return snapshotFile; | ||
1407 | 84 | } | ||
1408 | 85 | |||
1409 | 86 | function restoreSnapshot(name) { | ||
1410 | 87 | var snapshotFile = editingSessionPath + "/edit." + name; | ||
1411 | 88 | GalleryFileUtils.copy(snapshotFile, currentFile); | ||
1412 | 89 | data.refreshFromDisk(); | ||
1413 | 90 | } | ||
1414 | 91 | |||
1415 | 92 | function checkpoint() { | ||
1416 | 93 | level++; | ||
1417 | 94 | items = items.slice(0, level); | ||
1418 | 95 | items.push(createSnapshot(items.length)); | ||
1419 | 96 | } | ||
1420 | 97 | |||
1421 | 98 | function revertToPristine() { | ||
1422 | 99 | if (!GalleryFileUtils.exists(pristineFile)) { | ||
1423 | 100 | restoreSnapshot(0); | ||
1424 | 101 | items = items.slice(0, 1); | ||
1425 | 102 | level = 0; | ||
1426 | 103 | } else { | ||
1427 | 104 | GalleryFileUtils.copy(pristineFile, currentFile); | ||
1428 | 105 | data.refreshFromDisk(); | ||
1429 | 106 | items = []; | ||
1430 | 107 | checkpoint(); | ||
1431 | 108 | level = 0; | ||
1432 | 109 | _revertedInThisSession = true; | ||
1433 | 110 | } | ||
1434 | 111 | } | ||
1435 | 112 | |||
1436 | 113 | property Action undoAction: Action { | ||
1437 | 114 | text: i18n.tr("Undo") | ||
1438 | 115 | iconName: "undo" | ||
1439 | 116 | enabled: items.length > 0 && level > 0 && actionsEnabled | ||
1440 | 117 | onTriggered: restoreSnapshot(--level); | ||
1441 | 118 | } | ||
1442 | 119 | |||
1443 | 120 | property Action redoAction: Action { | ||
1444 | 121 | text: i18n.tr("Redo") | ||
1445 | 122 | iconName: "redo" | ||
1446 | 123 | enabled: level < items.length - 1 && actionsEnabled | ||
1447 | 124 | onTriggered: restoreSnapshot(++level); | ||
1448 | 125 | } | ||
1449 | 126 | |||
1450 | 127 | property Action revertAction: Action { | ||
1451 | 128 | text: i18n.tr("Revert to Original") | ||
1452 | 129 | iconSource: Qt.resolvedUrl("assets/edit_revert.png") | ||
1453 | 130 | enabled: actionsEnabled && | ||
1454 | 131 | (level > 0 || (!_revertedInThisSession && _pristineFileExists)) | ||
1455 | 132 | onTriggered: revertRequested() | ||
1456 | 133 | } | ||
1457 | 134 | } | ||
1458 | 135 | 0 | ||
1459 | === removed file 'rc/qml/MediaViewer/PhotoEditor/ExposureAdjuster.qml' | |||
1460 | --- rc/qml/MediaViewer/PhotoEditor/ExposureAdjuster.qml 2015-11-05 19:04:22 +0000 | |||
1461 | +++ rc/qml/MediaViewer/PhotoEditor/ExposureAdjuster.qml 1970-01-01 00:00:00 +0000 | |||
1462 | @@ -1,104 +0,0 @@ | |||
1463 | 1 | /* | ||
1464 | 2 | * Copyright (C) 2014-2015 Canonical Ltd | ||
1465 | 3 | * | ||
1466 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1467 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1468 | 6 | * published by the Free Software Foundation. | ||
1469 | 7 | * | ||
1470 | 8 | * This program is distributed in the hope that it will be useful, | ||
1471 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1472 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1473 | 11 | * GNU General Public License for more details. | ||
1474 | 12 | * | ||
1475 | 13 | * You should have received a copy of the GNU General Public License | ||
1476 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1477 | 15 | */ | ||
1478 | 16 | |||
1479 | 17 | import QtQuick 2.4 | ||
1480 | 18 | import Ubuntu.Components 1.3 | ||
1481 | 19 | |||
1482 | 20 | // When the photo editor uses a proper PageStack this will switch back to being | ||
1483 | 21 | // an Item as it will not need to cover what is below it. | ||
1484 | 22 | Rectangle { | ||
1485 | 23 | id: adjuster | ||
1486 | 24 | color:"black" | ||
1487 | 25 | |||
1488 | 26 | property alias exposure: exposureSelector.value | ||
1489 | 27 | property bool enabled | ||
1490 | 28 | |||
1491 | 29 | signal confirm() | ||
1492 | 30 | signal cancel() | ||
1493 | 31 | |||
1494 | 32 | Image { | ||
1495 | 33 | id: targetImage | ||
1496 | 34 | anchors.fill: parent | ||
1497 | 35 | fillMode: Image.PreserveAspectFit | ||
1498 | 36 | asynchronous: true | ||
1499 | 37 | cache: false | ||
1500 | 38 | sourceSize { | ||
1501 | 39 | width: targetImage.width | ||
1502 | 40 | height: targetImage.height | ||
1503 | 41 | } | ||
1504 | 42 | } | ||
1505 | 43 | |||
1506 | 44 | Column { | ||
1507 | 45 | anchors.left: parent.left | ||
1508 | 46 | anchors.right: parent.right | ||
1509 | 47 | anchors.bottom: parent.bottom | ||
1510 | 48 | anchors.margins: units.gu(2) | ||
1511 | 49 | spacing: units.gu(2) | ||
1512 | 50 | |||
1513 | 51 | Slider { | ||
1514 | 52 | id: exposureSelector | ||
1515 | 53 | live: false | ||
1516 | 54 | minimumValue: -1.0 | ||
1517 | 55 | maximumValue: +1.0 | ||
1518 | 56 | value: 0.0 | ||
1519 | 57 | enabled: adjuster.enabled | ||
1520 | 58 | |||
1521 | 59 | anchors.left: parent.left | ||
1522 | 60 | anchors.right: parent.right | ||
1523 | 61 | height: units.gu(2) | ||
1524 | 62 | |||
1525 | 63 | function formatValue(value) { | ||
1526 | 64 | return (Math.round(value * 100) / 100).toString() | ||
1527 | 65 | } | ||
1528 | 66 | } | ||
1529 | 67 | Row { | ||
1530 | 68 | anchors.horizontalCenter: parent.horizontalCenter | ||
1531 | 69 | spacing: units.gu(2) | ||
1532 | 70 | Button { | ||
1533 | 71 | text: i18n.tr("Done") | ||
1534 | 72 | color: UbuntuColors.green | ||
1535 | 73 | enabled: adjuster.enabled | ||
1536 | 74 | onTriggered: { | ||
1537 | 75 | targetImage.source = ""; | ||
1538 | 76 | confirm(); | ||
1539 | 77 | } | ||
1540 | 78 | } | ||
1541 | 79 | Button { | ||
1542 | 80 | text: i18n.tr("Cancel") | ||
1543 | 81 | color: UbuntuColors.red | ||
1544 | 82 | enabled: adjuster.enabled | ||
1545 | 83 | onTriggered: { | ||
1546 | 84 | targetImage.source = ""; | ||
1547 | 85 | cancel(); | ||
1548 | 86 | } | ||
1549 | 87 | } | ||
1550 | 88 | } | ||
1551 | 89 | } | ||
1552 | 90 | |||
1553 | 91 | function start(target) { | ||
1554 | 92 | targetImage.source = target; | ||
1555 | 93 | exposure = 0.0; | ||
1556 | 94 | opacity = 1.0; | ||
1557 | 95 | } | ||
1558 | 96 | |||
1559 | 97 | function reload() { | ||
1560 | 98 | var path = targetImage.source; | ||
1561 | 99 | targetImage.asynchronous = false; | ||
1562 | 100 | targetImage.source = ""; | ||
1563 | 101 | targetImage.asynchronous = true; | ||
1564 | 102 | targetImage.source = path; | ||
1565 | 103 | } | ||
1566 | 104 | } | ||
1567 | 105 | 0 | ||
1568 | === removed file 'rc/qml/MediaViewer/PhotoEditor/GraphicsRoutines.js' | |||
1569 | --- rc/qml/MediaViewer/PhotoEditor/GraphicsRoutines.js 2015-02-26 21:00:19 +0000 | |||
1570 | +++ rc/qml/MediaViewer/PhotoEditor/GraphicsRoutines.js 1970-01-01 00:00:00 +0000 | |||
1571 | @@ -1,108 +0,0 @@ | |||
1572 | 1 | /* | ||
1573 | 2 | * Copyright (C) 2012 Canonical Ltd | ||
1574 | 3 | * | ||
1575 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1576 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1577 | 6 | * published by the Free Software Foundation. | ||
1578 | 7 | * | ||
1579 | 8 | * This program is distributed in the hope that it will be useful, | ||
1580 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1581 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1582 | 11 | * GNU General Public License for more details. | ||
1583 | 12 | * | ||
1584 | 13 | * You should have received a copy of the GNU General Public License | ||
1585 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1586 | 15 | * | ||
1587 | 16 | * Authors: | ||
1588 | 17 | * Lucas Beeler <lucas@yorba.org> | ||
1589 | 18 | */ | ||
1590 | 19 | |||
1591 | 20 | /* Given 'input', constrain the value of 'input' to range between | ||
1592 | 21 | * 'lowConstraint' and 'highConstraint', inclusive. Return the constrained | ||
1593 | 22 | * value without modifying 'input'. | ||
1594 | 23 | */ | ||
1595 | 24 | function clamp(input, lowConstraint, highConstraint) { | ||
1596 | 25 | if (input < lowConstraint) | ||
1597 | 26 | return lowConstraint; | ||
1598 | 27 | else if (input > highConstraint) | ||
1599 | 28 | return highConstraint; | ||
1600 | 29 | else | ||
1601 | 30 | return input; | ||
1602 | 31 | } | ||
1603 | 32 | |||
1604 | 33 | function cloneRect(source) { | ||
1605 | 34 | var ret = { }; | ||
1606 | 35 | ret.x = source.x; | ||
1607 | 36 | ret.y = source.y; | ||
1608 | 37 | ret.width = source.width; | ||
1609 | 38 | ret.height = source.height; | ||
1610 | 39 | |||
1611 | 40 | return ret; | ||
1612 | 41 | } | ||
1613 | 42 | |||
1614 | 43 | function interpolateRect(start, end, factor) { | ||
1615 | 44 | var result = { }; | ||
1616 | 45 | |||
1617 | 46 | result.x = start.x + factor * (end.x - start.x); | ||
1618 | 47 | result.y = start.y + factor * (end.y - start.y); | ||
1619 | 48 | result.width = start.width + factor * (end.width - start.width); | ||
1620 | 49 | result.height = start.height + factor * (end.height - start.height); | ||
1621 | 50 | |||
1622 | 51 | return result; | ||
1623 | 52 | } | ||
1624 | 53 | |||
1625 | 54 | /* Forces Geometry object 'item' to fit centered inside Geometry object | ||
1626 | 55 | * 'viewport', preserving the aspect of ratio of 'item' but potentially scaling | ||
1627 | 56 | * and translating it so that it snugly fits centered inside of 'viewport'. | ||
1628 | 57 | * Return the new scaled-up and translated Geometry for 'item'. | ||
1629 | 58 | */ | ||
1630 | 59 | function fitRect(viewport, item) { | ||
1631 | 60 | if (item.width == 0 || item.height == 0) { | ||
1632 | 61 | return viewport; | ||
1633 | 62 | } | ||
1634 | 63 | |||
1635 | 64 | var itemAspectRatio = item.width / item.height; | ||
1636 | 65 | var viewportAspectRatio = viewport.width / viewport.height; | ||
1637 | 66 | |||
1638 | 67 | var result = { }; | ||
1639 | 68 | if (itemAspectRatio > viewportAspectRatio) { | ||
1640 | 69 | var scaleFactor = viewport.width / item.width; | ||
1641 | 70 | result.width = viewport.width; | ||
1642 | 71 | result.height = item.height * scaleFactor | ||
1643 | 72 | } else { | ||
1644 | 73 | scaleFactor = viewport.height / item.height; | ||
1645 | 74 | result.width = item.width * scaleFactor | ||
1646 | 75 | result.height = viewport.height; | ||
1647 | 76 | } | ||
1648 | 77 | |||
1649 | 78 | result.width = clamp(result.width, 0, viewport.width); | ||
1650 | 79 | result.height = clamp(result.height, 0, viewport.height); | ||
1651 | 80 | result.x = viewport.x + (viewport.width - result.width) / 2; | ||
1652 | 81 | result.y = viewport.y + (viewport.height - result.height) / 2; | ||
1653 | 82 | result.scaleFactor = scaleFactor; | ||
1654 | 83 | |||
1655 | 84 | return result; | ||
1656 | 85 | } | ||
1657 | 86 | |||
1658 | 87 | function getRelativeRect(geom, relativeTo) { | ||
1659 | 88 | var result = { }; | ||
1660 | 89 | |||
1661 | 90 | result.x = (geom.x - relativeTo.x) / relativeTo.width; | ||
1662 | 91 | result.y = (geom.y - relativeTo.y) / relativeTo.height; | ||
1663 | 92 | result.width = geom.width / relativeTo.width; | ||
1664 | 93 | result.height = geom.height / relativeTo.height; | ||
1665 | 94 | |||
1666 | 95 | return result; | ||
1667 | 96 | } | ||
1668 | 97 | |||
1669 | 98 | function sizeToRect(rect, qmlItem) { | ||
1670 | 99 | qmlItem.x = rect.x; | ||
1671 | 100 | qmlItem.y = rect.y; | ||
1672 | 101 | qmlItem.width = rect.width; | ||
1673 | 102 | qmlItem.height = rect.height; | ||
1674 | 103 | } | ||
1675 | 104 | |||
1676 | 105 | function areEqual(geom1, geom2) { | ||
1677 | 106 | return (geom1.x === geom2.x && geom1.y === geom2.y && geom1.width === | ||
1678 | 107 | geom2.width && geom1.height === geom2.height); | ||
1679 | 108 | } | ||
1680 | 109 | 0 | ||
1681 | === removed directory 'rc/qml/MediaViewer/PhotoEditor/assets' | |||
1682 | === removed file 'rc/qml/MediaViewer/PhotoEditor/assets/crop-handle@20.png' | |||
1683 | 110 | Binary 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 | 1 | Binary 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 |
1684 | === removed file 'rc/qml/MediaViewer/PhotoEditor/assets/edit_autocorrect@27.png' | |||
1685 | 111 | Binary 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 | 2 | Binary 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 |
1686 | === removed file 'rc/qml/MediaViewer/PhotoEditor/assets/edit_crop@27.png' | |||
1687 | 112 | Binary 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 | 3 | Binary 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 |
1688 | === removed file 'rc/qml/MediaViewer/PhotoEditor/assets/edit_exposure@27.png' | |||
1689 | 113 | Binary 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 | 4 | Binary 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 |
1690 | === removed file 'rc/qml/MediaViewer/PhotoEditor/assets/edit_revert@27.png' | |||
1691 | 114 | Binary 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 | 5 | Binary 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 |
1692 | === removed file 'rc/qml/MediaViewer/PhotoEditor/assets/edit_rotate_left@27.png' | |||
1693 | 115 | Binary 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 | 6 | Binary 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 |
1694 | === renamed file 'rc/qml/MediaViewer/ExtrasPhotoEditorPage.qml' => 'rc/qml/MediaViewer/PhotoEditorPage.qml' | |||
1695 | === modified file 'src/gallery-application.cpp' | |||
1696 | --- src/gallery-application.cpp 2015-04-30 22:00:38 +0000 | |||
1697 | +++ src/gallery-application.cpp 2015-12-16 13:52:17 +0000 | |||
1698 | @@ -37,8 +37,6 @@ | |||
1699 | 37 | #include "photo.h" | 37 | #include "photo.h" |
1700 | 38 | 38 | ||
1701 | 39 | // photoeditor | 39 | // photoeditor |
1702 | 40 | #include "photo-data.h" | ||
1703 | 41 | #include "file-utils.h" | ||
1704 | 42 | #include "photo-image-provider.h" | 40 | #include "photo-image-provider.h" |
1705 | 43 | 41 | ||
1706 | 44 | // qml | 42 | // qml |
1707 | @@ -175,8 +173,6 @@ | |||
1708 | 175 | qmlRegisterType<QmlEventCollectionModel>("Gallery", 1, 0, "EventCollectionModel"); | 173 | qmlRegisterType<QmlEventCollectionModel>("Gallery", 1, 0, "EventCollectionModel"); |
1709 | 176 | qmlRegisterType<QmlEventOverviewModel>("Gallery", 1, 0, "EventOverviewModel"); | 174 | qmlRegisterType<QmlEventOverviewModel>("Gallery", 1, 0, "EventOverviewModel"); |
1710 | 177 | qmlRegisterType<QmlMediaCollectionModel>("Gallery", 1, 0, "MediaCollectionModel"); | 175 | qmlRegisterType<QmlMediaCollectionModel>("Gallery", 1, 0, "MediaCollectionModel"); |
1711 | 178 | qmlRegisterType<PhotoData>("Gallery", 1, 0, "GalleryPhotoData"); | ||
1712 | 179 | qmlRegisterSingletonType<FileUtils>("Gallery", 1, 0, "GalleryFileUtils", exportFileUtilsSingleton); | ||
1713 | 180 | 176 | ||
1714 | 181 | qRegisterMetaType<QList<MediaSource*> >("MediaSourceList"); | 177 | qRegisterMetaType<QList<MediaSource*> >("MediaSourceList"); |
1715 | 182 | qRegisterMetaType<QSet<DataObject*> >("QSet<DataObject*>"); | 178 | qRegisterMetaType<QSet<DataObject*> >("QSet<DataObject*>"); |
1716 | @@ -269,15 +265,6 @@ | |||
1717 | 269 | setMediaFile(m_cmdLineParser->mediaFile()); | 265 | setMediaFile(m_cmdLineParser->mediaFile()); |
1718 | 270 | } | 266 | } |
1719 | 271 | 267 | ||
1720 | 272 | QObject* GalleryApplication::exportFileUtilsSingleton(QQmlEngine *engine, | ||
1721 | 273 | QJSEngine *scriptEngine) | ||
1722 | 274 | { | ||
1723 | 275 | Q_UNUSED(engine); | ||
1724 | 276 | Q_UNUSED(scriptEngine); | ||
1725 | 277 | |||
1726 | 278 | return new FileUtils(); | ||
1727 | 279 | } | ||
1728 | 280 | |||
1729 | 281 | /*! | 268 | /*! |
1730 | 282 | * \brief GalleryApplication::initCollections | 269 | * \brief GalleryApplication::initCollections |
1731 | 283 | */ | 270 | */ |
1732 | 284 | 271 | ||
1733 | === modified file 'src/gallery-application.h' | |||
1734 | --- src/gallery-application.h 2015-02-26 20:28:06 +0000 | |||
1735 | +++ src/gallery-application.h 2015-12-16 13:52:17 +0000 | |||
1736 | @@ -93,8 +93,6 @@ | |||
1737 | 93 | private: | 93 | private: |
1738 | 94 | void registerQML(); | 94 | void registerQML(); |
1739 | 95 | void createView(); | 95 | void createView(); |
1740 | 96 | static QObject* exportFileUtilsSingleton(QQmlEngine *engine, | ||
1741 | 97 | QJSEngine *scriptEngine); | ||
1742 | 98 | 96 | ||
1743 | 99 | QQuickView *m_view; | 97 | QQuickView *m_view; |
1744 | 100 | GalleryManager *m_galleryManager; | 98 | GalleryManager *m_galleryManager; |
1745 | 101 | 99 | ||
1746 | === modified file 'src/photoeditor/CMakeLists.txt' | |||
1747 | --- src/photoeditor/CMakeLists.txt 2015-02-26 20:34:48 +0000 | |||
1748 | +++ src/photoeditor/CMakeLists.txt 2015-12-16 13:52:17 +0000 | |||
1749 | @@ -14,24 +14,11 @@ | |||
1750 | 14 | ) | 14 | ) |
1751 | 15 | 15 | ||
1752 | 16 | set(gallery_photoeditor_HDRS | 16 | set(gallery_photoeditor_HDRS |
1753 | 17 | file-utils.h | ||
1754 | 18 | imaging.h | ||
1755 | 19 | orientation.h | ||
1756 | 20 | photo-caches.h | ||
1757 | 21 | photo-data.h | ||
1758 | 22 | photo-edit-command.h | ||
1759 | 23 | photo-edit-thread.h | ||
1760 | 24 | photo-image-provider.h | 17 | photo-image-provider.h |
1761 | 25 | photo-metadata.h | 18 | photo-metadata.h |
1762 | 26 | ) | 19 | ) |
1763 | 27 | 20 | ||
1764 | 28 | set(gallery_photoeditor_SRCS | 21 | set(gallery_photoeditor_SRCS |
1765 | 29 | file-utils.cpp | ||
1766 | 30 | imaging.cpp | ||
1767 | 31 | orientation.cpp | ||
1768 | 32 | photo-caches.cpp | ||
1769 | 33 | photo-data.cpp | ||
1770 | 34 | photo-edit-thread.cpp | ||
1771 | 35 | photo-image-provider.cpp | 22 | photo-image-provider.cpp |
1772 | 36 | photo-metadata.cpp | 23 | photo-metadata.cpp |
1773 | 37 | ) | 24 | ) |
1774 | 38 | 25 | ||
1775 | === removed file 'src/photoeditor/file-utils.cpp' | |||
1776 | --- src/photoeditor/file-utils.cpp 2015-02-26 20:28:06 +0000 | |||
1777 | +++ src/photoeditor/file-utils.cpp 1970-01-01 00:00:00 +0000 | |||
1778 | @@ -1,97 +0,0 @@ | |||
1779 | 1 | /* | ||
1780 | 2 | * Copyright (C) 2014 Canonical Ltd | ||
1781 | 3 | * | ||
1782 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1783 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1784 | 6 | * published by the Free Software Foundation. | ||
1785 | 7 | * | ||
1786 | 8 | * This program is distributed in the hope that it will be useful, | ||
1787 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1788 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1789 | 11 | * GNU General Public License for more details. | ||
1790 | 12 | * | ||
1791 | 13 | * You should have received a copy of the GNU General Public License | ||
1792 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1793 | 15 | */ | ||
1794 | 16 | |||
1795 | 17 | #include "file-utils.h" | ||
1796 | 18 | |||
1797 | 19 | #include <QDebug> | ||
1798 | 20 | #include <QDir> | ||
1799 | 21 | #include <QFile> | ||
1800 | 22 | #include <QFileInfo> | ||
1801 | 23 | #include <QTemporaryDir> | ||
1802 | 24 | |||
1803 | 25 | FileUtils::FileUtils(QObject *parent) : | ||
1804 | 26 | QObject(parent) | ||
1805 | 27 | { | ||
1806 | 28 | } | ||
1807 | 29 | |||
1808 | 30 | bool FileUtils::createDirectory(QString path) const | ||
1809 | 31 | { | ||
1810 | 32 | if (path.isEmpty()) return false; | ||
1811 | 33 | |||
1812 | 34 | return QDir(path).mkpath("."); | ||
1813 | 35 | } | ||
1814 | 36 | |||
1815 | 37 | QString FileUtils::createTemporaryDirectory(QString pathTemplate) const | ||
1816 | 38 | { | ||
1817 | 39 | QTemporaryDir dir(pathTemplate); | ||
1818 | 40 | if (!dir.isValid()) return QString(); | ||
1819 | 41 | |||
1820 | 42 | dir.setAutoRemove(false); | ||
1821 | 43 | return dir.path(); | ||
1822 | 44 | } | ||
1823 | 45 | |||
1824 | 46 | bool FileUtils::removeDirectory(QString path, bool recursive) const | ||
1825 | 47 | { | ||
1826 | 48 | if (path.isEmpty()) return false; | ||
1827 | 49 | |||
1828 | 50 | QDir dir(path); | ||
1829 | 51 | return (recursive) ? dir.removeRecursively() : dir.rmdir("."); | ||
1830 | 52 | } | ||
1831 | 53 | |||
1832 | 54 | bool FileUtils::remove(QString path) const | ||
1833 | 55 | { | ||
1834 | 56 | if (path.isEmpty()) return false; | ||
1835 | 57 | return QFile::remove(path); | ||
1836 | 58 | } | ||
1837 | 59 | |||
1838 | 60 | bool FileUtils::copy(QString sourceFile, QString destinationFile) const | ||
1839 | 61 | { | ||
1840 | 62 | if (sourceFile.isEmpty() || destinationFile.isEmpty()) return false; | ||
1841 | 63 | |||
1842 | 64 | if (QFileInfo(destinationFile).exists()) QFile::remove(destinationFile); | ||
1843 | 65 | return QFile::copy(sourceFile, destinationFile); | ||
1844 | 66 | } | ||
1845 | 67 | |||
1846 | 68 | bool FileUtils::rename(QString sourceFile, QString destinationFile) const | ||
1847 | 69 | { | ||
1848 | 70 | if (sourceFile.isEmpty() || destinationFile.isEmpty()) return false; | ||
1849 | 71 | |||
1850 | 72 | if (QFileInfo(destinationFile).exists()) QFile::remove(destinationFile); | ||
1851 | 73 | return QFile::rename(sourceFile, destinationFile); | ||
1852 | 74 | } | ||
1853 | 75 | |||
1854 | 76 | QString FileUtils::parentDirectory(QString path) const | ||
1855 | 77 | { | ||
1856 | 78 | if (QFileInfo(path).isDir()) { | ||
1857 | 79 | QDir dir(path); | ||
1858 | 80 | dir.cdUp(); | ||
1859 | 81 | return dir.absolutePath(); | ||
1860 | 82 | } else { | ||
1861 | 83 | return QFileInfo(path).dir().absolutePath(); | ||
1862 | 84 | } | ||
1863 | 85 | } | ||
1864 | 86 | |||
1865 | 87 | QString FileUtils::nameFromPath(QString path) const | ||
1866 | 88 | { | ||
1867 | 89 | QString name = QFileInfo(path).fileName(); | ||
1868 | 90 | if (name.isEmpty()) name = QDir(path).dirName(); | ||
1869 | 91 | return name; | ||
1870 | 92 | } | ||
1871 | 93 | |||
1872 | 94 | bool FileUtils::exists(QString path) const | ||
1873 | 95 | { | ||
1874 | 96 | return QFileInfo::exists(path); | ||
1875 | 97 | } | ||
1876 | 98 | 0 | ||
1877 | === removed file 'src/photoeditor/file-utils.h' | |||
1878 | --- src/photoeditor/file-utils.h 2015-02-26 20:28:06 +0000 | |||
1879 | +++ src/photoeditor/file-utils.h 1970-01-01 00:00:00 +0000 | |||
1880 | @@ -1,42 +0,0 @@ | |||
1881 | 1 | /* | ||
1882 | 2 | * Copyright (C) 2014 Canonical Ltd | ||
1883 | 3 | * | ||
1884 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1885 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1886 | 6 | * published by the Free Software Foundation. | ||
1887 | 7 | * | ||
1888 | 8 | * This program is distributed in the hope that it will be useful, | ||
1889 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1890 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1891 | 11 | * GNU General Public License for more details. | ||
1892 | 12 | * | ||
1893 | 13 | * You should have received a copy of the GNU General Public License | ||
1894 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1895 | 15 | */ | ||
1896 | 16 | |||
1897 | 17 | #ifndef PHOTOUTILS_H | ||
1898 | 18 | #define PHOTOUTILS_H | ||
1899 | 19 | |||
1900 | 20 | #include <QObject> | ||
1901 | 21 | |||
1902 | 22 | class FileUtils : public QObject | ||
1903 | 23 | { | ||
1904 | 24 | Q_OBJECT | ||
1905 | 25 | public: | ||
1906 | 26 | explicit FileUtils(QObject *parent = 0); | ||
1907 | 27 | |||
1908 | 28 | Q_INVOKABLE bool createDirectory(QString path) const; | ||
1909 | 29 | Q_INVOKABLE bool removeDirectory(QString path, bool recursive = false) const; | ||
1910 | 30 | Q_INVOKABLE QString createTemporaryDirectory(QString pathTemplate) const; | ||
1911 | 31 | |||
1912 | 32 | Q_INVOKABLE bool remove(QString path) const; | ||
1913 | 33 | Q_INVOKABLE bool copy(QString sourceFile, QString destinationFile) const; | ||
1914 | 34 | Q_INVOKABLE bool rename(QString sourceFile, QString destinationFile) const; | ||
1915 | 35 | |||
1916 | 36 | Q_INVOKABLE QString parentDirectory(QString path) const; | ||
1917 | 37 | Q_INVOKABLE QString nameFromPath(QString path) const; | ||
1918 | 38 | |||
1919 | 39 | Q_INVOKABLE bool exists(QString path) const; | ||
1920 | 40 | }; | ||
1921 | 41 | |||
1922 | 42 | #endif // PHOTOUTILS_H | ||
1923 | 43 | 0 | ||
1924 | === removed file 'src/photoeditor/imaging.cpp' | |||
1925 | --- src/photoeditor/imaging.cpp 2015-02-26 20:28:06 +0000 | |||
1926 | +++ src/photoeditor/imaging.cpp 1970-01-01 00:00:00 +0000 | |||
1927 | @@ -1,363 +0,0 @@ | |||
1928 | 1 | /* | ||
1929 | 2 | * Copyright (C) 2012 Canonical Ltd | ||
1930 | 3 | * | ||
1931 | 4 | * This program is free software: you can redistribute it and/or modify | ||
1932 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
1933 | 6 | * published by the Free Software Foundation. | ||
1934 | 7 | * | ||
1935 | 8 | * This program is distributed in the hope that it will be useful, | ||
1936 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1937 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1938 | 11 | * GNU General Public License for more details. | ||
1939 | 12 | * | ||
1940 | 13 | * You should have received a copy of the GNU General Public License | ||
1941 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1942 | 15 | * | ||
1943 | 16 | * Authors: | ||
1944 | 17 | * Lucas Beeler <lucas@yorba.org> | ||
1945 | 18 | */ | ||
1946 | 19 | |||
1947 | 20 | |||
1948 | 21 | #include <QApplication> | ||
1949 | 22 | #include <qmath.h> | ||
1950 | 23 | |||
1951 | 24 | #include "imaging.h" | ||
1952 | 25 | |||
1953 | 26 | /*! | ||
1954 | 27 | * \brief HSVTransformation::transformPixel | ||
1955 | 28 | * \param pixel_color | ||
1956 | 29 | * \return | ||
1957 | 30 | */ | ||
1958 | 31 | QColor HSVTransformation::transformPixel(const QColor &pixel_color) const | ||
1959 | 32 | { | ||
1960 | 33 | QColor result; | ||
1961 | 34 | |||
1962 | 35 | int h, s, v; | ||
1963 | 36 | pixel_color.getHsv(&h, &s, &v); | ||
1964 | 37 | |||
1965 | 38 | v = remap_table_[v]; | ||
1966 | 39 | |||
1967 | 40 | result.setHsv(h, s, v); | ||
1968 | 41 | |||
1969 | 42 | return result; | ||
1970 | 43 | } | ||
1971 | 44 | |||
1972 | 45 | /*! | ||
1973 | 46 | * \brief IntensityHistogram::IntensityHistogram | ||
1974 | 47 | * \param basis_image | ||
1975 | 48 | */ | ||
1976 | 49 | IntensityHistogram::IntensityHistogram(const QImage& basis_image) | ||
1977 | 50 | { | ||
1978 | 51 | for (int i = 0; i < 256; i++) | ||
1979 | 52 | m_counts[i] = 0; | ||
1980 | 53 | |||
1981 | 54 | int width = basis_image.width(); | ||
1982 | 55 | int height = basis_image.height(); | ||
1983 | 56 | |||
1984 | 57 | for (int j = 0; j < height; j++) { | ||
1985 | 58 | QApplication::processEvents(); | ||
1986 | 59 | |||
1987 | 60 | for (int i = 0; i < width; i++) { | ||
1988 | 61 | QColor c = QColor(basis_image.pixel(i, j)); | ||
1989 | 62 | int intensity = c.value(); | ||
1990 | 63 | m_counts[intensity]++; | ||
1991 | 64 | } | ||
1992 | 65 | } | ||
1993 | 66 | |||
1994 | 67 | float pixel_count = (float)(width * height); | ||
1995 | 68 | float accumulator = 0.0f; | ||
1996 | 69 | for (int i = 0; i < 256; i++) { | ||
1997 | 70 | m_probabilities[i] = ((float) m_counts[i]) / pixel_count; | ||
1998 | 71 | accumulator += m_probabilities[i]; | ||
1999 | 72 | m_cumulativeProbabilities[i] = accumulator; | ||
2000 | 73 | } | ||
2001 | 74 | } | ||
2002 | 75 | |||
2003 | 76 | /*! | ||
2004 | 77 | * \brief IntensityHistogram::getCumulativeProbability | ||
2005 | 78 | * \param level | ||
2006 | 79 | * \return | ||
2007 | 80 | */ | ||
2008 | 81 | float IntensityHistogram::getCumulativeProbability(int level) | ||
2009 | 82 | { | ||
2010 | 83 | return m_cumulativeProbabilities[level]; | ||
2011 | 84 | } | ||
2012 | 85 | |||
2013 | 86 | |||
2014 | 87 | const float ToneExpansionTransformation::DEFAULT_LOW_DISCARD_MASS = 0.02f; | ||
2015 | 88 | const float ToneExpansionTransformation::DEFAULT_HIGH_DISCARD_MASS = 0.98f; | ||
2016 | 89 | /*! | ||
2017 | 90 | * \brief ToneExpansionTransformation::ToneExpansionTransformation | ||
2018 | 91 | * \param h | ||
2019 | 92 | * \param low_discard_mass | ||
2020 | 93 | * \param high_discard_mass | ||
2021 | 94 | */ | ||
2022 | 95 | ToneExpansionTransformation::ToneExpansionTransformation(IntensityHistogram h, | ||
2023 | 96 | float low_discard_mass, float high_discard_mass) | ||
2024 | 97 | { | ||
2025 | 98 | if (low_discard_mass == -1.0f) | ||
2026 | 99 | low_discard_mass = DEFAULT_LOW_DISCARD_MASS; | ||
2027 | 100 | if (high_discard_mass == -1.0f) | ||
2028 | 101 | high_discard_mass = DEFAULT_HIGH_DISCARD_MASS; | ||
2029 | 102 | |||
2030 | 103 | m_lowDiscardMass = low_discard_mass; | ||
2031 | 104 | m_highDiscardMass = high_discard_mass; | ||
2032 | 105 | |||
2033 | 106 | m_lowKink = 0; | ||
2034 | 107 | m_highKink = 255; | ||
2035 | 108 | |||
2036 | 109 | while (h.getCumulativeProbability(m_lowKink) < low_discard_mass) | ||
2037 | 110 | m_lowKink++; | ||
2038 | 111 | |||
2039 | 112 | while (h.getCumulativeProbability(m_highKink) > high_discard_mass) | ||
2040 | 113 | m_highKink--; | ||
2041 | 114 | |||
2042 | 115 | m_lowKink = clampi(m_lowKink, 0, 255); | ||
2043 | 116 | m_highKink = clampi(m_highKink, 0, 255); | ||
2044 | 117 | |||
2045 | 118 | buildRemapTable(); | ||
2046 | 119 | } | ||
2047 | 120 | |||
2048 | 121 | /*! | ||
2049 | 122 | * \brief ToneExpansionTransformation::isIdentity | ||
2050 | 123 | * \return | ||
2051 | 124 | */ | ||
2052 | 125 | bool ToneExpansionTransformation::isIdentity() const | ||
2053 | 126 | { | ||
2054 | 127 | return ((m_lowKink == 0) && (m_highKink == 255)); | ||
2055 | 128 | } | ||
2056 | 129 | |||
2057 | 130 | /*! | ||
2058 | 131 | * \brief ToneExpansionTransformation::buildRemapTable | ||
2059 | 132 | */ | ||
2060 | 133 | void ToneExpansionTransformation::buildRemapTable() | ||
2061 | 134 | { | ||
2062 | 135 | float low_kink_f = ((float) m_lowKink) / 255.0f; | ||
2063 | 136 | float high_kink_f = ((float) m_highKink) / 255.0f; | ||
2064 | 137 | |||
2065 | 138 | float slope = 1.0f / (high_kink_f - low_kink_f); | ||
2066 | 139 | float intercept = -(low_kink_f / (high_kink_f - low_kink_f)); | ||
2067 | 140 | |||
2068 | 141 | int i = 0; | ||
2069 | 142 | for ( ; i <= m_lowKink; i++) | ||
2070 | 143 | remap_table_[i] = 0; | ||
2071 | 144 | |||
2072 | 145 | for ( ; i < m_highKink; i++) | ||
2073 | 146 | remap_table_[i] = (int) ((255.0f * (slope * (((float) i) / 255.0f) + | ||
2074 | 147 | intercept)) + 0.5); | ||
2075 | 148 | |||
2076 | 149 | for ( ; i < 256; i++) | ||
2077 | 150 | remap_table_[i] = 255; | ||
2078 | 151 | } | ||
2079 | 152 | |||
2080 | 153 | /*! | ||
2081 | 154 | * \brief ToneExpansionTransformation::lowDiscardMass | ||
2082 | 155 | * \return | ||
2083 | 156 | */ | ||
2084 | 157 | float ToneExpansionTransformation::lowDiscardMass() const | ||
2085 | 158 | { | ||
2086 | 159 | return m_lowDiscardMass; | ||
2087 | 160 | } | ||
2088 | 161 | |||
2089 | 162 | /*! | ||
2090 | 163 | * \brief ToneExpansionTransformation::highDiscardMass | ||
2091 | 164 | * \return | ||
2092 | 165 | */ | ||
2093 | 166 | float ToneExpansionTransformation::highDiscardMass() const | ||
2094 | 167 | { | ||
2095 | 168 | return m_highDiscardMass; | ||
2096 | 169 | } | ||
2097 | 170 | |||
2098 | 171 | |||
2099 | 172 | /*! | ||
2100 | 173 | * \brief HermiteGammaApproximationFunction::HermiteGammaApproximationFunction | ||
2101 | 174 | * \param user_interval_upper | ||
2102 | 175 | */ | ||
2103 | 176 | HermiteGammaApproximationFunction::HermiteGammaApproximationFunction( | ||
2104 | 177 | float user_interval_upper) | ||
2105 | 178 | { | ||
2106 | 179 | m_nonzeroIntervalUpper = clampf(user_interval_upper, 0.1f, 1.0f); | ||
2107 | 180 | m_xScale = 1.0f / m_nonzeroIntervalUpper; | ||
2108 | 181 | } | ||
2109 | 182 | |||
2110 | 183 | /*! | ||
2111 | 184 | * \brief HermiteGammaApproximationFunction::evaluate | ||
2112 | 185 | * \param x | ||
2113 | 186 | * \return | ||
2114 | 187 | */ | ||
2115 | 188 | float HermiteGammaApproximationFunction::evaluate(float x) | ||
2116 | 189 | { | ||
2117 | 190 | if (x < 0.0f) | ||
2118 | 191 | return 0.0f; | ||
2119 | 192 | else if (x > m_nonzeroIntervalUpper) | ||
2120 | 193 | return 0.0f; | ||
2121 | 194 | else { | ||
2122 | 195 | float indep_var = m_xScale * x; | ||
2123 | 196 | |||
2124 | 197 | float dep_var = 6.0f * ((indep_var * indep_var * indep_var) - | ||
2125 | 198 | (2.0f * (indep_var * indep_var)) + (indep_var)); | ||
2126 | 199 | |||
2127 | 200 | return clampf(dep_var, 0.0f, 1.0f); | ||
2128 | 201 | } | ||
2129 | 202 | } | ||
2130 | 203 | |||
2131 | 204 | |||
2132 | 205 | const float ShadowDetailTransformation::MAX_EFFECT_SHIFT = 0.5f; | ||
2133 | 206 | const float ShadowDetailTransformation::MIN_TONAL_WIDTH = 0.1f; | ||
2134 | 207 | const float ShadowDetailTransformation::MAX_TONAL_WIDTH = 1.0f; | ||
2135 | 208 | const float ShadowDetailTransformation::TONAL_WIDTH = 1.0f; | ||
2136 | 209 | /*! | ||
2137 | 210 | * \brief ShadowDetailTransformation::ShadowDetailTransformation | ||
2138 | 211 | * \param intensity | ||
2139 | 212 | */ | ||
2140 | 213 | ShadowDetailTransformation::ShadowDetailTransformation(float intensity) | ||
2141 | 214 | { | ||
2142 | 215 | m_intensity = intensity; | ||
2143 | 216 | float effect_shift = MAX_EFFECT_SHIFT * intensity; | ||
2144 | 217 | |||
2145 | 218 | HermiteGammaApproximationFunction func = | ||
2146 | 219 | HermiteGammaApproximationFunction(TONAL_WIDTH); | ||
2147 | 220 | |||
2148 | 221 | for (int i = 0; i < 256; i++) { | ||
2149 | 222 | float x = ((float) i) / 255.0f; | ||
2150 | 223 | float weight = func.evaluate(x); | ||
2151 | 224 | |||
2152 | 225 | int remapped = (int) ((255.0f * (weight * (x + effect_shift)) + ((1.0f - | ||
2153 | 226 | weight) * x)) + 0.5f); | ||
2154 | 227 | remap_table_[i] = clampi(remapped, i, 255); | ||
2155 | 228 | } | ||
2156 | 229 | } | ||
2157 | 230 | |||
2158 | 231 | /*! | ||
2159 | 232 | * \brief ShadowDetailTransformation::isIdentity | ||
2160 | 233 | * \return | ||
2161 | 234 | */ | ||
2162 | 235 | bool ShadowDetailTransformation::isIdentity() const | ||
2163 | 236 | { | ||
2164 | 237 | return (m_intensity == 0.0f); | ||
2165 | 238 | } | ||
2166 | 239 | |||
2167 | 240 | |||
2168 | 241 | const int AutoEnhanceTransformation::SHADOW_DETECT_MIN_INTENSITY = 2; | ||
2169 | 242 | const int AutoEnhanceTransformation::SHADOW_DETECT_MAX_INTENSITY = 90; | ||
2170 | 243 | const int AutoEnhanceTransformation::SHADOW_DETECT_INTENSITY_RANGE = | ||
2171 | 244 | AutoEnhanceTransformation::SHADOW_DETECT_MAX_INTENSITY - | ||
2172 | 245 | AutoEnhanceTransformation::SHADOW_DETECT_MIN_INTENSITY; | ||
2173 | 246 | const int AutoEnhanceTransformation::EMPIRICAL_DARK = 40; | ||
2174 | 247 | const float AutoEnhanceTransformation::SHADOW_AGGRESSIVENESS_MUL = 0.45f; | ||
2175 | 248 | /*! | ||
2176 | 249 | * \brief AutoEnhanceTransformation::AutoEnhanceTransformation | ||
2177 | 250 | * \param basis | ||
2178 | 251 | */ | ||
2179 | 252 | AutoEnhanceTransformation::AutoEnhanceTransformation(const QImage& basis) | ||
2180 | 253 | : m_shadowTransform(0), m_toneExpansionTransform(0) | ||
2181 | 254 | { | ||
2182 | 255 | IntensityHistogram histogram = IntensityHistogram(basis); | ||
2183 | 256 | |||
2184 | 257 | /* compute the percentage of pixels in the image that fall into the | ||
2185 | 258 | shadow range -- this measures "of the pixels in the image, how many of | ||
2186 | 259 | them are in shadow?" */ | ||
2187 | 260 | float pct_in_range = 100.0f * | ||
2188 | 261 | (histogram.getCumulativeProbability(SHADOW_DETECT_MAX_INTENSITY) - | ||
2189 | 262 | histogram.getCumulativeProbability(SHADOW_DETECT_MIN_INTENSITY)); | ||
2190 | 263 | |||
2191 | 264 | /* compute the mean intensity of the pixels that are in the shadow range -- | ||
2192 | 265 | this measures "of those pixels that are in shadow, just how dark are | ||
2193 | 266 | they?" */ | ||
2194 | 267 | float sh_prob_mu = | ||
2195 | 268 | (histogram.getCumulativeProbability(SHADOW_DETECT_MIN_INTENSITY) + | ||
2196 | 269 | histogram.getCumulativeProbability(SHADOW_DETECT_MAX_INTENSITY)) * 0.5f; | ||
2197 | 270 | int sh_intensity_mu = SHADOW_DETECT_MIN_INTENSITY; | ||
2198 | 271 | for ( ; sh_intensity_mu <= SHADOW_DETECT_MAX_INTENSITY; sh_intensity_mu++) { | ||
2199 | 272 | if (histogram.getCumulativeProbability(sh_intensity_mu) >= sh_prob_mu) | ||
2200 | 273 | break; | ||
2201 | 274 | } | ||
2202 | 275 | |||
2203 | 276 | /* if more than 30 percent of the pixels in the image are in the shadow | ||
2204 | 277 | detection range, or if the mean intensity within the shadow range is less | ||
2205 | 278 | than an empirically determined threshold below which pixels appear very | ||
2206 | 279 | dark, regardless of the percent of pixels in it, then perform shadow | ||
2207 | 280 | detail enhancement. Otherwise, skip shadow detail enhancement and perform | ||
2208 | 281 | contrast expansion only */ | ||
2209 | 282 | if ((pct_in_range > 30.0f) || ((pct_in_range > 10.0f) && | ||
2210 | 283 | (sh_intensity_mu < EMPIRICAL_DARK))) { | ||
2211 | 284 | float shadow_trans_effect_size = ((((float) SHADOW_DETECT_MAX_INTENSITY) - | ||
2212 | 285 | ((float) sh_intensity_mu)) / | ||
2213 | 286 | ((float) SHADOW_DETECT_INTENSITY_RANGE)); | ||
2214 | 287 | shadow_trans_effect_size *= SHADOW_AGGRESSIVENESS_MUL; | ||
2215 | 288 | |||
2216 | 289 | m_shadowTransform | ||
2217 | 290 | = new ShadowDetailTransformation(shadow_trans_effect_size); | ||
2218 | 291 | |||
2219 | 292 | QImage shadow_corrected_image = QImage(basis); | ||
2220 | 293 | // Can't write into indexed images, due to a limitation in Qt. | ||
2221 | 294 | if (shadow_corrected_image.format() == QImage::Format_Indexed8) | ||
2222 | 295 | shadow_corrected_image = shadow_corrected_image.convertToFormat( | ||
2223 | 296 | QImage::Format_RGB32); | ||
2224 | 297 | |||
2225 | 298 | for (int j = 0; j < shadow_corrected_image.height(); j++) { | ||
2226 | 299 | QApplication::processEvents(); | ||
2227 | 300 | |||
2228 | 301 | for (int i = 0; i < shadow_corrected_image.width(); i++) { | ||
2229 | 302 | QColor px = m_shadowTransform->transformPixel( | ||
2230 | 303 | QColor(shadow_corrected_image.pixel(i, j))); | ||
2231 | 304 | shadow_corrected_image.setPixel(i, j, px.rgb()); | ||
2232 | 305 | } | ||
2233 | 306 | } | ||
2234 | 307 | |||
2235 | 308 | m_toneExpansionTransform = new ToneExpansionTransformation( | ||
2236 | 309 | IntensityHistogram(shadow_corrected_image), 0.005f, 0.995f); | ||
2237 | 310 | |||
2238 | 311 | } else { | ||
2239 | 312 | m_toneExpansionTransform = new ToneExpansionTransformation( | ||
2240 | 313 | IntensityHistogram(basis)); | ||
2241 | 314 | } | ||
2242 | 315 | } | ||
2243 | 316 | |||
2244 | 317 | /*! | ||
2245 | 318 | * \brief AutoEnhanceTransformation::~AutoEnhanceTransformation | ||
2246 | 319 | */ | ||
2247 | 320 | AutoEnhanceTransformation::~AutoEnhanceTransformation() | ||
2248 | 321 | { | ||
2249 | 322 | if (m_shadowTransform) | ||
2250 | 323 | delete m_shadowTransform; | ||
2251 | 324 | delete m_toneExpansionTransform; | ||
2252 | 325 | } | ||
2253 | 326 | |||
2254 | 327 | /*! | ||
2255 | 328 | * \brief AutoEnhanceTransformation::transformPixel | ||
2256 | 329 | * \param pixel_color | ||
2257 | 330 | * \return | ||
2258 | 331 | */ | ||
2259 | 332 | QColor AutoEnhanceTransformation::transformPixel( | ||
2260 | 333 | const QColor& pixel_color) const | ||
2261 | 334 | { | ||
2262 | 335 | QColor px = pixel_color; | ||
2263 | 336 | |||
2264 | 337 | if (m_shadowTransform) | ||
2265 | 338 | px = m_shadowTransform->transformPixel(px); | ||
2266 | 339 | |||
2267 | 340 | px = m_toneExpansionTransform->transformPixel(px); | ||
2268 | 341 | |||
2269 | 342 | /* if tone expansion occurs, boost saturation to compensate for boosted | ||
2270 | 343 | dynamic range */ | ||
2271 | 344 | if (!m_toneExpansionTransform->isIdentity()) { | ||
2272 | 345 | int h, s, v; | ||
2273 | 346 | px.getHsv(&h, &s, &v); | ||
2274 | 347 | |||
2275 | 348 | float compensation_multiplier = | ||
2276 | 349 | (m_toneExpansionTransform->lowDiscardMass() < 0.01f) ? 1.02f : 1.10f; | ||
2277 | 350 | |||
2278 | 351 | s = (int) (((float) s) * compensation_multiplier); | ||
2279 | 352 | s = clampi(s, 0, 255); | ||
2280 | 353 | |||
2281 | 354 | px.setHsv(h, s, v); | ||
2282 | 355 | } | ||
2283 | 356 | |||
2284 | 357 | return px; | ||
2285 | 358 | } | ||
2286 | 359 | |||
2287 | 360 | bool AutoEnhanceTransformation::isIdentity() const | ||
2288 | 361 | { | ||
2289 | 362 | return false; | ||
2290 | 363 | } | ||
2291 | 364 | 0 | ||
2292 | === removed file 'src/photoeditor/imaging.h' | |||
2293 | --- src/photoeditor/imaging.h 2015-02-26 20:28:06 +0000 | |||
2294 | +++ src/photoeditor/imaging.h 1970-01-01 00:00:00 +0000 | |||
2295 | @@ -1,169 +0,0 @@ | |||
2296 | 1 | /* | ||
2297 | 2 | * Copyright (C) 2012 Canonical Ltd | ||
2298 | 3 | * | ||
2299 | 4 | * This program is free software: you can redistribute it and/or modify | ||
2300 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
2301 | 6 | * published by the Free Software Foundation. | ||
2302 | 7 | * | ||
2303 | 8 | * This program is distributed in the hope that it will be useful, | ||
2304 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2305 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2306 | 11 | * GNU General Public License for more details. | ||
2307 | 12 | * | ||
2308 | 13 | * You should have received a copy of the GNU General Public License | ||
2309 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2310 | 15 | * | ||
2311 | 16 | * Authors: | ||
2312 | 17 | * Lucas Beeler <lucas@yorba.org> | ||
2313 | 18 | */ | ||
2314 | 19 | |||
2315 | 20 | #ifndef GALLERY_UTIL_IMAGING_H_ | ||
2316 | 21 | #define GALLERY_UTIL_IMAGING_H_ | ||
2317 | 22 | |||
2318 | 23 | #include <QColor> | ||
2319 | 24 | #include <QImage> | ||
2320 | 25 | #include <QVector4D> | ||
2321 | 26 | |||
2322 | 27 | /*! | ||
2323 | 28 | * \brief clampi | ||
2324 | 29 | * \param i | ||
2325 | 30 | * \param min | ||
2326 | 31 | * \param max | ||
2327 | 32 | * \return | ||
2328 | 33 | */ | ||
2329 | 34 | inline int clampi(int i, int min, int max) { | ||
2330 | 35 | return (i < min) ? min : ((i > max) ? max : i); | ||
2331 | 36 | } | ||
2332 | 37 | |||
2333 | 38 | /*! | ||
2334 | 39 | * \brief clampf | ||
2335 | 40 | * \param x | ||
2336 | 41 | * \param min | ||
2337 | 42 | * \param max | ||
2338 | 43 | * \return | ||
2339 | 44 | */ | ||
2340 | 45 | inline float clampf(float x, float min, float max) { | ||
2341 | 46 | return (x < min) ? min : ((x > max) ? max : x); | ||
2342 | 47 | } | ||
2343 | 48 | |||
2344 | 49 | /*! | ||
2345 | 50 | * \brief The HSVTransformation class | ||
2346 | 51 | */ | ||
2347 | 52 | class HSVTransformation | ||
2348 | 53 | { | ||
2349 | 54 | public: | ||
2350 | 55 | HSVTransformation() { } | ||
2351 | 56 | virtual ~HSVTransformation() { } | ||
2352 | 57 | |||
2353 | 58 | virtual QColor transformPixel(const QColor& pixel_color) const; | ||
2354 | 59 | virtual bool isIdentity() const = 0; | ||
2355 | 60 | |||
2356 | 61 | protected: | ||
2357 | 62 | int remap_table_[256]; | ||
2358 | 63 | }; | ||
2359 | 64 | |||
2360 | 65 | /*! | ||
2361 | 66 | * \brief The IntensityHistogram class | ||
2362 | 67 | */ | ||
2363 | 68 | class IntensityHistogram | ||
2364 | 69 | { | ||
2365 | 70 | public: | ||
2366 | 71 | IntensityHistogram(const QImage& basis_image); | ||
2367 | 72 | virtual ~IntensityHistogram() { } | ||
2368 | 73 | |||
2369 | 74 | float getCumulativeProbability(int level); | ||
2370 | 75 | |||
2371 | 76 | private: | ||
2372 | 77 | int m_counts[256]; | ||
2373 | 78 | float m_probabilities[256]; | ||
2374 | 79 | float m_cumulativeProbabilities[256]; | ||
2375 | 80 | }; | ||
2376 | 81 | |||
2377 | 82 | /*! | ||
2378 | 83 | * \brief The ToneExpansionTransformation class | ||
2379 | 84 | */ | ||
2380 | 85 | class ToneExpansionTransformation : public virtual HSVTransformation | ||
2381 | 86 | { | ||
2382 | 87 | static const float DEFAULT_LOW_DISCARD_MASS; | ||
2383 | 88 | static const float DEFAULT_HIGH_DISCARD_MASS; | ||
2384 | 89 | |||
2385 | 90 | public: | ||
2386 | 91 | ToneExpansionTransformation(IntensityHistogram h, float lowDiscardMass = | ||
2387 | 92 | -1.0f, float highDiscardMass = -1.0f); | ||
2388 | 93 | virtual ~ToneExpansionTransformation() { } | ||
2389 | 94 | |||
2390 | 95 | bool isIdentity() const; | ||
2391 | 96 | |||
2392 | 97 | float lowDiscardMass() const; | ||
2393 | 98 | float highDiscardMass() const; | ||
2394 | 99 | |||
2395 | 100 | private: | ||
2396 | 101 | void buildRemapTable(); | ||
2397 | 102 | |||
2398 | 103 | int m_lowKink; | ||
2399 | 104 | int m_highKink; | ||
2400 | 105 | float m_lowDiscardMass; | ||
2401 | 106 | float m_highDiscardMass; | ||
2402 | 107 | }; | ||
2403 | 108 | |||
2404 | 109 | /*! | ||
2405 | 110 | * \brief The HermiteGammaApproximationFunction class | ||
2406 | 111 | */ | ||
2407 | 112 | class HermiteGammaApproximationFunction | ||
2408 | 113 | { | ||
2409 | 114 | public: | ||
2410 | 115 | HermiteGammaApproximationFunction(float user_interval_upper); | ||
2411 | 116 | virtual ~HermiteGammaApproximationFunction() { } | ||
2412 | 117 | |||
2413 | 118 | float evaluate(float x); | ||
2414 | 119 | |||
2415 | 120 | private: | ||
2416 | 121 | float m_xScale; | ||
2417 | 122 | float m_nonzeroIntervalUpper; | ||
2418 | 123 | }; | ||
2419 | 124 | |||
2420 | 125 | /*! | ||
2421 | 126 | * \brief The ShadowDetailTransformation class | ||
2422 | 127 | */ | ||
2423 | 128 | class ShadowDetailTransformation : public virtual HSVTransformation | ||
2424 | 129 | { | ||
2425 | 130 | static const float MAX_EFFECT_SHIFT; | ||
2426 | 131 | static const float MIN_TONAL_WIDTH; | ||
2427 | 132 | static const float MAX_TONAL_WIDTH; | ||
2428 | 133 | static const float TONAL_WIDTH; | ||
2429 | 134 | |||
2430 | 135 | public: | ||
2431 | 136 | ShadowDetailTransformation(float intensity); | ||
2432 | 137 | |||
2433 | 138 | bool isIdentity() const; | ||
2434 | 139 | |||
2435 | 140 | private: | ||
2436 | 141 | float m_intensity; | ||
2437 | 142 | }; | ||
2438 | 143 | |||
2439 | 144 | /*! | ||
2440 | 145 | * \brief The AutoEnhanceTransformation class | ||
2441 | 146 | */ | ||
2442 | 147 | class AutoEnhanceTransformation : public virtual HSVTransformation | ||
2443 | 148 | { | ||
2444 | 149 | static const int SHADOW_DETECT_MIN_INTENSITY; | ||
2445 | 150 | static const int SHADOW_DETECT_MAX_INTENSITY; | ||
2446 | 151 | static const int SHADOW_DETECT_INTENSITY_RANGE; | ||
2447 | 152 | static const int EMPIRICAL_DARK; | ||
2448 | 153 | static const float SHADOW_AGGRESSIVENESS_MUL; | ||
2449 | 154 | |||
2450 | 155 | public: | ||
2451 | 156 | AutoEnhanceTransformation(const QImage& basis_image); | ||
2452 | 157 | virtual ~AutoEnhanceTransformation(); | ||
2453 | 158 | |||
2454 | 159 | QColor transformPixel(const QColor& pixel_color) const; | ||
2455 | 160 | bool isIdentity() const; | ||
2456 | 161 | |||
2457 | 162 | private: | ||
2458 | 163 | ShadowDetailTransformation* m_shadowTransform; | ||
2459 | 164 | ToneExpansionTransformation* m_toneExpansionTransform; | ||
2460 | 165 | }; | ||
2461 | 166 | |||
2462 | 167 | |||
2463 | 168 | #endif // GALLERY_UTIL_IMAGING_H_ | ||
2464 | 169 | |||
2465 | 170 | 0 | ||
2466 | === removed file 'src/photoeditor/orientation.cpp' | |||
2467 | --- src/photoeditor/orientation.cpp 2015-02-26 20:28:06 +0000 | |||
2468 | +++ src/photoeditor/orientation.cpp 1970-01-01 00:00:00 +0000 | |||
2469 | @@ -1,152 +0,0 @@ | |||
2470 | 1 | /* | ||
2471 | 2 | * Copyright (C) 2011 Canonical Ltd | ||
2472 | 3 | * | ||
2473 | 4 | * This program is free software: you can redistribute it and/or modify | ||
2474 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
2475 | 6 | * published by the Free Software Foundation. | ||
2476 | 7 | * | ||
2477 | 8 | * This program is distributed in the hope that it will be useful, | ||
2478 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2479 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2480 | 11 | * GNU General Public License for more details. | ||
2481 | 12 | * | ||
2482 | 13 | * You should have received a copy of the GNU General Public License | ||
2483 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2484 | 15 | * | ||
2485 | 16 | * Authors: | ||
2486 | 17 | * Lucas Beeler <lucas@yorba.org> | ||
2487 | 18 | */ | ||
2488 | 19 | |||
2489 | 20 | #include <cstdio> | ||
2490 | 21 | |||
2491 | 22 | #include "orientation.h" | ||
2492 | 23 | |||
2493 | 24 | /*! | ||
2494 | 25 | * \brief OrientationCorrection::fromOrientation | ||
2495 | 26 | * \param o | ||
2496 | 27 | * \return | ||
2497 | 28 | */ | ||
2498 | 29 | OrientationCorrection OrientationCorrection::fromOrientation(Orientation o) | ||
2499 | 30 | { | ||
2500 | 31 | double rotation_angle = 0.0; | ||
2501 | 32 | double horizontal_scale_factor = 1.0; | ||
2502 | 33 | |||
2503 | 34 | switch (o) { | ||
2504 | 35 | case TOP_RIGHT_ORIGIN: | ||
2505 | 36 | horizontal_scale_factor = -1.0; | ||
2506 | 37 | break; | ||
2507 | 38 | |||
2508 | 39 | case BOTTOM_RIGHT_ORIGIN: | ||
2509 | 40 | rotation_angle = 180.0; | ||
2510 | 41 | break; | ||
2511 | 42 | |||
2512 | 43 | case BOTTOM_LEFT_ORIGIN: | ||
2513 | 44 | horizontal_scale_factor = -1.0; | ||
2514 | 45 | rotation_angle = 180.0; | ||
2515 | 46 | break; | ||
2516 | 47 | |||
2517 | 48 | case LEFT_TOP_ORIGIN: | ||
2518 | 49 | horizontal_scale_factor = -1.0; | ||
2519 | 50 | rotation_angle = -90.0; | ||
2520 | 51 | break; | ||
2521 | 52 | |||
2522 | 53 | case RIGHT_TOP_ORIGIN: | ||
2523 | 54 | rotation_angle = 90.0; | ||
2524 | 55 | break; | ||
2525 | 56 | |||
2526 | 57 | case RIGHT_BOTTOM_ORIGIN: | ||
2527 | 58 | horizontal_scale_factor = -1.0; | ||
2528 | 59 | rotation_angle = 90.0; | ||
2529 | 60 | break; | ||
2530 | 61 | |||
2531 | 62 | case LEFT_BOTTOM_ORIGIN: | ||
2532 | 63 | rotation_angle = -90.0; | ||
2533 | 64 | break; | ||
2534 | 65 | |||
2535 | 66 | default: | ||
2536 | 67 | ; // do nothing | ||
2537 | 68 | break; | ||
2538 | 69 | } | ||
2539 | 70 | |||
2540 | 71 | return OrientationCorrection(rotation_angle, horizontal_scale_factor); | ||
2541 | 72 | } | ||
2542 | 73 | |||
2543 | 74 | /*! | ||
2544 | 75 | * \brief OrientationCorrection::identity | ||
2545 | 76 | * \return | ||
2546 | 77 | */ | ||
2547 | 78 | OrientationCorrection OrientationCorrection::identity() | ||
2548 | 79 | { | ||
2549 | 80 | return OrientationCorrection(0.0, 1.0); | ||
2550 | 81 | } | ||
2551 | 82 | |||
2552 | 83 | /*! | ||
2553 | 84 | * \brief OrientationCorrection::rotateOrientation | ||
2554 | 85 | * \param orientation | ||
2555 | 86 | * \param left | ||
2556 | 87 | * \return | ||
2557 | 88 | */ | ||
2558 | 89 | Orientation OrientationCorrection::rotateOrientation(Orientation orientation, bool left) | ||
2559 | 90 | { | ||
2560 | 91 | QVector<Orientation> sequence_a; | ||
2561 | 92 | QVector<Orientation> sequence_b; | ||
2562 | 93 | sequence_a << | ||
2563 | 94 | TOP_LEFT_ORIGIN << LEFT_BOTTOM_ORIGIN << BOTTOM_RIGHT_ORIGIN << RIGHT_TOP_ORIGIN; | ||
2564 | 95 | sequence_b << | ||
2565 | 96 | TOP_RIGHT_ORIGIN << RIGHT_BOTTOM_ORIGIN << BOTTOM_LEFT_ORIGIN << LEFT_TOP_ORIGIN; | ||
2566 | 97 | |||
2567 | 98 | const QVector<Orientation>& sequence = ( | ||
2568 | 99 | sequence_a.contains(orientation) ? sequence_a : sequence_b); | ||
2569 | 100 | |||
2570 | 101 | int current = sequence.indexOf(orientation); | ||
2571 | 102 | int jump = (left ? 1 : sequence.count() - 1); | ||
2572 | 103 | int next = (current + jump) % sequence.count(); | ||
2573 | 104 | |||
2574 | 105 | return sequence[next]; | ||
2575 | 106 | } | ||
2576 | 107 | |||
2577 | 108 | /*! | ||
2578 | 109 | * \brief OrientationCorrection::toTransform | ||
2579 | 110 | * Returns the correction as a QTransform. | ||
2580 | 111 | * \return Returns the correction as a QTransform. | ||
2581 | 112 | */ | ||
2582 | 113 | QTransform OrientationCorrection::toTransform() const | ||
2583 | 114 | { | ||
2584 | 115 | QTransform result; | ||
2585 | 116 | result.scale(m_horizontalScaleFactor, 1.0); | ||
2586 | 117 | result.rotate(m_rotationAngle); | ||
2587 | 118 | |||
2588 | 119 | return result; | ||
2589 | 120 | } | ||
2590 | 121 | |||
2591 | 122 | /*! | ||
2592 | 123 | * \brief OrientationCorrection::isFlippedFrom | ||
2593 | 124 | * Returns whether the two orientations are flipped relative to each other. | ||
2594 | 125 | * Ignores rotation_angle; only checks horizontal_scale_factor_. | ||
2595 | 126 | * \param other | ||
2596 | 127 | * \return | ||
2597 | 128 | */ | ||
2598 | 129 | bool OrientationCorrection::isFlippedFrom( | ||
2599 | 130 | const OrientationCorrection& other) const | ||
2600 | 131 | { | ||
2601 | 132 | return (m_horizontalScaleFactor != other.m_horizontalScaleFactor); | ||
2602 | 133 | } | ||
2603 | 134 | |||
2604 | 135 | /*! | ||
2605 | 136 | * \brief OrientationCorrection::getNormalizedRotationDifference | ||
2606 | 137 | * Returns the rotation difference in degrees (this - other), normalized to | ||
2607 | 138 | * 0, 90, 180, or 270. Ignores the horizontal_scale_factor_. | ||
2608 | 139 | * \param other | ||
2609 | 140 | * \return | ||
2610 | 141 | */ | ||
2611 | 142 | int OrientationCorrection::getNormalizedRotationDifference( | ||
2612 | 143 | const OrientationCorrection& other) const | ||
2613 | 144 | { | ||
2614 | 145 | int degrees_rotation = (int)m_rotationAngle - (int)other.m_rotationAngle; | ||
2615 | 146 | if (degrees_rotation < 0) | ||
2616 | 147 | degrees_rotation += 360; | ||
2617 | 148 | |||
2618 | 149 | Q_ASSERT(degrees_rotation == 0 || degrees_rotation == 90 || | ||
2619 | 150 | degrees_rotation == 180 || degrees_rotation == 270); | ||
2620 | 151 | return degrees_rotation; | ||
2621 | 152 | } | ||
2622 | 153 | 0 | ||
2623 | === removed file 'src/photoeditor/orientation.h' | |||
2624 | --- src/photoeditor/orientation.h 2015-02-26 20:28:06 +0000 | |||
2625 | +++ src/photoeditor/orientation.h 1970-01-01 00:00:00 +0000 | |||
2626 | @@ -1,64 +0,0 @@ | |||
2627 | 1 | /* | ||
2628 | 2 | * Copyright (C) 2011 Canonical Ltd | ||
2629 | 3 | * | ||
2630 | 4 | * This program is free software: you can redistribute it and/or modify | ||
2631 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
2632 | 6 | * published by the Free Software Foundation. | ||
2633 | 7 | * | ||
2634 | 8 | * This program is distributed in the hope that it will be useful, | ||
2635 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2636 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2637 | 11 | * GNU General Public License for more details. | ||
2638 | 12 | * | ||
2639 | 13 | * You should have received a copy of the GNU General Public License | ||
2640 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2641 | 15 | * | ||
2642 | 16 | * Authors: | ||
2643 | 17 | * Lucas Beeler <lucas@yorba.org> | ||
2644 | 18 | */ | ||
2645 | 19 | |||
2646 | 20 | #ifndef GALLERY_ORIENTATION_H_ | ||
2647 | 21 | #define GALLERY_ORIENTATION_H_ | ||
2648 | 22 | |||
2649 | 23 | #include <QTransform> | ||
2650 | 24 | |||
2651 | 25 | enum Orientation { | ||
2652 | 26 | ORIGINAL_ORIENTATION = 0, | ||
2653 | 27 | MIN_ORIENTATION = 1, | ||
2654 | 28 | TOP_LEFT_ORIGIN = 1, | ||
2655 | 29 | TOP_RIGHT_ORIGIN = 2, | ||
2656 | 30 | BOTTOM_RIGHT_ORIGIN = 3, | ||
2657 | 31 | BOTTOM_LEFT_ORIGIN = 4, | ||
2658 | 32 | LEFT_TOP_ORIGIN = 5, | ||
2659 | 33 | RIGHT_TOP_ORIGIN = 6, | ||
2660 | 34 | RIGHT_BOTTOM_ORIGIN = 7, | ||
2661 | 35 | LEFT_BOTTOM_ORIGIN = 8, | ||
2662 | 36 | MAX_ORIENTATION = 8 | ||
2663 | 37 | }; | ||
2664 | 38 | |||
2665 | 39 | /*! | ||
2666 | 40 | * \brief The OrientationCorrection struct | ||
2667 | 41 | */ | ||
2668 | 42 | class OrientationCorrection | ||
2669 | 43 | { | ||
2670 | 44 | public: | ||
2671 | 45 | static OrientationCorrection fromOrientation(Orientation o); | ||
2672 | 46 | static OrientationCorrection identity(); | ||
2673 | 47 | static Orientation rotateOrientation(Orientation orientation, bool left); | ||
2674 | 48 | |||
2675 | 49 | QTransform toTransform() const; | ||
2676 | 50 | |||
2677 | 51 | bool isFlippedFrom(const OrientationCorrection& other) const; | ||
2678 | 52 | int getNormalizedRotationDifference(const OrientationCorrection& other) const; | ||
2679 | 53 | |||
2680 | 54 | private: | ||
2681 | 55 | OrientationCorrection(double rotation_angle, double horizontal_scale_factor) | ||
2682 | 56 | : m_rotationAngle(rotation_angle), | ||
2683 | 57 | m_horizontalScaleFactor(horizontal_scale_factor) { } | ||
2684 | 58 | |||
2685 | 59 | const double m_rotationAngle; | ||
2686 | 60 | const double m_horizontalScaleFactor; | ||
2687 | 61 | }; | ||
2688 | 62 | |||
2689 | 63 | |||
2690 | 64 | #endif // GALLERY_ORIENTATION_H_ | ||
2691 | 65 | 0 | ||
2692 | === removed file 'src/photoeditor/photo-caches.cpp' | |||
2693 | --- src/photoeditor/photo-caches.cpp 2015-02-26 20:28:06 +0000 | |||
2694 | +++ src/photoeditor/photo-caches.cpp 1970-01-01 00:00:00 +0000 | |||
2695 | @@ -1,183 +0,0 @@ | |||
2696 | 1 | /* | ||
2697 | 2 | * Copyright (C) 2012 Canonical Ltd | ||
2698 | 3 | * | ||
2699 | 4 | * This program is free software: you can redistribute it and/or modify | ||
2700 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
2701 | 6 | * published by the Free Software Foundation. | ||
2702 | 7 | * | ||
2703 | 8 | * This program is distributed in the hope that it will be useful, | ||
2704 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2705 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2706 | 11 | * GNU General Public License for more details. | ||
2707 | 12 | * | ||
2708 | 13 | * You should have received a copy of the GNU General Public License | ||
2709 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2710 | 15 | * | ||
2711 | 16 | * Authors: | ||
2712 | 17 | * Charles Lindsay <chaz@yorba.org> | ||
2713 | 18 | */ | ||
2714 | 19 | |||
2715 | 20 | #include "photo-caches.h" | ||
2716 | 21 | |||
2717 | 22 | #include <QDir> | ||
2718 | 23 | #include <utime.h> | ||
2719 | 24 | |||
2720 | 25 | const QString PhotoCaches::ORIGINAL_DIR = ".original"; | ||
2721 | 26 | const QString PhotoCaches::ENHANCED_DIR = ".enhanced"; | ||
2722 | 27 | |||
2723 | 28 | /*! | ||
2724 | 29 | * \brief PhotoCaches::PhotoCaches | ||
2725 | 30 | * \param file | ||
2726 | 31 | */ | ||
2727 | 32 | PhotoCaches::PhotoCaches(const QFileInfo& file) : m_file(file), | ||
2728 | 33 | m_originalFile(file.dir(), | ||
2729 | 34 | QString("%1/%2").arg(ORIGINAL_DIR).arg(file.fileName())), | ||
2730 | 35 | m_enhancedFile(file.dir(), | ||
2731 | 36 | QString("%1/%2").arg(ENHANCED_DIR).arg(file.fileName())) | ||
2732 | 37 | { | ||
2733 | 38 | // We always want our file checks to hit the disk. | ||
2734 | 39 | m_file.setCaching(false); | ||
2735 | 40 | m_originalFile.setCaching(false); | ||
2736 | 41 | m_enhancedFile.setCaching(false); | ||
2737 | 42 | } | ||
2738 | 43 | |||
2739 | 44 | /*! | ||
2740 | 45 | * \brief PhotoCaches::hasCachedOriginal | ||
2741 | 46 | * \return | ||
2742 | 47 | */ | ||
2743 | 48 | bool PhotoCaches::hasCachedOriginal() const | ||
2744 | 49 | { | ||
2745 | 50 | return m_originalFile.exists(); | ||
2746 | 51 | } | ||
2747 | 52 | |||
2748 | 53 | /*! | ||
2749 | 54 | * \brief PhotoCaches::hasCachedEnhanced | ||
2750 | 55 | * \return | ||
2751 | 56 | */ | ||
2752 | 57 | bool PhotoCaches::hasCachedEnhanced() const | ||
2753 | 58 | { | ||
2754 | 59 | return m_enhancedFile.exists(); | ||
2755 | 60 | } | ||
2756 | 61 | |||
2757 | 62 | /*! | ||
2758 | 63 | * \brief PhotoCaches::originalFile | ||
2759 | 64 | * \return | ||
2760 | 65 | */ | ||
2761 | 66 | const QFileInfo& PhotoCaches::originalFile() const | ||
2762 | 67 | { | ||
2763 | 68 | return m_originalFile; | ||
2764 | 69 | } | ||
2765 | 70 | |||
2766 | 71 | /*! | ||
2767 | 72 | * \brief PhotoCaches::enhancedFile | ||
2768 | 73 | * \return | ||
2769 | 74 | */ | ||
2770 | 75 | const QFileInfo& PhotoCaches::enhancedFile() const | ||
2771 | 76 | { | ||
2772 | 77 | return m_enhancedFile; | ||
2773 | 78 | } | ||
2774 | 79 | |||
2775 | 80 | /*! | ||
2776 | 81 | * \brief PhotoCaches::pristineFile | ||
2777 | 82 | * Returns original_file() if it exists; otherwise, returns the file passed | ||
2778 | 83 | * to the constructor. | ||
2779 | 84 | * \return | ||
2780 | 85 | */ | ||
2781 | 86 | const QFileInfo& PhotoCaches::pristineFile() const | ||
2782 | 87 | { | ||
2783 | 88 | return (hasCachedOriginal() ? m_originalFile : m_file); | ||
2784 | 89 | } | ||
2785 | 90 | |||
2786 | 91 | /*! | ||
2787 | 92 | * \brief PhotoCaches::cacheOriginal | ||
2788 | 93 | * Moves the pristine file into .original so we don't mess it up. Note that | ||
2789 | 94 | * this potentially removes the main file, so it must be followed by a copy | ||
2790 | 95 | * from original (or elsewhere) back to the file. | ||
2791 | 96 | * \return | ||
2792 | 97 | */ | ||
2793 | 98 | bool PhotoCaches::cacheOriginal() | ||
2794 | 99 | { | ||
2795 | 100 | if (hasCachedOriginal()) { | ||
2796 | 101 | return true; | ||
2797 | 102 | } | ||
2798 | 103 | |||
2799 | 104 | m_file.dir().mkdir(ORIGINAL_DIR); | ||
2800 | 105 | |||
2801 | 106 | return rename(m_file, m_originalFile); | ||
2802 | 107 | } | ||
2803 | 108 | |||
2804 | 109 | /*! | ||
2805 | 110 | * \brief PhotoCaches::restoreOriginal | ||
2806 | 111 | * Moves the file out of .original, overwriting the main file. Note that | ||
2807 | 112 | * this removes the .original file. | ||
2808 | 113 | * \return | ||
2809 | 114 | */ | ||
2810 | 115 | bool PhotoCaches::restoreOriginal() | ||
2811 | 116 | { | ||
2812 | 117 | if (!hasCachedOriginal()) { | ||
2813 | 118 | return true; | ||
2814 | 119 | } | ||
2815 | 120 | |||
2816 | 121 | remove(m_file); | ||
2817 | 122 | // touch the file so that the thumbnails will correctly regenerate | ||
2818 | 123 | utime(m_originalFile.absoluteFilePath().toUtf8(), NULL); | ||
2819 | 124 | return rename(m_originalFile, m_file); | ||
2820 | 125 | } | ||
2821 | 126 | |||
2822 | 127 | /*! | ||
2823 | 128 | * \brief PhotoCaches::cacheEnhancedFromOriginal | ||
2824 | 129 | * Copies the file in .original to .enhanced so it can then be enhanced. | ||
2825 | 130 | * \return | ||
2826 | 131 | */ | ||
2827 | 132 | bool PhotoCaches::cacheEnhancedFromOriginal() | ||
2828 | 133 | { | ||
2829 | 134 | m_file.dir().mkdir(ENHANCED_DIR); | ||
2830 | 135 | |||
2831 | 136 | // If called subsequently, the previously cached version is replaced. | ||
2832 | 137 | remove(m_enhancedFile); | ||
2833 | 138 | return copy(pristineFile(), m_enhancedFile); | ||
2834 | 139 | } | ||
2835 | 140 | |||
2836 | 141 | /*! | ||
2837 | 142 | * \brief PhotoCaches::overwriteFromCache | ||
2838 | 143 | * Tries to overwrite the file from one of its cached versions. | ||
2839 | 144 | * \param preferEnhanced | ||
2840 | 145 | * \return | ||
2841 | 146 | */ | ||
2842 | 147 | bool PhotoCaches::overwriteFromCache(bool preferEnhanced) | ||
2843 | 148 | { | ||
2844 | 149 | if (preferEnhanced && hasCachedEnhanced()) { | ||
2845 | 150 | remove(m_file); | ||
2846 | 151 | return copy(m_enhancedFile, m_file); | ||
2847 | 152 | } else if (hasCachedOriginal()) { | ||
2848 | 153 | remove(m_file); | ||
2849 | 154 | return copy(m_originalFile, m_file); | ||
2850 | 155 | } else { | ||
2851 | 156 | return true; | ||
2852 | 157 | } | ||
2853 | 158 | } | ||
2854 | 159 | |||
2855 | 160 | /*! | ||
2856 | 161 | * \brief PhotoCaches::discardCachedOriginal | ||
2857 | 162 | */ | ||
2858 | 163 | void PhotoCaches::discardCachedOriginal() | ||
2859 | 164 | { | ||
2860 | 165 | remove(m_originalFile); | ||
2861 | 166 | } | ||
2862 | 167 | |||
2863 | 168 | /*! | ||
2864 | 169 | * \brief PhotoCaches::discardCachedEnhanced | ||
2865 | 170 | */ | ||
2866 | 171 | void PhotoCaches::discardCachedEnhanced() | ||
2867 | 172 | { | ||
2868 | 173 | remove(m_enhancedFile); | ||
2869 | 174 | } | ||
2870 | 175 | |||
2871 | 176 | /*! | ||
2872 | 177 | * \brief PhotoCaches::discardAll | ||
2873 | 178 | */ | ||
2874 | 179 | void PhotoCaches::discardAll() | ||
2875 | 180 | { | ||
2876 | 181 | discardCachedOriginal(); | ||
2877 | 182 | discardCachedEnhanced(); | ||
2878 | 183 | } | ||
2879 | 184 | 0 | ||
2880 | === removed file 'src/photoeditor/photo-caches.h' | |||
2881 | --- src/photoeditor/photo-caches.h 2015-02-26 20:28:06 +0000 | |||
2882 | +++ src/photoeditor/photo-caches.h 1970-01-01 00:00:00 +0000 | |||
2883 | @@ -1,76 +0,0 @@ | |||
2884 | 1 | /* | ||
2885 | 2 | * Copyright (C) 2012 Canonical Ltd | ||
2886 | 3 | * | ||
2887 | 4 | * This program is free software: you can redistribute it and/or modify | ||
2888 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
2889 | 6 | * published by the Free Software Foundation. | ||
2890 | 7 | * | ||
2891 | 8 | * This program is distributed in the hope that it will be useful, | ||
2892 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2893 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2894 | 11 | * GNU General Public License for more details. | ||
2895 | 12 | * | ||
2896 | 13 | * You should have received a copy of the GNU General Public License | ||
2897 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2898 | 15 | * | ||
2899 | 16 | * Authors: | ||
2900 | 17 | * Charles Lindsay <chaz@yorba.org> | ||
2901 | 18 | */ | ||
2902 | 19 | |||
2903 | 20 | #ifndef GALLERY_PHOTO_CACHES_H_ | ||
2904 | 21 | #define GALLERY_PHOTO_CACHES_H_ | ||
2905 | 22 | |||
2906 | 23 | #include <QFile> | ||
2907 | 24 | #include <QFileInfo> | ||
2908 | 25 | #include <QString> | ||
2909 | 26 | |||
2910 | 27 | /*! | ||
2911 | 28 | * \brief The PhotoCaches class | ||
2912 | 29 | * | ||
2913 | 30 | * An abstraction around the various files we keep in addition to the photo | ||
2914 | 31 | * file itself: the original, the pristine version of the file without any | ||
2915 | 32 | * applied edits; and the enhanced, a version of the original with auto-enhance | ||
2916 | 33 | * applied to it (necessary because of how slow auto-enhance is). | ||
2917 | 34 | */ | ||
2918 | 35 | class PhotoCaches | ||
2919 | 36 | { | ||
2920 | 37 | public: | ||
2921 | 38 | static const QString ORIGINAL_DIR; | ||
2922 | 39 | static const QString ENHANCED_DIR; | ||
2923 | 40 | |||
2924 | 41 | PhotoCaches(); // FIXME: remove this class and only deal with a stack of temporary rollback files | ||
2925 | 42 | PhotoCaches(const QFileInfo& file); | ||
2926 | 43 | |||
2927 | 44 | bool hasCachedOriginal() const; | ||
2928 | 45 | bool hasCachedEnhanced() const; | ||
2929 | 46 | |||
2930 | 47 | const QFileInfo& originalFile() const; | ||
2931 | 48 | const QFileInfo& enhancedFile() const; | ||
2932 | 49 | const QFileInfo& pristineFile() const; | ||
2933 | 50 | |||
2934 | 51 | bool cacheOriginal(); | ||
2935 | 52 | bool restoreOriginal(); | ||
2936 | 53 | bool cacheEnhancedFromOriginal(); | ||
2937 | 54 | bool overwriteFromCache(bool preferEnhanced); | ||
2938 | 55 | |||
2939 | 56 | void discardCachedOriginal(); | ||
2940 | 57 | void discardCachedEnhanced(); | ||
2941 | 58 | void discardAll(); | ||
2942 | 59 | |||
2943 | 60 | private: | ||
2944 | 61 | static bool remove(const QFileInfo& file) { | ||
2945 | 62 | return QFile::remove(file.filePath()); | ||
2946 | 63 | } | ||
2947 | 64 | static bool rename(const QFileInfo& oldName, const QFileInfo& newName) { | ||
2948 | 65 | return QFile::rename(oldName.filePath(), newName.filePath()); | ||
2949 | 66 | } | ||
2950 | 67 | static bool copy(const QFileInfo& oldName, const QFileInfo& newName) { | ||
2951 | 68 | return QFile::copy(oldName.filePath(), newName.filePath()); | ||
2952 | 69 | } | ||
2953 | 70 | |||
2954 | 71 | QFileInfo m_file; | ||
2955 | 72 | QFileInfo m_originalFile; | ||
2956 | 73 | QFileInfo m_enhancedFile; | ||
2957 | 74 | }; | ||
2958 | 75 | |||
2959 | 76 | #endif | ||
2960 | 77 | 0 | ||
2961 | === removed file 'src/photoeditor/photo-data.cpp' | |||
2962 | --- src/photoeditor/photo-data.cpp 2015-02-26 20:28:06 +0000 | |||
2963 | +++ src/photoeditor/photo-data.cpp 1970-01-01 00:00:00 +0000 | |||
2964 | @@ -1,275 +0,0 @@ | |||
2965 | 1 | /* | ||
2966 | 2 | * Copyright (C) 2011-2014 Canonical Ltd | ||
2967 | 3 | * | ||
2968 | 4 | * This program is free software: you can redistribute it and/or modify | ||
2969 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
2970 | 6 | * published by the Free Software Foundation. | ||
2971 | 7 | * | ||
2972 | 8 | * This program is distributed in the hope that it will be useful, | ||
2973 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2974 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2975 | 11 | * GNU General Public License for more details. | ||
2976 | 12 | * | ||
2977 | 13 | * You should have received a copy of the GNU General Public License | ||
2978 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2979 | 15 | * | ||
2980 | 16 | * Authors: | ||
2981 | 17 | * Jim Nelson <jim@yorba.org> | ||
2982 | 18 | * Lucas Beeler <lucas@yorba.org> | ||
2983 | 19 | * Charles Lindsay <chaz@yorba.org> | ||
2984 | 20 | * Eric Gregory <eric@yorba.org> | ||
2985 | 21 | * Clint Rogers <clinton@yorba.org> | ||
2986 | 22 | * Ugo Riboni <ugo.riboni@canonical.com> | ||
2987 | 23 | */ | ||
2988 | 24 | |||
2989 | 25 | #include "photo-data.h" | ||
2990 | 26 | #include "photo-edit-command.h" | ||
2991 | 27 | #include "photo-edit-thread.h" | ||
2992 | 28 | |||
2993 | 29 | // medialoader | ||
2994 | 30 | #include "photo-metadata.h" | ||
2995 | 31 | |||
2996 | 32 | // util | ||
2997 | 33 | #include "imaging.h" | ||
2998 | 34 | |||
2999 | 35 | #include <QApplication> | ||
3000 | 36 | #include <QDebug> | ||
3001 | 37 | #include <QDir> | ||
3002 | 38 | #include <QFileInfo> | ||
3003 | 39 | #include <QImage> | ||
3004 | 40 | #include <QImageReader> | ||
3005 | 41 | #include <QImageWriter> | ||
3006 | 42 | #include <QStack> | ||
3007 | 43 | #include <QStandardPaths> | ||
3008 | 44 | |||
3009 | 45 | /*! | ||
3010 | 46 | * \brief Photo::isValid | ||
3011 | 47 | * \param file | ||
3012 | 48 | * \return | ||
3013 | 49 | */ | ||
3014 | 50 | bool PhotoData::isValid(const QFileInfo& file) | ||
3015 | 51 | { | ||
3016 | 52 | QImageReader reader(file.filePath()); | ||
3017 | 53 | QByteArray format = reader.format(); | ||
3018 | 54 | |||
3019 | 55 | if (QString(format).toLower() == "tiff") { | ||
3020 | 56 | // QImageReader.canRead() will detect some raw files as readable TIFFs, | ||
3021 | 57 | // though QImage will fail to load them. | ||
3022 | 58 | QString extension = file.suffix().toLower(); | ||
3023 | 59 | if (extension != "tiff" && extension != "tif") | ||
3024 | 60 | return false; | ||
3025 | 61 | } | ||
3026 | 62 | |||
3027 | 63 | PhotoMetadata* tmp = PhotoMetadata::fromFile(file); | ||
3028 | 64 | if (tmp == NULL) | ||
3029 | 65 | return false; | ||
3030 | 66 | |||
3031 | 67 | delete tmp; | ||
3032 | 68 | return reader.canRead() && | ||
3033 | 69 | QImageWriter::supportedImageFormats().contains(reader.format()); | ||
3034 | 70 | } | ||
3035 | 71 | |||
3036 | 72 | /*! | ||
3037 | 73 | * \brief Photo::Photo | ||
3038 | 74 | * \param file | ||
3039 | 75 | */ | ||
3040 | 76 | PhotoData::PhotoData() | ||
3041 | 77 | : QObject(), | ||
3042 | 78 | m_editThread(0), | ||
3043 | 79 | m_busy(false), | ||
3044 | 80 | m_orientation(TOP_LEFT_ORIGIN) | ||
3045 | 81 | { | ||
3046 | 82 | } | ||
3047 | 83 | |||
3048 | 84 | void PhotoData::setPath(QString path) | ||
3049 | 85 | { | ||
3050 | 86 | if (QFileInfo(path).absoluteFilePath() != m_file.absoluteFilePath()) { | ||
3051 | 87 | QFileInfo newFile(path); | ||
3052 | 88 | if (newFile.exists() && newFile.isFile()) { | ||
3053 | 89 | QByteArray format = QImageReader(newFile.absoluteFilePath()).format(); | ||
3054 | 90 | m_fileFormat = QString(format).toLower(); | ||
3055 | 91 | if (m_fileFormat == "jpg") // Why does Qt expose two different names here? | ||
3056 | 92 | m_fileFormat = "jpeg"; | ||
3057 | 93 | |||
3058 | 94 | m_file = newFile; | ||
3059 | 95 | Q_EMIT pathChanged(); | ||
3060 | 96 | |||
3061 | 97 | if (fileFormatHasMetadata()) { | ||
3062 | 98 | PhotoMetadata* metadata = PhotoMetadata::fromFile(newFile.absoluteFilePath()); | ||
3063 | 99 | m_orientation = metadata->orientation(); | ||
3064 | 100 | delete metadata; | ||
3065 | 101 | Q_EMIT orientationChanged(); | ||
3066 | 102 | } | ||
3067 | 103 | } | ||
3068 | 104 | } | ||
3069 | 105 | } | ||
3070 | 106 | |||
3071 | 107 | QString PhotoData::path() const | ||
3072 | 108 | { | ||
3073 | 109 | return m_file.absoluteFilePath(); | ||
3074 | 110 | } | ||
3075 | 111 | |||
3076 | 112 | QFileInfo PhotoData::file() const | ||
3077 | 113 | { | ||
3078 | 114 | return m_file; | ||
3079 | 115 | } | ||
3080 | 116 | |||
3081 | 117 | /*! | ||
3082 | 118 | * \brief Photo::~Photo | ||
3083 | 119 | */ | ||
3084 | 120 | PhotoData::~PhotoData() | ||
3085 | 121 | { | ||
3086 | 122 | if (m_editThread) { | ||
3087 | 123 | m_editThread->wait(); | ||
3088 | 124 | finishEditing(); | ||
3089 | 125 | } | ||
3090 | 126 | } | ||
3091 | 127 | |||
3092 | 128 | /*! | ||
3093 | 129 | * \brief Photo::orientation | ||
3094 | 130 | * \return | ||
3095 | 131 | */ | ||
3096 | 132 | Orientation PhotoData::orientation() const | ||
3097 | 133 | { | ||
3098 | 134 | return m_orientation; | ||
3099 | 135 | } | ||
3100 | 136 | |||
3101 | 137 | void PhotoData::refreshFromDisk() | ||
3102 | 138 | { | ||
3103 | 139 | if (fileFormatHasMetadata()) { | ||
3104 | 140 | PhotoMetadata* metadata = PhotoMetadata::fromFile(m_file.absoluteFilePath()); | ||
3105 | 141 | qDebug() << "Refreshing orient." << m_orientation << "to" << metadata->orientation(); | ||
3106 | 142 | m_orientation = metadata->orientation(); | ||
3107 | 143 | delete metadata; | ||
3108 | 144 | Q_EMIT orientationChanged(); | ||
3109 | 145 | } | ||
3110 | 146 | |||
3111 | 147 | Q_EMIT dataChanged(); | ||
3112 | 148 | } | ||
3113 | 149 | |||
3114 | 150 | /*! | ||
3115 | 151 | * \brief Photo::rotateRight | ||
3116 | 152 | */ | ||
3117 | 153 | void PhotoData::rotateRight() | ||
3118 | 154 | { | ||
3119 | 155 | Orientation current = fileFormatHasOrientation() ? orientation() : | ||
3120 | 156 | TOP_LEFT_ORIGIN; | ||
3121 | 157 | Orientation rotated = OrientationCorrection::rotateOrientation(current, | ||
3122 | 158 | false); | ||
3123 | 159 | qDebug() << " Rotate from orientation " << current << "to" << rotated; | ||
3124 | 160 | |||
3125 | 161 | PhotoEditCommand command; | ||
3126 | 162 | command.type = EDIT_ROTATE; | ||
3127 | 163 | command.orientation = rotated; | ||
3128 | 164 | asyncEdit(command); | ||
3129 | 165 | } | ||
3130 | 166 | |||
3131 | 167 | /*! | ||
3132 | 168 | * \brief Photo::autoEnhance | ||
3133 | 169 | */ | ||
3134 | 170 | void PhotoData::autoEnhance() | ||
3135 | 171 | { | ||
3136 | 172 | PhotoEditCommand command; | ||
3137 | 173 | command.type = EDIT_ENHANCE; | ||
3138 | 174 | asyncEdit(command); | ||
3139 | 175 | } | ||
3140 | 176 | |||
3141 | 177 | /*! | ||
3142 | 178 | * \brief Photo::exposureCompensation Changes the brightnes of the image | ||
3143 | 179 | * \param value Value for the compensation. -1.0 moves the image into total black. | ||
3144 | 180 | * +1.0 to total white. 0.0 leaves it as it is. | ||
3145 | 181 | */ | ||
3146 | 182 | void PhotoData::exposureCompensation(qreal value) | ||
3147 | 183 | { | ||
3148 | 184 | PhotoEditCommand command; | ||
3149 | 185 | command.type = EDIT_COMPENSATE_EXPOSURE; | ||
3150 | 186 | command.exposureCompensation = value; | ||
3151 | 187 | asyncEdit(command); | ||
3152 | 188 | } | ||
3153 | 189 | |||
3154 | 190 | /*! | ||
3155 | 191 | * \brief Photo::crop | ||
3156 | 192 | * Specify all coords in [0.0, 1.0], where 1.0 is the full size of the image. | ||
3157 | 193 | * They will be clamped to this range if you don't. | ||
3158 | 194 | * \param vrect the rectangle specifying the region to be cropped | ||
3159 | 195 | */ | ||
3160 | 196 | void PhotoData::crop(QVariant vrect) | ||
3161 | 197 | { | ||
3162 | 198 | PhotoEditCommand command; | ||
3163 | 199 | command.type = EDIT_CROP; | ||
3164 | 200 | command.crop_rectangle = vrect.toRectF(); | ||
3165 | 201 | asyncEdit(command); | ||
3166 | 202 | } | ||
3167 | 203 | |||
3168 | 204 | /*! | ||
3169 | 205 | * \brief Photo::asyncEdit does edit the photo according to the given command | ||
3170 | 206 | * in a background thread. | ||
3171 | 207 | * \param The command defining the edit operation to perform. | ||
3172 | 208 | */ | ||
3173 | 209 | void PhotoData::asyncEdit(const PhotoEditCommand& command) | ||
3174 | 210 | { | ||
3175 | 211 | if (m_busy) { | ||
3176 | 212 | qWarning() << "Can't start edit operation while another one is running."; | ||
3177 | 213 | return; | ||
3178 | 214 | } | ||
3179 | 215 | m_busy = true; | ||
3180 | 216 | Q_EMIT busyChanged(); | ||
3181 | 217 | m_editThread = new PhotoEditThread(this, command); | ||
3182 | 218 | connect(m_editThread, SIGNAL(finished()), this, SLOT(finishEditing())); | ||
3183 | 219 | m_editThread->start(); | ||
3184 | 220 | } | ||
3185 | 221 | |||
3186 | 222 | /*! | ||
3187 | 223 | * \brief Photo::finishEditing do all the updates once the editing is done | ||
3188 | 224 | */ | ||
3189 | 225 | void PhotoData::finishEditing() | ||
3190 | 226 | { | ||
3191 | 227 | if (!m_editThread || m_editThread->isRunning()) | ||
3192 | 228 | return; | ||
3193 | 229 | |||
3194 | 230 | m_editThread->deleteLater(); | ||
3195 | 231 | m_editThread = 0; | ||
3196 | 232 | m_busy = false; | ||
3197 | 233 | |||
3198 | 234 | refreshFromDisk(); | ||
3199 | 235 | |||
3200 | 236 | Q_EMIT busyChanged(); | ||
3201 | 237 | Q_EMIT editFinished(); | ||
3202 | 238 | } | ||
3203 | 239 | |||
3204 | 240 | /*! | ||
3205 | 241 | * \brief Photo::fileFormat returns the file format as QString | ||
3206 | 242 | * \return | ||
3207 | 243 | */ | ||
3208 | 244 | const QString &PhotoData::fileFormat() const | ||
3209 | 245 | { | ||
3210 | 246 | return m_fileFormat; | ||
3211 | 247 | } | ||
3212 | 248 | |||
3213 | 249 | /*! | ||
3214 | 250 | * \brief Photo::fileFormatHasMetadata | ||
3215 | 251 | * \return | ||
3216 | 252 | */ | ||
3217 | 253 | bool PhotoData::fileFormatHasMetadata() const | ||
3218 | 254 | { | ||
3219 | 255 | return (m_fileFormat == "jpeg" || m_fileFormat == "tiff" || | ||
3220 | 256 | m_fileFormat == "png"); | ||
3221 | 257 | } | ||
3222 | 258 | |||
3223 | 259 | /*! | ||
3224 | 260 | * \brief Photo::fileFormatHasOrientation | ||
3225 | 261 | * \return | ||
3226 | 262 | */ | ||
3227 | 263 | bool PhotoData::fileFormatHasOrientation() const | ||
3228 | 264 | { | ||
3229 | 265 | return (m_fileFormat == "jpeg"); | ||
3230 | 266 | } | ||
3231 | 267 | |||
3232 | 268 | /*! | ||
3233 | 269 | * \brief Photo::busy return true if there is an editing operation in progress | ||
3234 | 270 | * \return | ||
3235 | 271 | */ | ||
3236 | 272 | bool PhotoData::busy() const | ||
3237 | 273 | { | ||
3238 | 274 | return m_busy; | ||
3239 | 275 | } | ||
3240 | 276 | 0 | ||
3241 | === removed file 'src/photoeditor/photo-data.h' | |||
3242 | --- src/photoeditor/photo-data.h 2015-02-26 20:28:06 +0000 | |||
3243 | +++ src/photoeditor/photo-data.h 1970-01-01 00:00:00 +0000 | |||
3244 | @@ -1,92 +0,0 @@ | |||
3245 | 1 | /* | ||
3246 | 2 | * Copyright (C) 2011-2014 Canonical Ltd | ||
3247 | 3 | * | ||
3248 | 4 | * This program is free software: you can redistribute it and/or modify | ||
3249 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
3250 | 6 | * published by the Free Software Foundation. | ||
3251 | 7 | * | ||
3252 | 8 | * This program is distributed in the hope that it will be useful, | ||
3253 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3254 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3255 | 11 | * GNU General Public License for more details. | ||
3256 | 12 | * | ||
3257 | 13 | * You should have received a copy of the GNU General Public License | ||
3258 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3259 | 15 | * | ||
3260 | 16 | * Authors: | ||
3261 | 17 | * Jim Nelson <jim@yorba.org> | ||
3262 | 18 | * Lucas Beeler <lucas@yorba.org> | ||
3263 | 19 | * Charles Lindsay <chaz@yorba.org> | ||
3264 | 20 | * Ugo Riboni <ugo.riboni@canonical.com> | ||
3265 | 21 | */ | ||
3266 | 22 | |||
3267 | 23 | #ifndef PHOTO_DATA_H_ | ||
3268 | 24 | #define PHOTO_DATA_H_ | ||
3269 | 25 | |||
3270 | 26 | // util | ||
3271 | 27 | #include "orientation.h" | ||
3272 | 28 | |||
3273 | 29 | // QT | ||
3274 | 30 | #include <QFileInfo> | ||
3275 | 31 | #include <QVariant> | ||
3276 | 32 | |||
3277 | 33 | class PhotoEditCommand; | ||
3278 | 34 | class PhotoEditThread; | ||
3279 | 35 | |||
3280 | 36 | /*! | ||
3281 | 37 | * \brief The Photo class | ||
3282 | 38 | */ | ||
3283 | 39 | class PhotoData : public QObject | ||
3284 | 40 | { | ||
3285 | 41 | Q_OBJECT | ||
3286 | 42 | |||
3287 | 43 | Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged) | ||
3288 | 44 | Q_PROPERTY(int orientation READ orientation NOTIFY orientationChanged) | ||
3289 | 45 | Q_PROPERTY(bool busy READ busy NOTIFY busyChanged) | ||
3290 | 46 | |||
3291 | 47 | public: | ||
3292 | 48 | explicit PhotoData(); | ||
3293 | 49 | virtual ~PhotoData(); | ||
3294 | 50 | |||
3295 | 51 | static bool isValid(const QFileInfo& file); | ||
3296 | 52 | |||
3297 | 53 | QString path() const; | ||
3298 | 54 | void setPath(QString path); | ||
3299 | 55 | QFileInfo file() const; | ||
3300 | 56 | bool busy() const; | ||
3301 | 57 | |||
3302 | 58 | virtual Orientation orientation() const; | ||
3303 | 59 | |||
3304 | 60 | Q_INVOKABLE void refreshFromDisk(); | ||
3305 | 61 | Q_INVOKABLE void rotateRight(); | ||
3306 | 62 | Q_INVOKABLE void autoEnhance(); | ||
3307 | 63 | Q_INVOKABLE void exposureCompensation(qreal value); | ||
3308 | 64 | Q_INVOKABLE void crop(QVariant vrect); | ||
3309 | 65 | |||
3310 | 66 | const QString &fileFormat() const; | ||
3311 | 67 | bool fileFormatHasMetadata() const; | ||
3312 | 68 | bool fileFormatHasOrientation() const; | ||
3313 | 69 | |||
3314 | 70 | Q_SIGNALS: | ||
3315 | 71 | void pathChanged(); | ||
3316 | 72 | void orientationChanged(); | ||
3317 | 73 | void busyChanged(); | ||
3318 | 74 | |||
3319 | 75 | void editFinished(); | ||
3320 | 76 | void dataChanged(); | ||
3321 | 77 | |||
3322 | 78 | private Q_SLOTS: | ||
3323 | 79 | void finishEditing(); | ||
3324 | 80 | |||
3325 | 81 | private: | ||
3326 | 82 | void asyncEdit(const PhotoEditCommand& state); | ||
3327 | 83 | |||
3328 | 84 | QString m_fileFormat; | ||
3329 | 85 | PhotoEditThread *m_editThread; | ||
3330 | 86 | QFileInfo m_file; | ||
3331 | 87 | bool m_busy; | ||
3332 | 88 | |||
3333 | 89 | Orientation m_orientation; | ||
3334 | 90 | }; | ||
3335 | 91 | |||
3336 | 92 | #endif // PHOTO_DATA_H_ | ||
3337 | 93 | 0 | ||
3338 | === removed file 'src/photoeditor/photo-edit-command.h' | |||
3339 | --- src/photoeditor/photo-edit-command.h 2015-02-26 20:28:06 +0000 | |||
3340 | +++ src/photoeditor/photo-edit-command.h 1970-01-01 00:00:00 +0000 | |||
3341 | @@ -1,58 +0,0 @@ | |||
3342 | 1 | /* | ||
3343 | 2 | * Copyright (C) 2014 Canonical Ltd | ||
3344 | 3 | * | ||
3345 | 4 | * This program is free software: you can redistribute it and/or modify | ||
3346 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
3347 | 6 | * published by the Free Software Foundation. | ||
3348 | 7 | * | ||
3349 | 8 | * This program is distributed in the hope that it will be useful, | ||
3350 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3351 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3352 | 11 | * GNU General Public License for more details. | ||
3353 | 12 | * | ||
3354 | 13 | * You should have received a copy of the GNU General Public License | ||
3355 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3356 | 15 | */ | ||
3357 | 16 | |||
3358 | 17 | #ifndef GALLERY_PHOTO_EDIT_STATE_H_ | ||
3359 | 18 | #define GALLERY_PHOTO_EDIT_STATE_H_ | ||
3360 | 19 | |||
3361 | 20 | // util | ||
3362 | 21 | #include "orientation.h" | ||
3363 | 22 | |||
3364 | 23 | #include <QRectF> | ||
3365 | 24 | #include <QVector4D> | ||
3366 | 25 | |||
3367 | 26 | enum EditType { | ||
3368 | 27 | EDIT_NONE = 0, | ||
3369 | 28 | EDIT_ROTATE = 1, | ||
3370 | 29 | EDIT_CROP = 2, | ||
3371 | 30 | EDIT_ENHANCE = 3, | ||
3372 | 31 | EDIT_COMPENSATE_EXPOSURE = 4 | ||
3373 | 32 | }; | ||
3374 | 33 | |||
3375 | 34 | /*! | ||
3376 | 35 | * \brief The PhotoEditCommand class | ||
3377 | 36 | * | ||
3378 | 37 | * A single edit that will be applied to a photo. | ||
3379 | 38 | */ | ||
3380 | 39 | class PhotoEditCommand | ||
3381 | 40 | { | ||
3382 | 41 | public: | ||
3383 | 42 | EditType type; | ||
3384 | 43 | Orientation orientation; | ||
3385 | 44 | QRectF crop_rectangle; | ||
3386 | 45 | qreal exposureCompensation; | ||
3387 | 46 | /// The color balance parameters are stored here in the order: | ||
3388 | 47 | /// brightness (x), contrast(y), saturation(z), hue(w) | ||
3389 | 48 | QVector4D colorBalance_; | ||
3390 | 49 | |||
3391 | 50 | PhotoEditCommand() : | ||
3392 | 51 | type(EDIT_NONE), | ||
3393 | 52 | orientation(ORIGINAL_ORIENTATION), | ||
3394 | 53 | crop_rectangle(), | ||
3395 | 54 | exposureCompensation(0.0) { | ||
3396 | 55 | } | ||
3397 | 56 | }; | ||
3398 | 57 | |||
3399 | 58 | #endif | ||
3400 | 59 | 0 | ||
3401 | === removed file 'src/photoeditor/photo-edit-thread.cpp' | |||
3402 | --- src/photoeditor/photo-edit-thread.cpp 2015-02-26 20:28:06 +0000 | |||
3403 | +++ src/photoeditor/photo-edit-thread.cpp 1970-01-01 00:00:00 +0000 | |||
3404 | @@ -1,185 +0,0 @@ | |||
3405 | 1 | /* | ||
3406 | 2 | * Copyright (C) 2013-2014 Canonical Ltd | ||
3407 | 3 | * | ||
3408 | 4 | * This program is free software: you can redistribute it and/or modify | ||
3409 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
3410 | 6 | * published by the Free Software Foundation. | ||
3411 | 7 | * | ||
3412 | 8 | * This program is distributed in the hope that it will be useful, | ||
3413 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3414 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3415 | 11 | * GNU General Public License for more details. | ||
3416 | 12 | * | ||
3417 | 13 | * You should have received a copy of the GNU General Public License | ||
3418 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3419 | 15 | */ | ||
3420 | 16 | |||
3421 | 17 | #include "photo-edit-thread.h" | ||
3422 | 18 | #include "photo-data.h" | ||
3423 | 19 | |||
3424 | 20 | // medialoader | ||
3425 | 21 | #include "photo-metadata.h" | ||
3426 | 22 | |||
3427 | 23 | // util | ||
3428 | 24 | #include "imaging.h" | ||
3429 | 25 | |||
3430 | 26 | #include <QDebug> | ||
3431 | 27 | |||
3432 | 28 | /*! | ||
3433 | 29 | * \brief PhotoEditThread::PhotoEditThread | ||
3434 | 30 | */ | ||
3435 | 31 | PhotoEditThread::PhotoEditThread(PhotoData *photo, const PhotoEditCommand &command) | ||
3436 | 32 | : QThread(), | ||
3437 | 33 | m_photo(photo), | ||
3438 | 34 | m_command(command) | ||
3439 | 35 | { | ||
3440 | 36 | } | ||
3441 | 37 | |||
3442 | 38 | /*! | ||
3443 | 39 | * \brief PhotoEditThread::command resturns the editing command used for this processing | ||
3444 | 40 | * \return | ||
3445 | 41 | */ | ||
3446 | 42 | const PhotoEditCommand &PhotoEditThread::command() const | ||
3447 | 43 | { | ||
3448 | 44 | return m_command; | ||
3449 | 45 | } | ||
3450 | 46 | |||
3451 | 47 | /*! | ||
3452 | 48 | * \brief PhotoEditThread::run \reimp | ||
3453 | 49 | */ | ||
3454 | 50 | void PhotoEditThread::run() | ||
3455 | 51 | { | ||
3456 | 52 | // The only operation in which we don't have to work on the actual image | ||
3457 | 53 | // pixels is image rotation in the case where we can simply change the | ||
3458 | 54 | // metadata rotation field. | ||
3459 | 55 | if (m_command.type == EDIT_ROTATE && m_photo->fileFormatHasOrientation()) { | ||
3460 | 56 | handleSimpleMetadataRotation(m_command); | ||
3461 | 57 | return; | ||
3462 | 58 | } | ||
3463 | 59 | |||
3464 | 60 | // In all other cases we load the image, do the work, and save it back. | ||
3465 | 61 | QImage image(m_photo->file().filePath(), m_photo->fileFormat().toStdString().c_str()); | ||
3466 | 62 | if (image.isNull()) { | ||
3467 | 63 | qWarning() << "Error loading" << m_photo->file().filePath() << "for editing"; | ||
3468 | 64 | return; | ||
3469 | 65 | } | ||
3470 | 66 | |||
3471 | 67 | // Copy all metadata from the original image so that we can save it to the | ||
3472 | 68 | // new one after modifying the pixels. | ||
3473 | 69 | PhotoMetadata* original = PhotoMetadata::fromFile(m_photo->file()); | ||
3474 | 70 | |||
3475 | 71 | // If the photo was previously rotated through metadata and we are editing | ||
3476 | 72 | // the actual pixels, first rotate the image to match the orientation so | ||
3477 | 73 | // that the correct pixels are edited. | ||
3478 | 74 | // Obviously don't do this in the case we have been asked to do a rotation | ||
3479 | 75 | // operation on the pixels, as we would do it later as the operation itself. | ||
3480 | 76 | if (m_photo->fileFormatHasOrientation() && m_command.type != EDIT_ROTATE) { | ||
3481 | 77 | Orientation orientation = m_photo->orientation(); | ||
3482 | 78 | QTransform transform = OrientationCorrection::fromOrientation(orientation).toTransform(); | ||
3483 | 79 | image = image.transformed(transform); | ||
3484 | 80 | } | ||
3485 | 81 | |||
3486 | 82 | if (m_command.type == EDIT_ROTATE) { | ||
3487 | 83 | QTransform transform = OrientationCorrection::fromOrientation(m_command.orientation).toTransform(); | ||
3488 | 84 | image = image.transformed(transform); | ||
3489 | 85 | } else if (m_command.type == EDIT_CROP) { | ||
3490 | 86 | QRect rect; | ||
3491 | 87 | rect.setX(qBound(0.0, m_command.crop_rectangle.x(), 1.0) * image.width()); | ||
3492 | 88 | rect.setY(qBound(0.0, m_command.crop_rectangle.y(), 1.0) * image.height()); | ||
3493 | 89 | rect.setWidth(qBound(0.0, m_command.crop_rectangle.width(), 1.0) * image.width()); | ||
3494 | 90 | rect.setHeight(qBound(0.0, m_command.crop_rectangle.height(), 1.0) * image.height()); | ||
3495 | 91 | |||
3496 | 92 | image = image.copy(rect); | ||
3497 | 93 | } else if (m_command.type == EDIT_ENHANCE) { | ||
3498 | 94 | image = enhanceImage(image); | ||
3499 | 95 | } else if (m_command.type == EDIT_COMPENSATE_EXPOSURE) { | ||
3500 | 96 | image = compensateExposure(image, m_command.exposureCompensation); | ||
3501 | 97 | } else { | ||
3502 | 98 | qWarning() << "Edit thread running with unknown or no operation."; | ||
3503 | 99 | return; | ||
3504 | 100 | } | ||
3505 | 101 | |||
3506 | 102 | bool saved = image.save(m_photo->file().filePath(), | ||
3507 | 103 | m_photo->fileFormat().toStdString().c_str(), 90); | ||
3508 | 104 | if (!saved) | ||
3509 | 105 | qWarning() << "Error saving edited" << m_photo->file().filePath(); | ||
3510 | 106 | |||
3511 | 107 | PhotoMetadata* copy = PhotoMetadata::fromFile(m_photo->file()); | ||
3512 | 108 | original->copyTo(copy); | ||
3513 | 109 | copy->setOrientation(TOP_LEFT_ORIGIN); // reset previous orientation | ||
3514 | 110 | copy->updateThumbnail(image); | ||
3515 | 111 | copy->save(); | ||
3516 | 112 | |||
3517 | 113 | delete original; | ||
3518 | 114 | delete copy; | ||
3519 | 115 | } | ||
3520 | 116 | |||
3521 | 117 | /*! | ||
3522 | 118 | * \brief PhotoEditThread::handleSimpleMetadataRotation | ||
3523 | 119 | * Handler for the case of an image whose only change is to its | ||
3524 | 120 | * orientation; used to skip re-encoding of JPEGs. | ||
3525 | 121 | * \param state | ||
3526 | 122 | */ | ||
3527 | 123 | void PhotoEditThread::handleSimpleMetadataRotation(const PhotoEditCommand& state) | ||
3528 | 124 | { | ||
3529 | 125 | PhotoMetadata* metadata = PhotoMetadata::fromFile(m_photo->file()); | ||
3530 | 126 | metadata->setOrientation(state.orientation); | ||
3531 | 127 | metadata->save(); | ||
3532 | 128 | delete(metadata); | ||
3533 | 129 | } | ||
3534 | 130 | |||
3535 | 131 | /*! | ||
3536 | 132 | * \brief PhotoEditThread::enhanceImage | ||
3537 | 133 | */ | ||
3538 | 134 | QImage PhotoEditThread::enhanceImage(const QImage& image) | ||
3539 | 135 | { | ||
3540 | 136 | int width = image.width(); | ||
3541 | 137 | int height = image.height(); | ||
3542 | 138 | |||
3543 | 139 | QImage sample_img = (image.width() > 400) ? image.scaledToWidth(400) : image; | ||
3544 | 140 | |||
3545 | 141 | AutoEnhanceTransformation enhance = AutoEnhanceTransformation(sample_img); | ||
3546 | 142 | |||
3547 | 143 | QImage::Format dest_format = image.format(); | ||
3548 | 144 | |||
3549 | 145 | // Can't write into indexed images, due to a limitation in Qt. | ||
3550 | 146 | if (dest_format == QImage::Format_Indexed8) | ||
3551 | 147 | dest_format = QImage::Format_RGB32; | ||
3552 | 148 | |||
3553 | 149 | QImage enhanced_image(width, height, dest_format); | ||
3554 | 150 | |||
3555 | 151 | for (int j = 0; j < height; j++) { | ||
3556 | 152 | for (int i = 0; i < width; i++) { | ||
3557 | 153 | QColor px = enhance.transformPixel( | ||
3558 | 154 | QColor(image.pixel(i, j))); | ||
3559 | 155 | enhanced_image.setPixel(i, j, px.rgb()); | ||
3560 | 156 | } | ||
3561 | 157 | } | ||
3562 | 158 | |||
3563 | 159 | return enhanced_image; | ||
3564 | 160 | } | ||
3565 | 161 | |||
3566 | 162 | /*! | ||
3567 | 163 | * \brief PhotoEditThread::compensateExposure Compensates the exposure | ||
3568 | 164 | * Compensating the exposure is a change in brightnes | ||
3569 | 165 | * \param image Image to change the brightnes | ||
3570 | 166 | * \param compansation -1.0 is total dark, +1.0 is total bright | ||
3571 | 167 | * \return The image with adjusted brightnes | ||
3572 | 168 | */ | ||
3573 | 169 | QImage PhotoEditThread::compensateExposure(const QImage &image, qreal compensation) | ||
3574 | 170 | { | ||
3575 | 171 | int shift = qBound(-255, (int)(255*compensation), 255); | ||
3576 | 172 | QImage result(image.width(), image.height(), image.format()); | ||
3577 | 173 | |||
3578 | 174 | for (int j = 0; j < image.height(); j++) { | ||
3579 | 175 | for (int i = 0; i <image.width(); i++) { | ||
3580 | 176 | QColor px = image.pixel(i, j); | ||
3581 | 177 | int red = qBound(0, px.red() + shift, 255); | ||
3582 | 178 | int green = qBound(0, px.green() + shift, 255); | ||
3583 | 179 | int blue = qBound(0, px.blue() + shift, 255); | ||
3584 | 180 | result.setPixel(i, j, qRgb(red, green, blue)); | ||
3585 | 181 | } | ||
3586 | 182 | } | ||
3587 | 183 | |||
3588 | 184 | return result; | ||
3589 | 185 | } | ||
3590 | 186 | 0 | ||
3591 | === removed file 'src/photoeditor/photo-edit-thread.h' | |||
3592 | --- src/photoeditor/photo-edit-thread.h 2015-02-26 20:28:06 +0000 | |||
3593 | +++ src/photoeditor/photo-edit-thread.h 1970-01-01 00:00:00 +0000 | |||
3594 | @@ -1,56 +0,0 @@ | |||
3595 | 1 | /* | ||
3596 | 2 | * Copyright (C) 2013-2014 Canonical Ltd | ||
3597 | 3 | * | ||
3598 | 4 | * This program is free software: you can redistribute it and/or modify | ||
3599 | 5 | * it under the terms of the GNU General Public License version 3 as | ||
3600 | 6 | * published by the Free Software Foundation. | ||
3601 | 7 | * | ||
3602 | 8 | * This program is distributed in the hope that it will be useful, | ||
3603 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3604 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3605 | 11 | * GNU General Public License for more details. | ||
3606 | 12 | * | ||
3607 | 13 | * You should have received a copy of the GNU General Public License | ||
3608 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3609 | 15 | */ | ||
3610 | 16 | |||
3611 | 17 | #ifndef GALLERY_PHOTO_EDIT_THREAD_H_ | ||
3612 | 18 | #define GALLERY_PHOTO_EDIT_THREAD_H_ | ||
3613 | 19 | |||
3614 | 20 | #include "photo-caches.h" | ||
3615 | 21 | #include "photo-edit-command.h" | ||
3616 | 22 | |||
3617 | 23 | // util | ||
3618 | 24 | #include "orientation.h" | ||
3619 | 25 | |||
3620 | 26 | #include <QImage> | ||
3621 | 27 | #include <QThread> | ||
3622 | 28 | #include <QUrl> | ||
3623 | 29 | |||
3624 | 30 | class PhotoData; | ||
3625 | 31 | |||
3626 | 32 | /*! | ||
3627 | 33 | * \brief The PhotoEditThread class | ||
3628 | 34 | */ | ||
3629 | 35 | class PhotoEditThread: public QThread | ||
3630 | 36 | { | ||
3631 | 37 | Q_OBJECT | ||
3632 | 38 | public: | ||
3633 | 39 | PhotoEditThread(PhotoData *photo, const PhotoEditCommand& command); | ||
3634 | 40 | |||
3635 | 41 | const PhotoEditCommand& command() const; | ||
3636 | 42 | |||
3637 | 43 | protected: | ||
3638 | 44 | void run() Q_DECL_OVERRIDE; | ||
3639 | 45 | |||
3640 | 46 | private: | ||
3641 | 47 | QImage enhanceImage(const QImage& image); | ||
3642 | 48 | QImage compensateExposure(const QImage& image, qreal compansation); | ||
3643 | 49 | QImage doColorBalance(const QImage& image, qreal brightness, qreal contrast, qreal saturation, qreal hue); | ||
3644 | 50 | void handleSimpleMetadataRotation(const PhotoEditCommand& state); | ||
3645 | 51 | |||
3646 | 52 | PhotoData *m_photo; | ||
3647 | 53 | PhotoEditCommand m_command; | ||
3648 | 54 | }; | ||
3649 | 55 | |||
3650 | 56 | #endif |
FAILED: Continuous integration, rev:1256 jenkins. qa.ubuntu. com/job/ gallery- app-ci/ 1221/ jenkins. qa.ubuntu. com/job/ gallery- app-vivid- amd64-ci/ 149 jenkins. qa.ubuntu. com/job/ gallery- app-vivid- armhf-ci/ 149 jenkins. qa.ubuntu. com/job/ gallery- app-vivid- armhf-ci/ 149/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ gallery- app-vivid- i386-ci/ 149 jenkins. qa.ubuntu. com/job/ generic- click-autopilot -vivid- touch/349/ console jenkins. qa.ubuntu. com/job/ generic- click-autopilot -runner- touch/1011/ console jenkins. qa.ubuntu. com/job/ generic- click-builder- vivid-armhf/ 871 s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 26110
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/gallery- app-ci/ 1221/rebuild
http://