diff --git a/CMakeLists.txt b/CMakeLists.txt index 53283fdab2..856954c9b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,647 +1,647 @@ # # Copyright (c) 2010-2018 by Gilles Caulier, # Copyright (c) 2015 by Veaceslav Munteanu, # Copyright (c) 2015 by Mohamed Anwer, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. project(digikam) message(STATUS "----------------------------------------------------------------------------------") message(STATUS "Starting CMake configuration for: ${PROJECT_NAME}") # ============================================================================== # Information to update before to release this package. # digiKam version set(DIGIKAM_MAJOR_VERSION "6") set(DIGIKAM_MINOR_VERSION "0") set(DIGIKAM_PATCH_VERSION "0") # Suffix to add at end of version string. Usual values are: # "-git" : alpha code unstable from git. Do not use in production # "-beta1" : beta1 release. # "-beta2" : beta2 release. # "-beta3" : beta3 release. # "-rc" : release candidate. # "" : final release. Can be used in production. set(DIGIKAM_SUFFIX_VERSION "-git") # ============================================================================== # Set env. variables accordingly. # NOTE: This string is used to set libdigikamcore and libdigikamdatabase SO version ID set(DIGIKAM_VERSION_SHORT "${DIGIKAM_MAJOR_VERSION}.${DIGIKAM_MINOR_VERSION}.${DIGIKAM_PATCH_VERSION}" ) set(DIGIKAM_VERSION_STRING "${DIGIKAM_VERSION_SHORT}${DIGIKAM_SUFFIX_VERSION}" ) # Core Database XML version # We must set this variable here at top level because it is used in both # libs/database/core and data/database # Version history: # 1 : Original database XML file, published in production. # 2 : 08-08-2014 : Fix Images.names field size (see bug #327646). # 3 : 05/11/2015 : Add Face DB schema. set(DBCORECONFIG_XML_VERSION "3") # ============================================================================== set(CMAKE_MIN_VERSION "3.0.0") set(ECM_MIN_VERSION "1.7.0") set(KF5_MIN_VERSION "5.1.0") set(QT_MIN_VERSION "5.6.0") set(KSANE_MIN_VERSION "5.0.0") set(LENSFUN_MIN_VERSION "0.2.6.0") set(EXIV2_MIN_VERSION "0.26") set(OPENCV_MIN_VERSION "3.1.0") set(QTAV_MIN_VERSION "1.12.0") set(CALENDAR_MIN_VERSION "4.81.0") # Calendar Core dependency set(AKONADI_MIN_VERSION "4.89.0") # Akonadi Contact dependency set(VKONTAKTE_MIN_VERSION "4.70.0") # VKontakte library dependency cmake_minimum_required(VERSION ${CMAKE_MIN_VERSION}) ############## ECM setup ###################### find_package(ECM ${ECM_MIN_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${digikam_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH}) # Cmake macros include(GenerateExportHeader) include(CheckFunctionExists) include(FeatureSummary) # ECM macros include(ECMOptionalAddSubdirectory) include(ECMAddTests) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(ECMGeneratePriFile) include(ECMSetupVersion) include(ECMInstallIcons) include(ECMAddAppIcon) include(ECMPoQmTools) # KDE macros include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings) include(KDECompilerSettings) # Local macros include(MacroUtils) include(MacroLocalLibs) include(MacroOpenCV) include(MacroJPEG) include(MacroBoolTo01) # ============================================================================== option(ENABLE_KFILEMETADATASUPPORT "Build digiKam with KDE files indexer support (default=OFF)" OFF) option(ENABLE_AKONADICONTACTSUPPORT "Build digiKam with KDE Mail Contacts support (default=OFF)" OFF) option(ENABLE_MEDIAPLAYER "Build digiKam with Media Player support (default=OFF)" OFF) option(ENABLE_DBUS "Build digiKam with DBUS support (default=ON)" ON) option(ENABLE_APPSTYLES "Build digiKam with support for changing the widget application style (default=OFF)" OFF) # Mysql support options (experimental): option(ENABLE_MYSQLSUPPORT "Build digiKam with MySQL dabatase support (default=ON)" ON) option(ENABLE_INTERNALMYSQL "Build digiKam with internal MySQL server executable (default=ON)" ON) # Debug options: option(ENABLE_DIGIKAM_MODELTEST "Enable ModelTest on some models for debugging (default=OFF)" OFF) ############## Find Packages ################### find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Concurrent Widgets Gui Sql Xml XmlPatterns PrintSupport WebKitWidgets Network ) find_package(Qt5 ${QT_MIN_VERSION} OPTIONAL_COMPONENTS DBus OpenGL ) if(ENABLE_DBUS) if(NOT Qt5DBus_FOUND) set(ENABLE_DBUS OFF) endif() endif() if(BUILD_TESTING) find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Test) endif() find_package(KF5 ${KF5_MIN_VERSION} COMPONENTS XmlGui CoreAddons Config Service WindowSystem Solid I18n ) find_package(KF5 ${KF5_MIN_VERSION} QUIET OPTIONAL_COMPONENTS KIO # For Desktop integration (Widgets only). IconThemes # For Desktop integration. FileMetaData # For KDE file indexer support. ThreadWeaver # For Panorama tool. NotifyConfig # KDE desktop application notify configuration. Notifications # KDE desktop notifications integration. ) find_package(KF5 ${AKONADI_MIN_VERSION} QUIET OPTIONAL_COMPONENTS AkonadiContact # For KDE Mail Contacts support. ) find_package(KF5 ${CALENDAR_MIN_VERSION} QUIET OPTIONAL_COMPONENTS CalendarCore # For Calendar tool. ) if ("${KF5CalendarCore_VERSION}" VERSION_GREATER 5.6.40) set(HAVE_KCALENDAR_QDATETIME TRUE) endif() if(ENABLE_AKONADICONTACTSUPPORT AND NOT KF5AkonadiContact_FOUND) set(ENABLE_AKONADICONTACTSUPPORT OFF) endif() if(ENABLE_KFILEMETADATASUPPORT AND NOT KF5FileMetaData_FOUND) set(ENABLE_KFILEMETADATASUPPORT OFF) endif() # Check if KIO have been compiled with KIOWidgets. digiKam only needs this one. if(KF5KIO_FOUND) get_target_property(KIOWidgets_INCLUDE_DIRS KF5::KIOWidgets INTERFACE_INCLUDE_DIRECTORIES) message(STATUS "KF5::KIOWidgets include dirs: ${KIOWidgets_INCLUDE_DIRS}") if(NOT KIOWidgets_INCLUDE_DIRS) message(STATUS "KF5::KIOWidgets not available in shared KIO library. KIO support disabled.") set(KF5KIO_FOUND FALSE) endif() endif() # ============================================================================== # Dependencies Rules # mandatory DETECT_JPEG() set(DIGIKAM_LIBJPEG_DIR libjpeg-${JPEG_LIB_VERSION}) find_package(TIFF) find_package(PNG) find_package(Boost) find_package(LCMS2) find_package(EXPAT) # For DNGWriter: XMP SDK need Expat library to compile. find_package(Threads) # For DNGWriter and LibRaw which needs native threads support. find_package(Exiv2 ${EXIV2_MIN_VERSION}) set_package_properties("Exiv2" PROPERTIES DESCRIPTION "Required to build digiKam" URL "http://www.exiv2.org" TYPE RECOMMENDED PURPOSE "Library to manage image metadata" ) # -- check Media player -------------------------------------------------------- -find_package(FFmpeg COMPONENTS AVCODEC AVFORMAT AVUTIL) +find_package(FFmpeg COMPONENTS AVCODEC AVFILTER AVFORMAT AVUTIL SWSCALE) find_package(QtAV) if(ENABLE_MEDIAPLAYER) - if(${AVCODEC_FOUND} AND ${AVFORMAT_FOUND} AND ${AVUTIL_FOUND}) + if(${AVCODEC_FOUND} AND ${AVFILTER_FOUND} AND ${AVFORMAT_FOUND} AND ${AVUTIL_FOUND} AND ${SWSCALE_FOUND}) include_directories(${FFMPEG_INCLUDE_DIRS}) else() set(ENABLE_MEDIAPLAYER OFF) set(FFMPEG_FOUND OFF) message(STATUS "ENABLE_MEDIAPLAYER option is enabled but FFMpeg cannot be found. Media player support is disabled.") endif() if(NOT ${QtAV_FOUND} OR ${QTAV_VERSION_STRING} VERSION_LESS ${QTAV_MIN_VERSION}) set(ENABLE_MEDIAPLAYER OFF) set(QtAV_FOUND OFF) message(STATUS "ENABLE_MEDIAPLAYER option is enabled but QtAV cannot be found. Media player support is disabled.") else() include_directories(${QTAV_INCLUDE_DIRS}) endif() if (${QtAV_FOUND} AND ${FFMPEG_FOUND}) message(STATUS "Media player support is enabled.") endif() endif() # -- check OpenCV -------------------------------------------------------------- DETECT_OPENCV(${OPENCV_MIN_VERSION} core objdetect imgproc imgcodecs) if(${OpenCV_FOUND}) if(${OpenCV_VERSION} VERSION_LESS 3.1.0) message(STATUS "OpenCV < 3.1.0 have been found. Please use a more recent version.") set(OpenCV_FOUND FALSE) endif() endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/app/utils/libopencv.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/app/utils/libopencv.h) # -- optionals ----------------------------------------------------------------- find_package(FLEX) # For Panorama tool. find_package(BISON) # For Panorama tool. find_package(LibXslt) # For HTMLGallery tool. find_package(LibXml2) # For HTMLGallery tool. DETECT_LIBKSANE(${KSANE_MIN_VERSION}) # For digital scanner device support. DETECT_LIBMEDIAWIKI(${KF5_MIN_VERSION}) # For Mediawiki webservice support. DETECT_LIBKVKONTAKTE(${VKONTAKTE_MIN_VERSION}) # For VKontakte webservice support. find_package(Marble) # For geolocation support. find_package(PkgConfig) find_package(Jasper) # For JPEG 2000 support. find_package(Eigen3) # For Refocus tool. find_package(OpenGL) # For Presentation tool. # For Monitor Profiles management with LCMS find_package(X11) if(X11_FOUND) find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE COMPONENTS X11Extras) set(HAVE_X11 TRUE) else() set(HAVE_X11 FALSE) endif() # decide if Presentation tool can be built with OpenGL if(OPENGL_FOUND AND OPENGL_GLU_FOUND AND Qt5OpenGL_FOUND) set(HAVE_OPENGL TRUE) elseif() set(HAVE_OPENGL FALSE) endif() # For LibRaw if(WIN32) find_library(WSOCK32_LIBRARY wsock32) find_library(WS2_32_LIBRARY ws2_32) endif() # -- To link under Solaris (see bug #274484) ----------------------------------- if(NOT WIN32) find_library(MATH_LIBRARY m) endif() if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD) find_library(KVM_LIBRARY kvm) endif() # ============================================================================== # More Optional Dependencies find_package(Doxygen) find_package(Lqr-1) # -- libgphoto2 rules ---------------------------------------------------------- if(NOT WIN32) find_package(Gphoto2) if(Gphoto2_FOUND) if("${GPHOTO2_VERSION_STRING}" VERSION_GREATER "2.4.0") set(VERSION_GPHOTO2 true) else() set(VERSION_GPHOTO2 false) endif() if("${GPHOTO2_VERSION_STRING}" VERSION_GREATER "2.5.0") set(VERSION_GPHOTO25 true) message(STATUS "libgphoto2 API version >= 2.5") else() set(VERSION_GPHOTO25 false) message(STATUS "libgphoto2 API version < 2.5") endif() if(VERSION_GPHOTO25) set(HAVE_GPHOTO25 1) else() set(HAVE_GPHOTO25 0) endif() endif() endif() # -- Check LensFun library for Lens auto-correction tool ----------------------- find_package(LensFun) if(LENSFUN_VERSION) message(STATUS "liblensfun: Found version ${LENSFUN_VERSION} (required: ${LENSFUN_MIN_VERSION})") if(${LENSFUN_VERSION} VERSION_LESS ${LENSFUN_MIN_VERSION}) set(LensFun_FOUND FALSE) endif() else() message(STATUS "liblensfun: Version information not found, your version is probably too old.") set(LensFun_FOUND FALSE) endif() # -- Check dependencies for Panorama tool -------------------------------------- if(FLEX_FOUND AND BISON_FOUND AND KF5ThreadWeaver_FOUND) set(HAVE_PANORAMA 1) else() set(HAVE_PANORAMA 0) endif() # -- Check dependencies for HTMLGallery tool ----------------------------------- if(LibXml2_FOUND AND LibXslt_FOUND) set(HAVE_HTMLGALLERY 1) else() set(HAVE_HTMLGALLERY 0) endif() # -- compilation options definitions ------------------------------------------- MACRO_BOOL_TO_01(KF5Sane_FOUND HAVE_KSANE) MACRO_BOOL_TO_01(KF5FileMetaData_FOUND HAVE_KFILEMETADATA) MACRO_BOOL_TO_01(KF5AkonadiContact_FOUND HAVE_AKONADICONTACT) MACRO_BOOL_TO_01(KF5CalendarCore_FOUND HAVE_KCALENDAR) MACRO_BOOL_TO_01(KF5Notifications_FOUND HAVE_KNOTIFICATIONS) MACRO_BOOL_TO_01(KF5NotifyConfig_FOUND HAVE_KNOTIFYCONFIG) MACRO_BOOL_TO_01(KF5KIO_FOUND HAVE_KIO) MACRO_BOOL_TO_01(KF5IconThemes_FOUND HAVE_KICONTHEMES) MACRO_BOOL_TO_01(KF5MediaWiki_FOUND HAVE_MEDIAWIKI) MACRO_BOOL_TO_01(KF5Vkontakte_FOUND HAVE_VKONTAKTE) MACRO_BOOL_TO_01(LensFun_FOUND HAVE_LENSFUN) MACRO_BOOL_TO_01(Lqr-1_FOUND HAVE_LIBLQR_1) MACRO_BOOL_TO_01(Gphoto2_FOUND HAVE_GPHOTO2) MACRO_BOOL_TO_01(Jasper_FOUND HAVE_JASPER) MACRO_BOOL_TO_01(Eigen3_FOUND HAVE_EIGEN3) MACRO_BOOL_TO_01(Marble_FOUND HAVE_MARBLE) MACRO_BOOL_TO_01(ENABLE_MYSQLSUPPORT HAVE_MYSQLSUPPORT) MACRO_BOOL_TO_01(ENABLE_INTERNALMYSQL HAVE_INTERNALMYSQL) MACRO_BOOL_TO_01(ENABLE_MEDIAPLAYER HAVE_MEDIAPLAYER) MACRO_BOOL_TO_01(ENABLE_DBUS HAVE_DBUS) MACRO_BOOL_TO_01(ENABLE_APPSTYLES HAVE_APPSTYLE_SUPPORT) # Whether to use Qt's scaling to downscale previews. Under MacOSX, Qt # can make use of the higher physical resolution of Retina # displays. However, it seems that other Qt renderers perform badly at # this, so disable. If other renderers switch to coarser logical # pixels, one probably needs this feature on these platforms as well. MACRO_BOOL_TO_01(APPLE USE_QT_SCALING) # Set config files accordingly with optional dependencies configure_file(app/utils/digikam_config.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/app/utils/digikam_config.h) # ============================================================================== # Log messages message(STATUS "") message(STATUS "----------------------------------------------------------------------------------") message(STATUS " digiKam ${DIGIKAM_VERSION_STRING} dependencies results ") message(STATUS "") PRINT_COMPONENT_COMPILE_STATUS("MySQL Database Support" ENABLE_MYSQLSUPPORT) PRINT_COMPONENT_COMPILE_STATUS("MySQL Internal Support" ENABLE_INTERNALMYSQL) PRINT_COMPONENT_COMPILE_STATUS("DBUS Support" ENABLE_DBUS) PRINT_COMPONENT_COMPILE_STATUS("App. Style Support" ENABLE_APPSTYLES) # ============================================================================== PRINT_LIBRARY_STATUS("libboostgraph" "http://www.boost.org/doc/libs" "(version >= 1.43.0)" Boost_FOUND) PRINT_LIBRARY_STATUS("libexiv2" "http://www.exiv2.org" "(version >= ${EXIV2_MIN_VERSION}" Exiv2_FOUND) PRINT_LIBRARY_STATUS("libexpat" "http://expat.sourceforge.net" "(version >= 2.0.0)" EXPAT_FOUND) PRINT_LIBRARY_STATUS("libjpeg" "http://www.ijg.org" "(version >= 6b)" JPEG_FOUND) PRINT_LIBRARY_STATUS("libkde" "http://www.kde.org" "(version >= ${KF5_MIN_VERSION})" KF5_FOUND) PRINT_LIBRARY_STATUS("liblcms" "http://www.littlecms.com" "(version >= 1.0.0)" LCMS2_FOUND) PRINT_LIBRARY_STATUS("libopencv" "http://opencv.willowgarage.com" "(version >= ${OPENCV_MIN_VERSION})" OpenCV_FOUND) PRINT_LIBRARY_STATUS("libpng" "http://www.libpng.org/pub/png/libpng.html" "(version >= 1.2.7)" PNG_FOUND) PRINT_LIBRARY_STATUS("libpthread" "http://www.gnu.org/software/hurd/libpthread.html" "(version >= 2.0.0)" CMAKE_USE_PTHREADS_INIT OR CMAKE_USE_WIN32_THREADS_INIT) PRINT_LIBRARY_STATUS("libqt" "http://code.qt.io/cgit/qt/" "(version >= ${QT_MIN_VERSION})" Qt5_FOUND) PRINT_LIBRARY_STATUS("libtiff" "http://www.remotesensing.org/libtiff" "(version >= 3.8.2)" TIFF_FOUND) # ============================================================================== PRINT_OPTIONAL_LIBRARY_STATUS("bison" "https://www.gnu.org/software/bison/" "(version >= 2.5.0)" "digiKam will be compiled without Panorama support." BISON_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("doxygen" "http://www.doxygen.org" "(version >= 1.8.0)" "digiKam will be compiled without API documentation building support." Doxygen_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("flex" "http://flex.sourceforge.net/" "(version >= 2.5.0)" "digiKam will be compiled without Panorama support." FLEX_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libakonadicontact" "https://cgit.kde.org/akonadi-contacts.git/about/" "(version >= ${AKONADI_MIN_VERSION})" "digiKam will be compiled without KDE desktop address book support." KF5AkonadiContact_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libeigen3" "http://eigen.tuxfamily.org" "(version >= 3.0.0)" "digiKam will be compiled without Refocus tool support." Eigen3_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libgphoto2" "http://www.gphoto.org" "(version >= 2.4.0)" "digiKam will be compiled without GPhoto2 camera drivers support." Gphoto2_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libjasper" "http://www.ece.uvic.ca/~mdadams/jasper" "(version >= 1.7.0)" "digiKam will be compiled without JPEG2000 support." Jasper_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libkcalcore" "https://cgit.kde.org/kcalcore.git/about/" "(version >= ${CALENDAR_MIN_VERSION})" "digiKam will be compiled without advanced calendar support." KF5CalendarCore_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libmediawiki" "https://cgit.kde.org/libmediawiki.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without libmediawiki support." KF5MediaWiki_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libkvkontakte" "https://cgit.kde.org/libkvkontatke.git/about/" "(version >= ${VKONTAKTE_MIN_VERSION})" "digiKam will be compiled without libkvkontakte support." KF5Vkontakte_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libkfilemetadata" "https://cgit.kde.org/kfilemetadata.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without KDE desktop file metadata support." KF5FileMetaData_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libkiconthemes" "https://cgit.kde.org/kiconthemes.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without KDE desktop icon themes support." KF5IconThemes_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libkio" "https://cgit.kde.org/kio.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without KDE desktop integration support." KF5KIO_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libknotifications" "https://cgit.kde.org/knotifyconfig.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without KDE desktop notifications support." KF5Notifications_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libknotifyconfig" "https://cgit.kde.org/knotifications.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without KDE desktop notify configuration support." KF5NotifyConfig_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libksane" "https://cgit.kde.org/libksane.git/about/" "(version >= ${KSANE_MIN_VERSION})" "digiKam will be compiled without flat scanners support." KF5Sane_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("liblensfun" "http://lensfun.sourceforge.net" "(version >= 0.2.6)" "digiKam will be compiled without Lens Auto Correction tool support." LensFun_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("liblqr-1" "http://liblqr.wikidot.com" "(version >= 0.4.1)" "digiKam will be compiled without Contents Aware Resizer tool support." Lqr-1_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libmarble" "https://cgit.kde.org/marble.git/about/" "(version >= 0.22.0)" "digiKam will be compiled without geolocation maps support." Marble_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libqtav" "http://www.qtav.org/" "(version >= ${QTAV_MIN_VERSION})" "digiKam will be compiled without Media Player support." QtAV_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libthreadweaver" "https://cgit.kde.org/threadweaver.git/about/" "(version >= ${KF5_MIN_VERSION})" "digiKam will be compiled without Panorama support." KF5ThreadWeaver_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libxml2" "http://xmlsoft.org" "(version >= 2.7.0)" "digiKam will be compiled without HTMLGallery support." LibXml2_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("libxslt" "http://xmlsoft.org/XSLT" "(version >= 1.1.0)" "digiKam will be compiled without HTMLGallery support." LibXslt_FOUND) PRINT_OPTIONAL_LIBRARY_STATUS("OpenGL" "http://www.mesa3d.org" "(version >= 11.0.0)" "digiKam will be compiled without OpenGL support." HAVE_OPENGL) # ============================================================================== if(Boost_FOUND AND Exiv2_FOUND AND EXPAT_FOUND AND JPEG_FOUND AND KF5_FOUND AND LCMS2_FOUND AND OpenCV_FOUND AND PNG_FOUND AND Qt5_FOUND AND TIFF_FOUND AND Threads_FOUND AND (Qt5Test_FOUND OR NOT BUILD_TESTING) AND (Qt5X11Extras_FOUND OR NOT HAVE_X11) AND (CMAKE_USE_PTHREADS_INIT OR CMAKE_USE_WIN32_THREADS_INIT) ) message(STATUS " digiKam can be compiled.................. YES") set(DIGIKAM_CAN_BE_COMPILED true) else() message(FATAL_ERROR " digiKam will be compiled.................. NO (See the README file for more details about dependencies)") set(DIGIKAM_CAN_BE_COMPILED false) endif() message(STATUS "----------------------------------------------------------------------------------") message(STATUS "") if(DIGIKAM_CAN_BE_COMPILED) # ========================================================================== # Create git version header # We only do this IF we are in a .git dir find_file(GIT_MARKER entries PATHS ${CMAKE_SOURCE_DIR}/.git) if(NOT GIT_MARKER) set(GIT_MARKER ${CMAKE_SOURCE_DIR}/CMakeLists.txt) # Dummy file endif() # Add a custom command to drive the git script whenever the git entries # file changes. configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/gitscript.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/gitscript.cmake" @ONLY) # Add a custom target to drive the custom command. add_custom_target(digikam-gitversion ALL COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/gitscript.cmake") # ========================================================================== # To prevent warnings from M$ compiler if(WIN32 AND MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-D_ATL_SECURE_NO_WARNINGS) add_definitions(-D_AFX_SECURE_NO_WARNINGS) endif() # ========================================================================== # Definitions rules # Remove pedantic GCC flag which generate a lots of warnings on the console # with qCDebug() while(CMAKE_CXX_FLAGS MATCHES "-pedantic") string(REPLACE "-pedantic" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") endwhile() # Remove Wsuggest-override GCC flag which generate a lots of compile warnings while(CMAKE_CXX_FLAGS MATCHES "-Wsuggest-override") string(REPLACE "-Wsuggest-override" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") endwhile() # Remove Wdate-time GCC flag which generate a lots of compile warnings while(CMAKE_CXX_FLAGS MATCHES "-Wdate-time") string(REPLACE "-Wdate-time" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") endwhile() # translations catalog add_definitions(-DTRANSLATION_DOMAIN=\"digikam\") # NOTE: with libpgf 6.11.24 OpenMP is not very well supported. We disable # it to be safe. See B.K.O #273765 for details. add_definitions(-DLIBPGF_DISABLE_OPENMP) # To force to declare as exported classes in all sub components. # This will only done near to library declaration in Cmake files. if(WIN32) add_definitions(-Ddigikamcore_EXPORTS) add_definitions(-Ddigikamdatabase_EXPORTS) endif() # Enable C++ Exceptions support, require by Greycstoration algorithm # (CImg.h) and PGF codec kde_enable_exceptions() # ========================================================================== # Includes rules # Recursively get all directories which contain header files set(DK_INCLUDES_ALL "") HEADER_DIRECTORIES(DK_LOCAL_INCLUDES_RAW) # This macro will set all paths which do not contain 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() endforeach() set(DK_LOCAL_INCLUDES ${DK_LOCAL_INCLUDES} libs/jpegutils/${DIGIKAM_LIBJPEG_DIR}) include_directories(${DK_LOCAL_INCLUDES}) # for config headers digikam_version.h gitversion.h digikam_config.h # digikam_dbconfig.h libopencv.h include_directories(${CMAKE_CURRENT_BINARY_DIR}/app/utils) include_directories(${OpenCV_INCLUDE_DIRS}) include_directories(${LIBMEDIAWIKI_INCLUDES}) include_directories(${LIBKVKONTAKTE_INCLUDES}) # ========================================================================== # Common targets parts set(CMAKE_POSITION_INDEPENDENT_CODE ON) add_subdirectory(data) add_subdirectory(libs) add_subdirectory(utilities) add_subdirectory(app) add_subdirectory(showfoto) if(BUILD_TESTING) add_subdirectory(tests) endif() endif() # ============================================================================== # API documentation generation if(Doxygen_FOUND) set(API_DIR ${CMAKE_BINARY_DIR}/api) set(SOURCE_DIR ${CMAKE_SOURCE_DIR}) set(DOXYFILE ${CMAKE_BINARY_DIR}/Doxyfile) set(WARNFILE ${CMAKE_BINARY_DIR}/doxygen-warn.log) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/Doxyfile.cmake.in ${DOXYFILE}) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${DOXYFILE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) endif() diff --git a/cmake/modules/FindFFmpeg.cmake b/cmake/modules/FindFFmpeg.cmake index eef6c1a9aa..bdc03f56f6 100644 --- a/cmake/modules/FindFFmpeg.cmake +++ b/cmake/modules/FindFFmpeg.cmake @@ -1,163 +1,163 @@ # # CMake script to find FFMPEG libraries # Once done this will define: # # FFMPEG_FOUND - system has ffmpeg # FFMPEG_INCLUDE_DIR - Include directory necessary for using the ffmpeg headers # FFMPEG_LIBRARIES - Link these to use ffmpeg # FFMPEG_DEFINITIONS - Compiler switches required for using ffmpeg # # For each of the components it will additionally set. # # - AVCODEC # - AVDEVICE # - AVFORMAT # - AVUTIL # - POSTPROCESS # - SWSCALE # # NOTE: default components are AVFORMAT, AVUTIL, AVCODEC. # # The following variables will be defined # # _FOUND - System has # _INCLUDE_DIRS - Include directory necessary for using the headers # _LIBRARIES - Link these to use # _DEFINITIONS - Compiler switches required for using # _VERSION - The components version # # Copyright (c) 2006, Matthias Kretz, # Copyright (c) 2008, Alexander Neundorf, # Copyright (c) 2011, Michael Jansen, # Copyright (c) 2016-2018, 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(FindPackageHandleStandardArgs) # The default components were taken from a survey over other FindFFMPEG.cmake files IF(NOT FFmpeg_FIND_COMPONENTS) - SET(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL) + SET(FFmpeg_FIND_COMPONENTS AVCODEC AVFILTER AVFORMAT AVUTIL SWSCALE) ENDIF() # Macro to marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present. # MACRO(set_component_found _component ) IF(${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS) # message(STATUS " - ${_component} found.") SET(${_component}_FOUND TRUE) ELSE() # message(STATUS " - ${_component} not found.") ENDIF() ENDMACRO() # Macro to checks for the given component by invoking pkgconfig and then looking up the libraries and # include directories. # MACRO(find_component _component _pkgconfig _library _header) IF(NOT WIN32) # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls FIND_PACKAGE(PkgConfig) IF(PKG_CONFIG_FOUND) pkg_check_modules(PC_${_component} ${_pkgconfig}) ENDIF() ENDIF() FIND_PATH(${_component}_INCLUDE_DIRS ${_header} HINTS ${PC_${_component}_INCLUDEDIR} ${PC_${_component}_INCLUDE_DIRS} PATH_SUFFIXES ffmpeg ) FIND_LIBRARY(${_component}_LIBRARIES NAMES ${_library} HINTS ${PC_${_component}_LIBDIR} ${PC_${_component}_LIBRARY_DIRS} ) SET(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.") SET(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.") SET_COMPONENT_FOUND(${_component}) MARK_AS_ADVANCED( ${_component}_INCLUDE_DIRS ${_component}_LIBRARIES ${_component}_DEFINITIONS ${_component}_VERSION) ENDMACRO() # Check for cached results. If there are skip the costly part. if(NOT FFMPEG_LIBRARIES) # Check for all possible component. FIND_COMPONENT(AVCODEC libavcodec avcodec libavcodec/avcodec.h) FIND_COMPONENT(AVFILTER libavfilter avfilter libavfilter/avfilter.h) FIND_COMPONENT(AVFORMAT libavformat avformat libavformat/avformat.h) FIND_COMPONENT(AVDEVICE libavdevice avdevice libavdevice/avdevice.h) FIND_COMPONENT(AVUTIL libavutil avutil libavutil/avutil.h) FIND_COMPONENT(SWSCALE libswscale swscale libswscale/swscale.h) FIND_COMPONENT(POSTPROC libpostproc postproc libpostproc/postprocess.h) # Check if the required components were found and add their stuff to the FFMPEG_* vars. FOREACH(_component ${FFmpeg_FIND_COMPONENTS}) IF (${_component}_FOUND) # message(STATUS "Required component ${_component} present.") SET(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES}) SET(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS}) LIST(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS}) ELSE() # message(STATUS "Required component ${_component} missing.") ENDIF() ENDFOREACH() # Build the include path with duplicates removed. IF(FFMPEG_INCLUDE_DIRS) LIST(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) ENDIF() # cache the vars. SET(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE) SET(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE) SET(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE) MARK_AS_ADVANCED(FFMPEG_INCLUDE_DIRS FFMPEG_LIBRARIES FFMPEG_DEFINITIONS) ENDIF() # Now set the noncached _FOUND vars for the components. -FOREACH(_component AVCODEC AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWSCALE) +FOREACH(_component AVCODEC AVDEVICE AVFILTER AVFORMAT AVUTIL POSTPROCESS SWSCALE) SET_COMPONENT_FOUND(${_component}) ENDFOREACH() # Compile the list of required vars SET(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS) FOREACH(_component ${FFmpeg_FIND_COMPONENTS}) LIST(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS) ENDFOREACH() # Give a nice error message if some of the required vars are missing. FIND_PACKAGE_HANDLE_STANDARD_ARGS(FFmpeg DEFAULT_MSG ${_FFmpeg_REQUIRED_VARS}) MESSAGE(STATUS "FFMPEG_FOUND = ${FFMPEG_FOUND}") MESSAGE(STATUS "FFMPEG_INCLUDE_DIRS = ${FFMPEG_INCLUDE_DIRS}") MESSAGE(STATUS "FFMPEG_LIBRARIES = ${FFMPEG_LIBRARIES}") MESSAGE(STATUS "FFMPEG_DEFINITIONS = ${FFMPEG_DEFINITIONS}") diff --git a/libs/threadimageio/CMakeLists.txt b/libs/threadimageio/CMakeLists.txt index 8c8e5f8cf3..8e0f1059dc 100644 --- a/libs/threadimageio/CMakeLists.txt +++ b/libs/threadimageio/CMakeLists.txt @@ -1,56 +1,59 @@ # # Copyright (c) 2010-2018 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. if (POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif (POLICY CMP0063) set(libthreadimageio_SRCS dfileoperations.cpp filereadwritelock.cpp loadsavethread.cpp managedloadsavethread.cpp sharedloadsavethread.cpp loadingdescription.cpp loadingcache.cpp loadingcacheinterface.cpp loadsavetask.cpp previewloadthread.cpp previewtask.cpp previewsettings.cpp thumbnailbasic.cpp thumbnailcreator.cpp thumbnailloadthread.cpp thumbnailtask.cpp thumbnailsize.cpp ) if(ENABLE_MEDIAPLAYER) set(libthreadimageio_SRCS ${libthreadimageio_SRCS} - videothumbnailer.cpp + ffmpegthumbnailer/filmstripfilter.cpp + ffmpegthumbnailer/moviedecoder.cpp + ffmpegthumbnailer/imagewriter.cpp + ffmpegthumbnailer/videothumbnailer.cpp videothumbnailerjob.cpp ) endif() include_directories( $ $ $ $ $ $ $ ) if(ENABLE_DBUS) include_directories($) endif() add_library(threadimageio_src OBJECT ${libthreadimageio_SRCS}) diff --git a/libs/threadimageio/ffmpegthumbnailer/filmstrip.h b/libs/threadimageio/ffmpegthumbnailer/filmstrip.h new file mode 100644 index 0000000000..12c6050ea4 --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/filmstrip.h @@ -0,0 +1,575 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef FILMSTRIP_H +#define FILMSTRIP_H +#include + +static const quint32 SMALLEST_FILM_STRIP_WIDTH = 4; + +static const quint8 filmStrip4[4 * 4 * 3] = { + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0kkk\207\207\207777\0\0\0\237\237\237\303\303" + "\303RRR\0\0\0\0\0\0\0\0\0\0\0" +}; + +static const quint8 filmStrip8[8 * 8 * 3] = { + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\40\40\40""777::::::444\1\1\1\0\0\0\2" + "\2\2\205\205\205\320\320\320\333\333\333\333\333\333\313\313\313\32\32\32" + "\0\0\0\2\2\2\236\236\236\360\360\360\373\373\373\373\373\373\353\353\353" + "\37\37\37\0\0\0\0\0\0FFFsssyyyyyynnn\2\2\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" +}; + +static const quint8 filmStrip16[16 * 16 * 3] = { + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\11\11\11\14\14\14" + "\15\15\15\15\15\15\15\15\15\15\15\15\15\15\15\15\15\15\4\4\4\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0""888YYYrrr|||\200\200\200\200\200\200\200\200\200" + "\200\200\200zzzmmm\23\23\23\0\0\0\0\0\0\0\0\0\0\0\0\11\11\11YYY\214\214\214" + "\257\257\257\276\276\276\302\302\302\302\302\302\302\302\302\301\301\301" + "\273\273\273\250\250\250@@@\0\0\0\0\0\0\0\0\0\0\0\0\14\14\14qqq\257\257\257" + "\326\326\326\347\347\347\353\353\353\354\354\354\354\354\354\353\353\353" + "\344\344\344\317\317\317PPP\0\0\0\0\0\0\0\0\0\0\0\0\15\15\15{{{\274\274\274" + "\345\345\345\365\365\365\372\372\372\373\373\373\373\373\373\371\371\371" + "\363\363\363\335\335\335VVV\0\0\0\0\0\0\0\0\0\0\0\0\14\14\14xxx\270\270\270" + "\340\340\340\361\361\361\365\365\365\366\366\366\366\366\366\365\365\365" + "\356\356\356\331\331\331UUU\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1ggg\240\240\240" + "\306\306\306\326\326\326\332\332\332\334\334\334\334\334\334\332\332\332" + "\324\324\324\277\277\277\"\"\"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\32\32" + "\32\40\40\40###$$$$$$$$$$$$###\12\12\12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" +}; + +static const quint8 filmStrip32[32 * 32 * 3] = { + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\13\13\27\27\27" + "\34\34\34\40\40\40\"\"\"$$$%%%%%%&&&&&&&&&&&&&&&&&&%%%%%%###\35\35\35\3\3" + "\3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\34\34\34""666EEESSS]]]eeeiiilllmmmooonnnnnnnnnnnnnnnmmmkkkgggaaaXXX\15" + "\15\15\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\13\13" + "\13""666JJJ]]]nnn|||\205\205\205\213\213\213\217\217\217\220\220\220\221" + "\221\221\221\221\221\221\221\221\221\221\221\222\222\222\221\221\221\220" + "\220\220\215\215\215\210\210\210\201\201\201vvvXXX\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\27\27\27EEE]]]uuu\212\212\212\232" + "\232\232\245\245\245\254\254\254\260\260\260\262\262\262\263\263\263\263" + "\263\263\263\263\263\263\263\263\263\263\263\262\262\262\261\261\261\256" + "\256\256\252\252\252\241\241\241\222\222\222\200\200\200\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\34\34\34RRRnnn\212\212\212\242" + "\242\242\264\264\264\300\300\300\310\310\310\314\314\314\316\316\316\317" + "\317\317\317\317\317\317\317\317\317\317\317\317\317\317\317\317\317\315" + "\315\315\312\312\312\305\305\305\273\273\273\254\254\254\227\227\227\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\40\40\40\\\\\\|" + "||\232\232\232\264\264\264\307\307\307\324\324\324\334\334\334\341\341\341" + "\342\342\342\343\343\343\344\344\344\344\344\344\344\344\344\344\344\344" + "\343\343\343\342\342\342\337\337\337\331\331\331\317\317\317\277\277\277" + "\250\250\250\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\"\"\"ddd\206\206\206\245\245\245\300\300\300\324\324\324\342\342\342\352" + "\352\352\356\356\356\360\360\360\361\361\361\361\361\361\361\361\361\361" + "\361\361\361\361\361\361\361\361\360\360\360\354\354\354\346\346\346\334" + "\334\334\313\313\313\264\264\264\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0$$$hhh\212\212\212\253\253\253\307\307\307\333\333\333" + "\351\351\351\360\360\360\365\365\365\367\367\367\370\370\370\370\370\370" + "\370\370\370\370\370\370\370\370\370\367\367\367\366\366\366\363\363\363" + "\355\355\355\343\343\343\322\322\322\272\272\272\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0$$$iii\214\214\214\255\255\255\311\311" + "\311\336\336\336\353\353\353\363\363\363\370\370\370\372\372\372\373\373" + "\373\373\373\373\373\373\373\373\373\373\373\373\373\372\372\372\371\371" + "\371\366\366\366\360\360\360\345\345\345\324\324\324\274\274\274\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0$$$hhh\212\212\212\253" + "\253\253\307\307\307\333\333\333\351\351\351\360\360\360\365\365\365\367" + "\367\367\370\370\370\370\370\370\370\370\370\370\370\370\370\370\370\367" + "\367\367\366\366\366\363\363\363\355\355\355\343\343\343\322\322\322\272" + "\272\272\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\"\"" + "\"ddd\206\206\206\245\245\245\300\300\300\324\324\324\342\342\342\352\352" + "\352\356\356\356\360\360\360\361\361\361\361\361\361\361\361\361\361\361" + "\361\361\361\361\361\361\361\360\360\360\354\354\354\346\346\346\334\334" + "\334\313\313\313\264\264\264\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\23\23\23\\\\\\|||\232\232\232\264\264\264\310\310\310\324" + "\324\324\334\334\334\341\341\341\342\342\342\343\343\343\344\344\344\344" + "\344\344\344\344\344\344\344\344\343\343\343\342\342\342\337\337\337\331" + "\331\331\317\317\317\277\277\277\222\222\222\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0;;;nnn\212\212\212\242\242\242\264" + "\264\264\301\301\301\310\310\310\314\314\314\316\316\316\317\317\317\320" + "\320\320\320\320\320\320\320\320\320\320\320\317\317\317\315\315\315\313" + "\313\313\305\305\305\273\273\273\254\254\254\33\33\33\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\33\33\33:::EEEMMMSS" + "SVVVXXXYYYYYYZZZZZZZZZZZZYYYXXXWWWUUUFFF\10\10\10\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" +}; + +static const quint8 filmStrip64[64 * 64 * 3] = { + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\20\20\20\"\"" + "\"///666;;;@@@DDDHHHKKKMMMOOOQQQRRRRRRSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTT" + "TTTTTSSSSSSRRRRRRQQQOOOMMMHHH999\36\36\36\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3\3\3\36\36\36...444;;;BB" + "BHHHNNNRRRWWWZZZ]]]___aaabbbcccddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeedd" + "ddddcccbbbaaa___]]]ZZZWWWRRR===\10\10\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\36\36\36///777???GGGNNNVVV\\\\\\bbbgg" + "gkkknnnppprrrtttuuuvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvvvvvvuuutttrr" + "rqqqnnnkkkgggbbb\\\\\\CCC\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\20\20\20...666@@@IIIRRR[[[cccjjjqqqvvv{{{~~~\201\201\201\203" + "\203\203\205\205\205\206\206\206\207\207\207\207\207\207\210\210\210\210" + "\210\210\210\210\210\210\210\210\210\210\210\210\210\210\210\210\210\210" + "\210\210\210\210\210\210\210\210\210\210\210\207\207\207\207\207\207\206" + "\206\206\205\205\205\203\203\203\201\201\201~~~{{{vvvqqqjjjccc)))\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\"\"\"444>>>IIISSS^^^hhhqq" + "qyyy\200\200\200\206\206\206\213\213\213\217\217\217\222\222\222\225\225" + "\225\226\226\226\230\230\230\230\230\230\231\231\231\231\231\231\232\232" + "\232\232\232\232\232\232\232\232\232\232\232\232\232\232\232\232\232\232" + "\232\232\232\232\232\232\232\231\231\231\231\231\231\231\231\231\230\230" + "\230\226\226\226\225\225\225\222\222\222\217\217\217\213\213\213\206\206" + "\206\200\200\200yyyqqqRRR\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0///;;;GGGRRR^^^iiittt~~~\207\207\207\217\217\217\226\226\226\233\233" + "\233\237\237\237\243\243\243\245\245\245\247\247\247\251\251\251\251\251" + "\251\252\252\252\252\252\252\253\253\253\253\253\253\253\253\253\253\253" + "\253\253\253\253\253\253\253\253\253\253\253\253\253\253\253\253\252\252" + "\252\252\252\252\251\251\251\251\251\251\247\247\247\245\245\245\243\243" + "\243\240\240\240\233\233\233\226\226\226\217\217\217\207\207\207~~~ooo\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""666BBBNNN[[[hhhttt" + "\200\200\200\213\213\213\224\224\224\235\235\235\244\244\244\251\251\251" + "\256\256\256\262\262\262\264\264\264\266\266\266\267\267\267\270\270\270" + "\271\271\271\271\271\271\272\272\272\272\272\272\272\272\272\272\272\272" + "\272\272\272\272\272\272\272\272\272\272\272\272\272\272\272\272\272\272" + "\271\271\271\270\270\270\270\270\270\266\266\266\264\264\264\262\262\262" + "\256\256\256\252\252\252\244\244\244\235\235\235\224\224\224\213\213\213" + "\200\200\200\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0;;;HHH" + "UUUcccqqq~~~\213\213\213\226\226\226\241\241\241\251\251\251\261\261\261" + "\267\267\267\274\274\274\277\277\277\302\302\302\304\304\304\305\305\305" + "\306\306\306\307\307\307\307\307\307\310\310\310\310\310\310\310\310\310" + "\310\310\310\310\310\310\310\310\310\310\310\310\310\310\310\310\310\310" + "\310\310\310\307\307\307\306\306\306\306\306\306\304\304\304\302\302\302" + "\277\277\277\274\274\274\267\267\267\261\261\261\251\251\251\241\241\241" + "\226\226\226\213\213\213\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0@@@MMM\\\\\\jjjyyy\207\207\207\225\225\225\241\241\241\254\254\254" + "\265\265\265\275\275\275\303\303\303\310\310\310\314\314\314\317\317\317" + "\321\321\321\322\322\322\323\323\323\324\324\324\324\324\324\325\325\325" + "\325\325\325\325\325\325\325\325\325\325\325\325\325\325\325\325\325\325" + "\325\325\325\325\325\325\325\325\325\324\324\324\323\323\323\322\322\322" + "\321\321\321\317\317\317\314\314\314\310\310\310\303\303\303\275\275\275" + "\265\265\265\254\254\254\241\241\241\225\225\225\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0DDDRRRaaaqqq\200\200\200\217\217\217\235\235" + "\235\252\252\252\265\265\265\276\276\276\307\307\307\315\315\315\322\322" + "\322\326\326\326\331\331\331\333\333\333\334\334\334\335\335\335\336\336" + "\336\336\336\336\337\337\337\337\337\337\337\337\337\337\337\337\337\337" + "\337\337\337\337\337\337\337\337\337\337\337\337\337\337\337\337\336\336" + "\336\335\335\335\334\334\334\333\333\333\331\331\331\326\326\326\322\322" + "\322\315\315\315\307\307\307\277\277\277\265\265\265\252\252\252\235\235" + "\235\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0HHHWWWfffvvv\206" + "\206\206\226\226\226\244\244\244\261\261\261\275\275\275\306\306\306\317" + "\317\317\325\325\325\332\332\332\336\336\336\341\341\341\343\343\343\345" + "\345\345\345\345\345\346\346\346\347\347\347\347\347\347\347\347\347\347" + "\347\347\347\347\347\347\347\347\347\347\347\347\347\347\347\347\347\347" + "\347\347\347\347\347\346\346\346\346\346\346\345\345\345\343\343\343\341" + "\341\341\336\336\336\332\332\332\325\325\325\317\317\317\306\306\306\275" + "\275\275\261\261\261\244\244\244\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0KKKZZZjjj{{{\213\213\213\233\233\233\252\252\252\267\267\267" + "\303\303\303\315\315\315\325\325\325\334\334\334\341\341\341\345\345\345" + "\350\350\350\352\352\352\354\354\354\354\354\354\355\355\355\356\356\356" + "\356\356\356\356\356\356\356\356\356\356\356\356\356\356\356\356\356\356" + "\356\356\356\356\356\356\356\356\356\356\356\356\355\355\355\355\355\355" + "\354\354\354\352\352\352\350\350\350\345\345\345\341\341\341\334\334\334" + "\325\325\325\315\315\315\303\303\303\267\267\267\252\252\252\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0MMM]]]mmm~~~\217\217\217\237\237" + "\237\256\256\256\274\274\274\310\310\310\322\322\322\332\332\332\341\341" + "\341\346\346\346\352\352\352\355\355\355\357\357\357\361\361\361\362\362" + "\362\362\362\362\363\363\363\363\363\363\363\363\363\363\363\363\363\363" + "\363\363\363\363\363\363\363\363\363\363\363\363\363\363\363\363\363\363" + "\363\362\362\362\362\362\362\361\361\361\357\357\357\355\355\355\352\352" + "\352\346\346\346\341\341\341\332\332\332\322\322\322\310\310\310\274\274" + "\274\256\256\256\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0NN" + "N^^^ooo\200\200\200\221\221\221\242\242\242\261\261\261\277\277\277\313\313" + "\313\325\325\325\335\335\335\344\344\344\351\351\351\355\355\355\360\360" + "\360\362\362\362\364\364\364\365\365\365\365\365\365\366\366\366\366\366" + "\366\366\366\366\366\366\366\366\366\366\366\366\366\366\366\366\366\366" + "\366\366\366\366\366\366\366\366\366\366\365\365\365\365\365\365\364\364" + "\364\362\362\362\360\360\360\355\355\355\351\351\351\344\344\344\335\335" + "\335\325\325\325\313\313\313\277\277\277\261\261\261\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0PPP```qqq\202\202\202\223\223\223\244\244" + "\244\263\263\263\301\301\301\315\315\315\327\327\327\340\340\340\346\346" + "\346\354\354\354\357\357\357\362\362\362\364\364\364\366\366\366\367\367" + "\367\367\367\367\370\370\370\370\370\370\370\370\370\370\370\370\370\370" + "\370\370\370\370\370\370\370\370\370\370\370\370\370\370\370\370\370\370" + "\370\367\367\367\367\367\367\366\366\366\364\364\364\362\362\362\360\360" + "\360\354\354\354\346\346\346\340\340\340\327\327\327\315\315\315\301\301" + "\301\263\263\263\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0PP" + "P```qqq\203\203\203\224\224\224\245\245\245\264\264\264\302\302\302\316\316" + "\316\331\331\331\341\341\341\350\350\350\355\355\355\361\361\361\364\364" + "\364\366\366\366\370\370\370\371\371\371\371\371\371\372\372\372\372\372" + "\372\372\372\372\372\372\372\372\372\372\372\372\372\372\372\372\372\372" + "\372\372\372\372\372\372\372\372\372\372\371\371\371\371\371\371\370\370" + "\370\366\366\366\364\364\364\361\361\361\355\355\355\350\350\350\341\341" + "\341\331\331\331\316\316\316\302\302\302\264\264\264\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0PPP```qqq\203\203\203\224\224\224\245\245" + "\245\264\264\264\302\302\302\316\316\316\331\331\331\341\341\341\350\350" + "\350\355\355\355\361\361\361\364\364\364\366\366\366\370\370\370\371\371" + "\371\371\371\371\372\372\372\372\372\372\372\372\372\372\372\372\372\372" + "\372\372\372\372\372\372\372\372\372\372\372\372\372\372\372\372\372\372" + "\372\371\371\371\371\371\371\370\370\370\366\366\366\364\364\364\361\361" + "\361\355\355\355\350\350\350\341\341\341\331\331\331\316\316\316\302\302" + "\302\264\264\264\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0PP" + "P```qqq\202\202\202\223\223\223\244\244\244\263\263\263\301\301\301\315\315" + "\315\327\327\327\340\340\340\346\346\346\354\354\354\357\357\357\362\362" + "\362\364\364\364\366\366\366\367\367\367\367\367\367\370\370\370\370\370" + "\370\370\370\370\370\370\370\370\370\370\370\370\370\370\370\370\370\370" + "\370\370\370\370\370\370\370\370\370\370\367\367\367\367\367\367\366\366" + "\366\364\364\364\362\362\362\360\360\360\354\354\354\346\346\346\340\340" + "\340\327\327\327\315\315\315\301\301\301\263\263\263\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0NNN^^^ooo\200\200\200\221\221\221\242\242" + "\242\261\261\261\277\277\277\313\313\313\325\325\325\335\335\335\344\344" + "\344\351\351\351\355\355\355\360\360\360\362\362\362\364\364\364\365\365" + "\365\365\365\365\366\366\366\366\366\366\366\366\366\366\366\366\366\366" + "\366\366\366\366\366\366\366\366\366\366\366\366\366\366\366\366\366\366" + "\366\365\365\365\365\365\365\364\364\364\362\362\362\360\360\360\355\355" + "\355\351\351\351\344\344\344\335\335\335\325\325\325\313\313\313\277\277" + "\277\261\261\261\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0MM" + "M]]]mmm~~~\217\217\217\237\237\237\256\256\256\274\274\274\310\310\310\322" + "\322\322\332\332\332\341\341\341\346\346\346\352\352\352\355\355\355\357" + "\357\357\361\361\361\362\362\362\362\362\362\363\363\363\363\363\363\363" + "\363\363\363\363\363\363\363\363\363\363\363\363\363\363\363\363\363\363" + "\363\363\363\363\363\363\363\363\362\362\362\362\362\362\361\361\361\357" + "\357\357\355\355\355\352\352\352\346\346\346\341\341\341\332\332\332\322" + "\322\322\310\310\310\274\274\274\256\256\256\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0HHHZZZjjj{{{\213\213\213\233\233\233\252\252\252" + "\267\267\267\303\303\303\315\315\315\325\325\325\334\334\334\341\341\341" + "\345\345\345\350\350\350\352\352\352\354\354\354\354\354\354\355\355\355" + "\356\356\356\356\356\356\356\356\356\356\356\356\356\356\356\356\356\356" + "\356\356\356\356\356\356\356\356\356\356\356\356\356\356\356\355\355\355" + "\355\355\355\354\354\354\352\352\352\350\350\350\345\345\345\341\341\341" + "\334\334\334\325\325\325\315\315\315\303\303\303\267\267\267\243\243\243" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""999WWWfffvvv\206" + "\206\206\226\226\226\244\244\244\261\261\261\275\275\275\306\306\306\317" + "\317\317\325\325\325\332\332\332\336\336\336\341\341\341\343\343\343\345" + "\345\345\345\345\345\346\346\346\347\347\347\347\347\347\347\347\347\347" + "\347\347\347\347\347\347\347\347\347\347\347\347\347\347\347\347\347\347" + "\347\347\347\347\347\346\346\346\346\346\346\345\345\345\343\343\343\341" + "\341\341\336\336\336\332\332\332\325\325\325\317\317\317\306\306\306\275" + "\275\275\261\261\261\201\201\201\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\36\36\36RRRaaaqqq\200\200\200\217\217\217\235\235\235\252" + "\252\252\265\265\265\277\277\277\307\307\307\315\315\315\322\322\322\326" + "\326\326\331\331\331\333\333\333\334\334\334\335\335\335\336\336\336\336" + "\336\336\337\337\337\337\337\337\337\337\337\337\337\337\337\337\337\337" + "\337\337\337\337\337\337\337\337\337\337\337\337\337\337\336\336\336\335" + "\335\335\335\335\335\333\333\333\331\331\331\326\326\326\322\322\322\315" + "\315\315\307\307\307\277\277\277\265\265\265\252\252\252GGG\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0===\\\\\\jjjyyy\207\207\207" + "\225\225\225\241\241\241\254\254\254\265\265\265\275\275\275\303\303\303" + "\310\310\310\314\314\314\317\317\317\321\321\321\322\322\322\323\323\323" + "\324\324\324\324\324\324\325\325\325\325\325\325\325\325\325\325\325\325" + "\325\325\325\325\325\325\325\325\325\325\325\325\325\325\325\325\325\325" + "\324\324\324\323\323\323\322\322\322\321\321\321\317\317\317\314\314\314" + "\310\310\310\303\303\303\275\275\275\265\265\265\254\254\254~~~\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\10\10\10CCCcccq" + "qq~~~\213\213\213\227\227\227\241\241\241\252\252\252\261\261\261\270\270" + "\270\274\274\274\300\300\300\303\303\303\305\305\305\306\306\306\307\307" + "\307\310\310\310\310\310\310\311\311\311\311\311\311\311\311\311\311\311" + "\311\311\311\311\311\311\311\311\311\311\311\311\311\311\311\311\310\310" + "\310\310\310\310\307\307\307\306\306\306\305\305\305\303\303\303\300\300" + "\300\275\275\275\270\270\270\262\262\262\252\252\252~~~\22\22\22\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0))" + ")RRRooo\200\200\200\213\213\213\225\225\225\235\235\235\244\244\244\252\252" + "\252\257\257\257\262\262\262\265\265\265\267\267\267\270\270\270\271\271" + "\271\272\272\272\272\272\272\273\273\273\273\273\273\273\273\273\273\273" + "\273\273\273\273\273\273\273\273\273\273\273\273\273\273\273\273\272\272" + "\272\272\272\272\271\271\271\270\270\270\267\267\267\265\265\265\262\262" + "\262\257\257\257\243\243\243\201\201\201GGG\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" +}; + +#endif + \ No newline at end of file diff --git a/libs/threadimageio/ffmpegthumbnailer/filmstripfilter.cpp b/libs/threadimageio/ffmpegthumbnailer/filmstripfilter.cpp new file mode 100644 index 0000000000..6c93bb225a --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/filmstripfilter.cpp @@ -0,0 +1,97 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "filmstrip.h" +#include "filmstripfilter.h" + +namespace Digikam +{ + +static const int FILMHOLE_WIDTH = 12; +static const int FILMHOLE_HEIGHT = 10; + +static const quint8* determineFilmStrip(quint32 videoWidth, quint32& filmStripWidth, quint32& filmStripHeight) +{ + if (videoWidth <= SMALLEST_FILM_STRIP_WIDTH * 2) + { + return NULL; + } + + if (videoWidth <= 96) + { + filmStripWidth = filmStripHeight = 4; + return filmStrip4; + } + + if (videoWidth <= 192) + { + filmStripWidth = filmStripHeight = 8; + return filmStrip8; + } + + if (videoWidth <= 384) + { + filmStripWidth = filmStripHeight = 16; + return filmStrip16; + } + + if (videoWidth <= 768) + { + filmStripWidth = filmStripHeight = 32; + return filmStrip32; + } + + filmStripWidth = filmStripHeight = 64; + return filmStrip64; +} + + + +void FilmStripFilter::process(VideoFrame& videoFrame) +{ + quint32 filmStripWidth; + quint32 filmStripHeight; + const quint8* filmHole = determineFilmStrip(videoFrame.width, filmStripWidth, filmStripHeight); + + if (!filmHole) + { + return; + } + + int frameIndex = 0; + int filmHoleIndex = 0; + int offset = (videoFrame.width * 3) - 3; + + for (quint32 i = 0; i < videoFrame.height; ++i) + { + for (quint32 j = 0; j < filmStripWidth * 3; j+=3) + { + int currentFilmHoleIndex = filmHoleIndex + j; + + videoFrame.frameData[frameIndex + j] = filmHole[currentFilmHoleIndex]; + videoFrame.frameData[frameIndex + j + 1] = filmHole[currentFilmHoleIndex + 1]; + videoFrame.frameData[frameIndex + j + 2] = filmHole[currentFilmHoleIndex + 2]; + + videoFrame.frameData[frameIndex + offset - j] = filmHole[currentFilmHoleIndex]; + videoFrame.frameData[frameIndex + offset - j + 1] = filmHole[currentFilmHoleIndex + 1]; + videoFrame.frameData[frameIndex + offset - j + 2] = filmHole[currentFilmHoleIndex + 2]; + } + frameIndex += videoFrame.lineSize; + filmHoleIndex = (i % filmStripHeight) * filmStripWidth * 3; + } +} + +} diff --git a/libs/threadimageio/ffmpegthumbnailer/filmstripfilter.h b/libs/threadimageio/ffmpegthumbnailer/filmstripfilter.h new file mode 100644 index 0000000000..0cee4a958b --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/filmstripfilter.h @@ -0,0 +1,34 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef FILMSTRIPFILTER_H +#define FILMSTRIPFILTER_H + +#include "ifilter.h" + +namespace Digikam +{ + +class FilmStripFilter : public IFilter +{ +public: + virtual ~FilmStripFilter() {} + virtual void process(VideoFrame& videoFrame); +}; + +} + +#endif diff --git a/libs/threadimageio/ffmpegthumbnailer/histogram.h b/libs/threadimageio/ffmpegthumbnailer/histogram.h new file mode 100644 index 0000000000..dc96dee1cb --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/histogram.h @@ -0,0 +1,40 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef HISTOGRAM_H +#define HISTOGRAM_H + +#include + +namespace Digikam +{ + +template +struct Histogram { + T r[256]; + T g[256]; + T b[256]; + + Histogram() { + memset(r, 0, 255 * sizeof(T)); + memset(g, 0, 255 * sizeof(T)); + memset(b, 0, 255 * sizeof(T)); + } +}; + +} + +#endif diff --git a/libs/threadimageio/ffmpegthumbnailer/ifilter.h b/libs/threadimageio/ffmpegthumbnailer/ifilter.h new file mode 100644 index 0000000000..9e3ce01672 --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/ifilter.h @@ -0,0 +1,34 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef IFILTER_H +#define IFILTER_H + +#include "videoframe.h" + +namespace Digikam +{ + +class IFilter +{ +public: + virtual ~IFilter() {}; + virtual void process(VideoFrame& frameData) = 0; +}; + +} + +#endif diff --git a/libs/threadimageio/ffmpegthumbnailer/imagewriter.cpp b/libs/threadimageio/ffmpegthumbnailer/imagewriter.cpp new file mode 100644 index 0000000000..6bb452ec42 --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/imagewriter.cpp @@ -0,0 +1,40 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "imagewriter.h" +#include + + +using namespace std; + +namespace Digikam +{ + +ImageWriter::ImageWriter() +{ +} + +void ImageWriter::writeFrame(VideoFrame& frame, QImage& image) +{ + QImage previewImage(frame.width, frame.height, QImage::Format_RGB888); + for (quint32 y = 0; y < frame.height; y++) { + // Copy each line .. + memcpy(previewImage.scanLine(y), &frame.frameData[y*frame.lineSize], frame.width*3); + } + + image = previewImage; +} +} diff --git a/libs/threadimageio/ffmpegthumbnailer/imagewriter.h b/libs/threadimageio/ffmpegthumbnailer/imagewriter.h new file mode 100644 index 0000000000..93e9b6ccc9 --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/imagewriter.h @@ -0,0 +1,37 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef IMAGE_WRITER_H +#define IMAGE_WRITER_H +#include "videoframe.h" +#include +#include +#include +namespace Digikam +{ + +class ImageWriter +{ +public: + ImageWriter(); + virtual ~ImageWriter() {} + + virtual void writeFrame(VideoFrame& frame, QImage& image); +}; + +} + +#endif diff --git a/libs/threadimageio/ffmpegthumbnailer/moviedecoder.cpp b/libs/threadimageio/ffmpegthumbnailer/moviedecoder.cpp new file mode 100644 index 0000000000..246f7677e8 --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/moviedecoder.cpp @@ -0,0 +1,460 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "moviedecoder.h" + +#include +#include + +extern "C" { +#include +} + +using namespace std; + +namespace Digikam +{ + +MovieDecoder::MovieDecoder(const QString& filename, AVFormatContext* pavContext) + : m_VideoStream(-1) + , m_pFormatContext(pavContext) + , m_pVideoCodecContext(NULL) + , m_pVideoCodec(NULL) + , m_pVideoStream(NULL) + , m_pFrame(NULL) + , m_pFrameBuffer(NULL) + , m_pPacket(NULL) + , m_FormatContextWasGiven(pavContext != NULL) + , m_AllowSeek(true) + , m_initialized(false) + , m_bufferSinkContext(nullptr) + , m_bufferSourceContext(nullptr) + , m_filterGraph(nullptr) + , m_filterFrame(nullptr) +{ + initialize(filename); +} + +MovieDecoder::~MovieDecoder() +{ + destroy(); +} + +void MovieDecoder::initialize(const QString& filename) +{ + m_lastWidth = -1; + m_lastHeight = -1; + m_lastPixfmt = AV_PIX_FMT_NONE; + av_register_all(); + avcodec_register_all(); + + QFileInfo fileInfo(filename); + + if ((!m_FormatContextWasGiven) && avformat_open_input(&m_pFormatContext, fileInfo.absoluteFilePath().toLocal8Bit().data(), NULL, NULL) != 0) { + qDebug() << "Could not open input file: " << fileInfo.absoluteFilePath(); + return; + } + + if (avformat_find_stream_info(m_pFormatContext, 0) < 0) { + qDebug() << "Could not find stream information"; + return; + } + + initializeVideo(); + m_pFrame = av_frame_alloc(); + + if (m_pFrame) { + m_initialized=true; + } +} + +bool MovieDecoder::getInitialized() +{ + return m_initialized; +} + + +void MovieDecoder::destroy() +{ + deleteFilterGraph(); + if (m_pVideoCodecContext) { + avcodec_close(m_pVideoCodecContext); + m_pVideoCodecContext = NULL; + } + + if ((!m_FormatContextWasGiven) && m_pFormatContext) { + avformat_close_input(&m_pFormatContext); + m_pFormatContext = NULL; + } + + if (m_pPacket) { + av_packet_unref(m_pPacket); + delete m_pPacket; + m_pPacket = NULL; + } + + if (m_pFrame) { + av_frame_free(&m_pFrame); + m_pFrame = NULL; + } + + if (m_pFrameBuffer) { + av_free(m_pFrameBuffer); + m_pFrameBuffer = NULL; + } +} + +QString MovieDecoder::getCodec() +{ + QString codecName; + if (m_pVideoCodec) { + codecName=QString::fromLatin1(m_pVideoCodec->name); + } + return codecName; +} + +void MovieDecoder::initializeVideo() +{ + for (unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) { + if (m_pFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { + m_pVideoStream = m_pFormatContext->streams[i]; + m_VideoStream = i; + break; + } + } + + if (m_VideoStream == -1) { + qDebug() << "Could not find video stream"; + return; + } + + m_pVideoCodecContext = m_pFormatContext->streams[m_VideoStream]->codec; + m_pVideoCodec = avcodec_find_decoder(m_pVideoCodecContext->codec_id); + + if (m_pVideoCodec == NULL) { + // set to NULL, otherwise avcodec_close(m_pVideoCodecContext) crashes + m_pVideoCodecContext = NULL; + qDebug() << "Video Codec not found"; + return; + } + + m_pVideoCodecContext->workaround_bugs = 1; + + if (avcodec_open2(m_pVideoCodecContext, m_pVideoCodec, 0) < 0) { + qDebug() << "Could not open video codec"; + } +} + +int MovieDecoder::getWidth() +{ + if (m_pVideoCodecContext) { + return m_pVideoCodecContext->width; + } + + return -1; +} + +int MovieDecoder::getHeight() +{ + if (m_pVideoCodecContext) { + return m_pVideoCodecContext->height; + } + + return -1; +} + +int MovieDecoder::getDuration() +{ + if (m_pFormatContext) { + return static_cast(m_pFormatContext->duration / AV_TIME_BASE); + } + + return 0; +} + +void MovieDecoder::seek(int timeInSeconds) +{ + if (!m_AllowSeek) { + return; + } + + qint64 timestamp = AV_TIME_BASE * static_cast(timeInSeconds); + + if (timestamp < 0) { + timestamp = 0; + } + + int ret = av_seek_frame(m_pFormatContext, -1, timestamp, 0); + if (ret >= 0) { + avcodec_flush_buffers(m_pFormatContext->streams[m_VideoStream]->codec); + } else { + qDebug() << "Seeking in video failed"; + return; + } + + int keyFrameAttempts = 0; + bool gotFrame = 0; + + do { + int count = 0; + gotFrame = 0; + + while (!gotFrame && count < 20) { + getVideoPacket(); + gotFrame = decodeVideoPacket(); + ++count; + } + + ++keyFrameAttempts; + } while ((!gotFrame || !m_pFrame->key_frame) && keyFrameAttempts < 200); + + if (gotFrame == 0) { + qDebug() << "Seeking in video failed"; + } +} + + +void MovieDecoder::decodeVideoFrame() +{ + bool frameFinished = false; + + while (!frameFinished && getVideoPacket()) { + frameFinished = decodeVideoPacket(); + } + + if (!frameFinished) { + qDebug() << "decodeVideoFrame() failed: frame not finished"; + return; + } +} + +bool MovieDecoder::decodeVideoPacket() +{ + if (m_pPacket->stream_index != m_VideoStream) { + return false; + } + + av_frame_unref(m_pFrame); + + int frameFinished = 0; + +#if LIBAVCODEC_VERSION_MAJOR < 53 + int bytesDecoded = avcodec_decode_video(m_pVideoCodecContext, m_pFrame, &frameFinished, m_pPacket->data, m_pPacket->size); +#else + int bytesDecoded = avcodec_decode_video2(m_pVideoCodecContext, m_pFrame, &frameFinished, m_pPacket); +#endif + + if (bytesDecoded < 0) { + qDebug() << "Failed to decode video frame: bytesDecoded < 0"; + } + + return (frameFinished > 0); +} + +bool MovieDecoder::getVideoPacket() +{ + bool framesAvailable = true; + bool frameDecoded = false; + + int attempts = 0; + + if (m_pPacket) { + av_packet_unref(m_pPacket); + delete m_pPacket; + } + + m_pPacket = new AVPacket(); + + while (framesAvailable && !frameDecoded && (attempts++ < 1000)) { + framesAvailable = av_read_frame(m_pFormatContext, m_pPacket) >= 0; + if (framesAvailable) { + frameDecoded = m_pPacket->stream_index == m_VideoStream; + if (!frameDecoded) { + av_packet_unref(m_pPacket); + } + } + } + + return frameDecoded; +} + +void MovieDecoder::deleteFilterGraph() +{ + if (m_filterGraph) { + av_frame_free(&m_filterFrame); + avfilter_graph_free(&m_filterGraph); + m_filterGraph = nullptr; + } +} + +bool MovieDecoder::initFilterGraph(enum AVPixelFormat pixfmt, int width, int height) +{ + AVFilterInOut *inputs = nullptr, *outputs = nullptr; + + deleteFilterGraph(); + m_filterGraph = avfilter_graph_alloc(); + + QByteArray arguments("buffer="); + arguments += "video_size=" + QByteArray::number(width) + "x" + QByteArray::number(height) + ":"; + arguments += "pix_fmt=" + QByteArray::number(pixfmt) + ":"; + arguments += "time_base=1/1:pixel_aspect=0/1[in];"; + arguments += "[in]yadif[out];"; + arguments += "[out]buffersink"; + + int ret = avfilter_graph_parse2(m_filterGraph, arguments.constData(), &inputs, &outputs); + if (ret < 0) { + qWarning() << "Unable to parse filter graph"; + return false; + } + + if(inputs || outputs) + return -1; + + ret = avfilter_graph_config(m_filterGraph, nullptr); + if (ret < 0) { + qWarning() << "Unable to validate filter graph"; + return false; + } + + m_bufferSourceContext = avfilter_graph_get_filter(m_filterGraph, "Parsed_buffer_0"); + m_bufferSinkContext = avfilter_graph_get_filter(m_filterGraph, "Parsed_buffersink_2"); + if (!m_bufferSourceContext || !m_bufferSinkContext) { + qWarning() << "Unable to get source or sink"; + return false; + } + m_filterFrame = av_frame_alloc(); + m_lastWidth = width; + m_lastHeight = height; + m_lastPixfmt = pixfmt; + + return true; +} + +bool MovieDecoder::processFilterGraph(AVPicture *dst, const AVPicture *src, + enum AVPixelFormat pixfmt, int width, int height) +{ + if (!m_filterGraph || width != m_lastWidth || + height != m_lastHeight || pixfmt != m_lastPixfmt) { + + if (!initFilterGraph(pixfmt, width, height)) { + return false; + } + } + + memcpy(m_filterFrame->data, src->data, sizeof(src->data)); + memcpy(m_filterFrame->linesize, src->linesize, sizeof(src->linesize)); + m_filterFrame->width = width; + m_filterFrame->height = height; + m_filterFrame->format = pixfmt; + + int ret = av_buffersrc_add_frame(m_bufferSourceContext, m_filterFrame); + if (ret < 0) { + return false; + } + + ret = av_buffersink_get_frame(m_bufferSinkContext, m_filterFrame); + if (ret < 0) { + return false; + } + + av_picture_copy(dst, (const AVPicture *) m_filterFrame, pixfmt, width, height); + av_frame_unref(m_filterFrame); + + return true; +} + +void MovieDecoder::getScaledVideoFrame(int scaledSize, bool maintainAspectRatio, VideoFrame& videoFrame) +{ + if (m_pFrame->interlaced_frame) { + processFilterGraph((AVPicture*) m_pFrame, (AVPicture*) m_pFrame, m_pVideoCodecContext->pix_fmt, + m_pVideoCodecContext->width, m_pVideoCodecContext->height); + } + + int scaledWidth, scaledHeight; + convertAndScaleFrame(AV_PIX_FMT_RGB24, scaledSize, maintainAspectRatio, scaledWidth, scaledHeight); + + videoFrame.width = scaledWidth; + videoFrame.height = scaledHeight; + videoFrame.lineSize = m_pFrame->linesize[0]; + + videoFrame.frameData.clear(); + videoFrame.frameData.resize(videoFrame.lineSize * videoFrame.height); + memcpy((&(videoFrame.frameData.front())), m_pFrame->data[0], videoFrame.lineSize * videoFrame.height); +} + +void MovieDecoder::convertAndScaleFrame(AVPixelFormat format, int scaledSize, bool maintainAspectRatio, int& scaledWidth, int& scaledHeight) +{ + calculateDimensions(scaledSize, maintainAspectRatio, scaledWidth, scaledHeight); + SwsContext* scaleContext = sws_getContext(m_pVideoCodecContext->width, m_pVideoCodecContext->height, + m_pVideoCodecContext->pix_fmt, scaledWidth, scaledHeight, + format, SWS_BICUBIC, NULL, NULL, NULL); + + if (NULL == scaleContext) { + qDebug() << "Failed to create resize context"; + return; + } + + AVFrame* convertedFrame = NULL; + uint8_t* convertedFrameBuffer = NULL; + + createAVFrame(&convertedFrame, &convertedFrameBuffer, scaledWidth, scaledHeight, format); + + sws_scale(scaleContext, m_pFrame->data, m_pFrame->linesize, 0, m_pVideoCodecContext->height, + convertedFrame->data, convertedFrame->linesize); + sws_freeContext(scaleContext); + + av_frame_free(&m_pFrame); + av_free(m_pFrameBuffer); + + m_pFrame = convertedFrame; + m_pFrameBuffer = convertedFrameBuffer; +} + +void MovieDecoder::calculateDimensions(int squareSize, bool maintainAspectRatio, int& destWidth, int& destHeight) +{ + if (!maintainAspectRatio) { + destWidth = squareSize; + destHeight = squareSize; + } else { + int srcWidth = m_pVideoCodecContext->width; + int srcHeight = m_pVideoCodecContext->height; + int ascpectNominator = m_pVideoCodecContext->sample_aspect_ratio.num; + int ascpectDenominator = m_pVideoCodecContext->sample_aspect_ratio.den; + + if (ascpectNominator != 0 && ascpectDenominator != 0) { + srcWidth = srcWidth * ascpectNominator / ascpectDenominator; + } + + if (srcWidth > srcHeight) { + destWidth = squareSize; + destHeight = static_cast(static_cast(squareSize) / srcWidth * srcHeight); + } else { + destWidth = static_cast(static_cast(squareSize) / srcHeight * srcWidth); + destHeight = squareSize; + } + } +} + +void MovieDecoder::createAVFrame(AVFrame** avFrame, quint8** frameBuffer, int width, int height, AVPixelFormat format) +{ + *avFrame = av_frame_alloc(); + + int numBytes = avpicture_get_size(format, width, height); + *frameBuffer = reinterpret_cast(av_malloc(numBytes)); + avpicture_fill((AVPicture*) *avFrame, *frameBuffer, format, width, height); +} + +} diff --git a/libs/threadimageio/ffmpegthumbnailer/moviedecoder.h b/libs/threadimageio/ffmpegthumbnailer/moviedecoder.h new file mode 100644 index 0000000000..c793eb9989 --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/moviedecoder.h @@ -0,0 +1,89 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef MOVIEDECODER_H +#define MOVIEDECODER_H + +#include "videoframe.h" +#include + +extern "C" { +#include +#include +#include +#include +#include +} + +namespace Digikam +{ + +class MovieDecoder +{ +public: + MovieDecoder(const QString& filename, AVFormatContext* pavContext = NULL); + ~MovieDecoder(); + + QString getCodec(); + void seek(int timeInSeconds); + void decodeVideoFrame(); + void getScaledVideoFrame(int scaledSize, bool maintainAspectRatio, VideoFrame& videoFrame); + + int getWidth(); + int getHeight(); + int getDuration(); + + void initialize(const QString& filename); + void destroy(); + bool getInitialized(); + +private: + void initializeVideo(); + + bool decodeVideoPacket(); + bool getVideoPacket(); + void convertAndScaleFrame(AVPixelFormat format, int scaledSize, bool maintainAspectRatio, int& scaledWidth, int& scaledHeight); + void createAVFrame(AVFrame** avFrame, quint8** frameBuffer, int width, int height, AVPixelFormat format); + void calculateDimensions(int squareSize, bool maintainAspectRatio, int& destWidth, int& destHeight); + + void deleteFilterGraph(); + bool initFilterGraph(enum AVPixelFormat pixfmt, int width, int height); + bool processFilterGraph(AVPicture *dst, const AVPicture *src, enum AVPixelFormat pixfmt, int width, int height); + +private: + int m_VideoStream; + AVFormatContext* m_pFormatContext; + AVCodecContext* m_pVideoCodecContext; + AVCodec* m_pVideoCodec; + AVStream* m_pVideoStream; + AVFrame* m_pFrame; + quint8* m_pFrameBuffer; + AVPacket* m_pPacket; + bool m_FormatContextWasGiven; + bool m_AllowSeek; + bool m_initialized; + AVFilterContext* m_bufferSinkContext; + AVFilterContext* m_bufferSourceContext; + AVFilterGraph* m_filterGraph; + AVFrame* m_filterFrame; + int m_lastWidth; + int m_lastHeight; + enum AVPixelFormat m_lastPixfmt; +}; + +} + +#endif diff --git a/libs/threadimageio/ffmpegthumbnailer/videoframe.h b/libs/threadimageio/ffmpegthumbnailer/videoframe.h new file mode 100644 index 0000000000..513d679619 --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/videoframe.h @@ -0,0 +1,43 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef VIDEOFRAME_H +#define VIDEOFRAME_H + +#include +#include +#include + +namespace Digikam +{ + +struct VideoFrame { + VideoFrame() + : width(0), height(0), lineSize(0) {} + + VideoFrame(int width, int height, int lineSize) + : width(width), height(height), lineSize(lineSize) {} + + quint32 width; + quint32 height; + quint32 lineSize; + + std::vector frameData; +}; + +} + +#endif diff --git a/libs/threadimageio/ffmpegthumbnailer/videothumbnailer.cpp b/libs/threadimageio/ffmpegthumbnailer/videothumbnailer.cpp new file mode 100644 index 0000000000..613f8a3000 --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/videothumbnailer.cpp @@ -0,0 +1,224 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "videothumbnailer.h" + +#include "moviedecoder.h" +#include "filmstripfilter.h" +#include "imagewriter.h" + +#include +#include +#include +#include +#include +#include + + +using namespace std; + +namespace Digikam +{ + +static const int SMART_FRAME_ATTEMPTS = 25; + +VideoThumbnailer::VideoThumbnailer() + : m_ThumbnailSize(128) + , m_SeekPercentage(10) + , m_OverlayFilmStrip(false) + , m_WorkAroundIssues(false) + , m_MaintainAspectRatio(true) + , m_SmartFrameSelection(false) +{ +} + +VideoThumbnailer::VideoThumbnailer(int thumbnailSize, bool workaroundIssues, bool maintainAspectRatio, bool smartFrameSelection) + : m_ThumbnailSize(thumbnailSize) + , m_SeekPercentage(10) + , m_WorkAroundIssues(workaroundIssues) + , m_MaintainAspectRatio(maintainAspectRatio) + , m_SmartFrameSelection(smartFrameSelection) +{ +} + +VideoThumbnailer::~VideoThumbnailer() +{ +} + +void VideoThumbnailer::setSeekPercentage(int percentage) +{ + m_SeekTime.clear(); + m_SeekPercentage = percentage > 95 ? 95 : percentage; +} + +void VideoThumbnailer::setSeekTime(const QString& seekTime) +{ + m_SeekTime = seekTime; +} + +void VideoThumbnailer::setThumbnailSize(int size) +{ + m_ThumbnailSize = size; +} + +void VideoThumbnailer::setWorkAroundIssues(bool workAround) +{ + m_WorkAroundIssues = workAround; +} + +void VideoThumbnailer::setMaintainAspectRatio(bool enabled) +{ + m_MaintainAspectRatio = enabled; +} + +void VideoThumbnailer::setSmartFrameSelection(bool enabled) +{ + m_SmartFrameSelection = enabled; +} + +int timeToSeconds(const QString& time) +{ + return QTime::fromString(time, QLatin1String("hh:mm:ss")).secsTo(QTime(0, 0, 0)); +} + +void VideoThumbnailer::generateThumbnail(const QString& videoFile, ImageWriter& imageWriter, QImage &image) +{ + MovieDecoder movieDecoder(videoFile, NULL); + if (movieDecoder.getInitialized()) { + movieDecoder.decodeVideoFrame(); //before seeking, a frame has to be decoded + + if ((!m_WorkAroundIssues) || (movieDecoder.getCodec() != QLatin1String("h264"))) { //workaround for bug in older ffmpeg (100% cpu usage when seeking in h264 files) + int secondToSeekTo = m_SeekTime.isEmpty() ? movieDecoder.getDuration() * m_SeekPercentage / 100 : timeToSeconds(m_SeekTime); + movieDecoder.seek(secondToSeekTo); + } + + VideoFrame videoFrame; + + if (m_SmartFrameSelection) { + generateSmartThumbnail(movieDecoder, videoFrame); + } else { + movieDecoder.getScaledVideoFrame(m_ThumbnailSize, m_MaintainAspectRatio, videoFrame); + } + + applyFilters(videoFrame); + imageWriter.writeFrame(videoFrame, image); + } +} + +void VideoThumbnailer::generateSmartThumbnail(MovieDecoder& movieDecoder, VideoFrame& videoFrame) +{ + vector videoFrames(SMART_FRAME_ATTEMPTS); + vector > histograms(SMART_FRAME_ATTEMPTS); + + for (int i = 0; i < SMART_FRAME_ATTEMPTS; ++i) { + movieDecoder.decodeVideoFrame(); + movieDecoder.getScaledVideoFrame(m_ThumbnailSize, m_MaintainAspectRatio, videoFrames[i]); + generateHistogram(videoFrames[i], histograms[i]); + } + + int bestFrame = getBestThumbnailIndex(videoFrames, histograms); + + Q_ASSERT(bestFrame != -1); + videoFrame = videoFrames[bestFrame]; +} + +void VideoThumbnailer::generateThumbnail(const QString& videoFile, QImage &image) +{ + ImageWriter* imageWriter = new ImageWriter(); + generateThumbnail(videoFile, *imageWriter, image); + delete imageWriter; +} + +void VideoThumbnailer::addFilter(IFilter* filter) +{ + m_Filters.push_back(filter); +} + +void VideoThumbnailer::removeFilter(IFilter* filter) +{ + for (vector::iterator iter = m_Filters.begin(); + iter != m_Filters.end(); + ++iter) { + if (*iter == filter) { + m_Filters.erase(iter); + break; + } + } +} + +void VideoThumbnailer::clearFilters() +{ + m_Filters.clear(); +} + +void VideoThumbnailer::applyFilters(VideoFrame& videoFrame) +{ + for (vector::iterator iter = m_Filters.begin(); + iter != m_Filters.end(); + ++iter) { + (*iter)->process(videoFrame); + } +} + +void VideoThumbnailer::generateHistogram(const VideoFrame& videoFrame, Histogram& histogram) +{ + for (quint32 i = 0; i < videoFrame.height; ++i) { + int pixelIndex = i * videoFrame.lineSize; + for (quint32 j = 0; j < videoFrame.width * 3; j += 3) { + ++histogram.r[videoFrame.frameData[pixelIndex + j]]; + ++histogram.g[videoFrame.frameData[pixelIndex + j + 1]]; + ++histogram.b[videoFrame.frameData[pixelIndex + j + 2]]; + } + } +} + +int VideoThumbnailer::getBestThumbnailIndex(vector& videoFrames, const vector >& histograms) +{ + Q_UNUSED(videoFrames); + Histogram avgHistogram; + for (size_t i = 0; i < histograms.size(); ++i) { + for (int j = 0; j < 255; ++j) { + avgHistogram.r[j] += static_cast(histograms[i].r[j]) / histograms.size(); + avgHistogram.g[j] += static_cast(histograms[i].g[j]) / histograms.size(); + avgHistogram.b[j] += static_cast(histograms[i].b[j]) / histograms.size(); + } + } + + int bestFrame = -1; + float minRMSE = FLT_MAX; + for (size_t i = 0; i < histograms.size(); ++i) { + //calculate root mean squared error + float rmse = 0.0; + for (int j = 0; j < 255; ++j) { + float error = fabsf(avgHistogram.r[j] - histograms[i].r[j]) + + fabsf(avgHistogram.g[j] - histograms[i].g[j]) + + fabsf(avgHistogram.b[j] - histograms[i].b[j]); + rmse += (error * error) / 255; + } + + rmse = sqrtf(rmse); + if (rmse < minRMSE) { + minRMSE = rmse; + bestFrame = i; + } + } +#ifdef DEBUG_MODE + cout << "Best frame was: " << bestFrame << "(RMSE: " << minRMSE << ")" << endl; +#endif + return bestFrame; +} + +} diff --git a/libs/threadimageio/ffmpegthumbnailer/videothumbnailer.h b/libs/threadimageio/ffmpegthumbnailer/videothumbnailer.h new file mode 100644 index 0000000000..06a1f2db68 --- /dev/null +++ b/libs/threadimageio/ffmpegthumbnailer/videothumbnailer.h @@ -0,0 +1,82 @@ +// Copyright (C) 2010 Dirk Vanden Boer +// +// 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 of the License, 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. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef VIDEO_THUMBNAILER_H +#define VIDEO_THUMBNAILER_H + +#include +#include +#include +#include + +#include "digikam_export.h" +#include "ifilter.h" +#include "histogram.h" +#include +#include + + +namespace Digikam +{ + +class VideoFrame; +class ImageWriter; +class MovieDecoder; + +class DIGIKAM_EXPORT VideoThumbnailer +{ +public: + VideoThumbnailer(); + VideoThumbnailer(int thumbnailSize, bool workaroundIssues, bool maintainAspectRatio, bool smartFrameSelection); + ~VideoThumbnailer(); + + void generateThumbnail(const QString& videoFile, QImage &image); + + void setThumbnailSize(int size); + void setSeekPercentage(int percentage); + void setSeekTime(const QString& seekTime); + void setWorkAroundIssues(bool workAround); + void setMaintainAspectRatio(bool enabled); + void setSmartFrameSelection(bool enabled); + void addFilter(IFilter* filter); + void removeFilter(IFilter* filter); + void clearFilters(); + +private: + void generateThumbnail(const QString& videoFile, ImageWriter& imageWriter, QImage& image); + void generateSmartThumbnail(MovieDecoder& movieDecoder, VideoFrame& videoFrame); + + QString getMimeType(const QString& videoFile); + QString getExtension(const QString& videoFilename); + + void generateHistogram(const VideoFrame& videoFrame, Histogram& histogram); + int getBestThumbnailIndex(std::vector& videoFrames, const std::vector >& histograms); + void applyFilters(VideoFrame& frameData); + +private: + int m_ThumbnailSize; + quint16 m_SeekPercentage; + bool m_OverlayFilmStrip; + bool m_WorkAroundIssues; + bool m_MaintainAspectRatio; + bool m_SmartFrameSelection; + QString m_SeekTime; + std::vector m_Filters; +}; + +} + +#endif diff --git a/libs/threadimageio/videothumbnailer.cpp b/libs/threadimageio/videothumbnailer.cpp deleted file mode 100644 index 7602554407..0000000000 --- a/libs/threadimageio/videothumbnailer.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* ============================================================ - * - * This file is a part of digiKam project - * http://www.digikam.org - * - * Date : 2016-04-21 - * Description : QtAV based video thumbnailer - * - * Copyright (C) 2016-2018 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 "videothumbnailer.h" - -// Qt includes - -#include -#include -#include -#include - -// QtAV includes - -#include -#include -#include - -// Local includes - -#include "metaengine_rotation.h" -#include "thumbnailsize.h" -#include "digikam_debug.h" -#include "dmetadata.h" - -namespace Digikam -{ - -static uchar sprocket_large_png[] = -{ - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, - 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x0f, - 0x08, 0x06, 0x00, 0x00, 0x00, 0x0b, 0x5a, 0x84, 0x6b, 0x00, 0x00, 0x00, - 0x06, 0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xa0, - 0xbd, 0xa7, 0x93, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, - 0x00, 0x0b, 0x0e, 0x00, 0x00, 0x0b, 0x0e, 0x01, 0x40, 0xbe, 0xe1, 0x41, - 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xd6, 0x06, 0x1d, - 0x08, 0x25, 0x03, 0x5a, 0x69, 0xff, 0x95, 0x00, 0x00, 0x02, 0x4a, 0x49, - 0x44, 0x41, 0x54, 0x78, 0xda, 0x45, 0x93, 0x4d, 0x2b, 0xf5, 0x41, 0x18, - 0xc6, 0x7f, 0xc7, 0xb9, 0xbd, 0xbf, 0x53, 0x38, 0x84, 0x28, 0x59, 0x49, - 0x3d, 0x3b, 0x65, 0xc7, 0xea, 0x2c, 0x64, 0x65, 0x23, 0xf9, 0x3a, 0x16, - 0xbe, 0x80, 0x94, 0x4f, 0x20, 0x14, 0x59, 0xe8, 0xf9, 0x06, 0x3c, 0x1b, - 0x2c, 0xc8, 0x42, 0x88, 0xbc, 0xe6, 0xf5, 0x78, 0xe7, 0x3c, 0xae, 0xae, - 0x26, 0x53, 0xd3, 0xcc, 0xfc, 0xff, 0x33, 0xbf, 0xfb, 0xbe, 0xae, 0xb9, - 0x27, 0xc3, 0xc0, 0xc0, 0x3f, 0xf2, 0xf9, 0x3f, 0xb4, 0xb5, 0xc1, 0xc2, - 0x02, 0x64, 0x32, 0x90, 0xcb, 0xc1, 0xf5, 0x35, 0x94, 0x94, 0x40, 0x63, - 0x23, 0x3c, 0x3f, 0xc3, 0xe3, 0x23, 0x34, 0x34, 0x40, 0xb1, 0x08, 0xb7, - 0xb7, 0x50, 0x5e, 0x0e, 0xd9, 0x2c, 0xbc, 0xbc, 0xf8, 0xdb, 0xd8, 0x18, - 0xd4, 0xd7, 0xc3, 0xfa, 0x3a, 0x19, 0xfa, 0xfb, 0xff, 0xfe, 0x80, 0x46, - 0x48, 0x6d, 0x78, 0x18, 0x4a, 0x4b, 0xa1, 0xb9, 0x19, 0xca, 0xca, 0xe0, - 0xe9, 0x09, 0x6a, 0x6a, 0xa0, 0xae, 0x0e, 0x6e, 0x6e, 0xe0, 0xeb, 0x4b, - 0x6b, 0x07, 0x79, 0x78, 0x30, 0x78, 0x63, 0x03, 0x52, 0xeb, 0xe9, 0x21, - 0xa8, 0xad, 0x1d, 0x21, 0x82, 0x9f, 0x11, 0x0e, 0x0f, 0xbd, 0x39, 0x9f, - 0x87, 0xca, 0x4a, 0xc1, 0x3d, 0x7a, 0xee, 0x20, 0x1f, 0x1f, 0xf0, 0xf9, - 0x09, 0xef, 0xef, 0xf0, 0xf6, 0x06, 0xab, 0xab, 0x5e, 0xb7, 0xb6, 0x42, - 0x45, 0x05, 0x54, 0x57, 0x13, 0x34, 0x35, 0x41, 0x77, 0x37, 0x74, 0x74, - 0xc0, 0xfe, 0xbe, 0x01, 0x55, 0x55, 0x3e, 0xf4, 0x7b, 0x58, 0xdf, 0x7d, - 0x28, 0xc2, 0x70, 0x07, 0x91, 0x25, 0xde, 0x33, 0x38, 0x68, 0x35, 0x9b, - 0x9b, 0x04, 0xca, 0x6c, 0x6d, 0x0d, 0x40, 0x1e, 0x6a, 0x93, 0x7e, 0xca, - 0x2b, 0xc1, 0x0c, 0x2e, 0x14, 0x34, 0x37, 0xd4, 0xdd, 0xc0, 0x08, 0xed, - 0xb5, 0x25, 0xcb, 0xcb, 0x66, 0xb4, 0xb4, 0x10, 0xf4, 0xf5, 0xd9, 0x60, - 0x65, 0x2c, 0x6f, 0xe4, 0x93, 0xc0, 0x3a, 0xf4, 0xfa, 0x2a, 0xb8, 0xc0, - 0xee, 0x77, 0x77, 0x52, 0xa1, 0x00, 0xba, 0x28, 0xc3, 0xcf, 0xcf, 0xbd, - 0x1e, 0x1a, 0x42, 0x6a, 0x74, 0x89, 0x81, 0x3e, 0x44, 0x68, 0xb3, 0x0e, - 0x2a, 0x5b, 0x07, 0x31, 0x54, 0x07, 0x75, 0xfb, 0x92, 0xe8, 0x00, 0x56, - 0xa0, 0xff, 0x1e, 0x93, 0xc7, 0x89, 0x71, 0x79, 0x49, 0x70, 0x74, 0x04, - 0x3b, 0x3b, 0xe0, 0x66, 0xc3, 0x2d, 0xcd, 0xa3, 0xa5, 0x1a, 0x16, 0x91, - 0x40, 0x29, 0x90, 0xbc, 0xb5, 0x7c, 0x57, 0x00, 0xba, 0xf0, 0x40, 0x52, - 0x47, 0x47, 0xa1, 0xab, 0x0b, 0xe6, 0xe6, 0x5c, 0x9f, 0xc5, 0xa2, 0x32, - 0xd6, 0x4d, 0xa6, 0x00, 0x92, 0x2b, 0x79, 0x82, 0xb9, 0x67, 0x32, 0x82, - 0xc9, 0x12, 0x07, 0x1a, 0x1f, 0xb7, 0xc2, 0xad, 0x2d, 0x02, 0x6d, 0xdc, - 0xdd, 0x85, 0xd3, 0x53, 0x65, 0xa0, 0xf4, 0x55, 0x56, 0x82, 0x18, 0x98, - 0xcd, 0x0a, 0xee, 0xb5, 0x65, 0x27, 0x05, 0xc9, 0x0e, 0x9f, 0xdb, 0xde, - 0x46, 0xf2, 0xb5, 0x2f, 0x38, 0x39, 0x81, 0x83, 0x03, 0x70, 0x53, 0x61, - 0xab, 0xc8, 0x3d, 0x2a, 0xa0, 0x60, 0xce, 0x54, 0xd9, 0x4b, 0xae, 0xe6, - 0xf2, 0x5b, 0x70, 0xd9, 0x65, 0x65, 0x7b, 0x7b, 0xa8, 0xa9, 0x3c, 0x83, - 0xef, 0x6f, 0x98, 0x98, 0xb0, 0xfc, 0xd9, 0x59, 0x3f, 0xc1, 0x8b, 0x0b, - 0x49, 0x54, 0x86, 0xee, 0x2e, 0xa3, 0xe4, 0x73, 0xaa, 0x53, 0xaf, 0x65, - 0x9f, 0xda, 0xd4, 0x94, 0xe7, 0x2b, 0x2b, 0x04, 0xed, 0xed, 0xb0, 0xb4, - 0xe4, 0x37, 0xae, 0x76, 0x75, 0xa5, 0x2e, 0x59, 0x02, 0xca, 0x78, 0xc9, - 0x52, 0xf7, 0x3a, 0xc2, 0x40, 0x2b, 0xb0, 0xa7, 0x6a, 0x8b, 0x8b, 0x56, - 0xd6, 0xdb, 0x4b, 0x70, 0x76, 0xa6, 0x92, 0x30, 0xc8, 0x0f, 0x40, 0xd9, - 0xca, 0x3f, 0x83, 0x0b, 0x05, 0x59, 0xa1, 0xae, 0xb5, 0xe1, 0xe9, 0xd9, - 0xa6, 0xe7, 0x0d, 0xbf, 0xfe, 0xe6, 0x72, 0x04, 0xc7, 0xc7, 0x30, 0x39, - 0x09, 0x9d, 0x9d, 0x30, 0x33, 0x03, 0xb2, 0xe3, 0xfe, 0x5e, 0x50, 0xf9, - 0x26, 0x80, 0x46, 0xd9, 0x61, 0xb0, 0x2d, 0x49, 0x97, 0xa2, 0x17, 0xe5, - 0x4a, 0x98, 0x9e, 0xb6, 0xda, 0xf9, 0x79, 0xfe, 0x03, 0xe1, 0xc7, 0xff, - 0x96, 0xed, 0xf6, 0x1b, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, - 0x44, 0xae, 0x42, 0x60, 0x82 -}; -static int sprocket_large_png_len = 701; - -class VideoThumbnailer::Private -{ -public: - - Private() - { - createStrip = false; - exifRotate = false; - audioFile = false; - thumbSize = ThumbnailSize::Huge; - position = 0; - duration = 0; - extractor = 0; - } - - bool createStrip; - bool exifRotate; - bool audioFile; - int thumbSize; - qint64 position; - qint64 duration; - QString file; - QString name; - QImage strip; - VideoFrameExtractor* extractor; -}; - -VideoThumbnailer::VideoThumbnailer(QObject* const parent) - : QObject(parent), - d(new Private) -{ - d->extractor = new VideoFrameExtractor(); - d->extractor->setAutoExtract(false); - - connect(d->extractor, SIGNAL(frameExtracted(QtAV::VideoFrame)), - this, SLOT(slotFrameExtracted(QtAV::VideoFrame))); - -#if QTAV_VERSION > QTAV_VERSION_CHK(1, 12, 0) - connect(d->extractor, SIGNAL(aborted(QString)), - this, SLOT(slotFrameError())); - - connect(d->extractor, SIGNAL(error(QString)), - this, SLOT(slotFrameError())); -#else - connect(d->extractor, SIGNAL(error()), - this, SLOT(slotFrameError())); -#endif - - d->strip = QImage::fromData(sprocket_large_png, sprocket_large_png_len, "PNG"); -} - -VideoThumbnailer::~VideoThumbnailer() -{ - if (d->extractor) - { - d->extractor->deleteLater(); - } - - delete d; -} - -void VideoThumbnailer::slotGetThumbnail(const QString& file, int size, bool strip, bool rotate) -{ - d->createStrip = strip; - d->exifRotate = rotate; - - if (size < ThumbnailSize::Step || size > ThumbnailSize::HD) - { - qCDebug(DIGIKAM_GENERAL_LOG) << "Invalid video thumbnail size : " << size; - d->thumbSize = ThumbnailSize::Huge; - } - else - { - d->thumbSize = size; - } - - if (!QFile::exists(file)) - { - qCDebug(DIGIKAM_GENERAL_LOG) << "Video file " << file << " does not exist."; - emit signalThumbnailFailed(file); - return; - } - - QMimeDatabase mimeDB; - - d->file = file; - d->name = QUrl::fromLocalFile(d->file).fileName(); - bool video = mimeDB.mimeTypeForFile(d->file).name().startsWith(QLatin1String("video/")); - d->audioFile = mimeDB.mimeTypeForFile(d->file).name().startsWith(QLatin1String("audio/mpeg")); - - if (!video && !d->audioFile) - { - qCDebug(DIGIKAM_GENERAL_LOG) << "Mime type is not video or audio/mpeg from " << d->name; - emit signalThumbnailFailed(d->file); - return; - } - - AVDemuxer demuxer; - demuxer.setMedia(d->file); - - if (!demuxer.load()) - { - qCDebug(DIGIKAM_GENERAL_LOG) << "Video cannot loaded from " << d->name; - emit signalThumbnailFailed(d->file); - return; - } - - d->duration = demuxer.duration(); - demuxer.unload(); - - if (d->duration <= 0) - { - qCDebug(DIGIKAM_GENERAL_LOG) << "Video has no valid duration for " << d->name; - emit signalThumbnailFailed(d->file); - return; - } - - qCDebug(DIGIKAM_GENERAL_LOG) << "Video duration for " << d->name << "is " << d->duration << " seconds"; - - d->position = 0; - tryExtractVideoFrame(); -} - -void VideoThumbnailer::tryExtractVideoFrame() -{ - d->position += (qint64)(d->duration * 0.2); - - if (d->position >= d->duration) - { - qCDebug(DIGIKAM_GENERAL_LOG) << "Problem while video data extraction from " << d->name; - emit signalThumbnailFailed(d->file); - return; - } - - qCDebug(DIGIKAM_GENERAL_LOG) << "Trying to get thumbnail from " << d->name << " at position " << d->position; - - d->extractor->setSource(d->file); - d->extractor->setPosition(d->position); - d->extractor->extract(); -} - -void VideoThumbnailer::slotFrameError() -{ - if (d->audioFile) - { - qCDebug(DIGIKAM_GENERAL_LOG) << "Audio file has no embedded image for " << d->name; - emit signalThumbnailFailed(d->file); - return; - } - - tryExtractVideoFrame(); -} - -void VideoThumbnailer::slotFrameExtracted(const QtAV::VideoFrame& frame) -{ - QImage img = frame.toImage(); - - if (!img.isNull()) - { - img = img.scaled(d->thumbSize, d->thumbSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); - - if (d->exifRotate) - { - DMetadata meta(d->file); - int orientation = meta.getImageOrientation(); - - if (orientation != DMetadata::ORIENTATION_NORMAL && - orientation != DMetadata::ORIENTATION_UNSPECIFIED) - { - QMatrix matrix = MetaEngineRotation::toMatrix((MetaEngine::ImageOrientation)orientation); - img = img.transformed(matrix); - } - } - - if (d->createStrip && img.width() > d->strip.width() && img.height() > d->strip.height()) - { - // Add a video strip on the left side of video thumb. - - for (int y = 0 ; y < img.height() ; y += d->strip.height()) - { - for (int ys = 0 ; ys < d->strip.height() ; ys++) - { - int pos = y + ys; - - if (pos < img.height()) - { - memcpy((void*)img.constScanLine(pos), (void*)d->strip.constScanLine(ys), d->strip.bytesPerLine()); - } - } - } - } - - qCDebug(DIGIKAM_GENERAL_LOG) << "Video frame extracted with size " << img.size(); - emit signalThumbnailDone(d->file, img.copy()); - } - else - { - qCDebug(DIGIKAM_GENERAL_LOG) << "Video frame is null from " << d->name; - emit signalThumbnailFailed(d->file); - } -} - -} // namespace Digikam diff --git a/libs/threadimageio/videothumbnailer.h b/libs/threadimageio/videothumbnailer.h deleted file mode 100644 index fe3cb385fa..0000000000 --- a/libs/threadimageio/videothumbnailer.h +++ /dev/null @@ -1,81 +0,0 @@ -/* ============================================================ - * - * This file is a part of digiKam project - * http://www.digikam.org - * - * Date : 2016-04-21 - * Description : QtAV based video thumbnailer - * - * Copyright (C) 2016-2018 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 VIDEOTHUMBNAILER_H -#define VIDEOTHUMBNAILER_H - -// Qt includes - -#include -#include -#include - -// QtAV includes - -#include - -// Local includes - -#include "digikam_config.h" -#include "digikam_export.h" - -using namespace QtAV; - -namespace Digikam -{ - -class DIGIKAM_EXPORT VideoThumbnailer : public QObject -{ - Q_OBJECT - -public: - - explicit VideoThumbnailer(QObject* const parent=0); - virtual ~VideoThumbnailer(); - -public Q_SLOTS: - - void slotGetThumbnail(const QString&, int size, bool strip, bool rotate); - -Q_SIGNALS: - - void signalThumbnailDone(const QString&, const QImage&); - void signalThumbnailFailed(const QString&); - -private: - - void tryExtractVideoFrame(); - -private Q_SLOTS: - - void slotFrameError(); - void slotFrameExtracted(const QtAV::VideoFrame& frame); - -private: - - class Private; - Private* const d; -}; - -} // namespace Digikam - -#endif /* VIDEOTHUMBNAILER_H */ diff --git a/libs/threadimageio/videothumbnailerjob.cpp b/libs/threadimageio/videothumbnailerjob.cpp index 947eadd906..36ea28304f 100644 --- a/libs/threadimageio/videothumbnailerjob.cpp +++ b/libs/threadimageio/videothumbnailerjob.cpp @@ -1,202 +1,174 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2016-04-21 * Description : a class to manage video thumbnails extraction * * Copyright (C) 2016-2018 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 "videothumbnailerjob.h" // Qt includes #include #include #include // Local includes #include "videothumbnailer.h" +#include "filmstripfilter.h" #include "thumbnailsize.h" #include "digikam_debug.h" namespace Digikam { class VideoThumbnailerJob::Private { public: Private() { canceled = false; running = false; - jobDone = true; createStrip = true; exifRotate = true; thumbSize = ThumbnailSize::Huge; - vthumb = 0; } volatile bool canceled; volatile bool running; - volatile bool jobDone; bool createStrip; bool exifRotate; int thumbSize; QMutex mutex; QWaitCondition condVar; QStringList todo; - QString currentFile; - VideoThumbnailer* vthumb; }; VideoThumbnailerJob::VideoThumbnailerJob(QObject* const parent) : QThread(parent), d(new Private) { - d->vthumb = new VideoThumbnailer(this); - - connect(this, SIGNAL(signalGetThumbnail(QString,int,bool,bool)), - d->vthumb, SLOT(slotGetThumbnail(QString,int,bool,bool))); - - connect(d->vthumb, SIGNAL(signalThumbnailDone(QString,QImage)), - this, SLOT(slotThumbnailDone(QString,QImage))); - - connect(d->vthumb, SIGNAL(signalThumbnailFailed(QString)), - this, SLOT(slotThumbnailFailed(QString))); } VideoThumbnailerJob::~VideoThumbnailerJob() { // clear updateItems, stop processing slotCancel(); // stop thread { QMutexLocker lock(&d->mutex); d->running = false; d->condVar.wakeAll(); } wait(); delete d; } void VideoThumbnailerJob::setThumbnailSize(int size) { d->thumbSize = size; } void VideoThumbnailerJob::setCreateStrip(bool strip) { d->createStrip = strip; } void VideoThumbnailerJob::setExifRotate(bool rotate) { d->exifRotate = rotate; } void VideoThumbnailerJob::slotCancel() { QMutexLocker lock(&d->mutex); d->running = false; d->todo.clear(); } void VideoThumbnailerJob::addItems(const QStringList& files) { if (files.isEmpty()) { return; } { QMutexLocker lock(&d->mutex); d->running = true; d->todo << files; if (!isRunning()) { start(LowPriority); } } - processOne(); -} - -void VideoThumbnailerJob::processOne() -{ - if (!d->todo.isEmpty()) - { - d->condVar.wakeAll(); - } - else - { - emit signalThumbnailJobFinished(); - } + d->condVar.wakeAll(); } void VideoThumbnailerJob::run() { while (d->running) { QMutexLocker lock(&d->mutex); - if (d->jobDone && !d->todo.isEmpty()) + if (!d->todo.isEmpty()) { - d->jobDone = false; - d->currentFile = d->todo.takeFirst(); - qCDebug(DIGIKAM_GENERAL_LOG) << "Request to get thumbnail for " << d->currentFile; - emit signalGetThumbnail(d->currentFile, d->thumbSize, d->createStrip, d->exifRotate); + QString file = d->todo.takeFirst(); + qCDebug(DIGIKAM_GENERAL_LOG) << "Request to get thumbnail for " << file; + + VideoThumbnailer thumbnailer; + FilmStripFilter filmStrip; + QImage img; + + if (d->createStrip) + { + thumbnailer.addFilter(&filmStrip); + } + + thumbnailer.setThumbnailSize(d->thumbSize); + thumbnailer.generateThumbnail(file, img); + + if (!img.isNull()) + { + qCDebug(DIGIKAM_GENERAL_LOG) << "Video thumbnail extracted for " << file << " :: " << img; + emit signalThumbnailDone(file, img); + } + else + { + qCDebug(DIGIKAM_GENERAL_LOG) << "Failed to extract video thumbnail for " << file; + emit signalThumbnailFailed(file); + } + } + else + { + emit signalThumbnailJobFinished(); + d->condVar.wait(&d->mutex); } - - d->condVar.wait(&d->mutex); - } -} - -void VideoThumbnailerJob::slotThumbnailDone(const QString& file, const QImage& img) -{ - if (d->jobDone || d->currentFile != file) - { - return; - } - - qCDebug(DIGIKAM_GENERAL_LOG) << "Video thumbnail extracted for " << file << " :: " << img; - emit signalThumbnailDone(file, img); - d->jobDone = true; - processOne(); -} - -void VideoThumbnailerJob::slotThumbnailFailed(const QString& file) -{ - if (d->jobDone || d->currentFile != file) - { - return; } - - qCDebug(DIGIKAM_GENERAL_LOG) << "Failed to extract video thumbnail for " << file; - emit signalThumbnailFailed(file); - d->jobDone = true; - processOne(); } } // namespace Digikam diff --git a/libs/threadimageio/videothumbnailerjob.h b/libs/threadimageio/videothumbnailerjob.h index 725c284b36..24f67f2af0 100644 --- a/libs/threadimageio/videothumbnailerjob.h +++ b/libs/threadimageio/videothumbnailerjob.h @@ -1,107 +1,98 @@ /* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2016-04-21 * Description : a class to manage video thumbnails extraction * * Copyright (C) 2016-2018 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 VIDEOTHUMBNAILERJOB_H #define VIDEOTHUMBNAILERJOB_H // Qt includes #include #include #include #include #include // Local includes #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT VideoThumbnailerJob : public QThread { Q_OBJECT public: /** Standard constructor and destructor */ explicit VideoThumbnailerJob(QObject* const parent); virtual ~VideoThumbnailerJob(); /** Set size of thumbnails to generate */ void setThumbnailSize(int size); /** Add a film strip on the left side of video thumnails */ void setCreateStrip(bool strip); /** Set exif rotation of thumbnails to generate */ void setExifRotate(bool rotate); /** Add new video files to process on the pending list */ void addItems(const QStringList&); Q_SIGNALS: /** Emit when thumnail is generated and ready to use. */ void signalThumbnailDone(const QString&, const QImage&); /** Emit when thumbnail cannot be generated for a video file */ void signalThumbnailFailed(const QString&); /* Emit when the pending list is empty */ void signalThumbnailJobFinished(); - /// Internal use only. - void signalGetThumbnail(const QString&, int size, bool strip, bool rotate); - public Q_SLOTS: void slotCancel(); -private Q_SLOTS: - - void slotThumbnailDone(const QString&, const QImage&); - void slotThumbnailFailed(const QString&); - private: void run(); - void processOne(); private: class Private; Private* const d; }; } // namespace Digikam #endif /* VIDEOTHUMBNAILERJOB_H */