diff --git a/NEWS b/NEWS index 0b45fdab8e..ec3bdaafb6 100644 --- a/NEWS +++ b/NEWS @@ -1,553 +1,554 @@ digiKam 7.0.0-beta2 - Release date: 2020-01-26 ***************************************************************************************************** NEW FEATURES: FaceManagement: New Neural Network engine based on OpenCV Deep Learning module to detect and recognize faces. FaceManagement: Face Scan dialog contents is now simlified and embeded into left side-bar tab. SlideShow : Add new shuffle mode. HTMLGallery : Add new theme "Html5Responsive". General : Update internal libraw engine to last 201910 snapshot aka next 0.20.0 release (https://www.libraw.org/news/libraw-snapshot-201910) ***************************************************************************************************** BUGFIXES: 001 ==> 384401 - Various recognition algorithm improvements for face detection. 002 ==> 413701 - DigiKam 6.3.0 does not build against current plasma. 003 ==> 413748 - Broken theme on Windows 10 after Digikam update. 004 ==> 413738 - Video Preview changes VLC volume. 005 ==> 382311 - Photos in collapsed groups are incorrectly excluded if first photo in group does not match filter. 006 ==> 396337 - In tag filter view (or any other filitered view), grouped photos should not always have "group" thumbnail 007 ==> 413704 - Filters do not work on hidden grouped images. 007 ==> 413233 - Application crashes when click in Albums left panel. 008 ==> 413759 - Tarball is missing translations. 009 ==> 413879 - Buttons without theme colors. 010 ==> 392015 - Show "Unknown" faces in a more visible and preeminent place in the "People" list. 011 ==> 413837 - Reverse geolocation deletes previous tags from the picture. 012 ==> 376629 - Face tagging dropdown: "Add in persons" adds person not label. 013 ==> 413923 - The peoples list shows just one line of height (one name) when expanded for the first time. 014 ==> 413924 - The face rectangle behavior when show face tags is OFF. 015 ==> 341111 - MYSQL : deleting an image involves "too many" queries. 016 ==> 413938 - Metadata is not written to all pictures in a tag hierarchy. 017 ==> 413916 - Compile Error with lqr on neon. 018 ==> 413972 - align_image_stack and enfuse use only 1 CPU core if called by digiKam plugin, 4 CPU core on command line. 019 ==> 413981 - Refresh has no effect in Preview mode. 020 ==> 413985 - Can't move an album too far down - no treeview scrolling. 021 ==> 412678 - Renaming folder leads to an error. 022 ==> 303239 - GROUP : grouped images are found, but do not show in searches. 023 ==> 321339 - GROUP : suppress display of albums containing only grouped images. 024 ==> 289911 - 23HQ should be split from flickr uploader as a new export tool. 025 ==> 414112 - Rounding problems in Resize Image function. 026 ==> 414052 - Packaging error of digikam-6.4.0.tar.xz, archive file contains a huge number of unnecessary files. 027 ==> 404667 - Bug of proofing for illuminant A and color cast. 028 ==> 414247 - digikam: error while loading shared libraries: libcudart.so.8.0. 029 ==> 392758 - Slideshow with random pictures [patch]. 030 ==> 414300 - Buttons on either side completely unresponsive. 031 ==> 414320 - Performance regression when scanning directories with many sub-directories. 032 ==> 414284 - Improve Setup/Slideshow View Options Layout. 033 ==> 414420 - Date/time in sidecar files for Videos seems to be ignored by digikam. 034 ==> 414340 - RawTherapee plugin. 035 ==> 414473 - Importing problem with M2Ts video files. 036 ==> 414521 - File management is broken. 037 ==> 205406 - Add Javascript to scaling images in html gallery [patch]. 038 ==> 334680 - Allow to limit exported files by type-mime in html gallery. 039 ==> 092462 - Add a new option to limit amount of thumbnails per index page. 040 ==> 136389 - Add new option to show image date and time in html gallery. 041 ==> 305167 - Add back filenames in html gallery. 042 ==> 114216 - Add a new option to export and merge more than one album in html gallery. 043 ==> 414516 - digikam crash when double-clicking on Google Maps when editing geolocation. 044 ==> 414603 - Pictures sort by freehand - drag & drop. 045 ==> 414630 - Shortcut bug in caption (tag). 046 ==> 414484 - Cannot use Rawtherapee or Darktable for raw image import on Windows. 047 ==> 166577 - List of available cameras in search. 048 ==> 414637 - New option for search. 049 ==> 414401 - RawTherapee does not work as Raw Import Tool if both are AppImages. 050 ==> 412067 - I get a error message: "Error while opening the database". 051 ==> 414902 - After using the "healing clone" tool only preview is available from other tools. 052 ==> 402894 - Display of original image. 053 ==> 380345 - Batch converting RAW to JPEG saves as new version. 054 ==> 383716 - "Save Changes" button same as "Save As New Version" Button. 055 ==> 307374 - Wrong image count in album view with versioning. 056 ==> 412961 - All image versions remain visible. 057 ==> 399923 - Segmentation fault during face detection. 058 ==> 392651 - Digikam crashes when Face scanning reaches 29%. 059 ==> 397919 - Segmentation fault during maintenance. 060 ==> 365354 - MYSQL : Application crash on scanning for faces in large picture set. 061 ==> 391014 - Crashes on close. 062 ==> 387821 - Face Recognition and Finding crashs after reaching 50%. 063 ==> 414749 - Export google photos: error transmission date creation. 064 ==> 377127 - Wrong item count on years in Dates View. 065 ==> 350350 - Moving images notifies me moving finished when it did not finish yet. 066 ==> 342191 - digiKam keeps crashing after crash with face-detection and manual tagging at the same time. 067 ==> 309769 - Crash on startup: 'Program received signal SIGILL, Illegal instruction.' (libopencv_nonfree). 068 ==> 286418 - digikam crashed during face recognition. 069 ==> 402470 - Crashed While I Walked Away During Face Detection. 070 ==> 415046 - Facial Recognition crash during scan. 071 ==> 360477 - Crash when adding a new face tag. 072 ==> 351638 - digiKam crash after facemarking. 073 ==> 350599 - Crash after maintenance. 074 ==> 350549 - Massive memory usage when assigning a tag to a recognized face. 075 ==> 312243 - digiKam crash when detected face moved or resized. 076 ==> 329108 - Crash when running Scanning faces/Clear and rebuild all training data. 077 ==> 280958 - Removing non-faces crashes digiKam. 078 ==> 312289 - Face Scan Crash. 079 ==> 285444 - Crash when adding face tag. 080 ==> 328413 - Crash while tagging faces - no background detection in process. 081 ==> 303328 - digiKam crashes repeatedly while tagging faces. 082 ==> 307554 - digiKam crash when scanning faces. 083 ==> 301506 - Crash while Tagging and Grouping Faces. 084 ==> 309306 - Crash during face-recognition. 085 ==> 284137 - digiKam crashed while moving a face tag after rotating a picture. 086 ==> 299173 - Crash during face tagging. 087 ==> 328560 - Crash when attempting to exit digiKam after aborted face recognition. 088 ==> 323828 - Crash when assigning a name to a single unknown face. 089 ==> 344735 - Crash if I name a face. 090 ==> 275688 - Crash during face detection, whilst tagging. 091 ==> 322022 - Faces and crash. 092 ==> 326750 - digiKam sometimes crashes when tagging recogniced faces with names. 093 ==> 314877 - Face Scan Crash. 094 ==> 271791 - digiKam crashes when removing a "face image" from the current list when the list is not yet loaded completely. 095 ==> 270410 - digiKam crashes when confirming faces. 096 ==> 283197 - Crash during tagging faces. 097 ==> 326689 - digiKam crashes while tagging faces. 098 ==> 284398 - Crash while tagging persons. 099 ==> 334158 - digiKam crash while tagging faces. 100 ==> 321851 - Face scan crash but only on multithread. 101 ==> 308393 - Crash while scanning faces. 102 ==> 326570 - digiKam crashes while recognizing/tagging faces. 103 ==> 343014 - Crashed when select another tag (face tag management). 104 ==> 327699 - Crashing while face detection. 105 ==> 308645 - digiKam crashed when I clicked scan faces while another scan was already running. 106 ==> 287961 - Crash while face-tagging pictures and doing import. 107 ==> 317863 - digiKam crashes as detected faces are being assigned to people or being removed while face detection is running. 108 ==> 302354 - Crash finding again faces (probably a duplicate of 262596). 109 ==> 320861 - Crash when adding tag to photo of the face scan result. 110 ==> 326794 - Tagging faces crashed digiKam. 111 ==> 274850 - digiKam crash on face scanning. 112 ==> 283165 - Crash happened while running face recognition. 113 ==> 280521 - digiKam crashes when deselecting two pictures in in quick succession from the face view. 114 ==> 303304 - Crash during face detection. 115 ==> 329596 - digiKam crashes during face recognition. 116 ==> 298599 - digiKam crashed while scaning for faces in the background. 117 ==> 304360 - Crash during face tagging. 118 ==> 280620 - digiKam crashes when tagging multiple non-faces. 119 ==> 302437 - Crash when re-searching for faces. 120 ==> 274727 - Crash on scan of faces. 121 ==> 333582 - Scanning for faces crashes. 122 ==> 290826 - digiKam crashed while tagging faces. 123 ==> 289003 - Crash when trying to face-scan all images. 124 ==> 280618 - digiKam crashes when tagging multiple faces. 125 ==> 329651 - Face detection crashes when reaches 3 GB of Memory, the computer has 6GB and 2 are still free. 126 ==> 294452 - digiKam crash in "Scan collection for faces". 127 ==> 268102 - Crash when finding faces. 128 ==> 323823 - Crash while first face detection after update. 129 ==> 336236 - Crash while tagging faces on more than one image at same time. 130 ==> 279266 - digiKam crashes while trying to tag faces. 131 ==> 312442 - digiKam crashed when editing face tags. 132 ==> 275827 - digiKam crash whilst scanning for faces, starting to enter tag. 133 ==> 330828 - Crash while detecting faces. 134 ==> 324711 - Crash when scaning faces. 135 ==> 323654 - digiKam crashed while i was tagging faces. 136 ==> 283540 - digiKam crashes on entering people tages (face recognition). 137 ==> 277099 - digiKam crash when adding a tag to a face. 138 ==> 296281 - digiKam crashed while manually adding a face. 139 ==> 325526 - Crash while tagging faces. 140 ==> 275637 - Performing multiple scans and recognises causes crash. 141 ==> 289228 - Crash when detecting faces. 142 ==> 317413 - digiKam crashed while tagging faces. 143 ==> 334580 - Face detection crashes digiKam. 144 ==> 301781 - digiKam crashed while scanning photos for faces. 145 ==> 321273 - crash during face scan. 146 ==> 296784 - digiKam crashes while tagging faces. 147 ==> 285517 - digiKam crashed when tagging faces. 148 ==> 290818 - digiKam crash during face scanning. 149 ==> 271375 - digiKam crashed when unchecking face. 150 ==> 317290 - Crashes during face scan. 151 ==> 308575 - digiKam crashes while scanning for faces. 152 ==> 301832 - digiKam crashes a while after starting face detection scan. 153 ==> 300357 - digiKam crashes on Face Recognition scan. 154 ==> 309142 - Face Crash. 155 ==> 279781 - digiKam crashes while using face detection tool. 156 ==> 275387 - digiKam crashes on face recognition. 157 ==> 311934 - digiKam was crashing when I was face-tagging photos. 158 ==> 323428 - Crash while tagging a lot of faces at the same time. 159 ==> 262873 - digiKam crashes on scanning for faces. 160 ==> 280520 - Crash while running a face detection, and marking some of the faces. 161 ==> 322187 - Crash while 'detect and recognize faces'. 162 ==> 334337 - Crash when tagging faces in RAW picture files. 163 ==> 301856 - digiKam crashes during face tagging. 164 ==> 275541 - digiKam crashed while doing face recognition. 165 ==> 324093 - digiKam crashed when I clicked the check mark to assign an existing name to several selected face images. No background tasks were running. 166 ==> 286071 - Crash while adding face tags. 167 ==> 280901 - Crash when rejecting faces in different albums. 168 ==> 290891 - digiKam Crash while Face Recognition. 169 ==> 293418 - Crashes when scanning for faces. 170 ==> 273161 - digiKam: malloc(): smallbin double linked list corrupted. 171 ==> 268046 - Switch foto with face recognition. 172 ==> 277163 - digikam valgrind issues during face recognition testing. 173 ==> 302359 - Crash creating a tag while scanning faces. 174 ==> 307110 - digiKam crashes when manually adding face tags, then moving to next image. 175 ==> 339263 - digiKam crash. 176 ==> 379470 - Crash when performing face detection or recognition on large collection. 177 ==> 284154 - Crash upon exit. 178 ==> 392142 - Use Tensorflow for face and object recognition. 179 ==> 303501 - Double free or corruption during face tagging. 180 ==> 314646 - digikam crash when (tagging) scanning collection for faces. 181 ==> 317450 - Attempting to name a person while face tagging is in progress. 182 ==> 318640 - Face detection crash. 183 ==> 325385 - Face scan and using all processor cores. 184 ==> 325712 - Face tagging has massive memory leak. 185 ==> 326323 - Running Face Scan. 186 ==> 329164 - Changing face tags in an image causes digiKam to eat up all the virtual memory. 187 ==> 331912 - Face detection and recognition dos not work, tags and factags counts for one person are different, memory goes up and up without any result. 188 ==> 334509 - Person Detection. 189 ==> 337936 - Assigning a new person. 190 ==> 342144 - Face detection. 191 ==> 344661 - Face recognition makes digikam fill all the available memory. 192 ==> 345395 - Face Management the Memory and Swap growed to max. 193 ==> 365669 - Face Recognition improvement suggestion. 194 ==> 376901 - Face Recognition Algorithm Improvements. 195 ==> 338072 - digiKam face detection. 196 ==> 387870 - undefined reference to typeinfo for cv::face::FaceRecognizer. 197 ==> 327197 - When add one face tag in mainview/albums/one pictures view digikam and/or opencv fullfill the memory. 198 ==> 353859 - FaceEngine multi threaded ? 199 ==> 342004 - FacesEngine header and pkgconfig files not installed. 200 ==> 406838 - digiKam crashes while labeling unconfirmed faces. 201 ==> 409437 - Crash when doing any face detection. 202 ==> 404853 - digiKam faces engine fails to compile on PowerPC. 203 ==> 405625 - digiKam faces engine fails to compile on PowerPC with AltiVec enabled. 204 ==> 194401 - Face detection / recognition for digiKam for tags. 205 ==> 339823 - Detect and recognice faces crashes. 206 ==> 351077 - Crash during facial detection and recognition. 207 ==> 375317 - MYSQL: digikam crashes during face recognition. 208 ==> 299066 - digiKam crashed while tagging a large amount of people. 209 ==> 268761 - Segfault when tagging people without existing tag in preview mode. 210 ==> 374165 - Tag Change in Menu Person search crashes. 211 ==> 347753 - Surface freezes after some faces tagged. 212 ==> 375945 - Face detection not scanning my images. 213 ==> 343314 - Mass face tagging pictures when writing the tags into the files often crashes at the end of the writing. 214 ==> 345909 - digiKam crashed when face taging multiple photos. 215 ==> 401306 - digiKam doesn't compile with Opencv 4. 216 ==> 376766 - Face detection is using all CPU cores regardless of the checked option. 217 ==> 402320 - Memory hole (>1.5GB resident after 2 minutes) when detecting faces. 218 ==> 389031 - Scanning collection for faces causes produces lots of OpenCV errors. 219 ==> 325331 - MySQL : when adding a new face, tag is created with a new _Digikam_root_ tag in database. 220 ==> 262577 - Scanning collection for new faces (skip already scanned images) does not do anything. 221 ==> 330143 - "Detect and recognize faces" does no detection. 222 ==> 392527 - "Add A Face Tag" Dialogue Is Too Small. 223 ==> 372761 - Face Tag selection needs extra confirm now. 224 ==> 375418 - Glitch in face selection in album view. 225 ==> 281792 - When tagging with 'Return', too many faces are tagged. 226 ==> 282592 - Rejecting multiple faces at a time doesn't work properly. 227 ==> 376681 - Region Coordinates Are Sometimes inf / Large Numbers. 228 ==> 380251 - "Show only face tags..." not working properly. 229 ==> 326538 - When a picture is in portrait, face thumbnails are not rotated. 230 ==> 279208 - After the upgrade to Plasma and digiKam Faces Are Not Detected Anymore. 231 ==> 316856 - I suggest to extend face recognition to text-recognition system. 232 ==> 381378 - Face rectangle from XMP sidecar drawn incorrectly for EXIF rotated images. 233 ==> 326035 - Stepping through previews with "show face tags" enabled does not always show the tags. 234 ==> 326033 - Adding or changing face tag in preview window causes all tags on that preview to "hide". 235 ==> 265022 - UI's handling of People tags is confusing. 236 ==> 262180 - The toggle button to show/hide faces is difficult to see. 237 ==> 316161 - Reuse face tags from another picture. 238 ==> 412999 - Some photos downloaded in previous version of digikam no more recognized as already downloaded. 239 ==> 334215 - Cannot open RAW files from Sony A7R. 240 ==> 340595 - Convertion Sony A7 raw file turns to red the result. 241 ==> 307313 - digiKam uses wrong darkness/saturation values. 242 ==> 315156 - CR2 files from Canon PowerShot G1 X the colours are messed up. 243 ==> 332126 - LibRaw_r_LIBRARIES CMake Error: The following variables are used in this project, but they are set to NOTFOUND. 244 ==> 352996 - digiKam crashes while scanning for new files in Collection. 245 ==> 362779 - Can't decode PEF image from Pentax K-1 (36mp). 246 ==> 364063 - Crash on startup (related to crashing on conversion to DNG?). 247 ==> 221984 - Choosing manual WB does not allow user to tweak the previously selected WB setting. 248 ==> 182611 - Segfault with Samsung S85 RAW images. 249 ==> 178760 - Conversion of Canon .cr2 files broken as used in Darkroom. 250 ==> 364230 - Crash on startup. 251 ==> 244142 - digiKam crash. 252 ==> 362418 - Crashes on launch. 253 ==> 253091 - digiKam will not launch. 254 ==> 338075 - Tagging RAW images for Canon EOS-1Ds corrupts them. 255 ==> 205006 - On first run, digikam crashes while creating the index. 256 ==> 409148 - Sony A7r3 ARW files are displayed blurred in preview. 257 ==> 388339 - Crash when importing Pentax DNG file. 258 ==> 329230 - Crash when tuning a raw image. 259 ==> 253877 - digiKam Crash when loading. 260 ==> 134700 - CR2 thumbnails are not rotated. 261 ==> 090875 - Preview of RAW-Files in album. 262 ==> 143681 - Can't edit/view NEF anymore. 263 ==> 341024 - digiKam crash in a dir with only Canon Raw and JPEG files. 264 ==> 132695 - CR2 RAW files do not open. 265 ==> 339924 - RAW preview using embedded JPG. 266 ==> 142057 - Speed to view raw pictures. 267 ==> 187015 - Libraw breaks RW2 file handling. 268 ==> 126151 - Raw (NEF) thumbnail not correctly showed. 269 ==> 143244 - Error processing RAW files. 270 ==> 235321 - Tried to open X3F file. Was showing up in preview but crashed while opening in editor. 271 ==> 306843 - Raw-pics rotating. 272 ==> 149328 - Let RawEngine use raw decoding options when generating thumbnails. 273 ==> 150872 - Crash during during RAW conversion. 274 ==> 182013 - Sensitivity is not shown for rw2 files. 275 ==> 157619 - Let digiKam use preview images from raw files for more speed. 276 ==> 220322 - Crash when reading nef data: Warning: Exif tag Exif.NikonPreview.JPEGInterchangeFormatLength not encoded. 277 ==> 165176 - Previews of raw pics are not created when "create all previews new" is selected. 278 ==> 149086 - RAF-picture finepix not show in Imageeditor (thumbnails are displayed). 279 ==> 123950 - Crashing on raw files (.nef). 280 ==> 320049 - RawEngine is crashing on decoding some sigma raw files. 281 ==> 277707 - Easy way to convert Raw, to get same image as seen in preview. 282 ==> 146738 - Fuji *.RAF files aren't recognized as RAW files. 283 ==> 331397 - Preview and thumbnails colors are wrong for cr2. 284 ==> 210659 - Inconsistent Save / Save As handling in image editor after raw import. 285 ==> 155950 - Raw file open with action causes two instances. 286 ==> 337601 - digiKam crashed on closing after interrupted raw processing. 287 ==> 219748 - Image editor doesn't import raw images twice. 288 ==> 272725 - Some PEF/jpeg files not recognized as raw/non raw 289 ==> 154922 - Start to use libopenraw instead of libraw. 290 ==> 359949 - Bad canon raw file makes digikam to crash at startup. 291 ==> 101281 - EXIF info from RAW images not woking properly. 292 ==> 342233 - Crash when decoding pentax k-r raw photo. 293 ==> 139550 - Autocorrect levels for raw photos (which are shown too dark). 294 ==> 099437 - Incorrect Libraw option. 295 ==> 155156 - Raw file conversion crash single/batch mode. 296 ==> 146259 - Raw Converter won't convert to 16bit PNG. 297 ==> 151523 - RAW converter crashes on startup. 298 ==> 242479 - Crashes on appling refocus tool to raw image. 299 ==> 361678 - Dng Converter crashes when opening a raw file (*.NEF in my case). 300 ==> 382576 - DNG Image Converter crashed trying to convert a RAW image. 301 ==> 326268 - SONY SLT A58. 302 ==> 140087 - Sony Alpha Super SteadyShot Meta Info. 303 ==> 282116 - DNG Converter fails to convert Nikon NEF to DNG. 304 ==> 270457 - Crash when opening a NIKON-jpg with 1.3 MB. 305 ==> 135011 - Sort images by EXIF date seems buggy for Nikon (or not just Nikon?). 306 ==> 141249 - CANON EOS 5D not support. 307 ==> 240750 - DNG Converter produces black files (Canon 5D mkII) 308 ==> 361660 - DNG Image Converter crashes when converting Canon cr2 file. 309 ==> 211908 - TIF (RAW from Phase One and old Canon, not TIFF/EP) opens as thumbnail. 310 ==> 388222 - high RAM memory consumption of digiKam. 311 ==> 338249 - digiKam uses all free memory and gets terminated. 312 ==> 131277 - Memory leak in image editor. 313 ==> 252443 - digiKam leaves a zombie after quit. doesn't free memory. 314 ==> 321784 - Recreating fingerprints leaks memory. 315 ==> 381877 - digiKam start allocating all memory when scans a new collection. 316 ==> 098227 - Huge memory leak when downloading from camera. 317 ==> 330227 - Image quality sorter leaks memory. 318 ==> 412893 - Application windows are incorrectly magnified in macOS. 319 ==> 413656 - Manage Tags window appears with messed up double size graphics. 320 ==> 092783 - Usabilitiy of RAW file support. 321 ==> 158911 - digiKam crashes before RAW convert. 322 ==> 362870 - DNG Convert crash on convert NEF file. 323 ==> 367859 - DNG Converter crashed after trying to nconvert CR2 file. 324 ==> 369289 - DNG converter crashes when converting Olympus (.ORF) files. 325 ==> 407203 - Crash on trying to convert a .NEF file. 326 ==> 370623 - digiKam converter to DNG report an error while to process NEF image. 327 ==> 354364 - Crash of DNG converter. 328 ==> 338842 - Moving DNG images to another album corrupts DNG properties. 329 ==> 401849 - Cannot Save Current Search. 330 ==> 372972 - Find duplicates "search in drop down" only shows 1 item. 331 ==> 351521 - digiKam crashes when searching by date. 332 ==> 335978 - digiKam crashes when using timeline. 333 ==> 335052 - Crash on looking for duplicate. 334 ==> 333952 - Crashed when trying to search for duplicates. 335 ==> 218022 - Albums are sorted alphabetically within the month. 336 ==> 326495 - Calendar: higher flexibility in the layout. 337 ==> 281848 - Search for images with no goelocation. 338 ==> 283045 - Crash when searching with timeline. 339 ==> 281895 - digiKam crashed the selection date in the calendar. 340 ==> 280760 - Find duplicates should handle raw files more intelligently. 341 ==> 182029 - Duplicate item count not changed after duplicates deleted. 342 ==> 182043 - Duplicates search result in an inconsistent/broken state. 343 ==> 182492 - No photos are displayed when selecting a timeframe in the timeline. 344 ==> 140732 - Show all pictures of year when selecting year in date-tree. 345 ==> 240738 - Batch deletion of duplicate or similar files. 346 ==> 241536 - Reproducible crash when bilding fingerprint. 347 ==> 242438 - Crashes while creating fingerprints. 348 ==> 246500 - Fingerprint. 349 ==> 246635 - Crash when rebuilding fingerprints. 350 ==> 253382 - digiKam crashs creating fingerprints. 351 ==> 265670 - digiKam crashes when generating thumbnails and fingerprints in parallel. 352 ==> 265245 - Crash upon selecting certain month in timeline. 353 ==> 261418 - Improved handling of duplicate images. 354 ==> 263002 - Crash during browsing timeline. 355 ==> 147981 - Ability to move images to different albums from seach results. 356 ==> 149025 - Do not sort images in Search by album. 357 ==> 147407 - Root folders/years should display all child images. 358 ==> 155286 - Crash during duplicate search. 359 ==> 169404 - Color selector in fuzzy search is black. 360 ==> 214665 - Crash when cleaning duplicated images. 361 ==> 243136 - Fingerprints. 362 ==> 247550 - digiKam does not quit gracefully. 363 ==> 406228 - Getting random unextpected 'Database is locked' events. 364 ==> 409884 - digiKam Crash. 365 ==> 413944 - digiKam crashes while album browsing. 366 ==> 134817 - Introduce symlinks for album collection. 367 ==> 149983 - Show recursively sub-album images. 268 ==> 226770 - digiKam edit photo shift+space. 369 ==> 237161 - digiKam crashes on album viewing. 370 ==> 240436 - digiKam crashes when selecting a photo in an album. 371 ==> 291514 - Suggestions for improving face recognition performance. 372 ==> 271679 - digiKam detects but does not recognize faces. 373 ==> 292248 - Recognize faces does nothing. 374 ==> 321297 - Name not set on face recognition run. 375 ==> 314744 - Face do not recognize. 376 ==> 402021 - Face recognition not working. 377 ==> 392518 - Face recognition using the deep learning algo, dont move faces to Unconfirmed. 378 ==> 391671 - Face recognition fails giving a lot of addition effort to correct the errors. 379 ==> 392010 - Face recognition assigns faces to people without confirmation, leading to false positives. 380 ==> 277620 - Face recognition should propose the tagged name as default in the face identification page. 381 ==> 414308 - On a person, be able to view only the news found. 382 ==> 404167 - Improve confirmation process. 383 ==> 247571 - Crash while browsing in album view. 384 ==> 263209 - New Album crash. 385 ==> 275684 - Crash during tagging. 386 ==> 297044 - digiKam crashes when moving images to a new Album. 387 ==> 305108 - Crash during import of images. 388 ==> 317440 - digiKam crashes after deleting a tag. 389 ==> 335708 - Crash on image import. 390 ==> 337839 - I experience many crashes, without precise action. 391 ==> 339720 - Very slow tagging when missing Plasma icons have been used for tags. 392 ==> 343026 - Crashes when downloading. 393 ==> 361084 - Crash while importing. 394 ==> 117561 - Should store hashes of files if file is moved. 395 ==> 186920 - Too many open files. 396 ==> 135051 - digiKam crashes when building database from existing directory. 397 ==> 135689 - New folders not visible. 398 ==> 150181 - digiKam wants to delete the whole database (24.000Images) at startup when USB-Disk with the Photos is not connected. 399 ==> 194630 - digiKam fails to start ? after Plasma update. 400 ==> 202956 - Crashing on start up. 401 ==> 204071 - Just started digiKam and it crashed. 402 ==> 218726 - digiKam always crashes at startup with segmentation fault. 403 ==> 218860 - digiKam crashes when starting from krunner. 404 ==> 220172 - digiKam crash on scanning directories [mem2chunk_check, free_check, QHashData::free_helper]. 405 ==> 242305 - digiKam Crash. 406 ==> 242818 - digiKam crash at startup. 407 ==> 246065 - digiKam crashes at startup. 408 ==> 246534 - digiKam crashes at every start. 409 ==> 250418 - digiKam crashes on startup. 410 ==> 261624 - Moving an album from one collection to another doesn't update the source collection. 411 ==> 338171 - When automatic recognition guesses the same name for multiple faces in one picture, assigning a different name deletes all the face boxes except one. 412 ==> 392016 - Confirmed and unconfirmed faces look the same in a person's face list. 413 ==> 286452 - No way to scan untagged photos for faces, or scan ALL folders. 414 ==> 411732 - UI for assigning people tag is very fragile. 415 ==> 365668 - Face tagger allows text input when hovered over, then does crazy stuff. 416 ==> 413923 - The peoples list shows just one line of height (one name) when expanded for the first time. 417 ==> 326034 - Allow to add "unknown" face tags in Preview mode. 418 ==> 388649 - Face tag rectangle cursor sometimes disappear. 419 ==> 415560 - No default selection for Scan Collection. 420 ==> 415460 - JFIF files have APP0 marker after SOI where there should be APP1. 421 ==> 415582 - People Unknown faces does not decrease to zero. 422 ==> 415599 - Add date to help DNN face recognition for baby/kid/adult distinction. 423 ==> 415602 - How to erase faces rectangles for an album. 424 ==> 316897 - Face Detection improvements by colors filtering and using EXIF orientation. 425 ==> 088895 - Stops after a few percent with: Unknown event: 3. 426 ==> 091548 - Find duplicate images claims no album is selected although one is 427 ==> 101958 - Can not select duplicate images by clicking on a line instead of the select box of 6 pix square. 428 ==> 107095 - Double image removal: Use trashcan. 429 ==> 113557 - digiKam crashes during "Find Duplicate Images" with SIGSEGV. 430 ==> 117578 - Bad UI fpr searching duplicate images. 431 ==> 181698 - Cannot delete fuzzy searches. 432 ==> 181720 - Can't rename fuzzy searches. 433 ==> 183008 - Fuzzy image search requires two clicks to navigate back. 434 ==> 199045 - Find duplicates in a directory/directories. 435 ==> 199732 - Deleting duplicated images crashes digiKam. 436 ==> 222273 - Run fingerprint scan on new images. 437 ==> 231047 - Crash when updating fingerprints on a large photo collection (100GB). 438 ==> 235763 - Reproducible crash, when attempting to rebuild all fingerprints. 439 ==> 167168 - Timeline view shows redundant extra sections. 440 ==> 168004 - Timeline gives zero height column for dates with a single entry. 441 ==> 279674 - Incorporate "event view". 442 ==> 261216 - Debugging doesn´t work with installed. 443 ==> 109022 - Iphoto like calendar search. 444 ==> 109703 - Updating of calendar view in Date panel. 445 ==> 109705 - When quit date panel with day selected no images in other panels. 446 ==> 130230 - Wrong date in header using date-view. 447 ==> 213619 - Add a new view of albums based on due date. 448 ==> 375306 - Add 'Go to album' in search results. 449 ==> 128101 - Crash when selecting "Comments&Tags" tab, after selecting "Search" tab, with no search results selected. 450 ==> 146091 - Displaying raw photos by Tag or Date takes very long to load. 451 ==> 091372 - Make searching for multiple tags possible. 452 ==> 098846 - Searching photos with no tag. 453 ==> 113806 - Is it possible to have the size of the quick search window saved? 454 ==> 114848 - Search dialog: images not rotated correctly. 455 ==> 115536 - Quick search dialog box: wrong attached tips. 456 ==> 133294 - Advanced search dialog - combo box with tag names (when tag is to be matched) doesn't stretch with the dialog. 457 ==> 149555 - Always present search box instead of search by right-clicking and selecting simple or advanced search. 458 ==> 185106 - Advanced search is not saved/restored correctly. 459 ==> 120922 - Clicking on Advanced search crashes application. 460 ==> 141035 - Advanced Search gives error when trying to search by rating. 461 ==> 147429 - "and not" option in search function. 462 ==> 415489 - Win32 7.0.0 Beta 1 Crash On Opening Preview. 463 ==> 407540 - Missing EXIF parameter in DNG when converting Panasonic RW2 images. 464 ==> 399159 - Lenses name lost on converting to DNG. 465 ==> 118396 - Ability to search through image comments. 466 ==> 139283 - IPTC Caption comment in search function 467 ==> 155735 - Make it possible to seach on IPTC-text. 468 ==> 095584 - No indication of the end of download from camera. 469 ==> 179712 - Open image in embeded editor lead to digikam crash. 470 ==> 297293 - Option to use libnotify for non-blocking errors/message. 471 ==> 316928 - Servicing: statut of each servicing option should be searchable. 472 ==> 375521 - Progress bars waste useful space. 473 ==> 218583 - Showfoto customizing toolbar freerotation. 474 ==> 228879 - Geolocation Editor: Missing progress bar. 475 ==> 272158 - More user feedback about background operations. 476 ==> 301064 - Async task UI persists on leaving geolocation UI. 477 ==> 164600 - No picture in view pane. 478 ==> 297295 - Zooming: full picture thumbnail to show position. 479 ==> 160894 - Add new picLens like 3D view mode. 480 ==> 325530 - Preview and Editor return "Failed to Load Image" error when viewing JPEG (only) files. 481 ==> 256309 - Wish to save settings in batch queue manager. 482 ==> 271198 - Make queues saveable and use them as single action. 483 ==> 287407 - Save tools with settings in BQM. 484 ==> 318771 - Saved process don't remember settings in BQM. 485 ==> 320358 - Files randomly fail to process. 486 ==> 342433 - Crashes after tagging a few batches of photos. 487 ==> 171073 - Statistic of database, all pictures? 488 ==> 119228 - Storage of images on removable media. 489 ==> 144724 - Freezing by using NAS. 490 ==> 189362 - After moving the collection on a new HD, Digikam doesn't find it. 491 ==> 249375 - Crash when device with open window is removed. 492 ==> 272918 - digiKam crash after scanning ssh network drive. 493 ==> 275559 - digiKam crashes while browsing over NFS. 494 ==> 415679 - Faces are not recognized. 495 ==> 220903 - Duplicate records in ImageComments table after migration. 496 ==> 267800 - Keywords from IPTC-Metadata are not imported. 497 ==> 280678 - MIGRATION : cannot migrate from sqlite database. 498 ==> 301075 - MySQL : external database empty after migration. 499 ==> 328729 - digiKam crash while switching MySQL to SQLite 500 ==> 329849 - MIGRATION : db conversion fails. 501 ==> 361809 - Migration failed from SQLite to MySQL. 502 ==> 415700 - "Welcome to" screen still mentions v5.0. 503 ==> 415566 - Use existing face rectangles to improve recognition. 504 ==> 415603 - Add "Face Recognition" optional item in toolbar. 505 ==> 415592 - When renaming a tag, if I say no to updating, clicking Save won't trigger update again. 506 ==> 415766 - HEIF thumbnails are not shown in Timeline view. 507 ==> 396734 - Error while executing DBAction [ "UpdateSchemaFromV7ToV9" ] Statement [ "DROP TRIGGER IF EXISTS delete_image;" ]. 508 ==> 268204 - MYSQL : file-names are case-INsensitive. 509 ==> 383927 - OpenSuse digikam.coredb: Core database: schema update to V 8 failed! 510 ==> 319420 - Failed to update the database schema from version 5 to version 6. 511 ==> 288599 - Schema update to V6 failed. 512 ==> 218571 - Schema update version 4 to 5 failed. 513 ==> 230606 - digiKam doesn’t display images imported from database. 514 ==> 288839 - Failed to update the database schema from version 5 to version 6. 515 ==> 392179 - Database Upgrade to v9 fails. 516 ==> 396765 - Schema update to V 9 failed. 517 ==> 190411 - Metadata are invisible on older pictures. 518 ==> 110066 - Md5 Checksums to identify pictures. 519 ==> 415767 - Thumbnails for Portrait oriented images aren't rotated properly. 520 ==> 415791 - TimeAdjust can not adjust time by 1 unit - only by 2. 521 ==> 415557 - Face detection - Advanced settings : restricted folder scope not taken into account. 522 ==> 415702 - Cannot abort or stop find Duplicate process. 523 ==> 415796 - Face thumbnail zoom level too wide. 524 ==> 366551 - Unclear icon-item overlay when associating people to faces. 525 ==> 415535 - Import from a local/remote folder. 526 ==> 415643 - digiKam crashes Face detection. 527 ==> 415685 - digiKam crashes when scanning faces. 528 ==> 415877 - "Reset" button in Tag Manager not always working to reset icon. 529 ==> 415561 - Face detection based on deep learning leads to no result under Windows. 530 ==> 415920 - The unconfirmed faces count in the left sidebar is missing. 531 ==> 415944 - 7.0.0beta2 removed all my Geolocation bookmarks. 532 ==> 414016 - Log file /var/log/syslog grows in size very fast. 533 ==> 414028 - digiKam spamming journal with details of almost everything it does... 534 ==> 415882 - digiKam 6.4 crashes on processing a large number of new HEIC/HEIF files. 535 ==> 414115 - G'mic plugin not available on Mac. 536 ==> 416018 - Face detection: specified list of albums not taken into account. 537 ==> 416028 - "Search in Tags" selection for Face detection/recognition unduly changes. -538 ==> +538 ==> 416120 - Error transfering. +539 ==> diff --git a/core/data/geolocation/geoiface/backend-googlemaps.html b/core/data/geolocation/geoiface/backend-googlemaps.html index 0fa1878ea9..1563b9d5b5 100644 --- a/core/data/geolocation/geoiface/backend-googlemaps.html +++ b/core/data/geolocation/geoiface/backend-googlemaps.html @@ -1,9 +1,9 @@ - +
diff --git a/core/dplugins/generic/webservices/facebook/fbtalker.cpp b/core/dplugins/generic/webservices/facebook/fbtalker.cpp index 855aa15b9c..22fb4f2586 100644 --- a/core/dplugins/generic/webservices/facebook/fbtalker.cpp +++ b/core/dplugins/generic/webservices/facebook/fbtalker.cpp @@ -1,860 +1,832 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-12-26 * Description : a tool to export items to Facebook web service * * Copyright (C) 2008-2010 by Luka Renko * Copyright (c) 2011 by Dirk Tilger * Copyright (C) 2008-2020 by Gilles Caulier * Copyright (C) 2018 by Thanh Trung Dinh * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "fbtalker.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include +// KDE includes + +#include + // Local includes #include "digikam_debug.h" -#include "digikam_config.h" #include "digikam_version.h" #include "fbmpform.h" #include "wstoolutils.h" - -#ifdef HAVE_QWEBENGINE -# include "webwidget_qwebengine.h" -#else -# include "webwidget.h" -#endif +#include "webbrowserdlg.h" using namespace Digikam; namespace DigikamGenericFaceBookPlugin { bool operator< (const FbUser& first, const FbUser& second) { return first.name < second.name; } bool operator< (const FbAlbum& first, const FbAlbum& second) { return first.title < second.title; } // ----------------------------------------------------------------------------- class Q_DECL_HIDDEN FbTalker::Private { public: enum State { FB_GETLOGGEDINUSER = 0, FB_LOGOUTUSER, FB_LISTALBUMS, FB_CREATEALBUM, FB_ADDPHOTO }; public: explicit Private() - { - apiURL = QLatin1String("https://graph.facebook.com/%1/%2"); - authUrl = QLatin1String("https://www.facebook.com/dialog/oauth"); - tokenUrl = QLatin1String("https://graph.facebook.com/oauth/access_token"); - redirectUrl = QLatin1String("https://www.facebook.com/connect/login_success.html"); - scope = QLatin1String("user_photos,publish_pages,manage_pages"); //publish_to_groups,user_friends not necessary? - apikey = QLatin1String("400589753481372"); - clientSecret = QLatin1String("5b0b5cd096e110cd4f4c72f517e2c544"); - - serviceName = QLatin1String("Facebook"); - serviceTime = QLatin1String("token_time"); - serviceKey = QLatin1String("access_token"); - - dialog = nullptr; - parent = nullptr; - settings = nullptr; - netMngr = nullptr; - reply = nullptr; - view = nullptr; - state = FB_GETLOGGEDINUSER; + : dialog(nullptr), + parent(nullptr), + settings(nullptr), + netMngr(nullptr), + reply(nullptr), + browser(nullptr), + state(FB_GETLOGGEDINUSER) + { + apiURL = QLatin1String("https://graph.facebook.com/%1/%2"); + authUrl = QLatin1String("https://www.facebook.com/dialog/oauth"); + tokenUrl = QLatin1String("https://graph.facebook.com/oauth/access_token"); + redirectUrl = QLatin1String("https://www.facebook.com/connect/login_success.html"); + scope = QLatin1String("user_photos,publish_pages,manage_pages"); //publish_to_groups,user_friends not necessary? + apikey = QLatin1String("400589753481372"); + clientSecret = QLatin1String("5b0b5cd096e110cd4f4c72f517e2c544"); + + serviceName = QLatin1String("Facebook"); + serviceTime = QLatin1String("token_time"); + serviceKey = QLatin1String("access_token"); } QString apiURL; QString authUrl; QString tokenUrl; QString redirectUrl; QString scope; QString apikey; QString clientSecret; QString accessToken; QString serviceName; QString serviceTime; QString serviceKey; QDateTime expiryTime; QDialog* dialog; QWidget* parent; QSettings* settings; QNetworkAccessManager* netMngr; QNetworkReply* reply; - WebWidget* view; + WebBrowserDlg* browser; State state; FbUser user; }; // ----------------------------------------------------------------------------- FbTalker::FbTalker(QWidget* const parent) : d(new Private()) { d->parent = parent; d->netMngr = new QNetworkAccessManager(this); - d->settings = WSToolUtils::getOauthSettings(this); connect(this, SIGNAL(linkingSucceeded()), this, SLOT(slotLinkingSucceeded())); connect(d->netMngr, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotFinished(QNetworkReply*))); } FbTalker::~FbTalker() { if (d->reply) { d->reply->abort(); } - clearCookies(); - delete d; } // ---------------------------------------------------------------------------------------------- void FbTalker::link() { emit signalBusy(true); emit signalLoginProgress(1, 3); QUrl url(d->authUrl); QUrlQuery query(url); query.addQueryItem(QLatin1String("client_id"), d->apikey); query.addQueryItem(QLatin1String("response_type"), QLatin1String("token")); query.addQueryItem(QLatin1String("redirect_uri"), d->redirectUrl); query.addQueryItem(QLatin1String("scope"), d->scope); url.setQuery(query); - if (!d->view) - { - d->view = new WebWidget(d->parent); - d->view->setWindowFlags(Qt::Dialog); - d->view->resize(800, 600); + delete d->browser; + d->browser = new WebBrowserDlg(url, d->parent, true); + d->browser->setModal(true); - connect(d->view, SIGNAL(urlChanged(QUrl)), - this, SLOT(slotCatchUrl(QUrl))); + connect(d->browser, SIGNAL(urlChanged(QUrl)), + this, SLOT(slotCatchUrl(QUrl))); - connect(d->view, SIGNAL(closeView(bool)), - this, SIGNAL(signalBusy(bool))); - } + connect(d->browser, SIGNAL(closeView(bool)), + this, SIGNAL(signalBusy(bool))); - d->view->load(url); - d->view->show(); + d->browser->show(); } void FbTalker::unlink() { d->accessToken = QString(); d->user = FbUser(); d->settings->beginGroup(d->serviceName); d->settings->remove(QString()); d->settings->endGroup(); - clearCookies(); - emit linkingSucceeded(); } void FbTalker::cancel() { if (d->reply) { d->reply->abort(); d->reply = nullptr; } emit signalBusy(false); } void FbTalker::slotLinkingSucceeded() { if (d->accessToken.isEmpty()) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "UNLINK to Facebook"; emit signalBusy(false); return; } qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Facebook"; - if (d->view) + if (d->browser) { - d->view->close(); + d->browser->close(); } getLoggedInUser(); } void FbTalker::slotCatchUrl(const QUrl& url) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received URL from webview:" << url; QString str = url.toString(); QUrlQuery query(str.section(QLatin1Char('#'), -1, -1)); if (query.hasQueryItem(QLatin1String("access_token"))) { d->accessToken = query.queryItemValue(QLatin1String("access_token")); int seconds = query.queryItemValue(QLatin1String("expires_in")).toInt(); d->expiryTime = QDateTime::currentDateTime().addSecs(seconds); writeSettings(); qDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token received"; emit linkingSucceeded(); } else { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "No access token in URL"; emit signalBusy(false); } } FbUser FbTalker::getUser() const { return d->user; } bool FbTalker::linked() { return (!d->accessToken.isEmpty()); } -void FbTalker::clearCookies() -{ -#ifdef HAVE_QWEBENGINE - if (d->view) - { - d->view->page()->profile()->cookieStore()->deleteAllCookies(); - } -#else - if (d->view) - { - d->view->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); - } -#endif -} - void FbTalker::getLoggedInUser() { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "getLoggedInUser called"; if (d->reply) { d->reply->abort(); d->reply = nullptr; } emit signalBusy(true); emit signalLoginProgress(2, 3); QUrl url(d->apiURL.arg(QLatin1String("me")).arg(QString())); QUrlQuery q; q.addQueryItem(QLatin1String("access_token"), d->accessToken); url.setQuery(q); QNetworkRequest netRequest(url); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/x-www-form-urlencoded")); d->reply = d->netMngr->get(netRequest); d->state = Private::FB_GETLOGGEDINUSER; } // ---------------------------------------------------------------------------------------------- void FbTalker::logout() { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "logout called"; if (d->reply) { d->reply->abort(); d->reply = nullptr; } emit signalBusy(true); QUrl url(QLatin1String("https://www.facebook.com/logout.php")); QUrlQuery q; q.addQueryItem(QLatin1String("next"), d->redirectUrl); q.addQueryItem(QLatin1String("access_token"), d->accessToken); url.setQuery(q); QNetworkRequest netRequest(url); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/x-www-form-urlencoded")); d->reply = d->netMngr->get(netRequest); d->state = Private::FB_LOGOUTUSER; } //---------------------------------------------------------------------------------------------------- void FbTalker::listAlbums(long long userID) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Requesting albums for user" << userID; if (d->reply) { d->reply->abort(); d->reply = nullptr; } emit signalBusy(true); emit signalLoginProgress(1, 3); QUrl url; /* * If userID is specified, load albums of that user, * else load albums of current user */ if (!userID) { url = QUrl(d->apiURL.arg(d->user.id) .arg(QLatin1String("albums"))); } else { url = QUrl(d->apiURL.arg(userID) .arg(QLatin1String("albums"))); } QUrlQuery q; q.addQueryItem(QLatin1String("fields"), QLatin1String("id,name,description,privacy,link,location")); q.addQueryItem(QLatin1String("access_token"), d->accessToken); url.setQuery(q); QNetworkRequest netRequest(url); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/x-www-form-urlencoded")); d->reply = d->netMngr->get(netRequest); d->state = Private::FB_LISTALBUMS; } void FbTalker::createAlbum(const FbAlbum& album) { if (d->reply) { d->reply->abort(); d->reply = nullptr; } emit signalBusy(true); QUrlQuery params; params.addQueryItem(QLatin1String("access_token"), d->accessToken); params.addQueryItem(QLatin1String("name"), album.title); if (!album.location.isEmpty()) params.addQueryItem(QLatin1String("location"), album.location); /* * description is deprecated and now a param of message */ if (!album.description.isEmpty()) params.addQueryItem(QLatin1String("message"), album.description); // TODO (Dirk): Wasn't that a requested feature in Bugzilla? switch (album.privacy) { case FB_ME: params.addQueryItem(QLatin1String("privacy"), QLatin1String("{'value':'SELF'}")); break; case FB_FRIENDS: params.addQueryItem(QLatin1String("privacy"), QLatin1String("{'value':'ALL_FRIENDS'}")); break; case FB_FRIENDS_OF_FRIENDS: params.addQueryItem(QLatin1String("privacy"), QLatin1String("{'value':'FRIENDS_OF_FRIENDS'}")); break; case FB_EVERYONE: params.addQueryItem(QLatin1String("privacy"), QLatin1String("{'value':'EVERYONE'}")); break; case FB_CUSTOM: //TODO params.addQueryItem(QLatin1String("privacy"), QLatin1String("{'value':'CUSTOM'}")); break; } QUrl url(QUrl(d->apiURL.arg(d->user.id) .arg(QLatin1String("albums")))); url.setQuery(params); QNetworkRequest netRequest(url); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/x-www-form-urlencoded")); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "url to create new album" << netRequest.url() << params.query(); d->reply = d->netMngr->post(netRequest, params.query().toUtf8()); d->state = Private::FB_CREATEALBUM; } void FbTalker::addPhoto(const QString& imgPath, const QString& albumID, const QString& caption) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Adding photo" << imgPath << "to album with id" << albumID << "using caption '" << caption << "'"; if (d->reply) { d->reply->abort(); d->reply = nullptr; } emit signalBusy(true); QMap args; args[QLatin1String("access_token")] = d->accessToken; if (!caption.isEmpty()) args[QLatin1String("message")] = caption; FbMPForm form; for (QMap::const_iterator it = args.constBegin() ; it != args.constEnd() ; ++it) { form.addPair(it.key(), it.value()); } qCDebug(DIGIKAM_WEBSERVICES_LOG) << "FORM:" << endl << form.formData(); if (!form.addFile(QUrl::fromLocalFile(imgPath).fileName(), imgPath)) { emit signalAddPhotoDone(666, i18n("Cannot open file")); emit signalBusy(false); return; } form.finish(); QVariant arg_1; if (albumID.isEmpty()) { arg_1 = d->user.id; } else { arg_1 = albumID; } QNetworkRequest netRequest(QUrl(d->apiURL.arg(arg_1.toString()).arg(QLatin1String("photos")))); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType()); d->reply = d->netMngr->post(netRequest, form.formData()); d->state = Private::FB_ADDPHOTO; } //---------------------------------------------------------------------------------------------------- QString FbTalker::errorToText(int errCode, const QString &errMsg) { QString transError; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "errorToText:" << errCode << ":" << errMsg; switch (errCode) { case 0: transError = QLatin1String(""); break; case 2: transError = i18n("The service is not available at this time."); break; case 4: transError = i18n("The application has reached the maximum number of requests allowed."); break; case 102: transError = i18n("Invalid session key or session expired. Try to log in again."); break; case 120: transError = i18n("Invalid album ID."); break; case 321: transError = i18n("Album is full."); break; case 324: transError = i18n("Missing or invalid file."); break; case 325: transError = i18n("Too many unapproved photos pending."); break; default: transError = errMsg; break; } return transError; } void FbTalker::slotFinished(QNetworkReply* reply) { if (reply != d->reply) { return; } d->reply = nullptr; if (reply->error() != QNetworkReply::NoError) { if (d->state == Private::FB_ADDPHOTO) { emit signalBusy(false); emit signalAddPhotoDone(reply->error(), reply->errorString()); } else { emit signalBusy(false); QMessageBox::critical(QApplication::activeWindow(), i18n("Error"), reply->errorString()); } reply->deleteLater(); return; } QByteArray buffer = reply->readAll(); switch(d->state) { case (Private::FB_GETLOGGEDINUSER): parseResponseGetLoggedInUser(buffer); break; case (Private::FB_LOGOUTUSER): parseResponseLogoutUser(); break; case (Private::FB_LISTALBUMS): parseResponseListAlbums(buffer); break; case (Private::FB_CREATEALBUM): parseResponseCreateAlbum(buffer); break; case (Private::FB_ADDPHOTO): parseResponseAddPhoto(buffer); break; } reply->deleteLater(); } int FbTalker::parseErrorResponse(const QDomElement& e, QString& errMsg) { int errCode = -1; for (QDomNode node = e.firstChild() ; !node.isNull() ; node = node.nextSibling()) { if (!node.isElement()) continue; if (node.nodeName() == QLatin1String("error_code")) { errCode = node.toElement().text().toInt(); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error Code:" << errCode; } else if (node.nodeName() == QLatin1String("error_msg")) { errMsg = node.toElement().text(); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error Text:" << errMsg; } } return errCode; } void FbTalker::parseResponseGetLoggedInUser(const QByteArray& data) { QString errMsg; QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Logged in data" << doc; if (err.error != QJsonParseError::NoError) { emit signalBusy(false); return; } QJsonObject jsonObject = doc.object(); d->user.id = jsonObject[QLatin1String("id")].toString(); if (!(QString::compare(jsonObject[QLatin1String("id")].toString(), QLatin1String(""), Qt::CaseInsensitive) == 0)) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "ID found in response of GetLoggedInUser"; } d->user.name = jsonObject[QLatin1String("name")].toString(); d->user.profileURL = jsonObject[QLatin1String("link")].toString(); emit signalLoginDone(0, QString()); } void FbTalker::parseResponseAddPhoto(const QByteArray& data) { qCDebug(DIGIKAM_WEBSERVICES_LOG) <<"Parse Add Photo data is" << data; int errCode = -1; QString errMsg; QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { emit signalBusy(false); return; } QJsonObject jsonObject = doc.object(); if (jsonObject.contains(QLatin1String("id"))) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Id of photo exported is" << jsonObject[QLatin1String("id")].toString(); errCode = 0; } if (jsonObject.contains(QLatin1String("error"))) { QJsonObject obj = jsonObject[QLatin1String("error")].toObject(); errCode = obj[QLatin1String("code")].toInt(); errMsg = obj[QLatin1String("message")].toString(); } qCDebug(DIGIKAM_WEBSERVICES_LOG) << "add photo:" << doc; emit signalBusy(false); emit signalAddPhotoDone(errCode, errorToText(errCode, errMsg)); } void FbTalker::parseResponseCreateAlbum(const QByteArray& data) { qCDebug(DIGIKAM_WEBSERVICES_LOG) <<"Parse Create album data is" << data; int errCode = -1; QString errMsg; QString newAlbumID; QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { emit signalBusy(false); return; } QJsonObject jsonObject = doc.object(); if (jsonObject.contains(QLatin1String("id"))) { newAlbumID = jsonObject[QLatin1String("id")].toString(); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Id of album created is" << newAlbumID; errCode = 0; } if (jsonObject.contains(QLatin1String("error"))) { QJsonObject obj = jsonObject[QLatin1String("error")].toObject(); errCode = obj[QLatin1String("code")].toInt(); errMsg = obj[QLatin1String("message")].toString(); } qCDebug(DIGIKAM_WEBSERVICES_LOG) << "error create photo:" << doc; emit signalBusy(false); emit signalCreateAlbumDone(errCode, errorToText(errCode, errMsg), newAlbumID); } void FbTalker::parseResponseListAlbums(const QByteArray& data) { int errCode = -1; QString errMsg; QJsonParseError err; QList albumsList; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { emit signalBusy(false); return; } QJsonObject jsonObject = doc.object(); if (jsonObject.contains(QLatin1String("data"))) { QJsonArray jsonArray = jsonObject[QLatin1String("data")].toArray(); foreach (const QJsonValue& value, jsonArray) { QJsonObject obj = value.toObject(); FbAlbum album; album.id = obj[QLatin1String("id")].toString(); album.title = obj[QLatin1String("name")].toString(); album.location = obj[QLatin1String("location")].toString(); album.url = obj[QLatin1String("link")].toString(); album.description = obj[QLatin1String("description")].toString(); album.uploadable = obj[QLatin1String("can_upload")].toBool(); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "can_upload " << album.uploadable; if (QString::compare(obj[QLatin1String("privacy")].toString(), QLatin1String("ALL_FRIENDS"), Qt::CaseInsensitive) == 0) { album.privacy = FB_FRIENDS; } else if (QString::compare(obj[QLatin1String("privacy")].toString(), QLatin1String("FRIENDS_OF_FRIENDS"), Qt::CaseInsensitive) == 0) { album.privacy = FB_FRIENDS; } else if (QString::compare(obj[QLatin1String("privacy")].toString(), QLatin1String("EVERYONE"), Qt::CaseInsensitive) == 0) { album.privacy = FB_EVERYONE; } else if (QString::compare(obj[QLatin1String("privacy")].toString(), QLatin1String("CUSTOM"), Qt::CaseInsensitive) == 0) { album.privacy = FB_CUSTOM; } else if (QString::compare(obj[QLatin1String("privacy")].toString(), QLatin1String("SELF"), Qt::CaseInsensitive) == 0) { album.privacy = FB_ME; } albumsList.append(album); } errCode = 0; } if (jsonObject.contains(QLatin1String("error"))) { QJsonObject obj = jsonObject[QLatin1String("error")].toObject(); errCode = obj[QLatin1String("code")].toInt(); errMsg = obj[QLatin1String("message")].toString(); } std::sort(albumsList.begin(), albumsList.end()); emit signalBusy(false); emit signalListAlbumsDone(errCode, errorToText(errCode, errMsg), albumsList); } void FbTalker::writeSettings() { d->settings->beginGroup(d->serviceName); d->settings->setValue(d->serviceTime, d->expiryTime); d->settings->setValue(d->serviceKey, d->accessToken); d->settings->endGroup(); } void FbTalker::readSettings() { d->settings->beginGroup(d->serviceName); d->expiryTime = d->settings->value(d->serviceTime).toDateTime(); d->accessToken = d->settings->value(d->serviceKey).toString(); d->settings->endGroup(); if (d->accessToken.isEmpty()) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token is empty"; emit signalLoginDone(-1, QString()); } else if (QDateTime::currentDateTime() > d->expiryTime) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token has expired"; d->accessToken = QString(); emit signalLoginDone(-1, QString()); } else { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Already Linked"; emit linkingSucceeded(); } } void FbTalker::parseResponseLogoutUser() { unlink(); emit signalLoginDone(-1, QString()); } } // namespace DigikamGenericFaceBookPlugin diff --git a/core/dplugins/generic/webservices/facebook/fbtalker.h b/core/dplugins/generic/webservices/facebook/fbtalker.h index 09100326ed..6ce32ae545 100644 --- a/core/dplugins/generic/webservices/facebook/fbtalker.h +++ b/core/dplugins/generic/webservices/facebook/fbtalker.h @@ -1,112 +1,111 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-12-26 * Description : a tool to export items to Facebook web service * * Copyright (C) 2008-2009 by Luka Renko * Copyright (C) 2008-2020 by Gilles Caulier * Copyright (C) 2018 by Thanh Trung Dinh * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_FB_TALKER_H #define DIGIKAM_FB_TALKER_H // Qt includes #include #include #include #include #include #include #include // Local includes #include "fbitem.h" class QDomElement; namespace DigikamGenericFaceBookPlugin { class FbTalker : public QObject { Q_OBJECT public: explicit FbTalker(QWidget* const parent); ~FbTalker(); void link(); void unlink(); bool linked(); void cancel(); FbUser getUser() const; void logout(); void listAlbums(long long userID = 0); void createAlbum(const FbAlbum& album); void addPhoto(const QString& imgPath, const QString& albumID, const QString& caption); void readSettings(); void writeSettings(); Q_SIGNALS: void signalBusy(bool val); void signalListAlbumsDone(int errCode, const QString& errMsg, const QList & albumsList); void signalCreateAlbumDone(int errCode, const QString& errMsg, const QString& newAlbumId); void signalAddPhotoDone(int errCode, const QString& errMsg); void signalLoginProgress(int step, int maxStep = 0, const QString& label = QString()); void signalLoginDone(int errCode, const QString& errMsg); void linkingSucceeded(); private: void getLoggedInUser(); - void clearCookies(); QString errorToText(int errCode, const QString& errMsg); int parseErrorResponse(const QDomElement& e, QString& errMsg); void parseResponseGetLoggedInUser(const QByteArray& data); void parseResponseAddPhoto(const QByteArray& data); void parseResponseCreateAlbum(const QByteArray& data); void parseResponseListAlbums(const QByteArray& data); void parseResponseLogoutUser(); private Q_SLOTS: void slotLinkingSucceeded(); void slotCatchUrl(const QUrl& url); void slotFinished(QNetworkReply* reply); private: class Private; Private* const d; }; } // namespace DigikamGenericFaceBookPlugin #endif // DIGIKAM_FB_TALKER_H diff --git a/core/dplugins/generic/webservices/flickr/flickrtalker.cpp b/core/dplugins/generic/webservices/flickr/flickrtalker.cpp index 61379d9e9f..0bb2d65c82 100644 --- a/core/dplugins/generic/webservices/flickr/flickrtalker.cpp +++ b/core/dplugins/generic/webservices/flickr/flickrtalker.cpp @@ -1,1221 +1,1209 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-07-07 * Description : a tool to export images to Flickr web service * * Copyright (C) 2005-2009 by Vardhman Jain * Copyright (C) 2009-2020 by Gilles Caulier * Copyright (C) 2017-2019 by Maik Qualmann * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "flickrtalker.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include // KDE includes #include // Local includes #include "dmetadata.h" #include "wstoolutils.h" #include "flickrmpform.h" #include "flickrwindow.h" +#include "webbrowserdlg.h" #include "digikam_debug.h" #include "digikam_config.h" #include "digikam_version.h" #include "previewloadthread.h" -#ifdef HAVE_QWEBENGINE -# include "webwidget_qwebengine.h" -#else -# include "webwidget.h" -#endif - // OAuth2 library includes #if defined(Q_CC_CLANG) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wextra-semi" #endif #include "o1.h" #include "o0globals.h" #include "o1requestor.h" #include "o0settingsstore.h" #if defined(Q_CC_CLANG) # pragma clang diagnostic pop #endif namespace DigikamGenericFlickrPlugin { class Q_DECL_HIDDEN FlickrTalker::Private { public: explicit Private() + : parent(nullptr), + netMngr(nullptr), + reply(nullptr), + settings(nullptr), + state(FE_LOGOUT), + iface(nullptr), + o1(nullptr), + store(nullptr), + requestor(nullptr), + browser(nullptr) { - parent = nullptr; - netMngr = nullptr; - reply = nullptr; - settings = nullptr; - state = FE_LOGOUT; - iface = nullptr; - o1 = nullptr; - store = nullptr; - requestor = nullptr; - view = nullptr; + apiUrl = QLatin1String("https://www.flickr.com/services/rest/"); + authUrl = QLatin1String("https://www.flickr.com/services/oauth/authorize?perms=write"); + tokenUrl = QLatin1String("https://www.flickr.com/services/oauth/request_token"); + accessUrl = QLatin1String("https://www.flickr.com/services/oauth/access_token"); + uploadUrl = QLatin1String("https://up.flickr.com/services/upload/"); + callbackUrl = QLatin1String("https://www.flickr.com"); + + apikey = QLatin1String("74f882bf4dabe22baaaace1f6d33c66b"); + secret = QLatin1String("537d58e3ead2d6d5"); } QWidget* parent; QString serviceName; QString apiUrl; QString authUrl; QString tokenUrl; QString accessUrl; QString uploadUrl; QString callbackUrl; QString apikey; QString secret; QString maxSize; QString username; QString userId; QString lastTmpFile; QNetworkAccessManager* netMngr; QNetworkReply* reply; QSettings* settings; State state; DInfoInterface* iface; O1* o1; O0SettingsStore* store; O1Requestor* requestor; - WebWidget* view; + WebBrowserDlg* browser; }; FlickrTalker::FlickrTalker(QWidget* const parent, const QString& serviceName, DInfoInterface* const iface) : d(new Private) { d->parent = parent; d->serviceName = serviceName; d->iface = iface; m_photoSetsList = nullptr; m_authProgressDlg = nullptr; - d->apiUrl = QLatin1String("https://www.flickr.com/services/rest/"); - d->authUrl = QLatin1String("https://www.flickr.com/services/oauth/authorize?perms=write"); - d->tokenUrl = QLatin1String("https://www.flickr.com/services/oauth/request_token"); - d->accessUrl = QLatin1String("https://www.flickr.com/services/oauth/access_token"); - d->uploadUrl = QLatin1String("https://up.flickr.com/services/upload/"); - d->callbackUrl = QLatin1String("https://www.flickr.com"); - - d->apikey = QLatin1String("74f882bf4dabe22baaaace1f6d33c66b"); - d->secret = QLatin1String("537d58e3ead2d6d5"); - - d->netMngr = new QNetworkAccessManager(this); + d->netMngr = new QNetworkAccessManager(this); connect(d->netMngr, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotFinished(QNetworkReply*))); /* Initialize selected photo set as empty. */ m_selectedPhotoSet = FPhotoSet(); /* Initialize photo sets list. */ m_photoSetsList = new QLinkedList(); d->o1 = new O1(this); d->o1->setLocalPort(80); d->o1->setClientId(d->apikey); d->o1->setClientSecret(d->secret); d->o1->setCallbackUrl(d->callbackUrl); d->o1->setAuthorizeUrl(QUrl(d->authUrl)); d->o1->setAccessTokenUrl(QUrl(d->accessUrl)); d->o1->setRequestTokenUrl(QUrl(d->tokenUrl)); d->o1->setUseExternalWebInterceptor(true); d->settings = WSToolUtils::getOauthSettings(this); d->store = new O0SettingsStore(d->settings, QLatin1String(O2_ENCRYPTION_KEY), this); d->store->setGroupKey(d->serviceName); d->o1->setStore(d->store); connect(d->o1, SIGNAL(linkingFailed()), this, SLOT(slotLinkingFailed())); connect(d->o1, SIGNAL(linkingSucceeded()), this, SLOT(slotLinkingSucceeded())); connect(d->o1, SIGNAL(openBrowser(QUrl)), this, SLOT(slotOpenBrowser(QUrl))); d->requestor = new O1Requestor(d->netMngr, d->o1, this); } FlickrTalker::~FlickrTalker() { if (d->reply) { d->reply->abort(); } WSToolUtils::removeTemporaryDir(d->serviceName.toLatin1().constData()); delete m_photoSetsList; delete d; } void FlickrTalker::link(const QString& userName) { emit signalBusy(true); if (userName.isEmpty()) { d->store->setGroupKey(d->serviceName); } else { d->store->setGroupKey(d->serviceName + userName); } d->o1->link(); } void FlickrTalker::unLink() { d->o1->unlink(); } void FlickrTalker::removeUserName(const QString& userName) { if (userName.startsWith(d->serviceName)) { d->settings->beginGroup(userName); d->settings->remove(QString()); d->settings->endGroup(); } } void FlickrTalker::slotCatchUrl(const QUrl& url) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received URL from webview:" << url; QString str = url.toString(); QUrlQuery query(str.section(QLatin1Char('?'), -1, -1)); if (query.hasQueryItem(QLatin1String("oauth_token"))) { QMultiMap queryParams; queryParams.insert(QLatin1String("oauth_token"), query.queryItemValue(QLatin1String("oauth_token"))); queryParams.insert(QLatin1String("oauth_verifier"), query.queryItemValue(QLatin1String("oauth_verifier"))); d->o1->onVerificationReceived(queryParams); } } void FlickrTalker::slotLinkingFailed() { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Flickr fail"; d->username = QString(); emit signalBusy(false); } void FlickrTalker::slotLinkingSucceeded() { if (!d->o1->linked()) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "UNLINK to Flickr ok"; d->username = QString(); return; } - if (d->view) + if (d->browser) { - d->view->close(); + d->browser->close(); } qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Flickr ok"; d->username = d->o1->extraTokens()[QLatin1String("username")].toString(); d->userId = d->o1->extraTokens()[QLatin1String("user_nsid")].toString(); if (d->store->groupKey() == d->serviceName) { d->settings->beginGroup(d->serviceName); QStringList keys = d->settings->allKeys(); d->settings->endGroup(); foreach (const QString& key, keys) { d->settings->beginGroup(d->serviceName); QVariant value = d->settings->value(key); d->settings->endGroup(); d->settings->beginGroup(d->serviceName + d->username); d->settings->setValue(key, value); d->settings->endGroup(); } d->store->setGroupKey(d->serviceName + d->username); removeUserName(d->serviceName); } emit signalLinkingSucceeded(); } void FlickrTalker::slotOpenBrowser(const QUrl& url) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Open Browser... (" << url << ")"; - delete d->view; - d->view = new WebWidget(d->parent); - d->view->resize(800, 600); + delete d->browser; + d->browser = new WebBrowserDlg(url, d->parent, true); + d->browser->setModal(true); - connect(d->view, SIGNAL(urlChanged(QUrl)), + connect(d->browser, SIGNAL(urlChanged(QUrl)), this, SLOT(slotCatchUrl(QUrl))); -#ifdef HAVE_QWEBENGINE - d->view->page()->profile()->cookieStore()->deleteAllCookies(); -#else - d->view->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); -#endif + connect(d->browser, SIGNAL(closeView(bool)), + this, SIGNAL(signalBusy(bool))); - d->view->setWindowFlags(Qt::Dialog); - d->view->load(url); - d->view->show(); + d->browser->show(); } QString FlickrTalker::getMaxAllowedFileSize() { return d->maxSize; } void FlickrTalker::maxAllowedFileSize() { if (d->reply) { d->reply->abort(); d->reply = nullptr; } if (!d->o1->linked()) return; QUrl url(d->apiUrl); QNetworkRequest netRequest(url); QList reqParams = QList(); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM)); reqParams << O0RequestParameter("method", "flickr.people.getLimits"); QByteArray postData = O1::createQueryParameters(reqParams); d->reply = d->requestor->post(netRequest, reqParams, postData); d->state = FE_GETMAXSIZE; m_authProgressDlg->setLabelText(i18n("Getting the maximum allowed file size.")); m_authProgressDlg->setMaximum(4); m_authProgressDlg->setValue(1); emit signalBusy(true); } void FlickrTalker::listPhotoSets() { if (d->reply) { d->reply->abort(); d->reply = nullptr; } if (!d->o1->linked()) return; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "List photoset invoked"; QUrl url(d->apiUrl); QNetworkRequest netRequest(url); QList reqParams = QList(); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM)); reqParams << O0RequestParameter("method", "flickr.photosets.getList"); QByteArray postData = O1::createQueryParameters(reqParams); d->reply = d->requestor->post(netRequest, reqParams, postData); d->state = FE_LISTPHOTOSETS; emit signalBusy(true); } void FlickrTalker::getPhotoProperty(const QString& method, const QStringList& argList) { if (d->reply) { d->reply->abort(); d->reply = nullptr; } if (!d->o1->linked()) return; QUrl url(d->apiUrl); QNetworkRequest netRequest(url); QList reqParams = QList(); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM)); reqParams << O0RequestParameter("method", method.toLatin1()); for (QStringList::const_iterator it = argList.constBegin(); it != argList.constEnd(); ++it) { QStringList str = (*it).split(QLatin1Char('='), QString::SkipEmptyParts); reqParams << O0RequestParameter(str[0].toLatin1(), str[1].toLatin1()); } QByteArray postData = O1::createQueryParameters(reqParams); d->reply = d->requestor->post(netRequest, reqParams, postData); d->state = FE_GETPHOTOPROPERTY; emit signalBusy(true); } void FlickrTalker::listPhotos(const QString& /*albumName*/) { // TODO } void FlickrTalker::createPhotoSet(const QString& /*albumName*/, const QString& albumTitle, const QString& albumDescription, const QString& primaryPhotoId) { if (d->reply) { d->reply->abort(); d->reply = nullptr; } if (!d->o1->linked()) return; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Create photoset invoked"; QUrl url(d->apiUrl); QNetworkRequest netRequest(url); QList reqParams = QList(); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM)); reqParams << O0RequestParameter("method", "flickr.photosets.create"); reqParams << O0RequestParameter("title", albumTitle.toLatin1()); reqParams << O0RequestParameter("description", albumDescription.toLatin1()); reqParams << O0RequestParameter("primary_photo_id", primaryPhotoId.toLatin1()); QByteArray postData = O1::createQueryParameters(reqParams); d->reply = d->requestor->post(netRequest, reqParams, postData); d->state = FE_CREATEPHOTOSET; emit signalBusy(true); } void FlickrTalker::addPhotoToPhotoSet(const QString& photoId, const QString& photoSetId) { if (d->reply) { d->reply->abort(); d->reply = nullptr; } if (!d->o1->linked()) return; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "AddPhotoToPhotoSet invoked"; /* If the photoset id starts with the special string "UNDEFINED_", it means * it doesn't exist yet on Flickr and needs to be created. Note that it's * not necessary to subsequently add the photo to the photo set, as this * is done in the set creation call to Flickr. */ if (photoSetId.startsWith(QLatin1String("UNDEFINED_"))) { createPhotoSet(QLatin1String(""), m_selectedPhotoSet.title, m_selectedPhotoSet.description, photoId); } else { QUrl url(d->apiUrl); QNetworkRequest netRequest(url); QList reqParams = QList(); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM)); reqParams << O0RequestParameter("method", "flickr.photosets.addPhoto"); reqParams << O0RequestParameter("photoset_id", photoSetId.toLatin1()); reqParams << O0RequestParameter("photo_id", photoId.toLatin1()); QByteArray postData = O1::createQueryParameters(reqParams); d->reply = d->requestor->post(netRequest, reqParams, postData); d->state = FE_ADDPHOTOTOPHOTOSET; emit signalBusy(true); } } bool FlickrTalker::addPhoto(const QString& photoPath, const FPhotoInfo& info, bool original, bool rescale, int maxDim, int imageQuality) { if (d->reply) { d->reply->abort(); d->reply = nullptr; } if (!d->o1->linked()) return false; emit signalBusy(true); QUrl url(d->uploadUrl); QNetworkRequest netRequest(url); QList reqParams = QList(); QString path = photoPath; FlickrMPForm form; QString ispublic = (info.is_public == 1) ? QLatin1String("1") : QLatin1String("0"); form.addPair(QLatin1String("is_public"), ispublic, QLatin1String("text/plain")); reqParams << O0RequestParameter("is_public", ispublic.toLatin1()); QString isfamily = (info.is_family == 1) ? QLatin1String("1") : QLatin1String("0"); form.addPair(QLatin1String("is_family"), isfamily, QLatin1String("text/plain")); reqParams << O0RequestParameter("is_family", isfamily.toLatin1()); QString isfriend = (info.is_friend == 1) ? QLatin1String("1") : QLatin1String("0"); form.addPair(QLatin1String("is_friend"), isfriend, QLatin1String("text/plain")); reqParams << O0RequestParameter("is_friend", isfriend.toLatin1()); QString safetyLevel = QString::number(static_cast(info.safety_level)); form.addPair(QLatin1String("safety_level"), safetyLevel, QLatin1String("text/plain")); reqParams << O0RequestParameter("safety_level", safetyLevel.toLatin1()); QString contentType = QString::number(static_cast(info.content_type)); form.addPair(QLatin1String("content_type"), contentType, QLatin1String("text/plain")); reqParams << O0RequestParameter("content_type", contentType.toLatin1()); QString tags = QLatin1Char('"') + info.tags.join(QLatin1String("\" \"")) + QLatin1Char('"'); if (tags.length() > 0) { form.addPair(QLatin1String("tags"), tags, QLatin1String("text/plain")); reqParams << O0RequestParameter("tags", tags.toUtf8()); } if (!info.title.isEmpty()) { form.addPair(QLatin1String("title"), info.title, QLatin1String("text/plain")); reqParams << O0RequestParameter("title", info.title.toUtf8()); } if (!info.description.isEmpty()) { form.addPair(QLatin1String("description"), info.description, QLatin1String("text/plain")); reqParams << O0RequestParameter("description", info.description.toUtf8()); } if (!original) { QImage image = PreviewLoadThread::loadHighQualitySynchronously(photoPath).copyQImage(); if (image.isNull()) { image.load(photoPath); } if (!image.isNull()) { if (!d->lastTmpFile.isEmpty()) { QFile::remove(d->lastTmpFile); } path = WSToolUtils::makeTemporaryDir(d->serviceName.toLatin1().constData()).filePath(QFileInfo(photoPath) .baseName().trimmed() + QLatin1String(".jpg")); if (rescale && (image.width() > maxDim || image.height() > maxDim)) { image = image.scaled(maxDim, maxDim, Qt::KeepAspectRatio, Qt::SmoothTransformation); } image.save(path, "JPEG", imageQuality); d->lastTmpFile = path; // Restore all metadata. DMetadata meta; if (meta.load(photoPath)) { meta.setItemDimensions(image.size()); meta.setItemOrientation(MetaEngine::ORIENTATION_NORMAL); // NOTE: see bug #153207: Flickr use IPTC keywords to create Tags in web interface // As IPTC do not support UTF-8, we need to remove it. // This function call remove all Application2 Tags. meta.removeIptcTags(QStringList() << QLatin1String("Application2")); // NOTE: see bug # 384260: Flickr use Xmp.dc.subject to create Tags // in web interface, we need to remove it. // This function call remove all Dublin Core Tags. meta.removeXmpTags(QStringList() << QLatin1String("dc")); meta.setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY); meta.save(path, true); } else { qCWarning(DIGIKAM_WEBSERVICES_LOG) << "Flickr::Image do not have metadata"; } qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Resizing and saving to temp file: " << path; } } QFileInfo tempFileInfo(path); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "QUrl path is " << QUrl::fromLocalFile(path) << "Image size (in bytes) is "<< tempFileInfo.size(); if (tempFileInfo.size() > (getMaxAllowedFileSize().toLongLong())) { emit signalAddPhotoFailed(i18n("File Size exceeds maximum allowed file size.")); emit signalBusy(false); return false; } if (!form.addFile(QLatin1String("photo"), path)) { emit signalBusy(false); return false; } form.finish(); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, form.contentType()); d->reply = d->requestor->post(netRequest, reqParams, form.formData()); d->state = FE_ADDPHOTO; return true; } void FlickrTalker::setGeoLocation(const QString& photoId, const QString& lat, const QString& lon) { if (d->reply) { d->reply->abort(); d->reply = nullptr; } if (!d->o1->linked()) return; QUrl url(d->apiUrl); QNetworkRequest netRequest(url); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String(O2_MIME_TYPE_XFORM)); QList reqParams = QList(); reqParams << O0RequestParameter("method", "flickr.photos.geo.setLocation"); reqParams << O0RequestParameter("photo_id", photoId.toLatin1()); reqParams << O0RequestParameter("lat", lat.toLatin1()); reqParams << O0RequestParameter("lon", lon.toLatin1()); QByteArray postData = O1::createQueryParameters(reqParams); d->reply = d->requestor->post(netRequest, reqParams, postData); d->state = FE_SETGEO; emit signalBusy(true); } QString FlickrTalker::getUserName() const { return d->username; } QString FlickrTalker::getUserId() const { return d->userId; } void FlickrTalker::cancel() { if (d->reply) { d->reply->abort(); d->reply = nullptr; } if (m_authProgressDlg && !m_authProgressDlg->isHidden()) { m_authProgressDlg->hide(); } } void FlickrTalker::slotError(const QString& error) { QString transError; int errorNo = error.toInt(); switch (errorNo) { case 2: transError = i18n("No photo specified"); break; case 3: transError = i18n("General upload failure"); break; case 4: transError = i18n("Filesize was zero"); break; case 5: transError = i18n("Filetype was not recognized"); break; case 6: transError = i18n("User exceeded upload limit"); break; case 96: transError = i18n("Invalid signature"); break; case 97: transError = i18n("Missing signature"); break; case 98: transError = i18n("Login Failed / Invalid auth token"); break; case 100: transError = i18n("Invalid API Key"); break; case 105: transError = i18n("Service currently unavailable"); break; case 108: transError = i18n("Invalid Frob"); break; case 111: transError = i18n("Format \"xxx\" not found"); break; case 112: transError = i18n("Method \"xxx\" not found"); break; case 114: transError = i18n("Invalid SOAP envelope"); break; case 115: transError = i18n("Invalid XML-RPC Method Call"); break; case 116: transError = i18n("The POST method is now required for all setters"); break; default: transError = i18n("Unknown error"); break; }; QMessageBox::critical(QApplication::activeWindow(), i18n("Error"), i18n("Error Occurred: %1\nCannot proceed any further.", transError)); } void FlickrTalker::slotFinished(QNetworkReply* reply) { emit signalBusy(false); if (reply != d->reply) { return; } d->reply = nullptr; if (reply->error() != QNetworkReply::NoError) { if (d->state == FE_ADDPHOTO) { emit signalAddPhotoFailed(reply->errorString()); } else { QMessageBox::critical(QApplication::activeWindow(), i18n("Error"), reply->errorString()); } reply->deleteLater(); return; } QByteArray buffer = reply->readAll(); switch (d->state) { case (FE_LOGIN): //parseResponseLogin(buffer); break; case (FE_LISTPHOTOSETS): parseResponseListPhotoSets(buffer); break; case (FE_LISTPHOTOS): parseResponseListPhotos(buffer); break; case (FE_GETPHOTOPROPERTY): parseResponsePhotoProperty(buffer); break; case (FE_ADDPHOTO): parseResponseAddPhoto(buffer); break; case (FE_ADDPHOTOTOPHOTOSET): parseResponseAddPhotoToPhotoSet(buffer); break; case (FE_CREATEPHOTOSET): parseResponseCreatePhotoSet(buffer); break; case (FE_GETMAXSIZE): parseResponseMaxSize(buffer); break; case (FE_SETGEO): parseResponseSetGeoLocation(buffer); break; default: // FR_LOGOUT break; } reply->deleteLater(); } void FlickrTalker::parseResponseMaxSize(const QByteArray& data) { QString errorString; QDomDocument doc(QLatin1String("mydocument")); if (!doc.setContent(data)) { return; } QDomElement docElem = doc.documentElement(); QDomNode node = docElem.firstChild(); QDomElement e; while (!node.isNull()) { if (node.isElement() && node.nodeName() == QLatin1String("person")) { e = node.toElement(); QDomNode details = e.firstChild(); while (!details.isNull()) { if (details.isElement()) { e = details.toElement(); if (details.nodeName() == QLatin1String("videos")) { QDomAttr a = e.attributeNode(QLatin1String("maxupload")); d->maxSize = a.value(); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Max upload size is"<maxSize; } } details = details.nextSibling(); } } if (node.isElement() && node.nodeName() == QLatin1String("err")) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response"; errorString = node.toElement().attribute(QLatin1String("code")); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << errorString; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg")); } node = node.nextSibling(); } m_authProgressDlg->hide(); } void FlickrTalker::parseResponseCreatePhotoSet(const QByteArray& data) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Parse response create photoset received " << data; //bool success = false; QDomDocument doc(QLatin1String("getListPhotoSets")); if (!doc.setContent(data)) { return; } QDomElement docElem = doc.documentElement(); QDomNode node = docElem.firstChild(); QDomElement e; while (!node.isNull()) { if (node.isElement() && node.nodeName() == QLatin1String("photoset")) { // Parse the id from the response. QString new_id = node.toElement().attribute(QLatin1String("id")); // Set the new id in the photo sets list. QLinkedList::iterator it = m_photoSetsList->begin(); while (it != m_photoSetsList->end()) { if (it->id == m_selectedPhotoSet.id) { it->id = new_id; break; } ++it; } // Set the new id in the selected photo set. m_selectedPhotoSet.id = new_id; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "PhotoSet created successfully with id" << new_id; emit signalAddPhotoSetSucceeded(); } if (node.isElement() && node.nodeName() == QLatin1String("err")) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response"; QString code = node.toElement().attribute(QLatin1String("code")); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code; QString msg = node.toElement().attribute(QLatin1String("msg")); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << msg; QMessageBox::critical(QApplication::activeWindow(), i18n("Error"), i18n("PhotoSet creation failed: ") + msg); } node = node.nextSibling(); } } void FlickrTalker::parseResponseListPhotoSets(const QByteArray& data) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseListPhotosets" << data; bool success = false; QDomDocument doc(QLatin1String("getListPhotoSets")); if (!doc.setContent(data)) { return; } QDomElement docElem = doc.documentElement(); QDomNode node = docElem.firstChild(); QDomElement e; QString photoSet_id, photoSet_title, photoSet_description; m_photoSetsList->clear(); while (!node.isNull()) { if (node.isElement() && node.nodeName() == QLatin1String("photosets")) { e = node.toElement(); QDomNode details = e.firstChild(); FPhotoSet fps; QDomNode detailsNode = details; while (!detailsNode.isNull()) { if (detailsNode.isElement()) { e = detailsNode.toElement(); if (detailsNode.nodeName() == QLatin1String("photoset")) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "id=" << e.attribute(QLatin1String("id")); photoSet_id = e.attribute(QLatin1String("id")); // this is what is obtained from data. fps.id = photoSet_id; QDomNode photoSetDetails = detailsNode.firstChild(); QDomElement e_detail; while (!photoSetDetails.isNull()) { e_detail = photoSetDetails.toElement(); if (photoSetDetails.nodeName() == QLatin1String("title")) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Title=" << e_detail.text(); photoSet_title = e_detail.text(); fps.title = photoSet_title; } else if (photoSetDetails.nodeName() == QLatin1String("description")) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Description =" << e_detail.text(); photoSet_description = e_detail.text(); fps.description = photoSet_description; } photoSetDetails = photoSetDetails.nextSibling(); } m_photoSetsList->append(fps); } } detailsNode = detailsNode.nextSibling(); } details = details.nextSibling(); success = true; } if (node.isElement() && node.nodeName() == QLatin1String("err")) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response"; QString code = node.toElement().attribute(QLatin1String("code")); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg")); emit signalError(code); } node = node.nextSibling(); } qCDebug(DIGIKAM_WEBSERVICES_LOG) << "GetPhotoList finished"; if (!success) { emit signalListPhotoSetsFailed(i18n("Failed to fetch list of photo sets.")); } else { emit signalListPhotoSetsSucceeded(); maxAllowedFileSize(); } } void FlickrTalker::parseResponseListPhotos(const QByteArray& data) { QDomDocument doc(QLatin1String("getPhotosList")); if (!doc.setContent(data)) { return; } QDomElement docElem = doc.documentElement(); QDomNode node = docElem.firstChild(); //QDomElement e; //TODO } void FlickrTalker::parseResponseCreateAlbum(const QByteArray& data) { QDomDocument doc(QLatin1String("getCreateAlbum")); if (!doc.setContent(data)) { return; } QDomElement docElem = doc.documentElement(); QDomNode node = docElem.firstChild(); //TODO } void FlickrTalker::parseResponseAddPhoto(const QByteArray& data) { bool success = false; QString line; QDomDocument doc(QLatin1String("AddPhoto Response")); if (!doc.setContent(data)) { return; } QDomElement docElem = doc.documentElement(); QDomNode node = docElem.firstChild(); QDomElement e; QString photoId; while (!node.isNull()) { if (node.isElement() && node.nodeName() == QLatin1String("photoid")) { e = node.toElement(); // try to convert the node to an element. QDomNode details = e.firstChild(); photoId = e.text(); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Photoid= " << photoId; success = true; } if (node.isElement() && node.nodeName() == QLatin1String("err")) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response"; QString code = node.toElement().attribute(QLatin1String("code")); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg")); emit signalError(code); } node = node.nextSibling(); } if (!success) { emit signalAddPhotoFailed(i18n("Failed to upload photo")); } else { QString photoSetId = m_selectedPhotoSet.id; if (photoSetId == QLatin1String("-1")) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "PhotoSet Id not set, not adding the photo to any photoset"; emit signalAddPhotoSucceeded(photoId); } else { addPhotoToPhotoSet(photoId, photoSetId); } } } void FlickrTalker::parseResponsePhotoProperty(const QByteArray& data) { bool success = false; QString line; QDomDocument doc(QLatin1String("Photos Properties")); if (!doc.setContent(data)) { return; } QDomElement docElem = doc.documentElement(); QDomNode node = docElem.firstChild(); QDomElement e; while (!node.isNull()) { if (node.isElement() && node.nodeName() == QLatin1String("photoid")) { e = node.toElement(); // try to convert the node to an element. QDomNode details = e.firstChild(); success = true; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Photoid=" << e.text(); } if (node.isElement() && node.nodeName() == QLatin1String("err")) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Checking Error in response"; QString code = node.toElement().attribute(QLatin1String("code")); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error code=" << code; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Msg=" << node.toElement().attribute(QLatin1String("msg")); emit signalError(code); } node = node.nextSibling(); } qCDebug(DIGIKAM_WEBSERVICES_LOG) << "GetToken finished"; if (!success) { emit signalAddPhotoFailed(i18n("Failed to query photo information")); } else { emit signalAddPhotoSucceeded(QLatin1String("")); } } void FlickrTalker::parseResponseAddPhotoToPhotoSet(const QByteArray& data) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseListPhotosets" << data; emit signalAddPhotoSucceeded(QLatin1String("")); } void FlickrTalker::parseResponseSetGeoLocation(const QByteArray& data) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseResponseSetGeoLocation" << data; emit signalAddPhotoSucceeded(QLatin1String("")); } } // namespace DigikamGenericFlickrPlugin diff --git a/core/dplugins/generic/webservices/google/gswindow.cpp b/core/dplugins/generic/webservices/google/gswindow.cpp index 800c29ec2c..9717b7cdfb 100644 --- a/core/dplugins/generic/webservices/google/gswindow.cpp +++ b/core/dplugins/generic/webservices/google/gswindow.cpp @@ -1,1273 +1,1269 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2013-11-18 * Description : a tool to export items to Google web services * * Copyright (C) 2013 by Pankaj Kumar * Copyright (C) 2015 by Shourya Singh Gupta * Copyright (C) 2013-2018 by Caulier Gilles * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "gswindow.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include #include // Local includes #include "wstoolutils.h" #include "ditemslist.h" #include "digikam_version.h" #include "dprogresswdg.h" #include "gdtalker.h" #include "gsitem.h" #include "gsnewalbumdlg.h" #include "gswidget.h" #include "gptalker.h" #include "gsreplacedlg.h" #include "digikam_debug.h" #include "dfileoperations.h" namespace DigikamGenericGoogleServicesPlugin { class Q_DECL_HIDDEN GSWindow::Private { public: explicit Private() { widget = nullptr; albumDlg = nullptr; gphotoAlbumDlg = nullptr; talker = nullptr; gphotoTalker = nullptr; iface = nullptr; imagesCount = 0; imagesTotal = 0; renamingOpt = 0; service = GoogleService::GPhotoImport; } unsigned int imagesCount; unsigned int imagesTotal; int renamingOpt; QString serviceName; QString toolName; GoogleService service; QString tmp; GSWidget* widget; GSNewAlbumDlg* albumDlg; GSNewAlbumDlg* gphotoAlbumDlg; GDTalker* talker; GPTalker* gphotoTalker; QString currentAlbumId; QList< QPair > transferQueue; QList< QPair > uploadQueue; DInfoInterface* iface; DMetadata meta; }; GSWindow::GSWindow(DInfoInterface* const iface, QWidget* const /*parent*/, const QString& serviceName) : WSToolDialog(nullptr, QString::fromLatin1("%1Export Dialog").arg(serviceName)), d(new Private) { d->iface = iface; d->serviceName = serviceName; if (QString::compare(d->serviceName, QLatin1String("googledriveexport"), Qt::CaseInsensitive) == 0) { d->service = GoogleService::GDrive; d->toolName = QLatin1String("Google Drive"); } else if (QString::compare(d->serviceName, QLatin1String("googlephotoexport"), Qt::CaseInsensitive) == 0) { d->service = GoogleService::GPhotoExport; d->toolName = QLatin1String("Google Photos"); } else { d->service = GoogleService::GPhotoImport; d->toolName = QLatin1String("Google Photos"); } d->tmp = WSToolUtils::makeTemporaryDir("google").absolutePath() + QLatin1Char('/');; d->widget = new GSWidget(this, d->iface, d->service, d->toolName); setMainWidget(d->widget); setModal(false); switch (d->service) { case GoogleService::GDrive: setWindowTitle(i18n("Export to Google Drive")); startButton()->setText(i18n("Start Upload")); startButton()->setToolTip(i18n("Start upload to Google Drive")); - d->widget->setMinimumSize(700,500); + d->widget->setMinimumSize(700, 500); d->albumDlg = new GSNewAlbumDlg(this, d->serviceName, d->toolName); d->talker = new GDTalker(this); connect(d->talker,SIGNAL(signalBusy(bool)), this,SLOT(slotBusy(bool))); connect(d->talker,SIGNAL(signalAccessTokenObtained()), this,SLOT(slotAccessTokenObtained())); connect(d->talker, SIGNAL(signalAuthenticationRefused()), this,SLOT(slotAuthenticationRefused())); connect(d->talker,SIGNAL(signalSetUserName(QString)), this,SLOT(slotSetUserName(QString))); connect(d->talker,SIGNAL(signalListAlbumsDone(int,QString,QList)), this,SLOT(slotListAlbumsDone(int,QString,QList))); connect(d->talker,SIGNAL(signalCreateFolderDone(int,QString)), this,SLOT(slotCreateFolderDone(int,QString))); connect(d->talker,SIGNAL(signalAddPhotoDone(int,QString)), this,SLOT(slotAddPhotoDone(int,QString))); connect(d->talker, SIGNAL(signalUploadPhotoDone(int,QString,QStringList)), this, SLOT(slotUploadPhotoDone(int,QString,QStringList))); readSettings(); buttonStateChange(false); d->talker->doOAuth(); break; case GoogleService::GPhotoImport: case GoogleService::GPhotoExport: if (d->service == GoogleService::GPhotoExport) { setWindowTitle(i18n("Export to Google Photos Service")); startButton()->setText(i18n("Start Upload")); startButton()->setToolTip(i18n("Start upload to Google Photos Service")); d->widget->setMinimumSize(700, 500); } else { setWindowTitle(i18n("Import from Google Photos Service")); startButton()->setText(i18n("Start Download")); startButton()->setToolTip(i18n("Start download from Google Photos service")); d->widget->setMinimumSize(300, 400); } d->gphotoAlbumDlg = new GSNewAlbumDlg(this, d->serviceName, d->toolName); d->gphotoTalker = new GPTalker(this); connect(d->gphotoTalker, SIGNAL(signalBusy(bool)), this, SLOT(slotBusy(bool))); connect(d->gphotoTalker,SIGNAL(signalSetUserName(QString)), this,SLOT(slotSetUserName(QString))); connect(d->gphotoTalker, SIGNAL(signalAccessTokenObtained()), this, SLOT(slotAccessTokenObtained())); connect(d->gphotoTalker, SIGNAL(signalAuthenticationRefused()), this,SLOT(slotAuthenticationRefused())); connect(d->gphotoTalker, SIGNAL(signalListAlbumsDone(int,QString,QList)), this, SLOT(slotListAlbumsDone(int,QString,QList))); connect(d->gphotoTalker, SIGNAL(signalCreateAlbumDone(int,QString,QString)), this, SLOT(slotCreateFolderDone(int,QString,QString))); connect(d->gphotoTalker, SIGNAL(signalAddPhotoDone(int,QString)), this, SLOT(slotAddPhotoDone(int,QString))); connect(d->gphotoTalker, SIGNAL(signalUploadPhotoDone(int,QString,QStringList)), this, SLOT(slotUploadPhotoDone(int,QString,QStringList))); connect(d->gphotoTalker, SIGNAL(signalGetPhotoDone(int,QString,QByteArray,QString)), this, SLOT(slotGetPhotoDone(int,QString,QByteArray,QString))); readSettings(); buttonStateChange(false); d->gphotoTalker->doOAuth(); break; } connect(d->widget->imagesList(), SIGNAL(signalImageListChanged()), this, SLOT(slotImageListChanged())); connect(d->widget->getChangeUserBtn(), SIGNAL(clicked()), this, SLOT(slotUserChangeRequest())); connect(d->widget->getNewAlbmBtn(), SIGNAL(clicked()), this,SLOT(slotNewAlbumRequest())); connect(d->widget->getReloadBtn(), SIGNAL(clicked()), this, SLOT(slotReloadAlbumsRequest())); connect(startButton(), SIGNAL(clicked()), this, SLOT(slotStartTransfer())); connect(this, SIGNAL(finished(int)), this, SLOT(slotFinished())); } GSWindow::~GSWindow() { d->transferQueue.clear(); delete d->gphotoTalker; delete d->talker; delete d; } void GSWindow::reactivate() { d->widget->imagesList()->loadImagesFromCurrentSelection(); d->widget->progressBar()->hide(); show(); } void GSWindow::readSettings() { KConfig config; KConfigGroup grp; switch (d->service) { case GoogleService::GDrive: grp = config.group("Google Drive Settings"); break; default: grp = config.group("Google Photo Settings"); break; } d->currentAlbumId = grp.readEntry("Current Album", QString()); if (grp.readEntry("Resize", false)) { d->widget->getResizeCheckBox()->setChecked(true); d->widget->getDimensionSpB()->setEnabled(true); } else { d->widget->getResizeCheckBox()->setChecked(false); d->widget->getDimensionSpB()->setEnabled(false); } d->widget->getOriginalCheckBox()->setChecked(grp.readEntry("Upload Original", false)); d->widget->getPhotoIdCheckBox()->setChecked(grp.readEntry("Write PhotoID", true)); d->widget->getDimensionSpB()->setValue(grp.readEntry("Maximum Width", 1600)); d->widget->getImgQualitySpB()->setValue(grp.readEntry("Image Quality", 90)); if (d->service == GoogleService::GPhotoExport && d->widget->m_tagsBGrp) { d->widget->m_tagsBGrp->button(grp.readEntry("Tag Paths", 0))->setChecked(true); } KConfigGroup dialogGroup = config.group(QString::fromLatin1("%1Export Dialog").arg(d->serviceName)); winId(); KWindowConfig::restoreWindowSize(windowHandle(), dialogGroup); resize(windowHandle()->size()); } void GSWindow::writeSettings() { KConfig config; KConfigGroup grp; switch (d->service) { case GoogleService::GDrive: grp = config.group("Google Drive Settings"); break; default: grp = config.group("Google Photo Settings"); break; } grp.writeEntry("Current Album", d->currentAlbumId); grp.writeEntry("Resize", d->widget->getResizeCheckBox()->isChecked()); grp.writeEntry("Upload Original", d->widget->getOriginalCheckBox()->isChecked()); grp.writeEntry("Write PhotoID", d->widget->getPhotoIdCheckBox()->isChecked()); grp.writeEntry("Maximum Width", d->widget->getDimensionSpB()->value()); grp.writeEntry("Image Quality", d->widget->getImgQualitySpB()->value()); if (d->service == GoogleService::GPhotoExport && d->widget->m_tagsBGrp) { grp.writeEntry("Tag Paths", d->widget->m_tagsBGrp->checkedId()); } KConfigGroup dialogGroup = config.group(QString::fromLatin1("%1Export Dialog").arg(d->serviceName)); KWindowConfig::saveWindowSize(windowHandle(), dialogGroup); config.sync(); } void GSWindow::slotSetUserName(const QString& msg) { d->widget->updateLabels(msg); } void GSWindow::slotListPhotosDoneForDownload(int errCode, const QString& errMsg, const QList & photosList) { disconnect(d->gphotoTalker, SIGNAL(signalListPhotosDone(int,QString,QList)), this, SLOT(slotListPhotosDoneForDownload(int,QString,QList))); if (errCode == 0) { QMessageBox::critical(this, i18nc("@title:window", "Error"), i18n("Google Photos call failed: %1\n", errMsg)); return; } typedef QPair Pair; d->transferQueue.clear(); QList::const_iterator itPWP; for (itPWP = photosList.begin() ; itPWP != photosList.end() ; ++itPWP) { d->transferQueue.append(Pair((*itPWP).originalURL, (*itPWP))); } if (d->transferQueue.isEmpty()) return; d->currentAlbumId = d->widget->getAlbumsCoB()->itemData(d->widget->getAlbumsCoB()->currentIndex()).toString(); d->imagesTotal = d->transferQueue.count(); d->imagesCount = 0; d->widget->progressBar()->setFormat(i18n("%v / %m")); d->widget->progressBar()->show(); d->renamingOpt = 0; // start download with first photo in queue downloadNextPhoto(); } void GSWindow::slotListAlbumsDone(int code, const QString& errMsg, const QList & list) { switch (d->service) { case GoogleService::GDrive: if (code == 0) { QMessageBox::critical(this, i18nc("@title:window", "Error"), i18n("Google Drive call failed: %1\n", errMsg)); return; } d->widget->getAlbumsCoB()->clear(); for (int i = 0 ; i < list.size() ; ++i) { d->widget->getAlbumsCoB()->addItem(QIcon::fromTheme(QLatin1String("system-users")), list.value(i).title, list.value(i).id); if (d->currentAlbumId == list.value(i).id) { d->widget->getAlbumsCoB()->setCurrentIndex(i); } } buttonStateChange(true); d->talker->getUserName(); break; default: if (code == 0) { QMessageBox::critical(this, i18nc("@title:window", "Error"), i18n("Google Photos call failed: %1\n", errMsg)); return; } d->widget->getAlbumsCoB()->clear(); for (int i = 0 ; i < list.size() ; ++i) { if (d->service == GoogleService::GPhotoImport && i == 0) { // remove album continue; } QString albumIcon; if (list.at(i).isWriteable) { albumIcon = QLatin1String("folder"); } else { albumIcon = QLatin1String("folder-locked"); } d->widget->getAlbumsCoB()->addItem(QIcon::fromTheme(albumIcon), list.at(i).title, list.at(i).id); if (d->currentAlbumId == list.at(i).id) d->widget->getAlbumsCoB()->setCurrentIndex(i); buttonStateChange(true); } break; } } void GSWindow::slotBusy(bool val) { - if (val) - { - setCursor(Qt::WaitCursor); - d->widget->getChangeUserBtn()->setEnabled(false); - buttonStateChange(false); - } - else - { - setCursor(Qt::ArrowCursor); - d->widget->getChangeUserBtn()->setEnabled(true); - buttonStateChange(true); - } + setCursor(val ? Qt::WaitCursor + : Qt::ArrowCursor); + + d->widget->imagesList()->enableControlButtons(!val); + d->widget->imagesList()->enableDragAndDrop(!val); + d->widget->getChangeUserBtn()->setEnabled(!val); + d->widget->getOptionsBox()->setEnabled(!val); + buttonStateChange(!val); } void GSWindow::slotStartTransfer() { d->widget->imagesList()->clearProcessedStatus(); switch (d->service) { case GoogleService::GDrive: case GoogleService::GPhotoExport: if (d->widget->imagesList()->imageUrls().isEmpty()) { QMessageBox::critical(this, i18nc("@title:window", "Error"), i18n("No image selected. Please select which images should be uploaded.")); return; } break; case GoogleService::GPhotoImport: break; } switch (d->service) { case GoogleService::GDrive: if (!(d->talker->authenticated())) { QPointer warn = new QMessageBox(QMessageBox::Warning, i18n("Warning"), i18n("Authentication failed. Click \"Continue\" to authenticate."), QMessageBox::Yes | QMessageBox::No); (warn->button(QMessageBox::Yes))->setText(i18n("Continue")); (warn->button(QMessageBox::No))->setText(i18n("Cancel")); if (warn->exec() == QMessageBox::Yes) { d->talker->doOAuth(); delete warn; return; } else { delete warn; return; } } break; default: if (!(d->gphotoTalker->authenticated())) { QPointer warn = new QMessageBox(QMessageBox::Warning, i18n("Warning"), i18n("Authentication failed. Click \"Continue\" to authenticate."), QMessageBox::Yes | QMessageBox::No); (warn->button(QMessageBox::Yes))->setText(i18n("Continue")); (warn->button(QMessageBox::No))->setText(i18n("Cancel")); if (warn->exec() == QMessageBox::Yes) { d->gphotoTalker->doOAuth(); delete warn; return; } else { delete warn; return; } } if (d->service == GoogleService::GPhotoImport) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Google Photo Transfer invoked"; // list photos of the album, then start download connect(d->gphotoTalker, SIGNAL(signalListPhotosDone(int,QString,QList)), this, SLOT(slotListPhotosDoneForDownload(int,QString,QList))); d->gphotoTalker->listPhotos( d->widget->getAlbumsCoB()->itemData(d->widget->getAlbumsCoB()->currentIndex()).toString(), d->widget->getDimensionCoB()->itemData(d->widget->getDimensionCoB()->currentIndex()).toString()); return; } } typedef QPair Pair; for (int i = 0 ; i < (d->widget->imagesList()->imageUrls().size()) ; ++i) { DItemInfo info(d->iface->itemInfo(d->widget->imagesList()->imageUrls().value(i))); GSPhoto temp; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "in start transfer info" <service) { case GoogleService::GDrive: temp.title = info.title(); temp.description = info.comment().section(QLatin1String("\n"), 0, 0); break; default: temp.title = info.name(); // Google Photo doesn't support image titles. Include it in descriptions if needed. QStringList descriptions = QStringList() << info.title() << info.comment(); descriptions.removeAll(QLatin1String("")); temp.description = descriptions.join(QLatin1String("\n\n")); temp.description.replace(QLatin1Char('"'), QLatin1String("\\\"")); break; } temp.gpsLat.setNum(info.latitude()); temp.gpsLon.setNum(info.longitude()); temp.tags = info.tagsPath(); d->transferQueue.append(Pair(d->widget->imagesList()->imageUrls().value(i), temp)); } d->currentAlbumId = d->widget->getAlbumsCoB()->itemData(d->widget->getAlbumsCoB()->currentIndex()).toString(); d->imagesTotal = d->transferQueue.count(); d->imagesCount = 0; d->widget->progressBar()->setFormat(i18n("%v / %m")); d->widget->progressBar()->setMaximum(d->imagesTotal); d->widget->progressBar()->setValue(0); d->widget->progressBar()->show(); switch (d->service) { case GoogleService::GDrive: d->widget->progressBar()->progressScheduled(i18n("Google Drive export"), true, true); d->widget->progressBar()->progressThumbnailChanged( QIcon::fromTheme(QLatin1String("dk-googledrive")).pixmap(22, 22)); break; default: d->widget->progressBar()->progressScheduled(i18n("Google Photo export"), true, true); d->widget->progressBar()->progressThumbnailChanged( QIcon::fromTheme((QLatin1String("dk-googlephoto"))).pixmap(22, 22)); break; } uploadNextPhoto(); } void GSWindow::uploadNextPhoto() { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "in upload nextphoto" << d->transferQueue.count(); if (d->transferQueue.isEmpty()) { //d->widget->progressBar()->hide(); d->widget->progressBar()->progressCompleted(); /** * Now all raw photos have been added, * for GPhoto: prepare to upload on user account * for GDrive: get listPhotoId to write metadata and finish upload */ if (d->service == GoogleService::GPhotoExport) { emit d->gphotoTalker->signalReadyToUpload(); } else { emit d->talker->signalReadyToUpload(); } return; } typedef QPair Pair; Pair pathComments = d->transferQueue.first(); GSPhoto info = pathComments.second; bool res = true; d->widget->imagesList()->processing(pathComments.first); switch (d->service) { case GoogleService::GDrive: { res = d->talker->addPhoto(pathComments.first.toLocalFile(), info, d->currentAlbumId, d->widget->getOriginalCheckBox()->isChecked(), d->widget->getResizeCheckBox()->isChecked(), d->widget->getDimensionSpB()->value(), d->widget->getImgQualitySpB()->value()); break; } case GoogleService::GPhotoExport: { bool bCancel = false; bool bAdd = true; if (!info.id.isEmpty() && !info.editUrl.isEmpty()) { switch (d->renamingOpt) { case PWR_ADD_ALL: bAdd = true; break; case PWR_REPLACE_ALL: bAdd = false; break; default: { QPointer dlg = new ReplaceDialog(this, QLatin1String(""), d->iface, pathComments.first, info.thumbURL); dlg->exec(); switch (dlg->getResult()) { case PWR_ADD_ALL: d->renamingOpt = PWR_ADD_ALL; break; case PWR_ADD: bAdd = true; break; case PWR_REPLACE_ALL: d->renamingOpt = PWR_REPLACE_ALL; break; case PWR_REPLACE: bAdd = false; break; case PWR_CANCEL: default: bCancel = true; break; } delete dlg; break; } } } // adjust tags according to radio button clicked if (d->widget->m_tagsBGrp) { switch (d->widget->m_tagsBGrp->checkedId()) { case GPTagLeaf: { QStringList newTags; QStringList::const_iterator itT; for (itT = info.tags.constBegin() ; itT != info.tags.constEnd() ; ++itT) { QString strTmp = *itT; int idx = strTmp.lastIndexOf(QLatin1Char('/')); if (idx > 0) { strTmp.remove(0, idx + 1); } newTags.append(strTmp); } info.tags = newTags; break; } case GPTagSplit: { QSet newTagsSet; QStringList::const_iterator itT; for (itT = info.tags.constBegin() ; itT != info.tags.constEnd() ; ++itT) { QStringList strListTmp = itT->split(QLatin1Char('/')); QStringList::const_iterator itT2; for (itT2 = strListTmp.constBegin() ; itT2 != strListTmp.constEnd() ; ++itT2) { if (!newTagsSet.contains(*itT2)) { newTagsSet.insert(*itT2); } } } info.tags.clear(); QSet::const_iterator itT3; for (itT3 = newTagsSet.begin() ; itT3 != newTagsSet.end() ; ++itT3) { info.tags.append(*itT3); } break; } case GPTagCombined: default: break; } } if (bCancel) { slotTransferCancel(); res = true; } else { if (bAdd) { res = d->gphotoTalker->addPhoto(pathComments.first.toLocalFile(), info, d->currentAlbumId, d->widget->getOriginalCheckBox()->isChecked(), d->widget->getResizeCheckBox()->isChecked(), d->widget->getDimensionSpB()->value(), d->widget->getImgQualitySpB()->value()); } else { res = d->gphotoTalker->updatePhoto(pathComments.first.toLocalFile(), info, d->widget->getResizeCheckBox()->isChecked(), d->widget->getDimensionSpB()->value(), d->widget->getImgQualitySpB()->value()); } } break; } case GoogleService::GPhotoImport: break; } if (!res) { slotAddPhotoDone(0, QLatin1String("")); return; } } void GSWindow::downloadNextPhoto() { if (d->transferQueue.isEmpty()) { d->widget->progressBar()->hide(); d->widget->progressBar()->progressCompleted(); return; } d->widget->progressBar()->setMaximum(d->imagesTotal); d->widget->progressBar()->setValue(d->imagesCount); QString imgPath = d->transferQueue.first().first.url(); d->gphotoTalker->getPhoto(imgPath); } void GSWindow::slotGetPhotoDone(int errCode, const QString& errMsg, const QByteArray& photoData, const QString& fileName) { if (d->transferQueue.isEmpty()) { return; } GSPhoto item = d->transferQueue.first().second; /** * (Trung) * Google Photo API now does not support title for image, * so we use the file name from the header. As fallback * the creation time for image name instead. */ QString itemName(item.title); QString suffix(item.mimeType.section(QLatin1Char('/'), -1)); if (itemName.isEmpty() && !fileName.isEmpty()) { QFileInfo info(fileName); itemName = info.completeBaseName(); suffix = info.suffix(); } if (itemName.isEmpty()) { itemName = QString::fromLatin1("image-%1").arg(item.creationTime); // Replace colon for Windows file systems itemName.replace(QLatin1Char(':'), QLatin1Char('-')); } QUrl tmpUrl = QUrl::fromLocalFile(QString(d->tmp + itemName + QLatin1Char('.') + suffix)); if (errCode == 1) { QString errText; QFile imgFile(tmpUrl.toLocalFile()); if (!imgFile.open(QIODevice::WriteOnly)) { errText = imgFile.errorString(); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "error write"; } else if (imgFile.write(photoData) != photoData.size()) { errText = imgFile.errorString(); } else { imgFile.close(); } if (errText.isEmpty()) { if (d->meta.load(tmpUrl.toLocalFile())) { if (d->meta.supportXmp() && d->meta.canWriteXmp(tmpUrl.toLocalFile())) { d->meta.setXmpTagString("Xmp.digiKam.picasawebGPhotoId", item.id); d->meta.setXmpKeywords(item.tags); } if (!item.gpsLat.isEmpty() && !item.gpsLon.isEmpty()) { d->meta.setGPSInfo(0.0, item.gpsLat.toDouble(), item.gpsLon.toDouble()); } d->meta.setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY); d->meta.save(tmpUrl.toLocalFile()); } d->transferQueue.removeFirst(); d->imagesCount++; } else { QPointer warn = new QMessageBox(QMessageBox::Warning, i18n("Warning"), i18n("Failed to save photo: %1\n" "Do you want to continue?", errText), QMessageBox::Yes | QMessageBox::No); (warn->button(QMessageBox::Yes))->setText(i18n("Continue")); (warn->button(QMessageBox::No))->setText(i18n("Cancel")); if (warn->exec() != QMessageBox::Yes) { slotTransferCancel(); delete warn; return; } delete warn; } } else { QPointer warn = new QMessageBox(QMessageBox::Warning, i18n("Warning"), i18n("Failed to download photo: %1\n" "Do you want to continue?", errMsg), QMessageBox::Yes | QMessageBox::No); (warn->button(QMessageBox::Yes))->setText(i18n("Continue")); (warn->button(QMessageBox::No))->setText(i18n("Cancel")); if (warn->exec() != QMessageBox::Yes) { slotTransferCancel(); delete warn; return; } delete warn; } QUrl newUrl = QUrl::fromLocalFile(QString::fromLatin1("%1/%2").arg(d->widget->getDestinationPath()) .arg(tmpUrl.fileName())); newUrl = DFileOperations::getUniqueFileUrl(newUrl); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "location" << newUrl; if (!QFile::rename(tmpUrl.toLocalFile(), newUrl.toLocalFile())) { QMessageBox::critical(this, i18nc("@title:window", "Error"), i18n("Failed to save image to %1", newUrl.toLocalFile())); } emit updateHostApp(newUrl); downloadNextPhoto(); } void GSWindow::slotAddPhotoDone(int err, const QString& msg) { if (d->transferQueue.isEmpty()) { return; } if (err == 0) { d->widget->imagesList()->processed(d->transferQueue.first().first,false); QPointer warn = new QMessageBox(QMessageBox::Warning, i18n("Warning"), i18n("Failed to upload photo to %1.\n%2\n" "Do you want to continue?", d->toolName, msg), QMessageBox::Yes | QMessageBox::No); (warn->button(QMessageBox::Yes))->setText(i18n("Continue")); (warn->button(QMessageBox::No))->setText(i18n("Cancel")); if (warn->exec() != QMessageBox::Yes) { d->transferQueue.clear(); d->widget->progressBar()->hide(); } else { d->transferQueue.removeFirst(); d->imagesTotal--; d->widget->progressBar()->setMaximum(d->imagesTotal); d->widget->progressBar()->setValue(d->imagesCount); uploadNextPhoto(); } delete warn; } else { /** * (Trung) Take first item out of transferQueue and append to uploadQueue, * in order to use it again to write id in slotUploadPhotoDone */ QPair item = d->transferQueue.first(); d->uploadQueue.append(item); // Remove photo uploaded from the transfer queue d->transferQueue.removeFirst(); d->imagesCount++; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In slotAddPhotoSucceeded" << d->imagesCount; d->widget->progressBar()->setMaximum(d->imagesTotal); d->widget->progressBar()->setValue(d->imagesCount); uploadNextPhoto(); } } void GSWindow::slotUploadPhotoDone(int err, const QString& msg, const QStringList& listPhotoId) { if (err == 0) { QPointer warn = new QMessageBox(QMessageBox::Warning, i18n("Warning"), i18n("Failed to finish uploading photo to %1.\n%2\n" "No image uploaded to your account.", d->toolName, msg), QMessageBox::Yes); (warn->button(QMessageBox::Yes))->setText(i18n("OK")); d->uploadQueue.clear(); d->widget->progressBar()->hide(); delete warn; } else { foreach (const QString& photoId, listPhotoId) { // Remove image from upload list and from UI QPair item = d->uploadQueue.takeFirst(); d->widget->imagesList()->removeItemByUrl(item.first); QUrl fileUrl = item.first; qCDebug(DIGIKAM_WEBSERVICES_LOG) << "photoID:" << photoId; if (d->widget->getPhotoIdCheckBox()->isChecked() && d->meta.supportXmp() && d->meta.canWriteXmp(fileUrl.toLocalFile()) && d->meta.load(fileUrl.toLocalFile()) && !photoId.isEmpty()) { d->meta.setXmpTagString("Xmp.digiKam.picasawebGPhotoId", photoId); d->meta.save(fileUrl.toLocalFile()); } } if (!d->widget->imagesList()->imageUrls().isEmpty()) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "continue to upload"; emit d->gphotoTalker->signalReadyToUpload(); } } } void GSWindow::slotImageListChanged() { startButton()->setEnabled(!(d->widget->imagesList()->imageUrls().isEmpty())); } void GSWindow::slotNewAlbumRequest() { switch (d->service) { case GoogleService::GDrive: if (d->albumDlg->exec() == QDialog::Accepted) { GSFolder newFolder; d->albumDlg->getAlbumProperties(newFolder); d->currentAlbumId = d->widget->getAlbumsCoB()->itemData(d->widget->getAlbumsCoB()->currentIndex()).toString(); d->talker->createFolder(newFolder.title, d->currentAlbumId); } break; default: if (d->gphotoAlbumDlg->exec() == QDialog::Accepted) { GSFolder newFolder; d->gphotoAlbumDlg->getAlbumProperties(newFolder); d->gphotoTalker->createAlbum(newFolder); } break; } } void GSWindow::slotReloadAlbumsRequest() { switch (d->service) { case GoogleService::GDrive: d->talker->listFolders(); break; case GoogleService::GPhotoImport: case GoogleService::GPhotoExport: d->gphotoTalker->listAlbums(); break; } } void GSWindow::slotAccessTokenObtained() { switch (d->service) { case GoogleService::GDrive: d->talker->listFolders(); break; case GoogleService::GPhotoImport: case GoogleService::GPhotoExport: d->gphotoTalker->getLoggedInUser(); break; } } void GSWindow::slotAuthenticationRefused() { // QMessageBox::critical(this, i18nc("@title:window", "Error"), // i18n("An authentication error occurred: account failed to link")); // Clear list albums d->widget->getAlbumsCoB()->clear(); // Clear user name d->widget->updateLabels(QString()); return; } void GSWindow::slotCreateFolderDone(int code, const QString& msg, const QString& albumId) { switch (d->service) { case GoogleService::GDrive: if (code == 0) QMessageBox::critical(this, i18nc("@title:window", "Error"), i18n("Google Drive call failed:\n%1", msg)); else { d->currentAlbumId = albumId; d->talker->listFolders(); } break; case GoogleService::GPhotoImport: case GoogleService::GPhotoExport: if (code == 0) QMessageBox::critical(this, i18nc("@title:window", "Error"), i18n("Google Photos call failed:\n%1", msg)); else { d->currentAlbumId = albumId; d->gphotoTalker->listAlbums(); } break; } } void GSWindow::slotTransferCancel() { d->transferQueue.clear(); d->widget->progressBar()->hide(); switch (d->service) { case GoogleService::GDrive: d->talker->cancel(); break; case GoogleService::GPhotoImport: case GoogleService::GPhotoExport: d->gphotoTalker->cancel(); break; } } void GSWindow::slotUserChangeRequest() { QPointer warn = new QMessageBox(QMessageBox::Warning, i18n("Warning"), i18n("You will be logged out of your account, " "click \"Continue\" to authenticate for another account"), QMessageBox::Yes | QMessageBox::No); (warn->button(QMessageBox::Yes))->setText(i18n("Continue")); (warn->button(QMessageBox::No))->setText(i18n("Cancel")); if (warn->exec() == QMessageBox::Yes) { /** * We do not force user to logout from their account * We simply unlink user account and direct use to login page to login new account * (In the future, we may not unlink() user, but let them change account and * choose which one they want to use) * After unlink(), waiting actively until O2 completely unlink() account, before doOAuth() again */ switch (d->service) { case GoogleService::GDrive: d->talker->unlink(); while(d->talker->authenticated()); d->talker->doOAuth(); break; case GoogleService::GPhotoImport: case GoogleService::GPhotoExport: d->gphotoTalker->unlink(); while(d->gphotoTalker->authenticated()); d->gphotoTalker->doOAuth(); break; } } delete warn; } void GSWindow::buttonStateChange(bool state) { d->widget->getNewAlbmBtn()->setEnabled(state); d->widget->getReloadBtn()->setEnabled(state); startButton()->setEnabled(state); } void GSWindow::slotFinished() { writeSettings(); d->transferQueue.clear(); d->widget->imagesList()->listView()->clear(); } void GSWindow::closeEvent(QCloseEvent* e) { if (!e) { return; } slotFinished(); e->accept(); } } // namespace DigikamGenericGoogleServicesPlugin diff --git a/core/dplugins/generic/webservices/onedrive/odtalker.cpp b/core/dplugins/generic/webservices/onedrive/odtalker.cpp index dfc0a49583..cf2864690c 100644 --- a/core/dplugins/generic/webservices/onedrive/odtalker.cpp +++ b/core/dplugins/generic/webservices/onedrive/odtalker.cpp @@ -1,600 +1,587 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2018-05-20 * Description : a tool to export images to Onedrive web service * * Copyright (C) 2018 by Tarek Talaat * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "odtalker.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include // KDE includes #include #include // Local includes -#include "digikam_config.h" #include "digikam_debug.h" #include "digikam_version.h" #include "wstoolutils.h" #include "odwindow.h" #include "oditem.h" #include "odmpform.h" +#include "webbrowserdlg.h" #include "previewloadthread.h" -#ifdef HAVE_QWEBENGINE -# include "webwidget_qwebengine.h" -#else -# include "webwidget.h" -#endif - namespace DigikamGenericOneDrivePlugin { class Q_DECL_HIDDEN ODTalker::Private { public: enum State { OD_USERNAME = 0, OD_LISTFOLDERS, OD_CREATEFOLDER, OD_ADDPHOTO }; public: explicit Private() + : state(OD_USERNAME), + parent(nullptr), + netMngr(nullptr), + reply(nullptr), + settings(nullptr), + browser(nullptr) { clientId = QLatin1String("4c20a541-2ca8-4b98-8847-a375e4d33f34"); clientSecret = QLatin1String("wtdcaXADCZ0|tcDA7633|@*"); - scope = QLatin1String("Files.ReadWrite User.Read"); authUrl = QLatin1String("https://login.live.com/oauth20_authorize.srf"); tokenUrl = QLatin1String("https://login.live.com/oauth20_token.srf"); + scope = QLatin1String("Files.ReadWrite User.Read"); redirectUrl = QLatin1String("https://login.live.com/oauth20_desktop.srf"); - - serviceName = QLatin1String("Onedrive"); - serviceTime = QLatin1String("token_time"); - serviceKey = QLatin1String("access_token"); - - state = OD_USERNAME; - - parent = nullptr; - netMngr = nullptr; - reply = nullptr; - settings = nullptr; - view = nullptr; + serviceName = QLatin1String("Onedrive"); + serviceTime = QLatin1String("token_time"); + serviceKey = QLatin1String("access_token"); } public: QString clientId; QString clientSecret; QString authUrl; QString tokenUrl; QString scope; QString redirectUrl; QString accessToken; QString serviceName; QString serviceTime; QString serviceKey; QDateTime expiryTime; State state; QWidget* parent; QNetworkAccessManager* netMngr; QNetworkReply* reply; QSettings* settings; - WebWidget* view; + WebBrowserDlg* browser; QList > folderList; QList nextFolder; }; ODTalker::ODTalker(QWidget* const parent) : d(new Private) { d->parent = parent; d->netMngr = new QNetworkAccessManager(this); - d->view = new WebWidget(d->parent); - d->view->resize(800, 600); - d->settings = WSToolUtils::getOauthSettings(this); connect(this, SIGNAL(oneDriveLinkingFailed()), this, SLOT(slotLinkingFailed())); connect(this, SIGNAL(oneDriveLinkingSucceeded()), this, SLOT(slotLinkingSucceeded())); connect(d->netMngr, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotFinished(QNetworkReply*))); - - connect(d->view, SIGNAL(urlChanged(QUrl)), - this, SLOT(slotCatchUrl(QUrl))); - - connect(d->view, SIGNAL(closeView(bool)), - this, SIGNAL(signalBusy(bool))); } ODTalker::~ODTalker() { if (d->reply) { d->reply->abort(); } WSToolUtils::removeTemporaryDir("onedrive"); delete d; } void ODTalker::link() { emit signalBusy(true); QUrl url(d->authUrl); QUrlQuery query(url); query.addQueryItem(QLatin1String("client_id"), d->clientId); query.addQueryItem(QLatin1String("scope"), d->scope); query.addQueryItem(QLatin1String("redirect_uri"), d->redirectUrl); query.addQueryItem(QLatin1String("response_type"), QLatin1String("token")); url.setQuery(query); - d->view->setWindowFlags(Qt::Dialog); - d->view->load(url); - d->view->show(); + delete d->browser; + d->browser = new WebBrowserDlg(url, d->parent, true); + d->browser->setModal(true); + + connect(d->browser, SIGNAL(urlChanged(QUrl)), + this, SLOT(slotCatchUrl(QUrl))); + + connect(d->browser, SIGNAL(closeView(bool)), + this, SIGNAL(signalBusy(bool))); + + d->browser->show(); } void ODTalker::unLink() { d->accessToken = QString(); d->settings->beginGroup(d->serviceName); d->settings->remove(QString()); d->settings->endGroup(); -#ifdef HAVE_QWEBENGINE - d->view->page()->profile()->cookieStore()->deleteAllCookies(); -#else - d->view->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); -#endif - emit oneDriveLinkingSucceeded(); } void ODTalker::slotCatchUrl(const QUrl& url) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received URL from webview:" << url; QString str = url.toString(); QUrlQuery query(str.section(QLatin1Char('#'), -1, -1)); if (query.hasQueryItem(QLatin1String("access_token"))) { d->accessToken = query.queryItemValue(QLatin1String("access_token")); int seconds = query.queryItemValue(QLatin1String("expires_in")).toInt(); d->expiryTime = QDateTime::currentDateTime().addSecs(seconds); writeSettings(); qDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token received"; emit oneDriveLinkingSucceeded(); } else { emit oneDriveLinkingFailed(); } } void ODTalker::slotLinkingFailed() { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Onedrive fail"; emit signalBusy(false); } void ODTalker::slotLinkingSucceeded() { if (d->accessToken.isEmpty()) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "UNLINK to Onedrive"; emit signalBusy(false); return; } qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Onedrive"; - d->view->close(); + if (d->browser) + { + d->browser->close(); + } + emit signalLinkingSucceeded(); } bool ODTalker::authenticated() { return (!d->accessToken.isEmpty()); } void ODTalker::cancel() { if (d->reply) { d->reply->abort(); d->reply = nullptr; } emit signalBusy(false); } void ODTalker::createFolder(QString& path) { //path also has name of new folder so send path parameter accordingly QString name = QUrl(path).fileName(); QString folderPath = QUrl(path).adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path(); QUrl url; if (folderPath == QLatin1String("/")) { url = QUrl(QLatin1String("https://graph.microsoft.com/v1.0/me/drive/root/children")); } else { url = QUrl(QString::fromUtf8("https://graph.microsoft.com/v1.0/me/drive/root:/%1:/children").arg(folderPath)); } QNetworkRequest netRequest(url); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json")); netRequest.setRawHeader("Authorization", QString::fromLatin1("bearer %1").arg(d->accessToken).toUtf8()); QByteArray postData = QString::fromUtf8("{\"name\": \"%1\",\"folder\": {}}").arg(name).toUtf8(); d->reply = d->netMngr->post(netRequest, postData); d->state = Private::OD_CREATEFOLDER; emit signalBusy(true); } void ODTalker::getUserName() { QUrl url(QLatin1String("https://graph.microsoft.com/v1.0/me")); QNetworkRequest netRequest(url); netRequest.setRawHeader("Authorization", QString::fromLatin1("bearer %1").arg(d->accessToken).toUtf8()); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json")); d->reply = d->netMngr->get(netRequest); d->state = Private::OD_USERNAME; emit signalBusy(true); } /** Get list of folders by parsing json sent by onedrive */ void ODTalker::listFolders(const QString& folder) { QString nextFolder; if (folder.isEmpty()) { d->folderList.clear(); d->nextFolder.clear(); } else { nextFolder = QLatin1Char(':') + folder + QLatin1Char(':'); } QUrl url(QString::fromLatin1("https://graph.microsoft.com/v1.0/me/drive/root%1/" "children?select=name,folder,path,parentReference").arg(nextFolder)); QNetworkRequest netRequest(url); netRequest.setRawHeader("Authorization", QString::fromLatin1("bearer %1").arg(d->accessToken).toUtf8()); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json")); d->reply = d->netMngr->get(netRequest); d->state = Private::OD_LISTFOLDERS; emit signalBusy(true); } bool ODTalker::addPhoto(const QString& imgPath, const QString& uploadFolder, bool rescale, int maxDim, int imageQuality) { if (d->reply) { d->reply->abort(); d->reply = nullptr; } emit signalBusy(true); ODMPForm form; QString path = imgPath; QMimeDatabase mimeDB; if (mimeDB.mimeTypeForFile(imgPath).name().startsWith(QLatin1String("image/"))) { QImage image = PreviewLoadThread::loadHighQualitySynchronously(imgPath).copyQImage(); if (image.isNull()) { emit signalBusy(false); return false; } path = WSToolUtils::makeTemporaryDir("onedrive").filePath(QFileInfo(imgPath) .baseName().trimmed() + QLatin1String(".jpg")); if (rescale && (image.width() > maxDim || image.height() > maxDim)) { image = image.scaled(maxDim, maxDim, Qt::KeepAspectRatio, Qt::SmoothTransformation); } image.save(path, "JPEG", imageQuality); DMetadata meta; if (meta.load(imgPath)) { meta.setItemDimensions(image.size()); meta.setItemOrientation(DMetadata::ORIENTATION_NORMAL); meta.setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY); meta.save(path, true); } } if (!form.addFile(path)) { emit signalBusy(false); return false; } QString uploadPath = uploadFolder + QUrl(imgPath).fileName(); QUrl url(QString::fromLatin1("https://graph.microsoft.com/v1.0/me/drive/root:/%1:/content").arg(uploadPath)); QNetworkRequest netRequest(url); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/octet-stream")); netRequest.setRawHeader("Authorization", QString::fromLatin1("bearer {%1}").arg(d->accessToken).toUtf8()); d->reply = d->netMngr->put(netRequest, form.formData()); d->state = Private::OD_ADDPHOTO; return true; } void ODTalker::slotFinished(QNetworkReply* reply) { if (reply != d->reply) { return; } d->reply = nullptr; if (reply->error() != QNetworkReply::NoError) { if (d->state != Private::OD_CREATEFOLDER) { emit signalBusy(false); QMessageBox::critical(QApplication::activeWindow(), i18n("Error"), reply->errorString()); reply->deleteLater(); return; } } QByteArray buffer = reply->readAll(); switch (d->state) { case Private::OD_LISTFOLDERS: qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In OD_LISTFOLDERS"; parseResponseListFolders(buffer); break; case Private::OD_CREATEFOLDER: qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In OD_CREATEFOLDER"; parseResponseCreateFolder(buffer); break; case Private::OD_ADDPHOTO: qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In OD_ADDPHOTO"; parseResponseAddPhoto(buffer); break; case Private::OD_USERNAME: qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In OD_USERNAME"; parseResponseUserName(buffer); break; default: break; } reply->deleteLater(); } void ODTalker::parseResponseAddPhoto(const QByteArray& data) { QJsonDocument doc = QJsonDocument::fromJson(data); QJsonObject jsonObject = doc.object(); bool success = jsonObject.contains(QLatin1String("size")); emit signalBusy(false); if (!success) { emit signalAddPhotoFailed(i18n("Failed to upload photo")); } else { emit signalAddPhotoSucceeded(); } } void ODTalker::parseResponseUserName(const QByteArray& data) { QJsonDocument doc = QJsonDocument::fromJson(data); QString name = doc.object()[QLatin1String("displayName")].toString(); emit signalBusy(false); emit signalSetUserName(name); } void ODTalker::parseResponseListFolders(const QByteArray& data) { QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { emit signalBusy(false); emit signalListAlbumsFailed(i18n("Failed to list folders")); return; } QJsonObject jsonObject = doc.object(); //qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Json: " << doc; QJsonArray jsonArray = jsonObject[QLatin1String("value")].toArray(); if (d->folderList.isEmpty()) { d->folderList.append(qMakePair(QLatin1String(""), QLatin1String("root"))); } foreach (const QJsonValue& value, jsonArray) { QString path; QString listName; QString folderPath; QString folderName; QJsonObject folder; QJsonObject parent; QJsonObject obj = value.toObject(); folder = obj[QLatin1String("folder")].toObject(); parent = obj[QLatin1String("parentReference")].toObject(); if (!folder.isEmpty()) { folderPath = parent[QLatin1String("path")].toString(); folderName = obj[QLatin1String("name")].toString(); path = folderPath.section(QLatin1String("root:"), -1, -1) + QLatin1Char('/') + folderName; path = QUrl(path).toString(QUrl::FullyDecoded); listName = path.section(QLatin1Char('/'), 1); d->folderList.append(qMakePair(path, listName)); if (folder[QLatin1String("childCount")].toInt() > 0) { d->nextFolder << path; } } } if (!d->nextFolder.isEmpty()) { listFolders(d->nextFolder.takeLast()); } else { std::sort(d->folderList.begin(), d->folderList.end()); emit signalBusy(false); emit signalListAlbumsDone(d->folderList); } } void ODTalker::parseResponseCreateFolder(const QByteArray& data) { QJsonDocument doc = QJsonDocument::fromJson(data); QJsonObject jsonObject = doc.object(); bool fail = jsonObject.contains(QLatin1String("error")); emit signalBusy(false); if (fail) { QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); emit signalCreateFolderFailed(jsonObject[QLatin1String("error_summary")].toString()); } else { emit signalCreateFolderSucceeded(); } } void ODTalker::writeSettings() { d->settings->beginGroup(d->serviceName); d->settings->setValue(d->serviceTime, d->expiryTime); d->settings->setValue(d->serviceKey, d->accessToken); d->settings->endGroup(); } void ODTalker::readSettings() { d->settings->beginGroup(d->serviceName); d->expiryTime = d->settings->value(d->serviceTime).toDateTime(); d->accessToken = d->settings->value(d->serviceKey).toString(); d->settings->endGroup(); if (d->accessToken.isEmpty()) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Linking..."; link(); } else if (QDateTime::currentDateTime() > d->expiryTime) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token has expired"; d->accessToken = QString(); link(); } else { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Already Linked"; emit oneDriveLinkingSucceeded(); } } } // namespace DigikamGenericOneDrivePlugin diff --git a/core/dplugins/generic/webservices/pinterest/ptalker.cpp b/core/dplugins/generic/webservices/pinterest/ptalker.cpp index dcfc3add40..7fffb0b377 100644 --- a/core/dplugins/generic/webservices/pinterest/ptalker.cpp +++ b/core/dplugins/generic/webservices/pinterest/ptalker.cpp @@ -1,645 +1,623 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2018-05-20 * Description : a tool to export images to Pinterest web service * * Copyright (C) 2018 by Tarek Talaat * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "ptalker.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include // KDE includes #include #include // Local includes -#include "digikam_config.h" #include "digikam_debug.h" #include "digikam_version.h" #include "wstoolutils.h" #include "pwindow.h" #include "pitem.h" +#include "webbrowserdlg.h" #include "previewloadthread.h" -#ifdef HAVE_QWEBENGINE -# include "webwidget_qwebengine.h" -#else -# include "webwidget.h" -#endif - namespace DigikamGenericPinterestPlugin { class Q_DECL_HIDDEN PTalker::Private { public: enum State { P_USERNAME = 0, P_LISTBOARDS, P_CREATEBOARD, P_ADDPIN, P_ACCESSTOKEN }; public: explicit Private() + : parent(nullptr), + netMngr(nullptr), + reply(nullptr), + settings(nullptr), + state(P_USERNAME), + browser(nullptr) { clientId = QLatin1String("4983380570301022071"); clientSecret = QLatin1String("2a698db679125930d922a2dfb897e16b668a67c6f614593636e83fc3d8d9b47d"); authUrl = QLatin1String("https://api.pinterest.com/oauth/"); tokenUrl = QLatin1String("https://api.pinterest.com/v1/oauth/token"); redirectUrl = QLatin1String("https://login.live.com/oauth20_desktop.srf"); - scope = QLatin1String("read_public,write_public"); - serviceName = QLatin1String("Pinterest"); serviceKey = QLatin1String("access_token"); - - state = P_USERNAME; - - parent = nullptr; - netMngr = nullptr; - reply = nullptr; - settings = nullptr; - view = nullptr; } public: QString clientId; QString clientSecret; QString authUrl; QString tokenUrl; QString redirectUrl; QString accessToken; QString scope; QString userName; QString serviceName; QString serviceKey; QWidget* parent; QNetworkAccessManager* netMngr; QNetworkReply* reply; QSettings* settings; State state; DMetadata meta; QMap urlParametersMap; - WebWidget* view; + WebBrowserDlg* browser; }; PTalker::PTalker(QWidget* const parent) : d(new Private) { d->parent = parent; d->netMngr = new QNetworkAccessManager(this); - d->view = new WebWidget(d->parent); - d->view->resize(800, 600); - d->settings = WSToolUtils::getOauthSettings(this); -#ifndef HAVE_QWEBENGINE - d->view->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); -#endif - connect(d->netMngr, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotFinished(QNetworkReply*))); connect(this, SIGNAL(pinterestLinkingFailed()), this, SLOT(slotLinkingFailed())); connect(this, SIGNAL(pinterestLinkingSucceeded()), this, SLOT(slotLinkingSucceeded())); - - connect(d->view, SIGNAL(urlChanged(QUrl)), - this, SLOT(slotCatchUrl(QUrl))); - - connect(d->view, SIGNAL(closeView(bool)), - this, SIGNAL(signalBusy(bool))); } PTalker::~PTalker() { if (d->reply) { d->reply->abort(); } WSToolUtils::removeTemporaryDir("pinterest"); delete d; } void PTalker::link() { emit signalBusy(true); QUrl url(d->authUrl); QUrlQuery query(url); query.addQueryItem(QLatin1String("client_id"), d->clientId); query.addQueryItem(QLatin1String("scope"), d->scope); query.addQueryItem(QLatin1String("redirect_uri"), d->redirectUrl); query.addQueryItem(QLatin1String("response_type"), QLatin1String("code")); url.setQuery(query); - d->view->setWindowFlags(Qt::Dialog); - d->view->load(url); - d->view->show(); + delete d->browser; + d->browser = new WebBrowserDlg(url, d->parent, true); + d->browser->setModal(true); + + connect(d->browser, SIGNAL(urlChanged(QUrl)), + this, SLOT(slotCatchUrl(QUrl))); + + connect(d->browser, SIGNAL(closeView(bool)), + this, SIGNAL(signalBusy(bool))); + + d->browser->show(); } void PTalker::unLink() { d->accessToken = QString(); d->settings->beginGroup(d->serviceName); d->settings->remove(QString()); d->settings->endGroup(); -#ifdef HAVE_QWEBENGINE - d->view->page()->profile()->cookieStore()->deleteAllCookies(); -#else - d->view->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); -#endif - emit pinterestLinkingSucceeded(); } void PTalker::slotCatchUrl(const QUrl& url) { d->urlParametersMap = ParseUrlParameters(url.toString()); QString code = d->urlParametersMap.value(QLatin1String("code")); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Received URL from webview in link function: " << url ; if (!code.isEmpty()) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "CODE Received"; - d->view->close(); + d->browser->close(); getToken(code); emit signalBusy(false); } } void PTalker::getToken(const QString& code) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Code: " << code; QUrl url(d->tokenUrl); QUrlQuery query(url); query.addQueryItem(QLatin1String("grant_type"), QLatin1String("authorization_code")); query.addQueryItem(QLatin1String("client_id"), d->clientId); query.addQueryItem(QLatin1String("client_secret"), d->clientSecret); query.addQueryItem(QLatin1String("code"), code); url.setQuery(query); qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Token Request URL: " << url.toString(); QNetworkRequest netRequest(url); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/x-www-form-urlencoded")); netRequest.setRawHeader("Accept", "application/json"); d->reply = d->netMngr->post(netRequest, QByteArray()); d->state = Private::P_ACCESSTOKEN; } QMap PTalker::ParseUrlParameters(const QString& url) { QMap urlParameters; if (url.indexOf(QLatin1Char('?')) == -1) { return urlParameters; } QString tmp = url.right(url.length()-url.indexOf(QLatin1Char('?')) - 1); QStringList paramlist = tmp.split(QLatin1Char('&')); for (int i = 0 ; i < paramlist.count() ; ++i) { QStringList paramarg = paramlist.at(i).split(QLatin1Char('=')); if (paramarg.count() == 2) { urlParameters.insert(paramarg.at(0), paramarg.at(1)); } } return urlParameters; } void PTalker::slotLinkingFailed() { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Pinterest fail"; emit signalBusy(false); } void PTalker::slotLinkingSucceeded() { if (d->accessToken.isEmpty()) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "UNLINK to Pinterest ok"; emit signalBusy(false); return; } qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to Pinterest ok"; writeSettings(); emit signalLinkingSucceeded(); } bool PTalker::authenticated() { return (!d->accessToken.isEmpty()); } void PTalker::cancel() { if (d->reply) { d->reply->abort(); d->reply = nullptr; } emit signalBusy(false); } void PTalker::createBoard(QString& boardName) { QUrl url(QLatin1String("https://api.pinterest.com/v1/boards/")); QNetworkRequest netRequest(url); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json")); netRequest.setRawHeader("Authorization", QString::fromLatin1("Bearer %1").arg(d->accessToken).toUtf8()); QByteArray postData = QString::fromUtf8("{\"name\": \"%1\"}").arg(boardName).toUtf8(); /* qCDebug(DIGIKAM_WEBSERVICES_LOG) << "createBoard:" << postData; */ d->reply = d->netMngr->post(netRequest, postData); d->state = Private::P_CREATEBOARD; emit signalBusy(true); } void PTalker::getUserName() { QUrl url(QLatin1String("https://api.pinterest.com/v1/me/?fields=username")); QNetworkRequest netRequest(url); netRequest.setRawHeader("Authorization", QString::fromLatin1("Bearer %1").arg(d->accessToken).toUtf8()); d->reply = d->netMngr->get(netRequest); d->state = Private::P_USERNAME; emit signalBusy(true); } /** * Get list of boards by parsing json sent by pinterest */ void PTalker::listBoards(const QString& /*path*/) { QUrl url(QLatin1String("https://api.pinterest.com/v1/me/boards/"));; QNetworkRequest netRequest(url); netRequest.setRawHeader("Authorization", QString::fromLatin1("Bearer %1").arg(d->accessToken).toUtf8()); //netRequest.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/json")); d->reply = d->netMngr->get(netRequest); d->state = Private::P_LISTBOARDS; emit signalBusy(true); } bool PTalker::addPin(const QString& imgPath, const QString& uploadBoard, bool rescale, int maxDim, int imageQuality) { if (d->reply) { d->reply->abort(); d->reply = nullptr; } emit signalBusy(true); QImage image = PreviewLoadThread::loadHighQualitySynchronously(imgPath).copyQImage(); if (image.isNull()) { emit signalBusy(false); return false; } QString path = WSToolUtils::makeTemporaryDir("pinterest").filePath(QFileInfo(imgPath) .baseName().trimmed() + QLatin1String(".jpg")); if (rescale && (image.width() > maxDim || image.height() > maxDim)) { image = image.scaled(maxDim, maxDim, Qt::KeepAspectRatio, Qt::SmoothTransformation); } image.save(path, "JPEG", imageQuality); if (d->meta.load(imgPath)) { d->meta.setItemDimensions(image.size()); d->meta.setItemOrientation(DMetadata::ORIENTATION_NORMAL); d->meta.setMetadataWritingMode((int)DMetadata::WRITE_TO_FILE_ONLY); d->meta.save(path, true); } QString boardParam = d->userName + QLatin1Char('/') + uploadBoard; QUrl url(QString::fromLatin1("https://api.pinterest.com/v1/pins/?access_token=%1").arg(d->accessToken)); QHttpMultiPart* const multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); // Board Section QHttpPart board; QString boardHeader = QLatin1String("form-data; name=\"board\"") ; board.setHeader(QNetworkRequest::ContentDispositionHeader, boardHeader); QByteArray postData = boardParam.toUtf8(); board.setBody(postData); multiPart->append(board); // Note section QHttpPart note; QString noteHeader = QLatin1String("form-data; name=\"note\"") ; note.setHeader(QNetworkRequest::ContentDispositionHeader, noteHeader); postData = QByteArray(); note.setBody(postData); multiPart->append(note); // image section QFile* const file = new QFile(imgPath); if (!file) { return false; } if (!file->open(QIODevice::ReadOnly)) { return false; } QHttpPart imagePart; QString imagePartHeader = QLatin1String("form-data; name=\"image\"; filename=\"") + QFileInfo(imgPath).fileName() + QLatin1Char('"'); imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, imagePartHeader); imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("image/jpeg")); imagePart.setBodyDevice(file); multiPart->append(imagePart); QString content = QLatin1String("multipart/form-data;boundary=") + QString::fromUtf8(multiPart->boundary()); QNetworkRequest netRequest(url); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, content); d->reply = d->netMngr->post(netRequest, multiPart); // delete the multiPart and file with the reply multiPart->setParent(d->reply); d->state = Private::P_ADDPIN; return true; } void PTalker::slotFinished(QNetworkReply* reply) { if (reply != d->reply) { return; } d->reply = nullptr; if (reply->error() != QNetworkReply::NoError) { if (d->state != Private::P_CREATEBOARD) { emit signalBusy(false); QMessageBox::critical(QApplication::activeWindow(), i18n("Error"), reply->errorString()); /* qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Error content: " << QString(reply->readAll()); */ reply->deleteLater(); return; } } QByteArray buffer = reply->readAll(); /* qCDebug(DIGIKAM_WEBSERVICES_LOG) << "BUFFER" << QString(buffer); */ switch (d->state) { case Private::P_LISTBOARDS: qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In P_LISTBOARDS"; parseResponseListBoards(buffer); break; case Private::P_CREATEBOARD: qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In P_CREATEBOARD"; parseResponseCreateBoard(buffer); break; case Private::P_ADDPIN: qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In P_ADDPIN"; parseResponseAddPin(buffer); break; case Private::P_USERNAME: qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In P_USERNAME"; parseResponseUserName(buffer); break; case Private::P_ACCESSTOKEN: qCDebug(DIGIKAM_WEBSERVICES_LOG) << "In P_ACCESSTOKEN"; parseResponseAccessToken(buffer); break; default: break; } reply->deleteLater(); } void PTalker::parseResponseAccessToken(const QByteArray& data) { QJsonDocument doc = QJsonDocument::fromJson(data); QJsonObject jsonObject = doc.object(); d->accessToken = jsonObject[QLatin1String("access_token")].toString(); if (!d->accessToken.isEmpty()) { qDebug(DIGIKAM_WEBSERVICES_LOG) << "Access token Received: " << d->accessToken; emit pinterestLinkingSucceeded(); } else { emit pinterestLinkingFailed(); } emit signalBusy(false); } void PTalker::parseResponseAddPin(const QByteArray& data) { QJsonDocument doc = QJsonDocument::fromJson(data); QJsonObject jsonObject = doc.object()[QLatin1String("data")].toObject(); bool success = jsonObject.contains(QLatin1String("id")); emit signalBusy(false); if (!success) { emit signalAddPinFailed(i18n("Failed to upload Pin")); } else { emit signalAddPinSucceeded(); } } void PTalker::parseResponseUserName(const QByteArray& data) { QJsonDocument doc = QJsonDocument::fromJson(data); QJsonObject jsonObject = doc.object()[QLatin1String("data")].toObject(); d->userName = jsonObject[QLatin1String("username")].toString(); emit signalBusy(false); emit signalSetUserName(d->userName); } void PTalker::parseResponseListBoards(const QByteArray& data) { QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { emit signalBusy(false); emit signalListBoardsFailed(i18n("Failed to list boards")); return; } QJsonObject jsonObject = doc.object(); /* qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Json Listing Boards : " << doc; */ QJsonArray jsonArray = jsonObject[QLatin1String("data")].toArray(); QList > list; QString boardID; QString boardName; foreach (const QJsonValue& value, jsonArray) { QString boardID; QString boardName; QJsonObject obj = value.toObject(); boardID = obj[QLatin1String("id")].toString(); boardName = obj[QLatin1String("name")].toString(); list.append(qMakePair(boardID, boardName)); } emit signalBusy(false); emit signalListBoardsDone(list); } void PTalker::parseResponseCreateBoard(const QByteArray& data) { QJsonDocument doc = QJsonDocument::fromJson(data); QJsonObject jsonObject = doc.object(); bool fail = jsonObject.contains(QLatin1String("error")); emit signalBusy(false); if (fail) { QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); emit signalCreateBoardFailed(jsonObject[QLatin1String("error_summary")].toString()); } else { emit signalCreateBoardSucceeded(); } } void PTalker::writeSettings() { d->settings->beginGroup(d->serviceName); d->settings->setValue(d->serviceKey, d->accessToken); d->settings->endGroup(); } void PTalker::readSettings() { d->settings->beginGroup(d->serviceName); d->accessToken = d->settings->value(d->serviceKey).toString(); d->settings->endGroup(); if (d->accessToken.isEmpty()) { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Linking..."; link(); } else { qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Already Linked"; emit pinterestLinkingSucceeded(); } } } // namespace DigikamGenericPinterestPlugin diff --git a/core/libs/dialogs/webbrowserdlg.cpp b/core/libs/dialogs/webbrowserdlg.cpp index 19ce380277..4d9eaa22b7 100644 --- a/core/libs/dialogs/webbrowserdlg.cpp +++ b/core/libs/dialogs/webbrowserdlg.cpp @@ -1,262 +1,274 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2017-06-21 * Description : a simple web browser dialog based on Qt WebEngine. * * Copyright (C) 2017-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "webbrowserdlg.h" #include "digikam_config.h" // Qt includes #include #include #include #include #include #include -#include +#include #ifdef HAVE_QWEBENGINE # include # include +# include +# include #else # include #endif // KDE includes #include -#include #include +#include // Local includes #include "statusprogressbar.h" #include "searchtextbar.h" #include "dxmlguiwindow.h" +#include "digikam_debug.h" namespace Digikam { class Q_DECL_HIDDEN WebBrowserDlg::Private { public: explicit Private() { browser = nullptr; toolbar = nullptr; progressbar = nullptr; searchbar = nullptr; } public: QUrl home; #ifdef HAVE_QWEBENGINE QWebEngineView* browser; #else QWebView* browser; #endif QToolBar* toolbar; StatusProgressBar* progressbar; SearchTextBar* searchbar; }; -WebBrowserDlg::WebBrowserDlg(const QUrl& url, QWidget* const parent) +WebBrowserDlg::WebBrowserDlg(const QUrl& url, QWidget* const parent, bool hideDeskBrowser) : QDialog(parent), d(new Private) { setModal(false); d->home = url; #ifdef HAVE_QWEBENGINE d->browser = new QWebEngineView(this); + d->browser->page()->profile()->cookieStore()->deleteAllCookies(); #else d->browser = new QWebView(this); + d->browser->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true); + d->browser->page()->networkAccessManager()->setCookieJar(new QNetworkCookieJar()); #endif // -------------------------- d->toolbar = new QToolBar(this); d->toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); #ifdef HAVE_QWEBENGINE d->toolbar->addAction(d->browser->pageAction(QWebEnginePage::Back)); d->toolbar->addAction(d->browser->pageAction(QWebEnginePage::Forward)); d->toolbar->addAction(d->browser->pageAction(QWebEnginePage::Reload)); d->toolbar->addAction(d->browser->pageAction(QWebEnginePage::Stop)); #else d->toolbar->addAction(d->browser->pageAction(QWebPage::Back)); d->toolbar->addAction(d->browser->pageAction(QWebPage::Forward)); d->toolbar->addAction(d->browser->pageAction(QWebPage::Reload)); d->toolbar->addAction(d->browser->pageAction(QWebPage::Stop)); #endif QAction* const gohome = new QAction(QIcon::fromTheme(QLatin1String("go-home")), i18n("Home"), this); gohome->setToolTip(i18n("Go back to Home page")); d->toolbar->addAction(gohome); QAction* const deskweb = new QAction(QIcon::fromTheme(QLatin1String("internet-web-browser")), i18n("Desktop Browser"), this); deskweb->setToolTip(i18n("Open Home page with default desktop Web browser")); - d->toolbar->addAction(deskweb); + + if (!hideDeskBrowser) + { + d->toolbar->addAction(deskweb); + } // -------------------------- d->searchbar = new SearchTextBar(this, QLatin1String("WebBrowserDlgSearchBar")); d->searchbar->setHighlightOnResult(true); d->progressbar = new StatusProgressBar(this); d->progressbar->setProgressTotalSteps(100); d->progressbar->setAlignment(Qt::AlignLeft); d->progressbar->setNotify(false); // ---------------------- QGridLayout* const grid = new QGridLayout(this); grid->setSpacing(QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing)); grid->addWidget(d->toolbar, 0, 0, 1, 1); grid->addWidget(d->searchbar, 0, 2, 1, 1); grid->addWidget(d->browser, 1, 0, 1, 3); grid->addWidget(d->progressbar, 2, 0, 1, 3); grid->setColumnStretch(1, 10); grid->setRowStretch(1, 10); setLayout(grid); // ---------------------- /* #if QT_VERSION >= 0x050700 connect(d->browser, SIGNAL(iconChanged(const QIcon&)), this, SLOT(slotIconChanged(const QIcon&))); #endif */ connect(d->browser, SIGNAL(titleChanged(QString)), this, SLOT(slotTitleChanged(QString))); connect(d->browser, SIGNAL(urlChanged(QUrl)), this, SLOT(slotUrlChanged(QUrl))); connect(d->browser, SIGNAL(loadStarted()), this, SLOT(slotLoadingStarted())); connect(d->browser, SIGNAL(loadFinished(bool)), this, SLOT(slotLoadingFinished(bool))); connect(d->searchbar, SIGNAL(signalSearchTextSettings(SearchTextSettings)), this, SLOT(slotSearchTextChanged(SearchTextSettings))); connect(d->browser, SIGNAL(loadProgress(int)), d->progressbar, SLOT(setProgressValue(int))); connect(gohome, SIGNAL(triggered()), this, SLOT(slotGoHome())); connect(deskweb, SIGNAL(triggered()), this, SLOT(slotDesktopWebBrowser())); // ---------------------- KConfigGroup group = KSharedConfig::openConfig()->group("WebBrowserDlg"); winId(); windowHandle()->resize(800, 600); DXmlGuiWindow::restoreWindowSize(windowHandle(), group); resize(windowHandle()->size()); slotGoHome(); } WebBrowserDlg::~WebBrowserDlg() { delete d; } void WebBrowserDlg::closeEvent(QCloseEvent* e) { KConfigGroup group = KSharedConfig::openConfig()->group(QLatin1String("WebBrowserDlg")); DXmlGuiWindow::saveWindowSize(windowHandle(), group); + emit closeView(false); e->accept(); } void WebBrowserDlg::slotUrlChanged(const QUrl& url) { d->progressbar->setText(url.toString()); + emit urlChanged(url); } void WebBrowserDlg::slotTitleChanged(const QString& title) { setWindowTitle(title); } void WebBrowserDlg::slotIconChanged(const QIcon& icon) { setWindowIcon(icon); } void WebBrowserDlg::slotLoadingStarted() { d->progressbar->setProgressBarMode(StatusProgressBar::ProgressBarMode); } void WebBrowserDlg::slotLoadingFinished(bool b) { QString curUrl = d->browser->url().toString(); d->progressbar->setProgressBarMode(StatusProgressBar::TextMode, curUrl); if (!b) { d->progressbar->setText(i18n("Cannot load page %1", curUrl)); } } void WebBrowserDlg::slotSearchTextChanged(const SearchTextSettings& settings) { #ifdef HAVE_QWEBENGINE d->browser->findText(settings.text, (settings.caseSensitive == Qt::CaseSensitive) ? QWebEnginePage::FindCaseSensitively : QWebEnginePage::FindFlags(), [this](bool found) { d->searchbar->slotSearchResult(found); }); #else bool found = d->browser->findText( settings.text, (settings.caseSensitive == Qt::CaseInsensitive) ? QWebPage::FindCaseSensitively : QWebPage::FindFlags()); d->searchbar->slotSearchResult(found); #endif } void WebBrowserDlg::slotGoHome() { d->browser->setUrl(d->home); } void WebBrowserDlg::slotDesktopWebBrowser() { QDesktopServices::openUrl(d->home); } } // namespace Digikam diff --git a/core/libs/dialogs/webbrowserdlg.h b/core/libs/dialogs/webbrowserdlg.h index 896f29f9aa..499dd0ec39 100644 --- a/core/libs/dialogs/webbrowserdlg.h +++ b/core/libs/dialogs/webbrowserdlg.h @@ -1,76 +1,83 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2017-06-21 * Description : a simple web browser dialog based on Qt WebEngine. * * Copyright (C) 2017-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_WEB_BROWSER_DLG_H #define DIGIKAM_WEB_BROWSER_DLG_H // Qt include #include #include #include #include #include // Local includes #include "digikam_export.h" namespace Digikam { class SearchTextSettings; class DIGIKAM_EXPORT WebBrowserDlg : public QDialog { Q_OBJECT public: - explicit WebBrowserDlg(const QUrl& url, QWidget* const parent = nullptr); + explicit WebBrowserDlg(const QUrl& url, + QWidget* const parent, + bool hideDeskBrowser = false); ~WebBrowserDlg(); +Q_SIGNALS: + + void urlChanged(const QUrl& url); + void closeView(bool val); + private Q_SLOTS: void slotUrlChanged(const QUrl&); void slotLoadingStarted(); void slotLoadingFinished(bool); void slotIconChanged(const QIcon&); void slotTitleChanged(const QString&); void slotSearchTextChanged(const SearchTextSettings&); void slotGoHome(); void slotDesktopWebBrowser(); protected: void closeEvent(QCloseEvent*) override; private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_WEB_BROWSER_DLG_H diff --git a/core/libs/dimg/CMakeLists.txt b/core/libs/dimg/CMakeLists.txt index 51d6ccacf5..c7d0b6732a 100644 --- a/core/libs/dimg/CMakeLists.txt +++ b/core/libs/dimg/CMakeLists.txt @@ -1,222 +1,221 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. kde_enable_exceptions() # Boost uses operator names (and, not, ...) string(REPLACE "-fno-operator-names" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") set(libdimg_SRCS dimg.cpp dimg_bitsops.cpp dimg_colors.cpp dimg_copy.cpp dimg_data.cpp dimg_fileio.cpp dimg_metadata.cpp dimg_props.cpp dimg_qimage.cpp dimg_qpixmap.cpp dimg_scale.cpp dimg_transform.cpp - drawdecoding.cpp - dcolor.cpp - dcolorcomposer.cpp + color/dcolor.cpp + color/dcolorcomposer.cpp history/dimagehistory.cpp history/filteraction.cpp history/historyimageid.cpp ) set(libdimgfilters_SRCS filters/dimgbuiltinfilter.cpp filters/dimgthreadedfilter.cpp filters/dimgthreadedanalyser.cpp filters/dimgfiltermanager.cpp filters/dimgfiltergenerator.cpp filters/dpixelsaliasfilter.cpp filters/filteractionfilter.cpp filters/randomnumbergenerator.cpp - filters/rawprocessingfilter.cpp + filters/raw/rawprocessingfilter.cpp + filters/raw/drawdecoding.cpp filters/decorate/borderfilter.cpp filters/decorate/bordersettings.cpp filters/decorate/texturefilter.cpp filters/film/filmfilter.cpp filters/fx/blurfilter.cpp filters/fx/blurfxfilter.cpp filters/fx/colorfxfilter.cpp filters/fx/colorfxsettings.cpp filters/fx/distortionfxfilter.cpp filters/fx/charcoalfilter.cpp filters/fx/embossfilter.cpp filters/fx/filmgrainfilter.cpp filters/fx/filmgrainsettings.cpp filters/fx/invertfilter.cpp filters/fx/oilpaintfilter.cpp filters/fx/raindropfilter.cpp filters/auto/autolevelsfilter.cpp filters/auto/autoexpofilter.cpp filters/auto/equalizefilter.cpp filters/auto/stretchfilter.cpp filters/auto/normalizefilter.cpp filters/cb/cbfilter.cpp filters/cb/cbsettings.cpp filters/bcg/bcgfilter.cpp filters/bcg/bcgsettings.cpp filters/bw/bwsepiafilter.cpp filters/bw/bwsepiasettings.cpp filters/bw/tonalityfilter.cpp filters/bw/infraredfilter.cpp filters/bw/mixerfilter.cpp filters/bw/mixersettings.cpp filters/hsl/hslfilter.cpp filters/hsl/hslsettings.cpp filters/hsl/hspreviewwidget.cpp filters/icc/iccmanager.cpp filters/icc/iccprofile.cpp filters/icc/iccprofilesettings.cpp filters/icc/icctransform.cpp filters/icc/icctransformfilter.cpp filters/icc/iccsettingscontainer.cpp filters/icc/iccsettings.cpp filters/icc/digikam-lcms.cpp filters/lc/localcontrastfilter.cpp filters/lc/localcontrastsettings.cpp filters/lc/localcontrastcontainer.cpp filters/nr/nrfilter.cpp filters/nr/nrestimate.cpp filters/nr/nrsettings.cpp filters/sharp/sharpenfilter.cpp filters/sharp/unsharpmaskfilter.cpp filters/sharp/sharpsettings.cpp filters/levels/imagelevels.cpp filters/levels/levelsfilter.cpp filters/levels/imagehistogram.cpp filters/levels/histogrambox.cpp filters/levels/histogramwidget.cpp filters/levels/histogrampainter.cpp filters/curves/curvescontainer.cpp filters/curves/imagecurves.cpp filters/curves/curvesfilter.cpp filters/curves/curvessettings.cpp filters/curves/curveswidget.cpp filters/curves/curvesbox.cpp filters/wb/wbcontainer.cpp filters/wb/wbfilter.cpp filters/wb/wbsettings.cpp filters/transform/freerotationfilter.cpp filters/transform/freerotationsettings.cpp filters/transform/shearfilter.cpp filters/transform/autocrop.cpp filters/greycstoration/greycstorationfilter.cpp filters/greycstoration/greycstorationsettings.cpp filters/lens/antivignettingfilter.cpp filters/lens/antivignettingsettings.cpp filters/lens/lensdistortionfilter.cpp filters/lens/lensdistortionpixelaccess.cpp filters/redeye/redeyecorrectionfilter.cpp filters/redeye/redeyecorrectionsettings.cpp filters/redeye/redeyecorrectioncontainer.cpp filters/imgqsort/imagequalitycontainer.cpp filters/imgqsort/imagequalitysettings.cpp filters/imgqsort/imagequalityparser.cpp filters/imgqsort/imagequalityparser_blur.cpp filters/imgqsort/imagequalityparser_exposure.cpp filters/imgqsort/imagequalityparser_noise.cpp filters/imgqsort/imagequalityparser_compression.cpp ) # ================================================================================================== # get the gcc version # CImg.h version 1.2.8 do not compile fine with gcc 4.3.x # See bug #163118: digikam-0.9.4_beta5 compilation hangs with gcc 4.3 # Using -fno-tree-pre is work around this problem. # TODO is this hack anymore required? if(CMAKE_COMPILER_IS_GNUCXX) exec_program(${CMAKE_CXX_COMPILER} ARGS ${CMAKE_CXX_COMPILER_ARG1} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if (${GCC_VERSION} VERSION_LESS "4.3.0") message(STATUS "Adjusting compilation flags for GCC version (${GCC_VERSION} )") add_definitions(-fno-tree-pre) endif() endif() if(Lqr-1_FOUND) set(libdimgfilters_SRCS ${libdimgfilters_SRCS} filters/transform/contentawarefilter.cpp ) include_directories(${LQR-1_INCLUDE_DIRS}) endif() if(LensFun_FOUND) set(libdimgfilters_SRCS ${libdimgfilters_SRCS} filters/lens/lensfunfilter.cpp filters/lens/lensfuncameraselector.cpp filters/lens/lensfuniface.cpp filters/lens/lensfunsettings.cpp ) include_directories(${LENSFUN_INCLUDE_DIRS}) endif() if(Eigen3_FOUND) set(libdimgfilters_SRCS ${libdimgfilters_SRCS} filters/sharp/refocusfilter.cpp filters/sharp/refocusmatrix.cpp ) include_directories(${EIGEN3_INCLUDE_DIR}) endif() set(libdimgloaders_SRCS loaders/dimgloader.cpp loaders/jpegsettings.cpp loaders/pngsettings.cpp loaders/tiffsettings.cpp loaders/pgfsettings.cpp ) # JPEG2000 support if(Jasper_FOUND) set(libdimgloaders_SRCS ${libdimgloaders_SRCS} loaders/jp2ksettings.cpp ) endif() # HEIF support if(X265_FOUND) set(libdimgloaders_SRCS ${libdimgloaders_SRCS} loaders/heifsettings.cpp ) endif() include_directories( $ $ $ $ $ $ ) if(X11_FOUND) include_directories($) endif() add_library(dimg_src OBJECT ${libdimgloaders_SRCS} ${libdimgfilters_SRCS} ${libdimg_SRCS} - ${libdlib_SRCS} ) diff --git a/core/libs/dimg/dcolor.cpp b/core/libs/dimg/color/dcolor.cpp similarity index 100% rename from core/libs/dimg/dcolor.cpp rename to core/libs/dimg/color/dcolor.cpp diff --git a/core/libs/dimg/dcolor.h b/core/libs/dimg/color/dcolor.h similarity index 100% rename from core/libs/dimg/dcolor.h rename to core/libs/dimg/color/dcolor.h diff --git a/core/libs/dimg/dcolorblend.h b/core/libs/dimg/color/dcolorblend.h similarity index 100% rename from core/libs/dimg/dcolorblend.h rename to core/libs/dimg/color/dcolorblend.h diff --git a/core/libs/dimg/dcolorcomposer.cpp b/core/libs/dimg/color/dcolorcomposer.cpp similarity index 100% rename from core/libs/dimg/dcolorcomposer.cpp rename to core/libs/dimg/color/dcolorcomposer.cpp diff --git a/core/libs/dimg/dcolorcomposer.h b/core/libs/dimg/color/dcolorcomposer.h similarity index 100% rename from core/libs/dimg/dcolorcomposer.h rename to core/libs/dimg/color/dcolorcomposer.h diff --git a/core/libs/dimg/dcolorpixelaccess.h b/core/libs/dimg/color/dcolorpixelaccess.h similarity index 100% rename from core/libs/dimg/dcolorpixelaccess.h rename to core/libs/dimg/color/dcolorpixelaccess.h diff --git a/core/libs/dimg/filters/README b/core/libs/dimg/filters/README index e2ce0598b6..53ddf41a52 100644 --- a/core/libs/dimg/filters/README +++ b/core/libs/dimg/filters/README @@ -1,20 +1,21 @@ auto : Auto Color Correction. bw : Black and White. bcg : Brighness / Contrast / Gamma. cb : Color Balance Correction. curves : Histogram Curves Correction. decorate : Decorative Effects. fx : Special Effects. greystoration : CImg library interface for Greystoration filter. hsl : Hue / Saturation / Lightness. icc : Color Management. imgqsort : Image Quality Sorter. levels : Histogram Levels Correction. lens : Misc lens correction filters. lut3d : 3D LUT color filters. lc : Local Contrast (LDR Tonemapping). nr : Wavelets Noise Reduction. +raw : RAW image processing sharp : Sharp / Unsharp Mask / Refocus transform : Change Image Geometry. wb : White Balance. redeye : Red-Eye Detection and Correction. diff --git a/core/libs/dimg/filters/auto/autoexpofilter.h b/core/libs/dimg/filters/auto/autoexpofilter.h index 957dae3717..329713de78 100644 --- a/core/libs/dimg/filters/auto/autoexpofilter.h +++ b/core/libs/dimg/filters/auto/autoexpofilter.h @@ -1,85 +1,85 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-15-02 * Description : auto exposure image filter. * * Copyright (C) 2010-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_AUTO_EXPO_FILTER_H #define DIGIKAM_AUTO_EXPO_FILTER_H // Local includes #include "digikam_export.h" #include "wbfilter.h" #include "digikam_globals.h" #include "filteraction.h" namespace Digikam { class DImg; class DIGIKAM_EXPORT AutoExpoFilter : public WBFilter { public: explicit AutoExpoFilter(QObject* const parent = nullptr); AutoExpoFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent=nullptr); virtual ~AutoExpoFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:AutoExpoFilter"); } static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } static QString DisplayableName(); - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; + virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; private: DImg m_refImage; }; } // namespace Digikam #endif // DIGIKAM_AUTO_EXPO_FILTER_H diff --git a/core/libs/dimg/filters/auto/autolevelsfilter.cpp b/core/libs/dimg/filters/auto/autolevelsfilter.cpp index bc70e8cdf8..c6ef03acd5 100644 --- a/core/libs/dimg/filters/auto/autolevelsfilter.cpp +++ b/core/libs/dimg/filters/auto/autolevelsfilter.cpp @@ -1,189 +1,195 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : auto levels image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "autolevelsfilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" #include "digikam_debug.h" #include "imagehistogram.h" #include "imagelevels.h" namespace Digikam { AutoLevelsFilter::AutoLevelsFilter(QObject* const parent) : DImgThreadedFilter(parent) { initFilter(); } AutoLevelsFilter::AutoLevelsFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent) : DImgThreadedFilter(orgImage, parent, QLatin1String("AutoLevelsFilter")), m_refImage(*refImage) { initFilter(); } AutoLevelsFilter::~AutoLevelsFilter() { cancelFilter(); } QString AutoLevelsFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Auto Levels")); } void AutoLevelsFilter::filterImage() { if (m_refImage.isNull()) { m_refImage = m_orgImage; } autoLevelsCorrectionImage(); m_destImage = m_orgImage; } /** * Performs histogram auto correction of levels. * This method maximizes the tonal range in the Red, * Green, and Blue channels. It search the image shadow and highlight * limit values and adjust the Red, Green, and Blue channels * to a full histogram range. */ void AutoLevelsFilter::autoLevelsCorrectionImage() { if (m_orgImage.sixteenBit() != m_refImage.sixteenBit()) { qCDebug(DIGIKAM_DIMG_LOG) << "Ref. image and Org. has different bits depth"; return; } uchar* data = m_orgImage.bits(); int w = m_orgImage.width(); int h = m_orgImage.height(); bool sixteenBit = m_orgImage.sixteenBit(); QScopedArrayPointer desData; QScopedPointer histogram; QScopedPointer levels; postProgress(10); int sizeSixteenBit = w * h * 8; int sizeEightBit = w * h * 4; // Create the new empty destination image data space. + if (runningFlag()) { if (sixteenBit) { desData.reset(new uchar[sizeSixteenBit]); } else { desData.reset(new uchar[sizeEightBit]); } postProgress(20); } // Create an histogram of the reference image. + if (runningFlag()) { histogram.reset(new ImageHistogram(m_refImage)); histogram->calculate(); postProgress(30); } // Create an empty instance of levels to use. + if (runningFlag()) { levels.reset(new ImageLevels(sixteenBit)); postProgress(40); } // Initialize an auto levels correction of the histogram. + if (runningFlag()) { levels->levelsAuto(histogram.data()); postProgress(50); } // Calculate the LUT to apply on the image. + if (runningFlag()) { levels->levelsLutSetup(AlphaChannel); postProgress(60); } // Apply the lut to the image. + if (runningFlag()) { levels->levelsLutProcess(data, desData.data(), w, h); postProgress(70); } if (runningFlag()) { if (sixteenBit) { memcpy(data, desData.data(), sizeSixteenBit); } else { memcpy(data, desData.data(), sizeEightBit); } postProgress(80); } if (runningFlag()) { postProgress(90); } } FilterAction AutoLevelsFilter::filterAction() { return DefaultFilterAction(); } void AutoLevelsFilter::readParameters(const FilterAction& /*action*/) { return; } } // namespace Digikam diff --git a/core/libs/dimg/filters/auto/autolevelsfilter.h b/core/libs/dimg/filters/auto/autolevelsfilter.h index cad175ba17..65d38b5028 100644 --- a/core/libs/dimg/filters/auto/autolevelsfilter.h +++ b/core/libs/dimg/filters/auto/autolevelsfilter.h @@ -1,84 +1,85 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : auto levels image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_AUTO_LEVELS_FILTER_H #define DIGIKAM_AUTO_LEVELS_FILTER_H // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "digikam_globals.h" namespace Digikam { class DImg; class DIGIKAM_EXPORT AutoLevelsFilter : public DImgThreadedFilter { public: + explicit AutoLevelsFilter(QObject* const parent = nullptr); AutoLevelsFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent=nullptr); virtual ~AutoLevelsFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:AutoLevelsFilter"); } static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } static QString DisplayableName(); - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; + virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; void autoLevelsCorrectionImage(); private: DImg m_refImage; }; } // namespace Digikam #endif // DIGIKAM_AUTO_LEVELS_FILTER_H diff --git a/core/libs/dimg/filters/auto/equalizefilter.cpp b/core/libs/dimg/filters/auto/equalizefilter.cpp index 38036f0493..ac30a712b6 100644 --- a/core/libs/dimg/filters/auto/equalizefilter.cpp +++ b/core/libs/dimg/filters/auto/equalizefilter.cpp @@ -1,262 +1,266 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : equalize image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "equalizefilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" #include "digikam_debug.h" #include "imagehistogram.h" namespace Digikam { EqualizeFilter::EqualizeFilter(QObject* const parent) : DImgThreadedFilter(parent) { initFilter(); } EqualizeFilter::EqualizeFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent) : DImgThreadedFilter(orgImage, parent, QLatin1String("EqualizeFilter")), m_refImage(*refImage) { initFilter(); } EqualizeFilter::~EqualizeFilter() { } QString EqualizeFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Auto Equalize")); } void EqualizeFilter::filterImage() { if (m_refImage.isNull()) { m_refImage = m_orgImage; } equalizeImage(); m_destImage = m_orgImage; } /** * Performs an histogram equalization of the image. * this method adjusts the brightness of colors across the * active image so that the histogram for the value channel * is as nearly as possible flat, that is, so that each possible * brightness value appears at about the same number of pixels * as each other value. Sometimes Equalize works wonderfully at * enhancing the contrasts in an image. Other times it gives * garbage. It is a very powerful operation, which can either work * miracles on an image or destroy it. */ void EqualizeFilter::equalizeImage() { if (m_orgImage.sixteenBit() != m_refImage.sixteenBit()) { qCDebug(DIGIKAM_DIMG_LOG) << "Ref. image and Org. has different bits depth"; return; } struct double_packet high, low, intensity; int i; int progress; // Create an histogram of the reference image. + QScopedPointer histogram(new ImageHistogram(m_refImage)); histogram->calculate(); // Memory allocation. + QScopedArrayPointer map(new double_packet[histogram->getHistogramSegments()]); QScopedArrayPointer equalize_map(new int_packet[histogram->getHistogramSegments()]); if (map.isNull() || equalize_map.isNull()) { qCWarning(DIGIKAM_DIMG_LOG) << ("Unable to allocate memory!"); return; } // Integrate the histogram to get the equalization map. for (i = 0 ; runningFlag() && (i < histogram->getHistogramSegments()) ; ++i) { intensity.red += histogram->getValue(RedChannel, i); intensity.green += histogram->getValue(GreenChannel, i); intensity.blue += histogram->getValue(BlueChannel, i); intensity.alpha += histogram->getValue(AlphaChannel, i); map[i] = intensity; } // Stretch the histogram. low = map[0]; high = map[histogram->getHistogramSegments() - 1]; // TODO magic number 256 + for (i = 0 ; runningFlag() && (i < histogram->getHistogramSegments()) ; ++i) { if (high.red != low.red) equalize_map[i].red = (uint)(((256 * histogram->getHistogramSegments() - 1) * (map[i].red - low.red)) / (high.red - low.red)); if (high.green != low.green) equalize_map[i].green = (uint)(((256 * histogram->getHistogramSegments() - 1) * (map[i].green - low.green)) / (high.green - low.green)); if (high.blue != low.blue) equalize_map[i].blue = (uint)(((256 * histogram->getHistogramSegments() - 1) * (map[i].blue - low.blue)) / (high.blue - low.blue)); if (high.alpha != low.alpha) equalize_map[i].alpha = (uint)(((256 * histogram->getHistogramSegments() - 1) * (map[i].alpha - low.alpha)) / (high.alpha - low.alpha)); } uchar* data = m_orgImage.bits(); int w = m_orgImage.width(); int h = m_orgImage.height(); bool sixteenBit = m_orgImage.sixteenBit(); int size = w * h; // Apply results to image. // TODO magic number 257 + if (!sixteenBit) // 8 bits image. { uchar red, green, blue, alpha; uchar* ptr = data; for (i = 0 ; runningFlag() && (i < size) ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; alpha = ptr[3]; if (low.red != high.red) { red = (equalize_map[red].red) / 257; } if (low.green != high.green) { green = (equalize_map[green].green) / 257; } if (low.blue != high.blue) { blue = (equalize_map[blue].blue) / 257; } if (low.alpha != high.alpha) { alpha = (equalize_map[alpha].alpha) / 257; } ptr[0] = blue; ptr[1] = green; ptr[2] = red; ptr[3] = alpha; ptr += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } else // 16 bits image. { unsigned short red, green, blue, alpha; unsigned short* ptr = reinterpret_cast(data); for (i = 0 ; runningFlag() && (i < size) ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; alpha = ptr[3]; if (low.red != high.red) { red = (equalize_map[red].red) / 257; } if (low.green != high.green) { green = (equalize_map[green].green) / 257; } if (low.blue != high.blue) { blue = (equalize_map[blue].blue) / 257; } if (low.alpha != high.alpha) { alpha = (equalize_map[alpha].alpha) / 257; } ptr[0] = blue; ptr[1] = green; ptr[2] = red; ptr[3] = alpha; ptr += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } } FilterAction EqualizeFilter::filterAction() { return DefaultFilterAction(); } void EqualizeFilter::readParameters(const FilterAction& /*action*/) { return; } } // namespace Digikam diff --git a/core/libs/dimg/filters/auto/equalizefilter.h b/core/libs/dimg/filters/auto/equalizefilter.h index 416b289b51..53efafeca5 100644 --- a/core/libs/dimg/filters/auto/equalizefilter.h +++ b/core/libs/dimg/filters/auto/equalizefilter.h @@ -1,117 +1,117 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : equalize image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_EQUALIZE_FILTER_H #define DIGIKAM_EQUALIZE_FILTER_H // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "digikam_globals.h" namespace Digikam { class DImg; class DIGIKAM_EXPORT EqualizeFilter : public DImgThreadedFilter { public: explicit EqualizeFilter(QObject* const parent = nullptr); EqualizeFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent=nullptr); virtual ~EqualizeFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:EqualizeFilter"); } static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } static QString DisplayableName(); - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; + virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; void equalizeImage(); private: struct double_packet { double_packet() : red(0.0), green(0.0), blue(0.0), alpha(0.0) { } double red; double green; double blue; double alpha; }; struct int_packet { int_packet() : red(0), green(0), blue(0), alpha(0) { } unsigned int red; unsigned int green; unsigned int blue; unsigned int alpha; }; DImg m_refImage; }; } // namespace Digikam #endif // DIGIKAM_EQUALIZE_FILTER_H diff --git a/core/libs/dimg/filters/auto/normalizefilter.cpp b/core/libs/dimg/filters/auto/normalizefilter.cpp index b3067d05c7..d5f718d323 100644 --- a/core/libs/dimg/filters/auto/normalizefilter.cpp +++ b/core/libs/dimg/filters/auto/normalizefilter.cpp @@ -1,286 +1,287 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : normalize image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "normalizefilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" #include "digikam_debug.h" namespace Digikam { NormalizeFilter::NormalizeFilter(QObject* const parent) : DImgThreadedFilter(parent) { initFilter(); } NormalizeFilter::NormalizeFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent) : DImgThreadedFilter(orgImage, parent, QLatin1String("NormalizeFilter")), m_refImage(*refImage) { initFilter(); } NormalizeFilter::~NormalizeFilter() { cancelFilter(); } QString NormalizeFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Auto Normalize")); } void NormalizeFilter::filterImage() { if (m_refImage.isNull()) { m_refImage = m_orgImage; } normalizeImage(); m_destImage = m_orgImage; } /** * This method scales brightness values across the active * image so that the darkest point becomes black, and the * brightest point becomes as bright as possible without * altering its hue. This is often a magic fix for * images that are dim or washed out. */ void NormalizeFilter::normalizeImage() { if (m_orgImage.sixteenBit() != m_refImage.sixteenBit()) { qCDebug(DIGIKAM_DIMG_LOG) << "Ref. image and Org. has different bits depth"; return; } bool sixteenBit = m_orgImage.sixteenBit(); int segments = sixteenBit ? NUM_SEGMENTS_16BIT : NUM_SEGMENTS_8BIT; // Memory allocation. NormalizeParam param; param.lut = new unsigned short[segments]; // Find min. and max. values. param.min = segments - 1; param.max = 0; uint refSize = m_refImage.width() * m_refImage.height(); if (!sixteenBit) // 8 bits image. { uchar red, green, blue; uchar* ptr = m_refImage.bits(); for (uint i = 0 ; runningFlag() && (i < refSize) ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; if (red < param.min) { param.min = red; } if (red > param.max) { param.max = red; } if (green < param.min) { param.min = green; } if (green > param.max) { param.max = green; } if (blue < param.min) { param.min = blue; } if (blue > param.max) { param.max = blue; } ptr += 4; } } else // 16 bits image. { unsigned short red, green, blue; unsigned short* ptr = reinterpret_cast(m_refImage.bits()); for (uint i = 0 ; runningFlag() && (i < refSize) ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; if (red < param.min) { param.min = red; } if (red > param.max) { param.max = red; } if (green < param.min) { param.min = green; } if (green > param.max) { param.max = green; } if (blue < param.min) { param.min = blue; } if (blue > param.max) { param.max = blue; } ptr += 4; } } // Calculate LUT. if (runningFlag()) { unsigned short range = (unsigned short)(param.max - param.min); if (range != 0) { for (int x = (int)param.min ; x <= (int)param.max ; ++x) { param.lut[x] = (unsigned short)((segments - 1) * (x - param.min) / range); } } else { param.lut[(int)param.min] = (unsigned short)param.min; } } uchar* data = m_orgImage.bits(); int w = m_orgImage.width(); int h = m_orgImage.height(); uint size = w * h; // Apply LUT to image. + int progress = 0; if (!sixteenBit) // 8 bits image. { uchar red, green, blue; uchar* ptr = data; for (uint i = 0 ; runningFlag() && (i < size) ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; ptr[0] = param.lut[blue]; ptr[1] = param.lut[green]; ptr[2] = param.lut[red]; - ptr += 4; + ptr += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } else // 16 bits image. { unsigned short red, green, blue; unsigned short* ptr = reinterpret_cast(data); for (uint i = 0 ; runningFlag() && (i < size) ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; ptr[0] = param.lut[blue]; ptr[1] = param.lut[green]; ptr[2] = param.lut[red]; - ptr += 4; + ptr += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } delete [] param.lut; } FilterAction NormalizeFilter::filterAction() { return DefaultFilterAction(); } void NormalizeFilter::readParameters(const FilterAction& /*action*/) { return; } } // namespace Digikam diff --git a/core/libs/dimg/filters/auto/normalizefilter.h b/core/libs/dimg/filters/auto/normalizefilter.h index d5ef494bc9..2cc22a756b 100644 --- a/core/libs/dimg/filters/auto/normalizefilter.h +++ b/core/libs/dimg/filters/auto/normalizefilter.h @@ -1,92 +1,92 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : normalize image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_NORMALIZE_FILTER_H #define DIGIKAM_NORMALIZE_FILTER_H // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "digikam_globals.h" namespace Digikam { class DImg; class DIGIKAM_EXPORT NormalizeFilter : public DImgThreadedFilter { public: explicit NormalizeFilter(QObject* const parent = nullptr); NormalizeFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent=nullptr); virtual ~NormalizeFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:NormalizeFilter"); } static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } static QString DisplayableName(); - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; + virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; void normalizeImage(); private: struct NormalizeParam { unsigned short* lut; double min; double max; }; DImg m_refImage; }; } // namespace Digikam #endif // DIGIKAM_NORMALIZE_FILTER_H diff --git a/core/libs/dimg/filters/auto/stretchfilter.cpp b/core/libs/dimg/filters/auto/stretchfilter.cpp index 4e19e265e4..de5a8ae15a 100644 --- a/core/libs/dimg/filters/auto/stretchfilter.cpp +++ b/core/libs/dimg/filters/auto/stretchfilter.cpp @@ -1,461 +1,464 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : stretch contrast image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "stretchfilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" #include "digikam_debug.h" #include "imagehistogram.h" namespace Digikam { StretchFilter::StretchFilter(QObject* const parent) : DImgThreadedFilter(parent) { initFilter(); } StretchFilter::StretchFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent) : DImgThreadedFilter(orgImage, parent, QLatin1String("StretchFilter")), m_refImage(*refImage) { initFilter(); } StretchFilter::~StretchFilter() { cancelFilter(); } QString StretchFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Stretch Contrast")); } void StretchFilter::filterImage() { if (m_refImage.isNull()) { m_refImage = m_orgImage; } stretchContrastImage(); m_destImage = m_orgImage; } /** * Performs histogram normalization of the image. The algorithm normalizes * the pixel values from an image for to span the full range - * of color values. This is a contrast enhancement technique. + * of color values. This is a contrast enhancement technique. */ void StretchFilter::stretchContrastImage() { if (m_orgImage.sixteenBit() != m_refImage.sixteenBit()) { qCDebug(DIGIKAM_DIMG_LOG) << "Ref. image and Org. has different bits depth"; return; } struct double_packet high, low, intensity; long long number_pixels; long i; int progress; unsigned long threshold_intensity; // Create an histogram of the reference image. + QScopedPointer histogram(new ImageHistogram(m_refImage)); if (histogram.isNull()) { qCWarning(DIGIKAM_DIMG_LOG) << ("Unable to allocate memory!"); return; } histogram->calculate(); // Memory allocation. + QScopedArrayPointer normalize_map(new int_packet[histogram->getHistogramSegments()]); if (normalize_map.isNull()) { qCWarning(DIGIKAM_DIMG_LOG) << ("Unable to allocate memory!"); return; } // Find the histogram boundaries by locating the 0.1 percent levels. number_pixels = (long long)m_refImage.width() * (long long)m_refImage.height(); threshold_intensity = number_pixels / 1000; // Red. intensity = double_packet(); for (high.red = histogram->getMaxSegmentIndex() ; high.red != 0 ; --high.red) { intensity.red += histogram->getValue(RedChannel, (int)high.red); if (intensity.red > threshold_intensity) { break; } } if (low.red == high.red) { threshold_intensity = 0; intensity = double_packet(); for (low.red = 0 ; low.red < histogram->getMaxSegmentIndex() ; ++low.red) { intensity.red += histogram->getValue(RedChannel, (int)low.red); if (intensity.red > threshold_intensity) { break; } } intensity = double_packet(); for (high.red = histogram->getMaxSegmentIndex() ; high.red != 0 ; --high.red) { intensity.red += histogram->getValue(RedChannel, (int)high.red); if (intensity.red > threshold_intensity) { break; } } } // Green. intensity = double_packet(); for (high.green = histogram->getMaxSegmentIndex() ; high.green != 0 ; --high.green) { intensity.green += histogram->getValue(GreenChannel, (int)high.green); if (intensity.green > threshold_intensity) { break; } } if (low.green == high.green) { threshold_intensity = 0; intensity = double_packet(); for (low.green = 0 ; low.green < histogram->getMaxSegmentIndex() ; ++low.green) { intensity.green += histogram->getValue(GreenChannel, (int)low.green); if (intensity.green > threshold_intensity) { break; } } intensity = double_packet(); for (high.green = histogram->getMaxSegmentIndex() ; high.green != 0 ; --high.green) { intensity.green += histogram->getValue(GreenChannel, (int)high.green); if (intensity.green > threshold_intensity) { break; } } } // Blue. intensity = double_packet(); for (high.blue = histogram->getMaxSegmentIndex() ; high.blue != 0 ; --high.blue) { intensity.blue += histogram->getValue(BlueChannel, (int)high.blue); if (intensity.blue > threshold_intensity) { break; } } if (low.blue == high.blue) { threshold_intensity = 0; intensity = double_packet(); for (low.blue = 0 ; low.blue < histogram->getMaxSegmentIndex() ; ++low.blue) { intensity.blue += histogram->getValue(BlueChannel, (int)low.blue); if (intensity.blue > threshold_intensity) { break; } } intensity = double_packet(); for (high.blue = histogram->getMaxSegmentIndex() ; high.blue != 0 ; --high.blue) { intensity.blue += histogram->getValue(BlueChannel, (int)high.blue); if (intensity.blue > threshold_intensity) { break; } } } // Alpha. intensity = double_packet(); for (high.alpha = histogram->getMaxSegmentIndex() ; high.alpha != 0 ; --high.alpha) { intensity.alpha += histogram->getValue(AlphaChannel, (int)high.alpha); if (intensity.alpha > threshold_intensity) { break; } } if (low.alpha == high.alpha) { threshold_intensity = 0; intensity = double_packet(); for (low.alpha = 0 ; low.alpha < histogram->getMaxSegmentIndex() ; ++low.alpha) { intensity.alpha += histogram->getValue(AlphaChannel, (int)low.alpha); if (intensity.alpha > threshold_intensity) { break; } } intensity = double_packet(); for (high.alpha = histogram->getMaxSegmentIndex() ; high.alpha != 0 ; --high.alpha) { intensity.alpha += histogram->getValue(AlphaChannel, (int)high.alpha); if (intensity.alpha > threshold_intensity) { break; } } } // Stretch the histogram to create the normalized image mapping. - // TODO magic number 256 + for (i = 0 ; runningFlag() && (i <= (long)histogram->getMaxSegmentIndex()) ; ++i) { - if (i < (long) low.red) + if (i < (long) low.red) { normalize_map[i].red = 0; } else if (i > (long) high.red) { normalize_map[i].red = (256 * histogram->getHistogramSegments() - 1); } else if (low.red != high.red) { normalize_map[i].red = (int)(((256 * histogram->getHistogramSegments() - 1) * (i - low.red)) / (high.red - low.red)); } - if (i < (long) low.green) + if (i < (long) low.green) { normalize_map[i].green = 0; } else if (i > (long) high.green) { normalize_map[i].green = (256 * histogram->getHistogramSegments() - 1); } else if (low.green != high.green) { normalize_map[i].green = (int)(((256 * histogram->getHistogramSegments() - 1) * (i - low.green)) / (high.green - low.green)); } - if (i < (long) low.blue) + if (i < (long) low.blue) { normalize_map[i].blue = 0; } else if (i > (long) high.blue) { normalize_map[i].blue = (256 * histogram->getHistogramSegments() - 1); } else if (low.blue != high.blue) { normalize_map[i].blue = (int)(((256 * histogram->getHistogramSegments() - 1) * (i - low.blue)) / (high.blue - low.blue)); } - if (i < (long) low.alpha) + if (i < (long) low.alpha) { normalize_map[i].alpha = 0; } else if (i > (long) high.alpha) { normalize_map[i].alpha = (256 * histogram->getHistogramSegments() - 1); } else if (low.alpha != high.alpha) { normalize_map[i].alpha = (int)(((256 * histogram->getHistogramSegments() - 1) * (i - low.alpha)) / (high.alpha - low.alpha)); } } // Apply result to image. uchar* data = m_orgImage.bits(); int w = m_orgImage.width(); int h = m_orgImage.height(); bool sixteenBit = m_orgImage.sixteenBit(); int size = w * h; // TODO magic number 257 + if (!sixteenBit) // 8 bits image. { uchar red, green, blue, alpha; uchar* ptr = data; for (i = 0 ; runningFlag() && (i < size) ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; alpha = ptr[3]; if (low.red != high.red) { red = (normalize_map[red].red) / 257; } if (low.green != high.green) { green = (normalize_map[green].green) / 257; } if (low.blue != high.blue) { blue = (normalize_map[blue].blue) / 257; } if (low.alpha != high.alpha) { alpha = (normalize_map[alpha].alpha) / 257; } ptr[0] = blue; ptr[1] = green; ptr[2] = red; ptr[3] = alpha; - ptr += 4; + ptr += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } else // 16 bits image. { unsigned short red, green, blue, alpha; unsigned short* ptr = reinterpret_cast(data); for (i = 0 ; runningFlag() && (i < size) ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; alpha = ptr[3]; if (low.red != high.red) { red = (normalize_map[red].red) / 257; } if (low.green != high.green) { green = (normalize_map[green].green) / 257; } if (low.blue != high.blue) { blue = (normalize_map[blue].blue) / 257; } if (low.alpha != high.alpha) { alpha = (normalize_map[alpha].alpha) / 257; } ptr[0] = blue; ptr[1] = green; ptr[2] = red; ptr[3] = alpha; - ptr += 4; + ptr += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } } FilterAction StretchFilter::filterAction() { return DefaultFilterAction(); } void StretchFilter::readParameters(const FilterAction& /*action*/) { return; } } // namespace Digikam diff --git a/core/libs/dimg/filters/auto/stretchfilter.h b/core/libs/dimg/filters/auto/stretchfilter.h index 4f0a7b9614..2a1f599cab 100644 --- a/core/libs/dimg/filters/auto/stretchfilter.h +++ b/core/libs/dimg/filters/auto/stretchfilter.h @@ -1,117 +1,117 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : stretch contrast image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_STRETCH_FILTER_H #define DIGIKAM_STRETCH_FILTER_H // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "digikam_globals.h" namespace Digikam { class DImg; class DIGIKAM_EXPORT StretchFilter : public DImgThreadedFilter { public: explicit StretchFilter(QObject* const parent = nullptr); StretchFilter(DImg* const orgImage, const DImg* const refImage, QObject* const parent=nullptr); virtual ~StretchFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:StretchFilter"); } static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } static QString DisplayableName(); - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; + virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; void stretchContrastImage(); private: struct double_packet { double_packet() : red(0.0), green(0.0), blue(0.0), alpha(0.0) { } double red; double green; double blue; double alpha; }; struct int_packet { int_packet() : red(0), green(0), blue(0), alpha(0) { } unsigned int red; unsigned int green; unsigned int blue; unsigned int alpha; }; DImg m_refImage; }; } // namespace Digikam #endif // DIGIKAM_STRETCH_FILTER_H diff --git a/core/libs/dimg/filters/bcg/bcgcontainer.h b/core/libs/dimg/filters/bcg/bcgcontainer.h index ee864d5a87..b191d3ccaf 100644 --- a/core/libs/dimg/filters/bcg/bcgcontainer.h +++ b/core/libs/dimg/filters/bcg/bcgcontainer.h @@ -1,66 +1,66 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-12-15 * Description : a Brightness/Contrast/Gamma settings container. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_BCG_CONTAINER_H #define DIGIKAM_BCG_CONTAINER_H // Qt includes #include // Local includes #include "digikam_export.h" namespace Digikam { class FilterAction; class DIGIKAM_EXPORT BCGContainer { public: explicit BCGContainer(); bool isDefault() const; - bool operator==(const BCGContainer& other) const; + bool operator==(const BCGContainer& other) const; void writeToFilterAction(FilterAction& action, const QString& prefix = QString()) const; static BCGContainer fromFilterAction(const FilterAction& action, const QString& prefix = QString()); public: int channel; double brightness; double contrast; double gamma; }; } // namespace Digikam #endif // DIGIKAM_BCG_CONTAINER_H diff --git a/core/libs/dimg/filters/bcg/bcgfilter.cpp b/core/libs/dimg/filters/bcg/bcgfilter.cpp index 3cbbf5e970..e8c92ba920 100644 --- a/core/libs/dimg/filters/bcg/bcgfilter.cpp +++ b/core/libs/dimg/filters/bcg/bcgfilter.cpp @@ -1,316 +1,319 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-03-06 * Description : a Brightness/Contrast/Gamma image filter. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "bcgfilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" namespace Digikam { BCGContainer::BCGContainer() { channel = LuminosityChannel; brightness = 0.0; contrast = 0.0; gamma = 1.0; } bool BCGContainer::isDefault() const { return (*this == BCGContainer()); } bool BCGContainer::operator==(const BCGContainer& other) const { - return (channel == other.channel && - brightness == other.brightness && - contrast == other.contrast && - gamma == other.gamma); + return ( + (channel == other.channel) && + (brightness == other.brightness) && + (contrast == other.contrast) && + (gamma == other.gamma) + ); } void BCGContainer::writeToFilterAction(FilterAction& action, const QString& prefix) const { action.addParameter(prefix + QLatin1String("channel"), channel); action.addParameter(prefix + QLatin1String("brightness"), brightness); action.addParameter(prefix + QLatin1String("contrast"), contrast); action.addParameter(prefix + QLatin1String("gamma"), gamma); } BCGContainer BCGContainer::fromFilterAction(const FilterAction& action, const QString& prefix) { BCGContainer settings; settings.channel = action.parameter(prefix + QLatin1String("channel"), settings.channel); settings.brightness = action.parameter(prefix + QLatin1String("brightness"), settings.brightness); settings.contrast = action.parameter(prefix + QLatin1String("contrast"), settings.contrast); settings.gamma = action.parameter(prefix + QLatin1String("gamma"), settings.gamma); return settings; } // ----------------------------------------------------------------------------------------------- class Q_DECL_HIDDEN BCGFilter::Private { public: explicit Private() { memset(map, 0, sizeof(map)); memset(map16, 0, sizeof(map16)); } int map[256]; int map16[65536]; BCGContainer settings; }; BCGFilter::BCGFilter(QObject* const parent) : DImgThreadedFilter(parent, QLatin1String("BCGFilter")), d(new Private) { reset(); initFilter(); } BCGFilter::BCGFilter(DImg* const orgImage, QObject* const parent, const BCGContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("BCGFilter")), d(new Private) { d->settings = settings; reset(); initFilter(); } BCGFilter::BCGFilter(const BCGContainer& settings, DImgThreadedFilter* const master, const DImg& orgImage, const DImg& destImage, int progressBegin, int progressEnd) : DImgThreadedFilter(master, orgImage, destImage, progressBegin, progressEnd, QLatin1String("WBFilter")), d(new Private) { d->settings = settings; reset(); filterImage(); } BCGFilter::~BCGFilter() { cancelFilter(); delete d; } QString BCGFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Brightness / Contrast / Gamma Filter")); } FilterAction BCGFilter::filterAction() { DefaultFilterAction action; d->settings.writeToFilterAction(action); + return std::move(action); } void BCGFilter::readParameters(const FilterAction& action) { d->settings = BCGContainer::fromFilterAction(action); } void BCGFilter::filterImage() { setGamma(d->settings.gamma); setBrightness(d->settings.brightness); setContrast(d->settings.contrast); applyBCG(m_orgImage); m_destImage = m_orgImage; } void BCGFilter::setGamma(double val) { val = (val < 0.01) ? 0.01 : val; - for (int i = 0; i < 65536; ++i) + for (int i = 0 ; i < 65536 ; ++i) { d->map16[i] = lround(pow(((double)d->map16[i] / 65535.0), (1.0 / val)) * 65535.0); } - for (int i = 0; i < 256; ++i) + for (int i = 0 ; i < 256 ; ++i) { d->map[i] = lround(pow(((double)d->map[i] / 255.0), (1.0 / val)) * 255.0); } } void BCGFilter::setBrightness(double val) { int val1 = lround(val * 65535); - for (int i = 0; i < 65536; ++i) + for (int i = 0 ; i < 65536 ; ++i) { d->map16[i] = d->map16[i] + val1; } val1 = lround(val * 255); - for (int i = 0; i < 256; ++i) + for (int i = 0 ; i < 256 ; ++i) { d->map[i] = d->map[i] + val1; } } void BCGFilter::setContrast(double val) { - for (int i = 0; i < 65536; ++i) + for (int i = 0 ; i < 65536 ; ++i) { d->map16[i] = lround((d->map16[i] - 32767) * val) + 32767; } - for (int i = 0; i < 256; ++i) + for (int i = 0 ; i < 256 ; ++i) { d->map[i] = lround((d->map[i] - 127) * val) + 127; } } void BCGFilter::reset() { // initialize to linear mapping - for (int i = 0; i < 65536; ++i) + for (int i = 0 ; i < 65536 ; ++i) { d->map16[i] = i; } - for (int i = 0; i < 256; ++i) + for (int i = 0 ; i < 256 ; ++i) { d->map[i] = i; } } void BCGFilter::applyBCG(DImg& image) { if (image.isNull()) { return; } applyBCG(image.bits(), image.width(), image.height(), image.sixteenBit()); } void BCGFilter::applyBCG(uchar* const bits, uint width, uint height, bool sixteenBits) { if (!bits) { return; } uint size = width * height; int progress; if (!sixteenBits) // 8 bits image. { uchar* data = bits; - for (uint i = 0; runningFlag() && (i < size); ++i) + for (uint i = 0 ; runningFlag() && (i < size) ; ++i) { switch (d->settings.channel) { case BlueChannel: data[0] = CLAMP0255(d->map[data[0]]); break; case GreenChannel: data[1] = CLAMP0255(d->map[data[1]]); break; case RedChannel: data[2] = CLAMP0255(d->map[data[2]]); break; default: // all channels data[0] = CLAMP0255(d->map[data[0]]); data[1] = CLAMP0255(d->map[data[1]]); data[2] = CLAMP0255(d->map[data[2]]); break; } data += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } else // 16 bits image. { ushort* data = reinterpret_cast(bits); - for (uint i = 0; runningFlag() && (i < size); ++i) + for (uint i = 0 ; runningFlag() && (i < size) ; ++i) { switch (d->settings.channel) { case BlueChannel: data[0] = CLAMP065535(d->map16[data[0]]); break; case GreenChannel: data[1] = CLAMP065535(d->map16[data[1]]); break; case RedChannel: data[2] = CLAMP065535(d->map16[data[2]]); break; default: // all channels data[0] = CLAMP065535(d->map16[data[0]]); data[1] = CLAMP065535(d->map16[data[1]]); data[2] = CLAMP065535(d->map16[data[2]]); break; } data += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } } } // namespace Digikam diff --git a/core/libs/dimg/filters/bcg/bcgfilter.h b/core/libs/dimg/filters/bcg/bcgfilter.h index 54d38b7fd1..16d7c1ee00 100644 --- a/core/libs/dimg/filters/bcg/bcgfilter.h +++ b/core/libs/dimg/filters/bcg/bcgfilter.h @@ -1,97 +1,97 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-03-06 * Description : a Brightness/Contrast/Gamma image filter. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_BCG_FILTER_H #define DIGIKAM_BCG_FILTER_H // Local includes #include "bcgcontainer.h" #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "digikam_globals.h" namespace Digikam { class DImg; class DIGIKAM_EXPORT BCGFilter : public DImgThreadedFilter { public: explicit BCGFilter(QObject* const parent=nullptr); explicit BCGFilter(DImg* const orgImage, QObject* const parent=nullptr, const BCGContainer& settings=BCGContainer()); explicit BCGFilter(const BCGContainer& settings, DImgThreadedFilter* const master, const DImg& orgImage, const DImg& destImage, int progressBegin=0, int progressEnd=100); virtual ~BCGFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:BCGFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; + virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; void reset(); void setGamma(double val); void setBrightness(double val); void setContrast(double val); void applyBCG(DImg& image); void applyBCG(uchar* const bits, uint width, uint height, bool sixteenBits); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_BCG_FILTER_H diff --git a/core/libs/dimg/filters/bcg/bcgsettings.cpp b/core/libs/dimg/filters/bcg/bcgsettings.cpp index d5c6875ccf..22515c9224 100644 --- a/core/libs/dimg/filters/bcg/bcgsettings.cpp +++ b/core/libs/dimg/filters/bcg/bcgsettings.cpp @@ -1,196 +1,196 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-02-09 * Description : BCG settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "bcgsettings.h" // Qt includes #include #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "dnuminput.h" #include "digikam_debug.h" #include "dexpanderbox.h" namespace Digikam { class Q_DECL_HIDDEN BCGSettings::Private { public: explicit Private() : bInput(nullptr), cInput(nullptr), gInput(nullptr) { } static const QString configBrightnessAdjustmentEntry; static const QString configContrastAdjustmentEntry; static const QString configGammaAdjustmentEntry; DIntNumInput* bInput; DIntNumInput* cInput; DDoubleNumInput* gInput; }; const QString BCGSettings::Private::configBrightnessAdjustmentEntry(QLatin1String("BrightnessAdjustment")); const QString BCGSettings::Private::configContrastAdjustmentEntry(QLatin1String("ContrastAdjustment")); const QString BCGSettings::Private::configGammaAdjustmentEntry(QLatin1String("GammaAdjustment")); // -------------------------------------------------------- BCGSettings::BCGSettings(QWidget* const parent) : QWidget(parent), d(new Private) { const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QGridLayout* grid = new QGridLayout(parent); QLabel* const label2 = new QLabel(i18n("Brightness:")); - d->bInput = new DIntNumInput(); + d->bInput = new DIntNumInput(); d->bInput->setRange(-100, 100, 1); d->bInput->setDefaultValue(0); d->bInput->setWhatsThis(i18n("Set here the brightness adjustment of the image.")); QLabel* const label3 = new QLabel(i18n("Contrast:")); - d->cInput = new DIntNumInput(); + d->cInput = new DIntNumInput(); d->cInput->setRange(-100, 100, 1); d->cInput->setDefaultValue(0); d->cInput->setWhatsThis(i18n("Set here the contrast adjustment of the image.")); QLabel* const label4 = new QLabel(i18n("Gamma:")); - d->gInput = new DDoubleNumInput(); + d->gInput = new DDoubleNumInput(); d->gInput->setDecimals(2); d->gInput->setRange(0.1, 3.0, 0.01); d->gInput->setDefaultValue(1.0); d->gInput->setWhatsThis(i18n("Set here the gamma adjustment of the image.")); // ------------------------------------------------------------- grid->addWidget(label2, 0, 0, 1, 5); grid->addWidget(d->bInput, 1, 0, 1, 5); grid->addWidget(label3, 2, 0, 1, 5); grid->addWidget(d->cInput, 3, 0, 1, 5); grid->addWidget(label4, 4, 0, 1, 5); grid->addWidget(d->gInput, 5, 0, 1, 5); grid->setRowStretch(6, 10); grid->setContentsMargins(spacing, spacing, spacing, spacing); grid->setSpacing(spacing); // ------------------------------------------------------------- connect(d->bInput, SIGNAL(valueChanged(int)), this, SIGNAL(signalSettingsChanged())); connect(d->cInput, SIGNAL(valueChanged(int)), this, SIGNAL(signalSettingsChanged())); connect(d->gInput, SIGNAL(valueChanged(double)), this, SIGNAL(signalSettingsChanged())); } BCGSettings::~BCGSettings() { delete d; } BCGContainer BCGSettings::settings() const { BCGContainer prm; prm.brightness = (double)d->bInput->value() / 250.0; prm.contrast = (double)(d->cInput->value() / 100.0) + 1.00; prm.gamma = d->gInput->value(); return prm; } void BCGSettings::setSettings(const BCGContainer& settings) { blockSignals(true); d->bInput->setValue((int)(settings.brightness * 250.0)); d->cInput->setValue((int)((settings.contrast - 1.0) * 100.0)); d->gInput->setValue(settings.gamma); blockSignals(false); } void BCGSettings::resetToDefault() { blockSignals(true); d->bInput->slotReset(); d->cInput->slotReset(); d->gInput->slotReset(); blockSignals(false); } BCGContainer BCGSettings::defaultSettings() const { BCGContainer prm; prm.brightness = (double)(d->bInput->defaultValue() / 250.0); prm.contrast = (double)(d->cInput->defaultValue() / 100.0) + 1.00; prm.gamma = d->gInput->defaultValue(); return prm; } void BCGSettings::readSettings(KConfigGroup& group) { BCGContainer prm; BCGContainer defaultPrm = defaultSettings(); prm.brightness = group.readEntry(d->configBrightnessAdjustmentEntry, defaultPrm.brightness); prm.contrast = group.readEntry(d->configContrastAdjustmentEntry, defaultPrm.contrast); prm.gamma = group.readEntry(d->configGammaAdjustmentEntry, defaultPrm.gamma); setSettings(prm); } void BCGSettings::writeSettings(KConfigGroup& group) { BCGContainer prm = settings(); group.writeEntry(d->configBrightnessAdjustmentEntry, prm.brightness); group.writeEntry(d->configContrastAdjustmentEntry, prm.contrast); group.writeEntry(d->configGammaAdjustmentEntry, prm.gamma); } } // namespace Digikam diff --git a/core/libs/dimg/filters/bcg/bcgsettings.h b/core/libs/dimg/filters/bcg/bcgsettings.h index 6f8ae77d40..406870eea4 100644 --- a/core/libs/dimg/filters/bcg/bcgsettings.h +++ b/core/libs/dimg/filters/bcg/bcgsettings.h @@ -1,71 +1,71 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-02-09 * Description : BCG settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_BCG_SETTINGS_H #define DIGIKAM_BCG_SETTINGS_H // Local includes #include // Local includes #include "digikam_export.h" #include "bcgfilter.h" class KConfigGroup; namespace Digikam { class DIGIKAM_EXPORT BCGSettings : public QWidget { Q_OBJECT public: explicit BCGSettings(QWidget* const parent); ~BCGSettings(); - BCGContainer defaultSettings() const; + BCGContainer defaultSettings() const; void resetToDefault(); - BCGContainer settings() const; + BCGContainer settings() const; void setSettings(const BCGContainer& settings); void readSettings(KConfigGroup& group); void writeSettings(KConfigGroup& group); Q_SIGNALS: void signalSettingsChanged(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_BCG_SETTINGS_H diff --git a/core/libs/dimg/filters/bw/bwsepiafilter.cpp b/core/libs/dimg/filters/bw/bwsepiafilter.cpp index 7ba62053a9..ee4e7d98ba 100644 --- a/core/libs/dimg/filters/bw/bwsepiafilter.cpp +++ b/core/libs/dimg/filters/bw/bwsepiafilter.cpp @@ -1,544 +1,545 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-03-06 * Description : black and white image filter. * * Copyright (C) 2005-2020 by Gilles Caulier - * Copyright (C) 2010 by Martin Klapetek + * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "bwsepiafilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" #include "mixerfilter.h" #include "infraredfilter.h" namespace Digikam { class Q_DECL_HIDDEN BWSepiaFilter::Private { public: explicit Private() : redAttn(0.0), greenAttn(0.0), blueAttn(0.0), redMult(0.0), greenMult(0.0), blueMult(0.0), settings(false) { } // Color filter attenuation in percents. double redAttn; double greenAttn; double blueAttn; - // Channel mixer color multiplier. + /// Channel mixer color multiplier. double redMult; double greenMult; double blueMult; BWSepiaContainer settings; }; BWSepiaFilter::BWSepiaFilter(QObject* const parent) : DImgThreadedFilter(parent), d(new Private) { initFilter(); } BWSepiaFilter::BWSepiaFilter(DImg* const orgImage, QObject* const parent, const BWSepiaContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("BWSepiaFilter")), d(new Private) { d->settings = settings; initFilter(); } BWSepiaFilter::~BWSepiaFilter() { cancelFilter(); delete d; } QString BWSepiaFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Black & White / Sepia Filter")); } void BWSepiaFilter::filterImage() { if (d->settings.preview) { m_destImage = getThumbnailForEffect(m_orgImage); } else { m_destImage = m_orgImage; postProgress(10); // Apply black and white filter. blackAndWhiteConversion(m_destImage, d->settings.filterType); postProgress(20); // Apply black and white film type. blackAndWhiteConversion(m_destImage, d->settings.filmType); postProgress(30); // Apply color tone filter. blackAndWhiteConversion(m_destImage, d->settings.toneType); postProgress(40); // Calculate and apply the luminosity curve on image. CurvesFilter curves(&m_destImage, nullptr, d->settings.curvesPrm); postProgress(50); curves.startFilterDirectly(); postProgress(60); m_destImage.putImageData(curves.getTargetImage().bits()); postProgress(70); // Adjust contrast. BCGFilter bcg(&m_destImage, nullptr, d->settings.bcgPrm); postProgress(80); bcg.startFilterDirectly(); postProgress(90); m_destImage.putImageData(bcg.getTargetImage().bits()); postProgress(100); } } DImg BWSepiaFilter::getThumbnailForEffect(DImg& img) { postProgress(10); DImg thumb = img.copy(); postProgress(25); if (d->settings.previewType < BWSepiaContainer::BWGeneric) { // In Filter view, we will render a preview of the B&W filter with the generic B&W film. blackAndWhiteConversion(thumb, d->settings.previewType); postProgress(50); blackAndWhiteConversion(thumb, BWSepiaContainer::BWGeneric); postProgress(75); } else { // In Film and Tone view, we will render the preview without to use the B&W Filter. postProgress(50); blackAndWhiteConversion(thumb, d->settings.previewType); postProgress(75); } postProgress(90); return (thumb); } void BWSepiaFilter::blackAndWhiteConversion(DImg& img, int type) { TonalityContainer toneSettings; switch (type) { case BWSepiaContainer::BWNoFilter: { d->redAttn = 0.0; d->greenAttn = 0.0; d->blueAttn = 0.0; break; } case BWSepiaContainer::BWGreenFilter: { d->redAttn = -0.20 * d->settings.strength; d->greenAttn = +0.11 * d->settings.strength; d->blueAttn = +0.09 * d->settings.strength; break; } case BWSepiaContainer::BWOrangeFilter: { d->redAttn = +0.48 * d->settings.strength; d->greenAttn = -0.37 * d->settings.strength; d->blueAttn = -0.11 * d->settings.strength; break; } case BWSepiaContainer::BWRedFilter: { d->redAttn = +0.60 * d->settings.strength; d->greenAttn = -0.49 * d->settings.strength; d->blueAttn = -0.11 * d->settings.strength; break; } case BWSepiaContainer::BWYellowFilter: { d->redAttn = +0.30 * d->settings.strength; d->greenAttn = -0.31 * d->settings.strength; d->blueAttn = +0.01 * d->settings.strength; break; } case BWSepiaContainer::BWYellowGreenFilter: { d->redAttn = +0.25 * d->settings.strength; d->greenAttn = +0.65 * d->settings.strength; d->blueAttn = +0.15 * d->settings.strength; break; } case BWSepiaContainer::BWBlueFilter: { d->redAttn = +0.15 * d->settings.strength; d->greenAttn = +0.15 * d->settings.strength; d->blueAttn = +0.80 * d->settings.strength; break; } // -------------------------------------------------------------------------------- case BWSepiaContainer::BWGeneric: case BWSepiaContainer::BWNoTone: { d->redMult = +0.24; d->greenMult = +0.68; d->blueMult = +0.08; applyChannelMixer(img); break; } case BWSepiaContainer::BWAgfa200X: { d->redMult = +0.18; d->greenMult = +0.41; d->blueMult = +0.41; applyChannelMixer(img); break; } case BWSepiaContainer::BWAgfapan25: { d->redMult = +0.25; d->greenMult = +0.39; d->blueMult = +0.36; applyChannelMixer(img); break; } case BWSepiaContainer::BWAgfapan100: { d->redMult = +0.21; d->greenMult = +0.40; d->blueMult = +0.39; applyChannelMixer(img); break; } case BWSepiaContainer::BWAgfapan400: { d->redMult = +0.20; d->greenMult = +0.41; d->blueMult = +0.39; applyChannelMixer(img); break; } case BWSepiaContainer::BWIlfordDelta100: { d->redMult = +0.21; d->greenMult = +0.42; d->blueMult = +0.37; applyChannelMixer(img); break; } case BWSepiaContainer::BWIlfordDelta400: { d->redMult = +0.22; d->greenMult = +0.42; d->blueMult = +0.36; applyChannelMixer(img); break; } case BWSepiaContainer::BWIlfordDelta400Pro3200: { d->redMult = +0.31; d->greenMult = +0.36; d->blueMult = +0.33; applyChannelMixer(img); break; } case BWSepiaContainer::BWIlfordFP4: { d->redMult = +0.28; d->greenMult = +0.41; d->blueMult = +0.31; applyChannelMixer(img); break; } case BWSepiaContainer::BWIlfordHP5: { d->redMult = +0.23; d->greenMult = +0.37; d->blueMult = +0.40; applyChannelMixer(img); break; } case BWSepiaContainer::BWIlfordPanF: { d->redMult = +0.33; d->greenMult = +0.36; d->blueMult = +0.31; applyChannelMixer(img); break; } case BWSepiaContainer::BWIlfordXP2Super: { d->redMult = +0.21; d->greenMult = +0.42; d->blueMult = +0.37; applyChannelMixer(img); break; } case BWSepiaContainer::BWKodakTmax100: { d->redMult = +0.24; d->greenMult = +0.37; d->blueMult = +0.39; applyChannelMixer(img); break; } case BWSepiaContainer::BWKodakTmax400: { d->redMult = +0.27; d->greenMult = +0.36; d->blueMult = +0.37; applyChannelMixer(img); break; } case BWSepiaContainer::BWKodakTriX: { d->redMult = +0.25; d->greenMult = +0.35; d->blueMult = +0.40; applyChannelMixer(img); break; } // -------------------------------------------------------------------------------- case BWSepiaContainer::BWIlfordSFX200: { d->redMult = +0.4; d->greenMult = +2.1; d->blueMult = -0.8; applyInfraredFilter(img, 200); break; } case BWSepiaContainer::BWIlfordSFX400: { d->redMult = +0.4; d->greenMult = +2.1; d->blueMult = -0.8; applyInfraredFilter(img, 400); break; } case BWSepiaContainer::BWIlfordSFX800: { d->redMult = +0.4; d->greenMult = +2.1; d->blueMult = -0.8; applyInfraredFilter(img, 800); break; } case BWSepiaContainer::BWKodakHIE: { d->redMult = +1.0; d->greenMult = +1.0; d->blueMult = -1.0; applyInfraredFilter(img, 100); break; } // -------------------------------------------------------------------------------- case BWSepiaContainer::BWSepiaTone: { toneSettings.redMask = 162; toneSettings.greenMask = 132; toneSettings.blueMask = 101; applyToneFilter(img, toneSettings); break; } case BWSepiaContainer::BWBrownTone: { toneSettings.redMask = 129; toneSettings.greenMask = 115; toneSettings.blueMask = 104; applyToneFilter(img, toneSettings); break; } case BWSepiaContainer::BWColdTone: { toneSettings.redMask = 102; toneSettings.greenMask = 109; toneSettings.blueMask = 128; applyToneFilter(img, toneSettings); break; } case BWSepiaContainer::BWSeleniumTone: { toneSettings.redMask = 122; toneSettings.greenMask = 115; toneSettings.blueMask = 122; applyToneFilter(img, toneSettings); break; } case BWSepiaContainer::BWPlatinumTone: { toneSettings.redMask = 115; toneSettings.greenMask = 110; toneSettings.blueMask = 106; applyToneFilter(img, toneSettings); break; } case BWSepiaContainer::BWGreenTone: { toneSettings.redMask = 125; toneSettings.greenMask = 125; toneSettings.blueMask = 105; applyToneFilter(img, toneSettings); break; } } } void BWSepiaFilter::applyChannelMixer(DImg& img) { MixerContainer settings; settings.bMonochrome = true; settings.blackRedGain = d->redMult + d->redMult * d->redAttn; settings.blackGreenGain = d->greenMult + d->greenMult * d->greenAttn; settings.blackBlueGain = d->blueMult + d->blueMult * d->blueAttn; MixerFilter mixer(&img, nullptr, settings); mixer.startFilterDirectly(); img.putImageData(mixer.getTargetImage().bits()); } void BWSepiaFilter::applyInfraredFilter(DImg& img, int sensibility) { InfraredContainer settings; settings.sensibility = sensibility; settings.redGain = d->redMult + d->redMult * d->redAttn; settings.greenGain = d->greenMult + d->greenMult * d->greenAttn; settings.blueGain = d->blueMult + d->blueMult * d->blueAttn; InfraredFilter infra(&img, nullptr, settings); infra.startFilterDirectly(); img.putImageData(infra.getTargetImage().bits()); } void BWSepiaFilter::applyToneFilter(DImg& img, TonalityContainer& settings) { // Value to multiply RGB 8 bits component of mask used by TonalityFilter. + int mul = img.sixteenBit() ? 255 : 1; settings.redMask = settings.redMask * mul; settings.greenMask = settings.greenMask * mul; settings.blueMask = settings.blueMask * mul; TonalityFilter tone(&img, nullptr, settings); tone.startFilterDirectly(); img.putImageData(tone.getTargetImage().bits()); } FilterAction BWSepiaFilter::filterAction() { DefaultFilterAction action(d->settings.curvesPrm.isStoredLosslessly()); action.setDisplayableName(DisplayableName()); action.addParameter(QLatin1String("filmType"), d->settings.filmType); action.addParameter(QLatin1String("filterType"), d->settings.filterType); action.addParameter(QLatin1String("preview"), d->settings.preview); action.addParameter(QLatin1String("previewType"), d->settings.previewType); action.addParameter(QLatin1String("strength"), d->settings.strength); action.addParameter(QLatin1String("toneType"), d->settings.toneType); // Version 2: BWKodakHIE added + action.supportOlderVersionIf(1, d->settings.filmType < BWSepiaContainer::BWKodakHIE); d->settings.curvesPrm.writeToFilterAction(action); d->settings.bcgPrm.writeToFilterAction(action); return std::move(action); } void BWSepiaFilter::readParameters(const FilterAction& action) { d->settings.filmType = action.parameter(QLatin1String("filmType")).toInt(); d->settings.filterType = action.parameter(QLatin1String("filterType")).toInt(); d->settings.preview = action.parameter(QLatin1String("preview")).toBool(); d->settings.previewType = action.parameter(QLatin1String("previewType")).toInt(); d->settings.strength = action.parameter(QLatin1String("strength")).toDouble(); d->settings.toneType = action.parameter(QLatin1String("toneType")).toInt(); d->settings.curvesPrm = CurvesContainer::fromFilterAction(action); d->settings.bcgPrm = BCGContainer::fromFilterAction(action); } - } // namespace Digikam diff --git a/core/libs/dimg/filters/bw/bwsepiafilter.h b/core/libs/dimg/filters/bw/bwsepiafilter.h index 477c9757a7..5640a28c2e 100644 --- a/core/libs/dimg/filters/bw/bwsepiafilter.h +++ b/core/libs/dimg/filters/bw/bwsepiafilter.h @@ -1,199 +1,203 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-03-06 * Description : black and white image filter. * * Copyright (C) 2005-2020 by Gilles Caulier - * Copyright (C) 2010 by Martin Klapetek + * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_BW_SEPIA_FILTER_H #define DIGIKAM_BW_SEPIA_FILTER_H // Qt includes #include // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "digikam_globals.h" #include "bcgfilter.h" #include "curvesfilter.h" #include "tonalityfilter.h" namespace Digikam { class DImg; class DIGIKAM_EXPORT BWSepiaContainer { public: enum BlackWhiteConversionType { - BWNoFilter = 0, // B&W filter to the front of lens. + BWNoFilter = 0, ///< B&W filter to the front of lens. BWGreenFilter, BWOrangeFilter, BWRedFilter, BWYellowFilter, BWYellowGreenFilter, BWBlueFilter, - BWGeneric, // B&W film simulation. + BWGeneric, ///< B&W film simulation. BWAgfa200X, BWAgfapan25, BWAgfapan100, BWAgfapan400, BWIlfordDelta100, BWIlfordDelta400, BWIlfordDelta400Pro3200, BWIlfordFP4, BWIlfordHP5, BWIlfordPanF, BWIlfordXP2Super, BWKodakTmax100, BWKodakTmax400, BWKodakTriX, - BWIlfordSFX200, // Infrared film simulation. + BWIlfordSFX200, ///< Infrared film simulation. BWIlfordSFX400, BWIlfordSFX800, - BWNoTone, // Chemical color tone filter. + BWNoTone, ///< Chemical color tone filter. BWSepiaTone, BWBrownTone, BWColdTone, BWSeleniumTone, BWPlatinumTone, BWGreenTone, // Filter version 2 - BWKodakHIE // Infrared film simulation. + BWKodakHIE ///< Infrared film simulation. }; public: BWSepiaContainer() + : preview(false), + previewType(BWGeneric), + filmType(BWGeneric), + filterType(BWNoFilter), + toneType(BWNoTone), + strength(1.0) { - previewType = BWGeneric; - preview = false; - filmType = BWGeneric; - filterType = BWNoFilter; - toneType = BWNoTone; - strength = 1.0; }; explicit BWSepiaContainer(int ptype) + : preview(true), + previewType(ptype), + filmType(BWGeneric), + filterType(BWNoFilter), + toneType(BWNoTone), + strength(1.0) { - previewType = ptype; - preview = true; - strength = 1.0; - filmType = BWGeneric; - filterType = BWNoFilter; - toneType = BWNoTone; }; BWSepiaContainer(int ptype, const CurvesContainer& container) + : preview(true), + previewType(ptype), + filmType(BWGeneric), + filterType(BWNoFilter), + toneType(BWNoTone), + strength(1.0), + curvesPrm(container) { - previewType = ptype; - preview = true; - strength = 1.0; - filmType = BWGeneric; - filterType = BWNoFilter; - toneType = BWNoTone; - curvesPrm = container; }; - ~BWSepiaContainer() {}; + ~BWSepiaContainer() + { + }; public: bool preview; int previewType; int filmType; int filterType; int toneType; double strength; CurvesContainer curvesPrm; BCGContainer bcgPrm; }; // ----------------------------------------------------------------------------------------------- class DIGIKAM_EXPORT BWSepiaFilter : public DImgThreadedFilter { public: explicit BWSepiaFilter(QObject* const parent = nullptr); - explicit BWSepiaFilter(DImg* orgImage, QObject* const parent=nullptr, const BWSepiaContainer& settings=BWSepiaContainer()); + explicit BWSepiaFilter(DImg* orgImage, + QObject* const parent=nullptr, + const BWSepiaContainer& settings=BWSepiaContainer()); virtual ~BWSepiaFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:BWSepiaFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1 << 2; } static int CurrentVersion() { return 2; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + virtual FilterAction filterAction() override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; DImg getThumbnailForEffect(DImg& img); void blackAndWhiteConversion(DImg& img, int type); void applyChannelMixer(DImg& img); void applyInfraredFilter(DImg& img, int sensibility); void applyToneFilter(DImg& img, TonalityContainer& settings); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_BW_SEPIA_FILTER_H diff --git a/core/libs/dimg/filters/bw/bwsepiasettings.cpp b/core/libs/dimg/filters/bw/bwsepiasettings.cpp index b1ef4b465c..dc16008080 100644 --- a/core/libs/dimg/filters/bw/bwsepiasettings.cpp +++ b/core/libs/dimg/filters/bw/bwsepiasettings.cpp @@ -1,655 +1,658 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-02-23 * Description : black and white settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "bwsepiasettings.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "dlayoutbox.h" #include "dexpanderbox.h" #include "dfiledialog.h" #include "dnuminput.h" #include "digikam_debug.h" #include "previewlist.h" #include "curvesbox.h" #include "curveswidget.h" #include "imagecurves.h" namespace Digikam { class Q_DECL_HIDDEN BWSepiaSettings::Private { public: enum SettingsTab { FilmTab = 0, BWFiltersTab, ToneTab, LuminosityTab }; public: explicit Private() : bwFilters(nullptr), bwFilm(nullptr), bwTone(nullptr), tab(nullptr), cInput(nullptr), strengthInput(nullptr), curvesBox(nullptr) { } static const QString configSettingsTabEntry; static const QString configBWFilterEntry; static const QString configBWFilmEntry; static const QString configBWToneEntry; static const QString configContrastAdjustmentEntry; static const QString configStrengthAdjustmentEntry; static const QString configCurveEntry; PreviewList* bwFilters; PreviewList* bwFilm; PreviewList* bwTone; DExpanderBoxExclusive* tab; DIntNumInput* cInput; DIntNumInput* strengthInput; CurvesBox* curvesBox; DImg thumbImage; public: PreviewListItem* addItem(PreviewList* const list, const QString& name, BWSepiaContainer::BlackWhiteConversionType type); }; const QString BWSepiaSettings::Private::configSettingsTabEntry(QLatin1String("Settings Tab")); const QString BWSepiaSettings::Private::configBWFilterEntry(QLatin1String("BW Filter")); const QString BWSepiaSettings::Private::configBWFilmEntry(QLatin1String("BW Film")); const QString BWSepiaSettings::Private::configBWToneEntry(QLatin1String("BW Tone")); const QString BWSepiaSettings::Private::configContrastAdjustmentEntry(QLatin1String("ContrastValueAdjustment")); const QString BWSepiaSettings::Private::configStrengthAdjustmentEntry(QLatin1String("StrengthAdjustment")); const QString BWSepiaSettings::Private::configCurveEntry(QLatin1String("BWSepiaCurve")); PreviewListItem* BWSepiaSettings::Private::addItem(PreviewList* const list, const QString& name, BWSepiaContainer::BlackWhiteConversionType type) { BWSepiaFilter* const filter = new BWSepiaFilter(&thumbImage, nullptr, BWSepiaContainer(type)); PreviewListItem* const item = list->addItem(filter, name, type); return item; } // -------------------------------------------------------- BWSepiaSettings::BWSepiaSettings(QWidget* const parent, DImg* const img) : QWidget(parent), d(new Private) { if (!img->isNull()) { d->thumbImage = img->smoothScale(128, 128, Qt::KeepAspectRatio); } else { QString backGround = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("digikam/about/images/body-background.jpg")); d->thumbImage = DImg(backGround).smoothScale(128, 128, Qt::KeepAspectRatio); } const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QGridLayout* const grid = new QGridLayout(parent); d->tab = new DExpanderBoxExclusive(this); PreviewListItem* item; d->bwFilm = new PreviewList; item = d->addItem(d->bwFilm, i18nc("generic black and white film", "Generic"), BWSepiaContainer::BWGeneric); item->setWhatsThis(i18n("Generic:" "

Simulate a generic black and white film.

")); item = d->addItem(d->bwFilm, i18n("Agfa 200X"), BWSepiaContainer::BWAgfa200X); item->setWhatsThis(i18n("Agfa 200X:" "

Simulate the Agfa 200X black and white film at 200 ISO.

")); item = d->addItem(d->bwFilm, i18n("Agfa Pan 25"), BWSepiaContainer::BWAgfapan25); item->setWhatsThis(i18n("Agfa Pan 25:" "

Simulate the Agfa Pan black and white film at 25 ISO.

")); item = d->addItem(d->bwFilm, i18n("Agfa Pan 100"), BWSepiaContainer::BWAgfapan100); item->setWhatsThis(i18n("Agfa Pan 100:" "

Simulate the Agfa Pan black and white film at 100 ISO.

")); item = d->addItem(d->bwFilm, i18n("Agfa Pan 400"), BWSepiaContainer::BWAgfapan400); item->setWhatsThis(i18n("Agfa Pan 400:" "

Simulate the Agfa Pan black and white film at 400 ISO.

")); item = d->addItem(d->bwFilm, i18n("Ilford Delta 100"), BWSepiaContainer::BWIlfordDelta100); item->setWhatsThis(i18n("Ilford Delta 100:" "

Simulate the Ilford Delta black and white film at 100 ISO.

")); item = d->addItem(d->bwFilm, i18n("Ilford Delta 400"), BWSepiaContainer::BWIlfordDelta400); item->setWhatsThis(i18n("Ilford Delta 400:" "

Simulate the Ilford Delta black and white film at 400 ISO.

")); item = d->addItem(d->bwFilm, i18n("Ilford Delta 400 Pro 3200"), BWSepiaContainer::BWIlfordDelta400Pro3200); item->setWhatsThis(i18n("Ilford Delta 400 Pro 3200:" "

Simulate the Ilford Delta 400 Pro black and white film at 3200 ISO.

")); item = d->addItem(d->bwFilm, i18n("Ilford FP4 Plus"), BWSepiaContainer::BWIlfordFP4); item->setWhatsThis(i18n("Ilford FP4 Plus:" "

Simulate the Ilford FP4 Plus black and white film at 125 ISO.

")); item = d->addItem(d->bwFilm, i18n("Ilford HP5 Plus"), BWSepiaContainer::BWIlfordHP5); item->setWhatsThis(i18n("Ilford HP5 Plus:" "

Simulate the Ilford HP5 Plus black and white film at 400 ISO.

")); item = d->addItem(d->bwFilm, i18n("Ilford PanF Plus"), BWSepiaContainer::BWIlfordPanF); item->setWhatsThis(i18n("Ilford PanF Plus:" "

Simulate the Ilford PanF Plus black and white film at 50 ISO.

")); item = d->addItem(d->bwFilm, i18n("Ilford XP2 Super"), BWSepiaContainer::BWIlfordXP2Super); item->setWhatsThis(i18n("Ilford XP2 Super:" "

Simulate the Ilford XP2 Super black and white film at 400 ISO.

")); item = d->addItem(d->bwFilm, i18n("Kodak Tmax 100"), BWSepiaContainer::BWKodakTmax100); item->setWhatsThis(i18n("Kodak Tmax 100:" "

Simulate the Kodak Tmax black and white film at 100 ISO.

")); item = d->addItem(d->bwFilm, i18n("Kodak Tmax 400"), BWSepiaContainer::BWKodakTmax400); item->setWhatsThis(i18n("Kodak Tmax 400:" "

Simulate the Kodak Tmax black and white film at 400 ISO.

")); item = d->addItem(d->bwFilm, i18n("Kodak TriX"), BWSepiaContainer::BWKodakTriX); item->setWhatsThis(i18n("Kodak TriX:" "

Simulate the Kodak TriX black and white film at 400 ISO.

")); // ------------------------------------------------------------- item = d->addItem(d->bwFilm, i18n("Ilford SPX 200 (Infrared)"), BWSepiaContainer::BWIlfordSFX200); item->setWhatsThis(i18n("Ilford SPX 200:" "

Simulate the Ilford SPX infrared film at 200 ISO.

")); item = d->addItem(d->bwFilm, i18n("Ilford SPX 400 (Infrared)"), BWSepiaContainer::BWIlfordSFX400); item->setWhatsThis(i18n("Ilford SPX 400:" "

Simulate the Ilford SPX infrared film at 400 ISO.

")); item = d->addItem(d->bwFilm, i18n("Ilford SPX 800 (Infrared)"), BWSepiaContainer::BWIlfordSFX800); item->setWhatsThis(i18n("Ilford SPX 800:" "

Simulate the Ilford SPX infrared film at 800 ISO.

")); item = d->addItem(d->bwFilm, i18n("Kodak HIE (Infrared)"), BWSepiaContainer::BWKodakHIE); item->setWhatsThis(i18n("Kodak HIE:" "

Simulate the Kodak HIE infrared film.

")); // ------------------------------------------------------------- QWidget* vbox = new QWidget(); QVBoxLayout* vlay = new QVBoxLayout(vbox); d->bwFilters = new PreviewList(vbox); item = d->addItem(d->bwFilters, i18n("No Lens Filter"), BWSepiaContainer::BWNoFilter); item->setWhatsThis(i18n("No Lens Filter:" "

Do not apply a lens filter when rendering the image.

")); item = d->addItem(d->bwFilters, i18n("Green Filter"), BWSepiaContainer::BWGreenFilter); item->setWhatsThis(i18n("Black & White with Green Filter:" "

Simulate black and white film exposure using a green filter. " "This is useful for all scenic shoots, especially " "portraits photographed against the sky.

")); item = d->addItem(d->bwFilters, i18n("Orange Filter"), BWSepiaContainer::BWOrangeFilter); item->setWhatsThis(i18n("Black & White with Orange Filter:" "

Simulate black and white film exposure using an orange filter. " "This will enhance landscapes, marine scenes and aerial " "photography.

")); item = d->addItem(d->bwFilters, i18n("Red Filter"), BWSepiaContainer::BWRedFilter); item->setWhatsThis(i18n("Black & White with Red Filter:" "

Simulate black and white film exposure using a red filter. " "This creates dramatic sky effects, and simulates moonlight scenes " "in the daytime.

")); item = d->addItem(d->bwFilters, i18n("Yellow Filter"), BWSepiaContainer::BWYellowFilter); item->setWhatsThis(i18n("Black & White with Yellow Filter:" "

Simulate black and white film exposure using a yellow filter. " "This has the most natural tonal correction, and improves contrast. Ideal for " "landscapes.

")); item = d->addItem(d->bwFilters, i18n("Yellow-Green Filter"), BWSepiaContainer::BWYellowGreenFilter); item->setWhatsThis(i18n("Black & White with Yellow-Green Filter:" "

Simulate black and white film exposure using a yellow-green filter. " "A yellow-green filter is highly effective for outdoor portraits because " "red is rendered dark while green appears lighter. Great for correcting skin tones, " "bringing out facial expressions in close-ups and emphasizing the feeling of liveliness. " "This filter is highly effective for indoor portraits under tungsten lighting.

")); item = d->addItem(d->bwFilters, i18n("Blue Filter"), BWSepiaContainer::BWBlueFilter); item->setWhatsThis(i18n("Black & White with Blue Filter:" "

Simulate black and white film exposure using a blue filter. " "This accentuates haze and fog. Used for dye transfer and contrast effects.

")); DHBox* const hbox1 = new DHBox(vbox); QLabel* const label1 = new QLabel(i18n("Strength:"), hbox1); label1->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); d->strengthInput = new DIntNumInput(hbox1); d->strengthInput->setRange(1, 5, 1); d->strengthInput->setDefaultValue(1); d->strengthInput->setWhatsThis(i18n("Here, set the strength adjustment of the lens filter.")); vlay->addWidget(d->bwFilters); vlay->addWidget(hbox1); vlay->setContentsMargins(QMargins()); vlay->setSpacing(spacing); // ------------------------------------------------------------- d->bwTone = new PreviewList; item = d->addItem(d->bwTone, i18n("No Tone Filter"), BWSepiaContainer::BWNoTone); item->setWhatsThis(i18n("No Tone Filter:" "

Do not apply a tone filter to the image.

")); item = d->addItem(d->bwTone, i18n("Sepia Filter"), BWSepiaContainer::BWSepiaTone); item->setWhatsThis(i18n("Black & White with Sepia Tone:" "

Gives a warm highlight and mid-tone while adding a bit of coolness to " "the shadows - very similar to the process of bleaching a print and " "re-developing in a sepia toner.

")); item = d->addItem(d->bwTone, i18n("Brown Filter"), BWSepiaContainer::BWBrownTone); item->setWhatsThis(i18n("Black & White with Brown Tone:" "

This filter is more neutral than the Sepia Tone " "filter.

")); item = d->addItem(d->bwTone, i18n("Cold Filter"), BWSepiaContainer::BWColdTone); item->setWhatsThis(i18n("Black & White with Cold Tone:" "

Start subtly and replicate printing on a cold tone black and white " "paper such as a bromide enlarging " "paper.

")); item = d->addItem(d->bwTone, i18n("Selenium Filter"), BWSepiaContainer::BWSeleniumTone); item->setWhatsThis(i18n("Black & White with Selenium Tone:" "

This effect replicates traditional selenium chemical toning done " "in the darkroom.

")); item = d->addItem(d->bwTone, i18n("Platinum Filter"), BWSepiaContainer::BWPlatinumTone); item->setWhatsThis(i18n("Black & White with Platinum Tone:" "

This effect replicates traditional platinum chemical toning done " "in the darkroom.

")); item = d->addItem(d->bwTone, i18n("Green Filter"), BWSepiaContainer::BWGreenTone); item->setWhatsThis(i18n("Black & White with greenish tint:" "

This effect is also known as Verdante.

")); // ------------------------------------------------------------- QWidget* lumBox = new QWidget(); // NOTE: add a method to be able to use curves widget without image data as simple curve editor. + if (!img->isNull()) { d->curvesBox = new CurvesBox(256, 192, *img, lumBox); } else { d->curvesBox = new CurvesBox(256, 192, DImg(1, 1, true, false, (uchar*)"\x00\x00\x00\x00\x00\x00\x00\x00"), lumBox); } d->curvesBox->enableCurveTypes(true); d->curvesBox->enableResetButton(true); d->curvesBox->setWhatsThis(i18n("This is the curve adjustment of the image luminosity")); // ------------------------------------------------------------- DHBox* const hbox2 = new DHBox(lumBox); QLabel* const label2 = new QLabel(i18n("Contrast:"), hbox2); label2->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); d->cInput = new DIntNumInput(hbox2); d->cInput->setRange(-100, 100, 1); d->cInput->setDefaultValue(0); d->cInput->setWhatsThis(i18n("Set here the contrast adjustment of the image.")); QGridLayout* gridTab2 = new QGridLayout(lumBox); gridTab2->addWidget(d->curvesBox, 0, 0, 1, 1); gridTab2->addWidget(hbox2, 1, 0, 1, 1); gridTab2->setRowStretch(2, 10); gridTab2->setContentsMargins(spacing, spacing, spacing, spacing); gridTab2->setSpacing(0); // ------------------------------------------------------------- // Some new icons may be needed : a film roll, a lens filter and ? + d->tab->addItem(d->bwFilm, QIcon::fromTheme(QLatin1String("filmgrain")), i18n("Film"), QLatin1String("Film"), true); d->tab->addItem(vbox, QIcon::fromTheme(QLatin1String("lensautofix")), i18n("Lens Filters"), QLatin1String("Lens Filters"), false); d->tab->addItem(d->bwTone, QIcon::fromTheme(QLatin1String("fill-color")), i18n("Tone"), QLatin1String("Tone"), false); d->tab->addItem(lumBox, QIcon::fromTheme(QLatin1String("adjustcurves")), i18n("Luminosity"), QLatin1String("Luminosity"), false); d->tab->addStretch(); grid->addWidget(d->tab, 0, 0, 1, 10); grid->setRowStretch(0, 10); grid->setContentsMargins(spacing, spacing, spacing, spacing); grid->setSpacing(spacing); // ------------------------------------------------------------- connect(d->bwFilters, SIGNAL(itemSelectionChanged()), this, SLOT(slotFilterSelected())); connect(d->strengthInput, SIGNAL(valueChanged(int)), this, SIGNAL(signalSettingsChanged())); connect(d->bwFilm, SIGNAL(itemSelectionChanged()), this, SIGNAL(signalSettingsChanged())); connect(d->bwTone, SIGNAL(itemSelectionChanged()), this, SIGNAL(signalSettingsChanged())); connect(d->cInput, SIGNAL(valueChanged(int)), this, SIGNAL(signalSettingsChanged())); connect(d->curvesBox, SIGNAL(signalCurvesChanged()), this, SIGNAL(signalSettingsChanged())); connect(d->curvesBox, SIGNAL(signalChannelReset(int)), this, SIGNAL(signalSettingsChanged())); } BWSepiaSettings::~BWSepiaSettings() { delete d; } void BWSepiaSettings::startPreviewFilters() { d->bwFilters->startFilters(); d->bwFilm->startFilters(); d->bwTone->startFilters(); } void BWSepiaSettings::slotFilterSelected() { int filter = d->bwFilters->currentId(); if (filter == BWSepiaContainer::BWNoFilter) { d->strengthInput->setEnabled(false); } else { d->strengthInput->setEnabled(true); } emit signalSettingsChanged(); } BWSepiaContainer BWSepiaSettings::settings() const { BWSepiaContainer prm; prm.filmType = d->bwFilm->currentId(); prm.filterType = d->bwFilters->currentId(); prm.toneType = d->bwTone->currentId(); prm.bcgPrm.contrast = ((double)(d->cInput->value() / 100.0) + 1.00); prm.strength = 1.0 + ((double)d->strengthInput->value() - 1.0) * (1.0 / 3.0); prm.curvesPrm = d->curvesBox->curves()->getContainer(LuminosityChannel); return prm; } void BWSepiaSettings::setSettings(const BWSepiaContainer& settings) { blockSignals(true); d->bwFilm->setCurrentId(settings.filmType); d->bwFilters->setCurrentId(settings.filterType); d->bwTone->setCurrentId(settings.toneType); d->cInput->setValue((int)((settings.bcgPrm.contrast - 1.00) * 100.0)); d->strengthInput->setValue((int)(1.0 + (settings.strength - 1.0) * 3.0)); d->curvesBox->curves()->setContainer(settings.curvesPrm); d->curvesBox->update(); slotFilterSelected(); blockSignals(false); } void BWSepiaSettings::resetToDefault() { blockSignals(true); d->bwFilters->setCurrentId(BWSepiaContainer::BWNoFilter); d->bwFilm->setCurrentId(BWSepiaContainer::BWGeneric); d->bwTone->setCurrentId(BWSepiaContainer::BWNoTone); d->cInput->slotReset(); d->strengthInput->slotReset(); d->curvesBox->curves()->curvesChannelReset(LuminosityChannel); d->curvesBox->reset(); blockSignals(false); slotFilterSelected(); } BWSepiaContainer BWSepiaSettings::defaultSettings() const { BWSepiaContainer prm; prm.bcgPrm.contrast = ((double)(d->cInput->defaultValue() / 100.0) + 1.00); prm.strength = 1.0 + ((double)d->strengthInput->defaultValue() - 1.0) * (1.0 / 3.0); return prm; } void BWSepiaSettings::readSettings(KConfigGroup& group) { BWSepiaContainer prm; BWSepiaContainer defaultPrm = defaultSettings(); d->tab->readSettings(group); prm.filmType = group.readEntry(d->configBWFilmEntry, defaultPrm.filmType); prm.filterType = group.readEntry(d->configBWFilterEntry, defaultPrm.filterType); prm.toneType = group.readEntry(d->configBWToneEntry, defaultPrm.toneType); prm.bcgPrm.contrast = group.readEntry(d->configContrastAdjustmentEntry, defaultPrm.bcgPrm.contrast); prm.strength = group.readEntry(d->configStrengthAdjustmentEntry, defaultPrm.strength); d->curvesBox->readCurveSettings(group, d->configCurveEntry); prm.curvesPrm = d->curvesBox->curves()->getContainer(LuminosityChannel); setSettings(prm); } void BWSepiaSettings::writeSettings(KConfigGroup& group) { BWSepiaContainer prm = settings(); d->tab->writeSettings(group); group.writeEntry(d->configBWFilmEntry, prm.filmType); group.writeEntry(d->configBWFilterEntry, prm.filterType); group.writeEntry(d->configBWToneEntry, prm.toneType); group.writeEntry(d->configContrastAdjustmentEntry, prm.bcgPrm.contrast); group.writeEntry(d->configStrengthAdjustmentEntry, prm.strength); d->curvesBox->writeCurveSettings(group, d->configCurveEntry); } void BWSepiaSettings::loadSettings() { QUrl loadFile = DFileDialog::getOpenFileUrl(qApp->activeWindow(), i18n("Black & White Settings File to Load"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)), QLatin1String("*")); if (loadFile.isEmpty()) { return; } QFile file(loadFile.toLocalFile()); if (file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); if (stream.readLine() != QLatin1String("# Black & White Configuration File")) { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("\"%1\" is not a Black & White settings text file.", loadFile.fileName())); file.close(); return; } blockSignals(true); d->bwFilm->setCurrentId(stream.readLine().toInt()); d->bwFilters->setCurrentId(stream.readLine().toInt()); d->bwTone->setCurrentId(stream.readLine().toInt()); d->cInput->setValue(stream.readLine().toInt()); for (int i = 0 ; i < 5 ; ++i) { d->curvesBox->curves()->curvesChannelReset(i); } d->curvesBox->curves()->setCurveType(d->curvesBox->channel(), ImageCurves::CURVE_SMOOTH); d->curvesBox->reset(); // TODO cant we use the kconfig mechanisms provided by CurveWidget here? QPoint disable = ImageCurves::getDisabledValue(); QPoint p; for (int j = 0 ; j < ImageCurves::NUM_POINTS ; ++j) { p.setX(stream.readLine().toInt()); p.setY(stream.readLine().toInt()); - if (d->curvesBox->curves()->isSixteenBits() && p != disable) + if (d->curvesBox->curves()->isSixteenBits() && (p != disable)) { p.setX(p.x()*ImageCurves::MULTIPLIER_16BIT); p.setY(p.y()*ImageCurves::MULTIPLIER_16BIT); } d->curvesBox->curves()->setCurvePoint(LuminosityChannel, j, p); } d->curvesBox->curves()->curvesCalculateAllCurves(); blockSignals(false); } else { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("Cannot load settings from the Black & White text file.")); } file.close(); } void BWSepiaSettings::saveAsSettings() { QUrl saveFile = DFileDialog::getSaveFileUrl(qApp->activeWindow(), i18n("Black & White Settings File to Save"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)), QLatin1String("*")); if (saveFile.isEmpty()) { return; } QFile file(saveFile.toLocalFile()); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream << QLatin1String("# Black & White Configuration File\n"); stream << d->bwFilm->currentId() << "\n"; stream << d->bwFilters->currentId() << "\n"; stream << d->bwTone->currentId() << "\n"; stream << d->cInput->value() << "\n"; // TODO cant we use the kconfig mechanisms provided by CurveWidget here? + for (int j = 0 ; j < ImageCurves::NUM_POINTS ; ++j) { QPoint p = d->curvesBox->curves()->getCurvePoint(LuminosityChannel, j); if (d->curvesBox->curves()->isSixteenBits()) { p.setX(p.x() / ImageCurves::MULTIPLIER_16BIT); p.setY(p.y() / ImageCurves::MULTIPLIER_16BIT); } stream << p.x() << "\n"; stream << p.y() << "\n"; } } else { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("Cannot save settings to the Black & White text file.")); } file.close(); } void BWSepiaSettings::setScaleType(HistogramScale scale) { d->curvesBox->setScale(scale); } } // namespace Digikam diff --git a/core/libs/dimg/filters/bw/bwsepiasettings.h b/core/libs/dimg/filters/bw/bwsepiasettings.h index 7ff37c3374..efc8879880 100644 --- a/core/libs/dimg/filters/bw/bwsepiasettings.h +++ b/core/libs/dimg/filters/bw/bwsepiasettings.h @@ -1,83 +1,83 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-02-23 * Description : black and white settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_BW_SEPIA_SETTINGS_H #define DIGIKAM_BW_SEPIA_SETTINGS_H // Local includes #include // Local includes #include "digikam_export.h" #include "bwsepiafilter.h" #include "dimg.h" class KConfigGroup; namespace Digikam { class DIGIKAM_EXPORT BWSepiaSettings : public QWidget { Q_OBJECT public: explicit BWSepiaSettings(QWidget* const parent, DImg* const img); ~BWSepiaSettings(); BWSepiaContainer defaultSettings() const; void resetToDefault(); - BWSepiaContainer settings() const; + BWSepiaContainer settings() const; void setSettings(const BWSepiaContainer& settings); void readSettings(KConfigGroup& group); void writeSettings(KConfigGroup& group); void loadSettings(); void saveAsSettings(); void setScaleType(HistogramScale scale); void startPreviewFilters(); Q_SIGNALS: void signalSettingsChanged(); private Q_SLOTS: void slotFilterSelected(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_BW_SEPIA_SETTINGS_H diff --git a/core/libs/dimg/filters/bw/infraredfilter.cpp b/core/libs/dimg/filters/bw/infraredfilter.cpp index c4399bfe8c..57b50b8f24 100644 --- a/core/libs/dimg/filters/bw/infraredfilter.cpp +++ b/core/libs/dimg/filters/bw/infraredfilter.cpp @@ -1,240 +1,242 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-25 * Description : Infrared threaded image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2006-2010 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "infraredfilter.h" // C++ includes #include #include // Qt includes #include // KDE includes #include // Local includes #include "dimg.h" #include "blurfilter.h" #include "mixerfilter.h" #include "digikam_globals.h" namespace Digikam { InfraredFilter::InfraredFilter(QObject* const parent) : DImgThreadedFilter(parent) { initFilter(); } InfraredFilter::InfraredFilter(DImg* const orgImage, QObject* const parent, const InfraredContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("Infrared")), m_settings(settings) { initFilter(); } InfraredFilter::~InfraredFilter() { cancelFilter(); } QString InfraredFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Infrared Filter")); } -/** This method is based on the Simulate Infrared Film tutorial from GimpGuru.org web site - available at this url : http://www.gimpguru.org/Tutorials/SimulatedInfrared/ - - More info about IR film can be seen at this url : - - http://www.pauck.de/marco/photo/infrared/comparison_of_films/comparison_of_films.html -*/ - +/** + * This method is based on the Simulate Infrared Film tutorial from GimpGuru.org web site + * available at this url : http://www.gimpguru.org/Tutorials/SimulatedInfrared/ + * + * More info about IR film can be seen at this url : + * + * http://www.pauck.de/marco/photo/infrared/comparison_of_films/comparison_of_films.html + */ void InfraredFilter::filterImage() { m_destImage.putImageData(m_orgImage.bits()); int Width = m_destImage.width(); int Height = m_destImage.height(); int bytesDepth = m_destImage.bytesDepth(); bool sixteenBit = m_destImage.sixteenBit(); uchar* data = m_destImage.bits(); postProgress(10); if (!runningFlag()) { return; } // Infrared film variables depending on Sensibility. // We can reproduce famous Ilford SFX200 infrared film // http://www.ilford.com/html/us_english/prod_html/sfx200/sfx200.html // This film have a sensibility escursion from 200 to 800 ISO. // Over 800 ISO, we reproduce The Kodak HIE high speed infrared film. int blurRadius = (int)((m_settings.sensibility / 200.0) + 1.0); // Gaussian blur infrared highlight effect [2 to 5]. int offset, progress; uchar* pOverlayBits = nullptr; // Overlay to merge with original converted in gray scale. uchar* pOutBits = m_destImage.bits(); // Destination image with merged grain mask and original. DColor bwData, bwBlurData, maskData, overData, outData; postProgress(20); if (!runningFlag()) { return; } //------------------------------------------ // 1 - Create GrayScale green boosted image. //------------------------------------------ // Convert to gray scale with boosting Green channel. // Infrared film increase green color. DImg BWImage(Width, Height, sixteenBit, true, data); // Black and White conversion. MixerContainer settings; settings.bMonochrome = true; settings.blackRedGain = m_settings.redGain; settings.blackGreenGain = m_settings.greenGain - (m_settings.sensibility / 2000.0); // Infrared green color boost [1.7 to 2.0]. settings.blackBlueGain = m_settings.blueGain; MixerFilter mixer(&BWImage, nullptr, settings); mixer.startFilterDirectly(); BWImage.putImageData(mixer.getTargetImage().bits()); postProgress(30); if (!runningFlag()) { return; } // Apply a Gaussian blur to the black and white image. // This way simulate Infrared film dispersion for the highlights. DImg BWBlurImage(Width, Height, sixteenBit); BlurFilter(this, BWImage, BWBlurImage, 10, 20, blurRadius); // save a memcpy pOverlayBits = BWBlurImage.bits(); postProgress(40); if (!runningFlag()) { return; } //------------------------------------------ // 2 - Merge Grayscale image & overlay mask. //------------------------------------------ // Merge overlay and gray scale image using 'Overlay' Gimp method for increase the highlight. // The result is usually a brighter picture. // Overlay mode composite value computation is D = A * (B + (2 * B) * (255 - A)). outData.setSixteenBit(sixteenBit); - for (int x = 0 ; runningFlag() && x < Width ; ++x) + for (int x = 0 ; runningFlag() && (x < Width) ; ++x) { - for (int y = 0 ; runningFlag() && y < Height ; ++y) + for (int y = 0 ; runningFlag() && (y < Height) ; ++y) { offset = x * bytesDepth + (y * Width * bytesDepth); bwData.setColor(BWImage.bits() + offset, sixteenBit); overData.setColor(pOverlayBits + offset, sixteenBit); if (sixteenBit) { outData.setRed(intMult16(bwData.red(), bwData.red() + intMult16(2 * overData.red(), 65535 - bwData.red()))); outData.setGreen(intMult16(bwData.green(), bwData.green() + intMult16(2 * overData.green(), 65535 - bwData.green()))); outData.setBlue(intMult16(bwData.blue(), bwData.blue() + intMult16(2 * overData.blue(), 65535 - bwData.blue()))); } else { outData.setRed(intMult8(bwData.red(), bwData.red() + intMult8(2 * overData.red(), 255 - bwData.red()))); outData.setGreen(intMult8(bwData.green(), bwData.green() + intMult8(2 * overData.green(), 255 - bwData.green()))); outData.setBlue(intMult8(bwData.blue(), bwData.blue() + intMult8(2 * overData.blue(), 255 - bwData.blue()))); } outData.setAlpha(bwData.alpha()); outData.setPixel(pOutBits + offset); } // Update progress bar in dialog. progress = (int)(50.0 + ((double)x * 50.0) / Width); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } int InfraredFilter::intMult8(uint a, uint b) { uint t = a * b + 0x80; + return ((t >> 8) + t) >> 8; } int InfraredFilter::intMult16(uint a, uint b) { uint t = a * b + 0x8000; + return ((t >> 16) + t) >> 16; } FilterAction InfraredFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); action.addParameter(QLatin1String("blueGain"), m_settings.blueGain); action.addParameter(QLatin1String("greenGain"), m_settings.greenGain); action.addParameter(QLatin1String("redGain"), m_settings.redGain); action.addParameter(QLatin1String("sensibility"), m_settings.sensibility); return action; } void InfraredFilter::readParameters(const FilterAction& action) { m_settings.blueGain = action.parameter(QLatin1String("blueGain")).toDouble(); m_settings.greenGain = action.parameter(QLatin1String("greenGain")).toDouble(); m_settings.redGain = action.parameter(QLatin1String("redGain")).toDouble(); m_settings.sensibility = action.parameter(QLatin1String("sensibility")).toInt(); } } // namespace Digikam diff --git a/core/libs/dimg/filters/bw/infraredfilter.h b/core/libs/dimg/filters/bw/infraredfilter.h index 83d3abc12e..05e43c51c6 100644 --- a/core/libs/dimg/filters/bw/infraredfilter.h +++ b/core/libs/dimg/filters/bw/infraredfilter.h @@ -1,116 +1,116 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-25 * Description : Infrared threaded image filter. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2006-2010 by Marcel Wiesweg - * Copyright (C) 2010 by Martin Klapetek + * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_INFRARED_FILTER_H #define DIGIKAM_INFRARED_FILTER_H // Local includes #include "dimgthreadedfilter.h" #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT InfraredContainer { public: explicit InfraredContainer() + : sensibility(200), + redGain(0.4), + greenGain(2.1), + blueGain(-0.8) { - sensibility = 200; - redGain = 0.4; - greenGain = 2.1; - blueGain = -0.8; }; ~InfraredContainer() { }; public: - // Sensibility: 200..2600 ISO + /// Sensibility: 200..2600 ISO int sensibility; double redGain; double greenGain; double blueGain; }; // --------------------------------------------------------------------------- class DIGIKAM_EXPORT InfraredFilter : public DImgThreadedFilter { public: explicit InfraredFilter(QObject* const parent = nullptr); explicit InfraredFilter(DImg* const orgImage, QObject* const parent=nullptr, const InfraredContainer& settings=InfraredContainer()); ~InfraredFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:InfraredFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + virtual FilterAction filterAction() override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; inline int intMult8(uint a, uint b); inline int intMult16(uint a, uint b); private: InfraredContainer m_settings; }; } // namespace Digikam #endif // DIGIKAM_INFRARED_FILTER_H diff --git a/core/libs/dimg/filters/bw/mixerfilter.cpp b/core/libs/dimg/filters/bw/mixerfilter.cpp index f90d8ddda6..d5806442a4 100644 --- a/core/libs/dimg/filters/bw/mixerfilter.cpp +++ b/core/libs/dimg/filters/bw/mixerfilter.cpp @@ -1,240 +1,241 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : Chanels mixer filter * * Copyright (C) 2005-2020 by Gilles Caulier - * Copyright (C) 2010 by Martin Klapetek + * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "mixerfilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" #include "dcolor.h" namespace Digikam { MixerFilter::MixerFilter(QObject* const parent) : DImgThreadedFilter(parent) { initFilter(); } MixerFilter::MixerFilter(DImg* const orgImage, QObject* const parent, const MixerContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("MixerFilter")), m_settings(settings) { initFilter(); } MixerFilter::~MixerFilter() { cancelFilter(); } QString MixerFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Channel Mixer Tool")); } void MixerFilter::filterImage() { m_destImage.putImageData(m_orgImage.bits()); uchar* bits = m_destImage.bits(); uint width = m_destImage.width(); uint height = m_destImage.height(); bool sixteenBit = m_destImage.sixteenBit(); uint size = width * height; int progress; uint i; double rnorm = 1; // red channel normalizer use in RGB mode. double mnorm = 1; // monochrome normalizer used in Monochrome mode. if (m_settings.bMonochrome) { mnorm = CalculateNorm(m_settings.blackRedGain, m_settings.blackGreenGain, m_settings.blackBlueGain, m_settings.bPreserveLum); } else { rnorm = CalculateNorm(m_settings.redRedGain, m_settings.redGreenGain, m_settings.redBlueGain, m_settings.bPreserveLum); } double gnorm = CalculateNorm(m_settings.greenRedGain, m_settings.greenGreenGain, m_settings.greenBlueGain, m_settings.bPreserveLum); double bnorm = CalculateNorm(m_settings.blueRedGain, m_settings.blueGreenGain, m_settings.blueBlueGain, m_settings.bPreserveLum); if (!sixteenBit) // 8 bits image. { uchar nGray, red, green, blue; uchar* ptr = bits; for (i = 0 ; i < size ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; if (m_settings.bMonochrome) { nGray = MixPixel(m_settings.blackRedGain, m_settings.blackGreenGain, m_settings.blackBlueGain, (unsigned short)red, (unsigned short)green, (unsigned short)blue, sixteenBit, mnorm); ptr[0] = ptr[1] = ptr[2] = nGray; } else { ptr[0] = (uchar)MixPixel(m_settings.blueRedGain, m_settings.blueGreenGain, m_settings.blueBlueGain, (unsigned short)red, (unsigned short)green, (unsigned short)blue, sixteenBit, bnorm); ptr[1] = (uchar)MixPixel(m_settings.greenRedGain, m_settings.greenGreenGain, m_settings.greenBlueGain, (unsigned short)red, (unsigned short)green, (unsigned short)blue, sixteenBit, gnorm); ptr[2] = (uchar)MixPixel(m_settings.redRedGain, m_settings.redGreenGain, m_settings.redBlueGain, (unsigned short)red, (unsigned short)green, (unsigned short)blue, sixteenBit, rnorm); } ptr += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } else // 16 bits image. { unsigned short nGray, red, green, blue; unsigned short* ptr = reinterpret_cast(bits); for (i = 0 ; i < size ; ++i) { blue = ptr[0]; green = ptr[1]; red = ptr[2]; if (m_settings.bMonochrome) { nGray = MixPixel(m_settings.blackRedGain, m_settings.blackGreenGain, m_settings.blackBlueGain, red, green, blue, sixteenBit, mnorm); ptr[0] = ptr[1] = ptr[2] = nGray; } else { ptr[0] = MixPixel(m_settings.blueRedGain, m_settings.blueGreenGain, m_settings.blueBlueGain, red, green, blue, sixteenBit, bnorm); ptr[1] = MixPixel(m_settings.greenRedGain, m_settings.greenGreenGain, m_settings.greenBlueGain, red, green, blue, sixteenBit, gnorm); ptr[2] = MixPixel(m_settings.redRedGain, m_settings.redGreenGain, m_settings.redBlueGain, red, green, blue, sixteenBit, rnorm); } ptr += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } } double MixerFilter::CalculateNorm(double RedGain, double GreenGain, double BlueGain, bool bPreserveLum) { double lfSum = RedGain + GreenGain + BlueGain; if ((lfSum == 0.0) || (!bPreserveLum)) { return (1.0); } return (fabs(1.0 / lfSum)); } unsigned short MixerFilter::MixPixel(double RedGain, double GreenGain, double BlueGain, unsigned short R, unsigned short G, unsigned short B, bool sixteenBit, double Norm) { double lfMix = Norm * (RedGain * (double)R + GreenGain * (double)G + BlueGain * (double)B); + return ((unsigned short)CLAMP((int)lfMix, 0, sixteenBit ? 65535 : 255)); } FilterAction MixerFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); action.addParameter(QLatin1String("blackBlueGain"), m_settings.blackBlueGain); action.addParameter(QLatin1String("blackGreenGain"), m_settings.blackGreenGain); action.addParameter(QLatin1String("blackRedGain"), m_settings.blackRedGain); action.addParameter(QLatin1String("blueBlueGain"), m_settings.blueBlueGain); action.addParameter(QLatin1String("blueGreenGain"), m_settings.blueGreenGain); action.addParameter(QLatin1String("blueRedGain"), m_settings.blueRedGain); action.addParameter(QLatin1String("bMonochrome"), m_settings.bMonochrome); action.addParameter(QLatin1String("bPreserveLum"), m_settings.bPreserveLum); action.addParameter(QLatin1String("greenBlueGain"), m_settings.greenBlueGain); action.addParameter(QLatin1String("greenGreenGain"), m_settings.greenGreenGain); action.addParameter(QLatin1String("greenRedGain"), m_settings.greenRedGain); action.addParameter(QLatin1String("redBlueGain"), m_settings.redBlueGain); action.addParameter(QLatin1String("redGreenGain"), m_settings.redGreenGain); action.addParameter(QLatin1String("redRedGain"), m_settings.redRedGain); return action; } void MixerFilter::readParameters(const Digikam::FilterAction& action) { m_settings.blackBlueGain = action.parameter(QLatin1String("blackBlueGain")).toDouble(); m_settings.blackGreenGain = action.parameter(QLatin1String("blackGreenGain")).toDouble(); m_settings.blackRedGain = action.parameter(QLatin1String("blackRedGain")).toDouble(); m_settings.blueBlueGain = action.parameter(QLatin1String("blueBlueGain")).toDouble(); m_settings.blueGreenGain = action.parameter(QLatin1String("blueGreenGain")).toDouble(); m_settings.blueRedGain = action.parameter(QLatin1String("blueRedGain")).toDouble(); m_settings.bMonochrome = action.parameter(QLatin1String("bMonochrome")).toBool(); m_settings.bPreserveLum = action.parameter(QLatin1String("bPreserveLum")).toBool(); m_settings.greenBlueGain = action.parameter(QLatin1String("greenBlueGain")).toDouble(); m_settings.greenGreenGain = action.parameter(QLatin1String("greenGreenGain")).toDouble(); m_settings.greenRedGain = action.parameter(QLatin1String("greenRedGain")).toDouble(); m_settings.redBlueGain = action.parameter(QLatin1String("redBlueGain")).toDouble(); m_settings.redGreenGain = action.parameter(QLatin1String("redGreenGain")).toDouble(); m_settings.redRedGain = action.parameter(QLatin1String("redRedGain")).toDouble(); } } // namespace Digikam diff --git a/core/libs/dimg/filters/bw/mixerfilter.h b/core/libs/dimg/filters/bw/mixerfilter.h index 55b306229b..335bd182bf 100644 --- a/core/libs/dimg/filters/bw/mixerfilter.h +++ b/core/libs/dimg/filters/bw/mixerfilter.h @@ -1,143 +1,143 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : Chanels mixer filter * * Copyright (C) 2005-2020 by Gilles Caulier - * Copyright (C) 2010 by Martin Klapetek + * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_MIXER_FILTER_H #define DIGIKAM_MIXER_FILTER_H // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "digikam_globals.h" namespace Digikam { class DImg; class DIGIKAM_EXPORT MixerContainer { public: explicit MixerContainer() + : bPreserveLum(true), + bMonochrome(false), + redRedGain(1.0), + redGreenGain(0.0), + redBlueGain(0.0), + greenRedGain(0.0), + greenGreenGain(1.0), + greenBlueGain(0.0), + blueRedGain(0.0), + blueGreenGain(0.0), + blueBlueGain(1.0), + blackRedGain(1.0), + blackGreenGain(0.0), + blackBlueGain(0.0) { - bPreserveLum = true; - bMonochrome = false; - redRedGain = 1.0; - redGreenGain = 0.0; - redBlueGain = 0.0; - greenRedGain = 0.0; - greenGreenGain = 1.0; - greenBlueGain = 0.0; - blueRedGain = 0.0; - blueGreenGain = 0.0; - blueBlueGain = 1.0; - blackRedGain = 1.0; - blackGreenGain = 0.0; - blackBlueGain = 0.0; }; ~MixerContainer() { }; public: bool bPreserveLum; bool bMonochrome; // Standard settings. double redRedGain; double redGreenGain; double redBlueGain; double greenRedGain; double greenGreenGain; double greenBlueGain; double blueRedGain; double blueGreenGain; double blueBlueGain; // Monochrome settings. double blackRedGain; double blackGreenGain; double blackBlueGain; }; // ----------------------------------------------------------------------------------------------- class DIGIKAM_EXPORT MixerFilter : public DImgThreadedFilter { public: explicit MixerFilter(QObject* const parent = nullptr); explicit MixerFilter(DImg* const orgImage, QObject* const parent=nullptr, const MixerContainer& settings=MixerContainer()); virtual ~MixerFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:MixerFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + virtual FilterAction filterAction() override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; inline double CalculateNorm(double RedGain, double GreenGain, double BlueGain, bool bPreserveLum); inline unsigned short MixPixel(double RedGain, double GreenGain, double BlueGain, unsigned short R, unsigned short G, unsigned short B, bool sixteenBit, double Norm); private: MixerContainer m_settings; }; } // namespace Digikam #endif // DIGIKAM_MIXER_FILTER_H diff --git a/core/libs/dimg/filters/bw/mixersettings.cpp b/core/libs/dimg/filters/bw/mixersettings.cpp index f188c7a404..7d11a25d3d 100644 --- a/core/libs/dimg/filters/bw/mixersettings.cpp +++ b/core/libs/dimg/filters/bw/mixersettings.cpp @@ -1,708 +1,709 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-02-18 * Description : Channel mixer settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "mixersettings.h" // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "dnuminput.h" #include "dfiledialog.h" #include "dexpanderbox.h" #include "digikam_debug.h" namespace Digikam { class Q_DECL_HIDDEN MixerSettings::Private { public: - explicit Private() : - currentChannel(RedChannel), + explicit Private() + : currentChannel(RedChannel), monochromeTips(nullptr), totalPercents(nullptr), outChannelLabel(nullptr), resetButton(nullptr), preserveLuminosity(nullptr), monochrome(nullptr), outChannelCB(nullptr), redGain(nullptr), greenGain(nullptr), blueGain(nullptr) { } static const QString configMonochromeEntry; static const QString configPreserveLuminosityEntry; static const QString configRedRedGainEntry; static const QString configRedGreenGainEntry; static const QString configRedBlueGainEntry; static const QString configGreenRedGainEntry; static const QString configGreenGreenGainEntry; static const QString configGreenBlueGainEntry; static const QString configBlueRedGainEntry; static const QString configBlueGreenGainEntry; static const QString configBlueBlueGainEntry; static const QString configBlackRedGainEntry; static const QString configBlackGreenGainEntry; static const QString configBlackBlueGainEntry; int currentChannel; QLabel* monochromeTips; QLabel* totalPercents; QLabel* outChannelLabel; QPushButton* resetButton; QCheckBox* preserveLuminosity; QCheckBox* monochrome; QComboBox* outChannelCB; MixerContainer mixerSettings; DDoubleNumInput* redGain; DDoubleNumInput* greenGain; DDoubleNumInput* blueGain; }; const QString MixerSettings::Private::configMonochromeEntry(QLatin1String("Monochrome")); const QString MixerSettings::Private::configPreserveLuminosityEntry(QLatin1String("PreserveLuminosity")); const QString MixerSettings::Private::configRedRedGainEntry(QLatin1String("RedRedGain")); const QString MixerSettings::Private::configRedGreenGainEntry(QLatin1String("RedGreenGain")); const QString MixerSettings::Private::configRedBlueGainEntry(QLatin1String("RedBlueGain")); const QString MixerSettings::Private::configGreenRedGainEntry(QLatin1String("GreenRedGain")); const QString MixerSettings::Private::configGreenGreenGainEntry(QLatin1String("GreenGreenGain")); const QString MixerSettings::Private::configGreenBlueGainEntry(QLatin1String("GreenBlueGain")); const QString MixerSettings::Private::configBlueRedGainEntry(QLatin1String("BlueRedGain")); const QString MixerSettings::Private::configBlueGreenGainEntry(QLatin1String("BlueGreenGain")); const QString MixerSettings::Private::configBlueBlueGainEntry(QLatin1String("BlueBlueGain")); const QString MixerSettings::Private::configBlackRedGainEntry(QLatin1String("BlackRedGain")); const QString MixerSettings::Private::configBlackGreenGainEntry(QLatin1String("BlackGreenGain")); const QString MixerSettings::Private::configBlackBlueGainEntry(QLatin1String("BlackBlueGain")); // -------------------------------------------------------- MixerSettings::MixerSettings(QWidget* const parent) : QWidget(parent), d(new Private) { - const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); + const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); QGridLayout* const grid = new QGridLayout(this); d->outChannelLabel = new QLabel(i18n("Output Channel:")); d->outChannelCB = new QComboBox; d->outChannelCB->addItem(i18n("Red"), QVariant(RedChannel)); d->outChannelCB->addItem(i18n("Green"), QVariant(GreenChannel)); d->outChannelCB->addItem(i18n("Blue"), QVariant(BlueChannel)); QLabel* const redLabel = new QLabel(i18n("Red (%):")); d->redGain = new DDoubleNumInput; d->redGain->setDecimals(1); d->redGain->setRange(-200.0, 200.0, 1); d->redGain->setDefaultValue(0); d->redGain->setWhatsThis(i18n("Select the red color gain, as a percentage, " "for the current channel.")); QLabel* const greenLabel = new QLabel(i18n("Green (%):")); d->greenGain = new DDoubleNumInput; d->greenGain->setDecimals(1); d->greenGain->setRange(-200.0, 200.0, 1); d->greenGain->setDefaultValue(0); d->greenGain->setWhatsThis(i18n("Select the green color gain, as a percentage, " "for the current channel.")); QLabel* const blueLabel = new QLabel(i18n("Blue (%):")); d->blueGain = new DDoubleNumInput; d->blueGain->setDecimals(1); d->blueGain->setRange(-200.0, 200.0, 1); d->blueGain->setDefaultValue(0); d->blueGain->setWhatsThis(i18n("Select the blue color gain, as a percentage, " "for the current channel.")); // ------------------------------------------------------------- d->resetButton = new QPushButton(i18n("&Reset")); d->resetButton->setIcon(QIcon::fromTheme(QLatin1String("document-revert"))); d->resetButton->setWhatsThis(i18n("Reset color channels' gains settings from " "the currently selected channel.")); d->totalPercents = new QLabel(); d->totalPercents->setAlignment(Qt::AlignRight | Qt::AlignVCenter); // ------------------------------------------------------------- d->preserveLuminosity = new QCheckBox(i18n("Preserve luminosity")); d->preserveLuminosity->setWhatsThis(i18n("Enable this option is you want preserve " "the image luminosity.")); // ------------------------------------------------------------- d->monochrome = new QCheckBox(i18n("Monochrome")); d->monochromeTips = new QLabel(i18n("

Use Monochrome mode to convert color picture to Black and White:

" "

The red channel modifies the contrast of photograph.

" "

The green channel enhances or reduces the details level of photograph.

" "

The blue channel affects the noise of photograph.

" "

Note: in this mode, the histogram will display only luminosity values.

")); d->monochromeTips->setEnabled(false); d->monochromeTips->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); d->monochromeTips->setWordWrap(true); d->monochromeTips->setOpenExternalLinks(true); d->monochromeTips->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); d->monochromeTips->setLineWidth(1); // ------------------------------------------------------------- grid->addWidget(d->outChannelLabel, 0, 0, 1, 1); grid->addWidget(d->outChannelCB, 0, 3, 1, 2); grid->addWidget(redLabel, 1, 0, 1, 1); grid->addWidget(d->redGain, 1, 1, 1, 4); grid->addWidget(greenLabel, 2, 0, 1, 1); grid->addWidget(d->greenGain, 2, 1, 1, 4); grid->addWidget(blueLabel, 3, 0, 1, 1); grid->addWidget(d->blueGain, 3, 1, 1, 4); grid->addWidget(d->resetButton, 4, 0, 1, 2); grid->addWidget(d->totalPercents, 4, 3, 1, 1); grid->addWidget(d->preserveLuminosity, 5, 0, 1, 5); grid->addWidget(d->monochrome, 6, 0, 1, 5); grid->addWidget(d->monochromeTips, 7, 0, 1, 5); grid->setRowStretch(8, 10); grid->setColumnStretch(2, 10); grid->setContentsMargins(spacing, spacing, spacing, spacing); grid->setSpacing(spacing); // ------------------------------------------------------------- connect(d->redGain, SIGNAL(valueChanged(double)), this, SLOT(slotGainsChanged())); connect(d->greenGain, SIGNAL(valueChanged(double)), this, SLOT(slotGainsChanged())); connect(d->blueGain, SIGNAL(valueChanged(double)), this, SLOT(slotGainsChanged())); connect(d->resetButton, SIGNAL(clicked()), this, SLOT(slotResetCurrentChannel())); connect(d->monochrome, SIGNAL(toggled(bool)), this, SLOT(slotMonochromeActived(bool))); connect(d->preserveLuminosity, SIGNAL(toggled(bool)), this, SLOT(slotLuminosityChanged(bool))); connect(d->outChannelCB, SIGNAL(activated(int)), this, SLOT(slotOutChannelChanged())); } MixerSettings::~MixerSettings() { delete d; } void MixerSettings::setMonochromeTipsVisible(bool b) { - b ? d->monochromeTips->show() : d->monochromeTips->hide(); + b ? d->monochromeTips->show() + : d->monochromeTips->hide(); } void MixerSettings::slotOutChannelChanged() { int index = d->outChannelCB->currentIndex(); d->currentChannel = (ChannelType)(d->outChannelCB->itemData(index).toInt()); updateSettingsWidgets(); emit signalOutChannelChanged(); } int MixerSettings::currentChannel() const { return d->currentChannel; } void MixerSettings::slotResetCurrentChannel() { switch (d->currentChannel) { case GreenChannel: { d->mixerSettings.greenRedGain = 0.0; d->mixerSettings.greenGreenGain = 1.0; d->mixerSettings.greenBlueGain = 0.0; break; } case BlueChannel: { d->mixerSettings.blueRedGain = 0.0; d->mixerSettings.blueGreenGain = 0.0; d->mixerSettings.blueBlueGain = 1.0; break; } default: // Red or monochrome. { if (d->monochrome->isChecked()) { d->mixerSettings.blackRedGain = 1.0; d->mixerSettings.blackGreenGain = 0.0; d->mixerSettings.blackBlueGain = 0.0; } else { d->mixerSettings.redRedGain = 1.0; d->mixerSettings.redGreenGain = 0.0; d->mixerSettings.redBlueGain = 0.0; } break; } } updateSettingsWidgets(); emit signalSettingsChanged(); } void MixerSettings::slotGainsChanged() { switch (d->currentChannel) { case GreenChannel: { d->mixerSettings.greenRedGain = d->redGain->value() / 100.0; d->mixerSettings.greenGreenGain = d->greenGain->value() / 100.0; d->mixerSettings.greenBlueGain = d->blueGain->value() / 100.0; break; } case BlueChannel: { d->mixerSettings.blueRedGain = d->redGain->value() / 100.0; d->mixerSettings.blueGreenGain = d->greenGain->value() / 100.0; d->mixerSettings.blueBlueGain = d->blueGain->value() / 100.0; break; } default: // Red or monochrome. { if (d->monochrome->isChecked()) { d->mixerSettings.blackRedGain = d->redGain->value() / 100.0; d->mixerSettings.blackGreenGain = d->greenGain->value() / 100.0; d->mixerSettings.blackBlueGain = d->blueGain->value() / 100.0; } else { d->mixerSettings.redRedGain = d->redGain->value() / 100.0; d->mixerSettings.redGreenGain = d->greenGain->value() / 100.0; d->mixerSettings.redBlueGain = d->blueGain->value() / 100.0; } break; } } updateTotalPercents(); emit signalSettingsChanged(); } void MixerSettings::updateTotalPercents() { double total = d->redGain->value() + d->greenGain->value() + d->blueGain->value(); QString str; d->totalPercents->setText(i18n("Total: %1 (%)", str.sprintf("%3.1f", total))); } void MixerSettings::updateSettingsWidgets() { d->monochrome->blockSignals(true); d->preserveLuminosity->blockSignals(true); d->redGain->blockSignals(true); d->greenGain->blockSignals(true); d->blueGain->blockSignals(true); switch (d->currentChannel) { case GreenChannel: { d->redGain->setDefaultValue(0); d->greenGain->setDefaultValue(100); d->blueGain->setDefaultValue(0); d->redGain->setValue(d->mixerSettings.greenRedGain * 100.0); d->greenGain->setValue(d->mixerSettings.greenGreenGain * 100.0); d->blueGain->setValue(d->mixerSettings.greenBlueGain * 100.0); break; } case BlueChannel: { d->redGain->setDefaultValue(0); d->greenGain->setDefaultValue(0); d->blueGain->setDefaultValue(100); d->redGain->setValue(d->mixerSettings.blueRedGain * 100.0); d->greenGain->setValue(d->mixerSettings.blueGreenGain * 100.0); d->blueGain->setValue(d->mixerSettings.blueBlueGain * 100.0); break; } default: // Red or monochrome. { if (d->monochrome->isChecked()) { d->redGain->setDefaultValue(100); d->greenGain->setDefaultValue(0); d->blueGain->setDefaultValue(0); d->redGain->setValue(d->mixerSettings.blackRedGain * 100.0); d->greenGain->setValue(d->mixerSettings.blackGreenGain * 100.0); d->blueGain->setValue(d->mixerSettings.blackBlueGain * 100.0); } else { d->redGain->setDefaultValue(100); d->greenGain->setDefaultValue(0); d->blueGain->setDefaultValue(0); d->redGain->setValue(d->mixerSettings.redRedGain * 100.0); d->greenGain->setValue(d->mixerSettings.redGreenGain * 100.0); d->blueGain->setValue(d->mixerSettings.redBlueGain * 100.0); } break; } } d->monochrome->setChecked(d->mixerSettings.bMonochrome); d->preserveLuminosity->setChecked(d->mixerSettings.bPreserveLum); updateTotalPercents(); d->monochrome->blockSignals(false); d->preserveLuminosity->blockSignals(false); d->redGain->blockSignals(false); d->greenGain->blockSignals(false); d->blueGain->blockSignals(false); } void MixerSettings::slotMonochromeActived(bool mono) { d->mixerSettings.bMonochrome = mono; d->monochromeTips->setEnabled(mono); d->outChannelLabel->setEnabled(!mono); d->outChannelCB->setEnabled(!mono); int id = d->outChannelCB->findData(QVariant(RedChannel)); d->outChannelCB->setCurrentIndex(id); slotOutChannelChanged(); emit signalMonochromeActived(mono); emit signalSettingsChanged(); } void MixerSettings::slotLuminosityChanged(bool lum) { d->mixerSettings.bPreserveLum = lum; emit signalSettingsChanged(); } MixerContainer MixerSettings::settings() const { return d->mixerSettings; } void MixerSettings::setSettings(const MixerContainer& settings) { blockSignals(true); d->mixerSettings = settings; updateSettingsWidgets(); slotMonochromeActived(d->mixerSettings.bMonochrome); blockSignals(false); } void MixerSettings::resetToDefault() { setSettings(defaultSettings()); } MixerContainer MixerSettings::defaultSettings() const { MixerContainer prm; prm.bMonochrome = false; prm.bPreserveLum = true; prm.redRedGain = 1.0; prm.redGreenGain = 0.0; prm.redBlueGain = 0.0; prm.greenRedGain = 0.0; prm.greenGreenGain = 1.0; prm.greenBlueGain = 0.0; prm.blueRedGain = 0.0; prm.blueGreenGain = 0.0; prm.blueBlueGain = 1.0; prm.blackRedGain = 1.0; prm.blackGreenGain = 0.0; prm.blackBlueGain = 0.0; return prm; } void MixerSettings::readSettings(KConfigGroup& group) { MixerContainer prm; MixerContainer defaultPrm = defaultSettings(); prm.bMonochrome = group.readEntry(d->configMonochromeEntry, defaultPrm.bMonochrome); prm.bPreserveLum = group.readEntry(d->configPreserveLuminosityEntry, defaultPrm.bPreserveLum); prm.redRedGain = group.readEntry(d->configRedRedGainEntry, defaultPrm.redRedGain); prm.redGreenGain = group.readEntry(d->configRedGreenGainEntry, defaultPrm.redGreenGain); prm.redBlueGain = group.readEntry(d->configRedBlueGainEntry, defaultPrm.redBlueGain); prm.greenRedGain = group.readEntry(d->configGreenRedGainEntry, defaultPrm.greenRedGain); prm.greenGreenGain = group.readEntry(d->configGreenGreenGainEntry, defaultPrm.greenGreenGain); prm.greenBlueGain = group.readEntry(d->configGreenBlueGainEntry, defaultPrm.greenBlueGain); prm.blueRedGain = group.readEntry(d->configBlueRedGainEntry, defaultPrm.blueRedGain); prm.blueGreenGain = group.readEntry(d->configBlueGreenGainEntry, defaultPrm.blueGreenGain); prm.blueBlueGain = group.readEntry(d->configBlueBlueGainEntry, defaultPrm.blueBlueGain); prm.blackRedGain = group.readEntry(d->configBlackRedGainEntry, defaultPrm.blackRedGain); prm.blackGreenGain = group.readEntry(d->configBlackGreenGainEntry, defaultPrm.blackGreenGain); prm.blackBlueGain = group.readEntry(d->configBlackBlueGainEntry, defaultPrm.blackBlueGain); setSettings(prm); } void MixerSettings::writeSettings(KConfigGroup& group) { MixerContainer prm = settings(); group.writeEntry(d->configMonochromeEntry, prm.bMonochrome); group.writeEntry(d->configPreserveLuminosityEntry, prm.bPreserveLum); group.writeEntry(d->configRedRedGainEntry, prm.redRedGain); group.writeEntry(d->configRedGreenGainEntry, prm.redGreenGain); group.writeEntry(d->configRedBlueGainEntry, prm.redBlueGain); group.writeEntry(d->configGreenRedGainEntry, prm.greenRedGain); group.writeEntry(d->configGreenGreenGainEntry, prm.greenGreenGain); group.writeEntry(d->configGreenBlueGainEntry, prm.greenBlueGain); group.writeEntry(d->configBlueRedGainEntry, prm.blueRedGain); group.writeEntry(d->configBlueGreenGainEntry, prm.blueGreenGain); group.writeEntry(d->configBlueBlueGainEntry, prm.blueBlueGain); group.writeEntry(d->configBlackRedGainEntry, prm.blackRedGain); group.writeEntry(d->configBlackGreenGainEntry, prm.blackGreenGain); group.writeEntry(d->configBlackBlueGainEntry, prm.blackBlueGain); } void MixerSettings::loadSettings() { QUrl loadGainsFileUrl; FILE* fp = nullptr; MixerContainer settings; loadGainsFileUrl = DFileDialog::getOpenFileUrl(qApp->activeWindow(), i18n("Select Gimp Gains Mixer File to Load"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)), QLatin1String("*")); if (loadGainsFileUrl.isEmpty()) { return; } fp = fopen(QFile::encodeName(loadGainsFileUrl.toLocalFile()).constData(), "r"); if (fp) { char buf1[1024]; char buf2[1024]; char buf3[1024]; buf1[0] = '\0'; fgets(buf1, 1023, fp); fscanf(fp, "%*s %256s", buf1); fscanf(fp, "%*s %256s", buf1); // preview flag, preserved for compatibility fscanf(fp, "%*s %256s", buf1); if (strcmp(buf1, "true") == 0) { settings.bMonochrome = true; } else { settings.bMonochrome = false; } fscanf(fp, "%*s %256s", buf1); if (strcmp(buf1, "true") == 0) { settings.bPreserveLum = true; } else { settings.bPreserveLum = false; } fscanf(fp, "%*s %256s %256s %256s", buf1, buf2, buf3); settings.redRedGain = atof(buf1); settings.redGreenGain = atof(buf2); settings.redBlueGain = atof(buf3); fscanf(fp, "%*s %256s %256s %256s", buf1, buf2, buf3); settings.greenRedGain = atof(buf1); settings.greenGreenGain = atof(buf2); settings.greenBlueGain = atof(buf3); fscanf(fp, "%*s %256s %256s %256s", buf1, buf2, buf3); settings.blueRedGain = atof(buf1); settings.blueGreenGain = atof(buf2); settings.blueBlueGain = atof(buf3); fscanf(fp, "%*s %256s %256s %256s", buf1, buf2, buf3); settings.blackRedGain = atof(buf1); settings.blackGreenGain = atof(buf2); settings.blackBlueGain = atof(buf3); fclose(fp); setSettings(settings); } else { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("Cannot load settings from the Gains Mixer text file.")); return; } } void MixerSettings::saveAsSettings() { QUrl saveGainsFileUrl; FILE* fp = nullptr; saveGainsFileUrl = DFileDialog::getSaveFileUrl(qApp->activeWindow(), i18n("Gimp Gains Mixer File to Save"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)), QLatin1String("*")); if (saveGainsFileUrl.isEmpty()) { return; } fp = fopen(QFile::encodeName(saveGainsFileUrl.toLocalFile()).constData(), "w"); if (fp) { const char* str = nullptr; char buf1[256]; char buf2[256]; char buf3[256]; switch (d->currentChannel) { case RedChannel: str = "RED"; break; case GreenChannel: str = "GREEN"; break; case BlueChannel: str = "BLUE"; break; default: qCWarning(DIGIKAM_DIMG_LOG) << "Unknown Color channel gains"; break; } fprintf(fp, "# Channel Mixer Configuration File\n"); fprintf(fp, "CHANNEL: %s\n", str); fprintf(fp, "PREVIEW: %s\n", "true"); // preserved for compatibility fprintf(fp, "MONOCHROME: %s\n", d->mixerSettings.bMonochrome ? "true" : "false"); fprintf(fp, "PRESERVE_LUMINOSITY: %s\n", d->mixerSettings.bPreserveLum ? "true" : "false"); sprintf(buf1, "%5.3f", d->mixerSettings.redRedGain); sprintf(buf2, "%5.3f", d->mixerSettings.redGreenGain); sprintf(buf3, "%5.3f", d->mixerSettings.redBlueGain); fprintf(fp, "RED: %s %s %s\n", buf1, buf2, buf3); sprintf(buf1, "%5.3f", d->mixerSettings.greenRedGain); sprintf(buf2, "%5.3f", d->mixerSettings.greenGreenGain); sprintf(buf3, "%5.3f", d->mixerSettings.greenBlueGain); fprintf(fp, "GREEN: %s %s %s\n", buf1, buf2, buf3); sprintf(buf1, "%5.3f", d->mixerSettings.blueRedGain); sprintf(buf2, "%5.3f", d->mixerSettings.blueGreenGain); sprintf(buf3, "%5.3f", d->mixerSettings.blueBlueGain); fprintf(fp, "BLUE: %s %s %s\n", buf1, buf2, buf3); sprintf(buf1, "%5.3f", d->mixerSettings.blackRedGain); sprintf(buf2, "%5.3f", d->mixerSettings.blackGreenGain); sprintf(buf3, "%5.3f", d->mixerSettings.blackBlueGain); fprintf(fp, "BLACK: %s %s %s\n", buf1, buf2, buf3); fclose(fp); } else { QMessageBox::critical(qApp->activeWindow(), qApp->applicationName(), i18n("Cannot save settings to the Gains Mixer text file.")); return; } } } // namespace Digikam diff --git a/core/libs/dimg/filters/bw/mixersettings.h b/core/libs/dimg/filters/bw/mixersettings.h index 38f44f6eca..79d08493b7 100644 --- a/core/libs/dimg/filters/bw/mixersettings.h +++ b/core/libs/dimg/filters/bw/mixersettings.h @@ -1,93 +1,93 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-02-18 * Description : Channel mixer settings view. * * Copyright (C) 2010-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_MIXER_SETTINGS_H #define DIGIKAM_MIXER_SETTINGS_H // Local includes #include // Local includes #include "digikam_export.h" #include "mixerfilter.h" class KConfigGroup; namespace Digikam { class DIGIKAM_EXPORT MixerSettings : public QWidget { Q_OBJECT public: explicit MixerSettings(QWidget* const parent); ~MixerSettings(); MixerContainer defaultSettings() const; void resetToDefault(); - MixerContainer settings() const; + MixerContainer settings() const; void setSettings(const MixerContainer& settings); void readSettings(KConfigGroup& group); void writeSettings(KConfigGroup& group); void loadSettings(); void saveAsSettings(); int currentChannel() const; void setMonochromeTipsVisible(bool b); Q_SIGNALS: void signalSettingsChanged(); void signalMonochromeActived(bool); void signalOutChannelChanged(); private: void updateSettingsWidgets(); void updateTotalPercents(); private Q_SLOTS: void slotResetCurrentChannel(); void slotGainsChanged(); void slotMonochromeActived(bool); void slotLuminosityChanged(bool); void slotOutChannelChanged(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_MIXER_SETTINGS_H diff --git a/core/libs/dimg/filters/bw/tonalityfilter.cpp b/core/libs/dimg/filters/bw/tonalityfilter.cpp index 16d6f164f0..3607bc951d 100644 --- a/core/libs/dimg/filters/bw/tonalityfilter.cpp +++ b/core/libs/dimg/filters/bw/tonalityfilter.cpp @@ -1,158 +1,158 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : Change tonality image filter * * Copyright (C) 2005-2020 by Gilles Caulier - * Copyright (C) 2010 by Martin Klapetek + * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "tonalityfilter.h" // C++ includes #include #include // KDE includes #include // Local includes #include "dimg.h" #include "dcolor.h" namespace Digikam { TonalityFilter::TonalityFilter(QObject* const parent) : DImgThreadedFilter(parent) { initFilter(); } TonalityFilter::TonalityFilter(DImg* const orgImage, QObject* const parent, const TonalityContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("TonalityFilter")), m_settings(settings) { initFilter(); } TonalityFilter::~TonalityFilter() { cancelFilter(); } QString TonalityFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Tonality Filter")); } - + /** * Change color tonality of an image for applying a RGB color mask. */ void TonalityFilter::filterImage() { m_destImage.putImageData(m_orgImage.bits()); uchar* bits = m_destImage.bits(); uint width = m_destImage.width(); uint height = m_destImage.height(); bool sixteenBit = m_destImage.sixteenBit(); uint size = width * height; int progress; int hue, sat, lig; DColor mask(m_settings.redMask, m_settings.greenMask, m_settings.blueMask, 0, sixteenBit); mask.getHSL(&hue, &sat, &lig); if (!sixteenBit) // 8 bits image. { uchar* ptr = bits; for (uint i = 0 ; i < size ; ++i) { // Convert to grayscale using tonal mask lig = lround(0.3 * ptr[2] + 0.59 * ptr[1] + 0.11 * ptr[0]); mask.setHSL(hue, sat, lig, sixteenBit); ptr[0] = (uchar)mask.blue(); ptr[1] = (uchar)mask.green(); ptr[2] = (uchar)mask.red(); ptr += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } else // 16 bits image. { unsigned short* ptr = reinterpret_cast(bits); for (uint i = 0 ; i < size ; ++i) { // Convert to grayscale using tonal mask lig = lround(0.3 * ptr[2] + 0.59 * ptr[1] + 0.11 * ptr[0]); mask.setHSL(hue, sat, lig, sixteenBit); ptr[0] = (unsigned short)mask.blue(); ptr[1] = (unsigned short)mask.green(); ptr[2] = (unsigned short)mask.red(); ptr += 4; progress = (int)(((double)i * 100.0) / size); - if (progress % 5 == 0) + if ((progress % 5) == 0) { postProgress(progress); } } } } FilterAction TonalityFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); action.addParameter(QLatin1String("blueMask"), m_settings.blueMask); action.addParameter(QLatin1String("greenMask"), m_settings.greenMask); action.addParameter(QLatin1String("redMask"), m_settings.redMask); return action; } void TonalityFilter::readParameters(const Digikam::FilterAction& action) { m_settings.blueMask = action.parameter(QLatin1String("blueMask")).toInt(); m_settings.greenMask = action.parameter(QLatin1String("greenMask")).toInt(); m_settings.redMask = action.parameter(QLatin1String("redMask")).toInt(); } } // namespace Digikam diff --git a/core/libs/dimg/filters/bw/tonalityfilter.h b/core/libs/dimg/filters/bw/tonalityfilter.h index 4a8d75a8e8..eb99c163a7 100644 --- a/core/libs/dimg/filters/bw/tonalityfilter.h +++ b/core/libs/dimg/filters/bw/tonalityfilter.h @@ -1,111 +1,111 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : Change tonality image filter * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_TONALITY_FILTER_H #define DIGIKAM_TONALITY_FILTER_H // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "digikam_globals.h" namespace Digikam { class DImg; class DIGIKAM_EXPORT TonalityContainer { public: explicit TonalityContainer() + : redMask(0), + greenMask(0), + blueMask(0) { - redMask = 0; - greenMask = 0; - blueMask = 0; }; ~TonalityContainer() { }; public: int redMask; int greenMask; int blueMask; }; // ----------------------------------------------------------------------------------------------- class DIGIKAM_EXPORT TonalityFilter : public DImgThreadedFilter { public: explicit TonalityFilter(QObject* const parent = nullptr); explicit TonalityFilter(DImg* const orgImage, QObject* const parent=nullptr, const TonalityContainer& settings=TonalityContainer()); virtual ~TonalityFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:TonalityFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; - void readParameters(const FilterAction& action) override; + virtual FilterAction filterAction() override; + void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; private: TonalityContainer m_settings; }; } // namespace Digikam #endif // DIGIKAM_TONALITY_FILTER_H diff --git a/core/libs/dimg/filters/dimgbuiltinfilter.cpp b/core/libs/dimg/filters/dimgbuiltinfilter.cpp index d9e5e7710d..9309b717ad 100644 --- a/core/libs/dimg/filters/dimgbuiltinfilter.cpp +++ b/core/libs/dimg/filters/dimgbuiltinfilter.cpp @@ -1,523 +1,527 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-11-10 * Description : basic filter management for DImg builtin methods * * Copyright (C) 2010-2011 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dimgbuiltinfilter.h" // KDE includes #include // Local includes #include "dimg.h" #include "dimgthreadedfilter.h" #include "filteraction.h" namespace Digikam { DImgBuiltinFilter::DImgBuiltinFilter() : m_type(NoOperation) { } DImgBuiltinFilter::DImgBuiltinFilter(const FilterAction& action) { setAction(action); } DImgBuiltinFilter::DImgBuiltinFilter(Type type, const QVariant& arg) { setAction(type, arg); } void DImgBuiltinFilter::setAction(const FilterAction& action) { m_type = NoOperation; - if (action.identifier() == QLatin1String("transform:rotate") && action.version() == 1) + if ((action.identifier() == QLatin1String("transform:rotate")) && (action.version() == 1)) { int angle = action.parameter(QLatin1String("angle")).toInt(); - if (angle == 90) + if (angle == 90) { m_type = Rotate90; } else if (angle == 180) { m_type = Rotate180; } else { m_type = Rotate270; } } - else if (action.identifier() == QLatin1String("transform:flip") && action.version() == 1) + else if ((action.identifier() == QLatin1String("transform:flip")) && (action.version() == 1)) { QString direction = action.parameter(QLatin1String("direction")).toString(); if (direction == QLatin1String("vertical")) { m_type = FlipVertically; } else { m_type = FlipHorizontally; } } - else if (action.identifier() == QLatin1String("transform:crop") && action.version() == 1) + else if ((action.identifier() == QLatin1String("transform:crop")) && (action.version() == 1)) { m_type = Crop; int x = action.parameter(QLatin1String("x")).toInt(); int y = action.parameter(QLatin1String("y")).toInt(); int width = action.parameter(QLatin1String("width")).toInt(); int height = action.parameter(QLatin1String("height")).toInt(); m_arg = QRect(x, y, width, height); } - else if (action.identifier() == QLatin1String("transform:resize") && action.version() == 1) + else if ((action.identifier() == QLatin1String("transform:resize")) && (action.version() == 1)) { m_type = Resize; int width = action.parameter(QLatin1String("width")).toInt(); int height = action.parameter(QLatin1String("height")).toInt(); m_arg = QSize(width, height); } - else if (action.identifier() == QLatin1String("transform:convertDepth") && action.version() == 1) + else if ((action.identifier() == QLatin1String("transform:convertDepth")) && (action.version() == 1)) { int depth = action.parameter(QLatin1String("depth")).toInt(); if (depth == 16) { m_type = ConvertTo16Bit; } else { m_type = ConvertTo8Bit; } } } void DImgBuiltinFilter::setAction(Type type, const QVariant& arg) { m_type = type; m_arg = arg; } bool DImgBuiltinFilter::isValid() const { switch (m_type) { case NoOperation: return false; case Crop: return m_arg.type() == QVariant::Rect; case Resize: return m_arg.type() == QVariant::Size; default: return true; } } void DImgBuiltinFilter::apply(DImg& image) const { switch (m_type) { case NoOperation: break; case Rotate90: image.rotate(DImg::ROT90); break; case Rotate180: image.rotate(DImg::ROT180); break; case Rotate270: image.rotate(DImg::ROT270); break; case FlipHorizontally: image.flip(DImg::HORIZONTAL); break; case FlipVertically: image.flip(DImg::VERTICAL); break; case Crop: image.crop(m_arg.toRect()); break; case Resize: { QSize s = m_arg.toSize(); image.resize(s.width(), s.height()); break; } case ConvertTo8Bit: image.convertToEightBit(); break; case ConvertTo16Bit: image.convertToSixteenBit(); break; } } FilterAction DImgBuiltinFilter::filterAction() const { FilterAction action; switch (m_type) { case NoOperation: default: return action; case Rotate90: case Rotate180: case Rotate270: { action = FilterAction(QLatin1String("transform:rotate"), 1); int angle; - if (m_type == Rotate90) + if (m_type == Rotate90) { angle = 90; } else if (m_type == Rotate180) { angle = 180; } else { angle = 270; } action.addParameter(QLatin1String("angle"), angle); break; } case FlipHorizontally: case FlipVertically: { action = FilterAction(QLatin1String("transform:flip"), 1); - action.addParameter(QLatin1String("direction"), m_type == FlipHorizontally ? QLatin1String("horizontal") : QLatin1String("vertical")); + action.addParameter(QLatin1String("direction"), (m_type == FlipHorizontally) ? QLatin1String("horizontal") + : QLatin1String("vertical")); break; } case Crop: { action = FilterAction(QLatin1String("transform:crop"), 1); QRect r = m_arg.toRect(); action.addParameter(QLatin1String("x"), r.x()); action.addParameter(QLatin1String("y"), r.y()); action.addParameter(QLatin1String("width"), r.width()); action.addParameter(QLatin1String("height"), r.height()); break; } case Resize: { action = FilterAction(QLatin1String("transform:resize"), 1); QSize s = m_arg.toSize(); action.addParameter(QLatin1String("width"), s.width()); action.addParameter(QLatin1String("height"), s.height()); break; } case ConvertTo8Bit: case ConvertTo16Bit: { action = FilterAction(QLatin1String("transform:convertDepth"), 1); - action.addParameter(QLatin1String("depth"), m_type == ConvertTo8Bit ? 8 : 16); + action.addParameter(QLatin1String("depth"), (m_type == ConvertTo8Bit) ? 8 : 16); break; } } action.setDisplayableName(displayableName()); return action; } DImgBuiltinFilter DImgBuiltinFilter::reverseFilter() const { switch (m_type) { case Rotate90: return DImgBuiltinFilter(Rotate270); case Rotate180: return DImgBuiltinFilter(Rotate180); case Rotate270: return DImgBuiltinFilter(Rotate90); case FlipHorizontally: case FlipVertically: return DImgBuiltinFilter(m_type); case Crop: case Resize: case ConvertTo8Bit: case ConvertTo16Bit: case NoOperation: default: return DImgBuiltinFilter(); } } bool DImgBuiltinFilter::isReversible() const { return reverseFilter().isValid(); } QStringList DImgBuiltinFilter::supportedFilters() { return QStringList() << QLatin1String("transform:rotate") << QLatin1String("transform:flip") << QLatin1String("transform:crop") << QLatin1String("transform:resize") << QLatin1String("transform:convertDepth"); } QList DImgBuiltinFilter::supportedVersions(const QString& filterIdentifier) { QList versions; // So far, all filters are at version 1 + if (isSupported(filterIdentifier)) { versions << 1; } return versions; } QString DImgBuiltinFilter::i18nDisplayableName() const { QByteArray latin1 = displayableName().toLatin1(); + return i18n(latin1.data()); } QString DImgBuiltinFilter::displayableName() const { switch (m_type) { case NoOperation: break; case Rotate90: return QString::fromUtf8(I18N_NOOP("Rotate Right")); case Rotate180: return QString::fromUtf8(I18N_NOOP("Rotate 180°")); case Rotate270: return QString::fromUtf8(I18N_NOOP("Rotate Left")); case FlipHorizontally: return QString::fromUtf8(I18N_NOOP("Flip Horizontally")); case FlipVertically: return QString::fromUtf8(I18N_NOOP("Flip Vertically")); case Crop: return QString::fromUtf8(I18N_NOOP("Crop")); case Resize: return QString::fromUtf8(I18N_NOOP("Resize")); case ConvertTo8Bit: return QString::fromUtf8(I18N_NOOP("Convert to 8 Bit")); case ConvertTo16Bit: return QString::fromUtf8(I18N_NOOP("Convert to 16 Bit")); } return QString(); } QString DImgBuiltinFilter::filterIcon() const { switch (m_type) { case NoOperation: break; case Rotate90: return QLatin1String("object-rotate-left"); case Rotate180: return QLatin1String("transform-rotate"); case Rotate270: return QLatin1String("object-rotate-right"); case FlipHorizontally: return QLatin1String("object-flip-horizontal"); case FlipVertically: return QLatin1String("object-flip-vertical"); case Crop: return QLatin1String("transform-crop"); case Resize: return QLatin1String("transform-scale"); case ConvertTo8Bit: return QLatin1String("depth16to8"); case ConvertTo16Bit: return QLatin1String("depth8to16"); } return QString(); } QString DImgBuiltinFilter::i18nDisplayableName(const QString& filterIdentifier) { - if (filterIdentifier == QLatin1String("transform:rotate")) + if (filterIdentifier == QLatin1String("transform:rotate")) { return i18nc("Rotate image", "Rotate"); } else if (filterIdentifier == QLatin1String("transform:flip")) { return i18nc("Flip image", "Flip"); } else if (filterIdentifier == QLatin1String("transform:crop")) { return i18nc("Crop image", "Crop"); } else if (filterIdentifier == QLatin1String("transform:resize")) { return i18nc("Resize image", "Resize"); } else if (filterIdentifier == QLatin1String("transform:convertDepth")) { return i18nc("Convert image bit depth", "Convert Depth"); } return QString(); } QString DImgBuiltinFilter::filterIcon(const QString& filterIdentifier) { - if (filterIdentifier == QLatin1String("transform:rotate")) + if (filterIdentifier == QLatin1String("transform:rotate")) { return QLatin1String("transform-rotate"); } else if (filterIdentifier == QLatin1String("transform:flip")) { return QLatin1String("object-flip-horizontal"); } else if (filterIdentifier == QLatin1String("transform:crop")) { return QLatin1String("transform-crop"); } else if (filterIdentifier == QLatin1String("transform:resize")) { return QLatin1String("transform-scale"); } else if (filterIdentifier == QLatin1String("transform:convertDepth")) { return QLatin1String("fill-color"); } return QString(); } bool DImgBuiltinFilter::isSupported(const QString& filterIdentifier) { return filterIdentifier.startsWith(QLatin1String("transform:")) && supportedFilters().contains(filterIdentifier); } bool DImgBuiltinFilter::isSupported(const QString& filterIdentifier, int version) { if (!isSupported(filterIdentifier)) { return false; } // at the moment, all filters are at version 1 - return version == 1; + + return (version == 1); } // ------------------------------------------------------------------------------------------------------------------- class Q_DECL_HIDDEN DImgBuiltinThreadedFilter : public DImgThreadedFilter { public: explicit DImgBuiltinThreadedFilter(const DImgBuiltinFilter& filter, DImg* const orgImage, QObject* const parent = nullptr) : DImgThreadedFilter(orgImage, parent), m_filter(filter) { } explicit DImgBuiltinThreadedFilter(const DImgBuiltinFilter& filter, QObject* const parent = nullptr) : DImgThreadedFilter(parent), m_filter(filter) { } virtual QString filterIdentifier() const override { return m_filter.filterAction().identifier(); } virtual FilterAction filterAction() override { return m_filter.filterAction(); } void readParameters(const FilterAction& action) override { m_filter = DImgBuiltinFilter(action); } protected: void filterImage() override { m_destImage = m_orgImage; m_filter.apply(m_destImage); } DImgBuiltinFilter m_filter; }; DImgThreadedFilter* DImgBuiltinFilter::createThreadedFilter(DImg* const orgImage, QObject* const parent) const { return new DImgBuiltinThreadedFilter(*this, orgImage, parent); } DImgThreadedFilter* DImgBuiltinFilter::createThreadedFilter(QObject* const parent) const { return new DImgBuiltinThreadedFilter(*this, parent); } } // namespace Digikam diff --git a/core/libs/dimg/filters/dimgbuiltinfilter.h b/core/libs/dimg/filters/dimgbuiltinfilter.h index 8dc8ef031c..e7dcf196a6 100644 --- a/core/libs/dimg/filters/dimgbuiltinfilter.h +++ b/core/libs/dimg/filters/dimgbuiltinfilter.h @@ -1,138 +1,152 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-11-10 * Description : basic filter management for DImg builtin methods * * Copyright (C) 2010-2011 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIMG_BUILT_IN_FILTERS_H #define DIGIKAM_DIMG_BUILT_IN_FILTERS_H // Qt includes #include // Local includes #include "digikam_export.h" namespace Digikam { class DImg; class DImgThreadedFilter; class FilterAction; class DIGIKAM_EXPORT DImgBuiltinFilter { public: enum Type { NoOperation, Rotate90, Rotate180, Rotate270, FlipHorizontally, FlipVertically, - Crop, /// Argument: QRect - Resize, /// Argument: QSize + Crop, ///< Argument: QRect + Resize, ///< Argument: QSize ConvertTo8Bit, ConvertTo16Bit }; public: - /// Create a filter performing no operation + /** + * Create a filter performing no operation + */ DImgBuiltinFilter(); /** * Create a filter for the given action. If the action is not supported, * the filter will perform no operation. */ explicit DImgBuiltinFilter(const FilterAction& action); /** * Create a filter of the given type. * See documentation of Type for required arguments. */ explicit DImgBuiltinFilter(Type type, const QVariant& arg = QVariant()); - /// same as constructor + /** + * same as constructor + */ void setAction(const FilterAction& action); void setAction(Type type, const QVariant& arg = QVariant()); /** * Returns the reverse action of this filter. * If the current action is not revertible, returns an invalid filter. */ - DImgBuiltinFilter reverseFilter() const; - bool isReversible() const; + DImgBuiltinFilter reverseFilter() const; + bool isReversible() const; /** * Checks that the action is supported and valid arguments are set */ - bool isValid() const; + bool isValid() const; - /// Apply the described change to the given image reference - void apply(DImg& image) const; + /** + * Apply the described change to the given image reference + */ + void apply(DImg& image) const; /** * NOTE: The following methods are also accessed by the more general * DImgFilterManager methods, so you usually do not need to call these directly. */ - /// Returns the FilterAction describing this filter. - FilterAction filterAction() const; + /** + * Returns the FilterAction describing this filter. + */ + FilterAction filterAction() const; - /// Returns a displayableName for this filter - QString displayableName() const; - QString i18nDisplayableName() const; - QString filterIcon() const; + /** + * Returns a displayableName for this filter + */ + QString displayableName() const; + QString i18nDisplayableName() const; + QString filterIcon() const; /** * Returns a DImgThreadedFilter which executes this builtin action. */ DImgThreadedFilter* createThreadedFilter(QObject* const parent = nullptr) const; DImgThreadedFilter* createThreadedFilter(DImg* const orgImage, QObject* const parent = nullptr) const; public: static QString i18nDisplayableName(const QString& filterIdentifier); static QString filterIcon(const QString& filterIdentifier); static QStringList supportedFilters(); - /// Returns a list of supported versions of the given filter + /** + * Returns a list of supported versions of the given filter + */ static QList supportedVersions(const QString& filterIdentifier); - /// Returns if the given filter and version are supported by DImgBuiltinFilter + /** + * Returns if the given filter and version are supported by DImgBuiltinFilter + */ static bool isSupported(const QString& filterIdentifier); static bool isSupported(const QString& filterIdentifier, int version); protected: Type m_type; QVariant m_arg; }; } // namespace Digikam #endif // DIGIKAM_DIMG_BUILT_IN_FILTERS_H diff --git a/core/libs/dimg/filters/dimgfiltergenerator.h b/core/libs/dimg/filters/dimgfiltergenerator.h index 2e7ef94e12..03a031c917 100644 --- a/core/libs/dimg/filters/dimgfiltergenerator.h +++ b/core/libs/dimg/filters/dimgfiltergenerator.h @@ -1,123 +1,125 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-06-24 * Description : class for creating a particular filter * * Copyright (C) 2010-2011 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIMG_FILTER_GENERATOR_H #define DIGIKAM_DIMG_FILTER_GENERATOR_H // Qt includes #include #include #include // Local includes #include "digikam_export.h" namespace Digikam { class DImgThreadedFilter; class DIGIKAM_EXPORT DImgFilterGenerator { public: - DImgFilterGenerator() {}; + DImgFilterGenerator() {}; virtual ~DImgFilterGenerator() {}; /// Returns a list with identifiers of supported filters - virtual QStringList supportedFilters() = 0; + virtual QStringList supportedFilters() = 0; /// Returns a list with the supported versions for the given identifier - virtual QList supportedVersions(const QString& filterIdentifier) = 0; + virtual QList supportedVersions(const QString& filterIdentifier) = 0; /// Returns a QString with filter name for displaying in views - virtual QString displayableName(const QString& filterIdentifier) = 0; + virtual QString displayableName(const QString& filterIdentifier) = 0; /// Convenience methods virtual bool isSupported(const QString& filterIdentifier); virtual bool isSupported(const QString& filterIdentifier, int version); /// Create the filter for the given combination of identifier and version - virtual DImgThreadedFilter* createFilter(const QString& filterIdentifier, int version) = 0; + virtual DImgThreadedFilter* createFilter(const QString& filterIdentifier, + int version) = 0; }; // ----------------------------------------------------------------------------------- template - class BasicDImgFilterGenerator : public DImgFilterGenerator { public: - /** A sample implementation for one DImgThreadedFilter class. - * The class must provide two static methods, FilterIdentifier() and SupportedVersions(). + /** + * A sample implementation for one DImgThreadedFilter class. + * The class must provide two static methods, FilterIdentifier() and SupportedVersions(). */ BasicDImgFilterGenerator() { } QStringList supportedFilters() override { return QList() << T::FilterIdentifier(); } QList supportedVersions(const QString& filterIdentifier) override { if (filterIdentifier == T::FilterIdentifier()) { return T::SupportedVersions(); } return QList(); } DImgThreadedFilter* createFilter(const QString& filterIdentifier, int version) override { - if (filterIdentifier == T::FilterIdentifier() && + if ((filterIdentifier == T::FilterIdentifier()) && T::SupportedVersions().contains(version)) { - T* t = new T; + T* const t = new T; t->setFilterVersion(version); + return t; } return nullptr; } QString displayableName(const QString& filterIdentifier) override { if (filterIdentifier == T::FilterIdentifier()) { return T::DisplayableName(); } return QString(); } }; } // namespace Digikam #endif // DIGIKAM_DIMG_FILTER_GENERATOR_H diff --git a/core/libs/dimg/filters/dimgfiltermanager.cpp b/core/libs/dimg/filters/dimgfiltermanager.cpp index 1b6fcc77c3..7b02f59682 100644 --- a/core/libs/dimg/filters/dimgfiltermanager.cpp +++ b/core/libs/dimg/filters/dimgfiltermanager.cpp @@ -1,495 +1,502 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-06-24 * Description : manager for filters (registering, creating etc) * * Copyright (C) 2010-2011 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dimgfiltermanager.h" // Qt includes #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "digikam_config.h" #include "dimgfiltergenerator.h" #include "dimgbuiltinfilter.h" #include "filteraction.h" #include "rawprocessingfilter.h" // Filter includes #include "antivignettingfilter.h" #include "autoexpofilter.h" #include "autolevelsfilter.h" #include "bcgfilter.h" #include "blurfilter.h" #include "blurfxfilter.h" #include "borderfilter.h" #include "bwsepiafilter.h" #include "cbfilter.h" #include "charcoalfilter.h" #include "colorfxfilter.h" #include "curvesfilter.h" #include "distortionfxfilter.h" #include "embossfilter.h" #include "equalizefilter.h" #include "filmgrainfilter.h" #include "freerotationfilter.h" #include "greycstorationfilter.h" #include "hslfilter.h" #include "icctransformfilter.h" #include "infraredfilter.h" #include "invertfilter.h" #include "lensdistortionfilter.h" #include "levelsfilter.h" #include "localcontrastfilter.h" #include "mixerfilter.h" #include "normalizefilter.h" #include "nrfilter.h" #include "oilpaintfilter.h" #include "redeyecorrectionfilter.h" #include "raindropfilter.h" #include "sharpenfilter.h" #include "shearfilter.h" #include "stretchfilter.h" #include "texturefilter.h" #include "tonalityfilter.h" #include "unsharpmaskfilter.h" #include "wbfilter.h" #include "filmfilter_p.h" #ifdef HAVE_LIBLQR_1 # include "contentawarefilter.h" #endif /* HAVE_LIBLQR_1 */ #ifdef HAVE_LENSFUN # include "lensfunfilter.h" #endif // HAVE_LENSFUN #ifdef HAVE_EIGEN3 # include "refocusfilter.h" #endif // HAVE_EIGEN3 namespace Digikam { typedef QSharedPointer ImgFilterPtr; class Q_DECL_HIDDEN DImgFilterManager::Private { public: explicit Private() : mutex(QMutex::Recursive) { } ~Private() { } void setupCoreGenerators(); void setupFilterIcons(); void setupI18nStrings(); void addGenerator(const ImgFilterPtr& generator); public: QMap filterMap; QList coreGenerators; QHash filterIcons; QHash i18nFilterNames; QMutex mutex; }; void DImgFilterManager::Private::setupCoreGenerators() { //Please keep this list sorted alphabetically coreGenerators << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) + #ifdef HAVE_LIBLQR_1 << ImgFilterPtr(new BasicDImgFilterGenerator()) #endif // HAVE_LIBLQR_1 + << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) + #ifdef HAVE_LENSFUN << ImgFilterPtr(new BasicDImgFilterGenerator()) #endif // HAVE_LENSFUN + << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) + #ifdef HAVE_EIGEN3 << ImgFilterPtr(new BasicDImgFilterGenerator()) #endif // HAVE_EIGEN3 + << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()) << ImgFilterPtr(new BasicDImgFilterGenerator()); } void DImgFilterManager::Private::setupFilterIcons() { //Please keep this list sorted alphabetically filterIcons.insert(QLatin1String("digikam:AntiVignettingFilter"), QLatin1String("antivignetting")); filterIcons.insert(QLatin1String("digikam:AutoExpoFilter"), QLatin1String("autocorrection")); filterIcons.insert(QLatin1String("digikam:AutoLevelsfilter"), QLatin1String("autocorrection")); filterIcons.insert(QLatin1String("digikam:BCGFilter"), QLatin1String("contrast")); filterIcons.insert(QLatin1String("digikam:BlurFilter"), QLatin1String("blurimage")); filterIcons.insert(QLatin1String("digikam:BlurFXFilter"), QLatin1String("blurfx")); filterIcons.insert(QLatin1String("digikam:BorderFilter"), QLatin1String("bordertool")); filterIcons.insert(QLatin1String("digikam:BWSepiaFilter"), QLatin1String("bwtonal")); filterIcons.insert(QLatin1String("digikam:ColorBalanceFilter"), QLatin1String("adjustrgb")); filterIcons.insert(QLatin1String("digikam:CharcoalFilter"), QLatin1String("charcoaltool")); filterIcons.insert(QLatin1String("digikam:ColorFX"), QLatin1String("colorfx")); filterIcons.insert(QLatin1String("digikam:ContentAwareFilter"), QLatin1String("transform-scale")); filterIcons.insert(QLatin1String("digikam:CurvesFilter"), QLatin1String("adjustcurves")); filterIcons.insert(QLatin1String("digikam:DistortionFXFilter"), QLatin1String("draw-spiral")); filterIcons.insert(QLatin1String("digikam:EmbossFilter"), QLatin1String("embosstool")); filterIcons.insert(QLatin1String("digikam:EqualizeFilter"), QLatin1String("autocorrection")); filterIcons.insert(QLatin1String("digikam:FilmFilter"), QLatin1String("colorneg")); filterIcons.insert(QLatin1String("digikam:FilmGrainFilter"), QLatin1String("filmgrain")); filterIcons.insert(QLatin1String("digikam:FreeRotationFilter"), QLatin1String("transform-rotate")); filterIcons.insert(QLatin1String("digikam:GreycstorationFilter"), QLatin1String("restoration")); filterIcons.insert(QLatin1String("digikam:HSLFilter"), QLatin1String("adjusthsl")); filterIcons.insert(QLatin1String("digikam:InvertFilter"), QLatin1String("edit-select-invert")); filterIcons.insert(QLatin1String("digikam:LensDistortionFilter"), QLatin1String("lensdistortion")); filterIcons.insert(QLatin1String("digikam:LensFunFilter"), QLatin1String("lensautofix")); filterIcons.insert(QLatin1String("digikam:LevelsFilter"), QLatin1String("adjustlevels")); filterIcons.insert(QLatin1String("digikam:LocalContrastFilter"), QLatin1String("contrast")); filterIcons.insert(QLatin1String("digikam:MixerFilter"), QLatin1String("channelmixer")); filterIcons.insert(QLatin1String("digikam:NoiseReductionFilter"), QLatin1String("noisereduction")); filterIcons.insert(QLatin1String("digikam:NormalizeFilter"), QLatin1String("autocorrection")); filterIcons.insert(QLatin1String("digikam:OilPaintFilter"), QLatin1String("oilpaint")); filterIcons.insert(QLatin1String("digikam:RainDropFilter"), QLatin1String("raindrop")); filterIcons.insert(QLatin1String("digikam:RatioCrop"), QLatin1String("transform-crop")); filterIcons.insert(QLatin1String("digikam:RedEyeCorrectionFilter"), QLatin1String("redeyes")); filterIcons.insert(QLatin1String("digikam:RefocusFilter"), QLatin1String("sharpenimage")); filterIcons.insert(QLatin1String("digikam:Rotate90"), QLatin1String("object-rotate-right")); filterIcons.insert(QLatin1String("digikam:Rotate270"), QLatin1String("object-rotate-left")); filterIcons.insert(QLatin1String("digikam:SharpenFilter"), QLatin1String("sharpenimage")); filterIcons.insert(QLatin1String("digikam:ShearFilter"), QLatin1String("transform-shear-left")); filterIcons.insert(QLatin1String("digikam:StretchFilter"), QLatin1String("autocorrection")); filterIcons.insert(QLatin1String("digikam:TextureFilter"), QLatin1String("texture")); filterIcons.insert(QLatin1String("digikam:TonalityFilter"), QLatin1String("contrast")); filterIcons.insert(QLatin1String("digikam:UnsharpMaskFilter"), QLatin1String("sharpenimage")); filterIcons.insert(QLatin1String("digikam:WhiteBalanceFilter"), QLatin1String("bordertool")); filterIcons.insert(QLatin1String("digikam:RawConverter"), QLatin1String("image-x-adobe-dng")); } void DImgFilterManager::Private::setupI18nStrings() { // i18nFilterNames.insert("someIdentifier", i18n("display name")); } void DImgFilterManager::Private::addGenerator(const ImgFilterPtr& generator) { QMutexLocker lock(&mutex); foreach (const QString& id, generator->supportedFilters()) { if (filterMap.contains(id)) { qCDebug(DIGIKAM_DIMG_LOG) << "Attempt to register filter identifier" << id << "twice. Ignoring."; continue; } filterMap[id] = generator; } } // ----------------------------------------------------------------------------------------- class Q_DECL_HIDDEN DImgFilterManagerCreator { public: DImgFilterManager object; }; Q_GLOBAL_STATIC(DImgFilterManagerCreator, creator) DImgFilterManager* DImgFilterManager::instance() { return &creator->object; } DImgFilterManager::DImgFilterManager() : d(new Private) { QMutexLocker lock(&d->mutex); d->setupCoreGenerators(); d->setupFilterIcons(); d->setupI18nStrings(); foreach (const ImgFilterPtr& gen, d->coreGenerators) { d->addGenerator(gen); } } DImgFilterManager::~DImgFilterManager() { delete d; } void DImgFilterManager::addGenerator(DImgFilterGenerator* const generator) { ImgFilterPtr shared(generator); d->addGenerator(shared); } void DImgFilterManager::removeGenerator(DImgFilterGenerator* const /*generator*/) { /* QMutexLocker lock(&d->mutex); QMap::iterator it; - for (it = d->filterMap.begin(); it != d->filterMap.end();) + for (it = d->filterMap.begin() ; it != d->filterMap.end() ; ) { if (it.value() == generator) { it = d->filterMap.erase(it); } else { ++it; } } */ } QStringList DImgFilterManager::supportedFilters() { QMutexLocker lock(&d->mutex); return DImgBuiltinFilter::supportedFilters() + d->filterMap.keys(); } QList DImgFilterManager::supportedVersions(const QString& filterIdentifier) { if (DImgBuiltinFilter::isSupported(filterIdentifier)) { return DImgBuiltinFilter::supportedVersions(filterIdentifier); } QMutexLocker lock(&d->mutex); - DImgFilterGenerator* gen = d->filterMap.value(filterIdentifier).data(); + DImgFilterGenerator* const gen = d->filterMap.value(filterIdentifier).data(); if (gen) { return gen->supportedVersions(filterIdentifier); } return QList(); } QString DImgFilterManager::displayableName(const QString& filterIdentifier) { QMutexLocker lock(&d->mutex); - DImgFilterGenerator* gen = d->filterMap.value(filterIdentifier).data(); + DImgFilterGenerator* const gen = d->filterMap.value(filterIdentifier).data(); if (gen) { return gen->displayableName(filterIdentifier); } return QString(); } QString DImgFilterManager::filterIcon(const QString& filterIdentifier) { if (DImgBuiltinFilter::isSupported(filterIdentifier)) { return DImgBuiltinFilter::filterIcon(filterIdentifier); } QMutexLocker lock(&d->mutex); + return d->filterIcons.value(filterIdentifier); } QString DImgFilterManager::filterIcon(const FilterAction& action) { QString iconName = filterIcon(action.identifier()); if (!iconName.isNull()) { return iconName; } return QLatin1String("document-edit"); } QString DImgFilterManager::i18nDisplayableName(const QString& filterIdentifier) { QMutexLocker lock(&d->mutex); QString name = d->i18nFilterNames.value(filterIdentifier); if (!name.isEmpty()) { return name; } if (DImgBuiltinFilter::isSupported(filterIdentifier)) { return DImgBuiltinFilter::i18nDisplayableName(filterIdentifier); } name = displayableName(filterIdentifier); if (!name.isEmpty()) { QByteArray latin1 = name.toLatin1(); QString translated = i18n(latin1.constData()); if (translated != name) { return translated; } return name; } QString digikamNamespace = QLatin1String("digikam:"); if (filterIdentifier.startsWith(digikamNamespace)) { return filterIdentifier.mid(digikamNamespace.length()); } return filterIdentifier; } QString DImgFilterManager::i18nDisplayableName(const FilterAction& action) { if (action.displayableName().isEmpty() && action.identifier().isEmpty()) { return i18n("Unknown filter"); } else { QString i18nDispName = i18nDisplayableName(action.identifier()); QString metadataDispName = action.displayableName(); - if (!i18nDispName.isEmpty()) + if (!i18nDispName.isEmpty()) { return i18nDispName; } else if (!metadataDispName.isEmpty()) { return metadataDispName; } else { return action.identifier(); } } } bool DImgFilterManager::isSupported(const QString& filterIdentifier) { QMutexLocker lock(&d->mutex); if (DImgBuiltinFilter::isSupported(filterIdentifier)) { return true; } return d->filterMap.contains(filterIdentifier); } bool DImgFilterManager::isSupported(const QString& filterIdentifier, int version) { QMutexLocker lock(&d->mutex); if (DImgBuiltinFilter::isSupported(filterIdentifier, version)) { return true; } - DImgFilterGenerator* gen = d->filterMap.value(filterIdentifier).data(); + DImgFilterGenerator* const gen = d->filterMap.value(filterIdentifier).data(); if (gen) { return gen->isSupported(filterIdentifier, version); } return false; } bool DImgFilterManager::isRawConversion(const QString& filterIdentifier) { return filterIdentifier == RawProcessingFilter::FilterIdentifier(); } DImgThreadedFilter* DImgFilterManager::createFilter(const QString& filterIdentifier, int version) { QMutexLocker lock(&d->mutex); qCDebug(DIGIKAM_DIMG_LOG) << "Creating filter " << filterIdentifier; - DImgFilterGenerator* gen = d->filterMap.value(filterIdentifier).data(); + DImgFilterGenerator* const gen = d->filterMap.value(filterIdentifier).data(); if (gen) { return gen->createFilter(filterIdentifier, version); } return nullptr; } } // namespace Digikam diff --git a/core/libs/dimg/filters/dimgfiltermanager.h b/core/libs/dimg/filters/dimgfiltermanager.h index 0ebc00db29..0b9395b8bc 100644 --- a/core/libs/dimg/filters/dimgfiltermanager.h +++ b/core/libs/dimg/filters/dimgfiltermanager.h @@ -1,120 +1,125 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-06-24 * Description : manager for filters (registering, creating etc) * * Copyright (C) 2010-2011 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIMG_FILTER_MANAGER_H #define DIGIKAM_DIMG_FILTER_MANAGER_H // Qt includes #include #include #include // Local includes #include "dimgfiltergenerator.h" #include "digikam_export.h" namespace Digikam { class FilterAction; class DIGIKAM_EXPORT DImgFilterManager : public DImgFilterGenerator { public: static DImgFilterManager* instance(); - /// Returns a list of the supported filter identifiers - QStringList supportedFilters() override; + /** + * Returns a list of the supported filter identifiers + */ + QStringList supportedFilters() override; - /// Returns a list of supported versions of the given filter - QList supportedVersions(const QString& filterIdentifier) override; + /** + * Returns a list of supported versions of the given filter + */ + QList supportedVersions(const QString& filterIdentifier) override; /** * Returns the (untranslated) displayable name for the given identifier. * This is only possible for supported filters. If you have a FilterAction, * it may already contain a displayable name. */ - QString displayableName(const QString& filterIdentifier) override; + QString displayableName(const QString& filterIdentifier) override; /** * Returns the translated displayable name */ QString i18nDisplayableName(const QString& filterIdentifier); QString i18nDisplayableName(const FilterAction& action); /** * Returns an icon for the given filter. * If no icon is known, returns a null string. */ QString filterIcon(const QString& filterIdentifier); QString filterIcon(const FilterAction& action); /** * Returns true if the given filter, or, more specifically, * the given filter in the given version is supported. */ - bool isSupported(const QString& filterIdentifier) override; - bool isSupported(const QString& filterIdentifier, int version) override; + bool isSupported(const QString& filterIdentifier) override; + bool isSupported(const QString& filterIdentifier, int version) override; /** * Returns true if the given filter is to be considered * as a step converting a RAW image to a normal image. */ bool isRawConversion(const QString& filterIdentifier); /** * Create a filter from an installed manager. * Returns 0 if no filter could be created. This is true * if identifier/version is not supported, or the filter is builtin. * Note: You probably want to use FilterActionFilter. */ - DImgThreadedFilter* createFilter(const QString& filterIdentifier, int version) override; + DImgThreadedFilter* createFilter(const QString& filterIdentifier, + int version) override; /** * Registers all filter provided by this generator. */ void addGenerator(DImgFilterGenerator* const generator); void removeGenerator(DImgFilterGenerator* const generator); private: DImgFilterManager(); // Disable ~DImgFilterManager(); // Disable DImgFilterManager(const DImgFilterManager&); // Disable DImgFilterManager& operator=(const DImgFilterManager&); // Disable private: friend class DImgFilterManagerCreator; class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_DIMG_FILTER_MANAGER_H diff --git a/core/libs/dimg/filters/dimgthreadedanalyser.h b/core/libs/dimg/filters/dimgthreadedanalyser.h index 2605618e83..f7ca74cece 100644 --- a/core/libs/dimg/filters/dimgthreadedanalyser.h +++ b/core/libs/dimg/filters/dimgthreadedanalyser.h @@ -1,105 +1,107 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-11-13 * Description : threaded image analys class. * this class is dedicated to run algorithm in a separated thread * over an image to process analys. No image data are changed. * * Copyright (C) 2012-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIMG_THREADED_ANALYSER_H #define DIGIKAM_DIMG_THREADED_ANALYSER_H // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" namespace Digikam { class DIGIKAM_EXPORT DImgThreadedAnalyser : public DImgThreadedFilter { public: - /** Constructs a filter without argument. - * You need to call setupFilter() and startFilter() - * to start the threaded computation. - * To run filter without to use multithreading, call startFilterDirectly(). + /** + * Constructs a filter without argument. + * You need to call setupFilter() and startFilter() + * to start the threaded computation. + * To run filter without to use multithreading, call startFilterDirectly(). + * NOTE: Versionning is not supported in this class */ explicit DImgThreadedAnalyser(QObject* const parent=nullptr, const QString& name = QString()); - /** Constructs an image ananlyser with all arguments (ready to use). - * The given original image will be copied. - * You need to call startFilter() to start the threaded computation. - * To run analyser without to use multithreading, call startFilterDirectly(). + /** + * Constructs an image ananlyser with all arguments (ready to use). + * The given original image will be copied. + * You need to call startFilter() to start the threaded computation. + * To run analyser without to use multithreading, call startFilterDirectly(). */ explicit DImgThreadedAnalyser(DImg* const orgImage, QObject* const parent=nullptr, const QString& name = QString()); ~DImgThreadedAnalyser(); private: - // NOTE: Versionning is not supported in this class - FilterAction filterAction() override { // return null object return FilterAction(); }; void readParameters(const FilterAction&) override { // Do nothing. }; QString filterIdentifier() const override { // return null object return QString(); }; - QList supportedVersions() const override + QList supportedVersions() const override { // return null object return QList(); }; - void prepareDestImage() override + void prepareDestImage() override { // No destination image is required here. }; - void filterImage() override + void filterImage() override { startAnalyse(); }; protected: - /** Main image analys method. Override in subclass. + /** + * Main image analys method. Override in subclass. */ virtual void startAnalyse() = 0; }; } // namespace Digikam #endif // DIGIKAM_DIMG_THREADED_ANALYSER_H diff --git a/core/libs/dimg/filters/dimgthreadedfilter.cpp b/core/libs/dimg/filters/dimgthreadedfilter.cpp index 5dba3e1933..774bfa5ce0 100644 --- a/core/libs/dimg/filters/dimgthreadedfilter.cpp +++ b/core/libs/dimg/filters/dimgthreadedfilter.cpp @@ -1,293 +1,300 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-25 * Description : threaded image filter class. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2007-2012 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dimgthreadedfilter.h" // Qt includes #include #include #include // Local includes #include "digikam_debug.h" namespace Digikam { DImgThreadedFilter::DImgThreadedFilter(QObject* const parent, const QString& name) - : DynamicThread(parent) + : DynamicThread(parent), + m_version(1), + m_wasCancelled(false) { setOriginalImage(DImg()); setFilterName(name); - m_version = 1; - m_wasCancelled = false; initMaster(); } DImgThreadedFilter::DImgThreadedFilter(DImg* const orgImage, QObject* const parent, const QString& name) - : DynamicThread(parent) + : DynamicThread(parent), + m_version(1), + m_wasCancelled(false) { // remove meta data + setOriginalImage(orgImage->copyImageData()); setFilterName(name); - m_version = 1; - m_wasCancelled = false; initMaster(); } DImgThreadedFilter::DImgThreadedFilter(DImgThreadedFilter* const master, const DImg& orgImage, const DImg& destImage, int progressBegin, int progressEnd, const QString& name) + : m_version(1), + m_wasCancelled(false), + m_destImage(destImage) { setFilterName(name); setOriginalImage(orgImage); - m_destImage = destImage; - m_version = 1; - m_wasCancelled = false; initSlave(master, progressBegin, progressEnd); } DImgThreadedFilter::~DImgThreadedFilter() { // NOTE: use dynamic binding as this virtual method can be re-implemented in derived classes. + this->cancelFilter(); if (m_master) { m_master->setSlave(nullptr); } } void DImgThreadedFilter::initSlave(DImgThreadedFilter* const master, int progressBegin, int progressEnd) { m_master = master; m_slave = nullptr; m_progressBegin = progressBegin; m_progressSpan = progressEnd - progressBegin; m_progressCurrent = 0; if (m_master) { m_master->setSlave(this); } } void DImgThreadedFilter::initMaster() { m_master = nullptr; m_slave = nullptr; m_progressBegin = 0; m_progressSpan = 100; m_progressCurrent = 0; } void DImgThreadedFilter::setupFilter(const DImg& orgImage) { setOriginalImage(orgImage); + // some filters may require that initFilter is called + initFilter(); } void DImgThreadedFilter::setupAndStartDirectly(const DImg& orgImage, DImgThreadedFilter* const master, int progressBegin, int progressEnd) { initSlave(master, progressBegin, progressEnd); setupFilter(orgImage); } void DImgThreadedFilter::setOriginalImage(const DImg& orgImage) { m_orgImage = orgImage; } void DImgThreadedFilter::setFilterName(const QString& name) { m_name = QString(name); } QList DImgThreadedFilter::supportedVersions() const { - return QList() << 1; + return (QList() << 1); } void DImgThreadedFilter::setFilterVersion(int version) { if (supportedVersions().contains(version)) { m_version = version; } } int DImgThreadedFilter::filterVersion() const { return m_version; } void DImgThreadedFilter::initFilter() { prepareDestImage(); if (m_master) { startFilterDirectly(); } } void DImgThreadedFilter::prepareDestImage() { m_destImage.reset(); if (!m_orgImage.isNull()) { - m_destImage = DImg(m_orgImage.width(), m_orgImage.height(), + m_destImage = DImg(m_orgImage.width(), m_orgImage.height(), m_orgImage.sixteenBit(), m_orgImage.hasAlpha()); } } void DImgThreadedFilter::startFilter() { if (m_orgImage.width() && m_orgImage.height()) { start(); } else // No image data { emit finished(false); qCDebug(DIGIKAM_DIMG_LOG) << m_name << "::No valid image data !!! ..."; } } void DImgThreadedFilter::startFilterDirectly() { if (m_orgImage.width() && m_orgImage.height()) { emit started(); m_wasCancelled = false; try { QDateTime now = QDateTime::currentDateTime(); filterImage(); //qCDebug(DIGIKAM_DIMG_LOG) << m_name << ":: excecution time : " << now.msecsTo(QDateTime::currentDateTime()) << " ms"; } catch (std::bad_alloc& ex) { - //TODO: User notification + // TODO: User notification qCCritical(DIGIKAM_DIMG_LOG) << "Caught out-of-memory exception! Aborting operation" << ex.what(); emit finished(false); return; } emit finished(!m_wasCancelled); } else // No image data { emit finished(false); qCDebug(DIGIKAM_DIMG_LOG) << m_name << "::No valid image data !!! ..."; } } void DImgThreadedFilter::run() { startFilterDirectly(); } void DImgThreadedFilter::cancelFilter() { if (isRunning()) { m_wasCancelled = true; } stop(); if (m_slave) { m_slave->stop(); + // do not wait on slave, it is not running in its own separate thread! //m_slave->cleanupFilter(); } wait(); cleanupFilter(); } void DImgThreadedFilter::postProgress(int progr) { if (m_master) { progr = modulateProgress(progr); m_master->postProgress(progr); } else if (m_progressCurrent != progr) { emit progress(progr); m_progressCurrent = progr; } } void DImgThreadedFilter::setSlave(DImgThreadedFilter* const slave) { m_slave = slave; } int DImgThreadedFilter::modulateProgress(int progress) { return m_progressBegin + (int)((double)progress * (double)m_progressSpan / 100.0); } bool DImgThreadedFilter::parametersSuccessfullyRead() const { return true; } QString DImgThreadedFilter::readParametersError(const FilterAction&) const { return QString(); } QList DImgThreadedFilter::multithreadedSteps(int stop, int start) const { uint nbCore = QThreadPool::globalInstance()->maxThreadCount(); float step = ((float)stop - (float)start) / (float)nbCore; QList vals; vals << start; for (uint i = 1 ; i < nbCore ; ++i) + { vals << vals.last() + step; + } vals << stop; return vals; } } // namespace Digikam diff --git a/core/libs/dimg/filters/dimgthreadedfilter.h b/core/libs/dimg/filters/dimgthreadedfilter.h index eb989d7444..4a8e39bb54 100644 --- a/core/libs/dimg/filters/dimgthreadedfilter.h +++ b/core/libs/dimg/filters/dimgthreadedfilter.h @@ -1,296 +1,323 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-05-25 * Description : threaded image filter class. * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2007-2012 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIMG_THREADED_FILTER_H #define DIGIKAM_DIMG_THREADED_FILTER_H // Local includes #include "digikam_export.h" #include "dimg.h" #include "dynamicthread.h" #include "filteraction.h" class QObject; namespace Digikam { class DIGIKAM_EXPORT DImgThreadedFilter : public DynamicThread { Q_OBJECT public: - /** Constructs a filter without argument. - * You need to call setupFilter() and startFilter() - * to start the threaded computation. - * To run filter without to use multithreading, call startFilterDirectly(). + /** + * Constructs a filter without argument. + * You need to call setupFilter() and startFilter() + * to start the threaded computation. + * To run filter without to use multithreading, call startFilterDirectly(). */ explicit DImgThreadedFilter(QObject* const parent=nullptr, const QString& name = QString()); - /** Constructs a filter with all arguments (ready to use). - * The given original image will be copied. - * You need to call startFilter() to start the threaded computation. - * To run filter without to use multithreading, call startFilterDirectly(). + /** + * Constructs a filter with all arguments (ready to use). + * The given original image will be copied. + * You need to call startFilter() to start the threaded computation. + * To run filter without to use multithreading, call startFilterDirectly(). */ DImgThreadedFilter(DImg* const orgImage, QObject* const parent, const QString& name = QString()); ~DImgThreadedFilter(); - /** You need to call this and then start filter of you used - * the constructor not setting an original image. - * The original image's data will not be copied. + /** + * You need to call this and then start filter of you used + * the constructor not setting an original image. + * The original image's data will not be copied. */ void setupFilter(const DImg& orgImage); - /** Initializes the filter for use as a slave and directly starts computation (in-thread) + /** + * Initializes the filter for use as a slave and directly starts computation (in-thread) */ void setupAndStartDirectly(const DImg& orgImage, DImgThreadedFilter* const master, int progressBegin = 0, int progressEnd = 100); void setOriginalImage(const DImg& orgImage); void setFilterName(const QString& name); DImg getTargetImage() { return m_destImage; }; const QString& filterName() { return m_name; }; - /** This method return a list of steps to process parallelized operation in filter using QtConcurrents API. - * Usually, start and stop are rows or columns from image to process. By default, whole image will be processed - * and start value is 0. In this case stop will be last row or column to process. - * Between range [start,stop], this method will divide by equal steps depending of number of CPU cores available. - * To be sure that all values will be processed, in case of CPU core division give rest, the last step compensate - * the difference. - * See Blur filter loop implementation for example to see how to use this method with QtConcurrents API. + /** + * This method return a list of steps to process parallelized operation in filter using QtConcurrents API. + * Usually, start and stop are rows or columns from image to process. By default, whole image will be processed + * and start value is 0. In this case stop will be last row or column to process. + * Between range [start,stop], this method will divide by equal steps depending of number of CPU cores available. + * To be sure that all values will be processed, in case of CPU core division give rest, the last step compensate + * the difference. + * See Blur filter loop implementation for example to see how to use this method with QtConcurrents API. */ - QList multithreadedSteps(int stop, int start=0) const; + QList multithreadedSteps(int stop, int start=0) const; - /** Start the threaded computation. + /** + * Start the threaded computation. */ virtual void startFilter(); - /** Cancel the threaded computation. + /** + * Cancel the threaded computation. */ virtual void cancelFilter(); - /** Start computation of this filter, directly in this thread. + /** + * Start computation of this filter, directly in this thread. */ virtual void startFilterDirectly(); - /** Returns the action description corresponding to currently set options. + /** + * Returns the action description corresponding to currently set options. */ - virtual FilterAction filterAction() = 0; + virtual FilterAction filterAction() = 0; - virtual void readParameters(const FilterAction&) = 0; + virtual void readParameters(const FilterAction&) = 0; - /** Return the identifier for this filter in the image history. + /** + * Return the identifier for this filter in the image history. */ - virtual QString filterIdentifier() const = 0; + virtual QString filterIdentifier() const = 0; - virtual QList supportedVersions() const; + virtual QList supportedVersions() const; /** * Replaying a filter action: * Set the filter version. A filter may implement different versions, to preserve * image history when the algorithm is changed. * Any value set here must be contained in supportedVersions, otherwise * this call will be ignored. Default value is 1. * (Note: If you intend to _record_ a filter action, please look at FilterAction's m_version) */ void setFilterVersion(int version); - int filterVersion() const; + int filterVersion() const; /** * Optional: error handling for readParameters. * When readParameters() has been called, this method will return true * if the call was successful, and false if not. * If returning false, readParametersError() will give an error message. * The default implementation always returns success. You only need to reimplement * when a filter is likely to fail in a different environment, e.g. * depending on availability of installed files. * These methods have an undefined return value if readParameters() was not called * previously. */ - virtual bool parametersSuccessfullyRead() const; - virtual QString readParametersError(const FilterAction& actionThatFailed) const; + virtual bool parametersSuccessfullyRead() const; + virtual QString readParametersError(const FilterAction& actionThatFailed) const; Q_SIGNALS: - /** This signal is emitted when image data is available and the computation has started. + /** + * This signal is emitted when image data is available and the computation has started. */ void started(); - /** Emitted when progress info from the calculation is available. + /** + * Emitted when progress info from the calculation is available. */ void progress(int progress); - /** Emitted when the computation has completed. - @param success True if computation finished without interruption on valid data - False if the thread was canceled, or no data is available. - */ + /** + * Emitted when the computation has completed. + * @param success True if computation finished without interruption on valid data + * False if the thread was canceled, or no data is available. + */ void finished(bool success); protected: - /** Start filter operation before threaded method. Must be called by your constructor. + /** + * Start filter operation before threaded method. Must be called by your constructor. */ virtual void initFilter(); - /** List of threaded operations by filter. + /** + * List of threaded operations by filter. */ virtual void run() override; - /** Main image filter method. Override in subclass. + /** + * Main image filter method. Override in subclass. */ virtual void filterImage() = 0; - /** Clean up filter data if necessary, called by stopComputation() method. - Override in subclass. + /** + * Clean up filter data if necessary, called by stopComputation() method. + * Override in subclass. */ virtual void cleanupFilter() {}; - /** Emit progress info. + /** + * Emit progress info. */ void postProgress(int progress); protected: /** - Support for chaining two filters as master and thread. - - Do not call startFilter() or startFilterDirectly() on this. - The computation will be started from initFilter() which you must - call from the derived class constructor. - - Constructor for slave mode: - Constructs a new slave filter with the specified master. - The filter will be executed in the current thread. - orgImage and destImage will not be copied. - Note that the slave is still free to reallocate his destImage. - progressBegin and progressEnd can indicate the progress span - that the slave filter uses in the parent filter's progress. - Any derived filter class that is publicly available to other filters - should implement an additional constructor using this constructor. + * Support for chaining two filters as master and thread. + * + * Do not call startFilter() or startFilterDirectly() on this. + * The computation will be started from initFilter() which you must + * call from the derived class constructor. + * + * Constructor for slave mode: + * Constructs a new slave filter with the specified master. + * The filter will be executed in the current thread. + * orgImage and destImage will not be copied. + * Note that the slave is still free to reallocate his destImage. + * progressBegin and progressEnd can indicate the progress span + * that the slave filter uses in the parent filter's progress. + * Any derived filter class that is publicly available to other filters + * should implement an additional constructor using this constructor. */ DImgThreadedFilter(DImgThreadedFilter* const master, const DImg& orgImage, const DImg& destImage, int progressBegin=0, int progressEnd=100, const QString& name=QString()); - /** Initialize the filter for use as a slave - reroutes progress info to master. - * Note: Computation will be started from setupFilter(). + /** + * Initialize the filter for use as a slave - reroutes progress info to master. + * Note: Computation will be started from setupFilter(). */ void initSlave(DImgThreadedFilter* const master, int progressBegin = 0, int progressEnd = 100); - /** Inform the master that there is currently a slave. At destruction of the slave, call with slave=0. + /** + * Inform the master that there is currently a slave. At destruction of the slave, call with slave=0. */ void setSlave(DImgThreadedFilter* const slave); - /** This method modulates the progress value from the 0..100 span to the span of this slave. - * Called by postProgress if master is not null. + /** + * This method modulates the progress value from the 0..100 span to the span of this slave. + * Called by postProgress if master is not null. */ virtual int modulateProgress(int progress); void initMaster(); virtual void prepareDestImage(); /** * Convenience class to spare the few repeating lines of code */ template class DefaultFilterAction : public FilterAction { public: explicit DefaultFilterAction(FilterAction::Category category = FilterAction::ReproducibleFilter) : FilterAction(Filter::FilterIdentifier(), Filter::CurrentVersion(), category) { setDisplayableName(Filter::DisplayableName()); } explicit DefaultFilterAction(bool isReproducible) : FilterAction(Filter::FilterIdentifier(), Filter::CurrentVersion(), isReproducible ? FilterAction::ReproducibleFilter : FilterAction::ComplexFilter) { setDisplayableName(Filter::DisplayableName()); } /** * Preserve backwards compatibility: * If a given condition (some new feature is not used) is true, * decrease the version so that older digikam versions can still replay the action */ void supportOlderVersionIf(int version, bool condition) { if (condition && version <= m_version) { m_version = version; } } }; protected: int m_version; bool m_wasCancelled; - /** The progress span that a slave filter uses in the parent filter's progress. + /** + * The progress span that a slave filter uses in the parent filter's progress. */ int m_progressBegin; int m_progressSpan; - int m_progressCurrent; // To prevent signals bombarding with progress indicator value in postProgress(). + int m_progressCurrent; ///< To prevent signals bombarding with progress indicator value in postProgress(). - /** Filter name. + /** + * Filter name. */ QString m_name; - /** Copy of original Image data. + /** + * Copy of original Image data. */ DImg m_orgImage; - /** Output image data. + /** + * Output image data. */ DImg m_destImage; - /** The current slave. Any filter might want to use another filter while processing. + /** + * The current slave. Any filter might want to use another filter while processing. */ DImgThreadedFilter* m_slave; - /** The master of this slave filter. Progress info will be routed to this one. + /** + * The master of this slave filter. Progress info will be routed to this one. */ DImgThreadedFilter* m_master; }; } // namespace Digikam #endif // DIGIKAM_DIMG_THREADED_FILTER_H diff --git a/core/libs/dimg/filters/dpixelsaliasfilter.cpp b/core/libs/dimg/filters/dpixelsaliasfilter.cpp index 2d6634878d..f528385440 100644 --- a/core/libs/dimg/filters/dpixelsaliasfilter.cpp +++ b/core/libs/dimg/filters/dpixelsaliasfilter.cpp @@ -1,188 +1,188 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-24-01 * Description : pixels antialiasing filter * * Copyright (C) 2005-2020 by Gilles Caulier - * Copyright (C) 2010 by Martin Klapetek + * Copyright (C) 2010 by Martin Klapetek * * Original channel mixer algorithm copyrighted 2002 by * Martin Guldahl from Gimp 2.2 * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dpixelsaliasfilter.h" // C++ includes #include #include // Local includes #include "imagehistogram.h" #include "dcolor.h" namespace Digikam { DPixelsAliasFilter::DPixelsAliasFilter() { } DPixelsAliasFilter::~DPixelsAliasFilter() { } /** * Function to perform pixel antialiasing with 8 bits/color/pixel images. This method is used to smooth target * image in transformation method like free rotation or shear tool. */ void DPixelsAliasFilter::pixelAntiAliasing(uchar* data, int Width, int Height, double X, double Y, uchar* A, uchar* R, uchar* G, uchar* B) { int nX, nY, j; double lfWeightX[2], lfWeightY[2], lfWeight; double lfTotalR = 0.0, lfTotalG = 0.0, lfTotalB = 0.0, lfTotalA = 0.0; nX = (int)X; nY = (int)Y; if (Y >= 0.0) { lfWeightY[0] = 1.0 - (lfWeightY[1] = Y - (double)nY); } else { lfWeightY[1] = 1.0 - (lfWeightY[0] = -(Y - (double)nY)); } if (X >= 0.0) { lfWeightX[0] = 1.0 - (lfWeightX[1] = X - (double)nX); } else { lfWeightX[1] = 1.0 - (lfWeightX[0] = -(X - (double)nX)); } for (int loopx = 0 ; loopx <= 1 ; ++loopx) { for (int loopy = 0 ; loopy <= 1 ; ++loopy) { lfWeight = lfWeightX[loopx] * lfWeightY[loopy]; j = setPositionAdjusted(Width, Height, nX + loopx, nY + loopy); lfTotalB += ((double)data[j] * lfWeight); ++j; lfTotalG += ((double)data[j] * lfWeight); ++j; lfTotalR += ((double)data[j] * lfWeight); ++j; lfTotalA += ((double)data[j] * lfWeight); ++j; } } *B = CLAMP0255((int)lfTotalB); *G = CLAMP0255((int)lfTotalG); *R = CLAMP0255((int)lfTotalR); *A = CLAMP0255((int)lfTotalA); } /** * Function to perform pixel antialiasing with 16 bits/color/pixel images. This method is used to smooth target * image in transformation method like free rotation or shear tool. */ void DPixelsAliasFilter::pixelAntiAliasing16(unsigned short* data, int Width, int Height, double X, double Y, unsigned short* A, unsigned short* R, unsigned short* G, unsigned short* B) { int nX, nY, j; double lfWeightX[2], lfWeightY[2], lfWeight; double lfTotalR = 0.0, lfTotalG = 0.0, lfTotalB = 0.0, lfTotalA = 0.0; nX = (int)X; nY = (int)Y; if (Y >= 0.0) { lfWeightY[0] = 1.0 - (lfWeightY[1] = Y - (double)nY); } else { lfWeightY[1] = 1.0 - (lfWeightY[0] = -(Y - (double)nY)); } if (X >= 0.0) { lfWeightX[0] = 1.0 - (lfWeightX[1] = X - (double)nX); } else { lfWeightX[1] = 1.0 - (lfWeightX[0] = -(X - (double)nX)); } for (int loopx = 0 ; loopx <= 1 ; ++loopx) { for (int loopy = 0 ; loopy <= 1 ; ++loopy) { lfWeight = lfWeightX[loopx] * lfWeightY[loopy]; j = setPositionAdjusted(Width, Height, nX + loopx, nY + loopy); lfTotalB += ((double)data[j] * lfWeight); ++j; lfTotalG += ((double)data[j] * lfWeight); ++j; lfTotalR += ((double)data[j] * lfWeight); ++j; lfTotalA += ((double)data[j] * lfWeight); ++j; } } *B = CLAMP065535((int)lfTotalB); *G = CLAMP065535((int)lfTotalG); *R = CLAMP065535((int)lfTotalR); *A = CLAMP065535((int)lfTotalA); } inline int DPixelsAliasFilter::setPositionAdjusted(int Width, - int Height, - int X, - int Y) + int Height, + int X, + int Y) { X = (X < 0) ? 0 : (X >= Width) ? Width - 1 : X; Y = (Y < 0) ? 0 : (Y >= Height) ? Height - 1 : Y; - + return (Y * Width * 4 + 4 * X); } } // namespace Digikam diff --git a/core/libs/dimg/filters/filteractionfilter.cpp b/core/libs/dimg/filters/filteractionfilter.cpp index 80f398e621..d34a7a7ad5 100644 --- a/core/libs/dimg/filters/filteractionfilter.cpp +++ b/core/libs/dimg/filters/filteractionfilter.cpp @@ -1,265 +1,267 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-11-10 * Description : meta-filter to apply FilterAction * * Copyright (C) 2010-2011 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "filteractionfilter.h" // Qt includes #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "digikam_export.h" #include "dimgbuiltinfilter.h" #include "dimgfiltermanager.h" #include "filteraction.h" namespace Digikam { class Q_DECL_HIDDEN FilterActionFilter::Private { public: explicit Private() + : continueOnError(false) { - continueOnError = false; } bool continueOnError; QList actions; QList appliedActions; QString errorMessage; }; FilterActionFilter::FilterActionFilter(QObject* const parent) : DImgThreadedFilter(parent), d(new Private) { initFilter(); } FilterActionFilter::~FilterActionFilter() { delete d; } void FilterActionFilter::setContinueOnError(bool cont) { d->continueOnError = cont; } void FilterActionFilter::setFilterActions(const QList& actions) { d->actions = actions; } void FilterActionFilter::addFilterActions(const QList& actions) { d->actions += actions; } void FilterActionFilter::setFilterAction(const FilterAction& action) { d->actions.clear(); d->actions << action; } void FilterActionFilter::addFilterAction(const FilterAction& action) { d->actions << action; } QList FilterActionFilter::filterActions() const { return d->actions; } bool FilterActionFilter::isReproducible() const { foreach (const FilterAction& action, d->actions) { - if (!action.isNull() && action.category() != FilterAction::ReproducibleFilter) + if (!action.isNull() && + (action.category() != FilterAction::ReproducibleFilter)) { return false; } } return true; } bool FilterActionFilter::isComplexAction() const { foreach (const FilterAction& action, d->actions) { - if (!action.isNull() && action.category() != FilterAction::ReproducibleFilter && - action.category() != FilterAction::ComplexFilter) + if (!action.isNull() && + (action.category() != FilterAction::ReproducibleFilter) && + (action.category() != FilterAction::ComplexFilter)) { return false; } } return true; } bool FilterActionFilter::isSupported() const { foreach (const FilterAction& action, d->actions) { if (!action.isNull() && !DImgFilterManager::instance()->isSupported(action.identifier(), action.version())) { return false; } } return true; } bool FilterActionFilter::completelyApplied() const { - return d->appliedActions.size() == d->actions.size(); + return (d->appliedActions.size() == d->actions.size()); } QList FilterActionFilter::appliedFilterActions() const { return d->appliedActions; } FilterAction FilterActionFilter::failedAction() const { if (d->appliedActions.size() >= d->actions.size()) { return FilterAction(); } return d->actions.at(d->appliedActions.size()); } int FilterActionFilter::failedActionIndex() const { return d->appliedActions.size(); } QString FilterActionFilter::failedActionMessage() const { return d->errorMessage; } void FilterActionFilter::filterImage() { d->appliedActions.clear(); d->errorMessage.clear(); const float progressIncrement = 1.0 / qMax(1, d->actions.size()); float progress = 0; postProgress(0); DImg img = m_orgImage; foreach (const FilterAction& action, d->actions) { qCDebug(DIGIKAM_DIMG_LOG) << "Replaying action" << action.identifier(); if (action.isNull()) { continue; } if (DImgBuiltinFilter::isSupported(action.identifier())) { DImgBuiltinFilter filter(action); if (!filter.isValid()) { d->errorMessage = i18n("Built-in transformation not supported"); if (d->continueOnError) { continue; } else { break; } } filter.apply(img); d->appliedActions << filter.filterAction(); } else { QScopedPointer filter (DImgFilterManager::instance()->createFilter(action.identifier(), action.version())); if (!filter) { d->errorMessage = i18n("Filter identifier or version is not supported"); if (d->continueOnError) { continue; } else { break; } } filter->readParameters(action); if (!filter->parametersSuccessfullyRead()) { d->errorMessage = filter->readParametersError(action); if (d->continueOnError) { continue; } else { break; } } // compute filter->setupAndStartDirectly(img, this, (int)progress, (int)(progress + progressIncrement)); img = filter->getTargetImage(); d->appliedActions << filter->filterAction(); } progress += progressIncrement; postProgress((int)progress); } m_destImage = img; } } // namespace Digikam diff --git a/core/libs/dimg/filters/filteractionfilter.h b/core/libs/dimg/filters/filteractionfilter.h index 8e3394f1ed..762c146d01 100644 --- a/core/libs/dimg/filters/filteractionfilter.h +++ b/core/libs/dimg/filters/filteractionfilter.h @@ -1,137 +1,137 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-11-10 * Description : meta-filter to apply FilterActions * * Copyright (C) 2010-2011 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_FILTER_ACTION_FILTER_H #define DIGIKAM_FILTER_ACTION_FILTER_H // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "filteraction.h" namespace Digikam { class DIGIKAM_EXPORT FilterActionFilter : public DImgThreadedFilter { public: /** * A meta-filter applying other filter according to a list of FilterActions */ explicit FilterActionFilter(QObject* const parent = nullptr); ~FilterActionFilter(); /** * Per default, the filter will stop when it encounters an unsupported action. * If you want it to continue, set this to true. * Only the last occurred error will then be reported. */ void setContinueOnError(bool cont); /** * Set - or add to existing list - the given filter actions */ void setFilterActions(const QList& actions); void addFilterActions(const QList& actions); void setFilterAction(const FilterAction& action); void addFilterAction(const FilterAction& action); - QList filterActions() const; + QList filterActions() const; /** * Returns the list of applied filter actions. This is probably identical * to filterActions, but it can differ in some situations: * - if completelyApplied() is false, it will contain only the successful actions * - the list is regenerated by the filters. If filterActions contains * actions with an older version, still supported by the filter, * the filter will now possibly return the newer, current version */ - QList appliedFilterActions() const; + QList appliedFilterActions() const; /** * Returns true if all FilterActions are reproducible */ - bool isReproducible() const; + bool isReproducible() const; /** * Returns true if all FilterActions are reproducible * or are ComplexFilters. That means the identical result * may not be reproducible, but a sufficiently similar result * may be available and apply will probably complete. */ - bool isComplexAction() const; + bool isComplexAction() const; /** * Returns true if all actions are supported. */ - bool isSupported() const; + bool isSupported() const; /** * After the thread was run, you can find out if application was successful. * A precondition is that at least isComplexAction() and isSupported() returns true. * If all filters applied cleanly, completelyApplied() returns true. * appliedActions() returns all applied actions, if completelyApplied(), * the same as filterActions(). * If not completelyApplied, failedAction() returns the action that failed, * failedActionIndex its index in filterActions(), and failedActionMessage * an optional error message. * Note that finished(true) does not mean that completelyApplied() is also true. */ - bool completelyApplied() const; - FilterAction failedAction() const; - int failedActionIndex() const; - QString failedActionMessage() const; + bool completelyApplied() const; + FilterAction failedAction() const; + int failedActionIndex() const; + QString failedActionMessage() const; /** * These methods do not make sense here. Use filterActions. */ - virtual FilterAction filterAction() override + virtual FilterAction filterAction() override { return FilterAction(); } - virtual void readParameters(const FilterAction&) override + virtual void readParameters(const FilterAction&) override { } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return QString(); } protected: - virtual void filterImage() override; + virtual void filterImage() override; private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_FILTER_ACTION_FILTER_H diff --git a/core/libs/dimg/filters/randomnumbergenerator.cpp b/core/libs/dimg/filters/randomnumbergenerator.cpp index dc8873eff5..cc03202592 100644 --- a/core/libs/dimg/filters/randomnumbergenerator.cpp +++ b/core/libs/dimg/filters/randomnumbergenerator.cpp @@ -1,242 +1,246 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-11-03 * Description : Generating random numbers * * Copyright (C) 2010-2011 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "randomnumbergenerator.h" // Boost includes // Pragma directives to reduce warnings from Boost header files. #if !defined(Q_OS_DARWIN) && defined(Q_CC_GNU) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wundef" #endif #if defined(Q_OS_DARWIN) && defined(Q_CC_CLANG) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wundef" # pragma clang diagnostic ignored "-Wunnamed-type-template-args" #endif #include #include #include #include #include // Restore warnings #if !defined(Q_OS_DARWIN) && defined(Q_CC_GNU) # pragma GCC diagnostic pop #endif #if defined(Q_OS_DARWIN) && defined(Q_CC_CLANG) # pragma clang diagnostic pop #endif // Qt includes #include #include #include // Local includes #include "digikam_export.h" namespace Digikam { NonDeterministicRandomData::NonDeterministicRandomData(int s) { #ifndef Q_OS_WIN { // Try urandom for UNIX platforms. QFile urandom(QLatin1String("/dev/urandom")); if (urandom.exists() && urandom.open(QIODevice::ReadOnly)) { resize(s); if (urandom.read(data(), s) == s) { urandom.close(); return; } urandom.close(); } } #endif /* * Fallback, mostly for Windows, where UUID generation * is supposed to be very good. */ if (isEmpty()) { reserve(s); while (size() < s) { append(QByteArray::fromHex(QUuid::createUuid() .toString() .remove(QLatin1Char('{')) .remove(QLatin1Char('}')) .remove(QLatin1Char('-')) .toLatin1()) ); } resize(s); } #if 0 /** * Implementation with boost::random_device. * Works on Windows only starting with boost 1.43, * before, only urandom is supported, but for that, * we have a easy code snippet already. */ const int stepSize = sizeof(boost::random_device::result_type); int steps = s / stepSize; if (s % stepSize) { ++steps; } resize(steps * stepSize); boost::random_device device; boost::random_device::result_type* ptr = reinterpret_cast(data()); for (int i = 0 ; i < stepSize ; ++i) { *ptr++ = device(); } resize(s); #endif } // --------------------------------------------------------------------------------- class Q_DECL_HIDDEN RandomNumberGenerator::Private { public: enum { // guaranteed constant initial seed, do not change InitialSeed = 5489 }; public: explicit Private() : seed(InitialSeed), engine(InitialSeed) { } quint32 seed; boost::mt19937 engine; }; RandomNumberGenerator::RandomNumberGenerator() : d(new Private) { } RandomNumberGenerator::~RandomNumberGenerator() { delete d; } quint32 RandomNumberGenerator::nonDeterministicSeed() { NonDeterministicRandomData seed(sizeof(quint32)); + return *reinterpret_cast(seed.data()); } quint32 RandomNumberGenerator::timeSeed() { uint seed; seed = quintptr(&seed) + QDateTime::currentDateTime().toTime_t(); + return seed; } quint32 RandomNumberGenerator::seedNonDeterministic() { d->seed = nonDeterministicSeed(); d->engine.seed(d->seed); + return d->seed; } quint32 RandomNumberGenerator::seedByTime() { d->seed = timeSeed(); d->engine.seed(d->seed); + return d->seed; } void RandomNumberGenerator::seed(quint32 seed) { d->seed = seed; d->engine.seed(seed); } void RandomNumberGenerator::reseed() { seed(d->seed); } quint32 RandomNumberGenerator::currentSeed() const { return d->seed; } int RandomNumberGenerator::number(int min, int max) { boost::uniform_smallint<> distribution(min, max); boost::variate_generator > generator(d->engine, distribution); return generator(); } double RandomNumberGenerator::number(double min, double max) { boost::uniform_real<> distribution(min, max); boost::variate_generator > generator(d->engine, distribution); return generator(); } bool RandomNumberGenerator::yesOrNo(double p) { boost::bernoulli_distribution<> distribution(p); boost::variate_generator > generator(d->engine, distribution); return generator(); } } // namespace Digikam diff --git a/core/libs/dimg/drawdecoding.cpp b/core/libs/dimg/filters/raw/drawdecoding.cpp similarity index 100% rename from core/libs/dimg/drawdecoding.cpp rename to core/libs/dimg/filters/raw/drawdecoding.cpp diff --git a/core/libs/dimg/drawdecoding.h b/core/libs/dimg/filters/raw/drawdecoding.h similarity index 100% rename from core/libs/dimg/drawdecoding.h rename to core/libs/dimg/filters/raw/drawdecoding.h diff --git a/core/libs/dimg/filters/rawprocessingfilter.cpp b/core/libs/dimg/filters/raw/rawprocessingfilter.cpp similarity index 99% rename from core/libs/dimg/filters/rawprocessingfilter.cpp rename to core/libs/dimg/filters/raw/rawprocessingfilter.cpp index f5a6bb05db..364d53c51d 100644 --- a/core/libs/dimg/filters/rawprocessingfilter.cpp +++ b/core/libs/dimg/filters/raw/rawprocessingfilter.cpp @@ -1,176 +1,178 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-12-14 * Description : Filter to manage and help with raw loading * * Copyright (C) 2005-2020 by Gilles Caulier * Copyright (C) 2005-2012 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "rawprocessingfilter.h" // KDE includes #include // Local includes #include "drawdecoder.h" #include "bcgfilter.h" #include "curvesfilter.h" #include "digikam_export.h" #include "dimgloaderobserver.h" #include "filteraction.h" #include "icctransform.h" #include "wbfilter.h" namespace Digikam { RawProcessingFilter::RawProcessingFilter(QObject* const parent) : DImgThreadedFilter(parent), m_observer(nullptr) { } RawProcessingFilter::RawProcessingFilter(DImg* const orgImage, QObject* const parent, const DRawDecoding& settings, const QString& name) : DImgThreadedFilter(orgImage, parent, name), m_observer(nullptr) { setSettings(settings); initFilter(); } RawProcessingFilter::RawProcessingFilter(const DRawDecoding& settings, DImgThreadedFilter* const master, const DImg& orgImage, const DImg& destImage, int progressBegin, int progressEnd, const QString& name) : DImgThreadedFilter(master, orgImage, destImage, progressBegin, progressEnd, name), m_observer(nullptr) { setSettings(settings); // NOTE: use dynamic binding as this virtual method can be re-implemented in derived classes. this->filterImage(); } RawProcessingFilter::~RawProcessingFilter() { cancelFilter(); } QString RawProcessingFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Raw Conversion")); } void RawProcessingFilter::setSettings(const DRawDecoding& settings) { m_settings = settings; } DRawDecoding RawProcessingFilter::settings() const { return m_settings; } void RawProcessingFilter::setOutputProfile(const IccProfile& profile) { m_customOutputProfile = profile; } void RawProcessingFilter::setObserver(DImgLoaderObserver* const observer, int progressBegin, int progressEnd) { initSlave(nullptr, progressBegin, progressEnd); m_observer = observer; } FilterAction RawProcessingFilter::filterAction() { DefaultFilterAction action; m_settings.writeToFilterAction(action); return std::move(action); } void RawProcessingFilter::readParameters(const FilterAction& action) { m_settings = DRawDecoding::fromFilterAction(action); } void RawProcessingFilter::postProgress(int) { DImgThreadedFilter::postProgress(20); if (m_observer) { m_observer->progressInfo(float(modulateProgress(20)) / 100); } } bool RawProcessingFilter::continueQuery() const { if (m_observer && !m_observer->continueQuery()) { return false; } return runningFlag(); } void RawProcessingFilter::filterImage() { m_destImage = m_orgImage; // emulate LibRaw custom output profile + if (!m_customOutputProfile.isNull()) { // Note the m_destImage is not yet ready in load()! + IccTransform trans; trans.setIntent(IccTransform::Perceptual); trans.setEmbeddedProfile(m_destImage); trans.setOutputProfile(m_customOutputProfile); trans.apply(m_orgImage, m_observer); m_destImage.setIccProfile(m_customOutputProfile); } postProgress(20); if (!m_settings.wb.isDefault()) { WBFilter wb(m_settings.wb, this, m_orgImage, m_destImage, 20, 40); } postProgress(40); if (!m_settings.bcg.isDefault()) { BCGFilter bcg(m_settings.bcg, this, m_orgImage, m_destImage, 40, 70); } postProgress(70); if (!m_settings.curvesAdjust.isEmpty()) { CurvesFilter curves(m_settings.curvesAdjust, this, m_orgImage, m_destImage, 70, 100); } postProgress(100); } } // namespace Digikam diff --git a/core/libs/dimg/filters/rawprocessingfilter.h b/core/libs/dimg/filters/raw/rawprocessingfilter.h similarity index 92% rename from core/libs/dimg/filters/rawprocessingfilter.h rename to core/libs/dimg/filters/raw/rawprocessingfilter.h index cdb6a57671..938da285c9 100644 --- a/core/libs/dimg/filters/rawprocessingfilter.h +++ b/core/libs/dimg/filters/raw/rawprocessingfilter.h @@ -1,140 +1,140 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-12-14 * Description : Filter to manage and help with raw loading * * Copyright (C) 2010-2011 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_RAW_PROCESSING_FILTERS_H #define DIGIKAM_RAW_PROCESSING_FILTERS_H // Local includes #include "digikam_export.h" #include "dimgthreadedfilter.h" #include "drawdecoding.h" #include "iccprofile.h" namespace Digikam { class DImgLoaderObserver; class FilterAction; /** * This is a special filter. * It implements RAW post processing. * Additionally, it provides some facilities for use from the DImg Raw loader. * * The original image shall come from RawEngine without further modification. */ class DIGIKAM_EXPORT RawProcessingFilter : public DImgThreadedFilter { public: /** * Default constructor. You need to call setSettings() and setOriginalImage() * before starting the filter. */ explicit RawProcessingFilter(QObject* const parent = nullptr); /** * Traditional constructor */ RawProcessingFilter(DImg* const orgImage, QObject* const parent, const DRawDecoding& settings, const QString& name = QString()); /** * For use with a master filter. Computation is started immediately. */ RawProcessingFilter(const DRawDecoding& settings, DImgThreadedFilter* const master, const DImg& orgImage, const DImg& destImage, int progressBegin=0, int progressEnd=100, const QString& name=QString()); ~RawProcessingFilter(); /** * Set the raw decoding settings. The post processing is carried out here, * the libraw settings are needed to construct the FilterAction. */ void setSettings(const DRawDecoding& settings); - DRawDecoding settings() const; + DRawDecoding settings() const; /** * As additional and first post-processing step, convert the image's * color space to the specified profile. */ void setOutputProfile(const IccProfile& profile); /** * Normally, filters post progress and are cancelled by DynamicThread facilities. * Here, as an alternative, a DImgLoaderObserver is set. It's continueQuery is called * and progress is posted in the given interval. */ void setObserver(DImgLoaderObserver* const observer, int progressBegin, int progressEnd); static QString FilterIdentifier() { return QLatin1String("digikam:RawConverter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } void readParameters(const FilterAction& action) override; - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } virtual FilterAction filterAction() override; protected: - void postProgress(int); // not virtual - bool continueQuery() const; // not virtual + void postProgress(int); // not virtual + bool continueQuery() const; // not virtual virtual void filterImage() override; protected: DRawDecoding m_settings; IccProfile m_customOutputProfile; DImgLoaderObserver* m_observer; }; } // namespace Digikam #endif // DIGIKAM_RAW_PROCESSING_FILTERS_H diff --git a/core/libs/dimg/history/dimagehistory.cpp b/core/libs/dimg/history/dimagehistory.cpp index da2634ad3f..f49586c1b3 100644 --- a/core/libs/dimg/history/dimagehistory.cpp +++ b/core/libs/dimg/history/dimagehistory.cpp @@ -1,796 +1,801 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-06-02 * Description : class for manipulating modifications changeset for non-destruct. editing * * Copyright (C) 2010 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dimagehistory.h" // Qt includes #include #include #include #include #include #include // Local includes #include "digikam_debug.h" namespace Digikam { class Q_DECL_HIDDEN DImageHistory::Private : public QSharedData { public: explicit Private() { } QList entries; }; // ----------------------------------------------------------------------------------------------- class Q_DECL_HIDDEN PrivateSharedNull : public QSharedDataPointer { public: PrivateSharedNull() : QSharedDataPointer(new DImageHistory::Private) { } }; Q_GLOBAL_STATIC(PrivateSharedNull, imageHistoryPrivSharedNull) // ----------------------------------------------------------------------------------------------- DImageHistory::DImageHistory() : d(*imageHistoryPrivSharedNull) { } DImageHistory::DImageHistory(const DImageHistory& other) { d = other.d; } DImageHistory::~DImageHistory() { } DImageHistory& DImageHistory::operator=(const DImageHistory& other) { d = other.d; + return *this; } bool DImageHistory::isNull() const { - return d == *imageHistoryPrivSharedNull; + return (d == *imageHistoryPrivSharedNull); } bool DImageHistory::isEmpty() const { return d->entries.isEmpty(); } bool DImageHistory::isValid() const { if (d->entries.isEmpty()) { return false; } - else if (d->entries.count() == 1 && - d->entries.first().referredImages.count() == 1 && + else if ((d->entries.count() == 1) && + (d->entries.first().referredImages.count() == 1) && d->entries.first().referredImages.first().isCurrentFile()) { return false; } else { foreach (const Entry& e, d->entries) { if (!e.action.isNull()) { return true; } foreach (const HistoryImageId& id, e.referredImages) { if (id.isValid() && !id.isCurrentFile()) { return true; } } } } return false; } int DImageHistory::size() const { return d->entries.size(); } static bool operator==(const DImageHistory::Entry& e1, const DImageHistory::Entry& e2) { - return e1.action == e2.action && - e1.referredImages == e2.referredImages; + return ((e1.action == e2.action) && + (e1.referredImages == e2.referredImages)); } bool DImageHistory::operator==(const DImageHistory& other) const { - return d->entries == other.d->entries; + return (d->entries == other.d->entries); } bool DImageHistory::operator<(const Digikam::DImageHistory& other) const { if (d->entries.size() < other.size()) { return true; } return false; } bool DImageHistory::operator>(const Digikam::DImageHistory& other) const { if (d->entries.size() > other.size()) { return true; } return false; } QList &DImageHistory::entries() { return d->entries; } const QList &DImageHistory::entries() const { return d->entries; } DImageHistory::Entry& DImageHistory::operator[](int i) { return d->entries[i]; } const DImageHistory::Entry& DImageHistory::operator[](int i) const { return d->entries.at(i); } DImageHistory& DImageHistory::operator<<(const FilterAction& action) { if (action.isNull()) { return *this; } Entry entry; entry.action = action; d->entries << entry; //qCDebug(DIGIKAM_DIMG_LOG) << "Entry added, total count " << d->entries.count(); + return *this; } DImageHistory& DImageHistory::operator<<(const HistoryImageId& id) { appendReferredImage(id); + return *this; } void DImageHistory::appendReferredImage(const HistoryImageId& id) { insertReferredImage(d->entries.size() - 1, id); } void DImageHistory::insertReferredImage(int index, const HistoryImageId& id) { if (!id.isValid()) { qCWarning(DIGIKAM_DIMG_LOG) << "Attempt to add an invalid HistoryImageId"; return; } index = qBound(0, index, d->entries.size() - 1); if (id.isCurrentFile()) { // enforce to have exactly one Current id adjustReferredImages(); } if (d->entries.isEmpty()) { d->entries << Entry(); } d->entries[index].referredImages << id; } void DImageHistory::removeLast() { if (!d->entries.isEmpty()) { d->entries.removeLast(); } } const FilterAction& DImageHistory::action(int i) const { return d->entries.at(i).action; } QList DImageHistory::allActions() const { QList actions; foreach (const Entry& entry, d->entries) { if (!entry.action.isNull()) { actions << entry.action; } } return actions; } int DImageHistory::actionCount() const { int count = 0; foreach (const Entry& entry, d->entries) { if (!entry.action.isNull()) { ++count; } } return count; } bool DImageHistory::hasActions() const { foreach (const Entry& entry, d->entries) { if (!entry.action.isNull()) { return true; } } return false; } QList &DImageHistory::referredImages(int i) { return d->entries[i].referredImages; } const QList &DImageHistory::referredImages(int i) const { return d->entries.at(i).referredImages; } QList DImageHistory::allReferredImages() const { QList ids; foreach (const Entry& entry, d->entries) { ids << entry.referredImages; } return ids; } bool DImageHistory::hasReferredImages() const { foreach (const Entry& entry, d->entries) { if (!entry.referredImages.isEmpty()) { return true; } } return false; } bool DImageHistory::hasReferredImageOfType(HistoryImageId::Type type) const { foreach (const Entry& entry, d->entries) { foreach (const HistoryImageId& id, entry.referredImages) { if (id.m_type == type) { return true; } } } return false; } bool DImageHistory::hasCurrentReferredImage() const { return hasReferredImageOfType(HistoryImageId::Current); } bool DImageHistory::hasOriginalReferredImage() const { return hasReferredImageOfType(HistoryImageId::Original); } QList DImageHistory::referredImagesOfType(HistoryImageId::Type type) const { QList ids; foreach (const Entry& entry, d->entries) { foreach (const HistoryImageId& id, entry.referredImages) { if (id.m_type == type) { ids << id; } } } return ids; } HistoryImageId DImageHistory::currentReferredImage() const { foreach (const Entry& entry, d->entries) { foreach (const HistoryImageId& id, entry.referredImages) { if (id.isCurrentFile()) { return id; } } } return HistoryImageId(); } HistoryImageId DImageHistory::originalReferredImage() const { foreach (const Entry& entry, d->entries) { foreach (const HistoryImageId& id, entry.referredImages) { if (id.isOriginalFile()) { return id; } } } return HistoryImageId(); } void DImageHistory::clearReferredImages() { - for (int i=0; ientries.size(); ++i) + for (int i = 0 ; i < d->entries.size() ; ++i) { d->entries[i].referredImages.clear(); } } void DImageHistory::adjustReferredImages() { - for (int i=0; ientries.size(); ++i) + for (int i = 0 ; i < d->entries.size() ; ++i) { Entry& entry = d->entries[i]; - for (int e=0; eentries.size(); ++i) + for (int i = 0 ; i < d->entries.size() ; ++i) { Entry& entry = d->entries[i]; - for (int e=0; eentries.size(); ++i) + for (int i = 0 ; i < d->entries.size() ; ++i) { Entry& entry = d->entries[i]; - for (int e=0; eentries.size(); ++i) + for (int i = 0 ; i < d->entries.size() ; ++i) { Entry& entry = d->entries[i]; - for (int e=0; e& params = step.action.parameters(); if (!params.isEmpty()) { QList keys = params.keys(); std::sort(keys.begin(), keys.end()); foreach (const QString& key, keys) { QHash::const_iterator it; - for (it = params.find(key); it != params.end() && it.key() == key; ++it) + for (it = params.find(key) ; it != params.end() && it.key() == key ; ++it) { stream.writeStartElement(QLatin1String("param")); stream.writeAttribute(QLatin1String("name"), key); stream.writeAttribute(QLatin1String("value"), it.value().toString()); stream.writeEndElement(); //param } } } stream.writeEndElement(); //params stream.writeEndElement(); //filter } if (!step.referredImages.isEmpty()) { foreach (const HistoryImageId& imageId, step.referredImages) { if (!imageId.isValid()) { continue; } if (imageId.isCurrentFile()) { continue; } stream.writeStartElement(QLatin1String("file")); if (!imageId.m_uuid.isNull()) { stream.writeAttribute(QLatin1String("uuid"), imageId.m_uuid); } if (imageId.isOriginalFile()) { stream.writeAttribute(QLatin1String("type"), QLatin1String("original")); } else if (imageId.isSourceFile()) { stream.writeAttribute(QLatin1String("type"), QLatin1String("source")); } stream.writeStartElement(QLatin1String("fileParams")); if (!imageId.m_fileName.isNull()) { stream.writeAttribute(QLatin1String("fileName"), imageId.m_fileName); } if (!imageId.m_filePath.isNull()) { stream.writeAttribute(QLatin1String("filePath"), imageId.m_filePath); } if (!imageId.m_uniqueHash.isNull()) { stream.writeAttribute(QLatin1String("fileHash"), imageId.m_uniqueHash); } if (imageId.m_fileSize) { stream.writeAttribute(QLatin1String("fileSize"), QString::number(imageId.m_fileSize)); } if (imageId.isOriginalFile() && !imageId.m_creationDate.isNull()) { stream.writeAttribute(QLatin1String("creationDate"), imageId.m_creationDate.toString(Qt::ISODate)); } stream.writeEndElement(); //fileParams stream.writeEndElement(); //file } } } stream.writeEndElement(); //history stream.writeEndDocument(); //qCDebug(DIGIKAM_DIMG_LOG) << xmlHistory; return xmlHistory; } DImageHistory DImageHistory::fromXml(const QString& xml) //DImageHistory { //qCDebug(DIGIKAM_DIMG_LOG) << "Parsing image history XML"; DImageHistory h; if (xml.isEmpty()) { return h; } QXmlStreamReader stream(xml); if (!stream.readNextStartElement()) { return h; } if (stream.name() != QLatin1String("history")) { return h; } QString originalUUID; QDateTime originalCreationDate; while (stream.readNextStartElement()) { if (stream.name() == QLatin1String("file")) { //qCDebug(DIGIKAM_DIMG_LOG) << "Parsing file tag"; HistoryImageId imageId(stream.attributes().value(QLatin1String("uuid")).toString()); if (stream.attributes().value(QLatin1String("type")) == QLatin1String("original")) { imageId.m_type = HistoryImageId::Original; } else if (stream.attributes().value(QLatin1String("type")) == QLatin1String("source")) { imageId.m_type = HistoryImageId::Source; } else { imageId.m_type = HistoryImageId::Intermediate; } while (stream.readNextStartElement()) { if (stream.name() == QLatin1String("fileParams")) { imageId.setFileName(stream.attributes().value(QLatin1String("fileName")).toString()); imageId.setPath(stream.attributes().value(QLatin1String("filePath")).toString()); QString date = stream.attributes().value(QLatin1String("creationDate")).toString(); if (!date.isNull()) { imageId.setCreationDate(QDateTime::fromString(date, Qt::ISODate)); } QString size = stream.attributes().value(QLatin1String("fileSize")).toString(); if (stream.attributes().hasAttribute(QLatin1String("fileHash")) && !size.isNull()) { imageId.setUniqueHash(stream.attributes().value(QLatin1String("fileHash")).toString(), size.toInt()); } stream.skipCurrentElement(); } else { stream.skipCurrentElement(); } } if (imageId.isOriginalFile()) { - originalUUID = imageId.m_uuid; + originalUUID = imageId.m_uuid; originalCreationDate = imageId.m_creationDate; } else { imageId.m_originalUUID = originalUUID; if (imageId.m_creationDate.isNull()) { imageId.m_creationDate = originalCreationDate; } } if (imageId.isValid()) { h << imageId; } } else if (stream.name() == QLatin1String("filter")) { //qCDebug(DIGIKAM_DIMG_LOG) << "Parsing filter tag"; FilterAction::Category c = FilterAction::ComplexFilter; QStringRef categoryString = stream.attributes().value(QLatin1String("filterCategory")); - if (categoryString == QLatin1String("reproducible")) + if (categoryString == QLatin1String("reproducible")) { c = FilterAction::ReproducibleFilter; } else if (categoryString == QLatin1String("complex")) { c = FilterAction::ComplexFilter; } else if (categoryString == QLatin1String("documentedHistory")) { c = FilterAction::DocumentedHistory; } FilterAction action(stream.attributes().value(QLatin1String("filterName")).toString(), stream.attributes().value(QLatin1String("filterVersion")).toString().toInt(), c); action.setDisplayableName(stream.attributes().value(QLatin1String("filterDisplayName")).toString()); if (stream.attributes().value(QLatin1String("branch")) == QLatin1String("true")) { action.addFlag(FilterAction::ExplicitBranch); } while (stream.readNextStartElement()) { if (stream.name() == QLatin1String("params")) { while (stream.readNextStartElement()) { if (stream.name() == QLatin1String("param") && stream.attributes().hasAttribute(QLatin1String("name"))) { action.addParameter(stream.attributes().value(QLatin1String("name")).toString(), stream.attributes().value(QLatin1String("value")).toString()); stream.skipCurrentElement(); } else { stream.skipCurrentElement(); } } } else { stream.skipCurrentElement(); } } h << action; } else { stream.skipCurrentElement(); } } if (stream.hasError()) { //TODO: error handling qCDebug(DIGIKAM_DIMG_LOG) << "An error occurred during parsing: " << stream.errorString(); } //qCDebug(DIGIKAM_DIMG_LOG) << "Parsing done"; return h; } } // namespace digikam diff --git a/core/libs/dimg/history/dimagehistory.h b/core/libs/dimg/history/dimagehistory.h index d550dadb01..c5ddd7ea1a 100644 --- a/core/libs/dimg/history/dimagehistory.h +++ b/core/libs/dimg/history/dimagehistory.h @@ -1,220 +1,226 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-06-02 * Description : class for manipulating modifications changeset for non-destruct. editing * * Copyright (C) 2010 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIMAGE_HISTORY_H #define DIGIKAM_DIMAGE_HISTORY_H // Qt includes #include #include #include #include #include #include #include #include // Local includes #include "digikam_export.h" #include "filteraction.h" #include "historyimageid.h" namespace Digikam { class DIGIKAM_EXPORT DImageHistory { public: class Entry { public: /** * A DImageHistory is a list of entries. * * Each entry has one action. The action can be null, * but it shall be null only if it is the action of the first * entry, with the "Original" as referred image, * representing the action of digitization. * * There can be zero, one or any number * of referred images per entry. * A referred image is a file in the state after the action is applied. */ FilterAction action; QList referredImages; }; public: DImageHistory(); DImageHistory(const DImageHistory& other); ~DImageHistory(); DImageHistory& operator=(const DImageHistory& other); /** * A history is null if it is constructed with the default constructor */ - bool isNull() const; + bool isNull() const; /** * A history is considered empty if there are no entries. */ - bool isEmpty() const; + bool isEmpty() const; /** * A history is a valid history (telling something about the past), * if the history is not empty, and there is at least one * referred image other than the "Current" entry, * or there is a valid action. */ - bool isValid() const; + bool isValid() const; /// Returns the number of entries - int size() const; + int size() const; - bool operator==(const DImageHistory& other) const; - bool operator!=(const DImageHistory& other) const { return !operator==(other); } - bool operator<(const DImageHistory& other) const; - bool operator>(const DImageHistory& other) const; + bool operator==(const DImageHistory& other) const; + bool operator!=(const DImageHistory& other) const + { + return !operator==(other); + } + bool operator<(const DImageHistory& other) const; + bool operator>(const DImageHistory& other) const; /** * Appends a new filter action to the history. */ DImageHistory& operator<<(const FilterAction& action); /** * Appends a new referred image, representing the current state * of the history. * If you add an id of type Current, adjustReferredImages() will be called. */ DImageHistory& operator<<(const HistoryImageId& imageId); void appendReferredImage(const HistoryImageId& id); void insertReferredImage(int entryIndex, const HistoryImageId& id); /// Removes the last entry from the history void removeLast(); /** * Access entries. * There are size() entries. */ QList& entries(); - const QList& entries() const; + const QList& entries() const; Entry& operator[](int i); - const Entry& operator[](int i) const; + const Entry& operator[](int i) const; /** * Access actions. * * There is one action per entry, * but the action may be null. */ /// Returns if there is any non-null action - bool hasActions() const; - bool hasFilters() const { return hasActions(); } + bool hasActions() const; + bool hasFilters() const + { + return hasActions(); + } /// Returns the number of non-null actions - int actionCount() const; + int actionCount() const; /// Gets all actions which are not null - QList allActions() const; - const FilterAction& action(int i) const; + QList allActions() const; + const FilterAction& action(int i) const; /** * Access referred images */ QList& referredImages(int i); - const QList& referredImages(int i) const; - QList allReferredImages() const; - HistoryImageId currentReferredImage() const; - HistoryImageId originalReferredImage() const; - QList referredImagesOfType(HistoryImageId::Type type) const; - bool hasReferredImages() const; - bool hasReferredImageOfType(HistoryImageId::Type type) const; - bool hasCurrentReferredImage() const; - bool hasOriginalReferredImage() const; + const QList& referredImages(int i) const; + QList allReferredImages() const; + HistoryImageId currentReferredImage() const; + HistoryImageId originalReferredImage() const; + QList referredImagesOfType(HistoryImageId::Type type) const; + bool hasReferredImages() const; + bool hasReferredImageOfType(HistoryImageId::Type type) const; + bool hasCurrentReferredImage() const; + bool hasOriginalReferredImage() const; /** * Edit referred images */ /// Remove all referredImages, leaving the entries list untouched void clearReferredImages(); /** * Adjusts the type of a Current HistoryImageId: * If it is the first entry, it becomes Original, * if it is in an intermediate entry, it becomes Intermediate, * if in the last entry, it stays current. */ void adjustReferredImages(); /// Changes the UUID of the current (last added current) referred image void adjustCurrentUuid(const QString& uuid); /** * Remove file path entries pointing to the given absolute path * from any referred images. This is useful when said file * is about to be overwritten. * All other HistoryImageId fields remain unchanged, no HistoryImageId is removed. * path: directory path, without filename. */ void purgePathFromReferredImages(const QString& path, const QString& fileName); /** * Change file path entries of the current referred image */ void moveCurrentReferredImage(const QString& newPath, const QString& newFileName); /** - * Serialize to and from XML. + * Serialize toand from XML. * * Note: The "Current" entry is skipped when writing to XML, * so make sure the file into the metadata of which you write the XML, * is the file marked as "Current" in this history. */ - QString toXml() const; + QString toXml() const; static DImageHistory fromXml(const QString& xml); public: // Set as public there because of PrivateSharedNull class Private; private: QSharedDataPointer d; }; } // namespace Digikam Q_DECLARE_METATYPE(Digikam::DImageHistory) #endif // DIGIKAM_DIMAGE_HISTORY_H diff --git a/core/libs/dimg/history/filteraction.cpp b/core/libs/dimg/history/filteraction.cpp index 6650d1e74c..8ae02d82c9 100644 --- a/core/libs/dimg/history/filteraction.cpp +++ b/core/libs/dimg/history/filteraction.cpp @@ -1,173 +1,175 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-06-02 * Description : class that holds list of applied filters to image * * Copyright (C) 2010 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "filteraction.h" namespace Digikam { FilterAction::FilterAction() : m_category(ReproducibleFilter), m_version(0) { } FilterAction::FilterAction(const QString& identifier, int version, FilterAction::Category category) : m_category(category), m_identifier(identifier), m_version(version) { } bool FilterAction::isNull() const { return m_identifier.isEmpty(); } bool FilterAction::operator==(const FilterAction& other) const { - return m_identifier == other.m_identifier && - m_version == other.m_version && - m_category == other.m_category && - m_description == other.m_description && - m_displayableName == other.m_displayableName && - m_params == other.m_params; + return ( + (m_identifier == other.m_identifier) && + (m_version == other.m_version) && + (m_category == other.m_category) && + (m_description == other.m_description) && + (m_displayableName == other.m_displayableName) && + (m_params == other.m_params) + ); } FilterAction::Category FilterAction::category() const { return m_category; } QString FilterAction::identifier() const { return m_identifier; } int FilterAction::version() const { return m_version; } QString FilterAction::description() const { return m_description; } void FilterAction::setDescription(const QString& description) { m_description = description; } QString FilterAction::displayableName() const { return m_displayableName; } void FilterAction::setDisplayableName(const QString& displayableName) { m_displayableName = displayableName; } FilterAction::Flags FilterAction::flags() const { return m_flags; } void FilterAction::setFlags(Flags flags) { m_flags = flags; } void FilterAction::addFlag(Flags flags) { m_flags |= flags; } void FilterAction::removeFlag(Flags flags) { m_flags &= ~flags; } bool FilterAction::hasParameters() const { return !m_params.isEmpty(); } const QHash &FilterAction::parameters() const { return m_params; } QHash &FilterAction::parameters() { return m_params; } bool FilterAction::hasParameter(const QString& key) const { return m_params.contains(key); } const QVariant FilterAction::parameter(const QString& key) const { return m_params.value(key); } QVariant& FilterAction::parameter(const QString& key) { return m_params[key]; } void FilterAction::setParameter(const QString& key, const QVariant& value) { m_params.insert(key, value); } void FilterAction::addParameter(const QString& key, const QVariant& value) { m_params.insertMulti(key, value); } void FilterAction::addParameters(const QHash& params) { m_params = m_params.unite(params); } void FilterAction::setParameters(const QHash& params) { m_params = params; } void FilterAction::removeParameters(const QString& key) { m_params.remove(key); } void FilterAction::clearParameters() { m_params.clear(); } } // namespace Digikam diff --git a/core/libs/dimg/history/filteraction.h b/core/libs/dimg/history/filteraction.h index 81fef272c5..2a55809af3 100644 --- a/core/libs/dimg/history/filteraction.h +++ b/core/libs/dimg/history/filteraction.h @@ -1,186 +1,195 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-06-02 * Description : class that holds list of applied filters to image * * Copyright (C) 2010 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_FILTER_ACTION_H #define DIGIKAM_FILTER_ACTION_H // Qt includes #include #include #include #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT FilterAction { public: enum Category { - // Do not change existing values, they are written to XML in files! - /** When given the set of stored parameters and the original data, - * an identical result will be produced. */ + /** + * NOTE: Do not change existing values, they are written to XML in files! + */ + + /** + * When given the set of stored parameters and the original data, + * an identical result will be produced. + */ ReproducibleFilter = 0, /** - * The operation is documented and a number of parameters may be known, - * but the identical result cannot be reproduced. - * It may be possible to produce a sufficiently similar result. + * The operation is documented and a number of parameters may be known, + * but the identical result cannot be reproduced. + * It may be possible to produce a sufficiently similar result. */ ComplexFilter = 1, - /** The source images are known, a textual description may be added, - * but there is no way to automatically replay + /** + * The source images are known, a textual description may be added, + * but there is no way to automatically replay */ DocumentedHistory = 2, CategoryFirst = ReproducibleFilter, CategoryLast = DocumentedHistory }; enum Flag { - /** The editing step of this filter action explicitly branches from the parent. - * This is an optional hint that the result is meant as a new version. + /** + * The editing step of this filter action explicitly branches from the parent. + * This is an optional hint that the result is meant as a new version. */ ExplicitBranch = 1 << 0 }; Q_DECLARE_FLAGS(Flags, Flag) public: FilterAction(); FilterAction(const QString& identifier, int version, Category category = ReproducibleFilter); - bool isNull() const; + bool isNull() const; - bool operator==(const FilterAction& other) const; + bool operator==(const FilterAction& other) const; - Category category() const; + Category category() const; /** * Returns a technical identifier for the filter used to produce this action. * Can include a namespace. Example: digikam:charcoal */ - QString identifier() const; + QString identifier() const; /** * Returns the version (>= 1) of the filter used to produce this action. * When a filter / tool is found by the identifier, it can decide * by the version if it supports this action and which parameters it expects. */ - int version() const; + int version() const; /** * Returns a description / comment for this action. * In the case of DocumentedHistory, this may be the most useful value. */ - QString description() const; + QString description() const; void setDescription(const QString& description); - QString displayableName() const; + QString displayableName() const; void setDisplayableName(const QString& displayableName); - Flags flags() const; + Flags flags() const; void setFlags(Flags flags); void addFlag(Flags flags); void removeFlag(Flags flags); /** * Access parameters. * A parameters is a key -> value pair. * Keys need not be unique, but you can decide to use unique keys. * There are accessors for both contexts. */ - bool hasParameters() const; - const QHash& parameters() const; + bool hasParameters() const; + const QHash& parameters() const; QHash& parameters(); bool hasParameter(const QString& key) const; const QVariant parameter(const QString& key) const; QVariant& parameter(const QString& key); - /// Returns parameter converted from QVariant to given type + /** + * Returns parameter converted from QVariant to given type + */ template - T parameter(const QString& key) const + T parameter(const QString& key) const { return parameter(key).value(); } /** * Read parameter with a default value: * If there is a parameter for the given key, return it converted * from QVariant to the template type. * If there is no parameter, return the given default value. */ template - T parameter(const QString& key, const T& defaultValue) const + T parameter(const QString& key, const T& defaultValue) const { QVariant var = parameter(key); return (var.isValid()) ? var.value() : defaultValue; } /// Sets parameter, removing all other values for the same key void setParameter(const QString& key, const QVariant& value); /// Adds a parameter, possibly keeping existing parameters with the same key. void addParameter(const QString& key, const QVariant& value); /// Removes all parameters for key void removeParameters(const QString& key); /// Clear all parameters void clearParameters(); /// Adds a set of parameters void addParameters(const QHash& params); /// Replaces parameters void setParameters(const QHash& params); protected: // Note: Value class, do not create a d-pointer Category m_category; Flags m_flags; QString m_identifier; int m_version; QString m_description; QString m_displayableName; QHash m_params; }; } // namespace Digikam Q_DECLARE_METATYPE(Digikam::FilterAction) Q_DECLARE_OPERATORS_FOR_FLAGS(Digikam::FilterAction::Flags) #endif // DIGIKAM_FILTER_ACTION_H diff --git a/core/libs/dimg/history/historyimageid.cpp b/core/libs/dimg/history/historyimageid.cpp index 55cc0ea699..742e9ba59a 100644 --- a/core/libs/dimg/history/historyimageid.cpp +++ b/core/libs/dimg/history/historyimageid.cpp @@ -1,178 +1,182 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-06-02 * Description : class holding properties of referenced files used in non-dest. editing * * Copyright (C) 2010 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "historyimageid.h" // Qt includes #include #include namespace Digikam { HistoryImageId::HistoryImageId() : m_type(InvalidType), m_fileSize(0) { } HistoryImageId::HistoryImageId(const QString& uuid, Type type) : m_type(type), m_uuid(uuid), m_fileSize(0) { } void HistoryImageId::setType(HistoryImageId::Type type) { m_type = type; } void HistoryImageId::setUuid(const QString& uuid) { m_uuid = uuid; } void HistoryImageId::setFileName(const QString& fileName) { m_fileName = fileName; } void HistoryImageId::setCreationDate(const QDateTime& creationDate) { m_creationDate = creationDate; } void HistoryImageId::setPathOnDisk(const QString& filePath) { - QUrl url = QUrl::fromLocalFile(filePath); + QUrl url = QUrl::fromLocalFile(filePath); m_filePath = url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile() + QLatin1Char('/'); } void HistoryImageId::setPath(const QString& path) { m_filePath = path; if (!m_filePath.endsWith(QLatin1Char('/'))) { m_filePath += QLatin1Char('/'); } } void HistoryImageId::setUniqueHash(const QString& uniqueHash, qlonglong fileSize) { m_uniqueHash = uniqueHash; m_fileSize = fileSize; } bool HistoryImageId::isValid() const { - return (m_type != InvalidType) - && (!m_uuid.isEmpty() || !m_fileName.isEmpty()); + return ( + (m_type != InvalidType) && + (!m_uuid.isEmpty() || !m_fileName.isEmpty()) + ); } HistoryImageId::Type HistoryImageId::type() const { return m_type; } QString HistoryImageId::path() const { return m_filePath; } QString HistoryImageId::filePath() const { - return m_filePath + m_fileName; + return (m_filePath + m_fileName); } bool HistoryImageId::hasFileOnDisk() const { - return !m_filePath.isEmpty() && !m_fileName.isEmpty(); + return (!m_filePath.isEmpty() && !m_fileName.isEmpty()); } bool HistoryImageId::hasFileName() const { return !m_fileName.isEmpty(); } QString HistoryImageId::fileName() const { return m_fileName; } bool HistoryImageId::hasUuid() const { return !m_uuid.isEmpty(); } QString HistoryImageId::uuid() const { return m_uuid; } bool HistoryImageId::hasCreationDate() const { return m_creationDate.isValid(); } QDateTime HistoryImageId::creationDate() const { return m_creationDate; } bool HistoryImageId::hasUniqueHashIdentifier() const { - return !m_uniqueHash.isEmpty() && m_fileSize; + return (!m_uniqueHash.isEmpty() && m_fileSize); } QString HistoryImageId::uniqueHash() const { return m_uniqueHash; } qlonglong HistoryImageId::fileSize() const { return m_fileSize; } QString HistoryImageId::originalUuid() const { return m_originalUUID; } bool HistoryImageId::operator==(const HistoryImageId& other) const { - return m_uuid == other.m_uuid && - m_type == other.m_type && - m_fileName == other.m_fileName && - m_filePath == other.m_filePath && - m_creationDate == other.m_creationDate && - m_uniqueHash == other.m_uniqueHash && - m_fileSize == other.m_fileSize && - m_originalUUID == other.m_originalUUID; + return ( + (m_uuid == other.m_uuid) && + (m_type == other.m_type) && + (m_fileName == other.m_fileName) && + (m_filePath == other.m_filePath) && + (m_creationDate == other.m_creationDate) && + (m_uniqueHash == other.m_uniqueHash) && + (m_fileSize == other.m_fileSize) && + (m_originalUUID == other.m_originalUUID) + ); } } // namespace Digikam diff --git a/core/libs/dimg/history/historyimageid.h b/core/libs/dimg/history/historyimageid.h index b47f73ba9b..ed77bb2622 100644 --- a/core/libs/dimg/history/historyimageid.h +++ b/core/libs/dimg/history/historyimageid.h @@ -1,182 +1,187 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2010-06-02 * Description : class holding properties of referenced files used in non-dest. editing * * Copyright (C) 2010 by Marcel Wiesweg * Copyright (C) 2010 by Martin Klapetek * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_HISTORY_IMAGE_ID_H #define DIGIKAM_HISTORY_IMAGE_ID_H // Qt includes #include #include #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT HistoryImageId { public: enum Type { InvalidType = 0, /** * The original file (typically created by a camera) */ Original = 1 << 0, /** * A file created during the editing the history, * between the original file and the current file. */ Intermediate = 1 << 1, /** * When a file is created from multiple files, there can be no direct * original (panorama) but multiple sources, or one direct original * and some other, additional source files. * To record source files outside of the direct history, this type is used. */ Source = 1 << 2, /** * The "current" file. This is a special entry: It refers to the file from * which this history was read. It need not be written to the file, * because it describes the file itself. There is typically * exactly one current entry if the history is associated with an image; * there can be no current entry. */ Current = 1 << 3 }; /** * Note: In this class, the Type is used as a simple enum, * but it is also prepared for usage as flags. */ Q_DECLARE_FLAGS(Types, Type) public: /// Creates an invalid HistoryImageId HistoryImageId(); /// Creates an id with the given UUID and type explicit HistoryImageId(const QString& uuid, Type type = Current); /// A valid id needs at least a valid type and a UUID or a filename - bool isValid() const; + bool isValid() const; - Type type() const; + Type type() const; - bool isOriginalFile() const + bool isOriginalFile() const { - return type() == Original; + return (type() == Original); } - bool isSourceFile() const + bool isSourceFile() const { - return type() == Source; + return (type() == Source); } - bool isIntermediateFile() const + bool isIntermediateFile() const { - return type() == Intermediate; + return (type() == Intermediate); } - bool isCurrentFile() const + bool isCurrentFile() const { - return type() == Current; + return (type() == Current); } - bool operator==(const HistoryImageId& other) const; + bool operator==(const HistoryImageId& other) const; void setType(HistoryImageId::Type type); void setUuid(const QString& uuid); void setFileName(const QString& fileName); void setCreationDate(const QDateTime& creationDate); void setPathOnDisk(const QString& filePath); void setPath(const QString& path); void setUniqueHash(const QString& uniqueHash, qlonglong fileSize); - bool hasFileOnDisk() const; + bool hasFileOnDisk() const; - ///If a file on disk is referenced: Returns the path, without filename, with a trailing slash - QString path() const; + /// If a file on disk is referenced: Returns the path, without filename, with a trailing slash + QString path() const; /// If a file on disk is referenced: Returns the full file path (folder + filename) - QString filePath() const; + QString filePath() const; - bool hasFileName() const; + bool hasFileName() const; /// If a file on disk is referenced: Returns the file name (without folder) - QString fileName() const; + QString fileName() const; - bool hasUuid() const; - QString uuid() const; - bool hasCreationDate() const; - QDateTime creationDate() const; - bool hasUniqueHashIdentifier() const; - QString uniqueHash() const; - qlonglong fileSize() const; - QString originalUuid() const; + bool hasUuid() const; + QString uuid() const; + bool hasCreationDate() const; + QDateTime creationDate() const; + bool hasUniqueHashIdentifier() const; + QString uniqueHash() const; + qlonglong fileSize() const; + QString originalUuid() const; public: /// Type of this History Image Id Type m_type; /** * A unique identifier for the referred file. This id shall be changed each time * the image is edited. */ QString m_uuid; /// The filename of the referred file QString m_fileName; + /// The creationDate of the original image QDateTime m_creationDate; + /// The path of the referred file (NOTE: without file name!, including trailing slash) QString m_filePath; + /// The uniqueHash of the referred file QString m_uniqueHash; + /// The file size of the referred file qlonglong m_fileSize; + /** * A unique identifier designating the _original image_ from which the referred * image was created. Typically, this is a RAW or JPEG created by the camera in * the moment of taking the photograph. */ QString m_originalUUID; }; } // namespace Digikam Q_DECLARE_METATYPE(Digikam::HistoryImageId) Q_DECLARE_OPERATORS_FOR_FLAGS(Digikam::HistoryImageId::Types) #endif // DIGIKAM_HISTORY_IMAGE_ID_H diff --git a/core/libs/dimg/loaders/dimgloader.cpp b/core/libs/dimg/loaders/dimgloader.cpp index 4c9ec92b41..a5cf8bd6a7 100644 --- a/core/libs/dimg/loaders/dimgloader.cpp +++ b/core/libs/dimg/loaders/dimgloader.cpp @@ -1,297 +1,297 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-06-14 * Description : DImg image loader interface * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2005-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dimgloader.h" // C++ includes #include #include #include // Local includes #include "digikam_debug.h" #include "dimg_p.h" #include "dmetadata.h" #include "dimgloaderobserver.h" #include "kmemoryinfo.h" namespace Digikam { DImgLoader::DImgLoader(DImg* const image) : m_image(image), m_loadFlags(LoadAll) { } DImgLoader::~DImgLoader() { } void DImgLoader::setLoadFlags(LoadFlags flags) { m_loadFlags = flags; } bool DImgLoader::hasLoadedData() const { - return (m_loadFlags & LoadImageData) && m_image->m_priv->data; + return ((m_loadFlags & LoadImageData) && m_image->m_priv->data); } int DImgLoader::granularity(DImgLoaderObserver* const observer, int total, float progressSlice) { // Splits expect total value into the chunks where checks shall occur // and combines this with a possible correction factor from observer. // Progress slice is the part of 100% concerned with the current granularity // (E.g. in a loop only the values from 10% to 90% are used, then progressSlice is 0.8) // Current default is 1/20, that is progress info every 5% int granularity = 0; if (observer) { granularity = (int)((total / (20 * progressSlice)) / observer->granularity()); } - return granularity ? granularity : 1; + return (granularity ? granularity : 1); } unsigned char*& DImgLoader::imageData() { return m_image->m_priv->data; } unsigned int DImgLoader::imageNumBytes() const { return m_image->numBytes(); } unsigned int& DImgLoader::imageWidth() { return m_image->m_priv->width; } unsigned int& DImgLoader::imageHeight() { return m_image->m_priv->height; } bool DImgLoader::imageHasAlpha() const { return m_image->hasAlpha(); } bool DImgLoader::imageSixteenBit() const { return m_image->sixteenBit(); } int DImgLoader::imageBitsDepth() const { return m_image->bitsDepth(); } int DImgLoader::imageBytesDepth() const { return m_image->bytesDepth(); } void DImgLoader::imageSetIccProfile(const IccProfile& profile) { m_image->setIccProfile(profile); } QVariant DImgLoader::imageGetAttribute(const QString& key) const { return m_image->attribute(key); } QString DImgLoader::imageGetEmbbededText(const QString& key) const { return m_image->embeddedText(key); } void DImgLoader::imageSetAttribute(const QString& key, const QVariant& value) { m_image->setAttribute(key, value); } QMap& DImgLoader::imageEmbeddedText() const { QMutexLocker lock(&m_image->m_priv->mutex); return m_image->m_priv->embeddedText; } void DImgLoader::imageSetEmbbededText(const QString& key, const QString& text) { m_image->setEmbeddedText(key, text); } void DImgLoader::loadingFailed() { if (m_image->m_priv->data) { delete [] m_image->m_priv->data; } m_image->m_priv->data = nullptr; m_image->m_priv->width = 0; m_image->m_priv->height = 0; } qint64 DImgLoader::checkAllocation(qint64 fullSize) { if ((quint64)fullSize >= std::numeric_limits::max()) { qCWarning(DIGIKAM_DIMG_LOG) << "Cannot allocate buffer of size" << fullSize; return 0; } // Do extra check if allocating serious amounts of memory. // At the time of writing (2011), I consider 100 MB as "serious". if (fullSize > (qint64)(100 * 1024 * 1024)) { KMemoryInfo memory = KMemoryInfo::currentInfo(); int res = memory.isValid(); - if (res == -1) + if (res == -1) { qCWarning(DIGIKAM_DIMG_LOG) << "Not a recognized platform to get memory information"; return -1; } else if (res == 0) { qCWarning(DIGIKAM_DIMG_LOG) << "Error to get physical memory information form a recognized platform"; return 0; } qint64 available = memory.bytes(KMemoryInfo::AvailableMemory); if (fullSize > available) { qCWarning(DIGIKAM_DIMG_LOG) << "Not enough memory to allocate buffer of size " << fullSize; qCWarning(DIGIKAM_DIMG_LOG) << "Available memory size is " << available; return 0; } } return fullSize; } bool DImgLoader::readMetadata(const QString& filePath) { if (!((m_loadFlags & LoadMetadata) || (m_loadFlags & LoadUniqueHash) || (m_loadFlags & LoadImageHistory))) { return false; } DMetadata metaDataFromFile; if (!metaDataFromFile.load(filePath)) { m_image->setMetadata(MetaEngineData()); return false; } m_image->setMetadata(metaDataFromFile.data()); if (m_loadFlags & LoadImageHistory) { DImageHistory history = DImageHistory::fromXml(metaDataFromFile.getItemHistory()); HistoryImageId id = m_image->createHistoryImageId(filePath, HistoryImageId::Current); history << id; m_image->setItemHistory(history); imageSetAttribute(QLatin1String("originalImageHistory"), QVariant::fromValue(history)); } return true; } bool DImgLoader::saveMetadata(const QString& filePath) { DMetadata metaDataToFile(filePath); metaDataToFile.setData(m_image->getMetadata()); return metaDataToFile.applyChanges(true); } bool DImgLoader::checkExifWorkingColorSpace() const { DMetadata metaData(m_image->getMetadata()); IccProfile profile = metaData.getIccProfile(); if (!profile.isNull()) { m_image->setIccProfile(profile); return true; } return false; } void DImgLoader::storeColorProfileInMetadata() { IccProfile profile = m_image->getIccProfile(); if (profile.isNull()) { return; } DMetadata metaData(m_image->getMetadata()); metaData.setIccProfile(profile); m_image->setMetadata(metaData.data()); } void DImgLoader::purgeExifWorkingColorSpace() { DMetadata meta(m_image->getMetadata()); meta.removeExifColorSpace(); m_image->setMetadata(meta.data()); } unsigned char* DImgLoader::new_failureTolerant(size_t unsecureSize) { return new_failureTolerant(unsecureSize); } unsigned char* DImgLoader::new_failureTolerant(quint64 w, quint64 h, uint typesPerPixel) { return new_failureTolerant(w, h, typesPerPixel); } unsigned short* DImgLoader::new_short_failureTolerant(size_t unsecureSize) { return new_failureTolerant(unsecureSize); } unsigned short* DImgLoader::new_short_failureTolerant(quint64 w, quint64 h, uint typesPerPixel) { return new_failureTolerant(w, h, typesPerPixel); } } // namespace Digikam diff --git a/core/libs/dimg/loaders/dimgloader.h b/core/libs/dimg/loaders/dimgloader.h index ea9e679ec5..6599a998fe 100644 --- a/core/libs/dimg/loaders/dimgloader.h +++ b/core/libs/dimg/loaders/dimgloader.h @@ -1,199 +1,204 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2005-06-14 * Description : DImg image loader interface * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2005-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_DIMG_LOADER_H #define DIGIKAM_DIMG_LOADER_H // C++ includes #include // Qt includes #include #include #include // Local includes #include "digikam_debug.h" #include "digikam_export.h" #include "dimg.h" namespace Digikam { class DImgLoaderObserver; class DMetadata; class DImgLoader { public: /** This is the list of loading modes usable by DImg image plugins */ enum LoadFlag { // Load image information without image data LoadItemInfo = 1, /// Image info as width and height LoadMetadata = 2, /// Image metadata LoadICCData = 4, /// Image color profile LoadImageData = 8, /// Full image data LoadUniqueHash = 16, /// Image unique hash LoadImageHistory = 32, /// Image version history // Special mode to load reduced image data LoadPreview = 64, /// Load embedded preview image instead full size image // Helper to load all information, metadata and full image. LoadAll = LoadItemInfo | LoadMetadata | LoadICCData | LoadImageData | LoadUniqueHash | LoadImageHistory }; Q_DECLARE_FLAGS(LoadFlags, LoadFlag) public: void setLoadFlags(LoadFlags flags); virtual ~DImgLoader(); - virtual bool load(const QString& filePath, DImgLoaderObserver* const observer) = 0; - virtual bool save(const QString& filePath, DImgLoaderObserver* const observer) = 0; + virtual bool load(const QString& filePath, DImgLoaderObserver* const observer) = 0; + virtual bool save(const QString& filePath, DImgLoaderObserver* const observer) = 0; - virtual bool hasLoadedData() const; - virtual bool hasAlpha() const = 0; - virtual bool sixteenBit() const = 0; - virtual bool isReadOnly() const = 0; + virtual bool hasLoadedData() const; + virtual bool hasAlpha() const = 0; + virtual bool sixteenBit() const = 0; + virtual bool isReadOnly() const = 0; static unsigned char* new_failureTolerant(size_t unsecureSize); static unsigned char* new_failureTolerant(quint64 w, quint64 h, uint typesPerPixel); static unsigned short* new_short_failureTolerant(size_t unsecureSize); static unsigned short* new_short_failureTolerant(quint64 w, quint64 h, uint typesPerPixel); - /** Value returned : -1 : unsupported platform - * 0 : parse failure from supported platform - * 1 : parse done with success from supported platform + /** + * Value returned : -1 : unsupported platform + * 0 : parse failure from supported platform + * 1 : parse done with success from supported platform */ static qint64 checkAllocation(qint64 fullSize); template static Type* new_failureTolerant(size_t unsecureSize); template static Type* new_failureTolerant(quint64 w, quint64 h, uint typesPerPixel); protected: explicit DImgLoader(DImg* const image); unsigned char*& imageData(); unsigned int& imageWidth(); unsigned int& imageHeight(); - bool imageHasAlpha() const; - bool imageSixteenBit() const; + bool imageHasAlpha() const; + bool imageSixteenBit() const; - unsigned int imageNumBytes() const; - int imageBitsDepth() const; - int imageBytesDepth() const; + unsigned int imageNumBytes() const; + int imageBitsDepth() const; + int imageBytesDepth() const; void imageSetIccProfile(const IccProfile& profile); - QVariant imageGetAttribute(const QString& key) const; - void imageSetAttribute(const QString& key, const QVariant& value); + QVariant imageGetAttribute(const QString& key) const; + void imageSetAttribute(const QString& key, + const QVariant& value); - QMap& imageEmbeddedText() const; - QString imageGetEmbbededText(const QString& key) const; - void imageSetEmbbededText(const QString& key, const QString& text); + QMap& imageEmbeddedText() const; + QString imageGetEmbbededText(const QString& key) const; + void imageSetEmbbededText(const QString& key, + const QString& text); void loadingFailed(); - bool checkExifWorkingColorSpace() const; + bool checkExifWorkingColorSpace() const; void purgeExifWorkingColorSpace(); void storeColorProfileInMetadata(); virtual bool readMetadata(const QString& filePath); virtual bool saveMetadata(const QString& filePath); virtual int granularity(DImgLoaderObserver* const observer, int total, float progressSlice = 1.0); protected: DImg* m_image; LoadFlags m_loadFlags; private: DImgLoader(); }; // --------------------------------------------------------------------------------------------------- /// Allows safe multiplication of requested pixel number and bytes per pixel, avoiding particularly /// 32bit overflow and exceeding the size_t type template Q_INLINE_TEMPLATE Type* DImgLoader::new_failureTolerant(quint64 w, quint64 h, uint typesPerPixel) { quint64 requested = w * h * quint64(typesPerPixel); quint64 maximum = std::numeric_limits::max(); if (requested > maximum) { qCCritical(DIGIKAM_DIMG_LOG) << "Requested memory of" << requested*quint64(sizeof(Type)) << "is larger than size_t supported by platform."; return nullptr; } return new_failureTolerant(requested); } template Q_INLINE_TEMPLATE Type* DImgLoader::new_failureTolerant(size_t size) { qint64 res = checkAllocation(size); - switch(res) + switch (res) { case 0: // parse failure from supported platform return nullptr; break; + case -1: // unsupported platform // We will try to continue to allocate break; + default: // parse done with success from supported platform break; } Type* const reserved = new (std::nothrow) Type[size]; if (!reserved) { qCCritical(DIGIKAM_DIMG_LOG) << "Failed to allocate chunk of memory of size" << size; } return reserved; } Q_DECLARE_OPERATORS_FOR_FLAGS(DImgLoader::LoadFlags) } // namespace Digikam #endif // DIGIKAM_DIMG_LOADER_H diff --git a/core/libs/dimg/loaders/heifsettings.cpp b/core/libs/dimg/loaders/heifsettings.cpp index 59acadad90..c7bf599039 100644 --- a/core/libs/dimg/loaders/heifsettings.cpp +++ b/core/libs/dimg/loaders/heifsettings.cpp @@ -1,149 +1,149 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-10-02 * Description : save HEIF image options. * * Copyright (C) 2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "heifsettings.h" // Qt includes #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "dnuminput.h" namespace Digikam { class Q_DECL_HIDDEN HEIFSettings::Private { public: explicit Private() + : HEIFGrid(nullptr), + labelHEIFcompression(nullptr), + HEIFLossLess(nullptr), + HEIFcompression(nullptr) { - HEIFGrid = nullptr; - labelHEIFcompression = nullptr; - HEIFcompression = nullptr; - HEIFLossLess = nullptr; } QGridLayout* HEIFGrid; QLabel* labelHEIFcompression; QCheckBox* HEIFLossLess; DIntNumInput* HEIFcompression; }; HEIFSettings::HEIFSettings(QWidget* const parent) : QWidget(parent), d(new Private) { setAttribute(Qt::WA_DeleteOnClose); const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); d->HEIFGrid = new QGridLayout(this); d->HEIFLossLess = new QCheckBox(i18n("Lossless HEIF files"), this); d->HEIFLossLess->setWhatsThis(i18n("

Toggle lossless compression for HEIF images.

" "

If this option is enabled, a lossless method will be used " "to compress HEIF pictures.

")); d->HEIFcompression = new DIntNumInput(this); d->HEIFcompression->setDefaultValue(3); d->HEIFcompression->setRange(1, 9, 1); d->labelHEIFcompression = new QLabel(i18n("HEIF quality:"), this); d->HEIFcompression->setWhatsThis(i18n("

The quality value for HEIF images:

" "

1: high quality (no compression and " "large file size)
" "3: good quality (default)
" "6: medium quality
" "9: low quality (high compression and small " "file size)

" "

Note: HEIF is not a lossless image " "compression format when you use this setting.

")); d->HEIFGrid->addWidget(d->HEIFLossLess, 0, 0, 1, 2); d->HEIFGrid->addWidget(d->labelHEIFcompression, 1, 0, 1, 2); d->HEIFGrid->addWidget(d->HEIFcompression, 2, 0, 1, 2); d->HEIFGrid->setColumnStretch(1, 10); d->HEIFGrid->setRowStretch(3, 10); d->HEIFGrid->setContentsMargins(spacing, spacing, spacing, spacing); d->HEIFGrid->setSpacing(spacing); connect(d->HEIFLossLess, SIGNAL(toggled(bool)), this, SLOT(slotToggleHEIFLossLess(bool))); connect(d->HEIFLossLess, SIGNAL(toggled(bool)), this, SIGNAL(signalSettingsChanged())); connect(d->HEIFcompression, SIGNAL(valueChanged(int)), this, SIGNAL(signalSettingsChanged())); } HEIFSettings::~HEIFSettings() { delete d; } void HEIFSettings::setCompressionValue(int val) { d->HEIFcompression->setValue(val); } int HEIFSettings::getCompressionValue() const { return d->HEIFcompression->value(); } void HEIFSettings::setLossLessCompression(bool b) { d->HEIFLossLess->setChecked(b); slotToggleHEIFLossLess(d->HEIFLossLess->isChecked()); } bool HEIFSettings::getLossLessCompression() const { return d->HEIFLossLess->isChecked(); } void HEIFSettings::slotToggleHEIFLossLess(bool b) { d->HEIFcompression->setEnabled(!b); d->labelHEIFcompression->setEnabled(!b); } } // namespace Digikam diff --git a/core/libs/dimg/loaders/heifsettings.h b/core/libs/dimg/loaders/heifsettings.h index 2ae18007f6..f4e07b25c3 100644 --- a/core/libs/dimg/loaders/heifsettings.h +++ b/core/libs/dimg/loaders/heifsettings.h @@ -1,69 +1,69 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-10-02 * Description : save HEIF image options. * * Copyright (C) 2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_HEIF_SETTINGS_H #define DIGIKAM_HEIF_SETTINGS_H // Qt includes #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT HEIFSettings : public QWidget { Q_OBJECT public: explicit HEIFSettings(QWidget* const parent = nullptr); ~HEIFSettings(); void setCompressionValue(int val); - int getCompressionValue() const; + int getCompressionValue() const; void setLossLessCompression(bool b); bool getLossLessCompression() const; Q_SIGNALS: void signalSettingsChanged(); private Q_SLOTS: void slotToggleHEIFLossLess(bool); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_HEIF_SETTINGS_H diff --git a/core/libs/dimg/loaders/jp2ksettings.cpp b/core/libs/dimg/loaders/jp2ksettings.cpp index 8d39b02867..ce7f67c7b4 100644 --- a/core/libs/dimg/loaders/jp2ksettings.cpp +++ b/core/libs/dimg/loaders/jp2ksettings.cpp @@ -1,149 +1,149 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-08-02 * Description : save JPEG 2000 image options. * * Copyright (C) 2007-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "jp2ksettings.h" // Qt includes #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "dnuminput.h" namespace Digikam { class Q_DECL_HIDDEN JP2KSettings::Private { public: explicit Private() + : JPEG2000Grid(nullptr), + labelJPEG2000compression(nullptr), + JPEG2000LossLess(nullptr), + JPEG2000compression(nullptr) { - JPEG2000Grid = nullptr; - labelJPEG2000compression = nullptr; - JPEG2000compression = nullptr; - JPEG2000LossLess = nullptr; } QGridLayout* JPEG2000Grid; QLabel* labelJPEG2000compression; QCheckBox* JPEG2000LossLess; DIntNumInput* JPEG2000compression; }; JP2KSettings::JP2KSettings(QWidget* const parent) : QWidget(parent), d(new Private) { setAttribute(Qt::WA_DeleteOnClose); const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); d->JPEG2000Grid = new QGridLayout(this); d->JPEG2000LossLess = new QCheckBox(i18n("Lossless JPEG 2000 files"), this); d->JPEG2000LossLess->setWhatsThis(i18n("

Toggle lossless compression for JPEG 2000 images.

" "

If this option is enabled, a lossless method will be used " "to compress JPEG 2000 pictures.

")); d->JPEG2000compression = new DIntNumInput(this); d->JPEG2000compression->setDefaultValue(75); d->JPEG2000compression->setRange(1, 100, 1); d->labelJPEG2000compression = new QLabel(i18n("JPEG 2000 quality:"), this); d->JPEG2000compression->setWhatsThis(i18n("

The quality value for JPEG 2000 images:

" "

1: low quality (high compression and small " "file size)
" "50: medium quality
" "75: good quality (default)
" "100: high quality (no compression and " "large file size)

" "

Note: JPEG 2000 is not a lossless image " "compression format when you use this setting.

")); d->JPEG2000Grid->addWidget(d->JPEG2000LossLess, 0, 0, 1, 2); d->JPEG2000Grid->addWidget(d->labelJPEG2000compression, 1, 0, 1, 2); d->JPEG2000Grid->addWidget(d->JPEG2000compression, 2, 0, 1, 2); d->JPEG2000Grid->setColumnStretch(1, 10); d->JPEG2000Grid->setRowStretch(3, 10); d->JPEG2000Grid->setContentsMargins(spacing, spacing, spacing, spacing); d->JPEG2000Grid->setSpacing(spacing); connect(d->JPEG2000LossLess, SIGNAL(toggled(bool)), this, SLOT(slotToggleJPEG2000LossLess(bool))); connect(d->JPEG2000LossLess, SIGNAL(toggled(bool)), this, SIGNAL(signalSettingsChanged())); connect(d->JPEG2000compression, SIGNAL(valueChanged(int)), this, SIGNAL(signalSettingsChanged())); } JP2KSettings::~JP2KSettings() { delete d; } void JP2KSettings::setCompressionValue(int val) { d->JPEG2000compression->setValue(val); } int JP2KSettings::getCompressionValue() const { return d->JPEG2000compression->value(); } void JP2KSettings::setLossLessCompression(bool b) { d->JPEG2000LossLess->setChecked(b); slotToggleJPEG2000LossLess(d->JPEG2000LossLess->isChecked()); } bool JP2KSettings::getLossLessCompression() const { return d->JPEG2000LossLess->isChecked(); } void JP2KSettings::slotToggleJPEG2000LossLess(bool b) { d->JPEG2000compression->setEnabled(!b); d->labelJPEG2000compression->setEnabled(!b); } } // namespace Digikam diff --git a/core/libs/dimg/loaders/jp2ksettings.h b/core/libs/dimg/loaders/jp2ksettings.h index 8fb5dc9477..d45d3f900a 100644 --- a/core/libs/dimg/loaders/jp2ksettings.h +++ b/core/libs/dimg/loaders/jp2ksettings.h @@ -1,69 +1,69 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-08-02 * Description : save JPEG 2000 image options. * * Copyright (C) 2007-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_JP2K_SETTINGS_H #define DIGIKAM_JP2K_SETTINGS_H // Qt includes #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT JP2KSettings : public QWidget { Q_OBJECT public: explicit JP2KSettings(QWidget* const parent = nullptr); ~JP2KSettings(); void setCompressionValue(int val); - int getCompressionValue() const; + int getCompressionValue() const; void setLossLessCompression(bool b); bool getLossLessCompression() const; Q_SIGNALS: void signalSettingsChanged(); private Q_SLOTS: void slotToggleJPEG2000LossLess(bool); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_JP2K_SETTINGS_H diff --git a/core/libs/dimg/loaders/jpegsettings.cpp b/core/libs/dimg/loaders/jpegsettings.cpp index 90a2319273..4e7d37b24b 100644 --- a/core/libs/dimg/loaders/jpegsettings.cpp +++ b/core/libs/dimg/loaders/jpegsettings.cpp @@ -1,178 +1,178 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-08-02 * Description : save JPEG image options. * * Copyright (C) 2007-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "jpegsettings.h" // Qt includes #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "dnuminput.h" namespace Digikam { class Q_DECL_HIDDEN JPEGSettings::Private { public: explicit Private() : JPEGGrid(nullptr), labelJPEGcompression(nullptr), labelWarning(nullptr), labelSubSampling(nullptr), subSamplingCB(nullptr), JPEGcompression(nullptr) { } QGridLayout* JPEGGrid; QLabel* labelJPEGcompression; QLabel* labelWarning; QLabel* labelSubSampling; QComboBox* subSamplingCB; DIntNumInput* JPEGcompression; }; JPEGSettings::JPEGSettings(QWidget* const parent) : QWidget(parent), d(new Private) { setAttribute(Qt::WA_DeleteOnClose); const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); d->JPEGGrid = new QGridLayout(this); d->JPEGcompression = new DIntNumInput(this); d->JPEGcompression->setDefaultValue(75); d->JPEGcompression->setRange(1, 100, 1); d->labelJPEGcompression = new QLabel(i18n("JPEG quality:"), this); d->JPEGcompression->setWhatsThis(i18n("

The JPEG quality:

" "

1: low quality (high compression and small " "file size)
" "50: medium quality
" "75: good quality (default)
" "100: high quality (no compression and " "large file size)

" "

Note: JPEG always uses lossy compression.

")); d->labelWarning = new QLabel(i18n("" "Warning: JPEG is a " "lossy image compression format." ""), this); d->labelWarning->setOpenExternalLinks(true); d->labelWarning->setFrameStyle(QFrame::Box | QFrame::Plain); d->labelWarning->setLineWidth(1); d->labelWarning->setFrameShape(QFrame::Box); d->labelSubSampling = new QLabel(i18n("Chroma subsampling:"), this); d->subSamplingCB = new QComboBox(this); d->subSamplingCB->insertItem(0, i18n("4:4:4 (best quality)")); // 1x1, 1x1, 1x1 (4:4:4) d->subSamplingCB->insertItem(1, i18n("4:2:2 (good quality)")); // 2x1, 1x1, 1x1 (4:2:2) d->subSamplingCB->insertItem(2, i18n("4:2:0 (low quality)")); // 2x2, 1x1, 1x1 (4:2:0) d->subSamplingCB->insertItem(3, i18n("4:1:1 (low quality)")); // 4x1, 1x1, 1x1 (4:1:1) d->subSamplingCB->setWhatsThis(i18n("

Chroma subsampling reduces file size by taking advantage of the " "eye's lesser sensitivity to color resolution. How perceptible the " "difference is depends on the image - large photos will generally " "show no difference, while sharp, down-scaled pixel graphics may " "lose fine color detail.

" "

4:4:4 - No chroma subsampling, highest " "quality but lowest compression.

" "

4:2:2 - Chroma halved horizontally, average " "compression, average quality.

" "

4:2:0 - Chroma quartered in 2x2 blocks, " "high compression but low quality.

" "

4:1:1 - Chroma quartered in 4x1 blocks, " "high compression but low quality.

" "

Note: JPEG always uses lossy compression.

")); d->JPEGGrid->addWidget(d->labelJPEGcompression, 0, 0, 1, 2); d->JPEGGrid->addWidget(d->JPEGcompression, 1, 0, 1, 2); d->JPEGGrid->addWidget(d->labelSubSampling, 2, 0, 1, 2); d->JPEGGrid->addWidget(d->subSamplingCB, 3, 0, 1, 2); d->JPEGGrid->addWidget(d->labelWarning, 4, 0, 1, 1); d->JPEGGrid->setColumnStretch(1, 10); d->JPEGGrid->setRowStretch(5, 10); d->JPEGGrid->setContentsMargins(spacing, spacing, spacing, spacing); d->JPEGGrid->setSpacing(spacing); connect(d->JPEGcompression, SIGNAL(valueChanged(int)), this, SIGNAL(signalSettingsChanged())); connect(d->subSamplingCB, SIGNAL(activated(int)), this, SIGNAL(signalSettingsChanged())); } JPEGSettings::~JPEGSettings() { delete d; } void JPEGSettings::setCompressionValue(int val) { d->JPEGcompression->setValue(val); } int JPEGSettings::getCompressionValue() const { return d->JPEGcompression->value(); } void JPEGSettings::setSubSamplingValue(int val) { d->subSamplingCB->setCurrentIndex(val); } int JPEGSettings::getSubSamplingValue() const { return d->subSamplingCB->currentIndex(); } int JPEGSettings::convertCompressionForLibJpeg(int value) { // JPEG quality slider settings : 1 - 100 ==> libjpeg settings : 25 - 100. - return((int)((75.0 / 100.0) * (float)value + 26.0 - (75.0 / 100.0))); + return ((int)((75.0 / 100.0) * (float)value + 26.0 - (75.0 / 100.0))); } } // namespace Digikam diff --git a/core/libs/dimg/loaders/pgfsettings.cpp b/core/libs/dimg/loaders/pgfsettings.cpp index 84d1165084..e5e4353190 100644 --- a/core/libs/dimg/loaders/pgfsettings.cpp +++ b/core/libs/dimg/loaders/pgfsettings.cpp @@ -1,149 +1,149 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-06-06 * Description : save PGF image options. * * Copyright (C) 2009-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "pgfsettings.h" // Qt includes #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "dnuminput.h" namespace Digikam { class Q_DECL_HIDDEN PGFSettings::Private { public: explicit Private() + : PGFGrid(nullptr), + labelPGFcompression(nullptr), + PGFLossLess(nullptr), + PGFcompression(nullptr) { - PGFGrid = nullptr; - labelPGFcompression = nullptr; - PGFcompression = nullptr; - PGFLossLess = nullptr; } QGridLayout* PGFGrid; QLabel* labelPGFcompression; QCheckBox* PGFLossLess; DIntNumInput* PGFcompression; }; PGFSettings::PGFSettings(QWidget* const parent) : QWidget(parent), d(new Private) { setAttribute(Qt::WA_DeleteOnClose); const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); - d->PGFGrid = new QGridLayout(this); - d->PGFLossLess = new QCheckBox(i18n("Lossless PGF files"), this); + d->PGFGrid = new QGridLayout(this); + d->PGFLossLess = new QCheckBox(i18n("Lossless PGF files"), this); d->PGFLossLess->setWhatsThis(i18n("

Toggle lossless compression for PGF images.

" "

If this option is enabled, a lossless method will be used " "to compress PGF pictures.

")); d->PGFcompression = new DIntNumInput(this); d->PGFcompression->setDefaultValue(3); d->PGFcompression->setRange(1, 9, 1); d->labelPGFcompression = new QLabel(i18n("PGF quality:"), this); d->PGFcompression->setWhatsThis(i18n("

The quality value for PGF images:

" "

1: high quality (no compression and " "large file size)
" "3: good quality (default)
" "6: medium quality
" "9: low quality (high compression and small " "file size)

" "

Note: PGF is not a lossless image " "compression format when you use this setting.

")); d->PGFGrid->addWidget(d->PGFLossLess, 0, 0, 1, 2); d->PGFGrid->addWidget(d->labelPGFcompression, 1, 0, 1, 2); d->PGFGrid->addWidget(d->PGFcompression, 2, 0, 1, 2); d->PGFGrid->setColumnStretch(1, 10); d->PGFGrid->setRowStretch(3, 10); d->PGFGrid->setContentsMargins(spacing, spacing, spacing, spacing); d->PGFGrid->setSpacing(spacing); connect(d->PGFLossLess, SIGNAL(toggled(bool)), this, SLOT(slotTogglePGFLossLess(bool))); connect(d->PGFLossLess, SIGNAL(toggled(bool)), this, SIGNAL(signalSettingsChanged())); connect(d->PGFcompression, SIGNAL(valueChanged(int)), this, SIGNAL(signalSettingsChanged())); } PGFSettings::~PGFSettings() { delete d; } void PGFSettings::setCompressionValue(int val) { d->PGFcompression->setValue(val); } int PGFSettings::getCompressionValue() const { return d->PGFcompression->value(); } void PGFSettings::setLossLessCompression(bool b) { d->PGFLossLess->setChecked(b); slotTogglePGFLossLess(d->PGFLossLess->isChecked()); } bool PGFSettings::getLossLessCompression() const { return d->PGFLossLess->isChecked(); } void PGFSettings::slotTogglePGFLossLess(bool b) { d->PGFcompression->setEnabled(!b); d->labelPGFcompression->setEnabled(!b); } } // namespace Digikam diff --git a/core/libs/dimg/loaders/pgfsettings.h b/core/libs/dimg/loaders/pgfsettings.h index 62d542fac8..e4567a32bd 100644 --- a/core/libs/dimg/loaders/pgfsettings.h +++ b/core/libs/dimg/loaders/pgfsettings.h @@ -1,69 +1,69 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-06-06 * Description : save PGF image options. * * Copyright (C) 2009-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_PGF_SETTINGS_H #define DIGIKAM_PGF_SETTINGS_H // Qt includes #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT PGFSettings : public QWidget { Q_OBJECT public: explicit PGFSettings(QWidget* const parent = nullptr); ~PGFSettings(); void setCompressionValue(int val); - int getCompressionValue() const; + int getCompressionValue() const; void setLossLessCompression(bool b); bool getLossLessCompression() const; Q_SIGNALS: void signalSettingsChanged(); private Q_SLOTS: void slotTogglePGFLossLess(bool); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_PGF_SETTINGS_H diff --git a/core/libs/dimg/loaders/pngsettings.cpp b/core/libs/dimg/loaders/pngsettings.cpp index cf298a448a..9949b94f28 100644 --- a/core/libs/dimg/loaders/pngsettings.cpp +++ b/core/libs/dimg/loaders/pngsettings.cpp @@ -1,121 +1,121 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-08-02 * Description : save PNG image options. * * Copyright (C) 2007-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "pngsettings.h" // Qt includes #include #include #include #include #include #include // KDE includes #include // Local includes #include "dnuminput.h" namespace Digikam { class Q_DECL_HIDDEN PNGSettings::Private { public: explicit Private() + : PNGGrid(nullptr), + labelPNGcompression(nullptr), + PNGcompression(nullptr) { - PNGGrid = nullptr; - labelPNGcompression = nullptr; - PNGcompression = nullptr; } QGridLayout* PNGGrid; QLabel* labelPNGcompression; DIntNumInput* PNGcompression; }; PNGSettings::PNGSettings(QWidget* parent) : QWidget(parent), d(new Private) { setAttribute(Qt::WA_DeleteOnClose); const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); d->PNGGrid = new QGridLayout(this); d->PNGcompression = new DIntNumInput(this); d->PNGcompression->setDefaultValue(6); d->PNGcompression->setRange(1, 9, 1); d->labelPNGcompression = new QLabel(i18n("PNG compression:"), this); d->PNGcompression->setWhatsThis(i18n("

The compression value for PNG images:

" "

1: low compression (large file size but " "short compression duration - default)
" "5: medium compression
" "9: high compression (small file size but " "long compression duration)

" "

Note: PNG is always a lossless image " "compression format.

")); d->PNGGrid->addWidget(d->labelPNGcompression, 0, 0, 1, 2); d->PNGGrid->addWidget(d->PNGcompression, 1, 1, 1, 2); d->PNGGrid->setColumnStretch(1, 10); d->PNGGrid->setRowStretch(2, 10); d->PNGGrid->setContentsMargins(spacing, spacing, spacing, spacing); d->PNGGrid->setSpacing(spacing); connect(d->PNGcompression, SIGNAL(valueChanged(int)), this, SIGNAL(signalSettingsChanged())); } PNGSettings::~PNGSettings() { delete d; } void PNGSettings::setCompressionValue(int val) { d->PNGcompression->setValue(val); } int PNGSettings::getCompressionValue() const { return d->PNGcompression->value(); } int PNGSettings::convertCompressionForLibPng(int value) { // PNG compression slider settings : 1 - 9 ==> libpng settings : 100 - 1. - return((int)(((1.0 - 100.0) / 8.0) * (float)value + 100.0 - ((1.0 - 100.0) / 8.0))); + return ((int)(((1.0 - 100.0) / 8.0) * (float)value + 100.0 - ((1.0 - 100.0) / 8.0))); } } // namespace Digikam diff --git a/core/libs/dimg/loaders/tiffsettings.cpp b/core/libs/dimg/loaders/tiffsettings.cpp index 6f0df1e0cd..6ab6e9067a 100644 --- a/core/libs/dimg/loaders/tiffsettings.cpp +++ b/core/libs/dimg/loaders/tiffsettings.cpp @@ -1,101 +1,101 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2007-08-02 * Description : save TIFF image options. * * Copyright (C) 2007-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "tiffsettings.h" // Qt includes #include #include #include #include #include #include #include // KDE includes #include namespace Digikam { class Q_DECL_HIDDEN TIFFSettings::Private { public: explicit Private() + : TIFFGrid(nullptr), + TIFFcompression(nullptr) { - TIFFGrid = nullptr; - TIFFcompression = nullptr; } QGridLayout* TIFFGrid; QCheckBox* TIFFcompression; }; TIFFSettings::TIFFSettings(QWidget* const parent) : QWidget(parent), d(new Private) { setAttribute(Qt::WA_DeleteOnClose); - const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); + const int spacing = QApplication::style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); d->TIFFGrid = new QGridLayout(this); d->TIFFcompression = new QCheckBox(i18n("Compress TIFF files"), this); d->TIFFcompression->setWhatsThis(i18n("

Toggle compression for TIFF images.

" "

If this option is enabled, the final size " "of the TIFF image is reduced.

" "

A lossless compression format (Deflate) " "is used to save the file.

")); d->TIFFGrid->addWidget(d->TIFFcompression, 0, 0, 1, 2); d->TIFFGrid->setColumnStretch(1, 10); d->TIFFGrid->setRowStretch(1, 10); d->TIFFGrid->setContentsMargins(spacing, spacing, spacing, spacing); d->TIFFGrid->setSpacing(spacing); connect(d->TIFFcompression, SIGNAL(toggled(bool)), this, SIGNAL(signalSettingsChanged())); } TIFFSettings::~TIFFSettings() { delete d; } void TIFFSettings::setCompression(bool b) { d->TIFFcompression->setChecked(b); } bool TIFFSettings::getCompression() const { return d->TIFFcompression->isChecked(); } } // namespace Digikam diff --git a/core/libs/dplugins/CMakeLists.txt b/core/libs/dplugins/CMakeLists.txt index 72cc008bf1..841eaf4d3b 100644 --- a/core/libs/dplugins/CMakeLists.txt +++ b/core/libs/dplugins/CMakeLists.txt @@ -1,89 +1,89 @@ # # Copyright (c) 2010-2020 by Gilles Caulier, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. include_directories($ $ $ $ $ $ $ $ $ ) if(ENABLE_QWEBENGINE) include_directories($) else() include_directories($) endif() if(KF5KIO_FOUND) include_directories($ $ ) endif() set(libdpluginsinterface_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/core/dplugin.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/dplugingeneric.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/dplugineditor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/dpluginrawimport.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/dplugindimg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/dpluginauthor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/dpluginaction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginloader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginloader_p.cpp ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginconfview.cpp ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginconfviewgeneric.cpp ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginconfvieweditor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginconfviewdimg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginsetup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/setup/dpluginaboutdlg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/iface/dinfointerface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/iface/dmetainfoiface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dplugindialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dwizardpage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dwizarddlg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dsavesettingswidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dwizardpage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dpreviewmanager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/widgets/dpreviewimage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/widgets/ditemslist.cpp ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wscomboboxdelegate.cpp ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wscomboboxintermediate.cpp ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wstooldialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wstoolutils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wssettingswidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wsselectuserdlg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wslogindialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wsnewalbumdialog.cpp ${CMAKE_CURRENT_SOURCE_DIR}/webservices/wssettings.cpp ) -if(ENABLE_QWEBENGINE) - set(libdpluginsinterface_SRCS - ${libdpluginsinterface_SRCS} - ${CMAKE_CURRENT_SOURCE_DIR}/webservices/webwidget_qwebengine.cpp - ) -else() - set(libdpluginsinterface_SRCS - ${libdpluginsinterface_SRCS} - ${CMAKE_CURRENT_SOURCE_DIR}/webservices/webwidget.cpp - ) -endif() +#if(ENABLE_QWEBENGINE) +# set(libdpluginsinterface_SRCS +# ${libdpluginsinterface_SRCS} +# ${CMAKE_CURRENT_SOURCE_DIR}/webservices/webwidget_qwebengine.cpp +# ) +#else() +# set(libdpluginsinterface_SRCS +# ${libdpluginsinterface_SRCS} +# ${CMAKE_CURRENT_SOURCE_DIR}/webservices/webwidget.cpp +# ) +#endif() add_library(dpluginsinterface_src OBJECT ${libdpluginsinterface_SRCS}) # Parse O2 library code and rules at end, and compilation rules remove important Qt definitions. if(WIN32) # NOTE: this O2 export symbols rule must be on top level. add_definitions(-DO2_DLL_EXPORT) endif() add_subdirectory(webservices) diff --git a/core/libs/transitionmngr/effectmngr_p.h b/core/libs/transitionmngr/effectmngr_p.h index 78b40507b7..70138c44a0 100644 --- a/core/libs/transitionmngr/effectmngr_p.h +++ b/core/libs/transitionmngr/effectmngr_p.h @@ -1,109 +1,109 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2017-05-24 * Description : video frame effects manager. * * Copyright (C) 2017-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #ifndef DIGIKAM_EFFECT_MNGR_PRIVATE_H #define DIGIKAM_EFFECT_MNGR_PRIVATE_H // C++ includes #include // Qt includes #include #include #include #include // Local includes #include "effectmngr.h" #include "digikam_config.h" #include "digikam_debug.h" namespace Digikam { class Q_DECL_HIDDEN EffectMngr::Private { public: typedef int (EffectMngr::Private::*EffectMethod)(bool); public: explicit Private() - : eff_curEffect(EffectMngr::None), - eff_isRunning(false), + : eff_isRunning(false), + eff_curEffect(EffectMngr::None), eff_step(0), eff_imgFrames(125) { registerEffects(); } ~Private() { } QMap eff_effectList; QImage eff_image; QImage eff_curFrame; QSize eff_outSize; bool eff_isRunning; EffectMngr::EffectType eff_curEffect; int eff_step; int eff_imgFrames; public: void registerEffects(); EffectMngr::EffectType getRandomEffect() const; private: /** * Internal functions to render an effect frame. * The effect movement must be adjusted accordingly with amount of image frames to encode. * aInit is to true when effect is initialized (first call). * The integer value is a tempo in ms to wait between frames, * or -1 if the effect is completed. */ int effectNone(bool aInit); int effectRandom(bool aInit); int effectKenBurnsZoomIn(bool aInit); int effectKenBurnsZoomOut(bool aInit); int effectKenBurnsPanLR(bool aInit); int effectKenBurnsPanRL(bool aInit); int effectKenBurnsPanTB(bool aInit); int effectKenBurnsPanBT(bool aInit); void updateCurrentFrame(const QRectF& area); }; } // namespace Digikam #endif // DIGIKAM_EFFECT_MNGR_PRIVATE_H diff --git a/core/tests/dialogs/browser.cpp b/core/tests/dialogs/browser.cpp index 4ee229cfa7..d9d599e492 100644 --- a/core/tests/dialogs/browser.cpp +++ b/core/tests/dialogs/browser.cpp @@ -1,51 +1,51 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2017-05-25 * Description : a stand alone tool to browse a web page. * * Copyright (C) 2017-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ // Qt includes #include #include #include // Local includes #include "webbrowserdlg.h" using namespace Digikam; int main(int argc, char* argv[]) { QApplication a(argc, argv); if (argc == 1) { qDebug() << "browser - web page url to show"; qDebug() << "Usage: url top open"; return -1; } - WebBrowserDlg browser(QUrl(QString::fromUtf8(argv[1]))); + WebBrowserDlg browser(QUrl(QString::fromUtf8(argv[1])), nullptr); browser.show(); return a.exec(); } diff --git a/project/bundles/3rdparty/ext_qtav/CMakeLists.txt b/project/bundles/3rdparty/ext_qtav/CMakeLists.txt index 2639f733df..41a8cfb773 100644 --- a/project/bundles/3rdparty/ext_qtav/CMakeLists.txt +++ b/project/bundles/3rdparty/ext_qtav/CMakeLists.txt @@ -1,134 +1,131 @@ # Script to build QtAV for digiKam bundle. # # Copyright (c) 2015-2020 by Gilles Caulier # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # set(PREFIX_ext_qtav "${EXTPREFIX}") set(QTAV_VERSION v1.13.0) #set(QTAV_VERSION master) if(MINGW) if (${MXE_ARCHBITS} MATCHES "64") set(LIB_DIR "lib_win_x86_64") else() set(LIB_DIR "lib_win_x86") endif() ExternalProject_Add(ext_qtav DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} GIT_REPOSITORY https://github.com/wang-bin/QtAV.git # GIT_TAG ${QTAV_VERSION} CONFIGURE_COMMAND git submodule update --init && rm -rfv ${MXE_INSTALL_PREFIX}/qt5/include/QtAV && rm -rfv ${MXE_INSTALL_PREFIX}/qt5/include/QtAVWidgets && rm -fv ${MXE_INSTALL_PREFIX}/qt5/lib/libQtAV1.a && rm -fv ${MXE_INSTALL_PREFIX}/qt5/lib/libQtAVWidgets1.a && rm -fv ${MXE_INSTALL_PREFIX}/qt5/bin/QtAV1.dll && rm -fv ${MXE_INSTALL_PREFIX}/qt5/bin/QtAVWidgets1.dll && mkdir build && cd build && ${MXE_INSTALL_PREFIX}/qt5/bin/qmake CONFIG+=no_examples .. BUILD_COMMAND cd build && $(MAKE) INSTALL_COMMAND cp -rfv /src/QtAV ${MXE_INSTALL_PREFIX}/qt5/include/ && cp -rfv /widgets/QtAVWidgets ${MXE_INSTALL_PREFIX}/qt5/include/ && cp -fv /build/${LIB_DIR}/libQtAV1.a ${MXE_INSTALL_PREFIX}/qt5/lib/ && cp -fv /build/${LIB_DIR}/libQtAVWidgets1.a ${MXE_INSTALL_PREFIX}/qt5/lib/ && cp -fv /build/${LIB_DIR}/QtAV1.dll ${MXE_INSTALL_PREFIX}/qt5/bin/ && cp -fv /build/${LIB_DIR}/QtAVWidgets1.dll ${MXE_INSTALL_PREFIX}/qt5/bin/ BUILD_IN_SOURCE 1 UPDATE_COMMAND "" ALWAYS 0 ) elseif(APPLE) ExternalProject_Add(ext_qtav DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} GIT_REPOSITORY https://github.com/wang-bin/QtAV.git # GIT_TAG ${QTAV_VERSION} PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/qtav-rules-macos.patch CONFIGURE_COMMAND git submodule update --init && rm -rfv ${INSTALL_ROOT}/libexec/qt5/lib/QtAV.framework && rm -rfv ${INSTALL_ROOT}/libexec/qt5/lib/QtAVWidgets.framework && rm -rfv ${INSTALL_ROOT}/libexec/qt5/include/QtAV && rm -rfv ${INSTALL_ROOT}/libexec/qt5/include/QtAVWidgets && mkdir build && cd build && qmake .. BUILD_COMMAND cd build && $(MAKE) INSTALL_COMMAND install_name_tool -id ${INSTALL_ROOT}/libexec/qt5/lib/QtAV.framework/Versions/1/QtAV /build/lib_osx_x86_64_llvm/QtAV.framework/QtAV && install_name_tool -id ${INSTALL_ROOT}/libexec/qt5/lib/QtAVWidgets.framework/Versions/1/QtAVWidgets /build/lib_osx_x86_64_llvm/QtAVWidgets.framework/QtAVWidgets && install_name_tool -change QtAV.framework/Versions/1/QtAV ${INSTALL_ROOT}/libexec/qt5/lib/QtAV.framework/Versions/1/QtAV /build/lib_osx_x86_64_llvm/QtAVWidgets.framework/QtAVWidgets && cp -rfv /build/lib_osx_x86_64_llvm/QtAV.framework ${INSTALL_ROOT}/libexec/qt5/lib/ && cp -rfv /build/lib_osx_x86_64_llvm/QtAVWidgets.framework ${INSTALL_ROOT}/libexec/qt5/lib/ && ln -s ${INSTALL_ROOT}/libexec/qt5/lib/QtAV.framework/Headers ${INSTALL_ROOT}/libexec/qt5/include/QtAV && ln -s ${INSTALL_ROOT}/libexec/qt5/lib/QtAVWidgets.framework/Headers ${INSTALL_ROOT}/libexec/qt5/include/QtAVWidgets BUILD_IN_SOURCE 1 UPDATE_COMMAND "" ALWAYS 0 ) else() #Linux set(LIB_DIR "lib") ExternalProject_Add(ext_qtav DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} -# URL https://sourceforge.net/projects/qtav/files/v1.12.0/v1.12.0.tar.gz/download -# URL_MD5 59d858d19706d75099a9dba531572529 - GIT_REPOSITORY https://github.com/wang-bin/QtAV.git # GIT_TAG ${QTAV_VERSION} CONFIGURE_COMMAND cd && git submodule update --init && rm -rfv ${INSTALL_ROOT}/include/QtAV && rm -rfv ${INSTALL_ROOT}/include/QtAVWidgets && rm -fv ${INSTALL_ROOT}/lib/libQtAV.so && rm -fv ${INSTALL_ROOT}/lib/libQtAV.so.1 && rm -fv ${INSTALL_ROOT}/lib/libQtAV.so.1.13 && rm -fv ${INSTALL_ROOT}/lib/libQtAV.so.1.13.0 && rm -fv ${INSTALL_ROOT}/lib/libQtAVWidgets.so && rm -fv ${INSTALL_ROOT}/lib/libQtAVWidgets.so.1 && rm -fv ${INSTALL_ROOT}/lib/libQtAVWidgets.so.1.13 && rm -fv ${INSTALL_ROOT}/lib/libQtAVWidgets.so.1.13.0 && cd && mkdir build && cd build && $(CMAKE_COMMAND) .. -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_qtav} -DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE} -DBUILD_EXAMPLES=OFF -DBUILD_TESTS=OFF -DBUILD_QT5OPENGL=ON BUILD_COMMAND cd /build && $(MAKE) INSTALL_COMMAND cp -rfv /src/QtAV ${INSTALL_ROOT}/include/ && cp -rfv /widgets/QtAVWidgets ${INSTALL_ROOT}/include/ && cp -fv /build/${LIB_DIR}/libQtAV.so ${INSTALL_ROOT}/lib/ && cp -fv /build/${LIB_DIR}/libQtAV.so.1 ${INSTALL_ROOT}/lib/ && cp -fv /build/${LIB_DIR}/libQtAV.so.1.13.0 ${INSTALL_ROOT}/lib/ && cp -fv /build/${LIB_DIR}/libQtAVWidgets.so ${INSTALL_ROOT}/lib/ && cp -fv /build/${LIB_DIR}/libQtAVWidgets.so.1 ${INSTALL_ROOT}/lib/ && cp -fv /build/${LIB_DIR}/libQtAVWidgets.so.1.13.0 ${INSTALL_ROOT}/lib/ UPDATE_COMMAND "" ALWAYS 0 ) endif() diff --git a/project/bundles/appimage/01-build-host.sh b/project/bundles/appimage/01-build-host.sh index c412031b69..b49e2ad13f 100755 --- a/project/bundles/appimage/01-build-host.sh +++ b/project/bundles/appimage/01-build-host.sh @@ -1,202 +1,200 @@ #!/bin/bash # Script to build a Linux Host installation to compile an AppImage bundle of digiKam. # This script must be run as sudo # # Copyright (c) 2015-2020 by Gilles Caulier # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # # Halt and catch errors set -eE trap 'PREVIOUS_COMMAND=$THIS_COMMAND; THIS_COMMAND=$BASH_COMMAND' DEBUG trap 'echo "FAILED COMMAND: $PREVIOUS_COMMAND"' ERR ################################################################################################# # Manage script traces to log file mkdir -p ./logs exec > >(tee ./logs/build-host.full.log) 2>&1 ################################################################################################# echo "01-build-host.sh : build a Linux host installation to compile an AppImage bundle." echo "---------------------------------------------------------------------------------" ################################################################################################# # Pre-processing checks . ./config.sh . ./common.sh ChecksRunAsRoot StartScript ChecksCPUCores HostAdjustments RegisterRemoteServers ORIG_WD="`pwd`" ################################################################################################# echo -e "---------- Update Linux Host\n" urpmi --auto --auto-update ################################################################################################# if [[ "$(arch)" = "x86_64" ]] ; then LIBSUFFIX=lib64 else LIBSUFFIX=lib fi echo -e "---------- Install New Development Packages\n" # Packages for base dependencies and Qt5. urpmi --auto \ wget \ tar \ bzip2 \ gettext \ git \ subversion \ libtool \ which \ fuse \ automake \ cmake \ gcc-c++ \ patch \ libdrm-devel \ libxcb \ libxcb-devel \ xcb-util-keysyms-devel \ xcb-util-devel \ xkeyboard-config \ xscreensaver \ gperf \ ruby \ bison \ flex \ zlib-devel \ expat-devel \ fuse-devel \ glibc-devel \ mysql-devel \ eigen3-devel \ openssl-devel \ cppunit-devel \ libstdc++-devel \ libxml2-devel \ libstdc++-devel \ lcms2-devel \ glibc-devel \ libudev-devel \ sqlite-devel \ libexif-devel \ libxslt-devel \ xz-devel \ lz4-devel \ inotify-tools-devel \ openssl-devel \ cups-devel \ openal-soft-devel \ libical-devel \ libcap-devel \ fontconfig-devel \ freetype-devel \ patchelf \ dpkg \ python \ ruby \ ruby-devel \ sqlite3-devel \ ffmpeg-devel \ boost-devel \ gphoto2-devel \ sane-backends \ jasper-devel \ ${LIBSUFFIX}xkbcommon-devel \ ${LIBSUFFIX}sane1-devel \ ${LIBSUFFIX}xcb-util1 \ ${LIBSUFFIX}xi-devel \ ${LIBSUFFIX}xtst-devel \ ${LIBSUFFIX}xrandr-devel \ ${LIBSUFFIX}xcursor-devel \ ${LIBSUFFIX}xcomposite-devel \ ${LIBSUFFIX}xrender-devel \ ${LIBSUFFIX}mesagl1-devel \ ${LIBSUFFIX}mesaglu1-devel \ ${LIBSUFFIX}mesaegl1-devel \ ${LIBSUFFIX}mesaegl1 \ ${LIBSUFFIX}ltdl-devel \ ${LIBSUFFIX}glib2.0-devel \ ${LIBSUFFIX}usb1.0-devel \ ${LIBSUFFIX}jpeg-devel \ ${LIBSUFFIX}png-devel \ ${LIBSUFFIX}tiff-devel \ ${LIBSUFFIX}lqr-devel \ ${LIBSUFFIX}fftw-devel \ ${LIBSUFFIX}curl-devel \ ${LIBSUFFIX}magick-devel ################################################################################################# echo -e "---------- Clean-up Old Packages\n" # Remove system based devel package to prevent conflict with new one. urpme --auto --force ${LIBSUFFIX}qt5core5 || true ################################################################################################# echo -e "---------- Prepare Linux host to Compile Extra Dependencies\n" # Workaround for: On Mageia 6, .pc files in /usr/lib/pkgconfig are not recognized # However, this is where .pc files get installed when bulding libraries... (FIXME) # I found this by comparing the output of librevenge's "make install" command # between Ubuntu and CentOS 6 ln -sf /usr/share/pkgconfig /usr/lib/pkgconfig # Make sure we build from the /, parts of this script depends on that. We also need to run as root... cd / # Create the build dir for the 3rdparty deps if [ ! -d $BUILDING_DIR ] ; then mkdir $BUILDING_DIR fi if [ ! -d $DOWNLOAD_DIR ] ; then mkdir $DOWNLOAD_DIR fi ################################################################################################# cd $BUILDING_DIR rm -rf $BUILDING_DIR/* || true cmake $ORIG_WD/../3rdparty \ -DCMAKE_INSTALL_PREFIX:PATH=/usr \ -DINSTALL_ROOT=/usr \ -DEXTERNALS_DOWNLOAD_DIR=$DOWNLOAD_DIR \ -DENABLE_QTWEBENGINE=$DK_QTWEBENGINE # Low level libraries and Qt5 dependencies # NOTE: The order to compile each component here is very important. #cmake --build . --config RelWithDebInfo --target ext_libicu -- -j$CPU_CORES cmake --build . --config RelWithDebInfo --target ext_qt -- -j$CPU_CORES # depend of tiff, png, jpeg if [[ $DK_QTWEBENGINE = 0 ]] ; then cmake --build . --config RelWithDebInfo --target ext_qtwebkit -- -j$CPU_CORES # depend of Qt and libicu fi -cmake --build . --config RelWithDebInfo --target ext_qtav -- -j$CPU_CORES # depend of qt and ffmpeg - cmake --build . --config RelWithDebInfo --target ext_exiv2 -- -j$CPU_CORES cmake --build . --config RelWithDebInfo --target ext_opencv -- -j$CPU_CORES ################################################################################################# TerminateScript diff --git a/project/bundles/appimage/03-build-digikam.sh b/project/bundles/appimage/03-build-digikam.sh index b34072852e..851d172838 100755 --- a/project/bundles/appimage/03-build-digikam.sh +++ b/project/bundles/appimage/03-build-digikam.sh @@ -1,188 +1,189 @@ #! /bin/bash # Script to build digiKam under Linux host # This script must be run as sudo # # Copyright (c) 2015-2020 by Gilles Caulier # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # # Halt and catch errors set -eE trap 'PREVIOUS_COMMAND=$THIS_COMMAND; THIS_COMMAND=$BASH_COMMAND' DEBUG trap 'echo "FAILED COMMAND: $PREVIOUS_COMMAND"' ERR if [ "root" != "$USER" ]; then echo "This script must be run as root..." exit fi ################################################################################################# # Manage script traces to log file mkdir -p ./logs exec > >(tee ./logs/build-digikam.full.log) 2>&1 ################################################################################################# echo "03-build-digikam.sh : build digiKam." echo "------------------------------------" ################################################################################################# # Pre-processing checks . ./config.sh . ./common.sh ChecksRunAsRoot StartScript ChecksCPUCores HostAdjustments RegisterRemoteServers ################################################################################################# # Pathes rules ORIG_PATH="$PATH" ORIG_WD="`pwd`" ################################################################################################# # Install out-dated dependencies cd $BUILDING_DIR rm -rf $BUILDING_DIR/* || true cmake $ORIG_WD/../3rdparty \ -DCMAKE_INSTALL_PREFIX:PATH=/usr \ -DINSTALL_ROOT=/usr \ -DEXTERNALS_DOWNLOAD_DIR=$DOWNLOAD_DIR \ -DENABLE_QTWEBENGINE=$DK_QTWEBENGINE +cmake --build . --config RelWithDebInfo --target ext_qtav -- -j$CPU_CORES # depend of qt and ffmpeg cmake --build . --config RelWithDebInfo --target ext_lensfun -- -j$CPU_CORES ################################################################################################# # Build digiKam in temporary directory and installation if [ -d "$DK_BUILDTEMP/digikam-$DK_VERSION" ] ; then echo "---------- Updating existing $DK_BUILDTEMP" cd "$DK_BUILDTEMP" cd digikam-$DK_VERSION git reset --hard git pull mkdir -p build cd build else echo "---------- Creating $DK_BUILDTEMP" mkdir -p "$DK_BUILDTEMP" if [ $? -ne 0 ] ; then echo "---------- Cannot create $DK_BUILDTEMP directory." echo "---------- Aborting..." exit; fi cd "$DK_BUILDTEMP" echo -e "\n\n" echo "---------- Downloading digiKam $DK_VERSION" git clone --progress --verbose $DK_GITURL digikam-$DK_VERSION cd digikam-$DK_VERSION if [ $? -ne 0 ] ; then echo "---------- Cannot clone repositories." echo "---------- Aborting..." exit; fi git checkout $DK_VERSION mkdir build cd build fi echo -e "\n\n" echo "---------- Configure digiKam $DK_VERSION" cmake -G "Unix Makefiles" .. \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_INSTALL_PREFIX=/usr \ -DBUILD_TESTING=OFF \ -DDIGIKAMSC_CHECKOUT_PO=ON \ -DDIGIKAMSC_CHECKOUT_DOC=OFF \ -DDIGIKAMSC_COMPILE_PO=ON \ -DDIGIKAMSC_COMPILE_DOC=OFF \ -DDIGIKAMSC_COMPILE_DIGIKAM=ON \ -DENABLE_KFILEMETADATASUPPORT=OFF \ -DENABLE_AKONADICONTACTSUPPORT=OFF \ -DENABLE_MYSQLSUPPORT=ON \ -DENABLE_INTERNALMYSQL=ON \ -DENABLE_MEDIAPLAYER=ON \ -DENABLE_DBUS=OFF \ -DENABLE_APPSTYLES=ON \ -DENABLE_QWEBENGINE=$DK_QTWEBENGINE \ -DENABLE_FACESENGINE_DNN=ON \ -DENABLE_KIO=OFF \ -DENABLE_LEGACY=OFF \ -Wno-dev if [ $? -ne 0 ]; then echo "---------- Cannot configure digiKam $DK_VERSION." echo "---------- Aborting..." exit; fi cat ../build/core/app/utils/digikam_version.h | grep "digikam_version\[\]" | awk '{print $6}' | tr -d '";' > $ORIG_WD/data/RELEASEID.txt echo -e "\n\n" echo "---------- Building digiKam $DK_VERSION" make -j$CPU_CORES if [ $? -ne 0 ]; then echo "---------- Cannot compile digiKam $DK_VERSION." echo "---------- Aborting..." exit; fi echo -e "\n\n" echo "---------- Installing digiKam $DK_VERSION" echo -e "\n\n" make install/fast && cd "$ORIG_WD" && rm -rf "$DK_BUILDTEMP" if [ $? -ne 0 ]; then echo "---------- Cannot install digiKam $DK_VERSION." echo "---------- Aborting..." exit; fi ################################################################################################# # Install Extra Plugins cd $BUILDING_DIR rm -rf $BUILDING_DIR/* || true cmake $ORIG_WD/../3rdparty \ -DCMAKE_INSTALL_PREFIX:PATH=/usr \ -DINSTALL_ROOT=/usr \ -DEXTERNALS_DOWNLOAD_DIR=$DOWNLOAD_DIR \ -DENABLE_QTWEBENGINE=$DK_QTWEBENGINE cmake --build . --config RelWithDebInfo --target ext_gmic_qt -- -j$CPU_CORES ################################################################################################# export PATH=$ORIG_PATH TerminateScript diff --git a/project/bundles/macports/01-build-macports.sh b/project/bundles/macports/01-build-macports.sh index b028f0b79c..bbd532976a 100755 --- a/project/bundles/macports/01-build-macports.sh +++ b/project/bundles/macports/01-build-macports.sh @@ -1,313 +1,312 @@ #! /bin/bash # Script to build a bundle Macports installation with all digiKam dependencies in a dedicated directory # This script must be run as sudo # # Copyright (c) 2015 by Shanti, # Copyright (c) 2015-2020 by Gilles Caulier # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # # Ask to run as root (( EUID != 0 )) && exec sudo -- "$0" "$@" # Halt and catch errors set -eE trap 'PREVIOUS_COMMAND=$THIS_COMMAND; THIS_COMMAND=$BASH_COMMAND' DEBUG trap 'echo "FAILED COMMAND: $PREVIOUS_COMMAND"' ERR ################################################################################################# # Manage script traces to log file mkdir -p ./logs exec > >(tee ./logs/build-macports.full.log) 2>&1 ################################################################################################# echo "01-build-macports.sh : build a bundle Macports install with digiKam dependencies." echo "---------------------------------------------------------------------------------" ################################################################################################# # Pre-processing checks . ./config.sh . ./common.sh StartScript ChecksRunAsRoot ChecksXCodeCLI ChecksCPUCores OsxCodeName #RegisterRemoteServers ################################################################################################# # Pathes rules ORIG_PATH="$PATH" ORIG_WD="`pwd`" export PATH=$INSTALL_PREFIX/bin:/$INSTALL_PREFIX/sbin:/$INSTALL_PREFIX/libexec/qt5/bin:$ORIG_PATH ################################################################################################# # Check if /opt exists and standard Macports install path if [ -d "/opt" ] ; then if [ -d "/opt/local" ] ; then echo "---------- A standard Macports install exists on /opt/local." echo " To prevent wrong links from this bundle to this repository" echo " this one must be disabled (moving to /opt/local.back for ex)." echo "---------- Aborting..." exit; fi else echo "---------- /opt do not exist, creating" mkdir "/opt" if [ $? -ne 0 ] ; then echo "---------- Cannot create /opt directory." echo "---------- Aborting..." exit; fi fi ################################################################################################# # Check if a previous bundle already exist CONTINUE_INSTALL=0 if [ -d "$INSTALL_PREFIX" ] ; then read -p "$INSTALL_PREFIX already exist. Do you want to remove it or to continue an aborted previous installation ? [(r)emove/(c)ontinue/(s)top] " answer if echo "$answer" | grep -iq "^r" ;then echo "---------- Removing existing $INSTALL_PREFIX" mv $INSTALL_PREFIX $INSTALL_PREFIX.old rm -rf $INSTALL_PREFIX.old elif echo "$answer" | grep -iq "^c" ;then echo "---------- Continue aborted previous installation in $INSTALL_PREFIX" CONTINUE_INSTALL=1 else echo "---------- Aborting..." exit; fi fi if [[ $CONTINUE_INSTALL == 0 ]]; then ################################################################################################# # Target directory creation echo "---------- Creating $INSTALL_PREFIX" mkdir "$INSTALL_PREFIX" if [ $? -ne 0 ] ; then echo "---------- Cannot create target install directory $INSTALL_PREFIX" echo "---------- Aborting..." exit; fi ################################################################################################# # Check latest Macports version available if necessary if [ -z $MP_VERSION ] ; then MP_LASTEST_VER=$(curl $MP_URL | \ egrep -o 'href="MacPorts-[0-9]+\.[0-9]+\.[0-9]+' | \ sed 's/^href="MacPorts-//' | \ sort -t. -rn -k1,1 -k2,2 -k3,3 | head -1) if [ -z $MP_LASTEST_VER ] ; then echo "---------- Cannot check the lastest Macports verion from $MP_URL" echo "---------- Aborting..." exit; fi echo "---------- Detected lastest Macports version : $MP_LASTEST_VER" MP_VERSION=$MP_LASTEST_VER fi ################################################################################################# # Build Macports in temporary directory and installation if [ -d "$MP_BUILDTEMP" ] ; then echo "---------- Removing existing $MP_BUILDTEMP" rm -rf "$MP_BUILDTEMP" fi echo "---------- Creating $MP_BUILDTEMP" mkdir "$MP_BUILDTEMP" if [ $? -ne 0 ] ; then echo "---------- Cannot create temporary directory $MP_BUILDTEMP to compile Macports" echo "---------- Aborting..." exit; fi cd "$MP_BUILDTEMP" echo -e "\n\n" echo "---------- Downloading MacPorts $MP_VERSION" curl -o "MacPorts-$MP_VERSION.tar.bz2" "$MP_URL/MacPorts-$MP_VERSION.tar.bz2" tar jxvf MacPorts-$MP_VERSION.tar.bz2 cd MacPorts-$MP_VERSION echo -e "\n\n" echo "---------- Configuring MacPorts" ./configure --prefix="$INSTALL_PREFIX" \ --with-applications-dir="$INSTALL_PREFIX/Applications" \ --with-no-root-privileges \ --with-install-user="$(id -n -u)" \ --with-install-group="$(id -n -g)" echo -e "\n\n" echo "---------- Building MacPorts" make -j$CPU_CORES echo -e "\n\n" echo "---------- Installing MacPorts" echo -e "\n\n" make install && cd "$ORIG_WD" && rm -rf "$MP_BUILDTEMP" cat << EOF >> "$INSTALL_PREFIX/etc/macports/macports.conf" +no_root -startupitem startupitem_type none startupitem_install no macosx_deployment_target $OSX_MIN_TARGET EOF fi ################################################################################################# # Macports update echo -e "\n" echo "---------- Updating MacPorts" port -v selfupdate if [[ $CONTINUE_INSTALL == 0 ]]; then # port -v upgrade outdated echo -e "\n" #echo "---------- Modifying net-snmp portfile to install when not root" #sed -e "/install.asroot/ s|yes|no|" -i ".orig" "`port file net-snmp`" fi ################################################################################################# # Dependencies build and installation echo -e "\n" echo "---------- Building digiKam dependencies with Macports" # With OSX less than El Capitan, we need a more recent Clang compiler than one provided by XCode. if [[ $MAJOR_OSX_VERSION -lt 10 ]]; then echo "---------- Install more recent Clang compiler from Macports for specific ports" port install clang_select port install clang-3.4 port select --set clang mp-clang-3.4 fi echo -e "\n" port install \ ld64 +ld64_xcode \ cmake \ libpng \ jpeg \ tiff \ boost \ eigen3 \ gettext \ libusb \ libgphoto2 \ jasper \ lcms2 \ expat \ libxml2 \ libxslt \ libical \ lensfun \ bison \ x265 \ ffmpeg \ qt5-qtbase \ qt5-qtdeclarative \ qt5-qtmacextras \ qt5-qtquickcontrols \ qt5-qtxmlpatterns \ qt5-qtsvg \ qt5-qttools \ qt5-qttranslations \ qt5-qtimageformats \ qt5-sqlite-plugin \ qt5-mysql-plugin \ wget +ssl \ ImageMagick if [[ $DK_QTWEBENGINE = 1 ]] ; then port install qt5-qtwebengine fi # sane-backends echo -e "\n" echo -e "---------- Compilation logs of Macports packages with suspicious installation\n" find $INSTALL_PREFIX/var/macports/logs/ -name main.log echo -e "\n----------" echo -e "\n" ################################################################################################# # Create the build dir for the 3rdparty deps if [ ! -d $BUILDING_DIR ] ; then mkdir $BUILDING_DIR fi if [ ! -d $DOWNLOAD_DIR ] ; then mkdir $DOWNLOAD_DIR fi cd $BUILDING_DIR rm -rf $BUILDING_DIR/* || true cmake $ORIG_WD/../3rdparty \ -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_PREFIX \ -DINSTALL_ROOT=$INSTALL_PREFIX \ -DEXTERNALS_DOWNLOAD_DIR=$DOWNLOAD_DIR \ -DENABLE_QTWEBENGINE=$DK_QTWEBENGINE \ -Wno-dev if [[ $DK_QTWEBENGINE = 0 ]] ; then cmake --build . --config RelWithDebInfo --target ext_qtwebkit -- -j$CPU_CORES fi cmake --build . --config RelWithDebInfo --target ext_opencv -- -j$CPU_CORES cmake --build . --config RelWithDebInfo --target ext_exiv2 -- -j$CPU_CORES -cmake --build . --config RelWithDebInfo --target ext_qtav -- -j$CPU_CORES ################################################################################################# export PATH=$ORIG_PATH TerminateScript diff --git a/project/bundles/macports/03-build-digikam.sh b/project/bundles/macports/03-build-digikam.sh index b6a6cbd42a..0636bd4cfc 100755 --- a/project/bundles/macports/03-build-digikam.sh +++ b/project/bundles/macports/03-build-digikam.sh @@ -1,188 +1,189 @@ #! /bin/bash # Script to build digiKam using MacPorts # This script must be run as sudo # # Copyright (c) 2015-2020 by Gilles Caulier # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # # Ask to run as root (( EUID != 0 )) && exec sudo -- "$0" "$@" # Halt and catch errors set -eE trap 'PREVIOUS_COMMAND=$THIS_COMMAND; THIS_COMMAND=$BASH_COMMAND' DEBUG trap 'echo "FAILED COMMAND: $PREVIOUS_COMMAND"' ERR ################################################################################################# # Manage script traces to log file mkdir -p ./logs exec > >(tee ./logs/build-digikam.full.log) 2>&1 ################################################################################################# echo "03-build-digikam.sh : build digiKam using MacPorts." echo "---------------------------------------------------" ################################################################################################# # Pre-processing checks . ./config.sh . ./common.sh StartScript ChecksRunAsRoot ChecksXCodeCLI ChecksCPUCores OsxCodeName #RegisterRemoteServers ################################################################################################# # Pathes rules ORIG_PATH="$PATH" ORIG_WD="`pwd`" export PATH=$INSTALL_PREFIX/bin:/$INSTALL_PREFIX/sbin:$ORIG_PATH ################################################################################################# # Install out-dated dependencies cd $BUILDING_DIR rm -rf $BUILDING_DIR/* || true cmake $ORIG_WD/../3rdparty \ -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_PREFIX \ -DINSTALL_ROOT=$INSTALL_PREFIX \ -DEXTERNALS_DOWNLOAD_DIR=$DOWNLOAD_DIR \ -DENABLE_QTWEBENGINE=$DK_QTWEBENGINE \ -Wno-dev +cmake --build . --config RelWithDebInfo --target ext_qtav -- -j$CPU_CORES cmake --build . --config RelWithDebInfo --target ext_lensfun -- -j$CPU_CORES ################################################################################################# # Build digiKam in temporary directory and installation if [ -d "$DK_BUILDTEMP/digikam-$DK_VERSION" ] ; then echo "---------- Updating existing $DK_BUILDTEMP" cd "$DK_BUILDTEMP" cd digikam-$DK_VERSION git reset --hard git pull mkdir -p build else echo "---------- Creating $DK_BUILDTEMP" mkdir -p "$DK_BUILDTEMP" if [ $? -ne 0 ] ; then echo "---------- Cannot create $DK_BUILDTEMP directory." echo "---------- Aborting..." exit; fi cd "$DK_BUILDTEMP" echo -e "\n\n" echo "---------- Downloading digiKam $DK_VERSION" git clone --progress --verbose $DK_GITURL digikam-$DK_VERSION cd digikam-$DK_VERSION if [ $? -ne 0 ] ; then echo "---------- Cannot clone repositories." echo "---------- Aborting..." exit; fi git checkout $DK_VERSION mkdir build fi echo -e "\n\n" echo "---------- Configure digiKam $DK_VERSION" sed -e "s/DIGIKAMSC_CHECKOUT_PO=OFF/DIGIKAMSC_CHECKOUT_PO=ON/g" ./bootstrap.macports > ./tmp.macports ; mv -f ./tmp.macports ./bootstrap.macports sed -e "s/DIGIKAMSC_COMPILE_PO=OFF/DIGIKAMSC_COMPILE_PO=ON/g" ./bootstrap.macports > ./tmp.macports ; mv -f ./tmp.macports ./bootstrap.macports sed -e "s/DBUILD_TESTING=ON/DBUILD_TESTING=OFF/g" ./bootstrap.macports > ./tmp.macports ; mv -f ./tmp.macports ./bootstrap.macports sed -e "s/DENABLE_DBUS=ON/DENABLE_DBUS=OFF/g" ./bootstrap.macports > ./tmp.macports ; mv -f ./tmp.macports ./bootstrap.macports if [[ $DK_QTWEBENGINE = 1 ]] ; then sed -e "s/DENABLE_QWEBENGINE=OFF/DENABLE_QWEBENGINE=ON/g" ./bootstrap.macports > ./tmp.macports ; mv -f ./tmp.macports ./bootstrap.macports fi chmod +x ./bootstrap.macports cp -f $ORIG_WD/fixbundledatapath.sh $DK_BUILDTEMP/digikam-$DK_VERSION ./fixbundledatapath.sh ./bootstrap.macports "$INSTALL_PREFIX" "Debug" "x86_64" "-Wno-dev" if [ $? -ne 0 ]; then echo "---------- Cannot configure digiKam $DK_VERSION." echo "---------- Aborting..." exit; fi cat ./build/core/app/utils/digikam_version.h | grep "digikam_version\[\]" | awk '{print $6}' | tr -d '";' > $ORIG_WD/data/RELEASEID.txt echo -e "\n\n" echo "---------- Building digiKam $DK_VERSION" cd build make -j$CPU_CORES if [ $? -ne 0 ]; then echo "---------- Cannot compile digiKam $DK_VERSION." echo "---------- Aborting..." exit; fi echo -e "\n\n" echo "---------- Installing digiKam $DK_VERSION" echo -e "\n\n" make install/fast && cd "$ORIG_WD" && rm -rf "$DK_BUILDTEMP" if [ $? -ne 0 ]; then echo "---------- Cannot install digiKam $DK_VERSION." echo "---------- Aborting..." exit; fi ################################################################################################# # Install Extra Plugins cd $BUILDING_DIR rm -rf $BUILDING_DIR/* || true cmake $ORIG_WD/../3rdparty \ -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_PREFIX \ -DINSTALL_ROOT=$INSTALL_PREFIX \ -DEXTERNALS_DOWNLOAD_DIR=$DOWNLOAD_DIR \ -DENABLE_QTWEBENGINE=$DK_QTWEBENGINE \ -Wno-dev cmake --build . --config RelWithDebInfo --target ext_gmic_qt -- -j$CPU_CORES mv -f $INSTALL_PREFIX/libexec/qt5/plugins/digikam/editor/*.so $INSTALL_PREFIX/lib/plugins/digikam/editor/ ################################################################################################# export PATH=$ORIG_PATH TerminateScript diff --git a/project/bundles/mxe/01-build-mxe.sh b/project/bundles/mxe/01-build-mxe.sh index 77f9aaabed..96cbabcc24 100755 --- a/project/bundles/mxe/01-build-mxe.sh +++ b/project/bundles/mxe/01-build-mxe.sh @@ -1,186 +1,185 @@ #! /bin/bash # Script to build a bundle MXE installation with all digiKam low level dependencies in a dedicated directory. # # Copyright (c) 2015-2020 by Gilles Caulier # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # # Halt and catch errors set -eE trap 'PREVIOUS_COMMAND=$THIS_COMMAND; THIS_COMMAND=$BASH_COMMAND' DEBUG trap 'echo "FAILED COMMAND: $PREVIOUS_COMMAND"' ERR ################################################################################################# # Manage script traces to log file mkdir -p ./logs exec > >(tee ./logs/build-mxe.full.log) 2>&1 ################################################################################################# echo "01-build-mxe.sh : build a bundle MXE install with digiKam dependencies." echo "-----------------------------------------------------------------------" ################################################################################################# # Pre-processing checks . ./config.sh . ./common.sh StartScript ChecksCPUCores RegisterRemoteServers ################################################################################################# # Pathes rules ORIG_PATH="$PATH" ORIG_WD="`pwd`" export PATH=$MXE_BUILDROOT/usr/bin:$MXE_INSTALL_PREFIX/qt5/bin:$PATH ############################################################################################### # Check if a previous bundle already exist CONTINUE_INSTALL=0 if [ -d "$MXE_BUILDROOT" ] ; then read -p "$MXE_BUILDROOT already exist. Do you want to remove it or to continue an aborted previous installation ? [(r)emove/(c)ontinue/(s)top] " answer if echo "$answer" | grep -iq "^r" ;then echo "---------- Removing existing $MXE_BUILDROOT" # chmod +w "$MXE_BUILDROOT/usr/readonly" # chattr -i "$MXE_BUILDROOT/usr/readonly/.gitkeep" rm -rf "$MXE_BUILDROOT" elif echo "$answer" | grep -iq "^c" ;then echo "---------- Continue aborted previous installation in $MXE_BUILDROOT" CONTINUE_INSTALL=1 else echo "---------- Aborting..." exit; fi fi if [[ $CONTINUE_INSTALL == 0 ]]; then ################################################################################################# # Checkout latest MXE from github git clone $MXE_GIT_URL $MXE_BUILDROOT fi ################################################################################################# # MXE git revision to use cd $MXE_BUILDROOT if [[ $MXE_GIT_REVISION == "master" ]]; then echo -e "\n" echo "---------- Updating MXE" git pull else echo -e "\n" echo "---------- Checkout MXE revision to $MXE_GIT_REVISION" git checkout $MXE_GIT_REVISION fi ################################################################################################# # Dependencies build and installation echo -e "\n" echo "---------- Building digiKam low level dependencies with MXE" make MXE_TARGETS=$MXE_BUILD_TARGETS \ gcc \ gdb \ cmake \ gettext \ freeglut \ libxml2 \ libxslt \ libjpeg-turbo \ libpng \ tiff \ boost \ expat \ lcms \ liblqr-1 \ eigen \ jasper \ zlib \ mman-win32 \ pthreads \ qtbase \ qttranslations \ qtimageformats \ qtwebkit \ qttools \ qtwinextras \ qtscript \ x265 \ ffmpeg \ openal \ libical \ imagemagick echo -e "\n" ################################################################################################# echo -e "\n" echo "---------- Building digiKam 3rd-party dependencies with MXE" # Create the build dir for the 3rdparty deps if [ ! -d $BUILDING_DIR ] ; then mkdir -p $BUILDING_DIR fi if [ ! -d $DOWNLOAD_DIR ] ; then mkdir -p $DOWNLOAD_DIR fi cd $BUILDING_DIR rm -rf $BUILDING_DIR/* || true ${MXE_BUILD_TARGETS}-cmake $ORIG_WD/../3rdparty \ -DMXE_TOOLCHAIN=${MXE_TOOLCHAIN} \ -DMXE_BUILDROOT=${MXE_BUILDROOT} \ -DMXE_ARCHBITS=${MXE_ARCHBITS} \ -DMXE_INSTALL_PREFIX=${MXE_INSTALL_PREFIX} \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_COLOR_MAKEFILE=ON \ -DCMAKE_INSTALL_PREFIX=${MXE_INSTALL_PREFIX} \ -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ -DCMAKE_TOOLCHAIN_FILE=${MXE_TOOLCHAIN} \ -DCMAKE_FIND_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ -DCMAKE_SYSTEM_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include \ -DCMAKE_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include \ -DCMAKE_LIBRARY_PATH=${CMAKE_PREFIX_PATH}/lib \ -DZLIB_ROOT=${CMAKE_PREFIX_PATH} \ -DINSTALL_ROOT=${MXE_INSTALL_PREFIX} \ -DEXTERNALS_DOWNLOAD_DIR=$DOWNLOAD_DIR # Low level libraries # NOTE: The order to compile each component here is very important. ${MXE_BUILD_TARGETS}-cmake --build . --config RelWithDebInfo --target ext_opencv -- -j$CPU_CORES ${MXE_BUILD_TARGETS}-cmake --build . --config RelWithDebInfo --target ext_exiv2 -- -j$CPU_CORES -${MXE_BUILD_TARGETS}-cmake --build . --config RelWithDebInfo --target ext_qtav -- -j$CPU_CORES #${MXE_BUILD_TARGETS}-cmake --build . --config RelWithDebInfo --target ext_libgphoto2 -- -j$CPU_CORES ${MXE_BUILD_TARGETS}-cmake --build . --config RelWithDebInfo --target ext_drmingw -- -j$CPU_CORES ################################################################################################# export PATH=$ORIG_PATH TerminateScript diff --git a/project/bundles/mxe/03-build-digikam.sh b/project/bundles/mxe/03-build-digikam.sh index cd36f4205f..440d40d7a5 100755 --- a/project/bundles/mxe/03-build-digikam.sh +++ b/project/bundles/mxe/03-build-digikam.sh @@ -1,188 +1,189 @@ #! /bin/bash # Script to build digiKam using MXE # This script must be run as sudo # # Copyright (c) 2015-2020 by Gilles Caulier # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # # Halt and catch errors set -eE trap 'PREVIOUS_COMMAND=$THIS_COMMAND; THIS_COMMAND=$BASH_COMMAND' DEBUG trap 'echo "FAILED COMMAND: $PREVIOUS_COMMAND"' ERR ################################################################################################# # Manage script traces to log file mkdir -p ./logs exec > >(tee ./logs/build-digikam.full.log) 2>&1 ################################################################################################# echo "03-build-digikam.sh : build digiKam using MEX." echo "---------------------------------------------------" ################################################################################################# # Pre-processing checks . ./config.sh . ./common.sh StartScript ChecksCPUCores RegisterRemoteServers ################################################################################################# # Pathes rules ORIG_PATH="$PATH" ORIG_WD="`pwd`" export PATH=$MXE_BUILDROOT/usr/bin:$MXE_INSTALL_PREFIX/qt5/bin:$PATH cd $MXE_BUILDROOT ################################################################################################# # Install out-dated dependencies cd $BUILDING_DIR rm -rf $BUILDING_DIR/* || true ${MXE_BUILD_TARGETS}-cmake $ORIG_WD/../3rdparty \ -DMXE_TOOLCHAIN=${MXE_TOOLCHAIN} \ -DMXE_BUILDROOT=${MXE_BUILDROOT} \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_COLOR_MAKEFILE=ON \ -DCMAKE_INSTALL_PREFIX=${MXE_INSTALL_PREFIX} \ -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ -DCMAKE_TOOLCHAIN_FILE=${MXE_TOOLCHAIN} \ -DCMAKE_FIND_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ -DCMAKE_SYSTEM_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include \ -DCMAKE_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include \ -DCMAKE_LIBRARY_PATH=${CMAKE_PREFIX_PATH}/lib \ -DZLIB_ROOT=${CMAKE_PREFIX_PATH} \ -DINSTALL_ROOT=${MXE_INSTALL_PREFIX} \ -DEXTERNALS_DOWNLOAD_DIR=$DOWNLOAD_DIR +${MXE_BUILD_TARGETS}-cmake --build . --config RelWithDebInfo --target ext_qtav -- -j$CPU_CORES ${MXE_BUILD_TARGETS}-cmake --build . --config RelWithDebInfo --target ext_lensfun -- -j$CPU_CORES ################################################################################################# # Build digiKam in temporary directory and installation if [ -d "$DK_BUILDTEMP/digikam-$DK_VERSION" ] ; then echo "---------- Updating existing $DK_BUILDTEMP" cd "$DK_BUILDTEMP" cd digikam-$DK_VERSION git reset --hard git pull mkdir -p build.mxe else echo "---------- Creating $DK_BUILDTEMP" mkdir -p "$DK_BUILDTEMP" if [ $? -ne 0 ] ; then echo "---------- Cannot create $DK_BUILDTEMP directory." echo "---------- Aborting..." exit; fi cd "$DK_BUILDTEMP" echo -e "\n\n" echo "---------- Downloading digiKam $DK_VERSION" git clone --progress --verbose $DK_GITURL digikam-$DK_VERSION cd digikam-$DK_VERSION if [ $? -ne 0 ] ; then echo "---------- Cannot clone repositories." echo "---------- Aborting..." exit; fi git checkout $DK_VERSION mkdir build.mxe fi echo -e "\n\n" echo "---------- Configure digiKam $DK_VERSION" sed -e "s/DIGIKAMSC_CHECKOUT_PO=OFF/DIGIKAMSC_CHECKOUT_PO=ON/g" ./bootstrap.mxe > ./tmp.mxe ; mv -f ./tmp.mxe ./bootstrap.mxe sed -e "s/DIGIKAMSC_COMPILE_PO=OFF/DIGIKAMSC_COMPILE_PO=ON/g" ./bootstrap.mxe > ./tmp.mxe ; mv -f ./tmp.mxe ./bootstrap.mxe sed -e "s/DBUILD_TESTING=ON/DBUILD_TESTING=OFF/g" ./bootstrap.mxe > ./tmp.mxe ; mv -f ./tmp.mxe ./bootstrap.mxe sed -e "s/DENABLE_DBUS=ON/DENABLE_DBUS=OFF/g" ./bootstrap.mxe > ./tmp.mxe ; mv -f ./tmp.mxe ./bootstrap.mxe sed -e "s/DENABLE_DRMINGW=OFF/DENABLE_DRMINGW=ON/g" ./bootstrap.mxe > ./tmp.mxe ; mv -f ./tmp.mxe ./bootstrap.mxe chmod +x ./bootstrap.mxe ./bootstrap.mxe $MXE_BUILDROOT RelWithDebInfo -DPng2Ico_EXECUTABLE=${ORIG_WD}/png2ico/png2ico if [ $? -ne 0 ]; then echo "---------- Cannot configure digiKam $DK_VERSION." echo "---------- Aborting..." exit; fi cat ./build.mxe/core/app/utils/digikam_version.h | grep "digikam_version\[\]" | awk '{print $6}' | tr -d '";' > $ORIG_WD/data/RELEASEID.txt echo -e "\n\n" echo "---------- Building digiKam $DK_VERSION" cd build.mxe make -j$CPU_CORES if [ $? -ne 0 ]; then echo "---------- Cannot compile digiKam $DK_VERSION." echo "---------- Aborting..." exit; fi echo -e "\n\n" echo "---------- Installing digiKam $DK_VERSION" echo -e "\n\n" make install/fast && cd "$ORIG_WD" && rm -rf "$DK_BUILDTEMP" if [ $? -ne 0 ]; then echo "---------- Cannot install digiKam $DK_VERSION." echo "---------- Aborting..." exit; fi ################################################################################################# # Install Extra Plugins cd $BUILDING_DIR rm -rf $BUILDING_DIR/* || true ${MXE_BUILD_TARGETS}-cmake $ORIG_WD/../3rdparty \ -DMXE_TOOLCHAIN=${MXE_TOOLCHAIN} \ -DMXE_BUILDROOT=${MXE_BUILDROOT} \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DCMAKE_COLOR_MAKEFILE=ON \ -DCMAKE_INSTALL_PREFIX=${MXE_INSTALL_PREFIX} \ -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \ -DCMAKE_TOOLCHAIN_FILE=${MXE_TOOLCHAIN} \ -DCMAKE_FIND_PREFIX_PATH=${CMAKE_PREFIX_PATH} \ -DCMAKE_SYSTEM_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include \ -DCMAKE_INCLUDE_PATH=${CMAKE_PREFIX_PATH}/include \ -DCMAKE_LIBRARY_PATH=${CMAKE_PREFIX_PATH}/lib \ -DZLIB_ROOT=${CMAKE_PREFIX_PATH} \ -DINSTALL_ROOT=${MXE_INSTALL_PREFIX} \ -DEXTERNALS_DOWNLOAD_DIR=$DOWNLOAD_DIR ${MXE_BUILD_TARGETS}-cmake --build . --config RelWithDebInfo --target ext_gmic_qt -- -j$CPU_CORES ################################################################################################# export PATH=$ORIG_PATH TerminateScript