diff --git a/Mainpage.dox b/Mainpage.dox index eaf0414e4c..cdefa96e29 100644 --- a/Mainpage.dox +++ b/Mainpage.dox @@ -1,1126 +1,1127 @@ /** \mainpage digiKam API reference page. \author (c) 2001-2019 digiKam team. \brief digiKam is an advanced open-source digital photo management application that runs on Linux, Windows, and MacOS. \image html logo-digikam.png \section sourcedirs Source Code Directories digiKam is split into a number of components, each ones located to a dedicated directory. The main namespace is Digikam for the digiKam application and all sub components. A second one is ShowFoto for the stand alone version of image editor. See below the complete list of directories used by the project: | SOURCE TREE-VIEW | DETAILS | | ----------------------------------------------------------- | ----------------------------------------------------------------------- | |├── AUTHORS | List of developers and contributors to the project | |├── bootstrap.linux | Configuration script to compile under Linux | |├── bootstrap.local | Configuration script to compile a local version under Linux | |├── bootstrap.macports | Configuration script to compile under MacOS with Macports | |├── bootstrap.mxe | Configuration script to cross-compile for Windows under Linux with MXE | |├── build | Temporary directory created by bootstrap script to host compiled files | |├── ChangeLog | Complete list of source code changes since the project origin | |├── CMakeLists.txt | Main Cmake script including lead compilation rules for the project | |├── COPYING | Main project license | |├── COPYING-CMAKE-SCRIPTS | Cmake files license | |├── COPYING.LIB | Internal shared libraries license | |├── download-repos | Script to download all source code including extra repositories | |├── gits | Script to perform git in recursive mode over multiple repositories | |├── gitup | Script to perform git pull --rebase in reccursive mode | |├── Mainpage.dox | API documentation main page based on Doxygen | |├── Messages.sh | Script to extract strings for translators | |├── NEWS | Notice to resume all project changes done at release time | |├── README.DEVEL | Read me file for developers | |├── README.md | First start helper documentation | |├── README.BUNDLES | Read me for Linux, MacOs, and Windows bundles support | |├── build | Directory to store compiled files and binary targets | |├── core | All source code are hosted in this directory | |│ ├── app | Lead application component | |│ │ ├── date | All date relevant views | |│ │ ├── dragdrop | Drag and drop helper classes | |│ │ ├── filters | Tags filter widgets | |│ │ ├── items | Item management classes | |│ │ │ ├── delegate | Item view delegate | |│ │ │ ├── overlays | Item overlays | |│ │ │ ├── thumbbar | Item thumbbar widget | |│ │ │ ├── utils | Item utility classes | |│ │ │ └── views | Item view classes | |│ │ ├── main | Main digiKam application | |│ │ ├── utils | Generic utility classes | |│ │ └── views | Views classes | |│ │ ├── preview | Item preview classes | |│ │ ├── sidebar | Left sidebar contents | |│ │ ├── stack | Stacked-view show in central place of main digiKam window | |│ │ ├── tableview | Table-view classes | |│ │ └── utils | View utility classes | |│ ├── cmake | Extra Cmake scripts will be hosted here | |│ │ ├── modules | Cmake scripts to find extra dependencies | |│ │ └── templates | Cmake template files used at configuration time | |│ │ ├── about | Welcome page files (HTML + CSS) | |│ │ ├── colorschemes | GUI Color scheme files | |│ │ ├── database | Database XML configuration files | |│ │ ├── facesengine | Face detection and recognition data files | |│ │ ├── filters | Image filters data files | |│ │ ├── geolocation | Geolocation tool data files | |│ │ ├── hotplug | Hotplug Linux integration files | |│ │ ├── htmlgallery | HTML gallery tool data files | |│ │ ├── icons | Application icons | |│ │ ├── metadata | Metadata tool data files | |│ │ ├── pics | Application pictures | |│ │ ├── printcreator | Print Creator tool data files | |│ │ ├── profiles | Basis open source ICC color profiles | |│ │ └── scripts | Miscs maintenance scripts | |│ ├── dplugins | All digiKam plugins will be hosted in this directory | |│ │ ├── bqm | All Batch Queue Manager plugins | |│ │ │ ├── colors | All color adjustments plugins | |│ │ │ ├── convert | All file convert plugins | |│ │ │ ├── custom | All user-custom processing plugins | |│ │ │ ├── decorate | All decorate item plugins | |│ │ │ ├── enhance | All enhance item plugins | |│ │ │ ├── filters | All filter item plugins | |│ │ │ ├── metadata | All metadata edit plugins | |│ │ │ └── transform | All tranform item plugins | |│ │ ├── editor | All Image Editor plugins | |│ │ │ ├── colors | All color adjustments plugins | |│ │ │ ├── decorate | All decorate item plugins | |│ │ │ ├── enhance | All enhance item plugins | |│ │ │ ├── file | All file processing plugins | |│ │ │ ├── filters | All filter item plugins | |│ │ │ └── transform | All tranform item plugins | |│ │ └── generic | All generic plugins | |│ │ ├── import | Tools to import items | |│ │ ├── metadata | Plugins to change items metadata | |│ │ ├── tools | Plugins hosted in Tools main menu | |│ │ ├── view | Plugins to display items | |│ │ └── webservices | All plugins to import and export items to remote web-services | |│ ├── libs | digiKam core sub-components (few are shared with Showfoto) | |│ │ ├── album | All classes use to manage digiKam albums operations and properties | |│ │ ├── database | All low level database interface is here | |│ │ │ ├── collection | All classes relevant of collections management | |│ │ │ ├── coredb | The core database interface used to host all image properties | |│ │ │ ├── dbjobs | All database multi-threaded jobs | |│ │ │ ├── engine | The low level database engine classes | |│ │ │ ├── haar | The similarity low level algorithms to compute image finger-prints | |│ │ │ ├── history | The item history classes for the database | |│ │ │ ├── item | The database item classes, including containers, lister, and scanner | |│ │ │ ├── models | The databse model classes | |│ │ │ ├── server | The Mysql internal server | |│ │ │ ├── similaritydb | The similarity database | |│ │ │ ├── tags | The database tags management classes | |│ │ │ ├── thumbsdb | The thumbnails database | |│ │ │ └── utils | Miscs tools and widgets used with database | |│ │ ├── dialogs | Common dialogs | |│ │ ├── dimg | The Qt digiKam image data container support ICC and 16 bits color depth | |│ │ │ ├── filters | All image filters will be hosted here. All support 16 bits color depth | |│ │ │ │ ├── auto | Auto colors correction filters | |│ │ │ │ ├── bcg | Brightness-Contrast-Gamma filter | |│ │ │ │ ├── bw | Black and White image converter, including infrared filter | |│ │ │ │ ├── cb | Colors balance filter | |│ │ │ │ ├── curves | Colors curves filter | |│ │ │ │ ├── decorate | Decorate filters | |│ │ │ │ ├── film | Analog film emulation filters | |│ │ │ │ ├── fx | Special effect filters | |│ │ │ │ ├── greycstoration | Cimg based restoration filter | |│ │ │ │ ├── hsl | Hue-Saturation-Lightness filter | |│ │ │ │ ├── icc | Icc color profile filters | |│ │ │ │ ├── imgqsort | The image quality sort algorithms | |│ │ │ │ ├── lc | Local contrast filter (pseudo HDR) | |│ │ │ │ ├── lens | Lens corrections filters, including Qt Lensfun interface | |│ │ │ │ ├── levels | Color levels filter | |│ │ │ │ ├── nr | Wavelets noise reduction filter | |│ │ │ │ ├── redeye | Red-eyes parser and fixer | |│ │ │ │ ├── sharp | Image sharp filter, including Unsharped-mask and Refocus | |│ │ │ │ ├── transform | All image transformation filters | |│ │ │ │ └── wb | White balance filter | |│ │ │ ├── imagehistory | Image history interface for image container | |│ │ │ └── loaders | All DImg image loaders are here | |│ │ ├── metadataengine | The metadata wrapper based on Exiv2 for image and FFMpeg for video | |│ │ ├── dngwriter | Qt classes to convert RAW files to DNG format | |│ │ │ └── extra | DNG and XMP sdks from Adobe | |│ │ ├── dplugins | All shared dplugins classes are hosted here | |│ │ │ ├── core | Low level classes for plugins definitions | |│ │ │ ├── iface | Low level classes for host interface definitions | |│ │ │ ├── setup | Classes to setup plugins in configuration pannel | |│ │ │ ├── webservices | Common classes for Webservices tools | |│ │ │ └── widgets | Common widget sfor plugins | |│ │ ├── dtrash | digiKam trash manager full independant of desktop trash | |│ │ ├── facesengine | Face detection and recognition engine + Faces database implementations | |│ │ │ ├── alignment-congealing | Face alignment based on congealing method | |│ │ │ ├── alignment-flandmark | Face alignment based on flandmark method | |│ │ │ ├── detection | Face detection algorithms | |│ │ │ ├── dnnface | Deep-learning face algorithms | |│ │ │ ├── facedb | Faces database classes | |│ │ │ ├── opencv3-face | OpenCV version 3 face management classes | |│ │ │ ├── preprocessing-tantriggs | Face pre-processing based on tantriggs method | |│ │ │ ├── recognition-dlib-dnn | Deep-learning faces recognition module | |│ │ │ ├── recognition-opencv-eigenfaces | Eigen faces recognition mudule | |│ │ │ ├── recognition-opencv-fisherfaces | Fisher faces recognition module | |│ │ │ ├── recognition-opencv-lbph | LBPH bases faces recognition module | |│ │ │ └── shape-predictor | Shape predictor algorithms | |│ │ ├── fileactionmanager | Classes to connect database and metadata actions to file operations | |│ │ ├── filters | Widgets to filter items by metadata properties | |│ │ ├── imageproperties | All widgets used in right side-bar from all main views | |│ │ ├── iojobs | Multithreaded jobs manager used with files operations | |│ │ ├── jpegutils | Utilities to process JPEG files | |│ │ │ └── libjpeg | JPEG loss-less transform private implementations from libjpeg | |│ │ ├── kmemoryinfo | Qt backend to analyze system memory information | |│ │ ├── models | Qt models used with item views | |│ │ ├── notificationmanager | Multi-desktop notifications wrapper | |│ │ ├── pgfutils | Qt Classes to work with PGF image format | |│ │ ├── progressmanager | Multi-level operations progress widget | |│ │ ├── rawengine | Qt classes to work with libraw decoder | |│ │ │ └── libraw | Internal Libraw sdk | |│ │ ├── settings | digiKam settings manager | |│ │ ├── tags | Classes to play with tags | |│ │ │ └── tagsmanager | Tags manager view | |│ │ ├── template | Metadata template support | |│ │ ├── threadimageio | Classes to process thumbs and preview extraction including video support| |│ │ ├── threads | Classes to manage and chain threads using multi-core | |│ │ ├── timeadjust | Common classes time adjustements tools | |│ │ ├── transitionmngr | Frames transitions manager | |│ │ ├── versionmanager | Classes to manage versionning operations | |│ │ ├── video | Classes to play with video contents | |│ │ └── widgets | To host plenty of widgets used everywhere | |│ │ ├── colors | Colors relevant views | |│ │ ├── combo | Combo-box helper classes | |│ │ ├── common | Uncategorized widgets | |│ │ ├── files | File operation classes | |│ │ ├── fonts | Font management classes | |│ │ ├── graphicsview | Graphics-view implementation (model-view) | |│ │ ├── iccprofiles | ICC color profiles widgets | |│ │ ├── imagehistory | Image history widgets | |│ │ ├── itemview | Item-view implementations (model-view) | |│ │ ├── layout | Layout helper classes | |│ │ ├── mainview | Common top-level view implementations | |│ │ ├── metadata | Metadata widgets | |│ │ └── range | Range helper classes | |│ ├── showfoto | Stand alone image editor | |│ │ ├── main | Main Showfoto application | |│ │ ├── setup | Showfoto Setup views | |│ │ └── thumbbar | Showfoto thumb-bar views | |│ ├── tests | Unit tests | |│ └── utilities | digiKam utilities and advanced tools (few are shared with showfoto) | |│ ├── advancedrename | Advance rename tool | |│ ├── extrasupport | Extra desktop features support as Baloo search engine | |│ ├── facemanagement | Face management classes and tools | |│ ├── firstrun | First-run assistant to configure lead digiKam settings | |│ ├── fuzzysearch | Similarity search tools | |│ ├── geolocation | All geo-location tools are located here | |│ │ ├── editor | Tool to edit items geo-location | |│ │ ├── geoiface | All shared classes used by geo-location tools | |│ │ ├── geomapwrapper | Legacy helper classes for geo-location support | |│ │ └── mapsearches | Tool to perform map searches | |│ ├── imageeditor | The famous digiKam image editor, a lots of classes shared with Showfoto | |│ │ ├── core | Core implementation including canvas and tools interface | |│ │ ├── dialogs | Image editor dialogs | |│ │ ├── editor | The core image editors classes | |│ │ ├── main | The main digiKam image editor view, not shared with Showfoto | |│ │ ├── rawimport | The Raw import tool | |│ │ └── widgets | All common widgets | |│ ├── import | The import tools, including USB MAss Storage, Gphoto2, and Sane support | |│ │ ├── backend | Camera backends | |│ │ ├── dialogs | Import tools dialogs | |│ │ ├── items | Import item classes | |│ │ ├── main | Import tool main view | |│ │ ├── models | Import model classes | |│ │ ├── views | Import view classes | |│ │ └── widgets | Import common widgets | |│ ├── lighttable | The Light-table tool to compare images side by side | |│ ├── maintenance | The digiKam tool to maintain the database contents | |│ ├── queuemanager | The famous Batch Queue Manager tool | |│ │ ├── main | The main BQM view | |│ │ ├── manager | The multi-core manager to run tools in background | |│ │ ├── tools | All BQM tools classed by functions | |│ │ └── views | The BQM internal views | |│ ├── searchwindow | The powerfull advanced search tool | |│ ├── setup | All digiKam setup panel, with few ones shared with Showfoto | |│ │ ├── album | Album configuration views | |│ │ ├── camera | Camera configuration views | |│ │ ├── collections | Collection configuration views | |│ │ ├── editor | Image Editor configuration views | |│ │ └── metadata | Metadata configuration views | |│ └── slideshow | The simple slideshow tool | |├── doc | Handbooks directory | |│ ├── digikam | digiKam manual | |│ ├── project | Extra files for the documentation | |│ └── showfoto | Showfoto manual | |├── extra | Optional extra libraries to compile advanced features | |└── project | Extra project parts | | ├── bundles | Bundles build scripts | | │ ├── 3rdparty | External components required to build bundles | | │ ├── appimage | Linux AppImage | | │ ├── macports | MacOs package | | │ └── mxe | Windows installer | | ├── documents | Project documentations | | ├── release | Release scripts | | ├── reports | Static analyzers report scripts | | └── scripts | 3rdparty source code management scripts | \section externaldeps External Dependencies \subsection depscheckout Dependencies To Checkout All Source Code - Perl http://www.perl.org - Git http://git-scm.com \subsection depstrans Dependencies To Process Translations Files (optional) - Ruby http://www.ruby-lang.org (to extract translations files from remote repositories) - Subversion https://subversion.apache.org/ (to extract translations files from remote repositories) - Gettext https://www.gnu.org/software/gettext (including Msgfmt to compile po files to mo files) \subsection depscomplink Dependencies To Compile And Link Source Code The full list of mandatory (X) and optional (opt) external dependencies required to compile and link digiKam source code is listed below. | Dependency | Status | Version | Project URL | Remarks | TODO | |----------------------|----------|-------------|---------------------------------------------------|--------------------------------------------------------|---------------------------------------------------------------------------------------| | CMake | X | >= 3.0.0 | http://www.cmake.org | > 3.1.0 hightly recommended | | | ECM | X | >= 1.7.0 | https://cgit.kde.org/extra-cmake-modules.git/ | | | | Qt5::Core | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Gui | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Widgets | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Network | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Sql | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | Including Qt5::Sqlite and Qt5::Mysql plugins | | | Qt5::Xml | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Concurrent | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::PrintSupport | X | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | | | | Qt5::Svg | X | >= 5.9 | http://code.qt.io/cgit/qt/qtsvg.git/ | | | | Qt5::XmlPatterns | X | >= 5.9 | http://code.qt.io/cgit/qt/qtxmlpatterns.git/ | | | | Qt5::WebKitWidgets | X | >= 5.9 | http://code.qt.io/cgit/qt/qtwebkit.git/ | To render web contents | Still the default while transitional stage (WebEngine do not compile with Mingw) | | Qt5::WebEngine | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtwebengine.git/ | To render web contents (ENABLE_QWEBENGINE=on) | To replace QWebKitWidgets. Must be the default in the future when Mingw supported | | Qt5::X11Extras | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtx11extras.git/ | For color management support under Linux | | | Qt5::DBus | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | Optional: only for Linux Desktop | | | Qt5::OpenGL | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | For Presentation tool | | | Qt5::Test | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | To compile test codes (BUILD_TESTING=on) | | | Qt5::Qml | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | To compile test codes (BUILD_TESTING=on) O2 unit tests | | | Qt5::WebView | opt | >= 5.9 | http://code.qt.io/cgit/qt/qtbase.git/ | To compile test codes (BUILD_TESTING=on) O2 unit tests | | | KF5::Config | X | >= 5.5.0 | https://cgit.kde.org/kconfig.git/about/ | | | | KF5::XmlGui | X | >= 5.5.0 | https://cgit.kde.org/kxmlgui.git/about/ | | | | KF5::I18n | X | >= 5.5.0 | https://cgit.kde.org/ki18n.git/about/ | | | | KF5::WindowSystem | X | >= 5.5.0 | https://cgit.kde.org/kwindowsystem.git/about/ | | | | KF5::Service | X | >= 5.5.0 | https://cgit.kde.org/kservice.git/about/ | TODO: make optional for Linux desktop (DFileOperations)| | | KF5::Solid | X | >= 5.5.0 | https://cgit.kde.org/solid.git/about/ | | | | KF5::CoreAddons | X | >= 5.5.0 | https://cgit.kde.org/kcoreaddons.git/about/ | Needs for KAboutData | | | KF5::NotifyConfig | opt | >= 5.5.0 | https://cgit.kde.org/knotifyconfig.git/about/ | For Linux desktop application notify configuration | | | KF5::Notifications | opt | >= 5.5.0 | https://cgit.kde.org/knotifications.git/about/ | For Linux desktop notifications integrations | | | KF5::ThreadWeaver | opt | >= 5.5.0 | https://cgit.kde.org/threadweaver.git/about/ | For panorama tool | | | KF5::IconThemes | opt | >= 5.5.0 | https://cgit.kde.org/kiconthemes.git/about/ | Optional: only for Linux Desktop (KIconDialog) | | | KF5::FileMetaData | opt | >= 5.5.0 | https://cgit.kde.org/kfilemetadata.git/about/ | KDE files indexer (ENABLE_KFILEMETADATASUPPORT=on) | Still experimental, disabled by default. Implementation from KDE side very unstable | | KF5::AkonadiContact | opt | >= 17.12.1 | https://cgit.kde.org/akonadi-contacts.git/about/ | KDE Mail contacts (ENABLE_AKONADICONTACTSUPPORT=on) | Still experimental, disabled by default. Need testing | | KF5::CalendarCore | opt | >= 17.12.1 | https://cgit.kde.org/kcalcore.git/about/ | For calendar tool to setup ical special events | | | KF5::KIO | opt | >= 5.5.0 | https://cgit.kde.org/kio.git/about/ | Optional: only for Linux Desktop | | | libopencv | X | >= 3.1 | http://opencv.willowgarage.com | OpenCV 4 supported with digiKam 6.1.0 | | | libpthread | X | >= 2.0.0 | http://www.gnu.org/software/hurd/libpthread.html | For DNG converter | | | libtiff | X | >= 4.0 | http://www.remotesensing.org/libtiff | For DImg image loader | | | libpng | X | >= 1.6 | http://www.libpng.org/pub/png/libpng.html | For DImg image loader | | | libjpeg | X | >= 6b | http://www.ijg.org | jpeglib >= 8.0 hightly recommended for RawEngine | | | libboost | X | >= 1.55.0 | http://www.boost.org/doc/libs | For Versionning support | | | liblcms | X | >= 2.x | http://www.littlecms.com | For Color Management support | | | libexpat | X | >= 2.1.0 | http://expat.sourceforge.net | For DNG converter | | | libexiv2 | X | >= 0.27.0 | http://www.exiv2.org | Metadata low level management. | | | libxml2 | opt | >= 2.7.0 | http://xmlsoft.org | For HtmlGallery tool | | | libxslt | opt | >= 1.1.0 | http://xmlsoft.org/XSLT | For HtmlGallery tool | | | libqtav | opt | >= 1.12.0 | http://www.qtav.org | To play video and audio (ENABLE_MEDIAPLAYER=on) | | | libffmpeg | opt | >= 3.3.x | http://www.ffmpeg.org | To play video and audio (ENABLE_MEDIAPLAYER=on) | libavformat, libavutil, libavcodec used to extract video metadata | | Flex | opt | >= 2.5.0 | http://flex.sourceforge.net | For Panorama tool | | | Bison | opt | >= 2.5.0 | http://www.gnu.org/software/bison/bison.html | For Panorama tool | | | libmesa | opt | >= 11.0 | http://www.mesa3d.org | For Presentation tools (Linux only) | | | libkvkontakte | opt | >= 4.70.0 | https://cgit.kde.org/libkvkontatke.git/about/ | For Vkontakte tool (DIGIKAMSC_COMPILE_LIBKVKONTAKTE=on)| | | libksane | opt | >= 5.0.0 | https://cgit.kde.org/libksane.git/about/ | Digital scanner (DIGIKAMSC_COMPILE_LIBKSANE=on) | | | libjpasper | opt | >= 1.900.1 | http://www.ece.uvic.ca/~mdadams/jasper | For JPEG-2000 support | | | libmarble | opt | >= 0.22 | https://cgit.kde.org/marble.git/about/ | For geolocation support | | | libeigen3 | opt | >= 3.2 | http://eigen.tuxfamily.org | For Refocus tool | See if Clapack from OpenCV can be used instead | | liblensfun | opt | >= 0.2.8 | https://github.com/lensfun/lensfun | For LensCorrection tool | | | liblqr-1 | opt | >= 0.4.2 | http://liblqr.wikidot.com | For Liquid rescale tool | | | libgphoto2 | opt | >= 2.5 | http://www.gphoto.org | Digital camera drivers support. Need libusb-1 | | | libgomp | opt | >= 5.0 | https://gcc.gnu.org/onlinedocs/libgomp | OpenMP support for RawEngine | | | libmagick++ | opt | >= 6.7.0 | https://www.imagemagick.org | ImageMagick codecs support for DImg image loader | Version >= 7.0 recommended | | DrMingw | opt | >= 0.8.2 | https://github.com/jrfonseca/drmingw | Mingw crash handler (run-time deps only for windows) | | +| libx265 | opt | >= 2.2 | http://x265.org/ | For HEIC encoding support | | \section getsourcecode Get Source Code \subsection softcomponents Software Components digiKam project use a single git repository to host whole source code base. The project page is given below: https://phabricator.kde.org/source/digikam/ Even if code base can be compiled as well, more features can be enabled if extra components are downloaded and compiled at the same time. The project pages of these extra components are given below: - \b digiKam \b handbook : https://phabricator.kde.org/source/digikam-doc/ \section develenv Development Envirronnement If you are a developer with push access to the git repositories, it is strongly recommended to use the "kde:" prefix and let git use the read-only mirrors for pulling. If you did not clone this repository from "kde:", do it again: \code git config --global url.git://anongit.kde.org/.insteadof kde: git config --global url.ssh://git@git.kde.org/.pushinsteadof kde: git clone kde:digikam \endcode See below an example of \b .gitconfig file working with a developer account : \code [url "git://anongit.kde.org/"] insteadof = kde:// [url "git@git.kde.org:"] pushinsteadof = kde:// [url "ssh://git@git.kde.org/"] pushinsteadof = kde:// [alias] up = pull --rebase -v --stat ci = commit -a -v [core] editor = mcedit [user] name = my name email = my email [push] default = tracking [color] # turn on color diff = auto status = auto branch = auto interactive = auto ui = auto [color "branch"] current = green bold local = green remote = red bold [color "diff"] meta = yellow bold frag = magenta bold old = red bold new = green bold [color "status"] added = green bold changed = yellow bold untracked = red [color "sh"] branch = yellow [color "sh"] \endcode \section cmakeoptions Cmake Configuration Options To configure the project with CMake, use dedicated "bootstrap" script for your platform where all available configuration options are present with default values. There are two configuration sections : the top level and the core. \subsection topleveloptions Top Level Configuration - Packaging options: - \b DIGIKAMSC_COMPILE_DIGIKAM : Build digiKam core (default=ON). - \b DIGIKAMSC_CHECKOUT_PO : Extract application translation files from remote repositories (default=ON). - \b DIGIKAMSC_CHECKOUT_DOC : Extract documentation translation files from remote repositories (default=ON). - \b DIGIKAMSC_COMPILE_DOC : Build handbook i18n files. It require to checkout documentation translations files before from remote repositories (default=ON). - \b DIGIKAMSC_COMPILE_PO : Build GUI i18n files. It require to checkout application translations files before from remote repositories (default=ON). - Developers only options: - \b BUILD_TESTING=ON : Build tests code (default=ON). \subsection coreoptions Core Configuration - Extra feature support options: - \b ENABLE_KFILEMETADATASUPPORT : Build digiKam with KDE files indexer support (default=OFF). - \b ENABLE_AKONADICONTACTSUPPORT : Build digiKam with KDE Mail Contacts support (default=OFF). - \b ENABLE_MEDIAPLAYER : Build digiKam with Media Player support (default=OFF). - \b ENABLE_DBUS : Build digiKam with DBUS support (default=ON). - \b ENABLE_APPSTYLES : Build digiKam with support for changing the widget application style (default=OFF). - \b ENABLE_QWEBENGINE : Build digiKam with QWebEngine instead of QWebKit (default=OFF). - \b ENABLE_FACESENGINE_DNN : Build digiKam with Faces Engine Neural Network support (default=ON). - Experimental support options - \b ENABLE_MYSQLSUPPORT : Build digiKam with MySQL dabatase support (default=ON). - \b ENABLE_INTERNALMYSQL : Build digiKam with internal MySQL server executable (default=ON). - Developers only options: - \b ENABLE_DIGIKAM_MODELTEST : Enable ModelTest on some models for debugging (default=OFF). - Packaging options - \b ENABLE_DRMINGW : Enable the Dr. Mingw crash handler for windows (default=ON). - \b ENABLE_MINGW_HARDENING_LINKER : Enable DEP (NX), ASLR, and high-entropy ASLR linker flags for MinGW (default ON). \section setuplocaldev Setup Local Compilation and Run-Time This section describes how to install digiKam from the git repository, while keeping a system-wide digiKam install. This procedure is based on the configure script \b bootstrap.local 1. Set the root directory for your git install in bootstrap.local (DIGIKAM_INSTALL_PREFIX variable) 2. If you want a clean build directory, set CLEANROOT to 1 3. Type the following command in your terminal: \code $ ./bootstrap.local # or "./bootstrap.local --eclipse" if you intend to use Eclipse $ cd build $ make $ make install $ KDESYCOCA="/your/root/directory/var/tmp/kde-$USER/ksycoca5" kbuildsycoca5 \endcode To run digikam, use the following commands: \code $ export KDESYCOCA=/your/root/directory/var/tmp/kde-$USER/ksycoca5 $ export QT_PLUGIN_PATH=/your/root/directory/lib64/plugins:/your/root/directory/lib/plugins:$QT_PLUGIN_PATH $ export XDG_DATA_DIRS=/your/root/directory/share:$XDG_DATA_DIRS $ /your/root/directory/bin/digikam \endcode The same applies for all binaries in /your/root/directory/bin/ If your shell is bash, you can edit your \b .bashrc file (in $HOME) and add the follwing alias: \code DIGIKAMROOT="/your/root/directory" alias digikam-dev="KDESYCOCA=\$DIGIKAMROOT/var/tmp/kde-$USER/ksycoca5 XDG_DATA_DIRS=\$DIGIKAMROOT/share:\$XDG_DATA_DIRS QT_PLUGIN_PATH=\$DIGIKAMROOT/lib64/plugins:\$DIGIKAMROOT/lib/plugins:\$QT_PLUGIN_PATH \$DIGIKAMROOT/bin/digikam" \endcode then you can start your newly installed digikam with \code $ digikam-dev \endcode \section debugtraces Debug Traces At Run-Time digiKam uses categorized logging at run-time. By default, all debug messages are printed on the console. To disable output, you can either fine-grained control by using one or more logging categories listed below. Note: under Windows, to catch all debug messages you need to install an extra Microsoft application named DebugView available at this url: http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx \subsection Enable Logging Using an Environment Variable You can set the environment variable \b QT_LOGGING_RULES. Rules are divided by semicolons. E.g. you can start digiKam like this on the commandline with thumbnails and core database messages disabled: \code export QT_LOGGING_RULES='digikam.thumbsdb=false:digikam.coredb=false' digikam \endcode \subsection digiKamloggincat Logging Categories in digiKam All loggin categories are listed in \ref digikam_debug.cpp source code. \subsection moreaboutloggincat Further Reading For more details see the Qt framework documentation about loggin categories available at this url: https://doc.qt.io/qt-5/qloggingcategory.html#details \section cmakecompilrules Cmake compilation rules \subsection cmakeintro Introduction The whole project is written mostly in C++/Qt and the Cmake framework is used to compile under Linux, MacOs, and Windows. The Cmake rules have been configured to reduce the linking overhead and improve CPU utilization with modular design. Independent Cmake configuration is presents in following folders: - root source dir - core - extra - doc The Cmake rules for the core component will link the following executable: - digikam - showfoto - digikam database server - if compilation is enabled in cmake files - various test executables - if testing is enabled Each of them depend on various sources which must be compiled before. A complete description of source code direction is given to the sourcedirs section. \subsection cmakeimpldetails CMake Implementation Details \subsubsection includedirs Include Directories Local include directories are all managed by this snippet of code: \code set(DK_INCLUDES_ALL "") HEADER_DIRECTORIES(DK_LOCAL_INCLUDES_RAW) \endcode The libjpeg- folders are all included, so we need to delete them all and include the correct one only: \code # This macro will set all paths which do not containt libjpeg- # We will add later the directory we need FOREACH(var ${DK_LOCAL_INCLUDES_RAW}) STRING(REGEX MATCH "libjpeg-" item ${var}) IF(item STREQUAL "") LIST(APPEND DK_LOCAL_INCLUDES ${var}) ENDIF(item) ENDFOREACH(var) set(DK_LOCAL_INCLUDES ${DK_LOCAL_INCLUDES} libs/jpegutils/${DIGIKAM_LIBJPEG_DIR}) include_directories(${DK_LOCAL_INCLUDES}) \endcode There is no need for manual intervention to add new includes, even if you add a new folder, just keep in mind to use: \code #include "tagmngrlistitem.h" \endcode instead of : \code #include "models/tagmngrlistitem.h" \endcode \subsubsection sharedlibs Shared Libraries To avoid linking overhead and make a better use of sources there are some dynamic libs as these one: - digikamcore : core components used by almost all executables as digiKam and Showfoto. - digikamdatabase : database components, also used together with digikamcore but only for digiKam Please add sources to digikam core or digikam database only if they don't depend on any big component from digikam main executable. These two shared libs must be kept small because they link in a lot of places \subsubsection staticlibs Static Libraries Currently cmake configuration features a lots of shared libraries as: - metadataedit - geolocationedit - digikamfaceengine This libraries are linked in digikam main executable and some tests tools. Avoid making static libraries if possible, and use OBJECT libraries instead. Only make STATIC libraries which does not depend on other digikam code. Also make sure you put the PRIVATE parameter when setting the target_link_libraries. \code target_link_libraries(digikamcore PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets ) \endcode \subsubsection objlibs Object Libraries While static libraries are still collection of objects, CMake offer a better approach by allowing to specify an OBJECT library: \code set(libslideshow_SRCS slidetoolbar.cpp slideosd.cpp slideproperties.cpp slideimage.cpp slideerror.cpp slideend.cpp slideshow.cpp slidehelp.cpp slideshowsettings.cpp ) add_library(slideshow_src OBJECT ${libslideshow_SRCS}) \endcode OBJECT library is a cmake internal implementation feature and allow to easily manage sources. Here is an example of how to make a shared lib using OBJECT libraries: \code add_library(digikamcore SHARED $ # the lib we made few lines above $ $ .... ) \endcode \section codecontrib Contribute To The Code This section's purpose is to guide contributors and developers to help on the digiKam project. \subsection startopensource Starting With Open-Source Before to contribute to digiKam project, please take a look to this link which provide 10 golden rules for starting with open source project: http://schlitt.info/opensource/blog/0541_10_golden_rules_for_starting_with_open_source.html \subsection codeformat Source Code Formatting Adhere to this style guide strictly while adding new code to digiKam or working on existing code. \subsubsection indentlength Indentation length Indent with 4 spaces exactly. for eg: \code void function() { ....int a; // 4 spaces from beginning ....for (int i = 0 ; i < 10 ; ++i) // 4 spaces from beginning ....{ // 4 spaces from beginning ........a = i; // 4 spaces from previous indent block \endcode Emacs by default will indent to 4 spaces vim users add this to you .vimrc set tabstop=4 \subsubsection tabsspaces Tabs vs Spaces Absolutely no tabs. Use a sensible editor which will convert tabs to spaces. This will reduce unnecessary changes in your cvs commits. Emacs by default will convert tab to spaces. For vim users, add this to your .vimrc set expandtab \subsubsection linelength Line length Line length should never exceed 80 chars (unless really necessary - these cases are rare). Having long lines greatly reduces readability of code \subsubsection bracketing Bracketing In all cases, {} brackets should start on a newline and should be aligned with previous line (follow the indentation spaces). for eg. \code class A { //new line ... for (int i = 0 ; i < 10 ; ++i) { //new line if (a == foobar) { //new line ... } else { // new line .. } \endcode \subsubsection accessmod Positioning of Access modifiers public, private, protected, public slots, ... should be aligned to the beginning of the line with no margin \code class A { public: // aligned to left ... private Q_SLOTS: // aligned to left \endcode Follow a consistent order in defining these attributes. The recommended order is public, protected (functions), private (functions), signals, public slots, protected slots, private slots, private (variables) \subsection itemsnames Class, file and Variable names \subsubsection classfilenames Class and filenames - filenames should always be in lower-case - class names should match the filenames. Capitalize the first letter and other letters logically to improve readability \subsubsection protectvars Protected Member variables - protected member variable names should always be of the form m_varName. - Capitalize logically so that it becomes easy to read it. Do not capitalize the first letter after _ (Use m_varName not m_VarName) - variable names should be indicative of their functionality and also of the type they belong too if they are instances of qt widgets. for eg, QCheckBox* m_autoRotateCheckBox; \subsubsection nomembvars Non-Member variables - non-member variables should follow the same naming convention as the member variables, except for the leading m_ \subsubsection privmembvars Private Member variables - private member variables must be stored in a d private container to reduce compilation time and improve binary compatibility between digiKam components. See more information how to use a 'd' private class at this url: http://developer.kde.org/policies/librarypolicy.html \subsection commentsspaces Comments and Whitespace Use whitespaces liberally to improve readability. Add blank lines between logical sections of the code. Comment as much as possible. Position comments at the beginning of the section/line you want to comment, NEVER at the end of the line, excepted for special cases for ex to describe enum values. \code // put your comments here a = (b == foobar) ? 1 : -1; a = (b == foobar) ? 1 : -1; // you are asking for trouble by putting comments here \endcode \subsection headerfiles Header Files - Add copyright to top of every file. Use the same header than others digiKam source code. - Double inclusion protection defines are all upper case letters and are composed of the classname and a H suffix separated by underscore \code #ifndef DIGIKAM_ANOTHER_NICE_CLASS_H #define DIGIKAM_ANOTHER_NICE_CLASS_H class AnotherNiceClass { ... } #endif // DIGIKAM_ANOTHER_NICE_CLASS_H \endcode - Use forward declarations as much as possible. \code class QFileInfo; class A { ....QFileInfo* m_fileInfo; \endcode \subsection autocodeformat Automatic source code formatting The above coding style guidelines can be automatically applied with astyle (http://astyle.sourceforge.net/). Run it in the directory where the files are located that should be formatted. To apply the coding guidelines with astyle is to use the fileformatter.py script in project/scripts directory. This script will also clean up the source tree and remove backup files that had been created by astyle, if the appropriate command line argument is given. To handle the command easier, create a bash function in ~/.bashrc, e.g. \code dkfrmcode() { astyle --style=allman \ --indent=spaces=4 \ --convert-tabs \ --indent-switches \ --break-blocks \ --break-closing-brackets \ --pad-header \ --align-pointer=type \ --indent-col1-comments \ --add-brackets \ --min-conditional-indent=0 \ `find $1 -type f -name '*.cpp'` `find $1 -type f -name '*.c'` `find $1 -type f -name '*.h'` } \endcode You can pass a parameter to the function, in this case the first parameter is the directory, where files should be formatted. Examples: 1. Run astyle in the current directory \code $> dkfrmcode \endcode 2. Run astyle in a different directory \code $> dkfrmcode /home/user/code/git/digikam/ \endcode \subsection generalrecommend General recommendations Please take a look into this contrib page tips before to write code/patches for digiKam project : http://techbase.kde.org/Contribute Use the same .cpp/.h header than the rest of digiKam project. Use a decent editor which does auto-indentation/syntax-highlighting for you, as Kate or QtCreator There are excellent initializer scripts in the kdesdk package for xemacs and vim which can substantially increase your productivity. Just to give a taste of what i can do with emacs (and kdesdk): * automatically insert copyright (and ifdefs) in new files. * insertion of class function definitions for declared class functions in header with one keystroke * switch between header and declaration files with one keystroke * go to corresponding definition/declaration with one keystroke * tab completion of variable/function names already declared. \subsection gdbbacktrace GDB Backtrace If you found a context to crash digiKam, you can provide a backtrace using GDB debugger. digiKam need to be compiled with all debug info else the backtrace will not suitable. There is a configure option for that: \code $> cmake . -DCMAKE_BUILD_TYPE=debugfull $> make $> su $> make install/fast \endcode To make a backtrace with GDB use following command: \code $ gdb digikam > catch throw > run > ... > _crash here_ > ... > bt > _the backtrace is here_ > quit \endcode Post this backtrace at the right place (Bugzilla or developement mailing list) for investigations by developers. For Windows users, take a look on this tutorial : http://techbase.kde.org/Development/Tutorials/Debugging/Debugging_on_MS_Windows \subsection memleak Memory Leak To check any memory leak problem in digiKam, valgrind is your friend (http://valgrind.org) Try this command line to use with valgrind : \code valgrind --tool=memcheck --leak-check=full --error-limit=no --suppressions=project/reports/digikam.supp digikam \endcode NOTE: digikam.supp file is available in digikam/project sub-folder. \subsection profilingcode Profiling With Cachegrind Valgrind also includes a tool to find out in which parts of your code time is spent. \code valgrind --tool=callgrind digikam \endcode Profiling can be disabled at startup to limit the output to the code you are interested in. Start with \code valgrind --tool=callgrind --instr-atstart=no digikam \endcode and prepare the situation you want to profile. Then, in another console, start profiling with "callgrind_control -i on" and, after the situation has passed, request a profile dump with "callgrind_control -d". The resulting callgrind.out files need to be viewed with the kcachegrind program, e.g.: \code kcachegrind callgrind.out.16693.1 \endcode \subsection unittests Unit Testing / Automated Testing Unit Testing is great way to ensure that software units (in OOP this normally means classes) work as expected. Wikipedia gives a good introduction to Unit Testing: http://en.wikipedia.org/wiki/Unit_testing It is also worth to follow most of QTTest API rules with digiKam: http://doc.qt.io/qt-5/qtest-tutorial.html The digiKam test suite is located under tests and will be compiled if BUILD_TESTING is turned ON at cmake configuration time. After compiling the source code the tests can be executed via \code make test \endcode The console output while running the tests is stored in Testing/Temporary/LastTest.log in the CMake binary dir. All tests are simple binaries that can be executed separately if needed. \subsection corruptedsignalslot Checking For Corrupt Qt Signal Slot Connection Use this alias for running digikam: \code alias digikamdbg="digikam 2>&1 | tee - /tmp/digikam.out; echo -e \"\n\n\nPossible connection errors:\n\n\"; cat /tmp/digikam.out | grep -A2 'Object::connect'" \endcode It will print a list of connection warnings after terminating the program. Moreover the complete console log of the last session is stored in /tmp/digikam.out. \subsection dupcodes Finding Duplicated Code Code duplication should be avoided as bugs have to be fixed for every piece of duplicated code. The current duplication can be analyzed eg. with Simian: http://www.redhillconsulting.com.au/products/simian/ In the digikam checkout directory run: \code java -jar simian.jar `find . -regex '.*\.cpp\|.*\.h' | grep -v 3rdparty` \endcode This prints out a list of duplicated code segments. \subsection docvalidcodecheck API Documentation Validation, User Documentation Validation, Source Code Checking The following site check on a daily basis for the a.m. errors: www.englishbreakfastnetwork.org/krazy/ It can be very useful, in particular before major releases. Don't trust it blindly! Sometimes they propose too advanced modifications that are no compatible with the prevailant include files. \subsection appusability Usability Issues OpenUsability project has define default menu structure and keyboard shortcuts: http://wiki.openusability.org/guidelines/index.php/Appendices:Keyboard_Shortcuts \subsection apidoc Generate API Documentation To generate API documentation, you need to install: - Doxygen program (http://www.doxygen.org) - Dot program (http://www.graphviz.org) After cmake generated a Makefile you can call 'make doc'. A new subfolder named 'html' will be created. Warning, this can take a while. For finding documentation errors, doxygen generates a warning log file at the cmake binary dir called 'doxygen-warn.log'. \subsection speedupcompil Speed Up The Code-Compile-Test Cycle Assuming you have setup your environment in ~/.bashrc as is suggested for development, you can add something like this to your ~/.bashrc: \code function digikam_start { LD_LIBRARY_PATH=${KDE_BUILD}/extragear/graphics/lib:${LD_LIBRARY_PATH} ${KDE_BUILD}/extragear/graphics/digikam/digikam/digikam } function digikam_start_gdb { LD_LIBRARY_PATH=${KDE_BUILD}/extragear/graphics/lib:${LD_LIBRARY_PATH} gdb ${KDE_BUILD}/extragear/graphics/digikam/digikam/digikam } \endcode This allows you to run digikam after compiling without the need of a "make install", even if you changed code in the libraries. \subsection gitbranches Working With Branches From Git Repository Example to create a local 'dplugins' development branch based on master: \code git checkout master git checkout -b development/dplugins \endcode Example to delete the local 'dplugins' developement branch: \code git checkout master git branch -d development/dplugins \endcode Example to create a remote 'dplugins' development branch from the local branch: \code git push -u origin development/dplugins \endcode Example to delete the remote 'dplugins' development branch: \code git push origin :development/dplugins \endcode \subsection gitbranches Sync a Branch With Master From Git Repository It typical to use a dedicated developement branch in Git to keep the master code stable for production. To synchronize branches with master, use these commands in your local branch checkout: \code $>git checkout master $>git pull --rebase $>git checkout -b MY_DEVEL_BRANCH GIT_REMOTE_PATH Branch 'MY_DEVEL_BRANCH' set up to track remote branch path 'GIT_REMOTE_PATH' from origin. To list GIT_REMOTE_PATH, use 'git branch -a' command Switched to a new branch 'MY_DEVEL_BRANCH' $>git merge master Merge made by the 'recursive' strategy. ... $>git push ... \endcode The first 2 lines make sure that your local master repository is up to date. The 3rd line creates the local development branch "MY_DEVEL_BRANCH". If you have already created this branch, just run "git checkout MY_DEVEL_BRANCH". Merging between master and "MY_DEVEL_BRANCH" branch is done in the 4th line. Git might ask you to resolve conflicts here. When it's done, it will ask you to provide a commit message. Finally you push your merge into the remote repository. */ diff --git a/core/app/DigikamCoreTarget.cmake b/core/app/DigikamCoreTarget.cmake index 093335b513..bf8c9f09e7 100644 --- a/core/app/DigikamCoreTarget.cmake +++ b/core/app/DigikamCoreTarget.cmake @@ -1,212 +1,212 @@ # # Copyright (c) 2010-2019 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. # digiKam CORE shared library set(DIGIKAMCORE_OBJECTS $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ # widgets $ $ $ $ $ # utilities $ $ $ $ utils/digikam_debug.cpp ) if(ENABLE_MEDIAPLAYER) set(DIGIKAMCORE_OBJECTS ${DIGIKAMCORE_OBJECTS} $ ) endif() if(Marble_FOUND) set(DIGIKAMCORE_OBJECTS ${DIGIKAMCORE_OBJECTS} $ $ ) endif() if(KF5FileMetaData_FOUND) set(DIGIKAMCORE_OBJECTS ${DIGIKAMCORE_OBJECTS} $ ) endif() if(KF5AkonadiContact_FOUND) set(DIGIKAMCORE_OBJECTS ${DIGIKAMCORE_OBJECTS} $ ) endif() add_library(digikamcore SHARED ${DIGIKAMCORE_OBJECTS} ) set_target_properties(digikamcore PROPERTIES VERSION ${DIGIKAM_VERSION_SHORT} SOVERSION ${DIGIKAM_VERSION_SHORT}) add_dependencies(digikamcore digikam-gitversion) generate_export_header(digikamcore BASE_NAME digikam EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/utils/digikam_core_export.h") target_link_libraries(digikamcore PRIVATE Qt5::Core Qt5::Gui Qt5::Xml Qt5::XmlPatterns Qt5::Widgets Qt5::Sql Qt5::PrintSupport Qt5::Concurrent KF5::Solid KF5::WindowSystem KF5::ConfigGui KF5::XmlGui KF5::I18n KF5::Service # Required by CImg which use pthread internally. ${CMAKE_THREAD_LIBS_INIT} ${EXPAT_LIBRARY} ${LCMS2_LIBRARIES} # filters ${TIFF_LIBRARIES} PNG::PNG ${JPEG_LIBRARIES} exiv2lib ${FFMPEG_LIBRARIES} ${OPENMP_LDFLAGS} ) if(ENABLE_QWEBENGINE) target_link_libraries(digikamcore PRIVATE Qt5::WebEngineWidgets) else() target_link_libraries(digikamcore PRIVATE Qt5::WebKitWidgets) endif() if(ENABLE_DBUS) target_link_libraries(digikamcore PRIVATE Qt5::DBus) endif() if(KF5IconThemes_FOUND) target_link_libraries(digikamcore PRIVATE KF5::IconThemes) endif() if(KF5KIO_FOUND) target_link_libraries(digikamcore PRIVATE KF5::KIOCore) target_link_libraries(digikamcore PRIVATE KF5::KIOWidgets) endif() if(KF5Notifications_FOUND) target_link_libraries(digikamcore PRIVATE KF5::Notifications) endif() if(KF5NotifyConfig_FOUND) target_link_libraries(digikamcore PRIVATE KF5::NotifyConfig) endif() if(Marble_FOUND) - target_link_libraries(digikamcore PRIVATE Marble) + target_link_libraries(digikamcore PRIVATE ${MARBLE_LIBRARIES}) endif() if(X11_FOUND) target_link_libraries(digikamcore PRIVATE Qt5::X11Extras ${X11_LIBRARIES}) endif() if(Jasper_FOUND) target_link_libraries(digikamcore PRIVATE ${JASPER_LIBRARIES}) endif() # LibLqr-1 library rules for content-aware filter if(Lqr-1_FOUND) target_link_libraries(digikamcore PRIVATE ${LQR-1_LIBRARIES}) endif() if(LensFun_FOUND) target_link_libraries(digikamcore PRIVATE ${LENSFUN_LIBRARIES}) endif() if(ImageMagick_FOUND) target_link_libraries(digikamcore PRIVATE ${ImageMagick_LIBRARIES}) endif() # for nrfilter if(OpenCV_FOUND) target_link_libraries(digikamcore PRIVATE ${OpenCV_LIBRARIES}) endif() if(KF5FileMetaData_FOUND) target_link_libraries(digikamcore PRIVATE KF5::FileMetaData) endif() if(KF5AkonadiContact_FOUND) target_link_libraries(digikamcore PRIVATE KF5::AkonadiContact) endif() if(APPLE) target_link_libraries(digikamcore PRIVATE /System/Library/Frameworks/AppKit.framework) endif() if(WIN32) target_link_libraries(digikamcore PRIVATE wsock32 ws2_32) endif() if(ENABLE_MEDIAPLAYER) target_link_libraries(digikamcore PRIVATE ${QTAV_LIBRARIES}) endif() # --- Install Rules --- install(TARGETS digikamcore EXPORT DigikamCoreConfig ${INSTALL_TARGETS_DEFAULT_ARGS}) install(EXPORT DigikamCoreConfig DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DigikamCore" NAMESPACE Digikam::) write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/DigikamCoreConfigVersion.cmake VERSION ${DIGIKAM_VERSION_SHORT} COMPATIBILITY SameMajorVersion) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/DigikamCoreConfigVersion.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DigikamCore") diff --git a/core/app/DigikamGuiTarget.cmake b/core/app/DigikamGuiTarget.cmake index 7b3e45b97c..68a08575dd 100644 --- a/core/app/DigikamGuiTarget.cmake +++ b/core/app/DigikamGuiTarget.cmake @@ -1,243 +1,243 @@ # # Copyright (c) 2010-2019 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. # digiKam GUI shared library if(ENABLE_DBUS) qt5_add_dbus_adaptor(digikamadaptor_SRCS main/org.kde.digikam.xml main/digikamapp.h Digikam::DigikamApp) endif() set(libdigikamgui_SRCS main/digikamapp.cpp main/digikamapp_solid.cpp main/digikamapp_camera.cpp main/digikamapp_import.cpp main/digikamapp_config.cpp main/digikamapp_tools.cpp main/digikamapp_setup.cpp date/dpopupframe.cpp date/ddateedit.cpp date/ddatetable.cpp date/ddatetable_p.cpp date/ddatepicker.cpp date/ddatepicker_p.cpp date/ddatetimeedit.cpp date/ddatepickerpopup.cpp date/datefolderview.cpp date/monthwidget.cpp date/timelinewidget.cpp dragdrop/importdragdrop.cpp dragdrop/albumdragdrop.cpp dragdrop/ddragobjects.cpp dragdrop/itemdragdrop.cpp dragdrop/tagdragdrop.cpp filters/filtersidebarwidget.cpp filters/tagfilterview.cpp items/delegate/digikamitemdelegate.cpp items/delegate/itemdelegate.cpp items/delegate/itemfacedelegate.cpp items/views/digikamitemview.cpp items/views/digikamitemview_p.cpp items/views/itemcategorizedview.cpp items/thumbbar/itemthumbnailbar.cpp items/thumbbar/itemthumbnaildelegate.cpp items/overlays/assignnameoverlay.cpp items/overlays/facerejectionoverlay.cpp items/overlays/groupindicatoroverlay.cpp items/overlays/itemfullscreenoverlay.cpp items/overlays/itemratingoverlay.cpp items/overlays/itemrotationoverlay.cpp items/overlays/itemcoordinatesoverlay.cpp items/overlays/itemselectionoverlay.cpp items/utils/itemviewutilities.cpp items/utils/tooltipfiller.cpp items/utils/contextmenuhelper.cpp items/utils/groupingviewimplementation.cpp items/utils/itemcategorydrawer.cpp utils/digikam_debug.cpp views/tableview/tableview.cpp views/tableview/tableview_treeview.cpp views/tableview/tableview_treeview_delegate.cpp views/tableview/tableview_column_configuration_dialog.cpp views/tableview/tableview_model.cpp views/tableview/tableview_columns.cpp views/tableview/tableview_column_audiovideo.cpp views/tableview/tableview_column_file.cpp views/tableview/tableview_column_geo.cpp views/tableview/tableview_column_digikam.cpp views/tableview/tableview_column_item.cpp views/tableview/tableview_column_photo.cpp views/tableview/tableview_column_thumbnail.cpp views/tableview/tableview_columnfactory.cpp views/tableview/tableview_selection_model_syncer.cpp views/stack/welcomepageview.cpp views/stack/itemiconview.cpp views/stack/trashview.cpp views/stack/stackedview.cpp views/preview/itempreviewcanvas.cpp views/preview/itempreviewview.cpp views/sidebar/leftsidebarwidgets.cpp views/sidebar/sidebarwidget.cpp views/utils/dmodelfactory.cpp views/utils/slideshowbuilder.cpp views/utils/componentsinfodlg.cpp ${digikamadaptor_SRCS} ) if(${Marble_FOUND}) set(libdigikamgui_SRCS ${libdigikamgui_SRCS} views/stack/mapwidgetview.cpp ) endif() add_library(digikamgui_src OBJECT ${libdigikamgui_SRCS} ) ######################### digiKam GUI objects ############################ set(DIGIKAM_OBJECTS $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ ) if(${Marble_FOUND}) set(DIGIKAM_OBJECTS ${DIGIKAM_OBJECTS} $ $ ) endif() #################### Digikam GUI shared Lib ################################ add_library(digikamgui SHARED ${DIGIKAM_OBJECTS} ) set_target_properties(digikamgui PROPERTIES VERSION ${DIGIKAM_VERSION_SHORT} SOVERSION ${DIGIKAM_VERSION_SHORT}) if(WIN32) set_target_properties(digikamgui PROPERTIES COMPILE_FLAGS -DJPEG_STATIC) endif() target_link_libraries(digikamgui PRIVATE digikamdatabase digikamcore Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Sql Qt5::PrintSupport KF5::XmlGui KF5::Solid KF5::ConfigCore KF5::ConfigGui KF5::Service KF5::WindowSystem KF5::I18n ${OpenCV_LIBRARIES} ) if(ENABLE_QWEBENGINE) target_link_libraries(digikamgui PRIVATE Qt5::WebEngineWidgets) else() target_link_libraries(digikamgui PRIVATE Qt5::WebKitWidgets) endif() if(ENABLE_DBUS) target_link_libraries(digikamgui PRIVATE Qt5::DBus) endif() if(KF5IconThemes_FOUND) target_link_libraries(digikamgui PRIVATE KF5::IconThemes) endif() if(KF5KIO_FOUND) target_link_libraries(digikamgui PRIVATE KF5::KIOWidgets) endif() if(${Marble_FOUND}) - target_link_libraries(digikamgui PRIVATE Marble) + target_link_libraries(digikamgui PRIVATE ${MARBLE_LIBRARIES}) endif() if(APPLE) target_link_libraries(digikamgui PRIVATE /System/Library/Frameworks/AppKit.framework) endif() if(NOT WIN32) # To link under Solaris (see bug #274484) target_link_libraries(digikamgui PRIVATE ${MATH_LIBRARY}) endif() if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) target_link_libraries(digikamcore PRIVATE ${KVM_LIBRARY}) endif() if(Gphoto2_FOUND) # See bug #258931: libgphoto2 library must be the last arg for linker. # See bug #268267 : digiKam need to be linked to libusb to prevent crash # at gphoto2 init if opencv is linked with libdc1394. Libusb linking rules are # add to gphoto2 linking rules by Gphoto2 cmake detection script. target_link_libraries(digikamgui PRIVATE ${GPHOTO2_LIBRARIES}) endif() # --- Install Rules --- install(TARGETS digikamgui EXPORT DigikamGuiConfig ${INSTALL_TARGETS_DEFAULT_ARGS}) install(EXPORT DigikamGuiConfig DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DigikamGui" NAMESPACE Digikam::) write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/DigikamGuiConfigVersion.cmake VERSION ${DIGIKAM_VERSION_SHORT} COMPATIBILITY SameMajorVersion) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/DigikamGuiConfigVersion.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/DigikamGui") diff --git a/core/app/utils/digikam_globals.cpp b/core/app/utils/digikam_globals.cpp index 112641f90b..626ecbc31f 100644 --- a/core/app/utils/digikam_globals.cpp +++ b/core/app/utils/digikam_globals.cpp @@ -1,294 +1,297 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-09-08 * Description : global macros, variables and flags * * Copyright (C) 2009-2019 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 "digikam_globals.h" // Qt includes #include #include #include #include #include #include #include #include #include // KDE includes #include // Local includes #include "digikam_config.h" #include "digikam_debug.h" #include "drawdecoder.h" #include "rawcameradlg.h" // Windows includes #ifdef HAVE_DRMINGW # include #endif namespace Digikam { QShortcut* defineShortcut(QWidget* const w, const QKeySequence& key, const QObject* receiver, const char* slot) { QShortcut* const s = new QShortcut(w); s->setKey(key); s->setContext(Qt::WidgetWithChildrenShortcut); QObject::connect(s, SIGNAL(activated()), receiver, slot); return s; } QStringList supportedImageMimeTypes(QIODevice::OpenModeFlag mode, QString& allTypes) { QStringList formats; QList supported; switch(mode) { case QIODevice::ReadOnly: supported = QImageReader::supportedImageFormats(); break; case QIODevice::WriteOnly: supported = QImageWriter::supportedImageFormats(); break; case QIODevice::ReadWrite: supported = QImageWriter::supportedImageFormats() + QImageReader::supportedImageFormats(); break; default: qCDebug(DIGIKAM_GENERAL_LOG) << "Unsupported mode!"; break; } bool tiff = false; bool jpeg = false; #ifdef HAVE_JASPER bool jp2k = false; #endif // HAVE_JASPER foreach(const QByteArray& frm, supported) { if (QString::fromLatin1(frm).contains(QLatin1String("tif"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("tiff"), Qt::CaseInsensitive)) { tiff = true; continue; } if (QString::fromLatin1(frm).contains(QLatin1String("jpg"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("jpeg"), Qt::CaseInsensitive)) { jpeg = true; continue; } #ifdef HAVE_JASPER if (QString::fromLatin1(frm).contains(QLatin1String("jp2"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("j2k"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("jpx"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("jpc"), Qt::CaseInsensitive) || QString::fromLatin1(frm).contains(QLatin1String("pgx"), Qt::CaseInsensitive)) { jp2k = true; continue; } #endif // HAVE_JASPER formats.append(i18n("%1 Image (%2)", QString::fromLatin1(frm).toUpper(), QLatin1String("*.") + QLatin1String(frm))); allTypes.append(QString::fromLatin1("*.%1 ").arg(QLatin1String(frm))); } if (tiff) { formats.append(i18n("TIFF Image (*.tiff *.tif)")); allTypes.append(QLatin1String("*.tiff *.tif ")); } if (jpeg) { formats.append(i18n("JPEG Image (*.jpg *.jpeg *.jpe)")); allTypes.append(QLatin1String("*.jpg *.jpeg *.jpe ")); } #ifdef HAVE_JASPER if (jp2k) { formats.append(i18n("JPEG2000 Image (*.jp2 *.j2k *.jpx *.pgx)")); allTypes.append(QLatin1String("*.jp2 *.j2k *.jpx *.pgx ")); } #endif // HAVE_JASPER formats << i18n("Progressive Graphics file (*.pgf)"); allTypes.append(QLatin1String("*.pgf ")); + formats << i18n("High Efficiency Image Coding (*.heic)"); + allTypes.append(QLatin1String("*.heic ")); + if (mode != QIODevice::WriteOnly) { formats << i18n("Raw Images (%1)", QLatin1String(DRawDecoder::rawFiles())); allTypes.append(QLatin1String(DRawDecoder::rawFiles())); formats << i18n("All supported files (%1)", allTypes); } return formats; } void showRawCameraList() { RawCameraDlg* const dlg = new RawCameraDlg(qApp->activeWindow()); dlg->show(); } QProcessEnvironment adjustedEnvironmentForAppImage() { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); // If we are running into AppImage bundle, switch env var to the right values. if (env.contains(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH")) && env.contains(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH")) && env.contains(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS")) && env.contains(QLatin1String("APPIMAGE_ORIGINAL_PATH"))) { qCDebug(DIGIKAM_GENERAL_LOG) << "Adjusting environment variables for AppImage bundle"; if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH")).isEmpty()) { env.insert(QLatin1String("LD_LIBRARY_PATH"), env.value(QLatin1String("APPIMAGE_ORIGINAL_LD_LIBRARY_PATH"))); } else { env.remove(QLatin1String("LD_LIBRARY_PATH")); } if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH")).isEmpty()) { env.insert(QLatin1String("QT_PLUGIN_PATH"), env.value(QLatin1String("APPIMAGE_ORIGINAL_QT_PLUGIN_PATH"))); } else { env.remove(QLatin1String("QT_PLUGIN_PATH")); } if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS")).isEmpty()) { env.insert(QLatin1String("XDG_DATA_DIRS"), env.value(QLatin1String("APPIMAGE_ORIGINAL_XDG_DATA_DIRS"))); } else { env.remove(QLatin1String("XDG_DATA_DIRS")); } if (!env.value(QLatin1String("APPIMAGE_ORIGINAL_PATH")).isEmpty()) { env.insert(QLatin1String("PATH"), env.value(QLatin1String("APPIMAGE_ORIGINAL_PATH"))); } else { env.remove(QLatin1String("PATH")); } } return env; } void tryInitDrMingw() { #ifdef HAVE_DRMINGW qCDebug(DIGIKAM_GENERAL_LOG) << "Loading DrMinGw run-time..."; wchar_t path[MAX_PATH]; QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QLatin1String("\\exchndl.dll"); if (pathStr.size() > MAX_PATH - 1) { qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: cannot find crash handler dll."; return; } int pathLen = pathStr.toWCharArray(path); path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator HMODULE hMod = LoadLibraryW(path); if (!hMod) { qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: cannot init crash handler dll."; return; } // No need to call ExcHndlInit since the crash handler is installed on DllMain auto myExcHndlSetLogFileNameA = reinterpret_cast(GetProcAddress(hMod, "ExcHndlSetLogFileNameA")); if (!myExcHndlSetLogFileNameA) { qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw: cannot init customized crash file."; return; } // Set the log file path to %LocalAppData%\kritacrash.log QString logFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).replace(L'/', L'\\') + QLatin1String("\\digikam_crash.log"); myExcHndlSetLogFileNameA(logFile.toLocal8Bit().data()); qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw run-time loaded."; qCDebug(DIGIKAM_GENERAL_LOG) << "DrMinGw crash-file will be located at: " << logFile; #endif // HAVE_DRMINGW } QString toolButtonStyleSheet() { return QLatin1String("QToolButton { padding: 1px; background-color: " " qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, " " stop: 0 rgba(100, 100, 100, 50%), " " stop: 1 rgba(170, 170, 170, 50%)); " "border: 1px solid rgba(170, 170, 170, 10%); } " "QToolButton:hover { border-color: white; } " "QToolButton:pressed { background-color: " " qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, " " stop: 0 rgba(40, 40, 40, 50%), " " stop: 1 rgba(90, 90, 90, 50%)); " "border-color: white; } " "QToolButton:checked { background-color: " " qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, " " stop: 0 rgba(40, 40, 40, 50%), " " stop: 1 rgba(90, 90, 90, 50%)); } " "QToolButton:disabled { background-color: " " qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, " " stop: 0 rgba(40, 40, 40, 50%), " " stop: 1 rgba(50, 50, 50, 50%)); }"); } } // namespace Digikam diff --git a/core/app/views/stack/stackedview.cpp b/core/app/views/stack/stackedview.cpp index a8256552cf..cd45321d71 100644 --- a/core/app/views/stack/stackedview.cpp +++ b/core/app/views/stack/stackedview.cpp @@ -1,580 +1,582 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-06-13 * Description : A widget stack to embedded album content view * or the current image preview. * * Copyright (C) 2006-2019 by Gilles Caulier * Copyright (C) 2013 by Michael G. Hansen * Copyright (C) 2015 by Mohamed_Anwer * * 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 "stackedview.h" // Qt includes #include #include // KDE includes #include // Local includes #include "digikam_config.h" #include "digikam_debug.h" #include "applicationsettings.h" #include "digikamitemview.h" #include "itemiconview.h" #include "dbinfoiface.h" #include "itemalbummodel.h" #include "itemalbumfiltermodel.h" #include "itempreviewview.h" #include "itemthumbnailbar.h" #include "loadingcacheinterface.h" #include "previewlayout.h" #include "welcomepageview.h" #include "thumbbardock.h" #include "tableview.h" #include "trashview.h" #include "dimg.h" #ifdef HAVE_MEDIAPLAYER # include "mediaplayerview.h" #endif //HAVE_MEDIAPLAYER #ifdef HAVE_MARBLE # include "mapwidgetview.h" #endif // HAVE_MARBLE namespace Digikam { class Q_DECL_HIDDEN StackedView::Private { public: explicit Private() { dockArea = nullptr; splitter = nullptr; thumbBar = nullptr; thumbBarDock = nullptr; imageIconView = nullptr; imagePreviewView = nullptr; welcomePageView = nullptr; needUpdateBar = false; syncingSelection = false; tableView = nullptr; trashView = nullptr; #ifdef HAVE_MEDIAPLAYER mediaPlayerView = nullptr; #endif //HAVE_MEDIAPLAYER #ifdef HAVE_MARBLE mapWidgetView = nullptr; #endif // HAVE_MARBLE } - bool needUpdateBar; - bool syncingSelection; + bool needUpdateBar; + bool syncingSelection; - QMainWindow* dockArea; - QSplitter* splitter; + QMainWindow* dockArea; + QSplitter* splitter; DigikamItemView* imageIconView; ItemThumbnailBar* thumbBar; ItemPreviewView* imagePreviewView; - ThumbBarDock* thumbBarDock; - WelcomePageView* welcomePageView; - TableView* tableView; - TrashView* trashView; + ThumbBarDock* thumbBarDock; + WelcomePageView* welcomePageView; + TableView* tableView; + TrashView* trashView; + + QMap stackMap; #ifdef HAVE_MEDIAPLAYER MediaPlayerView* mediaPlayerView; #endif //HAVE_MEDIAPLAYER #ifdef HAVE_MARBLE MapWidgetView* mapWidgetView; #endif // HAVE_MARBLE }; StackedView::StackedView(QWidget* const parent) : QStackedWidget(parent), d(new Private) { d->imageIconView = new DigikamItemView(this); d->imagePreviewView = new ItemPreviewView(this); d->thumbBarDock = new ThumbBarDock(); d->thumbBar = new ItemThumbnailBar(d->thumbBarDock); d->thumbBar->setModelsFiltered(d->imageIconView->imageModel(), d->imageIconView->imageFilterModel()); d->thumbBar->installOverlays(); d->thumbBarDock->setWidget(d->thumbBar); d->thumbBarDock->setObjectName(QLatin1String("mainwindow_thumbbar")); d->thumbBarDock->setWindowTitle(i18n("DigiKam Thumbnail Dock")); d->welcomePageView = new WelcomePageView(this); d->tableView = new TableView(d->imageIconView->getSelectionModel(), d->imageIconView->imageFilterModel(), this); d->tableView->setObjectName(QLatin1String("mainwindow_tableview")); d->trashView = new TrashView(this); #ifdef HAVE_MARBLE d->mapWidgetView = new MapWidgetView(d->imageIconView->getSelectionModel(), d->imageIconView->imageFilterModel(), this, MapWidgetView::ApplicationDigikam); d->mapWidgetView->setObjectName(QLatin1String("mainwindow_mapwidgetview")); #endif // HAVE_MARBLE #ifdef HAVE_MEDIAPLAYER d->mediaPlayerView = new MediaPlayerView(this); d->mediaPlayerView->setInfoInterface(new DBInfoIface(this, QList())); #endif //HAVE_MEDIAPLAYER - insertWidget(IconViewMode, d->imageIconView); - insertWidget(PreviewImageMode, d->imagePreviewView); - insertWidget(WelcomePageMode, d->welcomePageView); - insertWidget(TableViewMode, d->tableView); - insertWidget(TrashViewMode, d->trashView); + d->stackMap[addWidget(d->imageIconView)] = IconViewMode; + d->stackMap[addWidget(d->imagePreviewView)] = PreviewImageMode; + d->stackMap[addWidget(d->welcomePageView)] = WelcomePageMode; + d->stackMap[addWidget(d->tableView)] = TableViewMode; + d->stackMap[addWidget(d->trashView)] = TrashViewMode; #ifdef HAVE_MARBLE - insertWidget(MapWidgetMode, d->mapWidgetView); + d->stackMap[addWidget(d->mapWidgetView)] = MapWidgetMode; #endif // HAVE_MARBLE #ifdef HAVE_MEDIAPLAYER - insertWidget(MediaPlayerMode, d->mediaPlayerView); + d->stackMap[addWidget(d->mediaPlayerView)] = MediaPlayerMode; #endif //HAVE_MEDIAPLAYER setViewMode(IconViewMode); setAttribute(Qt::WA_DeleteOnClose); readSettings(); // ----------------------------------------------------------------- connect(d->imagePreviewView, SIGNAL(signalPopupTagsView()), this, SIGNAL(signalPopupTagsView())); connect(d->imagePreviewView, SIGNAL(signalGotoAlbumAndItem(ItemInfo)), this, SIGNAL(signalGotoAlbumAndItem(ItemInfo))); connect(d->imagePreviewView, SIGNAL(signalGotoDateAndItem(ItemInfo)), this, SIGNAL(signalGotoDateAndItem(ItemInfo))); connect(d->imagePreviewView, SIGNAL(signalGotoTagAndItem(int)), this, SIGNAL(signalGotoTagAndItem(int))); connect(d->imagePreviewView, SIGNAL(signalNextItem()), this, SIGNAL(signalNextItem())); connect(d->imagePreviewView, SIGNAL(signalPrevItem()), this, SIGNAL(signalPrevItem())); connect(d->imagePreviewView, SIGNAL(signalDeleteItem()), this, SIGNAL(signalDeleteItem())); connect(d->imagePreviewView, SIGNAL(signalEscapePreview()), this, SIGNAL(signalEscapePreview())); connect(d->imagePreviewView, SIGNAL(signalSlideShowCurrent()), this, SIGNAL(signalSlideShowCurrent())); connect(d->imagePreviewView->layout(), SIGNAL(zoomFactorChanged(double)), this, SLOT(slotZoomFactorChanged(double))); connect(d->imagePreviewView, SIGNAL(signalAddToExistingQueue(int)), this, SIGNAL(signalAddToExistingQueue(int))); connect(d->thumbBar, SIGNAL(selectionChanged()), this, SLOT(slotThumbBarSelectionChanged())); connect(d->imageIconView, SIGNAL(selectionChanged()), this, SLOT(slotIconViewSelectionChanged())); connect(d->thumbBarDock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), d->thumbBar, SLOT(slotDockLocationChanged(Qt::DockWidgetArea))); connect(d->imagePreviewView, SIGNAL(signalPreviewLoaded(bool)), this, SLOT(slotPreviewLoaded(bool))); #ifdef HAVE_MEDIAPLAYER connect(d->mediaPlayerView, SIGNAL(signalNextItem()), this, SIGNAL(signalNextItem())); connect(d->mediaPlayerView, SIGNAL(signalPrevItem()), this, SIGNAL(signalPrevItem())); connect(d->mediaPlayerView, SIGNAL(signalEscapePreview()), this, SIGNAL(signalEscapePreview())); #endif //HAVE_MEDIAPLAYER } StackedView::~StackedView() { delete d; } void StackedView::readSettings() { ApplicationSettings* settings = ApplicationSettings::instance(); bool showThumbbar = settings->getShowThumbbar(); d->thumbBarDock->setShouldBeVisible(showThumbbar); } void StackedView::setDockArea(QMainWindow* dockArea) { // Attach the thumbbar dock to the given dock area and place it initially on top. d->dockArea = dockArea; d->thumbBarDock->setParent(d->dockArea); d->dockArea->addDockWidget(Qt::TopDockWidgetArea, d->thumbBarDock); d->thumbBarDock->setFloating(false); } ThumbBarDock* StackedView::thumbBarDock() const { return d->thumbBarDock; } ItemThumbnailBar* StackedView::thumbBar() const { return d->thumbBar; } void StackedView::slotEscapePreview() { #ifdef HAVE_MEDIAPLAYER if (viewMode() == MediaPlayerMode) { d->mediaPlayerView->escapePreview(); } #endif //HAVE_MEDIAPLAYER } DigikamItemView* StackedView::imageIconView() const { return d->imageIconView; } ItemPreviewView* StackedView::imagePreviewView() const { return d->imagePreviewView; } #ifdef HAVE_MARBLE MapWidgetView* StackedView::mapWidgetView() const { return d->mapWidgetView; } #endif // HAVE_MARBLE TableView* StackedView::tableView() const { return d->tableView; } TrashView* StackedView::trashView() const { return d->trashView; } #ifdef HAVE_MEDIAPLAYER MediaPlayerView* StackedView::mediaPlayerView() const { return d->mediaPlayerView; } #endif //HAVE_MEDIAPLAYER bool StackedView::isInSingleFileMode() const { - return currentIndex() == PreviewImageMode || currentIndex() == MediaPlayerMode; + return viewMode() == PreviewImageMode || viewMode() == MediaPlayerMode; } bool StackedView::isInMultipleFileMode() const { - return (currentIndex() == IconViewMode || - currentIndex() == MapWidgetMode || - currentIndex() == TableViewMode); + return (viewMode() == IconViewMode || + viewMode() == MapWidgetMode || + viewMode() == TableViewMode); } bool StackedView::isInAbstractMode() const { - return currentIndex() == WelcomePageMode; + return viewMode() == WelcomePageMode; } void StackedView::setPreviewItem(const ItemInfo& info, const ItemInfo& previous, const ItemInfo& next) { if (info.isNull()) { if (viewMode() == MediaPlayerMode) { #ifdef HAVE_MEDIAPLAYER d->mediaPlayerView->setCurrentItem(); #endif //HAVE_MEDIAPLAYER } else if (viewMode() == PreviewImageMode) { d->imagePreviewView->setItemInfo(); } } else { if (info.category() == DatabaseItem::Audio || info.category() == DatabaseItem::Video || DImg::isAnimatedImage(info.fileUrl().toLocalFile())) // Special case for animated image as GIF or NMG { // Stop image viewer if (viewMode() == PreviewImageMode) { d->imagePreviewView->setItemInfo(); } #ifdef HAVE_MEDIAPLAYER setViewMode(MediaPlayerMode); d->mediaPlayerView->setCurrentItem(info.fileUrl(), !previous.isNull(), !next.isNull()); #endif //HAVE_MEDIAPLAYER } else // Static image or Raw image. { // Stop media player if running... if (viewMode() == MediaPlayerMode) { #ifdef HAVE_MEDIAPLAYER d->mediaPlayerView->setCurrentItem(); #endif //HAVE_MEDIAPLAYER } d->imagePreviewView->setItemInfo(info, previous, next); // NOTE: No need to toggle immediately in PreviewImageMode here, // because we will receive a signal for that when the image preview will be loaded. // This will prevent a flicker effect with the old image preview loaded in stack. } // do not touch the selection, only adjust current info QModelIndex currentIndex = d->thumbBar->imageSortFilterModel()->indexForItemInfo(info); d->thumbBar->selectionModel()->setCurrentIndex(currentIndex, QItemSelectionModel::NoUpdate); } } StackedView::StackedViewMode StackedView::viewMode() const { - return StackedViewMode(indexOf(currentWidget())); + return StackedViewMode(d->stackMap.value(currentIndex())); } void StackedView::setViewMode(const StackedViewMode mode) { qCDebug(DIGIKAM_GENERAL_LOG) << "Stacked View Mode : " << mode; if ((mode < StackedViewModeFirst) || (mode > StackedViewModeLast)) { return; } if (mode == PreviewImageMode || mode == MediaPlayerMode) { d->thumbBarDock->restoreVisibility(); syncSelection(d->imageIconView, d->thumbBar); } else { d->thumbBarDock->hide(); } if (mode == IconViewMode || mode == WelcomePageMode || mode == MapWidgetMode || mode == TableViewMode) { setPreviewItem(); - setCurrentIndex(mode); + setCurrentIndex(d->stackMap.key(mode)); } else { - setCurrentIndex(mode); + setCurrentIndex(d->stackMap.key(mode)); } #ifdef HAVE_MARBLE d->mapWidgetView->setActive(mode == MapWidgetMode); #endif // HAVE_MARBLE d->tableView->slotSetActive(mode == TableViewMode); if (mode == IconViewMode) { d->imageIconView->setFocus(); } #ifdef HAVE_MARBLE else if (mode == MapWidgetMode) { d->mapWidgetView->setFocus(); } #endif // HAVE_MARBLE else if (mode == TableViewMode) { d->tableView->setFocus(); } emit signalViewModeChanged(); } void StackedView::syncSelection(ItemCategorizedView* from, ItemCategorizedView* to) { ImageSortFilterModel* const fromModel = from->imageSortFilterModel(); ImageSortFilterModel* const toModel = to->imageSortFilterModel(); QModelIndex currentIndex = toModel->indexForItemInfo(from->currentInfo()); // sync selection QItemSelection selection = from->selectionModel()->selection(); QItemSelection newSelection; foreach (const QItemSelectionRange& range, selection) { QModelIndex topLeft = toModel->indexForItemInfo(fromModel->imageInfo(range.topLeft())); QModelIndex bottomRight = toModel->indexForItemInfo(fromModel->imageInfo(range.bottomRight())); newSelection.select(topLeft, bottomRight); } d->syncingSelection = true; if (currentIndex.isValid()) { // set current info to->setCurrentIndex(currentIndex); } to->selectionModel()->select(newSelection, QItemSelectionModel::ClearAndSelect); d->syncingSelection = false; } void StackedView::slotThumbBarSelectionChanged() { - if (currentIndex() != PreviewImageMode && currentIndex() != MediaPlayerMode) + if (viewMode() != PreviewImageMode && viewMode() != MediaPlayerMode) { return; } if (d->syncingSelection) { return; } syncSelection(d->thumbBar, d->imageIconView); } void StackedView::slotIconViewSelectionChanged() { - if (currentIndex() != IconViewMode) + if (viewMode() != IconViewMode) { return; } if (d->syncingSelection) { return; } syncSelection(d->imageIconView, d->thumbBar); } void StackedView::previewLoaded() { emit signalViewModeChanged(); } void StackedView::slotZoomFactorChanged(double z) { if (viewMode() == PreviewImageMode) { emit signalZoomFactorChanged(z); } } void StackedView::increaseZoom() { d->imagePreviewView->layout()->increaseZoom(); } void StackedView::decreaseZoom() { d->imagePreviewView->layout()->decreaseZoom(); } void StackedView::zoomTo100Percents() { d->imagePreviewView->layout()->setZoomFactor(1.0, QPoint()); } void StackedView::fitToWindow() { d->imagePreviewView->layout()->fitToWindow(); } void StackedView::toggleFitToWindowOr100() { d->imagePreviewView->layout()->toggleFitToWindowOr100(); } bool StackedView::maxZoom() { return d->imagePreviewView->layout()->atMaxZoom(); } bool StackedView::minZoom() { return d->imagePreviewView->layout()->atMinZoom(); } void StackedView::setZoomFactor(double z) { // Giving a null anchor means to use the current view center d->imagePreviewView->layout()->setZoomFactor(z, QPoint()); } void StackedView::setZoomFactorSnapped(double z) { d->imagePreviewView->layout()->setZoomFactor(z, QPoint(), SinglePhotoPreviewLayout::SnapZoomFactor); } double StackedView::zoomFactor() { return d->imagePreviewView->layout()->zoomFactor(); } double StackedView::zoomMin() { return d->imagePreviewView->layout()->minZoomFactor(); } double StackedView::zoomMax() { return d->imagePreviewView->layout()->maxZoomFactor(); } void StackedView::slotPreviewLoaded(bool) { setViewMode(StackedView::PreviewImageMode); previewLoaded(); } } // namespace Digikam diff --git a/core/app/views/stack/stackedview.h b/core/app/views/stack/stackedview.h index a9c1576458..5b8b5665a5 100644 --- a/core/app/views/stack/stackedview.h +++ b/core/app/views/stack/stackedview.h @@ -1,170 +1,170 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-06-13 * Description : A widget stack to embedded album content view * or the current image preview. * * Copyright (C) 2006-2019 by Gilles Caulier * Copyright (C) 2013 by Michael G. Hansen * * 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_STACKED_VIEW_H #define DIGIKAM_STACKED_VIEW_H // Qt includes #include #include // Local includes #include "digikam_config.h" #include "digikam_export.h" #include "iteminfo.h" #include "thumbbardock.h" namespace Digikam { class DigikamItemView; class ItemCategorizedView; class ItemPreviewView; class ItemThumbnailBar; class TableView; class TrashView; #ifdef HAVE_MEDIAPLAYER class MediaPlayerView; #endif //HAVE_MEDIAPLAYER #ifdef HAVE_MARBLE class MapWidgetView; #endif // HAVE_MARBLE class StackedView : public QStackedWidget { Q_OBJECT public: enum StackedViewMode { StackedViewModeFirst = 0, IconViewMode = 0, PreviewImageMode = 1, WelcomePageMode = 2, TableViewMode = 3, TrashViewMode = 4, MapWidgetMode = 5, MediaPlayerMode = 6, StackedViewModeLast = 6 }; public: explicit StackedView(QWidget* const parent=nullptr); ~StackedView(); /* Attach the thumbnail dock widget to the specified QMainWindow. */ void setDockArea(QMainWindow*); - ThumbBarDock* thumbBarDock() const; + ThumbBarDock* thumbBarDock() const; ItemThumbnailBar* thumbBar() const; DigikamItemView* imageIconView() const; ItemPreviewView* imagePreviewView() const; - TableView* tableView() const; - TrashView* trashView() const; + TableView* tableView() const; + TrashView* trashView() const; #ifdef HAVE_MEDIAPLAYER MediaPlayerView* mediaPlayerView() const; #endif //HAVE_MEDIAPLAYER #ifdef HAVE_MARBLE MapWidgetView* mapWidgetView() const; #endif // HAVE_MARBLE /** * Single-file mode is image preview or media player, * multi-file is icon view or map, * abstract modes do not handle files (welcome page) */ bool isInSingleFileMode() const; bool isInMultipleFileMode() const; bool isInAbstractMode() const; void setPreviewItem(const ItemInfo& info = ItemInfo(), const ItemInfo& previous = ItemInfo(), const ItemInfo& next = ItemInfo()); StackedViewMode viewMode() const; void setViewMode(const StackedViewMode mode); void previewLoaded(); void increaseZoom(); void decreaseZoom(); void fitToWindow(); void toggleFitToWindowOr100(); void zoomTo100Percents(); bool maxZoom(); bool minZoom(); void setZoomFactor(double z); void setZoomFactorSnapped(double z); double zoomFactor(); double zoomMin(); double zoomMax(); Q_SIGNALS: void signalNextItem(); void signalPrevItem(); void signalDeleteItem(); void signalViewModeChanged(); void signalEscapePreview(); void signalSlideShowCurrent(); void signalZoomFactorChanged(double); void signalPopupTagsView(); void signalAddToExistingQueue(int); void signalGotoAlbumAndItem(const ItemInfo&); void signalGotoDateAndItem(const ItemInfo&); void signalGotoTagAndItem(int); public Q_SLOTS: void slotEscapePreview(); private Q_SLOTS: void slotPreviewLoaded(bool); void slotZoomFactorChanged(double); void slotThumbBarSelectionChanged(); void slotIconViewSelectionChanged(); private: void readSettings(); void syncSelection(ItemCategorizedView* from, ItemCategorizedView* to); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_STACKED_VIEW_H diff --git a/core/cmake/modules/FindMarble.cmake b/core/cmake/modules/FindMarble.cmake new file mode 100644 index 0000000000..65857b27a2 --- /dev/null +++ b/core/cmake/modules/FindMarble.cmake @@ -0,0 +1,31 @@ +# - Try to find the Marble Library +# Once done this will define +# +# Marble_FOUND - system has Marble +# MARBLE_INCLUDE_DIR - the Marble include directory +# MARBLE_LIBRARIES - the marble core libraries +# ASTRO_LIBRARIES - the marble astro libraries +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +IF ( MARBLE_INCLUDE_DIR AND MARBLE_LIBRARIES ) + + # in cache already + SET( MARBLE_FIND_QUIETLY TRUE ) + +ENDIF ( MARBLE_INCLUDE_DIR AND MARBLE_LIBRARIES ) + +FIND_PATH( MARBLE_INCLUDE_DIR NAMES marble/MarbleModel.h ) +FIND_LIBRARY( MARBLE_LIBRARIES NAMES marblewidget-qt5 marblewidget-qt5d ) +FIND_LIBRARY( ASTRO_LIBRARIES NAMES astro astrod ) + +INCLUDE( FindPackageHandleStandardArgs ) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS( Marble DEFAULT_MSG MARBLE_INCLUDE_DIR MARBLE_LIBRARIES ASTRO_LIBRARIES) + +MESSAGE(STATUS "Marble_FOUND = ${Marble_FOUND}") +MESSAGE(STATUS "MARBLE_INCLUDE_DIR = ${MARBLE_INCLUDE_DIR}") +MESSAGE(STATUS "MARBLE_LIBRARIES = ${MARBLE_LIBRARIES}") +MESSAGE(STATUS "ASTRO_LIBRARIES = ${ASTRO_LIBRARIES}") diff --git a/core/dplugins/dimg/heif/CMakeLists.txt b/core/dplugins/dimg/heif/CMakeLists.txt index 0330af8641..070ecaa4f0 100644 --- a/core/dplugins/dimg/heif/CMakeLists.txt +++ b/core/dplugins/dimg/heif/CMakeLists.txt @@ -1,193 +1,195 @@ # # Copyright (c) 2015-2019 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(MacroDPlugins) include_directories($ $ $ $ $ ) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH}) find_package(X265) include(CheckFunctionExists) include(CheckIncludeFile) CHECK_FUNCTION_EXISTS(posix_memalign HAVE_POSIX_MEMALIGN) CHECK_INCLUDE_FILE(malloc.h HAVE_MALLOC_H) CHECK_INCLUDE_FILE(stdint.h HAVE_STDINT_H) CHECK_INCLUDE_FILE(stdbool.h HAVE_STDBOOL_H) CHECK_INCLUDE_FILE(inttypes.h HAVE_INTTYPES_H) CHECK_INCLUDE_FILE(stddef.h HAVE_STDDEF_H) CHECK_INCLUDE_FILE(strings.h HAVE_STRINGS_H) CHECK_INCLUDE_FILE(unistd.h HAVE_UNISTD_H) if(HAVE_INTTYPES_H) add_definitions(-DHAVE_INTTYPES_H) endif() if(HAVE_STDDEF_H) add_definitions(-DHAVE_STDDEF_H) endif() if(HAVE_STRINGS_H) add_definitions(-DHAVE_STRINGS_H) endif() if(HAVE_UNISTD_H) add_definitions(-DHAVE_UNISTD_H) endif() if(HAVE_MALLOC_H) add_definitions(-DHAVE_MALLOC_H) endif() if(HAVE_STDINT_H) add_definitions(-DHAVE_STDINT_H) endif() if(HAVE_STDBOOL_H) add_definitions(-DHAVE_STDBOOL_H) endif() if(HAVE_POSIX_MEMALIGN) add_definitions(-DHAVE_POSIX_MEMALIGN) endif() add_definitions(-DHAVE_LIBDE265=1) set(NUMERIC_VERSION 0x01050100) set(PACKAGE_VERSION 1.5.1) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/libheif/heif_version.h" ) set(NUMERIC_VERSION 0x01000000) set(PACKAGE_VERSION 1.0.0) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libde265/de265-version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/libde265/de265-version.h" ) set(libheif_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/libheif/bitstream.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/box.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/error.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_context.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_file.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_image.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_hevc.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_colorconversion.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_plugin_registry.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_plugin.cc ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_decoder_libde265.cc ) if(X265_FOUND) include_directories(${X265_INCLUDE_DIRS}) add_definitions(-DHAVE_X265=1) set(libheif_SRCS ${libheif_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/libheif/heif_encoder_x265.cc ) else() set(X265_LIBRARIES "") endif() foreach(_currentfile ${libheif_SRCS}) if(NOT MSVC) set_source_files_properties(${_currentfile} PROPERTIES COMPILE_FLAGS "-w") endif() endforeach() set(libde265_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/libde265/bitstream.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/cabac.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/de265.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/deblock.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/decctx.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/nal-parser.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/dpb.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/image.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/intrapred.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/md5.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/nal.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/pps.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/transform.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/refpic.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/sao.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/scan.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/sei.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/slice.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/sps.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/util.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/vps.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/vui.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/motion.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/threads.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/visualize.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/fallback.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/fallback-motion.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/fallback-dct.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/quality.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/configparam.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/image-io.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/alloc_pool.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/en265.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/contextmodel.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-core.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-types.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-params.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-context.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-syntax.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-intrapred.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encoder-motion.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/encpicbuf.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/sop.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/algo.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/coding-options.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/ctb-qscale.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-split.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-intrapartmode.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-interpartmode.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-skip.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-intra-inter.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/cb-mergeindex.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/tb-split.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/tb-transform.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/tb-intrapredmode.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/tb-rateestim.cc ${CMAKE_CURRENT_SOURCE_DIR}/libde265/encoder/algo/pb-mv.cc ) if(WIN32) set(libde265_SRCS ${libde265_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/libde265/extra/win32cond.cc ) endif() foreach(_currentfile ${libde265_SRCS}) if(NOT MSVC) set_source_files_properties(${_currentfile} PROPERTIES COMPILE_FLAGS "-w") endif() endforeach() set(dimgheifplugin_SRCS ${libheif_SRCS} ${libde265_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/dimgheifplugin.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dimgheifloader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimgheifloader_read.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dimgheifloader_save.cpp ) DIGIKAM_ADD_DIMG_PLUGIN(NAME HEIF SOURCES ${dimgheifplugin_SRCS} DEPENDS ${X265_LIBRARIES} ) diff --git a/core/dplugins/dimg/heif/dimgheifloader.cpp b/core/dplugins/dimg/heif/dimgheifloader.cpp index 52878e9edb..205bb805e8 100644 --- a/core/dplugins/dimg/heif/dimgheifloader.cpp +++ b/core/dplugins/dimg/heif/dimgheifloader.cpp @@ -1,638 +1,73 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-26 * Description : A HEIF IO file for DImg framework * * Copyright (C) 2019 by Gilles Caulier * - * Other HEIF loader implementions: - * https://github.com/KDE/krita/tree/master/plugins/impex/heif - * https://github.com/jakar/qt-heif-image-plugin - * https://github.com/ImageMagick/ImageMagick/blob/master/coders/heic.c - * * 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 "dimgheifloader.h" -// Qt includes - -#include -#include -#include -#include -#include -#include - // Local includes #include "digikam_debug.h" #include "dimg.h" #include "dimgloaderobserver.h" -#include "metaengine.h" namespace Digikam { DImgHEIFLoader::DImgHEIFLoader(DImg* const image) : DImgLoader(image) { m_hasAlpha = false; m_sixteenBit = false; m_observer = nullptr; } -bool DImgHEIFLoader::load(const QString& filePath, DImgLoaderObserver* const observer) -{ - m_observer = observer; - -// readMetadata(filePath, DImg::QIMAGE); NOTE: Exiv2 do not support HEIC yet - - FILE* const file = fopen(QFile::encodeName(filePath).constData(), "rb"); - - if (!file) - { - qWarning() << "Error: Could not open source file."; - loadingFailed(); - return false; - } - - const int headerLen = 12; - - unsigned char header[headerLen]; - - if (fread(&header, headerLen, 1, file) != 1) - { - qWarning() << "Error: Could not parse magic identifier."; - fclose(file); - loadingFailed(); - return false; - } - - if ((memcmp(&header[4], "ftyp", 4) != 0) && - (memcmp(&header[8], "heic", 4) != 0) && - (memcmp(&header[8], "heix", 4) != 0) && - (memcmp(&header[8], "mif1", 4) != 0)) - { - qWarning() << "Error: source file is not HEIC image."; - fclose(file); - loadingFailed(); - return false; - } - - fclose(file); - - if (observer) - { - observer->progressInfo(m_image, 0.1F); - } - - // ------------------------------------------------------------------- - // Initialize HEIF API. - - heif_item_id primary_image_id; - - struct heif_context* const heif_context = heif_context_alloc(); - struct heif_error error = heif_context_read_from_file(heif_context, - QFile::encodeName(filePath).constData(), NULL); - - if (!isHeifSuccess(&error)) - { - qWarning() << "Error: Could not read source file."; - loadingFailed(); - heif_context_free(heif_context); - return false; - } - - error = heif_context_get_primary_image_ID(heif_context, &primary_image_id); - - if (!isHeifSuccess(&error)) - { - qWarning() << "Error: Could not load image data."; - loadingFailed(); - heif_context_free(heif_context); - return false; - } - - return (readHEICImageByID(heif_context, primary_image_id)); -} - bool DImgHEIFLoader::hasAlpha() const { return m_hasAlpha; } bool DImgHEIFLoader::sixteenBit() const { return m_sixteenBit; } bool DImgHEIFLoader::isReadOnly() const { +#ifdef HAVE_X265 return false; +#else + return true; +#endif } bool DImgHEIFLoader::isHeifSuccess(struct heif_error* const error) { if (error->code == 0) - return true; - - qWarning() << "Cannot read HEIF image:" << error->message; - return false; -} - -bool DImgHEIFLoader::readHEICColorProfile(struct heif_image_handle* const image_handle) -{ -#if LIBHEIF_NUMERIC_VERSION >= 0x01040000 - - size_t length = heif_image_handle_get_raw_color_profile_size(image_handle); - - if (length > 0) - { - // Read color profile. - - QByteArray profile; - profile.resize(length); - - struct heif_error error = heif_image_handle_get_raw_color_profile(image_handle, - profile.data()); - - if (error.code == 0) - { - qDebug() << "HEIC color profile found with size:" << length; - imageSetIccProfile(IccProfile(profile)); - return true; - } - } - -#endif - - // If ICC profile is null, check Exif metadata. - checkExifWorkingColorSpace(); - - return true; -} - -bool DImgHEIFLoader::readHEICMetadata(struct heif_image_handle* const image_handle) -{ - heif_item_id dataIds[10]; - QByteArray exif; - QByteArray xmp; - - int count = heif_image_handle_get_list_of_metadata_block_IDs(image_handle, - nullptr, - dataIds, - 10); - - if (count > 0) - { - for (int i = 0 ; i < count ; ++i) - { - if (QLatin1String(heif_image_handle_get_metadata_type(image_handle, dataIds[i])) == QLatin1String("Exif")) - { - // Read Exif chunk. - - size_t length = heif_image_handle_get_metadata_size(image_handle, dataIds[i]); - - QByteArray exifChunk; - exifChunk.resize(length); - - struct heif_error error = heif_image_handle_get_metadata(image_handle, - dataIds[i], - exifChunk.data()); - - if ((error.code == 0) && (length > 4)) - { - // The first 4 bytes indicate the - // offset to the start of the TIFF header of the Exif data. - - int skip = ((exifChunk.constData()[0] << 24) | - (exifChunk.constData()[1] << 16) | - (exifChunk.constData()[2] << 8) | - exifChunk.constData()[3]) + 4; - - if (exifChunk.size() > skip) - { - // Copy the real exif data into the byte array - - qDebug() << "HEIC exif container found with size:" << length - skip; - exif.append((char*)(exifChunk.data() + skip), exifChunk.size() - skip); - } - } - } - - if ( - (QLatin1String(heif_image_handle_get_metadata_type(image_handle, dataIds[i])) == QLatin1String("mime")) && - (QLatin1String(heif_image_handle_get_metadata_content_type(image_handle, dataIds[i])) == QLatin1String("application/rdf+xml")) - ) - { - // Read Xmp chunk. - - size_t length = heif_image_handle_get_metadata_size(image_handle, dataIds[i]); - - xmp.resize(length); - - struct heif_error error = heif_image_handle_get_metadata(image_handle, - dataIds[i], - xmp.data()); - - if ((error.code == 0)) - { - qDebug() << "HEIC xmp container found with size:" << length; - } - else - { - xmp = QByteArray(); - } - } - } - } - - if (!exif.isEmpty() || !xmp.isEmpty()) { - MetaEngine meta; - - if (!exif.isEmpty()) - meta.setExif(exif); - - if (!xmp.isEmpty()) - meta.setXmp(xmp); - - m_image->setMetadata(meta.data()); return true; } + qWarning() << "Error while processing HEIC image:" << error->message; return false; } -bool DImgHEIFLoader::readHEICImageByID(struct heif_context* const heif_context, - heif_item_id image_id) -{ - struct heif_image* heif_image = nullptr; - struct heif_image_handle* image_handle = nullptr; - - struct heif_error error = heif_context_get_image_handle(heif_context, image_id, &image_handle); - - if (!isHeifSuccess(&error)) - { - return false; - } - - // NOTE: An HEIC image without ICC color profile or without metadata still valid. - - if (m_loadFlags & LoadICCData) - { - readHEICColorProfile(image_handle); - readHEICMetadata(image_handle); - } - - if (m_observer) - { - m_observer->progressInfo(m_image, 0.2F); - } - - if (m_loadFlags & LoadImageData) - { - // Copy HEIF image into data structures. - - struct heif_decoding_options* const decode_options = heif_decoding_options_alloc(); - decode_options->ignore_transformations = 1; - m_hasAlpha = heif_image_handle_has_alpha_channel(image_handle); - heif_chroma chroma = m_hasAlpha ? heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB; - - error = heif_decode_image(image_handle, - &heif_image, - heif_colorspace_RGB, - chroma, - decode_options); - - if (!isHeifSuccess(&error)) - { - heif_image_handle_release(image_handle); - return false; - } - - if (m_observer) - { - m_observer->progressInfo(m_image, 0.3F); - } - - heif_decoding_options_free(decode_options); - - int colorDepth = heif_image_get_bits_per_pixel(heif_image, heif_channel_interleaved); - imageWidth() = heif_image_get_width(heif_image, heif_channel_interleaved); - imageHeight() = heif_image_get_height(heif_image, heif_channel_interleaved); - - qDebug() << "HEIC image properties: size(" << imageWidth() << "x" << imageHeight() - << "), Alpha:" << m_hasAlpha << ", Color depth :" << colorDepth; - - if (!QSize(imageWidth(), imageHeight()).isValid()) - { - heif_image_release(heif_image); - heif_image_handle_release(image_handle); - return false; - } - - int stride = 0; - uint8_t* const ptr = heif_image_get_plane(heif_image, heif_channel_interleaved, &stride); - - qDebug() << "HEIC data :" << ptr; - qDebug() << "HEIC stride:" << stride; - - uchar* data = nullptr; - - if (colorDepth == 24 || // RGB - colorDepth == 32) // RGBA - { - qDebug() << "Color bytes depth: 8"; - m_sixteenBit = false; - } - else if (colorDepth == 48 || // RGB - colorDepth == 64) // RGBA - { - qDebug() << "Color bytes depth: 16"; - m_sixteenBit = true; - } - else - { - qWarning() << "Color bits depth: " << colorDepth << ": not supported!"; - heif_image_release(heif_image); - heif_image_handle_release(image_handle); - return false; - } - - if (m_sixteenBit) - { - data = new_failureTolerant(imageWidth(), imageHeight(), 8); // 16 bits/color/pixel - } - else - { - data = new_failureTolerant(imageWidth(), imageHeight(), 4); // 8 bits/color/pixel - } - - if (m_observer) - { - m_observer->progressInfo(m_image, 0.4F); - } - - uchar* dst = data; - unsigned short* dst16 = reinterpret_cast(data); - uchar* src = reinterpret_cast(ptr); - unsigned short* src16 = reinterpret_cast(ptr); - unsigned int checkPoint = 0; - unsigned int size = imageHeight()*imageWidth(); - - for (unsigned int i = 0 ; i < size ; ++i) - { - if (!m_sixteenBit) // 8 bits image. - { - // Blue - dst[0] = src[2]; - // Green - dst[1] = src[1]; - // Red - dst[2] = src[3]; - - // Alpha - - if (m_hasAlpha) - { - dst[3] = src[3]; - src += 4; - } - else - { - dst[3] = 0xFF; - src += 3; - } - - dst += 4; - } - else // 16 bits image. - { - // Blue - dst16[0] = src16[2]; - // Green - dst16[1] = src16[1]; - // Red - dst16[2] = src16[0]; - - // Alpha - - if (m_hasAlpha) - { - dst16[3] = src16[3]; - src16 += 4; - } - else - { - dst16[3] = 0xFFFF; - src16 += 3; - } - - dst16 += 4; - } - - - if (m_observer && i >= checkPoint) - { - checkPoint += granularity(m_observer, i, 0.8F); - - if (!m_observer->continueQuery(m_image)) - { - heif_image_release(heif_image); - heif_image_handle_release(image_handle); - - loadingFailed(); - return false; - } - - m_observer->progressInfo(m_image, 0.4 + (0.8 * (((float)i) / ((float)size)))); - } - } - - imageData() = data; - imageSetAttribute(QLatin1String("format"), QLatin1String("HEIF")); - imageSetAttribute(QLatin1String("originalColorModel"), DImg::RGB); - imageSetAttribute(QLatin1String("originalBitDepth"), m_sixteenBit ? 16 : 8); - imageSetAttribute(QLatin1String("originalSize"), QSize(imageWidth(), imageHeight())); - } - - if (m_observer) - { - m_observer->progressInfo(m_image, 0.9F); - } - - heif_image_release(heif_image); - heif_image_handle_release(image_handle); - - return true; -} - -bool DImgHEIFLoader::save(const QString& filePath, DImgLoaderObserver* const observer) -{ -#ifdef HAVE_X265 - - m_observer = observer; - - // ------------------------------------------------------------------- - // Open the file - - FILE* const f = fopen(QFile::encodeName(filePath).constData(), "wb"); - - if (!f) - { - qWarning() << "Cannot open target image file."; - return false; - } - - // TODO - -#endif - - return false; -} - -/* -KisImportExportErrorCode HeifExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration) -{ - KisImageSP image = document->savingImage(); - const KoColorSpace *cs = image->colorSpace(); - - // Convert to 8 bits rgba on saving - if (cs->colorModelId() != RGBAColorModelID || cs->colorDepthId() != Integer8BitsColorDepthID) { - cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Integer8BitsColorDepthID.id()); - image->convertImageColorSpace(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); - } - - int quality = configuration->getInt("quality", 50); - bool lossless = configuration->getBool("lossless", false); - bool has_alpha = configuration->getBool(KisImportExportFilter::ImageContainsTransparencyTag, false); - - - // If we want to add information from the document to the metadata, - // we should do that here. - - try { - // --- use standard HEVC encoder - - heif::Encoder encoder(heif_compression_HEVC); - - encoder.set_lossy_quality(quality); - encoder.set_lossless(lossless); - - - // --- convert KisImage to HEIF image --- - int width = image->width(); - int height = image->height(); - - heif::Context ctx; - - heif::Image img; - img.create(width,height, heif_colorspace_RGB, heif_chroma_444); - img.add_plane(heif_channel_R, width,height, 8); - img.add_plane(heif_channel_G, width,height, 8); - img.add_plane(heif_channel_B, width,height, 8); - - uint8_t* ptrR {0}; - uint8_t* ptrG {0}; - uint8_t* ptrB {0}; - uint8_t* ptrA {0}; - int strideR,strideG,strideB,strideA; - - ptrR = img.get_plane(heif_channel_R, &strideR); - ptrG = img.get_plane(heif_channel_G, &strideG); - ptrB = img.get_plane(heif_channel_B, &strideB); - - if (has_alpha) { - img.add_plane(heif_channel_Alpha, width,height, 8); - ptrA = img.get_plane(heif_channel_Alpha, &strideA); - } - - KisPaintDeviceSP pd = image->projection(); - - for (int y=0; ycreateHLineIteratorNG(0, y, width); - - for (int x=0; x::red(it->rawData()); - ptrG[y*strideG+x] = KoBgrTraits::green(it->rawData()); - ptrB[y*strideB+x] = KoBgrTraits::blue(it->rawData()); - - if (has_alpha) { - ptrA[y*strideA+x] = cs->opacityU8(it->rawData()); - } - - it->nextPixel(); - } - } - - // --- encode and write image - - heif::ImageHandle handle = ctx.encode_image(img, encoder); - - - - // --- add Exif / XMP metadata - - KisExifInfoVisitor exivInfoVisitor; - exivInfoVisitor.visit(image->rootLayer().data()); - - QScopedPointer metaDataStore; - if (exivInfoVisitor.metaDataCount() == 1) { - metaDataStore.reset(new KisMetaData::Store(*exivInfoVisitor.exifInfo())); - } - else { - metaDataStore.reset(new KisMetaData::Store()); - } - - if (!metaDataStore->empty()) { - { - KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value("exif"); - QBuffer buffer; - exifIO->saveTo(metaDataStore.data(), &buffer, KisMetaData::IOBackend::NoHeader); // Or JpegHeader? Or something else? - QByteArray data = buffer.data(); - - // Write the data to the file - ctx.add_exif_metadata(handle, data.constData(), data.size()); - } - { - KisMetaData::IOBackend* xmpIO = KisMetaData::IOBackendRegistry::instance()->value("xmp"); - QBuffer buffer; - xmpIO->saveTo(metaDataStore.data(), &buffer, KisMetaData::IOBackend::NoHeader); // Or JpegHeader? Or something else? - QByteArray data = buffer.data(); - - // Write the data to the file - ctx.add_XMP_metadata(handle, data.constData(), data.size()); - } - } - - - // --- write HEIF file - - Writer_QIODevice writer(io); - - ctx.write(writer); - } - catch (heif::Error err) { - return setHeifError(document, err); - } - - return ImportExportCodes::OK; -} -*/ - } // namespace Digikam diff --git a/core/dplugins/dimg/heif/dimgheifloader.h b/core/dplugins/dimg/heif/dimgheifloader.h index f33bd50316..b64db2c262 100644 --- a/core/dplugins/dimg/heif/dimgheifloader.h +++ b/core/dplugins/dimg/heif/dimgheifloader.h @@ -1,71 +1,89 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-26 * Description : A HEIF IO file for DImg framework * * Copyright (C) 2019 by Gilles Caulier * + * Other HEIF loader implementions: + * https://github.com/KDE/krita/tree/master/plugins/impex/heif + * https://github.com/jakar/qt-heif-image-plugin + * https://github.com/ImageMagick/ImageMagick/blob/master/coders/heic.c + * * 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_HEIF_LOADER_H #define DIGIKAM_DIMG_HEIF_LOADER_H // Local includes #include "dimg.h" #include "dimgloader.h" #include "digikam_export.h" #include "heif.h" using namespace Digikam; namespace Digikam { class DIGIKAM_EXPORT DImgHEIFLoader : public DImgLoader { public: explicit DImgHEIFLoader(DImg* const image); bool load(const QString& filePath, DImgLoaderObserver* const observer) override; bool save(const QString& filePath, DImgLoaderObserver* const observer) override; virtual bool hasAlpha() const override; virtual bool sixteenBit() const override; virtual bool isReadOnly() const override; + /** + * Determine libx265 encoder bits depth capability: 8=standard, 10, 12, or more. + * Return -1 if encoder instance is not found. + */ + static int x265MaxBitsDepth(); + private: bool isHeifSuccess(struct heif_error* const error); + + // Read operations bool readHEICColorProfile(struct heif_image_handle* const image_handle); bool readHEICMetadata(struct heif_image_handle* const image_handle); bool readHEICImageByID(struct heif_context* const heif_context, heif_item_id image_id); + // Save operations + bool saveHEICColorProfile(struct heif_image* const image); + bool saveHEICMetadata(struct heif_context* const heif_context, + struct heif_image_handle* const image_handle); + private: bool m_sixteenBit; bool m_hasAlpha; DImgLoaderObserver* m_observer; }; } // namespace Digikam #endif // DIGIKAM_DIMG_HEIF_LOADER_H diff --git a/core/dplugins/dimg/heif/dimgheifloader.cpp b/core/dplugins/dimg/heif/dimgheifloader_read.cpp similarity index 58% copy from core/dplugins/dimg/heif/dimgheifloader.cpp copy to core/dplugins/dimg/heif/dimgheifloader_read.cpp index 52878e9edb..c47c2fda89 100644 --- a/core/dplugins/dimg/heif/dimgheifloader.cpp +++ b/core/dplugins/dimg/heif/dimgheifloader_read.cpp @@ -1,638 +1,477 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-26 - * Description : A HEIF IO file for DImg framework + * Description : A HEIF IO file for DImg framework - read operations * * Copyright (C) 2019 by Gilles Caulier * - * Other HEIF loader implementions: - * https://github.com/KDE/krita/tree/master/plugins/impex/heif - * https://github.com/jakar/qt-heif-image-plugin - * https://github.com/ImageMagick/ImageMagick/blob/master/coders/heic.c - * * 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 "dimgheifloader.h" // Qt includes #include #include #include #include #include #include // Local includes #include "digikam_debug.h" #include "dimg.h" #include "dimgloaderobserver.h" #include "metaengine.h" namespace Digikam { -DImgHEIFLoader::DImgHEIFLoader(DImg* const image) - : DImgLoader(image) -{ - m_hasAlpha = false; - m_sixteenBit = false; - m_observer = nullptr; -} - bool DImgHEIFLoader::load(const QString& filePath, DImgLoaderObserver* const observer) { m_observer = observer; // readMetadata(filePath, DImg::QIMAGE); NOTE: Exiv2 do not support HEIC yet FILE* const file = fopen(QFile::encodeName(filePath).constData(), "rb"); if (!file) { qWarning() << "Error: Could not open source file."; loadingFailed(); return false; } const int headerLen = 12; unsigned char header[headerLen]; if (fread(&header, headerLen, 1, file) != 1) { qWarning() << "Error: Could not parse magic identifier."; fclose(file); loadingFailed(); return false; } if ((memcmp(&header[4], "ftyp", 4) != 0) && (memcmp(&header[8], "heic", 4) != 0) && (memcmp(&header[8], "heix", 4) != 0) && (memcmp(&header[8], "mif1", 4) != 0)) { qWarning() << "Error: source file is not HEIC image."; fclose(file); loadingFailed(); return false; } fclose(file); if (observer) { observer->progressInfo(m_image, 0.1F); } // ------------------------------------------------------------------- // Initialize HEIF API. heif_item_id primary_image_id; struct heif_context* const heif_context = heif_context_alloc(); struct heif_error error = heif_context_read_from_file(heif_context, QFile::encodeName(filePath).constData(), NULL); if (!isHeifSuccess(&error)) { qWarning() << "Error: Could not read source file."; loadingFailed(); heif_context_free(heif_context); return false; } error = heif_context_get_primary_image_ID(heif_context, &primary_image_id); if (!isHeifSuccess(&error)) { qWarning() << "Error: Could not load image data."; loadingFailed(); heif_context_free(heif_context); return false; } return (readHEICImageByID(heif_context, primary_image_id)); } -bool DImgHEIFLoader::hasAlpha() const -{ - return m_hasAlpha; -} - -bool DImgHEIFLoader::sixteenBit() const -{ - return m_sixteenBit; -} - -bool DImgHEIFLoader::isReadOnly() const -{ - return false; -} - -bool DImgHEIFLoader::isHeifSuccess(struct heif_error* const error) -{ - if (error->code == 0) - return true; - - qWarning() << "Cannot read HEIF image:" << error->message; - return false; -} - bool DImgHEIFLoader::readHEICColorProfile(struct heif_image_handle* const image_handle) { #if LIBHEIF_NUMERIC_VERSION >= 0x01040000 size_t length = heif_image_handle_get_raw_color_profile_size(image_handle); if (length > 0) { // Read color profile. QByteArray profile; profile.resize(length); struct heif_error error = heif_image_handle_get_raw_color_profile(image_handle, profile.data()); if (error.code == 0) { qDebug() << "HEIC color profile found with size:" << length; imageSetIccProfile(IccProfile(profile)); return true; } } +#else + Q_UNUSED(image_handle); #endif // If ICC profile is null, check Exif metadata. - checkExifWorkingColorSpace(); - return true; + if (checkExifWorkingColorSpace()) + { + return true; + } + + return false; } bool DImgHEIFLoader::readHEICMetadata(struct heif_image_handle* const image_handle) { heif_item_id dataIds[10]; QByteArray exif; QByteArray xmp; int count = heif_image_handle_get_list_of_metadata_block_IDs(image_handle, nullptr, dataIds, 10); if (count > 0) { for (int i = 0 ; i < count ; ++i) { if (QLatin1String(heif_image_handle_get_metadata_type(image_handle, dataIds[i])) == QLatin1String("Exif")) { // Read Exif chunk. size_t length = heif_image_handle_get_metadata_size(image_handle, dataIds[i]); QByteArray exifChunk; exifChunk.resize(length); struct heif_error error = heif_image_handle_get_metadata(image_handle, dataIds[i], exifChunk.data()); if ((error.code == 0) && (length > 4)) { // The first 4 bytes indicate the // offset to the start of the TIFF header of the Exif data. int skip = ((exifChunk.constData()[0] << 24) | (exifChunk.constData()[1] << 16) | (exifChunk.constData()[2] << 8) | exifChunk.constData()[3]) + 4; if (exifChunk.size() > skip) { // Copy the real exif data into the byte array qDebug() << "HEIC exif container found with size:" << length - skip; exif.append((char*)(exifChunk.data() + skip), exifChunk.size() - skip); } } } if ( (QLatin1String(heif_image_handle_get_metadata_type(image_handle, dataIds[i])) == QLatin1String("mime")) && (QLatin1String(heif_image_handle_get_metadata_content_type(image_handle, dataIds[i])) == QLatin1String("application/rdf+xml")) ) { // Read Xmp chunk. size_t length = heif_image_handle_get_metadata_size(image_handle, dataIds[i]); xmp.resize(length); struct heif_error error = heif_image_handle_get_metadata(image_handle, dataIds[i], xmp.data()); if ((error.code == 0)) { qDebug() << "HEIC xmp container found with size:" << length; } else { xmp = QByteArray(); } } } } if (!exif.isEmpty() || !xmp.isEmpty()) { MetaEngine meta; if (!exif.isEmpty()) meta.setExif(exif); if (!xmp.isEmpty()) meta.setXmp(xmp); m_image->setMetadata(meta.data()); return true; } return false; } bool DImgHEIFLoader::readHEICImageByID(struct heif_context* const heif_context, heif_item_id image_id) { struct heif_image* heif_image = nullptr; struct heif_image_handle* image_handle = nullptr; - - struct heif_error error = heif_context_get_image_handle(heif_context, image_id, &image_handle); + struct heif_error error = heif_context_get_image_handle(heif_context, + image_id, + &image_handle); if (!isHeifSuccess(&error)) { return false; } // NOTE: An HEIC image without ICC color profile or without metadata still valid. if (m_loadFlags & LoadICCData) { - readHEICColorProfile(image_handle); readHEICMetadata(image_handle); + readHEICColorProfile(image_handle); } if (m_observer) { m_observer->progressInfo(m_image, 0.2F); } if (m_loadFlags & LoadImageData) { // Copy HEIF image into data structures. struct heif_decoding_options* const decode_options = heif_decoding_options_alloc(); decode_options->ignore_transformations = 1; m_hasAlpha = heif_image_handle_has_alpha_channel(image_handle); - heif_chroma chroma = m_hasAlpha ? heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGB; + heif_chroma chroma = m_hasAlpha ? heif_chroma_interleaved_RGBA + : heif_chroma_interleaved_RGB; + + // Trace to check image size properties before decoding, as these values can be different. + qDebug() << "HEIC image size: (" + << heif_image_handle_get_width(image_handle) + << "x" + << heif_image_handle_get_height(image_handle) + << ")"; error = heif_decode_image(image_handle, &heif_image, heif_colorspace_RGB, chroma, decode_options); if (!isHeifSuccess(&error)) { heif_image_handle_release(image_handle); return false; } if (m_observer) { m_observer->progressInfo(m_image, 0.3F); } heif_decoding_options_free(decode_options); int colorDepth = heif_image_get_bits_per_pixel(heif_image, heif_channel_interleaved); imageWidth() = heif_image_get_width(heif_image, heif_channel_interleaved); imageHeight() = heif_image_get_height(heif_image, heif_channel_interleaved); - qDebug() << "HEIC image properties: size(" << imageWidth() << "x" << imageHeight() - << "), Alpha:" << m_hasAlpha << ", Color depth :" << colorDepth; + qDebug() << "Decoded HEIC image properties: size(" + << imageWidth() << "x" << imageHeight() + << "), Alpha:" << m_hasAlpha + << ", Color depth :" << colorDepth; if (!QSize(imageWidth(), imageHeight()).isValid()) { heif_image_release(heif_image); heif_image_handle_release(image_handle); return false; } int stride = 0; uint8_t* const ptr = heif_image_get_plane(heif_image, heif_channel_interleaved, &stride); - qDebug() << "HEIC data :" << ptr; - qDebug() << "HEIC stride:" << stride; + qDebug() << "HEIC data container:" << ptr; + qDebug() << "HEIC bytes per line:" << stride; + + if (!ptr || stride <= 0) + { + qWarning() << "HEIC data pixels information not valid!"; + heif_image_release(heif_image); + heif_image_handle_release(image_handle); + return false; + } uchar* data = nullptr; if (colorDepth == 24 || // RGB colorDepth == 32) // RGBA { qDebug() << "Color bytes depth: 8"; m_sixteenBit = false; } else if (colorDepth == 48 || // RGB colorDepth == 64) // RGBA { qDebug() << "Color bytes depth: 16"; m_sixteenBit = true; } else { qWarning() << "Color bits depth: " << colorDepth << ": not supported!"; heif_image_release(heif_image); heif_image_handle_release(image_handle); return false; } if (m_sixteenBit) { data = new_failureTolerant(imageWidth(), imageHeight(), 8); // 16 bits/color/pixel } else { data = new_failureTolerant(imageWidth(), imageHeight(), 4); // 8 bits/color/pixel } if (m_observer) { m_observer->progressInfo(m_image, 0.4F); } uchar* dst = data; - unsigned short* dst16 = reinterpret_cast(data); - uchar* src = reinterpret_cast(ptr); - unsigned short* src16 = reinterpret_cast(ptr); + unsigned short* dst16 = nullptr; + uchar* src = nullptr; + unsigned short* src16 = nullptr; unsigned int checkPoint = 0; - unsigned int size = imageHeight()*imageWidth(); - for (unsigned int i = 0 ; i < size ; ++i) + for (unsigned int y = 0 ; y < imageHeight() ; ++y) { - if (!m_sixteenBit) // 8 bits image. - { - // Blue - dst[0] = src[2]; - // Green - dst[1] = src[1]; - // Red - dst[2] = src[3]; + src = reinterpret_cast(ptr + (y * stride)); + src16 = reinterpret_cast(src); - // Alpha - - if (m_hasAlpha) - { - dst[3] = src[3]; - src += 4; - } - else + for (unsigned int x = 0 ; x < imageWidth() ; ++x) + { + if (!m_sixteenBit) // 8 bits image. { - dst[3] = 0xFF; - src += 3; - } + // Blue + dst[0] = src[2]; + // Green + dst[1] = src[1]; + // Red + dst[2] = src[3]; - dst += 4; - } - else // 16 bits image. - { - // Blue - dst16[0] = src16[2]; - // Green - dst16[1] = src16[1]; - // Red - dst16[2] = src16[0]; + // Alpha - // Alpha + if (m_hasAlpha) + { + dst[3] = src[3]; + src += 4; + } + else + { + dst[3] = 0xFF; + src += 3; + } - if (m_hasAlpha) - { - dst16[3] = src16[3]; - src16 += 4; + dst += 4; } - else + else // 16 bits image. { - dst16[3] = 0xFFFF; - src16 += 3; - } + // Blue + dst16[0] = src16[2]; + // Green + dst16[1] = src16[1]; + // Red + dst16[2] = src16[0]; - dst16 += 4; - } + // Alpha + if (m_hasAlpha) + { + dst16[3] = src16[3]; + src16 += 4; + } + else + { + dst16[3] = 0xFFFF; + src16 += 3; + } - if (m_observer && i >= checkPoint) - { - checkPoint += granularity(m_observer, i, 0.8F); + dst16 += 4; + } + } - if (!m_observer->continueQuery(m_image)) - { - heif_image_release(heif_image); - heif_image_handle_release(image_handle); + if (m_observer && y >= checkPoint) + { + checkPoint += granularity(m_observer, y, 0.8F); - loadingFailed(); - return false; - } + if (!m_observer->continueQuery(m_image)) + { + heif_image_release(heif_image); + heif_image_handle_release(image_handle); + + loadingFailed(); + return false; + } - m_observer->progressInfo(m_image, 0.4 + (0.8 * (((float)i) / ((float)size)))); + m_observer->progressInfo(m_image, 0.4 + (0.8 * (((float)y) / ((float)imageHeight())))); } } imageData() = data; imageSetAttribute(QLatin1String("format"), QLatin1String("HEIF")); imageSetAttribute(QLatin1String("originalColorModel"), DImg::RGB); imageSetAttribute(QLatin1String("originalBitDepth"), m_sixteenBit ? 16 : 8); imageSetAttribute(QLatin1String("originalSize"), QSize(imageWidth(), imageHeight())); } if (m_observer) { m_observer->progressInfo(m_image, 0.9F); } heif_image_release(heif_image); heif_image_handle_release(image_handle); return true; } -bool DImgHEIFLoader::save(const QString& filePath, DImgLoaderObserver* const observer) -{ -#ifdef HAVE_X265 - - m_observer = observer; - - // ------------------------------------------------------------------- - // Open the file - - FILE* const f = fopen(QFile::encodeName(filePath).constData(), "wb"); - - if (!f) - { - qWarning() << "Cannot open target image file."; - return false; - } - - // TODO - -#endif - - return false; -} - -/* -KisImportExportErrorCode HeifExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration) -{ - KisImageSP image = document->savingImage(); - const KoColorSpace *cs = image->colorSpace(); - - // Convert to 8 bits rgba on saving - if (cs->colorModelId() != RGBAColorModelID || cs->colorDepthId() != Integer8BitsColorDepthID) { - cs = KoColorSpaceRegistry::instance()->colorSpace(RGBAColorModelID.id(), Integer8BitsColorDepthID.id()); - image->convertImageColorSpace(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); - } - - int quality = configuration->getInt("quality", 50); - bool lossless = configuration->getBool("lossless", false); - bool has_alpha = configuration->getBool(KisImportExportFilter::ImageContainsTransparencyTag, false); - - - // If we want to add information from the document to the metadata, - // we should do that here. - - try { - // --- use standard HEVC encoder - - heif::Encoder encoder(heif_compression_HEVC); - - encoder.set_lossy_quality(quality); - encoder.set_lossless(lossless); - - - // --- convert KisImage to HEIF image --- - int width = image->width(); - int height = image->height(); - - heif::Context ctx; - - heif::Image img; - img.create(width,height, heif_colorspace_RGB, heif_chroma_444); - img.add_plane(heif_channel_R, width,height, 8); - img.add_plane(heif_channel_G, width,height, 8); - img.add_plane(heif_channel_B, width,height, 8); - - uint8_t* ptrR {0}; - uint8_t* ptrG {0}; - uint8_t* ptrB {0}; - uint8_t* ptrA {0}; - int strideR,strideG,strideB,strideA; - - ptrR = img.get_plane(heif_channel_R, &strideR); - ptrG = img.get_plane(heif_channel_G, &strideG); - ptrB = img.get_plane(heif_channel_B, &strideB); - - if (has_alpha) { - img.add_plane(heif_channel_Alpha, width,height, 8); - ptrA = img.get_plane(heif_channel_Alpha, &strideA); - } - - KisPaintDeviceSP pd = image->projection(); - - for (int y=0; ycreateHLineIteratorNG(0, y, width); - - for (int x=0; x::red(it->rawData()); - ptrG[y*strideG+x] = KoBgrTraits::green(it->rawData()); - ptrB[y*strideB+x] = KoBgrTraits::blue(it->rawData()); - - if (has_alpha) { - ptrA[y*strideA+x] = cs->opacityU8(it->rawData()); - } - - it->nextPixel(); - } - } - - // --- encode and write image - - heif::ImageHandle handle = ctx.encode_image(img, encoder); - - - - // --- add Exif / XMP metadata - - KisExifInfoVisitor exivInfoVisitor; - exivInfoVisitor.visit(image->rootLayer().data()); - - QScopedPointer metaDataStore; - if (exivInfoVisitor.metaDataCount() == 1) { - metaDataStore.reset(new KisMetaData::Store(*exivInfoVisitor.exifInfo())); - } - else { - metaDataStore.reset(new KisMetaData::Store()); - } - - if (!metaDataStore->empty()) { - { - KisMetaData::IOBackend* exifIO = KisMetaData::IOBackendRegistry::instance()->value("exif"); - QBuffer buffer; - exifIO->saveTo(metaDataStore.data(), &buffer, KisMetaData::IOBackend::NoHeader); // Or JpegHeader? Or something else? - QByteArray data = buffer.data(); - - // Write the data to the file - ctx.add_exif_metadata(handle, data.constData(), data.size()); - } - { - KisMetaData::IOBackend* xmpIO = KisMetaData::IOBackendRegistry::instance()->value("xmp"); - QBuffer buffer; - xmpIO->saveTo(metaDataStore.data(), &buffer, KisMetaData::IOBackend::NoHeader); // Or JpegHeader? Or something else? - QByteArray data = buffer.data(); - - // Write the data to the file - ctx.add_XMP_metadata(handle, data.constData(), data.size()); - } - } - - - // --- write HEIF file - - Writer_QIODevice writer(io); - - ctx.write(writer); - } - catch (heif::Error err) { - return setHeifError(document, err); - } - - return ImportExportCodes::OK; -} -*/ - } // namespace Digikam diff --git a/core/dplugins/dimg/heif/dimgheifloader_save.cpp b/core/dplugins/dimg/heif/dimgheifloader_save.cpp new file mode 100644 index 0000000000..7b70d1095e --- /dev/null +++ b/core/dplugins/dimg/heif/dimgheifloader_save.cpp @@ -0,0 +1,474 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * https://www.digikam.org + * + * Date : 2019-09-26 + * Description : A HEIF IO file for DImg framework - save operations + * + * Copyright (C) 2019 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 "dimgheifloader.h" + +// Qt includes + +#include +#include +#include +#include +#include +#include + +// Local includes + +#include "digikam_debug.h" +#include "dimg.h" +#include "dimgloaderobserver.h" +#include "metaengine.h" + +// libx265 includes + +#ifdef HAVE_X265 +# include +#endif + +namespace Digikam +{ + +int DImgHEIFLoader::x265MaxBitsDepth() +{ + int maxOutputBitsDepth = -1; + +#ifdef HAVE_X265 + + for (int i = 16 ; i >= 8 ; i-=2) + { + qDebug() << "Check HEVC encoder for" << i << "bits encoding..."; + const x265_api* const api = x265_api_get(i); + + if (api) + { + maxOutputBitsDepth = i; + break; + } + } + + qDebug() << "HEVC encoder max bits depth:" << maxOutputBitsDepth; + +#endif + + if (maxOutputBitsDepth == -1) + { + qWarning() << "Cannot get max supported HEVC encoder bits depth!"; + } + + return maxOutputBitsDepth; +} + +bool DImgHEIFLoader::save(const QString& filePath, DImgLoaderObserver* const observer) +{ + m_observer = observer; + + // ------------------------------------------------------------------- + // Open the file + + FILE* const f = fopen(QFile::encodeName(filePath).constData(), "wb"); + + if (!f) + { + qWarning() << "Cannot open target image file."; + return false; + } + + fclose(f); + + QVariant qualityAttr = imageGetAttribute(QLatin1String("quality")); + int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 50; + QVariant losslessAttr = imageGetAttribute(QLatin1String("lossless")); + bool lossless = losslessAttr.isValid() ? qualityAttr.toBool() : false; + + // --- Determine libx265 encoder bits depth capability: 8=standard, 10, 12, or later 16. + + int maxOutputBitsDepth = x265MaxBitsDepth(); + + if (maxOutputBitsDepth == -1) + { + return false; + } + + heif_chroma chroma; + + if (maxOutputBitsDepth > 8) // 16 bits image. + { + chroma = imageHasAlpha() ? heif_chroma_interleaved_RRGGBBAA_BE + : heif_chroma_interleaved_RRGGBB_BE; + } + else + { + chroma = imageHasAlpha() ? heif_chroma_interleaved_RGBA + : heif_chroma_interleaved_RGB; + } + + // --- use standard HEVC encoder + + qDebug() << "HEVC encoder setup..."; + + struct heif_context* const ctx = heif_context_alloc(); + + if (!ctx) + { + qWarning() << "Cannot create HEIC context!"; + return false; + } + + struct heif_encoder* encoder = nullptr; + struct heif_error error = heif_context_get_encoder_for_format(ctx, + heif_compression_HEVC, + &encoder); + + if (!isHeifSuccess(&error)) + { + heif_context_free(ctx); + return false; + } + + heif_encoder_set_lossy_quality(encoder, quality); + heif_encoder_set_lossless(encoder, lossless); + + struct heif_image* image = nullptr; + error = heif_image_create(imageWidth(), + imageHeight(), + heif_colorspace_RGB, + chroma, + &image); + + if (!isHeifSuccess(&error)) + { + heif_encoder_release(encoder); + heif_context_free(ctx); + return false; + } + + // --- Save color profile before to create image data, as converting to color space can be processed at this stage. + + qDebug() << "HEIC set color profile..."; + + saveHEICColorProfile(image); + + // --- Add image data + + qDebug() << "HEIC setup data plane..."; + + error = heif_image_add_plane(image, + heif_channel_interleaved, + imageWidth(), + imageHeight(), + maxOutputBitsDepth); + + if (!isHeifSuccess(&error)) + { + heif_encoder_release(encoder); + heif_context_free(ctx); + return false; + } + + int stride = 0; + uint8_t* const data = heif_image_get_plane(image, + heif_channel_interleaved, + &stride); + + if (!data || stride <= 0) + { + qWarning() << "HEIC data pixels information not valid!"; + heif_encoder_release(encoder); + heif_context_free(ctx); + return false; + } + + qDebug() << "HEIC data container:" << data; + qDebug() << "HEIC bytes per line:" << stride; + + uint checkpoint = 0; + unsigned char r = 0; + unsigned char g = 0; + unsigned char b = 0; + unsigned char a = 0; + unsigned char* src = nullptr; + unsigned char* dst = nullptr; + unsigned short r16 = 0; + unsigned short g16 = 0; + unsigned short b16 = 0; + unsigned short a16 = 0; + unsigned short* src16 = nullptr; + unsigned short* dst16 = nullptr; + int div16 = 16 - maxOutputBitsDepth; + int mul8 = maxOutputBitsDepth - 8; + int nbOutputBytesPerColor = (maxOutputBitsDepth > 8) ? (imageHasAlpha() ? 4 * 2 : 3 * 2) // output data stored on 16 bits + : (imageHasAlpha() ? 4 : 3 ); // output data stored on 8 bits + + qDebug() << "HEIC output bytes per color:" << nbOutputBytesPerColor; + qDebug() << "HEIC 16 to 8 bits coeff. :" << div16; + qDebug() << "HEIC 8 to 16 bits coeff. :" << mul8; + + for (unsigned int y = 0 ; y < imageHeight() ; ++y) + { + src = &imageData()[(y * imageWidth()) * imageBytesDepth()]; + src16 = reinterpret_cast(src); + dst = reinterpret_cast(data + (y * stride)); + dst16 = reinterpret_cast(dst); + + for (unsigned int x = 0 ; x < imageWidth() ; ++x) + { + if (imageSixteenBit()) // 16 bits source image. + { + b16 = src16[0]; + g16 = src16[1]; + r16 = src16[2]; + + if (imageHasAlpha()) + { + a16 = src16[3]; + } + + if (maxOutputBitsDepth > 8) // From 16 bits to 10 bits or more. + { + dst16[0] = (unsigned short)(r16 >> div16); + dst16[1] = (unsigned short)(g16 >> div16); + dst16[2] = (unsigned short)(b16 >> div16); + + if (imageHasAlpha()) + { + dst16[3] = (unsigned short)(a16 >> div16); + dst16 += 4; + } + else + { + dst16 += 3; + } + } + else // From 16 bits to 8 bits. + { + dst[0] = (unsigned char)(r16 >> div16); + dst[1] = (unsigned char)(g16 >> div16); + dst[2] = (unsigned char)(b16 >> div16); + + if (imageHasAlpha()) + { + dst[3] = (unsigned char)(a16 >> div16); + dst += 4; + } + else + { + dst += 3; + } + } + + src16 += 4; + } + else // 8 bits source image. + { + b = src[0]; + g = src[1]; + r = src[2]; + + if (imageHasAlpha()) + { + a = src[3]; + } + + if (maxOutputBitsDepth > 8) // From 8 bits to 10 bits or more. + { + dst16[0] = (unsigned short)(r << mul8); + dst16[1] = (unsigned short)(g << mul8); + dst16[2] = (unsigned short)(b << mul8); + + if (imageHasAlpha()) + { + dst16[3] = (unsigned short)(a << mul8); + dst16 += 4; + } + else + { + dst16 += 3; + } + } + else // From 8 bits to 8 bits. + { + dst[0] = r; + dst[1] = g; + dst[2] = b; + + if (imageHasAlpha()) + { + dst[3] = a; + dst += 4; + } + else + { + dst += 3; + } + } + + src += 4; + } + } + + if (m_observer && y == (long)checkpoint) + { + checkpoint += granularity(m_observer, imageHeight(), 0.8F); + + if (!m_observer->continueQuery(m_image)) + { + heif_encoder_release(encoder); + heif_context_free(ctx); + return false; + } + + m_observer->progressInfo(m_image, 0.1 + (0.8 * (((float)y) / ((float)imageHeight())))); + } + } + + qDebug() << "HEIC image encoding..."; + + // --- encode and write image + + struct heif_encoding_options* options = heif_encoding_options_alloc(); + options->save_alpha_channel = imageHasAlpha() ? 1 : 0; + struct heif_image_handle* hdl = nullptr; + error = heif_context_encode_image(ctx, image, encoder, options, &hdl); + + if (!isHeifSuccess(&error)) + { + heif_encoding_options_free(options); + heif_image_handle_release(hdl); + heif_encoder_release(encoder); + heif_context_free(ctx); + return false; + } + + heif_encoding_options_free(options); + heif_encoder_release(encoder); + + // --- Add Exif and XMP metadata + + qDebug() << "HEIC metadata storage..."; + + saveHEICMetadata(ctx, hdl); + + heif_image_handle_release(hdl); + + // --- TODO: Add thumnail image. + + // --- write HEIF file + + qDebug() << "HEIC flush to file..."; + + error = heif_context_write_to_file(ctx, QFile::encodeName(filePath).constData()); + + if (!isHeifSuccess(&error)) + { + heif_context_free(ctx); + return false; + } + + heif_context_free(ctx); + + imageSetAttribute(QLatin1String("savedFormat"), QLatin1String("HEIC")); + saveMetadata(filePath); + + return true; +} + +bool DImgHEIFLoader::saveHEICColorProfile(struct heif_image* const image) +{ +#if LIBHEIF_NUMERIC_VERSION >= 0x01040000 + + QByteArray profile = m_image->getIccProfile().data(); + + if (!profile.isEmpty()) + { + // Save color profile. + + struct heif_error error = heif_image_set_raw_color_profile(image, + "prof", // FIXME: detect string in profile data + profile.data(), + profile.size()); + + if (error.code != 0) + { + qWarning() << "Cannot set HEIC color profile!"; + return false; + } + + qDebug() << "Stored HEIC color profile size:" << profile.size(); + } +#else + Q_UNUSED(image_handle); +#endif + + return true; +} + +bool DImgHEIFLoader::saveHEICMetadata(struct heif_context* const heif_context, + struct heif_image_handle* const image_handle) +{ + MetaEngine meta(m_image->getMetadata()); + + if (!meta.hasExif() && !meta.hasXmp()) + { + return false; + } + + QByteArray exif = meta.getExifEncoded(); + QByteArray xmp = meta.getXmp(); + struct heif_error error; + + if (!exif.isEmpty()) + { + error = heif_context_add_exif_metadata(heif_context, + image_handle, + exif.data(), + exif.size()); + + if (error.code != 0) + { + qWarning() << "Cannot store HEIC Exif metadata!"; + return false; + } + + qDebug() << "Stored HEIC Exif data size:" << exif.size(); + } + + if (!xmp.isEmpty()) + { + error = heif_context_add_XMP_metadata(heif_context, + image_handle, + xmp.data(), + xmp.size()); + + if (error.code != 0) + { + qWarning() << "Cannot store HEIC Xmp metadata!"; + return false; + } + + qDebug() << "Stored HEIC Xmp data size:" << xmp.size(); + } + + return true; +} + +} // namespace Digikam diff --git a/core/dplugins/dimg/heif/dimgheifplugin.cpp b/core/dplugins/dimg/heif/dimgheifplugin.cpp index cf87e26c66..4726cf0b80 100644 --- a/core/dplugins/dimg/heif/dimgheifplugin.cpp +++ b/core/dplugins/dimg/heif/dimgheifplugin.cpp @@ -1,183 +1,200 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-22 * Description : HEIF DImg plugin. * * Copyright (C) 2019 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 "dimgheifplugin.h" // C++ includes #include // Qt includes #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "digikam_globals.h" #include "dimgheifloader.h" namespace DigikamHEIFDImgPlugin { DImgHEIFPlugin::DImgHEIFPlugin(QObject* const parent) : DPluginDImg(parent) { } DImgHEIFPlugin::~DImgHEIFPlugin() { } QString DImgHEIFPlugin::name() const { return i18n("HEIF loader"); } QString DImgHEIFPlugin::iid() const { return QLatin1String(DPLUGIN_IID); } QIcon DImgHEIFPlugin::icon() const { return QIcon::fromTheme(QLatin1String("image-x-generic")); } QString DImgHEIFPlugin::description() const { return i18n("An image loader based on Libheif codec"); } QString DImgHEIFPlugin::details() const { + QString x265Notice = i18n("This library is not present on your system."); + +#ifdef HAVE_X265 + int depth = DImgHEIFLoader::x265MaxBitsDepth(); + + if (depth != -1) + { + x265Notice = i18n("This library is available on your system with a maximum color depth " + "support of %1 bits.", depth); + } + else + { + x265Notice = i18n("This library is available on your system but is not able to encode " + "image with a suitable color depth."); + } +#endif + return i18n("

This plugin permit to load and save image using Libheif codec.

" "

High Efficiency Image File Format (HEIF), also known as High Efficiency Image Coding (HEIC), " "is a file format for individual images and image sequences. It was developed by the " "Moving Picture Experts Group (MPEG) and it claims that twice as much information can be " "stored in a HEIF image as in a JPEG image of the same size, resulting in a better quality image. " "HEIF also supports animation, and is capable of storing more information than an animated GIF " "at a small fraction of the size.

" + "

Encoding HEIC is relevant of optional libx265 codec. %1

" "

See " - "High Efficiency Image File Format for details.

" - ); + "High Efficiency Image File Format for details.

", x265Notice); } QList DImgHEIFPlugin::authors() const { return QList() << DPluginAuthor(QString::fromUtf8("Gilles Caulier"), QString::fromUtf8("caulier dot gilles at gmail dot com"), QString::fromUtf8("(C) 2019")) ; } void DImgHEIFPlugin::setup(QObject* const /*parent*/) { // Nothing to do } QString DImgHEIFPlugin::loaderName() const { return QLatin1String("HEIF"); } QString DImgHEIFPlugin::typeMimes() const { return QLatin1String("HEIC"); } bool DImgHEIFPlugin::canRead(const QString& filePath) const { QFileInfo fileInfo(filePath); if (!fileInfo.exists()) { qCDebug(DIGIKAM_DIMG_LOG) << "File " << filePath << " does not exist"; return false; } // First simply check file extension QString ext = fileInfo.suffix().toUpper(); if (!ext.isEmpty() && (ext == QLatin1String("HEIC"))) { return true; } // In second, we trying to parse file header. FILE* const f = fopen(QFile::encodeName(filePath).constData(), "rb"); if (!f) { qCDebug(DIGIKAM_DIMG_LOG) << "Failed to open file " << filePath; return false; } const int headerLen = 12; unsigned char header[headerLen]; if (fread(&header, headerLen, 1, f) != 1) { qCDebug(DIGIKAM_DIMG_LOG) << "Failed to read header of file " << filePath; fclose(f); return false; } fclose(f); if ((memcmp(&header[4], "ftyp", 4) == 0) || (memcmp(&header[8], "heic", 4) == 0) || (memcmp(&header[8], "heix", 4) == 0) || (memcmp(&header[8], "mif1", 4) == 0)) { return true; } return false; } bool DImgHEIFPlugin::canWrite(const QString& format) const { #ifdef HAVE_X265 if (format.toUpper() == QLatin1String("HEIC")) { return true; } #endif return false; } DImgLoader* DImgHEIFPlugin::loader(DImg* const image, const DRawDecoding&) const { return new DImgHEIFLoader(image); } } // namespace DigikamHEIFDImgPlugin diff --git a/core/dplugins/dimg/heif/dimgheifplugin.h b/core/dplugins/dimg/heif/dimgheifplugin.h index 335037ea17..bbb278ac4f 100644 --- a/core/dplugins/dimg/heif/dimgheifplugin.h +++ b/core/dplugins/dimg/heif/dimgheifplugin.h @@ -1,73 +1,73 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-26 * Description : HEIF DImg plugin. * * Copyright (C) 2019 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_DIMG_PLUGIN_H -#define DIGIKAM_HEIF_DIMG_PLUGIN_H +#ifndef DIGIKAM_DIMG_HEIF_PLUGIN_H +#define DIGIKAM_DIMG_HEIF_PLUGIN_H // Qt includes #include #include // Local includes #include "dplugindimg.h" #include "dimg.h" #include "dimgloader.h" #define DPLUGIN_IID "org.kde.digikam.plugin.dimg.HEIF" using namespace Digikam; namespace DigikamHEIFDImgPlugin { class DImgHEIFPlugin : public DPluginDImg { Q_OBJECT Q_PLUGIN_METADATA(IID DPLUGIN_IID) Q_INTERFACES(Digikam::DPluginDImg) public: explicit DImgHEIFPlugin(QObject* const parent = nullptr); ~DImgHEIFPlugin(); QString name() const override; QString iid() const override; QIcon icon() const override; QString details() const override; QString description() const override; QList authors() const override; void setup(QObject* const) override; QString loaderName() const; QString typeMimes() const; bool canRead(const QString& filePath) const; bool canWrite(const QString& format) const; DImgLoader* loader(DImg* const image, const DRawDecoding& rawSettings = DRawDecoding()) const; }; } // namespace DigikamHEIFDImgPlugin -#endif // DIGIKAM_HEIF_DIMG_PLUGIN_H +#endif // DIGIKAM_DIMG_HEIF_PLUGIN_H diff --git a/core/dplugins/dimg/imagemagick/dimgimagemagickplugin.cpp b/core/dplugins/dimg/imagemagick/dimgimagemagickplugin.cpp index 25fcfa6f7b..e11e7b2091 100644 --- a/core/dplugins/dimg/imagemagick/dimgimagemagickplugin.cpp +++ b/core/dplugins/dimg/imagemagick/dimgimagemagickplugin.cpp @@ -1,268 +1,287 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-21 * Description : ImageMagick DImg plugin. * * Copyright (C) 2019 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 "dimgimagemagickplugin.h" // Image Magick includes #include #if MagickLibVersion < 0x700 # include #endif using namespace Magick; using namespace MagickCore; // Qt includes #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "digikam_globals.h" #include "dimgimagemagickloader.h" #include "drawdecoder.h" namespace DigikamImageMagickDImgPlugin { DImgImageMagickPlugin::DImgImageMagickPlugin(QObject* const parent) : DPluginDImg(parent) { MagickCoreGenesis((char*)NULL ,MagickFalse); } DImgImageMagickPlugin::~DImgImageMagickPlugin() { MagickCoreTerminus(); } QString DImgImageMagickPlugin::name() const { return i18n("ImageMagick loader"); } QString DImgImageMagickPlugin::iid() const { return QLatin1String(DPLUGIN_IID); } QIcon DImgImageMagickPlugin::icon() const { return QIcon::fromTheme(QLatin1String("image-x-generic")); } QString DImgImageMagickPlugin::description() const { return i18n("An image loader based on ImageMagick coders"); } QString DImgImageMagickPlugin::details() const { return i18n("

This plugin permit to load and save image using ImageMagick coders.

" "

ImageMagick is a free and open-source software suite for converting raster image and vector image files. " "It can read and write over 200 image file formats.

" "

See ImageMagick documentation for details.

" ); } QList DImgImageMagickPlugin::authors() const { return QList() << DPluginAuthor(QString::fromUtf8("Maik Qualmann"), QString::fromUtf8("metzpinguin at gmail dot com"), QString::fromUtf8("(C) 2019")) << DPluginAuthor(QString::fromUtf8("Gilles Caulier"), QString::fromUtf8("caulier dot gilles at gmail dot com"), QString::fromUtf8("(C) 2006-2019")) ; } void DImgImageMagickPlugin::setup(QObject* const /*parent*/) { // Nothing to do } QString DImgImageMagickPlugin::loaderName() const { return QLatin1String("IMAGEMAGICK"); } QString DImgImageMagickPlugin::typeMimes() const { QStringList formats; ExceptionInfo ex; - size_t n = 0; + size_t n = 0; const MagickInfo** inflst = GetMagickInfoList("*", &n, &ex); + if (!inflst) + { + qWarning() << "ImageMagick coders list is null!"; + return QString(); + } + for (uint i = 0 ; i < n ; ++i) { const MagickInfo* inf = inflst[i]; - if (inf->decoder) + if (inf && inf->decoder) { #if (MagickLibVersion >= 0x69A && defined(magick_module)) formats.append(QString::fromLatin1(inf->magick_module).toUpper()); #else formats.append(QString::fromLatin1(inf->module).toUpper()); #endif } } qDebug() << "ImageMagick support this formats:" << formats; formats.removeAll(QLatin1String("JPEG")); // JPEG file format formats.removeAll(QLatin1String("JPG")); // JPEG file format formats.removeAll(QLatin1String("JPE")); // JPEG file format formats.removeAll(QLatin1String("PNG")); formats.removeAll(QLatin1String("TIFF")); formats.removeAll(QLatin1String("TIF")); formats.removeAll(QLatin1String("PGF")); formats.removeAll(QLatin1String("JP2")); // JPEG2000 file format formats.removeAll(QLatin1String("JPX")); // JPEG2000 file format formats.removeAll(QLatin1String("JPC")); // JPEG2000 code stream formats.removeAll(QLatin1String("J2K")); // JPEG2000 code stream formats.removeAll(QLatin1String("PGX")); // JPEG2000 WM format + formats.removeAll(QLatin1String("HEIC")); QString rawFilesExt = QString::fromLatin1(DRawDecoder::rawFiles()).remove(QLatin1String("*.")).toUpper(); foreach (const QString& str, rawFilesExt.split(QLatin1Char(' '))) { formats.removeAll(str); // All Raw image formats } QString ret; foreach (const QString& str, formats) { if (!ret.contains(str)) { ret += QString::fromUtf8("%1 ").arg(str.toUpper()); } } return ret; } bool DImgImageMagickPlugin::canRead(const QString& filePath) const { QString mimeType(QMimeDatabase().mimeTypeForFile(filePath).name()); // Ignore non image format. if ( mimeType.startsWith(QLatin1String("video/")) || mimeType.startsWith(QLatin1String("audio/")) ) { return false; } QString format = QFileInfo(filePath).suffix().toUpper(); QString blackList = QString::fromLatin1(DRawDecoder::rawFiles()).remove(QLatin1String("*.")).toUpper(); // Ignore RAW files blackList.append(QLatin1String(" JPEG JPG JPE PNG TIF TIFF PGF JP2 JPX JPC J2K PGX HEIC ")); // Ignore native loaders if (blackList.toUpper().contains(format)) { return false; } QStringList formats; ExceptionInfo ex; size_t n = 0; const MagickInfo** inflst = GetMagickInfoList("*", &n, &ex); + if (!inflst) + { + qWarning() << "ImageMagick coders list is null!"; + return false; + } + for (uint i = 0 ; i < n ; ++i) { const MagickInfo* inf = inflst[i]; - if (inf->decoder) + if (inf && inf->decoder) { #if (MagickLibVersion >= 0x69A && defined(magick_module)) formats.append(QString::fromLatin1(inf->magick_module).toUpper()); #else formats.append(QString::fromLatin1(inf->module).toUpper()); #endif } } if (!formats.contains(format)) { return false; } return true; } bool DImgImageMagickPlugin::canWrite(const QString& format) const { QString blackList = QString::fromLatin1(DRawDecoder::rawFiles()).remove(QLatin1String("*.")).toUpper(); // Ignore RAW files blackList.append(QLatin1String(" JPEG JPG JPE PNG TIF TIFF PGF JP2 JPX JPC J2K PGX HEIC ")); // Ignore native loaders if (blackList.toUpper().contains(format)) { return false; } // NOTE: Native loaders support are previously black-listed. // For ex, if tiff is supported in write mode by ImageMagick it will never be handled. QStringList formats; ExceptionInfo ex; size_t n = 0; const MagickInfo** inflst = GetMagickInfoList("*", &n, &ex); + if (!inflst) + { + qWarning() << "ImageMagick coders list is null!"; + return false; + } + for (uint i = 0 ; i < n ; ++i) { const MagickInfo* inf = inflst[i]; - if (inf->encoder) + if (inf && inf->encoder) { #if (MagickLibVersion >= 0x69A && defined(magick_module)) formats.append(QString::fromLatin1(inf->magick_module).toUpper()); #else formats.append(QString::fromLatin1(inf->module).toUpper()); #endif } } if (!formats.contains(format)) { return false; } return true; } DImgLoader* DImgImageMagickPlugin::loader(DImg* const image, const DRawDecoding&) const { return new DImgImageMagickLoader(image); } } // namespace DigikamImageMagickDImgPlugin diff --git a/core/dplugins/dimg/imagemagick/dimgimagemagickplugin.h b/core/dplugins/dimg/imagemagick/dimgimagemagickplugin.h index 59fcb7084e..02616ec236 100644 --- a/core/dplugins/dimg/imagemagick/dimgimagemagickplugin.h +++ b/core/dplugins/dimg/imagemagick/dimgimagemagickplugin.h @@ -1,72 +1,72 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-21 * Description : ImageMagick DImg plugin. * * Copyright (C) 2019 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_IMAGE_MAGICK_DIMG_PLUGIN_H -#define DIGIKAM_IMAGE_MAGICK_DIMG_PLUGIN_H +#ifndef DIGIKAM_DIMG_IMAGE_MAGICK_PLUGIN_H +#define DIGIKAM_DIMG_IMAGE_MAGICK_PLUGIN_H // Qt includes #include #include // Local includes #include "dplugindimg.h" #include "dimg.h" #define DPLUGIN_IID "org.kde.digikam.plugin.dimg.ImageMagick" using namespace Digikam; namespace DigikamImageMagickDImgPlugin { class DImgImageMagickPlugin : public DPluginDImg { Q_OBJECT Q_PLUGIN_METADATA(IID DPLUGIN_IID) Q_INTERFACES(Digikam::DPluginDImg) public: explicit DImgImageMagickPlugin(QObject* const parent = nullptr); ~DImgImageMagickPlugin(); QString name() const override; QString iid() const override; QIcon icon() const override; QString details() const override; QString description() const override; QList authors() const override; void setup(QObject* const) override; QString loaderName() const; QString typeMimes() const; bool canRead(const QString& filePath) const; bool canWrite(const QString& format) const; DImgLoader* loader(DImg* const image, const DRawDecoding& rawSettings = DRawDecoding()) const; }; } // namespace DigikamImageMagickDImgPlugin -#endif // DIGIKAM_IMAGE_MAGICK_DIMG_PLUGIN_H +#endif // DIGIKAM_DIMG_IMAGE_MAGICK_PLUGIN_H diff --git a/core/dplugins/dimg/jpeg/dimgjpegplugin.h b/core/dplugins/dimg/jpeg/dimgjpegplugin.h index 46410b2bac..99416e57f3 100644 --- a/core/dplugins/dimg/jpeg/dimgjpegplugin.h +++ b/core/dplugins/dimg/jpeg/dimgjpegplugin.h @@ -1,73 +1,73 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-21 * Description : JPEG DImg plugin. * * Copyright (C) 2019 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_JPEG_DIMG_PLUGIN_H -#define DIGIKAM_JPEG_DIMG_PLUGIN_H +#ifndef DIGIKAM_DIMG_JPEG_PLUGIN_H +#define DIGIKAM_DIMG_JPEG_PLUGIN_H // Qt includes #include #include // Local includes #include "dplugindimg.h" #include "dimg.h" #include "dimgloader.h" #define DPLUGIN_IID "org.kde.digikam.plugin.dimg.JPEG" using namespace Digikam; namespace DigikamJPEGDImgPlugin { class DImgJPEGPlugin : public DPluginDImg { Q_OBJECT Q_PLUGIN_METADATA(IID DPLUGIN_IID) Q_INTERFACES(Digikam::DPluginDImg) public: explicit DImgJPEGPlugin(QObject* const parent = nullptr); ~DImgJPEGPlugin(); QString name() const override; QString iid() const override; QIcon icon() const override; QString details() const override; QString description() const override; QList authors() const override; void setup(QObject* const) override; QString loaderName() const; QString typeMimes() const; bool canRead(const QString& filePath) const; bool canWrite(const QString& format) const; DImgLoader* loader(DImg* const image, const DRawDecoding& rawSettings = DRawDecoding()) const; }; } // namespace DigikamJPEGDImgPlugin -#endif // DIGIKAM_JPEG_DIMG_PLUGIN_H +#endif // DIGIKAM_DIMG_JPEG_PLUGIN_H diff --git a/core/dplugins/dimg/jpeg2000/dimgjpeg2000loader.cpp b/core/dplugins/dimg/jpeg2000/dimgjpeg2000loader.cpp index 5af1f610d1..bc15e66990 100644 --- a/core/dplugins/dimg/jpeg2000/dimgjpeg2000loader.cpp +++ b/core/dplugins/dimg/jpeg2000/dimgjpeg2000loader.cpp @@ -1,896 +1,898 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-06-14 * Description : A JPEG-2000 IO file for DImg framework * * Copyright (C) 2006-2019 by Gilles Caulier * * This implementation use Jasper API * library : http://www.ece.uvic.ca/~mdadams/jasper * Other JPEG-2000 encoder-decoder : http://www.openjpeg.org * * Others Linux JPEG-2000 Loader implementation: * https://github.com/ImageMagick/ImageMagick/blob/master/coders/jp2.c * * 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 "dimgjpeg2000loader.h" // Qt includes #include #include #include // Local includes #include "digikam_config.h" #include "dimg.h" #include "digikam_debug.h" #include "dimgloaderobserver.h" #include "dmetadata.h" // Jasper includes #ifndef Q_CC_MSVC extern "C" { #endif #if defined(Q_OS_DARWIN) && defined(Q_CC_CLANG) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wshift-negative-value" #endif #include #if defined(Q_OS_DARWIN) && defined(Q_CC_CLANG) # pragma clang diagnostic pop #endif #ifndef Q_CC_MSVC } #endif namespace DigikamJPEG2000DImgPlugin { DImgJPEG2000Loader::DImgJPEG2000Loader(DImg* const image) : DImgLoader(image) { m_hasAlpha = false; m_sixteenBit = false; } bool DImgJPEG2000Loader::load(const QString& filePath, DImgLoaderObserver* const observer) { readMetadata(filePath, DImg::JPEG); FILE* const file = fopen(QFile::encodeName(filePath).constData(), "rb"); if (!file) { loadingFailed(); return false; } unsigned char header[9]; if (fread(&header, 9, 1, file) != 1) { fclose(file); loadingFailed(); return false; } fclose(file); unsigned char jp2ID[5] = { 0x6A, 0x50, 0x20, 0x20, 0x0D, }; unsigned char jpcID[2] = { 0xFF, 0x4F }; if (memcmp(&header[4], &jp2ID, 5) != 0 && memcmp(&header, &jpcID, 2) != 0) { // not a jpeg2000 file loadingFailed(); return false; } imageSetAttribute(QLatin1String("format"), QLatin1String("JP2")); if (!(m_loadFlags & LoadImageData) && !(m_loadFlags & LoadICCData)) { // libjasper will load the full image in memory already when calling jas_image_decode. // This is bad when scanning. See bugs 215458 and 195583. // FIXME: Use Exiv2 to extract this info DMetadata metadata(filePath); QSize size = metadata.getItemDimensions(); if (size.isValid()) { imageWidth() = size.width(); imageHeight() = size.height(); } return true; } // ------------------------------------------------------------------- // Initialize JPEG 2000 API. long i, x, y; int components[4]; unsigned int maximum_component_depth, scale[4], x_step[4], y_step[4]; unsigned long number_components; jas_image_t* jp2_image = nullptr; jas_stream_t* jp2_stream = nullptr; jas_matrix_t* pixels[4]; int init = jas_init(); if (init != 0) { qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Unable to init JPEG2000 decoder"; loadingFailed(); return false; } jp2_stream = jas_stream_fopen(QFile::encodeName(filePath).constData(), "rb"); if (jp2_stream == nullptr) { qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Unable to open JPEG2000 stream"; loadingFailed(); return false; } int fmt = jas_image_strtofmt(QByteArray("jp2").data()); jp2_image = jas_image_decode(jp2_stream, fmt, nullptr); if (jp2_image == nullptr) { jas_stream_close(jp2_stream); qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Unable to decode JPEG2000 image"; loadingFailed(); return false; } jas_stream_close(jp2_stream); // some pseudo-progress if (observer) { observer->progressInfo(m_image, 0.1F); } // ------------------------------------------------------------------- // Check color space. int colorModel; switch (jas_clrspc_fam(jas_image_clrspc(jp2_image))) { case JAS_CLRSPC_FAM_RGB: { components[0] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_RGB_R); components[1] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_RGB_G); components[2] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_RGB_B); if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0)) { jas_image_destroy(jp2_image); qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Error parsing JPEG2000 image : Missing Image Channel"; loadingFailed(); return false; } number_components = 3; components[3] = jas_image_getcmptbytype(jp2_image, 3); if (components[3] > 0) { m_hasAlpha = true; ++number_components; } colorModel = DImg::RGB; break; } case JAS_CLRSPC_FAM_GRAY: { components[0] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_GRAY_Y); if (components[0] < 0) { jas_image_destroy(jp2_image); qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Error parsing JP2000 image : Missing Image Channel"; loadingFailed(); return false; } number_components = 1; colorModel = DImg::GRAYSCALE; break; } case JAS_CLRSPC_FAM_YCBCR: { components[0] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_YCBCR_Y); components[1] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_YCBCR_CB); components[2] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_YCBCR_CR); if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0)) { jas_image_destroy(jp2_image); qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Error parsing JP2000 image : Missing Image Channel"; loadingFailed(); return false; } number_components = 3; components[3] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_UNKNOWN); if (components[3] > 0) { m_hasAlpha = true; ++number_components; } // FIXME : image->colorspace=YCbCrColorspace; colorModel = DImg::YCBCR; break; } default: { jas_image_destroy(jp2_image); qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Error parsing JP2000 image : Colorspace Model Is Not Supported"; loadingFailed(); return false; } } // ------------------------------------------------------------------- // Check image geometry. imageWidth() = jas_image_width(jp2_image); imageHeight() = jas_image_height(jp2_image); for (i = 0; i < (long)number_components; ++i) { if ((((jas_image_cmptwidth(jp2_image, components[i])* jas_image_cmpthstep(jp2_image, components[i])) != (long)imageWidth())) || (((jas_image_cmptheight(jp2_image, components[i])* jas_image_cmptvstep(jp2_image, components[i])) != (long)imageHeight())) || (jas_image_cmpttlx(jp2_image, components[i]) != 0) || (jas_image_cmpttly(jp2_image, components[i]) != 0) || (jas_image_cmptsgnd(jp2_image, components[i]) != false)) { jas_image_destroy(jp2_image); qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Error parsing JPEG2000 image : Irregular Channel Geometry Not Supported"; loadingFailed(); return false; } x_step[i] = jas_image_cmpthstep(jp2_image, components[i]); y_step[i] = jas_image_cmptvstep(jp2_image, components[i]); } // ------------------------------------------------------------------- // Get image format. maximum_component_depth = 0; for (i = 0; i < (long)number_components; ++i) { maximum_component_depth = qMax((long)jas_image_cmptprec(jp2_image, components[i]), (long)maximum_component_depth); pixels[i] = jas_matrix_create(1, ((unsigned int)imageWidth()) / x_step[i]); if (!pixels[i]) { jas_image_destroy(jp2_image); qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Error decoding JPEG2000 image data : Memory Allocation Failed"; loadingFailed(); return false; } } if (maximum_component_depth > 8) { m_sixteenBit = true; } for (i = 0 ; i < (long)number_components ; ++i) { scale[i] = 1; int prec = jas_image_cmptprec(jp2_image, components[i]); if (m_sixteenBit && prec < 16) { scale[i] = (1 << (16 - jas_image_cmptprec(jp2_image, components[i]))); } } // ------------------------------------------------------------------- // Get image data. QScopedArrayPointer data; if (m_loadFlags & LoadImageData) { if (m_sixteenBit) // 16 bits image. { data.reset(new_failureTolerant(imageWidth(), imageHeight(), 8)); } else { data.reset(new_failureTolerant(imageWidth(), imageHeight(), 4)); } if (!data) { qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Error decoding JPEG2000 image data : Memory Allocation Failed"; jas_image_destroy(jp2_image); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); loadingFailed(); return false; } uint checkPoint = 0; uchar* dst = data.data(); unsigned short* dst16 = reinterpret_cast(data.data()); for (y = 0 ; y < (long)imageHeight() ; ++y) { for (i = 0 ; i < (long)number_components; ++i) { int ret = jas_image_readcmpt(jp2_image, (short)components[i], 0, ((unsigned int) y) / y_step[i], ((unsigned int) imageWidth()) / x_step[i], 1, pixels[i]); if (ret != 0) { qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Error decoding JPEG2000 image data"; jas_image_destroy(jp2_image); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); loadingFailed(); return false; } } switch (number_components) { case 1: // Grayscale. { if (!m_sixteenBit) // 8 bits image. { for (x = 0 ; x < (long)imageWidth() ; ++x) { dst[0] = (uchar)(scale[0] * jas_matrix_getv(pixels[0], x / x_step[0])); dst[1] = dst[0]; dst[2] = dst[0]; dst[3] = 0xFF; dst += 4; } } else // 16 bits image. { for (x = 0 ; x < (long)imageWidth() ; ++x) { dst16[0] = (unsigned short)(scale[0] * jas_matrix_getv(pixels[0], x / x_step[0])); dst16[1] = dst16[0]; dst16[2] = dst16[0]; dst16[3] = 0xFFFF; dst16 += 4; } } break; } case 3: // RGB. { if (!m_sixteenBit) // 8 bits image. { for (x = 0 ; x < (long)imageWidth() ; ++x) { // Blue dst[0] = (uchar)(scale[2] * jas_matrix_getv(pixels[2], x / x_step[2])); // Green dst[1] = (uchar)(scale[1] * jas_matrix_getv(pixels[1], x / x_step[1])); // Red dst[2] = (uchar)(scale[0] * jas_matrix_getv(pixels[0], x / x_step[0])); // Alpha dst[3] = 0xFF; dst += 4; } } else // 16 bits image. { for (x = 0 ; x < (long)imageWidth() ; ++x) { // Blue dst16[0] = (unsigned short)(scale[2] * jas_matrix_getv(pixels[2], x / x_step[2])); // Green dst16[1] = (unsigned short)(scale[1] * jas_matrix_getv(pixels[1], x / x_step[1])); // Red dst16[2] = (unsigned short)(scale[0] * jas_matrix_getv(pixels[0], x / x_step[0])); // Alpha dst16[3] = 0xFFFF; dst16 += 4; } } break; } case 4: // RGBA. { if (!m_sixteenBit) // 8 bits image. { for (x = 0 ; x < (long)imageWidth() ; ++x) { // Blue dst[0] = (uchar)(scale[2] * jas_matrix_getv(pixels[2], x / x_step[2])); // Green dst[1] = (uchar)(scale[1] * jas_matrix_getv(pixels[1], x / x_step[1])); // Red dst[2] = (uchar)(scale[0] * jas_matrix_getv(pixels[0], x / x_step[0])); // Alpha dst[3] = (uchar)(scale[3] * jas_matrix_getv(pixels[3], x / x_step[3])); dst += 4; } } else // 16 bits image. { for (x = 0 ; x < (long)imageWidth() ; ++x) { // Blue dst16[0] = (unsigned short)(scale[2] * jas_matrix_getv(pixels[2], x / x_step[2])); // Green dst16[1] = (unsigned short)(scale[1] * jas_matrix_getv(pixels[1], x / x_step[1])); // Red dst16[2] = (unsigned short)(scale[0] * jas_matrix_getv(pixels[0], x / x_step[0])); // Alpha dst16[3] = (unsigned short)(scale[3] * jas_matrix_getv(pixels[3], x / x_step[3])); dst16 += 4; } } break; } } // use 0-10% and 90-100% for pseudo-progress if (observer && y >= (long)checkPoint) { checkPoint += granularity(observer, y, 0.8F); if (!observer->continueQuery(m_image)) { jas_image_destroy(jp2_image); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); loadingFailed(); return false; } observer->progressInfo(m_image, 0.1 + (0.8 * (((float)y) / ((float)imageHeight())))); } } } // ------------------------------------------------------------------- // Get ICC color profile. if (m_loadFlags & LoadICCData) { jas_iccprof_t* icc_profile = nullptr; jas_stream_t* icc_stream = nullptr; jas_cmprof_t* cm_profile = nullptr; // To prevent cppcheck warnings. (void)icc_profile; (void)icc_stream; (void)cm_profile; cm_profile = jas_image_cmprof(jp2_image); if (cm_profile != nullptr) { icc_profile = jas_iccprof_createfromcmprof(cm_profile); } if (icc_profile != nullptr) { icc_stream = jas_stream_memopen(nullptr, 0); if (icc_stream != nullptr) { if (jas_iccprof_save(icc_profile, icc_stream) == 0) { if (jas_stream_flush(icc_stream) == 0) { jas_stream_memobj_t* blob = (jas_stream_memobj_t*) icc_stream->obj_; QByteArray profile_rawdata; profile_rawdata.resize(blob->len_); memcpy(profile_rawdata.data(), blob->buf_, blob->len_); imageSetIccProfile(IccProfile(profile_rawdata)); jas_stream_close(icc_stream); } } } } else { // If ICC profile is null, check Exif metadata. checkExifWorkingColorSpace(); } } if (observer) { observer->progressInfo(m_image, 1.0); } imageData() = data.take(); imageSetAttribute(QLatin1String("format"), QLatin1String("JP2")); imageSetAttribute(QLatin1String("originalColorModel"), colorModel); imageSetAttribute(QLatin1String("originalBitDepth"), maximum_component_depth); imageSetAttribute(QLatin1String("originalSize"), QSize(imageWidth(), imageHeight())); jas_image_destroy(jp2_image); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); return true; } bool DImgJPEG2000Loader::save(const QString& filePath, DImgLoaderObserver* const observer) { FILE* const file = fopen(QFile::encodeName(filePath).constData(), "wb"); if (!file) { return false; } fclose(file); // ------------------------------------------------------------------- // Initialize JPEG 2000 API. long i, x, y; unsigned long number_components; jas_image_t* jp2_image = nullptr; jas_stream_t* jp2_stream = nullptr; jas_matrix_t* pixels[4]; jas_image_cmptparm_t component_info[4]; int init = jas_init(); if (init != 0) { qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Unable to init JPEG2000 decoder"; return false; } jp2_stream = jas_stream_fopen(QFile::encodeName(filePath).constData(), "wb"); if (jp2_stream == nullptr) { qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Unable to open JPEG2000 stream"; return false; } number_components = imageHasAlpha() ? 4 : 3; for (i = 0 ; i < (long)number_components ; ++i) { component_info[i].tlx = 0; component_info[i].tly = 0; component_info[i].hstep = 1; component_info[i].vstep = 1; component_info[i].width = imageWidth(); component_info[i].height = imageHeight(); component_info[i].prec = imageBitsDepth(); component_info[i].sgnd = false; } jp2_image = jas_image_create(number_components, component_info, JAS_CLRSPC_UNKNOWN); if (jp2_image == nullptr) { jas_stream_close(jp2_stream); qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Unable to create JPEG2000 image"; return false; } if (observer) { observer->progressInfo(m_image, 0.1F); } // ------------------------------------------------------------------- // Check color space. if (number_components >= 3) // RGB & RGBA { // Alpha Channel if (number_components == 4) { jas_image_setcmpttype(jp2_image, 3, JAS_IMAGE_CT_OPACITY); } jas_image_setclrspc(jp2_image, JAS_CLRSPC_SRGB); jas_image_setcmpttype(jp2_image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R)); jas_image_setcmpttype(jp2_image, 1, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G)); jas_image_setcmpttype(jp2_image, 2, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B)); } // ------------------------------------------------------------------- // Set ICC color profile. // FIXME : doesn't work yet! jas_cmprof_t* cm_profile = nullptr; jas_iccprof_t* icc_profile = nullptr; // To prevent cppcheck warnings. (void)cm_profile; (void)icc_profile; QByteArray profile_rawdata = m_image->getIccProfile().data(); - - icc_profile = jas_iccprof_createfrombuf((uchar*)profile_rawdata.data(), profile_rawdata.size()); + icc_profile = jas_iccprof_createfrombuf((uchar*)profile_rawdata.data(), profile_rawdata.size()); if (icc_profile != nullptr) { cm_profile = jas_cmprof_createfromiccprof(icc_profile); if (cm_profile != nullptr) { jas_image_setcmprof(jp2_image, cm_profile); //enable when it works: purgeExifWorkingColorSpace(); } } // workaround: storeColorProfileInMetadata(); // ------------------------------------------------------------------- // Convert to JPEG 2000 pixels. for (i = 0 ; i < (long)number_components ; ++i) { pixels[i] = jas_matrix_create(1, (unsigned int)imageWidth()); if (pixels[i] == nullptr) { for (x = 0 ; x < i ; ++x) { jas_matrix_destroy(pixels[x]); } jas_image_destroy(jp2_image); qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Error encoding JPEG2000 image data : Memory Allocation Failed"; return false; } } - unsigned char* data = imageData(); - unsigned char* pixel; - unsigned short r, g, b, a = 0; - uint checkpoint = 0; + unsigned char* data = imageData(); + unsigned char* pixel = nullptr; + unsigned short r = 0; + unsigned short g = 0; + unsigned short b = 0; + unsigned short a = 0; + uint checkpoint = 0; for (y = 0 ; y < (long)imageHeight() ; ++y) { if (observer && y == (long)checkpoint) { checkpoint += granularity(observer, imageHeight(), 0.8F); if (!observer->continueQuery(m_image)) { jas_image_destroy(jp2_image); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); return false; } observer->progressInfo(m_image, 0.1 + (0.8 * (((float)y) / ((float)imageHeight())))); } for (x = 0 ; x < (long)imageWidth() ; ++x) { pixel = &data[((y * imageWidth()) + x) * imageBytesDepth()]; if (imageSixteenBit()) // 16 bits image. { b = (unsigned short)(pixel[0] + 256 * pixel[1]); g = (unsigned short)(pixel[2] + 256 * pixel[3]); r = (unsigned short)(pixel[4] + 256 * pixel[5]); if (imageHasAlpha()) { a = (unsigned short)(pixel[6] + 256 * pixel[7]); } } else // 8 bits image. { b = (unsigned short)pixel[0]; g = (unsigned short)pixel[1]; r = (unsigned short)pixel[2]; if (imageHasAlpha()) { a = (unsigned short)(pixel[3]); } } jas_matrix_setv(pixels[0], x, r); jas_matrix_setv(pixels[1], x, g); jas_matrix_setv(pixels[2], x, b); if (number_components > 3) { jas_matrix_setv(pixels[3], x, a); } } for (i = 0 ; i < (long)number_components ; ++i) { int ret = jas_image_writecmpt(jp2_image, (short) i, 0, (unsigned int)y, (unsigned int)imageWidth(), 1, pixels[i]); if (ret != 0) { qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Error encoding JPEG2000 image data"; jas_image_destroy(jp2_image); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); return false; } } } QVariant qualityAttr = imageGetAttribute(QLatin1String("quality")); int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90; if (quality < 0) { quality = 90; } if (quality > 100) { quality = 100; } // optstr: // - rate=#B => the resulting file size is about # bytes // - rate=0.0 .. 1.0 => the resulting file size is about the factor times // the uncompressed size // use sprintf for locale-aware string char rateBuffer[16]; sprintf(rateBuffer, "rate=%.2g", (quality / 100.0)); qCDebug(DIGIKAM_DIMG_LOG_JP2K) << "JPEG2000 quality: " << quality; qCDebug(DIGIKAM_DIMG_LOG_JP2K) << "JPEG2000 " << rateBuffer; int fmt = jas_image_strtofmt(QByteArray("jp2").data()); int ret = jas_image_encode(jp2_image, jp2_stream, fmt, rateBuffer); if (ret != 0) { qCWarning(DIGIKAM_DIMG_LOG_JP2K) << "Unable to encode JPEG2000 image"; jas_image_destroy(jp2_image); jas_stream_close(jp2_stream); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); return false; } if (observer) { observer->progressInfo(m_image, 1.0); } jas_image_destroy(jp2_image); jas_stream_close(jp2_stream); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); imageSetAttribute(QLatin1String("savedFormat"), QLatin1String("JP2")); saveMetadata(filePath); return true; } bool DImgJPEG2000Loader::hasAlpha() const { return m_hasAlpha; } bool DImgJPEG2000Loader::sixteenBit() const { return m_sixteenBit; } bool DImgJPEG2000Loader::isReadOnly() const { return false; } } // namespace DigikamJPEG2000DImgPlugin diff --git a/core/dplugins/dimg/jpeg2000/dimgjpeg2000loader.h b/core/dplugins/dimg/jpeg2000/dimgjpeg2000loader.h index 83871bba8b..c6030c82c1 100644 --- a/core/dplugins/dimg/jpeg2000/dimgjpeg2000loader.h +++ b/core/dplugins/dimg/jpeg2000/dimgjpeg2000loader.h @@ -1,60 +1,60 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-06-14 * Description : A JPEG-2000 IO file for DImg framework * * Copyright (C) 2006-2019 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_JP2000_LOADER_H -#define DIGIKAM_DIMG_JP2000_LOADER_H +#ifndef DIGIKAM_DIMG_JPEG_2000_LOADER_H +#define DIGIKAM_DIMG_JPEG_2000_LOADER_H // Local includes #include "dimg.h" #include "dimgloader.h" #include "digikam_export.h" using namespace Digikam; namespace DigikamJPEG2000DImgPlugin { class DIGIKAM_EXPORT DImgJPEG2000Loader : public DImgLoader { public: explicit DImgJPEG2000Loader(DImg* const image); bool load(const QString& filePath, DImgLoaderObserver* const observer) override; bool save(const QString& filePath, DImgLoaderObserver* const observer) override; virtual bool hasAlpha() const override; virtual bool sixteenBit() const override; virtual bool isReadOnly() const override; private: bool m_sixteenBit; bool m_hasAlpha; }; } // namespace DigikamJPEG2000DImgPlugin -#endif // DIGIKAM_DIMG_JP2000_LOADER_H +#endif // DIGIKAM_DIMG_JPEG_2000_LOADER_H diff --git a/core/dplugins/dimg/jpeg2000/dimgjpeg2000plugin.h b/core/dplugins/dimg/jpeg2000/dimgjpeg2000plugin.h index 3fc8310013..0fc094da75 100644 --- a/core/dplugins/dimg/jpeg2000/dimgjpeg2000plugin.h +++ b/core/dplugins/dimg/jpeg2000/dimgjpeg2000plugin.h @@ -1,73 +1,73 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-22 * Description : JPEG-2000 DImg plugin. * * Copyright (C) 2019 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_JPEG2000_DIMG_PLUGIN_H -#define DIGIKAM_JPEG2000_DIMG_PLUGIN_H +#ifndef DIGIKAM_DIMG_JPEG_2000_PLUGIN_H +#define DIGIKAM_DIMG_JPEG_2000_PLUGIN_H // Qt includes #include #include // Local includes #include "dplugindimg.h" #include "dimg.h" #include "dimgloader.h" #define DPLUGIN_IID "org.kde.digikam.plugin.dimg.JPEG2000" using namespace Digikam; namespace DigikamJPEG2000DImgPlugin { class DImgJPEG2000Plugin : public DPluginDImg { Q_OBJECT Q_PLUGIN_METADATA(IID DPLUGIN_IID) Q_INTERFACES(Digikam::DPluginDImg) public: explicit DImgJPEG2000Plugin(QObject* const parent = nullptr); ~DImgJPEG2000Plugin(); QString name() const override; QString iid() const override; QIcon icon() const override; QString details() const override; QString description() const override; QList authors() const override; void setup(QObject* const) override; QString loaderName() const; QString typeMimes() const; bool canRead(const QString& filePath) const; bool canWrite(const QString& format) const; DImgLoader* loader(DImg* const image, const DRawDecoding& rawSettings = DRawDecoding()) const; }; } // namespace DigikamJPEG2000DImgPlugin -#endif // DIGIKAM_JPEG2000_DIMG_PLUGIN_H +#endif // DIGIKAM_DIMG_JPEG_2000_PLUGIN_H diff --git a/core/dplugins/dimg/pgf/dimgpgfplugin.h b/core/dplugins/dimg/pgf/dimgpgfplugin.h index 5ef81ff90f..4e14cc009d 100644 --- a/core/dplugins/dimg/pgf/dimgpgfplugin.h +++ b/core/dplugins/dimg/pgf/dimgpgfplugin.h @@ -1,73 +1,73 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-22 * Description : PGF DImg plugin. * * Copyright (C) 2019 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_DIMG_PLUGIN_H -#define DIGIKAM_PGF_DIMG_PLUGIN_H +#ifndef DIGIKAM_DIMG_PGF_PLUGIN_H +#define DIGIKAM_DIMG_PGF_PLUGIN_H // Qt includes #include #include // Local includes #include "dplugindimg.h" #include "dimg.h" #include "dimgloader.h" #define DPLUGIN_IID "org.kde.digikam.plugin.dimg.PGF" using namespace Digikam; namespace DigikamPGFDImgPlugin { class DImgPGFPlugin : public DPluginDImg { Q_OBJECT Q_PLUGIN_METADATA(IID DPLUGIN_IID) Q_INTERFACES(Digikam::DPluginDImg) public: explicit DImgPGFPlugin(QObject* const parent = nullptr); ~DImgPGFPlugin(); QString name() const override; QString iid() const override; QIcon icon() const override; QString details() const override; QString description() const override; QList authors() const override; void setup(QObject* const) override; QString loaderName() const; QString typeMimes() const; bool canRead(const QString& filePath) const; bool canWrite(const QString& format) const; DImgLoader* loader(DImg* const image, const DRawDecoding& rawSettings = DRawDecoding()) const; }; } // namespace DigikamPGFDImgPlugin -#endif // DIGIKAM_PGF_DIMG_PLUGIN_H +#endif // DIGIKAM_DIMG_PGF_PLUGIN_H diff --git a/core/dplugins/dimg/png/dimgpngplugin.h b/core/dplugins/dimg/png/dimgpngplugin.h index 84ff57b25b..332d66626a 100644 --- a/core/dplugins/dimg/png/dimgpngplugin.h +++ b/core/dplugins/dimg/png/dimgpngplugin.h @@ -1,73 +1,73 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-22 * Description : PNG DImg plugin. * * Copyright (C) 2019 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_PNG_DIMG_PLUGIN_H -#define DIGIKAM_PNG_DIMG_PLUGIN_H +#ifndef DIGIKAM_DIMG_PNG_PLUGIN_H +#define DIGIKAM_DIMG_PNG_PLUGIN_H // Qt includes #include #include // Local includes #include "dplugindimg.h" #include "dimg.h" #include "dimgloader.h" #define DPLUGIN_IID "org.kde.digikam.plugin.dimg.PNG" using namespace Digikam; namespace DigikamPNGDImgPlugin { class DImgPNGPlugin : public DPluginDImg { Q_OBJECT Q_PLUGIN_METADATA(IID DPLUGIN_IID) Q_INTERFACES(Digikam::DPluginDImg) public: explicit DImgPNGPlugin(QObject* const parent = nullptr); ~DImgPNGPlugin(); QString name() const override; QString iid() const override; QIcon icon() const override; QString details() const override; QString description() const override; QList authors() const override; void setup(QObject* const) override; QString loaderName() const; QString typeMimes() const; bool canRead(const QString& filePath) const; bool canWrite(const QString& format) const; DImgLoader* loader(DImg* const image, const DRawDecoding& rawSettings = DRawDecoding()) const; }; } // namespace DigikamPNGDImgPlugin -#endif // DIGIKAM_PNG_DIMG_PLUGIN_H +#endif // DIGIKAM_DIMG_PNG_PLUGIN_H diff --git a/core/dplugins/dimg/qimage/dimgqimageplugin.h b/core/dplugins/dimg/qimage/dimgqimageplugin.h index 93eafe0139..b695ad1c27 100644 --- a/core/dplugins/dimg/qimage/dimgqimageplugin.h +++ b/core/dplugins/dimg/qimage/dimgqimageplugin.h @@ -1,72 +1,72 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-20 * Description : QImage DImg plugin. * * Copyright (C) 2019 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_QIMAGE_DIMG_PLUGIN_H -#define DIGIKAM_QIMAGE_DIMG_PLUGIN_H +#ifndef DIGIKAM_DIMG_QIMAGE_PLUGIN_H +#define DIGIKAM_DIMG_QIMAGE_PLUGIN_H // Qt includes #include #include // Local includes #include "dplugindimg.h" #include "dimg.h" #define DPLUGIN_IID "org.kde.digikam.plugin.dimg.QImage" using namespace Digikam; namespace DigikamQImageDImgPlugin { class DImgQImagePlugin : public DPluginDImg { Q_OBJECT Q_PLUGIN_METADATA(IID DPLUGIN_IID) Q_INTERFACES(Digikam::DPluginDImg) public: explicit DImgQImagePlugin(QObject* const parent = nullptr); ~DImgQImagePlugin(); QString name() const override; QString iid() const override; QIcon icon() const override; QString details() const override; QString description() const override; QList authors() const override; void setup(QObject* const) override; QString loaderName() const; QString typeMimes() const; bool canRead(const QString& filePath) const; bool canWrite(const QString& format) const; DImgLoader* loader(DImg* const image, const DRawDecoding& rawSettings = DRawDecoding()) const; }; } // namespace DigikamQImageDImgPlugin -#endif // DIGIKAM_QIMAGE_DIMG_PLUGIN_H +#endif // DIGIKAM_DIMG_QIMAGE_PLUGIN_H diff --git a/core/dplugins/dimg/raw/dimgrawplugin.h b/core/dplugins/dimg/raw/dimgrawplugin.h index 351b477801..4def845572 100644 --- a/core/dplugins/dimg/raw/dimgrawplugin.h +++ b/core/dplugins/dimg/raw/dimgrawplugin.h @@ -1,73 +1,73 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-22 * Description : RAW DImg plugin. * * Copyright (C) 2019 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_RAW_DIMG_PLUGIN_H -#define DIGIKAM_RAW_DIMG_PLUGIN_H +#ifndef DIGIKAM_DIMG_RAW_PLUGIN_H +#define DIGIKAM_DIMG_RAW_PLUGIN_H // Qt includes #include #include // Local includes #include "dplugindimg.h" #include "dimg.h" #include "dimgloader.h" #define DPLUGIN_IID "org.kde.digikam.plugin.dimg.RAW" using namespace Digikam; namespace DigikamRAWDImgPlugin { class DImgRAWPlugin : public DPluginDImg { Q_OBJECT Q_PLUGIN_METADATA(IID DPLUGIN_IID) Q_INTERFACES(Digikam::DPluginDImg) public: explicit DImgRAWPlugin(QObject* const parent = nullptr); ~DImgRAWPlugin(); QString name() const override; QString iid() const override; QIcon icon() const override; QString details() const override; QString description() const override; QList authors() const override; void setup(QObject* const) override; QString loaderName() const; QString typeMimes() const; bool canRead(const QString& filePath) const; bool canWrite(const QString& format) const; DImgLoader* loader(DImg* const image, const DRawDecoding& rawSettings = DRawDecoding()) const; }; } // namespace DigikamRAWDImgPlugin -#endif // DIGIKAM_RAW_DIMG_PLUGIN_H +#endif // DIGIKAM_DIMG_RAW_PLUGIN_H diff --git a/core/dplugins/dimg/tiff/dimgtiffplugin.cpp b/core/dplugins/dimg/tiff/dimgtiffplugin.cpp index 8c85af3100..81a7383112 100644 --- a/core/dplugins/dimg/tiff/dimgtiffplugin.cpp +++ b/core/dplugins/dimg/tiff/dimgtiffplugin.cpp @@ -1,185 +1,187 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-22 * Description : TIFF DImg plugin. * * Copyright (C) 2019 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 "dimgtiffplugin.h" // C++ includes #include // Qt includes #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "digikam_globals.h" #include "dimgtiffloader.h" namespace DigikamTIFFDImgPlugin { DImgTIFFPlugin::DImgTIFFPlugin(QObject* const parent) : DPluginDImg(parent) { } DImgTIFFPlugin::~DImgTIFFPlugin() { } QString DImgTIFFPlugin::name() const { return i18n("TIFF loader"); } QString DImgTIFFPlugin::iid() const { return QLatin1String(DPLUGIN_IID); } QIcon DImgTIFFPlugin::icon() const { return QIcon::fromTheme(QLatin1String("image-tiff")); } QString DImgTIFFPlugin::description() const { return i18n("An image loader based on Libtiff codec"); } QString DImgTIFFPlugin::details() const { return i18n("

This plugin permit to load and save image using Libtiff codec.

" "

Tagged Image File Format, abbreviated TIFF or TIF, is a computer file format " "for storing raster graphics images, popular among graphic artists, the publishing " "industry, and photographers. TIFF is widely supported by scanning, faxing, " "word processing, optical character recognition, image manipulation, " "desktop publishing, and page-layout applications.

" "

See " "Tagged Image File Format documentation for details.

" ); } QList DImgTIFFPlugin::authors() const { return QList() << DPluginAuthor(QString::fromUtf8("Renchi Raju"), QString::fromUtf8("renchi dot raju at gmail dot com"), QString::fromUtf8("(C) 2005")) << DPluginAuthor(QString::fromUtf8("Gilles Caulier"), QString::fromUtf8("caulier dot gilles at gmail dot com"), QString::fromUtf8("(C) 2006-2019")) ; } void DImgTIFFPlugin::setup(QObject* const /*parent*/) { // Nothing to do } QString DImgTIFFPlugin::loaderName() const { return QLatin1String("TIFF"); } QString DImgTIFFPlugin::typeMimes() const { return QLatin1String("TIF TIFF"); } bool DImgTIFFPlugin::canRead(const QString& filePath) const { QFileInfo fileInfo(filePath); if (!fileInfo.exists()) { qCDebug(DIGIKAM_DIMG_LOG) << "File " << filePath << " does not exist"; return false; } // First simply check file extension QString ext = fileInfo.suffix().toUpper(); if (!ext.isEmpty() && (ext == QLatin1String("TIFF") || ext == QLatin1String("TIF"))) { return true; } // In second, we trying to parse file header. FILE* const f = fopen(QFile::encodeName(filePath).constData(), "rb"); if (!f) { qCDebug(DIGIKAM_DIMG_LOG) << "Failed to open file " << filePath; return false; } - const int headerLen = 9; + const int headerLen = 10; unsigned char header[headerLen]; if (fread(&header, headerLen, 1, f) != 1) { qCDebug(DIGIKAM_DIMG_LOG) << "Failed to read header of file " << filePath; fclose(f); return false; } fclose(f); - uchar tiffBigID[2] = { 0x4D, 0x4D }; - uchar tiffLilID[2] = { 0x49, 0x49 }; + uchar tiffBigID[4] = { 0x4D, 0x4D, 0x00, 0x2A }; + uchar tiffLilID[4] = { 0x49, 0x49, 0x2A, 0x00 }; + uchar Cr2Header[10] = { 0x49, 0x49, 0x2A, 0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0x52 }; - if (memcmp(&header, &tiffBigID, 2) == 0 || - memcmp(&header, &tiffLilID, 2) == 0) + if (memcmp(&header, &Cr2Header, 10) != 0 && + (memcmp(&header, &tiffBigID, 4) == 0 || + memcmp(&header, &tiffLilID, 4) == 0)) { return true; } return false; } bool DImgTIFFPlugin::canWrite(const QString& format) const { if ((format == QLatin1String("TIFF") || format == QLatin1String("TIF"))) { return true; } return false; } DImgLoader* DImgTIFFPlugin::loader(DImg* const image, const DRawDecoding&) const { return new DImgTIFFLoader(image); } } // namespace DigikamTIFFDImgPlugin diff --git a/core/dplugins/dimg/tiff/dimgtiffplugin.h b/core/dplugins/dimg/tiff/dimgtiffplugin.h index f1db1f68c2..c4562c72ef 100644 --- a/core/dplugins/dimg/tiff/dimgtiffplugin.h +++ b/core/dplugins/dimg/tiff/dimgtiffplugin.h @@ -1,73 +1,73 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-22 * Description : TIFF DImg plugin. * * Copyright (C) 2019 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_TIFF_DIMG_PLUGIN_H -#define DIGIKAM_TIFF_DIMG_PLUGIN_H +#ifndef DIGIKAM_DIMG_TIFF_PLUGIN_H +#define DIGIKAM_DIMG_TIFF_PLUGIN_H // Qt includes #include #include // Local includes #include "dplugindimg.h" #include "dimg.h" #include "dimgloader.h" #define DPLUGIN_IID "org.kde.digikam.plugin.dimg.TIFF" using namespace Digikam; namespace DigikamTIFFDImgPlugin { class DImgTIFFPlugin : public DPluginDImg { Q_OBJECT Q_PLUGIN_METADATA(IID DPLUGIN_IID) Q_INTERFACES(Digikam::DPluginDImg) public: explicit DImgTIFFPlugin(QObject* const parent = nullptr); ~DImgTIFFPlugin(); QString name() const override; QString iid() const override; QIcon icon() const override; QString details() const override; QString description() const override; QList authors() const override; void setup(QObject* const) override; QString loaderName() const; QString typeMimes() const; bool canRead(const QString& filePath) const; bool canWrite(const QString& format) const; DImgLoader* loader(DImg* const image, const DRawDecoding& rawSettings = DRawDecoding()) const; }; } // namespace DigikamTIFFDImgPlugin -#endif // DIGIKAM_TIFF_DIMG_PLUGIN_H +#endif // DIGIKAM_DIMG_TIFF_PLUGIN_H diff --git a/core/dplugins/editor/enhance/healingclone/healingclonetool.cpp b/core/dplugins/editor/enhance/healingclone/healingclonetool.cpp index 76544fbd41..bc15cd593b 100644 --- a/core/dplugins/editor/enhance/healingclone/healingclonetool.cpp +++ b/core/dplugins/editor/enhance/healingclone/healingclonetool.cpp @@ -1,667 +1,667 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2017-06-15 * Description : a tool to replace part of the image using another * * Copyright (C) 2004-2019 by Gilles Caulier * Copyright (C) 2017 by Shaza Ismail Kaoud * Copyright (C) 2019 by Ahmed Fathi * * 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 "healingclonetool.h" // C++ includes #include // Qt includes #include #include #include #include #include #include // KDE includes #include #include // Local includes #include "dexpanderbox.h" #include "dnuminput.h" #include "editortoolsettings.h" #include "imageiface.h" #include "itempropertiestxtlabel.h" #include "healingclonetoolwidget.h" namespace DigikamEditorHealingCloneToolPlugin { class Q_DECL_HIDDEN HealingCloneTool::Private { public: explicit Private() : btnSize(QSize(50, 50)), iconSize(QSize(30, 30)), radiusInput(nullptr), blurPercent(nullptr), previewWidget(nullptr), gboxSettings(nullptr), srcButton(nullptr), lassoButton(nullptr), moveButton(nullptr), undoCloneButton(nullptr), redoCloneButton(nullptr), resetLassoPoint(true), insideLassoOperation(false) { } static const QString configGroupName; static const QString configRadiusAdjustmentEntry; static const QString configBlurAdjustmentEntry; const QSize btnSize; const QSize iconSize; DIntNumInput* radiusInput; DDoubleNumInput* blurPercent; HealingCloneToolWidget* previewWidget; EditorToolSettings* gboxSettings; QPushButton* srcButton; QPushButton* lassoButton; QPushButton* moveButton; QPushButton* undoCloneButton; QPushButton* redoCloneButton; DImg cloneImg; std::stack undoStack; std::stack redoStack; bool resetLassoPoint; bool insideLassoOperation; QPoint previousLassoPoint; QPoint startLassoPoint; std::vector lassoColors; std::vector lassoPoints; QPolygon lassoPolygon; std::vector> lassoFlags; std::map, DColor> lassoColorsMap; }; const QString HealingCloneTool::Private::configGroupName(QLatin1String("Healing Clone Tool")); const QString HealingCloneTool::Private::configRadiusAdjustmentEntry(QLatin1String("RadiusAdjustment")); const QString HealingCloneTool::Private::configBlurAdjustmentEntry(QLatin1String("BlurAdjustment")); // -------------------------------------------------------- HealingCloneTool::HealingCloneTool(QObject* const parent) : EditorTool(parent), d(new Private) { setObjectName(QLatin1String("healing clone")); setToolHelp(QLatin1String("healingclonetool.anchor")); d->gboxSettings = new EditorToolSettings(0); d->previewWidget = new HealingCloneToolWidget; refreshImage(); d->previewWidget->setFocusPolicy(Qt::StrongFocus); setToolView(d->previewWidget); setPreviewModeMask(PreviewToolBar::PreviewTargetImage); // -------------------------------------------------------- QLabel* const label = new QLabel(i18n("Brush Radius:")); d->radiusInput = new DIntNumInput(); d->radiusInput->setRange(0, 200, 1); d->radiusInput->setDefaultValue(50); d->radiusInput->setWhatsThis(i18n("A radius of 0 has no effect, " "1 and above determine the brush radius " "that determines the size of parts copied in the image. \nShortcut :: [ and ]")); d->radiusInput->setToolTip(i18n("A radius of 0 has no effect, " "1 and above determine the brush radius " "that determines the size of parts copied in the image. \nShortcut :: [ and ]")); d->previewWidget->setBrushValue(d->radiusInput->value()); // -------------------------------------------------------- QLabel* const label2 = new QLabel(i18n("Radial Blur Percent:")); d->blurPercent = new DDoubleNumInput(); d->blurPercent->setRange(0, 100, 0.1); d->blurPercent->setDefaultValue(0); d->blurPercent->setWhatsThis(i18n("A percent of 0 has no effect, values " "above 0 represent a factor for mixing " "the destination color with source color " "this is done radially i.e. the inner part of " "the brush radius is totally from source and mixing " "with destination is done gradually till the outer part " "of the circle.")); // -------------------------------------------------------- d->srcButton = new QPushButton(); d->srcButton->setFixedSize(d->btnSize); d->srcButton->setIcon(QIcon::fromTheme(QLatin1String("crosshairs"))); d->srcButton->setIconSize(d->iconSize); d->srcButton->setWhatsThis(i18n("Select Source Point.\nShortcut: S")); d->srcButton->setToolTip(i18n("Select Source Point.\nShortcut: S")); // -------------------------------------------------------- d->lassoButton = new QPushButton(); d->lassoButton->setFixedSize(d->btnSize); d->lassoButton->setIcon(QIcon::fromTheme(QLatin1String("edit-select-lasso"))); d->lassoButton->setIconSize(d->iconSize); d->lassoButton->setWhatsThis(i18n("Polygon Selection With Lasso.\nShortcut: L\n" "To Continue polygon, either press L or double click\n" "To Cancel, press ESC")); d->lassoButton->setToolTip(i18n("Polygon Selection With Lasso.\nShortcut: L\n" "To Continue polygon, either press L or double click\n" "To Cancel, press ESC")); // -------------------------------------------------------- d->moveButton = new QPushButton(); d->moveButton->setFixedSize(d->btnSize); d->moveButton->setIcon(QIcon::fromTheme(QLatin1String("transform-browse"))); d->moveButton->setIconSize(d->iconSize); d->moveButton->setWhatsThis(i18n("Move Image.\nShortcut: M")); d->moveButton->setToolTip(i18n("Move Image.\nShortcut: M")); // -------------------------------------------------------- d->undoCloneButton = new QPushButton(); d->undoCloneButton->setFixedSize(d->btnSize); d->undoCloneButton->setIcon(QIcon::fromTheme(QLatin1String("edit-undo"))); d->undoCloneButton->setIconSize(d->iconSize); d->undoCloneButton->setWhatsThis(i18n("Undo clone operation.\nShortcut: CTRL+Z")); d->undoCloneButton->setToolTip(i18n("Undo clone operation.\nShortcut: CTRL+Z")); // -------------------------------------------------------- d->redoCloneButton = new QPushButton(); d->redoCloneButton->setFixedSize(d->btnSize); d->redoCloneButton->setIcon(QIcon::fromTheme(QLatin1String("edit-redo"))); d->redoCloneButton->setIconSize(d->iconSize); d->redoCloneButton->setWhatsThis(i18n("Redo clone operation.\nShortcut: CTRL+Y")); d->redoCloneButton->setToolTip(i18n("Redo clone operation.\nShortcut: CTRL+Y")); // -------------------------------------------------------- QString help = i18n("

How To Use:

" "* Press s to switch to source-selection mode, and select source point on image.
" "* Press s again and start cloning.
" "* Press [ and ] to change brush size.
" "* Press CTRL+Mousewheel to zoom in/out.
" "* Press m to pan the image if image is larger than viewport.
" "* Press l to start lasso mode. Start drawing lasso boundary either " "continuously or discretely, then double-click or press l again to close the boundary.
" "* Inside lasso mode, you can clone only inside the lasso region.

"); DTextBrowser* const label3 = new DTextBrowser(help); label3->setLinesNumber(20); // Tool Buttons const int spacing = d->gboxSettings->spacingHint(); QGridLayout* const grid = new QGridLayout(); QGroupBox* const iconsGroupBox = new QGroupBox(); QHBoxLayout* const iconsHBox = new QHBoxLayout(); iconsHBox->setSpacing(0); iconsHBox->addWidget(d->srcButton); iconsHBox->addWidget(d->lassoButton); iconsHBox->addWidget(d->moveButton); iconsHBox->addWidget(d->undoCloneButton); iconsHBox->addWidget(d->redoCloneButton); iconsGroupBox->setLayout(iconsHBox); // --- grid->addWidget(iconsGroupBox); grid->addWidget(new DLineWidget(Qt::Horizontal, d->gboxSettings->plainPage()), 3, 0, 1, 2); grid->addWidget(label, 4, 0, 1, 2); grid->addWidget(d->radiusInput, 5, 0, 1, 2); grid->addWidget(label2, 6, 0, 1, 2); grid->addWidget(d->blurPercent, 7, 0, 1, 2); grid->addWidget(new DLineWidget(Qt::Horizontal, d->gboxSettings->plainPage()), 8, 0, 1, 2); grid->addWidget(label3, 9, 0, 1, 2); grid->setRowStretch(10, 10); grid->setContentsMargins(spacing, spacing, spacing, spacing); grid->setSpacing(spacing); d->gboxSettings->plainPage()->setLayout(grid); // -------------------------------------------------------- setPreviewModeMask(PreviewToolBar::PreviewTargetImage); setToolSettings(d->gboxSettings); setToolView(d->previewWidget); // -------------------------------------------------------- d->lassoColors.push_back(DColor(Qt::red)); d->lassoColors.push_back(DColor(Qt::white)); d->lassoColors.push_back(DColor(Qt::black)); d->lassoColors.push_back(DColor(Qt::yellow)); d->lassoColors.push_back(DColor(Qt::blue)); d->lassoColors.push_back(DColor(Qt::yellow)); // -------------------------------------------------------- connect(d->radiusInput, SIGNAL(valueChanged(int)), this, SLOT(slotRadiusChanged(int))); connect(d->srcButton, SIGNAL(clicked(bool)), d->previewWidget, SLOT(slotSetSourcePoint())); connect(d->moveButton, SIGNAL(clicked(bool)), d->previewWidget, SLOT(slotMoveImage())); connect(d->lassoButton, SIGNAL(clicked(bool)), d->previewWidget, SLOT(slotLassoSelect())); connect(d->undoCloneButton, SIGNAL(clicked(bool)), this, SLOT(slotUndoClone())); connect(d->redoCloneButton, SIGNAL(clicked(bool)), this, SLOT(slotRedoClone())); connect(d->previewWidget, SIGNAL(signalClone(QPoint,QPoint)), this, SLOT(slotReplace(QPoint,QPoint))); connect(d->previewWidget, SIGNAL(signalLasso(QPoint)), this, SLOT(slotLasso(QPoint))); connect(d->previewWidget, SIGNAL(signalResetLassoPoint()), this, SLOT(slotResetLassoPoints())); connect(d->previewWidget, SIGNAL(signalContinuePolygon()), this, SLOT(slotContinuePolygon())); connect(d->previewWidget, SIGNAL(signalIncreaseBrushRadius()), this, SLOT(slotIncreaseBrushRadius())); connect(d->previewWidget, SIGNAL(signalDecreaseBrushRadius()), this, SLOT(slotDecreaseBrushRadius())); // undo - redo connect(d->previewWidget, SIGNAL(signalPushToUndoStack()), this, SLOT(slotPushToUndoStack())); connect(d->previewWidget, SIGNAL(signalUndoClone()), this, SLOT(slotUndoClone())); connect(d->previewWidget, SIGNAL(signalRedoClone()), this, SLOT(slotRedoClone())); d->cloneImg = d->previewWidget->getOriginalImage(); } HealingCloneTool::~HealingCloneTool() { delete d; } void HealingCloneTool::readSettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(d->configGroupName); d->radiusInput->setValue(group.readEntry(d->configRadiusAdjustmentEntry, d->radiusInput->defaultValue())); d->blurPercent->setValue(group.readEntry(d->configBlurAdjustmentEntry, d->blurPercent->defaultValue())); } void HealingCloneTool::writeSettings() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(d->configGroupName); group.writeEntry(d->configRadiusAdjustmentEntry, d->radiusInput->value()); group.writeEntry(d->configBlurAdjustmentEntry, d->blurPercent->value()); config->sync(); } void HealingCloneTool::finalRendering() { ImageIface iface; FilterAction action(QLatin1String("digikam:healingCloneTool"), 1); iface.setOriginal(i18n("healingClone"), action, d->cloneImg); } void HealingCloneTool::slotResetSettings() { d->radiusInput->blockSignals(true); d->radiusInput->slotReset(); d->radiusInput->blockSignals(false); } void HealingCloneTool::slotResized() { toolView()->update(); } void HealingCloneTool::slotReplace(const QPoint& srcPoint, const QPoint& dstPoint) { clone(&d->cloneImg, srcPoint, dstPoint); } void HealingCloneTool::slotRadiusChanged(int r) { d->previewWidget->setBrushValue(r); } void HealingCloneTool::clone(DImg* const img, const QPoint& srcPoint, const QPoint& dstPoint) { double blurPercent = d->blurPercent->value() / 100; int radius = d->radiusInput->value(); for (int i = -1 * radius ; i < radius ; ++i) { for (int j = -1 * radius ; j < radius ; ++j) { int rPercent = (i * i) + (j * j); if (rPercent < (radius * radius)) // Check for inside the circle { if ((srcPoint.x() + i < 0) || (srcPoint.x() + i >= (int)img->width()) || (srcPoint.y() + j < 0) || (srcPoint.y() + j >= (int)img->height()) || (dstPoint.x() + i < 0) || (dstPoint.x() + i >= (int)img->width()) || (dstPoint.y() + j < 0) || (dstPoint.y() + j >= (int)img->height())) { continue; } DColor cSrc = img->getPixelColor(srcPoint.x() + i, srcPoint.y() + j); if (d->insideLassoOperation && !d->lassoPoints.empty()) { if (d->lassoFlags.at(dstPoint.x() + i).at(dstPoint.y() + j)) { continue; } bool isInside = d->lassoPolygon.containsPoint(QPoint(dstPoint.x() + i, dstPoint.y() + j), Qt::OddEvenFill); if (!isInside) { continue; } if (d->lassoFlags.at(srcPoint.x() + i).at(srcPoint.y() + j)) { cSrc = d->lassoColorsMap[std::make_pair(srcPoint.x() + i, srcPoint.y() + j)]; } } double rP = blurPercent * rPercent / (radius * radius); DColor cDst = img->getPixelColor(dstPoint.x() + i, dstPoint.y() + j); cSrc.multiply(1 - rP); cDst.multiply(rP); cSrc.blendAdd(cDst); img->setPixelColor(dstPoint.x() + i, dstPoint.y() + j, cSrc); d->previewWidget->setCloneVectorChanged(true); } } } d->previewWidget->updateImage(*img); } void HealingCloneTool::updateLasso(std::vector& points) { uint radius = 5; static uint colorCounter = 0; foreach (const QPoint& p, points) { for (uint i = 0 ; i < radius ; ++i) { for (uint j = 0 ; j < radius ; ++j) { uint x_shifted = p.x() + i; uint y_shifted = p.y() + j; DColor c = d->cloneImg.getPixelColor(x_shifted, y_shifted); d->lassoColorsMap.insert(std::make_pair(std::make_pair(x_shifted, y_shifted), c)); d->cloneImg.setPixelColor(x_shifted, y_shifted, d->lassoColors[(colorCounter) % d->lassoColors.size()]); d->lassoFlags.at(x_shifted).at(y_shifted) = true; colorCounter++; } } } d->previewWidget->updateImage(d->cloneImg); } void HealingCloneTool::slotLasso(const QPoint& dst) { if (d->resetLassoPoint) { d->previousLassoPoint = dst; d->resetLassoPoint = false; d->startLassoPoint = dst; } std::vector points = interpolate(d->previousLassoPoint, dst); d->lassoPoints.push_back(dst); d->previousLassoPoint = dst; updateLasso(points); d->previewWidget->setIsLassoPointsVectorEmpty(d->lassoPoints.empty()); } std::vector HealingCloneTool::interpolate(const QPoint& start, const QPoint& end) { std::vector points; points.push_back(start); QPointF distanceVec = QPoint(end.x()-start.x(), end.y() - start.y()); double distance = sqrt(distanceVec.x() * distanceVec.x() + distanceVec.y() * distanceVec.y()); //creating a unit vector distanceVec.setX(distanceVec.x() / distance); distanceVec.setY(distanceVec.y() / distance); int steps = (int) distance; for (int i = 0 ; i < steps ; ++i) { points.push_back(QPoint(start.x() + i * distanceVec.x(), start.y() + i * distanceVec.y())); } points.push_back(end); return points; } void HealingCloneTool::removeLassoPixels() { std::map, DColor>::iterator it; - for (it = d->lassoColorsMap.begin() ; it != d->lassoColorsMap.end() ; it++) + for (it = d->lassoColorsMap.begin() ; it != d->lassoColorsMap.end() ; ++it) { std::pair xy = it->first; DColor color = it->second; d->cloneImg.setPixelColor(xy.first, xy.second, color); } d->previewWidget->updateImage(d->cloneImg); } void HealingCloneTool::redrawLassoPixels() { int colorCounter = 0; std::map, DColor>::iterator it; - for (it = d->lassoColorsMap.begin() ; it != d->lassoColorsMap.end() ; it++) + for (it = d->lassoColorsMap.begin() ; it != d->lassoColorsMap.end() ; ++it) { colorCounter++; DColor color = d->lassoColors[(colorCounter) % d->lassoColors.size()]; std::pair xy = it->first; d->cloneImg.setPixelColor(xy.first, xy.second, color); } d->previewWidget->updateImage(d->cloneImg); } void HealingCloneTool::slotResetLassoPoints() { removeLassoPixels(); d->resetLassoPoint = true; d->lassoPoints.clear(); d->insideLassoOperation = true; d->lassoPolygon.clear(); d->lassoColorsMap.clear(); initializeLassoFlags(); d->previewWidget->setIsLassoPointsVectorEmpty(d->lassoPoints.empty()); } void HealingCloneTool::slotContinuePolygon() { if (d->lassoPoints.empty()) { return; } QPoint& start = d->startLassoPoint; QPoint& end = d->previousLassoPoint; std::vector points = interpolate(end,start); updateLasso(points); d->lassoPoints.push_back(start); QVector polygon; foreach (const QPoint& point, d->lassoPoints) { polygon.append(point); } d->lassoPolygon = QPolygon(polygon); } void HealingCloneTool::slotIncreaseBrushRadius() { int size = d->radiusInput->value(); d->radiusInput->setValue(size + 1); } void HealingCloneTool::slotDecreaseBrushRadius() { int size = d->radiusInput->value(); d->radiusInput->setValue(size - 1); } void HealingCloneTool::initializeLassoFlags() { int w = d->cloneImg.width(); int h = d->cloneImg.height(); d->lassoFlags.resize(w); for (int i = 0 ; i < w ; ++i) { d->lassoFlags.at(i).resize(h); } for (int i = 0 ; i < w ; ++i) { for (int j = 0 ; j < h ; ++j) { d->lassoFlags.at(i).at(j) = false; } } } void HealingCloneTool::slotPushToUndoStack() { d->redoStack = std::stack(); removeLassoPixels(); d->undoStack.push(d->previewWidget->getOriginalImage()); redrawLassoPixels(); } void HealingCloneTool::slotUndoClone() { if (d->undoStack.empty()) { return; } removeLassoPixels(); d->redoStack.push(d->previewWidget->getOriginalImage()); d->cloneImg = d->undoStack.top(); d->undoStack.pop(); d->previewWidget->updateImage(d->cloneImg); redrawLassoPixels(); } void HealingCloneTool::slotRedoClone() { // slotResetLassoPoints(); if (d->redoStack.empty()) { return; } removeLassoPixels(); d->undoStack.push(d->previewWidget->getOriginalImage()); d->cloneImg = d->redoStack.top(); d->redoStack.pop(); d->previewWidget->updateImage(d->cloneImg); redrawLassoPixels(); } void HealingCloneTool::refreshImage() { ImageRegionWidget* const wgt = dynamic_cast(d->previewWidget); if (wgt) { QRectF test = wgt->sceneRect(); ImageRegionItem* const item = dynamic_cast(wgt->item()); if (item) { int w = item->boundingRect().width(); int h = item->boundingRect().height(); test.setWidth(10); test.setHeight(10); wgt->fitInView(test, Qt::KeepAspectRatio); test.setWidth(w); test.setHeight(h); wgt->fitInView(test, Qt::KeepAspectRatio); } } } } // namespace DigikamEditorHealingCloneToolPlugin diff --git a/core/dplugins/editor/enhance/healingclone/healingclonetoolwidget.cpp b/core/dplugins/editor/enhance/healingclone/healingclonetoolwidget.cpp index b297573e83..b544a75cdb 100644 --- a/core/dplugins/editor/enhance/healingclone/healingclonetoolwidget.cpp +++ b/core/dplugins/editor/enhance/healingclone/healingclonetoolwidget.cpp @@ -1,674 +1,674 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2017-06-15 * Description : a brush for use with tool to replace part of the image using another * - * Copyright (C) 2017 by Shaza Ismail Kaoud - * Copyright (C) 2019 by Ahmed Fathi + * Copyright (C) 2017 by Shaza Ismail Kaoud + * Copyright (C) 2019 by Ahmed Fathi * * 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 "healingclonetoolwidget.h" // Qt includes #include #include #include // KDE includes #include // Local includes #include "digikam_debug.h" #include "overlaywidget.h" #include "previewlayout.h" namespace DigikamEditorHealingCloneToolPlugin { class Q_DECL_HIDDEN HealingCloneToolWidget::Private { public: explicit Private() : srcSet(true), isLassoPointsVectorEmpty(true), amIFocused(false), proceedInMoveEvent(false), cloneVectorChanged(true), brushRadius(1), brushValue(1), currentState(HealingCloneState::SELECT_SOURCE), previousState(HealingCloneState::DO_NOTHING), drawCursor(nullptr), sourceCursor(nullptr), sourceCursorCenter(nullptr) { - src = QPoint(0, 0); + src = QPoint(0, 0); } bool srcSet; bool isLassoPointsVectorEmpty; QPointF lastCursorPosition; QPoint src; QPoint dst; bool amIFocused; bool proceedInMoveEvent; bool cloneVectorChanged; int brushRadius; int brushValue; HealingCloneState currentState; HealingCloneState previousState; QGraphicsEllipseItem* drawCursor; QGraphicsEllipseItem* sourceCursor; QGraphicsEllipseItem* sourceCursorCenter; }; HealingCloneToolWidget::HealingCloneToolWidget(QWidget* const parent) : ImageRegionWidget(parent, false), d(new Private) { activateState(HealingCloneState::SELECT_SOURCE); updateSourceCursor(d->src, 10); connect(this, SIGNAL(viewportRectChanged(QRectF)), this, SLOT(slotImageRegionChanged())); } HealingCloneToolWidget::~HealingCloneToolWidget() { delete d->drawCursor; delete d->sourceCursor; delete d->sourceCursorCenter; delete d; } void HealingCloneToolWidget::mousePressEvent(QMouseEvent* e) { if (!d->amIFocused && (d->currentState == HealingCloneState::PAINT || d->currentState == HealingCloneState::LASSO_CLONE)) { d->amIFocused = true; return; } else if (!d->amIFocused) { d->amIFocused = true; } d->proceedInMoveEvent = true; if (d->currentState == HealingCloneState::DO_NOTHING) { ImageRegionWidget::mousePressEvent(e); return; } if ((d->currentState == HealingCloneState::PAINT || d->currentState == HealingCloneState::LASSO_CLONE)) { if (d->cloneVectorChanged) { setCloneVectorChanged(false); emit signalPushToUndoStack(); } } if (d->currentState == HealingCloneState::MOVE_IMAGE && (e->buttons() & Qt::LeftButton)) { ImageRegionWidget::mousePressEvent(e); } else if (d->srcSet) { ImageRegionWidget::mousePressEvent(e); } else if (d->currentState == HealingCloneState::LASSO_DRAW_BOUNDARY && (e->buttons() & Qt::LeftButton)) { QPoint dst = QPoint(e->x(), e->y()); emit signalLasso(mapToImageCoordinates(dst)); } else { if (e->button() == Qt::LeftButton) { d->dst = mapToImageCoordinates(e->pos()); emit signalClone(d->src, d->dst); } } } void HealingCloneToolWidget::mouseMoveEvent(QMouseEvent* e) { bool cursorOutsideScene = checkPointOutsideScene(e->pos()); d->lastCursorPosition = mapToScene(e->pos()); if (cursorOutsideScene && d->currentState != HealingCloneState::DO_NOTHING) { activateState(HealingCloneState::DO_NOTHING); } else if (!cursorOutsideScene && d->currentState == HealingCloneState::DO_NOTHING) { activateState(d->previousState); } setDrawCursorPosition(d->lastCursorPosition); if (d->currentState == HealingCloneState::DO_NOTHING) { ImageRegionWidget::mouseMoveEvent(e); return; } if (!d->proceedInMoveEvent) { return; } if (d->currentState == HealingCloneState::MOVE_IMAGE && (e->buttons() & Qt::LeftButton)) { ImageRegionWidget::mouseMoveEvent(e); } else if (d->currentState == HealingCloneState::LASSO_DRAW_BOUNDARY && (e->buttons() & Qt::LeftButton)) { QPoint dst = QPoint(e->x(), e->y()); emit signalLasso(mapToImageCoordinates(dst)); } else if ((e->buttons() & Qt::LeftButton) && !d->srcSet) { QPoint currentDst = mapToImageCoordinates(e->pos()); QPoint currentSrc = d->src; QPoint orgDst = d->dst; currentSrc = QPoint(currentSrc.x() + currentDst.x() - orgDst.x(), currentSrc.y() + currentDst.y() - orgDst.y()); // Source Cursor Update in scene coordinates QPointF tempCursorPosition = mapToScene(mapFromImageCoordinates(currentSrc)); setSourceCursorPosition(tempCursorPosition); emit signalClone(currentSrc, currentDst); } if (d->srcSet) { ImageRegionWidget::mouseMoveEvent(e); } } void HealingCloneToolWidget::mouseReleaseEvent(QMouseEvent* e) { ImageRegionWidget::mouseReleaseEvent(e); if (d->currentState == HealingCloneState::DO_NOTHING) { return; } if (d->currentState == HealingCloneState::MOVE_IMAGE) { // setCursor(Qt::OpenHandCursor); ImageRegionWidget::mouseReleaseEvent(e); } else if (d->srcSet) { d->src = mapToImageCoordinates(e->pos()); setSourceCursorPosition(mapToScene(e->pos())); undoSlotSetSourcePoint(); } else { QPointF tempCursorPosition = mapToScene(mapFromImageCoordinates(d->src)); setSourceCursorPosition(tempCursorPosition); } } -void HealingCloneToolWidget::mouseDoubleClickEvent(QMouseEvent* event) +void HealingCloneToolWidget::mouseDoubleClickEvent(QMouseEvent* e) { - if (event->button() == Qt::LeftButton) + if (e->button() == Qt::LeftButton) { if (d->currentState == HealingCloneState::LASSO_DRAW_BOUNDARY) { slotLassoSelect(); } } } -void HealingCloneToolWidget::keyPressEvent(QKeyEvent *e) +void HealingCloneToolWidget::keyPressEvent(QKeyEvent* e) { if (e->key() == Qt::Key_M) { slotMoveImage(); } else if (e->key() == Qt::Key_L) { slotLassoSelect(); } if (e->key() == Qt::Key_BracketLeft) { emit signalDecreaseBrushRadius(); } if (e->key() == Qt::Key_BracketRight) { emit signalIncreaseBrushRadius(); } if (e->matches(QKeySequence::Undo)) { emit signalUndoClone(); } if (e->matches(QKeySequence::Redo)) { emit signalRedoClone(); } QWidget::keyPressEvent(e); } -bool HealingCloneToolWidget::event(QEvent* event) +bool HealingCloneToolWidget::event(QEvent* e) { - QKeyEvent* const keyEvent = static_cast(event); + QKeyEvent* const keyEvent = static_cast(e); if (keyEvent && keyEvent->key() == Qt::Key_Escape && d->currentState != HealingCloneState::PAINT) { keyEvent->accept(); if (d->currentState == HealingCloneState::LASSO_DRAW_BOUNDARY) { if (!d->isLassoPointsVectorEmpty) { slotLassoSelect(); } slotLassoSelect(); } else if (d->currentState == HealingCloneState::LASSO_CLONE) { slotLassoSelect(); } return true; } - return QWidget::event(event); + return QWidget::event(e); } void HealingCloneToolWidget::keyReleaseEvent(QKeyEvent* e) { if (e->key() == Qt::Key_S) { if (d->currentState == HealingCloneState::SELECT_SOURCE) { undoSlotSetSourcePoint(); } else { slotSetSourcePoint(); } } } void HealingCloneToolWidget::focusOutEvent(QFocusEvent*) { d->amIFocused = false; d->proceedInMoveEvent = false; } void HealingCloneToolWidget::focusInEvent(QFocusEvent*) { } void HealingCloneToolWidget::slotSetSourcePoint() { d->srcSet = true; activateState(HealingCloneState::SELECT_SOURCE); } void HealingCloneToolWidget::slotMoveImage() { if (d->currentState == HealingCloneState::MOVE_IMAGE) { if (d->isLassoPointsVectorEmpty) { activateState(HealingCloneState::PAINT); } else { activateState(HealingCloneState::LASSO_CLONE); emit signalContinuePolygon(); } } else { activateState(HealingCloneState::MOVE_IMAGE); } } void HealingCloneToolWidget::slotLassoSelect() { if (d->currentState != HealingCloneState::LASSO_DRAW_BOUNDARY && d->currentState != HealingCloneState::LASSO_CLONE) { activateState(HealingCloneState::LASSO_DRAW_BOUNDARY); emit signalResetLassoPoint(); } else if (d->currentState == HealingCloneState::LASSO_DRAW_BOUNDARY) { if (d->isLassoPointsVectorEmpty) { activateState(HealingCloneState::PAINT); } else { activateState(HealingCloneState::LASSO_CLONE); emit signalContinuePolygon(); } } else if (d->currentState == HealingCloneState::LASSO_CLONE) { activateState(HealingCloneState::PAINT); emit signalResetLassoPoint(); } } void HealingCloneToolWidget::undoSlotSetSourcePoint() { d->srcSet = false; if (d->isLassoPointsVectorEmpty) { activateState(HealingCloneState::PAINT); } else { activateState(HealingCloneState::LASSO_CLONE); emit signalContinuePolygon(); } } void HealingCloneToolWidget::changeCursorShape(const QColor& color) { if (d->drawCursor) { scene()->removeItem(d->drawCursor); delete d->drawCursor; } int diameter = d->brushRadius * 2; d->drawCursor = new QGraphicsEllipseItem(0, 0, diameter, diameter); d->drawCursor->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); QPen pen(Qt::SolidLine); pen.setWidth(2); pen.setColor(color); d->drawCursor->setPen(pen); d->drawCursor->setBrush(QBrush(Qt::transparent)); d->drawCursor->setOpacity(1); scene()->addItem(d->drawCursor); QPointF tempCursorPosition = mapToScene(mapFromImageCoordinates(d->src)); updateSourceCursor(tempCursorPosition, diameter); } void HealingCloneToolWidget::setBrushValue(int value) { d->brushValue = value; slotImageRegionChanged(); } void HealingCloneToolWidget::setIsLassoPointsVectorEmpty(bool isEmpty) { d->isLassoPointsVectorEmpty = isEmpty; } void HealingCloneToolWidget::activateState(HealingCloneState state) { d->previousState = d->currentState; if (state != HealingCloneState::MOVE_IMAGE) { setDragMode(QGraphicsView::NoDrag); } if (d->currentState == HealingCloneState::LASSO_DRAW_BOUNDARY && state != HealingCloneState::LASSO_CLONE) { emit signalContinuePolygon(); } d->currentState = state; if (state == HealingCloneState::PAINT) { changeCursorShape(Qt::blue); setCursor(QCursor(Qt::BlankCursor)); setDrawCursorPosition(d->lastCursorPosition); } else if (state == HealingCloneState::MOVE_IMAGE) { if (QGraphicsView::dragMode() != QGraphicsView::ScrollHandDrag) { setDragMode(QGraphicsView::ScrollHandDrag); } } else if (state == HealingCloneState::LASSO_DRAW_BOUNDARY) { setCursor(QCursor(Qt::PointingHandCursor)); } else if (state == HealingCloneState::LASSO_CLONE) { changeCursorShape(Qt::blue); setCursor(QCursor(Qt::BlankCursor)); setDrawCursorPosition(d->lastCursorPosition); } else if (state == HealingCloneState::SELECT_SOURCE) { setCursor(QCursor(Qt::CrossCursor)); } else if (state ==HealingCloneState::DO_NOTHING) { setCursor(QCursor(Qt::ArrowCursor)); } } void HealingCloneToolWidget::setCloneVectorChanged(bool changed) { d->cloneVectorChanged = changed; } QPoint HealingCloneToolWidget::mapToImageCoordinates(const QPoint& point) const { QPoint ret; ImageRegionItem* const region = dynamic_cast(item()); if (region) { QPointF temp = region->zoomSettings()->mapZoomToImage(mapToScene(point)) ; ret = QPoint((int)temp.x(), (int)temp.y()); } return ret; } QPoint HealingCloneToolWidget::mapFromImageCoordinates(const QPoint& point) const { QPoint ret; ImageRegionItem* const region = dynamic_cast(item()); if (region) { ret = mapFromScene(region->zoomSettings()->mapImageToZoom(point)); } return ret; } void HealingCloneToolWidget::updateSourceCursor(const QPointF& pos, int diameter) { if (d->sourceCursor) { scene()->removeItem(d->sourceCursor); scene()->removeItem(d->sourceCursorCenter); delete d->sourceCursor; delete d->sourceCursorCenter; } d->sourceCursor = new QGraphicsEllipseItem(0, 0, diameter, diameter); d->sourceCursorCenter = new QGraphicsEllipseItem(0, 0, 2, 2); d->sourceCursor->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); d->sourceCursorCenter->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true); QPen pen(Qt::DashDotDotLine); pen.setWidth(2); pen.setColor(Qt::black); d->sourceCursor->setPen(pen); d->sourceCursor->setBrush(QBrush(Qt::transparent)); d->sourceCursor->setOpacity(1); scene()->addItem(d->sourceCursor); pen.setStyle(Qt::SolidLine); d->sourceCursorCenter->setPen(pen); d->sourceCursorCenter->setBrush(QBrush(Qt::black)); d->sourceCursorCenter->setOpacity(1); scene()->addItem(d->sourceCursorCenter); setSourceCursorPosition(pos); } void HealingCloneToolWidget::setDrawCursorPosition(const QPointF& topLeftPos) { if (!d->drawCursor) { return; } double d1 = d->drawCursor->rect().width() / 2.0; QPointF shiftedPos = QPointF(topLeftPos.x() - d1, topLeftPos.y() - d1); // check if source is outside scene bool drawCursorOutsideScene = (topLeftPos.x() < 0) || (topLeftPos.x() > scene()->width()) || (topLeftPos.y() < 0) || (topLeftPos.y() > scene()->height()); if (drawCursorOutsideScene || (d->currentState != HealingCloneState::PAINT && d->currentState != HealingCloneState::LASSO_CLONE)) { d->drawCursor->setVisible(false); } else { d->drawCursor->setPos(shiftedPos); d->drawCursor->setVisible(true); } } void HealingCloneToolWidget::setSourceCursorPosition(const QPointF& topLeftPos) { double d1 = d->sourceCursor->rect().width() / 2.0; QPointF shiftedPos = QPointF(topLeftPos.x() - d1, topLeftPos.y() - d1); double d2 = d->sourceCursorCenter->rect().width() / 2.0; QPointF shiftedPos2 = QPointF(topLeftPos.x() - d2, topLeftPos.y() - d2); // check if source is outside scene bool sourceCursorOutsideScene = (topLeftPos.x() < 0) || (topLeftPos.x() > scene()->width()) || (topLeftPos.y() < 0) || (topLeftPos.y() > scene()->height()); if (sourceCursorOutsideScene) { d->sourceCursor->setVisible(false); d->sourceCursorCenter->setVisible(false); } else { d->sourceCursor->setPos(shiftedPos); d->sourceCursorCenter->setPos(shiftedPos2); d->sourceCursor->setVisible(true); d->sourceCursorCenter->setVisible(true); } } bool HealingCloneToolWidget::checkPointOutsideScene(const QPoint& globalPoint) const { bool pointOutsideScene; QPointF temp = mapToScene(globalPoint); if (viewport()->width() > scene()->width()) { pointOutsideScene = (temp.x() < 0) || (temp.x() > scene()->width()) || (temp.y() < 0) || (temp.y() > scene()->height()); } else { QPoint bottomRight = QPoint(viewport()->width(), viewport()->height()); int right = mapToScene(bottomRight).x(); int left = right - viewport()->width(); int bottom = mapToScene(bottomRight).y(); int top = bottom - viewport()->height(); pointOutsideScene = (temp.x() < left) || (temp.x() + 1 > right) || (temp.y() < top) || (temp.y() + 1 > bottom); } return pointOutsideScene; } void HealingCloneToolWidget::slotImageRegionChanged() { double zoom = layout()->realZoomFactor(); d->brushRadius = qRound(d->brushValue * zoom); activateState(d->currentState); if (!d->lastCursorPosition.isNull()) { setDrawCursorPosition(d->lastCursorPosition); } } } // namespace DigikamEditorHealingCloneToolPlugin diff --git a/core/dplugins/editor/enhance/healingclone/healingclonetoolwidget.h b/core/dplugins/editor/enhance/healingclone/healingclonetoolwidget.h index 7b69291dfc..3b9a4494b9 100644 --- a/core/dplugins/editor/enhance/healingclone/healingclonetoolwidget.h +++ b/core/dplugins/editor/enhance/healingclone/healingclonetoolwidget.h @@ -1,130 +1,130 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2017-06-15 * Description : a brush for use with tool to replace part of the image using another * - * Copyright (C) 2017 by Shaza Ismail Kaoud - * Copyright (C) 2019 by Ahmed Fathi + * Copyright (C) 2017 by Shaza Ismail Kaoud + * Copyright (C) 2019 by Ahmed Fathi * * 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_HEALING_CLONE_TOOL_WIDGET_H #define DIGIKAM_HEALING_CLONE_TOOL_WIDGET_H // Local includes #include "previewtoolbar.h" #include "imageregionwidget.h" #include "imageregionitem.h" using namespace Digikam; namespace DigikamEditorHealingCloneToolPlugin { class HealingCloneToolWidget : public ImageRegionWidget { Q_OBJECT Q_ENUMS(HealingCloneState) public: enum HealingCloneState { SELECT_SOURCE, PAINT, LASSO_DRAW_BOUNDARY, LASSO_CLONE, MOVE_IMAGE, DO_NOTHING }; public: /** * Standard constructor */ explicit HealingCloneToolWidget(QWidget* const parent = nullptr); ~HealingCloneToolWidget(); void setBrushValue(int value); void setIsLassoPointsVectorEmpty(bool); void setCloneVectorChanged(bool); void setDrawCursorPosition(const QPointF& topLeftPos); void setSourceCursorPosition(const QPointF& topLeftPos); void changeCursorShape(const QColor& color); bool checkPointOutsideScene(const QPoint& point) const; void updateSourceCursor(const QPointF& pos = QPoint(), int diamter = 10); QPoint mapToImageCoordinates(const QPoint& point) const; QPoint mapFromImageCoordinates(const QPoint& point) const; public Q_SLOTS: /** * @brief slotSrcSet toggles the fixing of the brush source center */ void slotSetSourcePoint(); void slotMoveImage(); void slotLassoSelect(); Q_SIGNALS: /** * @brief signalClone emitted when the src is set and the user initiated a brush click * and keeps emitting with motion */ void signalClone(const QPoint& currentSrc, const QPoint& currentDst); void signalLasso(const QPoint& dst); void signalResetLassoPoint(); void signalContinuePolygon(); void signalIncreaseBrushRadius(); void signalDecreaseBrushRadius(); void signalPushToUndoStack(); void signalUndoClone(); void signalRedoClone(); private Q_SLOTS: void slotImageRegionChanged(); protected: void mouseReleaseEvent(QMouseEvent*) override; void mousePressEvent(QMouseEvent*) override; void mouseMoveEvent(QMouseEvent*) override; void mouseDoubleClickEvent(QMouseEvent*) override; void keyPressEvent(QKeyEvent*) override; void keyReleaseEvent(QKeyEvent*) override; void focusOutEvent(QFocusEvent*) override; void focusInEvent(QFocusEvent*) override; bool event(QEvent*) override; void undoSlotSetSourcePoint(); void activateState(HealingCloneState state); private: class Private; Private* const d; }; } // namespace DigikamEditorHealingCloneToolPlugin #endif // DIGIKAM_HEALING_CLONE_TOOL_WIDGET_H diff --git a/core/dplugins/rawimport/rawtherapee/rawimportrawtherapeeplugin.h b/core/dplugins/rawimport/rawtherapee/rawimportrawtherapeeplugin.h index f4a66d0de8..8b29ba053b 100644 --- a/core/dplugins/rawimport/rawtherapee/rawimportrawtherapeeplugin.h +++ b/core/dplugins/rawimport/rawtherapee/rawimportrawtherapeeplugin.h @@ -1,77 +1,77 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-18 * Description : RawTherapee raw import plugin. * * Copyright (C) 2019 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_RAWTHERAPEE_RAW_IMPORT_PLUGIN_H -#define DIGIKAM_RAWTHERAPEE_RAW_IMPORT_PLUGIN_H +#ifndef DIGIKAM_RAW_IMPORT_RAWTHERAPEE_PLUGIN_H +#define DIGIKAM_RAW_IMPORT_RAWTHERAPEE_PLUGIN_H // Qt includes #include // Local includes #include "dpluginrawimport.h" #define DPLUGIN_IID "org.kde.digikam.plugin.rawimport.RawTherapee" using namespace Digikam; namespace DigikamRawImportRawTherapeePlugin { class RawTherapeeRawImportPlugin : public DPluginRawImport { Q_OBJECT Q_PLUGIN_METADATA(IID DPLUGIN_IID) Q_INTERFACES(Digikam::DPluginRawImport) public: explicit RawTherapeeRawImportPlugin(QObject* const parent = nullptr); ~RawTherapeeRawImportPlugin(); QString name() const override; QString iid() const override; QIcon icon() const override; QString details() const override; QString description() const override; QList authors() const override; void setup(QObject* const) override; bool run(const QString& filePath, const DRawDecoding& def); private Q_SLOTS: void slotErrorOccurred(QProcess::ProcessError); void slotProcessFinished(int, QProcess::ExitStatus); void slotProcessReadyRead(); private: class Private; Private* const d; }; } // namespace DigikamRawImportRawTherapeePlugin -#endif // DIGIKAM_RAWTHERAPEE_RAW_IMPORT_PLUGIN_H +#endif // DIGIKAM_RAW_IMPORT_RAWTHERAPEE_PLUGIN_H diff --git a/core/dplugins/rawimport/ufraw/rawimportufrawplugin.h b/core/dplugins/rawimport/ufraw/rawimportufrawplugin.h index 6f0f50620a..7bfe23fe10 100644 --- a/core/dplugins/rawimport/ufraw/rawimportufrawplugin.h +++ b/core/dplugins/rawimport/ufraw/rawimportufrawplugin.h @@ -1,77 +1,77 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2019-09-18 * Description : UFRaw raw import plugin. * * Copyright (C) 2019 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_UFRAW_RAW_IMPORT_PLUGIN_H -#define DIGIKAM_UFRAW_RAW_IMPORT_PLUGIN_H +#ifndef DIGIKAM_RAW_IMPORT_UFRAW_PLUGIN_H +#define DIGIKAM_RAW_IMPORT_UFRAW_PLUGIN_H // Qt includes #include // Local includes #include "dpluginrawimport.h" #define DPLUGIN_IID "org.kde.digikam.plugin.rawimport.UFRaw" using namespace Digikam; namespace DigikamRawImportUFRawPlugin { class UFRawRawImportPlugin : public DPluginRawImport { Q_OBJECT Q_PLUGIN_METADATA(IID DPLUGIN_IID) Q_INTERFACES(Digikam::DPluginRawImport) public: explicit UFRawRawImportPlugin(QObject* const parent = nullptr); ~UFRawRawImportPlugin(); QString name() const override; QString iid() const override; QIcon icon() const override; QString details() const override; QString description() const override; QList authors() const override; void setup(QObject* const) override; bool run(const QString& filePath, const DRawDecoding& def); private Q_SLOTS: void slotErrorOccurred(QProcess::ProcessError); void slotProcessFinished(int, QProcess::ExitStatus); void slotProcessReadyRead(); private: - + class Private; Private* const d; }; } // namespace DigikamRawImportUFRawPlugin -#endif // DIGIKAM_UFRAW_RAW_IMPORT_PLUGIN_H +#endif // DIGIKAM_RAW_IMPORT_UFRAW_PLUGIN_H diff --git a/core/showfoto/main/showfoto.cpp b/core/showfoto/main/showfoto.cpp index 5ab68d93a4..54b3548e46 100644 --- a/core/showfoto/main/showfoto.cpp +++ b/core/showfoto/main/showfoto.cpp @@ -1,896 +1,896 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2004-11-22 * Description : stand alone digiKam image editor * * Copyright (C) 2004-2019 by Gilles Caulier * Copyright (C) 2006-2012 by Marcel Wiesweg * Copyright (C) 2009-2011 by Andi Clemens * Copyright (C) 2004-2005 by Renchi Raju * Copyright (C) 2005-2006 by Tom Albers * Copyright (C) 2008 by Arnd Baecker * Copyright (C) 2013-2015 by Mohamed_Anwer * * 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 "showfoto.h" #include "showfoto_p.h" namespace ShowFoto { ShowFoto::ShowFoto(const QList& urlList) : Digikam::EditorWindow(QLatin1String("Showfoto")), d(new Private) { setXMLFile(QLatin1String("showfotoui5.rc")); m_nonDestructive = false; // Show splash-screen at start up. KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(configGroupName()); if (group.readEntry(QLatin1String("ShowSplash"), true) && !qApp->isSessionRestored()) { d->splash = new Digikam::DSplashScreen(); d->splash->show(); } // Setup loading cache and thumbnails interface. Digikam::LoadingCacheInterface::initialize(); Digikam::MetaEngineSettings::instance(); d->thumbLoadThread = new Digikam::ThumbnailLoadThread(); d->thumbLoadThread->setThumbnailSize(Digikam::ThumbnailSize::Huge); d->thumbLoadThread->setSendSurrogatePixmap(true); // Check ICC profiles repository availability if (d->splash) { d->splash->setMessage(i18n("Checking ICC repository...")); } d->validIccPath = Digikam::SetupICC::iccRepositoryIsValid(); // Populate Themes if (d->splash) { d->splash->setMessage(i18n("Loading themes...")); } Digikam::ThemeManager::instance(); // Load plugins if (d->splash) { d->splash->setMessage(i18n("Load Plugins...")); } DPluginLoader* const dpl = Digikam::DPluginLoader::instance(); dpl->init(); // -- Build the GUI ----------------------------------- setupUserArea(); setupActions(); setupStatusBar(); createGUI(xmlFile()); registerPluginsActions(); cleanupActions(); // Create tool selection view setupSelectToolsAction(); // Create context menu. setupContextMenu(); // Make signals/slots connections setupConnections(); // Disable all actions toggleActions(false); // -- Read settings -------------------------------- readSettings(); applySettings(); setAutoSaveSettings(configGroupName(), true); d->rightSideBar->loadState(); //-------------------------------------------------- d->thumbBarDock->reInitialize(); // -- Load current items --------------------------- slotDroppedUrls(urlList, false); } ShowFoto::~ShowFoto() { delete m_canvas; m_canvas = nullptr; Digikam::ThumbnailLoadThread::cleanUp(); Digikam::LoadingCacheInterface::cleanUp(); Digikam::DPluginLoader::instance()->cleanUp(); delete d->model; delete d->filterModel; delete d->thumbBar; delete d->rightSideBar; delete d->thumbLoadThread; delete d; } bool ShowFoto::queryClose() { // wait if a save operation is currently running if (!waitForSavingToComplete()) { return false; } if (!d->thumbBar->currentInfo().isNull() && !promptUserSave(d->thumbBar->currentUrl())) { return false; } saveSettings(); return true; } void ShowFoto::show() { // Remove Splashscreen. if (d->splash) { d->splash->finish(this); delete d->splash; d->splash = nullptr; } // Display application window. QMainWindow::show(); // Report errors from ICC repository path. KSharedConfig::Ptr config = KSharedConfig::openConfig(); if (!d->validIccPath) { QString message = i18n("

The ICC profile path seems to be invalid.

" "

If you want to set it now, select \"Yes\", otherwise " "select \"No\". In this case, \"Color Management\" feature " "will be disabled until you solve this issue

"); if (QMessageBox::warning(this, qApp->applicationName(), message, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { if (!setup(true)) { KConfigGroup group = config->group(QLatin1String("Color Management")); group.writeEntry(QLatin1String("EnableCM"), false); config->sync(); } } else { KConfigGroup group = config->group(QLatin1String("Color Management")); group.writeEntry(QLatin1String("EnableCM"), false); config->sync(); } } } void ShowFoto::slotOpenFile() { if (!d->thumbBar->currentInfo().isNull() && !promptUserSave(d->thumbBar->currentUrl())) { return; } QList urls = Digikam::ImageDialog::getImageURLs(this, d->lastOpenedDirectory); if (urls.count() > 1) { d->infoList.clear(); d->model->clearShowfotoItemInfos(); openUrls(urls); emit signalInfoList(d->infoList); slotOpenUrl(d->thumbBar->currentInfo()); toggleNavigation(1); } else if (urls.count() == 1) { d->infoList.clear(); d->model->clearShowfotoItemInfos(); openFolder(urls.first().adjusted(QUrl::RemoveFilename)); emit signalInfoList(d->infoList); slotOpenUrl(d->thumbBar->findItemByUrl(urls.first())); d->thumbBar->setCurrentUrl(urls.first()); } } void ShowFoto::slotOpenFolder() { if (!d->thumbBar->currentInfo().isNull() && !promptUserSave(d->thumbBar->currentUrl())) { return; } QUrl url = DFileDialog::getExistingDirectoryUrl(this, i18n("Open Images From Folder"), d->lastOpenedDirectory); if (!url.isEmpty()) { d->infoList.clear(); d->model->clearShowfotoItemInfos(); openFolder(url); emit signalInfoList(d->infoList); slotOpenUrl(d->thumbBar->currentInfo()); toggleNavigation(1); } } void ShowFoto::openUrls(const QList &urls) { if (urls.isEmpty()) { return; } ShowfotoItemInfo iteminfo; DMetadata meta; for (QList::const_iterator it = urls.constBegin() ; it != urls.constEnd() ; ++it) { QFileInfo fi((*it).toLocalFile()); iteminfo.name = fi.fileName(); iteminfo.mime = fi.suffix(); iteminfo.size = fi.size(); - iteminfo.url = QUrl::fromLocalFile(fi.filePath()); iteminfo.folder = fi.path(); + iteminfo.url = QUrl::fromLocalFile(fi.filePath()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) iteminfo.dtime = fi.birthTime(); #else iteminfo.dtime = fi.created(); #endif meta.load(fi.filePath()); iteminfo.ctime = meta.getItemDateTime(); iteminfo.width = meta.getItemDimensions().width(); iteminfo.height = meta.getItemDimensions().height(); iteminfo.photoInfo = meta.getPhotographInformation(); if (!d->infoList.contains(iteminfo)) d->infoList << iteminfo; } } void ShowFoto::openFolder(const QUrl& url) { if (!url.isValid() || !url.isLocalFile()) { return; } d->lastOpenedDirectory = url; // Parse image IO mime types registration to get files filter pattern. QString filter; QStringList mimeTypes = supportedImageMimeTypes(QIODevice::ReadOnly, filter); QString patterns = filter.toLower(); patterns.append (QLatin1Char(' ')); patterns.append (filter.toUpper()); qCDebug(DIGIKAM_SHOWFOTO_LOG) << "patterns=" << patterns; // Get all image files from directory. QDir dir(url.toLocalFile(), patterns); dir.setFilter(QDir::Files); if (!dir.exists()) { return; } QApplication::setOverrideCursor(Qt::WaitCursor); QFileInfoList fileinfolist = dir.entryInfoList(); if (fileinfolist.isEmpty()) { QApplication::restoreOverrideCursor(); return; } QFileInfoList::const_iterator fi; ShowfotoItemInfo iteminfo; DMetadata meta; // And open all items in image editor. for (fi = fileinfolist.constBegin() ; fi != fileinfolist.constEnd() ; ++fi) { iteminfo.name = (*fi).fileName(); iteminfo.mime = (*fi).suffix(); iteminfo.size = (*fi).size(); iteminfo.folder = (*fi).path(); iteminfo.url = QUrl::fromLocalFile((*fi).filePath()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) iteminfo.dtime = (*fi).birthTime(); #else iteminfo.dtime = (*fi).created(); #endif meta.load((*fi).filePath()); iteminfo.ctime = meta.getItemDateTime(); iteminfo.width = meta.getItemDimensions().width(); iteminfo.height = meta.getItemDimensions().height(); iteminfo.photoInfo = meta.getPhotographInformation(); if (!d->infoList.contains(iteminfo)) d->infoList << iteminfo; } QApplication::restoreOverrideCursor(); } void ShowFoto::slotDroppedUrls(const QList& droppedUrls, bool dropped) { if (droppedUrls.isEmpty()) { return; } - QList validUrls; - - foreach (const QUrl& url, droppedUrls) - { - if (url.isValid()) - { - validUrls << url; - } - } - QList imagesUrls; QList foldersUrls; - foreach (const QUrl& url, validUrls) + foreach (const QUrl& drop, droppedUrls) { - if (QMimeDatabase().mimeTypeForUrl(url).name().startsWith(QLatin1String("image"), - Qt::CaseInsensitive)) + if (drop.isValid()) { - imagesUrls << url; - } + QFileInfo info(drop.toLocalFile()); + QString ext(info.suffix().toUpper()); + QUrl url(QUrl::fromLocalFile(info.canonicalFilePath())); + + // Add extra check of the image extensions that are still + // unknown in older Qt versions or have an application mime type. + if (QMimeDatabase().mimeTypeForUrl(url).name().startsWith(QLatin1String("image/")) || + ext == QLatin1String("HEIC") || + ext == QLatin1String("KRA")) + { + imagesUrls << url; + } - if (QMimeDatabase().mimeTypeForUrl(url).name() == QLatin1String("inode/directory")) - { - foldersUrls << url; + if (info.isDir()) + { + foldersUrls << url; + } } } if (!imagesUrls.isEmpty()) { openUrls(imagesUrls); } if (!foldersUrls.isEmpty()) { foreach (const QUrl& fUrl, foldersUrls) { openFolder(fUrl); } } if (!d->infoList.isEmpty()) { if (!dropped && foldersUrls.isEmpty() && imagesUrls.count() == 1) { openFolder(imagesUrls.first().adjusted(QUrl::RemoveFilename)); d->model->clearShowfotoItemInfos(); emit signalInfoList(d->infoList); slotOpenUrl(d->thumbBar->findItemByUrl(imagesUrls.first())); d->thumbBar->setCurrentUrl(imagesUrls.first()); return; } d->model->clearShowfotoItemInfos(); emit signalInfoList(d->infoList); slotOpenUrl(d->thumbBar->currentInfo()); } else { QMessageBox::information(this, qApp->applicationName(), i18n("There is no dropped item to process.")); - qWarning(DIGIKAM_SHOWFOTO_LOG) << "infolist is empty.."; + qCWarning(DIGIKAM_SHOWFOTO_LOG) << "infolist is empty.."; } } void ShowFoto::slotOpenUrl(const ShowfotoItemInfo& info) { if (d->thumbBar->currentInfo().isNull()) { return; } QString localFile; if (info.url.isLocalFile()) { // file protocol. We do not need the network localFile = info.url.toLocalFile(); } else { QMessageBox::critical(this, i18n("Error Loading File"), i18n("Failed to load file: %1\n" "Remote file handling is not supported", info.url.fileName())); return; } d->currentLoadedUrl = info.url; m_canvas->load(localFile, m_IOFileSettings); //TODO : add preload here like in ImageWindow::slotLoadCurrent() ??? } void ShowFoto::slotShowfotoItemInfoActivated(const ShowfotoItemInfo& info) { if (!d->thumbBar->currentInfo().isNull() && !promptUserSave(d->currentLoadedUrl)) { d->thumbBar->setCurrentUrl(d->currentLoadedUrl); return; } slotOpenUrl(info); } Digikam::ThumbBarDock* ShowFoto::thumbBar() const { return d->thumbBarDock; } Digikam::Sidebar* ShowFoto::rightSideBar() const { return (dynamic_cast(d->rightSideBar)); } void ShowFoto::slotChanged() { QString mpixels; QSize dims(m_canvas->imageWidth(), m_canvas->imageHeight()); mpixels.setNum(dims.width()*dims.height()/1000000.0, 'f', 2); QString str = (!dims.isValid()) ? i18nc("unknown image dimensions", "Unknown") : i18nc("%1 width, %2 height, %3 mpixels", "%1x%2 (%3Mpx)", dims.width(),dims.height(),mpixels); m_resLabel->setAdjustedText(str); if (!d->thumbBar->currentInfo().isNull()) { if (d->thumbBar->currentUrl().isValid()) { QRect sel = m_canvas->getSelectedArea(); Digikam::DImg* const img = m_canvas->interface()->getImg(); d->rightSideBar->itemChanged(d->thumbBar->currentUrl(), sel, img); } } } void ShowFoto::slotUpdateItemInfo() { d->itemsNb = d->thumbBar->showfotoItemInfos().size(); int index = 0; QString text; if (d->itemsNb > 0) { index = 1; for (int i = 0 ; i < d->itemsNb ; ++i) { QUrl url = d->thumbBar->showfotoItemInfos().at(i).url; if (url.matches(d->thumbBar->currentUrl(), QUrl::None)) { break; } ++index; } text = i18nc(" ( of )", "%1 (%2 of %3)", d->thumbBar->currentInfo().name, index, d->itemsNb); setCaption(QDir::toNativeSeparators(d->thumbBar->currentUrl() .adjusted(QUrl::RemoveFilename).toLocalFile())); } else { text = QLatin1String(""); setCaption(QLatin1String("")); } m_nameLabel->setText(text); toggleNavigation(index); } void ShowFoto::slotFirst() { if (!d->thumbBar->currentInfo().isNull() && !promptUserSave(d->thumbBar->currentUrl())) { return; } d->thumbBar->toFirstIndex(); d->thumbBar->setCurrentInfo(d->thumbBar->showfotoItemInfos().first()); slotOpenUrl(d->thumbBar->showfotoItemInfos().first()); } void ShowFoto::slotLast() { if (!d->thumbBar->currentInfo().isNull() && !promptUserSave(d->thumbBar->currentUrl())) { return; } d->thumbBar->toLastIndex(); d->thumbBar->setCurrentInfo(d->thumbBar->showfotoItemInfos().last()); slotOpenUrl(d->thumbBar->showfotoItemInfos().last()); } void ShowFoto::slotForward() { if (!d->thumbBar->currentInfo().isNull() && !promptUserSave(d->thumbBar->currentUrl())) { return; } bool currentIsNull = d->thumbBar->currentInfo().isNull(); if (!currentIsNull) { d->thumbBar->toNextIndex(); slotOpenUrl(d->thumbBar->currentInfo()); } } void ShowFoto::slotBackward() { if (!d->thumbBar->currentInfo().isNull() && !promptUserSave(d->thumbBar->currentUrl())) { return; } bool currentIsNull = d->thumbBar->currentInfo().isNull(); if (!currentIsNull) { d->thumbBar->toPreviousIndex(); slotOpenUrl(d->thumbBar->currentInfo()); } } void ShowFoto::slotPrepareToLoad() { Digikam::EditorWindow::slotPrepareToLoad(); // Here we enable specific actions on showfoto. d->openFilesInFolderAction->setEnabled(true); d->fileOpenAction->setEnabled(true); } void ShowFoto::slotLoadingStarted(const QString& filename) { Digikam::EditorWindow::slotLoadingStarted(filename); // Here we disable specific actions on showfoto. d->openFilesInFolderAction->setEnabled(false); d->fileOpenAction->setEnabled(false); } void ShowFoto::slotLoadingFinished(const QString& filename, bool success) { Digikam::EditorWindow::slotLoadingFinished(filename, success); // Here we re-enable specific actions on showfoto. d->openFilesInFolderAction->setEnabled(true); d->fileOpenAction->setEnabled(true); } void ShowFoto::slotSavingStarted(const QString& filename) { Digikam::EditorWindow::slotSavingStarted(filename); // Here we disable specific actions on showfoto. d->openFilesInFolderAction->setEnabled(false); d->fileOpenAction->setEnabled(false); } void ShowFoto::moveFile() { /* * moveFile() -> moveLocalFile() -> movingSaveFileFinished() * | | * finishSaving(true) save...IsComplete() */ qCDebug(DIGIKAM_SHOWFOTO_LOG) << m_savingContext.destinationURL << m_savingContext.destinationURL.isLocalFile(); if (m_savingContext.destinationURL.isLocalFile()) { qCDebug(DIGIKAM_SHOWFOTO_LOG) << "moving a local file"; EditorWindow::moveFile(); } else { QMessageBox::critical(this, i18n("Error Saving File"), i18n("Failed to save file: %1", i18n("Remote file handling is not supported"))); } } void ShowFoto::finishSaving(bool success) { Digikam::EditorWindow::finishSaving(success); // Here we re-enable specific actions on showfoto. d->openFilesInFolderAction->setEnabled(true); d->fileOpenAction->setEnabled(true); } void ShowFoto::saveIsComplete() { Digikam::LoadingCacheInterface::putImage(m_savingContext.destinationURL.toLocalFile(), m_canvas->currentImage()); //d->thumbBar->invalidateThumb(d->currentItem); // Pop-up a message to bring user when save is done. Digikam::DNotificationWrapper(QLatin1String("editorsavefilecompleted"), i18n("Image saved successfully"), this, windowTitle()); resetOrigin(); } void ShowFoto::saveAsIsComplete() { resetOriginSwitchFile(); /* Digikam::LoadingCacheInterface::putImage(m_savingContext.destinationURL.toLocalFile(), m_canvas->currentImage()); // Add the file to the list of thumbbar images if it's not there already Digikam::ThumbBarItem* foundItem = d->thumbBar->findItemByUrl(m_savingContext.destinationURL); d->thumbBar->invalidateThumb(foundItem); qCDebug(DIGIKAM_SHOWFOTO_LOG) << wantedUrls; if (!foundItem) { foundItem = new Digikam::ThumbBarItem(d->thumbBar, m_savingContext.destinationURL); } // shortcut slotOpenUrl d->thumbBar->blockSignals(true); d->thumbBar->setSelected(foundItem); d->thumbBar->blockSignals(false); d->currentItem = foundItem; slotUpdateItemInfo(); // Pop-up a message to bring user when save is done. Digikam::DNotificationWrapper("editorsavefilecompleted", i18n("Image saved successfully"), this, windowTitle()); */ } void ShowFoto::saveVersionIsComplete() { } QUrl ShowFoto::saveDestinationUrl() { if (d->thumbBar->currentInfo().isNull()) { qCWarning(DIGIKAM_GENERAL_LOG) << "Cannot return the url of the image to save " << "because no image is selected."; return QUrl(); } return d->thumbBar->currentUrl(); } bool ShowFoto::save() { if (d->thumbBar->currentInfo().isNull()) { qCWarning(DIGIKAM_GENERAL_LOG) << "This should not happen"; return true; } startingSave(d->currentLoadedUrl); return true; } bool ShowFoto::saveAs() { if (d->thumbBar->currentInfo().isNull()) { qCWarning(DIGIKAM_GENERAL_LOG) << "This should not happen"; return false; } return (startingSaveAs(d->currentLoadedUrl)); } void ShowFoto::slotDeleteCurrentItem() { QUrl urlCurrent(d->thumbBar->currentUrl()); QString warnMsg(i18n("About to delete file \"%1\"\nAre you sure?", urlCurrent.fileName())); if (QMessageBox::warning(this, qApp->applicationName(), warnMsg, QMessageBox::Apply | QMessageBox::Abort) != QMessageBox::Apply) { return; } else { bool ret = QFile::remove(urlCurrent.toLocalFile()); if (!ret) { QMessageBox::critical(this, qApp->applicationName(), i18n("Cannot delete \"%1\"", urlCurrent.fileName())); return; } // No error, remove item in thumbbar. d->model->removeIndex(d->thumbBar->currentIndex()); // Disable menu actions and SideBar if no current image. d->itemsNb = d->thumbBar->showfotoItemInfos().size(); if (d->itemsNb == 0) { slotUpdateItemInfo(); toggleActions(false); m_canvas->load(QString(), m_IOFileSettings); emit signalNoCurrentItem(); } else { // If there is an image after the deleted one, make that selected. slotOpenUrl(d->thumbBar->currentInfo()); } } } void ShowFoto::slotRevert() { if (!promptUserSave(d->thumbBar->currentUrl())) { return; } m_canvas->slotRestore(); } bool ShowFoto::saveNewVersion() { return false; } bool ShowFoto::saveCurrentVersion() { return false; } bool ShowFoto::saveNewVersionAs() { return false; } bool ShowFoto::saveNewVersionInFormat(const QString&) { return false; } void ShowFoto::slotSetupMetadataFilters(int tab) { Setup::execMetadataFilters(this, tab+1); } void ShowFoto::slotAddedDropedItems(QDropEvent* e) { QList list = e->mimeData()->urls(); QList urls; foreach (const QUrl& url, list) { QFileInfo fi(url.toLocalFile()); if (fi.exists()) { urls.append(url); } } e->accept(); if (!urls.isEmpty()) { slotDroppedUrls(urls, true); } } void ShowFoto::slotFileWithDefaultApplication() { Digikam::DFileOperations::openFilesWithDefaultApplication(QList() << d->thumbBar->currentUrl()); } void ShowFoto::slotOpenWith(QAction* action) { openWith(d->thumbBar->currentUrl(), action); } DInfoInterface* ShowFoto::infoIface(DPluginAction* const) { DMetaInfoIface* const iface = new DMetaInfoIface(this, d->thumbBar->urls()); connect(iface, SIGNAL(signalItemChanged(QUrl)), this, SLOT(slotChanged())); connect(iface, SIGNAL(signalImportedImage(QUrl)), this, SLOT(slotImportedImagefromScanner(QUrl))); return iface; } } // namespace ShowFoto #include "moc_showfoto.cpp" diff --git a/core/tests/dimg/magickloader.cpp b/core/tests/dimg/magickloader.cpp index a8e00ebb21..2526779047 100644 --- a/core/tests/dimg/magickloader.cpp +++ b/core/tests/dimg/magickloader.cpp @@ -1,218 +1,229 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2017-09-24 * Description : Test ImageMagick loader to QImage. * * Copyright (C) 2019 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. * * ============================================================ */ // ImageMagick includes #include #if MagickLibVersion < 0x700 # include #endif using namespace Magick; using namespace MagickCore; // Qt includes #include #include #include #include #include #include #include #include #include #include #include /** Convert from QImage to IM::Image */ /*Image* toImage(QImage* const qimage) { qDebug() << "toImage:" << qimage->width() << qimage->height(); Image* const newImage = new Image(Magick::Geometry(qimage->width(), qimage->height()), Magick::ColorRGB(0.5, 0.2, 0.3)); newImage->modifyImage(); double scale = 1 / 256.0; Magick::Quantum* pixels = 0; Magick::ColorRGB mgc; for (int y = 0 ; y < qimage->height() ; ++y) { pixels = newImage->setPixels(0, y, newImage->columns(), 1); for (int x = 0 ; x < qimage->width() ; ++x) { QColor pix = qimage->pixel(x, y); mgc.red (scale * pix.red()); mgc.green(scale * pix.green()); mgc.blue (scale * pix.blue()); *pixels++ = mgc; } newImage->syncPixels(); } return newImage; } */ /** Convert pixel from IM::Image to QImage. */ // -- ImageMagick codecs to QImage -------------------------------------------------------- bool loadWithImageMagick(const QString& path, QImage& qimg) { qDebug() << "Try to load image with ImageMagick codecs"; try { Magick::Image image; image.read(path.toUtf8().constData()); qDebug() << "IM toQImage :" << image.columns() << image.rows(); qDebug() << "IM QuantumRange :" << QuantumRange; Blob* const pixelBlob = new Blob; image.write(pixelBlob, "BGRA", 8); qDebug() << "IM blob size :" << pixelBlob->length(); qimg = QImage((uchar*)pixelBlob->data(), image.columns(), image.rows(), QImage::Format_ARGB32); #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) qDebug() << "QImage data size:" << qimg.sizeInBytes(); #else qDebug() << "QImage data size:" << qimg.byteCount(); #endif if (qimg.isNull()) { return false; } } catch (Exception& error_) { qWarning() << "Cannot load [" << path << "] due to ImageMagick exception:" << error_.what(); qimg = QImage(); return false; } return true; } // --------------------------------------------------------------------------------------------------- int main(int argc, char** argv) { MagickCoreGenesis((char*)NULL, MagickFalse); ExceptionInfo ex; size_t n = 0; const MagickInfo** inflst = GetMagickInfoList("*", &n, &ex); + + if (!inflst) + { + qWarning() << "ImageMagick coders list is null!"; + return -1; + } + qDebug().noquote() << "Name :: Module :: Mime Type :: Mode :: Version :: Description"; for (uint i = 0 ; i < n ; ++i) { const MagickInfo* inf = inflst[i]; - QString mode; - if (inf->decoder) mode.append(QLatin1Char('R')); - else mode.append(QLatin1Char('-')); + if (inf) + { + QString mode; - if (inf->encoder) mode.append(QLatin1Char('W')); - else mode.append(QLatin1Char('-')); + if (inf->decoder) mode.append(QLatin1Char('R')); + else mode.append(QLatin1Char('-')); + + if (inf->encoder) mode.append(QLatin1Char('W')); + else mode.append(QLatin1Char('-')); #if (MagickLibVersion >= 0x69A && defined(magick_module)) - QString mod = QLatin1String(inf->magick_module); + QString mod = QLatin1String(inf->magick_module); #else - QString mod = QLatin1String(inf->module); + QString mod = QLatin1String(inf->module); #endif - QString mime = QMimeDatabase().mimeTypeForFile(QFileInfo(QString::fromLatin1("foo.%1").arg(mod))).name(); + QString mime = QMimeDatabase().mimeTypeForFile(QFileInfo(QString::fromLatin1("foo.%1").arg(mod))).name(); - if (mod != QLatin1String("DNG") && - mod != QLatin1String("JPEG") && - mod != QLatin1String("PNG") && - mod != QLatin1String("TIFF") && - mod != QLatin1String("JP2") && - mime.startsWith(QLatin1String("image/"))) - { - qDebug().noquote() - << QString::fromLatin1("%1").arg(QLatin1String(inf->name), 16) << "::" - << QString::fromLatin1("%1").arg(mod, 16) << "::" - << QString::fromLatin1("%1").arg(mime, 28) << "::" - << QString::fromLatin1("%1").arg(mode, 5) << "::" - << QString::fromLatin1("%1").arg(QLatin1String(inf->version), 28) << "::" - << QString::fromLatin1("%1").arg(QLatin1String(inf->description), 64); + if (mod != QLatin1String("DNG") && + mod != QLatin1String("JPEG") && + mod != QLatin1String("PNG") && + mod != QLatin1String("TIFF") && + mod != QLatin1String("JP2") && + mime.startsWith(QLatin1String("image/"))) + { + qDebug().noquote() + << QString::fromLatin1("%1").arg(QLatin1String(inf->name), 16) << "::" + << QString::fromLatin1("%1").arg(mod, 16) << "::" + << QString::fromLatin1("%1").arg(mime, 28) << "::" + << QString::fromLatin1("%1").arg(mode, 5) << "::" + << QString::fromLatin1("%1").arg(QLatin1String(inf->version), 28) << "::" + << QString::fromLatin1("%1").arg(QLatin1String(inf->description), 64); + } } } QApplication app(argc, argv); QStringList list; if (argc <= 1) { list = QFileDialog::getOpenFileNames(nullptr, QString::fromLatin1("Select Image Files to Load"), QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).first(), QLatin1String("Image Files (*.png *.jpg *.tif *.bmp *.gif *.xcf *.kra *.psd)")); } else { for (int i = 1 ; i < argc ; ++i) { list.append(QString::fromLocal8Bit(argv[i])); } } qDebug() << "Files to load:" << list; if (!list.isEmpty()) { foreach (const QString& path, list) { QImage qimg; bool ret = loadWithImageMagick(path, qimg); if (ret) { QLabel* const lbl = new QLabel; lbl->setPixmap(QPixmap::fromImage(qimg.scaled(512, 512, Qt::KeepAspectRatio))); lbl->show(); } else { qWarning() << "exit -1"; MagickCoreTerminus(); return -1; } } app.exec(); } MagickCoreTerminus(); return 0; } diff --git a/core/tests/dimg/testdimgloader.cpp b/core/tests/dimg/testdimgloader.cpp index 4090e75259..a2129280c6 100644 --- a/core/tests/dimg/testdimgloader.cpp +++ b/core/tests/dimg/testdimgloader.cpp @@ -1,73 +1,73 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-10-23 * Description : a command line tool to test DImg image loader * * Copyright (C) 2012-2019 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 #include #include // Local includes #include "metaengine.h" #include "dimg.h" #include "drawdecoding.h" #include "dpluginloader.h" using namespace Digikam; int main(int argc, char** argv) { QApplication app(argc, argv); if (argc != 2) { qDebug() << "testdimgloader - test DImg image loader"; qDebug() << "Usage: "; return -1; } MetaEngine::initializeExiv2(); DPluginLoader::instance()->init(); QFileInfo input(QString::fromUtf8(argv[1])); - QString outFilePath(input.baseName() + QLatin1String(".png")); + QString outFilePath(input.baseName() + QLatin1String(".heic")); DRawDecoderSettings settings; settings.halfSizeColorImage = false; settings.sixteenBitsImage = false; settings.RGBInterpolate4Colors = false; settings.RAWQuality = DRawDecoderSettings::BILINEAR; DImg img(input.filePath(), nullptr, DRawDecoding(settings)); if (!img.isNull()) { - img.save(outFilePath, QLatin1String("png")); + img.save(outFilePath, QLatin1String("heic")); } DPluginLoader::instance()->cleanUp(); MetaEngine::cleanupExiv2(); return 0; } diff --git a/core/utilities/geolocation/geoiface/CMakeLists.txt b/core/utilities/geolocation/geoiface/CMakeLists.txt index 1d4fb9b40d..a8098b58ff 100644 --- a/core/utilities/geolocation/geoiface/CMakeLists.txt +++ b/core/utilities/geolocation/geoiface/CMakeLists.txt @@ -1,103 +1,103 @@ # # Copyright (c) 2010-2019, 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($ $ $ $ $ $ $ $ - $ + ${MARBLE_INCLUDE_DIR} ) if(ENABLE_QWEBENGINE) include_directories($) else() include_directories($) endif() set(libgeoiface_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/backends/backend-rg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/backends/backend-geonames-rg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/backends/backend-geonamesUS-rg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/backends/backend-osm-rg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/backends/mapbackend.cpp ${CMAKE_CURRENT_SOURCE_DIR}/backends/backendmarble.cpp ${CMAKE_CURRENT_SOURCE_DIR}/backends/backendmarblelayer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/backends/backendgooglemaps.cpp ${CMAKE_CURRENT_SOURCE_DIR}/correlator/track_correlator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/correlator/track_correlator_thread.cpp ${CMAKE_CURRENT_SOURCE_DIR}/correlator/track_listmodel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/correlator/gpscorrelatorwidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/reversegeocoding/rginfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/reversegeocoding/rgtagmodel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/reversegeocoding/rgwidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/reversegeocoding/simpletreemodel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tracks/trackreader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tracks/trackmanager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/lookup/lookupaltitude.cpp ${CMAKE_CURRENT_SOURCE_DIR}/lookup/lookupaltitudegeonames.cpp ${CMAKE_CURRENT_SOURCE_DIR}/lookup/lookupfactory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dragdrop/mapdragdrophandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dragdrop/gpsitemlistdragdrophandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/items/gpsitemcontainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/items/gpsitemdelegate.cpp ${CMAKE_CURRENT_SOURCE_DIR}/items/gpsitemmodel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/items/gpsitemsortproxymodel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/items/gpsitemlist.cpp ${CMAKE_CURRENT_SOURCE_DIR}/items/gpsitemlistcontextmenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/geodragdrophandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/geocoordinates.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/geoifacecommon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/geomodelhelper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/groupstatecomputer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/gpscommon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/gpsundocommand.cpp ${CMAKE_CURRENT_SOURCE_DIR}/core/gpsgeoifacemodelhelper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bookmark/gpsbookmarkowner.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bookmark/gpsbookmarkmodelhelper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bookmark/bookmarknode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bookmark/bookmarksmenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bookmark/bookmarksmngr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bookmark/bookmarksdlg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tiles/abstractmarkertiler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tiles/itemmarkertiler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tiles/tilegrouper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tiles/tileindex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/widgets/mapwidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/widgets/placeholderwidget.cpp ) if(ENABLE_QWEBENGINE) set(libgeoiface_SRCS ${libgeoiface_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/widgets/htmlwidget_qwebengine.cpp ) else() set(libgeoiface_SRCS ${libgeoiface_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/widgets/htmlwidget.cpp ) endif() # Marble translations need explicit loading ecm_create_qm_loader(libgeoiface_SRCS marble_qt) add_library(geoiface_src OBJECT ${libgeoiface_SRCS}) diff --git a/project/bundles/macports/04-build-installer.sh b/project/bundles/macports/04-build-installer.sh index ee94598cfe..175743174b 100755 --- a/project/bundles/macports/04-build-installer.sh +++ b/project/bundles/macports/04-build-installer.sh @@ -1,560 +1,562 @@ #! /bin/bash # Script to bundle data using previously-built digiKam installation. # and create a PKG file with Packages application (http://s.sudre.free.fr/Software/Packages/about.html) # This script must be run as sudo # # Copyright (c) 2015, Shanti, # Copyright (c) 2015-2019, 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-installer.full.log) 2>&1 ################################################################################################# echo "04-build-installer.sh : build digiKam bundle PKG." 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 DKRELEASEID=`cat $ORIG_WD/data/RELEASEID.txt` ################################################################################################# # Build icons-set ressource echo -e "\n---------- Build icons-set ressource\n" cd $ORIG_WD/icon-rcc rm -f CMakeCache.txt > /dev/null cp -f $ORIG_WD/../../../bootstrap.macports . cmake -DCMAKE_INSTALL_PREFIX="$INSTALL_PREFIX" \ -DCMAKE_BUILD_TYPE=debug \ -DCMAKE_COLOR_MAKEFILE=ON \ -Wno-dev \ . make -j$CPU_CORES cd $ORIG_WD ################################################################################################# # Configurations # Directory where this script is located (default - current directory) BUILDDIR="$PWD" # Directory where Packages project files are located PROJECTDIR="$BUILDDIR/installer" # Staging area where files to be packaged will be copied TEMPROOT="$BUILDDIR/$INSTALL_PREFIX" # Applications to be launched directly by user (create launch scripts) KDE_MENU_APPS="\ digikam \ showfoto \ " # Paths to search for applications above KDE_APP_PATHS="\ Applications/KF5 \ " # Other apps - non-MacOS binaries & libraries to be included with required dylibs OTHER_APPS="\ Applications/KF5/digikam.app/Contents/MacOS/digikam \ Applications/KF5/showfoto.app/Contents/MacOS/showfoto \ lib/plugins/imageformats/*.so \ lib/plugins/digikam/bqm/*.so \ lib/plugins/digikam/generic/*.so \ lib/plugins/digikam/editor/*.so \ lib/plugins/digikam/dimg/*.so \ lib/mariadb/bin/mysql \ lib/mariadb/bin/mysqld \ lib/mariadb/bin/my_print_defaults \ lib/mariadb/bin/mysqladmin \ lib/mariadb/bin/mysqltest \ lib/mariadb/mysql/*.dylib \ lib/mariadb/plugin/*.so \ -lib/ImageMagick-6.9.9/modules-Q16/coders/*.so \ -lib/ImageMagick-6.9.9/modules-Q16/filters/*.so \ +lib/ImageMagick*/modules-Q16/coders/*.so \ +lib/ImageMagick*/modules-Q16/filters/*.so \ bin/kbuildsycoca5 \ libexec/qt5/plugins/imageformats/*.dylib \ libexec/qt5/plugins/sqldrivers/*.dylib \ libexec/qt5/plugins/printsupport/*.dylib \ libexec/qt5/plugins/platforms/*.dylib \ libexec/qt5/plugins/mediaservice/*.dylib \ libexec/qt5/plugins/iconengines/*.dylib \ libexec/qt5/plugins/audio/*.dylib \ libexec/qt5/plugins/position/*.dylib \ libexec/qt5/plugins/geoservices/*.dylib \ " #lib/sane/*.so \ binaries="$OTHER_APPS" # Additional Files/Directories - to be copied recursively but not checked for dependencies OTHER_DIRS="\ lib/plugins \ lib/libgphoto2 \ lib/libgphoto2_port \ lib/mariadb \ -lib/ImageMagick-6.9.9 \ +lib/ImageMagick* \ share/mariadb \ +share/ImageMagick* \ etc/xdg \ +etc/ImageMagick* \ " #etc/sane.d \ # Additional Data Directories - to be copied recursively OTHER_DATA="\ share/applications \ share/OpenCV \ share/k* \ share/lensfun \ share/mime \ Library/Application/ \ Marble.app/Contents/Resources/ \ Marble.app/Contents/MacOS/resources/ \ " # Packaging tool paths PACKAGESBUILD="/usr/local/bin/packagesbuild" RECURSIVE_LIBRARY_LISTER="$BUILDDIR/rll.py" echo "digiKam version: $DKRELEASEID" # ./installer sub-dir must be writable by root chmod 777 ${PROJECTDIR} ################################################################################################# # Check if Packages CLI tools are installed if [[ (! -f "$PACKAGESBUILD") ]] ; then echo "Packages CLI tool is not installed" echo "See http://s.sudre.free.fr/Software/Packages/about.html for details." exit 1 else echo "Check Packages CLI tool passed..." fi ################################################################################################# # Create temporary dir to build package contents if [ -d "$TEMPROOT" ] ; then echo "---------- Removing temporary packaging directory $TEMPROOT" rm -rf "$TEMPROOT" fi echo "Creating $TEMPROOT" mkdir -p "$TEMPROOT/Applications/digiKam" ################################################################################################# # Prepare applications for MacOS echo "---------- Preparing Applications for MacOS" for app in $KDE_MENU_APPS ; do echo " $app" # Look for application for searchpath in $KDE_APP_PATHS ; do # Copy the application if it is found (create directory if necessary) if [ -d "$INSTALL_PREFIX/$searchpath/$app.app" ] ; then echo " Found $app in $INSTALL_PATH/$searchpath" # Create destination directory if necessary and copy app if [ ! -d "$TEMPROOT/$searchpath" ] ; then echo " Creating $TEMPROOT/$searchpath" mkdir -p "$TEMPROOT/$searchpath" fi echo " Copying $app" cp -pr "$INSTALL_PREFIX/$searchpath/$app.app" "$TEMPROOT/$searchpath/" # Add executable to list of binaries for which we need to collect dependencies for binaries="$binaries $searchpath/$app.app/Contents/MacOS/$app" # If application is to be run by user, create Applescript launcher to # load kbuildsycoca5. Set DYLD_IMAGE_SUFFIX if built with debug variant if [[ $KDE_MENU_APPS == *"$app"* ]] ; then echo " Creating launcher script for $app" # Debug variant needs DYLD_IMAGE_SUFFIX="_debug set at runtime if [[ $DK_DEBUG = 1 ]] ; then DYLD_ENV_CMD="DYLD_IMAGE_SUFFIX=_debug " else DYLD_ENV_CMD="" fi # ------ Create application launcher script # Partially derived from https://discussions.apple.com/thread/3934912 and # http://stackoverflow.com/questions/16064957/how-to-check-in-applescript-if-an-app-is-running-without-launching-it-via-osa # and https://discussions.apple.com/thread/4059113 cat << EOF | osacompile -o "$TEMPROOT/Applications/digiKam/$app.app" #!/usr/bin/osascript log "Running $DYLD_ENV_CMD $INSTALL_PREFIX/bin/kbuildsycoca5" do shell script "$DYLD_ENV_CMD $INSTALL_PREFIX/bin/kbuildsycoca5" do shell script "$DYLD_ENV_CMD open $INSTALL_PREFIX/$searchpath/$app.app" EOF # ------ End application launcher script # Get application icon for launcher. If no icon file matches pattern app_SRCS.icns, grab the first icon if [ -f "$INSTALL_PREFIX/$searchpath/$app.app/Contents/Resources/${app}_SRCS.icns" ] ; then echo " Found icon for $app launcher" cp -p "$INSTALL_PREFIX/$searchpath/$app.app/Contents/Resources/${app}_SRCS.icns" "$TEMPROOT/Applications/digiKam/$app.app/Contents/Resources/applet.icns" else for icon in "$INSTALL_PREFIX/$searchpath/$app.app/"Contents/Resources/*.icns ; do echo " Using icon for $app launcher: $icon" cp -p "$icon" "$TEMPROOT/Applications/digiKam/$app.app/Contents/Resources/applet.icns" break done fi chmod 755 "$TEMPROOT/Applications/digiKam/$app.app" fi # Don't keep looking through search paths once we've found the app break fi done done ################################################################################################# # Collect dylib dependencies for all binaries, # then copy them to the staging area (creating directories as required) echo "---------- Collecting dependencies for applications, binaries, and libraries:" cd "$INSTALL_PREFIX" "$RECURSIVE_LIBRARY_LISTER" $binaries | sort -u | \ while read lib ; do lib="`echo $lib | sed "s:$INSTALL_PREFIX/::"`" if [ ! -e "$TEMPROOT/$lib" ] ; then dir="${lib%/*}" if [ ! -d "$TEMPROOT/$dir" ] ; then echo " Creating $TEMPROOT/$dir" mkdir -p "$TEMPROOT/$dir" fi echo " $lib" cp -aH "$INSTALL_PREFIX/$lib" "$TEMPROOT/$dir/" fi done ################################################################################################# # Copy non-binary files and directories, creating parent directories if needed echo "---------- Copying binary files..." for path in $OTHER_APPS ; do dir="${path%/*}" if [ ! -d "$TEMPROOT/$dir" ] ; then echo " Creating $TEMPROOT/$dir" mkdir -p "$TEMPROOT/$dir" fi echo " Copying $path" cp -a "$INSTALL_PREFIX/$path" "$TEMPROOT/$dir/" done echo "---------- Copying directory contents..." for path in $OTHER_DIRS ; do dir="${path%/*}" if [ ! -d "$TEMPROOT/$dir" ] ; then echo " Creating $TEMPROOT/$dir" mkdir -p "$TEMPROOT/$dir" fi echo " Copying $path" cp -a "$INSTALL_PREFIX/$path" "$TEMPROOT/$dir/" done echo "---------- Copying data files..." # Special case with data dirs. QStandardPaths::GenericDataLocation was patched everywhere # in source code by QStandardPaths::AppDataLocation for path in $OTHER_DATA ; do echo " Copying $path" cp -a "$INSTALL_PREFIX/$path" "$TEMPROOT/Applications/KF5/digikam.app/Contents/Resources/" done echo "---------- Copying i18n..." i18nprefix=$INSTALL_PREFIX/share/ cd $i18nprefix FILES=$(cat $ORIG_WD/logs/build-extralibs.full.log | grep "$INSTALL_PREFIX/share/locale/" | cut -d' ' -f3 | awk '{sub("'"$i18nprefix"'","")}1') for FILE in $FILES ; do rsync -R "./$FILE" "$TEMPROOT/Applications/KF5/digikam.app/Contents/Resources/" done FILES=$(cat $ORIG_WD/logs/build-digikam.full.log | grep "$INSTALL_PREFIX/share/locale/" | cut -d' ' -f3 | awk '{sub("'"$i18nprefix"'","")}1') for FILE in $FILES ; do rsync -R "./$FILE" "$TEMPROOT/Applications/KF5/digikam.app/Contents/Resources/" done # Showfoto resources dir must be merged with digiKam. cp -a "$TEMPROOT/Applications/KF5/showfoto.app/Contents/Resources/" "$TEMPROOT/Applications/KF5/digikam.app/Contents/Resources/" rm -rf "$TEMPROOT/Applications/KF5/showfoto.app/Contents/Resources" # A symbolic link to install path where is installed digiKam resources will be used for Showfoto. ln -s "$INSTALL_PREFIX/Applications/KF5/digikam.app/Contents/Resources" "$TEMPROOT/Applications/KF5/showfoto.app/Contents/Resources" cd "$ORIG_WD" ################################################################################################# # Move digiKam and KF5 run-time plugins to the right place cp -a $TEMPROOT/lib/plugins $TEMPROOT/libexec/qt5/ rm -rf $TEMPROOT/lib/plugins ################################################################################################# # Create package pre-install script echo "---------- Create package pre-install script" # Delete /Applications entries, delete existing installation cat << EOF > "$PROJECTDIR/preinstall" #!/bin/bash if [ -d /Applications/digiKam ] ; then echo "Removing digikam from Applications folder" rm -r /Applications/digiKam fi if [ -d "$INSTALL_PREFIX" ] ; then echo "Removing $INSTALL_PREFIX" rm -rf "$INSTALL_PREFIX" fi EOF # Pre-install script need to be executable chmod 755 "$PROJECTDIR/preinstall" ################################################################################################# # Create package post-install script echo "---------- Create package post-install script" # Creates Applications menu icons cat << EOF > "$PROJECTDIR/postinstall" #!/bin/bash [[ ! -d /Applications/digiKam ]] && mkdir "/Applications/digiKam" for app in $INSTALL_PREFIX/Applications/digiKam/*.app ; do ln -s "\$app" /Applications/digiKam/\${app##*/} done EOF # Post-install script need to be executable chmod 755 "$PROJECTDIR/postinstall" ################################################################################################# # Copy icons-set resource files. cp $ORIG_WD/icon-rcc/breeze.rcc $TEMPROOT/Applications/KF5/digikam.app/Contents/Resources/ cp $ORIG_WD/icon-rcc/breeze-dark.rcc $TEMPROOT/Applications/KF5/digikam.app/Contents/Resources/ ################################################################################################# # Cleanup symbols in binary files to free space. #echo -e "\n---------- Strip symbols in binary files\n" # #if [[ $DK_DEBUG = 1 ]] ; then # find $TEMPROOT -name "*.so" | grep -Ev '(digikam|showfoto|exiv2)' | xargs strip -SXx # find $TEMPROOT -name "*.dylib" | grep -Ev '(digikam|showfoto|exiv2)' | xargs strip -SXx #else # find $TEMPROOT -name "*.so" | xargs strip -SXx # find $TEMPROOT -name "*.dylib" | xargs strip -SXx #fi ################################################################################################# # Relocate binary files # For details, see these urls: # https://stackoverflow.com/questions/9263256/can-you-please-help-me-understand-how-mach-o-libraries-work-in-mac-os-x # https://matthew-brett.github.io/docosx/mac_runtime_link.html # Not yet finalized ! if [ ]; then echo -e "\n---------- Relocate binary files" # relocate dynamic libraries with rpath DYLIBFILES=(`find $TEMPROOT/lib -name "*.dylib"`) RelocateBinaries DYLIBFILES[@] # relocate library executables and system objects files with rpath. # This include all binary files with extension as all Qt libraries. LIBEXECFILES=(`find $TEMPROOT/libexec -type f -perm +ugo+x`) RelocateBinaries LIBEXECFILES[@] # relocate main executable with rpath. MAINFILES="\ $TEMPROOT/Applications/KF5/digikam.app/Contents/MacOS/digikam \ $TEMPROOT/Applications/KF5/showfoto.app/Contents/MacOS/showfoto \ $TEMPROOT/bin/kbuildsycoca5 \ " RelocateBinaries MAINFILES[@] for APP in $MAINFILES ; do install_name_tool -add_rpath @executable_path/.. $APP install_name_tool -add_rpath @executable_path/../.. $APP install_name_tool -add_rpath @executable_path/../../.. $APP install_name_tool -add_rpath @executable_path/../../../.. $APP install_name_tool -add_rpath @executable_path/../../../../.. $APP install_name_tool -add_rpath @executable_path/../../../../../.. $APP done fi ################################################################################################# # Build PKG file echo "---------- Create MacOS package for digiKam $DKRELEASEID" mkdir -p $ORIG_WD/bundle rm -f $ORIG_WD/bundle/* || true TARGET_INSTALLER=digiKam-$DKRELEASEID$DK_EPOCH-MacOS-x86-64$DEBUG_SUF.pkg TARGET_PKG_FILE=$BUILDDIR/bundle/$TARGET_INSTALLER echo -e "Target PKG file : $TARGET_PKG_FILE" $PACKAGESBUILD -v "$PROJECTDIR/digikam.pkgproj" --package-version "$DKRELEASEID" mv "$PROJECTDIR/build/digikam.pkg" "$TARGET_PKG_FILE" ################################################################################################# # Show resume information and future instructions to host PKG file to remote server echo -e "\n---------- Compute package checksums for digiKam $DKRELEASEID\n" > $TARGET_PKG_FILE.sum echo "File : $TARGET_PKG_FILE" >> $TARGET_PKG_FILE.sum echo -n "Size : " >> $TARGET_PKG_FILE.sum du -h "$TARGET_PKG_FILE" | { read first rest ; echo $first ; } >> $TARGET_PKG_FILE.sum echo -n "MD5 sum : " >> $TARGET_PKG_FILE.sum md5 -q "$TARGET_PKG_FILE" >> $TARGET_PKG_FILE.sum echo -n "SHA1 sum : " >> $TARGET_PKG_FILE.sum shasum -a1 "$TARGET_PKG_FILE" | { read first rest ; echo $first ; } >> $TARGET_PKG_FILE.sum echo -n "SHA256 sum : " >> $TARGET_PKG_FILE.sum shasum -a256 "$TARGET_PKG_FILE" | { read first rest ; echo $first ; } >> $TARGET_PKG_FILE.sum # Checksums to post on Phabricator at release time. shasum -a256 "$TARGET_PKG_FILE" > $BUILDDIR/bundle/sha256_release.sum if [[ $DK_SIGN = 1 ]] ; then cat ~/.gnupg/dkorg-gpg-pwd.txt | gpg --batch --yes --passphrase-fd 0 -sabv "$TARGET_PKG_FILE" mv -f $TARGET_PKG_FILE.asc $TARGET_PKG_FILE.sig echo "File : $TARGET_PKG_FILE.sig" >> $TARGET_PKG_FILE.sum echo -n "Size : " >> $TARGET_PKG_FILE.sum du -h "$TARGET_PKG_FILE.sig" | { read first rest ; echo $first ; } >> $TARGET_PKG_FILE.sum echo -n "MD5 sum : " >> $TARGET_PKG_FILE.sum md5 -q "$TARGET_PKG_FILE.sig" >> $TARGET_PKG_FILE.sum echo -n "SHA1 sum : " >> $TARGET_PKG_FILE.sum shasum -a1 "$TARGET_PKG_FILE.sig" | { read first rest ; echo $first ; } >> $TARGET_PKG_FILE.sum echo -n "SHA256 sum : " >> $TARGET_PKG_FILE.sum shasum -a256 "$TARGET_PKG_FILE.sig" | { read first rest ; echo $first ; } >> $TARGET_PKG_FILE.sum # Checksums to post on Phabricator at release time. shasum -a256 "$TARGET_PKG_FILE.sig" >> $BUILDDIR/bundle/sha256_release.sum fi cat $TARGET_PKG_FILE.sum if [[ $DK_UPLOAD = 1 ]] ; then echo -e "---------- Cleanup older bundle Package files from files.kde.org repository \n" ssh $DK_UPLOADURL rm -f $DK_UPLOADDIR*-MacOS-x86-64*.pkg* echo -e "---------- Upload new bundle Package files to files.kde.org repository \n" rsync -r -v --progress -e ssh $BUILDDIR/bundle/$TARGET_INSTALLER $DK_UPLOADURL:$DK_UPLOADDIR scp $BUILDDIR/bundle/$TARGET_INSTALLER.sum $DK_UPLOADURL:$DK_UPLOADDIR if [[ $DK_SIGN = 1 ]] ; then scp $BUILDDIR/bundle/$TARGET_INSTALLER.sig $DK_UPLOADURL:$DK_UPLOADDIR fi else echo -e "\n------------------------------------------------------------------" curl https://download.kde.org/README_UPLOAD echo -e "------------------------------------------------------------------\n" fi ################################################################################################# export PATH=$ORIG_PATH TerminateScript diff --git a/project/bundles/mxe/01-build-mxe.sh b/project/bundles/mxe/01-build-mxe.sh index 695f502108..7884fd0d7d 100755 --- a/project/bundles/mxe/01-build-mxe.sh +++ b/project/bundles/mxe/01-build-mxe.sh @@ -1,187 +1,187 @@ #! /bin/bash # Script to build a bundle MXE installation with all digiKam low level dependencies in a dedicated directory. # # Copyright (c) 2015-2019, 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 + 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_lensfun -- -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